dhcp-4.4.1/000755 000765 000024 00000000000 13243313033 012712 5ustar00tmarkstaff000000 000000 dhcp-4.4.1/client/000755 000765 000024 00000000000 13243313033 014170 5ustar00tmarkstaff000000 000000 dhcp-4.4.1/common/000755 000765 000024 00000000000 13243313033 014202 5ustar00tmarkstaff000000 000000 dhcp-4.4.1/config+lt000755 000765 000024 00000001733 13243301226 014525 0ustar00tmarkstaff000000 000000 #!/bin/sh # # Copyright (C) 2016-2017 Internet Systems Consortium, Inc. ("ISC") # # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. # # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH # REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, # INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM # LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. # called by the legacy configure when libtool is enabled cp configure.ac+lt configure.ac autoreconf -i if test $? -ne 0; then echo autoreconf failed exit $? fi echo Reconfiguring with "$@" ./configure "$@" dhcp-4.4.1/configure.ac000644 000765 000024 00000105234 13243301226 015206 0ustar00tmarkstaff000000 000000 AC_INIT([DHCP],[4.4.1],[dhcp-users@isc.org]) # we specify "foreign" to avoid having to have the GNU mandated files, # like AUTHORS, COPYING, and such AM_INIT_AUTOMAKE([foreign]) # we specify AM_MAINTAINER_MODE to avoid problems with rebuilding # the configure and makefiles. Without it users doing things that # change the timestamps on the code, like checking it into a cvs # tree, could trigger a rebuild of the infrastructure files which # might fail if they don't have the correct tools. AM_MAINTAINER_MODE AC_CANONICAL_HOST # We want to turn on warnings if we are using gcc and the user did # not specify CFLAGS. The autoconf check for the C compiler sets the # CFLAGS if gcc is used, so we will save it before we run that check. SAVE_CFLAGS="$CFLAGS" # Now find our C compiler. AC_PROG_CC # Suppress warnings about --datarootdir AC_DEFUN([AC_DATAROOTDIR_CHECKED]) # If we have gcc, and AC_PROG_CC changed the flags, then we know the # user did not specify any flags. Add warnings in this case. if test "$GCC" = "yes"; then if test "$CFLAGS" != "$SAVE_CFLAGS"; then STD_CWARNINGS="$STD_CWARNINGS -Wall -Werror -fno-strict-aliasing" fi fi # We can have some flags to pass to bind configure BINDCONFIG= if test "$cross_compiling" = "yes"; then BINDCONFIG="--host=$host" fi # Pass CFLAGS and co. $ac_configure_args looks like "'arg1' 'arg2' ..." # and as there can be a space inside an argument some magic is required. # This sets $1 ... $N to my_configure_args, arg1 ... argN eval "set my_configure_args $ac_configure_args" # remove my_configure_args, i.e., the guard against empty $ac_configure_args shift # iterate on arguments and copying 'arg' when it begins by an upper case for a do case $a in [[A-Z]]*) BINDCONFIG="$BINDCONFIG '$a'" ;; esac done AC_SUBST(BINDCONFIG) # POSIX doesn't include the IPv6 Advanced Socket API and glibc hides # parts of the IPv6 Advanced Socket API as a result. This is stupid # as it breaks how the two halves (Basic and Advanced) of the IPv6 # Socket API were designed to be used but we have to live with it. # Use this to define _GNU_SOURCE to pull in the IPv6 Advanced Socket API. AC_USE_SYSTEM_EXTENSIONS AC_PROG_RANLIB AC_PATH_PROG(AR, ar) AC_SUBST(AR) if test "X$AR" = "X"; then AC_MSG_ERROR([ ar program not found. Please fix your PATH to include the directory in which ar resides, or set AR in the environment with the full path to ar.]) fi AC_CONFIG_HEADERS([includes/config.h]) # we sometimes need to know byte order for building packets AC_C_BIGENDIAN(AC_SUBST(byte_order, BIG_ENDIAN), AC_SUBST(byte_order, LITTLE_ENDIAN)) AC_DEFINE_UNQUOTED([DHCP_BYTE_ORDER], [$byte_order], [Define to BIG_ENDIAN for MSB (Motorola or SPARC CPUs) or LITTLE_ENDIAN for LSB (Intel CPUs).]) # Optional compile-time DEBUGging. AC_ARG_ENABLE(debug, AS_HELP_STRING([--enable-debug],[create a debug-only version of the software (default is no).]), [case "${enableval}" in yes) enable_debug=yes AC_DEFINE([DEBUG], [1], [Define to compile debug-only DHCP software.]) # Just override CFLAGS totally to remove optimization. CFLAGS="-g";; no) enable_debug=no ;; *) AC_MSG_ERROR([bad value ${enableval} for --enable-debug]) ;; esac],[enable_debug=no]) # XXX: there are actually quite a lot more DEBUG_ features we could enable, # but I don't want to pollute the --help space. # #/* #define DEBUG_TOKENS */ #/* #define DEBUG_PACKET */ #/* #define DEBUG_EXPRESSIONS */ #/* #define DEBUG_FIND_LEASE */ #/* #define DEBUG_EXPRESSION_PARSE */ #/* #define DEBUG_CLASS_MATCHING */ #/* #define DEBUG_MEMORY_LEAKAGE */ #/* #define DEBUG_MALLOC_POOL */ #/* #define DEBUG_LEASE_STATE_TRANSITIONS */ #/* #define DEBUG_RC_HISTORY */ #/* #define DEBUG_RC_HISTORY_EXHAUSTIVELY */ #/* #define RC_HISTORY_MAX 10240 */ #/* #define POINTER_DEBUG */ #/* #define DEBUG_FAILOVER_MESSAGES */ #/* #define DEBUG_FAILOVER_TIMING */ #/* #define DEBUG_DUMP_ALL_LEASES */ # Failover optional compile-time feature. AC_ARG_ENABLE(failover, AS_HELP_STRING([--enable-failover],[enable support for failover (default is yes)])) # Failover is on by default, so define if it is not explicitly disabled. if test "$enable_failover" != "no"; then enable_failover="yes" AC_DEFINE([FAILOVER_PROTOCOL], [1], [Define to include Failover Protocol support.]) fi # execute() support. AC_ARG_ENABLE(execute, AS_HELP_STRING([--enable-execute],[enable support for execute() in config (default is yes)])) # execute() is on by default, so define if it is not explicitly disabled. if test "$enable_execute" != "no" ; then enable_execute="yes" AC_DEFINE([ENABLE_EXECUTE], [1], [Define to include execute() config language support.]) fi # Server tracing support. AC_ARG_ENABLE(tracing, AS_HELP_STRING([--enable-tracing],[enable support for server activity tracing (default is yes)])) # tracing is on by default, so define if it is not explicitly disabled. if test "$enable_tracing" != "no" ; then AC_DEFINE([TRACING], [1], [Define to include server activity tracing support.]) fi # Delayed-ack feature support. AC_ARG_ENABLE(delayed_ack, AS_HELP_STRING([--enable-delayed-ack],[queues multiple DHCPACK replies (default is yes)])) if test "$enable_delayed_ack" != "no"; then enable_delayed_ack="yes" AC_DEFINE([DELAYED_ACK], [1], [Define to queue multiple DHCPACK replies per fsync.]) fi # DHCPv6 optional compile-time feature. AC_ARG_ENABLE(dhcpv6, AS_HELP_STRING([--enable-dhcpv6],[enable support for DHCPv6 (default is yes)])) # DHCPv6 is on by default, so define if it is not explicitly disabled. if test "$enable_dhcpv6" != "no"; then enable_dhcpv6="yes" AC_DEFINE([DHCPv6], [1], [Define to 1 to include DHCPv6 support.]) fi # DHCPv4o6 optional compile-time feature. AC_ARG_ENABLE(dhcpv4o6, AS_HELP_STRING([--enable-dhcpv4o6],[enable support for DHCPv4-over-DHCPv6 (default is no)])) # DHCPv4o6 is off by default, so define if it is explicitly enabled. if test "$enable_dhcpv4o6" = "yes"; then # DHCPv4o6 requires DHCPv6 if test "$enable_dhcpv6" = "no"; then AC_MSG_ERROR([dhcpv4o6 requires dhcpv6]) fi AC_DEFINE([DHCP4o6], [1], [Define to 1 to include DHCPv4 over DHCPv6 support.]) else # so we can report below enable_dhcpv4o6="no" fi # Relay port (draft-ietf-dhc-relay-port-10.txt) optional compile-time feature. AC_ARG_ENABLE(relay-port, AS_HELP_STRING([--enable-relay-port],[enable support for relay port (default is no)])) # Relay port is off by default (for now) if test "$enable_relay_port" = "yes"; then AC_DEFINE([RELAY_PORT], [1], [Define to 1 to include relay port support.]) else # so we can report below enable_relay_port="no" fi # PARANOIA is off by default (until we can test it with all features) AC_ARG_ENABLE(paranoia, AS_HELP_STRING([--enable-paranoia],[enable support for chroot/setuid (default is no)])) AC_ARG_ENABLE(early_chroot, AS_HELP_STRING([--enable-early-chroot],[enable chrooting prior to configuration (default is no)])) # If someone enables early chroot, but does not enable paranoia, do so for # them. if test "$enable_paranoia" != "yes" && \ test "$enable_early_chroot" = "yes" ; then enable_paranoia="yes" fi if test "$enable_paranoia" = "yes" ; then AC_DEFINE([PARANOIA], [1], [Define to any value to include Ari's PARANOIA patch.]) fi if test "$enable_early_chroot" = "yes" ; then AC_DEFINE([EARLY_CHROOT], [1], [Define to any value to chroot() prior to loading config.]) fi AC_ARG_ENABLE(ipv4_pktinfo, AS_HELP_STRING([--enable-ipv4-pktinfo],[enable use of pktinfo on IPv4 sockets (default is no)])) if test "$enable_ipv4_pktinfo" = "yes"; then AC_DEFINE([USE_V4_PKTINFO], [1], [Define to 1 to enable IPv4 packet info support.]) fi AC_ARG_ENABLE(use_sockets, AS_HELP_STRING([--enable-use-sockets],[use the standard BSD socket API (default is no)])) if test "$enable_use_sockets" = "yes"; then AC_DEFINE([USE_SOCKETS], [1], [Define to 1 to use the standard BSD socket API.]) fi # Include the PID in the log messages. This is useful when there may # be multiple instances of a program. # This is off by default AC_ARG_ENABLE(log_pid, AS_HELP_STRING([--enable-log-pid],[Include PIDs in syslog messages (default is no).])) if test "$enable_log_pid" = "yes" ; then AC_DEFINE([USE_LOG_PID], [1], [Define to include PIDs in syslog messages.]) fi # Allow for binary search when inserting v4 leases into queues AC_ARG_ENABLE(binary_leases, AS_HELP_STRING([--enable-binary-leases],[enable support for binary insertion of leases (default is no)])) # binary_leases is off by default. if test "$enable_binary_leases" = "yes"; then AC_DEFINE([BINARY_LEASES], [1], [Define to support binary insertion of leases into queues.]) else enable_binary_leases="no" fi # Testing section DISTCHECK_ATF_CONFIGURE_FLAG= atf_path="no" AC_ARG_WITH([atf], AS_HELP_STRING([--with-atf=PATH],[specify location where atf was installed (or "bind")]), [atf_path="$withval"]) AM_CONDITIONAL(BIND_ATF, test "$atf_path" = "bind") if test "$atf_path" = "bind" ; then DISTCHECK_ATF_CONFIGURE_FLAG="--with-atf=bind" atf_pcp="bind" atf_path="\${top_srcdir}/bind/atf" ATF_CFLAGS="-I$atf_path/include -DUNIT_TEST" ATF_LDFLAGS="-L$atf_path/lib -latf-c" ATF_BIN=`cd $srcdir; pwd`/bind/atf/bin AC_SUBST(ATF_CFLAGS) AC_SUBST(ATF_LDFLAGS) AC_SUBST(ATF_BIN) BINDCONFIG="$BINDCONFIG --with-atf" elif test "$atf_path" != "no" ; then DISTCHECK_ATF_CONFIGURE_FLAG="--with-atf=$atf_path" # Config path for pkg-config atf_pcp="" if test "$atf_path" != "yes" ; then if test -f $atf_path/lib/pkgconfig/atf-c.pc ; then atf_pcp=$atf_path/lib/pkgconfig elif test -f $atf_path/lib64/pkgconfig/atf-c.pc ; then atf_pcp=$atf_path/lib64/pkgconfig fi else # Not specified, try some common paths atf_dirs="/usr /usr/local /usr/pkg /opt /opt/local" for d in $atf_dirs do if test -f $d/lib/pkgconfig/atf-c.pc ; then atf_pcp=$d/lib/pkgconfig atf_path=$d elif test -f $d/lib64/pkgconfig/atf-c.pc ; then atf_pcp=$d/lib64/pkgconfig atf_path=$d fi done fi if test "$atf_pcp" = "" ; then AC_MSG_ERROR([Unable to find atf files in location specified]) else AC_CHECK_PROG([pkgcfg_found],[pkg-config],[pkg-config],[]) if test "$pkgcfg_found" = ""; then AC_MSG_ERROR([Could not locate ATF, pkg-config not installed]) fi ATF_CFLAGS="`PKG_CONFIG_PATH=$atf_pcp pkg-config --cflags atf-c` -DUNIT_TEST" ATF_LDFLAGS="`PKG_CONFIG_PATH=$atf_pcp pkg-config --libs atf-c`" if test -f $atf_pcp/atf-sh.pc ; then ATF_BIN="`PKG_CONFIG_PATH=$atf_pcp pkg-config --variable=exec_prefix atf-sh`/bin" else # older versions don't have atf-sh, try usual place ATF_BIN=$atf_path/bin fi UNITTESTS=tests AC_SUBST(ATF_CFLAGS) AC_SUBST(ATF_LDFLAGS) AC_SUBST(ATF_BIN) AC_SUBST(UNITTESTS) fi fi AM_CONDITIONAL(HAVE_ATF, test "$atf_pcp" != "") AM_COND_IF([HAVE_ATF], [AC_DEFINE([HAVE_ATF], [1], [ATF framework specified?])]) AC_SUBST(DISTCHECK_ATF_CONFIGURE_FLAG) ### ### Path fun. Older versions of DHCP were installed in /usr/sbin, so we ### need to look there and potentially overwrite by default (but not if ### the user configures an alternate value). LOCALSTATEDIR is totally ### braindead. No one uses /usr/local/var/db/ nor /usr/local/var/run, and ### they would be insane for suggesting it. We need to look in /var/for ### 'db' and 'state/dhcp' for db files, and /var/run for pid files by ### default. ### AC_PREFIX_PROGRAM(dhcpd) # XXX - isn't there SOME WAY to default autoconf to /var instead of # /usr/local/var/no/one/has/this/please/stop/trying? case "$localstatedir" in '${prefix}/var') localstatedir=/var ;; esac # Default server configuration file. AC_ARG_WITH(srv-conf-file, AS_HELP_STRING([--with-srv-conf-file=PATH],[Default file containing dhcpd configuration (default is typically /etc/dhcpd.conf)]), AC_DEFINE_UNQUOTED([_PATH_DHCPD_CONF], ["$withval"], [Default file containing dhcpd configuration.])) # Allow specification of alternate state files AC_ARG_WITH(srv-lease-file, AS_HELP_STRING([--with-srv-lease-file=PATH],[File for dhcpd leases (default is LOCALSTATEDIR/db/dhcpd.leases)]), AC_DEFINE_UNQUOTED([_PATH_DHCPD_DB], ["$withval"], [File for dhcpd leases.])) AC_MSG_CHECKING([for dhcpd.leases location]) if [[ "x$with_srv_lease_file" = "x" ]] ; then if [[ -d "${localstatedir}/db" ]] ; then with_srv_lease_file="${localstatedir}/db/dhcpd.leases" elif [[ -d "${localstatedir}/state" ]] ; then if [[ -d "${localstatedir}/state/dhcp" ]] ; then with_srv_lease_file="${localstatedir}/state/dhcp/dhcpd.leases" else with_srv_lease_file="${localstatedir}/state/dhcpd.leases" fi elif [[ -d "${localstatedir}/lib" ]] ; then if [[ -d "${localstatedir}/lib/dhcp" ]] ; then with_srv_lease_file="${localstatedir}/lib/dhcp/dhcpd.leases" else with_srv_lease_file="${localstatedir}/lib/dhcpd.leases" fi elif [[ -d "${localstatedir}/etc" ]] ; then with_srv_lease_file="${localstatedir}/etc/dhcpd.leases" else with_srv_lease_file="/etc/dhcpd.leases" fi fi AC_MSG_RESULT($with_srv_lease_file) AC_ARG_WITH(srv6-lease-file, AS_HELP_STRING([--with-srv6-lease-file=PATH],[File for dhcpd6 leases (default is LOCALSTATEDIR/db/dhcpd6.leases)]), AC_DEFINE_UNQUOTED([_PATH_DHCPD6_DB], ["$withval"], [File for dhcpd6 leases.])) AC_MSG_CHECKING([for dhcpd6.leases location]) if [[ "x$with_srv6_lease_file" = "x" ]] ; then if [[ -d "${localstatedir}/db" ]] ; then with_srv6_lease_file="${localstatedir}/db/dhcpd6.leases" elif [[ -d "${localstatedir}/state" ]] ; then if [[ -d "${localstatedir}/state/dhcp" ]] ; then with_srv6_lease_file="${localstatedir}/state/dhcp/dhcpd6.leases" else with_srv6_lease_file="${localstatedir}/state/dhcpd6.leases" fi elif [[ -d "${localstatedir}/lib" ]] ; then if [[ -d "${localstatedir}/lib/dhcp" ]] ; then with_srv6_lease_file="${localstatedir}/lib/dhcp/dhcpd6.leases" else with_srv6_lease_file="${localstatedir}/lib/dhcpd6.leases" fi elif [[ -d "${localstatedir}/etc" ]] ; then with_srv6_lease_file="${localstatedir}/etc/dhcpd6.leases" else with_srv6_lease_file="/etc/dhcpd6.leases" fi fi AC_MSG_RESULT($with_srv6_lease_file) AC_ARG_WITH(cli-lease-file, AS_HELP_STRING([--with-cli-lease-file=PATH],[File for dhclient leases (default is LOCALSTATEDIR/db/dhclient.leases)]), AC_DEFINE_UNQUOTED([_PATH_DHCLIENT_DB], ["$withval"], [File for dhclient leases.])) AC_MSG_CHECKING([for dhclient.leases location]) if [[ "x$with_cli_lease_file" = "x" ]] ; then if [[ -d "${localstatedir}/db" ]] ; then with_cli_lease_file="${localstatedir}/db/dhclient.leases" elif [[ -d "${localstatedir}/state" ]] ; then if [[ -d "${localstatedir}/state/dhcp" ]] ; then with_cli_lease_file="${localstatedir}/state/dhcp/dhclient.leases" else with_cli_lease_file="${localstatedir}/state/dhclient.leases" fi elif [[ -d "${localstatedir}/lib" ]] ; then if [[ -d "${localstatedir}/lib/dhcp" ]] ; then with_cli_lease_file="${localstatedir}/lib/dhcp/dhclient.leases" else with_cli_lease_file="${localstatedir}/lib/dhclient.leases" fi elif [[ -d "${localstatedir}/etc" ]] ; then with_cli_lease_file="${localstatedir}/etc/dhclient.leases" else with_cli_lease_file="/etc/dhclient.leases" fi fi AC_MSG_RESULT($with_cli_lease_file) AC_ARG_WITH(cli6-lease-file, AS_HELP_STRING([--with-cli6-lease-file=PATH],[File for dhclient6 leases (default is LOCALSTATEDIR/db/dhclient6.leases)]), AC_DEFINE_UNQUOTED([_PATH_DHCLIENT6_DB], ["$withval"], [File for dhclient6 leases.])) AC_MSG_CHECKING([for dhclient6.leases location]) if [[ "x$with_cli6_lease_file" = "x" ]] ; then if [[ -d "${localstatedir}/db" ]] ; then with_cli6_lease_file="${localstatedir}/db/dhclient6.leases" elif [[ -d "${localstatedir}/state" ]] ; then if [[ -d "${localstatedir}/state/dhcp" ]] ; then with_cli6_lease_file="${localstatedir}/state/dhcp/dhclient6.leases" else with_cli6_lease_file="${localstatedir}/state/dhclient6.leases" fi elif [[ -d "${localstatedir}/lib" ]] ; then if [[ -d "${localstatedir}/lib/dhcp" ]] ; then with_cli6_lease_file="${localstatedir}/lib/dhcp/dhclient6.leases" else with_cli6_lease_file="${localstatedir}/lib/dhclient6.leases" fi elif [[ -d "${localstatedir}/etc" ]] ; then with_cli6_lease_file="${localstatedir}/etc/dhclient6.leases" else with_cli6_lease_file="/etc/dhclient6.leases" fi fi AC_MSG_RESULT($with_cli6_lease_file) AC_ARG_WITH(srv-pid-file, AS_HELP_STRING([--with-srv-pid-file=PATH],[File for dhcpd process information (default is LOCALSTATEDIR/run/dhcpd.pid)]), AC_DEFINE_UNQUOTED([_PATH_DHCPD_PID], ["$withval"], [File for dhcpd process information.])) AC_ARG_WITH(srv6-pid-file, AS_HELP_STRING([--with-srv6-pid-file=PATH],[File for dhcpd6 process information (default is LOCALSTATEDIR/run/dhcpd6.pid)]), AC_DEFINE_UNQUOTED([_PATH_DHCPD6_PID], ["$withval"], [File for dhcpd6 process information.])) AC_ARG_WITH(cli-pid-file, AS_HELP_STRING([--with-cli-pid-file=PATH],[File for dhclient process information (default is LOCALSTATEDIR/run/dhclient.pid)]), AC_DEFINE_UNQUOTED([_PATH_DHCLIENT_PID], ["$withval"], [File for dhclient process information.])) AC_ARG_WITH(cli6-pid-file, AS_HELP_STRING([--with-cli6-pid-file=PATH],[File for dhclient6 process information (default is LOCALSTATEDIR/run/dhclient6.pid)]), AC_DEFINE_UNQUOTED([_PATH_DHCLIENT6_PID], ["$withval"], [File for dhclient6 process information.])) AC_ARG_WITH(relay-pid-file, AS_HELP_STRING([--with-relay-pid-file=PATH],[File for dhcrelay process information (default is LOCALSTATEDIR/run/dhcrelay.pid)]), AC_DEFINE_UNQUOTED([_PATH_DHCRELAY_PID], ["$withval"], [File for dhcrelay process information.])) AC_ARG_WITH(relay6-pid-file, AS_HELP_STRING([--with-relay6-pid-file=PATH],[File for dhcrelay6 process information (default is LOCALSTATEDIR/run/dhcrelay6.pid)]), AC_DEFINE_UNQUOTED([_PATH_DHCRELAY6_PID], ["$withval"], [File for dhcrelay6 process information.])) # Check basic types. AC_TYPE_INT8_T AC_TYPE_INT16_T AC_TYPE_INT32_T AC_TYPE_INT64_T # Some systems need the u_intX_t types defined across. AC_CHECK_TYPE([u_int8_t], [], [ AC_TYPE_UINT8_T AC_DEFINE(u_int8_t, [uint8_t], [Define a type for 8-bit unsigned integers.]) ]) AC_CHECK_TYPE([u_int16_t], [], [ AC_TYPE_UINT16_T AC_DEFINE(u_int16_t, [uint16_t], [Define a type for 16-bit unsigned integers.]) ]) AC_CHECK_TYPE([u_int32_t], [], [ AC_TYPE_UINT32_T AC_DEFINE(u_int32_t, [uint32_t], [Define a type for 32-bit unsigned integers.]) ]) AC_CHECK_TYPE([u_int64_t], [], [ AC_TYPE_UINT64_T AC_DEFINE(u_int64_t, [uint64_t], [Define a type for 64-bit unsigned integers.]) ]) # see if ifaddrs.h is available AC_CHECK_HEADERS(ifaddrs.h) # figure out what IPv4 interface code to use AC_CHECK_HEADERS(linux/types.h) # needed for linux/filter.h on old systems relay_port_supported="no" AC_CHECK_HEADER(linux/filter.h, DO_LPF=1, , [ #ifdef HAVE_LINUX_TYPES_H #include #endif ]) if test -n "$DO_LPF" then AC_DEFINE([HAVE_LPF], [1], [Define to 1 to use the Linux Packet Filter interface code.]) relay_port_supported="yes" else AC_CHECK_HEADER(sys/dlpi.h, DO_DLPI=1) if test -n "$DO_DLPI" then AC_DEFINE([HAVE_DLPI], [1], [Define to 1 to use DLPI interface code.]) else AC_CHECK_HEADER(net/bpf.h, DO_BPF=1) if test -n "$DO_BPF" then AC_DEFINE([HAVE_BPF], [1], [Define to 1 to use the Berkeley Packet Filter interface code.]) relay_port_supported="yes" fi fi fi if test "$enable_relay_port" = "yes"; then if test "$relay_port_supported" != "yes"; then AC_MSG_ERROR([--enable-relay-port requires BPF or LPF]) fi fi # SIOCGLIFCONF uses some transport structures. Trick is not all platforms # use the same structures. We like to use 'struct lifconf' and 'struct # lifreq', but we'll use these other structures if they're present. HPUX # does not define 'struct lifnum', but does use SIOCGLIFNUM - they use an # int value. # AC_MSG_CHECKING([for struct lifnum]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include #include ]], [[ struct lifnum a; ]])],[AC_MSG_RESULT(yes) AC_DEFINE([ISC_PLATFORM_HAVELIFNUM], [1], [Define to 1 if the system has 'struct lifnum'.])],[AC_MSG_RESULT(no)]) AC_MSG_CHECKING([for struct if_laddrconf]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include ]], [[ struct if_laddrconf a; ]])],[AC_MSG_RESULT(yes) AC_DEFINE([ISC_PLATFORM_HAVEIF_LADDRCONF], [1], [Define to 1 if the system has 'struct if_laddrconf'.])],[AC_MSG_RESULT(no)]) AC_MSG_CHECKING([for struct if_laddrreq]) AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include #include ]], [[ struct if_laddrreq a; ]])],[AC_MSG_RESULT(yes) AC_DEFINE([ISC_PLATFORM_HAVEIF_LADDRREQ], [1], [Define to 1 if the system has 'struct if_laddrreq'.])],[AC_MSG_RESULT(no)]) # # check for GCC noreturn attribute # AC_MSG_CHECKING(for GCC noreturn attribute) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[void foo() __attribute__((noreturn));]])],[AC_MSG_RESULT(yes) AC_DEFINE([ISC_DHCP_NORETURN], [__attribute__((noreturn))], [Define to the string for a noreturn attribute.])],[AC_MSG_RESULT(no) AC_DEFINE([ISC_DHCP_NORETURN], [], [Define to the string for a noreturn attribute.])]) # Look for optional headers. AC_CHECK_HEADERS(sys/socket.h net/if_dl.h net/if6.h regex.h) # Solaris needs some libraries for functions AC_SEARCH_LIBS(socket, [socket]) AC_SEARCH_LIBS(inet_ntoa, [nsl]) AC_SEARCH_LIBS(inet_aton, [socket nsl], , AC_DEFINE([NEED_INET_ATON], [1], [Define to 1 if the inet_aton() function is missing.])) # Check for a standalone regex library. AC_SEARCH_LIBS(regcomp, [regex]) AC_CHECK_FUNCS(strlcat) # For HP/UX we need -lipv6 for if_nametoindex, perhaps others. AC_SEARCH_LIBS(if_nametoindex, [ipv6]) # For some Solaris nanosleep is found by BIND in librt have_nanosleep="no" AC_CHECK_FUNC(nanosleep, have_nanosleep="yes") if test "$have_nanosleep" = "no"; then AC_CHECK_LIB(rt, nanosleep, have_nanosleep="rt") fi if test "$have_nanosleep" = "rt"; then LIBS="-lrt $LIBS" fi # check for /dev/random (declares HAVE_DEV_RANDOM) AC_MSG_CHECKING(for random device) AC_ARG_WITH(randomdev, AS_HELP_STRING([--with-randomdev=PATH],[Path for random device (default is /dev/random)]), use_randomdev="$withval", use_randomdev="unspec") if test "$use_randomdev" = "unspec"; then if test "$cross_compiling" = "yes"; then AC_MSG_RESULT(unspecified) AC_MSG_ERROR([ need --with-randomdev=PATH or --with-randomdev=no]) fi use_randomdev="/dev/random" elif test "$use_randomdev" = "yes"; then use_randomdev="/dev/random" fi if test "$use_randomdev" = "no"; then AC_MSG_RESULT(disabled) BINDCONFIG="$BINDCONFIG --with-randomdev=no" else if test "$cross_compiling" = "yes"; then AC_MSG_RESULT($use_randomdev (unchecked)) else AC_MSG_RESULT($use_randomdev) AC_CHECK_FILE($use_randomdev, AC_DEFINE([HAVE_DEV_RANDOM], [1], [Define to 1 if you have the /dev/random or other configured file.]), AC_MSG_ERROR(cannot find $use_randomdev)) fi BINDCONFIG="$BINDCONFIG --with-randomdev=$use_randomdev" fi BINDIOMUX="--disable-kqueue --disable-epoll --disable-devpoll" # check kqueue/epoll/devpoll alternative to select AC_ARG_ENABLE(kqueue, AS_HELP_STRING([--enable-kqueue],[use BSD kqueue (default is no)]), want_kqueue="$enableval", want_kqueue="no") if test "$want_kqueue" = "yes"; then BINDIOMUX="--enable-kqueue" AC_MSG_WARN([--enable-kqueue is not supported: it may lead to issues such as server looping]) fi AC_ARG_ENABLE(epoll, AS_HELP_STRING([--enable-epoll],[use Linux epoll (default is no)]), want_epoll="$enableval", want_epoll="no") if test "$want_epoll" = "yes"; then BINDIOMUX="--enable-epoll" AC_MSG_WARN([--enable-epoll is not supported: it may lead to issues such as server looping]) fi AC_ARG_ENABLE(devpoll, AS_HELP_STRING([--enable-devpoll],[use /dev/poll (default is no)]), want_devpoll="$enableval", want_devpoll="no") if test "$want_devpoll" = "yes"; then BINDIOMUX="--enable-devpoll" AC_MSG_WARN([--enable-devpoll is not supported: it may lead to issues such as server looping]) fi AC_SUBST(BINDIOMUX) # general extra bind configure arguments AC_ARG_WITH(bind-extra-config, AS_HELP_STRING([--with-bind-extra-config],[configure bind librairies with some extra options (default is none)]), use_xbindconfig="$withval", use_xbindconfig="") case "$use_xbindconfig" in yes|no|'') ;; *) BINDCONFIG="$BINDCONFIG $use_xbindconfig" AC_MSG_WARN([Most options to bind configure are not supported when used by ISC DHCP]) ;; esac # see if there is a "sa_len" field in our interface information structure AC_CHECK_MEMBER(struct sockaddr.sa_len, AC_DEFINE([HAVE_SA_LEN], [], [Define to 1 if the sockaddr structure has a length field.]), , [#include ]) # figure out pointer size SAVE_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -I$srcdir" AC_CHECK_SIZEOF(struct iaddr *, , [ #include "includes/inet.h" #include ]) CFLAGS="$SAVE_CFLAGS" # Solaris does not have the msg_control or msg_controlen members # in the msghdr structure unless you define: # # _XOPEN_SOURCE, _XOPEN_SOURCE_EXTENDED, and __EXTENSIONS__ # # See the "standards" man page for details. # # We check for the msg_control member, and if it is not found, we check # again with the appropriate defines added to the CFLAGS. (In order to # do this we have to remove the check from the cache, which is what the # "unset" is for.) AC_CHECK_MEMBER(struct msghdr.msg_control,, [CFLAGS="$CFLAGS -D_XOPEN_SOURCE -D_XOPEN_SOURCE_EXTENDED=1" CFLAGS="$CFLAGS -D__EXTENSIONS__" unset ac_cv_member_struct_msghdr_msg_control AC_CHECK_MEMBER(struct msghdr.msg_control,, [AC_MSG_ERROR([Missing msg_control member in msg_control structure.])], [ #include #include ]) ], [ #include #include ]) AC_CHECK_MEMBER(struct tpacket_auxdata.tp_vlan_tci, [AC_DEFINE([VLAN_TCI_PRESENT], [1], [tpacket_auxdata.tp_vlan_tci present])] ,, [#include ]) # bind/Makefile.in is not from automake so we need 2 variables for bind dir BINDSUBDIR= BINDDIR= BINDSRCDIR= BINDLIBIRSDIR= BINDLIBDNSDIR= BINDLIBISCCFGDIR= BINDLIBISCDIR= DISTCHECK_LIBBIND_CONFIGURE_FLAG= AC_ARG_WITH(libbind, AS_HELP_STRING([--with-libbind=PATH],[bind includes and libraries are in PATH]), use_libbind="$withval", use_libbind="no") case "$use_libbind" in yes) AC_MSG_ERROR([PATH is required in --with-libbind=PATH]) ;; no) BINDSUBDIR="\${top_srcdir}/bind" my_abs_srcdir=`cd $srcdir && pwd` BINDDIR="${my_abs_srcdir}/bind" if test ! -d "$srcdir/bind"; then AC_MSG_ERROR([Where to find or build bind includes and libraries must be specified]) fi if test -d "$srcdir/bind/bind9"; then BINDSRCDIR="${my_abs_srcdir}/bind/bind9" else if test ! -f "$srcdir/bind/version.tmp"; then AC_MSG_ERROR([Cannot find $srcdir/bind/version.tmp]) fi . "$srcdir/bind/version.tmp" bindversion=${MAJORVER}.${MINORVER}.${PATCHVER}${RELEASETYPE}${RELEASEVER} BINDSRCDIR="${my_abs_srcdir}/bind/bind-$bindversion" fi AC_CONFIG_FILES([$srcdir/bind/Makefile]) BINDLIBIRSDIR="$BINDSRCDIR/lib/irs" BINDLIBDNSDIR="$BINDSRCDIR/lib/dns" BINDLIBISCCFGDIR="$BINDSRCDIR/lib/isccfg" BINDLIBISCDIR="$BINDSRCDIR/lib/isc" ;; *) if test ! -d "$use_libbind"; then AC_MSG_ERROR([Cannot find bind directory at $use_libbind]) fi if test ! -d "$use_libbind/include" -o \ ! -f "$use_libbind/include/isc/buffer.h" then AC_MSG_ERROR([Cannot find bind includes at $use_libbind/include]) fi if test ! -d "$use_libbind/lib" -o \ \( ! -f "$use_libbind/lib/libisc.a" -a \ ! -f "$use_libbind/lib/libisc.la" \) then AC_MSG_ERROR([Cannot find bind libraries at $use_libbind/lib]) fi BINDDIR="$use_libbind" BINDLIBIRSDIR="$BINDDIR/lib" BINDLIBDNSDIR="$BINDDIR/lib" BINDLIBISCCFGDIR="$BINDDIR/lib" BINDLIBISCDIR="$BINDDIR/lib" DISTCHECK_LIBBIND_CONFIGURE_FLAG="--with-libbind=$use_libbind" ;; esac AC_SUBST(BINDSUBDIR) AC_SUBST(BINDDIR) AC_SUBST(BINDSRCDIR) AC_SUBST(BINDLIBIRSDIR) AC_SUBST(BINDLIBDNSDIR) AC_SUBST(BINDLIBISCCFGDIR) AC_SUBST(BINDLIBISCDIR) AC_SUBST(DISTCHECK_LIBBIND_CONFIGURE_FLAG) AM_CONDITIONAL(HAVE_BINDDIR, test "$use_libbind" = "no") # # GNU libtool support # case "$build_os" in sunos*) # Just set the maximum command line length for sunos # as it otherwise takes a exceptionally long time to # work it out. Required for libtool. lt_cv_sys_max_cmd_len=4096 ;; esac want_libtool="no" BINDLT= DISTCHECK_LIBTOOL_CONFIGURE_FLAG= AC_ARG_ENABLE(libtool, AS_HELP_STRING([--enable-libtool], [use GNU libtool for dynamic shared libraries (default is no).]), want_libtool="$enableval") if test "$use_libbind" != "no"; then if test "$want_libtool" = "yes" -a \ ! -f "$use_libbind/lib/libisc.la" then AC_MSG_ERROR([Cannot find dynamic libraries at $use_libbind/lib]) fi if test "$want_libtool" = "no" -a \ ! -f "$use_libbind/lib/libisc.a" then AC_MSG_ERROR([Cannot find static libraries at $use_libbind/lib]) fi fi if test "$want_libtool" = "yes"; then AC_MSG_WARN([legacy configure is used but libtool is enabled. Trying to recover...]) # expand $ac_configure_args eval "set my_configure_args $ac_configure_args" shift cd $srcdir; exec ./config+lt "$@" AC_MSG_ERROR([Recovering failed]) fi DHLIBS=LIBRARIES A=a AC_SUBST(DHLIBS) AC_SUBST(A) AC_SUBST(BINDLT) AC_SUBST(DISTCHECK_LIBTOOL_CONFIGURE_FLAG) # quoting in Makefile.am.in Q=@ AC_SUBST(Q) # install bind includes and libraries want_install_bind="no" if test "$want_libtool" = "yes"; then want_install_bind="yes" fi if test "$use_libbind" != "no"; then want_install_bind="no" fi AC_ARG_ENABLE(bind_install, AS_HELP_STRING([--enable-bind-install], [install bind includes and libraries (default is no).]), want_install_bind="$enableval") if test "$want_install_bind" = "yes"; then if test "$use_libbind" != "no"; then AC_MSG_WARN([--enable-bind-install does nothing when --with-libbind is set]) fi elif test "$want_libtool" = "yes" -a "$use_libbind" = "no"; then AC_MSG_WARN([embedded dynamic bind libraries must be installed]) fi AM_CONDITIONAL(INSTALL_BIND, test "$want_install_bind" = "yes") # OpenLDAP support. AC_ARG_WITH(ldap, AS_HELP_STRING([--with-ldap],[enable OpenLDAP support in dhcpd (default is no)]), [ldap=$withval], [ldap=no]) # OpenLDAP with SSL support. AC_ARG_WITH(ldapcrypto, AS_HELP_STRING([--with-ldapcrypto],[enable OpenLDAP crypto support in dhcpd (default is no)]), [ldapcrypto=$withval], [ldapcrypto=no]) # Gssapi to allow LDAP to authenticate with a keytab AC_ARG_WITH(ldap-gssapi, AC_HELP_STRING([--with-ldap-gssapi], [enable krb5/gssapi authentication for OpenLDAP in dhcpd (default is no)]), [ldap_gssapi=$withval], [ldap_gssapi=no]) # LDAP CASA auth support. AC_ARG_WITH(ldapcasa, AC_HELP_STRING([--with-ldapcasa], [enable LDAP CASA auth support in dhcpd (default is no)]), [ldapcasa=$withval], [ldapcasa=no]) # OpenLDAP support is disabled by default, if enabled then SSL support is an # extra optional that is also disabled by default. Enabling LDAP SSL support # implies enabling LDAP support. Similarly, KRB5 support implies LDAP support, # but doesn't include SSL. The two are not dependant. if test x$ldap = xyes || test x$ldapcrypto = xyes || test x$ldap_gssapi = xyes; then saved_LIBS="$LIBS" LIBS="" AC_SEARCH_LIBS(ldap_initialize, [ldap], , AC_MSG_FAILURE([*** Cannot find ldap_initialize with -lldap - do you need to install an OpenLDAP2 Devel package?])) AC_SEARCH_LIBS(ber_pvt_opt_on, [lber], , AC_MSG_FAILURE([*** Cannot find ber_pvt_opt_on with -llber - do you need to install an OpenLDAP2 Devel package?])) if test x$ldap_gssapi = xyes ; then AC_SEARCH_LIBS(krb5_init_context, [krb5], , AC_MSG_FAILURE([*** Cannot find krb5_init_context with -lkrb5 - do you need to install a Kerberos Devel package?])) fi # Create LDAP_LIBS which we specify them explicitly rather than lumping them in with LIBS AC_SUBST(LDAP_LIBS, [$LIBS]) LIBS="$saved_LIBS" AC_CHECK_HEADERS([ldap.h]) AC_CHECK_FUNCS([inet_pton inet_ntop]) LDAP_CFLAGS="-DLDAP_CONFIGURATION" if test x$ldapcasa = xyes ; then AC_CHECK_HEADERS([micasa_mgmd.h],[ LDAP_CFLAGS="$LDAP_CFLAGS -DLDAP_CASA_AUTH" ], AC_MSG_FAILURE([*** Cannot find micasa_mgmd.h for ldap casa auth support])) fi if test x$ldapcrypto = xyes ; then LDAP_CFLAGS="$LDAP_CFLAGS -DLDAP_USE_SSL" fi if test x$ldap_gssapi = xyes; then LDAP_CFLAGS="$LDAP_CFLAGS -DLDAP_USE_GSSAPI" fi AC_SUBST(LDAP_CFLAGS, [$LDAP_CFLAGS]) fi # Append selected warning levels to CFLAGS before substitution (but after # AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])],[],[]) & etc). CFLAGS="$CFLAGS $STD_CWARNINGS" # Try to add the bind and dhcp include directories CFLAGS="$CFLAGS -I\$(top_srcdir)/includes -I$BINDDIR/include" case "$host" in *-darwin*) CFLAGS="$CFLAGS -D__APPLE_USE_RFC_3542";; *-solaris*) # As of Solaris 11, ethernet dev files are in /dev/net AC_CHECK_FILE(/dev/net, [AC_DEFINE([USE_DEV_NET], [1], [Define to 1 if ethernet devices are in /dev/net])]) ;; esac AC_C_FLEXIBLE_ARRAY_MEMBER AC_CONFIG_FILES([ Makefile client/Makefile client/tests/Makefile common/Makefile.am common/Makefile common/tests/Makefile dhcpctl/Makefile.am dhcpctl/Makefile includes/Makefile omapip/Makefile.am omapip/Makefile relay/Makefile server/Makefile tests/Makefile.am tests/Makefile tests/unittest.sh server/tests/Makefile doc/devel/doxyfile ]) AC_OUTPUT if test "$enable_dhcpv4o6" = "yes"; then DHCP_VERSIONS="DHCPv4, DHCPv6 and DHCPv4-over-DHCPv6" elif test "$enable_dhcpv6" != "no"; then DHCP_VERSIONS="DHCPv4 and DHCPv6" else DHCP_VERSIONS="DHCPv4" fi cat > config.report << END ISC DHCP source configure results: -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- Package: Name: $PACKAGE_NAME Version: $PACKAGE_VERSION C Compiler: $CC Flags: DEFS: $DEFS CFLAGS: $CFLAGS DHCP versions: $DHCP_VERSIONS Features: debug: $enable_debug failover: $enable_failover execute: $enable_execute binary-leases: $enable_binary_leases dhcpv6: $enable_dhcpv6 delayed-ack: $enable_delayed_ack dhcpv4o6: $enable_dhcpv4o6 relay-port: $enable_relay_port Developer: ATF unittests : $atf_path END # TODO: Add Perl system tests if test "$atf_path" != "no" then echo "ATF_CFLAGS : $ATF_CFLAGS" >> config.report echo "ATF_LDFLAGS : $ATF_LDFLAGS" >> config.report echo "ATF_BIN : $ATF_BIN" >> config.report echo fi cat config.report echo echo Now you can type "make" to build ISC DHCP echo dhcp-4.4.1/configure.ac+lt000644 000765 000024 00000105435 13243301226 015624 0ustar00tmarkstaff000000 000000 AC_INIT([DHCP],[4.4.1],[dhcp-users@isc.org]) # for libtool AC_CONFIG_MACRO_DIR([m4]) # we specify "foreign" to avoid having to have the GNU mandated files, # like AUTHORS, COPYING, and such AM_INIT_AUTOMAKE([foreign]) # we specify AM_MAINTAINER_MODE to avoid problems with rebuilding # the configure and makefiles. Without it users doing things that # change the timestamps on the code, like checking it into a cvs # tree, could trigger a rebuild of the infrastructure files which # might fail if they don't have the correct tools. AM_MAINTAINER_MODE AC_CANONICAL_HOST # We want to turn on warnings if we are using gcc and the user did # not specify CFLAGS. The autoconf check for the C compiler sets the # CFLAGS if gcc is used, so we will save it before we run that check. SAVE_CFLAGS="$CFLAGS" # Now find our C compiler. AC_PROG_CC # Suppress warnings about --datarootdir AC_DEFUN([AC_DATAROOTDIR_CHECKED]) # If we have gcc, and AC_PROG_CC changed the flags, then we know the # user did not specify any flags. Add warnings in this case. if test "$GCC" = "yes"; then if test "$CFLAGS" != "$SAVE_CFLAGS"; then STD_CWARNINGS="$STD_CWARNINGS -Wall -Werror -fno-strict-aliasing" fi fi # We can have some flags to pass to bind configure BINDCONFIG= if test "$cross_compiling" = "yes"; then BINDCONFIG="--host=$host" fi # Pass CFLAGS and co. $ac_configure_args looks like "'arg1' 'arg2' ..." # and as there can be a space inside an argument some magic is required. # This sets $1 ... $N to my_configure_args, arg1 ... argN eval "set my_configure_args $ac_configure_args" # remove my_configure_args, i.e., the guard against empty $ac_configure_args shift # iterate on arguments and copying 'arg' when it begins by an upper case for a do case $a in [[A-Z]]*) BINDCONFIG="$BINDCONFIG '$a'" ;; esac done AC_SUBST(BINDCONFIG) # POSIX doesn't include the IPv6 Advanced Socket API and glibc hides # parts of the IPv6 Advanced Socket API as a result. This is stupid # as it breaks how the two halves (Basic and Advanced) of the IPv6 # Socket API were designed to be used but we have to live with it. # Use this to define _GNU_SOURCE to pull in the IPv6 Advanced Socket API. AC_USE_SYSTEM_EXTENSIONS AC_PATH_PROG(AR, ar) AC_SUBST(AR) if test "X$AR" = "X"; then AC_MSG_ERROR([ ar program not found. Please fix your PATH to include the directory in which ar resides, or set AR in the environment with the full path to ar.]) fi AC_CONFIG_HEADERS([includes/config.h]) # we sometimes need to know byte order for building packets AC_C_BIGENDIAN(AC_SUBST(byte_order, BIG_ENDIAN), AC_SUBST(byte_order, LITTLE_ENDIAN)) AC_DEFINE_UNQUOTED([DHCP_BYTE_ORDER], [$byte_order], [Define to BIG_ENDIAN for MSB (Motorola or SPARC CPUs) or LITTLE_ENDIAN for LSB (Intel CPUs).]) # Optional compile-time DEBUGging. AC_ARG_ENABLE(debug, AS_HELP_STRING([--enable-debug],[create a debug-only version of the software (default is no).]), [case "${enableval}" in yes) enable_debug=yes AC_DEFINE([DEBUG], [1], [Define to compile debug-only DHCP software.]) # Just override CFLAGS totally to remove optimization. CFLAGS="-g";; no) enable_debug=no ;; *) AC_MSG_ERROR([bad value ${enableval} for --enable-debug]) ;; esac],[enable_debug=no]) # XXX: there are actually quite a lot more DEBUG_ features we could enable, # but I don't want to pollute the --help space. # #/* #define DEBUG_TOKENS */ #/* #define DEBUG_PACKET */ #/* #define DEBUG_EXPRESSIONS */ #/* #define DEBUG_FIND_LEASE */ #/* #define DEBUG_EXPRESSION_PARSE */ #/* #define DEBUG_CLASS_MATCHING */ #/* #define DEBUG_MEMORY_LEAKAGE */ #/* #define DEBUG_MALLOC_POOL */ #/* #define DEBUG_LEASE_STATE_TRANSITIONS */ #/* #define DEBUG_RC_HISTORY */ #/* #define DEBUG_RC_HISTORY_EXHAUSTIVELY */ #/* #define RC_HISTORY_MAX 10240 */ #/* #define POINTER_DEBUG */ #/* #define DEBUG_FAILOVER_MESSAGES */ #/* #define DEBUG_FAILOVER_TIMING */ #/* #define DEBUG_DUMP_ALL_LEASES */ # Failover optional compile-time feature. AC_ARG_ENABLE(failover, AS_HELP_STRING([--enable-failover],[enable support for failover (default is yes)])) # Failover is on by default, so define if it is not explicitly disabled. if test "$enable_failover" != "no"; then enable_failover="yes" AC_DEFINE([FAILOVER_PROTOCOL], [1], [Define to include Failover Protocol support.]) fi # execute() support. AC_ARG_ENABLE(execute, AS_HELP_STRING([--enable-execute],[enable support for execute() in config (default is yes)])) # execute() is on by default, so define if it is not explicitly disabled. if test "$enable_execute" != "no" ; then enable_execute="yes" AC_DEFINE([ENABLE_EXECUTE], [1], [Define to include execute() config language support.]) fi # Server tracing support. AC_ARG_ENABLE(tracing, AS_HELP_STRING([--enable-tracing],[enable support for server activity tracing (default is yes)])) # tracing is on by default, so define if it is not explicitly disabled. if test "$enable_tracing" != "no" ; then AC_DEFINE([TRACING], [1], [Define to include server activity tracing support.]) fi # Delayed-ack feature support. AC_ARG_ENABLE(delayed_ack, AS_HELP_STRING([--enable-delayed-ack],[queues multiple DHCPACK replies (default is yes)])) if test "$enable_delayed_ack" != "no"; then enable_delayed_ack="yes" AC_DEFINE([DELAYED_ACK], [1], [Define to queue multiple DHCPACK replies per fsync.]) fi # DHCPv6 optional compile-time feature. AC_ARG_ENABLE(dhcpv6, AS_HELP_STRING([--enable-dhcpv6],[enable support for DHCPv6 (default is yes)])) # DHCPv6 is on by default, so define if it is not explicitly disabled. if test "$enable_dhcpv6" != "no"; then enable_dhcpv6="yes" AC_DEFINE([DHCPv6], [1], [Define to 1 to include DHCPv6 support.]) fi # DHCPv4o6 optional compile-time feature. AC_ARG_ENABLE(dhcpv4o6, AS_HELP_STRING([--enable-dhcpv4o6],[enable support for DHCPv4-over-DHCPv6 (default is no)])) # DHCPv4o6 is off by default, so define if it is explicitly enabled. if test "$enable_dhcpv4o6" = "yes"; then # DHCPv4o6 requires DHCPv6 if test "$enable_dhcpv6" = "no"; then AC_MSG_ERROR([dhcpv4o6 requires dhcpv6]) fi AC_DEFINE([DHCP4o6], [1], [Define to 1 to include DHCPv4 over DHCPv6 support.]) else # so we can report below enable_dhcpv4o6="no" fi # Relay port (draft-ietf-dhc-relay-port-10.txt) optional compile-time feature. AC_ARG_ENABLE(relay-port, AS_HELP_STRING([--enable-relay-port],[enable support for relay port (default is no)])) # Relay port is off by default (for now) if test "$enable_relay_port" = "yes"; then AC_DEFINE([RELAY_PORT], [1], [Define to 1 to include relay port support.]) else # so we can report below enable_relay_port="no" fi # PARANOIA is off by default (until we can test it with all features) AC_ARG_ENABLE(paranoia, AS_HELP_STRING([--enable-paranoia],[enable support for chroot/setuid (default is no)])) AC_ARG_ENABLE(early_chroot, AS_HELP_STRING([--enable-early-chroot],[enable chrooting prior to configuration (default is no)])) # If someone enables early chroot, but does not enable paranoia, do so for # them. if test "$enable_paranoia" != "yes" && \ test "$enable_early_chroot" = "yes" ; then enable_paranoia="yes" fi if test "$enable_paranoia" = "yes" ; then AC_DEFINE([PARANOIA], [1], [Define to any value to include Ari's PARANOIA patch.]) fi if test "$enable_early_chroot" = "yes" ; then AC_DEFINE([EARLY_CHROOT], [1], [Define to any value to chroot() prior to loading config.]) fi AC_ARG_ENABLE(ipv4_pktinfo, AS_HELP_STRING([--enable-ipv4-pktinfo],[enable use of pktinfo on IPv4 sockets (default is no)])) if test "$enable_ipv4_pktinfo" = "yes"; then AC_DEFINE([USE_V4_PKTINFO], [1], [Define to 1 to enable IPv4 packet info support.]) fi AC_ARG_ENABLE(use_sockets, AS_HELP_STRING([--enable-use-sockets],[use the standard BSD socket API (default is no)])) if test "$enable_use_sockets" = "yes"; then AC_DEFINE([USE_SOCKETS], [1], [Define to 1 to use the standard BSD socket API.]) fi # Include the PID in the log messages. This is useful when there may # be multiple instances of a program. # This is off by default AC_ARG_ENABLE(log_pid, AS_HELP_STRING([--enable-log-pid],[Include PIDs in syslog messages (default is no).])) if test "$enable_log_pid" = "yes" ; then AC_DEFINE([USE_LOG_PID], [1], [Define to include PIDs in syslog messages.]) fi # Allow for binary search when inserting v4 leases into queues AC_ARG_ENABLE(binary_leases, AS_HELP_STRING([--enable-binary-leases],[enable support for binary insertion of leases (default is no)])) # binary_leases is off by default. if test "$enable_binary_leases" = "yes"; then AC_DEFINE([BINARY_LEASES], [1], [Define to support binary insertion of leases into queues.]) else enable_binary_leases="no" fi # Testing section DISTCHECK_ATF_CONFIGURE_FLAG= atf_path="no" AC_ARG_WITH([atf], AS_HELP_STRING([--with-atf=PATH],[specify location where atf was installed (or "bind")]), [atf_path="$withval"]) AM_CONDITIONAL(BIND_ATF, test "$atf_path" = "bind") if test "$atf_path" = "bind" ; then DISTCHECK_ATF_CONFIGURE_FLAG="--with-atf=bind" atf_pcp="bind" atf_path="\${top_srcdir}/bind/atf" ATF_CFLAGS="-I$atf_path/include -DUNIT_TEST" ATF_LDFLAGS="-L$atf_path/lib -latf-c" ATF_BIN=`cd $srcdir; pwd`/bind/atf/bin AC_SUBST(ATF_CFLAGS) AC_SUBST(ATF_LDFLAGS) AC_SUBST(ATF_BIN) BINDCONFIG="$BINDCONFIG --with-atf" elif test "$atf_path" != "no" ; then DISTCHECK_ATF_CONFIGURE_FLAG="--with-atf=$atf_path" # Config path for pkg-config atf_pcp="" if test "$atf_path" != "yes" ; then if test -f $atf_path/lib/pkgconfig/atf-c.pc ; then atf_pcp=$atf_path/lib/pkgconfig elif test -f $atf_path/lib64/pkgconfig/atf-c.pc ; then atf_pcp=$atf_path/lib64/pkgconfig fi else # Not specified, try some common paths atf_dirs="/usr /usr/local /usr/pkg /opt /opt/local" for d in $atf_dirs do if test -f $d/lib/pkgconfig/atf-c.pc ; then atf_pcp=$d/lib/pkgconfig atf_path=$d elif test -f $d/lib64/pkgconfig/atf-c.pc ; then atf_pcp=$d/lib64/pkgconfig atf_path=$d fi done fi if test "$atf_pcp" = "" ; then AC_MSG_ERROR([Unable to find atf files in location specified]) else AC_CHECK_PROG([pkgcfg_found],[pkg-config],[pkg-config],[]) if test "$pkgcfg_found" = ""; then AC_MSG_ERROR([Could not locate ATF, pkg-config not installed]) fi ATF_CFLAGS="`PKG_CONFIG_PATH=$atf_pcp pkg-config --cflags atf-c` -DUNIT_TEST" ATF_LDFLAGS="`PKG_CONFIG_PATH=$atf_pcp pkg-config --libs atf-c`" if test -f $atf_pcp/atf-sh.pc ; then ATF_BIN="`PKG_CONFIG_PATH=$atf_pcp pkg-config --variable=exec_prefix atf-sh`/bin" else # older versions don't have atf-sh, try usual place ATF_BIN=$atf_path/bin fi UNITTESTS=tests AC_SUBST(ATF_CFLAGS) AC_SUBST(ATF_LDFLAGS) AC_SUBST(ATF_BIN) AC_SUBST(UNITTESTS) fi fi AM_CONDITIONAL(HAVE_ATF, test "$atf_pcp" != "") AM_COND_IF([HAVE_ATF], [AC_DEFINE([HAVE_ATF], [1], [ATF framework specified?])]) AC_SUBST(DISTCHECK_ATF_CONFIGURE_FLAG) ### ### Path fun. Older versions of DHCP were installed in /usr/sbin, so we ### need to look there and potentially overwrite by default (but not if ### the user configures an alternate value). LOCALSTATEDIR is totally ### braindead. No one uses /usr/local/var/db/ nor /usr/local/var/run, and ### they would be insane for suggesting it. We need to look in /var/for ### 'db' and 'state/dhcp' for db files, and /var/run for pid files by ### default. ### AC_PREFIX_PROGRAM(dhcpd) # XXX - isn't there SOME WAY to default autoconf to /var instead of # /usr/local/var/no/one/has/this/please/stop/trying? case "$localstatedir" in '${prefix}/var') localstatedir=/var ;; esac # Default server configuration file. AC_ARG_WITH(srv-conf-file, AS_HELP_STRING([--with-srv-conf-file=PATH],[Default file containing dhcpd configuration (default is typically /etc/dhcpd.conf)]), AC_DEFINE_UNQUOTED([_PATH_DHCPD_CONF], ["$withval"], [Default file containing dhcpd configuration.])) # Allow specification of alternate state files AC_ARG_WITH(srv-lease-file, AS_HELP_STRING([--with-srv-lease-file=PATH],[File for dhcpd leases (default is LOCALSTATEDIR/db/dhcpd.leases)]), AC_DEFINE_UNQUOTED([_PATH_DHCPD_DB], ["$withval"], [File for dhcpd leases.])) AC_MSG_CHECKING([for dhcpd.leases location]) if [[ "x$with_srv_lease_file" = "x" ]] ; then if [[ -d "${localstatedir}/db" ]] ; then with_srv_lease_file="${localstatedir}/db/dhcpd.leases" elif [[ -d "${localstatedir}/state" ]] ; then if [[ -d "${localstatedir}/state/dhcp" ]] ; then with_srv_lease_file="${localstatedir}/state/dhcp/dhcpd.leases" else with_srv_lease_file="${localstatedir}/state/dhcpd.leases" fi elif [[ -d "${localstatedir}/lib" ]] ; then if [[ -d "${localstatedir}/lib/dhcp" ]] ; then with_srv_lease_file="${localstatedir}/lib/dhcp/dhcpd.leases" else with_srv_lease_file="${localstatedir}/lib/dhcpd.leases" fi elif [[ -d "${localstatedir}/etc" ]] ; then with_srv_lease_file="${localstatedir}/etc/dhcpd.leases" else with_srv_lease_file="/etc/dhcpd.leases" fi fi AC_MSG_RESULT($with_srv_lease_file) AC_ARG_WITH(srv6-lease-file, AS_HELP_STRING([--with-srv6-lease-file=PATH],[File for dhcpd6 leases (default is LOCALSTATEDIR/db/dhcpd6.leases)]), AC_DEFINE_UNQUOTED([_PATH_DHCPD6_DB], ["$withval"], [File for dhcpd6 leases.])) AC_MSG_CHECKING([for dhcpd6.leases location]) if [[ "x$with_srv6_lease_file" = "x" ]] ; then if [[ -d "${localstatedir}/db" ]] ; then with_srv6_lease_file="${localstatedir}/db/dhcpd6.leases" elif [[ -d "${localstatedir}/state" ]] ; then if [[ -d "${localstatedir}/state/dhcp" ]] ; then with_srv6_lease_file="${localstatedir}/state/dhcp/dhcpd6.leases" else with_srv6_lease_file="${localstatedir}/state/dhcpd6.leases" fi elif [[ -d "${localstatedir}/lib" ]] ; then if [[ -d "${localstatedir}/lib/dhcp" ]] ; then with_srv6_lease_file="${localstatedir}/lib/dhcp/dhcpd6.leases" else with_srv6_lease_file="${localstatedir}/lib/dhcpd6.leases" fi elif [[ -d "${localstatedir}/etc" ]] ; then with_srv6_lease_file="${localstatedir}/etc/dhcpd6.leases" else with_srv6_lease_file="/etc/dhcpd6.leases" fi fi AC_MSG_RESULT($with_srv6_lease_file) AC_ARG_WITH(cli-lease-file, AS_HELP_STRING([--with-cli-lease-file=PATH],[File for dhclient leases (default is LOCALSTATEDIR/db/dhclient.leases)]), AC_DEFINE_UNQUOTED([_PATH_DHCLIENT_DB], ["$withval"], [File for dhclient leases.])) AC_MSG_CHECKING([for dhclient.leases location]) if [[ "x$with_cli_lease_file" = "x" ]] ; then if [[ -d "${localstatedir}/db" ]] ; then with_cli_lease_file="${localstatedir}/db/dhclient.leases" elif [[ -d "${localstatedir}/state" ]] ; then if [[ -d "${localstatedir}/state/dhcp" ]] ; then with_cli_lease_file="${localstatedir}/state/dhcp/dhclient.leases" else with_cli_lease_file="${localstatedir}/state/dhclient.leases" fi elif [[ -d "${localstatedir}/lib" ]] ; then if [[ -d "${localstatedir}/lib/dhcp" ]] ; then with_cli_lease_file="${localstatedir}/lib/dhcp/dhclient.leases" else with_cli_lease_file="${localstatedir}/lib/dhclient.leases" fi elif [[ -d "${localstatedir}/etc" ]] ; then with_cli_lease_file="${localstatedir}/etc/dhclient.leases" else with_cli_lease_file="/etc/dhclient.leases" fi fi AC_MSG_RESULT($with_cli_lease_file) AC_ARG_WITH(cli6-lease-file, AS_HELP_STRING([--with-cli6-lease-file=PATH],[File for dhclient6 leases (default is LOCALSTATEDIR/db/dhclient6.leases)]), AC_DEFINE_UNQUOTED([_PATH_DHCLIENT6_DB], ["$withval"], [File for dhclient6 leases.])) AC_MSG_CHECKING([for dhclient6.leases location]) if [[ "x$with_cli6_lease_file" = "x" ]] ; then if [[ -d "${localstatedir}/db" ]] ; then with_cli6_lease_file="${localstatedir}/db/dhclient6.leases" elif [[ -d "${localstatedir}/state" ]] ; then if [[ -d "${localstatedir}/state/dhcp" ]] ; then with_cli6_lease_file="${localstatedir}/state/dhcp/dhclient6.leases" else with_cli6_lease_file="${localstatedir}/state/dhclient6.leases" fi elif [[ -d "${localstatedir}/lib" ]] ; then if [[ -d "${localstatedir}/lib/dhcp" ]] ; then with_cli6_lease_file="${localstatedir}/lib/dhcp/dhclient6.leases" else with_cli6_lease_file="${localstatedir}/lib/dhclient6.leases" fi elif [[ -d "${localstatedir}/etc" ]] ; then with_cli6_lease_file="${localstatedir}/etc/dhclient6.leases" else with_cli6_lease_file="/etc/dhclient6.leases" fi fi AC_MSG_RESULT($with_cli6_lease_file) AC_ARG_WITH(srv-pid-file, AS_HELP_STRING([--with-srv-pid-file=PATH],[File for dhcpd process information (default is LOCALSTATEDIR/run/dhcpd.pid)]), AC_DEFINE_UNQUOTED([_PATH_DHCPD_PID], ["$withval"], [File for dhcpd process information.])) AC_ARG_WITH(srv6-pid-file, AS_HELP_STRING([--with-srv6-pid-file=PATH],[File for dhcpd6 process information (default is LOCALSTATEDIR/run/dhcpd6.pid)]), AC_DEFINE_UNQUOTED([_PATH_DHCPD6_PID], ["$withval"], [File for dhcpd6 process information.])) AC_ARG_WITH(cli-pid-file, AS_HELP_STRING([--with-cli-pid-file=PATH],[File for dhclient process information (default is LOCALSTATEDIR/run/dhclient.pid)]), AC_DEFINE_UNQUOTED([_PATH_DHCLIENT_PID], ["$withval"], [File for dhclient process information.])) AC_ARG_WITH(cli6-pid-file, AS_HELP_STRING([--with-cli6-pid-file=PATH],[File for dhclient6 process information (default is LOCALSTATEDIR/run/dhclient6.pid)]), AC_DEFINE_UNQUOTED([_PATH_DHCLIENT6_PID], ["$withval"], [File for dhclient6 process information.])) AC_ARG_WITH(relay-pid-file, AS_HELP_STRING([--with-relay-pid-file=PATH],[File for dhcrelay process information (default is LOCALSTATEDIR/run/dhcrelay.pid)]), AC_DEFINE_UNQUOTED([_PATH_DHCRELAY_PID], ["$withval"], [File for dhcrelay process information.])) AC_ARG_WITH(relay6-pid-file, AS_HELP_STRING([--with-relay6-pid-file=PATH],[File for dhcrelay6 process information (default is LOCALSTATEDIR/run/dhcrelay6.pid)]), AC_DEFINE_UNQUOTED([_PATH_DHCRELAY6_PID], ["$withval"], [File for dhcrelay6 process information.])) # Check basic types. AC_TYPE_INT8_T AC_TYPE_INT16_T AC_TYPE_INT32_T AC_TYPE_INT64_T # Some systems need the u_intX_t types defined across. AC_CHECK_TYPE([u_int8_t], [], [ AC_TYPE_UINT8_T AC_DEFINE(u_int8_t, [uint8_t], [Define a type for 8-bit unsigned integers.]) ]) AC_CHECK_TYPE([u_int16_t], [], [ AC_TYPE_UINT16_T AC_DEFINE(u_int16_t, [uint16_t], [Define a type for 16-bit unsigned integers.]) ]) AC_CHECK_TYPE([u_int32_t], [], [ AC_TYPE_UINT32_T AC_DEFINE(u_int32_t, [uint32_t], [Define a type for 32-bit unsigned integers.]) ]) AC_CHECK_TYPE([u_int64_t], [], [ AC_TYPE_UINT64_T AC_DEFINE(u_int64_t, [uint64_t], [Define a type for 64-bit unsigned integers.]) ]) # see if ifaddrs.h is available AC_CHECK_HEADERS(ifaddrs.h) # figure out what IPv4 interface code to use AC_CHECK_HEADERS(linux/types.h) # needed for linux/filter.h on old systems relay_port_supported="no" AC_CHECK_HEADER(linux/filter.h, DO_LPF=1, , [ #ifdef HAVE_LINUX_TYPES_H #include #endif ]) if test -n "$DO_LPF" then AC_DEFINE([HAVE_LPF], [1], [Define to 1 to use the Linux Packet Filter interface code.]) relay_port_supported="yes" else AC_CHECK_HEADER(sys/dlpi.h, DO_DLPI=1) if test -n "$DO_DLPI" then AC_DEFINE([HAVE_DLPI], [1], [Define to 1 to use DLPI interface code.]) else AC_CHECK_HEADER(net/bpf.h, DO_BPF=1) if test -n "$DO_BPF" then AC_DEFINE([HAVE_BPF], [1], [Define to 1 to use the Berkeley Packet Filter interface code.]) relay_port_supported="yes" fi fi fi if test "$enable_relay_port" = "yes"; then if test "$relay_port_supported" != "yes"; then AC_MSG_ERROR([--enable-relay-port requires BPF or LPF]) fi fi # SIOCGLIFCONF uses some transport structures. Trick is not all platforms # use the same structures. We like to use 'struct lifconf' and 'struct # lifreq', but we'll use these other structures if they're present. HPUX # does not define 'struct lifnum', but does use SIOCGLIFNUM - they use an # int value. # AC_MSG_CHECKING([for struct lifnum]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include #include ]], [[ struct lifnum a; ]])],[AC_MSG_RESULT(yes) AC_DEFINE([ISC_PLATFORM_HAVELIFNUM], [1], [Define to 1 if the system has 'struct lifnum'.])],[AC_MSG_RESULT(no)]) AC_MSG_CHECKING([for struct if_laddrconf]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include ]], [[ struct if_laddrconf a; ]])],[AC_MSG_RESULT(yes) AC_DEFINE([ISC_PLATFORM_HAVEIF_LADDRCONF], [1], [Define to 1 if the system has 'struct if_laddrconf'.])],[AC_MSG_RESULT(no)]) AC_MSG_CHECKING([for struct if_laddrreq]) AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include #include ]], [[ struct if_laddrreq a; ]])],[AC_MSG_RESULT(yes) AC_DEFINE([ISC_PLATFORM_HAVEIF_LADDRREQ], [1], [Define to 1 if the system has 'struct if_laddrreq'.])],[AC_MSG_RESULT(no)]) # # check for GCC noreturn attribute # AC_MSG_CHECKING(for GCC noreturn attribute) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[void foo() __attribute__((noreturn));]])],[AC_MSG_RESULT(yes) AC_DEFINE([ISC_DHCP_NORETURN], [__attribute__((noreturn))], [Define to the string for a noreturn attribute.])],[AC_MSG_RESULT(no) AC_DEFINE([ISC_DHCP_NORETURN], [], [Define to the string for a noreturn attribute.])]) # Look for optional headers. AC_CHECK_HEADERS(sys/socket.h net/if_dl.h net/if6.h regex.h) # Solaris needs some libraries for functions AC_SEARCH_LIBS(socket, [socket]) AC_SEARCH_LIBS(inet_ntoa, [nsl]) AC_SEARCH_LIBS(inet_aton, [socket nsl], , AC_DEFINE([NEED_INET_ATON], [1], [Define to 1 if the inet_aton() function is missing.])) # Check for a standalone regex library. AC_SEARCH_LIBS(regcomp, [regex]) AC_CHECK_FUNCS(strlcat) # For HP/UX we need -lipv6 for if_nametoindex, perhaps others. AC_SEARCH_LIBS(if_nametoindex, [ipv6]) # For some Solaris nanosleep is found by BIND in librt have_nanosleep="no" AC_CHECK_FUNC(nanosleep, have_nanosleep="yes") if test "$have_nanosleep" = "no"; then AC_CHECK_LIB(rt, nanosleep, have_nanosleep="rt") fi if test "$have_nanosleep" = "rt"; then LIBS="-lrt $LIBS" fi # check for /dev/random (declares HAVE_DEV_RANDOM) AC_MSG_CHECKING(for random device) AC_ARG_WITH(randomdev, AS_HELP_STRING([--with-randomdev=PATH],[Path for random device (default is /dev/random)]), use_randomdev="$withval", use_randomdev="unspec") if test "$use_randomdev" = "unspec"; then if test "$cross_compiling" = "yes"; then AC_MSG_RESULT(unspecified) AC_MSG_ERROR([ need --with-randomdev=PATH or --with-randomdev=no]) fi use_randomdev="/dev/random" elif test "$use_randomdev" = "yes"; then use_randomdev="/dev/random" fi if test "$use_randomdev" = "no"; then AC_MSG_RESULT(disabled) BINDCONFIG="$BINDCONFIG --with-randomdev=no" else if test "$cross_compiling" = "yes"; then AC_MSG_RESULT($use_randomdev (unchecked)) else AC_MSG_RESULT($use_randomdev) AC_CHECK_FILE($use_randomdev, AC_DEFINE([HAVE_DEV_RANDOM], [1], [Define to 1 if you have the /dev/random or other configured file.]), AC_MSG_ERROR(cannot find $use_randomdev)) fi BINDCONFIG="$BINDCONFIG --with-randomdev=$use_randomdev" fi BINDIOMUX="--disable-kqueue --disable-epoll --disable-devpoll" # check kqueue/epoll/devpoll alternative to select AC_ARG_ENABLE(kqueue, AS_HELP_STRING([--enable-kqueue],[use BSD kqueue (default is no)]), want_kqueue="$enableval", want_kqueue="no") if test "$want_kqueue" = "yes"; then BINDIOMUX="--enable-kqueue" AC_MSG_WARN([--enable-kqueue is not supported: it may lead to issues such as server looping]) fi AC_ARG_ENABLE(epoll, AS_HELP_STRING([--enable-epoll],[use Linux epoll (default is no)]), want_epoll="$enableval", want_epoll="no") if test "$want_epoll" = "yes"; then BINDIOMUX="--enable-epoll" AC_MSG_WARN([--enable-epoll is not supported: it may lead to issues such as server looping]) fi AC_ARG_ENABLE(devpoll, AS_HELP_STRING([--enable-devpoll],[use /dev/poll (default is no)]), want_devpoll="$enableval", want_devpoll="no") if test "$want_devpoll" = "yes"; then BINDIOMUX="--enable-devpoll" AC_MSG_WARN([--enable-devpoll is not supported: it may lead to issues such as server looping]) fi AC_SUBST(BINDIOMUX) # general extra bind configure arguments AC_ARG_WITH(bind-extra-config, AS_HELP_STRING([--with-bind-extra-config],[configure bind librairies with some extra options (default is none)]), use_xbindconfig="$withval", use_xbindconfig="") case "$use_xbindconfig" in yes|no|'') ;; *) BINDCONFIG="$BINDCONFIG $use_xbindconfig" AC_MSG_WARN([Most options to bind configure are not supported when used by ISC DHCP]) ;; esac # see if there is a "sa_len" field in our interface information structure AC_CHECK_MEMBER(struct sockaddr.sa_len, AC_DEFINE([HAVE_SA_LEN], [], [Define to 1 if the sockaddr structure has a length field.]), , [#include ]) # figure out pointer size SAVE_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -I$srcdir" AC_CHECK_SIZEOF(struct iaddr *, , [ #include "includes/inet.h" #include ]) CFLAGS="$SAVE_CFLAGS" # Solaris does not have the msg_control or msg_controlen members # in the msghdr structure unless you define: # # _XOPEN_SOURCE, _XOPEN_SOURCE_EXTENDED, and __EXTENSIONS__ # # See the "standards" man page for details. # # We check for the msg_control member, and if it is not found, we check # again with the appropriate defines added to the CFLAGS. (In order to # do this we have to remove the check from the cache, which is what the # "unset" is for.) AC_CHECK_MEMBER(struct msghdr.msg_control,, [CFLAGS="$CFLAGS -D_XOPEN_SOURCE -D_XOPEN_SOURCE_EXTENDED=1" CFLAGS="$CFLAGS -D__EXTENSIONS__" unset ac_cv_member_struct_msghdr_msg_control AC_CHECK_MEMBER(struct msghdr.msg_control,, [AC_MSG_ERROR([Missing msg_control member in msg_control structure.])], [ #include #include ]) ], [ #include #include ]) AC_CHECK_MEMBER(struct tpacket_auxdata.tp_vlan_tci, [AC_DEFINE([VLAN_TCI_PRESENT], [1], [tpacket_auxdata.tp_vlan_tci present])] ,, [#include ]) # bind/Makefile.in is not from automake so we need 2 variables for bind dir BINDSUBDIR= BINDDIR= BINDSRCDIR= BINDLIBIRSDIR= BINDLIBDNSDIR= BINDLIBISCCFGDIR= BINDLIBISCDIR= DISTCHECK_LIBBIND_CONFIGURE_FLAG= AC_ARG_WITH(libbind, AS_HELP_STRING([--with-libbind=PATH],[bind includes and libraries are in PATH]), use_libbind="$withval", use_libbind="no") case "$use_libbind" in yes) AC_MSG_ERROR([PATH is required in --with-libbind=PATH]) ;; no) BINDSUBDIR="\${top_srcdir}/bind" my_abs_srcdir=`cd $srcdir && pwd` BINDDIR="${my_abs_srcdir}/bind" if test ! -d "$srcdir/bind"; then AC_MSG_ERROR([Where to find or build bind includes and libraries must be specified]) fi if test -d "$srcdir/bind/bind9"; then BINDSRCDIR="${my_abs_srcdir}/bind/bind9" else if test ! -f "$srcdir/bind/version.tmp"; then AC_MSG_ERROR([Cannot find $srcdir/bind/version.tmp]) fi . "$srcdir/bind/version.tmp" bindversion=${MAJORVER}.${MINORVER}.${PATCHVER}${RELEASETYPE}${RELEASEVER} BINDSRCDIR="${my_abs_srcdir}/bind/bind-$bindversion" fi AC_CONFIG_FILES([$srcdir/bind/Makefile]) BINDLIBIRSDIR="$BINDSRCDIR/lib/irs" BINDLIBDNSDIR="$BINDSRCDIR/lib/dns" BINDLIBISCCFGDIR="$BINDSRCDIR/lib/isccfg" BINDLIBISCDIR="$BINDSRCDIR/lib/isc" ;; *) if test ! -d "$use_libbind"; then AC_MSG_ERROR([Cannot find bind directory at $use_libbind]) fi if test ! -d "$use_libbind/include" -o \ ! -f "$use_libbind/include/isc/buffer.h" then AC_MSG_ERROR([Cannot find bind includes at $use_libbind/include]) fi if test ! -d "$use_libbind/lib" -o \ \( ! -f "$use_libbind/lib/libisc.a" -a \ ! -f "$use_libbind/lib/libisc.la" \) then AC_MSG_ERROR([Cannot find bind libraries at $use_libbind/lib]) fi BINDDIR="$use_libbind" BINDLIBIRSDIR="$BINDDIR/lib" BINDLIBDNSDIR="$BINDDIR/lib" BINDLIBISCCFGDIR="$BINDDIR/lib" BINDLIBISCDIR="$BINDDIR/lib" DISTCHECK_LIBBIND_CONFIGURE_FLAG="--with-libbind=$use_libbind" ;; esac AC_SUBST(BINDSUBDIR) AC_SUBST(BINDDIR) AC_SUBST(BINDSRCDIR) AC_SUBST(BINDLIBIRSDIR) AC_SUBST(BINDLIBDNSDIR) AC_SUBST(BINDLIBISCCFGDIR) AC_SUBST(BINDLIBISCDIR) AC_SUBST(DISTCHECK_LIBBIND_CONFIGURE_FLAG) AM_CONDITIONAL(HAVE_BINDDIR, test "$use_libbind" = "no") # # GNU libtool support # case "$build_os" in sunos*) # Just set the maximum command line length for sunos # as it otherwise takes a exceptionally long time to # work it out. Required for libtool. lt_cv_sys_max_cmd_len=4096 ;; esac want_libtool="no" LT_INIT want_libtool="yes" BINDLT= DISTCHECK_LIBTOOL_CONFIGURE_FLAG= AC_ARG_ENABLE(libtool, AS_HELP_STRING([--enable-libtool], [use GNU libtool for dynamic shared libraries (default is yes).]), want_libtool="$enableval") if test "$use_libbind" != "no"; then if test "$want_libtool" = "yes" -a \ ! -f "$use_libbind/lib/libisc.la" then AC_MSG_ERROR([Cannot find dynamic libraries at $use_libbind/lib]) fi if test "$want_libtool" = "no" -a \ ! -f "$use_libbind/lib/libisc.a" then AC_MSG_ERROR([Cannot find static libraries at $use_libbind/lib]) fi fi if test "$want_libtool" = "no"; then AC_MSG_ERROR([libtool configure is used but libtool is disabled?]) fi DHLIBS=LTLIBRARIES A=la BINDLT="--with-libtool --disable-symtable" DISTCHECK_LIBTOOL_CONFIGURE_FLAG="--enable-libtool" AC_SUBST(DHLIBS) AC_SUBST(A) AC_SUBST(BINDLT) AC_SUBST(DISTCHECK_LIBTOOL_CONFIGURE_FLAG) # quoting in Makefile.am.in Q=@ AC_SUBST(Q) # install bind includes and libraries want_install_bind="no" want_install_bind="yes" if test "$want_libtool" = "yes"; then want_install_bind="yes" fi if test "$use_libbind" != "no"; then want_install_bind="no" fi AC_ARG_ENABLE(bind_install, AS_HELP_STRING([--enable-bind-install], [install bind includes and libraries.]), want_install_bind="$enableval") if test "$want_install_bind" = "yes"; then if test "$use_libbind" != "no"; then AC_MSG_WARN([--enable-bind-install does nothing when --with-libbind is set]) fi elif test "$want_libtool" = "yes" -a "$use_libbind" = "no"; then AC_MSG_WARN([embedded dynamic bind libraries must be installed]) fi AM_CONDITIONAL(INSTALL_BIND, test "$want_install_bind" = "yes") # OpenLDAP support. AC_ARG_WITH(ldap, AS_HELP_STRING([--with-ldap],[enable OpenLDAP support in dhcpd (default is no)]), [ldap=$withval], [ldap=no]) # OpenLDAP with SSL support. AC_ARG_WITH(ldapcrypto, AS_HELP_STRING([--with-ldapcrypto],[enable OpenLDAP crypto support in dhcpd (default is no)]), [ldapcrypto=$withval], [ldapcrypto=no]) # Gssapi to allow LDAP to authenticate with a keytab AC_ARG_WITH(ldap-gssapi, AC_HELP_STRING([--with-ldap-gssapi], [enable krb5/gssapi authentication for OpenLDAP in dhcpd (default is no)]), [ldap_gssapi=$withval], [ldap_gssapi=no]) # LDAP CASA auth support. AC_ARG_WITH(ldapcasa, AC_HELP_STRING([--with-ldapcasa], [enable LDAP CASA auth support in dhcpd (default is no)]), [ldapcasa=$withval], [ldapcasa=no]) # OpenLDAP support is disabled by default, if enabled then SSL support is an # extra optional that is also disabled by default. Enabling LDAP SSL support # implies enabling LDAP support. Similarly, KRB5 support implies LDAP support, # but doesn't include SSL. The two are not dependant. if test x$ldap = xyes || test x$ldapcrypto = xyes || test x$ldap_gssapi = xyes; then saved_LIBS="$LIBS" LIBS="" AC_SEARCH_LIBS(ldap_initialize, [ldap], , AC_MSG_FAILURE([*** Cannot find ldap_initialize with -lldap - do you need to install an OpenLDAP2 Devel package?])) AC_SEARCH_LIBS(ber_pvt_opt_on, [lber], , AC_MSG_FAILURE([*** Cannot find ber_pvt_opt_on with -llber - do you need to install an OpenLDAP2 Devel package?])) if test x$ldap_gssapi = xyes ; then AC_SEARCH_LIBS(krb5_init_context, [krb5], , AC_MSG_FAILURE([*** Cannot find krb5_init_context with -lkrb5 - do you need to install a Kerberos Devel package?])) fi # Create LDAP_LIBS which we specify them explicitly rather than lumping them in with LIBS AC_SUBST(LDAP_LIBS, [$LIBS]) LIBS="$saved_LIBS" AC_CHECK_HEADERS([ldap.h]) AC_CHECK_FUNCS([inet_pton inet_ntop]) LDAP_CFLAGS="-DLDAP_CONFIGURATION" if test x$ldapcasa = xyes ; then AC_CHECK_HEADERS([micasa_mgmd.h],[ LDAP_CFLAGS="$LDAP_CFLAGS -DLDAP_CASA_AUTH" ], AC_MSG_FAILURE([*** Cannot find micasa_mgmd.h for ldap casa auth support])) fi if test x$ldapcrypto = xyes ; then LDAP_CFLAGS="$LDAP_CFLAGS -DLDAP_USE_SSL" fi if test x$ldap_gssapi = xyes; then LDAP_CFLAGS="$LDAP_CFLAGS -DLDAP_USE_GSSAPI" fi AC_SUBST(LDAP_CFLAGS, [$LDAP_CFLAGS]) fi # Append selected warning levels to CFLAGS before substitution (but after # AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])],[],[]) & etc). CFLAGS="$CFLAGS $STD_CWARNINGS" # Try to add the bind and dhcp include directories CFLAGS="$CFLAGS -I\$(top_srcdir)/includes -I$BINDDIR/include" case "$host" in *-darwin*) CFLAGS="$CFLAGS -D__APPLE_USE_RFC_3542";; *-solaris*) # As of Solaris 11, ethernet dev files are in /dev/net AC_CHECK_FILE(/dev/net, [AC_DEFINE([USE_DEV_NET], [1], [Define to 1 if ethernet devices are in /dev/net])]) ;; esac AC_C_FLEXIBLE_ARRAY_MEMBER AC_CONFIG_FILES([ Makefile client/Makefile client/tests/Makefile common/Makefile.am common/Makefile common/tests/Makefile dhcpctl/Makefile.am dhcpctl/Makefile includes/Makefile omapip/Makefile.am omapip/Makefile relay/Makefile server/Makefile tests/Makefile.am tests/Makefile tests/unittest.sh server/tests/Makefile doc/devel/doxyfile ]) AC_OUTPUT AC_MSG_NOTICE([postconfig: run automake in $srcdir]) (cd $srcdir; automake) AC_MSG_NOTICE([postconfig: rerun config.status]) sh ./config.status if test "$enable_dhcpv4o6" = "yes"; then DHCP_VERSIONS="DHCPv4, DHCPv6 and DHCPv4-over-DHCPv6" elif test "$enable_dhcpv6" != "no"; then DHCP_VERSIONS="DHCPv4 and DHCPv6" else DHCP_VERSIONS="DHCPv4" fi cat > config.report << END ISC DHCP source configure results: -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- Package: Name: $PACKAGE_NAME Version: $PACKAGE_VERSION C Compiler: $CC Flags: DEFS: $DEFS CFLAGS: $CFLAGS DHCP versions: $DHCP_VERSIONS Features: debug: $enable_debug failover: $enable_failover execute: $enable_execute binary-leases: $enable_binary_leases dhcpv6: $enable_dhcpv6 delayed-ack: $enable_delayed_ack dhcpv4o6: $enable_dhcpv4o6 relay-port: $enable_relay_port Developer: ATF unittests : $atf_path END # TODO: Add Perl system tests if test "$atf_path" != "no" then echo "ATF_CFLAGS : $ATF_CFLAGS" >> config.report echo "ATF_LDFLAGS : $ATF_LDFLAGS" >> config.report echo "ATF_BIN : $ATF_BIN" >> config.report echo fi cat config.report echo echo Now you can type "make" to build ISC DHCP echo dhcp-4.4.1/contrib/000755 000765 000024 00000000000 13243313032 014351 5ustar00tmarkstaff000000 000000 dhcp-4.4.1/dhcpctl/000755 000765 000024 00000000000 13243313033 014333 5ustar00tmarkstaff000000 000000 dhcp-4.4.1/doc/000755 000765 000024 00000000000 13243313032 013456 5ustar00tmarkstaff000000 000000 dhcp-4.4.1/includes/000755 000765 000024 00000000000 13243313033 014520 5ustar00tmarkstaff000000 000000 dhcp-4.4.1/LICENSE000644 000765 000024 00000040745 13243304042 013731 0ustar00tmarkstaff000000 000000 # Copyright (c) 2004-2018 by Internet Systems Consortium, Inc. ("ISC") # Copyright (c) 1995-2003 by Internet Software Consortium # # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # Internet Systems Consortium, Inc. # 950 Charter Street # Redwood City, CA 94063 # # https://www.isc.org/ # # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0, included below. # # See the specific source files for any additional copyright or # license statements. Mozilla Public License, version 2.0 1. Definitions 1.1. "Contributor" means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software. 1.2. "Contributor Version" means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributor's Contribution. 1.3. "Contribution" means Covered Software of a particular Contributor. 1.4. "Covered Software" means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof. 1.5. "Incompatible With Secondary Licenses" means a. that the initial Contributor has attached the notice described in Exhibit B to the Covered Software; or b. that the Covered Software was made available under the terms of version 1.1 or earlier of the License, but not also under the terms of a Secondary License. 1.6. "Executable Form" means any form of the work other than Source Code Form. 1.7. "Larger Work" means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software. 1.8. "License" means this document. 1.9. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License. 1.10. "Modifications" means any of the following: a. any file in Source Code Form that results from an addition to, deletion from, or modification of the contents of Covered Software; or b. any new file in Source Code Form that contains any Covered Software. 1.11. "Patent Claims" of a Contributor means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the License, by the making, using, selling, offering for sale, having made, import, or transfer of either its Contributions or its Contributor Version. 1.12. "Secondary License" means either the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses. 1.13. "Source Code Form" means the form of the work preferred for making modifications. 1.14. "You" (or "Your") means an individual or a legal entity exercising rights under this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. 2. License Grants and Conditions 2.1. Grants Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license: a. under intellectual property rights (other than patent or trademark) Licensable by such Contributor to use, reproduce, make available, modify, display, perform, distribute, and otherwise exploit its Contributions, either on an unmodified basis, with Modifications, or as part of a Larger Work; and b. under Patent Claims of such Contributor to make, use, sell, offer for sale, have made, import, and otherwise transfer either its Contributions or its Contributor Version. 2.2. Effective Date The licenses granted in Section 2.1 with respect to any Contribution become effective for each Contribution on the date the Contributor first distributes such Contribution. 2.3. Limitations on Grant Scope The licenses granted in this Section 2 are the only rights granted under this License. No additional rights or licenses will be implied from the distribution or licensing of Covered Software under this License. Notwithstanding Section 2.1(b) above, no patent license is granted by a Contributor: a. for any code that a Contributor has removed from Covered Software; or b. for infringements caused by: (i) Your and any other third party's modifications of Covered Software, or (ii) the combination of its Contributions with other software (except as part of its Contributor Version); or c. under Patent Claims infringed by Covered Software in the absence of its Contributions. This License does not grant any rights in the trademarks, service marks, or logos of any Contributor (except as may be necessary to comply with the notice requirements in Section 3.4). 2.4. Subsequent Licenses No Contributor makes additional grants as a result of Your choice to distribute the Covered Software under a subsequent version of this License (see Section 10.2) or under the terms of a Secondary License (if permitted under the terms of Section 3.3). 2.5. Representation Each Contributor represents that the Contributor believes its Contributions are its original creation(s) or it has sufficient rights to grant the rights to its Contributions conveyed by this License. 2.6. Fair Use This License is not intended to limit any rights You have under applicable copyright doctrines of fair use, fair dealing, or other equivalents. 2.7. Conditions Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1. 3. Responsibilities 3.1. Distribution of Source Form All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You may not attempt to alter or restrict the recipients' rights in the Source Code Form. 3.2. Distribution of Executable Form If You distribute Covered Software in Executable Form then: a. such Covered Software must also be made available in Source Code Form, as described in Section 3.1, and You must inform recipients of the Executable Form how they can obtain a copy of such Source Code Form by reasonable means in a timely manner, at a charge no more than the cost of distribution to the recipient; and b. You may distribute such Executable Form under the terms of this License, or sublicense it under different terms, provided that the license for the Executable Form does not attempt to limit or alter the recipients' rights in the Source Code Form under this License. 3.3. Distribution of a Larger Work You may create and distribute a Larger Work under terms of Your choice, provided that You also comply with the requirements of this License for the Covered Software. If the Larger Work is a combination of Covered Software with a work governed by one or more Secondary Licenses, and the Covered Software is not Incompatible With Secondary Licenses, this License permits You to additionally distribute such Covered Software under the terms of such Secondary License(s), so that the recipient of the Larger Work may, at their option, further distribute the Covered Software under the terms of either this License or such Secondary License(s). 3.4. Notices You may not remove or alter the substance of any license notices (including copyright notices, patent notices, disclaimers of warranty, or limitations of liability) contained within the Source Code Form of the Covered Software, except that You may alter any license notices to the extent required to remedy known factual inaccuracies. 3.5. Application of Additional Terms You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of any Contributor. You must make it absolutely clear that any such warranty, support, indemnity, or liability obligation is offered by You alone, and You hereby agree to indemnify every Contributor for any liability incurred by such Contributor as a result of warranty, support, indemnity or liability terms You offer. You may include additional disclaimers of warranty and limitations of liability specific to any jurisdiction. 4. Inability to Comply Due to Statute or Regulation If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be placed in a text file included with all distributions of the Covered Software under this License. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. 5. Termination 5.1. The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60 days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30 days after Your receipt of the notice. 5.2. If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate. 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or Your distributors under this License prior to termination shall survive termination. 6. Disclaimer of Warranty Covered Software is provided under this License on an "as is" basis, without warranty of any kind, either expressed, implied, or statutory, including, without limitation, warranties that the Covered Software is free of defects, merchantable, fit for a particular purpose or non-infringing. The entire risk as to the quality and performance of the Covered Software is with You. Should any Covered Software prove defective in any respect, You (not any Contributor) assume the cost of any necessary servicing, repair, or correction. This disclaimer of warranty constitutes an essential part of this License. No use of any Covered Software is authorized under this License except under this disclaimer. 7. Limitation of Liability Under no circumstances and under no legal theory, whether tort (including negligence), contract, or otherwise, shall any Contributor, or anyone who distributes Covered Software as permitted above, be liable to You for any direct, indirect, special, incidental, or consequential damages of any character including, without limitation, damages for lost profits, loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses, even if such party shall have been informed of the possibility of such damages. This limitation of liability shall not apply to liability for death or personal injury resulting from such party's negligence to the extent applicable law prohibits such limitation. Some jurisdictions do not allow the exclusion or limitation of incidental or consequential damages, so this exclusion and limitation may not apply to You. 8. Litigation Any litigation relating to this License may be brought only in the courts of a jurisdiction where the defendant maintains its principal place of business and such litigation shall be governed by laws of that jurisdiction, without reference to its conflict-of-law provisions. Nothing in this Section shall prevent a party's ability to bring cross-claims or counter-claims. 9. Miscellaneous This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not be used to construe this License against a Contributor. 10. Versions of the License 10.1. New Versions Mozilla Foundation is the license steward. Except as provided in Section 10.3, no one other than the license steward has the right to modify or publish new versions of this License. Each version will be given a distinguishing version number. 10.2. Effect of New Versions You may distribute the Covered Software under the terms of the version of the License under which You originally received the Covered Software, or under the terms of any subsequent version published by the license steward. 10.3. Modified Versions If you create software not governed by this License, and you want to create a new license for such software, you may create and use a modified version of this License if you rename the license and remove any references to the name of the license steward (except to note that such modified license differs from this License). 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses If You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms of this version of the License, the notice described in Exhibit B of this License must be attached. Exhibit A - Source Code Form License Notice 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/. If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. You may add additional accurate notices of copyright ownership. Exhibit B - "Incompatible With Secondary Licenses" Notice This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. dhcp-4.4.1/m4/000755 000765 000024 00000000000 13243313032 013231 5ustar00tmarkstaff000000 000000 dhcp-4.4.1/Makefile.am000644 000765 000024 00000004105 13243301226 014747 0ustar00tmarkstaff000000 000000 # # automake adds dependencies that we don't like, so we explicitly remove them # Makefile: # # for libtool # ACLOCAL_AMFLAGS = -I m4 # # We have a lot of files that we want shipped with the distribution. # EXTRA_DIST = RELNOTES LICENSE configure.ac+lt config+lt \ contrib/3.0b1-lease-convert contrib/dhclient-tz-exithook.sh \ contrib/dhcp.spec contrib/sethostname.sh contrib/solaris.init \ contrib/ms2isc/Registry.pm contrib/ms2isc/ms2isc.pl \ contrib/ms2isc/readme.txt contrib/ldap/dhcpd-conf-to-ldap \ contrib/ldap/dhcp.schema contrib/ldap/README.ldap \ contrib/dhcp-lease-list.pl \ doc/BIND-libraries doc/DHCPv4-over-DHCPv6 \ doc/IANA-arp-parameters doc/Makefile doc/References.html \ doc/References.txt doc/References.xml doc/api+protocol \ doc/ja_JP.eucJP/dhclient-script.8 doc/ja_JP.eucJP/dhclient.8 \ doc/ja_JP.eucJP/dhclient.conf.5 doc/ja_JP.eucJP/dhclient.leases.5 \ doc/ja_JP.eucJP/dhcp-eval.5 doc/ja_JP.eucJP/dhcp-options.5 \ doc/examples/dhclient-dhcpv6.conf doc/examples/dhcpd-dhcpv6.conf \ doc/devel/arch.dox doc/devel/atf.dox doc/devel/contrib.dox \ doc/devel/debug.dox doc/devel/isc-logo.jpg doc/devel/libtool.dox \ doc/devel/mainpage.dox doc/devel/omapi.dox doc/devel/qa.dox \ client/tests/Atffile common/tests/Atffile server/tests/Atffile \ client/tests/Kyuafile common/tests/Kyuafile server/tests/Kyuafile \ m4/README if HAVE_BINDDIR EXTRA_DIST += bind/Makefile.in bind/bind.tar.gz bind/version.tmp endif # Use an autoconf substitution vs an automake conditional here # to fool automake when the bind directory does not exist. SUBDIRS = @BINDSUBDIR@ includes tests common omapip client dhcpctl relay server nobase_include_HEADERS = dhcpctl/dhcpctl.h # # distcheck tuning # DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_ATF_CONFIGURE_FLAG@ DISTCHECK_CONFIGURE_FLAGS += @DISTCHECK_LIBBIND_CONFIGURE_FLAG@ DISTCHECK_CONFIGURE_FLAGS += @DISTCHECK_LIBTOOL_CONFIGURE_FLAG@ distcheck-hook: if HAVE_BINDDIR chmod u+w $(distdir)/bind endif distclean-local: rm -f config.report dhcp-4.4.1/omapip/000755 000765 000024 00000000000 13243313033 014177 5ustar00tmarkstaff000000 000000 dhcp-4.4.1/README000644 000765 000024 00000074427 13243302032 013605 0ustar00tmarkstaff000000 000000 Internet Systems Consortium DHCP Distribution Version 4.4.1 28 February 2018 README FILE You should read this file carefully before trying to install or use the ISC DHCP Distribution. TABLE OF CONTENTS 1 WHERE TO FIND DOCUMENTATION 2 RELEASE STATUS 3 BUILDING THE DHCP DISTRIBUTION 3.1 UNPACKING IT 3.2 CONFIGURING IT 3.2.1 DYNAMIC DNS UPDATES 3.2.2 LOCALLY DEFINED OPTIONS 3.3 BUILDING IT 4 INSTALLING THE DHCP DISTRIBUTION 5 USING THE DHCP DISTRIBUTION 5.1 FIREWALL RULES 5.2 LINUX 5.2.1 IF_TR.H NOT FOUND 5.2.2 SO_ATTACH_FILTER UNDECLARED 5.2.3 PROTOCOL NOT CONFIGURED 5.2.4 BROADCAST 5.2.6 IP BOOTP AGENT 5.2.7 MULTIPLE INTERFACES 5.3 SCO 5.4 HP-UX 5.5 ULTRIX 5.6 FreeBSD 5.7 NeXTSTEP 5.8 SOLARIS 5.8.1 Solaris 11 5.8.2 Solaris 11 and ATF 5.8.3 Other Solaris Items 5.9 AIX 5.10 MacOS X 5.11 ATF 6 SUPPORT 6.1 HOW TO REPORT BUGS 7 HISTORY WHERE TO FIND DOCUMENTATION Documentation for this software includes this README file, the RELNOTES file, and the manual pages, which are in the server, common, client and relay subdirectories. The README file (this file) includes late-breaking operational and system-specific information that you should read even if you don't want to read the manual pages, and that you should *certainly* read if you run into trouble. Internet standards relating to the DHCP protocol are listed in the References document that is available in html, txt and xml formats in doc/ subdirectory. You will have the best luck reading the manual pages if you build this software and then install it, although you can read them directly out of the distribution if you need to. DHCP server documentation is in the dhcpd man page. Information about the DHCP server lease database is in the dhcpd.leases man page. Server configuration documentation is in the dhcpd.conf man page as well as the dhcp-options man page. A sample DHCP server configuration is in the file server/dhcpd.conf.example. The source for the dhcpd, dhcpd.leases and dhcpd.conf man pages is in the server/ sub- directory in the distribution. The source for the dhcp-options.5 man page is in the common/ subdirectory. DHCP Client documentation is in the dhclient man page. DHCP client configuration documentation is in the dhclient.conf man page and the dhcp-options man page. The DHCP client configuration script is documented in the dhclient-script man page. The format of the DHCP client lease database is documented in the dhclient.leases man page. The source for all these man pages is in the client/ subdirectory in the distribution. In addition, the dhcp-options man page should be referred to for information about DHCP options. DHCP relay agent documentation is in the dhcrelay man page, the source for which is distributed in the relay/ subdirectory. To read installed manual pages, use the man command. Type "man page" where page is the name of the manual page. This will only work if you have installed the ISC DHCP distribution using the ``make install'' command (described later). If you want to read manual pages that aren't installed, you can type ``nroff -man page |more'' where page is the filename of the unformatted manual page. The filename of an unformatted manual page is the name of the manual page, followed by '.', followed by some number - 5 for documentation about files, and 8 for documentation about programs. For example, to read the dhcp-options man page, you would type ``nroff -man common/dhcp-options.5 |more'', assuming your current working directory is the top level directory of the ISC DHCP Distribution. Please note that the pathnames of files to which our manpages refer will not be correct for your operating system until after you iterate 'make install' (so if you're reading a manpage out of the source directory, it may not have up-to-date information). BUILDING THE DHCP DISTRIBUTION UNPACKING IT To build the DHCP Distribution, unpack the compressed tar file using the tar utility and the gzip command - type something like: gunzip dhcp-4.4.1.tar.gz tar xvf dhcp-4.4.1.tar CONFIGURING IT Now, cd to the dhcp-4.4.1 subdirectory that you've just created and configure the source tree by typing: ./configure If the configure utility can figure out what sort of system you're running on, it will create a custom Makefile for you for that system; otherwise, it will complain. If it can't figure out what system you are using, that system is not supported - you are on your own. Several options may be enabled or disabled via the configure command. You can get a list of these by typing: ./configure --help If you want to use dynamic shared libraries automake, autoconf (aka GNU autotools) and libtool must be available. The DHCP distribution provides 3 configure.ac* files: the -lt version has no libtool support and was copied to the configure.ac standard file in the distribution. To enable libtool support you should perform these steps: cp configure.ac+lt configure.ac autoreconf -i after you can use the regenerated configure as usual (with libtool support (--enable-libtool) on by default): ./configure For compatibility (and people who don't read this documentation) the --enable-libtool configuration file is supported even by the distributed configure (and off by default). The previous steps are performed and the regenerated configure called with almost the same parameters (this "almost" makes the use of this feature not recommended). Note you can't go back from with libtool support to without libtool support by restoring configure.ac and rerun autoreconf. If you want or need to restore the without libtool support state the required way is to simply restore the whole distribution. DYNAMIC DNS UPDATES A fully-featured implementation of dynamic DNS updates is included in this release. It uses libraries from BIND and, to avoid issues with different versions, includes the necessary BIND version. The appropriate BIND libraries will be compiled and installed in the bind subdirectory as part of the make step. In order to build the necessary libraries you will need to have "gmake" available on your build system. There is documentation for the DDNS support in the dhcpd.conf manual page - see the beginning of this document for information on finding manual pages. LOCALLY DEFINED OPTIONS In previous versions of the DHCP server there was a mechanism whereby options that were not known by the server could be configured using a name made up of the option code number and an identifier: "option-nnn" This is no longer supported, because it is not future- proof. Instead, if you want to use an option that the server doesn't know about, you must explicitly define it using the method described in the dhcp-options man page under the DEFINING NEW OPTIONS heading. BUILDING IT Once you've run configure, just type ``make'', and after a while you should have a dhcp server. If you get compile errors on one of the supported systems mentioned earlier, please let us know. If you get warnings, it's not likely to be a problem - the DHCP server compiles completely warning-free on as many architectures as we can manage, but there are a few for which this is difficult. If you get errors on a system not mentioned above, you will need to do some programming or debugging on your own to get the DHCP Distribution working. If you cross compile you have to follow the instructions from the BIND README, in particular you must set the BUILD_CC environment variable. INSTALLING THE DHCP DISTRIBUTION Once you have successfully gotten the DHCP Distribution to build, you can install it by typing ``make install''. If you already have an old version of the DHCP Distribution installed, you may want to save it before typing ``make install''. USING THE DHCP DISTRIBUTION FIREWALL RULES If you are running the DHCP server or client on a computer that's also acting as a firewall, you must be sure to allow DHCP packets through the firewall. In particular, your firewall rules _must_ allow packets from IP address 0.0.0.0 to IP address 255.255.255.255 from UDP port 68 to UDP port 67 through. They must also allow packets from your local firewall's IP address and UDP port 67 through to any address your DHCP server might serve on UDP port 68. Finally, packets from relay agents on port 67 to the DHCP server on port 67, and vice versa, must be permitted. We have noticed that on some systems where we are using a packet filter, if you set up a firewall that blocks UDP port 67 and 68 entirely, packets sent through the packet filter will not be blocked. However, unicast packets will be blocked. This can result in strange behaviour, particularly on DHCP clients, where the initial packet exchange is broadcast, but renewals are unicast - the client will appear to be unable to renew until it starts broadcasting its renewals, and then suddenly it'll work. The fix is to fix the firewall rules as described above. PARTIAL SERVERS If you have a server that is connected to two networks, and you only want to provide DHCP service on one of those networks (e.g., you are using a cable modem and have set up a NAT router), if you don't write any subnet declaration for the network you aren't supporting, the DHCP server will ignore input on that network interface if it can. If it can't, it will refuse to run - some operating systems do not have the capability of supporting DHCP on machines with more than one interface, and ironically this is the case even if you don't want to provide DHCP service on one of those interfaces. LINUX There are three big LINUX issues: the all-ones broadcast address, Linux 2.1 ip_bootp_agent enabling, and operations with more than one network interface. There are also two potential compilation/runtime problems for Linux 2.1/2.2: the "SO_ATTACH_FILTER undeclared" problem and the "protocol not configured" problem. LINUX: PROTOCOL NOT CONFIGURED If you get the following message, it's because your kernel doesn't have the Linux packetfilter or raw packet socket configured: Make sure CONFIG_PACKET (Packet socket) and CONFIG_FILTER (Socket Filtering) are enabled in your kernel configuration If this happens, you need to configure your Linux kernel to support Socket Filtering and the Packet socket, or to select a kernel provided by your Linux distribution that has these enabled (virtually all modern ones do by default). LINUX: BROADCAST If you are running a recent version of Linux, this won't be a problem, but on older versions of Linux (kernel versions prior to 2.2), there is a potential problem with the broadcast address being sent incorrectly. In order for dhcpd to work correctly with picky DHCP clients (e.g., Windows 95), it must be able to send packets with an IP destination address of 255.255.255.255. Unfortunately, Linux changes an IP destination of 255.255.255.255 into the local subnet broadcast address (here, that's 192.5.5.223). This isn't generally a problem on Linux 2.2 and later kernels, since we completely bypass the Linux IP stack, but on old versions of Linux 2.1 and all versions of Linux prior to 2.1, it is a problem - pickier DHCP clients connected to the same network as the ISC DHCP server or ISC relay agent will not see messages from the DHCP server. It *is* possible to run into trouble with this on Linux 2.2 and later if you are running a version of the DHCP server that was compiled on a Linux 2.0 system, though. It is possible to work around this problem on some versions of Linux by creating a host route from your network interface address to 255.255.255.255. The command you need to use to do this on Linux varies from version to version. The easiest version is: route add -host 255.255.255.255 dev eth0 On some older Linux systems, you will get an error if you try to do this. On those systems, try adding the following entry to your /etc/hosts file: 255.255.255.255 all-ones Then, try: route add -host all-ones dev eth0 Another route that has worked for some users is: route add -net 255.255.255.0 dev eth0 If you are not using eth0 as your network interface, you should specify the network interface you *are* using in your route command. LINUX: IP BOOTP AGENT Some versions of the Linux 2.1 kernel apparently prevent dhcpd from working unless you enable it by doing the following: echo 1 >/proc/sys/net/ipv4/ip_bootp_agent LINUX: MULTIPLE INTERFACES Very old versions of the Linux kernel do not provide a networking API that allows dhcpd to operate correctly if the system has more than one broadcast network interface. However, Linux 2.0 kernels with version numbers greater than or equal to 2.0.31 add an API feature: the SO_BINDTODEVICE socket option. If SO_BINDTODEVICE is present, it is possible for dhcpd to operate on Linux with more than one network interface. In order to take advantage of this, you must be running a 2.0.31 or greater kernel, and you must have 2.0.31 or later system headers installed *before* you build the DHCP Distribution. We have heard reports that you must still add routes to 255.255.255.255 in order for the all-ones broadcast to work, even on 2.0.31 kernels. In fact, you now need to add a route for each interface. Hopefully the Linux kernel gurus will get this straight eventually. Linux 2.1 and later kernels do not use SO_BINDTODEVICE or require the broadcast address hack, but do support multiple interfaces, using the Linux Packet Filter. LINUX: OpenWrt DHCP 4.1 has been tested on OpenWrt 7.09 and 8.09. In keeping with standard practice, client/scripts now includes a dhclient-script file for OpenWrt. However, this is not sufficient by itself to run dhcp on OpenWrt; a full OpenWrt package for DHCP is available at ftp://ftp.isc.org/isc/dhcp/dhcp-4.1.0-openwrt.tar.gz LINUX: 802.1q VLAN INTERFACES If you're using 802.1q vlan interfaces on Linux, it is necessary to vconfig the subinterface(s) to rewrite the 802.1q information out of packets received by the dhcpd daemon via LPF: vconfig set_flag eth1.523 1 1 Note that this may affect the performance of your system, since the Linux kernel must rewrite packets received via this interface. For more information, consult the vconfig man pages. SCO ISC DHCP will now work correctly on newer versions of SCO out of the box (tested on OpenServer 5.05b, assumed to work on UnixWare 7). Older versions of SCO have the same problem as Linux (described earlier). The thing is, SCO *really* doesn't want to let you add a host route to the all-ones broadcast address. You can try the following: ifconfig net0 xxx.xxx.xxx.xxx netmask 0xNNNNNNNN broadcast 255.255.255.255 If this doesn't work, you can also try the following strange hack: ifconfig net0 alias 10.1.1.1 netmask 8.0.0.0 Apparently this works because of an interaction between SCO's support for network classes and the weird netmask. The 10.* network is just a dummy that can generally be assumed to be safe. Don't ask why this works. Just try it. If it works for you, great. HP-UX HP-UX has the same problem with the all-ones broadcast address that SCO and Linux have. One user reported that adding the following to /etc/rc.config.d/netconf helped (you may have to modify this to suit your local configuration): INTERFACE_NAME[0]=lan0 IP_ADDRESS[0]=1.1.1.1 SUBNET_MASK[0]=255.255.255.0 BROADCAST_ADDRESS[0]="255.255.255.255" LANCONFIG_ARGS[0]="ether" DHCP_ENABLE[0]=0 ULTRIX Now that we have Ultrix packet filter support, the DHCP Distribution on Ultrix should be pretty trouble-free. However, one thing you do need to be aware of is that it now requires that the pfilt device be configured into your kernel and present in /dev. If you type ``man packetfilter'', you will get some information on how to configure your kernel for the packet filter (if it isn't already) and how to make an entry for it in /dev. FreeBSD Versions of FreeBSD prior to 2.2 have a bug in BPF support in that the ethernet driver swaps the ethertype field in the ethernet header downstream from BPF, which corrupts the output packet. If you are running a version of FreeBSD prior to 2.2, and you find that dhcpd can't communicate with its clients, you should #define BROKEN_FREEBSD_BPF in site.h and recompile. Modern versions of FreeBSD include the ISC DHCP 3.0 client as part of the base system, and the full distribution (for the DHCP server and relay agent) is available from the Ports Collection in /usr/ports/net/isc-dhcp3, or as a package on FreeBSD installation CDROMs. NeXTSTEP The NeXTSTEP support uses the NeXTSTEP Berkeley Packet Filter extension, which is not included in the base NextStep system. You must install this extension in order to get dhcpd or dhclient to work. SOLARIS There are two known issues seen when compiling using the Sun compiler. The first is that older Sun compilers generate an error on some of our uses of the flexible array option. Newer versions only generate a warning, which can be safely ignored. If you run into this error ("type of struct member "buf" can not be derived from structure with flexible array member"), upgrade your tools to Oracle Solaris Studio (previously Sun Studio) 12 or something newer. The second is the interaction between the configure script and the makefiles for the Bind libraries. Currently we don't pass all environment variables between the DHCP configure and the Bind configure. If you attempt to specify the compiler you wish to use like this: CC=/opt/SUNWspro/bin/cc ./configure "make" may not build the Bind libraries with that compiler. In order to use the same compiler for Bind and DHCP we suggest the following commands: CC=/opt/SUNWspro/bin/cc ./configure CC=/opt/SUNWspro/bin/cc make Solaris 11 We have integrated a patch from Oracle to use sockets instead of DLPI on Solaris 11. This functionality was written for use with Solaris Studio 12.2 and requires the system/header package. By default this code is disabled in order to minimize disruptions for current users. In order to enable this code you will need to enable both USE_SOCKETS and USE_V4_PKTINFO as part of the configuration step. The command line would be something like: ./configure --enable-use-sockets --enable-ipv4-pktinfo Solaris 11 and ATF We have reports that ATF 0.15 and 0.16 do not build on Solaris 11. The following changes to the ATF source code appear to fix this issue: diff -ru atf-0.15/atf-c/tp_test.c atf-0.15-patched/atf-c/tp_test.c --- atf-0.15/atf-c/tp_test.c 2011-12-06 06:31:11.000000000 +0100 +++ atf-0.15-patched/atf-c/tp_test.c 2012-06-19 15:54:57.000000000 +0200 @@ -28,6 +28,7 @@ */ #include +#include #include #include diff -ru atf-0.15/atf-run/requirements.cpp atf-0.15-patched/atf-run/requirements.cpp --- atf-0.15/atf-run/requirements.cpp 2012-01-13 20:44:25.000000000 +0100 +++ atf-0.15-patched/atf-run/requirements.cpp 2012-06-19 15:41:51.000000000 +0200 @@ -29,7 +29,7 @@ extern "C" { #include -#include +//#include } #include Other Solaris Items One problem which has been observed and is not fixed in this patchlevel has to do with using DLPI on Solaris machines. The symptom of this problem is that the DHCP server never receives any requests. This has been observed with Solaris 2.6 and Solaris 7 on Intel x86 systems, although it may occur with other systems as well. If you encounter this symptom, and you are running the DHCP server on a machine with a single broadcast network interface, you may wish to edit the includes/site.h file and uncomment the #define USE_SOCKETS line. Then type ``make clean; make''. As an alternative workaround, it has been reported that running 'snoop' will cause the dhcp server to start receiving packets. So the practice reported to us is to run snoop at dhcpd startup time, with arguments to cause it to receive one packet and exit. snoop -c 1 udp port 67 > /dev/null & The DHCP client on Solaris will only work with DLPI. If you run it and it just keeps saying it's sending DHCPREQUEST packets, but never gets a response, you may be having DLPI trouble as described above. If so, we have no solution to offer at this time, aside from the above workaround which should also work here. Also, because Solaris requires you to "plumb" an interface before it can be detected by the DHCP client, you must either specify the name(s) of the interface(s) you want to configure on the command line, or must plumb the interfaces prior to invoking the DHCP client. This can be done with ``ifconfig iface plumb'', where iface is the name of the interface (e.g., ``ifconfig hme0 plumb''). It should be noted that Solaris versions from 2.6 onward include a DHCP client that you can run with ``/sbin/ifconfig iface dhcp start'' rather than using the ISC DHCP client, including DHCPv6. Consequently, we don't believe there is a need for the client to run on Solaris, and have not engineered the needed DHCPv6 modifications for the dhclient-script. If you feel this is in error, or have a need, please contact us. AIX The AIX support uses the BSD socket API, which cannot differentiate on which network interface a broadcast packet was received; thus the DHCP server and relay will work only on a single interface. (They do work on multi-interface machines if configured to listen on only one of the interfaces.) We have reports of Windows XP clients having difficulty retrieving addresses from a server running on an AIX machine. This issue was traced to the client requiring messages be sent to the all ones broadcast address (255.255.255.255) while the AIX server was sending to 192.168.0.255. You may be able to solve this by including a relay between the client and server with the relay configured to use a broadcast of all-ones. A second option that worked for AIX 5.1 but doesn't seem to work for AIX 5.3 was to: create a host file entry for all-ones (255.255.255.255) and then add a route: route add -host all-ones -interface The ISC DHCP distribution does not include a dhclient-script for AIX-- AIX comes with a DHCP client. Contribution of a working dhclient-script for AIX would be welcome. MacOS X The MacOS X system uses a TCP/IP stack derived from FreeBSD with a user-friendly interface named the System Configuration Framework. As it includes a builtin DHCPv4 client (you are better just using that), this text is only about the DHCPv6 client (``dhclient -6 ...''). The DNS configuration (domain search list and name servers' addresses) is managed by a System Configuration agent, not by /etc/resolv.conf (which is a link to /var/run/resolv.conf, which itself only reflects the internal state; the System Configuration framework's Dynamic Store). This means that modifying resolv.conf directly doesn't have the intended effect, instead the macos script sample creates its own resolv.conf.dhclient6 in /var/run, and inserts the contents of this file into the Dynamic Store. When updating the address configuration the System Configuration framework expects the prefix and a default router along with the configured address. As this extra information is not available via the DHCPv6 protocol the System Configuration framework isn't usable for address configuration, instead ifconfig is used directly. Note the Dynamic Store (from which /var/run/resolv.conf is built) is recomputed from scratch when the current location/set is changed. Running the dhclient-script reinstalls the resolv.conf.dhclient6 configuration. ATF Please see the file DHCP/doc/devel/atf.dox for a description of building and using these tools. The optional unit tests use ATF (Automated Testing Framework) including the atf-run and atf-report tools. ATF deprecated these tools in version 0.19 and removed these tools from its sources in version 0.20, requiring you to get an older version, use Kyua with an ATF compatibility package or use the version included in the Bind sources. SUPPORT The Internet Systems Consortium DHCP server is developed and distributed by ISC in the public trust, thanks to the generous donations of its sponsors. ISC now also offers commercial quality support contracts for ISC DHCP, more information about ISC Support Contracts can be found at the following URL: https://www.isc.org/services/support/ Please understand that we may not respond to support inquiries unless you have a support contract. ISC will continue its practice of always responding to critical items that effect the entire community, and responding to all other requests for support upon ISC's mailing lists on a best-effort basis. However, ISC DHCP has attracted a fairly sizable following on the Internet, which means that there are a lot of knowledgeable users who may be able to help you if you get stuck. These people generally read the dhcp-users@isc.org mailing list. Be sure to provide as much detail in your query as possible. If you are going to use ISC DHCP, you should probably subscribe to the dhcp-users or dhcp-announce mailing lists. WHERE TO SEND FEATURE REQUESTS: We like to hear your feedback. We may not respond to it all the time, but we do read it. If ISC DHCP doesn't work well for you, or you have an idea that would improve it for your use, please send your suggestion to dhcp-suggest@isc.org. This is also an excellent place to send patches that add new features. WHERE TO REPORT BUGS: If you want the act of sending in a bug report to result in you getting help in the form of a fixed piece of software, you are asking for help. Your bug report is helpful to us, but fundamentally you are making a support request, so please use the addresses described in the previous paragraphs. If you are _sure_ that your problem is a bug, and not user error, or if your bug report includes a patch, you can send it to our ticketing system at dhcp-bugs@isc.org. If you have not received a notice that the ticket has been resolved, then we're still working on it. PLEASE DO NOT REPORT BUGS IN OLD SOFTWARE RELEASES! Fetch the latest release and see if the bug is still in that version of the software, and if it is still present, _then_ report it. ISC release versions always have three numbers, for example: 1.2.3. The 'major release' is 1 here, the 'minor release' is 2, and the 'maintenance release' is 3. ISC will accept bug reports against the most recent two major.minor releases: for example, 1.0.0 and 0.9.0, but not 0.8.* or prior. PLEASE take a moment to determine where the ISC DHCP distribution that you're using came from. ISC DHCP is sometimes heavily modified by integrators in various operating systems - it's not that we feel that our software is perfect and incapable of having bugs, but rather that it is very frustrating to find out after many days trying to help someone that the sources you're looking at aren't what they're running. When in doubt, please retrieve the source distribution from ISC's web page and install it. HOW TO REPORT BUGS OR REQUEST HELP When you report bugs or ask for help, please provide us complete information. A list of information we need follows. Please read it carefully, and put all the information you can into your initial bug report. This will save us a great deal of time and more informative bug reports are more likely to get handled more quickly overall. 1. The specific operating system name and version of the machine on which the DHCP server or client is running. 2. The specific operating system name and version of the machine on which the client is running, if you are having trouble getting a client working with the server. 3. If you're running Linux, the version number we care about is the kernel version and maybe the library version, not the distribution version - e.g., while we don't mind knowing that you're running Redhat version mumble.foo, we must know what kernel version you're running, and it helps if you can tell us what version of the C library you're running, although if you don't know that off the top of your head it may be hard for you to figure it out, so don't go crazy trying. 4. The specific version of the DHCP distribution you're running, as reported by dhcpd -t. 5. Please explain the problem carefully, thinking through what you're saying to ensure that you don't assume we know something about your situation that we don't know. 6. Include your dhcpd.conf and dhcpd.leases file as MIME attachments if they're not over 100 kilobytes in size each. If they are this large, please make them available to us, e.g., via a hidden http:// URL or FTP site. If you're not comfortable releasing this information due to sensitive contents, you may encrypt the file to our release signing key, available on our website. 7. Include a log of your server or client running until it encounters the problem - for example, if you are having trouble getting some client to get an address, restart the server with the -d flag and then restart the client, and send us what the server prints. Likewise, with the client, include the output of the client as it fails to get an address or otherwise does the wrong thing. Do not leave out parts of the output that you think aren't interesting. 8. If the client or server is dumping core, please run the debugger and get a stack trace, and include that in your bug report. For example, if your debugger is gdb, do the following: gdb dhcpd dhcpd.core (gdb) where [...] (gdb) quit This assumes that it's the dhcp server you're debugging, and that the core file is in dhcpd.core. Please see https://www.isc.org/software/dhcp/ for details on how to subscribe to the ISC DHCP mailing lists. HISTORY ISC DHCP was originally written by Ted Lemon under a contract with Vixie Labs with the goal of being a complete reference implementation of the DHCP protocol. Funding for this project was provided by Internet Systems Consortium. The first release of the ISC DHCP distribution in December 1997 included just the DHCP server. Release 2 in June 1999 added a DHCP client and a BOOTP/DHCP relay agent. DHCP 3 was released in October 2001 and included DHCP failover support, OMAPI, Dynamic DNS, conditional behaviour, client classing, and more. Version 3 of the DHCP server was funded by Nominum, Inc. The 4.0 release in December 2007 introduced DHCPv6 protocol support for the server and client. This product includes cryptographic software written by Eric Young (eay@cryptsoft.com). dhcp-4.4.1/relay/000755 000765 000024 00000000000 13243313033 014026 5ustar00tmarkstaff000000 000000 dhcp-4.4.1/RELNOTES000644 000765 000024 00000636700 13243301564 014112 0ustar00tmarkstaff000000 000000 Internet Systems Consortium DHCP Distribution Version 4.4.1 28 February 2018 Release Notes NEW FEATURES Please note that that ISC DHCP is now licensed under the Mozilla Public License, MPL 2.0. Please see https://www.mozilla.org/en-US/MPL/2.0/ to read the MPL 2.0 license terms. The areas of focus for ISC DHCP 4.4 were: 1. Dynamic DNS additions 2. dhclient improvements 3. Support for dynamic shared libraries Dynamic DNS Improvements: - We added three new server configuration parameters which influence DDNS conflict resolution: 1. ddns-dual-stack-mixed-mode - alters DNS conflict resolution behavior to mitigate issues with non-compliant clients in dual stack environments. 2. ddns-guard-id-must-match - relaxes the DHCID RR client id matching requirement of DNS conflict resolution. 3. ddns-other-guard-is-dynamic - alters dual-stack-mixed-mode behavior to allow unguarded DNS entries to be overwritten in certain cases - The server now honors update-static-leases parameter for static DHCPv6 hosts. dhclient Improvements: - We've added three command line parameters to dhclient: 1. --prefix-len-hint - directs dhclient to use the given length as the prefix length hint when requesting prefixes 2. --decline-wait-time - instructs the client to wait the given number of seconds after declining an IPv4 address before issuing a discover 3. --address-prefix-len - specifies the prefix length passed by dhclient into the client script (via the environment variable ip6_prefixlen) with each IPv6 address. We added this parameter because we have changed the default value from 64 to 128 in order to be compliant with RFC3315bis draft (-09, page 64) and RFC5942, Section 4, point 1. **WARNING**: The new default value of 128 may not be backwardly compatible with your environment. If you are operating without a router, such as between VMs on a host, you may find they cannot see each other with prefix length of 128. In such cases, you'll need to either provide routing or use the command line parameter to set the value to 64. Alternatively you may change the default at compile time by setting DHCLIENT_DEFAULT_PREFIX_LEN in includes/site.h. - dhclient will now generate a DHCPv6 DECLINE message when the client script indicates a DAD failure Dynamic shared library support: Configure script, configure.ac+lt, which supports libtool is now provided with the source tar ball. This script can be used to configure ISC DHCP to build with libtool and thus use dynamic shared libraries. Other Highlights: - The server now supports dhcp-cache-threshold for DHCPv6 operations - The server now supports DHPv6 address allocation based on EUI-64 DUIDs - Experimental support for alternate relay port in the both the server and relay for IPv4, IPv6 and 4o6 (see: draft-ietf-dhc-relay-port-10.txt) For information on how to install, configure and run this software, as well as how to find documentation and report bugs, please consult the README file. ISC DHCP uses standard GNU configure for installation. Please review the output of "./configure --help" to see what options are available. The system has only been tested on Linux, FreeBSD, and Solaris, and may not work on other platforms. Please report any problems and suggested fixes to . ISC DHCP is open source software maintained by Internet Systems Consortium. This product includes cryptographic software written by Eric Young (eay@cryptsoft.com). Changes since 4.4.0 (New Features) - none Changes since 4.4.0 (Bug Fixes) - A delayed-ack value of 0 (the default), now correctly disables the delayed feature. A change in 4.4.0 prohibited lease updates marking leases active from be written to the lease file when delayed-ack is 0. This in turn, caused servers to lose active lease assignments upon restart. [ISC-Bugs #47141] ! Option reference count was not correctly decremented in error path when parsing buffer for options. Reported by Felix Wilhelm, Google Security Team. [ISC-Bugs #47140] CVE: CVE-2018-5733 ! Corrected an issue where large sized 'X/x' format options were causing option handling logic to overwrite memory when expanding them to human readable form. Reported by Felix Wilhelm, Google Security Team. [ISC-Bugs #47139] CVE: CVE-2018-5732 Changes since 4.4.0b1 (New Features) - Duplicate address detection when binding to a new IPv6 address was added to the following dhclient scripts: linux,freebsd,netbsd,openbsd, and macos. The scripts will check for DAD errors after binding to a new IPv6 address for at most --dad-wait-time seconds. If a DAD error is detected the script will exit with a value of 3, instructing dhclient to decline the address. If dad-wait-time is zero (the default), DAD error checking is not peformed. [ISC-Bugs 46805] - Support for sending and receiving additional DHCP4 options has been added to both the dhcpd and dhclient. Specifically: option codes 93,94, and 97 (RFC 4578); code 150 (RFC 5859); and codes 209,219, and 211 (RFC 5071). Beyond configuring, sending, requesting, and receiving these options neither server nor client apply any additional logic based on their values. Thanks to Peter Lewis for requesting this change. [ISC-Bugs 47062] Changes since 4.4.0b1 (Bug Fixes) - Added clarifying text to dhcpd.conf.5 explaining the class match expressions cannot rely on the results of executable statements. [ISC-Bugs #45451] - Fixed a bug which causes dhcpd and dhclient to crash on certain systems when given relative path names for lease or pid files on the command line. Affected systems are those on which the C library function, realpath() does not support a second parameter value of NULL (see manpages for realpath(3)). [ISC-Bugs #46957] - Fixed a build issue when building with embedded BIND9 under OpenBSD that was causing BIND9 build to not generate dns/enumclass.h and dns/enumtype.h. [ISC-Bugs #46971] - Added /m4/README to the distribution tarball. Some versions of ac_local() treat the absence of the m4 subdirectory as error rather than warning. This was causing the call to autoreconf, necessary for building with libtool, to fail. [ISC-Bugs #47075] Changes since 4.4.0a1 (New Features) - Added experimental support for relay port (draft-ietf-dhc-relay-port-10.txt) feature for DHCPv4, DHCPv6 and DHCPv4-over-DHCPv6. Relay port has to be enabled at compile time via --enable-relay-port and is fully backward compatible (i.e. works with previous implementations of servers and relays using the standard ports). A new --rp command line option specifies to dhcrelay an alternate source port for upstream (i.e. toward the server) messages. Thanks to Naiming Shen and Enke Chen of Cisco systems for submitting these patches. [ISC-Bugs #44535] - Added --release-on-roam to dhcpd server. When enabled and the server detects that a DHCPv6 client (IAID+DUID) has roamed to a new network, it will release the pre-existing leases on the old network and emit a log statement similar to the following: "Client: roamed to new network, releasing lease:
" The server will carry out all of the same steps that would normally occur when a client explicitly releases a lease. This behavior is disabled by default and may only be specified globally. Prior to this the server renders the leases unavailable until they expire or the server is restarted. Clients that need leases in multiple networks must supply a unique IAID in each IA. When release-on-roam is disabled (the default) the server maintains the prior behavior of making such leases unavailable until they expire or the server is restarted. Clients that need leases in multiple networks must supply a unique IAID in each IA. This parameter may only be specified at the global level. Thanks to Fernando Soto from BlueCat Networks for suggesting this change. [ISC-Bugs #44576] [ISC-Bugs #46849] - Support for delayed-ack is now compiled in by default. Prior to this it had to be enabled at compile time via --enable-delayed-acks. The default value for delayed-ack, however, has been changed from 28 to 0 (i.e. disabled). This was done to minimize the impact on users not currently using the feature. Please note that the delayed-ack feature is not currently compatible with support for DHPCv4-over-DHCPv6 so when a 4to6 port command line argument enables this in the server the delayed-ack value is reset to 0. [ISC-Bugs #42446] - The server (-6) now honors the parameter, update-static-leases, for static (fixed-address6) DHCPv6 leases. It is worth noting that because stateful data is not retained by the server for static leases, each time a client requests or renews a static lease, the server will perform DDNS updates for it. This may have significant performance implications for environments with many clients that request or renew static leases often. Similarly, the DNS entries will not be removed by server when a client issues a RELEASE nor if the lease is deleted from the configuration. In such cases the DNS entries must be removed manually. This feature is disabled by default. Thanks to both Bill Shirley and dgutier-at-cern-dot-ch for requesting this change. [ISC-Bugs #34097] [ISC-Bugs #41054] [ISC-Bugs #41450] - Added to the server (-6) a new statement, local-address6, which specifies the source address of packets sent by the server. An additional flag, bind-local-address6, disabled by default, binds the service socket to to local-address6. Note that bind-local-address does not work with direct clients: a relay has to forward packets to the server using the local-address6 destination. [ISC-Bugs #46084] Changes since 4.4.0a1 (Bugs) - The server now recognizes environment variables PATH_DHCPD_DB and PATH_DHCPD_PID. These had been incorrectly compiled out of the code unless DHCPv6 support was disabled. Additionally, the server man pages were corrected to accurately reflect how the server chooses file names (see lease-file-name and pid-file-name statements). Thanks to Fernando Soto at Bluecat Networks for bringing this matter to our attention. [ISC-Bugs #46859] - Removed an "Impossible condition" error upon exit in the dhcpd server that has been shutdown via OMAPI. This condition was only apparent under Solaris when building with --enable-use-sockets and --enable-ipv4-pktinfo. [ISC-Bugs #36118] - Corrected some minor Coverity issues: CID 1426059, 1426058, and 1426057. [ISC-Bugs #46836] - Added missing text to dhclient.8 and expanded release note coverage for --address-prefix-len changes. Changes since 4.3.6 (New Features) - Added --enable-bind-install to install embedded bind includes and libraries. Default is to not install them (it was the previous behavior). If you'd like to change the includedir and/or libdir installation directories to something different than for ISC DHCP you must pass them using the --with-bind-extra-config configuration arguments. [ISC-Bugs #39318] - Added support of dynamic shared libraries with libtool. A new --enable-libtool configuration parameter is available but should not be used directly: *please* read the build configuration section in the README file for the recommended procedure. [ISC-Bugs #29402] - IPv6 operation now supports an EUI-64 based address allocation which will calculate addresses for clients with EUI-64 DUIDs based on those DUIDs when enabled by setting use-eui-64 true. The parameter may defined down to the pool scope. Note this feature must be compiled in by defining EUI_64 in includes/site.h. This flag is undefined by default. [ISC-Bugs #43927] - The directory includes/isc-dhcp and it's only occupant, dst.h, have been removed from the source tree. They are obsolete for branches other than v4_1_esv. [ISC-bugs #45541] - Replaced ISC licensing with Mozilla Public License, MPL 2.0 licensing throughout. Please see https://www.mozilla.org/en-US/MPL/2.0/ to read the MPL 2.0 license terms. [ISC-Bugs #45541] - Load balancing for failover peers can now be disabled by setting "load balance max secs" to 0. Doing so for both peers means both servers will respond to all DHCPDISCOVERs or DHCPREQUESTs as soon as they are received. [ISC-Bugs #39669] - Added a new dhclient command line parameter, --prefix-len-hint . When used in conjunction with -P, it directs dhclient to use the given length as the prefix length hint when requesting prefixes. Thanks to both Indy, of the FireballISO open source project and H. Peter Anvin for suggesting this change. [ISC-Bugs #43792] [ISC-Bugs #35112] [ISC-Bugs #32228] [ISC-Bugs #29470] - dhclient will now wait for 10 seconds after declining an IPv4 address before issuing a discover. This is in keeping with RFC 2131, section 3.1.5. Prior to this dhclient did not wait at all. The amount of time dhclient waits can be specified via a new command line parameter: --decline-wait-time . A value of zero equates to no wait at all. Thanks to Pavel Kankovsky for bringing this matter to our attention. **NOTE: THIS IS CHANGE IN DEFAULT BEHAVIOR. [ISC-Bugs #45457] - dhclient will now include the lease address when logging DHCPOFFERs, DHCPREQUESTs, DHCPACKs, DHCPRELEASEs, and DHCPDECLINEs. Additionally, DHCPOFFERs will be logged before their corresponding DHCPREQUESTs are sent and logged. [ISC-Bugs #2729] - When given the -T command line argument, in addition to reading the current lease file, the server will write the leases to a temporary lease file. This can help detect issues in server configuration that only surface when leases are written to the file. The current lease file will not be modified and the temporary lease file is removed upon completion of the test. [ISC-Bugs #22267] - dhclient will now generate a DHCPv6 DECLINE message containing all IA_NA addresses which for which the client script indicates a DAD failure. After receiving the DECLINE reply, dhclient will restart the solicit process. Note, the client script must exit with a value of 3 to signify that the address failed DAD. Thanks to Jiri Popelka of Red Hat for submitting the patch that was the foundation for this change. **NOTE: THIS IS CHANGE IN DEFAULT BEHAVIOR. [ISC-Bugs #21237] [ISC-Bugs #23357] [ISC-Bugs #36966] - Replaced compilation option, enable-secs-byteorder, with a run-time, server configuration parameter, check-secs-byte-order. When enabled, the server will check for clients that do the byte ordering on the secs field incorrectly. This field should be in network byte order but some clients get it wrong. When this parameter is enabled the server will examine the secs field and if it looks wrong (high byte non zero and low byte zero) swap the bytes. The default is disabled. This parameter is only useful when doing load balancing within failover. [ISC-Bugs #45364] - The default value for server (-6) parameter, prefix-length-mode, has been changed from "exact" to "prefer". In "prefer" mode the server will offer the first available prefix with the same length as that requested by the client. If none are found then it will offer the first available prefix of any length. This is more in line with with RFC 8168 and should improve the out-of-the-box user experience. **NOTE: THIS IS CHANGE IN DEFAULT BEHAVIOR. [ISC-Bugs #45615] - Added support for 'dhcp-cache-threshold' to IPv6 operation: If a client renews before 'dhcp-cache-threshold' percent of its lease has elapsed (default 25%), the server will reuse the allocated lease (provide a lease within the currently allocated lease-time) rather than extend or renew the lease. This allows the server to reply without needlessly writing leases to disk. The preferred and valid lease lifetimes sent to the client will be reduced by the age of the lease. The option may be specified down to the pool level and is supported for all three pool types: NA, TA, and PD. [ISC-Bugs #45292] - Added three new server configuration parameters which influence DDNS: 1. ddns-dual-stack-mixed-mode - alters DNS conflict resolution behavior to mitigate issues with non-compliant clients in dual stack environments. 2. ddns-guard-id-must-match - relaxes the DHCID RR client id matching requirement of DNS conflict resolution. 3. ddns-other-guard-is-dynamic - alters dual-stack-mixed-mode behavior to allow unguarded DNS entries to be overwritten in certain cases [ISC-Bugs #42620] [ISC-Bugs #42621] [ISC-Bugs #44753] - A "key-algorithm " statement has been added to omshell to allow the specification of the key algorithm to use during transaction authentication. Prior to this it was hard-coded to be hmac-md5. It now supports all of the same algorithms as the dhcpd server: hmac-md5 (the default), hmac-sha1, hmac-sha224, hmac-sha256, hmac-sha384, and hmac-sha512. [ISC-Bugs #46771] - Added a server configuration parameter, persist-eui-64-leases, which determines whether or not EUI-64 based leases are written to the leases file. Default is true. [ISC-Bugs #45046] - Changed the default value of the prefix length passed by dhclient into the client script for each IPv6 address from 64 to 128. This was done to comply with RFC3315bis draft (-09, page 64) and RFC5942, Section 4, point 1. In addition, dhclient now supports a command line argument, --address-prefix-len, which may be used to override the default value. **WARNING**: This change may not be backwardly compatible with your environment. If you are operating without a router, such as between VMs on a host, you may find they cannot see each with prefix length of 128. In such cases, you'll need to either provide routing or use the command line parameter to set the value to 64. Alternatively you may change the default at compile time by setting DHCLIENT_DEFAULT_PREFIX_LEN in includes/site.h. [ISC-Bugs #23252] [ISC-Bugs #37221] - Modified dhclient (-6) to bypass sending a confirm (INIT REBOOT) when it has only expired address associations. Thanks to Jiri Popelka at Red Hat for raising the issue and submitting the patch. [ISC-Bugs #22675] Changes since 4.3.6 (Bugs): - Corrected an issue where the server would return a client's previously released prefix lease even when the client provides a prefix length hint that does not match the prior lease. Now the server will only return the previous lease if it exactly matches the hint. If not it will attempt to allocate a new prefix based on the hint and the prefix-length-mode. Thanks to Tim DeNike - Lightspeed Communications for pointing out the error of our ways. [ISC-bugs #45780] - Added explicit include of BIND9 isc/util.h to adapt to revisions in BIND9 (see BIND9 ticket #46311). Prior to this the build was failing with implicit function declarations errors for POST() and INSIST(). [ISC-bugs #46332] - Added to code ignore empty IPv4 host name option (code 12). While RFC 2132 states the option cannot be empty, some clients are apparently capable of sending it. Prior to this the server was attempting to use it and store it in the lease file causing issues with DDNS and so forth. [ISC-bugs #43786] - Corrected dhclient command line parsing for --dad-wait-time that causes even valid values to fail as invalid on some environments. [ISC-Bugs #46535] - Replaced iasubopt::heap_index with separate values for active and inactive heaps: iasubopt::active_index and iasubopt::inactive_index. This was done to accommodate a change in behavior in BIND9 isc_heap_delete(). [ISC-bugs #46719] ! Plugged a socket descriptor leak in OMAPI, that can occur when there is data pending to be written to an OMAPI connection, when the connection is closed by the reader. Thanks to Pavel Zhukov at RedHat for bringing this issue to our attention and whose patch helped guide us in the right direction. [ISC-Bugs #46767] - The ability of the server to send back dhcp6.vendor-opts values has been restored. A change in 4.3.5 (see #29246) which enabled it to send back the FQDN option unfortunately broke its ability send back dhcp6.vendor-opts. Thanks to Sumant Gupta (sumantgupta at gmail dot com) of Landis+Gry for bringing this issue to our attention. [ISC-Bugs #46427] Changes since 4.3.6b1 - None Changes since 4.3.5 - The server now allows the client identifier (option 61) to own leases in more than one subnet concurrently. Prior to this the server would incorrectly release an existing lease in one subnet prior to assigning a lease in another subnet. Note that the prior behavior can be still be achieved by enabling one-lease-per-client. Thanks to both David Zych at the University of Illinois and Norm Proffitt of Infoblox for reporting the issue; and Norm for suggesting a solution. [ISC-Bugs #41358] - When replying to a DHCPINFORM, the server will now include options specified at the pool scope, provided the ciaddr field of the DHCPINFORM is populated. Prior to this the server only evaluated options down to the subnet scope. Thanks to Fernando Soto at BlueCat Networks for reporting the issue. [ISC-Bugs #43219] [ISC-Bugs #45051] - When memory allocation fails in a repeated way the process writes "Run out of memory." on the standard error and exists with status 1. [ISC-Bugs #32744] - The new lmdb (Lightning Memory DataBase) bind9 configure option is now disabled by default to avoid the presence of this library to be detected which can lead to a link failure. [ISC-Bugs #45069] - The linux interface discovery code has been modified to use getifaddrs() as is done for BSD and OS-X. Prior to this the code would only recognize the first address on an interface and thereby omit vlans. Thanks to Jiri Popelka at Redhat, Marius Tomaschewski at SUSE, and Wei Kong at Novell, who all submitted patches. [ISC-Bugs #28761] [ISC-Bugs #31992] [ISC-Bugs #25428] [ISC-Bugs #31940] [ISC-Bugs #32935] - Fixed a bug in OMAPI that causes omshell to crash when a name-value pair with a zero length value is shipped in an object. Thanks to Fernando Soto at BlueCat Networks for reporting the issue and supplying the patch. [ISC-Bugs #29108] - On 64-bit platforms, dhclient now generates the correct value for the script environment variable, "expiry", the lease expiry value exceeds 0x7FFFFFFF. Prior to this such values would produce negative values for expiry in the script environment. [ISC-Bugs #43326] - Common timer logic was modified to cap the maximum timeout values at 0x7FFFFFFF - 1. Values larger than that were causing fatal timer out of range errors on 64-bit platforms. Thanks to Jiri Popelka at Red Hat for reporting the issue. [ISC-Bugs #28038] - DHCP6 FQDN option unpacking code now correctly handles values that contain spaces, special, or non-printable characters. Prior to this the buffer size needed was underestimated causing a conversion error message to be logged and DNS updates to be skipped. Thanks to Fernando Soto at BlueCat Networks for bringing the matter to our attention. [ISC-Bugs #43592] - When running in -6 mode, dhclient can enforce the require option statement and will discard offered leases that do not contain all the required options specified in the client configuration. If not enabled the client will still consider such leases. This must be enabled at compile time (see ENFORCE_DHCPV6_CLIENT_REQUIRE in includes/site.h). Thanks to Mritunjaykumar Dubey at Nokia for reporting the issue. [ISC-Bugs #41473] - Altered DHCPv4 lease time calculation to avoid roll over errors on 64-bit OS systems when using -1 or large values for default-lease-time. Rollover values will be replaced with 0x7FFFFFFF - 1. This alleviates unintentionally short expiration times being handed out when infinite lease times (-1) in conjunction with failover. Our thanks to Alessandro Gherardi for bringing the issue to our attention. [ISC-Bugs #41976] - Added new compile time option --with-srv-conf-file which specifies a default location of the server configuration file. [ISC-Bugs #44765] - Added --dad-wait-time parameter to dhclient. It specifies the maximum time, in seconds, that the client process should wait for the duplicate address detection to complete before initiating DHCP requests. This value is propagated to the dhclient script and the script is responsible for waiting the specified amount of time or until DAD has completed. If the script does not support it, specifying this parameter has no effect. The default value is 0 which specifies that the script should not wait for DAD. With this change the following scripts have been modified to support the new parameter: freebsd, linux, macos, netbsd, openbsd. [ISC-Bugs #36169] - The server nows checks both the address and length of a prefix delegation when attempting to match it to a prefix pool. This ensures the server responds properly when pool configurations change such that once valid, "in-pool" delegations are now treated as being invalid. During lease file loading at startup, the server will discard any PD leases that are deemed "out-of-pool" either by address or mis-matched prefix length. Clients seeking to renew or rebind such leases will get a response of No Binding in the case of the former, and the prefix delegation with lifetimes set to zero in the case of the latter. Thanks to Mark Nejedlo at TDS Telecom for reporting this issue. [ISC-Bugs #35378] - Modified DDNS support initialization such that DNS related ports will only be opened by the server (dhcpd) at startup if ddns-update-style is not "none"; by dhclient only if and when the it first attempts an update; and never by dhcrelay. Prior to this all three always did the initialization at startup which causes them to always open on and listen for traffic on two random ports. Thanks to Rodney Beede for reporting this issue. [ISC-Bugs #45290] [ISC-Bugs #33377] - Added error logging to two memory allocation failure checks. Thanks to Bill Parker (wp02855 at gmail dot com) for reporting the issue. [ISC-Bugs #41185] - Corrected a dhclient -6 issue that caused the client to crash with an "Impossible condition" error after de-preferencing its only IA binding. The crash occurred when server configuration changes rendered the existing binding out-of-range and no other leases were available to offer. Thanks to Pierre Clerissi for bringing this issue to our attention. [ISC-Bugs #44373] - By defining CALL_SCRIPT_ON_ONETRY_FAIL in includes/site.h, dhclient will now call the script with reason set to FAIL when run with -1 (one try) and there are no server responses. This applies to IPv4 mode only. Thanks for a patch by Martin Pitt which got to us via Andrew Pollock. [ISC-bugs #18183] - The server now detects failover peers that are not referenced in at least one pool when run with the command line option for test mode, -T. Prior to this the check was performed too far down stream to be detected in test mode. [ISC-Bugs #29892] - Linux script updated. The script is now based on Debian version. It uses ip tool from iproute2 package and ifconfig is no longer used. This also addresses an issue of calling arping with inappropriate parameter. [ISC-bugs #19430] [ISC-bugs #18111] - Changed severity of the log message indicating UDP checksum errors in the received packets from 'info' to 'debug' to avoid logging excessive number of false positives when UDP checksum offloading is enabled. [ISC-bugs #41757] - The directory minires has been removed from the source tree. It has long been obsolete for branches other than v4_1_esv. Additionally, includes/minires.h was renamed includes/ns_name.h. [ISC-bugs #45471] - Replaced ifconfig parameters "add" and "delete" with "alias" and "-alias" for IPv6 mode in the client scripts, netbsd and openbsd. This was preventing IPv6 addresses from being added or removed from interfaces. Thanks to Tim Dean for reporting this issue. [ISC-bugs #31573] Changes since 4.3.5b1 - Corrected a bug which could cause the server to sporadically crash while loading lease files with the lease-id-format is set to "hex". Our thanks to Jay Ford, University of Iowa for reporting the issue. [ISC-Bugs #43185] - Eliminated a noisy, but otherwise harmless debug log statment that may appear during server startup when building with --enable-binary-leases and configuring multiple pools in a shared network. Thanks to Fernando Soto from BlueCat Networks for reporting the issue and supplying a patch. [ISC-Bugs #43262] Changes since 4.3.4 - Fixed util/bindvar.sh error handling. [ISC-Bugs #41973] - Correct error message in relay to use remote id length instead of circuit id length. [ISC-Bugs #42556] - Add logic to test directory Makefiles to avoid copying Attfile(s) when building within the source tree. This eliminates a noisy but otherwise harmless error message when running "make check". [ISC-Bugs #41883] - Leases are now scrubbed of certain prior use information when pool re-balancing reassigns them from one FO peer to the other. This corrects an issue where leases that were offered but not used by the client retained the client hostname from the original client. Thanks to Pavel Polacek, Jan Evangelista Purkyne University for reporting the issue. [ISC-Bugs #42008] - In the LDAP code and schema add some missing '6' characters to use the v6 instead of the v4 versions. Thanks to Denis Taranushin for reporting this issue and supplying its patch. [ISC-Bugs #42666] - Correct how the pick-first-value expression is written to a lease file. Previously it was written as a concat expression due to a cut and paste error. [ISC-Bugs #42253] - Modify the DDNS code to clean up the PTR record even if there are issues while cleaning up the A or AAAA records. [ISC-Bugs #23954] - Added global configuration parameter, abandon-lease-time, which determines the amount of time a lease remains abandoned. The default is 84600 seconds. Additionaly, the server now conducts a ping check (if ping checks are enabled) prior to offering an abandoned lease to client. Our thanks to David Zych at University of Illinois for reporting the issue and working with us to produce a viable solution. [ISC-Bugs #41815] - Correct handling of interface names during interface discovery. This addresses an issue where interface names of 15 characters in length could lead to crashes or interface recognition errors during startup of dhcpd, dhclient, and dhcrelay. [ISC-Bugs #42226] - Updates to contrib/dhcp-lease-list.pl to make it more friendly. The updates are: looking for the lease file in more places and skipping the "processing complete" output when creating machine readable output. Thanks to Cameron Paine (cbp at null dot net) for the patch. [ISC-Bugs #42113] - When reusing a lease for dhcp-cache-threshold return the hostname to the original lease. Also if the host pointer, UID or hardware address change don't allow reuse of the lease. Thanks to Michael Vincent for reporting this and helping us verify the problem and fix. [ISC-Bugs #42849] - Change dmalloc to use a size_t as the length argument to bring it in line with the call it will make to malloc(). [ISC-Bugs #40843] - If the failover socket can't be bound, close it. Otherwise if the user configures an incorrect address in the failover stanza the server will continue to open new sockets every 90 seconds until it runs out. [ISC-Bugs #42452] - Add DHCPv4-mode, dhcrelay command line options, "-iu" and "-id", that allow interfaces to be upstream or downstream respectively. Upstream interfaces will accept and forward only BOOTP replies, while downstream interfaces will accept and forward only BOOTP requests. [ISC-Bugs #41547] - Clean up some memory references in the vendor-class construct. [ISC-Bugs #42984] Changes since 4.3.4b1 - None Changes since 4.3.3 - Corrected a static analyzer warning in common/execute.c [ISC-Bugs #40374] - ISC DHCP now follows the common convention to use the base name a program is invoked with (aka argv[0], vs. a builtin name) for logs. This should help differentiate syslog entries for DHCPv4 and DHCPv6 servers. You can define OLD_LOG_NAME in includes/site.h to keep the previous behavior. [ISC-Bugs #38692] - The Linux packet filter code now correctly treats only the least significant 12 bits in an inbound packet's TCI value as the VLAN id (per IEEE 802.1Q). Prior to this it was using the entire 16 bit value as the VLAN id and incorrectly discarding packets. Thanks to Jiri Popelka at Red Hat for reporting this issue and supplying its patch. [ISC-Bugs #40591] - Fixed several static analysis issues such as potential null references, unchecked strdup returns. Thanks to Bill Parker (wp02855 at gmail dot com) who identified these issues and supplied patches to address them. [ISC-Bugs #40754] [ISC-Bugs #40823] - Corrected compilation errors that prohibited building the server and its ATF unit tests when failover is disabled. [ISC-Bugs #40372] - Added the lease address to the end of the debug level log message emitted when an existing lease is renewed within the dhcp-cache-threshold. Thanks to Nathan Neulinger at Missouri S&T for suggesting the change. [ISC-Bugs #40598] - Added dhcpv6 and delayed-ack to settings listed in the "Features:" section of the configure script output. Additionally, all of the features reported on will now always show either a "yes" or "no" value. Prior to this features left to their default setting would not show a value. [ISC-Bugs #40381] - Added a parameter, authoring-byte-order, to the lease file. This value is automatically added to the top of new lease files by the server and indicates the internal byte order (big endian or little endian) of the server. This permits lease files generated on a server with one form of byte order to be used on a server with the opposite form. Our thanks to Timothe Litt for calling this to our attention and for the suggestions he provided. [ISC-Bugs #38396] - Fixed a small memory leak in the DHCPv6 version of the client code. This is unlikely to cause significant issues in actual use. [ISC-Bugs #40990] - Corrected a few minor memory leaks in omapi's dereferencing of host objects. Thanks to Jiri Popelka at Red Hat for reporting the issue and supplying the patches. [ISC-Bugs #33990] [ISC-Bugs #41325] - Cleaned up some of the Make infrastructure to make --with-libbind work better. Though it still only works with an absolute path. [ISC-Bugs #39210] - Made the embedded bind libraries able to be cross compiled (please refer to the bind9 documentation to learn how to cross compile DHCP and its bind library dependency). [ISC-Bugs #38836] - Update the client code to better support getting IA_NAs and IA_PDs in the same packet, see RFC7550 for some discussion. [ISC-Bugs #40190] ! Update the bounds checking when receiving a packet. Thanks to Sebastian Poehn from Sophos for the bug report and a suggested patch. [ISC-Bugs #41267] CVE: CVE-2015-8605 - When handling an incorrect command line for dhcpd, dhclient or dhcrelay print out a specific error message about the first error in addition to the usage string. This may be disabled by editing includes/site.h. [ISC-Bugs #40321] [ISC-Bugs #41454] - The configure script will now exit with an error message if it cannot find a GNU-style make tool (needed when building BIND libraries) or pkg-config (needed to locate ATF used for building unit tests). Prior to this the script would exit indicating success causing subsequent attempts to build the software to fail. [ISC-Bugs #40371] - Properly terminate strings before passing them to regex and fix a boundary error when creating certain new data strings. Thanks to Andrey Jr. Melnikov for the bug report. [ISC-Bugs #41217] - Option expressions, such as prepend and append, are now supported when running dhclient for IPv6. Prior to this such statements in the client configuration file would be parsed but have no affect. Thanks to Jiri Popelka at Red Hat for reporting the issue. [ISC-Bugs #39952] - A failover primary server will now accept a binding status update from the secondary which transitions a lease from ACTIVE to ABANDONED. This accounts for instances in which a client declines a lease and only the secondary server receives it. Prior to this the primary server would reject such an update as an "invalid state transition". [ISC_BUGS #25189] - Properly allocate memory for a bpf filter. Thanks to Bill Parker (wp02855 at gmail dot com) who identified this issue. [ISC-Bugs #41485] - Updated contrib/dhcp-lease-list.pl to handle garbage in the oui file better and to print out the hostnames a bit better. Thanks to Antoine Beaupré from Debian for the suggested patch. [ISC-Bugs #41288] - The DHCPv6 server now handles long valid and preferred lease times better. Values that would cause the internal end time of the lease to wrap are modified to work as infinite. [ISC-Bugs #40773] - Updated support for cross compiling by allowing the library archiver to be set at configure time via the environment variable 'AR'. [ISC-Bugs #41536] - The server will now match DHCPv6 relayed clients to host declarations which include the "hardware" statement, if the relay connected to the client supplies the client's hardware address via client-linklayer-address option as per RFC 6939. [ISC-Bugs #40334] - Allow a filename to be specified instead of /dev/random during configuration. This is passed to the BIND configuration to allow for cross compilation. [ISC-Bugs #33835] - Add more option definitions. [ISC-Bugs #40562] - Correct outputting of long lines in the lease file when writing a lease that includes long strings in an execute statement. [ISC-Bugs #40994] - The server will now correctly treat a lease as reserved when the client requests an infinite lease time (i.e. OxFFFFFFFF) and "infinite-is-reserved" is enabled. Prior to this the server would halt. In addition, corrections were made to the server to allow a lease's flags field to be set via omapi. Prior to this, the server, depending on the host architecture, would incorrectly parse the new flags value from the omapi message. [ISC-Bugs #31179] - ISC DHCP can now be configured and built from a directory other than the top level source directory. Note that "make distcheck" uses this feature. [ISC-Bugs #39262] - Add support for RFC 3527 to dhcrelay. A new, dhcrelay command line argument, "-U " enables the addition of a RFC 3527 compliant link selection suboption to the agent option added for clients directly connected to the relay. [ISC-Bugs #34875] [ISC-Bugs #41708] - Add a new global DHCPv6 option, dhcpv6-set-tee-times, which when enabled instructs the server to calculate T1 and T2 as recommended in RFC 3315, Section 22.4. [ISC-Bugs #25687] - Corrected minor Coverity issues. [ISC-Bugs #35144] - Add support for RFC 7341 DHCPv4 over DHCPv6 with a new configuration option "--enable-dhcpv4o6". Note this feature requires DHCPv6 support and is not compatible with delayed-ack. Both client and server use 2 processes which communicate over UDP on a pair of sockets. The new "-4o6 " command line argument enables DHCPv4 over DHCPv6 support and specifies the consecutive ports to use for inter-process communication. Please look at doc/DHCPv4-over-DHCPv6 for more details. [ISC-Bugs #35711] - Correct interface name formation when using DLPI under Solaris 11. As of Solaris 11, ethernet device files are located in "/dev/net". The configure script has been modified to detect this situation and adjust the directory used accordingly. Thanks to Jarkko Torppa for reporting this issue and submitting a patch [ISC-Bugs #37954] [ISC-Bugs #40752] - Add a dereference call when handling an error condition while decoding a packet. [ISC-Bugs #41774] - Add a new parameter, lease-id-format, to both dhcpd and dhclient. The parameter controls the format in which certain values are written to lease files. Formats supported are octal - quoted string containing octal escapes, and hex - unquoted, colon separated hex digits. Thanks to Jay Ford, University of Iowa for bringing the issue to our attention. [ISC-Bugs #26378] ! Add an option in site.h to limit the number of failover and control connections the server will accept. By default this is 200. [ISC-Bugs #41845] CVE: CVE-2016-2774 Changes since 4.3.3b1 - None Changes since 4.3.2 - The server now does a better check to see if it can allocate the memory for large blocks of v4 leases and should provide a slightly better error message. Note well: the server pre-allocates v4 addresses, if you use a large range, such as a /8, the server will attempt to use a large amount of memory and may not start if there either isn't enough memory or the size exceeds what the code supports. [ISC-Bugs #38637] - The server will now reject unicast Request, Renew, Decline, and Release messages from a client unless the server would have sent that client the dhcp6.unicast option. This behavior is in compliance with paragraph 1 in each of the sections 18.2,1, 18.2.3, 18.2.6, and 18.2.7 of RFC 3315. Prior to this, the server would simply accept the messages. Now, in order for the server to accept such a message, the server configuration must include the dhcp6.unicast option either globally or within the shared network to which the requested lease belongs. In other words, the server will map the first IA_XX address found within the client message to a shared-network and look for the presence of the unicast option there and then globally. Thanks to Jiri Popelka at Red Hat for this issue and his patch which inspired the fix. [ISC-Bugs #21235] - The ATF (Automated Testing Framework) tools used for optional unit tests can now be built from its embedded sources in bind, solving the atf-run / atf-report issue with recent (>= 0.20) versions of ATF. The new configuration option is "./configure --with-atf=bind". [ISC-Bugs #38754, #39300] - Corrected a compilation error introduced by the fix for ISC-Bugs #22806. On older linuxes that do not include the tpacket_auxdata structure don't bother allocating the cmsgbuf as it isn't necessary and we don't have a proper length for it. [ISC-Bugs #39209] - Remove the dst directory. This was replaced in 4.2.0 with the dst code from the Bind libraries but we continued to include it for backwards compatibility. As we have now released 4.3.x it seems reasonable to remove it. [ISC-Buts #39019] - Write out the DUID server id on startup in all cases, previously if it was read in from server-duid option in the config or lease files for DHCPv4 it would not be written to the new lease file. [ISC-Bugs #37791] - When parsing dates for leases convert dates past 2038 to "never". This avoids problems with integer overflows in the date and time handling code for people that decide to use very large lease times or add a lease entry with a date far in the future. [ISC-Bugs #33056] - Leave the siaddr field clear when sending a NACK as per RFC 2131 table 3. [ISC-Bugs #38769] - In the client don't send expired addresses to the script as part of the binding process. Thanks to Sven Trenkel at Google for reporting the issue and suggesting the patch. [ISC-Bugs #38631] - While parsing IPv6 addresses treat "add" as part of the address instead of as a token. [ISC-Bugs #39529] - Add support for accessing the v4 lease queues (active, free etc) in a binary fashion instead of needing to walk through a linear list to insert, find or remove an entry from the queues. In addition add a compile time option "--enable-binary-leases" to enable the new code or to continue using the old code. The old code is the default. Thanks to Fernando Soto from BlueCat Networks for the patch. [ISC-Bugs #39078] - Delayed-ack now works properly with Failover. Prior to this, bind updates post startup were being queued but never delivered. Among other things, this was causing leases to not transition from expired or released to free. [ISC-Bugs #31474] - Clean up parsing of v6 lease files a bit to avoid infinite loops if the lease file is corrupt in certain ways. [ISC-Bugs #39760] - Corrected a crash in dhclient that occurs during lease renewal if the client is performing its own DNS updates. Thanks to Jiri Popelka at Red Hat for the bug report. [ISC-Bugs #38639] - Corrected an issue in v6 lease file parsing. Prior to this, when encountering a lease with an address for which no configured pool exists, the server was declaring the lease file corrupt and incorrectly skipping over the subsequent entry in the file. The server will now emit a log message indicating that no pool was found for the address (or prefix) and correctly resume parsing with the next entry in the lease file. Our thanks to Michal Žejdl for reporting the issue. [ISC-Bugs #39314] - Be more liberal in finding a subnet group associated with a static prefix. When we added the class matching code for v6 we also added a requirement that the static prefix must be within a subnet the client was in, in order to find the proper statements. We now look for a subnet based on the prefix, failing that on the static address for the client and failing that on the shared network itself. [ISC-Bugs #38329] - Add a new action expression "parse_vendor_options", which can be used to parse a vendor-encapsualted-option received by the server based on the encoding specified by the vendor-option-space statement. [ISC-Bugs #36449] - Enhance the PARANOIA patch to include fchown() the lease file to allow it to be manipulated after the server does a chown(). Thanks to Jiri Popelka at Red Hat for the patch. [ISC-Bugs #36978] - Relax the requirement that prefix pools must be within the subnet. This was added in as part of #32453 in order to avoid configuration mistakes but is being removed as prefixes aren't required to be within the same subnet and many people configure them in that fashion. [ISC-Bugs #40077] - Fixed a server crash that could occur when the server attempts to remove the billing class from the last lease billed to a dynamic class after said class has been deleted. Our thanks to Lasse Pesonen for reporting the issue. [ISC-Bugs #39978] - LDAP Patches - Numerous small patches submitted by contributors have been applied to the contributed code which supplies LDAP support. In addition, two larger submissions have also been included. The first adds support for IPv6 configuration and the second provides GSSAPI authentication. We would like to thank the following for their contributions (alphabetically): Alex Novak at SUSE Bill Parker (wp02855 at gmail dot com) Jiri Popelka at Red Hat Marius Tomaschewski at SUSE (william at adelaide.edu.au), The University of Adelaide [ISC-Bugs #39056] [ISC-Bugs #22742] [ISC-Bugs #24449] [ISC-Bugs #28545] [ISC-Bugs #29873] [ISC-Bugs #30183] [ISC-Bugs #30402] [ISC-Bugs #32217] [ISC-Bugs #32240] [ISC-Bugs #33176] [ISC-Bugs #33178] [ISC-Bugs #36409] [ISC-Bugs #36774] [ISC-Bugs #37876] - Handle an out of memory condition in the client a bit better. Thanks to Frédéric Perrin from Brocade for finding the issue and suggesting a patch. [ISC-Bugs #39279] Changes since 4.3.2rc2 - None Changes since 4.3.2rc1 - Corrected a compilation error introduced by the fix for ISC-Bugs #37415. The error occurs on Linux variants that do not support VLAN tag information in packet auxiliary data. The configure script now only enables inclusion of the VLAN tag-based logic if it is supported by the underlying OS. [ISC-Bugs #38677] Changes since 4.3.2b1 - Specifying the option, --disable-debug, on the configure script command line now disables debug features. Prior to this, specifying --disable-debug incorrectly enabled debug features. Thanks to Gustavo Zacarias for reporting the issue. [ISC-Bugs #37780] - Unit test execution now uses a path augmented during configuration processing of the --with-atf option to locate ATF runtime tools, atf-run and atf-report. For most installations of ATF, this should alleviate the need to manually include them in the PATH, as was formerly required. If the configure script cannot locate the tools it will emit a warning, informing the user that the tools must be in the PATH when running unit tests. Secondly, please note that "make check" will now exit with a failure status code (non-zero) if one or more unit tests fail. This means that invoking "make check" from an upper level directory will cause the make process to STOP after the first test subdirectory with failed test(s). To force all tests in all subdirectories to run, regardless of individual test outcome, use the command "make -k check". [ISC-Bugs #38619] Changes since 4.3.1 - Corrected parser's right brace matching when a statement contains an error. [ISC-Bugs #36021] - TSIG-authenticated dynamic DNS updates now support the use of these additional algorithms: hmac-sha1, hmac-sha224, hmac-sha256, hmac-sha384, and hmac-sha512 [ISC-Bugs #36947] - Added check for invalid failover message type. Thanks to Tobias Stoeckmann working with the OpenBSD project who spotted the issue and provided the patch. [ISC-Bugs #36653] - Corrected rate limiting checks for bad packet logging. Thanks to Tobias Stoeckmann working with the OpenBSD project who spotted the issue and provided the patch. [ISC-Bugs #36897] - Log statements depicting what files will be used by the server now occur after the configuration file has been processed. [ISC-Bugs #36671] - Addressed Coverity issues reported as of 07-31-2014: [ISC-Bugs #36712] Corrects Coverity reported "high" impact issues. [ISC-Bugs #36933] Corrects Coverity reported "medium" impact issues [ISC-Bugs #37708] Fixes compilation error in dst_api.c seen in older compilers that was introduced by #36712 - Server now supports a failover split value of 256. [ISC-Bugs] #36664] - Remove unneeded error #defines. These defines were included in case external programs required the older versions of the macro. They have been #ifdeffed for now and will be removed at a future date. See site.h for the #define to include them again, but you should switch to using the DHCP_R_* versions instead of the ISC_R_* versions. Also ISC_R_MULTIPLE has been removed as it is also defined in bind. [ISC-Bugs #37128] - Added checks in range6 and prefix6 statement parsing to ensure addresses are within the declared subnet. Thanks to Jiri Popelka at Red Hat for the bug report and patch. [ISC-Bugs #32453] [ISC-Bugs #17766] [ISC-Bugs #18510] [ISC-Bugs #23698] [ISC-Bugs #28883] - Addressed checksum issues: Added checksum readiness check to Linux packet filtering which eliminates invalid packet drops due to checksum errors when checksum offloading is in use. Based on dhcp-4.2.2-xen-checksum.patch made to the Fedora project. [ISC-Bugs #22806] [ISC-Bugs #15902] [ISC-Bugs #17739] [ISC-Bugs #18010] [ISC-Bugs #22556] [ISC-Bugs #29769] Inbound packets with UDP checksums of 0xffff now validate correctly rather than being dropped. [ISC-Bugs #24216] [ISC-Bugs #25587] - Added the echo-client-id configuration parameter to the server configuration. The server now supports RFC 6842 compliant behavior by setting a new configuration parameter, echo-client-id. When enabled, the server will include the client identifier option (Option code 61) if received, in its responses. The server identifier returned in NAKs (if enabled) will now be the globally defined value (if one) if the server cannot attribute the inbound request to a known subnet. [ISC-Bugs #35958] [ISC-Bugs #32545] - Added support of the configuration parameter, use-host-decl-names, to BOOTP request handling. [ISC-Bugs #36233] - Added logic to ignore the signal, SIGPIPE, which ensures write failures will be delivered as errors rather than as SIGPIPE signals on all OSs. Thanks to Marius Tomaschewski from SUSE who reported the issue and provided the patch upon which the fix is based. [ISC-Bugs #32222] - In the failover code, handle the case of communications being interrupted when the servers are dealing with POTENTIAL-CONFLICT. This patch allows the primary to accept the secondary moving from POTENTIAL-CONFLICT to RESOLUTION-INTERRUPTED as well as handling the bind update process better. In addition the code to resend update or update all requests has been modified to send requests more often. [ISC-Bugs #36810] [ISC-Bugs #20352] - By default, the server will now choose the value to use in the forward DNS name from the following in order of preference: 1. FQDN option if provided by the client 2. Host name option if provided by the client 3. Configured option host-name if defined As before, this may be overridden by defining ddns-hostname to the desired value (or expression). In addition, the server logic has been extended to use the value of the host name declaration if use-host-decl-names is enabled and no other value is available. [ISC-Bugs #21323] - DNS updates were being attempted when dhcp-cache-threshold enabled the use of the existing lease and the forward DNS name had not changed. This has been corrected. [ISC-Bugs #37368] [ISC-Bugs #38636] - Corrected an issue which caused dhclient to incorrectly form the result when prepending or appending to the IPv4 domain-search option, received from the server, when either of the values being combined contain compressed components. [ISC-Bugs #20558] - Added the server-id-check parameter to the server configuration. This parameter allows run-time control over whether or not a server, participating in failover, verifies the dhcp-server-identifier option in DHCP REQUESTs against the server's id before processing the request. Formerly, enabling this behavior was done at compilation time through the use of the #define, SERVER_ID_CHECK, which has been removed from site.h The functionality is now only available through the new runtime parameter. [ISC-Bugs #37551] - During startup, when the server encounters a lease whose binding state is FTS_BACKUP but whose pool has no configured failover peer, it will reset the lease's binding state to FTS_FREE. This allows the leases to be reclaimed by the server after a pool's configuration has changed from failover to standalone. Prior to this such leases would remain stuck in the backup state making them unavailable for assignment. Note this conversion will occur whether or not the server is compiled for failover. [ISC-Bugs #36960] - Fixed a small issue in the treatment of hosts in the inform processing that could cause the response to an inform to include information from the wrong scope. The two examples we've heard of are getting subnet instead of group information associated with a host entry, or getting global information instead of subnet if the host entry was built via omapi. Thanks to Julien Soula at University of Lille for finding the bug and supplying a patch. [ISC-Bugs #35712] - Avoid calling pool_timer() recursively from supersede_lease(). This could result in leases changing state incorrectly or delaying the running of the leae expiration code. [ISC-Bugs #38002] - Move the check for a PID file and process to be before we rewrite the lease file. This avoids the possibility of starting a second instance of a server which changes the current lease file confusing the first instance. This check is only included if the admin hasn't disabled PID files. [ISC-Bugs #38078] [ISC-Bugs #38143] - In the client code change the way preferred_life and max_life are printed for environment variables to be unsigned rather than signed. Thanks to Jiri Popelka at Red Hat for the bug report and patch. [ISC-Bugs #37084] - Modified Linux packet handling such that packets received via VLAN are now seen only by the VLAN interface. Prior to this, such packets were seen by both the VLAN interface and its parent (physical) interface, causing the server to respond to both. Note this remains an issue for non-Linux OSs. Thanks to Jiri Popelka at Red Hat for the patch. [ISC-Bugs #37415] [ISC-Bugs #37133] [ISC-Bugs #36668] [ISC-Bugs #36652] - Log content has been changed to more directly suggest that admins should check for multiple IPv6 clients attempting to use the same DUID when only abandoned addresses are available. Debug level logging will now emit counts of the total number of, in-use, and abandoned addresses in a shared subnet when the server finds no addresses available for a given DUID. Lastly, threshold logging is now automatically disabled for shared subnets whose total number of possible addresses exceeds (2^64)-1. [ISC-Bugs #26376] [ISC-Bugs #38131] - Added a global parameter, prefix-length-mode, which may be used to determine how the server uses a non-zero value for prefix-length supplied by clients when soliciting DHCPv6 prefixes. The server supports selection modes of: ignore, prefer, exact, minimum and maximum which are described in detail in the server man pages. The prior behavior of the server was to only offer a prefix whose length exactly matched the prefix-length value requested. If no such prefixes were available, the server returned a status of none available. Note the default mode, "exact", provides this same behavior. [ISC-Bugs #36780] [ISC-Bugs #32228] - Corrected inconsistencies in dhcrelay's setting the upper interface hop count limit such that it now sets it to 32 when the upstream address is a multicast address per RFC 3315 Section 20. Prior to this if the -u argument preceded the -l argument on the command line or if the same interface was specified for both; the logic to set the hop limit count for the upper interface was skipped. This caused the hop count limit to be set to the default value (typically 1) in the outbound upstream packets. [ISC-Bugs #37426] Changes since 4.3.1b1 - Modify the linux and openwrt dhclient scripts to process information from a stateless request. Thanks to Jiri Popelka at Red Hat for the bug report and patch. [ISC-Bugs #36102] - Remove more unused RCSID tags. These weren't noticed in 4.3 as the code isn't used anymore but we remove them here to keep the code consistent across versions. [ISC-Bugs #36451] Changes since 4.3.0 - Tidy up several small tickets. Correct parsing of DUID from config file, previously the LL type was put in the wrong place in the DUID string. [ISC-Bugs #20962] Add code to parse "do-forward-updates" as well as "do-forward-update" Thanks to Jiri Popelka at Red Hat. [ISC-Bugs #31328] Remove log_priority as it isn't currently used. [ISC-Bugs #33397] Increase the size of the buffer used for reading interface information. [ISC-Bugs #34858] - Remove an extra set of the msg_controllen variable. [ISC-Bugs #21035] - Add a more understandable error message if a configuration attempts to add multiple keys for a single zone. Thanks to a patch from Jiri Popelka at Red Hat. [ISC-Bugs #31892] - Fix some minor issues in the dst code. [ISC-Bugs #34172] - Properly #ifdef functions so that the code can compile without NSUPDATE. [ISC-Bugs #35058] - Update the partner's stos (start time of state, basically when we last heard from this partner) field when updating the state in failover. [ISC-Bugs #35549] - Modify the overload processing to allow space for the remote agent ID. [ISC-Bugs #35569] Handle the ordering of the SUBNET_MASK option even if it is the last option in the list. [ISC-Bugs #24580] - Remove the code that allows a server to follow RFC3315 instead of the subsequent errata from August 2010 when determining which IAs to include if no addresses will be assigned. [ISC-Bugs #28938] - Remove unused RCSID tags. [ISC-Bugs #35846] - Correct the v6 client timing code. When doing the timing backoff for MRT limit it to MRD. Thanks to Jiri Popelka at Red Hat for the bug report and fix. [ISC-Bugs #21238 - Add a log entry when killing a client and remove the PID files when a server, relay or client are killed. [ISC-Bugs #16970] [ISC-Bugs #17258] - Some minor cleanups in the client code. In addition to checking for dhcpc check for bootpc in the services list. [ISC-Bugs #18933] Correct the client code to only try to get a lease once when the given the "-1" argument. Thanks to Jiri Popelka at Red Hat for the bug report and fix. [ISC-Bugs #26735] When asked for the version don't send the output to syslog. [ISC-Bugs #29772] Add the next server information to the environment variables for use by the client script. In order to avoid changing the client lease file the next server information isn't written to it. Thanks to Tomas Hozza at Red Hat for the suggestion and a prototype fix. [ISC-Bugs #33098] - Several updates to the dhcp server code. When not in quiet mode print out the files being used. [ISC-Bugs #17551] As accessing some pid files may require privileges move the dropping of permission bits due to the paranoia patch to be after the pid code. Thanks to Jiri Popelka at Red Hat for the bug report and fix. [ISC-Bugs #25806] When processing a "--version" request don't output the version information to syslog. - Add the "enable-log-pid" build option to the configure script. When enabled this causes the client, server and relay programs to include the PID number in syslog messages. Thanks to Marius Tomaschewski for the suggestion and proto-patch. [ISC-Bugs #29713] - Add a #define to specify the prefix length used when a client attempts to configure an address. This can be modified by editing includes/site.h. By default it is set to 64. While 128 might be a better choice it would also be a change for currently running systems, so we have left it at 64. [ISC-Bugs #DHCP-2] - Add a run time option to the client "-df" to allow the administrator to point to a second lease file the client can search for a DUID. This can be used to allow a v4 and a v6 instance of the client to share a DUID. The second file will only be searched if there isn't a DUID in the main lease file and the DUID will be written out to the main lease file. [ISC-Bugs #34886] - Have the client fsync the lease file to avoid lease corruption if the client hibernates or otherwise shuts down. [ISC-Bugs #35894] - Add a check for L2VLAN in bpf.c to help support VLAN interfaces Thanks to Steinar Haug for the suggestion. [ISC-Bugs #36033] - Modify the handling of the resolv.conf file to allow the DHCP process to start up even if the resolv.conf file has problems. [ISC-Bugs #35989] - Add threshold logging functionality. Two new options, log-threshold-low and log-threshold-high, indicate to the server if and when it should log an error message as addresses in a pool are used. [ISC-Bugs #34487] - Add code to properly dereference a pointer in the dhclient code on an error condition. [ISC-Bugs #36194] - Add code to help clean up soft leases. [ISC-Bugs #36304] - Disable the gentle shutdown functionality until we can determine the best way to present it to remove or reduce the side effects. [ISC-Bugs #36066] Changes since 4.3.0rc1 - None Changes since 4.3.0b1 - Tidy up receive packet processing. Thanks to Brad Plank of GTA for reporting the issue and suggesting a possible patch. [ISC-Bugs #34447] Changes since 4.3.0a1 - Modify the message displayed when a process hits a fatal error. The new message is much shorter and simply points to the README and our website for directions on bug submissions. [ISC-Bugs #24789] Changes since 4.2.0 (new features) - If a client renews before 'dhcp-cache-threshold' percent of its lease has elapsed (default 25%), the server will reuse the allocated lease (provide a lease within the currently allocated lease-time) rather than extend or renew the lease. This absolves the server of needing to perform an fsync() operation on the lease database before reply, which improves performance. [ISC-Bugs #22228] Updated this patch to support asynchronous DDNS. If the server is attempting to do DDNS on a lease it should be updated and written to disk even if that wouldn't be necessary due to the thresholding. [ISC-Bugs #26311] - The 'no available billing' log line now also logs the name of the last matching billing class tried before failing to provide a billing. [ISC-Bugs #21759] - A problem with missing get_hw_addr function when --enable-use-sockets was used is now solved on GNU/Linux, BSD and GNU/Hurd systems. Note that use-sockets feature was not tested on those systems. Client and server code no longer use MAX_PATH constant that is not defined on GNU/Hurd systems. [ISC-Bugs #25979] - Add a perl script in the contrib directory, dhcp-lease-list.pl, which can parse v4 lease files and output the lease information in a more human friendly manner. This was written by Christian Hammers with some updates by vom and ISC. This is contributed code and is not supported by ISC; however it may be useful to some users. [ISC-Bugs #20680] - Add support in v6 for on-commit, on-expire and on-release. [ISC-Bugs #27912] - Add support for using classes with v6. [ISC-Bugs #26510] - Update the DDNS code to current standards and allow for sharing of DDNS entries between v4 and v6 clients. The new code is used if the ddns-update-style is set to "standard", the older code is still available if ddns-update-style is set to "interim". The oldest DDNS code "ad-hoc" has been removed. Thanks to Thomas Pegeot who submitted a patch for this issue. This patch is based on that work with some modifications. [ISC-Bugs #21139] - Add a configuration option to the server to suppress using fsync(). Enabling this option will mean that fsync() is never called. This may provide better performance but there is also a risk that a lease will not be properly written to the disk after it has been issued to a client and before the server stops. Using this option is not recommended. [ISC-Bugs #34810] - Add some logging statements to indicate when the server is ready to serve. One statement is emitted after the server has finished reading its files and is about to enter the dispatch loop. This is "Server starting service.". The second is emitted when a server determines that both it and its failover peer are in the normal state. This is "failover peer : Both servers normal." [ISC-Bugs #33208] - Add support for accessing options from v6 relays. The v6relay statement allows the administrator to choose which relay to use when searching for an option, see the dhcp-options man page for a description. The host-identifier option has also been updated to support the use of relay options, see the dhcpd.conf man page for a description. [ISC-Bugs #19598] - When doing DDNS if there isn't an appropriate zone statement attempt to find a reasonable nameserver via a DNS resolver. This restores some functionality that was lost in the transition to asynchronous DDNS. Due to the lack of security and increase in fragility of the system when using this feature we strongly recommend the use of appropriate zone statements rather than using this functionality. [ISC-Bugs #30461] - Add support for specifying the address from which to send DDNS updates on the DHCP server. There are two new options "ddns-local-address4" and "ddns-local-address6" that each take one instance of their respective address types. [ISC-Bugs #34779] - Add ignore-client-uids option in the server. This option causes the server to not record a client's uid in its lease. This violates the specification but may also be useful when a client can dual boot using different client ids but the same mac address. Thank you to Brian De Wolf at Cal Poly Pomona for the patch. [ISC-Bugs #32427] [ISC-Bugs #35066] - Extend the DHCPINFORM processing to honor the subnet selection option and take host declarations into account. Thanks to Christof Chen for testing and submitting the patch. [ISC-Bugs #35015] - Extend the hardware expression to look into the lease structure for a hardware address if there is no packet. This allows the server to find the hardware address during on-expiry processing. [ISC-Bugs #24584] - Add definitions for some options that have been specified by the IETF. [ISC-Bugs #29268] [ISC-Bugs #35198] Changes since 4.2.0 (bug fixes) - When using 'ignore client-updates;', the FQDN returned to the client is no longer truncated to one octet. - Cleaned up an unused hardware address variable in nak_lease(). - Manpage entries for the ia-pd and ia-prefix options were updated to reflect support for prefix delegation. - Cleaned up some compiler warnings - An optimization described in the failover protocol draft is now included, which permits a DHCP server operating in communications-interrupted state to 'rewind' a lease to the state most recently transmitted to its peer, greatly increasing a server's endurance in communications-interrupted. This is supported using a new 'rewind state' record on the dhcpd.leases entry for each lease. - Fix the trace code which was broken by the changes to the DDNS code. - Update the fsync code to work with the changes to the DDNS code. It now uses a timer instead of noticing if there are no more packets to process. - When constructing the DNS name structure from a text string append the root to relative names. This satisfies a requirement in the DNS library that names be absolute instead of relative and prevents DHCP from crashing. [ISC-Bugs #21054] - "The LDAP Patch" that has been circulating for some time, written by Brian Masney and S.Kalyanasundraram and maintained for application to the DHCP-4 sources by David Cantrell has been included. Please be advised that these sources were contributed, and do not yet meet the high standards we place on production sources we include by default. As a result, the LDAP features are only included by using a compile-time option which defaults off, and if you enable it you do so under your own recognizance. We will be improving this software over time. [ISC-Bugs #17741] - Prohibit including lease time information in a response to a DHCP INFORM. [ISC-Bugs #21092] ! Accept a client id of length 0 while hashing. Previously the server would exit if it attempted to hash a zero length client id, providing attackers with a simple denial of service attack. [ISC-Bugs #21253] CERT: VU#541921 - CVE: CVE-2010-2156 - A memory leak in ddns processing was closed. [ISC-Bugs #21377] - Modify the exception handling for initial context creation. Previously we would try and clean up before exiting. This could present problems when the cleanup required part of the context that wasn't available. It also didn't do much as we exited afterwards anyway. Now we simply log the error and exit. [ISC-Bugs #21093] - A bug was fixed that could cause the DHCPv6 server to advertise/assign a previously allocated (active) lease to a client that has changed subnets, despite being on different shared networks. Dynamic prefixes specifically allocated in shared networks also now are not offered if the client has moved. [ISC-Bugs #21152] - Add some debugging output for use with the DDNS code. [ISC-Bugs #20916] - Fix the trace code to handle timing events better and to truncate a file before using instead of overwriting it. [ISC-Bugs #20969] - Modify the determination of the default TTL to use for DDNS updates. The user may still configure the ttl via ddns-ttl. The default for both v4 and v6 is now 1/2 the (preferred) lease time with a limit. The previous defaults (1/2 lease time without a limit for v4 and a default value for v6) may be used by defining USE_OLD_DDNS_TTL in site.h [ISC-Bugs #21126] - libisc/libdns is now brought up to version 9.7.1rc1. This corrects three reported flaws in ISC DHCP; o DHCP processes (dhcpd, dhclient) fail to start if one of either the IPv4 or IPv6 address families is not present. [ISC-Bugs #21122] o Assertion failure when attempting to cancel a previously running DDNS update. [ISC-Bugs #21133] o Compilation failure of libisc/libdns due to the use of a flexible array member. [ISC-Bugs #21316] - Add declaration for variable in debug code in alloc.c. [ISC-Bugs #21472] - Documentation cleanup covering multiple tickets [ISC-Bugs #20265] [ISC-Bugs #20259] minor cleanup [ISC-Bugs #20263] add text describing some default values [ISC-Bugs #20193] single quotes at the start of a line indicate a control line to nroff, escape them if we actually want a quote. [ISC-Bugs #18916] sync the pointer to web pages amongst the different docs - 'get-host-names true;' now also works even if 'use-host-decl-names true;' was also configured. The nature of this repair also fixes another error; the host-name supplied by a client is no longer overridden by a reverse lookup of the lease address. Thanks to a patch from Wilco Baan Hofman supplied to us by the Debian package maintenance team. [ISC-Bugs #21691] {Debian Bug#509445} - The .TH tag for the dhcp-options manpage was typo repaired thanks to a report from jidanni and the Debian package maintenance team. [ISC-Bugs #21676] {Debian Bug#563613} - More documentation changes - primarily to put the options in the dhclient and dhcpd man pages into the standard form. Thanks in part to a patch from David Cantrell at Red Hat. [ISC-Bugs #20264] and parts of [ISC-Bugs #17744] dhclient.8 changes - Add code to clear the pointer to an object in an OMAPI handle when the object is freed due to a dereference. [ISC-Bugs #21306] - Fixed a bug that leaks host record references onto lease structures, causing the server to apply configuration intended for one host to any other innocent clients that come along later. [ISC-Bugs #22018] - Minor code fixes [ISC-Bugs #19566] When trying to find the zone for a name for ddns allow the name to be at the apex of the zone. [ISC-Bugs #19617] Restrict length of interface name read from command line in dhcpd - based on a patch from David Cantrell at Red Hat. [ISC-Bugs #20039] Correct some error messages in dhcpd.c [ISC-Bugs #20070] Better range check on values when creating a DHCID. [ISC-Bugs #20198] Avoid writing past the end of the field when adding overly long file or server names to a packet and add a log message if the configuration supplied overly long names for these fields. Thanks to Martin Pala. [ISC-Bugs #21497] Add a little more randomness to rng seed in client thanks to a patch from Jeremiah Jinno. - Correct error handling in DLPI [ISC-Bugs #20378] - Remove __sun__ and __hpux__ typedefs in osdep.h as they are now being checked in configure. [ISC-Bugs #20443] - Modify how the cmsg header is allocated the v6 send and received routines to compile on more compilers. [ISC-Bugs #20524] - When parsing a domain name free the memory for the name after we are done with it. [ISC-Bugs #20824] - Add an elapsed time option to the release message and refactor the code to move most of the common code to a single routine. [ISC-Bugs #21171]. - Two identical log messages for commit_leases() have been disambiguated. [ISC-Bugs #18915] - Parse date strings more properly - the code now handles semi-colons in date strings correctly. Thanks to a patch from Jiri Popelka at Red Hat. [ISC-Bugs #21501, #20598] - Fixes to lease input and output. [ISC-Bugs #20418] - Some systems don't support the "%s" argument to strftime, paste together the same string using mktime instead. [ISC-Bugs #19596] - When parsing iaid values accept printable characters. [ISC-Bugs #21585] - Always print time values in omshell as hex instead of ascii if the values happen to be printable characters. - Minor changes for scripts, configure.ac and Makefiles [ISC-Bugs #19147] Use domain-search instead of domain-name in manual and example conf file. Thanks to a patch from David Cantrell at Red Hat. [ISC-Bugs #19761] Restore address when doing a rebind in DHCPv6 [ISC-Bugs #19945] Properly close the quote on some arguments. [ISC-Bugs #20952] Add 64 bit types to configure.ac [ISC-Bugs #21308] Add "PATH=" to CLIENT_PATH environment variable - Update the code to parse dhcpv6 lease files to accept a semi-colon at the end of the max-life and preferred-life clauses. In order to be backwards compatible with older lease files not finding a semi-colon is also accepted. [ISC-Bugs #22303]. ! Handle a relay forward message with an unspecified address in the link address field. Previously such a message would cause the server to crash. Thanks to a report from John Gibbins. [ISC-Bugs #21992] CERT: VU#102047 CVE: CVE-2010-3611 - ./configure on longer searches for -lcrypto to explicitly link against. This fixes a bug where 'dhclient' would have shared library dependencies on '/usr/lib'. [ISC-Bugs #21967] - Handle pipe failures more gracefully. Some OSes pass a SIGPIPE signal to a process and will kill the process if the signal isn't caught. This patch adds code to turn off the SIGPIPE signal via a setsockopt() call. The signal is already being ignored as part of the ISC library. [ISC-Bugs #22269] - Restore printing of values in omshell to the style pre 21585. For 21585 we changed the print routines to always display time values as a hex list. This had a side effect of printing all data strings as a hex list. We shall investigate other ways of displaying time values more usefully. [ISC-Bugs #22626] ! Fix the handling of connection requests on the failover port. Previously a connection request from a source that wasn't listed as a failover peer would cause the server to become non-responsive. Thanks to a report from Brad Bendily, brad@bendily.com. [ISC-Bugs #22679] CERT: VU#159528 CVE: CVE-2010-3616 - Don't pass the ISC_R_INPROGRESS status to the omapi signal handlers. Passing it through to the handlers caused the omshell program to fail to connect to the server. [ISC-Bugs #21839] - Fix the parenthesis in the code to process configuration statements beginning with "auth". The previous arrangement caused "auto-partner-down" to be processed incorrectly. [ISC-Bugs #21854] - Limit the timeout period allowed in the dispatch code to 2^^32-1 seconds. Thanks to a report from Jiri Popelka at Red Hat. [ISC-Bugs #22033], [Red Hat Bug #628258] - When processing the format flags for a given option consume the flag indicating an optional value correctly. A symptom of this bug was an infinite loop when trying to parse the slp-service-scope option. Thanks to a patch from Marius Tomaschewski. [ISC-Bugs #22055] - Disable the use of kqueue in the ISC library. This avoids a problem between the fork and socket code that caused the dhcpd process to use all available cpu if the program daemonized itself. [ISC-Bugs #21911] ! When processing a request in the DHCPv6 server code that specifies an address that is tagged as abandoned (meaning we received a decline request for it previously) don't attempt to move it from the inactive to active pool as doing so can result in the server crashing on an assert failure. Also retag the lease as active and reset its timeout value. [ISC-Bugs #21921] - Removed the restriction on using IPv6 addresses in IPv4 mode. This allows IPv4 options which contain IPv6 addresses to be specified. For example the 6rd option can be specified and used like this: [ISC-Bugs #23039] option 6rd code 212 = { integer 8, integer 8, ip6-address, array of ip-address }; option 6rd 16 10 2001:: 1.2.3.4, 5.6.7.8; - Handle some DDNS corner cases better. Maintain the DDNS transaction information when updating a lease and cancel any existing transactions when removing the ddns information. [ISC-Bugs #23103] - Some fixes for LDAP [ISC-Bugs #21783] - Include lber library when building ldap [ISC-Bugs #22888] - Enable the ldap code when buidling common The above fixes are from Jiri Popelka at Red Hat. - Modify the dlpi code to accept getmsg() returning a positive value. [ISC-Bugs #22824] ! In dhclient check the data for some string options for reasonableness before passing it along to the script that interfaces with the OS. [ISC-Bugs #23722] CVE: CVE-2011-0997 - DHCPv6 server now responds properly if client asks for a prefix that is already assigned to a different client. [ISC-Bugs #23948] - Add the option "--no-pid" to the client, relay and server code, to disable writing a pid file. Add the option "-pf pidfile" to the relay to allow the user to supply the pidfile name at runtime. Add the "with-relay6-pid-file" option to configure to allow the user to supply the pidfile name for the relay in v6 mode at configure time. [ISC-Bugs #23351] [ISC-Bugs #17541] - 'dhclient' no longer waits a random interval after first starting up to begin in the INIT state. This conforms to RFC 2131, but elects not to implement a 'SHOULD' direction in section 4.1. The goal of this change is to start up faster. [ISC-Bugs #19660] - Added 'initial-delay' parameter that specifies maximum amount of time before client goes to the INIT state. The default value is 0. In previous versions of the code client could wait up to 5 seconds. The old behavior may be restored by using 'initial-delay 5;' in the client config file. [ISC-Bugs #19660] - ICMP ping-check should now sit closer to precisely the number of seconds configured (or default 1), due to making use of the new microsecond scale timer internally to dhcpd. This corrects a bug where the server may immediately timeout an ICMP ping-check if it was made late in the current second. [ISC-Bugs #19660] - The DHCP client will schedule renewal and rebinding events in microseconds if the DHCP server provided a lease-time that would result in sub-1-second timers. This corrects a bug where a 2-second or lower lease-time would cause the DHCP client to enter an infinite loop by scheduling renewal at zero seconds. [ISC-Bugs #19660] - Client lease records are recorded at most once every 15 seconds. This keeps the client from filling the lease database disk quickly on very small lease times. [ISC-Bugs #19660] - To defend against RFC 2131 non-compliant DHCP servers which fail to advertise a lease-time (either mangled, or zero in value) the DHCP client now adds the server to the reject list ACL and returns to INIT state to hopefully find an RFC 2131 compliant server (or retry in INIT forever). [ISC-Bugs #19660] - Parameters configured to evaluate from user defined function calls can now be correctly written to dhcpd.leases (as on 'on events' or dynamic host records inserted via OMAPI). [ISC-Bugs #22266] - If a 'next-server' parameter is configured in a dynamic host record via OMAPI as a domain name, the syntax written to disk is now correctly parsed upon restart. [ISC-Bugs #22266] - The DHCP server now responds to DHCPLEASEQUERY messages from agents using IP addresses not covered by a subnet in configuration. Whether or not to respond to such an agent is still governed by the 'allow leasequery;' configuration parameter, in the case of an agent not covered by a configured subnet the root configuration area is examined. Server now also returns vendor-class-id option, if client sent it. [ISC-Bugs #21094] - Documentation fixes [ISC-Bugs #17959] add text to AIX section describing how to have it send responses to the all-ones address. [ISC-Bugs #19615] update the includes in dhcpctl/dhcpctl.3 to be more correct [ISC-Bugs #20676] update dhcpd.conf.5 to include the RFC numbers for DDNS - Relay no longer crashes, when DHCP packet is received over interface without any IPv4 address assigned. Also extended logging message about discarding packets with invalid hlen with information about relevant interface name. [ISC-Bugs #22409] - Relay now properly logs that packet was received over interface without global IPv6 address [ISC-Bugs #24070] - Linux Packet Filter interface improvement. sockaddr_pkt structure is used, rather than sockaddr. Packet ethertype is now forced to ETH_P_IP. [ISC-Bugs #18975] - Minor code cleanups - but note port change for #23196 [ISC-Bugs #23470] - Modify when an ignore return macro is defined to handle unsed error return warnings for more versions of gcc. [ISC-Bugs #23196] - Modify the reply handling in the server code to send to a specified port rather than to the source port for the incoming message. Sending to the source port was test code that should have been removed. The previous functionality may be restored by defining REPLY_TO_SOURCE_PORT in the includes/site.h file. We suggest you don't enable this except for testing purposes. [ISC-Bugs #22695] - Close a file descriptor in an error path. [ISC-Bugs #19368] - Tidy up variable types in validate_port. - Code cleanup: remove obsolete PROTO, KandR, INLINE and ANSI_DECL macros [ISC-Bugs #13151] - Compilation problem with gcc4.5 and omshell.c resolved. [ISC-Bugs #23831] - Client Script fixes [ISC-Bugs #23045] Typos in client/scripts/openbsd [ISC-Bugs #23565] In the client scripts add a zone id (interface id) if the domain search address is link local. [ISC-Bugs #1277] In some of the client scripts add code to handle the case of the default router information being changed without the address being changed. - Documentation cleanup [ISC-Bugs #23326] Updated References document, several man page updates - Server no longer complains about NULL pointer when configured server-identifier expression fails to evaluate. [ISC-Bugs #24547] - Convert ISC_R_INPROGRESS status to ISC_R_SUCCESS when called from other than the dispatch handler. This fixes an issue where omshell, when run from the same platform as the server, would appear to fail to connect. This is a companion to #21839. [ISC-Bugs #23592] - Enlarge the buffer size used by the Omshell code and some of the print routines to allow for greater than 60 characters or, when printing as hex strings, 20 characters. [ISC-Bugs #22743] - In Solaris 11 switch to using sockets instead of DLPI, thanks to a patch form Oracle. [ISC-Bugs #24634]. - Strict checks for content of domain-name DHCPv4 option can now be configured during compilation time. Even though RFC2132 does not allow to store more than one domain in domain-name option, such behavior is now enabled by default, but this may change some time in the future. See ACCEPT_LIST_IN_DOMAIN_NAME define in includes/site.h. [ISC-Bugs #24167] - DNS Update fix. A misconfigured server could crash during DNS update processing if the configuration included overlapping pools or multiple fixed-address entries for a single address. This issue affected both IPv4 and IPv6. The fix allows a server to detect such conditions, provides the user with extra information and recommended steps to fix the problem. If the user enables the appropriate option in site.h then server will be terminated [ISC-Bugs #23595] ! Two packets were found that cause a server to halt. The code has been updated to properly process or reject the packets as appropriate. Thanks to David Zych at University of Illinois for reporting this issue. [ISC-Bugs #24960] One CVE number for each class of packet. CVE-2011-2748 CVE-2011-2749 - Fix the code that checks for an existing DDNS transaction to cancel when removing DDNS information, so that we will continue with the processing if we have a lease even if it doesn't have an outstanding transaction. [ISC-Bugs #24682] - Add AM_MAINTAINER_MODE to configure.ac to avoid rebuilding configuration files. [ISC-Bugs #24107] - Add support for passing DDNS information to a DNS server over an IPv6 address. [ISC-Bugs #22647] - Enhanced patch for 23595 to handle IPv4 fixed addresses more cleanly. [ISC-Bugs #23595] ! Add a check for a null pointer before calling the regexec function. Without this check we could, under some circumstances, pass a null pointer to the regexec function causing it to segfault. Thanks to a report from BlueCat Networks. [ISC-Bugs #26704]. CVE: CVE-2011-4539 ! Modify the DDNS handling code. In a previous patch we added logging code to the DDNS handling. This code included a bug that caused it to attempt to dereference a NULL pointer and eventually segfault. While reviewing the code as we addressed this problem, we determined that some of the updates to the lease structures would not work as planned since the structures being updated were in the process of being freed: these updates were removed. In addition we removed an incorrect call to the DDNS removal function that could cause a failure during the removal of DDNS information from the DNS server. Thanks to Jasper Jongmans for reporting this issue. [ISC-Bugs #27078] CVE: CVE-2011-4868 - Fixed the code that checks if an address the server is planning to hand out is in a reserved range. This would appear as the server being out of addresses in pools with particular ranges. [ISC-Bugs #26498] - In the DDNS code handle error conditions more gracefully and add more logging code. The major change is to handle unexpected cancel events from the DNS client code. [ISC-Bugs #26287] - Tidy up the receive calls and eliminate the need for found_pkt. [ISC-Bugs #25066] - Add support for Infiniband over sockets to the server and relay code. We've tested this on Solaris and hope to expand support for Infiniband in the future. This patch also corrects some issues we found in the socket code. [ISC-Bugs #24245] - Add a compile time check for the presence of the noreturn attribute and use it for log_fatal if it's available. This will help code checking programs to eliminate false positives. [ISC-Bugs #27539] - Fixed many compilation problems ("set, but not used" warnings) for gcc 4.6 that may affect Ubuntu 11.10 users. [ISC-Bugs #27588] - Modify the code that determines if an outstanding DDNS request should be cancelled. This patch results in cancelling the outstanding request less often. It fixes the problem caused by a client doing a release where the TXT and PTR records weren't removed from the DNS. [ISC-BUGS #27858] - Use offsetof() instead of sizeof() to get the sizes for dhcpv6_relay_packet and dhcpv6_packet in several more places. Thanks to a report from Bruno Verstuyft and Vincent Demaertelaere of Excentis. [ISC-Bugs #27941] - Remove outdated note in the description of the bootp keyword about the option not satisfying the requirement of failover peers for denying dynamic bootp clients. [ISC-bugs #28574] - Multiple items to clean up IPv6 address processing. When processing an IA that we've seen check to see if the addresses are usable (not in use by somebody else) before handing it out. When reading in leases from the file discard expired addresses. When picking an address for a client include the IA ID in addition to the client ID to generally pick different addresses for different IAs. [ISC-Bugs #23138] [ISC-Bugs #27945] [ISC-Bugs #25586] [ISC-Bugs #27684] - Remove unnecessary checks in the lease query code and clean up several compiler issues (some dereferences of NULL and treating an int as a boolean). [ISC-Bugs #26203] - Fix the NA and PD allocation code to handle the case where a client provides a preference and the server doesn't have any addresses or prefixes available. Previously the server ignored the request with this patch it replies with a NoAddrsAvail or NoPrefixAvail response. By default the code performs according to the errata of August 2010 for RFC 3315 section 17.2.2; to enable the previous style see the section on RFC3315_PRE_ERRATA_2010_08 in includes/site.h. This option may be removed in the future. Thanks to Jiri Popelka at Red Hat for the patch. [ISC-Bugs #22676] - Fix up some issues found by static analysis. A potential memory leak and NULL dereference in omapi. The use of a boolean test instead of a bitwise test in dst. [ISC-Bugs #28941] - Rotate the lease file when running in v6 mode. Thanks to Christoph Moench-Tegeder at Astaro for the report and the first version of the patch. [ISC-Bugs #24887] - Correct code to calculate timing values in client to compare rebind value to infinity instead of renew value. Thanks to Chenda Huang from H3C Technologies Co., Limited for reporting this issue. [ISC-Bugs #29062] - Fix some issues in the code for parsing and printing options. [ISC-Bugs #22625] - properly print options that have several fields followed by an array of something for example "fIa" [ISC-Bugs #27289] - properly parse options in declarations that have several fields followed by an array of something for example "fIa" [ISC-Bugs #27296] - properly determine if we parsed a 16 or 32 bit value in evaluate_numeric_expression (extract-int). [ISC-Bugs #27314] - properly parse a zero length option from a lease file. Thanks to Marius Tomaschewski from SUSE for the report and prototype patch for this ticket as well as ticket 27289. ! Previously the server code was relaxed to allow packets with zero length client ids to be processed. Under some situations use of zero length client ids can cause the server to go into an infinite loop. As such ids are not valid according to RFC 2132 section 9.14 the server no longer accepts them. Client ids with a length of 1 are also invalid but the server still accepts them in order to minimize disruption. The restriction will likely be tightened in the future to disallow ids with a length of 1. Thanks to Markus Hietava of Codenomicon CROSS project for the finding this issue and CERT-FI for vulnerability coordination. [ISC-Bugs #29851] CVE: CVE-2012-3571 ! When attempting to convert a DUID from a client id option into a hardware address handle unexpected client ids properly. Thanks to Markus Hietava of Codenomicon CROSS project for the finding this issue and CERT-FI for vulnerability coordination. [ISC-Bugs #29852] CVE: CVE-2012-3570 ! A pair of memory leaks were found and fixed. Thanks to Glen Eustace of Massey University, New Zealand for finding this issue. [ISC-Bugs #30024] CVE: CVE-2012-3954 - Existing legacy unit-tests have been migrated to Automated Test Framework (ATF). Several new tests have been developed. To enable unit-tests, please use --with-atf in configure script. A Developer's Guide has been added. To generate it, please use make devel in the doc directory. It is currently in early stages of development, but is expected to grow in the near future. [ISC-Bugs 25901] ! An issue with the use of lease times was found and fixed. Making certain changes to the end time of an IPv6 lease could cause the server to abort. Thanks to Glen Eustace of Massey University, New Zealand for finding this issue. [ISC-Bugs #30281] CVE: CVE-2012-3955 - Update the memory leakage debug code to work with v6. [ISC-Bugs #30297] - Relax the requirements for deleting an A or AAAA record. Previously the DDNS removal code required both the A or AAAA record and the TXT record to exist. This requirement could cause problems if something interrupted the removal leaving the TXT record alone. This relaxation was codified in RFC 4703. [ISC-Bugs #30734] - Modify the failover code to handle incorrect peer names better. Previously the structure holding the name might have been freed inappropriately in some cases and not freed in other cases. [ISC-Bugs #30320] - Add a configure option, enable-secs-byteorder, to deal with clients that do the byte ordering on the secs field incorrectly. This field should be in network byte order but some clients get it wrong. When this option is enabled the server will examine the secs field and if it looks wrong (high byte non zero and low byte zero) swap the bytes. The default is disabled. This option is only useful when doing load balancing within failover. [ISC-Bugs #26108] - Fix a set of issues that were discovered via a code inspection tool. Thanks to Jiri Popelka and Tomas Hozza Red Hat for the logs and patches. [ISC-Bugs #23833] - Parsing unquoted base64 strings improved. Parser now properly handles strings that contain reserved names. [ISC-Bugs #23048] - Modify the nak_lease function to make some attempts to find a server-identifier option to use for the NAK. [ISC-Bugs #25689] - The client now passes information about the options it requested from the server to the script code via environment variables. These variables are of the form requested_=1 with the option name being the same as used in the new_* and old_* variables. [ISC-Bugs #29068] - Add support for a simple check that the server id in a request message to a failover peer matches the server id of the server. This support is enabled by editing the file includes/site.h and uncommenting the definition for SERVER_ID_CHECK. The option has several restrictions and issues - please read the comment in the site.h file before enabling it. [ISC-Bugs #31463] - Tidy up some compiler issues in the debug code. [ISC-Bugs #26460] - Move the dhcpd.conf example file to dhcpd.conf.example to avoid overwriting the dhcpd.conf file when installing a new version of ISC DHCP. The user will now need to manual copy and edit the dhcpd.conf file as desired. [ISC-Bugs #19337] - Check the status value when trying to read from a connection to see if it may have been closed. If it appears closed don't try to read from it again. This avoids a potential busy-wait like loop when the peer names are mismatched. [ISC-Bugs #31231] - Remove an unused variable to keep compilers happy. [ISC-Bugs #31983] - Modify test makefiles to be more similar to standard makefiles and comment out a currently unused test. [ISC-Bugs #32089] - Address static analysis warnings. [ISC-Bugs #33510] [ISC-Bugs #33511] - Silence benign static analysis warnings. [ISC-Bugs #33428] - Add check for 64-bit package for atf. [ISC-Bugs #32206] - Use newer auto* tool packages and turn on RFC_3542 support on Mac OS. [ISC-Bugs #26303] - Remove a variable when it isn't being used due to #ifdefs to avoid a compiler warning on Solaris using GCC. [ISC-Bugs #33032] - Add a check for too much whitespace in a config or lease file. Thanks to Paolo Pellegrino for finding the issue and a suggestion for the patch. [ISC-Bugs #33351] - Fix several problems with using OMAPI to manipulate class and subclass objects. [ISC-Bugs #27452] - Added a sleep call after killing the old client to allow time for the sockets to be cleaned. This should allow the -r option to work more consistently. [ISC-Bugs #18175] - Missing files for ISC DHCP Developer's Guide are now included in the release tarballs. To generate this documentation, please use make devel command in doc directory. [ISC-Bugs #32767] - Update client script for use with openwrt. [ISC-Bugs #29843] - Fix the socket handling for DHCPv6 clients to allow multiple instances of a client on a single machine to work properly. Previously only one client would receive the packets. Thanks to Jiri Popelka at Red Hat for the bug report and a potential patch. [ISC-Bugs #34784] - Added support for gentle shutdown after signal is received. [ISC-Bugs #32692] [ISC-Bugs 34945] - Enhance the DHCPv6 server logging to include the addresses that are assigned to the clients. [ISC-Bugs #26377] - Fix an operation in the DDNS code to be a bitwise instead of logical or. [ISC-Bugs #35138] Changes since 4.1.0 (new features) - Failover port configuration can now be left to defaults (port 647) as described in the -12 revision of the Failover draft (and assigned by IANA). Thanks in part to a patch from David Cantrell at Red Hat. - If configured, dhclient may now transmit to an anycast MAC address, rather than using a broadcast address. Thanks to a patch from David Cantrell at Red Hat. - Added client support for setting interface MTU and metric, thanks to Roy "UberLord" Marples . - Added client -D option to specify DUID type to send. - A new failover configuration parameter has been introduced for those environments where DHCP servers can be reasonably guaranteed to be "down" when the failover TCP socket is severed, "auto-partner-down". This parameter is not generally safe, and by default is disabled, so please carefully review the documentation of this parameter in the dhcpd.conf(5) manpage before determining to use it yourself. - Added a configuration function, 'gethostname()', which calls the system function of the same name and presents the results as a data expression. This function can be used to incorporate the system level hostname of the system the DHCP software is operating on in responses or queries (such as including a failover partner's hostname in a dhcp message or binding scope, or having a DHCP client send any system hostname in the host-name or FQDN options by default). - The dhcp-renewal-time and dhcp-rebinding-time options may now be configured for DHCPv4 operation and used independently of the dhcp-lease-time calculations. Invalid renew and rebinding times (e.g., greater than the determined lease time) are omitted. - Processing the DHCP to DNS server transactions in an asynchronous fashion, the DHCP server or client can now continue with its processing while awaiting replies from the DNS server. - The 'hardware [ethernet|etc] ...;' parameter in host records has been extended to attempt to match DHCPv6 clients by the last octets of a DUID-LL or DUID-LLT provided by the client. Changes since 4.1.0 (bug fixes) - Remove infinite loop in token_print_indent_concat(). - Validate the argument to the -p option. - The notorious 'option ... larger than buffer' log line, which is seen in some malformed DHCP client packets, was modified. It now logs the universe name, and does not log the length values (which are bogus corruption read from the packet anyway). It also carries a hopefully more useful explanation. - Suppress spurious warnings from configure about --datarootdir - A bug was fixed that caused the server not to answer some valid Solicit and Request packets, if the dynamic range covering any requested addresses had been deleted from configuration. - Update the code to deal with GCC 4.3. This included two sets of changes. The first is to the configuration files to include the use of AC_USE_SYSTEM_EXTENSIONS. The second is to deal with return values that were being ignored. - The db-time-format option was documented in manpages. - Using reserved leases no longer results in 'lease with binding state free not on its queue' error messages, thanks to a patch from Frode Nordahl. - Fix a build error in dhcrelay, using older versions of gcc with dhcpv6 disabled. - Two uninitialized stack structures are now memset to zero, thanks to a patch from David Cantrell at Red Hat. - Fixed a cosmetic bug where pretty-printing valid domain-search options would result in an erroneous error log message ('garbage in format string'). - A bug in DLPI packet transmission (Solaris, HP/UX) that caused the server to stop receiving packets is fixed. The same fix also means that the MAC address will no longer appear 'bogus' on DLPI-based systems. - A bug in select handling was discovered where the results of one select() call were discarded, causing the server to process the next select() call and use more system calls than required. This has been repaired - the sockets will be handled after the first return from select(), resulting in fewer system calls. - The update-conflict-detection feature would leave an FQDN updated without a DHCID (still currently implemented as a TXT RR). This would cause later expiration or release events to fail to remove the domain name. The feature now also inserts the client's up to date DHCID record, so records may safely be removed at expiration or release time. Thanks to a patch submitted by Christof Chen. - Memory leak in the load_balance_mine() function is fixed. This would leak ~20-30 octets per DHCPDISCOVER packet while failover was in use and in normal state. - Various compilation fixes have been included for the memory related DEBUG #defines in includes/site.h. - Fixed Linux client script 'unary operator expected' errors with DHCPv6. - Fixed setting hostname in Linux hosts that require hostname argument to be double-quoted. Also allow server-provided hostname to override hostnames 'localhost' and '(none)'. - Fixed failover reconnection retry code to continue to retry to reconnect rather than restarting the listener. - Compilation on Solaris with USE_SOCKETS defined in includes/site.h has been repaired. Other USE_ overrides should work better. - A check for the local flavor of IFNAMSIZ had a broken 'else' condition, that probably still resulted in the correct behaviour (but wouldn't use a larger defined value provided by the host OS). - Fixed a bug where an OMAPI socket disconnection message would not result in scheduling a failover reconnection, if the link had not negotiated a failover connect yet (e.g.: connection refused, asynch socket connect() timeouts). - A bug was fixed that caused the 'conflict-done' state to fail to be parsed in failover state records. ! A stack overflow vulnerability was fixed in dhclient that could allow remote attackers to execute arbitrary commands as root on the system, or simply terminate the client, by providing an over-long subnet-mask option. CERT VU#410676 - CVE-2009-0692 - Fixed a bug where relay agent options would never be returned when processing a DHCPINFORM. - Versions 3.0.x syntax with multiple name->code option definitions is now supported. Note that, similarly to 3.0.x, for by-code lookups only the last option definition is used. - Fixed a bug where a time difference of greater than 60 seconds between a failover pair could cause the primary to crash on contact with the secondary. Thanks to a patch from Steinar Haug. - Don't look for IPv6 interfaces on Linux when running in DHCPv4 mode. Thanks to patches from Matthew Newton and David Cantrell. - Secondary servers in a failover pair will now perform ddns removals if they had performed ddns updates on a lease that is expiring, or was released through the primary. As part of the same fix, stale binding scopes will now be removed if a change in identity of a lease's active client is detected, rather than simply if a lease is noticed to have expired (which it may have expired without a failover server noticing in some situations). - A patch supplied by David Cantrell at RedHat was applied that detects invalid calling parameters given to the ns_name_ntop() function. Specifically, it detects if the caller passed a pointer and size pair that causes the pointer to integer-wrap past zero. ! Fixed a fenceposting bug when a client had two host records configured, one using 'uid' and the other using 'hardware ethernet'. CVE-2009-1892 - Fixed the check in the dhcp_interface_signal_handler routine to verify the existence of the linked signal handler before calling it. - Both host and subnet6 configuration groups are now included whether a fixed-address6 (DHCPv6) is in use or not. Host scoped configuration takes precedence. This fixes two bugs, one where host scoped configuration would not be included from a non-fixed-address6 host record, and the equal and opposite bug where subnet6 scoped configuration would not be used when over-riding values were not present in a matching fixed-address6 host configuration. - ./configure now checks to ensure the intX_t and u_intX_t types are defined, correcting a compilation failure when using Sun's compiler. - Modified the handling of a connection to avoid releasing the omapi io object for the connection while it is still in use. One symptom from this error was a segfault when a failover secondary attempted to connect to the failover primary if their clocks were not synchronized. - Clean up to allow compilation with gcc 2.95.4 on FreeBSD. Remove an extra semi-colon from common/dns.c and moved setting a variable to NULL in server/dhcpv6.c to allow the compiler to decide that the variable was always properly set. Changes since 4.1.0b1 - A missing "else" in dhcrelay.c could have caused an interface not to be recognized. Changes since 4.1.0a2 - A cosmetic bug in DHCPDECLINE processing was fixed which caused all successful DHCPDECLINEs to be logged as "not found" rather than "abandoned". - Added configuration file examples for DHCPv6. - Some failover debugging #defines have been better defined and some high frequency messages moved to a deeper debugging symbol. - The CLTT parameter in failover is now only updated by client activity, and not by failover binding updates (taking on the peer's CLTT). - Failover BNDUPD messages are now discarded if they conflict with an update that has been transmitted, but not acknowledged. - A bug cleaning up unknown-xxx temporary option definitions was fixed. - Delayed-ack is now a compile-time option, compiled out by default. This feature is simply too experimental for right now, and causes some problems to some failover installations. We will revisit this in future releases. - The !inet_pton() call in res_mkupdrec was adjusted to '<= 0' as inet_pton returns either 1, 0, or -1. - A dhclient-script for MacOS X has been included, which enables 'dhclient -6' support. - DDNS removal routines were updated so that the DHCID is not removed until the client has been deprived of all A and AAAA records (not only the last one of either of those). This resolves a bug where dual stack clients would not be able to regain their names after either expiration event. Changes since 4.1.0a1 - Corrected list of failover state values in dhcpd man page. - Fixed a bug that caused some request types to be logged incorrectly. - Clients that sent a parameter request list containing the routers option before the subnet mask option were receiving only the latter. Fixed. - The server wasn't always sending the FQDN option when it should. - A partner-down failover server no longer emits 'peer holds all free leases' if it is able to newly-allocate one of the peer's leases. - Fixed a coredump when adding a class via OMAPI. - Check whether files are zero length before trying to parse them. - Ari Edelkind's PARANOIA patch has been included and may be compiled in via two ./configure parameters, --enable-paranoia and --enable-early-chroot. - ./configure was extended to cover many optional build features, such as failover, server tracing, debugging, and the execute() command. - There is now a default 1/4 of a second scheduled delay between delayed fsync()'s, it can be configured by the max-ack-delay configuration parameter. - A bug was fixed where the length of a hostname was miscalculated, so that hosts were given odd-looking domain names ("foo.bar.ba.example.com"). - Shared network selection should be done from the innermost relay valid link-address field, rather than the outermost. - Prefix pools are attached to shared network scopes. - Merged IA_XX related structures. - Add DHCPv6 files in configure. - A memory leak when using omapi has been fixed. - DHCPv6 vendor-class options (VSIO) are now only sent when they appear on the DHCPv6 ORO. This resolves a bug where VSIO options were placed in IA_NA encapsulated options fields. - Integrated client with stateless, temporary address and prefix delegation support. - A double-dereference in dhclient transmission of DHCPDECLINEs was repaired. - Fix handling of format code 'Z'. - Support "-1" argument in DHCPv6. - Merge DHCPv6-only "dhcrelay6" into general-purpose "dhcrelay" (use "-6" option to select DHCPv6 mode). - Fix handling of -A and -a flags in dhcrelay; it was failing to expand packet size as needed to add relay agent options. - A bug in subnet6 parsing where options contained in subnet6 clauses would not be applied to clients addressed within that network was repaired. - When configuring a "subnet {}" or "subnet6 {}" without an explicit shared-network enclosing it, the DHCP software would synthesize a shared-network to contain the subnet. However, all configuration parameters within the subnet more intuitively belong "to any client on that interface", or rather the synthesized shared-network. So, when a shared-network is synthesized, it is used to contain the configuration present inside the subnet {} clause. This means that the configuration will be valid for all clients on that network, not just those addressed out of the stated subnet. If you intended the opposite, the workaround is to explicitly configure an empty shared-network. - A bug was fixed where Information-Request processing was not sourcing configured option values. - A warning was added since the DHCPv6 processing software does not yet support class statements. - Compilation warnings on GCC 4.3 relating to bootp source address selection were repaired. - The v6 BSD socket method was updated to use a single UDP BSD socket no matter how many interfaces are involved, differentiating the interfaces the packets were received on by the interface index supplied by the OS. - The relay agent no longer listens to the All DHCP Servers Multicast address. - A bug was fixed in data_string_sprintfa() where va_start was only called once for two invocations of vsprintf() variants. - ERO (RFC 4994) server support. - Basic and partial DHCPv6 leasequery support. - Reliable DHCPv6 release (previous behavior, send release and exit, is still available with dhclient -6 -1 -r). Changes since 4.0.0 (new features) - Added DHCPv6 rapid commit support. - Added explicit parser support for zero-length DHCP options, such as rapid-commit, via format code 'Z'. - It's now possible to update the "ends" field of a lease with OMAPI. This is useful if you want not only to release a lease, but also make it available for reuse right away. Hat tip to Christof Chen. - Fixed definition of the iaaddr hash functions to use the correct functions when referencing and dereferencing memory. - Some definitions not in phase with the IANA registry were updated. - Allocated interface IDs are better controlled ('u' bit set to zero, reserved IDs avoided). - Unicast options are taken into account only for RENEWs. - NoAddrsAvail answers to SOLICITs are always ADVERTISEs even when a SOLICIT carries a rapid-commit option. - Return in place of raise an impossible condition when one tries to release an empty active lease. - Timer granularity is now 1/100s in the DHCPv6 client. - The dhclient-script was updated to create a host route for the default gateway if the supplied subnet mask for an IPv4 address was a /32. This allows the client to work in 'captive' network environments, where the operator does not want clients to crosstalk directly. - MINUS tokens should be parsable again. - Multiple (up to "delayed-ack x;" maximum) DHCPv4 packets are now queued and released in bursts after single fsync() events when the upper limit is reached or if the receiving sockets go dry. The practical upshot is that fsync-coupled server performance is now multiplicitively increased. The default delayed ack limit is 28. Thanks entirely to a patch from Christof Chen. Changes since 4.0.0 (bug fixes) - DHCP now builds on AIX. - Exit with warning when DHCPv6-specific statements are used in the config file but -6 is not specified. - Fixed "--version" flag in dhcrelay - The 'min-secs' configuration parameter's log message has been updated to be more helpful. - The warning logged when an address range doesn't fit in the subnets they were declared has been updated to be more helpful and identify the typo in configuration that created the spanning addresses. - A bug in failover pool rebalancing that caused POOLREQ message ping-pongs was repaired. - A flaw in failover pool rebalancing that could cause POOLREQ messages to be sent outside of the min-balance/max-balance scheduled intervals has been repaired. - A cosmetic bug during potential-conflict recovery that caused the peer's 'conflict-done' state message to be logged as 'unknown-state' has been repaired. It is now logged correctly. - A bug was fixed where the 'giaddr' may be used to find the client's subnet rather than its own 'ciaddr'. - A log message was introduced to clarify the situation where a failover 'address' parameter (the server's local address) did not resolve to an IPv4 address. - The minimum site code value was set to 224 in 3.1.0 to track RFC3942. This broke a lot of legacy site local configurations. The new code in place will track site local space minimum option codes and logs a warning to encourage updates and exploration of site local code migration problems. Option codes less than 128 in site local spaces remain inaccessible. - A possible relay agent option bug was repaired where random server initialization state may have been used to signal the relay agent information options sub-option code for the 'END' of the option space. - Fixes to allow code to compile and run on Solaris 9. - Fixes to allow code to compile on Mac OS X Leopard (10.5). - When server is configured with options that it overrides, a warning is issued when the configuration file is read, rather than at the time the option is overridden. This was important, because the warning was given every time the option was overridden, which could create a lot of unnecessary logging. - Fixed a compilation problems on platforms that define a value for FDDI, which conflicts with a dhcp configuration syntax token by the same name. - When a failover server suspects it has encountered a peer running a version 3.0.x failover server, a warning that the failover wire protocol is incompatible is printed. - The failover server no longer issues a floating point error if it encounters a previously undefined option code. - Fix startup error messages to report a missing "subnet6 declaration", rather than a missing "subnet declaration", when running as a DHCPv6 server. - DHCPv6 client timestamp in DUID was based on the year 1970 rather than the year 2000. - Warn when attempting to use a hardware parameter in DHCPv6. - DHCPv6 released resources are now marked as released by the client. - 'Soft' bindings have no more side-effects. Changes since 4.0.0b3 - The reverse dns name for PTR updates on IPv6 addresses has been fixed to use ip6.arpa. rather than default to in-addr.arpa and require user configuration. - dhc6_lease_destroy() and dhc6_ia_destroy() now set lease and IA pointers to NULL after freeing, to prevent subsequent accesses to freed memory. - The DHCPv6 server would not send the preference option unless the client requested it, via the ORO. This has been fixed, so the DHCPv6 server will always send the preference value if it is configured. - When addresses were passed as hints to the server in an IA, they were incorrectly handled, sometimes being treated as an error. Now the server will treat these as hints and ignore them if it cannot supply a requested address. - If the client had multiple addresses, and one expired (was not renewed by the server), the client would continue to attempt to renew the same old address over and over. Now, the client will omit any expired addresses from future Confirm, Renew, or Rebind messages. - dhclient -6 will now select renew/rebind timers based upon the longest address expiration time rather than the shortest expiration time, in order to avoid cascading renewals in the event a server elects not to extend one of multiple IAADDR leases. - The server now limits clients that request multiple addresses to one address per IA by default, which can be adjusted through the "limit-addrs-per-ia" configuration option. - The DHCPv6 client now issues fresh transaction IDs on Renew and Rebind message exchanges, rather than using the most recent ID. - The DHCPv6 server now replies to Information-Request messages. - A bug was fixed in the dhclient-script for BSDs to correctly carry error codes through some conditions. - The parsing of some options in the dhclient lease file, in particular the success DHCPv6 status-code, was fixed. - A bug was fixed that caused the DHCPv6 ORO option to be corrupted with seemingly random values. - A reference overleak in DHCPv6 shared network processing was repaired. - ./configure now autodetects local database locations rather than trying to put dhcpd.leases and dhclient.leases in /usr/local/var/db, which no one ever has. - Regression fix for bug where server advertised a IPv6 address in response to a SOLICIT but would not return the address in response to a REQUEST. - A bug was fixed where the DHCPv6 server puts the NoAddrsAvail status code in the IA_NA was fixed. The status code now appears in the root level. Changes since 4.0.0b2 - Clarified error message when lease limit exceeded - Relative time may now be used as a qualifier for 'allow' and 'deny' access control lists. These directives may be used to assist in re-addressing address pools without having to constantly reconfigure the server. Please see 'man dhcpd.conf' for more information on allow/deny 'after time' syntax. Thanks to a patch from Christof Chen. - The server will now include multiple IA_NA's and multiple IAADDRs within them, if advertised by the client. It still only seeks to allocate one new address. Changes since 4.0.0b1 - Use different paths for PID and lease files when running in DHCPv4 or DHCPv6 mode, so that servers for both protocols can be run simultaneously on a single interface. - Fixed a buffer overflow error which could have allowed a denial of service under unusual server configurations - Eliminated a spurious error message from the client - A number of bugs with the internal handling of lease state on the server have been fixed. Some of these could cause server crashes. - The peer_wants_leases() changes pulled up from 3.1.0 were corrected, 'never used' leases will no longer consistently shift between servers on every pool rebalance run. - sendmsg()/recvmsg() control buffers are now declared in such a way to ensure they are correctly aligned on all (esp. 64-bit) architectures. - The client leasing subsystem was streamlined and corrected to account more closely for changes in client link attachment selection. Changes since 4.0.0a3 - The DHCP server no longer requires a "ddns-update-style" statement, and now defaults to "none", which means DNS updates are disabled. - Log messages when failover peer names mismatch have been improved to point out the problem. - Bug where server advertised a IPv6 address in response to a SOLICIT but would not return the address in response to a REQUEST. Thanks to Dennis Kou for finding the bug. - Fixed an error causing the server to lock up on lease expiration, reported independently by Jothilingam Vasu and Dennis Kou. - Fixed a ./configure bug where compile tests were failing due to "-Werror" (unused variable) rather than the actual test failure. Lead to inconsistent and unworkable auto-configurations. - Compilation with DLPI and -Werror has been repaired. - Error in decoding IA_NA option if multiple interfaces are present fixed by Marcus Goller. - DHCPv6 server Confirm message processing has been enhanced - it no longer replies only to clients with host {} records, it now replies as directed in RFC3315 section 18.2.2 - that is, to all clients regardless of the existence of bindings. - A core dump during expired lease cleanup has been repaired. - DDNS updates state information are now stored in 'binding scopes' that follow the leases through their lifecycles. This enables DDNS teardowns on leases that are assigned and expired inbetween a server restart (the state is recovered from dhcpd.leases). Arbitrary user-specified binding scopes ('set var = "value";') are not yet supported. - Additional compilation problems on HP/UX have been repaired. Changes since 4.0.0a2 - Fix for startup where there are no IPv4 addresses on an interface. Thanks to Marcus Goller for reporting the bug. - Fixed file descriptor leak on listen failure. Thanks to Tom Clark. - Bug in server configuration parser caused server to get stuck on startup for certain bad pool declarations. Thanks to Guillaume Knispel for the bug report and fix. - Code cleaned to remove warnings reported by "gcc -Wall". - DHCPv6 is now the default. You can disable DHCPv6 support using the "--disable-dhcpv6" flag when you run the configure script. - An internal database inconsistency bug was repaired where the server would segfault if a client attempted to renew a lease that had been loaded from persistent storage. - 'request' and 'also request' syntaxes have been added to accommodate the DHCPv6 client configuration. 'send dhcp6.oro' is no longer necessary. - Bug fixed where configuration file parsing did not work with zero-length options; this made it impossible to set the rapid-commit option. - Bogus messages about host records with IPv4 fixed-addresses being of non-128-bits in length were removed. Changes since 4.0.0a1 - Bug in octal parsing fixed. Thanks to Bernd Fuhrmann for the report and fix. - Autoconf now supplies proper flags for Solaris DHCPv6 builds. - Fix for parsing error on some IPv6 addresses. - Invalid CIDR representation for IPv6 subnets or ranges now checked for when loading configuration. - Compilation on HP/UX has been repaired. The changes should generally apply to any architecture that supplies SIOCGLIFCONF but does not use 'struct lifconf' structures to pass values. - Two new operators, ~= and ~~, have been integrated to implement boolean matches by regular expression (such as may be used in class matching statements). Thanks to a patch by Alexandr S. Agranovsky, which underwent slight modification. - Fix for icmp packets on 64-bit systems (bug introduced in 4.0). - A bug was fixed in interface discovery wherein an error identifying a server-configured interface with no IPv4 addresses would SEGV. - Fixed a bug in which write_lease() might report a failure incorrectly - Added support for DHCPv6 Release messages - Added -x option to dhclient, which triggers dhclient processes to exit gracefully without releasing leases first - All binaries (client, server, relay) now change directories to / before going into daemon mode, so as not to hold $CWD open - Fixed a bug parsing DHCPv6 client-id's in host-identifier statements - Fixed a bug with the 'ddns-updates' boolean server configuration parameter, which caused the server to fail. Changes since 4.0.0-20070413 - Old (expired) leases are now cleaned. - IPv6 subnets now have support for arbitrary allocation ranges via a new 'range6' configuration directive. - An obviated option code hash lookup to find D6O_CLIENTID was removed. - Corrected some situations where variables might be used without being initialized. - Silenced several other compiler warnings. - Include the more standard sys/uio.h rather than rely upon other header files to include it (fixes a BSD 4.2 compile failure). - Duplicate dhclient-script updates for DHCPv6 to all provided scripts. - DHCPv4 I/O methods that failed to sense hardware address were corrected. - DHCPv4 is now the default (as documented) rather than DHCPv6. The default was set to DHCPv6 to facilitate ease early development, and forgotten. - Corrected a segmentation violation in DHCPv4 socket processing. - dhclient will now fork() into the background once it binds to an IPv6 address, or immediately if the -n flag is supplied. - -q is now the default behaviour on dhclient, with -d or -v enabling non-quiet (stderr logging) mode. - Fix documentation of the domain-search atom (quoted, with commas). - Document DHCPv6 options presently in the default table. - Replaced ./configure shellscripting with GNU Autoconf. Changes since 3.1.0 (NEW FEATURES) - DHCPv6 Client and Server protocol support. Use '-6' to run the daemons as v6-only. Use '-4' to run the daemons as v4-only (default. There is no support currently for both. - Server support for multiple IA_NA options, containing at most one IAADDR option. - Client support for one IA_NA option, containing any number of IAADDR options. - Server support for the DHCPv6 Information-request message. - Inappropriate unicast DHCPv6 messages sent to the server are now discarded, and this has rearchitected the IO system slightly. - The DHCPv6 server DUID defaults to type 1, is persistently stored in the leases database, and can be over-ridden (either completely, or by specifying type 1 or type 2). - The server only uses Rapid-Commit if it has been configured with the Rapid-Commit option and the client requests it. - DDNS support. We now update AAAA records in the same place we would update A records, if we have an IPv6 address. We also generate IP6.ARPA style names for PTR records if we're dealing with an IPv6 address. Both A and AAAA updates are done using the same 'fqdn.' virtual option space (although the DHCPv4 FQDN and DHCPv6 FQDN options are formatted differently, they both use the same code here). - The Linux dhclient-script attempts to set and remove assigned addresses, and to configure /etc/resolv.conf from nameserver and domain name configurations. It can be extended to configure other parameters. - Initial DHCPv6 lease support. - The IO system now tracks all local IP addresses, so that the DHCP applications (particularly the dhcrelay) can discern between what frames were transmitted to it, and what frames are being carried through it which it should not intercept. Changes since 3.1.0 (Maintenance) - A bug was repaired where MAC Address Affinity for virgin leases always mapped to the primary. Virgin leases now have an interleaved preference between primary and secondary. - A bug was repaired where MAC Address Affinity for clients with no client identifier was sometimes mishashed to the peer. Load balancing during runtime and pool rebalancing were opposing. - An assertion in lease counting relating to reserved leases was repaired. - The subnet-mask option inclusion now conforms with RFC2132 section 3.3; it will only appear prior to the routers option if it is present on the Parameter-Request-List. The subnet-mask option will also only be included by default (if it is not on the PRL) in response to DISCOVER or REQUEST messages. - The FQDN option is only supplied if the client supplied an FQDN option or if the FQDN option was explicitly requested on the PRL. - Dynamic BOOTP leases are now load balanced in failover. Changes since 3.1.0rc1 - The parse warning that 'deny dynamic bootp;' must be configured for failover protected subnets was removed. Changes since 3.1.0b2 - Failover rebalance events no longer play ping pong with round errors (moving leases between free and back to backup where there are an odd number of leases). - The 'pool' log line has been split into two messages, one before the rebalance run, and one after. - Any queued BNDACKs are transmitted before transmitting new BNDUPDs. This enforces the correct sequence of events for the remote server processing these messages. Changes since 3.1.0b1 - Fixed a bug that caused OMAPI clients to freeze when opening lease objects. - A new server config option "fqdn-reply" specifies whether the server should send out option 81 (FQDN). Defaults to "on". If set to "off", the FQDN option is not sent, even if the client requested it. This is needed because some clients misbehave otherwise. Thanks to Christof Chen at Allianz. - Allow trace output files (-tf option) to be overwritten, rather than crashing dhcpd if the file already exists - A bug was fixed that caused dhcpd to segfault if a pool was declared outside the scope of a subnet in dhcpd.conf. - Some uninitialized values were repaired in dhcpleasequery.c that caused the server to abort. - A new server config option, 'do-reverse-updates', has been added which causes the server to abstain from performing updates on PTR records. Thanks to a patch from Christof Chen at Allianz. - A bug was repaired in subencapsulation support, where spaces separated by empty spaces would not get included. - A bug in dhclient was repaired which caused it to send parameter request lists of 55 bytes in length no matter how long the declared PRL was. - 'dhcp.c(3953): non-null pointer' has been repaired. This fixes a flaw wherein the DHCPv4 server may ignore a configured server-identifier. - A flaw in failover startup sequences was repaired that sometimes left the primary DHCP server's pool rebalance schedules unscheduled. - Corrected a flaw that broke encapsulated spaces included due to presence on the parameter request list. Changes since 3.1.0a3 - Some spelling fixes. Changes since 3.1.0a2 - A bug was fixed where attempting to permit leasequeries results in a fatal internal error, "Unable to find server option 49". - A bug was fixed in dhclient rendering the textual output form of the domain-search option syntax. Changes since 3.1.0a1 - A bug in the FQDN universe that added FQDN codes to the NWIP universe's hash table was repaired. - The servers now try harder to transmit pending binding updates when entering normal state. - UPDREQ/UPDREQALL handling was optimized - it no longer dequeues and requeues all pending updates. This should reduce the number of spurious 'xid mismatch' log messages. - An option definition referencing leak was fixed, which resulted in early termination of dhclient upon the renewal event. - Some default hash table sizes were tweaked, some upwards, some downwards. 3.1.0a1's tables resulted in a reduction in default server memory use. The new selected values provide more of a zero sum (increasing the size of tables likely to be populated, decreasing the size of tables unlikely). - Lease structures appear in three separate hashes: by IP address, by UID, and by hardware address. One type of table was used for all three, and improvements to IP address hashing were applied to all three (so UID and hardware addresses were treated like 4-byte integers). There are now two types of tables, and the uid/hw hashes use functions more appropriate to their needs. - The max-lease-misbalance percentage no longer causes scheduled rebalance runs to be skipped: it still governs the schedule, but every scheduled run will attempt balance. - A segfault bug in recursive encapsulation support has been corrected. Changes since 3.0 (New Features) - A workaround for certain STSN servers that send a mangled domain-name option was introduced for dhclient. The client will now accept corrupted server responses, if they contain a valid DHCP_MESSAGE_TYPE (OFFER, ACK, or NAK). The server will continue to not accept corrupt client packets. - Support for 'reserved' (pseudo-static) and BOOTP leases via failover was introduced. - Support for adding, removing, and managing class and subclass statements via OMAPI. - The failover implementation was updated to comply with revision 12 of the protocol draft. - 'make install' now creates the initial zero-length dhcpd.leases file if one does not already exist on the system. - RFC3942 compliance, site-local option spaces start at 224 now, not 128. - The Load Balance Algorithm was misimplemented. The current implementation matches RFC 3074. - lcase() and ucase() configuration expressions have been added which adjust their arguments from upper to lower and lower to upper cases respectively. Thanks to a patch from Albert Herranz. - The dhclient 'reject ...;' statement, which rejects leases given by named server-identifiers, now permits address ranges to be specified in CIDR notation. Thanks to a patch from David Boyce. - The subnet-mask option is now supplied by default, but at lowest priority. This helps a small minority of clients that provide parameter request lists, but do not list the subnet-mask option because they were designed to interoperate with a server that behaves in this manner. - The FQDN option is similarly supplied even if it does not appear on the parameter request list, but not to the exclusion of options that do appear at the parameter request list. Up until now it had ultimate priority over the client's parameter request list. - Varying option space code and length bit widths (8/16/32) are now supported. This is a milestone in achieving RFC 3925 "VIVSO" and DHCPv6 support. - A new common (server or client) option, 'db-time-format local;', has been added which prints the local time in /var/db/dhcpd.leases rather than UTC. Thanks to a patch from Ken Lalonde. - Some patches to improve DHCP Server startup speed from Andrew Matheson have been incorporated. - Failover pairs now implement 'MAC Affinity' on leases moving from the active to free states. Leases that belonged to the failover secondary are moved to BACKUP state rather than FREE upon exiting EXPIRED state. If lease rebalancing must move leases, it tries first to move leases that belong to the peer in need. - The server no longer sends POOLREQ messages unless the pool is severely misbalanced in the peer's favor (see 'man dhcpd.conf' for more details). - Pool rebalance events no longer happen upon successfully allocating a lease. Instead, they happen on a schedule. See 'man dhcpd.conf' for the min-balance and max-balance statements for more information. - The DHCP Relay Agent Information Option / Link Selection Sub-Option is now supported. (See RFC3527 for details). - A new DDNS related server option, update-conflict-detection, has been added. If this option is enabled, dhcpd will perform normal DHCID conflict resolution (the default). If this option is disabled, it will instead trust the assigned name implicitly (removing any other bindings on that name). This option has not been made available in dhclient. - In those cases where the DHCP software manufactures an IP header (to transmit via bpf, lpf, etc), the IP TTL the software selects has been increased from 16 to 128. This is intended to match Microsoft Windows DHCP Client behaviour, to increase compatibility. - 'ignore client-updates;' now has behaviour that is different from 'deny client-updates;'. The client's request is not truly ignored, rather it is encouraged. Should this value be configured, the server updates DNS as though client-updates were set to 'deny'. That is, it enters into DNS whatever it is configured to do already, provided it is configured to. Then it sends a response to the client that lets the client believe it is performing client updates (which it will), probably for a different name. In essence, this lets the client do as it will, ignoring this aspect of their request. - Support for compressed 'domain name list' style DHCP option contents, and in particular the domain search option (#119) was added. - The DHCP LEASEQUERY protocol as defined in RFC4388 is now implemented. LEASEQUERY lets you query the DHCP server for information about a lease, using either an IP address, MAC address, or client identifier. Thanks to a patch from Justin Haddad. - DHCPD is now RFC2131 section 4.1 compliant (broadcast to all-ones ip and ethernet mac address) on the SCO platform specifically without any strange ifconfig hacks. Many thanks go to the Kroger Co. for donating the hardware and funding the development. - A new common configuration executable statement, execute(), has been added. This permits dhcpd or dhclient to execute a named external program with command line arguments specified from other configuration language. Thanks to a patch written by Mattias Ronnblom, gotten to us via Robin Breathe. - A new dhcp server option 'adaptive-lease-time-threshold' has been added which causes the server to substantially reduce lease-times if there are few (configured percentage) remaining leases. Thanks to a patch submitted from Christof Chen. - Encapsulated option spaces within encapsulated option spaces is now formally supported. Changes since 3.0.6rc1 - supersede_lease() now requeues leases in their respective hardware address hash bucket. This mirrors client identifier behaviour. Changes since 3.0.5 - Assorted fixes for broken network devices: Packet length is now determined from the IP header length field to finally calculate the UDP payload length, because some NIC drivers return more data than they actually received. - UDP packets are now stored in aligned data structures. - A logic error in omapi interface code was repaired that might result in incorrectly indicating 'up' state when any flags were set, rather than specifically the INTERFACE_REQUESTED flag. Thanks to a patch from Jochen Voss which got to us via Andrew Pollock at Debian. - A reference leak on binding scopes set by ddns updates was repaired. - A memory leak in the minires_nsendsigned() function call was repaired. Effectively, this leaked ~176 bytes per DDNS update. - In the case where an "L2" DHCP Relay Agent (one that does not set giaddr) was directly attached to the same broadcast domain as the DHCP server, the RFC3046 relay agent information option was not being returned to the relay in the server's replies. This was fixed; the dhcp server no longer requires the giaddr to reply with relay agent information. Note that this also improves compatibility with L2 devices that "intercept" DHCP packets and expect relay agent information even in unicast (renewal) replies. Thanks to a patch from Pekka Silvonen. - A bug was fixed where the BOOTP header 'sname' field had a value, the copy written to persistent storage was actually the contents of the 'file' field. - A bug was fixed where the nwip virtual option space was referencing the fqdn option's virtual option space's option cache. - Timestamp parsing errors that indicated missing "minutes" fields rather than the actually missing "seconds" fields have been repaired thanks to a patch from Kevin Steves. - A grammar error in the dhclient.8 manpage was repaired thanks to a patch from Chris Wagner. - Several spelling typos were repaired, and some cross-references to other relevant documents were included in the manpages, thanks to a patch by Andrew Pollock which got to us via Tomas Pospisek. - Some bugs were fixed in the 'emergency relay agent options hologram' which is used to retain relay agent option contents from when the client was in INIT or REBIND states. This should solve problems where relay agent options were not echoed from the server, even when giaddr was set. - dhclient now closes its descriptor to dhclient.leases prior to executing dhclient-script. Thanks to a patch from Tomas Pospisek. - The server's "by client-id" and "by hardware address" hash table lists are now sorted according to the preference to re-allocate that lease to returning clients. This should eliminate pool starvation problems arising when "INIT" clients were given new leases rather than presently active ones. Changes since 3.0.5rc1 - A bug was repaired in fixes to the dhclient, which sought to run the dhclient-script with the 'EXPIRE' state should it receive a NAK in response to a REQUEST. The client now iterates the PREINIT state after the EXPIRE state, so that interfaces that might be configured 'down' can be brought back 'up' and initialized. - DHCPINFORM handling for clients that properly set ciaddr and come to the server via a relay aget has been repaired. Changes since 3.0.4 - A warning that host statements declared within subnet or shared-network scopes are actually global has been added. - The default minimum lease time (if min-lease-time was not specified) was raised from 0 to 300. 0 is not thought to be sensible, and is known to be damaging. - Added additional fatal error sanity checks surrounding lease binding state count calculations (free/active counts used for failover pool balancing). - Some time value size fixes in 3.0.4 brought on from FreeBSD /usr/ports were misapplied to server values rather than client values. The server no longer advertises 8-byte lease-time options when on 64-bit platforms. - A bug where leases not in ACTIVE state would get billed to billed classes (classes with lease limitations) was fixed. Non-active leases OFFERed to clients are no longer billed (but billing is checked before offering). - The dhcpd.conf.5 manpage was updated in regard to the ddns-domainname configuration option - the default configuration and results should be more clear now. - If the dhclient were to receive a DHCPNAK while it was in the RENEW state (and consequently, had an active, 'bound' address and related configuration options), it would fail to 'tear down' this information before proceeding into INIT state. dhclient now iterates the dhclient- script with the 'EXPIRE' action to cause these teardowns prior to entering INIT state. Thanks to a patch from Chris Zimmerman. - The omapi.1 manpage had some formatting errors repaired thanks to a patch from Yoshihiko Sarumaru. - A few lines of code that were failover-specific were moved within #if defined() clauses so that compilation without failover could be made possible. - The log message emitted when the 'leased-address' value was not available in dhcpd.conf "executable statements" has been updated to be more helpful. Manpage information for this value has also been updated. - Abandoned or dissociated (err condition) leases now remove any related dynamic dns bindings. Thanks to a patch from Patrick Schoo. - Attempting to write a new lease file to replace a corrupt (due to encountering non-retryable errors during writing) lease file should no longer result in an infinite recursion. - Host declaration hardware addresses and client identifiers may only be configured once. dhcpd will now fail to load config files that specify multiple identifiers (previous versions would silently over-ride the value with the later configured value). - Several option codes that have been allocated since our last release have been named and documented. - Option names of the form "unknown-123" have been removed from the in- memory hash tables. In order to support options of these names that may appear in dhclient.leases or similar in previous versions, the parser will now find the new option code definition, or mock up a generic option code definition. This should result in a smooth transition from one name to the other, as the new name is used to write new output. Changes since 3.0.4rc1 - The dhcp-options.5 manpage was updated to correct indentation errors thanks to a patch from Jean Delvare. Changes since 3.0.4b3 - Some manual pages were clarified pursuant to discussion on the dhcp-server mailing list. Changes since 3.0.4b2 - Null-termination sensing for certain clients that unfortunately require it in DHCPINFORM processing was repaired. - The host-name option and a few others were moved from "X" format to "t" format to be compatible with new NULL handling functions. - DHCPINFORM processing is a little more careful about return addressing its responses, or if responding via a relay. The INFORM related messages also log the 'effective client ip address' rather than the client's supplied ciaddr (since some clients produce null ciaddrs). - The server was inappropriately sending leases to the RESET state in the event that multiple active leases were found to match a singly-identified client. This was changed to RELEASED (by accepting a different, ACTIVE binding, the client is implicitly releasing its lease). This repairs a bug wherein secondary servers in failover pairs detecting this condition move leases to RESET, and primaries refuse to accept that state transition (properly). - The memset-after-dmalloc() changes made in 3.0.4b1 have been backed out. Changes since 3.0.4b1 - Command line parsing in omshell was repaired - it no longer closes STDIN after reading one line. - The resolver library no longer closes the /etc/resolv.conf file descriptor it opened twice. - Changes to trailing NULL removal in 't' option-atoms has been rethought, it now includes 'd' (domain name) types, and tries hard not to rewind an option beyond the start of the text field it is un-terminating. Changes since 3.0.3 - A DDNS update handling function was misusing the DNS error codes, rather than the internal generic result enumeration. The result is a confusing syslog line, logging the wrong condition. - The DHCP Server was not checking pool balance in the case where it brought a non-ACTIVE lease out of storage for a client that was returning to use a lease it once had long ago, and had since expired. - Failover peers no longer bother to look for free leases to allocate when they already found the client's ACTIVE lease. DISCOVERs are load balanced whether freely-allocated or not, unless the server doubts the peer has leases to allocate. - Fixed a bug in dhcrelay agent addition code that suppressed trailing PAD options - it was suppressing only one trailing PAD option, rather than the entire block of them. ! Fixed some unlikely overlapping-region memcpy() bugs in dhcrelay agent option addition and stripping code. Added a few sanity checks. Although highly improbable, due to requiring the reception of a DHCP datagram well in excess of all known to be used physical MTU limitations, it is possible this may have been used in a stack overflow security vulnerability. Thanks to a patch from infamous42md. ! Added some sanity checks to OMAPI connection/authentication code. Although highly improbable, due to having to deliver in excess of 2^32 bytes of data via the OMAPI channel, not to mention requiring dhcpd to be able to malloc() a memory region 2^32 bytes in size, it was possible this might have resulted in a heap overflow security vulnerability. Thanks to a patch from infamous42md. - dmalloc() memset()'s the non-debug (data) portion of the allocated memory to zero. Code that memset()'s the result returned by dmalloc() to zero is redundant. These redundancies were removed. - Some type declaration corrections to u_int16_t were made in common/tr.c (Token Ring support) thanks to a patch from Jason Vas Dias at Red Hat. - A failover bug that was allowing leases that EXPIRED or were RELEASED where tsfp and tstp are identical timestamps to languish in these transitional states has been repaired. As a side effect, lease databases should be kept more consistent overall, not just for these transitional states. - If the lease db is deleted out from under the daemon, and it moves to rewrite the db, it will go ahead with the operation and move the new db into place once it detects the old db does not exist. - dhclient now ignores IRDA, SIT, and IEEE1394 network interfaces, as it is either nonsensical or (in the case of IEEE1394) is not known to support these interfaces. Thanks to Marius Gedminas and Andrew Pollock of Debian. - Some previously undocumented reasons for dhclient-script invoking has been documented in the dhclient-script.8 manpage. - Failover potential expiry calculations (TSTP) have been corrected. Results should be substantially more consistent, and proper given the constraints. - Adjusted lease state validation checks in potential-conflict, to account for possible clock skew similarly to normal state, and several previously illegal transitions were made legal (ex: active->released). - An impossible sanity check was removed from omapi/buffer.c, thanks to a patch from 'infamous42md'. - An OMAPI host/network byte order problem in lease time values has been repaired. - Several minor bugs, largely relating to treating 8-byte time values as 4-byte entities, have been repaired after careful review of the FreeBSD ports collection's patch set. Thanks to the nameless entities who have contributed to the FreeBSD ports. - When writing a trace file, the file is now created with permissions 0600, to help administrators avoid accidentally publicising sensitive config data. - The calculation of the maximum size of DHCP packets no longer includes Ethernet framing overhead. The result is that the 'Maximum Message Size' option advertised by clients, or the default value 576, is no longer reduced by 14 bytes, and instead directly reflects the IP level MTU (and the default, minimum allowed IP MTU of 576). - The special status of RELEASED/EXPIRED/RESET leases when a server is operating in partner-down was fixed. It no longer requires a lease be twice the MCLT beyond STOS to 'reallocate', and the expiry event to turn these into FREE leases without peer acknowledgement (after STOS+MCLT) has been repaired. - Compilation on older Solaris systems (lacking /usr/include/sys/int_types.h) has been repaired. - "append"ing a string onto the end of a "t" type option (such as the domain-name field) that had been improperly NULL-terminated by the DHCP server will no longer result in a truncated string containing only the option from the server, and not the expected appended value. Thanks to a patch from Jason Vas Dias at Red Hat. - File handlers on configuration state (config files and lease dbs) should be treated consistently, regardless of whether TRACING is defined or not. - The Linux build environment has had some minor improvements - better sensing of 64-bit pointer sizes (only used for establishing an icmp_id), and corrections to #if operators regarding LINUX_MAJOR should it ever move to 3.[01].x. - The server now tries harder to survive the condition where it is unable to open a new lease file to rewrite the lease state database. Changes since 3.0.3b3 - dhclient.conf documentation for interface {} was updated to reflect recent discussion on the dhcp-hackers mailing list. - In response to reports that the software does not compile on GCC 4.0.0, -Werror was removed from Makefile.conf for all platforms that used it. We will address the true problem in a future release; this is a temporary workaround. Changes since 3.0.3b2 - An error in code changes introduced in 3.0.3b2 was corrected, which caused static BOOTP clients to receive random addresses. Changes since 3.0.3b1 - A bug was fixed in BOOTPREQUEST handling code wherein stale references to host records would be left behind on leases that were not allocated to the client currently booting (eg in the case where the host was denied booting). - The dhcpd.conf.5 manpage was updated to be more clear in regards to multiple host declarations (thanks to Vincent McIntyre). 'Interim' style dynamic updates were also retouched. Changes since 3.0.2 - A bug was fixed where a server might load balance a DHCP REQUEST to its peer after already choosing not to load balance the preceding DISCOVER. The peer cannot allocate the originating server's lease. - In the case where a secondary server lost its stable storage while the primary was still in communications-interrupted, and came back online, the lease databases would not be fully transferred to the secondary. This was due to the secondary errantly sending an extra UPDREQ message when the primary made its state transition to PARTNER-DOWN known. - The package will now compile cleanly in gcc 3.3 and 3.4. As a side effect, lease structures will be 9 bytes smaller on all platforms. Thanks to Jason Vas Dias at Red Hat. - Interface discovery code in DISCOVER_UNCONFIGURED mode is now properly restricted to only detecting broadcast interfaces. Thanks to a patch from Jason Vas Dias at Red Hat. - decode_udp_ip_header was changed so that the IP address was copied out to a variable, rather than referenced by a pointer. This enforces 4-byte alignment of the 32-bit IP address value. Thanks to a patch from Dr. Peter Poeml. - An incorrect log message was corrected thanks to a patch from Dr. Peter Poeml. - A bug in DDNS was repaired, where if the server's first DDNS action was a DDNS removal rather than a DDNS update, the resolver library's retransmit timer and retry timer was set to the default, implying a 15 second timeout interval. Which is a little excessive in a synchronous, single-threaded system. In all cases, ISC DHCP should now hold fast to a 1-second timeout, trying only once. - The siaddr field was being improperly set to the server-identifier when responding to DHCP messages. RFC2131 clarified the siaddr field as meaning the 'next server in the bootstrap process', eg a tftp server. The siaddr field is now left zeroed unless next-server is configured. - mockup_lease() could have returned in an error condition (or in the condition where no fixed-address was found matching the shared network) with stale references to a host record. This is probably not a memory leak since host records generally never die anyway. - A bug was repaired where failover servers would let stale client identifiers persist on leases that were reallocated to new clients not sending an id. - Binding scopes ("set var = value;") are now removed from leases allocated by failover peers if the lease had expired. This should help reduce the number of stale binding scopes on leases. - A small memory leak was closed involving client identifiers larger than 7 bytes, and failover. - Configuring a subnet in dhcpd.conf with a subnet mask of 32 bits might cause an internal function to overflow heap. Thanks to Jason Vas Dias at Red Hat. - Some inconsistencies in treating numbers that the lexer parsed as 'NUMBER' or 'NUMBER_OR_NAME' was repaired. Hexadecimal parsing is affected, and should work better. - In several cases, parse warnings were being issued before the lexical token had been advanced to the token whose value was causing an error... causing parse warnings to claim the problem is on the wrong token. - Host declarations matching on client identifier for dynamic leases will no longer match fixed-address host declarations (this is now identical to behaviour for host records matching on hardware address). Changes since 3.0.2rc3 - A previously undocumented configuration directive, 'local-address', was documented in the dhcpd.conf manpage. Changes since 3.0.2rc2 - Two variables introduced in 3.0.2b1 were used without being initialized in the case where neither the FILE nor SNAME fields were available for overloading. This was repaired. - A heretofore believed to be impossible corner case of the option overloading implementation turned out to be possible ("Unable to sort overloaded options after 10 tries."). The implementation was reworked to consider the case of an option so large it would require more than three chunks to fit. - Many other instances of variables being used without being initialized were repaired. - An uninitialized variable in omapi_io_destroy() led to the discovery that this function may result in orphaned pointers (and hence, a memory leak). Changes since 3.0.2rc1 - allocate_lease() was rewritten to repair a bug in which the server would try to allocate an ABANDONED lease when FREE leases were available. Changes since 3.0.2b1 - Some dhcp-eval.5 manpage formatting was repaired. Changes since 3.0.1 - A bug was fixed in the server's 'option overloading' implementation, where options loaded into the 'file' and 'sname' packet fields were not aligned precisely as rfc2131 dictates. - The FreeBSD client script was changed to support the case where a domain name was not provided by the server. - A memory leak in 'omshell' per each command line parsed was repaired, thanks to a patch from Jarkko Torppa. - Log functions writing to stderr were adjusted to use the STDERR_FILENO system definition rather than '2'. This is a no-op for 90% of platforms. - One call to trace_write_packet_iov() counted the number of io vectors incorrectly, causing inconsistent tracefiles. This was fixed. - Some expression parse failure memory leaks were closed. - A host byte order problem in tracefiles was repaired. - Pools configured in DHCPD for failover possessing permission lists that previously were assumed to not include dynamic bootp clients are now a little more pessimistic. The result is, dhcpd will nag you about just about most pools that possess a 'allow' statement with no 'deny' that would definitely match a dynamic bootp client. - The 'ddns-update-style' configuration warning bit now insists that the configuration be globally scoped. - Two memory leaks in dhclient were closed thanks to a patch from Felix Farkas. - Some minor but excellently pedantic documentation errors were fixed thanks to a patch from Thomas Klausner. - Bugs in operator precedence in executable statements have been repaired once again. More legal syntaxes should be parsed legally. - Failing to initialize a tracefile for any reason if a tracefile was specified is now a fatal error. Thanks to a patch from Albert Herranz. - Corrected a bug in which the number of leases transferred as calculated by the failover primary and sent to peers in POOLRESP responses may be incorrect. This value is not believed to be used by other failover implementations, excepting perhaps as logged information. - Corrected a bug in which 'dhcp_failover_send_poolresp()' was in fact sending POOLREQ messages instead of POOLRESP mesasges. This message was essentially ignored since failover secondaries effectively do not respond to POOLREQ messages. - Type definitions for various bitwidths of integers in the sunos5-5 build of ISC DHCP have been fixed. It should compile and run more easily when built in 64-bit for this platform. - "allow known-clients;" is now a legal syntax, to avoid confusion. - If one dhcp server chooses to 'load balance' a request to its failover peer, it first checks to see if it believes said peer has a free lease to allocate before ignoring the DISCOVER. - log() was logging a work buffer, rather than the value returned by executing the statements configured by the user. In some cases, the work buffer and the intended results were the same. In some other cases, they were not. This was fixed thanks to a patch from Gunnar Fjone and directconnect.no. - Compiler warnings for some string type conversions was fixed, thanks to Andreas Gustafsson. - The netbsd build environments were simplified to one, in which -Wconversion is not used, thanks to Andreas Gustafsson. - How randomness in the backoff-cutoff dhclient configuration variable is implemented was better documented in the manpage, and the behaviour of dhclient in REQUEST timeout handling was changed to match that of DISCOVER timeout handling. - Omapi was hardened against clients that pass in null values, thanks to a patch from Mark Jason Dominus. - A bug was fixed in dhclient that kept it from doing client-side ddns updates. Thanks to a patch from Andreas Gustafsson, which underwent some modification after review by Jason Vas Dias. - Failover implementations disconnected due to the network between them (rather than one of the two shutting down) will now try to re-establish the failover connection every 5 seconds, rather than to simply try once and give up until one of them is restarted. Thanks to a patch from Ulf Ekberg from Infoblox, and field testing by Greger V. Teigre which led to an enhancement to it. - A problem that kept DHCP Failover secondaries from tearing down ddns records was repaired. Thanks to a patch from Ulf Ekberg from Infoblox. - 64bit pointer sizes are detected properly on FreeBSD now. - A bug was repaired where the DHCP server would leave stale references to host records on leases it once thought about offering to certain clients. The result would be to apply host and 'known' scopes to the wrong clients (possibly denying booting). NOTE: The 'mis-host' patch that was being circulated as a workaround is not the way this bug was fixed. If you were a victim of this bug in 3.0.1, you are cautioned to proceed carefully and see if it fixes your problem. - A bug was repaired in the server's DHCPINFORM handling, where it tried to divine the client's address from the source packet and would get it wrong. Thanks to Anshuman Singh Rawat. - A log message was introduced to help illuminate the case where the server was unable to find a lease to assign to any BOOTP client. Thanks to Daniel Baker. - A minor dhcpd.conf.5 manpage error was fixed. Changes since 3.0.1rc14 - The global variable 'cur_time' was centralized and is now uniformly of a type #defined in system-dependent headers. It had previously been defined in one of many places as a 32-bit value, and this causes mayhem on 64-bit big endian systems. It probably wasn't too healthy on little endian systems either. - A printf format string error introduced in rc14 was repaired. - AIX system-dependent header file was altered to only define NO_SNPRINTF if the condition used to #ifdef in vsnprintf in AIX' header files is false. - The Alpha/OSF system-dependent header file was altered to define NO_SNPRINTF on OS revisions older than 4.0G. - omapip/test.c had string.h added to its includes. Changes since 3.0.1rc13 ! CAN-2004-0460 - CERT VU#317350: Five stack overflow exploits were closed in logging messages with excessively long hostnames provided by the clients. It is highly probable that these could have been used by attackers to gain arbitrary root access on systems using ISC DHCP 3.0.1 release candidates 12 or 13. Special thanks to Gregory Duchemin for both finding and solving the problem. ! CAN-2004-0461 - CERT VU#654390: Once the above was closed, an opening in log_*() functions was evidenced, on some specific platforms where vsnprintf() was not believed to be available and calls were wrapped to sprintf() instead. Again, credit goes to Gregory Duchemin for finding the problem. Calls to snprintf() are now linked to a distribution-local snprintf implementation, only in those cases where the architecture is not known to provide one (see includes/cf/[arch].h). If you experience linking problems with snprintf/vsnprintf or 'isc_print_' functions, this is where to look. This vulnerability did not exist in any previously published version of ISC DHCP. - Compilation on hpux 11.11 was repaired. - 'The cross-compile bug fix' was backed out. Changes since 3.0.1rc12 - Fixed a bug in omapi lease lookup function, to form the hardware address for the hash lookup correctly, thanks to a patch from Richard Hirst. - Fixed a bug where dhcrelay was sending relayed responses back to the broadcast address, but with the source's unicast mac address. Should now conform to rfc2131 section 4.1. - Cross-compile bug fix; use $(AR) instead of ar. Thanks to Morten Brorup. - Fixed a crash bug in dhclient where dhcpd servers that do not provide renewal times results in an FPE. As a side effect, dhclient can now properly handle 0xFFFFFFFF (-1) expiry times supplied by servers. Thanks to a patch from Burt Silverman. - The 'ping timeout' debugs from rc12 were removed to -DDEBUG only, and reformatted to correct a compilation error on Solaris platforms. - A patch was applied which fixes a case where leases read from the leases database do not properly over-ride previously read leases. - dhcpctl.3 manpage was tweaked. Changes since 3.0.1rc11 - A patch from Steve Campbell was applied with minor modifications to permit reverse dns PTR record updates with values containing spaces. - A patch from Florian Lohoff was applied with some modifications to dhcrelay. It now discards packets whose hop count exceeds 10 by default, and a command-line option (-c) can be used to set this threshold. - A failover bug relating to identifying peers by name length instead of by name was fixed. - Declaring failover configs within shared-network statements should no longer result in error. - The -nw command line option to dhclient now works. - Thanks to a patch from Michael Richardson: - Some problems with long option processing have been fixed. - Some fixes to minires so that updates of KEY records will work. - contrib/ms2isc was updated by Shu-Min Chang of the Intel Corporation. see contrib/ms2isc/readme.txt for revision notes. - Dhclient no longer uses shell commands to kill another instance of itself, it sends the signal directly. Thanks to a patch from Martin Blapp. - The FreeBSD dhclient-script was changed so that a failure to write to /etc/resolv.conf does not prematurely end the script. This keeps dhclient from looping infinitely when this is the case. Thanks to a patch from Martin Blapp. - A patch from Bill Stephens was applied which resolves a problem with lease expiry times in failover configurations. - A memory leak in configuration parsing was closed thanks to a patch from Steve G. - The function which discovers interfaces will now skip non-broadcast or point-to-point interfaces, thanks to a patch from David Brownlee. - Options not yet known by the dhcpd or dhclient have had their names changed such that they do not contain # symbols, in case they should ever appear in a lease file. An option that might have been named "#144" is now "unknown-144". - Another patch from Bill Stephens which allows the ping-check timeout to be configured as 'ping-timeout'. Defaults to 1. Changes since 3.0.1rc10 - Potential buffer overflows in minires repaired. - A change to the linux client script to use /bin/bash, since /bin/sh may not be bash. - Some missing va_end cleanups thanks to a patch from Thomas Klausner. - A correction of boolean parsing syntax validation - some illegal syntaxes that worked before are now detected and produce errs, some legal syntaxes that errored before will now work properly. - Some search-and-replace errors that caused some options to change their names was repaired. - Shu-min Chang of the Intel corporation has contributed a perl script and module that converts the MS NT4 DHCP configuration to a ISC DHCP3 configuration file. - Applied the remainder of the dhcpctl memory leak patch provided by Bill Squier at ReefEdge, Inc. (groo@reefedge.com). - Missing non-optional failover peer configurations will now result in a soft error rather than a null dereference. Changes since 3.0.1rc9 - A format string was corrected to fix compiler warnings. - A number of spelling corrections were made in the man pages. - The dhclient.conf.5 man page was changed to refer to do-forward-updates rather than a configuration option that doesn't exist. - A FreeBSD-specific bug in the interface removal handling was fixed. - A Linux-specific Token Ring detection problem was fixed. - Hashes removed from as-yet-unknown agent options, having those options appear in reality before we know about them will no longer produce self-corrupting lease databases. - dhclient will use the proper port numbers now when using the -g option. - A order-of-operations bug with 2 match clauses in 1 class statement is fixed thanks to a patch from Andrew Matheson. - Compilation problems on Solaris were fixed. - Compilation problems when built with DEBUG or DEBUG_PACKET were repaired. - A fix to the dhcp ack process which makes certain group options will be included in the first DHCPOFFER message was made thanks to a patch from Ling Gou. - A few memory leaks were repaired thanks to patches from Bill Squier at ReefEdge, Inc. (groo@reefedge.com). - A fix for shared-networks that sometimes give clients options for the wrong subnets (in particular, 'option routers') was applied, thanks to Ted Lemon for the patch. - Omshell's handling of dotted octets as values was changed such that dots one after the other produce zero values in the integer string. Changes since 3.0.1rc8 - Fix a format string vulnerability in the server that could lead to a remote root compromise (discovered by NGSEC Research Team, www.ngsec.com). - Add additional support for NetBSD/sparc64. - Fix a bug in the command-line parsing of the client. Also, resolve a memory leak. - Add better support for shells other than bash in the Linux client script. - Various build fixes for modern versions of FreeBSD and Linux. - Fix a bad bounds check when printing binding state names. - Clarify documentation about fixed-address and multiple addresses. - Fix a typo in the authoritative error message. - Make a log entry when we can't write a billing class. - Use conversion targets that are the right size on all architectures. - Increment the hop count when relaying. - Log a message when lease state is changed through OMAPI. - Don't rerun the shared_network when evaluating the pool. - Fix a reversed test in the parser. - Change the type of rbuf_max. - Make FTS_LAST a manifest constant to quiet warnings. Changes since 3.0.1rc7 - Fix two compiler warnings that are generated when compiling on Solaris with gcc. These stop the build, even though they weren't actually errors, because we prefer that our builds generate no warnings. Changes since 3.0.1rc6 - Don't allow a lease that's in the EXPIRED, RELEASED or RESET state to be renewed. - Implement lease stealing for cases where the primary has fewer leases than the secondary, as called for by the standard. - Add a fudge factor to the lease expiry acceptance code, (suggested by Kevin Miller of CMU). - Fix a bug in permit_list_match that made it much too willing to say that two permit lists matched. - Unless DEBUG_DNS_UPDATES is defined, print more user-friendly (and also more compact) messages about DNS updates. - Fix a bug in generating wire-format domain names for the FQDN option. - Fix a bug where the FQDN option would not be returned if the client requested it, contrary to the standard. - On Darwin, use the FreeBSD DHCP client script. - On NetBSD/sparc, don't check for casting warnings. - Add a flag in the DHCP client to disable updating the client's A record when sending an FQDN option indicating that the client is going to update its A record. - In the client, don't attempt a DNS update until one second after configuring the new IP address, and if the update times out, keep trying until a response, positive or negative, is received from the DNS server. - Fix an uninitialized memory bug in the DHCP client. - Apply some FreeBSD-specific bug fixes suggested by Murray Stokely. - Fix a bug in ns_parserr(), where it was returning the wrong sort of result code in some cases (suggested by Ben Harris of the NetBSD project). - Fix a bug in is_identifier(), where it was checking against EOF instead of the END_OF_FILE token (also suggested by Ben Harris). - Fix a bug where if an option universe contained no options, the DHCP server could dump core (Walter Steiner). - Fix a bug in the handling of encapsulated options. - Fix a bug that prevented NWIP suboptions from being processed. - Delete the FTS_BOOTP and FTS_RESERVED states and implement them as modifier flags to the FTS_ACTIVE state, as called for in the failover protocol standard. - Fix bugs in the pool merging code that resulted in references and dereferences of null pointers. This bug had no impact unless the POINTER_DEBUG flag was defined. - In the server, added a do-forward-updates flag that can be used to disable forward updates in all cases, so that sites that want the clients to take sole responsibility for updating their A record can do so. - Make it possible to disable optimization of PTR record updates. Changes since 3.0.1rc5 - Include some new documentation and changes provided by Karl Auer. - Add a workaround for some Lexmark printers that send a double-NUL- terminated host-name option, which would break DNS updates. - Fix an off-by-one error in the MAC-address checking code for DHCPRELEASE that was added in 3.0.1rc5. - Fix a bug where client-specific information was not being discarded from the lease when it expired or was released, resulting in problems if the lease was reallocated to a different client. - If more than one allocation pool is specified that has the same set of constraints as another allocation pool on the same shared network, merge the two pools. - Don't print an error in fallback_discard, since this just causes confusion and does not appear to be helping to encourage anyone to fix this bug. Changes since 3.0.1rc4 - Fix a bug that would cause the DHCP server to spin if asked to parse a certain kind of incorrect statement. - Fix a related bug that would prevent an error from being reported in the same case. - Additional documentation. - Make sure that the hardware address matches the lease when processing a DHCPRELEASE message. Changes since 3.0.1rc3 - A minor bug fix in the arguments to a logging function call. - Documentation update for dhcpd.conf. Changes since 3.0.1rc2 - Allow the primary to send a POOLREQ message. This isn't what the current failover draft says to do, so we may have to back it out if I can't get the authors to relent, but the scheme for balancing that's specified in the current draft seems needlessly hairy, so I'm floating a trial balloon. The rc1 code did not implement the method described in the draft either. Changes since 3.0.1rc1 - Treat NXDOMAIN and NXRRSET as success when we are trying to delete a domain or RRSET. This allows the DHCP server to forget about a name it added to the DNS once it's been removed, even if the DHCP server wasn't the one that removed it. - Install defaults for failover maximum outstanding updates and maximum silent time. This prevents problems that might occur if these values were not configured. - Don't do DDNS deletes if ddns-update-style is none. - Return relay agent information options in DHCPNAK. This prevents DHCPNAK messages from being dropped when the relay agent information option contains routing information. - Fix a problem where coming up in recover wouldn't result in an update request being sent. - Add some more chatty messages when we start a recovery update and when it's done. - Fix a possible problem where some state might have been left around after the peer lost contact and regained contact about how many updates were pending. - Don't nix a lease update because of a lease conflict. This test has never (as far as I know) prevented a mistake, and it appears to cause problems with failover. - Add support in rc history code for keeping a selective history, rather than a history of all references and dereferences. This code is only used when extensive additional debugging is enabled. Changes since 3.0 - Make allocators for hash tables. As a side effect, this fixes a memory smash in the subclass allocation code. - Fix a small bug in omshell where if you try to close an object when no object is open, it dumps core. - Fix an obscure coredump that could occur on shutdown. - Fix a bug in the recording of host declaration rubouts in the lease file. - Fix two potential spins in the host deletion code. - Fix a core dump that would happen if an application tried to update a host object attribute with a null value. Changes since 3.0 Release Candidate 12 - Fix a memory leak in the evaluation code. - Fix an obscure core dump. - Print a couple of new warnings when parsing the configuration file when crucial information is left out. - Log "no free leases" as an error. - Documentation updates. Changes since 3.0 Release Candidate 11 - Always return a subnet selection option if one is sent. - Fix a warning that was being printed because an automatic data structure wasn't zeroed. - Fix some failover state transitions that were being handled incorrectly. - When supersede_lease is called on a lease whose end time has already expired, but for which a state transition has not yet been done, do a state transition. This fixes the case where if the secondary allocated a lease to a client and the lease "expired" while the secondary was in partner-down, no expiry event would actually happen, so the lease would remain active until the primary was restarted. Changes since 3.0 Release Candidate 10 - Fix a bug that was preventing released leases from changing state in failover-enabled pools. - Fix a core dump in the client identifier finder code (for host declarations). - Finish fixing a bug where bogus data would sometimes get logged to the dhclient.leases file because it was opened as descriptor 2. - Fix the Linux dhclient-script according to suggestions made by several people on the dhcp-client mailing list. - Log successful DNS updates at LOG_INFO, not LOG_ERROR. - Print an error message and refuse to run if a failover peer is defined but not referenced by any pools. - Correct a confusing error message in failover. Changes since 3.0 Release Candidate 9 - Fix a bug in lease allocation for Dynamic BOOTP clients. Changes since 3.0 Release Candidate 8 Patchlevel 2 - Fix a bug that prevented update-static-leases from working. - Document failover-state OMAPI object. - Fix a compilation error on SunOS 4. Changes since 3.0 Release Candidate 8 Patchlevel 1 - Fix a parsing bug that broke dns updates (both interim and ad-hoc). This was introduced in rc8pl1 as an unintended result of the memory leakage fixes that were in pl1. - Fix a long-standing bug where the server would record that an update had been done for a client with no name, even though no update had been done, and then when the client's lease expired the deletion of that nonexistant record would time out because the name was the null string. - Clean up the omshell, dhcpctl and omapi man pages a bit. Changes since 3.0 Release Candidate 8 - Fix a bug that could cause the DHCP server to spin if one-lease-per-client was enabled. - Fix a bug that was causing core dumps on BSD/os in the presence of malformed packets. - In partner-down state, don't restrict lease lengths to MCLT. - On the failover secondary, record the MCLT received from the primary so that if we come up without a connection to the primary we don't wind up giving out zero-length leases. - Fix some compilation problems on BSD/os. - Fix a bunch of memory leaks. - Fix a couple of bugs in the option printer. - Fix an obscure error reporting bug in the dns update code, and also make the message clearer when a key algorithm isn't supported. - Fix a bug in the tracing code that prevented trace runs that used tcp connections from being played back. - Add some additional debugging capability for catching memory leaks on exit. - Make the client release the lease correctly on shutdown. - Add some configurability to the build system. - Install omshell manual page in man1, not man8. - Craig Gwydir sent in a patch that fixes a long-standing bug in the DHCP client that could cause core dumps, but that for some reason hadn't been noticed until now. Changes since 3.0 Release Candidate 7 - Fix a bug in failover where we weren't sending updates after a transition from communications-interrupted to normal. - Handle expired/released/reset -> free transition according to the protocol specification (this works - the other way not only wasn't conformant, but also didn't work). - Add a control object in both client and server that allows either daemon to be shut down cleanly. - When writing a lease, if we run out of disk space, shut down the output file and insist on writing a new one before proceeding. - In the server, if the OMAPI listener port is occupied, keep trying to get it, rather than simply giving up and exiting. - Support fetching variables from leases and also updating and adding variables to leases via OMAPI. - If two failover peers have wildly different clocks, refuse to start doing failover. - Fix a bug in the DNS update code that could cause core dumps when running on alpha processors. - Fixed a bug in ddns updates for static lease entries, thanks to a patch from Andrey M Linkevitch. - Add support for Darwin/MacOS X - Install omshell (including new documentation). - Support DNS updates in the client (this is a very obscure feature that most DHCP client users probably will not be able to use). - Somewhat cleaner status logging in the client. - Make OMAPI key naming syntax compatible with the way keys are actually named (key names are domain names). - Fix a bug in the lease file writer. - Install DHCP ISC headers in a different place than BIND 9 ISC headers, to avoid causing trouble in BIND 9 builds. - Don't send updates for attributes on an object when the attributes haven't changed. Support deleting attributes on remote objects. - Fix a number of bugs in omshell, and add the unset and refresh statements. - Handle disconnects in OMAPI a little bit more intelligently (so that the caller gets ECONNRESET instead of EINVAL). - Fix a bunch of bugs in the handling of clients that have existing leases when the try to renew their leases while failover is operating. Changes since 3.0 Release Candidate 6 - Fix a core dump that could happen when processing a DHCPREQUEST from a client that had a host declaration that contained both a fixed-address declaration and a dhcp-client-identifier option declaration, if the client identifier was longer than nine bytes. - Fix a memory leak that could happen in certain obscure cases when using omapi to manipulate leases. - Fix some bugs and omissions in omshell. Changes since 3.0 Release Candidate 5 - Fix a bug in omapi_object_dereference that prevented objects in chains from having their reference counts decreased on dereference. - Fix a bug in omapi_object_dereference that would prevent object chains from being freed upon removal of the last reference external to the chain. - Fix a number of other memory leaks in the OMAPI protocol subsystem. - Add code in the OMAPI protocol handler to trace memory leakage. - Clean up the memory allocation/reference history printer. - Support input of dotted quads and colon-separated hex lists as attribute values in omshell. - Fix a typo in the Linux interface discovery code. - Conditionalize a piece of trace code that wasn't conditional. Changes since 3.0 Release Candidate 4 - Fix a bug that would prevent leases from being abandoned properly on DHCPDECLINE. - Fix failover peer OMAPI support. - In failover, correctly handle expiration of leases. Previously, leases would never be reclaimed because they couldn't make the transition from EXPIRED to FREE. - Fix some broken failover state transitions. - Documentation fixes. - Take out an unnecessary check in DHCP relay agent information option stashing code that was preventing REBINDING clients from rebinding. - Prevent failover peers from allocating leases in DHCPREQUEST processing if the lease belongs to the other server. - Record server version in lease file introductory comment. - Correctly report connection errors in OMAPI and failover. - Make authentication signature algorithm name comparisons in OMAPI case-insensitive. - Fix compile problem on SunOS 4.x - If a signature algorithm is not terminated with '.', terminate it so that comparisons between fully-qualified names will work consistently. - Different SIOCGIFCONF probe code, may "fix" problem on some Linux systems with the probe not working correctly. - Don't allow user to type omapi key on command line of omshell. Changes since 3.0 Release Candidate 3 - Do lease billing on startup in a way that I *think* will finally do the billing correctly - the previous method could overbill as a result of duplicate leases. - Document OMAPI server objects. Changes since 3.0 Release Candidate 2 Patchlevel 1 - Fix some problems in the DDNS update code. Thanks to Albert Herranz for figuring out the main problem. - Fix some reference counting errors on host entries that were causing core dumps. - Fix a byte-swap bug in the token ring code, thanks to Jochen Friedrich. - Fix a bug in lease billing, thanks to Jonas Bulow. Changes since 3.0 Release Candidate 2 - Change the conditions under which a DHCPRELEASE is actually committed to be consistent with lease binding states rather than using the lease end time. This may fix some problems with the billing class code. - Fix a bug where lease updates would fail on Digital Unix (and maybe others) because malloc was called with a size of zero. - Fix a core dump that happens when the DHCP server can't create its trace file. Changes since 3.0 Release Candidate 1 Patchlevel 1 - Fix the dhcp_failover_put_message to not attempt to allocate a zero-length buffer. Some versions of malloc() fail if you try to allocate a zero-length buffer, and this was causing problems on, e.g., Digital Unix. - Fix a case where the failover code was printing an error message when no error had occurred. - Fix a problem where when a server went down and back up again, the peer would not see a state transition and so would stay in the non-communicating state. - Be smart about going into recover_wait. - Fix a problem in the failover implementation where peers would fail to come into sync if interrupted in the RECOVER state. This could have been the cause of some problems people have reported recently. - Fix a problem with billing classes where they would not be unbilled when the client lease expired. - If select fails, figure out which descriptor is bad, and cut it out of the I/O loop. This prevents a potentially nasty spin. I haven't heard any report it in a while, but it came up consistently in testing. - Fix a bug in the relay agent where if you specified interfaces on the command line, it would fail. - Fix a couple of small bugs in the omapi connection object (no known user impact). - Add the missing 3.0 Beta 1 lease conversion script. - Read dhcp client script hooks if they exist, rather than only if they're executable. Changes since 3.0 Release Candidate 1 - Fix a memory smash that happens when fixed-address leases are used. ANY SITE AT WHICH FIXED-ADDRESS STATEMENTS ARE BEING USED SHOULD UPGRADE IMMEDIATELY. This has been a long-standing bug - thanks to Alvise Nobile for discovering it and helping me to find it! - Fix a small bug in binary-to-ascii, thanks to H. Peter Anvin of Transmeta. - There is a known problem with the DHCP server doing failover on Compaq Alpha systems. This patchlevel is not a release candidate because of this bug. The bug should be straightforward to fix, so a new release candidate is expected shortly. - There is a known problem in the DDNS update code that is probably a bug, and is not, as far as we know, fixed in this patchlevel. Changes since 3.0 Beta 2 Patchlevel 24 - Went over problematic failover state transitions and made them all work, so that failover should now much less fragile. - Add some dhcpctl and omapi documentation - Fix compile errors when compiling with unusual predefines. - Make Token Ring work on Linux 2.4 - Fix the Digital Unix BPF_WORDALIGN bug. - Fix some dhcp client documentation errors. - Update some parts of the README file. - Support GCC on SCO. Changes since 3.0 Beta 2 Patchlevel 23 - Fix a bug in the DNS update code where a status code was not being checked. This may have been causing core dumps. - When parsing the lease file, if a lease declaration includes a billing class statement, and the lease already has a billing class, unbill the old class. - When processing failover transactions, where acks will be deferred, process the state transition immediately. - Don't try to use the new SIOCGIFCONF buffer size detection code on Linux 2.0, which doesn't provide this functionality. - Apply a patch suggested by Tuan Uong for a problem in dlpi.c. - Fix a problem in using the which command in the configure script. - Fix a parse error in the client when setting up an omapi listener. - Document the -n and -g flags to the client. - Make sure there is always a stdin and stdout on startup. This prevents shell scripts from accidentally writing error messages into configuration files that happen to be opened as stderr. - If an interface is removed, the client will now notice that it is gone rather than spinning. This has only been tested on NetBSD. - The client will attempt to get an address even if it can't create a lease file. - Don't overwrite tracefiles. - Fix some memory allocation bugs in failover. Changes since 3.0 Beta 2 Patchlevel 22 - Apply some patches suggested by Cyrille Lefevre, who is maintaining the FreeBSD ISC DHCP Distribution port. - Fix a core dump in DHCPRELEASE. Changes since 3.0 Beta 2 Patchlevel 21 - This time for sure: fix the spin described in the changes for pl20. Changes since 3.0 Beta 2 Patchlevel 20 - Fix a problem with Linux detecting large numbers of interfaces (Ben) - Fix a memory smash in the quotify code, which was introduced in pl19. - Actually fix the spin described in the changes for pl20. The previous fix only partially fixed the problem - enough to get it past the regression test. Changes since 3.0 Beta 2 Patchlevel 19 - Fix a bug that could cause the server to abort if compiled with POINTER_DEBUG enabled. - Fix a bug that could cause the server to spin when responding to a DHCPREQUEST. - Apply Joost Mulders' suggested patches for DLPI on x86. - Support NUL characters in quoted strings. - Install unformatted man pages on SunOS. Changes since 3.0 Beta 2 Patchlevel 18 - Allow the server to be placed in partner-down state using OMAPI. (Damien Neil) - Implement omshell, which can be used to do arbitrary things to the server (in theory). (Damien Neil) - Fix a case where if a client had two different leases the server could actually dereference the second one when it hadn't been referenced, leading to memory corruption and a core dump. (James Brister) - Fix a case where a client could request the address of another client's lease, but find_lease wouldn't detect that the other client had it, and would attempt to allocate it to the client, resulting in a lease conflict message. - Fix a case where a client with more than one client identifier could be given a lease where the hardware address was correct but the client identifier was not, resulting in a lease conflict message. - Fix a problem where the server could write out a colon-separated hex list as a value for a variable, which would then not parse. The fix is to always write strings as quoted strings, with any non-printable characters quoted as octal escape sequences. So a file written the old way still won't work, but new files written this way will work. - Fix documentation for sending non-standard options. - Use unparsable names for unknown options. WARNING: this will break any configuration files that use the option-nnn convention. If you want to continue to use this convention for some options, please be sure to write a definition, like this: option option-nnn code nnn = string; You can use a descriptive name instead of option-nnn if you like. - Fix a problem where we would see a DHCPDISCOVER/DHCPOFFER/ DHCPREQUEST/DHCPACK/DHCPREQUEST/DHCPNAK sequence. This was the result of a deceptively silly bug in supersede_lease. - Fix client script exit status check, according to a fix supplied by Hermann Lauer. - Fix an endianness bug in the tracefile support, regarding ICMP messages. - Fix a bug in the client where the medium would not work correctly if it contained quoted strings. ** there was no pl17 ** Changes since 3.0 Beta 2 Patchlevel 16 - Add support for transaction tracing. This allows the state of the DHCP server on startup, and all the subsequent transactions, to be recorded in a file which can then be played back to reproduce the behaviour of the DHCP server. This can be used to quickly reproduce bugs that cause core dumps or corruption, and also for tracking down memory leaks. - Incorporate some bug fixes provided by Joost Mulders for the DLPI package which should clear up problems people have been seeing on Solaris. - Fix bugs in the handling of options stored as linked lists (agent options, fqdn options and nwip options) that could cause memory corruption and core dumps. - Fix a bug in DHCPREQUEST handling that resulted in DHCPNAK messages not being send in some cases when they were needed. - Make the lease structure somewhat more compact. - Make initial failover startup *much* faster. This was researched and implemented by Damien Neil. - Add a --version flag to all executables, which prints the program name and version to standard output. - Don't rewrite the lease file every thousand leases. - A bug in nit.c for older SunOS machines was fixed by a patch sent in by Takeshi Hagiwara. - Fix a memory corruption bug in the DHCP client. - Lots of documentation updates. - Add a feature allowing environment variables to be passed to the DHCP client script on the DHCP client command line. - Fix client medium support, which had been broken for some time. - Fix a bug in the DHCP client initial startup backoff interval, which would cause two DHCPDISCOVERS to be sent back-to-back on startup. Changes since 3.0 Beta 2 Patchlevel 15 - Some documentation tweaks. - Maybe fix a problem in the DLPI code. - Fix some error code space inconsistencies in ddns update code. - Support relay agents that intercept unicast DHCP messages to stuff agent options into them. - Fix a small memory leak in the relay agent option support code. - Fix a core dump that would occur if a packet was sent with no options. Changes since 3.0 Beta 2 Patchlevel 14 - Finish fixing a long-standing bug in the agent options code. This was causing core dumps and failing to operate correctly - in particular, agent option stashing wasn't working. Agent option stashing should now be working, meaning that agent options can be used in class statements to control address allocation. - Fix up documentation. - Fix a couple of small memory leaks that would have added up significantly in a high-demand situation. - Add a log-facility configuration parameter. - Fix a compile error on some older operating systems. - Add the ability in the client to execute certain statements before transmitting packets to the server. Handy for debugging; not much practical use otherwise. - Don't send faked-out giaddr when renewing or bound - again, useful for debugging. Changes since 3.0 Beta 2 Patchlevel 13 - Fixed a problem where the fqdn decoder would sometimes try to store an option with an (unsigned) negative length, resulting in a core dump on some systems. - Work around the Win98 DHCP client, which NUL-terminates the FQDN option. - Work around Win98 and Win2k clients that will claim they want to do the update even when they don't have any way to do it. - Fix some log messages that can be printed when failover is operating that were not printing enough information. - It was possible for a DHCPDISCOVER to get an allocation even when the state machine said the server shouldn't be responding. - Don't load balance DHCPREQUESTs from clients in RENEWING and REBINDING, since in RENEWING, if we heard it, it's for us, and in REBINDING, the client wouldn't have got to REBINDING if its primary were answering. - When we get a bogus state lease binding state transition, don't do the transition. Changes since 3.0 Beta 2 Patchlevel 12 - Fixed a couple of silly compile errors. Changes since 3.0 Beta 2 Patchlevel 11 - Albert Herranz tracked down and fixed a subtle bug in the base64 decoder that would prevent any key with an 'x' in its base64 representation from working correctly. - Thanks to Chris Cheney and Michael Sanders, we have a fix for the hang that they both spotted in the DHCP server - when one-lease-per-client was set, the code to release the "other" lease could spin. - Fix a problem with alignment of the input buffer in bpf in cases where two packets arrive in the same bpf read. - Fix a problem where the relay agent would crash if you specified an interface name on the command line. - Add the ability to conditionalize client behaviour based on the client state. - Add support for the FQDN option, and added support for a new way of doing ddns updates (ddns update style interim) that allows more than one DHCP server to update the DNS for the same network(s). This was implemented by Damien Neil with some additional functionality added by Ted Lemon. - Damien added a "log" statement, so that the configuration file can be made to log debugging information and other information. - Fixed a bug that caused option buffers not to be terminated with an end option. - Fixed a long-standing bug in the support for option spaces where the options are stored as an ordered list rather than in a hash table, which could theoretically result in memory pool corruption. - Prevent hardware declarations with no actual hardware address from being written as something unparsable, and behave correctly in the face of a null hardware address on input. - Allow key names to be FQDNs, and qualify the algorithm name if it is specified unqualified. - Modify the DDNS update code so that it never prints the "resolver failed" message, but instead says *why* the resolver failed. - Officially support the subnet selection option, which now has an RFC. - Fix a build bug on MacOS X. - Allow administrator to disable ping checking. - Clean up dhcpd.conf documentation and add more information about how it works. Changes since 3.0 Beta 2 Patchlevel 10 - Fix a bug introduced during debugging (!) and accidentally committed to CVS. Changes since 3.0 Beta 2 Patchlevel 9 - Fix DHCP client handling of vendor encapsulated options. - Fix a bug in the handling of relay agent information options introduced in patchlevel 9. - Stash agent options on client leases by default, and use the stashed options at renewal time. - Add the ability to test the client's binding state in the client configuration language. - Fix a core dump in the DNS update code. - Fix some expression evaluation bugs that were causing updates to be done when no client hostname was received. - Fix expression evaluation debugging printfs. - Teach pretty_print_option to print options in option spaces other than the DHCP option space. - Add a warning message if the RHS of a not is not boolean. - Never select for more than a day, because some implementations of select will just fail if the timeout is too long (!). - Fix a case where a DHCPDISCOVER from an unknown network would be silently dropped. - Fix a bug where if a client requested an IP address for which a different client had the lease, the DHCP server would reallocate it anyway. - Fix the DNS update code so that if the client changes its name, the DNS will be correctly updated. Changes since 3.0 Beta 2 Patchlevel 8 - Oops, there was another subtle math error in the header-length bounds-checking. Changes since 3.0 Beta 2 Patchlevel 7 - Oops, forgot to byte-swap udp header length before bounds-checking it. Changes since 3.0 Beta 2 Patchlevel 6 - Fix a possible DoS attack where a client could cause the checksummer to dump core. This was a read, not a write, so it shouldn't be possible to exploit it any further than that. - Implement client- and server-side support for using the Client FQDN option. - Support for other option spaces in the client has been added. This means that it is now possible to define a vendor option space on the client, request options in that space from the server (which must define the same option space), and then use those options in the client. This also allows NWIP and Client FQDN options to be used meaningfully. - Add object initializer support. This means that objects can now be initialized to something other than all-zeros when allocated, which makes, e.g., the interface object support code a little more robust. - Fix an off-by-one bug in the host stuffer. This was causing host deletes not the work, and may also have been causing OMAPI connections to get dropped. Thanks to James Brister for tracking this one down! - Fixed a core dump in the interface discovery code that is triggered when there is no subnet declaration for an interface, but the server decides to continue running. Thanks to Shane Kerr for tracking down and fixing this problem. Changes since 3.0 Beta 2 Patchlevel 5 - Fix a bug in the recent enhancement to the interface discovery code to support arbitrary-length interface lists. - Support NUL-terminated DHCP options when initializing client-script environment. - Fix suffix operator. - Fix NetWare/IP option parsing. - Better error/status checking in dhcpctl initialization and omapi connection code. - Fix a potential memory smash in dhcpctl code. - Fix SunOS4 and (maybe) Ultrix builds. - Fix a bug where a certain sort of incoming packet could cause a core dump on Solaris (and probably elsewhere). - Add some more safety checks in error logging code. - Add support for ISC_R_INCOMPLETE in OMAPI protocol connection code. - Fix relay agent so that if an interface is specified on the command line, the relay agent does not dump core. - Fix class matching so that match if can be combined with match or spawn with. - Do not allow spurious leases in the lease database to introduce potentially bogus leases into the in-memory database. - Fix a byte-order problem in the client hardware address type code for OMAPI. - Be slightly less picky about what sort of hardware addresses OMAPI can install in host declarations. Changes since 3.0 Beta 2 Patchlevel 4 - Incorporated Peter Marschall's proposed change to array/record parsing, which allows things like the slp-agent option to be encoded correctly. Thanks very much to Peter for taking the initiative to do this, and for doing such a careful job of it (e.g., updating the comments)! - Added an encoding for the slp-agent option. :') - Fixed SunOS 4 build. Thanks to Robert Elz for responding to my request for help on this with patches! - Incorporated a change that should fix a problem reported by Philippe Jumelle where when the network connection between two servers is lost, they never reconnect. - Fix client script files other than that for NetBSD to actually use make_resolv_conf as documented in the manual page. - Fix a bug in the packet handling code that could result in a core dump. - Fix a bug in the bootp code where responses on the local net would be sent to the wrong MAC address. Thanks to Jerry Schave for catching this one. Changes since 3.0 Beta 2 Patchlevel 3 - In the DHCP client, execute client statements prior to using the values of options, so that the client configuration can overridden, e.g., the lease renewal time. - Fix a reference counting error that would result in very reproducible failures in updates, as well as occasional core dumps, if a zone was declared without a key. - Fix some Linux 2.0 compilation problems. - Fix a bug in scope evaluation during execution of "on" statements that caused values not to be recorded on leases. - If the dhcp-max-message-size option is specified in scope, and the client didn't send this option, use the one specified in scope to determine the maximum size of the response. Changes since 3.0 Beta 2 Patchlevel 2 - Fix a case where spawning subclasses were being allocated incorrectly, resulting in a core dump. - Fix a case where the DHCP server might inappropriately NAK a RENEWING client. - Fix a place dhcprequest() where static leases could leak. - Include memory.h in omapip_p.h so that we don't get warnings about using memcmp(). Changes since 3.0 Beta 2 Patchlevel 1 - Notice when SIOCFIGCONF returns more data than fit in the buffer - allocate a larger buffer, and retry. Thanks to Greg Fausak for pointing this out. - In the server, if no interfaces were configured, report an error and exit. - Don't ever record a state of 'startup'. - Don't try to evaluate the local failover binding address if none was specified. Thanks to Joseph Breu for finding this. dhcp-4.4.1/server/000755 000765 000024 00000000000 13243313034 014221 5ustar00tmarkstaff000000 000000 dhcp-4.4.1/tests/000755 000765 000024 00000000000 13243313033 014054 5ustar00tmarkstaff000000 000000 dhcp-4.4.1/tests/DHCPv6/000755 000765 000024 00000000000 13243313033 015046 5ustar00tmarkstaff000000 000000 dhcp-4.4.1/tests/failover/000755 000765 000024 00000000000 13243313033 015663 5ustar00tmarkstaff000000 000000 dhcp-4.4.1/tests/HOWTO-unit-test000644 000765 000024 00000004722 13243301226 016637 0ustar00tmarkstaff000000 000000 Introduction ------------ That is only a brief overview of tests in ISC DHCP. For more thorough description, see ISC DHCP Developer's Guide. You can generate it, by having Doxygen installed and doing: cd doc make devel and then opening doc/html/index.html Tests Overview -------------- In DHCP, a unit test exercises a particular piece of code in isolation. There is a separate unit test per module or API. Each unit test lives in a directory beneath the code it is designed to exercise. So, we (will eventually) have: server/tests/ client/tests/ common/tests/ dhcpctl/tests/ And so on. We are using ATF (Automated Test Framework) as a framework to run our unit tests. See ISC DHCP Developer's Guide for much more thorough description of unit-test and ATF framework in general. Installing ATF -------------- ATF sources can be downloaded from https://github.com/jmmv/kyua. ATF must be configured, compiled and then installed to be available during the DHCP configure procedure. Please follow INSTALL file supplied with ATF sources (it's essentially the typical ./configure && make && make install procedure). Beginning with ATF version 0.16, it is necessary to include the following options --enable-tools and --disable-shared when configuring ATF: configure --prefix= --enable-tools --disable-shared ISC DHCP unittests will run with ATF releases upto 0.19. Beginning with ATF 0.20, the tools, atf-run and atf-report required by ISC DHCP, were deprecated and are no longer included with ATF. Running Unit Tests ------------------ In order to run the unit tests for DHCP, enable ATF support during configure: $ ./configure --with-atf{=} where is the path into which ATF was installed. This would be same value used for --prefix when ATF was configured (default is /usr/local). And then build ISC_DHCP with: $ make Finally build and run unit tests with: $ make check This will traverse the source tree running the unit tests in each unit test subdirectory. Note that if one or more tests in a unit test subdirectory fail the make process will stop. To run all of the tests regardless of outcome, use: $ make -k check You can run a single test by going to the appropriate test directory and invoking the test directly: $ cd server/tests $ make check Adding a New Unit Test ---------------------- See ISC DHCP Developer's Guide. Adding a New Unit Test Program ------------------------------ See ISC DHCP Developer's Guide. dhcp-4.4.1/tests/Makefile.am000644 000765 000024 00000002245 13243313027 016116 0ustar00tmarkstaff000000 000000 EXTRA_DIST = failover/dhcp-1.cf failover/dhcp-2.cf failover/new-failover \ DHCPv6/000-badmsgtype.pl \ DHCPv6/010-solicit-noclientid.pl \ DHCPv6/011-solicit-serverid.pl \ DHCPv6/020-advertise-mcast.pl \ DHCPv6/030-request-noclientid.pl \ DHCPv6/031-request-noserverid.pl \ DHCPv6/032-request-badduid.pl \ DHCPv6/110-information-request-ia_na.pl \ DHCPv6/111-information-request-ia_ta.pl \ DHCPv6/112-badduid.pl \ DHCPv6/210-solicit-nohost.pl \ DHCPv6/211-solicit-opt-in-na.pl \ DHCPv6/212-solicit-opt-in-na-norapidcommit.pl \ DHCPv6/280-release-nohost.pl \ DHCPv6/281-release-bad-address.pl \ DHCPv6/282-release-no-address.pl \ DHCPv6/283-release.pl \ DHCPv6/290-decline-nohost.pl \ DHCPv6/291-decline-bad-address.pl \ DHCPv6/292-decline-no-address.pl \ DHCPv6/293-decline.pl \ DHCPv6/README DHCPv6/dhcp_client.pm \ DHCPv6/stubcli-opt-in-na.pl DHCPv6/stubcli.pl \ DHCPv6/test-a.conf DHCPv6/test-b.conf \ HOWTO-unit-test \ unit_test_sample.c AM_CPPFLAGS = -I.. check_LIBRARIES = libt_api.a libt_api_a_SOURCES = t_api.c t_api_dhcp.c dhcp-4.4.1/tests/Makefile.am.in000644 000765 000024 00000002250 13243301226 016515 0ustar00tmarkstaff000000 000000 EXTRA_DIST = failover/dhcp-1.cf failover/dhcp-2.cf failover/new-failover \ DHCPv6/000-badmsgtype.pl \ DHCPv6/010-solicit-noclientid.pl \ DHCPv6/011-solicit-serverid.pl \ DHCPv6/020-advertise-mcast.pl \ DHCPv6/030-request-noclientid.pl \ DHCPv6/031-request-noserverid.pl \ DHCPv6/032-request-badduid.pl \ DHCPv6/110-information-request-ia_na.pl \ DHCPv6/111-information-request-ia_ta.pl \ DHCPv6/112-badduid.pl \ DHCPv6/210-solicit-nohost.pl \ DHCPv6/211-solicit-opt-in-na.pl \ DHCPv6/212-solicit-opt-in-na-norapidcommit.pl \ DHCPv6/280-release-nohost.pl \ DHCPv6/281-release-bad-address.pl \ DHCPv6/282-release-no-address.pl \ DHCPv6/283-release.pl \ DHCPv6/290-decline-nohost.pl \ DHCPv6/291-decline-bad-address.pl \ DHCPv6/292-decline-no-address.pl \ DHCPv6/293-decline.pl \ DHCPv6/README DHCPv6/dhcp_client.pm \ DHCPv6/stubcli-opt-in-na.pl DHCPv6/stubcli.pl \ DHCPv6/test-a.conf DHCPv6/test-b.conf \ HOWTO-unit-test \ unit_test_sample.c AM_CPPFLAGS = -I.. check_@DHLIBS@ = libt_api.@A@ libt_api_@A@_SOURCES = t_api.c t_api_dhcp.c dhcp-4.4.1/tests/t_api.c000644 000765 000024 00000040051 13243301226 015315 0ustar00tmarkstaff000000 000000 /* * Copyright (C) 2004-2017 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2003 Internet Software Consortium. * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* $Id: t_api.c,v 1.4 2009/10/28 04:12:30 sar Exp $ */ /*! \file */ /* * This test API framework is taken from the BIND 9 code. It has been * modified to remove the DNS-specific parts, and the BIND-specific * parts. * * The DNS-specific parts are now wrapped with the DNS_SUPPORT macro, * and the BIND-specific parts are now wrapped with the BIND_SUPPORT * macro. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DNS_SUPPORT #include #include #endif /* DNS_SUPPORT */ #ifndef BIND_SUPPORT #define isc_commandline_parse getopt #define isc_commandline_argument optarg #define isc_commandline_option optopt #endif /* BIND_SUPPORT */ #include "t_api.h" #include "cdefs.h" static const char *Usage = "\t-a : run all tests\n" "\t-b : chdir to dir before running tests" "\t-c : use specified config file\n" "\t-d : set debug level to debug_level\n" "\t-h : print test info\n" "\t-u : print usage info\n" "\t-n : run specified test name\n" "\t-t : run specified test number\n" "\t-x : don't execute tests in a subproc\n" "\t-q : use 'timeout' as the timeout value\n"; /*!< * -a --> run all tests * -b dir --> chdir to dir before running tests * -c config --> use config file 'config' * -d --> turn on api debugging * -h --> print out available test names * -u --> print usage info * -n name --> run test named name * -tn --> run test n * -x --> don't execute testcases in a subproc * -q timeout --> use 'timeout' as the timeout value */ #define T_MAXTESTS 256 /*% must be 0 mod 8 */ #define T_MAXENV 256 #define T_DEFAULT_CONFIG "t_config" #define T_BUFSIZ 256 #define T_BIGBUF 4096 #define T_TCTOUT 60 int T_debug; int T_timeout; pid_t T_pid; static const char * T_config; static char T_tvec[T_MAXTESTS / 8]; static char * T_env[T_MAXENV + 1]; static char T_buf[T_BIGBUF]; static char * T_dir; static int t_initconf(const char *path); static int t_dumpconf(const char *path); static int t_putinfo(const char *key, const char *info); static char * t_getdate(char *buf, size_t buflen); static void printhelp(void); static void printusage(void); static int T_int; static void t_sighandler(int sig) { T_int = sig; } int main(int argc, char **argv) { int c; int tnum; int subprocs; pid_t deadpid; int status; int len; isc_boolean_t first; testspec_t *pts; struct sigaction sa; #ifdef BIND_SUPPORT isc_mem_debugging = ISC_MEM_DEBUGRECORD; #endif /* BIND_SUPPORT */ first = ISC_TRUE; subprocs = 1; T_timeout = T_TCTOUT; /* * -a option is now default. */ memset(T_tvec, 0xffff, sizeof(T_tvec)); /* * Parse args. */ while ((c = isc_commandline_parse(argc, argv, ":at:c:d:n:huxq:b:")) != -1) { if (c == 'a') { /* * Flag all tests to be run. */ memset(T_tvec, 0xffff, sizeof(T_tvec)); } else if (c == 'b') { T_dir = isc_commandline_argument; } else if (c == 't') { tnum = atoi(isc_commandline_argument); if ((tnum > 0) && (tnum < T_MAXTESTS)) { if (first) { /* * Turn off effect of -a default * and allow multiple -t and -n * options. */ memset(T_tvec, 0, sizeof(T_tvec)); first = ISC_FALSE; } /* * Flag test tnum to be run. */ tnum -= 1; T_tvec[tnum / 8] |= (0x01 << (tnum % 8)); } } else if (c == 'c') { T_config = isc_commandline_argument; } else if (c == 'd') { T_debug = atoi(isc_commandline_argument); } else if (c == 'n') { pts = &T_testlist[0]; tnum = 0; while (pts->pfv != NULL) { if (! strcmp(pts->func_name, isc_commandline_argument)) { if (first) { memset(T_tvec, 0, sizeof(T_tvec)); first = ISC_FALSE; } T_tvec[tnum/8] |= (0x01 << (tnum%8)); break; } ++pts; ++tnum; } if (pts->pfv == NULL) { fprintf(stderr, "no such test %s\n", isc_commandline_argument); exit(1); } } else if (c == 'h') { printhelp(); exit(0); } else if (c == 'u') { printusage(); exit(0); } else if (c == 'x') { subprocs = 0; } else if (c == 'q') { T_timeout = atoi(isc_commandline_argument); } else if (c == ':') { fprintf(stderr, "Option -%c requires an argument\n", isc_commandline_option); exit(1); } else if (c == '?') { fprintf(stderr, "Unrecognized option -%c\n", isc_commandline_option); exit(1); } } /* * Set cwd. */ if (T_dir != NULL) IGNORE_RET (chdir(T_dir)); /* * We don't want buffered output. */ (void)setbuf(stdout, NULL); (void)setbuf(stderr, NULL); /* * Setup signals. */ sa.sa_flags = 0; sigfillset(&sa.sa_mask); #ifdef SIGCHLD /* * This is mostly here for NetBSD's pthread implementation, until * people catch up to the latest unproven-pthread package. */ sa.sa_handler = SIG_DFL; (void)sigaction(SIGCHLD, &sa, NULL); #endif sa.sa_handler = t_sighandler; (void)sigaction(SIGINT, &sa, NULL); (void)sigaction(SIGALRM, &sa, NULL); /* * Output start stanza to journal. */ snprintf(T_buf, sizeof(T_buf), "%s:", argv[0]); len = strlen(T_buf); (void) t_getdate(T_buf + len, T_BIGBUF - len); t_putinfo("S", T_buf); /* * Setup the test environment using the config file. */ if (T_config == NULL) T_config = T_DEFAULT_CONFIG; t_initconf(T_config); if (T_debug) t_dumpconf(T_config); /* * Now invoke all the test cases. */ tnum = 0; pts = &T_testlist[0]; while (*pts->pfv != NULL) { if (T_tvec[tnum / 8] & (0x01 << (tnum % 8))) { if (subprocs) { T_pid = fork(); if (T_pid == 0) { (*pts->pfv)(); exit(0); } else if (T_pid > 0) { T_int = 0; sa.sa_handler = t_sighandler; (void)sigaction(SIGALRM, &sa, NULL); alarm(T_timeout); deadpid = (pid_t) -1; while (deadpid != T_pid) { deadpid = waitpid(T_pid, &status, 0); if (deadpid == T_pid) { if (WIFSIGNALED(status)) { if (WTERMSIG(status) == SIGTERM) t_info( "the test case timed out\n"); else t_info( "the test case caused exception %d\n", WTERMSIG(status)); t_result(T_UNRESOLVED); } } else if ((deadpid == -1) && (errno == EINTR) && T_int) { kill(T_pid, SIGTERM); T_int = 0; } else if ((deadpid == -1) && ((errno == ECHILD) || (errno == ESRCH))) break; } alarm(0); sa.sa_handler = SIG_IGN; (void)sigaction(SIGALRM, &sa, NULL); } else { t_info("fork failed, errno == %d\n", errno); t_result(T_UNRESOLVED); } } else { (*pts->pfv)(); } } ++pts; ++tnum; } snprintf(T_buf, sizeof(T_buf), "%s:", argv[0]); len = strlen(T_buf); (void) t_getdate(T_buf + len, T_BIGBUF - len); t_putinfo("E", T_buf); return(0); } void t_assert(const char *component, int anum, int class, const char *what, ...) { va_list args; (void)printf("T:%s:%d:%s\n", component, anum, class == T_REQUIRED ? "A" : "C"); /* * Format text to a buffer. */ va_start(args, what); (void)vsnprintf(T_buf, sizeof(T_buf), what, args); va_end(args); (void)t_putinfo("A", T_buf); (void)printf("\n"); } void t_info(const char *format, ...) { va_list args; va_start(args, format); (void) vsnprintf(T_buf, sizeof(T_buf), format, args); va_end(args); (void) t_putinfo("I", T_buf); } void t_result(int result) { const char *p; switch (result) { case T_PASS: p = "PASS"; break; case T_FAIL: p = "FAIL"; break; case T_UNRESOLVED: p = "UNRESOLVED"; break; case T_UNSUPPORTED: p = "UNSUPPORTED"; break; case T_UNTESTED: p = "UNTESTED"; break; case T_THREADONLY: p = "THREADONLY"; break; default: p = "UNKNOWN"; break; } printf("R:%s\n", p); } char * t_getenv(const char *name) { char *n; char **p; size_t len; n = NULL; if (name && *name) { p = &T_env[0]; len = strlen(name); while (*p != NULL) { if (strncmp(*p, name, len) == 0) { if ( *(*p + len) == '=') { n = *p + len + 1; break; } } ++p; } } return(n); } /* * * Read in the config file at path, initializing T_env. * * note: no format checking for now ... * */ static int t_initconf(const char *path) { int n; int rval; char **p; FILE *fp; rval = -1; fp = fopen(path, "r"); if (fp != NULL) { n = 0; p = &T_env[0]; while (n < T_MAXENV) { *p = t_fgetbs(fp); if (*p == NULL) break; if ((**p == '#') || (strchr(*p, '=') == NULL)) { /* * Skip comments and other junk. */ (void)free(*p); continue; } ++p; ++n; } (void)fclose(fp); rval = 0; } return (rval); } /* * * Dump T_env to stdout. * */ static int t_dumpconf(const char *path) { int rval; char **p; FILE *fp; rval = -1; fp = fopen(path, "r"); if (fp != NULL) { p = &T_env[0]; while (*p != NULL) { printf("C:%s\n", *p); ++p; } (void) fclose(fp); rval = 0; } return(rval); } /* * * Read a newline or EOF terminated string from fp. * On success: * return a malloc'd buf containing the string with * the newline converted to a '\0'. * On error: * return NULL. * * Caller is responsible for freeing buf. * */ char * t_fgetbs(FILE *fp) { int c; size_t n; size_t size; char *buf, *old; char *p; n = 0; size = T_BUFSIZ; old = buf = (char *) malloc(T_BUFSIZ * sizeof(char)); if (buf != NULL) { p = buf; while ((c = fgetc(fp)) != EOF) { if (c == '\n') break; *p++ = c; ++n; if ( n >= size ) { size += T_BUFSIZ; buf = (char *)realloc(buf, size * sizeof(char)); if (buf == NULL) goto err; old = buf; p = buf + n; } } *p = '\0'; if (c == EOF && n == 0U) { free(buf); return (NULL); } return (buf); } else { err: if (old != NULL) free(old); fprintf(stderr, "malloc/realloc failed %d", errno); return(NULL); } } /* * * Put info to log, using key. * For now, just dump it out. * Later format into pretty lines. * */ static int t_putinfo(const char *key, const char *info) { int rval; /* * For now. */ rval = printf("%s:%s", key, info); return(rval); } static char * t_getdate(char *buf, size_t buflen) { size_t n; time_t t; struct tm *p; t = time(NULL); p = localtime(&t); n = strftime(buf, buflen - 1, "%A %d %B %H:%M:%S %Y\n", p); return(n != 0U ? buf : NULL); } /* * Some generally used utilities. */ #ifdef DNS_SUPPORT struct dns_errormap { isc_result_t result; const char *text; } dns_errormap[] = { { ISC_R_SUCCESS, "ISC_R_SUCCESS" }, { ISC_R_EXISTS, "ISC_R_EXISTS" }, { ISC_R_NOTFOUND, "ISC_R_NOTFOUND" }, { ISC_R_NOSPACE, "ISC_R_NOSPACE" }, { ISC_R_UNEXPECTED, "ISC_R_UNEXPECTED" }, { ISC_R_UNEXPECTEDEND, "ISC_R_UNEXPECTEDEND" }, { ISC_R_RANGE, "ISC_R_RANGE" }, { DNS_R_LABELTOOLONG, "DNS_R_LABELTOOLONG" }, { DNS_R_BADESCAPE, "DNS_R_BADESCAPE" }, /* { DNS_R_BADBITSTRING, "DNS_R_BADBITSTRING" }, */ /* { DNS_R_BITSTRINGTOOLONG, "DNS_R_BITSTRINGTOOLONG"}, */ { DNS_R_EMPTYLABEL, "DNS_R_EMPTYLABEL" }, { DNS_R_BADDOTTEDQUAD, "DNS_R_BADDOTTEDQUAD" }, { DNS_R_UNKNOWN, "DNS_R_UNKNOWN" }, { DNS_R_BADLABELTYPE, "DNS_R_BADLABELTYPE" }, { DNS_R_BADPOINTER, "DNS_R_BADPOINTER" }, { DNS_R_TOOMANYHOPS, "DNS_R_TOOMANYHOPS" }, { DNS_R_DISALLOWED, "DNS_R_DISALLOWED" }, { DNS_R_EXTRATOKEN, "DNS_R_EXTRATOKEN" }, { DNS_R_EXTRADATA, "DNS_R_EXTRADATA" }, { DNS_R_TEXTTOOLONG, "DNS_R_TEXTTOOLONG" }, { DNS_R_SYNTAX, "DNS_R_SYNTAX" }, { DNS_R_BADCKSUM, "DNS_R_BADCKSUM" }, { DNS_R_BADAAAA, "DNS_R_BADAAAA" }, { DNS_R_NOOWNER, "DNS_R_NOOWNER" }, { DNS_R_NOTTL, "DNS_R_NOTTL" }, { DNS_R_BADCLASS, "DNS_R_BADCLASS" }, { DNS_R_PARTIALMATCH, "DNS_R_PARTIALMATCH" }, { DNS_R_NEWORIGIN, "DNS_R_NEWORIGIN" }, { DNS_R_UNCHANGED, "DNS_R_UNCHANGED" }, { DNS_R_BADTTL, "DNS_R_BADTTL" }, { DNS_R_NOREDATA, "DNS_R_NOREDATA" }, { DNS_R_CONTINUE, "DNS_R_CONTINUE" }, { DNS_R_DELEGATION, "DNS_R_DELEGATION" }, { DNS_R_GLUE, "DNS_R_GLUE" }, { DNS_R_DNAME, "DNS_R_DNAME" }, { DNS_R_CNAME, "DNS_R_CNAME" }, { DNS_R_NXDOMAIN, "DNS_R_NXDOMAIN" }, { DNS_R_NXRRSET, "DNS_R_NXRRSET" }, { DNS_R_BADDB, "DNS_R_BADDB" }, { DNS_R_ZONECUT, "DNS_R_ZONECUT" }, { DNS_R_NOTZONETOP, "DNS_R_NOTZONETOP" }, { DNS_R_SEENINCLUDE, "DNS_R_SEENINCLUDE" }, { DNS_R_SINGLETON, "DNS_R_SINGLETON" }, { (isc_result_t)0, NULL } }; isc_result_t t_dns_result_fromtext(char *name) { isc_result_t result; struct dns_errormap *pmap; result = ISC_R_UNEXPECTED; pmap = dns_errormap; while (pmap->text != NULL) { if (strcmp(name, pmap->text) == 0) break; ++pmap; } if (pmap->text != NULL) result = pmap->result; return (result); } struct dc_method_map { unsigned int dc_method; const char *text; } dc_method_map[] = { { DNS_COMPRESS_NONE, "DNS_COMPRESS_NONE" }, { DNS_COMPRESS_GLOBAL14, "DNS_COMPRESS_GLOBAL14" }, { DNS_COMPRESS_ALL, "DNS_COMPRESS_ALL" }, { 0, NULL } }; unsigned int t_dc_method_fromtext(char *name) { unsigned int dc_method; struct dc_method_map *pmap; dc_method = DNS_COMPRESS_NONE; pmap = dc_method_map; while (pmap->text != NULL) { if (strcmp(name, pmap->text) == 0) break; ++pmap; } if (pmap->text != NULL) dc_method = pmap->dc_method; return(dc_method); } #endif /* DNS_SUPPORT */ int t_bustline(char *line, char **toks) { int cnt; char *p; cnt = 0; if (line && *line) { while ((p = strtok(line, "\t")) && (cnt < T_MAXTOKS)) { *toks++ = p; line = NULL; ++cnt; } } return(cnt); } static void printhelp(void) { int cnt; testspec_t *pts; cnt = 1; pts = &T_testlist[0]; printf("Available tests:\n"); while (pts->func_name) { printf("\t%d\t%s\n", cnt, pts->func_name); ++pts; ++cnt; } } static void printusage(void) { printf("Usage:\n%s\n", Usage); } int t_eval(const char *filename, int (*func)(char **), int nargs) { FILE *fp; char *p; int line; int cnt; int result; int nfails; int nprobs; int npass; char *tokens[T_MAXTOKS + 1]; npass = 0; nfails = 0; nprobs = 0; fp = fopen(filename, "r"); if (fp != NULL) { line = 0; while ((p = t_fgetbs(fp)) != NULL) { ++line; /* * Skip comment lines. */ if ((isspace((unsigned char)*p)) || (*p == '#')) { (void)free(p); continue; } cnt = t_bustline(p, tokens); if (cnt == nargs) { result = func(tokens); switch (result) { case T_PASS: ++npass; break; case T_FAIL: ++nfails; break; case T_UNTESTED: break; default: ++nprobs; break; } } else { t_info("bad format in %s at line %d\n", filename, line); ++nprobs; } (void)free(p); } (void)fclose(fp); } else { t_info("Missing datafile %s\n", filename); ++nprobs; } result = T_UNRESOLVED; if (nfails == 0 && nprobs == 0 && npass > 0) result = T_PASS; else if (nfails > 0) result = T_FAIL; else if (npass == 0) result = T_UNTESTED; return (result); } dhcp-4.4.1/tests/t_api_dhcp.c000644 000765 000024 00000001471 13243301226 016316 0ustar00tmarkstaff000000 000000 /* * We have to have a number of symbols defined in order to build a * DHCP program. */ #include #include "dhcpd.h" void bootp(struct packet *packet) { } void dhcp(struct packet *packet) { } void dhcpv6(struct packet *packet) { } isc_result_t dhcpv4o6_handler(omapi_object_t *h) { return ISC_R_NOTIMPLEMENTED; } isc_result_t dhcp_set_control_state(control_object_state_t old, control_object_state_t new) { return ISC_R_NOTIMPLEMENTED; } int check_collection(struct packet *p, struct lease *l, struct collection *c) { return 0; } void classify (struct packet *p, struct class *c) { } isc_result_t find_class(struct class **class, const char *c1, const char *c2, int i) { return ISC_R_NOTFOUND; } int parse_allow_deny(struct option_cache **oc, struct parse *p, int i) { return 0; } dhcp-4.4.1/tests/unit_test_sample.c000644 000765 000024 00000000646 13243301226 017606 0ustar00tmarkstaff000000 000000 #include "config.h" #include "t_api.h" static void foo(void); /* * T_testlist is a list of tests that are invoked. */ testspec_t T_testlist[] = { { foo, "sample test" }, { NULL, NULL } }; static void foo(void) { static const char *test_desc = "this is an example test, for no actual module"; t_assert("sample", 1, T_REQUIRED, test_desc); /* ... */ /* Test code would go here. */ t_result(T_PASS); } dhcp-4.4.1/tests/unittest.sh.in000755 000765 000024 00000004725 13243301226 016710 0ustar00tmarkstaff000000 000000 #!/bin/sh # # Copyright (C) 2015-2018 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/. # # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH # REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, # INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM # LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. # # Script used to execute unit tests described by the Atffile in the current # directory. It exits with return value of atf-run, which will be 0 if all # tests passed, non-zero otherwise. # # Add configured path to ATF tools, atf-run and atf-report PATH="@ATF_BIN@:${PATH}" export PATH ATFRUN=`type atf-run 2>/dev/null | awk '{print $3}'` KYUA=`type kyua 2>/dev/null | awk '{print $3}'` # colors if not outputting to a dumb terminal and stdout is a tty if test "$TERM" != dumb && { test -t 1; } 2>/dev/null; then \ red='\033[0;31m' green='\033[0;32m' noclr='\033[0m' # if echo supports -e, we must use it to set colors # (output will be "" if its supported) if [ -z "`echo -e`" ] then dash_e="-e" fi fi; header="====================================================" status=0 if [ -n "@UNITTESTS@" -a -x "$ATFRUN" -a -f Atffile ] then # run the tests echo "Running unit tests..." atf-run > atf.out status=$? # set color based on success/failure if [ $status -eq 0 ] then color=$green else color=$red fi # spit out the test report # We print everything upto the summary in # "no color". Print the summary in our # result color. cat atf.out | atf-report | while read line do cnt=`echo $line | grep -c "Summary"` if [ $cnt -eq 1 ] then echo $dash_e $color$header fi echo $line; done echo $dash_e $header$noclr # clean up unless there were test failures if [ $status -eq 0 ] then rm -f atf.out fi elif [ -n "@UNITTESTS@" -a -x "$KYUA" -a -f Kyuafile ] then echo "Running unit tests..." kyua --logfile kyua.log test status=$? kyua report if [ $status -eq 0 ] then rm -f kyua.log fi fi exit $status dhcp-4.4.1/tests/failover/dhcp-1.cf000644 000765 000024 00000007312 13243301226 017255 0ustar00tmarkstaff000000 000000 authoritative; class "even" { match if ((extract-int (suffix (pick-first-value (option dhcp-client-identifier, hardware), 1), 8) % 2) = 0); } class "odd" { match if ((extract-int (suffix (pick-first-value (option dhcp-client-identifier, hardware), 1), 8) % 2) = 1); } lease-file-name "dhcp-1.leases"; pid-file-name "dhcp-1.pid"; ddns-update-style none; local-port 50002; remote-port 50003; omapi-port 50004; omapi-key FOO; default-lease-time 600; max-lease-time 600; failover peer "foo" { primary; address 10.0.0.1; port 51000; peer address 10.0.0.1; peer port 51001; max-response-delay 60; max-unacked-updates 10; mclt 100; hba ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff: 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00; load balance max seconds 2; } option space SUNW; option SUNW.root-mount-options code 1 = text; option SUNW.root-server-ip-address code 2 = ip-address; option SUNW.root-server-hostname code 3 = text; option SUNW.root-path-name code 4 = text; option SUNW.swap-server-ip-address code 5 = ip-address; option SUNW.swap-file-path code 6 = text; option SUNW.boot-file-path code 7 = text; option SUNW.posix-timezone-string code 8 = text; option SUNW.boot-read-size code 9 = unsigned integer 16; option SUNW.install-server-ip-address code 10 = ip-address; option SUNW.install-server-hostname code 11 = text; option SUNW.install-path code 12 = text; option SUNW.sysid-config-file-server code 13 = text; option SUNW.JumpStart-server code 14 = text; option SUNW.terminal-name code 15 = text; class "solaris-i86pc" { match if option vendor-class-identifier = "SUNW.i86pc"; vendor-option-space SUNW; option SUNW.boot-file-path "/platform/i86pc/kernel/unix"; option SUNW.root-path-name "/export/root/i86pc"; } class "solaris-sun4u" { match if option vendor-class-identifier = "SUNW.Ultra-5_10"; vendor-option-space SUNW; option SUNW.install-path "/export/2/s581_sparc"; option SUNW.root-path-name "/export/2/s581_sparc/Solaris_8/Tools/Boot"; } option domain-name "connectathon.org."; option SUNW.root-server-ip-address 172.16.113.1; option SUNW.root-server-hostname "sundhcp-server17-1"; class "sniffer" { match if option host-name = "sniffer"; } key FOO { algorithm HMAC-MD5.SIG-ALG.REG.INT; secret ABCD; } zone BISBEE.EXAMPLE.COM. { primary 127.0.0.1; key FOO; } zone 17.127.10.in-addr.arpa. { primary 127.0.0.1; key FOO; } zone 0.0.10.in-addr.arpa. { primary 127.0.0.1; key FOO; } subnet 204.152.186.128 netmask 255.255.255.192 { not authoritative; } shared-network LOCAL { subnet 127.0.0.0 netmask 255.255.255.0 { } subnet 10.0.2.0 netmask 255.255.255.0 { pool { deny dynamic bootp clients; failover peer "foo"; range 10.0.2.100 10.0.2.200; } } } shared-network NET-187 { subnet 204.152.187.0 netmask 255.255.255.0 { } subnet 205.140.116.224 netmask 255.255.255.248 { } subnet 10.0.1.0 netmask 255.255.255.0 { pool { deny dynamic bootp clients; failover peer "foo"; range 10.0.1.10 10.0.1.200; } } } subnet 10.0.0.0 netmask 255.255.255.0 { pool { deny dynamic bootp clients; allow members of "even"; option impress-servers 10.0.0.0; failover peer "foo"; range 10.0.0.10 10.0.0.54; range 10.0.0.100 10.0.0.149; } pool { deny dynamic bootp clients; allow members of "odd"; failover peer "foo"; option impress-servers 10.0.0.1; range 10.0.0.55 10.0.0.99; range 10.0.0.150 10.0.0.200; } pool { deny dynamic bootp clients; allow members of "sniffer"; failover peer "foo"; range 10.0.0.9 10.0.0.9; } option routers 10.0.0.1; option domain-name "bisbee.example.com"; option domain-name-servers 10.0.0.1; } dhcp-4.4.1/tests/failover/dhcp-2.cf000644 000765 000024 00000007114 13243301226 017256 0ustar00tmarkstaff000000 000000 authoritative; class "even" { match if ((extract-int (suffix (pick-first-value (option dhcp-client-identifier, hardware), 1), 8) % 2) = 0); } class "odd" { match if ((extract-int (suffix (pick-first-value (option dhcp-client-identifier, hardware), 1), 8) % 2) = 1); } lease-file-name "dhcp-2.leases"; pid-file-name "dhcp-2.pid"; local-port 50000; remote-port 50001; omapi-port 50005; ddns-update-style none; default-lease-time 600; max-lease-time 600; failover peer "foo" { secondary; address 10.0.0.1; port 51001; peer address 10.0.0.1; peer port 51000; max-response-delay 60; max-unacked-updates 10; mclt 100; load balance max seconds 2; } option space SUNW; option SUNW.root-mount-options code 1 = text; option SUNW.root-server-ip-address code 2 = ip-address; option SUNW.root-server-hostname code 3 = text; option SUNW.root-path-name code 4 = text; option SUNW.swap-server-ip-address code 5 = ip-address; option SUNW.swap-file-path code 6 = text; option SUNW.boot-file-path code 7 = text; option SUNW.posix-timezone-string code 8 = text; option SUNW.boot-read-size code 9 = unsigned integer 16; option SUNW.install-server-ip-address code 10 = ip-address; option SUNW.install-server-hostname code 11 = text; option SUNW.install-path code 12 = text; option SUNW.sysid-config-file-server code 13 = text; option SUNW.JumpStart-server code 14 = text; option SUNW.terminal-name code 15 = text; class "solaris-i86pc" { match if option vendor-class-identifier = "SUNW.i86pc"; vendor-option-space SUNW; option SUNW.boot-file-path "/platform/i86pc/kernel/unix"; option SUNW.root-path-name "/export/root/i86pc"; } class "solaris-sun4u" { match if option vendor-class-identifier = "SUNW.Ultra-5_10"; vendor-option-space SUNW; option SUNW.install-path "/export/2/s581_sparc"; option SUNW.root-path-name "/export/2/s581_sparc/Solaris_8/Tools/Boot"; } option domain-name "connectathon.org."; option SUNW.root-server-ip-address 172.16.113.1; option SUNW.root-server-hostname "sundhcp-server17-1"; class "sniffer" { match if option host-name = "sniffer"; } key FOO { algorithm HMAC-MD5.SIG-ALG.REG.INT; secret ABCD; } zone BISBEE.EXAMPLE.COM. { primary 127.0.0.1; key FOO; } zone 17.127.10.in-addr.arpa. { primary 127.0.0.1; key FOO; } zone 0.0.10.in-addr.arpa. { primary 127.0.0.1; key FOO; } subnet 204.152.186.128 netmask 255.255.255.192 { not authoritative; } shared-network LOCAL { subnet 127.0.0.0 netmask 255.255.255.0 { } subnet 10.0.2.0 netmask 255.255.255.0 { pool { deny dynamic bootp clients; failover peer "foo"; range 10.0.2.100 10.0.2.200; } } } shared-network 187-NET { subnet 204.152.187.0 netmask 255.255.255.0 { } subnet 205.140.116.224 netmask 255.255.255.248 { } subnet 10.0.1.0 netmask 255.255.255.0 { pool { deny dynamic bootp clients; failover peer "foo"; range 10.0.1.10 10.0.1.200; } } } subnet 10.0.0.0 netmask 255.255.255.0 { pool { deny dynamic bootp clients; allow members of "even"; option impress-servers 10.0.0.0; failover peer "foo"; range 10.0.0.10 10.0.0.54; range 10.0.0.100 10.0.0.149; } pool { deny dynamic bootp clients; allow members of "odd"; failover peer "foo"; option impress-servers 10.0.0.1; range 10.0.0.55 10.0.0.99; range 10.0.0.150 10.0.0.200; } pool { deny dynamic bootp clients; allow members of "sniffer"; failover peer "foo"; range 10.0.0.9 10.0.0.9; } option routers 10.0.0.1; option domain-name "bisbee.example.com"; option domain-name-servers 10.0.0.1; } dhcp-4.4.1/tests/failover/new-failover000755 000765 000024 00000001031 13243301226 020203 0ustar00tmarkstaff000000 000000 #!/bin/sh foo=10 while [ $foo -lt 100 ]; do cat >>dhcp-1.leases <<~ lease 10.0.0.$foo { starts 4 2001/05/01 02:19:16; ends 5 2021/05/03 02:29:16; binding state active; next binding state free; hardware ethernet 08:00:46:06:6c:23; uid "test-$foo"; } ~ foo=`expr $foo + 1` cat >>dhcp-2.leases <<~ lease 10.0.0.$foo { starts 4 2001/04/19 02:19:16; ends 5 2021/04/21 02:29:16; binding state active; next binding state free; hardware ethernet 08:00:46:06:6c:23; uid "test-$foo"; } ~ foo=`expr $foo + 1` done dhcp-4.4.1/tests/DHCPv6/000-badmsgtype.pl000644 000765 000024 00000010361 13243301226 020041 0ustar00tmarkstaff000000 000000 #! /usr/bin/perl -w # Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # Internet Systems Consortium, Inc. # 950 Charter Street # Redwood City, CA 94063 # # https://www.isc.org/ use strict; use English; use Time::HiRes qw( sleep ); use Socket; use Socket6; use IO::Select; use dhcp_client; # well-known addresses my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; my $All_DHCP_Servers = "ff05::1:3"; # ports my $client_port = 546; my $server_port = 547; # create a new Solicit message my $msg = dhcp_client::msg->new(255); # add the Client Identifier (required by DOCSIS and RFC 3315) $msg->add_option($OPT_CLIENTID, dhcp_client::duid()); # add Elapsed Time, set to 0 on first packet (required by RFC 3315) $msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); # add IA_NA for each interface (required by DOCSIS and RFC 3315) # XXX: should this be a single interface only? my $iaid = 0; foreach my $iface (dhcp_client::iface()) { my $option_data = pack("NNN", ++$iaid, 0, 0); $msg->add_option($OPT_IA_NA, $option_data); } # add Options Request (required by DOCSIS, recommended by RFC 3315) my @oro = ( ); $msg->add_option($OPT_ORO, pack("n*", @oro)); # timeout parameters my $IRT = $SOL_TIMEOUT; my $MRT = $SOL_MAX_RT; my $MRC = 1; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 my $MRD = 0; # sleep a random amount of time between 0 and 1 second, required by RFC 3315 # XXX: this seems pretty stupid sleep(rand($SOL_MAX_DELAY)); my $RT; my $count = 0; my $mrd_end_time; if ($MRD != 0) { $mrd_end_time = time() + $MRD; } my $reply_msg; do { # create our socket, and send our bogus packet socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); my $packet = $msg->packet(); my $send_ret = send(SOCK, $packet, 0, pack_sockaddr_in6($server_port, $addr)); if (not defined($send_ret)) { printf STDERR "Error \%d sending DHCPv6 message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } elsif ($send_ret != length($packet)) { print STDERR "Unable to send entire DHCPv6 message.\n"; exit(1); } $count++; my $RAND = rand(0.2) - 0.1; if (defined $RT) { $RT = 2*$RT + $RAND*$RT; if (($RT > $MRT) && ($MRT != 0)) { $RT = $MRT + $RAND*$RT; } } else { $RT = $IRT + $RAND*$IRT; } my $rt_end_time = time() + $RT; if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { $rt_end_time = $mrd_end_time; } for (;;) { my $timeout = $rt_end_time - time(); if ($timeout < 0) { last; } my @ready = IO::Select->new(\*SOCK)->can_read($timeout); if (@ready) { my $reply; my $recv_ret; $recv_ret = recv(SOCK, $reply, 1500, 0); if (not defined $recv_ret) { printf STDERR "Error \%d receiving DHCPv6 " . "message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } $reply_msg = dhcp_client::msg::decode($reply); if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || ($reply_msg->{msg_type} == $MSG_REPLY)) { last; } } } } until ($reply_msg || (($MRC != 0) && ($count > $MRC)) || (defined($mrd_end_time) && ($mrd_end_time > time()))); unless ($reply_msg) { if (($MRC != 0) && ($count >= $MRC)) { print STDERR "No reply after maximum retransmission count.\n"; } else { print STDERR "No reply after maximum retransmission duration.\n"; } } if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { print "Got DHCPv6 Reply message.\n"; exit(0); } #$Data::Dumper::Useqq = 1; #print Dumper($msg), "\n"; #print Dumper($msg->packet()), "\n"; # #print "packet length: ", length($msg->packet()), "\n"; dhcp-4.4.1/tests/DHCPv6/010-solicit-noclientid.pl000644 000765 000024 00000013335 13243301226 021503 0ustar00tmarkstaff000000 000000 #! /usr/bin/perl -w # Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # Internet Systems Consortium, Inc. # 950 Charter Street # Redwood City, CA 94063 # # https://www.isc.org/ use strict; use English; use Time::HiRes qw( sleep ); use Socket; use Socket6; use IO::Select; use dhcp_client; # XXX: for debugging use Data::Dumper; # not-yet-standard options my $OPT_TIME_SERVERS = 40; my $OPT_TIME_OFFSET = 41; # DOCSIS sub-options my $DOCSIS_OPT_ORO = 1; # 2 to 31 are reserved my $DOCSIS_OPT_TFTP_SERVERS = 32; my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; my $DOCSIS_OPT_SYSLOG_SERVERS = 34; my $DOCSIS_OPT_TLV5 = 35; my $DOCSIS_OPT_DEVICE_ID = 36; my $DOCSIS_OPT_CCC = 37; my $DOCSIS_OPT_VERS = 38; # well-known addresses my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; my $All_DHCP_Servers = "ff05::1:3"; # ports my $client_port = 546; my $server_port = 547; # create a new Solicit message my $msg = dhcp_client::msg->new($MSG_SOLICIT); # do NOT add the Client Identifier (required by DOCSIS and RFC 3315) #$msg->add_option($OPT_CLIENTID, dhcp_client::duid()); # add Elapsed Time, set to 0 on first packet (required by RFC 3315) $msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); # add IA_NA for each interface (required by DOCSIS and RFC 3315) # XXX: should this be a single interface only? my $iaid = 0; foreach my $iface (dhcp_client::iface()) { my $option_data = pack("NNN", ++$iaid, 0, 0); $msg->add_option($OPT_IA_NA, $option_data); } # add Reconfigure Accept (required by DOCSIS) $msg->add_option($OPT_RECONF_ACCEPT, ""); # add Options Request (required by DOCSIS, recommended by RFC 3315) my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET ); $msg->add_option($OPT_ORO, pack("n*", @oro)); # add Vendor Class option (required by DOCSIS) $msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0"); # add Vendor-specific Information Option option (required by DOCSIS) my $vsio = pack("N", 4491); # ORO (required by DOCSIS) my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS ); $vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro); # TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS) my $tlv5_data = "\x01\x02\x03\x0"; $vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data; # DOCSIS Device (required by DOCSIS) my $docsis_device_id = dhcp_client::mac_addr_binary(); $vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id)); $vsio .= $docsis_device_id; $msg->add_option($OPT_VENDOR_OPTS, $vsio); # add Rapid Commit option (required by DOCSIS) $msg->add_option($OPT_RAPID_COMMIT, ""); # timeout parameters, from DOCSIS my $IRT = $SOL_TIMEOUT; my $MRT = $SOL_MAX_RT; my $MRC = 1; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 my $MRD = 0; # sleep a random amount of time between 0 and 1 second, required by RFC 3315 # XXX: this seems pretty stupid sleep(rand($SOL_MAX_DELAY)); my $RT; my $count = 0; my $mrd_end_time; if ($MRD != 0) { $mrd_end_time = time() + $MRD; } my $reply_msg; do { # create our socket, and send our Solicit socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); my $packet = $msg->packet(); my $send_ret = send(SOCK, $packet, 0, pack_sockaddr_in6($server_port, $addr)); if (not defined($send_ret)) { printf STDERR "Error \%d sending DHCPv6 Solicit message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } elsif ($send_ret != length($packet)) { print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; exit(1); } $count++; my $RAND = rand(0.2) - 0.1; if (defined $RT) { $RT = 2*$RT + $RAND*$RT; if (($RT > $MRT) && ($MRT != 0)) { $RT = $MRT + $RAND*$RT; } } else { $RT = $IRT + $RAND*$IRT; } my $rt_end_time = time() + $RT; if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { $rt_end_time = $mrd_end_time; } for (;;) { my $timeout = $rt_end_time - time(); if ($timeout < 0) { # print STDERR "Timeout waiting for DHCPv6 Advertise ", # "or Reply message.\n"; last; } my @ready = IO::Select->new(\*SOCK)->can_read($timeout); if (@ready) { my $reply; my $recv_ret; $recv_ret = recv(SOCK, $reply, 1500, 0); if (not defined $recv_ret) { printf STDERR "Error \%d receiving DHCPv6 " . "message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } $reply_msg = dhcp_client::msg::decode($reply); if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || ($reply_msg->{msg_type} == $MSG_REPLY)) { last; } } } } until ($reply_msg || (($MRC != 0) && ($count > $MRC)) || (defined($mrd_end_time) && ($mrd_end_time > time()))); unless ($reply_msg) { if (($MRC != 0) && ($count >= $MRC)) { print STDERR "No reply after maximum retransmission count.\n"; } else { print STDERR "No reply after maximum retransmission duration.\n"; } } if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { print "Got DHCPv6 Reply message.\n"; exit(0); } #$Data::Dumper::Useqq = 1; #print Dumper($msg), "\n"; #print Dumper($msg->packet()), "\n"; # #print "packet length: ", length($msg->packet()), "\n"; dhcp-4.4.1/tests/DHCPv6/011-solicit-serverid.pl000644 000765 000024 00000013515 13243301226 021177 0ustar00tmarkstaff000000 000000 #! /usr/bin/perl -w # Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # Internet Systems Consortium, Inc. # 950 Charter Street # Redwood City, CA 94063 # # https://www.isc.org/ use strict; use English; use Time::HiRes qw( sleep ); use Socket; use Socket6; use IO::Select; use dhcp_client; # XXX: for debugging use Data::Dumper; # not-yet-standard options my $OPT_TIME_SERVERS = 40; my $OPT_TIME_OFFSET = 41; # DOCSIS sub-options my $DOCSIS_OPT_ORO = 1; # 2 to 31 are reserved my $DOCSIS_OPT_TFTP_SERVERS = 32; my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; my $DOCSIS_OPT_SYSLOG_SERVERS = 34; my $DOCSIS_OPT_TLV5 = 35; my $DOCSIS_OPT_DEVICE_ID = 36; my $DOCSIS_OPT_CCC = 37; my $DOCSIS_OPT_VERS = 38; # well-known addresses my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; my $All_DHCP_Servers = "ff05::1:3"; # ports my $client_port = 546; my $server_port = 547; # create a new Solicit message my $msg = dhcp_client::msg->new($MSG_SOLICIT); # add the Client Identifier (required by DOCSIS and RFC 3315) $msg->add_option($OPT_CLIENTID, dhcp_client::duid()); # add the Server Identifier (NOT ALLOWED by DOCSIS and RFC 3315) $msg->add_option($OPT_SERVERID, dhcp_client::duid()); # add Elapsed Time, set to 0 on first packet (required by RFC 3315) $msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); # add IA_NA for each interface (required by DOCSIS and RFC 3315) # XXX: should this be a single interface only? my $iaid = 0; foreach my $iface (dhcp_client::iface()) { my $option_data = pack("NNN", ++$iaid, 0, 0); $msg->add_option($OPT_IA_NA, $option_data); } # add Reconfigure Accept (required by DOCSIS) $msg->add_option($OPT_RECONF_ACCEPT, ""); # add Options Request (required by DOCSIS, recommended by RFC 3315) my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET ); $msg->add_option($OPT_ORO, pack("n*", @oro)); # add Vendor Class option (required by DOCSIS) $msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0"); # add Vendor-specific Information Option option (required by DOCSIS) my $vsio = pack("N", 4491); # ORO (required by DOCSIS) my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS ); $vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro); # TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS) my $tlv5_data = "\x01\x02\x03\x0"; $vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data; # DOCSIS Device (required by DOCSIS) my $docsis_device_id = dhcp_client::mac_addr_binary(); $vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id)); $vsio .= $docsis_device_id; $msg->add_option($OPT_VENDOR_OPTS, $vsio); # add Rapid Commit option (required by DOCSIS) $msg->add_option($OPT_RAPID_COMMIT, ""); # timeout parameters, from DOCSIS my $IRT = $SOL_TIMEOUT; my $MRT = $SOL_MAX_RT; my $MRC = 1; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 my $MRD = 0; # sleep a random amount of time between 0 and 1 second, required by RFC 3315 # XXX: this seems pretty stupid sleep(rand($SOL_MAX_DELAY)); my $RT; my $count = 0; my $mrd_end_time; if ($MRD != 0) { $mrd_end_time = time() + $MRD; } my $reply_msg; do { # create our socket, and send our Solicit socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); my $packet = $msg->packet(); my $send_ret = send(SOCK, $packet, 0, pack_sockaddr_in6($server_port, $addr)); if (not defined($send_ret)) { printf STDERR "Error \%d sending DHCPv6 Solicit message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } elsif ($send_ret != length($packet)) { print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; exit(1); } $count++; my $RAND = rand(0.2) - 0.1; if (defined $RT) { $RT = 2*$RT + $RAND*$RT; if (($RT > $MRT) && ($MRT != 0)) { $RT = $MRT + $RAND*$RT; } } else { $RT = $IRT + $RAND*$IRT; } my $rt_end_time = time() + $RT; if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { $rt_end_time = $mrd_end_time; } for (;;) { my $timeout = $rt_end_time - time(); if ($timeout < 0) { # print STDERR "Timeout waiting for DHCPv6 Advertise ", # "or Reply message.\n"; last; } my @ready = IO::Select->new(\*SOCK)->can_read($timeout); if (@ready) { my $reply; my $recv_ret; $recv_ret = recv(SOCK, $reply, 1500, 0); if (not defined $recv_ret) { printf STDERR "Error \%d receiving DHCPv6 " . "message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } $reply_msg = dhcp_client::msg::decode($reply); if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || ($reply_msg->{msg_type} == $MSG_REPLY)) { last; } } } } until ($reply_msg || (($MRC != 0) && ($count > $MRC)) || (defined($mrd_end_time) && ($mrd_end_time > time()))); unless ($reply_msg) { if (($MRC != 0) && ($count >= $MRC)) { print STDERR "No reply after maximum retransmission count.\n"; } else { print STDERR "No reply after maximum retransmission duration.\n"; } } if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { print "Got DHCPv6 Reply message.\n"; exit(0); } #$Data::Dumper::Useqq = 1; #print Dumper($msg), "\n"; #print Dumper($msg->packet()), "\n"; # #print "packet length: ", length($msg->packet()), "\n"; dhcp-4.4.1/tests/DHCPv6/020-advertise-mcast.pl000644 000765 000024 00000010374 13243301226 021003 0ustar00tmarkstaff000000 000000 #! /usr/bin/perl -w # Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # Internet Systems Consortium, Inc. # 950 Charter Street # Redwood City, CA 94063 # # https://www.isc.org/ use strict; use English; use Time::HiRes qw( sleep ); use Socket; use Socket6; use IO::Select; use dhcp_client; # well-known addresses my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; my $All_DHCP_Servers = "ff05::1:3"; # ports my $client_port = 546; my $server_port = 547; # create a new Solicit message my $msg = dhcp_client::msg->new($MSG_ADVERTISE); # add the Client Identifier (required by DOCSIS and RFC 3315) $msg->add_option($OPT_CLIENTID, dhcp_client::duid()); # add Elapsed Time, set to 0 on first packet (required by RFC 3315) $msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); # add IA_NA for each interface (required by DOCSIS and RFC 3315) # XXX: should this be a single interface only? my $iaid = 0; foreach my $iface (dhcp_client::iface()) { my $option_data = pack("NNN", ++$iaid, 0, 0); $msg->add_option($OPT_IA_NA, $option_data); } # add Options Request (required by DOCSIS, recommended by RFC 3315) my @oro = ( ); $msg->add_option($OPT_ORO, pack("n*", @oro)); # timeout parameters my $IRT = $SOL_TIMEOUT; my $MRT = $SOL_MAX_RT; my $MRC = 1; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 my $MRD = 0; # sleep a random amount of time between 0 and 1 second, required by RFC 3315 # XXX: this seems pretty stupid sleep(rand($SOL_MAX_DELAY)); my $RT; my $count = 0; my $mrd_end_time; if ($MRD != 0) { $mrd_end_time = time() + $MRD; } my $reply_msg; do { # create our socket, and send our bogus packet socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); my $packet = $msg->packet(); my $send_ret = send(SOCK, $packet, 0, pack_sockaddr_in6($server_port, $addr)); if (not defined($send_ret)) { printf STDERR "Error \%d sending DHCPv6 message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } elsif ($send_ret != length($packet)) { print STDERR "Unable to send entire DHCPv6 message.\n"; exit(1); } $count++; my $RAND = rand(0.2) - 0.1; if (defined $RT) { $RT = 2*$RT + $RAND*$RT; if (($RT > $MRT) && ($MRT != 0)) { $RT = $MRT + $RAND*$RT; } } else { $RT = $IRT + $RAND*$IRT; } my $rt_end_time = time() + $RT; if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { $rt_end_time = $mrd_end_time; } for (;;) { my $timeout = $rt_end_time - time(); if ($timeout < 0) { last; } my @ready = IO::Select->new(\*SOCK)->can_read($timeout); if (@ready) { my $reply; my $recv_ret; $recv_ret = recv(SOCK, $reply, 1500, 0); if (not defined $recv_ret) { printf STDERR "Error \%d receiving DHCPv6 " . "message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } $reply_msg = dhcp_client::msg::decode($reply); if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || ($reply_msg->{msg_type} == $MSG_REPLY)) { last; } } } } until ($reply_msg || (($MRC != 0) && ($count > $MRC)) || (defined($mrd_end_time) && ($mrd_end_time > time()))); unless ($reply_msg) { if (($MRC != 0) && ($count >= $MRC)) { print STDERR "No reply after maximum retransmission count.\n"; } else { print STDERR "No reply after maximum retransmission duration.\n"; } } if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { print "Got DHCPv6 Reply message.\n"; exit(0); } #$Data::Dumper::Useqq = 1; #print Dumper($msg), "\n"; #print Dumper($msg->packet()), "\n"; # #print "packet length: ", length($msg->packet()), "\n"; dhcp-4.4.1/tests/DHCPv6/030-request-noclientid.pl000644 000765 000024 00000013332 13243301226 021524 0ustar00tmarkstaff000000 000000 #! /usr/bin/perl -w # Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # Internet Systems Consortium, Inc. # 950 Charter Street # Redwood City, CA 94063 # # https://www.isc.org/ use strict; use English; use Time::HiRes qw( sleep ); use Socket; use Socket6; use IO::Select; use dhcp_client; # XXX: for debugging use Data::Dumper; # not-yet-standard options my $OPT_TIME_SERVERS = 40; my $OPT_TIME_OFFSET = 41; # DOCSIS sub-options my $DOCSIS_OPT_ORO = 1; # 2 to 31 are reserved my $DOCSIS_OPT_TFTP_SERVERS = 32; my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; my $DOCSIS_OPT_SYSLOG_SERVERS = 34; my $DOCSIS_OPT_TLV5 = 35; my $DOCSIS_OPT_DEVICE_ID = 36; my $DOCSIS_OPT_CCC = 37; my $DOCSIS_OPT_VERS = 38; # well-known addresses my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; my $All_DHCP_Servers = "ff05::1:3"; # ports my $client_port = 546; my $server_port = 547; # create a new Solicit message my $msg = dhcp_client::msg->new($MSG_REQUEST); # NOT add the Client Identifier (required by DOCSIS and RFC 3315) #$msg->add_option($OPT_CLIENTID, dhcp_client::duid()); # add Elapsed Time, set to 0 on first packet (required by RFC 3315) $msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); # add IA_NA for each interface (required by DOCSIS and RFC 3315) # XXX: should this be a single interface only? my $iaid = 0; foreach my $iface (dhcp_client::iface()) { my $option_data = pack("NNN", ++$iaid, 0, 0); $msg->add_option($OPT_IA_NA, $option_data); } # add Reconfigure Accept (required by DOCSIS) $msg->add_option($OPT_RECONF_ACCEPT, ""); # add Options Request (required by DOCSIS, recommended by RFC 3315) my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET ); $msg->add_option($OPT_ORO, pack("n*", @oro)); # add Vendor Class option (required by DOCSIS) $msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0"); # add Vendor-specific Information Option option (required by DOCSIS) my $vsio = pack("N", 4491); # ORO (required by DOCSIS) my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS ); $vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro); # TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS) my $tlv5_data = "\x01\x02\x03\x0"; $vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data; # DOCSIS Device (required by DOCSIS) my $docsis_device_id = dhcp_client::mac_addr_binary(); $vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id)); $vsio .= $docsis_device_id; $msg->add_option($OPT_VENDOR_OPTS, $vsio); # add Rapid Commit option (required by DOCSIS) $msg->add_option($OPT_RAPID_COMMIT, ""); # timeout parameters, from DOCSIS my $IRT = $SOL_TIMEOUT; my $MRT = $SOL_MAX_RT; my $MRC = 1; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 my $MRD = 0; # sleep a random amount of time between 0 and 1 second, required by RFC 3315 # XXX: this seems pretty stupid sleep(rand($SOL_MAX_DELAY)); my $RT; my $count = 0; my $mrd_end_time; if ($MRD != 0) { $mrd_end_time = time() + $MRD; } my $reply_msg; do { # create our socket, and send our Solicit socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); my $packet = $msg->packet(); my $send_ret = send(SOCK, $packet, 0, pack_sockaddr_in6($server_port, $addr)); if (not defined($send_ret)) { printf STDERR "Error \%d sending DHCPv6 Solicit message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } elsif ($send_ret != length($packet)) { print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; exit(1); } $count++; my $RAND = rand(0.2) - 0.1; if (defined $RT) { $RT = 2*$RT + $RAND*$RT; if (($RT > $MRT) && ($MRT != 0)) { $RT = $MRT + $RAND*$RT; } } else { $RT = $IRT + $RAND*$IRT; } my $rt_end_time = time() + $RT; if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { $rt_end_time = $mrd_end_time; } for (;;) { my $timeout = $rt_end_time - time(); if ($timeout < 0) { # print STDERR "Timeout waiting for DHCPv6 Advertise ", # "or Reply message.\n"; last; } my @ready = IO::Select->new(\*SOCK)->can_read($timeout); if (@ready) { my $reply; my $recv_ret; $recv_ret = recv(SOCK, $reply, 1500, 0); if (not defined $recv_ret) { printf STDERR "Error \%d receiving DHCPv6 " . "message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } $reply_msg = dhcp_client::msg::decode($reply); if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || ($reply_msg->{msg_type} == $MSG_REPLY)) { last; } } } } until ($reply_msg || (($MRC != 0) && ($count > $MRC)) || (defined($mrd_end_time) && ($mrd_end_time > time()))); unless ($reply_msg) { if (($MRC != 0) && ($count >= $MRC)) { print STDERR "No reply after maximum retransmission count.\n"; } else { print STDERR "No reply after maximum retransmission duration.\n"; } } if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { print "Got DHCPv6 Reply message.\n"; exit(0); } #$Data::Dumper::Useqq = 1; #print Dumper($msg), "\n"; #print Dumper($msg->packet()), "\n"; # #print "packet length: ", length($msg->packet()), "\n"; dhcp-4.4.1/tests/DHCPv6/031-request-noserverid.pl000644 000765 000024 00000013325 13243301226 021557 0ustar00tmarkstaff000000 000000 #! /usr/bin/perl -w # Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # Internet Systems Consortium, Inc. # 950 Charter Street # Redwood City, CA 94063 # # https://www.isc.org/ use strict; use English; use Time::HiRes qw( sleep ); use Socket; use Socket6; use IO::Select; use dhcp_client; # XXX: for debugging use Data::Dumper; # not-yet-standard options my $OPT_TIME_SERVERS = 40; my $OPT_TIME_OFFSET = 41; # DOCSIS sub-options my $DOCSIS_OPT_ORO = 1; # 2 to 31 are reserved my $DOCSIS_OPT_TFTP_SERVERS = 32; my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; my $DOCSIS_OPT_SYSLOG_SERVERS = 34; my $DOCSIS_OPT_TLV5 = 35; my $DOCSIS_OPT_DEVICE_ID = 36; my $DOCSIS_OPT_CCC = 37; my $DOCSIS_OPT_VERS = 38; # well-known addresses my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; my $All_DHCP_Servers = "ff05::1:3"; # ports my $client_port = 546; my $server_port = 547; # create a new Solicit message my $msg = dhcp_client::msg->new($MSG_REQUEST); # add the Client Identifier (required by DOCSIS and RFC 3315) $msg->add_option($OPT_CLIENTID, dhcp_client::duid()); # add Elapsed Time, set to 0 on first packet (required by RFC 3315) $msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); # add IA_NA for each interface (required by DOCSIS and RFC 3315) # XXX: should this be a single interface only? my $iaid = 0; foreach my $iface (dhcp_client::iface()) { my $option_data = pack("NNN", ++$iaid, 0, 0); $msg->add_option($OPT_IA_NA, $option_data); } # add Reconfigure Accept (required by DOCSIS) $msg->add_option($OPT_RECONF_ACCEPT, ""); # add Options Request (required by DOCSIS, recommended by RFC 3315) my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET ); $msg->add_option($OPT_ORO, pack("n*", @oro)); # add Vendor Class option (required by DOCSIS) $msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0"); # add Vendor-specific Information Option option (required by DOCSIS) my $vsio = pack("N", 4491); # ORO (required by DOCSIS) my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS ); $vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro); # TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS) my $tlv5_data = "\x01\x02\x03\x0"; $vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data; # DOCSIS Device (required by DOCSIS) my $docsis_device_id = dhcp_client::mac_addr_binary(); $vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id)); $vsio .= $docsis_device_id; $msg->add_option($OPT_VENDOR_OPTS, $vsio); # add Rapid Commit option (required by DOCSIS) $msg->add_option($OPT_RAPID_COMMIT, ""); # timeout parameters, from DOCSIS my $IRT = $SOL_TIMEOUT; my $MRT = $SOL_MAX_RT; my $MRC = 1; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 my $MRD = 0; # sleep a random amount of time between 0 and 1 second, required by RFC 3315 # XXX: this seems pretty stupid sleep(rand($SOL_MAX_DELAY)); my $RT; my $count = 0; my $mrd_end_time; if ($MRD != 0) { $mrd_end_time = time() + $MRD; } my $reply_msg; do { # create our socket, and send our Solicit socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); my $packet = $msg->packet(); my $send_ret = send(SOCK, $packet, 0, pack_sockaddr_in6($server_port, $addr)); if (not defined($send_ret)) { printf STDERR "Error \%d sending DHCPv6 Solicit message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } elsif ($send_ret != length($packet)) { print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; exit(1); } $count++; my $RAND = rand(0.2) - 0.1; if (defined $RT) { $RT = 2*$RT + $RAND*$RT; if (($RT > $MRT) && ($MRT != 0)) { $RT = $MRT + $RAND*$RT; } } else { $RT = $IRT + $RAND*$IRT; } my $rt_end_time = time() + $RT; if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { $rt_end_time = $mrd_end_time; } for (;;) { my $timeout = $rt_end_time - time(); if ($timeout < 0) { # print STDERR "Timeout waiting for DHCPv6 Advertise ", # "or Reply message.\n"; last; } my @ready = IO::Select->new(\*SOCK)->can_read($timeout); if (@ready) { my $reply; my $recv_ret; $recv_ret = recv(SOCK, $reply, 1500, 0); if (not defined $recv_ret) { printf STDERR "Error \%d receiving DHCPv6 " . "message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } $reply_msg = dhcp_client::msg::decode($reply); if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || ($reply_msg->{msg_type} == $MSG_REPLY)) { last; } } } } until ($reply_msg || (($MRC != 0) && ($count > $MRC)) || (defined($mrd_end_time) && ($mrd_end_time > time()))); unless ($reply_msg) { if (($MRC != 0) && ($count >= $MRC)) { print STDERR "No reply after maximum retransmission count.\n"; } else { print STDERR "No reply after maximum retransmission duration.\n"; } } if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { print "Got DHCPv6 Reply message.\n"; exit(0); } #$Data::Dumper::Useqq = 1; #print Dumper($msg), "\n"; #print Dumper($msg->packet()), "\n"; # #print "packet length: ", length($msg->packet()), "\n"; dhcp-4.4.1/tests/DHCPv6/032-request-badduid.pl000644 000765 000024 00000013537 13243301226 021001 0ustar00tmarkstaff000000 000000 #! /usr/bin/perl -w # Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # Internet Systems Consortium, Inc. # 950 Charter Street # Redwood City, CA 94063 # # https://www.isc.org/ use strict; use English; use Time::HiRes qw( sleep ); use Socket; use Socket6; use IO::Select; use dhcp_client; # XXX: for debugging use Data::Dumper; # not-yet-standard options my $OPT_TIME_SERVERS = 40; my $OPT_TIME_OFFSET = 41; # DOCSIS sub-options my $DOCSIS_OPT_ORO = 1; # 2 to 31 are reserved my $DOCSIS_OPT_TFTP_SERVERS = 32; my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; my $DOCSIS_OPT_SYSLOG_SERVERS = 34; my $DOCSIS_OPT_TLV5 = 35; my $DOCSIS_OPT_DEVICE_ID = 36; my $DOCSIS_OPT_CCC = 37; my $DOCSIS_OPT_VERS = 38; # well-known addresses my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; my $All_DHCP_Servers = "ff05::1:3"; # ports my $client_port = 546; my $server_port = 547; # create a new Solicit message my $msg = dhcp_client::msg->new($MSG_REQUEST); # add the Client Identifier (required by DOCSIS and RFC 3315) $msg->add_option($OPT_CLIENTID, dhcp_client::duid()); # add the Server Identifier (required by DOCSIS and RFC 3315) # but use *our* DUID $msg->add_option($OPT_SERVERID, dhcp_client::duid()); # add Elapsed Time, set to 0 on first packet (required by RFC 3315) $msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); # add IA_NA for each interface (required by DOCSIS and RFC 3315) # XXX: should this be a single interface only? my $iaid = 0; foreach my $iface (dhcp_client::iface()) { my $option_data = pack("NNN", ++$iaid, 0, 0); $msg->add_option($OPT_IA_NA, $option_data); } # add Reconfigure Accept (required by DOCSIS) $msg->add_option($OPT_RECONF_ACCEPT, ""); # add Options Request (required by DOCSIS, recommended by RFC 3315) my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET ); $msg->add_option($OPT_ORO, pack("n*", @oro)); # add Vendor Class option (required by DOCSIS) $msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0"); # add Vendor-specific Information Option option (required by DOCSIS) my $vsio = pack("N", 4491); # ORO (required by DOCSIS) my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS ); $vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro); # TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS) my $tlv5_data = "\x01\x02\x03\x0"; $vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data; # DOCSIS Device (required by DOCSIS) my $docsis_device_id = dhcp_client::mac_addr_binary(); $vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id)); $vsio .= $docsis_device_id; $msg->add_option($OPT_VENDOR_OPTS, $vsio); # add Rapid Commit option (required by DOCSIS) $msg->add_option($OPT_RAPID_COMMIT, ""); # timeout parameters, from DOCSIS my $IRT = $SOL_TIMEOUT; my $MRT = $SOL_MAX_RT; my $MRC = 1; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 my $MRD = 0; # sleep a random amount of time between 0 and 1 second, required by RFC 3315 # XXX: this seems pretty stupid sleep(rand($SOL_MAX_DELAY)); my $RT; my $count = 0; my $mrd_end_time; if ($MRD != 0) { $mrd_end_time = time() + $MRD; } my $reply_msg; do { # create our socket, and send our Solicit socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); my $packet = $msg->packet(); my $send_ret = send(SOCK, $packet, 0, pack_sockaddr_in6($server_port, $addr)); if (not defined($send_ret)) { printf STDERR "Error \%d sending DHCPv6 Solicit message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } elsif ($send_ret != length($packet)) { print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; exit(1); } $count++; my $RAND = rand(0.2) - 0.1; if (defined $RT) { $RT = 2*$RT + $RAND*$RT; if (($RT > $MRT) && ($MRT != 0)) { $RT = $MRT + $RAND*$RT; } } else { $RT = $IRT + $RAND*$IRT; } my $rt_end_time = time() + $RT; if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { $rt_end_time = $mrd_end_time; } for (;;) { my $timeout = $rt_end_time - time(); if ($timeout < 0) { # print STDERR "Timeout waiting for DHCPv6 Advertise ", # "or Reply message.\n"; last; } my @ready = IO::Select->new(\*SOCK)->can_read($timeout); if (@ready) { my $reply; my $recv_ret; $recv_ret = recv(SOCK, $reply, 1500, 0); if (not defined $recv_ret) { printf STDERR "Error \%d receiving DHCPv6 " . "message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } $reply_msg = dhcp_client::msg::decode($reply); if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || ($reply_msg->{msg_type} == $MSG_REPLY)) { last; } } } } until ($reply_msg || (($MRC != 0) && ($count > $MRC)) || (defined($mrd_end_time) && ($mrd_end_time > time()))); unless ($reply_msg) { if (($MRC != 0) && ($count >= $MRC)) { print STDERR "No reply after maximum retransmission count.\n"; } else { print STDERR "No reply after maximum retransmission duration.\n"; } } if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { print "Got DHCPv6 Reply message.\n"; exit(0); } #$Data::Dumper::Useqq = 1; #print Dumper($msg), "\n"; #print Dumper($msg->packet()), "\n"; # #print "packet length: ", length($msg->packet()), "\n"; dhcp-4.4.1/tests/DHCPv6/110-information-request-ia_na.pl000644 000765 000024 00000013341 13243301226 022765 0ustar00tmarkstaff000000 000000 #! /usr/bin/perl -w # Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # Internet Systems Consortium, Inc. # 950 Charter Street # Redwood City, CA 94063 # # https://www.isc.org/ use strict; use English; use Time::HiRes qw( sleep ); use Socket; use Socket6; use IO::Select; use dhcp_client; # XXX: for debugging use Data::Dumper; # not-yet-standard options my $OPT_TIME_SERVERS = 40; my $OPT_TIME_OFFSET = 41; # DOCSIS sub-options my $DOCSIS_OPT_ORO = 1; # 2 to 31 are reserved my $DOCSIS_OPT_TFTP_SERVERS = 32; my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; my $DOCSIS_OPT_SYSLOG_SERVERS = 34; my $DOCSIS_OPT_TLV5 = 35; my $DOCSIS_OPT_DEVICE_ID = 36; my $DOCSIS_OPT_CCC = 37; my $DOCSIS_OPT_VERS = 38; # well-known addresses my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; my $All_DHCP_Servers = "ff05::1:3"; # ports my $client_port = 546; my $server_port = 547; # create a new Solicit message my $msg = dhcp_client::msg->new($MSG_INFORMATION_REQUEST); # add the Client Identifier (required by DOCSIS and RFC 3315) $msg->add_option($OPT_CLIENTID, dhcp_client::duid()); # add Elapsed Time, set to 0 on first packet (required by RFC 3315) $msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); # add IA_NA for each interface (required by DOCSIS and RFC 3315) # XXX: should this be a single interface only? my $iaid = 0; foreach my $iface (dhcp_client::iface()) { my $option_data = pack("NNN", ++$iaid, 0, 0); $msg->add_option($OPT_IA_NA, $option_data); } # add Reconfigure Accept (required by DOCSIS) $msg->add_option($OPT_RECONF_ACCEPT, ""); # add Options Request (required by DOCSIS, recommended by RFC 3315) my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET ); $msg->add_option($OPT_ORO, pack("n*", @oro)); # add Vendor Class option (required by DOCSIS) $msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0"); # add Vendor-specific Information Option option (required by DOCSIS) my $vsio = pack("N", 4491); # ORO (required by DOCSIS) my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS ); $vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro); # TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS) my $tlv5_data = "\x01\x02\x03\x0"; $vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data; # DOCSIS Device (required by DOCSIS) my $docsis_device_id = dhcp_client::mac_addr_binary(); $vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id)); $vsio .= $docsis_device_id; $msg->add_option($OPT_VENDOR_OPTS, $vsio); # add Rapid Commit option (required by DOCSIS) $msg->add_option($OPT_RAPID_COMMIT, ""); # timeout parameters, from DOCSIS my $IRT = $SOL_TIMEOUT; my $MRT = $SOL_MAX_RT; my $MRC = 1; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 my $MRD = 0; # sleep a random amount of time between 0 and 1 second, required by RFC 3315 # XXX: this seems pretty stupid sleep(rand($SOL_MAX_DELAY)); my $RT; my $count = 0; my $mrd_end_time; if ($MRD != 0) { $mrd_end_time = time() + $MRD; } my $reply_msg; do { # create our socket, and send our Solicit socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); my $packet = $msg->packet(); my $send_ret = send(SOCK, $packet, 0, pack_sockaddr_in6($server_port, $addr)); if (not defined($send_ret)) { printf STDERR "Error \%d sending DHCPv6 Solicit message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } elsif ($send_ret != length($packet)) { print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; exit(1); } $count++; my $RAND = rand(0.2) - 0.1; if (defined $RT) { $RT = 2*$RT + $RAND*$RT; if (($RT > $MRT) && ($MRT != 0)) { $RT = $MRT + $RAND*$RT; } } else { $RT = $IRT + $RAND*$IRT; } my $rt_end_time = time() + $RT; if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { $rt_end_time = $mrd_end_time; } for (;;) { my $timeout = $rt_end_time - time(); if ($timeout < 0) { # print STDERR "Timeout waiting for DHCPv6 Advertise ", # "or Reply message.\n"; last; } my @ready = IO::Select->new(\*SOCK)->can_read($timeout); if (@ready) { my $reply; my $recv_ret; $recv_ret = recv(SOCK, $reply, 1500, 0); if (not defined $recv_ret) { printf STDERR "Error \%d receiving DHCPv6 " . "message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } $reply_msg = dhcp_client::msg::decode($reply); if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || ($reply_msg->{msg_type} == $MSG_REPLY)) { last; } } } } until ($reply_msg || (($MRC != 0) && ($count > $MRC)) || (defined($mrd_end_time) && ($mrd_end_time > time()))); unless ($reply_msg) { if (($MRC != 0) && ($count >= $MRC)) { print STDERR "No reply after maximum retransmission count.\n"; } else { print STDERR "No reply after maximum retransmission duration.\n"; } } if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { print "Got DHCPv6 Reply message.\n"; exit(0); } #$Data::Dumper::Useqq = 1; #print Dumper($msg), "\n"; #print Dumper($msg->packet()), "\n"; # #print "packet length: ", length($msg->packet()), "\n"; dhcp-4.4.1/tests/DHCPv6/111-information-request-ia_ta.pl000644 000765 000024 00000013351 13243301226 022775 0ustar00tmarkstaff000000 000000 #! /usr/bin/perl -w # Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # Internet Systems Consortium, Inc. # 950 Charter Street # Redwood City, CA 94063 # # https://www.isc.org/ use strict; use English; use Time::HiRes qw( sleep ); use Socket; use Socket6; use IO::Select; use dhcp_client; # XXX: for debugging use Data::Dumper; # not-yet-standard options my $OPT_TIME_SERVERS = 40; my $OPT_TIME_OFFSET = 41; # DOCSIS sub-options my $DOCSIS_OPT_ORO = 1; # 2 to 31 are reserved my $DOCSIS_OPT_TFTP_SERVERS = 32; my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; my $DOCSIS_OPT_SYSLOG_SERVERS = 34; my $DOCSIS_OPT_TLV5 = 35; my $DOCSIS_OPT_DEVICE_ID = 36; my $DOCSIS_OPT_CCC = 37; my $DOCSIS_OPT_VERS = 38; # well-known addresses my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; my $All_DHCP_Servers = "ff05::1:3"; # ports my $client_port = 546; my $server_port = 547; # create a new Solicit message my $msg = dhcp_client::msg->new($MSG_INFORMATION_REQUEST); # add the Client Identifier (required by DOCSIS and RFC 3315) $msg->add_option($OPT_CLIENTID, dhcp_client::duid()); # add Elapsed Time, set to 0 on first packet (required by RFC 3315) $msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); # add IA_TA for each interface (should be IA_NA, by DOCSIS and RFC 3315) # XXX: should this be a single interface only? my $iaid = 0; foreach my $iface (dhcp_client::iface()) { my $option_data = pack("NNN", ++$iaid, 0, 0); $msg->add_option($OPT_IA_TA, $option_data); } # add Reconfigure Accept (required by DOCSIS) $msg->add_option($OPT_RECONF_ACCEPT, ""); # add Options Request (required by DOCSIS, recommended by RFC 3315) my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET ); $msg->add_option($OPT_ORO, pack("n*", @oro)); # add Vendor Class option (required by DOCSIS) $msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0"); # add Vendor-specific Information Option option (required by DOCSIS) my $vsio = pack("N", 4491); # ORO (required by DOCSIS) my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS ); $vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro); # TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS) my $tlv5_data = "\x01\x02\x03\x0"; $vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data; # DOCSIS Device (required by DOCSIS) my $docsis_device_id = dhcp_client::mac_addr_binary(); $vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id)); $vsio .= $docsis_device_id; $msg->add_option($OPT_VENDOR_OPTS, $vsio); # add Rapid Commit option (required by DOCSIS) $msg->add_option($OPT_RAPID_COMMIT, ""); # timeout parameters, from DOCSIS my $IRT = $SOL_TIMEOUT; my $MRT = $SOL_MAX_RT; my $MRC = 1; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 my $MRD = 0; # sleep a random amount of time between 0 and 1 second, required by RFC 3315 # XXX: this seems pretty stupid sleep(rand($SOL_MAX_DELAY)); my $RT; my $count = 0; my $mrd_end_time; if ($MRD != 0) { $mrd_end_time = time() + $MRD; } my $reply_msg; do { # create our socket, and send our Solicit socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); my $packet = $msg->packet(); my $send_ret = send(SOCK, $packet, 0, pack_sockaddr_in6($server_port, $addr)); if (not defined($send_ret)) { printf STDERR "Error \%d sending DHCPv6 Solicit message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } elsif ($send_ret != length($packet)) { print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; exit(1); } $count++; my $RAND = rand(0.2) - 0.1; if (defined $RT) { $RT = 2*$RT + $RAND*$RT; if (($RT > $MRT) && ($MRT != 0)) { $RT = $MRT + $RAND*$RT; } } else { $RT = $IRT + $RAND*$IRT; } my $rt_end_time = time() + $RT; if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { $rt_end_time = $mrd_end_time; } for (;;) { my $timeout = $rt_end_time - time(); if ($timeout < 0) { # print STDERR "Timeout waiting for DHCPv6 Advertise ", # "or Reply message.\n"; last; } my @ready = IO::Select->new(\*SOCK)->can_read($timeout); if (@ready) { my $reply; my $recv_ret; $recv_ret = recv(SOCK, $reply, 1500, 0); if (not defined $recv_ret) { printf STDERR "Error \%d receiving DHCPv6 " . "message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } $reply_msg = dhcp_client::msg::decode($reply); if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || ($reply_msg->{msg_type} == $MSG_REPLY)) { last; } } } } until ($reply_msg || (($MRC != 0) && ($count > $MRC)) || (defined($mrd_end_time) && ($mrd_end_time > time()))); unless ($reply_msg) { if (($MRC != 0) && ($count >= $MRC)) { print STDERR "No reply after maximum retransmission count.\n"; } else { print STDERR "No reply after maximum retransmission duration.\n"; } } if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { print "Got DHCPv6 Reply message.\n"; exit(0); } #$Data::Dumper::Useqq = 1; #print Dumper($msg), "\n"; #print Dumper($msg->packet()), "\n"; # #print "packet length: ", length($msg->packet()), "\n"; dhcp-4.4.1/tests/DHCPv6/112-badduid.pl000644 000765 000024 00000013143 13243301226 017303 0ustar00tmarkstaff000000 000000 #! /usr/bin/perl -w # Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # Internet Systems Consortium, Inc. # 950 Charter Street # Redwood City, CA 94063 # # https://www.isc.org/ use strict; use English; use Time::HiRes qw( sleep ); use Socket; use Socket6; use IO::Select; use dhcp_client; # XXX: for debugging use Data::Dumper; # not-yet-standard options my $OPT_TIME_SERVERS = 40; my $OPT_TIME_OFFSET = 41; # DOCSIS sub-options my $DOCSIS_OPT_ORO = 1; # 2 to 31 are reserved my $DOCSIS_OPT_TFTP_SERVERS = 32; my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; my $DOCSIS_OPT_SYSLOG_SERVERS = 34; my $DOCSIS_OPT_TLV5 = 35; my $DOCSIS_OPT_DEVICE_ID = 36; my $DOCSIS_OPT_CCC = 37; my $DOCSIS_OPT_VERS = 38; # well-known addresses my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; my $All_DHCP_Servers = "ff05::1:3"; # ports my $client_port = 546; my $server_port = 547; # create a new Solicit message my $msg = dhcp_client::msg->new($MSG_INFORMATION_REQUEST); # add the Client Identifier (required by DOCSIS and RFC 3315) $msg->add_option($OPT_CLIENTID, dhcp_client::duid()); # add the Server Identifier (required by DOCSIS and RFC 3315) # but use *our* DUID $msg->add_option($OPT_SERVERID, dhcp_client::duid()); # add Elapsed Time, set to 0 on first packet (required by RFC 3315) $msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); # add Reconfigure Accept (required by DOCSIS) $msg->add_option($OPT_RECONF_ACCEPT, ""); # add Options Request (required by DOCSIS, recommended by RFC 3315) my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET ); $msg->add_option($OPT_ORO, pack("n*", @oro)); # add Vendor Class option (required by DOCSIS) $msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0"); # add Vendor-specific Information Option option (required by DOCSIS) my $vsio = pack("N", 4491); # ORO (required by DOCSIS) my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS ); $vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro); # TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS) my $tlv5_data = "\x01\x02\x03\x0"; $vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data; # DOCSIS Device (required by DOCSIS) my $docsis_device_id = dhcp_client::mac_addr_binary(); $vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id)); $vsio .= $docsis_device_id; $msg->add_option($OPT_VENDOR_OPTS, $vsio); # add Rapid Commit option (required by DOCSIS) $msg->add_option($OPT_RAPID_COMMIT, ""); # timeout parameters, from DOCSIS my $IRT = $SOL_TIMEOUT; my $MRT = $SOL_MAX_RT; my $MRC = 1; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 my $MRD = 0; # sleep a random amount of time between 0 and 1 second, required by RFC 3315 # XXX: this seems pretty stupid sleep(rand($SOL_MAX_DELAY)); my $RT; my $count = 0; my $mrd_end_time; if ($MRD != 0) { $mrd_end_time = time() + $MRD; } my $reply_msg; do { # create our socket, and send our Solicit socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); my $packet = $msg->packet(); my $send_ret = send(SOCK, $packet, 0, pack_sockaddr_in6($server_port, $addr)); if (not defined($send_ret)) { printf STDERR "Error \%d sending DHCPv6 Solicit message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } elsif ($send_ret != length($packet)) { print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; exit(1); } $count++; my $RAND = rand(0.2) - 0.1; if (defined $RT) { $RT = 2*$RT + $RAND*$RT; if (($RT > $MRT) && ($MRT != 0)) { $RT = $MRT + $RAND*$RT; } } else { $RT = $IRT + $RAND*$IRT; } my $rt_end_time = time() + $RT; if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { $rt_end_time = $mrd_end_time; } for (;;) { my $timeout = $rt_end_time - time(); if ($timeout < 0) { # print STDERR "Timeout waiting for DHCPv6 Advertise ", # "or Reply message.\n"; last; } my @ready = IO::Select->new(\*SOCK)->can_read($timeout); if (@ready) { my $reply; my $recv_ret; $recv_ret = recv(SOCK, $reply, 1500, 0); if (not defined $recv_ret) { printf STDERR "Error \%d receiving DHCPv6 " . "message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } $reply_msg = dhcp_client::msg::decode($reply); if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || ($reply_msg->{msg_type} == $MSG_REPLY)) { last; } } } } until ($reply_msg || (($MRC != 0) && ($count > $MRC)) || (defined($mrd_end_time) && ($mrd_end_time > time()))); unless ($reply_msg) { if (($MRC != 0) && ($count >= $MRC)) { print STDERR "No reply after maximum retransmission count.\n"; } else { print STDERR "No reply after maximum retransmission duration.\n"; } } if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { print "Got DHCPv6 Reply message.\n"; exit(0); } #$Data::Dumper::Useqq = 1; #print Dumper($msg), "\n"; #print Dumper($msg->packet()), "\n"; # #print "packet length: ", length($msg->packet()), "\n"; dhcp-4.4.1/tests/DHCPv6/210-solicit-nohost.pl000644 000765 000024 00000013330 13243301226 020662 0ustar00tmarkstaff000000 000000 #! /usr/bin/perl -w # Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # Internet Systems Consortium, Inc. # 950 Charter Street # Redwood City, CA 94063 # # https://www.isc.org/ use strict; use English; use Time::HiRes qw( sleep ); use Socket; use Socket6; use IO::Select; use dhcp_client; # XXX: for debugging use Data::Dumper; # not-yet-standard options my $OPT_TIME_SERVERS = 40; my $OPT_TIME_OFFSET = 41; # DOCSIS sub-options my $DOCSIS_OPT_ORO = 1; # 2 to 31 are reserved my $DOCSIS_OPT_TFTP_SERVERS = 32; my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; my $DOCSIS_OPT_SYSLOG_SERVERS = 34; my $DOCSIS_OPT_TLV5 = 35; my $DOCSIS_OPT_DEVICE_ID = 36; my $DOCSIS_OPT_CCC = 37; my $DOCSIS_OPT_VERS = 38; # well-known addresses my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; my $All_DHCP_Servers = "ff05::1:3"; # ports my $client_port = 546; my $server_port = 547; # create a new Solicit message my $msg = dhcp_client::msg->new($MSG_SOLICIT); # add the Client Identifier (required by DOCSIS and RFC 3315) $msg->add_option($OPT_CLIENTID, dhcp_client::duid()); # add Elapsed Time, set to 0 on first packet (required by RFC 3315) $msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); # add IA_NA for each interface (required by DOCSIS and RFC 3315) # XXX: should this be a single interface only? my $iaid = 0; foreach my $iface (dhcp_client::iface()) { my $option_data = pack("NNN", ++$iaid, 0, 0); $msg->add_option($OPT_IA_NA, $option_data); } # add Reconfigure Accept (required by DOCSIS) $msg->add_option($OPT_RECONF_ACCEPT, ""); # add Options Request (required by DOCSIS, recommended by RFC 3315) my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET ); $msg->add_option($OPT_ORO, pack("n*", @oro)); # add Vendor Class option (required by DOCSIS) $msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0"); # add Vendor-specific Information Option option (required by DOCSIS) my $vsio = pack("N", 4491); # ORO (required by DOCSIS) my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS ); $vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro); # TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS) my $tlv5_data = "\x01\x02\x03\x0"; $vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data; # DOCSIS Device (required by DOCSIS) my $docsis_device_id = dhcp_client::mac_addr_binary(); $vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id)); $vsio .= $docsis_device_id; $msg->add_option($OPT_VENDOR_OPTS, $vsio); # add Rapid Commit option (required by DOCSIS) $msg->add_option($OPT_RAPID_COMMIT, ""); # timeout parameters, from DOCSIS my $IRT = $SOL_TIMEOUT; my $MRT = $SOL_MAX_RT; my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 my $MRD = 0; # sleep a random amount of time between 0 and 1 second, required by RFC 3315 # XXX: this seems pretty stupid sleep(rand($SOL_MAX_DELAY)); my $RT; my $count = 0; my $mrd_end_time; if ($MRD != 0) { $mrd_end_time = time() + $MRD; } my $reply_msg; do { # create our socket, and send our Solicit socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); my $packet = $msg->packet(); my $send_ret = send(SOCK, $packet, 0, pack_sockaddr_in6($server_port, $addr)); if (not defined($send_ret)) { printf STDERR "Error \%d sending DHCPv6 Solicit message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } elsif ($send_ret != length($packet)) { print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; exit(1); } $count++; my $RAND = rand(0.2) - 0.1; if (defined $RT) { $RT = 2*$RT + $RAND*$RT; if (($RT > $MRT) && ($MRT != 0)) { $RT = $MRT + $RAND*$RT; } } else { $RT = $IRT + $RAND*$IRT; } my $rt_end_time = time() + $RT; if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { $rt_end_time = $mrd_end_time; } for (;;) { my $timeout = $rt_end_time - time(); if ($timeout < 0) { # print STDERR "Timeout waiting for DHCPv6 Advertise ", # "or Reply message.\n"; last; } my @ready = IO::Select->new(\*SOCK)->can_read($timeout); if (@ready) { my $reply; my $recv_ret; $recv_ret = recv(SOCK, $reply, 1500, 0); if (not defined $recv_ret) { printf STDERR "Error \%d receiving DHCPv6 " . "message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } $reply_msg = dhcp_client::msg::decode($reply, 1); if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || ($reply_msg->{msg_type} == $MSG_REPLY)) { last; } } } } until ($reply_msg || (($MRC != 0) && ($count > $MRC)) || (defined($mrd_end_time) && ($mrd_end_time > time()))); unless ($reply_msg) { if (($MRC != 0) && ($count >= $MRC)) { print STDERR "No reply after maximum retransmission count.\n"; } else { print STDERR "No reply after maximum retransmission duration.\n"; } } if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { print "Got DHCPv6 Reply message.\n"; exit(0); } #$Data::Dumper::Useqq = 1; #print Dumper($msg), "\n"; #print Dumper($msg->packet()), "\n"; # #print "packet length: ", length($msg->packet()), "\n"; dhcp-4.4.1/tests/DHCPv6/211-solicit-opt-in-na.pl000644 000765 000024 00000014041 13243301226 021153 0ustar00tmarkstaff000000 000000 #! /usr/bin/perl -w # Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # Internet Systems Consortium, Inc. # 950 Charter Street # Redwood City, CA 94063 # # https://www.isc.org/ use strict; use English; use Time::HiRes qw( sleep ); use Socket; use Socket6; use IO::Select; use dhcp_client; # XXX: for debugging use Data::Dumper; $Data::Dumper::Useqq = 1; # not-yet-standard options my $OPT_TIME_SERVERS = 40; my $OPT_TIME_OFFSET = 41; # DOCSIS sub-options my $DOCSIS_OPT_ORO = 1; # 2 to 31 are reserved my $DOCSIS_OPT_TFTP_SERVERS = 32; my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; my $DOCSIS_OPT_SYSLOG_SERVERS = 34; my $DOCSIS_OPT_TLV5 = 35; my $DOCSIS_OPT_DEVICE_ID = 36; my $DOCSIS_OPT_CCC = 37; my $DOCSIS_OPT_VERS = 38; # well-known addresses my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; my $All_DHCP_Servers = "ff05::1:3"; # ports my $client_port = 546; my $server_port = 547; # create a new Solicit message my $msg = dhcp_client::msg->new($MSG_SOLICIT); # add the Client Identifier (required by DOCSIS and RFC 3315) my $client_id = "\x00\x01\x00\x01\x0c\x00\xa1\x41\x00\x06\x5b\x50\x99\xf6"; #my $client_id = dhcp_client::duid(3); $msg->add_option($OPT_CLIENTID, $client_id); #$msg->add_option($OPT_CLIENTID, dhcp_client::duid(3)); # add Elapsed Time, set to 0 on first packet (required by RFC 3315) $msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); # add IA_NA for each interface (required by DOCSIS and RFC 3315) # XXX: should this be a single interface only? my $iaid = 0; foreach my $iface (dhcp_client::iface()) { my $option_data = pack("NNN", ++$iaid, 0, 0); my $enc_opt = dhcp_client::msg->new(0); $enc_opt->add_option($OPT_CLIENTID, $client_id); $option_data .= $enc_opt->packed_options(); $msg->add_option($OPT_IA_NA, $option_data); } # add Reconfigure Accept (required by DOCSIS) $msg->add_option($OPT_RECONF_ACCEPT, ""); # add Options Request (required by DOCSIS, recommended by RFC 3315) my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET ); $msg->add_option($OPT_ORO, pack("n*", @oro)); # add Vendor Class option (required by DOCSIS) $msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0"); # add Vendor-specific Information Option option (required by DOCSIS) my $vsio = pack("N", 4491); # ORO (required by DOCSIS) my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS ); $vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro); # TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS) my $tlv5_data = "\x01\x02\x03\x0"; $vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data; # DOCSIS Device (required by DOCSIS) my $docsis_device_id = dhcp_client::mac_addr_binary(); $vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id)); $vsio .= $docsis_device_id; $msg->add_option($OPT_VENDOR_OPTS, $vsio); # add Rapid Commit option (required by DOCSIS) $msg->add_option($OPT_RAPID_COMMIT, ""); # timeout parameters, from DOCSIS my $IRT = $SOL_TIMEOUT; my $MRT = $SOL_MAX_RT; my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 my $MRD = 0; # sleep a random amount of time between 0 and 1 second, required by RFC 3315 # XXX: this seems pretty stupid sleep(rand($SOL_MAX_DELAY)); my $RT; my $count = 0; my $mrd_end_time; if ($MRD != 0) { $mrd_end_time = time() + $MRD; } my $reply_msg; do { # create our socket, and send our Solicit socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); my $packet = $msg->packet(); my $send_ret = send(SOCK, $packet, 0, pack_sockaddr_in6($server_port, $addr)); if (not defined($send_ret)) { printf STDERR "Error \%d sending DHCPv6 Solicit message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } elsif ($send_ret != length($packet)) { print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; exit(1); } $count++; my $RAND = rand(0.2) - 0.1; if (defined $RT) { $RT = 2*$RT + $RAND*$RT; if (($RT > $MRT) && ($MRT != 0)) { $RT = $MRT + $RAND*$RT; } } else { $RT = $IRT + $RAND*$IRT; } my $rt_end_time = time() + $RT; if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { $rt_end_time = $mrd_end_time; } for (;;) { my $timeout = $rt_end_time - time(); if ($timeout < 0) { # print STDERR "Timeout waiting for DHCPv6 Advertise ", # "or Reply message.\n"; last; } my @ready = IO::Select->new(\*SOCK)->can_read($timeout); if (@ready) { my $reply; my $recv_ret; $recv_ret = recv(SOCK, $reply, 1500, 0); if (not defined $recv_ret) { printf STDERR "Error \%d receiving DHCPv6 " . "message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } $reply_msg = dhcp_client::msg::decode($reply, 1); if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || ($reply_msg->{msg_type} == $MSG_REPLY)) { last; } } } } until ($reply_msg || (($MRC != 0) && ($count > $MRC)) || (defined($mrd_end_time) && ($mrd_end_time > time()))); unless ($reply_msg) { if (($MRC != 0) && ($count >= $MRC)) { print STDERR "No reply after maximum retransmission count.\n"; } else { print STDERR "No reply after maximum retransmission duration.\n"; } } if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { print "Got DHCPv6 Reply message.\n"; #print Dumper($reply_msg), "\n"; exit(0); } #print Dumper($msg), "\n"; #print Dumper($msg->packet()), "\n"; # #print "packet length: ", length($msg->packet()), "\n"; dhcp-4.4.1/tests/DHCPv6/212-solicit-opt-in-na-norapidcommit.pl000644 000765 000024 00000014042 13243301226 024020 0ustar00tmarkstaff000000 000000 #! /usr/bin/perl -w # Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # Internet Systems Consortium, Inc. # 950 Charter Street # Redwood City, CA 94063 # # https://www.isc.org/ use strict; use English; use Time::HiRes qw( sleep ); use Socket; use Socket6; use IO::Select; use dhcp_client; # XXX: for debugging use Data::Dumper; $Data::Dumper::Useqq = 1; # not-yet-standard options my $OPT_TIME_SERVERS = 40; my $OPT_TIME_OFFSET = 41; # DOCSIS sub-options my $DOCSIS_OPT_ORO = 1; # 2 to 31 are reserved my $DOCSIS_OPT_TFTP_SERVERS = 32; my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; my $DOCSIS_OPT_SYSLOG_SERVERS = 34; my $DOCSIS_OPT_TLV5 = 35; my $DOCSIS_OPT_DEVICE_ID = 36; my $DOCSIS_OPT_CCC = 37; my $DOCSIS_OPT_VERS = 38; # well-known addresses my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; my $All_DHCP_Servers = "ff05::1:3"; # ports my $client_port = 546; my $server_port = 547; # create a new Solicit message my $msg = dhcp_client::msg->new($MSG_SOLICIT); # add the Client Identifier (required by DOCSIS and RFC 3315) my $client_id = "\x00\x01\x00\x01\x0c\x00\xa1\x41\x00\x06\x5b\x50\x99\xf6"; #my $client_id = dhcp_client::duid(3); $msg->add_option($OPT_CLIENTID, $client_id); #$msg->add_option($OPT_CLIENTID, dhcp_client::duid(3)); # add Elapsed Time, set to 0 on first packet (required by RFC 3315) $msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); # add IA_NA for each interface (required by DOCSIS and RFC 3315) # XXX: should this be a single interface only? my $iaid = 0; foreach my $iface (dhcp_client::iface()) { my $option_data = pack("NNN", ++$iaid, 0, 0); my $enc_opt = dhcp_client::msg->new(0); $enc_opt->add_option($OPT_CLIENTID, $client_id); $option_data .= $enc_opt->packed_options(); $msg->add_option($OPT_IA_NA, $option_data); } # add Reconfigure Accept (required by DOCSIS) $msg->add_option($OPT_RECONF_ACCEPT, ""); # add Options Request (required by DOCSIS, recommended by RFC 3315) my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET ); $msg->add_option($OPT_ORO, pack("n*", @oro)); # add Vendor Class option (required by DOCSIS) $msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0"); # add Vendor-specific Information Option option (required by DOCSIS) my $vsio = pack("N", 4491); # ORO (required by DOCSIS) my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS ); $vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro); # TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS) my $tlv5_data = "\x01\x02\x03\x0"; $vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data; # DOCSIS Device (required by DOCSIS) my $docsis_device_id = dhcp_client::mac_addr_binary(); $vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id)); $vsio .= $docsis_device_id; $msg->add_option($OPT_VENDOR_OPTS, $vsio); # add Rapid Commit option (required by DOCSIS) #$msg->add_option($OPT_RAPID_COMMIT, ""); # timeout parameters, from DOCSIS my $IRT = $SOL_TIMEOUT; my $MRT = $SOL_MAX_RT; my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 my $MRD = 0; # sleep a random amount of time between 0 and 1 second, required by RFC 3315 # XXX: this seems pretty stupid sleep(rand($SOL_MAX_DELAY)); my $RT; my $count = 0; my $mrd_end_time; if ($MRD != 0) { $mrd_end_time = time() + $MRD; } my $reply_msg; do { # create our socket, and send our Solicit socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); my $packet = $msg->packet(); my $send_ret = send(SOCK, $packet, 0, pack_sockaddr_in6($server_port, $addr)); if (not defined($send_ret)) { printf STDERR "Error \%d sending DHCPv6 Solicit message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } elsif ($send_ret != length($packet)) { print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; exit(1); } $count++; my $RAND = rand(0.2) - 0.1; if (defined $RT) { $RT = 2*$RT + $RAND*$RT; if (($RT > $MRT) && ($MRT != 0)) { $RT = $MRT + $RAND*$RT; } } else { $RT = $IRT + $RAND*$IRT; } my $rt_end_time = time() + $RT; if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { $rt_end_time = $mrd_end_time; } for (;;) { my $timeout = $rt_end_time - time(); if ($timeout < 0) { # print STDERR "Timeout waiting for DHCPv6 Advertise ", # "or Reply message.\n"; last; } my @ready = IO::Select->new(\*SOCK)->can_read($timeout); if (@ready) { my $reply; my $recv_ret; $recv_ret = recv(SOCK, $reply, 1500, 0); if (not defined $recv_ret) { printf STDERR "Error \%d receiving DHCPv6 " . "message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } $reply_msg = dhcp_client::msg::decode($reply, 1); if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || ($reply_msg->{msg_type} == $MSG_REPLY)) { last; } } } } until ($reply_msg || (($MRC != 0) && ($count > $MRC)) || (defined($mrd_end_time) && ($mrd_end_time > time()))); unless ($reply_msg) { if (($MRC != 0) && ($count >= $MRC)) { print STDERR "No reply after maximum retransmission count.\n"; } else { print STDERR "No reply after maximum retransmission duration.\n"; } } if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { print "Got DHCPv6 Reply message.\n"; #print Dumper($reply_msg), "\n"; exit(0); } #print Dumper($msg), "\n"; #print Dumper($msg->packet()), "\n"; # #print "packet length: ", length($msg->packet()), "\n"; dhcp-4.4.1/tests/DHCPv6/280-release-nohost.pl000644 000765 000024 00000010707 13243301226 020650 0ustar00tmarkstaff000000 000000 #! /usr/bin/perl -w # Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # Internet Systems Consortium, Inc. # 950 Charter Street # Redwood City, CA 94063 # # https://www.isc.org/ use strict; use English; use Time::HiRes qw( sleep ); use Socket; use Socket6; use IO::Select; use dhcp_client; # XXX: for debugging use Data::Dumper; # not-yet-standard options my $OPT_TIME_SERVERS = 40; my $OPT_TIME_OFFSET = 41; # DOCSIS sub-options my $DOCSIS_OPT_ORO = 1; # 2 to 31 are reserved my $DOCSIS_OPT_TFTP_SERVERS = 32; my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; my $DOCSIS_OPT_SYSLOG_SERVERS = 34; my $DOCSIS_OPT_TLV5 = 35; my $DOCSIS_OPT_DEVICE_ID = 36; my $DOCSIS_OPT_CCC = 37; my $DOCSIS_OPT_VERS = 38; # well-known addresses my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; my $All_DHCP_Servers = "ff05::1:3"; # ports my $client_port = 546; my $server_port = 547; # create a new message my $msg = dhcp_client::msg->new($MSG_RELEASE); # add the Client Identifier (required by DOCSIS and RFC 3315) $msg->add_option($OPT_CLIENTID, dhcp_client::duid()); # add the Server identifier (required by RFC 3315) $msg->add_option($OPT_SERVERID, "InfiniteEntropy"); # add Elapsed Time, set to 0 on first packet (required by RFC 3315) $msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); # timeout parameters, from DOCSIS my $IRT = $SOL_TIMEOUT; my $MRT = $SOL_MAX_RT; my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 my $MRD = 0; # sleep a random amount of time between 0 and 1 second, required by RFC 3315 # XXX: this seems pretty stupid sleep(rand($SOL_MAX_DELAY)); my $RT; my $count = 0; my $mrd_end_time; if ($MRD != 0) { $mrd_end_time = time() + $MRD; } my $reply_msg; do { # create our socket, and send our message socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); my $packet = $msg->packet(); my $send_ret = send(SOCK, $packet, 0, pack_sockaddr_in6($server_port, $addr)); if (not defined($send_ret)) { printf STDERR "Error \%d sending DHCPv6 message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } elsif ($send_ret != length($packet)) { print STDERR "Unable to send entire DHCPv6 message.\n"; exit(1); } $count++; my $RAND = rand(0.2) - 0.1; if (defined $RT) { $RT = 2*$RT + $RAND*$RT; if (($RT > $MRT) && ($MRT != 0)) { $RT = $MRT + $RAND*$RT; } } else { $RT = $IRT + $RAND*$IRT; } my $rt_end_time = time() + $RT; if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { $rt_end_time = $mrd_end_time; } for (;;) { my $timeout = $rt_end_time - time(); if ($timeout < 0) { # print STDERR "Timeout waiting for DHCPv6 Advertise ", # "or Reply message.\n"; last; } my @ready = IO::Select->new(\*SOCK)->can_read($timeout); if (@ready) { my $reply; my $recv_ret; $recv_ret = recv(SOCK, $reply, 1500, 0); if (not defined $recv_ret) { printf STDERR "Error \%d receiving DHCPv6 " . "message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } $reply_msg = dhcp_client::msg::decode($reply, 1); if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || ($reply_msg->{msg_type} == $MSG_REPLY)) { last; } } } } until ($reply_msg || (($MRC != 0) && ($count > $MRC)) || (defined($mrd_end_time) && ($mrd_end_time > time()))); unless ($reply_msg) { if (($MRC != 0) && ($count >= $MRC)) { print STDERR "No reply after maximum retransmission count.\n"; } else { print STDERR "No reply after maximum retransmission duration.\n"; } } if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { print "Got DHCPv6 Reply message.\n"; exit(0); } #$Data::Dumper::Useqq = 1; #print Dumper($msg), "\n"; #print Dumper($msg->packet()), "\n"; # #print "packet length: ", length($msg->packet()), "\n"; dhcp-4.4.1/tests/DHCPv6/281-release-bad-address.pl000644 000765 000024 00000011650 13243301226 021506 0ustar00tmarkstaff000000 000000 #! /usr/bin/perl -w # Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # Internet Systems Consortium, Inc. # 950 Charter Street # Redwood City, CA 94063 # # https://www.isc.org/ use strict; use English; use Time::HiRes qw( sleep ); use Socket; use Socket6; use IO::Select; use dhcp_client; # XXX: for debugging use Data::Dumper; # not-yet-standard options my $OPT_TIME_SERVERS = 40; my $OPT_TIME_OFFSET = 41; # DOCSIS sub-options my $DOCSIS_OPT_ORO = 1; # 2 to 31 are reserved my $DOCSIS_OPT_TFTP_SERVERS = 32; my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; my $DOCSIS_OPT_SYSLOG_SERVERS = 34; my $DOCSIS_OPT_TLV5 = 35; my $DOCSIS_OPT_DEVICE_ID = 36; my $DOCSIS_OPT_CCC = 37; my $DOCSIS_OPT_VERS = 38; # well-known addresses my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; my $All_DHCP_Servers = "ff05::1:3"; # ports my $client_port = 546; my $server_port = 547; # create a new Solicit message my $msg = dhcp_client::msg->new($MSG_RELEASE); # add the Client Identifier (required by DOCSIS and RFC 3315) $msg->add_option($OPT_CLIENTID, dhcp_client::duid()); # add the Server identifier (required by RFC 3315) $msg->add_option($OPT_SERVERID, "InfiniteEntropy"); # add Elapsed Time, set to 0 on first packet (required by RFC 3315) $msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); # add IA_NA for each interface (required by DOCSIS and RFC 3315) # XXX: should this be a single interface only? my $iaid = 0; foreach my $iface (dhcp_client::iface()) { my $iaaddr_option = inet_pton(AF_INET6, "1:2:3:4::"); $iaaddr_option .= pack("NN", 0, 0); my $option_data = pack("NNN", ++$iaid, 0, 0); $option_data .= pack("nn", $OPT_IAADDR, length($iaaddr_option)-8); $option_data .= $iaaddr_option; $msg->add_option($OPT_IA_NA, $option_data); } # timeout parameters, from DOCSIS my $IRT = $SOL_TIMEOUT; my $MRT = $SOL_MAX_RT; my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 my $MRD = 0; # sleep a random amount of time between 0 and 1 second, required by RFC 3315 # XXX: this seems pretty stupid sleep(rand($SOL_MAX_DELAY)); my $RT; my $count = 0; my $mrd_end_time; if ($MRD != 0) { $mrd_end_time = time() + $MRD; } my $reply_msg; do { # create our socket, and send our Solicit socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); my $packet = $msg->packet(); my $send_ret = send(SOCK, $packet, 0, pack_sockaddr_in6($server_port, $addr)); if (not defined($send_ret)) { printf STDERR "Error \%d sending DHCPv6 Solicit message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } elsif ($send_ret != length($packet)) { print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; exit(1); } $count++; my $RAND = rand(0.2) - 0.1; if (defined $RT) { $RT = 2*$RT + $RAND*$RT; if (($RT > $MRT) && ($MRT != 0)) { $RT = $MRT + $RAND*$RT; } } else { $RT = $IRT + $RAND*$IRT; } my $rt_end_time = time() + $RT; if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { $rt_end_time = $mrd_end_time; } for (;;) { my $timeout = $rt_end_time - time(); if ($timeout < 0) { # print STDERR "Timeout waiting for DHCPv6 Advertise ", # "or Reply message.\n"; last; } my @ready = IO::Select->new(\*SOCK)->can_read($timeout); if (@ready) { my $reply; my $recv_ret; $recv_ret = recv(SOCK, $reply, 1500, 0); if (not defined $recv_ret) { printf STDERR "Error \%d receiving DHCPv6 " . "message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } $reply_msg = dhcp_client::msg::decode($reply, 1); if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || ($reply_msg->{msg_type} == $MSG_REPLY)) { last; } } } } until ($reply_msg || (($MRC != 0) && ($count > $MRC)) || (defined($mrd_end_time) && ($mrd_end_time > time()))); unless ($reply_msg) { if (($MRC != 0) && ($count >= $MRC)) { print STDERR "No reply after maximum retransmission count.\n"; } else { print STDERR "No reply after maximum retransmission duration.\n"; } } if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { print "Got DHCPv6 Reply message.\n"; exit(0); } #$Data::Dumper::Useqq = 1; #print Dumper($msg), "\n"; #print Dumper($msg->packet()), "\n"; # #print "packet length: ", length($msg->packet()), "\n"; dhcp-4.4.1/tests/DHCPv6/282-release-no-address.pl000644 000765 000024 00000011347 13243301226 021400 0ustar00tmarkstaff000000 000000 #! /usr/bin/perl -w # Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # Internet Systems Consortium, Inc. # 950 Charter Street # Redwood City, CA 94063 # # https://www.isc.org/ use strict; use English; use Time::HiRes qw( sleep ); use Socket; use Socket6; use IO::Select; use dhcp_client; # XXX: for debugging use Data::Dumper; # not-yet-standard options my $OPT_TIME_SERVERS = 40; my $OPT_TIME_OFFSET = 41; # DOCSIS sub-options my $DOCSIS_OPT_ORO = 1; # 2 to 31 are reserved my $DOCSIS_OPT_TFTP_SERVERS = 32; my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; my $DOCSIS_OPT_SYSLOG_SERVERS = 34; my $DOCSIS_OPT_TLV5 = 35; my $DOCSIS_OPT_DEVICE_ID = 36; my $DOCSIS_OPT_CCC = 37; my $DOCSIS_OPT_VERS = 38; # well-known addresses my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; my $All_DHCP_Servers = "ff05::1:3"; # ports my $client_port = 546; my $server_port = 547; # create a new Solicit message my $msg = dhcp_client::msg->new($MSG_RELEASE); # add the Client Identifier (required by DOCSIS and RFC 3315) $msg->add_option($OPT_CLIENTID, dhcp_client::duid()); # add the Server identifier (required by RFC 3315) $msg->add_option($OPT_SERVERID, "InfiniteEntropy"); # add Elapsed Time, set to 0 on first packet (required by RFC 3315) $msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); # add IA_NA for each interface (required by DOCSIS and RFC 3315) # XXX: should this be a single interface only? my $iaid = 0; foreach my $iface (dhcp_client::iface()) { my $option_data = pack("NNN", ++$iaid, 0, 0); $msg->add_option($OPT_IA_NA, $option_data); } # timeout parameters, from DOCSIS my $IRT = $SOL_TIMEOUT; my $MRT = $SOL_MAX_RT; my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 my $MRD = 0; # sleep a random amount of time between 0 and 1 second, required by RFC 3315 # XXX: this seems pretty stupid sleep(rand($SOL_MAX_DELAY)); my $RT; my $count = 0; my $mrd_end_time; if ($MRD != 0) { $mrd_end_time = time() + $MRD; } my $reply_msg; do { # create our socket, and send our Solicit socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); my $packet = $msg->packet(); my $send_ret = send(SOCK, $packet, 0, pack_sockaddr_in6($server_port, $addr)); if (not defined($send_ret)) { printf STDERR "Error \%d sending DHCPv6 Solicit message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } elsif ($send_ret != length($packet)) { print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; exit(1); } $count++; my $RAND = rand(0.2) - 0.1; if (defined $RT) { $RT = 2*$RT + $RAND*$RT; if (($RT > $MRT) && ($MRT != 0)) { $RT = $MRT + $RAND*$RT; } } else { $RT = $IRT + $RAND*$IRT; } my $rt_end_time = time() + $RT; if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { $rt_end_time = $mrd_end_time; } for (;;) { my $timeout = $rt_end_time - time(); if ($timeout < 0) { # print STDERR "Timeout waiting for DHCPv6 Advertise ", # "or Reply message.\n"; last; } my @ready = IO::Select->new(\*SOCK)->can_read($timeout); if (@ready) { my $reply; my $recv_ret; $recv_ret = recv(SOCK, $reply, 1500, 0); if (not defined $recv_ret) { printf STDERR "Error \%d receiving DHCPv6 " . "message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } $reply_msg = dhcp_client::msg::decode($reply, 1); if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || ($reply_msg->{msg_type} == $MSG_REPLY)) { last; } } } } until ($reply_msg || (($MRC != 0) && ($count > $MRC)) || (defined($mrd_end_time) && ($mrd_end_time > time()))); unless ($reply_msg) { if (($MRC != 0) && ($count >= $MRC)) { print STDERR "No reply after maximum retransmission count.\n"; } else { print STDERR "No reply after maximum retransmission duration.\n"; } } if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { print "Got DHCPv6 Reply message.\n"; exit(0); } #$Data::Dumper::Useqq = 1; #print Dumper($msg), "\n"; #print Dumper($msg->packet()), "\n"; # #print "packet length: ", length($msg->packet()), "\n"; dhcp-4.4.1/tests/DHCPv6/283-release.pl000644 000765 000024 00000012135 13243301226 017340 0ustar00tmarkstaff000000 000000 #! /usr/bin/perl -w # Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # Internet Systems Consortium, Inc. # 950 Charter Street # Redwood City, CA 94063 # # https://www.isc.org/ use strict; use English; use Time::HiRes qw( sleep ); use Socket; use Socket6; use IO::Select; use dhcp_client; # XXX: for debugging use Data::Dumper; # not-yet-standard options my $OPT_TIME_SERVERS = 40; my $OPT_TIME_OFFSET = 41; # DOCSIS sub-options my $DOCSIS_OPT_ORO = 1; # 2 to 31 are reserved my $DOCSIS_OPT_TFTP_SERVERS = 32; my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; my $DOCSIS_OPT_SYSLOG_SERVERS = 34; my $DOCSIS_OPT_TLV5 = 35; my $DOCSIS_OPT_DEVICE_ID = 36; my $DOCSIS_OPT_CCC = 37; my $DOCSIS_OPT_VERS = 38; # well-known addresses my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; my $All_DHCP_Servers = "ff05::1:3"; # ports my $client_port = 546; my $server_port = 547; # create a new Solicit message my $msg = dhcp_client::msg->new($MSG_RELEASE); # add the Client Identifier (required by DOCSIS and RFC 3315) my $client_id = "\x00\x01\x00\x01\x0c\x00\xa1\x41\x00\x06\x5b\x50\x99\xf6"; $msg->add_option($OPT_CLIENTID, $client_id); #$msg->add_option($OPT_CLIENTID, dhcp_client::duid(3)); # add the Server identifier (required by RFC 3315) $msg->add_option($OPT_SERVERID, "InfiniteEntropy"); # add Elapsed Time, set to 0 on first packet (required by RFC 3315) $msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); # add IA_NA for each interface (required by DOCSIS and RFC 3315) # XXX: should this be a single interface only? my $iaid = 0; foreach my $iface (dhcp_client::iface()) { my $iaaddr_option = inet_pton(AF_INET6, "3ffe:aaaa:aaaa:aaaa::ffff"); $iaaddr_option .= pack("NN", 0, 0); my $option_data = pack("NNN", ++$iaid, 0, 0); $option_data .= pack("nn", $OPT_IAADDR, length($iaaddr_option)-8); $option_data .= $iaaddr_option; $msg->add_option($OPT_IA_NA, $option_data); } # timeout parameters, from DOCSIS my $IRT = $SOL_TIMEOUT; my $MRT = $SOL_MAX_RT; my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 my $MRD = 0; # sleep a random amount of time between 0 and 1 second, required by RFC 3315 # XXX: this seems pretty stupid sleep(rand($SOL_MAX_DELAY)); my $RT; my $count = 0; my $mrd_end_time; if ($MRD != 0) { $mrd_end_time = time() + $MRD; } my $reply_msg; do { # create our socket, and send our Solicit socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); my $packet = $msg->packet(); my $send_ret = send(SOCK, $packet, 0, pack_sockaddr_in6($server_port, $addr)); if (not defined($send_ret)) { printf STDERR "Error \%d sending DHCPv6 Solicit message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } elsif ($send_ret != length($packet)) { print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; exit(1); } $count++; my $RAND = rand(0.2) - 0.1; if (defined $RT) { $RT = 2*$RT + $RAND*$RT; if (($RT > $MRT) && ($MRT != 0)) { $RT = $MRT + $RAND*$RT; } } else { $RT = $IRT + $RAND*$IRT; } my $rt_end_time = time() + $RT; if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { $rt_end_time = $mrd_end_time; } for (;;) { my $timeout = $rt_end_time - time(); if ($timeout < 0) { # print STDERR "Timeout waiting for DHCPv6 Advertise ", # "or Reply message.\n"; last; } my @ready = IO::Select->new(\*SOCK)->can_read($timeout); if (@ready) { my $reply; my $recv_ret; $recv_ret = recv(SOCK, $reply, 1500, 0); if (not defined $recv_ret) { printf STDERR "Error \%d receiving DHCPv6 " . "message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } $reply_msg = dhcp_client::msg::decode($reply, 1); if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || ($reply_msg->{msg_type} == $MSG_REPLY)) { last; } } } } until ($reply_msg || (($MRC != 0) && ($count > $MRC)) || (defined($mrd_end_time) && ($mrd_end_time > time()))); unless ($reply_msg) { if (($MRC != 0) && ($count >= $MRC)) { print STDERR "No reply after maximum retransmission count.\n"; } else { print STDERR "No reply after maximum retransmission duration.\n"; } } if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { print "Got DHCPv6 Reply message.\n"; exit(0); } #$Data::Dumper::Useqq = 1; #print Dumper($msg), "\n"; #print Dumper($msg->packet()), "\n"; # #print "packet length: ", length($msg->packet()), "\n"; dhcp-4.4.1/tests/DHCPv6/290-decline-nohost.pl000644 000765 000024 00000010737 13243301226 020637 0ustar00tmarkstaff000000 000000 #! /usr/bin/perl -w # Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # Internet Systems Consortium, Inc. # 950 Charter Street # Redwood City, CA 94063 # # https://www.isc.org/ use strict; use English; use Time::HiRes qw( sleep ); use Socket; use Socket6; use IO::Select; use dhcp_client; # XXX: for debugging use Data::Dumper; # not-yet-standard options my $OPT_TIME_SERVERS = 40; my $OPT_TIME_OFFSET = 41; # DOCSIS sub-options my $DOCSIS_OPT_ORO = 1; # 2 to 31 are reserved my $DOCSIS_OPT_TFTP_SERVERS = 32; my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; my $DOCSIS_OPT_SYSLOG_SERVERS = 34; my $DOCSIS_OPT_TLV5 = 35; my $DOCSIS_OPT_DEVICE_ID = 36; my $DOCSIS_OPT_CCC = 37; my $DOCSIS_OPT_VERS = 38; # well-known addresses my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; my $All_DHCP_Servers = "ff05::1:3"; # ports my $client_port = 546; my $server_port = 547; # create a new Solicit message my $msg = dhcp_client::msg->new($MSG_DECLINE); # add the Client Identifier (required by DOCSIS and RFC 3315) $msg->add_option($OPT_CLIENTID, dhcp_client::duid()); # add the Server identifier (required by RFC 3315) $msg->add_option($OPT_SERVERID, "InfiniteEntropy"); # add Elapsed Time, set to 0 on first packet (required by RFC 3315) $msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); # timeout parameters, from DOCSIS my $IRT = $SOL_TIMEOUT; my $MRT = $SOL_MAX_RT; my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 my $MRD = 0; # sleep a random amount of time between 0 and 1 second, required by RFC 3315 # XXX: this seems pretty stupid sleep(rand($SOL_MAX_DELAY)); my $RT; my $count = 0; my $mrd_end_time; if ($MRD != 0) { $mrd_end_time = time() + $MRD; } my $reply_msg; do { # create our socket, and send our Solicit socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); my $packet = $msg->packet(); my $send_ret = send(SOCK, $packet, 0, pack_sockaddr_in6($server_port, $addr)); if (not defined($send_ret)) { printf STDERR "Error \%d sending DHCPv6 Solicit message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } elsif ($send_ret != length($packet)) { print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; exit(1); } $count++; my $RAND = rand(0.2) - 0.1; if (defined $RT) { $RT = 2*$RT + $RAND*$RT; if (($RT > $MRT) && ($MRT != 0)) { $RT = $MRT + $RAND*$RT; } } else { $RT = $IRT + $RAND*$IRT; } my $rt_end_time = time() + $RT; if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { $rt_end_time = $mrd_end_time; } for (;;) { my $timeout = $rt_end_time - time(); if ($timeout < 0) { # print STDERR "Timeout waiting for DHCPv6 Advertise ", # "or Reply message.\n"; last; } my @ready = IO::Select->new(\*SOCK)->can_read($timeout); if (@ready) { my $reply; my $recv_ret; $recv_ret = recv(SOCK, $reply, 1500, 0); if (not defined $recv_ret) { printf STDERR "Error \%d receiving DHCPv6 " . "message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } $reply_msg = dhcp_client::msg::decode($reply, 1); if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || ($reply_msg->{msg_type} == $MSG_REPLY)) { last; } } } } until ($reply_msg || (($MRC != 0) && ($count > $MRC)) || (defined($mrd_end_time) && ($mrd_end_time > time()))); unless ($reply_msg) { if (($MRC != 0) && ($count >= $MRC)) { print STDERR "No reply after maximum retransmission count.\n"; } else { print STDERR "No reply after maximum retransmission duration.\n"; } } if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { print "Got DHCPv6 Reply message.\n"; exit(0); } #$Data::Dumper::Useqq = 1; #print Dumper($msg), "\n"; #print Dumper($msg->packet()), "\n"; # #print "packet length: ", length($msg->packet()), "\n"; dhcp-4.4.1/tests/DHCPv6/291-decline-bad-address.pl000644 000765 000024 00000011650 13243301226 021472 0ustar00tmarkstaff000000 000000 #! /usr/bin/perl -w # Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # Internet Systems Consortium, Inc. # 950 Charter Street # Redwood City, CA 94063 # # https://www.isc.org/ use strict; use English; use Time::HiRes qw( sleep ); use Socket; use Socket6; use IO::Select; use dhcp_client; # XXX: for debugging use Data::Dumper; # not-yet-standard options my $OPT_TIME_SERVERS = 40; my $OPT_TIME_OFFSET = 41; # DOCSIS sub-options my $DOCSIS_OPT_ORO = 1; # 2 to 31 are reserved my $DOCSIS_OPT_TFTP_SERVERS = 32; my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; my $DOCSIS_OPT_SYSLOG_SERVERS = 34; my $DOCSIS_OPT_TLV5 = 35; my $DOCSIS_OPT_DEVICE_ID = 36; my $DOCSIS_OPT_CCC = 37; my $DOCSIS_OPT_VERS = 38; # well-known addresses my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; my $All_DHCP_Servers = "ff05::1:3"; # ports my $client_port = 546; my $server_port = 547; # create a new Solicit message my $msg = dhcp_client::msg->new($MSG_DECLINE); # add the Client Identifier (required by DOCSIS and RFC 3315) $msg->add_option($OPT_CLIENTID, dhcp_client::duid()); # add the Server identifier (required by RFC 3315) $msg->add_option($OPT_SERVERID, "InfiniteEntropy"); # add Elapsed Time, set to 0 on first packet (required by RFC 3315) $msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); # add IA_NA for each interface (required by DOCSIS and RFC 3315) # XXX: should this be a single interface only? my $iaid = 0; foreach my $iface (dhcp_client::iface()) { my $iaaddr_option = inet_pton(AF_INET6, "1:2:3:4::"); $iaaddr_option .= pack("NN", 0, 0); my $option_data = pack("NNN", ++$iaid, 0, 0); $option_data .= pack("nn", $OPT_IAADDR, length($iaaddr_option)-8); $option_data .= $iaaddr_option; $msg->add_option($OPT_IA_NA, $option_data); } # timeout parameters, from DOCSIS my $IRT = $SOL_TIMEOUT; my $MRT = $SOL_MAX_RT; my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 my $MRD = 0; # sleep a random amount of time between 0 and 1 second, required by RFC 3315 # XXX: this seems pretty stupid sleep(rand($SOL_MAX_DELAY)); my $RT; my $count = 0; my $mrd_end_time; if ($MRD != 0) { $mrd_end_time = time() + $MRD; } my $reply_msg; do { # create our socket, and send our Solicit socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); my $packet = $msg->packet(); my $send_ret = send(SOCK, $packet, 0, pack_sockaddr_in6($server_port, $addr)); if (not defined($send_ret)) { printf STDERR "Error \%d sending DHCPv6 Solicit message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } elsif ($send_ret != length($packet)) { print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; exit(1); } $count++; my $RAND = rand(0.2) - 0.1; if (defined $RT) { $RT = 2*$RT + $RAND*$RT; if (($RT > $MRT) && ($MRT != 0)) { $RT = $MRT + $RAND*$RT; } } else { $RT = $IRT + $RAND*$IRT; } my $rt_end_time = time() + $RT; if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { $rt_end_time = $mrd_end_time; } for (;;) { my $timeout = $rt_end_time - time(); if ($timeout < 0) { # print STDERR "Timeout waiting for DHCPv6 Advertise ", # "or Reply message.\n"; last; } my @ready = IO::Select->new(\*SOCK)->can_read($timeout); if (@ready) { my $reply; my $recv_ret; $recv_ret = recv(SOCK, $reply, 1500, 0); if (not defined $recv_ret) { printf STDERR "Error \%d receiving DHCPv6 " . "message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } $reply_msg = dhcp_client::msg::decode($reply, 1); if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || ($reply_msg->{msg_type} == $MSG_REPLY)) { last; } } } } until ($reply_msg || (($MRC != 0) && ($count > $MRC)) || (defined($mrd_end_time) && ($mrd_end_time > time()))); unless ($reply_msg) { if (($MRC != 0) && ($count >= $MRC)) { print STDERR "No reply after maximum retransmission count.\n"; } else { print STDERR "No reply after maximum retransmission duration.\n"; } } if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { print "Got DHCPv6 Reply message.\n"; exit(0); } #$Data::Dumper::Useqq = 1; #print Dumper($msg), "\n"; #print Dumper($msg->packet()), "\n"; # #print "packet length: ", length($msg->packet()), "\n"; dhcp-4.4.1/tests/DHCPv6/292-decline-no-address.pl000644 000765 000024 00000011347 13243301226 021364 0ustar00tmarkstaff000000 000000 #! /usr/bin/perl -w # Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # Internet Systems Consortium, Inc. # 950 Charter Street # Redwood City, CA 94063 # # https://www.isc.org/ use strict; use English; use Time::HiRes qw( sleep ); use Socket; use Socket6; use IO::Select; use dhcp_client; # XXX: for debugging use Data::Dumper; # not-yet-standard options my $OPT_TIME_SERVERS = 40; my $OPT_TIME_OFFSET = 41; # DOCSIS sub-options my $DOCSIS_OPT_ORO = 1; # 2 to 31 are reserved my $DOCSIS_OPT_TFTP_SERVERS = 32; my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; my $DOCSIS_OPT_SYSLOG_SERVERS = 34; my $DOCSIS_OPT_TLV5 = 35; my $DOCSIS_OPT_DEVICE_ID = 36; my $DOCSIS_OPT_CCC = 37; my $DOCSIS_OPT_VERS = 38; # well-known addresses my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; my $All_DHCP_Servers = "ff05::1:3"; # ports my $client_port = 546; my $server_port = 547; # create a new Solicit message my $msg = dhcp_client::msg->new($MSG_DECLINE); # add the Client Identifier (required by DOCSIS and RFC 3315) $msg->add_option($OPT_CLIENTID, dhcp_client::duid()); # add the Server identifier (required by RFC 3315) $msg->add_option($OPT_SERVERID, "InfiniteEntropy"); # add Elapsed Time, set to 0 on first packet (required by RFC 3315) $msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); # add IA_NA for each interface (required by DOCSIS and RFC 3315) # XXX: should this be a single interface only? my $iaid = 0; foreach my $iface (dhcp_client::iface()) { my $option_data = pack("NNN", ++$iaid, 0, 0); $msg->add_option($OPT_IA_NA, $option_data); } # timeout parameters, from DOCSIS my $IRT = $SOL_TIMEOUT; my $MRT = $SOL_MAX_RT; my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 my $MRD = 0; # sleep a random amount of time between 0 and 1 second, required by RFC 3315 # XXX: this seems pretty stupid sleep(rand($SOL_MAX_DELAY)); my $RT; my $count = 0; my $mrd_end_time; if ($MRD != 0) { $mrd_end_time = time() + $MRD; } my $reply_msg; do { # create our socket, and send our Solicit socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); my $packet = $msg->packet(); my $send_ret = send(SOCK, $packet, 0, pack_sockaddr_in6($server_port, $addr)); if (not defined($send_ret)) { printf STDERR "Error \%d sending DHCPv6 Solicit message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } elsif ($send_ret != length($packet)) { print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; exit(1); } $count++; my $RAND = rand(0.2) - 0.1; if (defined $RT) { $RT = 2*$RT + $RAND*$RT; if (($RT > $MRT) && ($MRT != 0)) { $RT = $MRT + $RAND*$RT; } } else { $RT = $IRT + $RAND*$IRT; } my $rt_end_time = time() + $RT; if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { $rt_end_time = $mrd_end_time; } for (;;) { my $timeout = $rt_end_time - time(); if ($timeout < 0) { # print STDERR "Timeout waiting for DHCPv6 Advertise ", # "or Reply message.\n"; last; } my @ready = IO::Select->new(\*SOCK)->can_read($timeout); if (@ready) { my $reply; my $recv_ret; $recv_ret = recv(SOCK, $reply, 1500, 0); if (not defined $recv_ret) { printf STDERR "Error \%d receiving DHCPv6 " . "message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } $reply_msg = dhcp_client::msg::decode($reply, 1); if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || ($reply_msg->{msg_type} == $MSG_REPLY)) { last; } } } } until ($reply_msg || (($MRC != 0) && ($count > $MRC)) || (defined($mrd_end_time) && ($mrd_end_time > time()))); unless ($reply_msg) { if (($MRC != 0) && ($count >= $MRC)) { print STDERR "No reply after maximum retransmission count.\n"; } else { print STDERR "No reply after maximum retransmission duration.\n"; } } if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { print "Got DHCPv6 Reply message.\n"; exit(0); } #$Data::Dumper::Useqq = 1; #print Dumper($msg), "\n"; #print Dumper($msg->packet()), "\n"; # #print "packet length: ", length($msg->packet()), "\n"; dhcp-4.4.1/tests/DHCPv6/293-decline.pl000644 000765 000024 00000012135 13243301226 017324 0ustar00tmarkstaff000000 000000 #! /usr/bin/perl -w # Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # Internet Systems Consortium, Inc. # 950 Charter Street # Redwood City, CA 94063 # # https://www.isc.org/ use strict; use English; use Time::HiRes qw( sleep ); use Socket; use Socket6; use IO::Select; use dhcp_client; # XXX: for debugging use Data::Dumper; # not-yet-standard options my $OPT_TIME_SERVERS = 40; my $OPT_TIME_OFFSET = 41; # DOCSIS sub-options my $DOCSIS_OPT_ORO = 1; # 2 to 31 are reserved my $DOCSIS_OPT_TFTP_SERVERS = 32; my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; my $DOCSIS_OPT_SYSLOG_SERVERS = 34; my $DOCSIS_OPT_TLV5 = 35; my $DOCSIS_OPT_DEVICE_ID = 36; my $DOCSIS_OPT_CCC = 37; my $DOCSIS_OPT_VERS = 38; # well-known addresses my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; my $All_DHCP_Servers = "ff05::1:3"; # ports my $client_port = 546; my $server_port = 547; # create a new Solicit message my $msg = dhcp_client::msg->new($MSG_DECLINE); # add the Client Identifier (required by DOCSIS and RFC 3315) my $client_id = "\x00\x01\x00\x01\x0c\x00\xa1\x41\x00\x06\x5b\x50\x99\xf6"; $msg->add_option($OPT_CLIENTID, $client_id); #$msg->add_option($OPT_CLIENTID, dhcp_client::duid(3)); # add the Server identifier (required by RFC 3315) $msg->add_option($OPT_SERVERID, "InfiniteEntropy"); # add Elapsed Time, set to 0 on first packet (required by RFC 3315) $msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); # add IA_NA for each interface (required by DOCSIS and RFC 3315) # XXX: should this be a single interface only? my $iaid = 0; foreach my $iface (dhcp_client::iface()) { my $iaaddr_option = inet_pton(AF_INET6, "3ffe:aaaa:aaaa:aaaa::ffff"); $iaaddr_option .= pack("NN", 0, 0); my $option_data = pack("NNN", ++$iaid, 0, 0); $option_data .= pack("nn", $OPT_IAADDR, length($iaaddr_option)-8); $option_data .= $iaaddr_option; $msg->add_option($OPT_IA_NA, $option_data); } # timeout parameters, from DOCSIS my $IRT = $SOL_TIMEOUT; my $MRT = $SOL_MAX_RT; my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 my $MRD = 0; # sleep a random amount of time between 0 and 1 second, required by RFC 3315 # XXX: this seems pretty stupid sleep(rand($SOL_MAX_DELAY)); my $RT; my $count = 0; my $mrd_end_time; if ($MRD != 0) { $mrd_end_time = time() + $MRD; } my $reply_msg; do { # create our socket, and send our Solicit socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); my $packet = $msg->packet(); my $send_ret = send(SOCK, $packet, 0, pack_sockaddr_in6($server_port, $addr)); if (not defined($send_ret)) { printf STDERR "Error \%d sending DHCPv6 Solicit message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } elsif ($send_ret != length($packet)) { print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; exit(1); } $count++; my $RAND = rand(0.2) - 0.1; if (defined $RT) { $RT = 2*$RT + $RAND*$RT; if (($RT > $MRT) && ($MRT != 0)) { $RT = $MRT + $RAND*$RT; } } else { $RT = $IRT + $RAND*$IRT; } my $rt_end_time = time() + $RT; if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { $rt_end_time = $mrd_end_time; } for (;;) { my $timeout = $rt_end_time - time(); if ($timeout < 0) { # print STDERR "Timeout waiting for DHCPv6 Advertise ", # "or Reply message.\n"; last; } my @ready = IO::Select->new(\*SOCK)->can_read($timeout); if (@ready) { my $reply; my $recv_ret; $recv_ret = recv(SOCK, $reply, 1500, 0); if (not defined $recv_ret) { printf STDERR "Error \%d receiving DHCPv6 " . "message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } $reply_msg = dhcp_client::msg::decode($reply, 1); if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || ($reply_msg->{msg_type} == $MSG_REPLY)) { last; } } } } until ($reply_msg || (($MRC != 0) && ($count > $MRC)) || (defined($mrd_end_time) && ($mrd_end_time > time()))); unless ($reply_msg) { if (($MRC != 0) && ($count >= $MRC)) { print STDERR "No reply after maximum retransmission count.\n"; } else { print STDERR "No reply after maximum retransmission duration.\n"; } } if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { print "Got DHCPv6 Reply message.\n"; exit(0); } #$Data::Dumper::Useqq = 1; #print Dumper($msg), "\n"; #print Dumper($msg->packet()), "\n"; # #print "packet length: ", length($msg->packet()), "\n"; dhcp-4.4.1/tests/DHCPv6/dhcp_client.pm000644 000765 000024 00000026327 13243301226 017673 0ustar00tmarkstaff000000 000000 #! /usr/bin/perl -w # Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # Internet Systems Consortium, Inc. # 950 Charter Street # Redwood City, CA 94063 # # https://www.isc.org/ package dhcp_client; require Exporter; @ISA = qw(Exporter); # message types $MSG_SOLICIT = 1; $MSG_ADVERTISE = 2; $MSG_REQUEST = 3; $MSG_CONFIRM = 4; $MSG_RENEW = 5; $MSG_REBIND = 6; $MSG_REPLY = 7; $MSG_RELEASE = 8; $MSG_DECLINE = 9; $MSG_RECONFIGURE = 10; $MSG_INFORMATION_REQUEST = 11; $MSG_RELAY_FORW = 12; $MSG_RELAY_REPL = 13; # option numbers $OPT_CLIENTID = 1; $OPT_SERVERID = 2; $OPT_IA_NA = 3; $OPT_IA_TA = 4; $OPT_IAADDR = 5; $OPT_ORO = 6; $OPT_PREFERENCE = 7; $OPT_ELAPSED_TIME = 8; $OPT_RELAY_MSG = 9; $OPT_AUTH = 11; $OPT_UNICAST = 12; $OPT_STATUS_CODE = 13; $OPT_RAPID_COMMIT = 14; $OPT_USER_CLASS = 15; $OPT_VENDOR_CLASS = 16; $OPT_VENDOR_OPTS = 17; $OPT_INTERFACE_ID = 18; $OPT_RECONF_MSG = 19; $OPT_RECONF_ACCEPT = 20; # timeouts $SOL_MAX_DELAY = 1; $SOL_TIMEOUT = 1; $SOL_MAX_RT = 120; $REQ_TIMEOUT = 1; $REQ_MAX_RT = 30; $REQ_MAX_RC = 10; $CNF_MAX_DELAY = 1; $CNF_MAX_RT = 4; $CNF_MAX_RD = 10; $REN_TIMEOUT = 10; $REN_MAX_RT = 600; $REB_TIMEOUT = 10; $REB_MAX_RT = 600; $INF_MAX_DELAY = 1; $INF_TIMEOUT = 1; $INF_MAX_RT = 120; $REL_TIMEOUT = 1; $REL_MAX_RC = 5; $DEC_TIMEOUT = 1; $DEC_MAX_RC = 5; $REC_TIMEOUT = 2; $REC_MAX_RC = 8; $HOP_COUNT_LIMIT = 32; @EXPORT = qw( $MSG_SOLICIT $MSG_ADVERTISE $MSG_REQUEST $MSG_CONFIRM $MSG_RENEW $MSG_REBIND $MSG_REPLY $MSG_RELEASE $MSG_DECLINE $MSG_RECONFIGURE $MSG_INFORMATION_REQUEST $MSG_RELAY_FORW $MSG_RELAY_REPL $OPT_CLIENTID $OPT_SERVERID $OPT_IA_NA $OPT_IA_TA $OPT_IAADDR $OPT_ORO $OPT_PREFERENCE $OPT_ELAPSED_TIME $OPT_RELAY_MSG $OPT_AUTH $OPT_UNICAST $OPT_STATUS_CODE $OPT_RAPID_COMMIT $OPT_USER_CLASS $OPT_VENDOR_CLASS $OPT_VENDOR_OPTS $OPT_INTERFACE_ID $OPT_RECONF_MSG $OPT_RECONF_ACCEPT $SOL_MAX_DELAY $SOL_TIMEOUT $SOL_MAX_RT $REQ_TIMEOUT $REQ_MAX_RT $REQ_MAX_RC $CNF_MAX_DELAY $CNF_MAX_RT $CNF_MAX_RD $REN_TIMEOUT $REN_MAX_RT $REB_TIMEOUT $REB_MAX_RT $INF_MAX_DELAY $INF_TIMEOUT $INF_MAX_RT $REL_TIMEOUT $REL_MAX_RC $DEC_TIMEOUT $DEC_MAX_RC $REC_TIMEOUT $REC_MAX_RC $HOP_COUNT_LIMIT ); my %msg_type_num = ( MSG_SOLICIT => 1, MSG_ADVERTISE => 2, MSG_REQUEST => 3, MSG_CONFIRM => 4, MSG_RENEW => 5, MSG_REBIND => 6, MSG_REPLY => 7, MSG_RELEASE => 8, MSG_DECLINE => 9, MSG_RECONFIGURE => 10, MSG_INFORMATION_REQUEST => 11, MSG_RELAY_FORW => 12, MSG_RELAY_REPL => 13, ); my %msg_num_type = reverse(%msg_type_num); my %opt_type_num = ( OPT_CLIENTID => 1, OPT_SERVERID => 2, OPT_IA_NA => 3, OPT_IA_TA => 4, OPT_IAADDR => 5, OPT_ORO => 6, OPT_PREFERENCE => 7, OPT_ELAPSED_TIME => 8, OPT_RELAY_MSG => 9, OPT_AUTH => 11, OPT_UNICAST => 12, OPT_STATUS_CODE => 13, OPT_RAPID_COMMIT => 14, OPT_USER_CLASS => 15, OPT_VENDOR_CLASS => 16, OPT_VENDOR_OPTS => 17, OPT_INTERFACE_ID => 18, OPT_RECONF_MSG => 19, OPT_RECONF_ACCEPT => 20, ); my %opt_num_type = reverse(%opt_type_num); my %status_code_num = ( Success => 0, UnspecFail => 1, NoAddrsAvail => 2, NoBinding => 3, NotOnLink => 4, UseMulticast => 5, ); my %status_num_code = reverse(%status_code_num); my %docsis_type_num = ( CL_OPTION_ORO => 1, CL_OPTION_TFTP_SERVERS => 32, CL_OPTION_CONFIG_FILE_NAME => 33, CL_OPTION_SYSLOG_SERVERS => 34, CL_OPTION_TLV5 => 35, CL_OPTION_DEVICE_ID => 36, CL_OPTION_CCC => 37, CL_OPTION_DOCSIS_VERS => 38, ); my %docsis_num_type = reverse(%docsis_type_num); use strict; use English; use POSIX; # XXX: very Solaris-specific sub iface { my @ifaces; foreach my $fname (glob("/etc/hostname.*")) { $fname =~ s[^/etc/hostname.][]; push(@ifaces, $fname); } return wantarray() ? @ifaces : $ifaces[0]; } # XXX: very Solaris-specific sub mac_addr { my @ip_addrs; foreach my $iface (iface()) { if (`ifconfig $iface 2>/dev/null` =~ /\sinet (\S+)\s/) { push(@ip_addrs, $1); } } my @mac_addrs; foreach my $line (split(/\n/, `arp -an 2>/dev/null`)) { my @parts = split(/\s+/, $line); my $ip = $parts[1]; my $mac = $parts[-1]; if (grep { $ip eq $_ } @ip_addrs) { $mac =~ s/://g; push(@mac_addrs, $mac); } } return wantarray() ? @mac_addrs : $mac_addrs[0]; } sub mac_addr_binary { my @mac_addr = split(//, mac_addr()); my $mac_addr = join("", map { chr(hex($_)) } @mac_addr); return $mac_addr; } # DHCPv6 times start 2000-01-01 00:00:00 my $dhcp_time_base = 946684800; #{ # local $ENV{TZ} = "UTC"; # POSIX::tzset(); # $dhcp_time_base = POSIX::mktime(0, 0, 0, 1, 0, 100); #} sub dhcpv6_time { return time() - $dhcp_time_base; } sub duid { my ($type) = @_; $type = 1 unless (defined $type); if (($type == 1) || ($type == 3)) { my $mac_addr = mac_addr_binary(); if ($type == 1) { my $time = pack("N", dhcpv6_time()); return "\x00\x01\x00\x01${time}${mac_addr}"; } else { return "\x00\x03\x00\x01${mac_addr}"; } } else { die "Unknown DUID type $type requested"; } } package dhcp_client::msg; use Socket; use Socket6; sub new { my ($pkg, $msg_type, $trans_id) = @_; my $this = {}; bless $this; $this->{msg_type} = $msg_type+0; if (defined $trans_id) { $this->{trans_id} = $trans_id; } else { $this->{trans_id} = chr(rand(256)) . chr(rand(256)) . chr(rand(256)); } $this->{options} = [ ]; return $this; } sub add_option { my ($this, $num, $data) = @_; push(@{$this->{options}}, [ $num, $data ]); } sub get_option { my ($this, $num) = @_; my @options; foreach my $option (@{$this->{options}}) { if ($option->[0] == $num) { push(@options, $option->[1]); } } return wantarray() ? @options : $options[0]; } sub packed_options { my ($this) = @_; my $options = ""; foreach my $option (@{$this->{options}}) { $options .= pack("nn", $option->[0], length($option->[1])); $options .= $option->[1]; } return $options; } sub packet { my ($this) = @_; my $packet = ""; $packet .= chr($this->{msg_type}); $packet .= $this->{trans_id}; $packet .= $this->packed_options(); return $packet; } sub unpack_options { my ($options) = @_; my @parsed_options; my $p = 0; while ($p < length($options)) { my ($id, $len) = unpack("nn", substr($options, $p, 4)); push(@parsed_options, [ $id, substr($options, $p + 4, $len) ]); $p += 4 + $len; } return @parsed_options; } sub print_docsis_option { my ($num, $data, $indent) = @_; print "${indent}DOCSIS Option $num"; if ($docsis_num_type{$num}) { print " ($docsis_num_type{$num})"; } print ", length ", length($data), "\n"; return unless ($docsis_num_type{$num}); if ($docsis_num_type{$num} eq "CL_OPTION_ORO") { my $num_oro = length($data) / 2; for (my $i=0; $i<$num_oro; $i++) { my $oro_num = unpack("n", substr($data, $i*2, 2)); print "${indent} $oro_num"; if ($docsis_num_type{$oro_num}) { print " ($docsis_num_type{$oro_num})"; } print "\n"; } } elsif ($docsis_num_type{$num} eq "CL_OPTION_TFTP_SERVERS") { my $num_servers = length($data) / 16; for (my $i=0; $i<$num_servers; $i++) { my $srv = inet_ntop(AF_INET6, substr($data, $i*16, 16)); print "$indent TFTP server ", ($i+1), ": "; print uc($srv), "\n"; } } elsif ($docsis_num_type{$num} eq "CL_OPTION_CONFIG_FILE_NAME") { print "$indent Config file name: \"$data\"\n" } elsif ($docsis_num_type{$num} eq "CL_OPTION_SYSLOG_SERVERS") { my $num_servers = length($data) / 16; for (my $i=0; $i<$num_servers; $i++) { my $srv = inet_ntop(AF_INET6, substr($data, $i*16, 16)); print "$indent syslog server ", ($i+1), ": "; print uc($srv), "\n"; } } } sub print_option { my ($num, $data, $indent) = @_; print "${indent}Option $num"; if ($opt_num_type{$num}) { print " ($opt_num_type{$num})"; } print ", length ", length($data), "\n"; if ($num == $dhcp_client::OPT_ORO) { my $num_oro = length($data) / 2; for (my $i=0; $i<$num_oro; $i++) { my $oro_num = unpack("n", substr($data, $i*2, 2)); print "${indent} $oro_num"; if ($opt_num_type{$oro_num}) { print " ($opt_num_type{$oro_num})"; } print "\n"; } } elsif (($num == $dhcp_client::OPT_CLIENTID) || ($num == $dhcp_client::OPT_SERVERID)) { print $indent, " "; if (length($data) > 0) { printf '%02X', ord(substr($data, 0, 1)); for (my $i=1; $i 12) { printf "${indent} IA_NA encapsulated options:\n"; foreach my $option (unpack_options(substr($data, 12))) { print_option(@{$option}, $indent . " "); } } } elsif ($num == $dhcp_client::OPT_IAADDR) { printf "${indent} IPv6 address: \%s\n", uc(inet_ntop(AF_INET6, substr($data, 0, 16))); printf "${indent} Preferred lifetime: \%d\n", unpack("N", substr($data, 16, 4)); printf "${indent} Valid lifetime: \%d\n", unpack("N", substr($data, 20, 4)); if (length($data) > 24) { printf "${indent} IAADDR encapsulated options:\n"; foreach my $option (unpack_options(substr($data, 24))) { print_option(@{$option}, $indent . " "); } } } elsif ($num == $dhcp_client::OPT_VENDOR_OPTS) { my $enterprise_number = unpack("N", substr($data, 0, 4)); print "${indent} Enterprise number: $enterprise_number\n"; # DOCSIS if ($enterprise_number == 4491) { foreach my $option (unpack_options(substr($data, 4))) { print_docsis_option(@{$option}, $indent . " "); } } } elsif ($num == $dhcp_client::OPT_STATUS_CODE) { my $code = ord(substr($data, 0, 1)); my $msg = substr($data, 1); print "${indent} Code: $code"; if ($status_num_code{$code}) { print " ($status_num_code{$code})"; } print "\n"; print "${indent} Message: \"$msg\"\n"; } } # XXX: we aren't careful about packet boundaries and values... # DO NOT RUN ON PRODUCTION SYSTEMS!!! sub decode { my ($packet, $print) = @_; my $msg_type = ord(substr($packet, 0, 1)); my $trans_id = substr($packet, 1, 3); my $msg = dhcp_client::msg->new($msg_type, $trans_id); if ($print) { print "DHCPv6 packet\n"; print " Message type: $msg_num_type{$msg_type}\n"; printf " Transaction id: 0x\%02X\%02X\%02X\n", ord(substr($trans_id, 0, 1)), ord(substr($trans_id, 1, 1)), ord(substr($trans_id, 2, 1)); print " Options:\n"; } foreach my $option (unpack_options(substr($packet, 4))) { print_option(@{$option}, " ") if ($print); $msg->add_option(@{$option}); } return $msg; } dhcp-4.4.1/tests/DHCPv6/README000644 000765 000024 00000004335 13243301226 015734 0ustar00tmarkstaff000000 000000 In order to test the DHCPv6 server, we have a configuration file with known values, and some Perl scripts designed to send and receive DHCPv6 packets to check various code paths. It is not complete test converage by any means, but it should be fairly easy to add additional tests as needed. The scripts themselves are not very well written. There is a lot of copied code, poor error handling, and so on. These should be rewritten at some point. To use, the DHCPv6 server must be running in test mode to send back to the originating port. (The scripts can be changed to bind to the appropriate client port, but they don't now, and have to run as root to do this). In server/dhcpv6.c, look for this comment: /* For testing, we reply to the sending port, so we don't need a root */ /* client */ to_addr.sin6_port = remote_port; /* to_addr.sin6_port = packet->client_port;*/ And change the code to use the client_port value. You will need to modify one of the test configuration files to use one of the physical subnets that your machine uses, in the subnet6 statement. Then run the server as root, in debug mode: # touch /tmp/test.leases # dhcpd -6 -cf test-a.conf -lf /tmp/test.leases -d You can invoke the scripts then: $ perl 000-badmsgtype.pl The expected results vary per script, depending on the behavior that is being tested. Notes about scripts: In order to manipulate IPv6 addresses, we need the Socket6 library, available from CPAN: http://search.cpan.org/~umemoto/Socket6-0.19/Socket6.pm The Perl that Sun issues for Solaris 10 is compiled with the Sun compiler. If you have the Sun compiler, then this will work fine. Otherwise you may need to install Perl from source. We need to get the hardware address in order to build DUID properly. The IO::Interface module reports hardware address, but not on Solaris 10 it seems. Rather than do this the "right way", we do it the "Perl way", and hack it. "ifconfig" does return the Ethernet address, but only to the root user. However, we can look for files of the name /etc/hostname.*, get the IP address from "ifconfig", and then check for those addresses in the ARP table. Client DUID is supposed to be an opaque value to the server, but we go ahead and make a "real" type 1 or type 3 DUID. dhcp-4.4.1/tests/DHCPv6/stubcli-opt-in-na.pl000644 000765 000024 00000013771 13243301226 020662 0ustar00tmarkstaff000000 000000 #! /usr/bin/perl -w # Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # Internet Systems Consortium, Inc. # 950 Charter Street # Redwood City, CA 94063 # # https://www.isc.org/ use strict; use English; use Time::HiRes qw( sleep ); use Socket; use Socket6; use IO::Select; use dhcp_client; # XXX: for debugging use Data::Dumper; $Data::Dumper::Useqq = 1; # not-yet-standard options my $OPT_TIME_SERVERS = 40; my $OPT_TIME_OFFSET = 41; # DOCSIS sub-options my $DOCSIS_OPT_ORO = 1; # 2 to 31 are reserved my $DOCSIS_OPT_TFTP_SERVERS = 32; my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; my $DOCSIS_OPT_SYSLOG_SERVERS = 34; my $DOCSIS_OPT_TLV5 = 35; my $DOCSIS_OPT_DEVICE_ID = 36; my $DOCSIS_OPT_CCC = 37; my $DOCSIS_OPT_VERS = 38; # well-known addresses my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; my $All_DHCP_Servers = "ff05::1:3"; # ports my $client_port = 546; my $server_port = 547; # create a new Solicit message my $msg = dhcp_client::msg->new($MSG_SOLICIT); # add the Client Identifier (required by DOCSIS and RFC 3315) my $client_id = "\x00\x01\x00\x01\x0c\x00\xa1\x41\x00\x06\x5b\x50\x99\xf6"; $msg->add_option($OPT_CLIENTID, $client_id); #$msg->add_option($OPT_CLIENTID, dhcp_client::duid()); # add Elapsed Time, set to 0 on first packet (required by RFC 3315) $msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); # add IA_NA for each interface (required by DOCSIS and RFC 3315) # XXX: should this be a single interface only? my $iaid = 0; foreach my $iface (dhcp_client::iface()) { my $option_data = pack("NNN", ++$iaid, 0, 0); my $enc_opt = dhcp_client::msg->new(0); $enc_opt->add_option($OPT_CLIENTID, $client_id); $option_data .= $enc_opt->packed_options(); $msg->add_option($OPT_IA_NA, $option_data); } # add Reconfigure Accept (required by DOCSIS) $msg->add_option($OPT_RECONF_ACCEPT, ""); # add Options Request (required by DOCSIS, recommended by RFC 3315) my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET ); $msg->add_option($OPT_ORO, pack("n*", @oro)); # add Vendor Class option (required by DOCSIS) $msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0"); # add Vendor-specific Information Option option (required by DOCSIS) my $vsio = pack("N", 4491); # ORO (required by DOCSIS) my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS ); $vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro); # TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS) my $tlv5_data = "\x01\x02\x03\x0"; $vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data; # DOCSIS Device (required by DOCSIS) my $docsis_device_id = dhcp_client::mac_addr_binary(); $vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id)); $vsio .= $docsis_device_id; $msg->add_option($OPT_VENDOR_OPTS, $vsio); # add Rapid Commit option (required by DOCSIS) $msg->add_option($OPT_RAPID_COMMIT, ""); # timeout parameters, from DOCSIS my $IRT = $SOL_TIMEOUT; my $MRT = $SOL_MAX_RT; my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 my $MRD = 0; # sleep a random amount of time between 0 and 1 second, required by RFC 3315 # XXX: this seems pretty stupid sleep(rand($SOL_MAX_DELAY)); my $RT; my $count = 0; my $mrd_end_time; if ($MRD != 0) { $mrd_end_time = time() + $MRD; } my $reply_msg; do { # create our socket, and send our Solicit socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); my $packet = $msg->packet(); my $send_ret = send(SOCK, $packet, 0, pack_sockaddr_in6($server_port, $addr)); if (not defined($send_ret)) { printf STDERR "Error \%d sending DHCPv6 Solicit message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } elsif ($send_ret != length($packet)) { print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; exit(1); } $count++; my $RAND = rand(0.2) - 0.1; if (defined $RT) { $RT = 2*$RT + $RAND*$RT; if (($RT > $MRT) && ($MRT != 0)) { $RT = $MRT + $RAND*$RT; } } else { $RT = $IRT + $RAND*$IRT; } my $rt_end_time = time() + $RT; if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { $rt_end_time = $mrd_end_time; } for (;;) { my $timeout = $rt_end_time - time(); if ($timeout < 0) { # print STDERR "Timeout waiting for DHCPv6 Advertise ", # "or Reply message.\n"; last; } my @ready = IO::Select->new(\*SOCK)->can_read($timeout); if (@ready) { my $reply; my $recv_ret; $recv_ret = recv(SOCK, $reply, 1500, 0); if (not defined $recv_ret) { printf STDERR "Error \%d receiving DHCPv6 " . "message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } $reply_msg = dhcp_client::msg::decode($reply, 1); if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || ($reply_msg->{msg_type} == $MSG_REPLY)) { last; } } } } until ($reply_msg || (($MRC != 0) && ($count > $MRC)) || (defined($mrd_end_time) && ($mrd_end_time > time()))); unless ($reply_msg) { if (($MRC != 0) && ($count >= $MRC)) { print STDERR "No reply after maximum retransmission count.\n"; } else { print STDERR "No reply after maximum retransmission duration.\n"; } } if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { print "Got DHCPv6 Reply message.\n"; #print Dumper($reply_msg), "\n"; exit(0); } #print Dumper($msg), "\n"; #print Dumper($msg->packet()), "\n"; # #print "packet length: ", length($msg->packet()), "\n"; dhcp-4.4.1/tests/DHCPv6/stubcli.pl000644 000765 000024 00000013330 13243301226 017051 0ustar00tmarkstaff000000 000000 #! /usr/bin/perl -w # Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # Internet Systems Consortium, Inc. # 950 Charter Street # Redwood City, CA 94063 # # https://www.isc.org/ use strict; use English; use Time::HiRes qw( sleep ); use Socket; use Socket6; use IO::Select; use dhcp_client; # XXX: for debugging use Data::Dumper; # not-yet-standard options my $OPT_TIME_SERVERS = 40; my $OPT_TIME_OFFSET = 41; # DOCSIS sub-options my $DOCSIS_OPT_ORO = 1; # 2 to 31 are reserved my $DOCSIS_OPT_TFTP_SERVERS = 32; my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; my $DOCSIS_OPT_SYSLOG_SERVERS = 34; my $DOCSIS_OPT_TLV5 = 35; my $DOCSIS_OPT_DEVICE_ID = 36; my $DOCSIS_OPT_CCC = 37; my $DOCSIS_OPT_VERS = 38; # well-known addresses my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; my $All_DHCP_Servers = "ff05::1:3"; # ports my $client_port = 546; my $server_port = 547; # create a new Solicit message my $msg = dhcp_client::msg->new($MSG_SOLICIT); # add the Client Identifier (required by DOCSIS and RFC 3315) $msg->add_option($OPT_CLIENTID, dhcp_client::duid()); # add Elapsed Time, set to 0 on first packet (required by RFC 3315) $msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); # add IA_NA for each interface (required by DOCSIS and RFC 3315) # XXX: should this be a single interface only? my $iaid = 0; foreach my $iface (dhcp_client::iface()) { my $option_data = pack("NNN", ++$iaid, 0, 0); $msg->add_option($OPT_IA_NA, $option_data); } # add Reconfigure Accept (required by DOCSIS) $msg->add_option($OPT_RECONF_ACCEPT, ""); # add Options Request (required by DOCSIS, recommended by RFC 3315) my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET ); $msg->add_option($OPT_ORO, pack("n*", @oro)); # add Vendor Class option (required by DOCSIS) $msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0"); # add Vendor-specific Information Option option (required by DOCSIS) my $vsio = pack("N", 4491); # ORO (required by DOCSIS) my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS ); $vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro); # TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS) my $tlv5_data = "\x01\x02\x03\x0"; $vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data; # DOCSIS Device (required by DOCSIS) my $docsis_device_id = dhcp_client::mac_addr_binary(); $vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id)); $vsio .= $docsis_device_id; $msg->add_option($OPT_VENDOR_OPTS, $vsio); # add Rapid Commit option (required by DOCSIS) $msg->add_option($OPT_RAPID_COMMIT, ""); # timeout parameters, from DOCSIS my $IRT = $SOL_TIMEOUT; my $MRT = $SOL_MAX_RT; my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 my $MRD = 0; # sleep a random amount of time between 0 and 1 second, required by RFC 3315 # XXX: this seems pretty stupid sleep(rand($SOL_MAX_DELAY)); my $RT; my $count = 0; my $mrd_end_time; if ($MRD != 0) { $mrd_end_time = time() + $MRD; } my $reply_msg; do { # create our socket, and send our Solicit socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); my $packet = $msg->packet(); my $send_ret = send(SOCK, $packet, 0, pack_sockaddr_in6($server_port, $addr)); if (not defined($send_ret)) { printf STDERR "Error \%d sending DHCPv6 Solicit message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } elsif ($send_ret != length($packet)) { print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; exit(1); } $count++; my $RAND = rand(0.2) - 0.1; if (defined $RT) { $RT = 2*$RT + $RAND*$RT; if (($RT > $MRT) && ($MRT != 0)) { $RT = $MRT + $RAND*$RT; } } else { $RT = $IRT + $RAND*$IRT; } my $rt_end_time = time() + $RT; if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { $rt_end_time = $mrd_end_time; } for (;;) { my $timeout = $rt_end_time - time(); if ($timeout < 0) { # print STDERR "Timeout waiting for DHCPv6 Advertise ", # "or Reply message.\n"; last; } my @ready = IO::Select->new(\*SOCK)->can_read($timeout); if (@ready) { my $reply; my $recv_ret; $recv_ret = recv(SOCK, $reply, 1500, 0); if (not defined $recv_ret) { printf STDERR "Error \%d receiving DHCPv6 " . "message;\n\%s\n", 0+$ERRNO, $ERRNO; exit(1); } $reply_msg = dhcp_client::msg::decode($reply, 1); if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || ($reply_msg->{msg_type} == $MSG_REPLY)) { last; } } } } until ($reply_msg || (($MRC != 0) && ($count > $MRC)) || (defined($mrd_end_time) && ($mrd_end_time > time()))); unless ($reply_msg) { if (($MRC != 0) && ($count >= $MRC)) { print STDERR "No reply after maximum retransmission count.\n"; } else { print STDERR "No reply after maximum retransmission duration.\n"; } } if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { print "Got DHCPv6 Reply message.\n"; exit(0); } #$Data::Dumper::Useqq = 1; #print Dumper($msg), "\n"; #print Dumper($msg->packet()), "\n"; # #print "packet length: ", length($msg->packet()), "\n"; dhcp-4.4.1/tests/DHCPv6/test-a.conf000644 000765 000024 00000003755 13243301226 017125 0ustar00tmarkstaff000000 000000 # # Define the DHCPv6 option space. # # Option numbers are assigned by IANA: # http://www.iana.org/assignments/dhcpv6-parameters # option dhcp6.time-servers code 40 = array of ip6-address; option dhcp6.time-offset code 41 = signed integer 32; # # Define the DOCSIS option space. # TODO: DOCSIS oro definition # option space docsis code width 2 length width 2; option vsio.docsis code 4491 = encapsulate docsis; option docsis.tftp-servers code 32 = array of ip6-address; option docsis.cablelabs-configuration-file code 33 = text; option docsis.cablelabs-syslog-servers code 34 = array of ip6-address; option docsis.device-id code 36 = string; # # Declare some options. # option dhcp6.time-servers 3ffe:bbbb:aaaa:aaaa::1, 3ffe:bbbb:aaaa:aaaa::2; option docsis.tftp-servers 3ffe:cccc:aaaa:aaaa::1, 3ffe:cccc:aaaa:aaaa::2; # # DNS server IP address to update dynamically # ddns-update-style interim; ddns-domainname "foo.com"; # # Per-host settings. # host cablemodem-1 { host-identifier option dhcp6.client-id 00:01:00:01:0c:00:a1:41:00:06:5b:50:99:f6; fixed-address6 3ffe:aaaa:aaaa:aaaa::ffff; ddns-domainname "bar.com"; option dhcp6.time-servers 3ffe:aaaa:aaaa:aaaa::1, 3ffe:aaaa:aaaa:aaaa::2; option docsis.tftp-servers 3ffe:aaaa:aaaa:aaaa::1, 3ffe:aaaa:aaaa:aaaa::2; option dhcp6.time-offset -14400; # -4 hours option docsis.cablelabs-configuration-file "bootfile.cfg"; option docsis.cablelabs-syslog-servers 3ffe:aaaa:aaaa:aaaa::1, 3ffe:aaaa:aaaa:aaaa::2; } #host cablemodem-2 { # host-identifier option docsis.device-id 00:06:5B:50:99:F6; # option dhcp6.time-servers 3ffe:dddd:aaaa:aaaa::1, # 3ffe:dddd:aaaa:aaaa::2; # option docsis.tftp-servers 3ffe:dddd:aaaa:aaaa::1, # 3ffe:dddd:aaaa:aaaa::2; # option dhcp6.time-offset -14400; # -4 hours # option docsis.cablelabs-configuration-file "bootfile.cfg"; # option docsis.cablelabs-syslog-servers 3ffe:aaaa:aaaa:aaaa::1, # 3ffe:aaaa:aaaa:aaaa::2; #} # XXX: for testing subnet6 fe80::20c:29ff:fe42:820/128 { } dhcp-4.4.1/tests/DHCPv6/test-b.conf000644 000765 000024 00000003473 13243301226 017123 0ustar00tmarkstaff000000 000000 # # Define the DHCPv6 option space. # # Option numbers are assigned by IANA: # http://www.iana.org/assignments/dhcpv6-parameters # option dhcpv6.time-servers code 40 = array of ip6-address; option dhcpv6.time-offset code 41 = signed integer 32; # # Define the DOCSIS option space. # option space docsis code width 2 length width 2; option docsis.tftp-servers code 32 = array of ip6-address; option docsis.cablelabs-configuration-file code 33 = text; option docsis.cablelabs-syslog-servers code 34 = array of ip6-address; option docsis.device-id code 36 = string; # # Declare some options. # option dhcpv6.time-servers 3ffe:bbbb:aaaa:aaaa::1, 3ffe:bbbb:aaaa:aaaa::2; option docsis.tftp-servers 3ffe:cccc:aaaa:aaaa::1, 3ffe:cccc:aaaa:aaaa::2; # # DNS server IP address to update dynamically # ddns-update-style interim; ddns-domainname "foo.com"; # # Per-host settings. # host cablemodem-1 { option dhcpv6.client-id 00:01:00:01:0c:00:a1:41:00:06:5b:50:99:f6; fixed-address6 3ffe:aaaa:aaaa:aaaa::ffff; ddns-domainname "bar.com"; option dhcpv6.time-servers 3ffe:aaaa:aaaa:aaaa::1, 3ffe:aaaa:aaaa:aaaa::2; option docsis.tftp-servers 3ffe:aaaa:aaaa:aaaa::1, 3ffe:aaaa:aaaa:aaaa::2; option dhcpv6.time-offset -14400; # -4 hours option docsis.cablelabs-configuration-file "bootfile.cfg"; option docsis.cablelabs-syslog-servers 3ffe:aaaa:aaaa:aaaa::1, 3ffe:aaaa:aaaa:aaaa::2; } host cablemodem-2 { option docsis.device-id 00:06:5B:50:99:F6; option dhcpv6.time-servers 3ffe:dddd:aaaa:aaaa::1, 3ffe:dddd:aaaa:aaaa::2; option docsis.tftp-servers 3ffe:dddd:aaaa:aaaa::1, 3ffe:dddd:aaaa:aaaa::2; option dhcpv6.time-offset -14400; # -4 hours option docsis.cablelabs-configuration-file "bootfile.cfg"; option docsis.cablelabs-syslog-servers 3ffe:aaaa:aaaa:aaaa::1, 3ffe:aaaa:aaaa:aaaa::2; } dhcp-4.4.1/server/bootp.c000644 000765 000024 00000033573 13243301226 015523 0ustar00tmarkstaff000000 000000 /* bootp.c BOOTP Protocol support. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1995-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #include #if defined (TRACING) # define send_packet trace_packet_send #endif void bootp (packet) struct packet *packet; { int result; struct host_decl *hp = (struct host_decl *)0; struct host_decl *host = (struct host_decl *)0; struct packet outgoing; struct dhcp_packet raw; struct sockaddr_in to; struct in_addr from; struct hardware hto; struct option_state *options = (struct option_state *)0; struct lease *lease = (struct lease *)0; unsigned i; struct data_string d1; struct option_cache *oc; char msgbuf [1024]; int ignorep; int peer_has_leases = 0; if (packet -> raw -> op != BOOTREQUEST) return; /* %Audit% This is log output. %2004.06.17,Safe% * If we truncate we hope the user can get a hint from the log. */ snprintf (msgbuf, sizeof msgbuf, "BOOTREQUEST from %s via %s", print_hw_addr (packet -> raw -> htype, packet -> raw -> hlen, packet -> raw -> chaddr), packet -> raw -> giaddr.s_addr ? inet_ntoa (packet -> raw -> giaddr) : packet -> interface -> name); if (!locate_network (packet)) { log_info ("%s: network unknown", msgbuf); return; } find_lease (&lease, packet, packet -> shared_network, 0, 0, (struct lease *)0, MDL); if (lease && lease->host) host_reference(&hp, lease->host, MDL); if (!lease || ((lease->flags & STATIC_LEASE) == 0)) { struct host_decl *h; /* We didn't find an applicable fixed-address host declaration. Just in case we may be able to dynamically assign an address, see if there's a host declaration that doesn't have an ip address associated with it. */ if (!hp) find_hosts_by_haddr(&hp, packet->raw->htype, packet->raw->chaddr, packet->raw->hlen, MDL); for (h = hp; h; h = h -> n_ipaddr) { if (!h -> fixed_addr) { host_reference(&host, h, MDL); break; } } if (hp) host_dereference(&hp, MDL); if (host) { host_reference(&hp, host, MDL); host_dereference(&host, MDL); } /* Allocate a lease if we have not yet found one. */ if (!lease) allocate_lease (&lease, packet, packet -> shared_network -> pools, &peer_has_leases); if (lease == NULL) { log_info("%s: BOOTP from dynamic client and no " "dynamic leases", msgbuf); goto out; } #if defined(FAILOVER_PROTOCOL) if ((lease->pool != NULL) && (lease->pool->failover_peer != NULL)) { dhcp_failover_state_t *peer; peer = lease->pool->failover_peer; /* If we are in a failover state that bars us from * answering, do not do so. * If we are in a cooperative state, load balance * (all) responses. */ if ((peer->service_state == not_responding) || (peer->service_state == service_startup)) { log_info("%s: not responding%s", msgbuf, peer->nrr); goto out; } else if((peer->service_state == cooperating) && !load_balance_mine(packet, peer)) { log_info("%s: load balance to peer %s", msgbuf, peer->name); goto out; } } #endif ack_lease (packet, lease, 0, 0, msgbuf, 0, hp); goto out; } /* Run the executable statements to compute the client and server options. */ option_state_allocate (&options, MDL); /* Execute the subnet statements. */ execute_statements_in_scope (NULL, packet, lease, NULL, packet->options, options, &lease->scope, lease->subnet->group, NULL, NULL); /* Execute statements from class scopes. */ for (i = packet -> class_count; i > 0; i--) { execute_statements_in_scope(NULL, packet, lease, NULL, packet->options, options, &lease->scope, packet->classes[i - 1]->group, lease->subnet->group, NULL); } /* Execute the host statements. */ if (hp != NULL) { execute_statements_in_scope(NULL, packet, lease, NULL, packet->options, options, &lease->scope, hp->group, lease->subnet->group, NULL); } /* Drop the request if it's not allowed for this client. */ if ((oc = lookup_option (&server_universe, options, SV_ALLOW_BOOTP)) && !evaluate_boolean_option_cache(&ignorep, packet, lease, NULL, packet->options, options, &lease->scope, oc, MDL)) { if (!ignorep) log_info ("%s: bootp disallowed", msgbuf); goto out; } if ((oc = lookup_option(&server_universe, options, SV_ALLOW_BOOTING)) && !evaluate_boolean_option_cache(&ignorep, packet, lease, NULL, packet->options, options, &lease->scope, oc, MDL)) { if (!ignorep) log_info ("%s: booting disallowed", msgbuf); goto out; } /* Set up the outgoing packet... */ memset (&outgoing, 0, sizeof outgoing); memset (&raw, 0, sizeof raw); outgoing.raw = &raw; /* If we didn't get a known vendor magic number on the way in, just copy the input options to the output. */ i = SV_ALWAYS_REPLY_RFC1048; if (!packet->options_valid && !(evaluate_boolean_option_cache(&ignorep, packet, lease, NULL, packet->options, options, &lease->scope, lookup_option (&server_universe, options, i), MDL))) { if (packet->packet_length > DHCP_FIXED_NON_UDP) { memcpy(outgoing.raw->options, packet->raw->options, packet->packet_length - DHCP_FIXED_NON_UDP); } outgoing.packet_length = (packet->packet_length < BOOTP_MIN_LEN) ? BOOTP_MIN_LEN : packet->packet_length; } else { /* Use the subnet mask from the subnet declaration if no other mask has been provided. */ oc = (struct option_cache *)0; i = DHO_SUBNET_MASK; if (!lookup_option (&dhcp_universe, options, i)) { if (option_cache_allocate (&oc, MDL)) { if (make_const_data (&oc -> expression, lease -> subnet -> netmask.iabuf, lease -> subnet -> netmask.len, 0, 0, MDL)) { option_code_hash_lookup(&oc->option, dhcp_universe.code_hash, &i, 0, MDL); save_option (&dhcp_universe, options, oc); } option_cache_dereference (&oc, MDL); } } /* If use-host-decl-names is enabled and there is a hostname * defined in the host delcartion, send it back in hostname * option */ use_host_decl_name(packet, lease, options); /* Pack the options into the buffer. Unlike DHCP, we can't pack options into the filename and server name buffers. */ outgoing.packet_length = cons_options (packet, outgoing.raw, lease, (struct client_state *)0, 0, packet -> options, options, &lease -> scope, 0, 0, 1, (struct data_string *)0, (const char *)0); if (outgoing.packet_length < BOOTP_MIN_LEN) outgoing.packet_length = BOOTP_MIN_LEN; } /* Take the fields that we care about... */ raw.op = BOOTREPLY; raw.htype = packet -> raw -> htype; raw.hlen = packet -> raw -> hlen; memcpy (raw.chaddr, packet -> raw -> chaddr, sizeof raw.chaddr); raw.hops = packet -> raw -> hops; raw.xid = packet -> raw -> xid; raw.secs = packet -> raw -> secs; raw.flags = packet -> raw -> flags; raw.ciaddr = packet -> raw -> ciaddr; /* yiaddr is an ipv4 address, it must be 4 octets. */ memcpy (&raw.yiaddr, lease->ip_addr.iabuf, 4); /* If we're always supposed to broadcast to this client, set the broadcast bit in the bootp flags field. */ if ((oc = lookup_option (&server_universe, options, SV_ALWAYS_BROADCAST)) && evaluate_boolean_option_cache (&ignorep, packet, lease, (struct client_state *)0, packet -> options, options, &lease -> scope, oc, MDL)) raw.flags |= htons (BOOTP_BROADCAST); /* Figure out the address of the next server. */ memset (&d1, 0, sizeof d1); oc = lookup_option (&server_universe, options, SV_NEXT_SERVER); if (oc && evaluate_option_cache (&d1, packet, lease, (struct client_state *)0, packet -> options, options, &lease -> scope, oc, MDL)) { /* If there was more than one answer, take the first. */ if (d1.len >= 4 && d1.data) memcpy (&raw.siaddr, d1.data, 4); data_string_forget (&d1, MDL); } else { if ((lease->subnet->shared_network->interface != NULL) && lease->subnet->shared_network->interface->address_count) raw.siaddr = lease->subnet->shared_network->interface->addresses[0]; else if (packet->interface->address_count) raw.siaddr = packet->interface->addresses[0]; } raw.giaddr = packet -> raw -> giaddr; /* Figure out the filename. */ oc = lookup_option (&server_universe, options, SV_FILENAME); if (oc && evaluate_option_cache (&d1, packet, lease, (struct client_state *)0, packet -> options, options, &lease -> scope, oc, MDL)) { memcpy (raw.file, d1.data, d1.len > sizeof raw.file ? sizeof raw.file : d1.len); if (sizeof raw.file > d1.len) memset (&raw.file [d1.len], 0, (sizeof raw.file) - d1.len); data_string_forget (&d1, MDL); } else memcpy (raw.file, packet -> raw -> file, sizeof raw.file); /* Choose a server name as above. */ oc = lookup_option (&server_universe, options, SV_SERVER_NAME); if (oc && evaluate_option_cache (&d1, packet, lease, (struct client_state *)0, packet -> options, options, &lease -> scope, oc, MDL)) { memcpy (raw.sname, d1.data, d1.len > sizeof raw.sname ? sizeof raw.sname : d1.len); if (sizeof raw.sname > d1.len) memset (&raw.sname [d1.len], 0, (sizeof raw.sname) - d1.len); data_string_forget (&d1, MDL); } /* Execute the commit statements, if there are any. */ execute_statements (NULL, packet, lease, NULL, packet->options, options, &lease->scope, lease->on_star.on_commit, NULL); /* We're done with the option state. */ option_state_dereference (&options, MDL); #if defined(DHCPv6) && defined(DHCP4o6) if (dhcpv4_over_dhcpv6 && (packet->dhcp4o6_response != NULL)) { /* Report what we're doing... */ log_info("%s", msgbuf); log_info("DHCP4o6 BOOTREPLY for %s to %s (%s) via %s", piaddr(lease->ip_addr), ((hp != NULL) && (hp->name != NULL)) ? hp -> name : "unknown", print_hw_addr (packet->raw->htype, packet->raw->hlen, packet->raw->chaddr), piaddr(packet->client_addr)); /* fill dhcp4o6_response */ packet->dhcp4o6_response->len = outgoing.packet_length; packet->dhcp4o6_response->buffer = NULL; if (!buffer_allocate(&packet->dhcp4o6_response->buffer, outgoing.packet_length, MDL)) { log_fatal("No memory to store DHCP4o6 reply."); } packet->dhcp4o6_response->data = packet->dhcp4o6_response->buffer->data; memcpy(packet->dhcp4o6_response->buffer->data, outgoing.raw, outgoing.packet_length); goto out; } #endif /* Set up the hardware destination address... */ hto.hbuf [0] = packet -> raw -> htype; hto.hlen = packet -> raw -> hlen + 1; memcpy (&hto.hbuf [1], packet -> raw -> chaddr, packet -> raw -> hlen); if (packet->interface->address_count) { from = packet->interface->addresses[0]; } else { log_error("%s: Interface %s appears to have no IPv4 " "addresses, and so dhcpd cannot select a source " "address.", msgbuf, packet->interface->name); goto out; } /* Report what we're doing... */ log_info("%s", msgbuf); log_info("BOOTREPLY for %s to %s (%s) via %s", piaddr(lease->ip_addr), ((hp != NULL) && (hp->name != NULL)) ? hp -> name : "unknown", print_hw_addr (packet->raw->htype, packet->raw->hlen, packet->raw->chaddr), packet->raw->giaddr.s_addr ? inet_ntoa (packet->raw->giaddr) : packet->interface->name); /* Set up the parts of the address that are in common. */ to.sin_family = AF_INET; #ifdef HAVE_SA_LEN to.sin_len = sizeof to; #endif memset (to.sin_zero, 0, sizeof to.sin_zero); /* If this was gatewayed, send it back to the gateway... */ if (raw.giaddr.s_addr) { to.sin_addr = raw.giaddr; to.sin_port = local_port; if (fallback_interface) { result = send_packet (fallback_interface, NULL, &raw, outgoing.packet_length, from, &to, &hto); if (result < 0) { log_error ("%s:%d: Failed to send %d byte long " "packet over %s interface.", MDL, outgoing.packet_length, fallback_interface->name); } goto out; } /* If it comes from a client that already knows its address and is not requesting a broadcast response, and we can unicast to a client without using the ARP protocol, sent it directly to that client. */ } else if (!(raw.flags & htons (BOOTP_BROADCAST)) && can_unicast_without_arp (packet -> interface)) { to.sin_addr = raw.yiaddr; to.sin_port = remote_port; /* Otherwise, broadcast it on the local network. */ } else { to.sin_addr = limited_broadcast; to.sin_port = remote_port; /* XXX */ } errno = 0; result = send_packet(packet->interface, packet, &raw, outgoing.packet_length, from, &to, &hto); if (result < 0) { log_error ("%s:%d: Failed to send %d byte long packet over %s" " interface.", MDL, outgoing.packet_length, packet->interface->name); } out: if (options) option_state_dereference (&options, MDL); if (lease) lease_dereference (&lease, MDL); if (hp) host_dereference (&hp, MDL); if (host) host_dereference (&host, MDL); } dhcp-4.4.1/server/class.c000644 000765 000024 00000021661 13243301226 015500 0ustar00tmarkstaff000000 000000 /* class.c Handling for client classes. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1998-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" struct executable_statement *default_classification_rules; int have_billing_classes; /* Build the default classification rule tree. */ void classification_setup () { /* eval ... */ default_classification_rules = (struct executable_statement *)0; if (!executable_statement_allocate (&default_classification_rules, MDL)) log_fatal ("Can't allocate check of default collection"); default_classification_rules -> op = eval_statement; /* check-collection "default" */ if (!expression_allocate (&default_classification_rules -> data.eval, MDL)) log_fatal ("Can't allocate default check expression"); default_classification_rules -> data.eval -> op = expr_check; default_classification_rules -> data.eval -> data.check = &default_collection; } void classify_client (packet) struct packet *packet; { execute_statements (NULL, packet, NULL, NULL, packet->options, NULL, &global_scope, default_classification_rules, NULL); } int check_collection (packet, lease, collection) struct packet *packet; struct lease *lease; struct collection *collection; { struct class *class, *nc; struct data_string data; int matched = 0; int status; int ignorep; int classfound; for (class = collection -> classes; class; class = class -> nic) { #if defined (DEBUG_CLASS_MATCHING) log_info ("checking against class %s...", class -> name); #endif memset (&data, 0, sizeof data); /* If there is a "match if" expression, check it. If we get a match, and there's no subclass expression, it's a match. If we get a match and there is a subclass expression, then we check the submatch. If it's not a match, that's final - we don't check the submatch. */ if (class -> expr) { status = (evaluate_boolean_expression_result (&ignorep, packet, lease, (struct client_state *)0, packet -> options, (struct option_state *)0, lease ? &lease -> scope : &global_scope, class -> expr)); if (status) { if (!class -> submatch) { matched = 1; #if defined (DEBUG_CLASS_MATCHING) log_info ("matches class."); #endif classify (packet, class); continue; } } else continue; } /* Check to see if the client matches an existing subclass. If it doesn't, and this is a spawning class, spawn a new subclass and put the client in it. */ if (class -> submatch) { status = (evaluate_data_expression (&data, packet, lease, (struct client_state *)0, packet -> options, (struct option_state *)0, lease ? &lease -> scope : &global_scope, class -> submatch, MDL)); if (status && data.len) { nc = (struct class *)0; classfound = class_hash_lookup (&nc, class -> hash, (const char *)data.data, data.len, MDL); #ifdef LDAP_CONFIGURATION if (!classfound && find_subclass_in_ldap (class, &nc, &data)) classfound = 1; #endif if (classfound) { #if defined (DEBUG_CLASS_MATCHING) log_info ("matches subclass %s.", print_hex_1 (data.len, data.data, 60)); #endif data_string_forget (&data, MDL); classify (packet, nc); matched = 1; class_dereference (&nc, MDL); continue; } if (!class -> spawning) { data_string_forget (&data, MDL); continue; } /* XXX Write out the spawned class? */ #if defined (DEBUG_CLASS_MATCHING) log_info ("spawning subclass %s.", print_hex_1 (data.len, data.data, 60)); #endif status = class_allocate (&nc, MDL); group_reference (&nc -> group, class -> group, MDL); class_reference (&nc -> superclass, class, MDL); nc -> lease_limit = class -> lease_limit; nc -> dirty = 1; if (nc -> lease_limit) { nc -> billed_leases = (dmalloc (nc -> lease_limit * sizeof (struct lease *), MDL)); if (!nc -> billed_leases) { log_error ("no memory for%s", " billing"); data_string_forget (&nc -> hash_string, MDL); class_dereference (&nc, MDL); data_string_forget (&data, MDL); continue; } memset (nc -> billed_leases, 0, (nc -> lease_limit * sizeof (struct lease *))); } data_string_copy (&nc -> hash_string, &data, MDL); data_string_forget (&data, MDL); if (!class -> hash) class_new_hash(&class->hash, SCLASS_HASH_SIZE, MDL); class_hash_add (class -> hash, (const char *) nc -> hash_string.data, nc -> hash_string.len, nc, MDL); classify (packet, nc); class_dereference (&nc, MDL); } } } return matched; } void classify (packet, class) struct packet *packet; struct class *class; { if (packet -> class_count < PACKET_MAX_CLASSES) class_reference (&packet -> classes [packet -> class_count++], class, MDL); else log_error ("too many classes match %s", print_hw_addr (packet -> raw -> htype, packet -> raw -> hlen, packet -> raw -> chaddr)); } isc_result_t unlink_class(struct class **class) { struct collection *lp; struct class *cp, *pp; for (lp = collections; lp; lp = lp -> next) { for (pp = 0, cp = lp -> classes; cp; pp = cp, cp = cp -> nic) if (cp == *class) { if (pp == 0) { lp->classes = cp->nic; } else { pp->nic = cp->nic; } cp->nic = 0; class_dereference(class, MDL); return ISC_R_SUCCESS; } } return ISC_R_NOTFOUND; } isc_result_t find_class (struct class **class, const char *name, const char *file, int line) { struct collection *lp; struct class *cp; for (lp = collections; lp; lp = lp -> next) { for (cp = lp -> classes; cp; cp = cp -> nic) if (cp -> name && !strcmp (name, cp -> name)) { return class_reference (class, cp, file, line); } } return ISC_R_NOTFOUND; } /* Removes the billing class from a lease * * Note that because classes can be created and removed dynamically, it is * possible that the class to which a lease was billed has since been deleted. * To cover the case where the lease is the last reference to a deleted class * we remove the lease reference from the class first, then the class from the * lease. To protect ourselves from the reverse situation, where the class is * the last reference to the lease (unlikely), we create a guard reference to * the lease, then remove it at the end. */ void unbill_class (lease) struct lease *lease; { int i; struct class* class = lease->billing_class; struct lease* refholder = NULL; /* if there's no billing to remove, nothing to do */ if (class == NULL) { return; } /* Find the lease in the list of the class's billed leases */ for (i = 0; i < class->lease_limit; i++) { if (class->billed_leases[i] == lease) break; } /* Create guard reference, so class cannot be last reference to lease */ lease_reference(&refholder, lease, MDL); /* If the class doesn't have the lease, then something is broken * programmatically. We'll log it but skip the lease dereference. */ if (i == class->lease_limit) { log_error ("lease %s unbilled with no billing arrangement.", piaddr(lease->ip_addr)); } else { /* Remove the lease from the class */ lease_dereference(&class->billed_leases[i], MDL); class->leases_consumed--; } /* Remove the class from the lease */ class_dereference(&lease->billing_class, MDL); /* Ditch our guard reference */ lease_dereference(&refholder, MDL); } int bill_class (lease, class) struct lease *lease; struct class *class; { int i; if (lease -> billing_class) { log_error ("lease billed with existing billing arrangement."); unbill_class (lease); } if (class -> leases_consumed == class -> lease_limit) return 0; for (i = 0; i < class -> lease_limit; i++) if (!class -> billed_leases [i]) break; if (i == class -> lease_limit) { log_error ("class billing consumption disagrees with leases."); return 0; } lease_reference (&class -> billed_leases [i], lease, MDL); class_reference (&lease -> billing_class, class, MDL); class -> leases_consumed++; return 1; } dhcp-4.4.1/server/confpars.c000644 000765 000024 00000502076 13243301226 016212 0ustar00tmarkstaff000000 000000 /* confpars.c Parser for dhcpd config file... */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1995-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ /*! \file server/confpars.c */ #include "dhcpd.h" static unsigned char global_host_once = 1; static int parse_binding_value(struct parse *cfile, struct binding_value *value); static void parse_authoring_byte_order (struct parse *cfile); static void parse_lease_id_format (struct parse *cfile); #ifdef DHCPv6 static int parse_iaid_duid(struct parse *cfile, struct ia_xx** ia, u_int32_t *iaid, const char* file, int line); #endif #if defined (TRACING) trace_type_t *trace_readconf_type; trace_type_t *trace_readleases_type; void parse_trace_setup () { trace_readconf_type = trace_type_register ("readconf", (void *)0, trace_conf_input, trace_conf_stop, MDL); trace_readleases_type = trace_type_register ("readleases", (void *)0, trace_conf_input, trace_conf_stop, MDL); } #endif /* conf-file :== parameters declarations END_OF_FILE parameters :== | parameter | parameters parameter declarations :== | declaration | declarations declaration */ isc_result_t readconf () { isc_result_t res; res = read_conf_file (path_dhcpd_conf, root_group, ROOT_GROUP, 0); #if defined(LDAP_CONFIGURATION) if (res != ISC_R_SUCCESS) return (res); return ldap_read_config (); #else return (res); #endif } isc_result_t read_conf_file (const char *filename, struct group *group, int group_type, int leasep) { int file; struct parse *cfile; isc_result_t status; #if defined (TRACING) char *fbuf, *dbuf; off_t flen; int result; unsigned tflen, ulen; trace_type_t *ttype; if (leasep) ttype = trace_readleases_type; else ttype = trace_readconf_type; /* If we're in playback, we need to snarf the contents of the named file out of the playback file rather than trying to open and read it. */ if (trace_playback ()) { dbuf = (char *)0; tflen = 0; status = trace_get_file (ttype, filename, &tflen, &dbuf); if (status != ISC_R_SUCCESS) return status; ulen = tflen; /* What we get back is filename\0contents, where contents is terminated just by the length. So we figure out the length of the filename, and subtract that and the NUL from the total length to get the length of the contents of the file. We make fbuf a pointer to the contents of the file, and leave dbuf as it is so we can free it later. */ tflen = strlen (dbuf); ulen = ulen - tflen - 1; fbuf = dbuf + tflen + 1; goto memfile; } #endif if ((file = open (filename, O_RDONLY)) < 0) { if (leasep) { log_error ("Can't open lease database %s: %m --", path_dhcpd_db); log_error (" check for failed database %s!", "rewrite attempt"); log_error ("Please read the dhcpd.leases manual%s", " page if you"); log_fatal ("don't know what to do about this."); } else { log_fatal ("Can't open %s: %m", filename); } } cfile = (struct parse *)0; #if defined (TRACING) flen = lseek (file, (off_t)0, SEEK_END); if (flen < 0) { boom: log_fatal ("Can't lseek on %s: %m", filename); } if (lseek (file, (off_t)0, SEEK_SET) < 0) goto boom; /* Can't handle files greater than 2^31-1. */ if (flen > 0x7FFFFFFFUL) log_fatal ("%s: file is too long to buffer.", filename); ulen = flen; /* Allocate a buffer that will be what's written to the tracefile, and also will be what we parse from. */ tflen = strlen (filename); dbuf = dmalloc (ulen + tflen + 1, MDL); if (!dbuf) log_fatal ("No memory for %s (%d bytes)", filename, ulen); /* Copy the name into the beginning, nul-terminated. */ strcpy (dbuf, filename); /* Load the file in after the NUL. */ fbuf = dbuf + tflen + 1; result = read (file, fbuf, ulen); if (result < 0) log_fatal ("Can't read in %s: %m", filename); if (result != ulen) log_fatal ("%s: short read of %d bytes instead of %d.", filename, ulen, result); close (file); memfile: /* If we're recording, write out the filename and file contents. */ if (trace_record ()) trace_write_packet (ttype, ulen + tflen + 1, dbuf, MDL); status = new_parse(&cfile, -1, fbuf, ulen, filename, 0); /* XXX */ #else status = new_parse(&cfile, file, NULL, 0, filename, 0); #endif if (status != ISC_R_SUCCESS || cfile == NULL) return status; if (leasep) status = lease_file_subparse (cfile); else status = conf_file_subparse (cfile, group, group_type); end_parse (&cfile); #if defined (TRACING) dfree (dbuf, MDL); #endif return status; } #if defined (TRACING) void trace_conf_input (trace_type_t *ttype, unsigned len, char *data) { char *fbuf; unsigned flen; unsigned tflen; struct parse *cfile = (struct parse *)0; static int postconf_initialized; static int leaseconf_initialized; isc_result_t status; /* Do what's done above, except that we don't have to read in the data, because it's already been read for us. */ tflen = strlen (data); flen = len - tflen - 1; fbuf = data + tflen + 1; /* If we're recording, write out the filename and file contents. */ if (trace_record ()) trace_write_packet (ttype, len, data, MDL); status = new_parse(&cfile, -1, fbuf, flen, data, 0); if (status == ISC_R_SUCCESS || cfile != NULL) { if (ttype == trace_readleases_type) lease_file_subparse (cfile); else conf_file_subparse (cfile, root_group, ROOT_GROUP); end_parse (&cfile); } /* Postconfiguration needs to be done after the config file has been loaded. */ if (!postconf_initialized && ttype == trace_readconf_type) { postconf_initialization (0); postconf_initialized = 1; } if (!leaseconf_initialized && ttype == trace_readleases_type) { db_startup (0); leaseconf_initialized = 1; postdb_startup (); } } void trace_conf_stop (trace_type_t *ttype) { } #endif /* conf-file :== parameters declarations END_OF_FILE parameters :== | parameter | parameters parameter declarations :== | declaration | declarations declaration */ isc_result_t conf_file_subparse (struct parse *cfile, struct group *group, int group_type) { const char *val; enum dhcp_token token; int declaration = 0; int status; do { token = peek_token (&val, (unsigned *)0, cfile); if (token == END_OF_FILE) break; declaration = parse_statement (cfile, group, group_type, (struct host_decl *)0, declaration); } while (1); skip_token(&val, (unsigned *)0, cfile); status = cfile->warnings_occurred ? DHCP_R_BADPARSE : ISC_R_SUCCESS; return status; } /* lease-file :== lease-declarations END_OF_FILE lease-statements :== | lease-declaration | lease-declarations lease-declaration */ isc_result_t lease_file_subparse (struct parse *cfile) { const char *val; enum dhcp_token token; isc_result_t status; do { token = next_token (&val, (unsigned *)0, cfile); if (token == END_OF_FILE) break; if (token == LEASE) { struct lease *lease = (struct lease *)0; if (parse_lease_declaration (&lease, cfile)) { enter_lease (lease); lease_dereference (&lease, MDL); } else parse_warn (cfile, "possibly corrupt lease file"); } else if (token == IA_NA) { parse_ia_na_declaration(cfile); } else if (token == IA_TA) { parse_ia_ta_declaration(cfile); } else if (token == IA_PD) { parse_ia_pd_declaration(cfile); } else if (token == CLASS) { parse_class_declaration(0, cfile, root_group, CLASS_TYPE_CLASS); } else if (token == SUBCLASS) { parse_class_declaration(0, cfile, root_group, CLASS_TYPE_SUBCLASS); } else if (token == HOST) { parse_host_declaration (cfile, root_group); } else if (token == GROUP) { parse_group_declaration (cfile, root_group); #if defined (FAILOVER_PROTOCOL) } else if (token == FAILOVER) { parse_failover_state_declaration (cfile, (dhcp_failover_state_t *)0); #endif #ifdef DHCPv6 } else if (token == SERVER_DUID) { parse_server_duid(cfile); #endif /* DHCPv6 */ } else if (token == AUTHORING_BYTE_ORDER) { parse_authoring_byte_order(cfile); } else { log_error ("Corrupt lease file - possible data loss!"); skip_to_semi (cfile); } } while (1); status = cfile->warnings_occurred ? DHCP_R_BADPARSE : ISC_R_SUCCESS; return status; } /* statement :== parameter | declaration parameter :== DEFAULT_LEASE_TIME lease_time | MAX_LEASE_TIME lease_time | DYNAMIC_BOOTP_LEASE_CUTOFF date | DYNAMIC_BOOTP_LEASE_LENGTH lease_time | BOOT_UNKNOWN_CLIENTS boolean | ONE_LEASE_PER_CLIENT boolean | GET_LEASE_HOSTNAMES boolean | USE_HOST_DECL_NAME boolean | NEXT_SERVER ip-addr-or-hostname SEMI | option_parameter | SERVER-IDENTIFIER ip-addr-or-hostname SEMI | FILENAME string-parameter | SERVER_NAME string-parameter | hardware-parameter | fixed-address-parameter | ALLOW allow-deny-keyword | DENY allow-deny-keyword | USE_LEASE_ADDR_FOR_DEFAULT_ROUTE boolean | AUTHORITATIVE | NOT AUTHORITATIVE declaration :== host-declaration | group-declaration | shared-network-declaration | subnet-declaration | VENDOR_CLASS class-declaration | USER_CLASS class-declaration | RANGE address-range-declaration */ int parse_statement (cfile, group, type, host_decl, declaration) struct parse *cfile; struct group *group; int type; struct host_decl *host_decl; int declaration; { enum dhcp_token token; const char *val; struct shared_network *share; char *n; struct hardware hardware; struct executable_statement *et, *ep; struct option *option = NULL; struct option_cache *cache; int lose; int known; isc_result_t status; unsigned code; token = peek_token (&val, (unsigned *)0, cfile); switch (token) { case INCLUDE: skip_token(&val, (unsigned *)0, cfile); token = next_token (&val, (unsigned *)0, cfile); if (token != STRING) { parse_warn (cfile, "filename string expected."); skip_to_semi (cfile); } else { status = read_conf_file (val, group, type, 0); if (status != ISC_R_SUCCESS) parse_warn (cfile, "%s: bad parse.", val); parse_semi (cfile); } return 1; case HOST: skip_token(&val, (unsigned *)0, cfile); if (type != HOST_DECL && type != CLASS_DECL) { if (global_host_once && (type == SUBNET_DECL || type == SHARED_NET_DECL)) { global_host_once = 0; log_error("WARNING: Host declarations are " "global. They are not limited to " "the scope you declared them in."); } parse_host_declaration (cfile, group); } else { parse_warn (cfile, "host declarations not allowed here."); skip_to_semi (cfile); } return 1; case GROUP: skip_token(&val, (unsigned *)0, cfile); if (type != HOST_DECL && type != CLASS_DECL) parse_group_declaration (cfile, group); else { parse_warn (cfile, "group declarations not allowed here."); skip_to_semi (cfile); } return 1; case SHARED_NETWORK: skip_token(&val, (unsigned *)0, cfile); if (type == SHARED_NET_DECL || type == HOST_DECL || type == SUBNET_DECL || type == CLASS_DECL) { parse_warn (cfile, "shared-network parameters not %s.", "allowed here"); skip_to_semi (cfile); break; } parse_shared_net_declaration (cfile, group); return 1; case SUBNET: case SUBNET6: skip_token(&val, (unsigned *)0, cfile); if (type == HOST_DECL || type == SUBNET_DECL || type == CLASS_DECL) { parse_warn (cfile, "subnet declarations not allowed here."); skip_to_semi (cfile); return 1; } /* If we're in a subnet declaration, just do the parse. */ if (group->shared_network != NULL) { if (token == SUBNET) { parse_subnet_declaration(cfile, group->shared_network); } else { parse_subnet6_declaration(cfile, group->shared_network); } break; } /* * Otherwise, cons up a fake shared network structure * and populate it with the lone subnet...because the * intention most likely is to refer to the entire link * by shorthand, any configuration inside the subnet is * actually placed in the shared-network's group. */ share = NULL; status = shared_network_allocate (&share, MDL); if (status != ISC_R_SUCCESS) log_fatal ("Can't allocate shared subnet: %s", isc_result_totext (status)); if (!clone_group (&share -> group, group, MDL)) log_fatal ("Can't allocate group for shared net"); shared_network_reference (&share -> group -> shared_network, share, MDL); /* * This is an implicit shared network, not explicit in * the config. */ share->flags |= SHARED_IMPLICIT; if (token == SUBNET) { parse_subnet_declaration(cfile, share); } else { parse_subnet6_declaration(cfile, share); } /* share -> subnets is the subnet we just parsed. */ if (share->subnets) { interface_reference(&share->interface, share->subnets->interface, MDL); /* Make the shared network name from network number. */ if (token == SUBNET) { n = piaddrmask(&share->subnets->net, &share->subnets->netmask); } else { n = piaddrcidr(&share->subnets->net, share->subnets->prefix_len); } share->name = strdup(n); if (share->name == NULL) log_fatal("Out of memory allocating default " "shared network name (\"%s\").", n); /* Copy the authoritative parameter from the subnet, since there is no opportunity to declare it here. */ share->group->authoritative = share->subnets->group->authoritative; enter_shared_network(share); } shared_network_dereference(&share, MDL); return 1; case VENDOR_CLASS: skip_token(&val, (unsigned *)0, cfile); if (type == CLASS_DECL) { parse_warn (cfile, "class declarations not allowed here."); skip_to_semi (cfile); break; } parse_class_declaration(NULL, cfile, group, CLASS_TYPE_VENDOR); return 1; case USER_CLASS: skip_token(&val, (unsigned *)0, cfile); if (type == CLASS_DECL) { parse_warn (cfile, "class declarations not allowed here."); skip_to_semi (cfile); break; } parse_class_declaration(NULL, cfile, group, CLASS_TYPE_USER); return 1; case CLASS: skip_token(&val, (unsigned *)0, cfile); if (type == CLASS_DECL) { parse_warn (cfile, "class declarations not allowed here."); skip_to_semi (cfile); break; } parse_class_declaration(NULL, cfile, group, CLASS_TYPE_CLASS); return 1; case SUBCLASS: skip_token(&val, (unsigned *)0, cfile); if (type == CLASS_DECL) { parse_warn (cfile, "class declarations not allowed here."); skip_to_semi (cfile); break; } parse_class_declaration(NULL, cfile, group, CLASS_TYPE_SUBCLASS); return 1; case HARDWARE: skip_token(&val, (unsigned *)0, cfile); memset (&hardware, 0, sizeof hardware); if (host_decl && memcmp(&hardware, &(host_decl->interface), sizeof(hardware)) != 0) { parse_warn(cfile, "Host %s hardware address already " "configured.", host_decl->name); break; } parse_hardware_param (cfile, &hardware); if (host_decl) host_decl -> interface = hardware; else parse_warn (cfile, "hardware address parameter %s", "not allowed here."); break; case FIXED_ADDR: case FIXED_ADDR6: skip_token(&val, NULL, cfile); cache = NULL; if (parse_fixed_addr_param(&cache, cfile, token)) { if (host_decl) { if (host_decl->fixed_addr) { option_cache_dereference(&cache, MDL); parse_warn(cfile, "Only one fixed address " "declaration per host."); } else { host_decl->fixed_addr = cache; } } else { parse_warn(cfile, "fixed-address parameter not " "allowed here."); option_cache_dereference(&cache, MDL); } } break; case POOL: skip_token(&val, (unsigned *)0, cfile); if (type == POOL_DECL) { parse_warn (cfile, "pool declared within pool."); skip_to_semi(cfile); } else if (type != SUBNET_DECL && type != SHARED_NET_DECL) { parse_warn (cfile, "pool declared outside of network"); skip_to_semi(cfile); } else parse_pool_statement (cfile, group, type); return declaration; case RANGE: skip_token(&val, (unsigned *)0, cfile); if (type != SUBNET_DECL || !group -> subnet) { parse_warn (cfile, "range declaration not allowed here."); skip_to_semi (cfile); return declaration; } parse_address_range (cfile, group, type, (struct pool *)0, (struct lease **)0); return declaration; #ifdef DHCPv6 case RANGE6: skip_token(NULL, NULL, cfile); if ((type != SUBNET_DECL) || (group->subnet == NULL)) { parse_warn (cfile, "range6 declaration not allowed here."); skip_to_semi(cfile); return declaration; } parse_address_range6(cfile, group, NULL); return declaration; case PREFIX6: skip_token(NULL, NULL, cfile); if ((type != SUBNET_DECL) || (group->subnet == NULL)) { parse_warn (cfile, "prefix6 declaration not allowed here."); skip_to_semi(cfile); return declaration; } parse_prefix6(cfile, group, NULL); return declaration; case FIXED_PREFIX6: skip_token(&val, NULL, cfile); if (!host_decl) { parse_warn (cfile, "fixed-prefix6 declaration not " "allowed here."); skip_to_semi(cfile); break; } parse_fixed_prefix6(cfile, host_decl); break; case POOL6: skip_token(&val, NULL, cfile); if (type == POOL_DECL) { parse_warn (cfile, "pool6 declared within pool."); skip_to_semi(cfile); } else if (type != SUBNET_DECL) { parse_warn (cfile, "pool6 declared outside of network"); skip_to_semi(cfile); } else parse_pool6_statement (cfile, group, type); return declaration; #endif /* DHCPv6 */ case TOKEN_NOT: skip_token(&val, (unsigned *)0, cfile); token = next_token (&val, (unsigned *)0, cfile); switch (token) { case AUTHORITATIVE: group -> authoritative = 0; goto authoritative; default: parse_warn (cfile, "expecting assertion"); skip_to_semi (cfile); break; } break; case AUTHORITATIVE: skip_token(&val, (unsigned *)0, cfile); group -> authoritative = 1; authoritative: if (type == HOST_DECL) parse_warn (cfile, "authority makes no sense here."); parse_semi (cfile); break; /* "server-identifier" is a special hack, equivalent to "option dhcp-server-identifier". */ case SERVER_IDENTIFIER: code = DHO_DHCP_SERVER_IDENTIFIER; if (!option_code_hash_lookup(&option, dhcp_universe.code_hash, &code, 0, MDL)) log_fatal("Server identifier not in hash (%s:%d).", MDL); skip_token(&val, (unsigned *)0, cfile); goto finish_option; case OPTION: skip_token(&val, (unsigned *)0, cfile); token = peek_token (&val, (unsigned *)0, cfile); if (token == SPACE) { if (type != ROOT_GROUP) { parse_warn (cfile, "option space definitions %s", "may not be scoped."); skip_to_semi (cfile); break; } parse_option_space_decl (cfile); return declaration; } known = 0; status = parse_option_name(cfile, 1, &known, &option); if (status == ISC_R_SUCCESS) { token = peek_token (&val, (unsigned *)0, cfile); if (token == CODE) { if (type != ROOT_GROUP) { parse_warn (cfile, "option definitions%s", " may not be scoped."); skip_to_semi (cfile); option_dereference(&option, MDL); break; } skip_token(&val, (unsigned *)0, cfile); /* * If the option was known, remove it from the * code and name hashes before redefining it. */ if (known) { option_name_hash_delete( option->universe->name_hash, option->name, 0, MDL); option_code_hash_delete( option->universe->code_hash, &option->code, 0, MDL); } parse_option_code_definition(cfile, option); option_dereference(&option, MDL); return declaration; } /* If this wasn't an option code definition, don't allow an unknown option. */ if (!known) { parse_warn (cfile, "unknown option %s.%s", option -> universe -> name, option -> name); skip_to_semi (cfile); option_dereference(&option, MDL); return declaration; } finish_option: et = (struct executable_statement *)0; if (!parse_option_statement (&et, cfile, 1, option, supersede_option_statement)) return declaration; option_dereference(&option, MDL); goto insert_statement; } else return declaration; break; case FAILOVER: if (type != ROOT_GROUP && type != SHARED_NET_DECL) { parse_warn (cfile, "failover peers may only be %s", "defined in shared-network"); log_error ("declarations and the outer scope."); skip_to_semi (cfile); break; } token = next_token (&val, (unsigned *)0, cfile); #if defined (FAILOVER_PROTOCOL) parse_failover_peer (cfile, group, type); #else parse_warn (cfile, "No failover support."); skip_to_semi (cfile); #endif break; #ifdef DHCPv6 case SERVER_DUID: parse_server_duid_conf(cfile); break; #endif /* DHCPv6 */ case LEASE_ID_FORMAT: token = next_token (&val, (unsigned *)0, cfile); parse_lease_id_format(cfile); break; case PERCENT: /* Used by the MA so simply ignore... */ skip_to_semi (cfile); break; default: et = (struct executable_statement *)0; lose = 0; if (!parse_executable_statement (&et, cfile, &lose, context_any)) { if (!lose) { if (declaration) parse_warn (cfile, "expecting a declaration"); else parse_warn (cfile, "expecting a parameter %s", "or declaration"); skip_to_semi (cfile); } return declaration; } if (!et) return declaration; insert_statement: if (group -> statements) { int multi = 0; /* If this set of statements is only referenced by this group, just add the current statement to the end of the chain. */ for (ep = group -> statements; ep -> next; ep = ep -> next) if (ep -> refcnt > 1) /* XXX */ multi = 1; if (!multi) { executable_statement_reference (&ep -> next, et, MDL); executable_statement_dereference (&et, MDL); return declaration; } /* Otherwise, make a parent chain, and put the current group statements first and the new statement in the next pointer. */ ep = (struct executable_statement *)0; if (!executable_statement_allocate (&ep, MDL)) log_fatal ("No memory for statements."); ep -> op = statements_statement; executable_statement_reference (&ep -> data.statements, group -> statements, MDL); executable_statement_reference (&ep -> next, et, MDL); executable_statement_dereference (&group -> statements, MDL); executable_statement_reference (&group -> statements, ep, MDL); executable_statement_dereference (&ep, MDL); } else { executable_statement_reference (&group -> statements, et, MDL); } executable_statement_dereference (&et, MDL); return declaration; } return 0; } #if defined (FAILOVER_PROTOCOL) void parse_failover_peer (cfile, group, type) struct parse *cfile; struct group *group; int type; { enum dhcp_token token; const char *val; dhcp_failover_state_t *peer; u_int32_t *tp; char *name; u_int32_t split; u_int8_t hba [32]; unsigned hba_len = sizeof hba; int i; struct expression *expr; isc_result_t status; dhcp_failover_config_t *cp; token = next_token (&val, (unsigned *)0, cfile); if (token != PEER) { parse_warn (cfile, "expecting \"peer\""); skip_to_semi (cfile); return; } token = next_token (&val, (unsigned *)0, cfile); if (is_identifier (token) || token == STRING) { name = dmalloc (strlen (val) + 1, MDL); if (!name) log_fatal ("no memory for peer name %s", name); strcpy (name, val); } else { parse_warn (cfile, "expecting failover peer name."); skip_to_semi (cfile); return; } /* See if there's a peer declaration by this name. */ peer = (dhcp_failover_state_t *)0; find_failover_peer (&peer, name, MDL); token = next_token (&val, (unsigned *)0, cfile); if (token == SEMI) { if (type != SHARED_NET_DECL) parse_warn (cfile, "failover peer reference not %s", "in shared-network declaration"); else { if (!peer) { parse_warn (cfile, "reference to unknown%s%s", " failover peer ", name); dfree (name, MDL); return; } dhcp_failover_state_reference (&group -> shared_network -> failover_peer, peer, MDL); } dhcp_failover_state_dereference (&peer, MDL); dfree (name, MDL); return; } else if (token == STATE) { if (!peer) { parse_warn (cfile, "state declaration for unknown%s%s", " failover peer ", name); dfree (name, MDL); return; } parse_failover_state_declaration (cfile, peer); dhcp_failover_state_dereference (&peer, MDL); dfree (name, MDL); return; } else if (token != LBRACE) { parse_warn (cfile, "expecting left brace"); skip_to_semi (cfile); } /* Make sure this isn't a redeclaration. */ if (peer) { parse_warn (cfile, "redeclaration of failover peer %s", name); skip_to_rbrace (cfile, 1); dhcp_failover_state_dereference (&peer, MDL); dfree (name, MDL); return; } status = dhcp_failover_state_allocate (&peer, MDL); if (status != ISC_R_SUCCESS) log_fatal ("Can't allocate failover peer %s: %s", name, isc_result_totext (status)); /* Save the name. */ peer -> name = name; do { cp = &peer -> me; peer: token = next_token (&val, (unsigned *)0, cfile); switch (token) { case RBRACE: break; case PRIMARY: peer -> i_am = primary; break; case SECONDARY: peer -> i_am = secondary; if (peer -> hba) parse_warn (cfile, "secondary may not define %s", "load balance settings."); break; case PEER: cp = &peer -> partner; goto peer; case ADDRESS: expr = (struct expression *)0; if (!parse_ip_addr_or_hostname (&expr, cfile, 0)) { skip_to_rbrace (cfile, 1); dhcp_failover_state_dereference (&peer, MDL); return; } option_cache (&cp -> address, (struct data_string *)0, expr, (struct option *)0, MDL); expression_dereference (&expr, MDL); break; case PORT: token = next_token (&val, (unsigned *)0, cfile); if (token != NUMBER) { parse_warn (cfile, "expecting number"); skip_to_rbrace (cfile, 1); } cp -> port = atoi (val); break; case MAX_LEASE_MISBALANCE: tp = &peer->max_lease_misbalance; goto parse_idle; case MAX_LEASE_OWNERSHIP: tp = &peer->max_lease_ownership; goto parse_idle; case MAX_BALANCE: tp = &peer->max_balance; goto parse_idle; case MIN_BALANCE: tp = &peer->min_balance; goto parse_idle; case AUTO_PARTNER_DOWN: tp = &peer->auto_partner_down; goto parse_idle; case MAX_RESPONSE_DELAY: tp = &cp -> max_response_delay; parse_idle: token = next_token (&val, (unsigned *)0, cfile); if (token != NUMBER) { parse_warn (cfile, "expecting number."); skip_to_rbrace (cfile, 1); dhcp_failover_state_dereference (&peer, MDL); return; } *tp = atoi (val); break; case MAX_UNACKED_UPDATES: tp = &cp -> max_flying_updates; goto parse_idle; case MCLT: tp = &peer -> mclt; goto parse_idle; case HBA: hba_len = 32; if (peer -> i_am == secondary) parse_warn (cfile, "secondary may not define %s", "load balance settings."); if (!parse_numeric_aggregate (cfile, hba, &hba_len, COLON, 16, 8)) { skip_to_rbrace (cfile, 1); dhcp_failover_state_dereference (&peer, MDL); return; } if (hba_len != 32) { parse_warn (cfile, "HBA must be exactly 32 bytes."); break; } make_hba: peer -> hba = dmalloc (32, MDL); if (!peer -> hba) { dfree (peer -> name, MDL); dfree (peer, MDL); } memcpy (peer -> hba, hba, 32); break; case SPLIT: token = next_token (&val, (unsigned *)0, cfile); if (peer -> i_am == secondary) parse_warn (cfile, "secondary may not define %s", "load balance settings."); if (token != NUMBER) { parse_warn (cfile, "expecting number"); skip_to_rbrace (cfile, 1); dhcp_failover_state_dereference (&peer, MDL); return; } split = atoi (val); if (split > 256) { parse_warn (cfile, "split must be between " "0 and 256, inclusive"); } else { memset (hba, 0, sizeof hba); for (i = 0; i < split; i++) { if (i < split) hba [i / 8] |= (1 << (i & 7)); } goto make_hba; } break; case LOAD: token = next_token (&val, (unsigned *)0, cfile); if (token != BALANCE) { parse_warn (cfile, "expecting 'balance'"); badload: skip_to_rbrace (cfile, 1); break; } token = next_token (&val, (unsigned *)0, cfile); if (token != TOKEN_MAX) { parse_warn (cfile, "expecting 'max'"); goto badload; } token = next_token (&val, (unsigned *)0, cfile); if (token != SECONDS) { parse_warn (cfile, "expecting 'secs'"); goto badload; } token = next_token (&val, (unsigned *)0, cfile); if (token != NUMBER) { parse_warn (cfile, "expecting number"); goto badload; } peer -> load_balance_max_secs = atoi (val); break; default: parse_warn (cfile, "invalid statement in peer declaration"); skip_to_rbrace (cfile, 1); dhcp_failover_state_dereference (&peer, MDL); return; } if (token != RBRACE && !parse_semi (cfile)) { skip_to_rbrace (cfile, 1); dhcp_failover_state_dereference (&peer, MDL); return; } } while (token != RBRACE); /* me.address can be null; the failover link initiate code tries to * derive a reasonable address to use. */ if (!peer -> partner.address) parse_warn (cfile, "peer address may not be omitted"); if (!peer->me.port) peer->me.port = DEFAULT_FAILOVER_PORT; if (!peer->partner.port) peer->partner.port = DEFAULT_FAILOVER_PORT; if (peer -> i_am == primary) { if (!peer -> hba) { parse_warn (cfile, "primary failover server must have hba or split."); } else if (!peer -> mclt) { parse_warn (cfile, "primary failover server must have mclt."); } } if (!peer->max_lease_misbalance) peer->max_lease_misbalance = DEFAULT_MAX_LEASE_MISBALANCE; if (!peer->max_lease_ownership) peer->max_lease_ownership = DEFAULT_MAX_LEASE_OWNERSHIP; if (!peer->max_balance) peer->max_balance = DEFAULT_MAX_BALANCE_TIME; if (!peer->min_balance) peer->min_balance = DEFAULT_MIN_BALANCE_TIME; if (!peer->me.max_flying_updates) peer->me.max_flying_updates = DEFAULT_MAX_FLYING_UPDATES; if (!peer->me.max_response_delay) peer->me.max_response_delay = DEFAULT_MAX_RESPONSE_DELAY; if (type == SHARED_NET_DECL) group->shared_network->failover_peer = peer; /* Set the initial state. */ peer->me.state = recover; peer->me.stos = cur_time; peer->partner.state = unknown_state; peer->partner.stos = cur_time; status = enter_failover_peer (peer); if (status != ISC_R_SUCCESS) parse_warn (cfile, "failover peer %s: %s", peer -> name, isc_result_totext (status)); dhcp_failover_state_dereference (&peer, MDL); } void parse_failover_state_declaration (struct parse *cfile, dhcp_failover_state_t *peer) { enum dhcp_token token; const char *val; char *name; dhcp_failover_state_t *state; dhcp_failover_config_t *cp; if (!peer) { token = next_token (&val, (unsigned *)0, cfile); if (token != PEER) { parse_warn (cfile, "expecting \"peer\""); skip_to_semi (cfile); return; } token = next_token (&val, (unsigned *)0, cfile); if (is_identifier (token) || token == STRING) { name = dmalloc (strlen (val) + 1, MDL); if (!name) log_fatal ("failover peer name %s: no memory", name); strcpy (name, val); } else { parse_warn (cfile, "expecting failover peer name."); skip_to_semi (cfile); return; } /* See if there's a peer declaration by this name. */ state = (dhcp_failover_state_t *)0; find_failover_peer (&state, name, MDL); if (!state) { parse_warn (cfile, "unknown failover peer: %s", name); skip_to_semi (cfile); return; } token = next_token (&val, (unsigned *)0, cfile); if (token != STATE) { parse_warn (cfile, "expecting 'state'"); if (token != SEMI) skip_to_semi (cfile); return; } } else { state = (dhcp_failover_state_t *)0; dhcp_failover_state_reference (&state, peer, MDL); } token = next_token (&val, (unsigned *)0, cfile); if (token != LBRACE) { parse_warn (cfile, "expecting left brace"); if (token != SEMI) skip_to_semi (cfile); dhcp_failover_state_dereference (&state, MDL); return; } do { token = next_token (&val, (unsigned *)0, cfile); switch (token) { case RBRACE: break; case MY: cp = &state -> me; do_state: token = next_token (&val, (unsigned *)0, cfile); if (token != STATE) { parse_warn (cfile, "expecting 'state'"); goto bogus; } parse_failover_state (cfile, &cp -> state, &cp -> stos); break; case PARTNER: cp = &state -> partner; goto do_state; case MCLT: if (state -> i_am == primary) { parse_warn (cfile, "mclt not valid for primary"); goto bogus; } token = next_token (&val, (unsigned *)0, cfile); if (token != NUMBER) { parse_warn (cfile, "expecting a number."); goto bogus; } state -> mclt = atoi (val); parse_semi (cfile); break; default: parse_warn (cfile, "expecting state setting."); bogus: skip_to_rbrace (cfile, 1); dhcp_failover_state_dereference (&state, MDL); return; } } while (token != RBRACE); dhcp_failover_state_dereference (&state, MDL); } void parse_failover_state (cfile, state, stos) struct parse *cfile; enum failover_state *state; TIME *stos; { enum dhcp_token token; const char *val; enum failover_state state_in; TIME stos_in; token = next_token (&val, (unsigned *)0, cfile); switch (token) { case UNKNOWN_STATE: state_in = unknown_state; break; case PARTNER_DOWN: state_in = partner_down; break; case NORMAL: state_in = normal; break; case COMMUNICATIONS_INTERRUPTED: state_in = communications_interrupted; break; case CONFLICT_DONE: state_in = conflict_done; break; case RESOLUTION_INTERRUPTED: state_in = resolution_interrupted; break; case POTENTIAL_CONFLICT: state_in = potential_conflict; break; case RECOVER: state_in = recover; break; case RECOVER_WAIT: state_in = recover_wait; break; case RECOVER_DONE: state_in = recover_done; break; case SHUTDOWN: state_in = shut_down; break; case PAUSED: state_in = paused; break; case STARTUP: state_in = startup; break; default: parse_warn (cfile, "unknown failover state"); skip_to_semi (cfile); return; } token = next_token (&val, (unsigned *)0, cfile); if (token == SEMI) { stos_in = cur_time; } else { if (token != AT) { parse_warn (cfile, "expecting \"at\""); skip_to_semi (cfile); return; } stos_in = parse_date (cfile); if (!stos_in) return; } /* Now that we've apparently gotten a clean parse, we can trust that this is a state that was fully committed to disk, so we can install it. */ *stos = stos_in; *state = state_in; } #endif /* defined (FAILOVER_PROTOCOL) */ /*! * \brief Parses an authoring-byte-order statement * * A valid statement looks like this: * * authoring-byte-order :== * PARSE_BYTE_ORDER TOKEN_LITTLE_ENDIAN | TOKEN_BIG_ENDIAN ; * * If the global, authoring_byte_order is not zero, then either the statement * has already been parsed or the function, parse_byte_order_uint32, has * been called which set it to the default. In either case, this is invalid * so we'll log it and bail. * * If the value is different from the current server's byte order, then we'll * log that fact and set authoring_byte_order to given value. This causes all * invocations of the function, parse_byte_order_uint32, to perform byte-order * conversion before returning the value. * * \param cfile the current parse file * */ void parse_authoring_byte_order (struct parse *cfile) { enum dhcp_token token; const char *val; unsigned int len; /* Either we've seen it already or it's after the first lease */ if (authoring_byte_order != 0) { parse_warn (cfile, "authoring-byte-order specified too late.\n" "It must occur before the first lease in file\n"); skip_to_semi (cfile); return; } token = next_token(&val, (unsigned *)0, cfile); switch(token) { case TOKEN_LITTLE_ENDIAN: authoring_byte_order = LITTLE_ENDIAN; break; case TOKEN_BIG_ENDIAN: authoring_byte_order = BIG_ENDIAN; break; default: parse_warn(cfile, "authoring-byte-order is invalid: " " it must be big-endian or little-endian."); skip_to_semi(cfile); return; } if (authoring_byte_order != DHCP_BYTE_ORDER) { log_error ("WARNING: Lease file authored using different" " byte order, will attempt to convert"); } token = next_token(&val, &len, cfile); if (token != SEMI) { parse_warn(cfile, "corrupt lease file; expecting a semicolon"); skip_to_semi(cfile); return; } } /*! * \brief Parses a lease-id-format statement * * A valid statement looks like this: * * lease-id-format :== * LEASE_ID_FORMAT TOKEN_OCTAL | TOKEN_HEX ; * * This function is used to parse the lease-id-format statement. It sets the * global variable, lease_id_format. * * \param cfile the current parse file * */ void parse_lease_id_format (struct parse *cfile) { enum dhcp_token token; const char *val; unsigned int len; token = next_token(&val, NULL, cfile); switch(token) { case TOKEN_OCTAL: lease_id_format = TOKEN_OCTAL; break; case TOKEN_HEX: lease_id_format = TOKEN_HEX; break; default: parse_warn(cfile, "lease-id-format is invalid: " " it must be octal or hex."); skip_to_semi(cfile); return; } log_debug("lease_id_format is: %s", lease_id_format == TOKEN_OCTAL ? "octal" : "hex"); token = next_token(&val, &len, cfile); if (token != SEMI) { parse_warn(cfile, "corrupt lease file; expecting a semicolon"); skip_to_semi(cfile); return; } } /*! * * \brief Parse allow and deny statements * * This function handles the common processing code for permit and deny * statements in the parse_pool_statement and parse_pool6_statement functions. * It reads in the configuration and constructs a new permit structure that it * attachs to the permit_head passed in from the caller. * * The allow or deny token should already be consumed, this function expects * one of the following: * known-clients; * unknown-clients; * known clients; * unknown clients; * authenticated clients; * unauthenticated clients; * all clients; * dynamic bootp clients; * members of ; * after ; * * \param[in] cfile = the configuration file being parsed * \param[in] permit_head = the head of the permit list (permit or prohibit) * to which to attach the newly created permit structure * \param[in] is_allow = 1 if this is being invoked for an allow statement * = 0 if this is being invoked for a deny statement * \param[in] valid_from = pointers to the time values from the enclosing pool * \param[in] valid_until or pond structure. One of them will be filled in if * the configuration includes an "after" clause */ void get_permit(cfile, permit_head, is_allow, valid_from, valid_until) struct parse *cfile; struct permit **permit_head; int is_allow; TIME *valid_from, *valid_until; { enum dhcp_token token; struct permit *permit; const char *val; int need_clients = 1; TIME t; /* Create our permit structure */ permit = new_permit(MDL); if (!permit) log_fatal ("no memory for permit"); token = next_token(&val, NULL, cfile); switch (token) { case UNKNOWN: permit->type = permit_unknown_clients; break; case KNOWN_CLIENTS: need_clients = 0; permit->type = permit_known_clients; break; case UNKNOWN_CLIENTS: need_clients = 0; permit->type = permit_unknown_clients; break; case KNOWN: permit->type = permit_known_clients; break; case AUTHENTICATED: permit->type = permit_authenticated_clients; break; case UNAUTHENTICATED: permit->type = permit_unauthenticated_clients; break; case ALL: permit->type = permit_all_clients; break; case DYNAMIC: permit->type = permit_dynamic_bootp_clients; if (next_token (&val, NULL, cfile) != TOKEN_BOOTP) { parse_warn (cfile, "expecting \"bootp\""); skip_to_semi (cfile); free_permit (permit, MDL); return; } break; case MEMBERS: need_clients = 0; if (next_token (&val, NULL, cfile) != OF) { parse_warn (cfile, "expecting \"of\""); skip_to_semi (cfile); free_permit (permit, MDL); return; } if (next_token (&val, NULL, cfile) != STRING) { parse_warn (cfile, "expecting class name."); skip_to_semi (cfile); free_permit (permit, MDL); return; } permit->type = permit_class; permit->class = NULL; find_class(&permit->class, val, MDL); if (!permit->class) parse_warn(cfile, "no such class: %s", val); break; case AFTER: need_clients = 0; if (*valid_from || *valid_until) { parse_warn(cfile, "duplicate \"after\" clause."); skip_to_semi(cfile); free_permit(permit, MDL); return; } t = parse_date_core(cfile); permit->type = permit_after; permit->after = t; if (is_allow) { *valid_from = t; } else { *valid_until = t; } break; default: parse_warn (cfile, "expecting permit type."); skip_to_semi (cfile); free_permit (permit, MDL); return; } /* * The need_clients flag is set if we are expecting the * CLIENTS token */ if ((need_clients != 0) && (next_token (&val, NULL, cfile) != CLIENTS)) { parse_warn (cfile, "expecting \"clients\""); skip_to_semi (cfile); free_permit (permit, MDL); return; } while (*permit_head) permit_head = &((*permit_head)->next); *permit_head = permit; parse_semi (cfile); return; } /* Permit_list_match returns 1 if every element of the permit list in lhs also appears in rhs. Note that this doesn't by itself mean that the two lists are equal - to check for equality, permit_list_match has to return 1 with (list1, list2) and with (list2, list1). */ int permit_list_match (struct permit *lhs, struct permit *rhs) { struct permit *plp, *prp; int matched; if (!lhs) return 1; if (!rhs) return 0; for (plp = lhs; plp; plp = plp -> next) { matched = 0; for (prp = rhs; prp; prp = prp -> next) { if (prp -> type == plp -> type && (prp -> type != permit_class || prp -> class == plp -> class)) { matched = 1; break; } } if (!matched) return 0; } return 1; } /*! * * \brief Parse a pool statement * * Pool statements are used to group declarations and permit & deny information * with a specific address range. They must be declared within a shared network * or subnet and there may be multiple pools withing a shared network or subnet. * Each pool may have a different set of permit or deny options. * * \param[in] cfile = the configuration file being parsed * \param[in] group = the group structure for this pool * \param[in] type = the type of the enclosing statement. This must be * SHARED_NET_DECL or SUBNET_DECL for this function. * * \return * void - This function either parses the statement and updates the structures * or it generates an error message and possible halts the program if * it encounters a problem. */ void parse_pool_statement (cfile, group, type) struct parse *cfile; struct group *group; int type; { enum dhcp_token token; const char *val; int done = 0; struct pool *pool, **p, *pp; int declaration = 0; isc_result_t status; struct lease *lpchain = NULL, *lp; pool = NULL; status = pool_allocate(&pool, MDL); if (status != ISC_R_SUCCESS) log_fatal ("no memory for pool: %s", isc_result_totext (status)); if (type == SUBNET_DECL) shared_network_reference(&pool->shared_network, group->subnet->shared_network, MDL); else if (type == SHARED_NET_DECL) shared_network_reference(&pool->shared_network, group->shared_network, MDL); else { parse_warn(cfile, "Dynamic pools are only valid inside " "subnet or shared-network statements."); skip_to_semi(cfile); return; } if (pool->shared_network == NULL || !clone_group(&pool->group, pool->shared_network->group, MDL)) log_fatal("can't clone pool group."); #if defined (FAILOVER_PROTOCOL) /* Inherit the failover peer from the shared network. */ if (pool->shared_network->failover_peer) dhcp_failover_state_reference (&pool->failover_peer, pool->shared_network->failover_peer, MDL); #endif if (!parse_lbrace(cfile)) { pool_dereference(&pool, MDL); return; } do { token = peek_token(&val, NULL, cfile); switch (token) { case TOKEN_NO: skip_token(&val, NULL, cfile); token = next_token(&val, NULL, cfile); if (token != FAILOVER || (token = next_token(&val, NULL, cfile)) != PEER) { parse_warn(cfile, "expecting \"failover peer\"."); skip_to_semi(cfile); continue; } #if defined (FAILOVER_PROTOCOL) if (pool->failover_peer) dhcp_failover_state_dereference (&pool->failover_peer, MDL); #endif break; #if defined (FAILOVER_PROTOCOL) case FAILOVER: skip_token(&val, NULL, cfile); token = next_token (&val, NULL, cfile); if (token != PEER) { parse_warn(cfile, "expecting 'peer'."); skip_to_semi(cfile); break; } token = next_token(&val, NULL, cfile); if (token != STRING) { parse_warn(cfile, "expecting string."); skip_to_semi(cfile); break; } if (pool->failover_peer) dhcp_failover_state_dereference (&pool->failover_peer, MDL); status = find_failover_peer(&pool->failover_peer, val, MDL); if (status != ISC_R_SUCCESS) parse_warn(cfile, "failover peer %s: %s", val, isc_result_totext (status)); else pool->failover_peer->pool_count++; parse_semi(cfile); break; #endif case RANGE: skip_token(&val, NULL, cfile); parse_address_range (cfile, group, type, pool, &lpchain); break; case ALLOW: skip_token(&val, NULL, cfile); get_permit(cfile, &pool->permit_list, 1, &pool->valid_from, &pool->valid_until); break; case DENY: skip_token(&val, NULL, cfile); get_permit(cfile, &pool->prohibit_list, 0, &pool->valid_from, &pool->valid_until); break; case RBRACE: skip_token(&val, NULL, cfile); done = 1; break; case END_OF_FILE: /* * We can get to END_OF_FILE if, for instance, * the parse_statement() reads all available tokens * and leaves us at the end. */ parse_warn(cfile, "unexpected end of file"); goto cleanup; default: declaration = parse_statement(cfile, pool->group, POOL_DECL, NULL, declaration); break; } } while (!done); /* See if there's already a pool into which we can merge this one. */ for (pp = pool->shared_network->pools; pp; pp = pp->next) { if (pp->group->statements != pool->group->statements) continue; #if defined (FAILOVER_PROTOCOL) if (pool->failover_peer != pp->failover_peer) continue; #endif if (!permit_list_match(pp->permit_list, pool->permit_list) || !permit_list_match(pool->permit_list, pp->permit_list) || !permit_list_match(pp->prohibit_list, pool->prohibit_list) || !permit_list_match(pool->prohibit_list, pp->prohibit_list)) continue; /* Okay, we can merge these two pools. All we have to do is fix up the leases, which all point to their pool. */ for (lp = lpchain; lp; lp = lp->next) { pool_dereference(&lp->pool, MDL); pool_reference(&lp->pool, pp, MDL); } #if defined (BINARY_LEASES) /* If we are doing binary leases we also need to add the * addresses in for leasechain allocation. */ pp->lease_count += pool->lease_count; #endif break; } /* If we didn't succeed in merging this pool into another, put it on the list. */ if (!pp) { p = &pool->shared_network->pools; for (; *p; p = &((*p)->next)) ; pool_reference(p, pool, MDL); } /* Don't allow a pool declaration with no addresses, since it is probably a configuration error. */ if (!lpchain) { parse_warn(cfile, "Pool declaration with no address range."); log_error("Pool declarations must always contain at least"); log_error("one range statement."); } cleanup: /* Dereference the lease chain. */ lp = NULL; while (lpchain) { lease_reference(&lp, lpchain, MDL); lease_dereference(&lpchain, MDL); if (lp->next) { lease_reference(&lpchain, lp->next, MDL); lease_dereference(&lp->next, MDL); lease_dereference(&lp, MDL); } } pool_dereference(&pool, MDL); } /* Expect a left brace; if there isn't one, skip over the rest of the statement and return zero; otherwise, return 1. */ int parse_lbrace (cfile) struct parse *cfile; { enum dhcp_token token; const char *val; token = next_token (&val, (unsigned *)0, cfile); if (token != LBRACE) { parse_warn (cfile, "expecting left brace."); skip_to_semi (cfile); return 0; } return 1; } /* host-declaration :== hostname RBRACE parameters declarations LBRACE */ void parse_host_declaration (cfile, group) struct parse *cfile; struct group *group; { const char *val; enum dhcp_token token; struct host_decl *host; char *name; int declaration = 0; int dynamicp = 0; int deleted = 0; isc_result_t status; int known; struct option *option; struct expression *expr = NULL; name = parse_host_name (cfile); if (!name) { parse_warn (cfile, "expecting a name for host declaration."); skip_to_semi (cfile); return; } host = (struct host_decl *)0; status = host_allocate (&host, MDL); if (status != ISC_R_SUCCESS) log_fatal ("can't allocate host decl struct %s: %s", name, isc_result_totext (status)); host -> name = name; if (!clone_group (&host -> group, group, MDL)) { log_fatal ("can't clone group for host %s", name); boom: host_dereference (&host, MDL); return; } if (!parse_lbrace (cfile)) goto boom; do { token = peek_token (&val, (unsigned *)0, cfile); if (token == RBRACE) { skip_token(&val, (unsigned *)0, cfile); break; } if (token == END_OF_FILE) { skip_token(&val, (unsigned *)0, cfile); parse_warn (cfile, "unexpected end of file"); break; } /* If the host declaration was created by the server, remember to save it. */ if (token == DYNAMIC) { dynamicp = 1; skip_token(&val, (unsigned *)0, cfile); if (!parse_semi (cfile)) break; continue; } /* If the host declaration was created by the server, remember to save it. */ if (token == TOKEN_DELETED) { deleted = 1; skip_token(&val, (unsigned *)0, cfile); if (!parse_semi (cfile)) break; continue; } if (token == GROUP) { struct group_object *go; skip_token(&val, (unsigned *)0, cfile); token = next_token (&val, (unsigned *)0, cfile); if (token != STRING && !is_identifier (token)) { parse_warn (cfile, "expecting string or identifier."); skip_to_rbrace (cfile, 1); break; } go = (struct group_object *)0; if (!group_hash_lookup (&go, group_name_hash, val, strlen (val), MDL)) { parse_warn (cfile, "unknown group %s in host %s", val, host -> name); } else { if (host -> named_group) group_object_dereference (&host -> named_group, MDL); group_object_reference (&host -> named_group, go, MDL); group_object_dereference (&go, MDL); } if (!parse_semi (cfile)) break; continue; } if (token == UID) { const char *s; unsigned char *t = 0; unsigned len; skip_token(&val, (unsigned *)0, cfile); data_string_forget (&host -> client_identifier, MDL); if (host->client_identifier.len != 0) { parse_warn(cfile, "Host %s already has a " "client identifier.", host->name); break; } /* See if it's a string or a cshl. */ token = peek_token (&val, (unsigned *)0, cfile); if (token == STRING) { skip_token(&val, &len, cfile); s = val; host -> client_identifier.terminated = 1; } else { len = 0; t = parse_numeric_aggregate (cfile, (unsigned char *)0, &len, ':', 16, 8); if (!t) { parse_warn (cfile, "expecting hex list."); skip_to_semi (cfile); } s = (const char *)t; } if (!buffer_allocate (&host -> client_identifier.buffer, len + host -> client_identifier.terminated, MDL)) log_fatal ("no memory for uid for host %s.", host -> name); host -> client_identifier.data = host -> client_identifier.buffer -> data; host -> client_identifier.len = len; memcpy (host -> client_identifier.buffer -> data, s, len + host -> client_identifier.terminated); if (t) dfree (t, MDL); if (!parse_semi (cfile)) break; continue; } if (token == HOST_IDENTIFIER) { if (host->host_id_option != NULL) { parse_warn(cfile, "only one host-identifier allowed " "per host"); skip_to_rbrace(cfile, 1); break; } skip_token(&val, NULL, cfile); token = next_token(&val, NULL, cfile); if (token == V6RELOPT) { token = next_token(&val, NULL, cfile); if (token != NUMBER) { parse_warn(cfile, "host-identifier v6relopt " "must have a number"); skip_to_rbrace(cfile, 1); break; } host->relays = atoi(val); if (host->relays < 0) { parse_warn(cfile, "host-identifier v6relopt " "must have a number >= 0"); skip_to_rbrace(cfile, 1); break; } } else if (token != OPTION) { parse_warn(cfile, "host-identifier must be an option" " or v6relopt"); skip_to_rbrace(cfile, 1); break; } known = 0; option = NULL; status = parse_option_name(cfile, 1, &known, &option); if ((status != ISC_R_SUCCESS) || (option == NULL)) { break; } if (!known) { parse_warn(cfile, "unknown option %s.%s", option->universe->name, option->name); skip_to_rbrace(cfile, 1); break; } if (! parse_option_data(&expr, cfile, 1, option)) { skip_to_rbrace(cfile, 1); option_dereference(&option, MDL); break; } if (!parse_semi(cfile)) { skip_to_rbrace(cfile, 1); expression_dereference(&expr, MDL); option_dereference(&option, MDL); break; } option_reference(&host->host_id_option, option, MDL); option_dereference(&option, MDL); data_string_copy(&host->host_id, &expr->data.const_data, MDL); expression_dereference(&expr, MDL); continue; } declaration = parse_statement(cfile, host->group, HOST_DECL, host, declaration); } while (1); if (deleted) { struct host_decl *hp = (struct host_decl *)0; if (host_hash_lookup (&hp, host_name_hash, (unsigned char *)host -> name, strlen (host -> name), MDL)) { delete_host (hp, 0); host_dereference (&hp, MDL); } } else { if (host -> named_group && host -> named_group -> group) { if (host -> group -> statements || (host -> group -> authoritative != host -> named_group -> group -> authoritative)) { if (host -> group -> next) group_dereference (&host -> group -> next, MDL); group_reference (&host -> group -> next, host -> named_group -> group, MDL); } else { group_dereference (&host -> group, MDL); group_reference (&host -> group, host -> named_group -> group, MDL); } } if (dynamicp) host -> flags |= HOST_DECL_DYNAMIC; else host -> flags |= HOST_DECL_STATIC; status = enter_host (host, dynamicp, 0); if (status != ISC_R_SUCCESS) parse_warn (cfile, "host %s: %s", host -> name, isc_result_totext (status)); } host_dereference (&host, MDL); } /* class-declaration :== STRING LBRACE parameters declarations RBRACE */ int parse_class_declaration (cp, cfile, group, type) struct class **cp; struct parse *cfile; struct group *group; int type; { const char *val; enum dhcp_token token; struct class *class = NULL, *pc = NULL; int declaration = 0; int lose = 0; struct data_string data; char *name; const char *tname; struct executable_statement *stmt = NULL; int new = 1; isc_result_t status = ISC_R_FAILURE; int matchedonce = 0; int submatchedonce = 0; unsigned code; token = next_token (&val, NULL, cfile); if (token != STRING) { parse_warn (cfile, "Expecting class name"); skip_to_semi (cfile); return 0; } /* See if there's already a class with the specified name. */ find_class (&pc, val, MDL); /* If it is a class, we're updating it. If it's any of the other * types (subclass, vendor or user class), the named class is a * reference to the parent class so its mandatory. */ if (pc && (type == CLASS_TYPE_CLASS)) { class_reference(&class, pc, MDL); new = 0; class_dereference(&pc, MDL); } else if (!pc && (type != CLASS_TYPE_CLASS)) { parse_warn(cfile, "no class named %s", val); skip_to_semi(cfile); return 0; } /* The old vendor-class and user-class declarations had an implicit match. We don't do the implicit match anymore. Instead, for backward compatibility, we have an implicit-vendor-class and an implicit-user-class. vendor-class and user-class declarations are turned into subclasses of the implicit classes, and the submatch expression of the implicit classes extracts the contents of the vendor class or user class. */ if ((type == CLASS_TYPE_VENDOR) || (type == CLASS_TYPE_USER)) { data.len = strlen (val); data.buffer = NULL; if (!buffer_allocate (&data.buffer, data.len + 1, MDL)) log_fatal ("no memory for class name."); data.data = &data.buffer -> data [0]; data.terminated = 1; tname = (type == CLASS_TYPE_VENDOR) ? "implicit-vendor-class" : "implicit-user-class"; } else if (type == CLASS_TYPE_CLASS) { tname = val; } else { tname = NULL; } if (tname) { name = dmalloc (strlen (tname) + 1, MDL); if (!name) log_fatal ("No memory for class name %s.", tname); strcpy (name, tname); } else name = NULL; /* If this is a straight subclass, parse the hash string. */ if (type == CLASS_TYPE_SUBCLASS) { token = peek_token (&val, NULL, cfile); if (token == STRING) { skip_token(&val, &data.len, cfile); data.buffer = NULL; if (!buffer_allocate (&data.buffer, data.len + 1, MDL)) { if (pc) class_dereference (&pc, MDL); return 0; } data.terminated = 1; data.data = &data.buffer -> data [0]; memcpy ((char *)data.buffer -> data, val, data.len + 1); } else if (token == NUMBER_OR_NAME || token == NUMBER) { memset (&data, 0, sizeof data); if (!parse_cshl (&data, cfile)) { if (pc) class_dereference (&pc, MDL); return 0; } } else { parse_warn (cfile, "Expecting string or hex list."); if (pc) class_dereference (&pc, MDL); return 0; } } /* See if there's already a class in the hash table matching the hash data. */ if (type != CLASS_TYPE_CLASS) class_hash_lookup (&class, pc -> hash, (const char *)data.data, data.len, MDL); /* If we didn't find an existing class, allocate a new one. */ if (!class) { /* Allocate the class structure... */ if (type == CLASS_TYPE_SUBCLASS) { status = subclass_allocate (&class, MDL); } else { status = class_allocate (&class, MDL); } if (pc) { group_reference (&class -> group, pc -> group, MDL); class_reference (&class -> superclass, pc, MDL); class -> lease_limit = pc -> lease_limit; if (class -> lease_limit) { class -> billed_leases = dmalloc (class -> lease_limit * sizeof (struct lease *), MDL); if (!class -> billed_leases) log_fatal ("no memory for billing"); memset (class -> billed_leases, 0, (class -> lease_limit * sizeof (struct lease *))); } data_string_copy (&class -> hash_string, &data, MDL); if (!pc -> hash && !class_new_hash (&pc->hash, SCLASS_HASH_SIZE, MDL)) log_fatal ("No memory for subclass hash."); class_hash_add (pc -> hash, (const char *)class -> hash_string.data, class -> hash_string.len, (void *)class, MDL); } else { if (class->group) group_dereference(&class->group, MDL); if (!clone_group (&class -> group, group, MDL)) log_fatal ("no memory to clone class group."); } /* If this is an implicit vendor or user class, add a statement that causes the vendor or user class ID to be sent back in the reply. */ if (type == CLASS_TYPE_VENDOR || type == CLASS_TYPE_USER) { stmt = NULL; if (!executable_statement_allocate (&stmt, MDL)) log_fatal ("no memory for class statement."); stmt -> op = supersede_option_statement; if (option_cache_allocate (&stmt -> data.option, MDL)) { stmt -> data.option -> data = data; code = (type == CLASS_TYPE_VENDOR) ? DHO_VENDOR_CLASS_IDENTIFIER : DHO_USER_CLASS; option_code_hash_lookup( &stmt->data.option->option, dhcp_universe.code_hash, &code, 0, MDL); } class -> statements = stmt; } /* Save the name, if there is one. */ if (class->name != NULL) dfree(class->name, MDL); class->name = name; } if (type != CLASS_TYPE_CLASS) data_string_forget(&data, MDL); /* Spawned classes don't have to have their own settings. */ if (class -> superclass) { token = peek_token (&val, NULL, cfile); if (token == SEMI) { skip_token(&val, NULL, cfile); if (cp) status = class_reference (cp, class, MDL); class_dereference (&class, MDL); if (pc) class_dereference (&pc, MDL); return cp ? (status == ISC_R_SUCCESS) : 1; } /* Give the subclass its own group. */ if (!clone_group (&class -> group, class -> group, MDL)) log_fatal ("can't clone class group."); } if (!parse_lbrace (cfile)) { class_dereference (&class, MDL); if (pc) class_dereference (&pc, MDL); return 0; } do { token = peek_token (&val, NULL, cfile); if (token == RBRACE) { skip_token(&val, NULL, cfile); break; } else if (token == END_OF_FILE) { skip_token(&val, NULL, cfile); parse_warn (cfile, "unexpected end of file"); break; } else if (token == DYNAMIC) { class->flags |= CLASS_DECL_DYNAMIC; skip_token(&val, NULL, cfile); if (!parse_semi (cfile)) break; continue; } else if (token == TOKEN_DELETED) { class->flags |= CLASS_DECL_DELETED; skip_token(&val, NULL, cfile); if (!parse_semi (cfile)) break; continue; } else if (token == MATCH) { if (pc) { parse_warn (cfile, "invalid match in subclass."); skip_to_semi (cfile); break; } skip_token(&val, NULL, cfile); token = peek_token (&val, NULL, cfile); if (token != IF) goto submatch; skip_token(&val, NULL, cfile); if (matchedonce) { parse_warn(cfile, "A class may only have " "one 'match if' clause."); skip_to_semi(cfile); break; } matchedonce = 1; if (class->expr) expression_dereference(&class->expr, MDL); if (!parse_boolean_expression (&class->expr, cfile, &lose)) { if (!lose) { parse_warn (cfile, "expecting boolean expr."); skip_to_semi (cfile); } } else { #if defined (DEBUG_EXPRESSION_PARSE) print_expression ("class match", class -> expr); #endif parse_semi (cfile); } } else if (token == SPAWN) { skip_token(&val, NULL, cfile); if (pc) { parse_warn (cfile, "invalid spawn in subclass."); skip_to_semi (cfile); break; } class -> spawning = 1; token = next_token (&val, NULL, cfile); if (token != WITH) { parse_warn (cfile, "expecting with after spawn"); skip_to_semi (cfile); break; } submatch: if (submatchedonce) { parse_warn (cfile, "can't override existing %s.", "submatch/spawn"); skip_to_semi (cfile); break; } submatchedonce = 1; if (class->submatch) expression_dereference(&class->submatch, MDL); if (!parse_data_expression (&class -> submatch, cfile, &lose)) { if (!lose) { parse_warn (cfile, "expecting data expr."); skip_to_semi (cfile); } } else { #if defined (DEBUG_EXPRESSION_PARSE) print_expression ("class submatch", class -> submatch); #endif parse_semi (cfile); } } else if (token == LEASE) { skip_token(&val, NULL, cfile); token = next_token (&val, NULL, cfile); if (token != LIMIT) { parse_warn (cfile, "expecting \"limit\""); if (token != SEMI) skip_to_semi (cfile); break; } token = next_token (&val, NULL, cfile); if (token != NUMBER) { parse_warn (cfile, "expecting a number"); if (token != SEMI) skip_to_semi (cfile); break; } class -> lease_limit = atoi (val); if (class->billed_leases) dfree(class->billed_leases, MDL); class -> billed_leases = dmalloc (class -> lease_limit * sizeof (struct lease *), MDL); if (!class -> billed_leases) log_fatal ("no memory for billed leases."); memset (class -> billed_leases, 0, (class -> lease_limit * sizeof (struct lease *))); have_billing_classes = 1; parse_semi (cfile); } else { declaration = parse_statement (cfile, class -> group, CLASS_DECL, NULL, declaration); } } while (1); if (class->flags & CLASS_DECL_DELETED) { if (type == CLASS_TYPE_CLASS) { struct class *theclass = NULL; status = find_class(&theclass, class->name, MDL); if (status == ISC_R_SUCCESS) { delete_class(theclass, 0); class_dereference(&theclass, MDL); } } else { class_hash_delete(pc->hash, (char *)class->hash_string.data, class->hash_string.len, MDL); } } else if (type == CLASS_TYPE_CLASS && new) { if (!collections -> classes) class_reference (&collections -> classes, class, MDL); else { struct class *c; for (c = collections -> classes; c -> nic; c = c -> nic) ; class_reference (&c -> nic, class, MDL); } } if (cp) /* should always be 0??? */ status = class_reference (cp, class, MDL); class_dereference (&class, MDL); if (pc) class_dereference (&pc, MDL); return cp ? (status == ISC_R_SUCCESS) : 1; } /* shared-network-declaration :== hostname LBRACE declarations parameters RBRACE */ void parse_shared_net_declaration (cfile, group) struct parse *cfile; struct group *group; { const char *val; enum dhcp_token token; struct shared_network *share; char *name; int declaration = 0; isc_result_t status; share = (struct shared_network *)0; status = shared_network_allocate (&share, MDL); if (status != ISC_R_SUCCESS) log_fatal ("Can't allocate shared subnet: %s", isc_result_totext (status)); if (clone_group (&share -> group, group, MDL) == 0) { log_fatal ("Can't clone group for shared net"); } shared_network_reference (&share -> group -> shared_network, share, MDL); /* Get the name of the shared network... */ token = peek_token (&val, (unsigned *)0, cfile); if (token == STRING) { skip_token(&val, (unsigned *)0, cfile); if (val [0] == 0) { parse_warn (cfile, "zero-length shared network name"); val = ""; } name = dmalloc (strlen (val) + 1, MDL); if (!name) log_fatal ("no memory for shared network name"); strcpy (name, val); } else { name = parse_host_name (cfile); if (!name) { parse_warn (cfile, "expecting a name for shared-network"); skip_to_semi (cfile); shared_network_dereference (&share, MDL); return; } } share -> name = name; if (!parse_lbrace (cfile)) { shared_network_dereference (&share, MDL); return; } do { token = peek_token (&val, (unsigned *)0, cfile); if (token == RBRACE) { skip_token(&val, (unsigned *)0, cfile); if (!share -> subnets) parse_warn (cfile, "empty shared-network decl"); else enter_shared_network (share); shared_network_dereference (&share, MDL); return; } else if (token == END_OF_FILE) { skip_token(&val, (unsigned *)0, cfile); parse_warn (cfile, "unexpected end of file"); break; } else if (token == INTERFACE) { skip_token(&val, (unsigned *)0, cfile); token = next_token (&val, (unsigned *)0, cfile); new_shared_network_interface (cfile, share, val); if (!parse_semi (cfile)) break; continue; } declaration = parse_statement (cfile, share -> group, SHARED_NET_DECL, (struct host_decl *)0, declaration); } while (1); shared_network_dereference (&share, MDL); } static int common_subnet_parsing(struct parse *cfile, struct shared_network *share, struct subnet *subnet) { enum dhcp_token token; struct subnet *t, *u; const char *val; int declaration = 0; enter_subnet(subnet); if (!parse_lbrace(cfile)) { subnet_dereference(&subnet, MDL); return 0; } do { token = peek_token(&val, NULL, cfile); if (token == RBRACE) { skip_token(&val, NULL, cfile); break; } else if (token == END_OF_FILE) { skip_token(&val, NULL, cfile); parse_warn (cfile, "unexpected end of file"); break; } else if (token == INTERFACE) { skip_token(&val, NULL, cfile); token = next_token(&val, NULL, cfile); new_shared_network_interface(cfile, share, val); if (!parse_semi(cfile)) break; continue; } declaration = parse_statement(cfile, subnet->group, SUBNET_DECL, NULL, declaration); } while (1); /* Add the subnet to the list of subnets in this shared net. */ if (share->subnets == NULL) { subnet_reference(&share->subnets, subnet, MDL); } else { u = NULL; for (t = share->subnets; t->next_sibling; t = t->next_sibling) { if (subnet_inner_than(subnet, t, 0)) { subnet_reference(&subnet->next_sibling, t, MDL); if (u) { subnet_dereference(&u->next_sibling, MDL); subnet_reference(&u->next_sibling, subnet, MDL); } else { subnet_dereference(&share->subnets, MDL); subnet_reference(&share->subnets, subnet, MDL); } subnet_dereference(&subnet, MDL); return 1; } u = t; } subnet_reference(&t->next_sibling, subnet, MDL); } subnet_dereference(&subnet, MDL); return 1; } /* subnet-declaration :== net NETMASK netmask RBRACE parameters declarations LBRACE */ void parse_subnet_declaration (cfile, share) struct parse *cfile; struct shared_network *share; { const char *val; enum dhcp_token token; struct subnet *subnet; struct iaddr iaddr; unsigned char addr [4]; unsigned len = sizeof addr; isc_result_t status; subnet = (struct subnet *)0; status = subnet_allocate (&subnet, MDL); if (status != ISC_R_SUCCESS) log_fatal ("Allocation of new subnet failed: %s", isc_result_totext (status)); shared_network_reference (&subnet -> shared_network, share, MDL); /* * If our parent shared network was implicitly created by the software, * and not explicitly configured by the user, then we actually put all * configuration scope in the parent (the shared network and subnet * share the same {}-level scope). * * Otherwise, we clone the parent group and continue as normal. */ if (share->flags & SHARED_IMPLICIT) { group_reference(&subnet->group, share->group, MDL); } else { if (!clone_group(&subnet->group, share->group, MDL)) { log_fatal("Allocation of group for new subnet failed."); } } subnet_reference (&subnet -> group -> subnet, subnet, MDL); /* Get the network number... */ if (!parse_numeric_aggregate (cfile, addr, &len, DOT, 10, 8)) { subnet_dereference (&subnet, MDL); return; } memcpy (iaddr.iabuf, addr, len); iaddr.len = len; subnet -> net = iaddr; token = next_token (&val, (unsigned *)0, cfile); if (token != NETMASK) { parse_warn (cfile, "Expecting netmask"); skip_to_semi (cfile); return; } /* Get the netmask... */ if (!parse_numeric_aggregate (cfile, addr, &len, DOT, 10, 8)) { subnet_dereference (&subnet, MDL); return; } memcpy (iaddr.iabuf, addr, len); iaddr.len = len; subnet -> netmask = iaddr; /* Validate the network number/netmask pair. */ if (host_addr (subnet -> net, subnet -> netmask)) { char *maskstr; /* dup it, since piaddr is re-entrant */ maskstr = strdup (piaddr (subnet -> netmask)); if (maskstr == NULL) { log_fatal("Allocation of subnet maskstr failed: %s", piaddr (subnet -> net)); } parse_warn (cfile, "subnet %s netmask %s: bad subnet number/mask combination.", piaddr (subnet -> net), maskstr); free(maskstr); subnet_dereference (&subnet, MDL); skip_to_semi (cfile); return; } common_subnet_parsing(cfile, share, subnet); } /* subnet6-declaration :== net / bits RBRACE parameters declarations LBRACE */ void parse_subnet6_declaration(struct parse *cfile, struct shared_network *share) { #if !defined(DHCPv6) parse_warn(cfile, "No DHCPv6 support."); skip_to_semi(cfile); #else /* defined(DHCPv6) */ struct subnet *subnet; isc_result_t status; enum dhcp_token token; const char *val; char *endp; int ofs; const static int mask[] = { 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE }; struct iaddr iaddr; #if defined(DHCP4o6) if ((local_family != AF_INET6) && !dhcpv4_over_dhcpv6) { parse_warn(cfile, "subnet6 statement is only supported " "in DHCPv6 and DHCPv4o6 modes."); skip_to_semi(cfile); return; } #else /* defined(DHCP4o6) */ if (local_family != AF_INET6) { parse_warn(cfile, "subnet6 statement is only supported " "in DHCPv6 mode."); skip_to_semi(cfile); return; } #endif /* !defined(DHCP4o6) */ subnet = NULL; status = subnet_allocate(&subnet, MDL); if (status != ISC_R_SUCCESS) { log_fatal("Allocation of new subnet failed: %s", isc_result_totext(status)); } shared_network_reference(&subnet->shared_network, share, MDL); /* * If our parent shared network was implicitly created by the software, * and not explicitly configured by the user, then we actually put all * configuration scope in the parent (the shared network and subnet * share the same {}-level scope). * * Otherwise, we clone the parent group and continue as normal. */ if (share->flags & SHARED_IMPLICIT) { group_reference(&subnet->group, share->group, MDL); } else { if (!clone_group(&subnet->group, share->group, MDL)) { log_fatal("Allocation of group for new subnet failed."); } } subnet_reference(&subnet->group->subnet, subnet, MDL); if (!parse_ip6_addr(cfile, &subnet->net)) { subnet_dereference(&subnet, MDL); return; } token = next_token(&val, NULL, cfile); if (token != SLASH) { parse_warn(cfile, "Expecting a '/'."); skip_to_semi(cfile); return; } token = next_token(&val, NULL, cfile); if (token != NUMBER) { parse_warn(cfile, "Expecting a number."); skip_to_semi(cfile); return; } subnet->prefix_len = strtol(val, &endp, 10); if ((subnet->prefix_len < 0) || (subnet->prefix_len > 128) || (*endp != '\0')) { parse_warn(cfile, "Expecting a number between 0 and 128."); skip_to_semi(cfile); return; } if (!is_cidr_mask_valid(&subnet->net, subnet->prefix_len)) { parse_warn(cfile, "New subnet mask too short."); skip_to_semi(cfile); return; } /* * Create a netmask. */ subnet->netmask.len = 16; ofs = subnet->prefix_len / 8; if (ofs < subnet->netmask.len) { subnet->netmask.iabuf[ofs] = mask[subnet->prefix_len % 8]; } while (--ofs >= 0) { subnet->netmask.iabuf[ofs] = 0xFF; } /* Validate the network number/netmask pair. */ iaddr = subnet_number(subnet->net, subnet->netmask); if (memcmp(&iaddr, &subnet->net, 16) != 0) { parse_warn(cfile, "subnet %s/%d: prefix not long enough for address.", piaddr(subnet->net), subnet->prefix_len); subnet_dereference(&subnet, MDL); skip_to_semi(cfile); return; } if (!common_subnet_parsing(cfile, share, subnet)) { return; } #endif /* defined(DHCPv6) */ } /* group-declaration :== RBRACE parameters declarations LBRACE */ void parse_group_declaration (cfile, group) struct parse *cfile; struct group *group; { const char *val; enum dhcp_token token; struct group *g; int declaration = 0; struct group_object *t = NULL; isc_result_t status; char *name = NULL; int deletedp = 0; int dynamicp = 0; int staticp = 0; g = NULL; if (!clone_group(&g, group, MDL)) log_fatal("no memory for explicit group."); token = peek_token(&val, NULL, cfile); if (is_identifier (token) || token == STRING) { skip_token(&val, NULL, cfile); name = dmalloc(strlen(val) + 1, MDL); if (!name) log_fatal("no memory for group decl name %s", val); strcpy(name, val); } if (!parse_lbrace(cfile)) { group_dereference(&g, MDL); return; } do { token = peek_token(&val, NULL, cfile); if (token == RBRACE) { skip_token(&val, NULL, cfile); break; } else if (token == END_OF_FILE) { skip_token(&val, NULL, cfile); parse_warn(cfile, "unexpected end of file"); break; } else if (token == TOKEN_DELETED) { skip_token(&val, NULL, cfile); parse_semi(cfile); deletedp = 1; } else if (token == DYNAMIC) { skip_token(&val, NULL, cfile); parse_semi(cfile); dynamicp = 1; } else if (token == STATIC) { skip_token(&val, NULL, cfile); parse_semi(cfile); staticp = 1; } declaration = parse_statement(cfile, g, GROUP_DECL, NULL, declaration); } while (1); if (name) { if (deletedp) { if (group_name_hash) { t = NULL; if (group_hash_lookup(&t, group_name_hash, name, strlen(name), MDL)) { delete_group(t, 0); } } } else { t = NULL; status = group_object_allocate(&t, MDL); if (status != ISC_R_SUCCESS) log_fatal("no memory for group decl %s: %s", val, isc_result_totext(status)); group_reference(&t->group, g, MDL); t->name = name; /* no need to include deletedp as it's handled above */ t->flags = ((staticp ? GROUP_OBJECT_STATIC : 0) | (dynamicp ? GROUP_OBJECT_DYNAMIC : 0)); supersede_group(t, 0); } if (t != NULL) group_object_dereference(&t, MDL); } } /* fixed-addr-parameter :== ip-addrs-or-hostnames SEMI ip-addrs-or-hostnames :== ip-addr-or-hostname | ip-addrs-or-hostnames ip-addr-or-hostname */ int parse_fixed_addr_param(struct option_cache **oc, struct parse *cfile, enum dhcp_token type) { int parse_ok; const char *val; enum dhcp_token token; struct expression *expr = NULL; struct expression *tmp, *new; int status; do { tmp = NULL; if (type == FIXED_ADDR) { parse_ok = parse_ip_addr_or_hostname(&tmp, cfile, 1); } else { /* INSIST(type == FIXED_ADDR6); */ parse_ok = parse_ip6_addr_expr(&tmp, cfile); } if (parse_ok) { if (expr != NULL) { new = NULL; status = make_concat(&new, expr, tmp); expression_dereference(&expr, MDL); expression_dereference(&tmp, MDL); if (!status) { return 0; } expr = new; } else { expr = tmp; } } else { if (expr != NULL) { expression_dereference (&expr, MDL); } return 0; } token = peek_token(&val, NULL, cfile); if (token == COMMA) { token = next_token(&val, NULL, cfile); } } while (token == COMMA); if (!parse_semi(cfile)) { if (expr) { expression_dereference (&expr, MDL); } return 0; } status = option_cache(oc, NULL, expr, NULL, MDL); expression_dereference(&expr, MDL); return status; } /* lease_declaration :== LEASE ip_address LBRACE lease_parameters RBRACE lease_parameters :== | lease_parameter | lease_parameters lease_parameter lease_parameter :== STARTS date | ENDS date | TIMESTAMP date | HARDWARE hardware-parameter | UID hex_numbers SEMI | HOSTNAME hostname SEMI | CLIENT_HOSTNAME hostname SEMI | CLASS identifier SEMI | DYNAMIC_BOOTP SEMI */ int parse_lease_declaration (struct lease **lp, struct parse *cfile) { const char *val; enum dhcp_token token; unsigned char addr [4]; unsigned len = sizeof addr; int seenmask = 0; int seenbit; char tbuf [32]; struct lease *lease; struct executable_statement *on; int lose; TIME t; int noequal, newbinding; struct binding *binding; struct binding_value *nv; isc_result_t status; struct option_cache *oc; pair *p; binding_state_t new_state; unsigned buflen = 0; struct class *class; lease = (struct lease *)0; status = lease_allocate (&lease, MDL); if (status != ISC_R_SUCCESS) return 0; /* Get the address for which the lease has been issued. */ if (!parse_numeric_aggregate (cfile, addr, &len, DOT, 10, 8)) { lease_dereference (&lease, MDL); return 0; } memcpy (lease -> ip_addr.iabuf, addr, len); lease -> ip_addr.len = len; if (!parse_lbrace (cfile)) { lease_dereference (&lease, MDL); return 0; } do { token = next_token (&val, (unsigned *)0, cfile); if (token == RBRACE) break; else if (token == END_OF_FILE) { parse_warn (cfile, "unexpected end of file"); break; } strncpy (tbuf, val, sizeof tbuf); tbuf [(sizeof tbuf) - 1] = 0; /* Parse any of the times associated with the lease. */ switch (token) { case STARTS: case ENDS: case TIMESTAMP: case TSTP: case TSFP: case ATSFP: case CLTT: t = parse_date (cfile); switch (token) { case STARTS: seenbit = 1; lease -> starts = t; break; case ENDS: seenbit = 2; lease -> ends = t; break; case TSTP: seenbit = 65536; lease -> tstp = t; break; case TSFP: seenbit = 131072; lease -> tsfp = t; break; case ATSFP: seenbit = 262144; lease->atsfp = t; break; case CLTT: seenbit = 524288; lease -> cltt = t; break; default: /* for gcc, we'll never get here. */ log_fatal ("Impossible error at %s:%d.", MDL); return 0; } break; /* Colon-separated hexadecimal octets... */ case UID: seenbit = 8; token = peek_token (&val, (unsigned *)0, cfile); if (token == STRING) { unsigned char *tuid; skip_token(&val, &buflen, cfile); if (buflen < sizeof lease -> uid_buf) { tuid = lease -> uid_buf; lease -> uid_max = sizeof lease -> uid_buf; } else { tuid = ((unsigned char *) dmalloc (buflen, MDL)); if (!tuid) { log_error ("no space for uid"); lease_dereference (&lease, MDL); return 0; } lease -> uid_max = buflen; } lease -> uid_len = buflen; memcpy (tuid, val, lease -> uid_len); lease -> uid = tuid; } else { buflen = 0; lease -> uid = (parse_numeric_aggregate (cfile, (unsigned char *)0, &buflen, ':', 16, 8)); if (!lease -> uid) { lease_dereference (&lease, MDL); return 0; } lease -> uid_len = buflen; lease -> uid_max = buflen; if (lease -> uid_len == 0) { lease -> uid = (unsigned char *)0; parse_warn (cfile, "zero-length uid"); seenbit = 0; parse_semi (cfile); break; } } parse_semi (cfile); if (!lease -> uid) { log_fatal ("No memory for lease uid"); } break; case CLASS: seenbit = 32; token = next_token (&val, (unsigned *)0, cfile); if (!is_identifier (token)) { if (token != SEMI) skip_to_rbrace (cfile, 1); lease_dereference (&lease, MDL); return 0; } parse_semi (cfile); /* for now, we aren't using this. */ break; case HARDWARE: seenbit = 64; parse_hardware_param (cfile, &lease -> hardware_addr); break; case TOKEN_RESERVED: seenbit = 0; lease->flags |= RESERVED_LEASE; parse_semi(cfile); break; case DYNAMIC_BOOTP: seenbit = 0; lease -> flags |= BOOTP_LEASE; parse_semi (cfile); break; /* XXX: Reverse compatibility? */ case TOKEN_ABANDONED: seenbit = 256; lease -> binding_state = FTS_ABANDONED; lease -> next_binding_state = FTS_ABANDONED; parse_semi (cfile); break; case TOKEN_NEXT: seenbit = 128; token = next_token (&val, (unsigned *)0, cfile); if (token != BINDING) { parse_warn (cfile, "expecting 'binding'"); skip_to_semi (cfile); break; } goto do_binding_state; case REWIND: seenbit = 512; token = next_token(&val, NULL, cfile); if (token != BINDING) { parse_warn(cfile, "expecting 'binding'"); skip_to_semi(cfile); break; } goto do_binding_state; case BINDING: seenbit = 256; do_binding_state: token = next_token (&val, (unsigned *)0, cfile); if (token != STATE) { parse_warn (cfile, "expecting 'state'"); skip_to_semi (cfile); break; } token = next_token (&val, (unsigned *)0, cfile); switch (token) { case TOKEN_ABANDONED: new_state = FTS_ABANDONED; break; case TOKEN_FREE: new_state = FTS_FREE; break; case TOKEN_ACTIVE: new_state = FTS_ACTIVE; break; case TOKEN_EXPIRED: new_state = FTS_EXPIRED; break; case TOKEN_RELEASED: new_state = FTS_RELEASED; break; case TOKEN_RESET: new_state = FTS_RESET; break; case TOKEN_BACKUP: new_state = FTS_BACKUP; break; /* RESERVED and BOOTP states preserved for * compatibleness with older versions. */ case TOKEN_RESERVED: new_state = FTS_ACTIVE; lease->flags |= RESERVED_LEASE; break; case TOKEN_BOOTP: new_state = FTS_ACTIVE; lease->flags |= BOOTP_LEASE; break; default: parse_warn (cfile, "%s: expecting a binding state.", val); skip_to_semi (cfile); return 0; } if (seenbit == 256) { lease -> binding_state = new_state; /* * Apply default/conservative next/rewind * binding states if they haven't been set * yet. These defaults will be over-ridden if * they are set later in parsing. */ if (!(seenmask & 128)) lease->next_binding_state = new_state; /* The most conservative rewind state. */ if (!(seenmask & 512)) lease->rewind_binding_state = new_state; } else if (seenbit == 128) lease -> next_binding_state = new_state; else if (seenbit == 512) lease->rewind_binding_state = new_state; else log_fatal("Impossible condition at %s:%d.", MDL); parse_semi (cfile); break; case CLIENT_HOSTNAME: seenbit = 1024; token = peek_token (&val, (unsigned *)0, cfile); if (token == STRING) { if (!parse_string (cfile, &lease -> client_hostname, (unsigned *)0)) { lease_dereference (&lease, MDL); return 0; } } else { lease -> client_hostname = parse_host_name (cfile); if (lease -> client_hostname) parse_semi (cfile); else { parse_warn (cfile, "expecting a hostname."); skip_to_semi (cfile); lease_dereference (&lease, MDL); return 0; } } break; case BILLING: seenbit = 2048; class = (struct class *)0; token = next_token (&val, (unsigned *)0, cfile); if (token == CLASS) { token = next_token (&val, (unsigned *)0, cfile); if (token != STRING) { parse_warn (cfile, "expecting string"); if (token != SEMI) skip_to_semi (cfile); token = BILLING; break; } if (lease -> billing_class) class_dereference (&lease -> billing_class, MDL); find_class (&class, val, MDL); if (!class) parse_warn (cfile, "unknown class %s", val); parse_semi (cfile); } else if (token == SUBCLASS) { if (lease -> billing_class) class_dereference (&lease -> billing_class, MDL); parse_class_declaration(&class, cfile, NULL, CLASS_TYPE_SUBCLASS); } else { parse_warn (cfile, "expecting \"class\""); if (token != SEMI) skip_to_semi (cfile); } if (class) { class_reference (&lease -> billing_class, class, MDL); class_dereference (&class, MDL); } break; case ON: on = (struct executable_statement *)0; lose = 0; if (!parse_on_statement (&on, cfile, &lose)) { skip_to_rbrace (cfile, 1); lease_dereference (&lease, MDL); return 0; } seenbit = 0; if ((on->data.on.evtypes & ON_EXPIRY) && on->data.on.statements) { seenbit |= 16384; executable_statement_reference (&lease->on_star.on_expiry, on->data.on.statements, MDL); } if ((on->data.on.evtypes & ON_RELEASE) && on->data.on.statements) { seenbit |= 32768; executable_statement_reference (&lease->on_star.on_release, on->data.on.statements, MDL); } executable_statement_dereference (&on, MDL); break; case OPTION: case SUPERSEDE: noequal = 0; seenbit = 0; oc = (struct option_cache *)0; if (parse_option_decl (&oc, cfile)) { if (oc -> option -> universe != &agent_universe) { parse_warn (cfile, "agent option expected."); option_cache_dereference (&oc, MDL); break; } if (!lease -> agent_options && !(option_chain_head_allocate (&lease -> agent_options, MDL))) { log_error ("no memory to stash agent option"); break; } for (p = &lease -> agent_options -> first; *p; p = &((*p) -> cdr)) ; *p = cons (0, 0); option_cache_reference (((struct option_cache **) &((*p) -> car)), oc, MDL); option_cache_dereference (&oc, MDL); } break; case TOKEN_SET: noequal = 0; token = next_token (&val, (unsigned *)0, cfile); if (token != NAME && token != NUMBER_OR_NAME) { parse_warn (cfile, "%s can't be a variable name", val); badset: skip_to_semi (cfile); lease_dereference (&lease, MDL); return 0; } seenbit = 0; special_set: if (lease -> scope) binding = find_binding (lease -> scope, val); else binding = (struct binding *)0; if (!binding) { if (!lease -> scope) if (!(binding_scope_allocate (&lease -> scope, MDL))) log_fatal ("no memory for scope"); binding = dmalloc (sizeof *binding, MDL); if (!binding) log_fatal ("No memory for lease %s.", "binding"); memset (binding, 0, sizeof *binding); binding -> name = dmalloc (strlen (val) + 1, MDL); if (!binding -> name) log_fatal ("No memory for binding %s.", "name"); strcpy (binding -> name, val); newbinding = 1; } else { newbinding = 0; } nv = NULL; if (!binding_value_allocate(&nv, MDL)) log_fatal("no memory for binding value."); if (!noequal) { token = next_token (&val, (unsigned *)0, cfile); if (token != EQUAL) { parse_warn (cfile, "expecting '=' in set statement."); goto badset; } } if (!parse_binding_value(cfile, nv)) { binding_value_dereference(&nv, MDL); lease_dereference(&lease, MDL); return 0; } if (newbinding) { binding_value_reference(&binding->value, nv, MDL); binding->next = lease->scope->bindings; lease->scope->bindings = binding; } else { binding_value_dereference(&binding->value, MDL); binding_value_reference(&binding->value, nv, MDL); } binding_value_dereference(&nv, MDL); parse_semi(cfile); break; /* case NAME: */ default: if (!strcasecmp (val, "ddns-fwd-name")) { seenbit = 4096; noequal = 1; goto special_set; } else if (!strcasecmp (val, "ddns-rev-name")) { seenbit = 8192; noequal = 1; goto special_set; } else parse_warn(cfile, "Unexpected configuration " "directive."); skip_to_semi (cfile); seenbit = 0; lease_dereference (&lease, MDL); return 0; } if (seenmask & seenbit) { parse_warn (cfile, "Too many %s parameters in lease %s\n", tbuf, piaddr (lease -> ip_addr)); } else seenmask |= seenbit; } while (1); /* If no binding state is specified, make one up. */ if (!(seenmask & 256)) { if (lease->ends > cur_time || lease->on_star.on_expiry || lease->on_star.on_release) lease->binding_state = FTS_ACTIVE; #if defined (FAILOVER_PROTOCOL) else if (lease->pool && lease->pool->failover_peer) lease->binding_state = FTS_EXPIRED; #endif else lease->binding_state = FTS_FREE; if (lease->binding_state == FTS_ACTIVE) { #if defined (FAILOVER_PROTOCOL) if (lease->pool && lease->pool->failover_peer) lease->next_binding_state = FTS_EXPIRED; else #endif lease->next_binding_state = FTS_FREE; } else lease->next_binding_state = lease->binding_state; /* The most conservative rewind state implies no rewind. */ lease->rewind_binding_state = lease->binding_state; } if (!(seenmask & 65536)) lease->tstp = lease->ends; lease_reference (lp, lease, MDL); lease_dereference (&lease, MDL); return 1; } /* Parse the right side of a 'binding value'. * * set foo = "bar"; is a string * set foo = false; is a boolean * set foo = %31; is a numeric value. */ static int parse_binding_value(struct parse *cfile, struct binding_value *value) { struct data_string *data; unsigned char *s; const char *val; unsigned buflen; int token; if ((cfile == NULL) || (value == NULL)) log_fatal("Invalid arguments at %s:%d.", MDL); token = peek_token(&val, NULL, cfile); if (token == STRING) { skip_token(&val, &buflen, cfile); value->type = binding_data; value->value.data.len = buflen; data = &value->value.data; if (!buffer_allocate(&data->buffer, buflen + 1, MDL)) log_fatal ("No memory for binding."); memcpy(data->buffer->data, val, buflen + 1); data->data = data->buffer->data; data->terminated = 1; } else if (token == NUMBER_OR_NAME) { value->type = binding_data; data = &value->value.data; s = parse_numeric_aggregate(cfile, NULL, &data->len, ':', 16, 8); if (s == NULL) { skip_to_semi(cfile); return 0; } if (data->len) { if (!buffer_allocate(&data->buffer, data->len + 1, MDL)) log_fatal("No memory for binding."); memcpy(data->buffer->data, s, data->len); data->data = data->buffer->data; dfree (s, MDL); } } else if (token == PERCENT) { skip_token(&val, NULL, cfile); token = next_token(&val, NULL, cfile); if (token != NUMBER) { parse_warn(cfile, "expecting decimal number."); if (token != SEMI) skip_to_semi(cfile); return 0; } value->type = binding_numeric; value->value.intval = atol(val); } else if (token == NAME) { token = next_token(&val, NULL, cfile); value->type = binding_boolean; if (!strcasecmp(val, "true")) value->value.boolean = 1; else if (!strcasecmp(val, "false")) value->value.boolean = 0; else { parse_warn(cfile, "expecting true or false"); if (token != SEMI) skip_to_semi(cfile); return 0; } } else { parse_warn (cfile, "expecting a constant value."); if (token != SEMI) skip_to_semi (cfile); return 0; } return 1; } /* address-range-declaration :== ip-address ip-address SEMI | DYNAMIC_BOOTP ip-address ip-address SEMI */ void parse_address_range (cfile, group, type, inpool, lpchain) struct parse *cfile; struct group *group; int type; struct pool *inpool; struct lease **lpchain; { struct iaddr low, high, net; unsigned char addr [4]; unsigned len = sizeof addr; enum dhcp_token token; const char *val; int dynamic = 0; struct subnet *subnet; struct shared_network *share; struct pool *pool; isc_result_t status; if ((token = peek_token (&val, (unsigned *)0, cfile)) == DYNAMIC_BOOTP) { skip_token(&val, (unsigned *)0, cfile); dynamic = 1; } /* Get the bottom address in the range... */ if (!parse_numeric_aggregate (cfile, addr, &len, DOT, 10, 8)) return; memcpy (low.iabuf, addr, len); low.len = len; /* Only one address? */ token = peek_token (&val, (unsigned *)0, cfile); if (token == SEMI) high = low; else { /* Get the top address in the range... */ if (!parse_numeric_aggregate (cfile, addr, &len, DOT, 10, 8)) return; memcpy (high.iabuf, addr, len); high.len = len; } token = next_token (&val, (unsigned *)0, cfile); if (token != SEMI) { parse_warn (cfile, "semicolon expected."); skip_to_semi (cfile); return; } if (type == SUBNET_DECL) { subnet = group -> subnet; share = subnet -> shared_network; } else { share = group -> shared_network; for (subnet = share -> subnets; subnet; subnet = subnet -> next_sibling) { net = subnet_number (low, subnet -> netmask); if (addr_eq (net, subnet -> net)) break; } if (!subnet) { parse_warn (cfile, "address range not on network %s", group -> shared_network -> name); log_error ("Be sure to place pool statement after %s", "related subnet declarations."); return; } } if (!inpool) { struct pool *last = (struct pool *)0; /* If we're permitting dynamic bootp for this range, then look for a pool with an empty prohibit list and a permit list with one entry that permits all clients. */ for (pool = share -> pools; pool; pool = pool -> next) { if ((!dynamic && !pool -> permit_list && pool -> prohibit_list && !pool -> prohibit_list -> next && (pool -> prohibit_list -> type == permit_dynamic_bootp_clients)) || (dynamic && !pool -> prohibit_list && pool -> permit_list && !pool -> permit_list -> next && (pool -> permit_list -> type == permit_all_clients))) { break; } last = pool; } /* If we didn't get a pool, make one. */ if (!pool) { struct permit *p; status = pool_allocate (&pool, MDL); if (status != ISC_R_SUCCESS) log_fatal ("no memory for ad-hoc pool: %s", isc_result_totext (status)); p = new_permit (MDL); if (!p) log_fatal ("no memory for ad-hoc permit."); /* Dynamic pools permit all clients. Otherwise we prohibit BOOTP clients. */ if (dynamic) { p -> type = permit_all_clients; pool -> permit_list = p; } else { p -> type = permit_dynamic_bootp_clients; pool -> prohibit_list = p; } if (share -> pools) pool_reference (&last -> next, pool, MDL); else pool_reference (&share -> pools, pool, MDL); shared_network_reference (&pool -> shared_network, share, MDL); if (!clone_group (&pool -> group, share -> group, MDL)) log_fatal ("no memory for anon pool group."); } else { pool = (struct pool *)0; if (last) pool_reference (&pool, last, MDL); else pool_reference (&pool, share -> pools, MDL); } } else { pool = (struct pool *)0; pool_reference (&pool, inpool, MDL); } #if defined (FAILOVER_PROTOCOL) if (pool -> failover_peer && dynamic) { /* Doctor, do you think I'm overly sensitive about getting bug reports I can't fix? */ parse_warn (cfile, "dynamic-bootp flag is %s", "not permitted for address"); log_error ("range declarations where there is a failover"); log_error ("peer in scope. If you wish to declare an"); log_error ("address range from which dynamic bootp leases"); log_error ("can be allocated, please declare it within a"); log_error ("pool declaration that also contains the \"no"); log_error ("failover\" statement. The failover protocol"); log_error ("itself does not permit dynamic bootp - this"); log_error ("is not a limitation specific to the ISC DHCP"); log_error ("server. Please don't ask me to defend this"); log_error ("until you have read and really tried %s", "to understand"); log_error ("the failover protocol specification."); /* We don't actually bomb at this point - instead, we let parse_lease_file notice the error and bomb at that point - it's easier. */ } #endif /* FAILOVER_PROTOCOL */ /* Create the new address range... */ new_address_range (cfile, low, high, subnet, pool, lpchain); pool_dereference (&pool, MDL); } #ifdef DHCPv6 static void add_ipv6_pool_to_subnet(struct subnet *subnet, u_int16_t type, struct iaddr *lo_addr, int bits, int units, struct ipv6_pond *pond) { struct ipv6_pool *pool; struct in6_addr tmp_in6_addr; int num_pools; struct ipv6_pool **tmp; /* * Create our pool. */ if (lo_addr->len != sizeof(tmp_in6_addr)) { log_fatal("Internal error: Attempt to add non-IPv6 address " "to IPv6 shared network."); } memcpy(&tmp_in6_addr, lo_addr->iabuf, sizeof(tmp_in6_addr)); pool = NULL; if (ipv6_pool_allocate(&pool, type, &tmp_in6_addr, bits, units, MDL) != ISC_R_SUCCESS) { log_fatal("Out of memory"); } /* * Add to our global IPv6 pool set. */ if (add_ipv6_pool(pool) != ISC_R_SUCCESS) { log_fatal ("Out of memory"); } /* * Link the pool to its network. */ pool->subnet = NULL; subnet_reference(&pool->subnet, subnet, MDL); pool->shared_network = NULL; shared_network_reference(&pool->shared_network, subnet->shared_network, MDL); pool->ipv6_pond = NULL; ipv6_pond_reference(&pool->ipv6_pond, pond, MDL); /* * Increase our array size for ipv6_pools in the pond */ if (pond->ipv6_pools == NULL) { num_pools = 0; } else { num_pools = 0; while (pond->ipv6_pools[num_pools] != NULL) { num_pools++; } } tmp = dmalloc(sizeof(struct ipv6_pool *) * (num_pools + 2), MDL); if (tmp == NULL) { log_fatal("Out of memory"); } if (num_pools > 0) { memcpy(tmp, pond->ipv6_pools, sizeof(struct ipv6_pool *) * num_pools); } if (pond->ipv6_pools != NULL) { dfree(pond->ipv6_pools, MDL); } pond->ipv6_pools = tmp; /* * Record this pool in our array of pools for this shared network. */ ipv6_pool_reference(&pond->ipv6_pools[num_pools], pool, MDL); pond->ipv6_pools[num_pools+1] = NULL; /* Update the number of elements in the pond. Conveniently * we have the total size of the block in bits and the amount * we would allocate per element in units. For an address units * will always be 128, for a prefix it will be something else. * * We need to make sure the number of elements isn't too large * to track. If so, we flag it to avoid wasting time with log * threshold logic. We also emit a log stating that log-threshold * will be disabled for the shared-network but that's done * elsewhere via report_log_threshold(). * */ /* Only bother if we aren't already flagged as jumbo */ if (pond->jumbo_range == 0) { if ((units - bits) > (sizeof(isc_uint64_t) * 8)) { pond->jumbo_range = 1; pond->num_total = POND_TRACK_MAX; } else { isc_uint64_t space_left = POND_TRACK_MAX - pond->num_total; isc_uint64_t addon = (isc_uint64_t)(1) << (units - bits); if (addon > space_left) { pond->jumbo_range = 1; pond->num_total = POND_TRACK_MAX; } else { pond->num_total += addon; } } } } /*! * * \brief Find or create a default pond * * Find or create an ipv6_pond on which to attach the ipv6_pools. We * check the shared network to see if there is a general purpose * entry - this will have an empty prohibit list and a permit list * with a single entry that permits all clients. If the shared * network doesn't have one of them create it and attach it to * the shared network and the return argument. * * This function is used when we have a range6 or prefix6 statement * inside a subnet6 statement but outside of a pool6 statement. * This routine constructs the missing ipv6_pond structure so * we always have * shared_network -> ipv6_pond -> ipv6_pool * * \param[in] group = a pointer to the group structure from which * we can find the subnet and shared netowrk * structures * \param[out] ret_pond = a pointer to space for the pointer to * the structure to return * * \return * void */ static void add_ipv6_pond_to_network(struct group *group, struct ipv6_pond **ret_pond) { struct ipv6_pond *pond = NULL, *last = NULL; struct permit *p; isc_result_t status; struct shared_network *shared = group->subnet->shared_network; for (pond = shared->ipv6_pond; pond; pond = pond->next) { if ((pond->group->statements == group->statements) && (pond->prohibit_list == NULL) && (pond->permit_list != NULL) && (pond->permit_list->next == NULL) && (pond->permit_list->type == permit_all_clients)) { ipv6_pond_reference(ret_pond, pond, MDL); return; } last = pond; } /* no pond available, make one */ status = ipv6_pond_allocate(&pond, MDL); if (status != ISC_R_SUCCESS) log_fatal ("no memory for ad-hoc ipv6 pond: %s", isc_result_totext (status)); p = new_permit (MDL); if (p == NULL) log_fatal ("no memory for ad-hoc ipv6 permit."); /* we permit all clients */ p->type = permit_all_clients; pond->permit_list = p; /* and attach the pond to the return argument and the shared network */ ipv6_pond_reference(ret_pond, pond, MDL); if (shared->ipv6_pond) ipv6_pond_reference(&last->next, pond, MDL); else ipv6_pond_reference(&shared->ipv6_pond, pond, MDL); shared_network_reference(&pond->shared_network, shared, MDL); if (!clone_group (&pond->group, group, MDL)) log_fatal ("no memory for anon pool group."); ipv6_pond_dereference(&pond, MDL); return; } /* address-range6-declaration :== ip-address6 ip-address6 SEMI | ip-address6 SLASH number SEMI | ip-address6 [SLASH number] TEMPORARY SEMI */ void parse_address_range6(struct parse *cfile, struct group *group, struct ipv6_pond *inpond) { struct iaddr lo, hi; int bits; enum dhcp_token token; const char *val; struct iaddrcidrnetlist *nets, net; struct iaddrcidrnetlist *p; u_int16_t type = D6O_IA_NA; struct ipv6_pond *pond = NULL; if (local_family != AF_INET6) { parse_warn(cfile, "range6 statement is only supported " "in DHCPv6 mode."); skip_to_semi(cfile); return; } /* This is enforced by the caller, this is just a sanity check. */ if (group->subnet == NULL) log_fatal("Impossible condition at %s:%d.", MDL); /* * Read starting address. */ if (!parse_ip6_addr(cfile, &lo)) { return; } /* Make sure starting address is within the subnet */ if (!addr_eq(group->subnet->net, subnet_number(lo, group->subnet->netmask))) { parse_warn(cfile, "range6 start address is outside the subnet"); skip_to_semi(cfile); return; } /* * zero out the net entry in case we use it */ memset(&net, 0, sizeof(net)); net.cidrnet.lo_addr = lo; /* * See if we we're using range or CIDR notation or TEMPORARY */ token = peek_token(&val, NULL, cfile); if (token == SLASH) { /* * '/' means CIDR notation, so read the bits we want. */ skip_token(NULL, NULL, cfile); token = next_token(&val, NULL, cfile); if (token != NUMBER) { parse_warn(cfile, "expecting number"); skip_to_semi(cfile); return; } net.cidrnet.bits = atoi(val); bits = net.cidrnet.bits; if ((bits < 0) || (bits > 128)) { parse_warn(cfile, "networks have 0 to 128 bits"); skip_to_semi(cfile); return; } if (bits < group->subnet->prefix_len) { parse_warn(cfile, "network mask smaller than subnet mask"); skip_to_semi(cfile); return; } if (!is_cidr_mask_valid(&net.cidrnet.lo_addr, bits)) { parse_warn(cfile, "network mask too short"); skip_to_semi(cfile); return; } /* * can be temporary (RFC 4941 like) */ token = peek_token(&val, NULL, cfile); if (token == TEMPORARY) { if (bits < 64) parse_warn(cfile, "temporary mask too short"); if (bits == 128) parse_warn(cfile, "temporary singleton?"); skip_token(NULL, NULL, cfile); type = D6O_IA_TA; } nets = &net; } else if (token == TEMPORARY) { /* * temporary (RFC 4941) */ type = D6O_IA_TA; skip_token(NULL, NULL, cfile); net.cidrnet.bits = 64; if (!is_cidr_mask_valid(&net.cidrnet.lo_addr, net.cidrnet.bits)) { parse_warn(cfile, "network mask too short"); skip_to_semi(cfile); return; } nets = &net; } else { /* * No '/', so we are looking for the end address of * the IPv6 pool. */ if (!parse_ip6_addr(cfile, &hi)) { return; } /* Make sure ending address is within the subnet */ if (!addr_eq(group->subnet->net, subnet_number(hi, group->subnet->netmask))) { parse_warn(cfile, "range6 end address is outside the subnet"); skip_to_semi(cfile); return; } /* * Convert our range to a set of CIDR networks. */ nets = NULL; if (range2cidr(&nets, &lo, &hi) != ISC_R_SUCCESS) { log_fatal("Error converting range to CIDR networks"); } } /* * See if we have a pond for this set of pools. * If the caller supplied one we use it, otherwise * check the shared network */ if (inpond != NULL) { ipv6_pond_reference(&pond, inpond, MDL); } else { add_ipv6_pond_to_network(group, &pond); } /* Now that we have a pond add the nets we have parsed */ for (p=nets; p != NULL; p=p->next) { add_ipv6_pool_to_subnet(group->subnet, type, &p->cidrnet.lo_addr, p->cidrnet.bits, 128, pond); } /* if we allocated a list free it now */ if (nets != &net) free_iaddrcidrnetlist(&nets); ipv6_pond_dereference(&pond, MDL); token = next_token(NULL, NULL, cfile); if (token != SEMI) { parse_warn(cfile, "semicolon expected."); skip_to_semi(cfile); return; } } /* prefix6-declaration :== ip-address6 ip-address6 SLASH number SEMI */ void parse_prefix6(struct parse *cfile, struct group *group, struct ipv6_pond *inpond) { struct iaddr lo, hi; int bits; enum dhcp_token token; const char *val; struct iaddrcidrnetlist *nets; struct iaddrcidrnetlist *p; struct ipv6_pond *pond = NULL; if (local_family != AF_INET6) { parse_warn(cfile, "prefix6 statement is only supported " "in DHCPv6 mode."); skip_to_semi(cfile); return; } /* This is enforced by the caller, so it's just a sanity check. */ if (group->subnet == NULL) log_fatal("Impossible condition at %s:%d.", MDL); /* * Read starting and ending address. */ if (!parse_ip6_addr(cfile, &lo)) { return; } #if 0 /* Prefixes are not required to be within the subnet, but I'm not * entirely sure that we won't want to revive this code as a warning * in the future so I'm ifdeffing it */ /* Make sure starting prefix is within the subnet */ if (!addr_eq(group->subnet->net, subnet_number(lo, group->subnet->netmask))) { parse_warn(cfile, "prefix6 start prefix" " is outside the subnet"); skip_to_semi(cfile); return; } #endif if (!parse_ip6_addr(cfile, &hi)) { return; } #if 0 /* Prefixes are not required to be within the subnet, but I'm not * entirely sure that we won't want to revive this code as a warning * in the future so I'm ifdeffing it */ /* Make sure ending prefix is within the subnet */ if (!addr_eq(group->subnet->net, subnet_number(hi, group->subnet->netmask))) { parse_warn(cfile, "prefix6 end prefix" " is outside the subnet"); skip_to_semi(cfile); return; } #endif /* * Next is '/' number ';'. */ token = next_token(NULL, NULL, cfile); if (token != SLASH) { parse_warn(cfile, "expecting '/'"); if (token != SEMI) skip_to_semi(cfile); return; } token = next_token(&val, NULL, cfile); if (token != NUMBER) { parse_warn(cfile, "expecting number"); if (token != SEMI) skip_to_semi(cfile); return; } bits = atoi(val); if ((bits <= 0) || (bits >= 128)) { parse_warn(cfile, "networks have 0 to 128 bits (exclusive)"); return; } #if 0 /* Prefixes are not required to be within the subnet, but I'm not * entirely sure that we won't want to revive this code as a warning * in the future so I'm ifdeffing it */ if (bits < group->subnet->prefix_len) { parse_warn(cfile, "network mask smaller than subnet mask"); skip_to_semi(cfile); return; } #endif if (!is_cidr_mask_valid(&lo, bits) || !is_cidr_mask_valid(&hi, bits)) { parse_warn(cfile, "network mask too short"); skip_to_semi(cfile); return; } token = next_token(NULL, NULL, cfile); if (token != SEMI) { parse_warn(cfile, "semicolon expected."); skip_to_semi(cfile); return; } /* * Convert our range to a set of CIDR networks. */ nets = NULL; if (range2cidr(&nets, &lo, &hi) != ISC_R_SUCCESS) { log_fatal("Error converting prefix to CIDR"); } /* * See if we have a pond for this set of pools. * If the caller supplied one we use it, otherwise * check the shared network */ if (inpond != NULL) { ipv6_pond_reference(&pond, inpond, MDL); } else { add_ipv6_pond_to_network(group, &pond); } for (p = nets; p != NULL; p = p->next) { /* Normalize and check. */ if (p->cidrnet.bits == 128) { p->cidrnet.bits = bits; } if (p->cidrnet.bits > bits) { parse_warn(cfile, "impossible mask length"); continue; } add_ipv6_pool_to_subnet(group->subnet, D6O_IA_PD, &p->cidrnet.lo_addr, p->cidrnet.bits, bits, pond); } free_iaddrcidrnetlist(&nets); } /* fixed-prefix6 :== ip6-address SLASH number SEMI */ void parse_fixed_prefix6(struct parse *cfile, struct host_decl *host_decl) { struct iaddrcidrnetlist *ia, **h; enum dhcp_token token; const char *val; /* * Get the head of the fixed-prefix list. */ h = &host_decl->fixed_prefix; /* * Walk to the end. */ while (*h != NULL) { h = &((*h)->next); } /* * Allocate a new iaddrcidrnetlist structure. */ ia = dmalloc(sizeof(*ia), MDL); if (!ia) { log_fatal("Out of memory"); } /* * Parse it. */ if (!parse_ip6_addr(cfile, &ia->cidrnet.lo_addr)) { dfree(ia, MDL); return; } token = next_token(NULL, NULL, cfile); if (token != SLASH) { dfree(ia, MDL); parse_warn(cfile, "expecting '/'"); if (token != SEMI) skip_to_semi(cfile); return; } token = next_token(&val, NULL, cfile); if (token != NUMBER) { dfree(ia, MDL); parse_warn(cfile, "expecting number"); if (token != SEMI) skip_to_semi(cfile); return; } token = next_token(NULL, NULL, cfile); if (token != SEMI) { dfree(ia, MDL); parse_warn(cfile, "semicolon expected."); skip_to_semi(cfile); return; } /* * Fill it. */ ia->cidrnet.bits = atoi(val); if ((ia->cidrnet.bits < 0) || (ia->cidrnet.bits > 128)) { dfree(ia, MDL); parse_warn(cfile, "networks have 0 to 128 bits"); return; } if (!is_cidr_mask_valid(&ia->cidrnet.lo_addr, ia->cidrnet.bits)) { dfree(ia, MDL); parse_warn(cfile, "network mask too short"); return; } /* * Store it. */ *h = ia; return; } /*! * * \brief Parse a pool6 statement * * Pool statements are used to group declarations and permit & deny information * with a specific address range. They must be declared within a shared network * or subnet and there may be multiple pools withing a shared network or subnet. * Each pool may have a different set of permit or deny options. * * \param[in] cfile = the configuration file being parsed * \param[in] group = the group structure for this pool * \param[in] type = the type of the enclosing statement. This must be * SUBNET_DECL for this function. * * \return * void - This function either parses the statement and updates the structures * or it generates an error message and possible halts the program if * it encounters a problem. */ void parse_pool6_statement (cfile, group, type) struct parse *cfile; struct group *group; int type; { enum dhcp_token token; const char *val; int done = 0; struct ipv6_pond *pond, **p; int declaration = 0; isc_result_t status; pond = NULL; status = ipv6_pond_allocate(&pond, MDL); if (status != ISC_R_SUCCESS) log_fatal("no memory for pool6: %s", isc_result_totext (status)); if (type == SUBNET_DECL) shared_network_reference(&pond->shared_network, group->subnet->shared_network, MDL); else { parse_warn(cfile, "pool6s are only valid inside " "subnet statements."); ipv6_pond_dereference(&pond, MDL); skip_to_semi(cfile); return; } if (clone_group(&pond->group, group, MDL) == 0) log_fatal("can't clone pool6 group."); if (parse_lbrace(cfile) == 0) { ipv6_pond_dereference(&pond, MDL); return; } do { token = peek_token(&val, NULL, cfile); switch (token) { case RANGE6: skip_token(NULL, NULL, cfile); parse_address_range6(cfile, group, pond); break; case PREFIX6: skip_token(NULL, NULL, cfile); parse_prefix6(cfile, group, pond); break; case ALLOW: skip_token(NULL, NULL, cfile); get_permit(cfile, &pond->permit_list, 1, &pond->valid_from, &pond->valid_until); break; case DENY: skip_token(NULL, NULL, cfile); get_permit(cfile, &pond->prohibit_list, 0, &pond->valid_from, &pond->valid_until); break; case RBRACE: skip_token(&val, NULL, cfile); done = 1; break; case END_OF_FILE: /* * We can get to END_OF_FILE if, for instance, * the parse_statement() reads all available tokens * and leaves us at the end. */ parse_warn(cfile, "unexpected end of file"); goto cleanup; default: declaration = parse_statement(cfile, pond->group, POOL_DECL, NULL, declaration); break; } } while (!done); /* * A possible optimization is to see if this pond can be merged into * an already existing pond. But I'll pass on that for now as we need * to repoint the leases to the other pond which is annoying. SAR */ /* * Add this pond to the list (will need updating if we add the * optimization). */ p = &pond->shared_network->ipv6_pond; for (; *p; p = &((*p)->next)) ; ipv6_pond_reference(p, pond, MDL); /* Don't allow a pool6 declaration with no addresses or prefixes, since it is probably a configuration error. */ if (pond->ipv6_pools == NULL) { parse_warn (cfile, "Pool6 declaration with no %s.", "address range6 or prefix6"); log_error ("Pool6 declarations must always contain at least"); log_error ("one range6 or prefix6 statement."); } cleanup: ipv6_pond_dereference(&pond, MDL); } #endif /* DHCPv6 */ /* allow-deny-keyword :== BOOTP | BOOTING | DYNAMIC_BOOTP | UNKNOWN_CLIENTS */ int parse_allow_deny (oc, cfile, flag) struct option_cache **oc; struct parse *cfile; int flag; { enum dhcp_token token; const char *val; unsigned char rf = flag; unsigned code; struct option *option = NULL; struct expression *data = (struct expression *)0; int status; if (!make_const_data (&data, &rf, 1, 0, 1, MDL)) return 0; token = next_token (&val, (unsigned *)0, cfile); switch (token) { case TOKEN_BOOTP: code = SV_ALLOW_BOOTP; break; case BOOTING: code = SV_ALLOW_BOOTING; break; case DYNAMIC_BOOTP: code = SV_DYNAMIC_BOOTP; break; case UNKNOWN_CLIENTS: code = SV_BOOT_UNKNOWN_CLIENTS; break; case DUPLICATES: code = SV_DUPLICATES; break; case DECLINES: code= SV_DECLINES; break; case CLIENT_UPDATES: code = SV_CLIENT_UPDATES; break; case LEASEQUERY: code = SV_LEASEQUERY; break; default: parse_warn (cfile, "expecting allow/deny key"); skip_to_semi (cfile); expression_dereference (&data, MDL); return 0; } /* Reference on option is passed to option cache. */ if (!option_code_hash_lookup(&option, server_universe.code_hash, &code, 0, MDL)) log_fatal("Unable to find server option %u (%s:%d).", code, MDL); status = option_cache(oc, NULL, data, option, MDL); expression_dereference (&data, MDL); parse_semi (cfile); return status; } void parse_ia_na_declaration(struct parse *cfile) { #if !defined(DHCPv6) parse_warn(cfile, "No DHCPv6 support."); skip_to_semi(cfile); #else /* defined(DHCPv6) */ enum dhcp_token token; struct ia_xx *ia = NULL; const char *val; struct ia_xx *old_ia; u_int32_t iaid; struct iaddr iaddr; binding_state_t state; u_int32_t prefer; u_int32_t valid; TIME end_time; struct iasubopt *iaaddr; struct ipv6_pool *pool; char addr_buf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; isc_boolean_t newbinding; struct binding_scope *scope = NULL; struct binding *bnd; struct binding_value *nv = NULL; struct executable_statement *on_star[2] = {NULL, NULL}; int lose, i; if (local_family != AF_INET6) { parse_warn(cfile, "IA_NA is only supported in DHCPv6 mode."); skip_to_semi(cfile); return; } if (!parse_iaid_duid(cfile, &ia, &iaid, MDL)) { return; } ia->ia_type = D6O_IA_NA; token = next_token(&val, NULL, cfile); if (token != LBRACE) { parse_warn(cfile, "corrupt lease file; expecting left brace"); skip_to_semi(cfile); return; } for (;;) { token = next_token(&val, NULL, cfile); if (token == RBRACE) break; if (token == CLTT) { ia->cltt = parse_date (cfile); continue; } if (token != IAADDR) { parse_warn(cfile, "corrupt lease file; " "expecting IAADDR or right brace"); skip_to_semi(cfile); return; } if (!parse_ip6_addr(cfile, &iaddr)) { parse_warn(cfile, "corrupt lease file; " "expecting IPv6 address"); skip_to_semi(cfile); return; } token = next_token(&val, NULL, cfile); if (token != LBRACE) { parse_warn(cfile, "corrupt lease file; " "expecting left brace"); skip_to_semi(cfile); return; } state = FTS_LAST+1; prefer = valid = 0; end_time = -1; for (;;) { token = next_token(&val, NULL, cfile); if (token == RBRACE) break; switch(token) { case END_OF_FILE: /* We hit the end of file and don't know * what parts of the lease we may be missing * don't try to salvage the lease */ parse_warn(cfile, "corrupt lease file; " "unexpected end of file"); return; /* Lease binding state. */ case BINDING: token = next_token(&val, NULL, cfile); if (token != STATE) { parse_warn(cfile, "corrupt lease file; " "expecting state"); skip_to_semi(cfile); return; } token = next_token(&val, NULL, cfile); switch (token) { case TOKEN_ABANDONED: state = FTS_ABANDONED; break; case TOKEN_FREE: state = FTS_FREE; break; case TOKEN_ACTIVE: state = FTS_ACTIVE; break; case TOKEN_EXPIRED: state = FTS_EXPIRED; break; case TOKEN_RELEASED: state = FTS_RELEASED; break; default: parse_warn(cfile, "corrupt lease " "file; " "expecting a " "binding state."); skip_to_semi(cfile); return; } token = next_token(&val, NULL, cfile); if (token != SEMI) { parse_warn(cfile, "corrupt lease file; " "expecting " "semicolon."); } break; /* Lease preferred lifetime. */ case PREFERRED_LIFE: token = next_token(&val, NULL, cfile); if (token != NUMBER) { parse_warn(cfile, "%s is not a valid " "preferred time", val); skip_to_semi(cfile); continue; } prefer = atoi (val); /* * Currently we peek for the semi-colon to * allow processing of older lease files that * don't have the semi-colon. Eventually we * should remove the peeking code. */ token = peek_token(&val, NULL, cfile); if (token == SEMI) { skip_token(&val, NULL, cfile); } else { parse_warn(cfile, "corrupt lease file; " "expecting semicolon."); } break; /* Lease valid lifetime. */ case MAX_LIFE: token = next_token(&val, NULL, cfile); if (token != NUMBER) { parse_warn(cfile, "%s is not a valid " "max time", val); skip_to_semi(cfile); continue; } valid = atoi (val); /* * Currently we peek for the semi-colon to * allow processing of older lease files that * don't have the semi-colon. Eventually we * should remove the peeking code. */ token = peek_token(&val, NULL, cfile); if (token == SEMI) { skip_token(&val, NULL, cfile); } else { parse_warn(cfile, "corrupt lease file; " "expecting semicolon."); } break; /* Lease expiration time. */ case ENDS: end_time = parse_date(cfile); break; /* Lease binding scopes. */ case TOKEN_SET: token = next_token(&val, NULL, cfile); if ((token != NAME) && (token != NUMBER_OR_NAME)) { parse_warn(cfile, "%s is not a valid " "variable name", val); skip_to_semi(cfile); continue; } if (scope != NULL) bnd = find_binding(scope, val); else { if (!binding_scope_allocate(&scope, MDL)) { log_fatal("Out of memory for " "lease binding " "scope."); } bnd = NULL; } if (bnd == NULL) { bnd = dmalloc(sizeof(*bnd), MDL); if (bnd == NULL) { log_fatal("No memory for " "lease binding."); } bnd->name = dmalloc(strlen(val) + 1, MDL); if (bnd->name == NULL) { log_fatal("No memory for " "binding name."); } strcpy(bnd->name, val); newbinding = ISC_TRUE; } else { newbinding = ISC_FALSE; } if (!binding_value_allocate(&nv, MDL)) { log_fatal("no memory for binding " "value."); } token = next_token(NULL, NULL, cfile); if (token != EQUAL) { parse_warn(cfile, "expecting '=' in " "set statement."); goto binding_err; } if (!parse_binding_value(cfile, nv)) { binding_err: binding_value_dereference(&nv, MDL); binding_scope_dereference(&scope, MDL); return; } if (newbinding) { binding_value_reference(&bnd->value, nv, MDL); bnd->next = scope->bindings; scope->bindings = bnd; } else { binding_value_dereference(&bnd->value, MDL); binding_value_reference(&bnd->value, nv, MDL); } binding_value_dereference(&nv, MDL); parse_semi(cfile); break; case ON: lose = 0; /* * Depending on the user config we may * have one or two on statements. We * need to save information about both * of them until we allocate the * iasubopt to hold them. */ if (on_star[0] == NULL) { if (!parse_on_statement (&on_star[0], cfile, &lose)) { parse_warn(cfile, "corrupt lease " "file; bad ON " "statement"); skip_to_rbrace (cfile, 1); return; } } else { if (!parse_on_statement (&on_star[1], cfile, &lose)) { parse_warn(cfile, "corrupt lease " "file; bad ON " "statement"); skip_to_rbrace (cfile, 1); return; } } break; default: parse_warn(cfile, "corrupt lease file; " "expecting ia_na contents, " "got '%s'", val); skip_to_semi(cfile); continue; } } if (state == FTS_LAST+1) { parse_warn(cfile, "corrupt lease file; " "missing state in iaaddr"); return; } if (end_time == -1) { parse_warn(cfile, "corrupt lease file; " "missing end time in iaaddr"); return; } iaaddr = NULL; if (iasubopt_allocate(&iaaddr, MDL) != ISC_R_SUCCESS) { log_fatal("Out of memory."); } memcpy(&iaaddr->addr, iaddr.iabuf, sizeof(iaaddr->addr)); iaaddr->plen = 0; iaaddr->state = state; iaaddr->prefer = prefer; iaaddr->valid = valid; if (iaaddr->state == FTS_RELEASED) iaaddr->hard_lifetime_end_time = end_time; if (scope != NULL) { binding_scope_reference(&iaaddr->scope, scope, MDL); binding_scope_dereference(&scope, MDL); } /* * Check on both on statements. Because of how we write the * lease file we know which is which if we have two but it's * easier to write the code to be independent. We do assume * that the statements won't overlap. */ for (i = 0; (i < 2) && on_star[i] != NULL ; i++) { if ((on_star[i]->data.on.evtypes & ON_EXPIRY) && on_star[i]->data.on.statements) { executable_statement_reference (&iaaddr->on_star.on_expiry, on_star[i]->data.on.statements, MDL); } if ((on_star[i]->data.on.evtypes & ON_RELEASE) && on_star[i]->data.on.statements) { executable_statement_reference (&iaaddr->on_star.on_release, on_star[i]->data.on.statements, MDL); } executable_statement_dereference (&on_star[i], MDL); } /* find the pool this address is in */ pool = NULL; if (find_ipv6_pool(&pool, D6O_IA_NA, &iaaddr->addr) != ISC_R_SUCCESS) { inet_ntop(AF_INET6, &iaaddr->addr, addr_buf, sizeof(addr_buf)); log_error("No pool found for IA_NA address %s", addr_buf); iasubopt_dereference(&iaaddr, MDL); continue; } #ifdef EUI_64 if ((pool->ipv6_pond->use_eui_64) && (!valid_for_eui_64_pool(pool, &ia->iaid_duid, IAID_LEN, &iaaddr->addr))) { log_error("Non EUI-64 lease in EUI-64 pool: %s" " discarding it", pin6_addr(&iaaddr->addr)); iasubopt_dereference(&iaaddr, MDL); continue; } #endif /* remove old information */ if (cleanup_lease6(ia_na_active, pool, iaaddr, ia) != ISC_R_SUCCESS) { inet_ntop(AF_INET6, &iaaddr->addr, addr_buf, sizeof(addr_buf)); parse_warn(cfile, "duplicate na lease for address %s", addr_buf); } /* * if we like the lease we add it to our various structues * otherwise we leave it and it will get cleaned when we * do the iasubopt_dereference. */ if ((state == FTS_ACTIVE) || (state == FTS_ABANDONED)) { ia_add_iasubopt(ia, iaaddr, MDL); ia_reference(&iaaddr->ia, ia, MDL); add_lease6(pool, iaaddr, end_time); } iasubopt_dereference(&iaaddr, MDL); ipv6_pool_dereference(&pool, MDL); } /* * If we have an existing record for this IA_NA, remove it. */ old_ia = NULL; if (ia_hash_lookup(&old_ia, ia_na_active, (unsigned char *)ia->iaid_duid.data, ia->iaid_duid.len, MDL)) { ia_hash_delete(ia_na_active, (unsigned char *)ia->iaid_duid.data, ia->iaid_duid.len, MDL); ia_dereference(&old_ia, MDL); } /* * If we have addresses, add this, otherwise don't bother. */ if (ia->num_iasubopt > 0) { ia_hash_add(ia_na_active, (unsigned char *)ia->iaid_duid.data, ia->iaid_duid.len, ia, MDL); } ia_dereference(&ia, MDL); #endif /* defined(DHCPv6) */ } void parse_ia_ta_declaration(struct parse *cfile) { #if !defined(DHCPv6) parse_warn(cfile, "No DHCPv6 support."); skip_to_semi(cfile); #else /* defined(DHCPv6) */ enum dhcp_token token; struct ia_xx *ia = NULL; const char *val; struct ia_xx *old_ia; u_int32_t iaid; struct iaddr iaddr; binding_state_t state; u_int32_t prefer; u_int32_t valid; TIME end_time; struct iasubopt *iaaddr; struct ipv6_pool *pool; char addr_buf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; isc_boolean_t newbinding; struct binding_scope *scope = NULL; struct binding *bnd; struct binding_value *nv = NULL; struct executable_statement *on_star[2] = {NULL, NULL}; int lose, i; if (local_family != AF_INET6) { parse_warn(cfile, "IA_TA is only supported in DHCPv6 mode."); skip_to_semi(cfile); return; } if (!parse_iaid_duid(cfile, &ia, &iaid, MDL)) { return; } ia->ia_type = D6O_IA_TA; token = next_token(&val, NULL, cfile); if (token != LBRACE) { parse_warn(cfile, "corrupt lease file; expecting left brace"); skip_to_semi(cfile); return; } for (;;) { token = next_token(&val, NULL, cfile); if (token == RBRACE) break; if (token == CLTT) { ia->cltt = parse_date (cfile); continue; } if (token != IAADDR) { parse_warn(cfile, "corrupt lease file; " "expecting IAADDR or right brace"); skip_to_semi(cfile); return; } if (!parse_ip6_addr(cfile, &iaddr)) { parse_warn(cfile, "corrupt lease file; " "expecting IPv6 address"); skip_to_semi(cfile); return; } token = next_token(&val, NULL, cfile); if (token != LBRACE) { parse_warn(cfile, "corrupt lease file; " "expecting left brace"); skip_to_semi(cfile); return; } state = FTS_LAST+1; prefer = valid = 0; end_time = -1; for (;;) { token = next_token(&val, NULL, cfile); if (token == RBRACE) break; switch(token) { case END_OF_FILE: /* We hit the end of file and don't know * what parts of the lease we may be missing * don't try to salvage the lease */ parse_warn(cfile, "corrupt lease file; " "unexpected end of file"); return; /* Lease binding state. */ case BINDING: token = next_token(&val, NULL, cfile); if (token != STATE) { parse_warn(cfile, "corrupt lease file; " "expecting state"); skip_to_semi(cfile); return; } token = next_token(&val, NULL, cfile); switch (token) { case TOKEN_ABANDONED: state = FTS_ABANDONED; break; case TOKEN_FREE: state = FTS_FREE; break; case TOKEN_ACTIVE: state = FTS_ACTIVE; break; case TOKEN_EXPIRED: state = FTS_EXPIRED; break; case TOKEN_RELEASED: state = FTS_RELEASED; break; default: parse_warn(cfile, "corrupt lease " "file; " "expecting a " "binding state."); skip_to_semi(cfile); return; } token = next_token(&val, NULL, cfile); if (token != SEMI) { parse_warn(cfile, "corrupt lease file; " "expecting " "semicolon."); } break; /* Lease preferred lifetime. */ case PREFERRED_LIFE: token = next_token(&val, NULL, cfile); if (token != NUMBER) { parse_warn(cfile, "%s is not a valid " "preferred time", val); skip_to_semi(cfile); continue; } prefer = atoi (val); /* * Currently we peek for the semi-colon to * allow processing of older lease files that * don't have the semi-colon. Eventually we * should remove the peeking code. */ token = peek_token(&val, NULL, cfile); if (token == SEMI) { skip_token(&val, NULL, cfile); } else { parse_warn(cfile, "corrupt lease file; " "expecting semicolon."); } break; /* Lease valid lifetime. */ case MAX_LIFE: token = next_token(&val, NULL, cfile); if (token != NUMBER) { parse_warn(cfile, "%s is not a valid " "max time", val); skip_to_semi(cfile); continue; } valid = atoi (val); /* * Currently we peek for the semi-colon to * allow processing of older lease files that * don't have the semi-colon. Eventually we * should remove the peeking code. */ token = peek_token(&val, NULL, cfile); if (token == SEMI) { skip_token(&val, NULL, cfile); } else { parse_warn(cfile, "corrupt lease file; " "expecting semicolon."); } break; /* Lease expiration time. */ case ENDS: end_time = parse_date(cfile); break; /* Lease binding scopes. */ case TOKEN_SET: token = next_token(&val, NULL, cfile); if ((token != NAME) && (token != NUMBER_OR_NAME)) { parse_warn(cfile, "%s is not a valid " "variable name", val); skip_to_semi(cfile); continue; } if (scope != NULL) bnd = find_binding(scope, val); else { if (!binding_scope_allocate(&scope, MDL)) { log_fatal("Out of memory for " "lease binding " "scope."); } bnd = NULL; } if (bnd == NULL) { bnd = dmalloc(sizeof(*bnd), MDL); if (bnd == NULL) { log_fatal("No memory for " "lease binding."); } bnd->name = dmalloc(strlen(val) + 1, MDL); if (bnd->name == NULL) { log_fatal("No memory for " "binding name."); } strcpy(bnd->name, val); newbinding = ISC_TRUE; } else { newbinding = ISC_FALSE; } if (!binding_value_allocate(&nv, MDL)) { log_fatal("no memory for binding " "value."); } token = next_token(NULL, NULL, cfile); if (token != EQUAL) { parse_warn(cfile, "expecting '=' in " "set statement."); goto binding_err; } if (!parse_binding_value(cfile, nv)) { binding_err: binding_value_dereference(&nv, MDL); binding_scope_dereference(&scope, MDL); return; } if (newbinding) { binding_value_reference(&bnd->value, nv, MDL); bnd->next = scope->bindings; scope->bindings = bnd; } else { binding_value_dereference(&bnd->value, MDL); binding_value_reference(&bnd->value, nv, MDL); } binding_value_dereference(&nv, MDL); parse_semi(cfile); break; case ON: lose = 0; /* * Depending on the user config we may * have one or two on statements. We * need to save information about both * of them until we allocate the * iasubopt to hold them. */ if (on_star[0] == NULL) { if (!parse_on_statement (&on_star[0], cfile, &lose)) { parse_warn(cfile, "corrupt lease " "file; bad ON " "statement"); skip_to_rbrace (cfile, 1); return; } } else { if (!parse_on_statement (&on_star[1], cfile, &lose)) { parse_warn(cfile, "corrupt lease " "file; bad ON " "statement"); skip_to_rbrace (cfile, 1); return; } } break; default: parse_warn(cfile, "corrupt lease file; " "expecting ia_ta contents, " "got '%s'", val); skip_to_semi(cfile); continue; } } if (state == FTS_LAST+1) { parse_warn(cfile, "corrupt lease file; " "missing state in iaaddr"); return; } if (end_time == -1) { parse_warn(cfile, "corrupt lease file; " "missing end time in iaaddr"); return; } iaaddr = NULL; if (iasubopt_allocate(&iaaddr, MDL) != ISC_R_SUCCESS) { log_fatal("Out of memory."); } memcpy(&iaaddr->addr, iaddr.iabuf, sizeof(iaaddr->addr)); iaaddr->plen = 0; iaaddr->state = state; iaaddr->prefer = prefer; iaaddr->valid = valid; if (iaaddr->state == FTS_RELEASED) iaaddr->hard_lifetime_end_time = end_time; if (scope != NULL) { binding_scope_reference(&iaaddr->scope, scope, MDL); binding_scope_dereference(&scope, MDL); } /* * Check on both on statements. Because of how we write the * lease file we know which is which if we have two but it's * easier to write the code to be independent. We do assume * that the statements won't overlap. */ for (i = 0; (i < 2) && on_star[i] != NULL ; i++) { if ((on_star[i]->data.on.evtypes & ON_EXPIRY) && on_star[i]->data.on.statements) { executable_statement_reference (&iaaddr->on_star.on_expiry, on_star[i]->data.on.statements, MDL); } if ((on_star[i]->data.on.evtypes & ON_RELEASE) && on_star[i]->data.on.statements) { executable_statement_reference (&iaaddr->on_star.on_release, on_star[i]->data.on.statements, MDL); } executable_statement_dereference (&on_star[i], MDL); } /* find the pool this address is in */ pool = NULL; if (find_ipv6_pool(&pool, D6O_IA_TA, &iaaddr->addr) != ISC_R_SUCCESS) { inet_ntop(AF_INET6, &iaaddr->addr, addr_buf, sizeof(addr_buf)); log_error("No pool found for IA_TA address %s", addr_buf); iasubopt_dereference(&iaaddr, MDL); continue; } /* remove old information */ if (cleanup_lease6(ia_ta_active, pool, iaaddr, ia) != ISC_R_SUCCESS) { inet_ntop(AF_INET6, &iaaddr->addr, addr_buf, sizeof(addr_buf)); parse_warn(cfile, "duplicate ta lease for address %s", addr_buf); } /* * if we like the lease we add it to our various structues * otherwise we leave it and it will get cleaned when we * do the iasubopt_dereference. */ if ((state == FTS_ACTIVE) || (state == FTS_ABANDONED)) { ia_add_iasubopt(ia, iaaddr, MDL); ia_reference(&iaaddr->ia, ia, MDL); add_lease6(pool, iaaddr, end_time); } ipv6_pool_dereference(&pool, MDL); iasubopt_dereference(&iaaddr, MDL); } /* * If we have an existing record for this IA_TA, remove it. */ old_ia = NULL; if (ia_hash_lookup(&old_ia, ia_ta_active, (unsigned char *)ia->iaid_duid.data, ia->iaid_duid.len, MDL)) { ia_hash_delete(ia_ta_active, (unsigned char *)ia->iaid_duid.data, ia->iaid_duid.len, MDL); ia_dereference(&old_ia, MDL); } /* * If we have addresses, add this, otherwise don't bother. */ if (ia->num_iasubopt > 0) { ia_hash_add(ia_ta_active, (unsigned char *)ia->iaid_duid.data, ia->iaid_duid.len, ia, MDL); } ia_dereference(&ia, MDL); #endif /* defined(DHCPv6) */ } void parse_ia_pd_declaration(struct parse *cfile) { #if !defined(DHCPv6) parse_warn(cfile, "No DHCPv6 support."); skip_to_semi(cfile); #else /* defined(DHCPv6) */ enum dhcp_token token; struct ia_xx *ia = NULL; const char *val; struct ia_xx *old_ia; u_int32_t iaid; struct iaddr iaddr; u_int8_t plen; binding_state_t state; u_int32_t prefer; u_int32_t valid; TIME end_time; struct iasubopt *iapref; struct ipv6_pool *pool; char addr_buf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; isc_boolean_t newbinding; struct binding_scope *scope = NULL; struct binding *bnd; struct binding_value *nv = NULL; struct executable_statement *on_star[2] = {NULL, NULL}; int lose, i; if (local_family != AF_INET6) { parse_warn(cfile, "IA_PD is only supported in DHCPv6 mode."); skip_to_semi(cfile); return; } if (!parse_iaid_duid(cfile, &ia, &iaid, MDL)) { return; } ia->ia_type = D6O_IA_PD; token = next_token(&val, NULL, cfile); if (token != LBRACE) { parse_warn(cfile, "corrupt lease file; expecting left brace"); skip_to_semi(cfile); return; } for (;;) { token = next_token(&val, NULL, cfile); if (token == RBRACE) break; if (token == CLTT) { ia->cltt = parse_date (cfile); continue; } if (token != IAPREFIX) { parse_warn(cfile, "corrupt lease file; expecting " "IAPREFIX or right brace"); skip_to_semi(cfile); return; } if (!parse_ip6_prefix(cfile, &iaddr, &plen)) { parse_warn(cfile, "corrupt lease file; " "expecting IPv6 prefix"); skip_to_semi(cfile); return; } token = next_token(&val, NULL, cfile); if (token != LBRACE) { parse_warn(cfile, "corrupt lease file; " "expecting left brace"); skip_to_semi(cfile); return; } state = FTS_LAST+1; prefer = valid = 0; end_time = -1; for (;;) { token = next_token(&val, NULL, cfile); if (token == RBRACE) break; switch(token) { case END_OF_FILE: /* We hit the end of file and don't know * what parts of the lease we may be missing * don't try to salvage the lease */ parse_warn(cfile, "corrupt lease file; " "unexpected end of file"); return; /* Prefix binding state. */ case BINDING: token = next_token(&val, NULL, cfile); if (token != STATE) { parse_warn(cfile, "corrupt lease file; " "expecting state"); skip_to_semi(cfile); return; } token = next_token(&val, NULL, cfile); switch (token) { case TOKEN_ABANDONED: state = FTS_ABANDONED; break; case TOKEN_FREE: state = FTS_FREE; break; case TOKEN_ACTIVE: state = FTS_ACTIVE; break; case TOKEN_EXPIRED: state = FTS_EXPIRED; break; case TOKEN_RELEASED: state = FTS_RELEASED; break; default: parse_warn(cfile, "corrupt lease " "file; " "expecting a " "binding state."); skip_to_semi(cfile); return; } token = next_token(&val, NULL, cfile); if (token != SEMI) { parse_warn(cfile, "corrupt lease file; " "expecting " "semicolon."); } break; /* Lease preferred lifetime. */ case PREFERRED_LIFE: token = next_token(&val, NULL, cfile); if (token != NUMBER) { parse_warn(cfile, "%s is not a valid " "preferred time", val); skip_to_semi(cfile); continue; } prefer = atoi (val); /* * Currently we peek for the semi-colon to * allow processing of older lease files that * don't have the semi-colon. Eventually we * should remove the peeking code. */ token = peek_token(&val, NULL, cfile); if (token == SEMI) { skip_token(&val, NULL, cfile); } else { parse_warn(cfile, "corrupt lease file; " "expecting semicolon."); } break; /* Lease valid lifetime. */ case MAX_LIFE: token = next_token(&val, NULL, cfile); if (token != NUMBER) { parse_warn(cfile, "%s is not a valid " "max time", val); skip_to_semi(cfile); continue; } valid = atoi (val); /* * Currently we peek for the semi-colon to * allow processing of older lease files that * don't have the semi-colon. Eventually we * should remove the peeking code. */ token = peek_token(&val, NULL, cfile); if (token == SEMI) { skip_token(&val, NULL, cfile); } else { parse_warn(cfile, "corrupt lease file; " "expecting semicolon."); } break; /* Prefix expiration time. */ case ENDS: end_time = parse_date(cfile); break; /* Prefix binding scopes. */ case TOKEN_SET: token = next_token(&val, NULL, cfile); if ((token != NAME) && (token != NUMBER_OR_NAME)) { parse_warn(cfile, "%s is not a valid " "variable name", val); skip_to_semi(cfile); continue; } if (scope != NULL) bnd = find_binding(scope, val); else { if (!binding_scope_allocate(&scope, MDL)) { log_fatal("Out of memory for " "lease binding " "scope."); } bnd = NULL; } if (bnd == NULL) { bnd = dmalloc(sizeof(*bnd), MDL); if (bnd == NULL) { log_fatal("No memory for " "prefix binding."); } bnd->name = dmalloc(strlen(val) + 1, MDL); if (bnd->name == NULL) { log_fatal("No memory for " "binding name."); } strcpy(bnd->name, val); newbinding = ISC_TRUE; } else { newbinding = ISC_FALSE; } if (!binding_value_allocate(&nv, MDL)) { log_fatal("no memory for binding " "value."); } token = next_token(NULL, NULL, cfile); if (token != EQUAL) { parse_warn(cfile, "expecting '=' in " "set statement."); goto binding_err; } if (!parse_binding_value(cfile, nv)) { binding_err: binding_value_dereference(&nv, MDL); binding_scope_dereference(&scope, MDL); return; } if (newbinding) { binding_value_reference(&bnd->value, nv, MDL); bnd->next = scope->bindings; scope->bindings = bnd; } else { binding_value_dereference(&bnd->value, MDL); binding_value_reference(&bnd->value, nv, MDL); } binding_value_dereference(&nv, MDL); parse_semi(cfile); break; case ON: lose = 0; /* * Depending on the user config we may * have one or two on statements. We * need to save information about both * of them until we allocate the * iasubopt to hold them. */ if (on_star[0] == NULL) { if (!parse_on_statement (&on_star[0], cfile, &lose)) { parse_warn(cfile, "corrupt lease " "file; bad ON " "statement"); skip_to_rbrace (cfile, 1); return; } } else { if (!parse_on_statement (&on_star[1], cfile, &lose)) { parse_warn(cfile, "corrupt lease " "file; bad ON " "statement"); skip_to_rbrace (cfile, 1); return; } } break; default: parse_warn(cfile, "corrupt lease file; " "expecting ia_pd contents, " "got '%s'", val); skip_to_semi(cfile); continue; } } if (state == FTS_LAST+1) { parse_warn(cfile, "corrupt lease file; " "missing state in iaprefix"); return; } if (end_time == -1) { parse_warn(cfile, "corrupt lease file; " "missing end time in iaprefix"); return; } iapref = NULL; if (iasubopt_allocate(&iapref, MDL) != ISC_R_SUCCESS) { log_fatal("Out of memory."); } memcpy(&iapref->addr, iaddr.iabuf, sizeof(iapref->addr)); iapref->plen = plen; iapref->state = state; iapref->prefer = prefer; iapref->valid = valid; if (iapref->state == FTS_RELEASED) iapref->hard_lifetime_end_time = end_time; if (scope != NULL) { binding_scope_reference(&iapref->scope, scope, MDL); binding_scope_dereference(&scope, MDL); } /* * Check on both on statements. Because of how we write the * lease file we know which is which if we have two but it's * easier to write the code to be independent. We do assume * that the statements won't overlap. */ for (i = 0; (i < 2) && on_star[i] != NULL ; i++) { if ((on_star[i]->data.on.evtypes & ON_EXPIRY) && on_star[i]->data.on.statements) { executable_statement_reference (&iapref->on_star.on_expiry, on_star[i]->data.on.statements, MDL); } if ((on_star[i]->data.on.evtypes & ON_RELEASE) && on_star[i]->data.on.statements) { executable_statement_reference (&iapref->on_star.on_release, on_star[i]->data.on.statements, MDL); } executable_statement_dereference (&on_star[i], MDL); } /* Find the pool this address is in. We need to check prefix * lengths too in case the pool has been reconfigured. */ pool = NULL; if ((find_ipv6_pool(&pool, D6O_IA_PD, &iapref->addr) != ISC_R_SUCCESS) || (pool->units != iapref->plen)) { inet_ntop(AF_INET6, &iapref->addr, addr_buf, sizeof(addr_buf)); log_error("No pool found for prefix %s/%d", addr_buf, iapref->plen); iasubopt_dereference(&iapref, MDL); continue; } /* remove old information */ if (cleanup_lease6(ia_pd_active, pool, iapref, ia) != ISC_R_SUCCESS) { inet_ntop(AF_INET6, &iapref->addr, addr_buf, sizeof(addr_buf)); parse_warn(cfile, "duplicate pd lease for address %s", addr_buf); } /* * if we like the lease we add it to our various structues * otherwise we leave it and it will get cleaned when we * do the iasubopt_dereference. */ if ((state == FTS_ACTIVE) || (state == FTS_ABANDONED)) { ia_add_iasubopt(ia, iapref, MDL); ia_reference(&iapref->ia, ia, MDL); add_lease6(pool, iapref, end_time); } ipv6_pool_dereference(&pool, MDL); iasubopt_dereference(&iapref, MDL); } /* * If we have an existing record for this IA_PD, remove it. */ old_ia = NULL; if (ia_hash_lookup(&old_ia, ia_pd_active, (unsigned char *)ia->iaid_duid.data, ia->iaid_duid.len, MDL)) { ia_hash_delete(ia_pd_active, (unsigned char *)ia->iaid_duid.data, ia->iaid_duid.len, MDL); ia_dereference(&old_ia, MDL); } /* * If we have prefixes, add this, otherwise don't bother. */ if (ia->num_iasubopt > 0) { ia_hash_add(ia_pd_active, (unsigned char *)ia->iaid_duid.data, ia->iaid_duid.len, ia, MDL); } ia_dereference(&ia, MDL); #endif /* defined(DHCPv6) */ } #ifdef DHCPv6 /* * When we parse a server-duid statement in a lease file, we are * looking at the saved server DUID from a previous run. In this case * we expect it to be followed by the binary representation of the * DUID stored in a string: * * server-duid "\000\001\000\001\015\221\034JRT\000\0224Y"; * * OR as a hex string of digits: * * server-duid 00:01:00:01:1e:68:b3:db:0a:00:27:00:00:02; */ void parse_server_duid(struct parse *cfile) { struct data_string duid; unsigned char bytes[128]; /* Maximum valid DUID is 128 */ unsigned int len; len = parse_X(cfile, bytes, sizeof(bytes)); if (len <= 2) { parse_warn(cfile, "Invalid duid contents"); skip_to_semi(cfile); return; } memset(&duid, 0x0, sizeof(duid)); if (!buffer_allocate(&duid.buffer, len, MDL)) { log_fatal("parse_server_duid: out of memory"); } memcpy(duid.buffer->data, bytes, len); duid.len = len; duid.data = duid.buffer->data; set_server_duid(&duid); data_string_forget(&duid, MDL); parse_semi(cfile); } /* * When we parse a server-duid statement in a config file, we will * have the type of the server DUID to generate, and possibly the * actual value defined. * * server-duid llt; * server-duid llt ethernet|ieee802|fddi 213982198 00:16:6F:49:7D:9B; * server-duid ll; * server-duid ll ethernet|ieee802|fddi 00:16:6F:49:7D:9B; * server-duid en 2495 "enterprise-specific-identifier-1234"; */ void parse_server_duid_conf(struct parse *cfile) { enum dhcp_token token; const char *val; unsigned int len; u_int32_t enterprise_number; int ll_type; struct data_string ll_addr; u_int32_t llt_time; struct data_string duid; int duid_type_num; /* * Consume the SERVER_DUID token. */ skip_token(NULL, NULL, cfile); /* * Obtain the DUID type. */ token = next_token(&val, NULL, cfile); /* * Enterprise is the easiest - enterprise number and raw data * are required. */ if (token == EN) { /* * Get enterprise number and identifier. */ token = next_token(&val, NULL, cfile); if (token != NUMBER) { parse_warn(cfile, "enterprise number expected"); skip_to_semi(cfile); return; } enterprise_number = atoi(val); token = next_token(&val, &len, cfile); if (token != STRING) { parse_warn(cfile, "identifier expected"); skip_to_semi(cfile); return; } /* * Save the DUID. */ memset(&duid, 0, sizeof(duid)); duid.len = 2 + 4 + len; if (!buffer_allocate(&duid.buffer, duid.len, MDL)) { log_fatal("Out of memory storing DUID"); } duid.data = (unsigned char *)duid.buffer->data; putUShort(duid.buffer->data, DUID_EN); putULong(duid.buffer->data + 2, enterprise_number); memcpy(duid.buffer->data + 6, val, len); set_server_duid(&duid); data_string_forget(&duid, MDL); } /* * Next easiest is the link-layer DUID. It consists only of * the LL directive, or optionally the specific value to use. * * If we have LL only, then we set the type. If we have the * value, then we set the actual DUID. */ else if (token == LL) { if (peek_token(NULL, NULL, cfile) == SEMI) { set_server_duid_type(DUID_LL); } else { /* * Get our hardware type and address. */ token = next_token(NULL, NULL, cfile); switch (token) { case ETHERNET: ll_type = HTYPE_ETHER; break; case TOKEN_RING: ll_type = HTYPE_IEEE802; break; case TOKEN_FDDI: ll_type = HTYPE_FDDI; break; default: parse_warn(cfile, "hardware type expected"); skip_to_semi(cfile); return; } memset(&ll_addr, 0, sizeof(ll_addr)); if (!parse_cshl(&ll_addr, cfile)) { return; } /* * Save the DUID. */ memset(&duid, 0, sizeof(duid)); duid.len = 2 + 2 + ll_addr.len; if (!buffer_allocate(&duid.buffer, duid.len, MDL)) { log_fatal("Out of memory storing DUID"); } duid.data = (unsigned char *)duid.buffer->data; putUShort(duid.buffer->data, DUID_LL); putUShort(duid.buffer->data + 2, ll_type); memcpy(duid.buffer->data + 4, ll_addr.data, ll_addr.len); set_server_duid(&duid); data_string_forget(&duid, MDL); data_string_forget(&ll_addr, MDL); } } /* * Finally the link-layer DUID plus time. It consists only of * the LLT directive, or optionally the specific value to use. * * If we have LLT only, then we set the type. If we have the * value, then we set the actual DUID. */ else if (token == LLT) { if (peek_token(NULL, NULL, cfile) == SEMI) { set_server_duid_type(DUID_LLT); } else { /* * Get our hardware type, timestamp, and address. */ token = next_token(NULL, NULL, cfile); switch (token) { case ETHERNET: ll_type = HTYPE_ETHER; break; case TOKEN_RING: ll_type = HTYPE_IEEE802; break; case TOKEN_FDDI: ll_type = HTYPE_FDDI; break; default: parse_warn(cfile, "hardware type expected"); skip_to_semi(cfile); return; } token = next_token(&val, NULL, cfile); if (token != NUMBER) { parse_warn(cfile, "timestamp expected"); skip_to_semi(cfile); return; } llt_time = atoi(val); memset(&ll_addr, 0, sizeof(ll_addr)); if (!parse_cshl(&ll_addr, cfile)) { return; } /* * Save the DUID. */ memset(&duid, 0, sizeof(duid)); duid.len = 2 + 2 + 4 + ll_addr.len; if (!buffer_allocate(&duid.buffer, duid.len, MDL)) { log_fatal("Out of memory storing DUID"); } duid.data = (unsigned char *)duid.buffer->data; putUShort(duid.buffer->data, DUID_LLT); putUShort(duid.buffer->data + 2, ll_type); putULong(duid.buffer->data + 4, llt_time); memcpy(duid.buffer->data + 8, ll_addr.data, ll_addr.len); set_server_duid(&duid); data_string_forget(&duid, MDL); data_string_forget(&ll_addr, MDL); } } /* * If users want they can use a number for DUID types. * This is useful for supporting future, not-yet-defined * DUID types. * * In this case, they have to put in the complete value. * * This also works for existing DUID types of course. */ else if (token == NUMBER) { duid_type_num = atoi(val); token = next_token(&val, &len, cfile); if (token != STRING) { parse_warn(cfile, "identifier expected"); skip_to_semi(cfile); return; } /* * Save the DUID. */ memset(&duid, 0, sizeof(duid)); duid.len = 2 + len; if (!buffer_allocate(&duid.buffer, duid.len, MDL)) { log_fatal("Out of memory storing DUID"); } duid.data = (unsigned char *)duid.buffer->data; putUShort(duid.buffer->data, duid_type_num); memcpy(duid.buffer->data + 2, val, len); set_server_duid(&duid); data_string_forget(&duid, MDL); } /* * Anything else is an error. */ else { parse_warn(cfile, "DUID type of LLT, EN, or LL expected"); skip_to_semi(cfile); return; } /* * Finally consume our trailing semicolon. */ token = next_token(NULL, NULL, cfile); if (token != SEMI) { parse_warn(cfile, "semicolon expected"); skip_to_semi(cfile); } } /*! * \brief Creates a byte-order corrected uint32 from a buffer * * This function creates an integer value from a buffer, converting from * the byte order specified by authoring-byte-order to the current server's * byte order if they are different. The conversion works in either direction. * * If the parameter, authoring-byte-order hasn't yet been encountered we will * emit a warning and then default the byte order to match the current server's * byte order (i.e. no conversion will done). * * \param source buffer containing the "raw" four byte data * \return uint32_t containing the corrected value */ uint32_t parse_byte_order_uint32(const void *source) { uint32_t value; /* use memcpy to avoid any alignment monkey business */ memcpy(&value, source, 4); if (authoring_byte_order == 0) { log_error ("WARNING: " "authoring-byte-order not in the lease file.\n" "Assuming file byte order matches this server.\n"); authoring_byte_order = DHCP_BYTE_ORDER; } if (authoring_byte_order != DHCP_BYTE_ORDER) { value = (((value >> 24) & 0xff) | // move byte 3 to byte 0 ((value << 8) & 0xff0000) | // move byte 1 to byte 2 ((value >> 8) & 0xff00) | // move byte 2 to byte 1 ((value << 24) & 0xff000000)); // byte 0 to byte 3 } return (value); } /* !brief Parses an iaid/duid string into an iaid and struct ia * * Given a string containing the iaid-duid value read from the file, * and using the format specified by input lease-id-format, convert * it into an IAID value and an ia_xx struct. * * \param cfile - file being parsed * \param ia - pointer in which to store the allocated ia_xx struct * \param iaid - pointer in which to return the IAID value * \param file - source file name of invocation * \param line - line numbe of invocation * * \return 0 if parsing fails, non-zero otherwise */ int parse_iaid_duid(struct parse* cfile, struct ia_xx** ia, u_int32_t *iaid, const char* file, int line) { unsigned char bytes[132]; /* Maximum valid IAID-DUID is 132 */ unsigned int len; if (!ia) { log_error("parse_iaid_duid: ia ptr cannot be null"); return (0); } *ia = NULL; len = parse_X(cfile, bytes, sizeof(bytes)); if (len <= 5) { parse_warn(cfile, "corrupt lease file; " "iaid+ia_xx string too short"); skip_to_semi(cfile); return (0); } /* Extract the IAID from the front */ *iaid = parse_byte_order_uint32(bytes); /* Instantiate the ia_xx */ if (ia_allocate(ia, *iaid, (const char*)bytes + 4, len - 4, file, line) != ISC_R_SUCCESS) { log_fatal("parse_iaid_duid:Out of memory."); } return (1); } #endif /* DHCPv6 */ dhcp-4.4.1/server/db.c000644 000765 000024 00000075343 13243301226 014766 0ustar00tmarkstaff000000 000000 /* db.c Persistent database management routines for DHCPD... */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1995-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #include #include #define LEASE_REWRITE_PERIOD 3600 static isc_result_t write_binding_scope(FILE *db_file, struct binding *bnd, char *prepend); FILE *db_file; static int counting = 0; static int count = 0; TIME write_time; int lease_file_is_corrupt = 0; /* Write a single binding scope value in parsable format. */ static isc_result_t write_binding_scope(FILE *db_file, struct binding *bnd, char *prepend) { char *s; if ((db_file == NULL) || (bnd == NULL) || (prepend == NULL)) return DHCP_R_INVALIDARG; if (bnd->value->type == binding_data) { if (bnd->value->value.data.data != NULL) { s = quotify_buf(bnd->value->value.data.data, bnd->value->value.data.len, '"', MDL); if (s != NULL) { errno = 0; fprintf(db_file, "%sset %s = %s;", prepend, bnd->name, s); dfree(s, MDL); if (errno) return ISC_R_FAILURE; } else { return ISC_R_FAILURE; } } } else if (bnd->value->type == binding_numeric) { errno = 0; fprintf(db_file, "%sset %s = %%%ld;", prepend, bnd->name, bnd->value->value.intval); if (errno) return ISC_R_FAILURE; } else if (bnd->value->type == binding_boolean) { errno = 0; fprintf(db_file, "%sset %s = %s;", prepend, bnd->name, bnd->value->value.intval ? "true" : "false"); if (errno) return ISC_R_FAILURE; } else if (bnd->value->type == binding_dns) { log_error("%s: persistent dns values not supported.", bnd->name); } else if (bnd->value->type == binding_function) { log_error("%s: persistent functions not supported.", bnd->name); } else { log_fatal("%s: unknown binding type %d", bnd->name, bnd->value->type); } return ISC_R_SUCCESS; } /* Write the specified lease to the current lease database file. */ int write_lease (lease) struct lease *lease; { int errors = 0; struct binding *b; char *s; const char *tval; /* If the lease file is corrupt, don't try to write any more leases until we've written a good lease file. */ if (lease_file_is_corrupt) if (!new_lease_file (0)) return 0; if (counting) ++count; errno = 0; fprintf (db_file, "lease %s {", piaddr (lease -> ip_addr)); if (errno) { ++errors; } if (lease->starts && ((tval = print_time(lease->starts)) == NULL || fprintf(db_file, "\n starts %s", tval) < 0)) ++errors; if (lease->ends && ((tval = print_time(lease->ends)) == NULL || fprintf(db_file, "\n ends %s", tval) < 0)) ++errors; if (lease->tstp && ((tval = print_time(lease->tstp)) == NULL || fprintf(db_file, "\n tstp %s", tval) < 0)) ++errors; if (lease->tsfp && ((tval = print_time(lease->tsfp)) == NULL || fprintf(db_file, "\n tsfp %s", tval) < 0)) ++errors; if (lease->atsfp && ((tval = print_time(lease->atsfp)) == NULL || fprintf(db_file, "\n atsfp %s", tval) < 0)) ++errors; if (lease->cltt && ((tval = print_time(lease->cltt)) == NULL || fprintf(db_file, "\n cltt %s", tval) < 0)) ++errors; if (fprintf (db_file, "\n binding state %s;", ((lease -> binding_state > 0 && lease -> binding_state <= FTS_LAST) ? binding_state_names [lease -> binding_state - 1] : "abandoned")) < 0) ++errors; if (lease -> binding_state != lease -> next_binding_state) if (fprintf (db_file, "\n next binding state %s;", ((lease -> next_binding_state > 0 && lease -> next_binding_state <= FTS_LAST) ? (binding_state_names [lease -> next_binding_state - 1]) : "abandoned")) < 0) ++errors; /* * In this case, if the rewind state is not present in the lease file, * the reader will use the current binding state as the most * conservative (safest) state. So if the in-memory rewind state is * for some reason invalid, the best thing to do is not to write a * state and let the reader take on a safe state. */ if ((lease->binding_state != lease->rewind_binding_state) && (lease->rewind_binding_state > 0) && (lease->rewind_binding_state <= FTS_LAST) && (fprintf(db_file, "\n rewind binding state %s;", binding_state_names[lease->rewind_binding_state-1])) < 0) ++errors; if (lease->flags & RESERVED_LEASE) if (fprintf(db_file, "\n reserved;") < 0) ++errors; if (lease->flags & BOOTP_LEASE) if (fprintf(db_file, "\n dynamic-bootp;") < 0) ++errors; /* If this lease is billed to a class and is still valid, write it out. */ if (lease -> billing_class && lease -> ends > cur_time) { if (!write_billing_class (lease -> billing_class)) { log_error ("unable to write class %s", lease -> billing_class -> name); ++errors; } } if (lease -> hardware_addr.hlen) { errno = 0; fprintf (db_file, "\n hardware %s %s;", hardware_types [lease -> hardware_addr.hbuf [0]], print_hw_addr (lease -> hardware_addr.hbuf [0], lease -> hardware_addr.hlen - 1, &lease -> hardware_addr.hbuf [1])); if (errno) ++errors; } if (lease -> uid_len) { s = format_lease_id(lease->uid, lease->uid_len, lease_id_format, MDL); if (s) { errno = 0; fprintf (db_file, "\n uid %s;", s); if (errno) ++errors; dfree (s, MDL); } else ++errors; } if (lease->scope != NULL) { for (b = lease->scope->bindings; b; b = b->next) { if (!b->value) continue; if (write_binding_scope(db_file, b, "\n ") != ISC_R_SUCCESS) ++errors; } } if (lease -> agent_options) { struct option_cache *oc; struct data_string ds; pair p; memset (&ds, 0, sizeof ds); for (p = lease -> agent_options -> first; p; p = p -> cdr) { oc = (struct option_cache *)p -> car; if (oc -> data.len) { errno = 0; fprintf (db_file, "\n option agent.%s %s;", oc -> option -> name, pretty_print_option (oc -> option, oc -> data.data, oc -> data.len, 1, 1)); if (errno) ++errors; } } } if (lease -> client_hostname && db_printable((unsigned char *)lease->client_hostname)) { s = quotify_string (lease -> client_hostname, MDL); if (s) { errno = 0; fprintf (db_file, "\n client-hostname \"%s\";", s); if (errno) ++errors; dfree (s, MDL); } else ++errors; } if (lease->on_star.on_expiry) { errno = 0; fprintf (db_file, "\n on expiry%s {", lease->on_star.on_expiry == lease->on_star.on_release ? " or release" : ""); write_statements (db_file, lease->on_star.on_expiry, 4); /* XXX */ fprintf (db_file, "\n }"); if (errno) ++errors; } if (lease->on_star.on_release && lease->on_star.on_release != lease->on_star.on_expiry) { errno = 0; fprintf (db_file, "\n on release {"); write_statements (db_file, lease->on_star.on_release, 4); /* XXX */ fprintf (db_file, "\n }"); if (errno) ++errors; } errno = 0; fputs ("\n}\n", db_file); if (errno) ++errors; if (errors) { log_info ("write_lease: unable to write lease %s", piaddr (lease -> ip_addr)); lease_file_is_corrupt = 1; } return !errors; } int write_host (host) struct host_decl *host; { int errors = 0; int i; struct data_string ip_addrs; /* If the lease file is corrupt, don't try to write any more leases until we've written a good lease file. */ if (lease_file_is_corrupt) if (!new_lease_file (0)) return 0; if (!db_printable((unsigned char *)host->name)) return 0; if (counting) ++count; errno = 0; fprintf (db_file, "host %s {", host -> name); if (errno) ++errors; if (host -> flags & HOST_DECL_DYNAMIC) { errno = 0; fprintf (db_file, "\n dynamic;"); if (errno) ++errors; } if (host -> flags & HOST_DECL_DELETED) { errno = 0; fprintf (db_file, "\n deleted;"); if (errno) ++errors; } else { if (host -> interface.hlen) { errno = 0; fprintf (db_file, "\n hardware %s %s;", hardware_types [host -> interface.hbuf [0]], print_hw_addr (host -> interface.hbuf [0], host -> interface.hlen - 1, &host -> interface.hbuf [1])); if (errno) ++errors; } if (host -> client_identifier.len) { int i; errno = 0; if (db_printable_len (host -> client_identifier.data, host -> client_identifier.len)) { fprintf (db_file, "\n uid \"%.*s\";", (int)host -> client_identifier.len, host -> client_identifier.data); if (errno) ++errors; } else { fprintf (db_file, "\n uid %2.2x", host -> client_identifier.data [0]); if (errno) ++errors; for (i = 1; i < host -> client_identifier.len; i++) { errno = 0; fprintf (db_file, ":%2.2x", host -> client_identifier.data [i]); if (errno) ++errors; } errno = 0; fputc (';', db_file); if (errno) ++errors; } } memset (&ip_addrs, 0, sizeof ip_addrs); if (host -> fixed_addr && evaluate_option_cache (&ip_addrs, (struct packet *)0, (struct lease *)0, (struct client_state *)0, (struct option_state *)0, (struct option_state *)0, &global_scope, host -> fixed_addr, MDL)) { errno = 0; fprintf (db_file, "\n fixed-address "); if (errno) ++errors; for (i = 0; i < ip_addrs.len - 3; i += 4) { errno = 0; fprintf (db_file, "%u.%u.%u.%u%s", ip_addrs.data [i] & 0xff, ip_addrs.data [i + 1] & 0xff, ip_addrs.data [i + 2] & 0xff, ip_addrs.data [i + 3] & 0xff, i + 7 < ip_addrs.len ? "," : ""); if (errno) ++errors; } /* We're done with ip_addrs so pitch it */ data_string_forget (&ip_addrs, MDL); errno = 0; fputc (';', db_file); if (errno) ++errors; } if (host -> named_group) { errno = 0; fprintf (db_file, "\n group \"%s\";", host -> named_group -> name); if (errno) ++errors; } if (host -> group && (!host -> named_group || host -> group != host -> named_group -> group) && host -> group != root_group) { errno = 0; write_statements (db_file, host -> group -> statements, 8); if (errno) ++errors; } } errno = 0; fputs ("\n}\n", db_file); if (errno) ++errors; if (errors) { log_info ("write_host: unable to write host %s", host -> name); lease_file_is_corrupt = 1; } return !errors; } int write_group (group) struct group_object *group; { int errors = 0; /* If the lease file is corrupt, don't try to write any more leases until we've written a good lease file. */ if (lease_file_is_corrupt) if (!new_lease_file (0)) return 0; if (!db_printable((unsigned char *)group->name)) return 0; if (counting) ++count; errno = 0; fprintf (db_file, "group %s {", group -> name); if (errno) ++errors; if (group -> flags & GROUP_OBJECT_DYNAMIC) { errno = 0; fprintf (db_file, "\n dynamic;"); if (errno) ++errors; } if (group -> flags & GROUP_OBJECT_STATIC) { errno = 0; fprintf (db_file, "\n static;"); if (errno) ++errors; } if (group -> flags & GROUP_OBJECT_DELETED) { errno = 0; fprintf (db_file, "\n deleted;"); if (errno) ++errors; } else { if (group -> group) { errno = 0; write_statements (db_file, group -> group -> statements, 8); if (errno) ++errors; } } errno = 0; fputs ("\n}\n", db_file); if (errno) ++errors; if (errors) { log_info ("write_group: unable to write group %s", group -> name); lease_file_is_corrupt = 1; } return !errors; } /* * Write an IA and the options it has. */ int write_ia(const struct ia_xx *ia) { struct iasubopt *iasubopt; struct binding *bnd; int i; char addr_buf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff.255.255.255.255")]; const char *binding_state; const char *tval; char *s; int fprintf_ret; #ifdef EUI_64 /* If we're not writing EUI64 leases to the file, then * we can skip writing this IA provided all of its leases * are EUI64. (Not sure you can ever have a case where * they aren't but doesn't hurt to check) */ if (ia->ia_type == D6O_IA_NA && !persist_eui64) { int i; for (i=0; i < ia->num_iasubopt; i++) { if (!ia->iasubopt[i]->ipv6_pool->ipv6_pond->use_eui_64) { break; } } if (i == ia->num_iasubopt) { /* Their all EUI64 so we can skip it */ return(1); } } #endif /* * If the lease file is corrupt, don't try to write any more * leases until we've written a good lease file. */ if (lease_file_is_corrupt) { if (!new_lease_file(0)) { return 0; } } if (counting) { ++count; } s = format_lease_id(ia->iaid_duid.data, ia->iaid_duid.len, lease_id_format, MDL); if (s == NULL) { goto error_exit; } switch (ia->ia_type) { case D6O_IA_NA: fprintf_ret = fprintf(db_file, "ia-na %s {\n", s); break; case D6O_IA_TA: fprintf_ret = fprintf(db_file, "ia-ta %s {\n", s); break; case D6O_IA_PD: fprintf_ret = fprintf(db_file, "ia-pd %s {\n", s); break; default: log_error("Unknown ia type %u for %s at %s:%d", (unsigned)ia->ia_type, s, MDL); fprintf_ret = -1; } dfree(s, MDL); if (fprintf_ret < 0) { goto error_exit; } if (ia->cltt != MIN_TIME) { tval = print_time(ia->cltt); if (tval == NULL) { goto error_exit; } if (fprintf(db_file, " cltt %s\n", tval) < 0) { goto error_exit; } } for (i=0; inum_iasubopt; i++) { iasubopt = ia->iasubopt[i]; inet_ntop(AF_INET6, &iasubopt->addr, addr_buf, sizeof(addr_buf)); if ((ia->ia_type != D6O_IA_PD) && (fprintf(db_file, " iaaddr %s {\n", addr_buf) < 0)) { goto error_exit; } if ((ia->ia_type == D6O_IA_PD) && (fprintf(db_file, " iaprefix %s/%d {\n", addr_buf, (int)iasubopt->plen) < 0)) { goto error_exit; } if ((iasubopt->state <= 0) || (iasubopt->state > FTS_LAST)) { log_fatal("Unknown iasubopt state %d at %s:%d", iasubopt->state, MDL); } binding_state = binding_state_names[iasubopt->state-1]; if (fprintf(db_file, " binding state %s;\n", binding_state) < 0) { goto error_exit; } if (fprintf(db_file, " preferred-life %u;\n", (unsigned)iasubopt->prefer) < 0) { goto error_exit; } if (fprintf(db_file, " max-life %u;\n", (unsigned)iasubopt->valid) < 0) { goto error_exit; } /* Note that from here on out, the \n is prepended to the * next write, rather than appended to the current write. */ if ((iasubopt->state == FTS_ACTIVE) || (iasubopt->state == FTS_ABANDONED) || (iasubopt->hard_lifetime_end_time != 0)) { tval = print_time(iasubopt->hard_lifetime_end_time); } else { tval = print_time(iasubopt->soft_lifetime_end_time); } if (tval == NULL) { goto error_exit; } if (fprintf(db_file, " ends %s", tval) < 0) { goto error_exit; } /* Write out any binding scopes: note that 'ends' above does * not have \n on the end! We want that. */ if (iasubopt->scope != NULL) bnd = iasubopt->scope->bindings; else bnd = NULL; for (; bnd != NULL ; bnd = bnd->next) { if (bnd->value == NULL) continue; /* We don't do a regular error_exit because the * lease db is not corrupt in this case. */ if (write_binding_scope(db_file, bnd, "\n ") != ISC_R_SUCCESS) goto error_exit; } if (iasubopt->on_star.on_expiry) { if (fprintf(db_file, "\n on expiry%s {", iasubopt->on_star.on_expiry == iasubopt->on_star.on_release ? " or release" : "") < 0) goto error_exit; write_statements(db_file, iasubopt->on_star.on_expiry, 6); if (fprintf(db_file, "\n }") < 0) goto error_exit; } if (iasubopt->on_star.on_release && iasubopt->on_star.on_release != iasubopt->on_star.on_expiry) { if (fprintf(db_file, "\n on release {") < 0) goto error_exit; write_statements(db_file, iasubopt->on_star.on_release, 6); if (fprintf(db_file, "\n }") < 0) goto error_exit; } if (fprintf(db_file, "\n }\n") < 0) goto error_exit; } if (fprintf(db_file, "}\n\n") < 0) goto error_exit; fflush(db_file); return 1; error_exit: log_info("write_ia: unable to write ia"); lease_file_is_corrupt = 1; return 0; } #ifdef DHCPv6 /* * Put a copy of the server DUID in the leases file. */ int write_server_duid(void) { struct data_string server_duid; char *s; int fprintf_ret; /* * Only write the DUID if it's been set. */ if (!server_duid_isset()) { return 1; } /* * If the lease file is corrupt, don't try to write any more * leases until we've written a good lease file. */ if (lease_file_is_corrupt) { if (!new_lease_file(0)) { return 0; } } /* * Get a copy of our server DUID and convert to a quoted string. */ memset(&server_duid, 0, sizeof(server_duid)); copy_server_duid(&server_duid, MDL); s = format_lease_id(server_duid.data, server_duid.len, lease_id_format, MDL); data_string_forget(&server_duid, MDL); if (s == NULL) { goto error_exit; } /* * Write to the leases file. */ fprintf_ret = fprintf(db_file, "server-duid %s;\n\n", s); dfree(s, MDL); if (fprintf_ret < 0) { goto error_exit; } /* * Check if we actually managed to write. */ fflush(db_file); return 1; error_exit: log_info("write_server_duid: unable to write server-duid"); lease_file_is_corrupt = 1; return 0; } #endif /* DHCPv6 */ #if defined (FAILOVER_PROTOCOL) int write_failover_state (dhcp_failover_state_t *state) { int errors = 0; const char *tval; if (lease_file_is_corrupt) if (!new_lease_file (0)) return 0; errno = 0; fprintf (db_file, "\nfailover peer \"%s\" state {", state -> name); if (errno) ++errors; tval = print_time(state->me.stos); if (tval == NULL || fprintf(db_file, "\n my state %s at %s", (state->me.state == startup) ? dhcp_failover_state_name_print(state->saved_state) : dhcp_failover_state_name_print(state->me.state), tval) < 0) ++errors; tval = print_time(state->partner.stos); if (tval == NULL || fprintf(db_file, "\n partner state %s at %s", dhcp_failover_state_name_print(state->partner.state), tval) < 0) ++errors; if (state -> i_am == secondary) { errno = 0; fprintf (db_file, "\n mclt %ld;", (unsigned long)state -> mclt); if (errno) ++errors; } errno = 0; fprintf (db_file, "\n}\n"); if (errno) ++errors; if (errors) { log_info ("write_failover_state: unable to write state %s", state -> name); lease_file_is_corrupt = 1; return 0; } return 1; } #endif int db_printable (s) const unsigned char *s; { int i; for (i = 0; s [i]; i++) if (!isascii (s [i]) || !isprint (s [i]) || s [i] == '"' || s [i] == '\\') return 0; return 1; } int db_printable_len (s, len) const unsigned char *s; unsigned len; { int i; for (i = 0; i < len; i++) if (!isascii (s [i]) || !isprint (s [i]) || s [i] == '"' || s [i] == '\\') return 0; return 1; } static int print_hash_string(FILE *fp, struct class *class) { int i; for (i = 0 ; i < class->hash_string.len ; i++) if (!isascii(class->hash_string.data[i]) || !isprint(class->hash_string.data[i])) break; if (i == class->hash_string.len) { if (fprintf(fp, " \"%.*s\"", (int)class->hash_string.len, class->hash_string.data) <= 0) { log_error("Failure writing hash string: %m"); return 0; } } else { if (fprintf(fp, " %2.2x", class->hash_string.data[0]) <= 0) { log_error("Failure writing hash string: %m"); return 0; } for (i = 1 ; i < class->hash_string.len ; i++) { if (fprintf(fp, ":%2.2x", class->hash_string.data[i]) <= 0) { log_error("Failure writing hash string: %m"); return 0; } } } return 1; } isc_result_t write_named_billing_class(const void *key, unsigned len, void *object) { const unsigned char *name = key; struct class *class = object; if (class->flags & CLASS_DECL_DYNAMIC) { numclasseswritten++; if (class->superclass == 0) { if (fprintf(db_file, "class \"%s\" {\n", name) <= 0) return ISC_R_IOERROR; } else { if (fprintf(db_file, "subclass \"%s\"", class->superclass->name) <= 0) return ISC_R_IOERROR; if (!print_hash_string(db_file, class)) return ISC_R_IOERROR; if (fprintf(db_file, " {\n") <= 0) return ISC_R_IOERROR; } if ((class->flags & CLASS_DECL_DELETED) != 0) { if (fprintf(db_file, " deleted;\n") <= 0) return ISC_R_IOERROR; } else { if (fprintf(db_file, " dynamic;\n") <= 0) return ISC_R_IOERROR; } if (class->lease_limit > 0) { if (fprintf(db_file, " lease limit %d;\n", class->lease_limit) <= 0) return ISC_R_IOERROR; } if (class->expr != 0) { if (fprintf(db_file, " match if ") <= 0) return ISC_R_IOERROR; errno = 0; write_expression(db_file, class->expr, 5, 5, 0); if (errno) return ISC_R_IOERROR; if (fprintf(db_file, ";\n") <= 0) return ISC_R_IOERROR; } if (class->submatch != 0) { if (class->spawning) { if (fprintf(db_file, " spawn ") <= 0) return ISC_R_IOERROR; } else { if (fprintf(db_file, " match ") <= 0) return ISC_R_IOERROR; } errno = 0; write_expression(db_file, class->submatch, 5, 5, 0); if (errno) return ISC_R_IOERROR; if (fprintf(db_file, ";\n") <= 0) return ISC_R_IOERROR; } if (class->statements != 0) { errno = 0; write_statements(db_file, class->statements, 8); if (errno) return ISC_R_IOERROR; } /* XXXJAB this isn't right, but classes read in off the leases file don't get the root group assigned to them (due to clone_group() call). */ if (class->group != 0 && class->group->authoritative != 0) { errno = 0; write_statements(db_file, class->group->statements, 8); if (errno) return ISC_R_IOERROR; } if (fprintf(db_file, "}\n\n") <= 0) return ISC_R_IOERROR; } if (class->hash != NULL) { /* yep. recursive. god help us. */ /* XXX - cannot check error status of this... * foo_hash_foreach returns a count of operations completed. */ class_hash_foreach(class->hash, write_named_billing_class); } return ISC_R_SUCCESS; } void write_billing_classes () { struct collection *lp; struct class *cp; for (lp = collections; lp; lp = lp -> next) { for (cp = lp -> classes; cp; cp = cp -> nic) { if (cp -> spawning && cp -> hash) { class_hash_foreach (cp -> hash, write_named_billing_class); } } } } /* Write a spawned class to the database file. */ int write_billing_class (class) struct class *class; { int errors = 0; if (lease_file_is_corrupt) if (!new_lease_file (0)) return 0; if (!class -> superclass) { errno = 0; fprintf (db_file, "\n billing class \"%s\";", class -> name); return !errno; } if (fprintf(db_file, "\n billing subclass \"%s\"", class -> superclass -> name) < 0) ++errors; if (!print_hash_string(db_file, class)) ++errors; if (fprintf(db_file, ";") < 0) ++errors; class -> dirty = !errors; if (errors) lease_file_is_corrupt = 1; return !errors; } /* Commit leases after a timeout. */ void commit_leases_timeout (void *foo) { commit_leases (); } /* Commit any leases that have been written out... */ int commit_leases () { /* Commit any outstanding writes to the lease database file. We need to do this even if we're rewriting the file below, just in case the rewrite fails. */ if (fflush (db_file) == EOF) { log_info("commit_leases: unable to commit, fflush(): %m"); return (0); } if ((dont_use_fsync == 0) && (fsync(fileno (db_file)) < 0)) { log_info ("commit_leases: unable to commit, fsync(): %m"); return (0); } /* If we haven't rewritten the lease database in over an hour, rewrite it now. (The length of time should probably be configurable. */ if (count && cur_time - write_time > LEASE_REWRITE_PERIOD) { count = 0; write_time = cur_time; new_lease_file(0); } return (1); } /* * rewrite the lease file about once an hour * This is meant as a quick patch for ticket 24887. It allows * us to rotate the v6 lease file without adding too many fsync() * calls. In the future wes should revisit this area and add * something similar to the delayed ack code for v4. */ int commit_leases_timed() { if ((count != 0) && (cur_time - write_time > LEASE_REWRITE_PERIOD)) { return (commit_leases()); } return (1); } void db_startup (int test_mode) { const char *current_db_path; isc_result_t status; #if defined (TRACING) if (!trace_playback ()) { #endif /* Unset authoring_byte_order so we'll know if it was specified in the lease file or not. */ authoring_byte_order = 0; /* Read in the existing lease file... */ status = read_conf_file (path_dhcpd_db, (struct group *)0, 0, 1); if (status != ISC_R_SUCCESS) { /* XXX ignore status? */ ; } #if defined (TRACING) } #endif #if defined (TRACING) /* If we're playing back, there is no lease file, so we can't append it, so we create one immediately (maybe this isn't the best solution... */ if (trace_playback ()) { new_lease_file (0); } #endif /* expire_all_pools will cause writes to the "current" lease file. * Therefore, in test mode we need to point db_file to a disposable * file to protect the original lease file. */ current_db_path = (test_mode ? "/dev/null" : path_dhcpd_db); db_file = fopen (current_db_path, "a"); if (!db_file) { log_fatal ("Can't open %s for append.", current_db_path); } expire_all_pools (); #if defined (TRACING) if (trace_playback ()) write_time = cur_time; else #endif time(&write_time); new_lease_file (test_mode); #if defined(REPORT_HASH_PERFORMANCE) log_info("Host HW hash: %s", host_hash_report(host_hw_addr_hash)); log_info("Host UID hash: %s", host_hash_report(host_uid_hash)); log_info("Lease IP hash: %s", lease_ip_hash_report(lease_ip_addr_hash)); log_info("Lease UID hash: %s", lease_id_hash_report(lease_uid_hash)); log_info("Lease HW hash: %s", lease_id_hash_report(lease_hw_addr_hash)); #endif } int new_lease_file (int test_mode) { char newfname [512]; char backfname [512]; TIME t; int db_fd; int db_validity; FILE *new_db_file; /* Make a temporary lease file... */ time(&t); db_validity = lease_file_is_corrupt; /* %Audit% Truncated filename causes panic. %2004.06.17,Safe% * This should never happen since the path is a configuration * variable from build-time or command-line. But if it should, * either by malice or ignorance, we panic, since the potential * for havoc is high. */ if (snprintf (newfname, sizeof newfname, "%s.%d", path_dhcpd_db, (int)t) >= sizeof newfname) log_fatal("new_lease_file: lease file path too long"); db_fd = open (newfname, O_WRONLY | O_TRUNC | O_CREAT, 0664); if (db_fd < 0) { log_error ("Can't create new lease file: %m"); return 0; } #if defined (PARANOIA) /* * If we are currently root and plan to change the * uid and gid change the file information so we * can manipulate it later, after we've changed * our group and user (that is dropped privileges.) */ if ((set_uid != 0) && (geteuid() == 0) && (set_gid != 0) && (getegid() == 0)) { if (fchown(db_fd, set_uid, set_gid)) { log_fatal ("Can't chown new lease file: %m"); } } #endif /* PARANOIA */ if ((new_db_file = fdopen(db_fd, "w")) == NULL) { log_error("Can't fdopen new lease file: %m"); close(db_fd); goto fdfail; } /* Close previous database, if any. */ if (db_file) fclose(db_file); db_file = new_db_file; errno = 0; fprintf (db_file, "# The format of this file is documented in the %s", "dhcpd.leases(5) manual page.\n"); if (errno) goto fail; fprintf (db_file, "# This lease file was written by isc-dhcp-%s\n\n", PACKAGE_VERSION); if (errno) goto fail; fprintf (db_file, "# authoring-byte-order entry is generated," " DO NOT DELETE\n"); if (errno) goto fail; fprintf (db_file, "authoring-byte-order %s;\n\n", (DHCP_BYTE_ORDER == LITTLE_ENDIAN ? "little-endian" : "big-endian")); if (errno) goto fail; /* At this point we have a new lease file that, so far, could not * be described as either corrupt nor valid. */ lease_file_is_corrupt = 0; /* Write out all the leases that we know of... */ counting = 0; if (!write_leases ()) goto fail; if (test_mode) { log_debug("Lease file test successful," " removing temp lease file: %s", newfname); (void)unlink (newfname); return (1); } #if defined (TRACING) if (!trace_playback ()) { #endif /* %Audit% Truncated filename causes panic. %2004.06.17,Safe% * This should never happen since the path is a configuration * variable from build-time or command-line. But if it should, * either by malice or ignorance, we panic, since the potential * for havoc is too high. */ if (snprintf (backfname, sizeof backfname, "%s~", path_dhcpd_db) >= sizeof backfname) log_fatal("new_lease_file: backup lease file path too long"); /* Get the old database out of the way... */ if (unlink (backfname) < 0 && errno != ENOENT) { log_error ("Can't remove old lease database backup %s: %m", backfname); goto fail; } if (link(path_dhcpd_db, backfname) < 0) { if (errno == ENOENT) { log_error("%s is missing - no lease db to backup.", path_dhcpd_db); } else { log_error("Can't backup lease database %s to %s: %m", path_dhcpd_db, backfname); goto fail; } } #if defined (TRACING) } #endif /* Move in the new file... */ if (rename (newfname, path_dhcpd_db) < 0) { log_error ("Can't install new lease database %s to %s: %m", newfname, path_dhcpd_db); goto fail; } counting = 1; return 1; fail: lease_file_is_corrupt = db_validity; fdfail: (void)unlink (newfname); return 0; } int group_writer (struct group_object *group) { if (!write_group (group)) return 0; if (!commit_leases ()) return 0; return 1; } dhcp-4.4.1/server/ddns.c000644 000765 000024 00000215225 13243301226 015324 0ustar00tmarkstaff000000 000000 /* ddns.c Dynamic DNS updates. */ /* * * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 2000-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * * This software has been donated to Internet Systems Consortium * by Damien Neil of Nominum, Inc. * * To learn more about Internet Systems Consortium, see * ``https://www.isc.org/''. */ #include "dhcpd.h" #include char *ddns_standard_tag = "ddns-dhcid"; char *ddns_interim_tag = "ddns-txt"; #ifdef NSUPDATE #if defined (DEBUG_DNS_UPDATES) static char* dump_ddns_cb_func(void *func); static char* dump_ddns_cb (dhcp_ddns_cb_t *ddns_cb); extern struct enumeration_value ddns_styles_values[]; #endif static void ddns_fwd_srv_connector(struct lease *lease, struct iasubopt *lease6, struct binding_scope **inscope, dhcp_ddns_cb_t *ddns_cb, isc_result_t eresult); static void copy_conflict_flags(u_int16_t *target, u_int16_t source); static void ddns_fwd_srv_add3(dhcp_ddns_cb_t *ddns_cb, isc_result_t eresult); /* * ddns_cb_free() is part of common lib, while ia_* routines are known * only in the server. Use this wrapper instead of ddns_cb_free() directly. */ static void destroy_ddns_cb(struct dhcp_ddns_cb *ddns_cb, char* file, int line) { if (!ddns_cb) { return; } if (ddns_cb->fixed6_ia) { ia_dereference(&ddns_cb->fixed6_ia, MDL); } ddns_cb_free(ddns_cb, file, line); } /* DN: No way of checking that there is enough space in a data_string's buffer. Be certain to allocate enough! TL: This is why the expression evaluation code allocates a *new* data_string. :') */ static void data_string_append (struct data_string *ds1, struct data_string *ds2) { memcpy (ds1 -> buffer -> data + ds1 -> len, ds2 -> data, ds2 -> len); ds1 -> len += ds2 -> len; } /* Determine what, if any, forward and reverse updates need to be * performed, and carry them through. */ int ddns_updates(struct packet *packet, struct lease *lease, struct lease *old, struct iasubopt *lease6, struct iasubopt *old6, struct option_state *options) { unsigned long ddns_ttl = DEFAULT_DDNS_TTL; struct data_string ddns_hostname; struct data_string ddns_domainname; struct data_string old_ddns_fwd_name; struct data_string ddns_fwd_name; struct data_string ddns_dhcid; struct binding_scope **scope = NULL; struct data_string d1; struct option_cache *oc; int s1, s2; int result = 0; int server_updates_a = 1; struct buffer *bp = (struct buffer *)0; int ignorep = 0, client_ignorep = 0; int rev_name_len; int i; dhcp_ddns_cb_t *ddns_cb; int do_remove = 0; if ((ddns_update_style != DDNS_UPDATE_STYLE_STANDARD) && (ddns_update_style != DDNS_UPDATE_STYLE_INTERIM)) return (0); /* * sigh, I want to cancel any previous udpates before we do anything * else but this means we need to deal with the lease vs lease6 * question twice. * If there is a ddns request already outstanding cancel it. */ if (lease != NULL) { if ((old != NULL) && (old->ddns_cb != NULL)) { ddns_cancel(old->ddns_cb, MDL); old->ddns_cb = NULL; } } else if (lease6 != NULL) { if ((old6 != NULL) && (old6->ddns_cb != NULL)) { ddns_cancel(old6->ddns_cb, MDL); old6->ddns_cb = NULL; } } else { log_fatal("Impossible condition at %s:%d.", MDL); /* Silence compiler warnings. */ result = 0; return(0); } /* allocate our control block */ ddns_cb = ddns_cb_alloc(MDL); if (ddns_cb == NULL) { return(0); } /* * Assume that we shall update both the A and ptr records and, * as this is an update, set the active flag */ ddns_cb->flags = DDNS_UPDATE_ADDR | DDNS_UPDATE_PTR | DDNS_ACTIVE_LEASE; /* * For v4 we flag static leases so we don't try * and manipulate the lease later. For v6 we don't * get static leases and don't need to flag them. */ if (lease != NULL) { scope = &(lease->scope); ddns_cb->address = lease->ip_addr; if (lease->flags & STATIC_LEASE) ddns_cb->flags |= DDNS_STATIC_LEASE; } else if (lease6 != NULL) { scope = &(lease6->scope); memcpy(ddns_cb->address.iabuf, lease6->addr.s6_addr, 16); ddns_cb->address.len = 16; if (lease6->static_lease) { /* We add a reference to keep ia && iasubopt alive * since static v6s are retained anywhere */ ia_reference(&ddns_cb->fixed6_ia, lease6->ia, MDL); ddns_cb->flags |= DDNS_STATIC_LEASE; } } memset (&d1, 0, sizeof(d1)); memset (&ddns_hostname, 0, sizeof (ddns_hostname)); memset (&ddns_domainname, 0, sizeof (ddns_domainname)); memset (&old_ddns_fwd_name, 0, sizeof (ddns_fwd_name)); memset (&ddns_fwd_name, 0, sizeof (ddns_fwd_name)); memset (&ddns_dhcid, 0, sizeof (ddns_dhcid)); /* If we are allowed to accept the client's update of its own A record, see if the client wants to update its own A record. */ if (!(oc = lookup_option(&server_universe, options, SV_CLIENT_UPDATES)) || evaluate_boolean_option_cache(&client_ignorep, packet, lease, NULL, packet->options, options, scope, oc, MDL)) { /* If there's no fqdn.no-client-update or if it's nonzero, don't try to use the client-supplied XXX */ if (!(oc = lookup_option (&fqdn_universe, packet -> options, FQDN_SERVER_UPDATE)) || evaluate_boolean_option_cache(&ignorep, packet, lease, NULL, packet->options, options, scope, oc, MDL)) goto noclient; /* Win98 and Win2k will happily claim to be willing to update an unqualified domain name. */ if (!(oc = lookup_option (&fqdn_universe, packet -> options, FQDN_DOMAINNAME))) goto noclient; if (!(oc = lookup_option (&fqdn_universe, packet -> options, FQDN_FQDN)) || !evaluate_option_cache(&ddns_fwd_name, packet, lease, NULL, packet->options, options, scope, oc, MDL)) goto noclient; ddns_cb->flags &= ~DDNS_UPDATE_ADDR; server_updates_a = 0; goto client_updates; } noclient: /* If do-forward-updates is disabled, this basically means don't do an update unless the client is participating, so if we get here and do-forward-updates is disabled, we can stop. */ if ((oc = lookup_option (&server_universe, options, SV_DO_FORWARD_UPDATES)) && !evaluate_boolean_option_cache(&ignorep, packet, lease, NULL, packet->options, options, scope, oc, MDL)) { goto out; } /* If it's a static lease, then don't do the DNS update unless we're specifically configured to do so. If the client asked to do its own update and we allowed that, we don't do this test. */ /* XXX: note that we cannot detect static DHCPv6 leases. */ if ((lease != NULL) && (lease->flags & STATIC_LEASE)) { if (!(oc = lookup_option(&server_universe, options, SV_UPDATE_STATIC_LEASES)) || !evaluate_boolean_option_cache(&ignorep, packet, lease, NULL, packet->options, options, scope, oc, MDL)) goto out; } /* * Compute the name for the A record. */ oc = lookup_option(&server_universe, options, SV_DDNS_HOST_NAME); if (oc) s1 = evaluate_option_cache(&ddns_hostname, packet, lease, NULL, packet->options, options, scope, oc, MDL); else s1 = 0; /* If we don't have a host name based on ddns-hostname then use * the host declaration name if there is one and use-host-decl-names * is turned on. */ if ((s1 == 0) && (lease && lease->host && lease->host->name)) { oc = lookup_option(&server_universe, options, SV_USE_HOST_DECL_NAMES); if (evaluate_boolean_option_cache(NULL, packet, lease, NULL, packet->options, options, scope, oc, MDL)) { s1 = ((data_string_new(&ddns_hostname, lease->host->name, strlen(lease->host->name), MDL) && ddns_hostname.len > 0)); } } oc = lookup_option(&server_universe, options, SV_DDNS_DOMAIN_NAME); if (oc) s2 = evaluate_option_cache(&ddns_domainname, packet, lease, NULL, packet->options, options, scope, oc, MDL); else s2 = 0; if (s1 && s2) { if (ddns_hostname.len + ddns_domainname.len > 253) { log_error ("ddns_update: host.domain name too long"); goto out; } if (buffer_allocate (&ddns_fwd_name.buffer, ddns_hostname.len + ddns_domainname.len + 2, MDL)) { ddns_fwd_name.data = ddns_fwd_name.buffer->data; data_string_append (&ddns_fwd_name, &ddns_hostname); ddns_fwd_name.buffer->data[ddns_fwd_name.len] = '.'; ddns_fwd_name.len++; data_string_append (&ddns_fwd_name, &ddns_domainname); ddns_fwd_name.buffer->data[ddns_fwd_name.len] ='\0'; ddns_fwd_name.terminated = 1; } } client_updates: /* See if there's a name already stored on the lease. */ if (find_bound_string(&old_ddns_fwd_name, *scope, "ddns-fwd-name")) { /* If there is, see if it's different. */ if (old_ddns_fwd_name.len != ddns_fwd_name.len || memcmp (old_ddns_fwd_name.data, ddns_fwd_name.data, old_ddns_fwd_name.len)) { /* * If the name is different, mark the old record * for deletion and continue getting the new info. */ do_remove = 1; goto in; } #if defined (DDNS_UPDATE_SLOW_TRANSITION) /* * If the slow transition code is enabled check to see * if the stored type (standard or interim doesn't * match the type currently in use. If it doesn't * try to remove and replace the DNS record */ if (((ddns_update_style == DDNS_UPDATE_STYLE_STANDARD) && find_bound_string(&ddns_dhcid, *scope, ddns_interim_tag)) || ((ddns_update_style == DDNS_UPDATE_STYLE_INTERIM) && find_bound_string(&ddns_dhcid, *scope, ddns_standard_tag))) { data_string_forget(&ddns_dhcid, MDL); do_remove = 1; goto in; } #endif /* See if the administrator wants to do updates even in cases where the update already appears to have been done. */ if (!(oc = lookup_option(&server_universe, options, SV_UPDATE_OPTIMIZATION)) || evaluate_boolean_option_cache(&ignorep, packet, lease, NULL, packet->options, options, scope, oc, MDL)) { result = 1; goto noerror; } /* If there's no "ddns-fwd-name" on the lease record, see if * there's a ddns-client-fqdn indicating a previous client * update (if it changes, we need to adjust the PTR). */ } else if (find_bound_string(&old_ddns_fwd_name, *scope, "ddns-client-fqdn")) { /* If the name is not different, no need to update the PTR record. */ if (old_ddns_fwd_name.len == ddns_fwd_name.len && !memcmp (old_ddns_fwd_name.data, ddns_fwd_name.data, old_ddns_fwd_name.len) && (!(oc = lookup_option(&server_universe, options, SV_UPDATE_OPTIMIZATION)) || evaluate_boolean_option_cache(&ignorep, packet, lease, NULL, packet->options, options, scope, oc, MDL))) { goto noerror; } } in: /* If we don't have a name that the client has been assigned, we can just skip all this. */ if ((!ddns_fwd_name.len) || (ddns_fwd_name.len > 255)) { if (ddns_fwd_name.len > 255) { log_error ("client provided fqdn: too long"); } /* If desired do the removals */ if (do_remove != 0) { (void) ddns_removals(lease, lease6, NULL, ISC_TRUE); } goto out; } /* * Compute the RR TTL. * * We have two ways of computing the TTL. * The old behavior was to allow for the customer to set up * the option or to default things. For v4 this was 1/2 * of the lease time, for v6 this was DEFAULT_DDNS_TTL. * The new behavior continues to allow the customer to set * up an option but the defaults are a little different. * We now use 1/2 of the (preferred) lease time for both * v4 and v6 and cap them at a maximum value. * If the customer chooses to use an experession that references * part of the lease the v6 value will be the default as there * isn't a lease available for v6. */ ddns_ttl = DEFAULT_DDNS_TTL; if (lease != NULL) { if (lease->ends <= cur_time) { ddns_ttl = 0; } else { ddns_ttl = (lease->ends - cur_time)/2; } } #ifndef USE_OLD_DDNS_TTL else if (lease6 != NULL) { ddns_ttl = lease6->prefer/2; } if (ddns_ttl > MAX_DEFAULT_DDNS_TTL) { ddns_ttl = MAX_DEFAULT_DDNS_TTL; } #endif if ((oc = lookup_option(&server_universe, options, SV_DDNS_TTL))) { if (evaluate_option_cache(&d1, packet, lease, NULL, packet->options, options, scope, oc, MDL)) { if (d1.len == sizeof (u_int32_t)) ddns_ttl = getULong (d1.data); data_string_forget (&d1, MDL); } } ddns_cb->ttl = ddns_ttl; /* * Compute the reverse IP name, starting with the domain name. */ oc = lookup_option(&server_universe, options, SV_DDNS_REV_DOMAIN_NAME); if (oc) s1 = evaluate_option_cache(&d1, packet, lease, NULL, packet->options, options, scope, oc, MDL); else s1 = 0; /* * Figure out the length of the part of the name that depends * on the address. */ if (ddns_cb->address.len == 4) { char buf[17]; /* XXX: WOW this is gross. */ rev_name_len = snprintf(buf, sizeof(buf), "%u.%u.%u.%u.", ddns_cb->address.iabuf[3] & 0xff, ddns_cb->address.iabuf[2] & 0xff, ddns_cb->address.iabuf[1] & 0xff, ddns_cb->address.iabuf[0] & 0xff) + 1; if (s1) { rev_name_len += d1.len; if (rev_name_len > 255) { log_error("ddns_update: Calculated rev domain " "name too long."); s1 = 0; data_string_forget(&d1, MDL); } } } else if (ddns_cb->address.len == 16) { /* * IPv6 reverse names are always the same length, with * 32 hex characters separated by dots. */ rev_name_len = sizeof("0.1.2.3.4.5.6.7." "8.9.a.b.c.d.e.f." "0.1.2.3.4.5.6.7." "8.9.a.b.c.d.e.f." "ip6.arpa."); /* Set s1 to make sure we gate into updates. */ s1 = 1; } else { log_fatal("invalid address length %d", ddns_cb->address.len); /* Silence compiler warnings. */ return 0; } /* See if we are configured NOT to do reverse ptr updates */ if ((oc = lookup_option(&server_universe, options, SV_DO_REVERSE_UPDATES)) && !evaluate_boolean_option_cache(&ignorep, packet, lease, NULL, packet->options, options, scope, oc, MDL)) { ddns_cb->flags &= ~DDNS_UPDATE_PTR; } if (s1) { if (buffer_allocate(&ddns_cb->rev_name.buffer, rev_name_len, MDL)) { struct data_string *rname = &ddns_cb->rev_name; rname->data = rname->buffer->data; if (ddns_cb->address.len == 4) { rname->len = sprintf((char *)rname->buffer->data, "%u.%u.%u.%u.", ddns_cb->address.iabuf[3] & 0xff, ddns_cb->address.iabuf[2] & 0xff, ddns_cb->address.iabuf[1] & 0xff, ddns_cb->address.iabuf[0] & 0xff); /* * d1.data may be opaque, garbage bytes, from * user (mis)configuration. */ data_string_append(rname, &d1); rname->buffer->data[rname->len] = '\0'; } else if (ddns_cb->address.len == 16) { char *p = (char *)&rname->buffer->data; unsigned char *a = ddns_cb->address.iabuf + 15; for (i=0; i<16; i++) { sprintf(p, "%x.%x.", (*a & 0xF), ((*a >> 4) & 0xF)); p += 4; a -= 1; } strcat(p, "ip6.arpa."); rname->len = strlen((const char *)rname->data); } rname->terminated = 1; } if (d1.data != NULL) data_string_forget(&d1, MDL); } /* * copy the string now so we can pass it to the dhcid routines * via the ddns_cb pointer */ data_string_copy(&ddns_cb->fwd_name, &ddns_fwd_name, MDL); /* * If we are updating the A record, compute the DHCID value. * We have two options for computing the DHCID value, the older * interim version and the newer standard version. The interim * has some issues but is left as is to avoid compatibility issues. * * We select the type of DHCID to construct and the information to * use for the digest based on 4701 section 3.3 */ if ((ddns_cb->flags & DDNS_UPDATE_ADDR) != 0) { int ddns_type; int ddns_len; if (ddns_update_style == DDNS_UPDATE_STYLE_STANDARD) { /* The standard style */ ddns_cb->lease_tag = ddns_standard_tag; ddns_cb->dhcid_class = dns_rdatatype_dhcid; ddns_cb->other_dhcid_class = dns_rdatatype_txt; ddns_type = 1; ddns_len = 4; } else { /* The older interim style */ ddns_cb->lease_tag = ddns_interim_tag; ddns_cb->dhcid_class = dns_rdatatype_txt; ddns_cb->other_dhcid_class = dns_rdatatype_dhcid; /* for backwards compatibility */ ddns_type = DHO_DHCP_CLIENT_IDENTIFIER; /* IAID incorrectly included */ ddns_len = 0; } if (lease6 != NULL) { if (lease6->ia->iaid_duid.len < ddns_len) goto badfqdn; result = get_dhcid(ddns_cb, 2, lease6->ia->iaid_duid.data + ddns_len, lease6->ia->iaid_duid.len - ddns_len); } else if ((lease != NULL) && (lease->uid != NULL) && (lease->uid_len != 0)) { /* If this is standard check for an RFC 4361 * compliant client identifier */ if ((ddns_update_style == DDNS_UPDATE_STYLE_STANDARD) && (lease->uid[0] == 255)) { if (lease->uid_len < 5) goto badfqdn; result = get_dhcid(ddns_cb, 2, lease->uid + 5, lease->uid_len - 5); } else { result = get_dhcid(ddns_cb, ddns_type, lease->uid, lease->uid_len); } } else if (lease != NULL) result = get_dhcid(ddns_cb, 0, lease->hardware_addr.hbuf, lease->hardware_addr.hlen); else log_fatal("Impossible condition at %s:%d.", MDL); if (!result) goto badfqdn; } /* * Perform updates. */ if (ddns_cb->flags & DDNS_UPDATE_ADDR) { copy_conflict_flags(&ddns_cb->flags, ddns_conflict_mask); } /* * Previously if we failed during the removal operations * we skipped the fqdn option processing. I'm not sure * if we want to continue with that if we fail before sending * the ddns messages. Currently we don't. */ if (do_remove) { /* * We should log a more specific error closer to the actual * error if we want one. ddns_removal failure not logged here. */ (void) ddns_removals(lease, lease6, ddns_cb, ISC_TRUE); } else { ddns_fwd_srv_connector(lease, lease6, scope, ddns_cb, ISC_R_SUCCESS); } ddns_cb = NULL; noerror: /* * If fqdn-reply option is disabled in dhcpd.conf, then don't * send the client an FQDN option at all, even if one was requested. * (WinXP clients allegedly misbehave if the option is present, * refusing to handle PTR updates themselves). */ if ((oc = lookup_option (&server_universe, options, SV_FQDN_REPLY)) && !evaluate_boolean_option_cache(&ignorep, packet, lease, NULL, packet->options, options, scope, oc, MDL)) { goto badfqdn; /* If we're ignoring client updates, then we tell a sort of 'white * lie'. We've already updated the name the server wants (per the * config written by the server admin). Now let the client do as * it pleases with the name they supplied (if any). * * We only form an FQDN option this way if the client supplied an * FQDN option that had FQDN_SERVER_UPDATE set false. */ } else if (client_ignorep && (oc = lookup_option(&fqdn_universe, packet->options, FQDN_SERVER_UPDATE)) && !evaluate_boolean_option_cache(&ignorep, packet, lease, NULL, packet->options, options, scope, oc, MDL)) { oc = lookup_option(&fqdn_universe, packet->options, FQDN_FQDN); if (oc && evaluate_option_cache(&d1, packet, lease, NULL, packet->options, options, scope, oc, MDL)) { if (d1.len == 0 || !buffer_allocate(&bp, d1.len + 5, MDL)) goto badfqdn; /* Server pretends it is not updating. */ bp->data[0] = 0; if (!save_option_buffer(&fqdn_universe, options, bp, &bp->data[0], 1, FQDN_SERVER_UPDATE, 0)) goto badfqdn; /* Client is encouraged to update. */ bp->data[1] = 0; if (!save_option_buffer(&fqdn_universe, options, bp, &bp->data[1], 1, FQDN_NO_CLIENT_UPDATE, 0)) goto badfqdn; /* Use the encoding of client's FQDN option. */ oc = lookup_option(&fqdn_universe, packet->options, FQDN_ENCODED); if (oc && evaluate_boolean_option_cache(&ignorep, packet, lease, NULL, packet->options, options, scope, oc, MDL)) bp->data[2] = 1; /* FQDN is encoded. */ else bp->data[2] = 0; /* FQDN is not encoded. */ if (!save_option_buffer(&fqdn_universe, options, bp, &bp->data[2], 1, FQDN_ENCODED, 0)) goto badfqdn; /* Current FQDN drafts indicate 255 is mandatory. */ bp->data[3] = 255; if (!save_option_buffer(&fqdn_universe, options, bp, &bp->data[3], 1, FQDN_RCODE1, 0)) goto badfqdn; bp->data[4] = 255; if (!save_option_buffer(&fqdn_universe, options, bp, &bp->data[4], 1, FQDN_RCODE2, 0)) goto badfqdn; /* Copy in the FQDN supplied by the client. Note well * that the format of this option in the cache is going * to be in text format. If the fqdn supplied by the * client is encoded, it is decoded into the option * cache when parsed out of the packet. It will be * re-encoded when the option is assembled to be * transmitted if the client elects that encoding. */ memcpy(&bp->data[5], d1.data, d1.len); if (!save_option_buffer(&fqdn_universe, options, bp, &bp->data[5], d1.len, FQDN_FQDN, 0)) goto badfqdn; data_string_forget(&d1, MDL); } /* Set up the outgoing FQDN option if there was an incoming * FQDN option. If there's a valid FQDN option, there MUST * be an FQDN_SERVER_UPDATES suboption, it's part of the fixed * length head of the option contents, so we test the latter * to detect the presence of the former. */ } else if ((oc = lookup_option(&fqdn_universe, packet->options, FQDN_ENCODED)) && buffer_allocate(&bp, ddns_fwd_name.len + 5, MDL)) { bp -> data [0] = server_updates_a; if (!save_option_buffer(&fqdn_universe, options, bp, &bp->data [0], 1, FQDN_SERVER_UPDATE, 0)) goto badfqdn; bp -> data [1] = server_updates_a; if (!save_option_buffer(&fqdn_universe, options, bp, &bp->data [1], 1, FQDN_NO_CLIENT_UPDATE, 0)) goto badfqdn; /* Do the same encoding the client did. */ if (evaluate_boolean_option_cache(&ignorep, packet, lease, NULL, packet->options, options, scope, oc, MDL)) bp -> data [2] = 1; else bp -> data [2] = 0; if (!save_option_buffer(&fqdn_universe, options, bp, &bp->data [2], 1, FQDN_ENCODED, 0)) goto badfqdn; bp -> data [3] = 255;//isc_rcode_to_ns (rcode1); if (!save_option_buffer(&fqdn_universe, options, bp, &bp->data [3], 1, FQDN_RCODE1, 0)) goto badfqdn; bp -> data [4] = 255;//isc_rcode_to_ns (rcode2); if (!save_option_buffer(&fqdn_universe, options, bp, &bp->data [4], 1, FQDN_RCODE2, 0)) goto badfqdn; if (ddns_fwd_name.len) { memcpy (&bp -> data [5], ddns_fwd_name.data, ddns_fwd_name.len); if (!save_option_buffer(&fqdn_universe, options, bp, &bp->data [5], ddns_fwd_name.len, FQDN_FQDN, 0)) goto badfqdn; } } badfqdn: out: /* * Final cleanup. */ if (ddns_cb != NULL) { destroy_ddns_cb(ddns_cb, MDL); } data_string_forget(&d1, MDL); data_string_forget(&ddns_hostname, MDL); data_string_forget(&ddns_domainname, MDL); data_string_forget(&old_ddns_fwd_name, MDL); data_string_forget(&ddns_fwd_name, MDL); if (bp) buffer_dereference(&bp, MDL); return result; } /*%< * Utility function to update text strings within a lease. * * The first issue is to find the proper scope. Sometimes we shall be * called with a pointer to the scope in other cases we need to find * the proper lease and then get the scope. Once we have the scope we update * the proper strings, as indicated by the state value in the control block. * Lastly, if we needed to find the scope we write it out, if we used a * scope that was passed as an argument we don't write it, assuming that * our caller (or his ...) will do the write. * *\li ddns_cb - the control block for the DDNS request * *\li inscope - a pointer to the scope to update. This may be NULL * in which case we use the control block to find the lease and * then the scope. * * Returns *\li ISC_R_SUCCESS * *\li ISC_R_FAILURE - The routine was unable to find an expected scope. * In some cases (static and inactive leases) we don't expect a scope * and return success. */ isc_result_t ddns_update_lease_text(dhcp_ddns_cb_t *ddns_cb, struct binding_scope **inscope) { struct binding_scope **scope = NULL; struct lease *lease = NULL; struct iasubopt *lease6 = NULL; struct ipv6_pool *pool = NULL; struct in6_addr addr; struct data_string lease_dhcid; /* * If the lease was static (for a fixed address) * we don't need to do any work. */ if (ddns_cb->flags & DDNS_STATIC_LEASE) return (ISC_R_SUCCESS); /* * If we are processing an expired or released v6 lease * or some types of v4 leases we don't actually have a * scope to update */ if ((ddns_cb->flags & DDNS_ACTIVE_LEASE) == 0) return (ISC_R_SUCCESS); if (inscope != NULL) { scope = inscope; } else if (ddns_cb->address.len == 4) { if (find_lease_by_ip_addr(&lease, ddns_cb->address, MDL) != 0){ scope = &(lease->scope); } } else if (ddns_cb->address.len == 16) { memcpy(&addr, &ddns_cb->address.iabuf, 16); if ((find_ipv6_pool(&pool, D6O_IA_TA, &addr) == ISC_R_SUCCESS) || (find_ipv6_pool(&pool, D6O_IA_NA, &addr) == ISC_R_SUCCESS)) { if (iasubopt_hash_lookup(&lease6, pool->leases, &addr, 16, MDL)) { scope = &(lease6->scope); } ipv6_pool_dereference(&pool, MDL); } } else { log_fatal("Impossible condition at %s:%d.", MDL); } if (scope == NULL) { /* If necessary get rid of the lease */ if (lease) { lease_dereference(&lease, MDL); } else if (lease6) { iasubopt_dereference(&lease6, MDL); } return(ISC_R_FAILURE); } /* We now have a scope and can proceed to update it */ switch(ddns_cb->state) { case DDNS_STATE_REM_PTR: unset(*scope, "ddns-rev-name"); if ((ddns_cb->flags & DDNS_CLIENT_DID_UPDATE) != 0) { unset(*scope, "ddns-client-fqdn"); } break; case DDNS_STATE_ADD_PTR: case DDNS_STATE_CLEANUP: bind_ds_value(scope, "ddns-rev-name", &ddns_cb->rev_name); if ((ddns_cb->flags & DDNS_UPDATE_ADDR) == 0) { bind_ds_value(scope, "ddns-client-fqdn", &ddns_cb->fwd_name); } break; case DDNS_STATE_ADD_FW_YXDHCID: case DDNS_STATE_ADD_FW_NXDOMAIN: case DDNS_STATE_DSMM_FW_ADD3: bind_ds_value(scope, "ddns-fwd-name", &ddns_cb->fwd_name); if (ddns_cb->lease_tag == ddns_standard_tag) { bind_ds_value(scope, ddns_standard_tag, &ddns_cb->dhcid); } else { /* convert from dns version to lease version of dhcid */ memset(&lease_dhcid, 0, sizeof(lease_dhcid)); dhcid_tolease(&ddns_cb->dhcid, &lease_dhcid); bind_ds_value(scope, ddns_interim_tag, &lease_dhcid); data_string_forget(&lease_dhcid, MDL); } break; case DDNS_STATE_REM_FW_NXRR: case DDNS_STATE_REM_FW_YXDHCID: case DDNS_STATE_REM_FW_DSMM_OTHER: unset(*scope, "ddns-fwd-name"); unset(*scope, ddns_cb->lease_tag); break; } /* If necessary write it out and get rid of the lease */ if (lease) { write_lease(lease); lease_dereference(&lease, MDL); } else if (lease6) { write_ia(lease6->ia); iasubopt_dereference(&lease6, MDL); } return(ISC_R_SUCCESS); } /* * This function should be called when update_lease_ptr function fails. * It does inform user about the condition, provides some hints how to * resolve this and dies gracefully. This can happend in at least three * cases (all are configuration mistakes): * a) IPv4: user have duplicate fixed-address entries (the same * address is defined twice). We may have found wrong lease. * b) IPv6: user have overlapping pools (we tried to find * a lease in a wrong pool) * c) IPv6: user have duplicate fixed-address6 entires (the same * address is defined twice). We may have found wrong lease. * * Comment: while it would be possible to recover from both cases * by forcibly searching for leases in *all* following pools, that would * only hide the real problem - a misconfiguration. Proper solution * is to log the problem, die and let the user fix his config file. */ void update_lease_failed(struct lease *lease, struct iasubopt *lease6, dhcp_ddns_cb_t *ddns_cb, dhcp_ddns_cb_t *ddns_cb_set, const char * file, int line) { char lease_address[MAX_ADDRESS_STRING_LEN + 64]; char reason[128]; /* likely reason */ sprintf(reason, "unknown"); sprintf(lease_address, "unknown"); /* * let's pretend that everything is ok, so we can continue for * information gathering purposes */ if (ddns_cb != NULL) { strncpy(lease_address, piaddr(ddns_cb->address), MAX_ADDRESS_STRING_LEN); if (ddns_cb->address.len == 4) { sprintf(reason, "duplicate IPv4 fixed-address entry"); } else if (ddns_cb->address.len == 16) { sprintf(reason, "duplicate IPv6 fixed-address6 entry " "or overlapping pools"); } else { /* * Should not happen. We have non-IPv4, non-IPv6 * address. Something is very wrong here. */ sprintf(reason, "corrupted ddns_cb structure (address " "length is %d)", ddns_cb->address.len); } } log_error("Failed to properly update internal lease structure with " "DDNS"); log_error("control block structures. Tried to update lease for" "%s address, ddns_cb=%p.", lease_address, ddns_cb); log_error("%s", ""); log_error("This condition can occur, if DHCP server configuration is " "inconsistent."); log_error("In particular, please do check that your configuration:"); log_error("a) does not have overlapping pools (especially containing"); log_error(" %s address).", lease_address); log_error("b) there are no duplicate fixed-address or fixed-address6"); log_error("entries for the %s address.", lease_address); log_error("%s", ""); log_error("Possible reason for this failure: %s", reason); log_fatal("%s(%d): Failed to update lease database with DDNS info for " "address %s. Lease database inconsistent. Unable to recover." " Terminating.", file, line, lease_address); } /* * utility function to update found lease. It does extra checks * that we are indeed updating the right lease. It may happen * that user have duplicate fixed-address entries, so we attempt * to update wrong lease. See also safe_lease6_update. */ void safe_lease_update(struct lease *lease, dhcp_ddns_cb_t *oldcb, dhcp_ddns_cb_t *newcb, const char *file, int line) { if (lease == NULL) { /* should never get here */ log_fatal("Impossible condition at %s:%d (called from %s:%d).", MDL, file, line); } if ( (lease->ddns_cb == NULL) && (newcb == NULL) ) { /* * Trying to clean up pointer that is already null. We * are most likely trying to update wrong lease here. */ /* * Previously this error message popped out during * DNS update for fixed leases. As we no longer * try to update the lease for a fixed (static) lease * this should not be a problem. */ log_error("%s(%d): Invalid lease update. Tried to " "clear already NULL DDNS control block " "pointer for lease %s.", file, line, piaddr(lease->ip_addr) ); #if defined (DNS_UPDATES_MEMORY_CHECKS) update_lease_failed(lease, NULL, oldcb, newcb, file, line); #endif /* * May not reach this: update_lease_failed calls * log_fatal. */ return; } if ( (lease->ddns_cb != NULL) && (lease->ddns_cb != oldcb) ) { /* * There is existing cb structure, but it differs from * what we expected to see there. Most likely we are * trying to update wrong lease. */ log_error("%s(%d): Failed to update internal lease " "structure with DDNS control block. Existing" " ddns_cb structure does not match " "expectations.IPv4=%s, old ddns_cb=%p, tried" "to update to new ddns_cb=%p", file, line, piaddr(lease->ip_addr), oldcb, newcb); #if defined (DNS_UPDATES_MEMORY_CHECKS) update_lease_failed(lease, NULL, oldcb, newcb, file, line); #endif /* * May not reach this: update_lease_failed calls * log_fatal. */ return; } /* additional IPv4 specific checks may be added here */ /* update the lease */ lease->ddns_cb = newcb; } void safe_lease6_update(struct iasubopt *lease6, dhcp_ddns_cb_t *oldcb, dhcp_ddns_cb_t *newcb, const char *file, int line) { char addrbuf[MAX_ADDRESS_STRING_LEN]; if (lease6 == NULL) { /* should never get here */ log_fatal("Impossible condition at %s:%d (called from %s:%d).", MDL, file, line); } if ( (lease6->ddns_cb == NULL) && (newcb == NULL) ) { inet_ntop(AF_INET6, &lease6->addr, addrbuf, MAX_ADDRESS_STRING_LEN); /* * Trying to clean up pointer that is already null. We * are most likely trying to update wrong lease here. */ log_error("%s(%d): Failed to update internal lease " "structure. Tried to clear already NULL " "DDNS control block pointer for lease %s.", file, line, addrbuf); #if defined (DNS_UPDATES_MEMORY_CHECKS) update_lease_failed(NULL, lease6, oldcb, newcb, file, line); #endif /* * May not reach this: update_lease_failed calls * log_fatal. */ return; } if ( (lease6->ddns_cb != NULL) && (lease6->ddns_cb != oldcb) ) { /* * there is existing cb structure, but it differs from * what we expected to see there. Most likely we are * trying to update wrong lease. */ inet_ntop(AF_INET6, &lease6->addr, addrbuf, MAX_ADDRESS_STRING_LEN); log_error("%s(%d): Failed to update internal lease " "structure with DDNS control block. Existing" " ddns_cb structure does not match " "expectations.IPv6=%s, old ddns_cb=%p, tried" "to update to new ddns_cb=%p", file, line, addrbuf, oldcb, newcb); #if defined (DNS_UPDATES_MEMORY_CHECKS) update_lease_failed(NULL, lease6, oldcb, newcb, file, line); #endif /* * May not reach this: update_lease_failed calls * log_fatal. */ return; } /* additional IPv6 specific checks may be added here */ /* update the lease */ lease6->ddns_cb = newcb; } /* * Utility function to update the pointer to the DDNS control block * in a lease. * SUCCESS - able to update the pointer * FAILURE - lease didn't exist or sanity checks failed * lease and lease6 may be empty in which case we attempt to find * the lease from the ddns_cb information. * ddns_cb is the control block to use if a lookup is necessary * ddns_cb_set is the pointer to insert into the lease and may be NULL * The last two arguments may look odd as they will be the same much of the * time, but I need an argument to tell me if I'm setting or clearing in * addition to the address information from the cb to look up the lease. * using the same value twice allows me more flexibility. */ isc_result_t ddns_update_lease_ptr(struct lease *lease, struct iasubopt *lease6, dhcp_ddns_cb_t *ddns_cb, dhcp_ddns_cb_t *ddns_cb_set, const char * file, int line) { char ddns_address[MAX_ADDRESS_STRING_LEN]; sprintf(ddns_address, "unknown"); if (ddns_cb == NULL) { log_info("%s(%d): No control block for lease update", file, line); return (ISC_R_FAILURE); } else { strcpy(ddns_address, piaddr(ddns_cb->address)); } #if defined (DEBUG_DNS_UPDATES) log_info("%s(%d): Updating lease_ptr for ddns_cp=%p (addr=%s)", file, line, ddns_cb, ddns_address ); #endif /* * If the lease was static (for a fixed address) * we don't need to do any work. */ if (ddns_cb->flags & DDNS_STATIC_LEASE) { #if defined (DEBUG_DNS_UPDATES) log_info("lease is static, returning"); #endif return (ISC_R_SUCCESS); } /* * If we are processing an expired or released v6 lease * we don't actually have a lease to update */ if ((ddns_cb->address.len == 16) && ((ddns_cb->flags & DDNS_ACTIVE_LEASE) == 0)) { return (ISC_R_SUCCESS); } if (lease != NULL) { safe_lease_update(lease, ddns_cb, ddns_cb_set, file, line); } else if (lease6 != NULL) { safe_lease6_update(lease6, ddns_cb, ddns_cb_set, file, line); } else if (ddns_cb->address.len == 4) { struct lease *find_lease = NULL; if (find_lease_by_ip_addr(&find_lease, ddns_cb->address, MDL) != 0) { #if defined (DEBUG_DNS_UPDATES) log_info("%s(%d): find_lease_by_ip_addr(%s) successful:" "lease=%p", file, line, ddns_address, find_lease); #endif safe_lease_update(find_lease, ddns_cb, ddns_cb_set, file, line); lease_dereference(&find_lease, MDL); } else { log_error("%s(%d): ddns_update_lease_ptr failed. " "Lease for %s not found.", file, line, piaddr(ddns_cb->address)); #if defined (DNS_UPDATES_MEMORY_CHECKS) update_lease_failed(NULL, NULL, ddns_cb, ddns_cb_set, file, line); #endif /* * may not reach this. update_lease_failed * calls log_fatal. */ return(ISC_R_FAILURE); } } else if (ddns_cb->address.len == 16) { struct iasubopt *find_lease6 = NULL; struct ipv6_pool *pool = NULL; struct in6_addr addr; char addrbuf[MAX_ADDRESS_STRING_LEN]; memcpy(&addr, &ddns_cb->address.iabuf, 16); if ((find_ipv6_pool(&pool, D6O_IA_TA, &addr) != ISC_R_SUCCESS) && (find_ipv6_pool(&pool, D6O_IA_NA, &addr) != ISC_R_SUCCESS)) { inet_ntop(AF_INET6, &addr, addrbuf, MAX_ADDRESS_STRING_LEN); log_error("%s(%d): Pool for lease %s not found.", file, line, addrbuf); #if defined (DNS_UPDATES_MEMORY_CHECKS) update_lease_failed(NULL, NULL, ddns_cb, ddns_cb_set, file, line); #endif /* * never reached. update_lease_failed * calls log_fatal. */ return(ISC_R_FAILURE); } if (iasubopt_hash_lookup(&find_lease6, pool->leases, &addr, 16, MDL)) { find_lease6->ddns_cb = ddns_cb_set; iasubopt_dereference(&find_lease6, MDL); } else { inet_ntop(AF_INET6, &addr, addrbuf, MAX_ADDRESS_STRING_LEN); log_error("%s(%d): Lease %s not found within pool.", file, line, addrbuf); #if defined (DNS_UPDATES_MEMORY_CHECKS) update_lease_failed(NULL, NULL, ddns_cb, ddns_cb_set, file, line); #endif /* * never reached. update_lease_failed * calls log_fatal. */ return(ISC_R_FAILURE); } ipv6_pool_dereference(&pool, MDL); } else { /* shouldn't get here */ log_fatal("Impossible condition at %s:%d, called from %s:%d.", MDL, file, line); } return(ISC_R_SUCCESS); } void ddns_ptr_add(dhcp_ddns_cb_t *ddns_cb, isc_result_t eresult) { if (eresult == ISC_R_SUCCESS) { log_info("Added reverse map from %.*s to %.*s", (int)ddns_cb->rev_name.len, (const char *)ddns_cb->rev_name.data, (int)ddns_cb->fwd_name.len, (const char *)ddns_cb->fwd_name.data); ddns_update_lease_text(ddns_cb, NULL); } else { log_error("Unable to add reverse map from %.*s to %.*s: %s", (int)ddns_cb->rev_name.len, (const char *)ddns_cb->rev_name.data, (int)ddns_cb->fwd_name.len, (const char *)ddns_cb->fwd_name.data, isc_result_totext (eresult)); } ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL); destroy_ddns_cb(ddns_cb, MDL); /* * A single DDNS operation may require several calls depending on * the current state as the prerequisites for the first message * may not succeed requiring a second operation and potentially * a ptr operation after that. The commit_leases operation is * invoked at the end of this set of operations in order to require * a single write for all of the changes. We call commit_leases * here rather than immediately after the call to update the lease * text in order to save any previously written data. */ commit_leases(); return; } /* * action routine when trying to remove a pointer * this will be called after the ddns queries have completed * if we succeeded in removing the pointer we go to the next step (if any) * if not we cleanup and leave. */ void ddns_ptr_remove(dhcp_ddns_cb_t *ddns_cb, isc_result_t eresult) { isc_result_t result = eresult; switch(eresult) { case ISC_R_SUCCESS: log_info("Removed reverse map on %.*s", (int)ddns_cb->rev_name.len, (const char *)ddns_cb->rev_name.data); /* fall through */ case DNS_R_NXRRSET: case DNS_R_NXDOMAIN: /* No entry is the same as success. * Remove the information from the lease and * continue with any next step */ ddns_update_lease_text(ddns_cb, NULL); /* trigger any add operation */ result = ISC_R_SUCCESS; #if defined (DEBUG_DNS_UPDATES) log_info("DDNS: removed map or no reverse map to remove %.*s", (int)ddns_cb->rev_name.len, (const char *)ddns_cb->rev_name.data); #endif break; default: log_error("Can't remove reverse map on %.*s: %s", (int)ddns_cb->rev_name.len, (const char *)ddns_cb->rev_name.data, isc_result_totext (eresult)); break; } /* If we aren't suppossed to do the next step, set the result * flag so ddns_fwd_srv_connector won't do much */ if ((ddns_cb->flags & DDNS_EXECUTE_NEXT) == 0) result = ISC_R_FAILURE; ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL); ddns_fwd_srv_connector(NULL, NULL, NULL, ddns_cb->next_op, result); destroy_ddns_cb(ddns_cb, MDL); return; } /* * If the first query succeeds, the updater can conclude that it * has added a new name whose only RRs are the A and DHCID RR records. * The A RR update is now complete (and a client updater is finished, * while a server might proceed to perform a PTR RR update). * -- "Interaction between DHCP and DNS" * * If the second query succeeds, the updater can conclude that the current * client was the last client associated with the domain name, and that * the name now contains the updated A RR. The A RR update is now * complete (and a client updater is finished, while a server would * then proceed to perform a PTR RR update). * -- "Interaction between DHCP and DNS" * * If the second query fails with NXRRSET, the updater must conclude * that the client's desired name is in use by another host. If * Dual Stack Mixed Mode (DSMM) is enabled and we proceed to a * third stage forward update attempt specific to DSMM rules. If not, * then the existing entries are left intact: * * At this juncture, the updater can decide (based on some administrative * configuration outside of the scope of this document) whether to let * the existing owner of the name keep that name, and to (possibly) * perform some name disambiguation operation on behalf of the current * client, or to replace the RRs on the name with RRs that represent * the current client. If the configured policy allows replacement of * existing records, the updater submits a query that deletes the * existing A RR and the existing DHCID RR, adding A and DHCID RRs that * represent the IP address and client-identity of the new client. * -- "Interaction between DHCP and DNS" */ void ddns_fwd_srv_add2(dhcp_ddns_cb_t *ddns_cb, isc_result_t eresult) { isc_result_t result; const char *logstr = NULL; char ddns_address[MAX_ADDRESS_STRING_LEN]; #if defined (DEBUG_DNS_UPDATES) log_info ("DDNS:ddns_fwd_srv_add2: %s eresult: %d", dump_ddns_cb(ddns_cb), eresult); #endif /* Construct a printable form of the address for logging */ strcpy(ddns_address, piaddr(ddns_cb->address)); switch(eresult) { case ISC_R_SUCCESS: log_info("Added new forward map from %.*s to %s", (int)ddns_cb->fwd_name.len, (const char *)ddns_cb->fwd_name.data, ddns_address); ddns_update_lease_text(ddns_cb, NULL); if ((ddns_cb->flags & DDNS_UPDATE_PTR) != 0) { /* if we have zone information get rid of it */ if (ddns_cb->zone != NULL) { ddns_cb_forget_zone(ddns_cb); } ddns_cb->state = DDNS_STATE_ADD_PTR; ddns_cb->cur_func = ddns_ptr_add; result = ddns_modify_ptr(ddns_cb, MDL); if (result == ISC_R_SUCCESS) { return; } } break; case DNS_R_YXRRSET: case DNS_R_YXDOMAIN: logstr = "DHCID mismatch, belongs to another client."; break; case DNS_R_NXDOMAIN: case DNS_R_NXRRSET: /* If DSMM is on we need to try forward add3 */ if (ddns_cb->flags & DDNS_DUAL_STACK_MIXED_MODE) { ddns_cb->state = DDNS_STATE_DSMM_FW_ADD3; ddns_cb->cur_func = ddns_fwd_srv_add3; result = ddns_modify_fwd(ddns_cb, MDL); if (result == ISC_R_SUCCESS) { return; } break; } logstr = "Has an address record but no DHCID, not mine."; break; default: logstr = isc_result_totext(eresult); break; } if (logstr != NULL) { log_error("Forward map from %.*s to %s FAILED: %s", (int)ddns_cb->fwd_name.len, (const char *)ddns_cb->fwd_name.data, ddns_address, logstr); } ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL); destroy_ddns_cb(ddns_cb, MDL); /* * A single DDNS operation may require several calls depending on * the current state as the prerequisites for the first message * may not succeed requiring a second operation and potentially * a ptr operation after that. The commit_leases operation is * invoked at the end of this set of operations in order to require * a single write for all of the changes. We call commit_leases * here rather than immediately after the call to update the lease * text in order to save any previously written data. */ commit_leases(); return; } void ddns_fwd_srv_add1(dhcp_ddns_cb_t *ddns_cb, isc_result_t eresult) { isc_result_t result; char ddns_address[MAX_ADDRESS_STRING_LEN]; #if defined (DEBUG_DNS_UPDATES) log_info ("DDNS: ddns_fwd_srv_add1: %s eresult: %d", dump_ddns_cb(ddns_cb), eresult); #endif /* Construct a printable form of the address for logging */ strcpy(ddns_address, piaddr(ddns_cb->address)); switch(eresult) { case ISC_R_SUCCESS: log_info ("Added new forward map from %.*s to %s", (int)ddns_cb->fwd_name.len, (const char *)ddns_cb->fwd_name.data, ddns_address); ddns_update_lease_text(ddns_cb, NULL); if ((ddns_cb->flags & DDNS_UPDATE_PTR) != 0) { /* if we have zone information get rid of it */ if (ddns_cb->zone != NULL) { ddns_cb_forget_zone(ddns_cb); } ddns_cb->state = DDNS_STATE_ADD_PTR; ddns_cb->cur_func = ddns_ptr_add; result = ddns_modify_ptr(ddns_cb, MDL); if (result == ISC_R_SUCCESS) { return; } } break; case DNS_R_YXDOMAIN: /* we can reuse the zone information */ ddns_cb->state = DDNS_STATE_ADD_FW_YXDHCID; ddns_cb->cur_func = ddns_fwd_srv_add2; result = ddns_modify_fwd(ddns_cb, MDL); if (result == ISC_R_SUCCESS) { return; } break; default: log_error ("Unable to add forward map from %.*s to %s: %s", (int)ddns_cb->fwd_name.len, (const char *)ddns_cb->fwd_name.data, ddns_address, isc_result_totext (eresult)); break; } ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL); destroy_ddns_cb(ddns_cb, MDL); /* * A single DDNS operation may require several calls depending on * the current state as the prerequisites for the first message * may not succeed requiring a second operation and potentially * a ptr operation after that. The commit_leases operation is * invoked at the end of this set of operations in order to require * a single write for all of the changes. We call commit_leases * here rather than immediately after the call to update the lease * text in order to save any previously written data. */ commit_leases(); return; } /* * This action routine is invoked after the DSMM third add stage is * attempted. If we succeeded we attempt to update the reverse DNS, * if not we cleanup and leave. */ void ddns_fwd_srv_add3(dhcp_ddns_cb_t *ddns_cb, isc_result_t eresult) { isc_result_t result; const char *logstr = NULL; char ddns_address[MAX_ADDRESS_STRING_LEN+1]; #if defined (DEBUG_DNS_UPDATES) log_info ("DDNS: ddns_fwd_srv_add3: %s eresult: %d", dump_ddns_cb(ddns_cb), eresult); #endif /* Construct a printable form of the address for logging */ memset(ddns_address, 0x0, sizeof(ddns_address)); strncpy(ddns_address, piaddr(ddns_cb->address), sizeof(ddns_address) - 1); switch(eresult) { case ISC_R_SUCCESS: log_info("Added new forward map from %.*s to %s", (int)ddns_cb->fwd_name.len, (const char *)ddns_cb->fwd_name.data, ddns_address); ddns_update_lease_text(ddns_cb, NULL); if ((ddns_cb->flags & DDNS_UPDATE_PTR) != 0) { /* if we have zone information get rid of it */ if (ddns_cb->zone != NULL) { ddns_cb_forget_zone(ddns_cb); } ddns_cb->state = DDNS_STATE_ADD_PTR; ddns_cb->cur_func = ddns_ptr_add; result = ddns_modify_ptr(ddns_cb, MDL); if (result == ISC_R_SUCCESS) { return; } } break; case DNS_R_YXRRSET: logstr = "an entry that is either static or " "owned by another client exists."; break; case DNS_R_NXRRSET: logstr = "static entry of the other protocol type exists."; break; default: logstr = isc_result_totext(eresult); break; } if (logstr != NULL) { log_error("Forward map from %.*s to %s FAILED: %s", (int)ddns_cb->fwd_name.len, (const char *)ddns_cb->fwd_name.data, ddns_address, logstr); } ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL); destroy_ddns_cb(ddns_cb, MDL); /* * A single DDNS operation may require several calls depending on * the current state as the prerequisites for the first message * may not succeed requiring a second operation and potentially * a ptr operation after that. The commit_leases operation is * invoked at the end of this set of operations in order to require * a single write for all of the changes. We call commit_leases * here rather than immediately after the call to update the lease * text in order to save any previously written data. */ commit_leases(); return; } static void ddns_fwd_srv_connector(struct lease *lease, struct iasubopt *lease6, struct binding_scope **inscope, dhcp_ddns_cb_t *ddns_cb, isc_result_t eresult) { isc_result_t result = ISC_R_FAILURE; #if defined (DEBUG_DNS_UPDATES) log_info ("DDNS: ddns_fwd_srv_connector: %s eresult: %d", dump_ddns_cb(ddns_cb), eresult); #endif if (ddns_cb == NULL) { /* nothing to do */ return; } if (eresult == ISC_R_SUCCESS) { /* * If we have updates dispatch as appropriate, * if not do FQDN binding if desired. */ if (ddns_cb->flags & DDNS_UPDATE_ADDR) { ddns_cb->state = DDNS_STATE_ADD_FW_NXDOMAIN; ddns_cb->cur_func = ddns_fwd_srv_add1; result = ddns_modify_fwd(ddns_cb, MDL); } else if ((ddns_cb->flags & DDNS_UPDATE_PTR) && (ddns_cb->rev_name.len != 0)) { ddns_cb->state = DDNS_STATE_ADD_PTR; ddns_cb->cur_func = ddns_ptr_add; result = ddns_modify_ptr(ddns_cb, MDL); } else { ddns_update_lease_text(ddns_cb, inscope); } } if (result == ISC_R_SUCCESS) { ddns_update_lease_ptr(lease, lease6, ddns_cb, ddns_cb, MDL); } else { destroy_ddns_cb(ddns_cb, MDL); } return; } /* * If the first query fails, the updater MUST NOT delete the DNS name. It * may be that the host whose lease on the server has expired has moved * to another network and obtained a lease from a different server, * which has caused the client's A RR to be replaced. It may also be * that some other client has been configured with a name that matches * the name of the DHCP client, and the policy was that the last client * to specify the name would get the name. In this case, the DHCID RR * will no longer match the updater's notion of the client-identity of * the host pointed to by the DNS name. * -- "Interaction between DHCP and DNS" */ void ddns_fwd_srv_rem2(dhcp_ddns_cb_t *ddns_cb, isc_result_t eresult) { #if defined (DEBUG_DNS_UPDATES) log_info ("DDNS: ddns_fwd_srv_rem2: %s eresult: %d", dump_ddns_cb(ddns_cb), eresult); #endif /* * To get here we have already managed to remove the A/AAAA * record and are trying to remove the DHCID/TXT record as well. * On success (removed DHCID/TXT) or YXRRSET (DHCID/TXT still in * use by something else) we clean up the lease. * On some other error we don't clean up the lease and hope that * if we try this again it will work. An example would be if we * got a timeout as the DNS server halted between the first and * second steps. The DNS server would still have the DHCID/TXT * and we would like to remove that in the future. * * On success set the EXECUTE_NEXT flag which triggers any * add that is next in the chain. */ if ((eresult == ISC_R_SUCCESS) || (eresult == DNS_R_YXRRSET)) { ddns_update_lease_text(ddns_cb, NULL); eresult = ISC_R_SUCCESS; } /* Do the next operation */ if ((ddns_cb->flags & DDNS_UPDATE_PTR) != 0) { /* if we have zone information get rid of it */ if (ddns_cb->zone != NULL) { ddns_cb_forget_zone(ddns_cb); } ddns_cb->state = DDNS_STATE_REM_PTR; ddns_cb->cur_func = ddns_ptr_remove; if (eresult == ISC_R_SUCCESS) ddns_cb->flags |= DDNS_EXECUTE_NEXT; eresult = ddns_modify_ptr(ddns_cb, MDL); if (eresult == ISC_R_SUCCESS) { return; } } ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL); ddns_fwd_srv_connector(NULL, NULL, NULL, ddns_cb->next_op, eresult); destroy_ddns_cb(ddns_cb, MDL); return; } /* * First action routine when trying to remove a fwd * this will be called after the ddns queries have completed * if we succeeded in removing the fwd we go to the next step (if any) * if not we cleanup and leave. */ void ddns_fwd_srv_rem1(dhcp_ddns_cb_t *ddns_cb, isc_result_t eresult) { isc_result_t result = eresult; char ddns_address[MAX_ADDRESS_STRING_LEN]; #if defined (DEBUG_DNS_UPDATES) log_info ("DDNS: ddns_fwd_srv_rem1: %s eresult: %d", dump_ddns_cb(ddns_cb), eresult); #endif switch(eresult) { case ISC_R_SUCCESS: /* Construct a printable form of the address for logging */ strcpy(ddns_address, piaddr(ddns_cb->address)); log_info("Removed forward map from %.*s to %s", (int)ddns_cb->fwd_name.len, (const char*)ddns_cb->fwd_name.data, ddns_address); /* Do the second step of the FWD removal */ ddns_cb->state = DDNS_STATE_REM_FW_NXRR; ddns_cb->cur_func = ddns_fwd_srv_rem2; result = ddns_modify_fwd(ddns_cb, MDL); if (result == ISC_R_SUCCESS) { return; } break; case DNS_R_NXRRSET: case DNS_R_NXDOMAIN: /* A result of not found means rem1 did not find a guard of * our * type. From this we assume either there are no address * record(s) of our type to delete or they are unguarded and * therefore presumed to be static. Either way, we're done * unless we're in DSMM and ddns-other-guard-is-dynamic is on. * In which case we need to see if a guard of the other type * exists, which permits us to delete any address records of * our type. */ #define DSMM_OGD (DDNS_DUAL_STACK_MIXED_MODE | \ DDNS_OTHER_GUARD_IS_DYNAMIC) if ((ddns_cb->flags & DSMM_OGD) == DSMM_OGD) { ddns_cb->state = DDNS_STATE_REM_FW_DSMM_OTHER; ddns_cb->cur_func = ddns_fwd_srv_rem2; result = ddns_modify_fwd(ddns_cb, MDL); if (result == ISC_R_SUCCESS) { return; } break; } ddns_update_lease_text(ddns_cb, NULL); #if defined (DEBUG_DNS_UPDATES) log_info("DDNS: no forward map to remove. %p", ddns_cb); #endif /* Trigger the add operation */ eresult = ISC_R_SUCCESS; /* Fall through */ default: /* We do the remove operation in most cases * but we don't want to continue with adding a forward * record if the forward removal had issues so we * check the eresult and set the EXECUTE_NEXT flag on * success. */ /* Do the remove operation */ if ((ddns_cb->flags & DDNS_UPDATE_PTR) != 0) { /* if we have zone information get rid of it */ if (ddns_cb->zone != NULL) { ddns_cb_forget_zone(ddns_cb); } ddns_cb->state = DDNS_STATE_REM_PTR; ddns_cb->cur_func = ddns_ptr_remove; if (eresult == ISC_R_SUCCESS) ddns_cb->flags |= DDNS_EXECUTE_NEXT; result = ddns_modify_ptr(ddns_cb, MDL); if (result == ISC_R_SUCCESS) { return; } } break; } ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL); ddns_fwd_srv_connector(NULL, NULL, NULL, ddns_cb->next_op, eresult); destroy_ddns_cb(ddns_cb, MDL); } /*%< * Remove relevant entries from DNS. * * \li lease - lease to start with if this is for v4 * * \li lease6 - lease to start with if this is for v6 * * \li add_ddns_cb - control block for additional DDNS work. This * is used when the code is going to add a DDNS entry after removing * the current entry. * * \li active - indication about the status of the lease. It is * ISC_TRUE if the lease is still active, and FALSE if the lease * is inactive. This is used to indicate if the lease is inactive or going * to inactive so we can avoid trying to update the lease with cb pointers * and text information if it isn't useful. * * Returns * \li #ISC_R_FAILURE - badness occurred and we weren't able to do what was wanted * \li #ISC_R_SUCCESS - we were able to do stuff but it's in progress * * in both cases any additional block has been passed on to it's handler */ isc_result_t ddns_removals(struct lease *lease, struct iasubopt *lease6, dhcp_ddns_cb_t *add_ddns_cb, isc_boolean_t active) { isc_result_t rcode, execute_add = ISC_R_FAILURE; struct binding_scope **scope = NULL; isc_result_t result = ISC_R_FAILURE; dhcp_ddns_cb_t *ddns_cb = NULL; struct data_string leaseid; #if defined (DEBUG_DNS_UPDATES) log_info ("DDNS: ddns_removals: %s", dump_ddns_cb(add_ddns_cb)); #endif /* * See if we need to cancel an outstanding request. Mostly this is * used to handle the case where this routine is called twice for * the same release or abandon event. * * When called from the dns code as part of an update request * (add_ddns_cb != NULL) any outstanding requests will have already * been cancelled. * * If the new request is just a removal and we have an outstanding * request we have several options: * * - we are doing an update or we are doing a removal and the active * flag has changed from TRUE to FALSE. In these cases we need to * cancel the old request and start the new one. * * - other wise we are doing a removal with the active flag unchanged. * In this case we can let the current removal continue and do not need * to start a new one. If the old request included an update to be * done after the removal we need to kill the update part of the * request. */ if (add_ddns_cb == NULL) { if ((lease != NULL) && (lease->ddns_cb != NULL)) { ddns_cb = lease->ddns_cb; /* * Is the old request an update or did the * the active flag change? */ if (((ddns_cb->state == DDNS_STATE_ADD_PTR) || (ddns_cb->state == DDNS_STATE_ADD_FW_NXDOMAIN) || (ddns_cb->state == DDNS_STATE_ADD_FW_YXDHCID)) || ((active == ISC_FALSE) && ((ddns_cb->flags & DDNS_ACTIVE_LEASE) != 0))) { /* Cancel the current request */ ddns_cancel(lease->ddns_cb, MDL); lease->ddns_cb = NULL; } else { /* Remvoval, check and remove updates */ if (ddns_cb->next_op != NULL) { destroy_ddns_cb(ddns_cb->next_op, MDL); ddns_cb->next_op = NULL; } #if defined (DEBUG_DNS_UPDATES) log_info("DDNS %s(%d): removal already in " "progress new ddns_cb=%p", MDL, ddns_cb); #endif return (ISC_R_SUCCESS); } } else if ((lease6 != NULL) && (lease6->ddns_cb != NULL)) { ddns_cb = lease6->ddns_cb; /* * Is the old request an update or did the * the active flag change? */ if (((ddns_cb->state == DDNS_STATE_ADD_PTR) || (ddns_cb->state == DDNS_STATE_ADD_FW_NXDOMAIN) || (ddns_cb->state == DDNS_STATE_ADD_FW_YXDHCID)) || ((active == ISC_FALSE) && ((ddns_cb->flags & DDNS_ACTIVE_LEASE) != 0))) { /* Cancel the current request */ ddns_cancel(lease6->ddns_cb, MDL); lease6->ddns_cb = NULL; } else { /* Remvoval, check and remove updates */ if (ddns_cb->next_op != NULL) { destroy_ddns_cb(ddns_cb->next_op, MDL); ddns_cb->next_op = NULL; } #if defined (DEBUG_DNS_UPDATES) log_info("DDNS %s(%d): removal already in " "progress new ddns_cb=%p", MDL, ddns_cb); #endif return (ISC_R_SUCCESS); } } ddns_cb = NULL; } /* allocate our control block */ ddns_cb = ddns_cb_alloc(MDL); if (ddns_cb == NULL) { goto cleanup; } /* Set the conflict detection flags based on global configuration */ copy_conflict_flags(&ddns_cb->flags, ddns_conflict_mask); /* * For v4 we flag static leases so we don't try * and manipulate the lease later. For v6 we don't * get static leases and don't need to flag them. */ if (lease != NULL) { scope = &(lease->scope); ddns_cb->address = lease->ip_addr; if (lease->flags & STATIC_LEASE) ddns_cb->flags |= DDNS_STATIC_LEASE; } else if (lease6 != NULL) { scope = &(lease6->scope); memcpy(&ddns_cb->address.iabuf, lease6->addr.s6_addr, 16); ddns_cb->address.len = 16; } else goto cleanup; /* * Set the flag bit if the lease is active, that is it isn't * expired or released. This is used to determine if we need * to update the scope information for both v4 and v6 and * the lease information for v6 when the response * from the DNS code is processed. */ if (active == ISC_TRUE) { ddns_cb->flags |= DDNS_ACTIVE_LEASE; } /* No scope implies that DDNS has not been performed for this lease. */ if (*scope == NULL) goto cleanup; if ((ddns_update_style != DDNS_UPDATE_STYLE_STANDARD) && (ddns_update_style != DDNS_UPDATE_STYLE_INTERIM)) goto cleanup; /* Assume that we are removing both records */ ddns_cb->flags |= DDNS_UPDATE_ADDR | DDNS_UPDATE_PTR; /* and that we want to do the add call */ execute_add = ISC_R_SUCCESS; /* * Look up stored names. */ /* * Find the fwd name and copy it to the control block. If we don't * have it we can't delete the fwd record but we can still try to * remove the ptr record and cleanup the lease information if the * client did the fwd update. */ if (!find_bound_string(&ddns_cb->fwd_name, *scope, "ddns-fwd-name")) { /* don't try and delete the A, or do the add */ ddns_cb->flags &= ~DDNS_UPDATE_ADDR; execute_add = ISC_R_FAILURE; /* Check if client did update */ if (find_bound_string(&ddns_cb->fwd_name, *scope, "ddns-client-fqdn")) { ddns_cb->flags |= DDNS_CLIENT_DID_UPDATE; } } /* * Find the txt or dhcid tag and copy it to the control block. If we * don't have one this isn't an interim or standard record so we can't * delete the A record using this mechanism but we can delete the ptr * record. In this case we will attempt to do any requested next step. */ memset(&leaseid, 0, sizeof(leaseid)); if (find_bound_string (&leaseid, *scope, ddns_standard_tag)) { /* We have a standard tag */ ddns_cb->lease_tag = ddns_standard_tag; ddns_cb->dhcid_class = dns_rdatatype_dhcid; ddns_cb->other_dhcid_class = dns_rdatatype_txt; data_string_copy(&ddns_cb->dhcid, &leaseid, MDL); data_string_forget(&leaseid, MDL); } else if (find_bound_string (&leaseid, *scope, ddns_interim_tag)) { /* we have an interim tag */ ddns_cb->lease_tag = ddns_interim_tag; ddns_cb->dhcid_class = dns_rdatatype_txt; ddns_cb->other_dhcid_class = dns_rdatatype_dhcid; if (dhcid_fromlease(&ddns_cb->dhcid, &leaseid) != ISC_R_SUCCESS) { /* We couldn't convert the dhcid from the lease * version to the dns version. We can't delete * the A record but can continue to the ptr */ ddns_cb->flags &= ~DDNS_UPDATE_ADDR; } data_string_forget(&leaseid, MDL); } else { ddns_cb->flags &= ~DDNS_UPDATE_ADDR; } /* * Find the rev name and copy it to the control block. If we don't * have it we can't get rid of it but we can try to remove the fwd * pointer if desired. */ if (!find_bound_string(&ddns_cb->rev_name, *scope, "ddns-rev-name")) { ddns_cb->flags &= ~DDNS_UPDATE_PTR; } /* * If we have a second control block for doing an add * after the remove finished attach it to our control block. */ ddns_cb->next_op = add_ddns_cb; /* * Now that we've collected the information we can try to process it. * If necessary we call an appropriate routine to send a message and * provide it with an action routine to run on the control block given * the results of the message. We have three entry points from here, * one for removing the A record, the next for removing the PTR and * the third for doing any requested add. */ if ((ddns_cb->flags & DDNS_UPDATE_ADDR) != 0) { if (ddns_cb->fwd_name.len != 0) { ddns_cb->state = DDNS_STATE_REM_FW_YXDHCID; ddns_cb->cur_func = ddns_fwd_srv_rem1; rcode = ddns_modify_fwd(ddns_cb, MDL); if (rcode == ISC_R_SUCCESS) { ddns_update_lease_ptr(lease, lease6, ddns_cb, ddns_cb, MDL); return (ISC_R_SUCCESS); } /* * We weren't able to process the request tag the * add so we won't execute it. */ execute_add = ISC_R_FAILURE; goto cleanup; } else { /*remove info from scope */ unset(*scope, "ddns-fwd-name"); unset(*scope, ddns_cb->lease_tag); } } if ((ddns_cb->flags & DDNS_UPDATE_PTR) != 0) { ddns_cb->state = DDNS_STATE_REM_PTR; ddns_cb->cur_func = ddns_ptr_remove; ddns_cb->flags |= DDNS_EXECUTE_NEXT; /* * if execute add isn't success remove the control block so * it won't be processed when the remove completes. We * also arrange to clean it up and get rid of it. */ if (execute_add != ISC_R_SUCCESS) { ddns_cb->next_op = NULL; ddns_fwd_srv_connector(lease, lease6, scope, add_ddns_cb, execute_add); add_ddns_cb = NULL; } else { result = ISC_R_SUCCESS; } rcode = ddns_modify_ptr(ddns_cb, MDL); if (rcode == ISC_R_SUCCESS) { ddns_update_lease_ptr(lease, lease6, ddns_cb, ddns_cb, MDL); return (result); } /* We weren't able to process the request tag the * add so we won't execute it */ execute_add = ISC_R_FAILURE; goto cleanup; } cleanup: /* * We've gotten here because we didn't need to send a message or * we failed when trying to do so. We send the additional cb * off to handle sending and/or cleanup and cleanup anything * we allocated here. */ ddns_fwd_srv_connector(lease, lease6, scope, add_ddns_cb, execute_add); if (ddns_cb != NULL) destroy_ddns_cb(ddns_cb, MDL); return (result); } /* Convenience function for setting flag bits in a mask */ void set_flag (u_int16_t *flags, u_int16_t flag, u_int16_t value) { if (flags) { if (value) { *flags |= flag; } else { *flags &= ~flag; } } } /* * Convenience function which replicates the conflict flags set in one * mask to another, while preserving all other flags. */ void copy_conflict_flags(u_int16_t *target, u_int16_t source) { if (target) { /* Preserve non conflict flags */ *target &= ~CONFLICT_BITS; /* Enable conflict flags per source */ *target |= source & CONFLICT_BITS; } } /* * Given an option_state, create a mask of conflict detection flags based * on the appropriate configuration parameters within the option state. */ u_int16_t get_conflict_mask(struct option_state *options) { int ddns_update_conflict_detection = 1; /* default on */ int ddns_dual_stack_mixed_mode = 0; /* default off */ int ddns_guard_id_must_match = 1; /* default on */ int ddns_other_guard_is_dynamic = 0; /* default off */ struct option_cache *oc = NULL; u_int16_t mask = 0; oc = lookup_option(&server_universe, options, SV_DDNS_CONFLICT_DETECT); if (oc) { ddns_update_conflict_detection = evaluate_boolean_option_cache(NULL, NULL, NULL, NULL, options, NULL, &global_scope, oc, MDL); } set_flag(&mask, DDNS_CONFLICT_DETECTION, ddns_update_conflict_detection); if (!ddns_update_conflict_detection) { #if defined (DEBUG_DNS_UPDATES) log_info ("DDNS conflict detection: off"); #endif /* Turn the rest of the conflict related flags off */ set_flag(&mask, DDNS_DUAL_STACK_MIXED_MODE, 0); set_flag(&mask, DDNS_GUARD_ID_MUST_MATCH, 0); set_flag(&mask, DDNS_OTHER_GUARD_IS_DYNAMIC, 0); return (mask); } // Get the values oc = lookup_option(&server_universe, options, SV_DDNS_DUAL_STACK_MIXED_MODE); if (oc) { ddns_dual_stack_mixed_mode = evaluate_boolean_option_cache(NULL, NULL, NULL, NULL, options, NULL, &global_scope, oc, MDL); } oc = lookup_option(&server_universe, options, SV_DDNS_GUARD_ID_MUST_MATCH); if (oc) { ddns_guard_id_must_match = evaluate_boolean_option_cache(NULL, NULL, NULL, NULL, options, NULL, &global_scope, oc, MDL); } oc = lookup_option(&server_universe, options, SV_DDNS_OTHER_GUARD_IS_DYNAMIC); if (oc) { ddns_other_guard_is_dynamic = evaluate_boolean_option_cache(NULL, NULL, NULL, NULL, options, NULL, &global_scope, oc, MDL); } // Set the flags set_flag(&mask, DDNS_DUAL_STACK_MIXED_MODE, ddns_dual_stack_mixed_mode); set_flag(&mask, DDNS_GUARD_ID_MUST_MATCH, ddns_guard_id_must_match); set_flag(&mask, DDNS_OTHER_GUARD_IS_DYNAMIC, ddns_other_guard_is_dynamic); #if defined (DEBUG_DNS_UPDATES) log_info ("DDNS conflict behavior:\n" "\tddns-update-style: %s\n" "\tupdate-conflict-detection: %d\n" "\tddns-dual-stack-mixed-mode: %d\n" "\tddns-guard-id-must-match %d\n" "\tddns-other-guard-is-dynamic: %d\n", ddns_styles_values[ddns_update_style].name, ddns_update_conflict_detection, ddns_dual_stack_mixed_mode, ddns_guard_id_must_match, ddns_other_guard_is_dynamic); #endif return (mask); } #if defined (DEBUG_DNS_UPDATES) /* Type used for creating lists of function pointers and their names */ typedef struct { void *ptr; char *name; } LabeledPtr; /* Returns the name of the function referred to by the given address */ char* dump_ddns_cb_func(void *func) { static LabeledPtr funcs[] = { { ddns_ptr_add, "ddns_ptr_add" }, { ddns_fwd_srv_add2, "ddns_fwd_srv_add2" }, { ddns_fwd_srv_add1, "ddns_fwd_srv_add1" }, { ddns_ptr_remove, "ddns_ptr_remove" }, { ddns_fwd_srv_rem2, "ddns_fwd_srv_rem2" }, { ddns_fwd_srv_rem1, "ddns_fwd_srv_rem1" }, { ddns_fwd_srv_add3, "ddns_fwd_srv_adde" }, { NULL, "unknown" } }; LabeledPtr* lp = funcs; if (!func) { return (""); } while ((lp->ptr) && (lp->ptr != func)) { ++lp; } return (lp->name); } /* Dumps basic control block info to the log */ char* dump_ddns_cb (dhcp_ddns_cb_t *ddns_cb) { static char output_buf[4096]; if (!ddns_cb) { return (""); } sprintf (output_buf, "ddns_cb: %p flags: %x state: %s cur_func: %s", ddns_cb, ddns_cb->flags, ddns_state_name(ddns_cb->state), dump_ddns_cb_func(ddns_cb->cur_func)); return(output_buf); } #endif /* DEBUG_DNS_UPDATES */ #endif /* NSUPDATE */ dhcp-4.4.1/server/dhcp.c000644 000765 000024 00000540524 13243301226 015315 0ustar00tmarkstaff000000 000000 /* dhcp.c DHCP Protocol engine. */ /* * Copyright (c) 2004-2018 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1995-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #include #include #include static void maybe_return_agent_options(struct packet *packet, struct option_state *options); static int reuse_lease (struct packet* packet, struct lease* new_lease, struct lease* lease, struct lease_state *state, int offer); #if defined(DHCPv6) && defined(DHCP4o6) static int locate_network6(struct packet *packet); #endif int outstanding_pings; #if defined(DELAYED_ACK) static void delayed_ack_enqueue(struct lease *); static void delayed_acks_timer(void *); struct leasequeue *ackqueue_head, *ackqueue_tail; static struct leasequeue *free_ackqueue; static struct timeval max_fsync; int outstanding_acks; int max_outstanding_acks = DEFAULT_DELAYED_ACK; int max_ack_delay_secs = DEFAULT_ACK_DELAY_SECS; int max_ack_delay_usecs = DEFAULT_ACK_DELAY_USECS; int min_ack_delay_usecs = DEFAULT_MIN_ACK_DELAY_USECS; #endif static char dhcp_message [256]; static int site_code_min; static int find_min_site_code(struct universe *); static isc_result_t lowest_site_code(const void *, unsigned, void *); static const char *dhcp_type_names [] = { "DHCPDISCOVER", "DHCPOFFER", "DHCPREQUEST", "DHCPDECLINE", "DHCPACK", "DHCPNAK", "DHCPRELEASE", "DHCPINFORM", "type 9", "DHCPLEASEQUERY", "DHCPLEASEUNASSIGNED", "DHCPLEASEUNKNOWN", "DHCPLEASEACTIVE" }; const int dhcp_type_name_max = ((sizeof dhcp_type_names) / sizeof (char *)); #if defined (TRACING) # define send_packet trace_packet_send #endif static TIME leaseTimeCheck(TIME calculated, TIME alternate); void dhcp (struct packet *packet) { int ms_nulltp = 0; struct option_cache *oc; struct lease *lease = NULL; const char *errmsg; struct data_string data; if (!locate_network(packet) && packet->packet_type != DHCPREQUEST && packet->packet_type != DHCPINFORM && packet->packet_type != DHCPLEASEQUERY) { const char *s; char typebuf[32]; errmsg = "unknown network segment"; bad_packet: if (packet->packet_type > 0 && packet->packet_type <= dhcp_type_name_max) { s = dhcp_type_names[packet->packet_type - 1]; } else { /* %Audit% Cannot exceed 28 bytes. %2004.06.17,Safe% */ sprintf(typebuf, "type %d", packet->packet_type); s = typebuf; } #if defined(DHCPv6) && defined(DHCP4o6) if (dhcpv4_over_dhcpv6 && (packet->dhcp4o6_response != NULL)) { log_info("DHCP4o6 %s from %s via %s: %s", s, (packet->raw->htype ? print_hw_addr(packet->raw->htype, packet->raw->hlen, packet->raw->chaddr) : ""), piaddr(packet->client_addr), errmsg); goto out; } #endif log_info("%s from %s via %s: %s", s, (packet->raw->htype ? print_hw_addr(packet->raw->htype, packet->raw->hlen, packet->raw->chaddr) : ""), packet->raw->giaddr.s_addr ? inet_ntoa(packet->raw->giaddr) : packet->interface->name, errmsg); goto out; } /* There is a problem with the relay agent information option, * which is that in order for a normal relay agent to append * this option, the relay agent has to have been involved in * getting the packet from the client to the server. Note * that this is the software entity known as the relay agent, * _not_ the hardware entity known as a router in which the * relay agent may be running, so the fact that a router has * forwarded a packet does not mean that the relay agent in * the router was involved. * * So when the client broadcasts (DHCPDISCOVER, or giaddr is set), * we can be sure that there are either agent options in the * packet, or there aren't supposed to be. When the giaddr is not * set, it's still possible that the client is on a directly * attached subnet, and agent options are being appended by an l2 * device that has no address, and so sets no giaddr. * * But in either case it's possible that the packets we receive * from the client in RENEW state may not include the agent options, * so if they are not in the packet we must "pretend" the last values * we observed were provided. */ if (packet->packet_type == DHCPREQUEST && packet->raw->ciaddr.s_addr && !packet->raw->giaddr.s_addr && (packet->options->universe_count <= agent_universe.index || packet->options->universes[agent_universe.index] == NULL)) { struct iaddr cip; cip.len = sizeof packet -> raw -> ciaddr; memcpy (cip.iabuf, &packet -> raw -> ciaddr, sizeof packet -> raw -> ciaddr); if (!find_lease_by_ip_addr (&lease, cip, MDL)) goto nolease; /* If there are no agent options on the lease, it's not interesting. */ if (!lease -> agent_options) goto nolease; /* The client should not be unicasting a renewal if its lease has expired, so make it go through the process of getting its agent options legally. */ if (lease -> ends < cur_time) goto nolease; if (lease -> uid_len) { oc = lookup_option (&dhcp_universe, packet -> options, DHO_DHCP_CLIENT_IDENTIFIER); if (!oc) goto nolease; memset (&data, 0, sizeof data); if (!evaluate_option_cache (&data, packet, (struct lease *)0, (struct client_state *)0, packet -> options, (struct option_state *)0, &global_scope, oc, MDL)) goto nolease; if (lease -> uid_len != data.len || memcmp (lease -> uid, data.data, data.len)) { data_string_forget (&data, MDL); goto nolease; } data_string_forget (&data, MDL); } else if ((lease -> hardware_addr.hbuf [0] != packet -> raw -> htype) || (lease -> hardware_addr.hlen - 1 != packet -> raw -> hlen) || memcmp (&lease -> hardware_addr.hbuf [1], packet -> raw -> chaddr, packet -> raw -> hlen)) goto nolease; /* Okay, so we found a lease that matches the client. */ option_chain_head_reference ((struct option_chain_head **) &(packet -> options -> universes [agent_universe.index]), lease -> agent_options, MDL); if (packet->options->universe_count <= agent_universe.index) packet->options->universe_count = agent_universe.index + 1; packet->agent_options_stashed = ISC_TRUE; } nolease: /* If a client null terminates options it sends, it probably * expects the server to reciprocate. */ if ((oc = lookup_option (&dhcp_universe, packet -> options, DHO_HOST_NAME))) { if (!oc -> expression) ms_nulltp = oc->flags & OPTION_HAD_NULLS; } /* Classify the client. */ classify_client (packet); switch (packet -> packet_type) { case DHCPDISCOVER: dhcpdiscover (packet, ms_nulltp); break; case DHCPREQUEST: dhcprequest (packet, ms_nulltp, lease); break; case DHCPRELEASE: dhcprelease (packet, ms_nulltp); break; case DHCPDECLINE: dhcpdecline (packet, ms_nulltp); break; case DHCPINFORM: dhcpinform (packet, ms_nulltp); break; case DHCPLEASEQUERY: dhcpleasequery(packet, ms_nulltp); break; case DHCPACK: case DHCPOFFER: case DHCPNAK: case DHCPLEASEUNASSIGNED: case DHCPLEASEUNKNOWN: case DHCPLEASEACTIVE: break; default: errmsg = "unknown packet type"; goto bad_packet; } out: if (lease) lease_dereference (&lease, MDL); } void dhcpdiscover (packet, ms_nulltp) struct packet *packet; int ms_nulltp; { struct lease *lease = (struct lease *)0; char msgbuf [1024]; /* XXX */ TIME when; const char *s; int peer_has_leases = 0; #if defined (FAILOVER_PROTOCOL) dhcp_failover_state_t *peer; #endif find_lease (&lease, packet, packet -> shared_network, 0, &peer_has_leases, (struct lease *)0, MDL); if (lease && lease -> client_hostname) { if ((strlen (lease -> client_hostname) <= 64) && db_printable((unsigned char *)lease->client_hostname)) s = lease -> client_hostname; else s = "Hostname Unsuitable for Printing"; } else s = (char *)0; /* %Audit% This is log output. %2004.06.17,Safe% * If we truncate we hope the user can get a hint from the log. */ #if defined(DHCPv6) && defined(DHCP4o6) if (dhcpv4_over_dhcpv6 && (packet->dhcp4o6_response != NULL)) { snprintf (msgbuf, sizeof msgbuf, "DHCP4o6 DHCPDISCOVER from %s %s%s%svia %s", (packet -> raw -> htype ? print_hw_addr (packet -> raw -> htype, packet -> raw -> hlen, packet -> raw -> chaddr) : (lease ? print_hex_1(lease->uid_len, lease->uid, 60) : "")), s ? "(" : "", s ? s : "", s ? ") " : "", piaddr(packet->client_addr)); } else #endif snprintf (msgbuf, sizeof msgbuf, "DHCPDISCOVER from %s %s%s%svia %s", (packet -> raw -> htype ? print_hw_addr (packet -> raw -> htype, packet -> raw -> hlen, packet -> raw -> chaddr) : (lease ? print_hex_1(lease->uid_len, lease->uid, 60) : "")), s ? "(" : "", s ? s : "", s ? ") " : "", packet -> raw -> giaddr.s_addr ? inet_ntoa (packet -> raw -> giaddr) : packet -> interface -> name); /* Sourceless packets don't make sense here. */ if (!packet -> shared_network) { #if defined(DHCPv6) && defined(DHCP4o6) if (dhcpv4_over_dhcpv6 && (packet->dhcp4o6_response != NULL)) { log_info ("DHCP4o6 packet from unknown subnet: %s", piaddr(packet->client_addr)); } else #endif log_info ("Packet from unknown subnet: %s", inet_ntoa (packet -> raw -> giaddr)); goto out; } #if defined (FAILOVER_PROTOCOL) if (lease && lease -> pool && lease -> pool -> failover_peer) { peer = lease -> pool -> failover_peer; /* * If the lease is ours to (re)allocate, then allocate it. * * If the lease is active, it belongs to the client. This * is the right lease, if we are to offer one. We decide * whether or not to offer later on. * * If the lease was last active, and we've reached this * point, then it was last active with the same client. We * can safely re-activate the lease with this client. */ if (lease->binding_state == FTS_ACTIVE || lease->rewind_binding_state == FTS_ACTIVE || lease_mine_to_reallocate(lease)) { ; /* This space intentionally left blank. */ /* Otherwise, we can't let the client have this lease. */ } else { #if defined (DEBUG_FIND_LEASE) log_debug ("discarding %s - %s", piaddr (lease -> ip_addr), binding_state_print (lease -> binding_state)); #endif lease_dereference (&lease, MDL); } } #endif /* If we didn't find a lease, try to allocate one... */ if (!lease) { if (!allocate_lease (&lease, packet, packet -> shared_network -> pools, &peer_has_leases)) { if (peer_has_leases) log_error ("%s: peer holds all free leases", msgbuf); else log_error ("%s: network %s: no free leases", msgbuf, packet -> shared_network -> name); return; } } #if defined (FAILOVER_PROTOCOL) if (lease && lease -> pool && lease -> pool -> failover_peer) { peer = lease -> pool -> failover_peer; if (peer -> service_state == not_responding || peer -> service_state == service_startup) { log_info ("%s: not responding%s", msgbuf, peer -> nrr); goto out; } } else peer = (dhcp_failover_state_t *)0; /* Do load balancing if configured. */ if (peer && (peer -> service_state == cooperating) && !load_balance_mine (packet, peer)) { if (peer_has_leases) { log_debug ("%s: load balance to peer %s", msgbuf, peer -> name); goto out; } else { log_debug ("%s: cancel load balance to peer %s - %s", msgbuf, peer -> name, "no free leases"); } } #endif /* If it's an expired lease, get rid of any bindings. */ if (lease -> ends < cur_time && lease -> scope) binding_scope_dereference (&lease -> scope, MDL); /* Set the lease to really expire in 2 minutes, unless it has not yet expired, in which case leave its expiry time alone. */ when = cur_time + 120; if (when < lease -> ends) when = lease -> ends; ack_lease (packet, lease, DHCPOFFER, when, msgbuf, ms_nulltp, (struct host_decl *)0); out: if (lease) lease_dereference (&lease, MDL); } void dhcprequest (packet, ms_nulltp, ip_lease) struct packet *packet; int ms_nulltp; struct lease *ip_lease; { struct lease *lease; struct iaddr cip; struct iaddr sip; struct subnet *subnet; int ours = 0; struct option_cache *oc; struct data_string data; char msgbuf [1024]; /* XXX */ const char *s; char smbuf [19]; #if defined (FAILOVER_PROTOCOL) dhcp_failover_state_t *peer; #endif int have_requested_addr = 0; oc = lookup_option (&dhcp_universe, packet -> options, DHO_DHCP_REQUESTED_ADDRESS); memset (&data, 0, sizeof data); if (oc && evaluate_option_cache (&data, packet, (struct lease *)0, (struct client_state *)0, packet -> options, (struct option_state *)0, &global_scope, oc, MDL)) { cip.len = 4; memcpy (cip.iabuf, data.data, 4); data_string_forget (&data, MDL); have_requested_addr = 1; } else { oc = (struct option_cache *)0; cip.len = 4; memcpy (cip.iabuf, &packet -> raw -> ciaddr.s_addr, 4); } /* Find the lease that matches the address requested by the client. */ subnet = (struct subnet *)0; lease = (struct lease *)0; if (find_subnet (&subnet, cip, MDL)) find_lease (&lease, packet, subnet -> shared_network, &ours, 0, ip_lease, MDL); if (lease && lease -> client_hostname) { if ((strlen (lease -> client_hostname) <= 64) && db_printable((unsigned char *)lease->client_hostname)) s = lease -> client_hostname; else s = "Hostname Unsuitable for Printing"; } else s = (char *)0; oc = lookup_option (&dhcp_universe, packet -> options, DHO_DHCP_SERVER_IDENTIFIER); memset (&data, 0, sizeof data); if (oc && evaluate_option_cache (&data, packet, (struct lease *)0, (struct client_state *)0, packet -> options, (struct option_state *)0, &global_scope, oc, MDL)) { sip.len = 4; memcpy (sip.iabuf, data.data, 4); data_string_forget (&data, MDL); /* piaddr() should not return more than a 15 byte string. * safe. */ sprintf (smbuf, " (%s)", piaddr (sip)); } else { smbuf [0] = 0; sip.len = 0; } /* %Audit% This is log output. %2004.06.17,Safe% * If we truncate we hope the user can get a hint from the log. */ #if defined(DHCPv6) && defined(DHCP4o6) if (dhcpv4_over_dhcpv6 && (packet->dhcp4o6_response != NULL)) { snprintf (msgbuf, sizeof msgbuf, "DHCP4o6 DHCPREQUEST for %s%s from %s %s%s%svia %s", piaddr (cip), smbuf, (packet -> raw -> htype ? print_hw_addr (packet -> raw -> htype, packet -> raw -> hlen, packet -> raw -> chaddr) : (lease ? print_hex_1(lease->uid_len, lease->uid, 60) : "")), s ? "(" : "", s ? s : "", s ? ") " : "", piaddr(packet->client_addr)); } else #endif snprintf (msgbuf, sizeof msgbuf, "DHCPREQUEST for %s%s from %s %s%s%svia %s", piaddr (cip), smbuf, (packet -> raw -> htype ? print_hw_addr (packet -> raw -> htype, packet -> raw -> hlen, packet -> raw -> chaddr) : (lease ? print_hex_1(lease->uid_len, lease->uid, 60) : "")), s ? "(" : "", s ? s : "", s ? ") " : "", packet -> raw -> giaddr.s_addr ? inet_ntoa (packet -> raw -> giaddr) : packet -> interface -> name); #if defined (FAILOVER_PROTOCOL) if (lease && lease -> pool && lease -> pool -> failover_peer) { peer = lease -> pool -> failover_peer; if (peer -> service_state == not_responding || peer -> service_state == service_startup) { log_info ("%s: not responding%s", msgbuf, peer -> nrr); goto out; } /* "load balance to peer" - is not done at all for request. * * If it's RENEWING, we are the only server to hear it, so * we have to serve it. If it's REBINDING, it's out of * communication with the other server, so there's no point * in waiting to serve it. However, if the lease we're * offering is not a free lease, then we may be the only * server that can offer it, so we can't load balance if * the lease isn't in the free or backup state. If it is * in the free or backup state, then that state is what * mandates one server or the other should perform the * allocation, not the LBA...we know the peer cannot * allocate a request for an address in our free state. * * So our only compass is lease_mine_to_reallocate(). This * effects both load balancing, and a sanity-check that we * are not going to try to allocate a lease that isn't ours. */ if ((lease -> binding_state == FTS_FREE || lease -> binding_state == FTS_BACKUP) && !lease_mine_to_reallocate (lease)) { log_debug ("%s: lease owned by peer", msgbuf); goto out; } /* * If the lease is in a transitional state, we can't * renew it unless we can rewind it to a non-transitional * state (active, free, or backup). lease_mine_to_reallocate() * checks for free/backup, so we only need to check for active. */ if ((lease->binding_state == FTS_RELEASED || lease->binding_state == FTS_EXPIRED) && lease->rewind_binding_state != FTS_ACTIVE && !lease_mine_to_reallocate(lease)) { log_debug("%s: lease in transition state %s", msgbuf, (lease->binding_state == FTS_RELEASED) ? "released" : "expired"); goto out; } /* It's actually very unlikely that we'll ever get here, but if we do, tell the client to stop using the lease, because the administrator reset it. */ if (lease -> binding_state == FTS_RESET && !lease_mine_to_reallocate (lease)) { log_debug ("%s: lease reset by administrator", msgbuf); nak_lease (packet, &cip, lease->subnet->group); goto out; } /* If server-id-check is enabled, verify that the client's * server source address (sip from incoming packet) is ours. * To avoid problems with confused clients we do some sanity * checks to verify sip's length and that it isn't all zeros. * We then get the server id we would likely use for this * packet and compare them. If they don't match it we assume * we didn't send the offer and so we don't process the * request. */ if ((server_id_check == 1) && (sip.len == 4) && (memcmp(sip.iabuf, "\0\0\0\0", sip.len) != 0)) { struct in_addr from; struct option_state *eval_options = NULL; eval_network_statements(&eval_options, packet, NULL); get_server_source_address(&from, eval_options, NULL, packet); option_state_dereference (&eval_options, MDL); if (memcmp(sip.iabuf, &from, sip.len) != 0) { log_debug("%s: not our server id", msgbuf); goto out; } } /* At this point it's possible that we will get a broadcast DHCPREQUEST for a lease that we didn't offer, because both we and the peer are in a position to offer it. In that case, we probably shouldn't answer. In order to not answer, we would have to compare the server identifier sent by the client with the list of possible server identifiers we can send, and if the client's identifier isn't on the list, drop the DHCPREQUEST. We aren't currently doing that for two reasons - first, it's not clear that all clients do the right thing with respect to sending the client identifier, which could mean that we might simply not respond to a client that is depending on us to respond. Secondly, we allow the user to specify the server identifier to send, and we don't enforce that the server identifier should be one of our IP addresses. This is probably not a big deal, but it's theoretically an issue. The reason we care about this is that if both servers send a DHCPACK to the DHCPREQUEST, they are then going to send dueling BNDUPD messages, which could cause trouble. I think it causes no harm, but it seems wrong. */ } else peer = (dhcp_failover_state_t *)0; #endif /* If a client on a given network REQUESTs a lease on an address on a different network, NAK it. If the Requested Address option was used, the protocol says that it must have been broadcast, so we can trust the source network information. If ciaddr was specified and Requested Address was not, then we really only know for sure what network a packet came from if it came through a BOOTP gateway - if it came through an IP router, we'll just have to assume that it's cool. If we don't think we know where the packet came from, it came through a gateway from an unknown network, so it's not from a RENEWING client. If we recognize the network it *thinks* it's on, we can NAK it even though we don't recognize the network it's *actually* on; otherwise we just have to ignore it. We don't currently try to take advantage of access to the raw packet, because it's not available on all platforms. So a packet that was unicast to us through a router from a RENEWING client is going to look exactly like a packet that was broadcast to us from an INIT-REBOOT client. Since we can't tell the difference between these two kinds of packets, if the packet appears to have come in off the local wire, we have to treat it as if it's a RENEWING client. This means that we can't NAK a RENEWING client on the local wire that has a bogus address. The good news is that we won't ACK it either, so it should revert to INIT state and send us a DHCPDISCOVER, which we *can* work with. Because we can't detect that a RENEWING client is on the wrong wire, it's going to sit there trying to renew until it gets to the REBIND state, when we *can* NAK it because the packet will get to us through a BOOTP gateway. We shouldn't actually see DHCPREQUEST packets from RENEWING clients on the wrong wire anyway, since their idea of their local router will be wrong. In any case, the protocol doesn't really allow us to NAK a DHCPREQUEST from a RENEWING client, so we can punt on this issue. */ if (!packet -> shared_network || (packet -> raw -> ciaddr.s_addr && packet -> raw -> giaddr.s_addr) || (have_requested_addr && !packet -> raw -> ciaddr.s_addr)) { /* If we don't know where it came from but we do know where it claims to have come from, it didn't come from there. */ if (!packet -> shared_network) { if (subnet && subnet -> group -> authoritative) { log_info ("%s: wrong network.", msgbuf); nak_lease (packet, &cip, NULL); goto out; } /* Otherwise, ignore it. */ log_info ("%s: ignored (%s).", msgbuf, (subnet ? "not authoritative" : "unknown subnet")); goto out; } /* If we do know where it came from and it asked for an address that is not on that shared network, nak it. */ if (subnet) subnet_dereference (&subnet, MDL); if (!find_grouped_subnet (&subnet, packet -> shared_network, cip, MDL)) { if (packet -> shared_network -> group -> authoritative) { log_info ("%s: wrong network.", msgbuf); nak_lease (packet, &cip, NULL); goto out; } log_info ("%s: ignored (not authoritative).", msgbuf); return; } } /* If the address the client asked for is ours, but it wasn't available for the client, NAK it. */ if (!lease && ours) { log_info ("%s: lease %s unavailable.", msgbuf, piaddr (cip)); nak_lease (packet, &cip, (subnet ? subnet->group : NULL)); goto out; } /* Otherwise, send the lease to the client if we found one. */ if (lease) { ack_lease (packet, lease, DHCPACK, 0, msgbuf, ms_nulltp, (struct host_decl *)0); } else log_info ("%s: unknown lease %s.", msgbuf, piaddr (cip)); out: if (subnet) subnet_dereference (&subnet, MDL); if (lease) lease_dereference (&lease, MDL); return; } void dhcprelease (packet, ms_nulltp) struct packet *packet; int ms_nulltp; { struct lease *lease = (struct lease *)0, *next = (struct lease *)0; struct iaddr cip; struct option_cache *oc; struct data_string data; const char *s; char msgbuf [1024], cstr[16]; /* XXX */ /* DHCPRELEASE must not specify address in requested-address option, but old protocol specs weren't explicit about this, so let it go. */ if ((oc = lookup_option (&dhcp_universe, packet -> options, DHO_DHCP_REQUESTED_ADDRESS))) { log_info ("DHCPRELEASE from %s specified requested-address.", print_hw_addr (packet -> raw -> htype, packet -> raw -> hlen, packet -> raw -> chaddr)); } oc = lookup_option (&dhcp_universe, packet -> options, DHO_DHCP_CLIENT_IDENTIFIER); memset (&data, 0, sizeof data); if (oc && evaluate_option_cache (&data, packet, (struct lease *)0, (struct client_state *)0, packet -> options, (struct option_state *)0, &global_scope, oc, MDL)) { find_lease_by_uid (&lease, data.data, data.len, MDL); data_string_forget (&data, MDL); /* See if we can find a lease that matches the IP address the client is claiming. */ while (lease) { if (lease -> n_uid) lease_reference (&next, lease -> n_uid, MDL); if (!memcmp (&packet -> raw -> ciaddr, lease -> ip_addr.iabuf, 4)) { break; } lease_dereference (&lease, MDL); if (next) { lease_reference (&lease, next, MDL); lease_dereference (&next, MDL); } } if (next) lease_dereference (&next, MDL); } /* The client is supposed to pass a valid client-identifier, but the spec on this has changed historically, so try the IP address in ciaddr if the client-identifier fails. */ if (!lease) { cip.len = 4; memcpy (cip.iabuf, &packet -> raw -> ciaddr, 4); find_lease_by_ip_addr (&lease, cip, MDL); } /* If the hardware address doesn't match, don't do the release. */ if (lease && (lease -> hardware_addr.hlen != packet -> raw -> hlen + 1 || lease -> hardware_addr.hbuf [0] != packet -> raw -> htype || memcmp (&lease -> hardware_addr.hbuf [1], packet -> raw -> chaddr, packet -> raw -> hlen))) lease_dereference (&lease, MDL); if (lease && lease -> client_hostname) { if ((strlen (lease -> client_hostname) <= 64) && db_printable((unsigned char *)lease->client_hostname)) s = lease -> client_hostname; else s = "Hostname Unsuitable for Printing"; } else s = (char *)0; /* %Audit% Cannot exceed 16 bytes. %2004.06.17,Safe% * We copy this out to stack because we actually want to log two * inet_ntoa()'s in this message. */ strncpy(cstr, inet_ntoa (packet -> raw -> ciaddr), 15); cstr[15] = '\0'; /* %Audit% This is log output. %2004.06.17,Safe% * If we truncate we hope the user can get a hint from the log. */ #if defined(DHCPv6) && defined(DHCP4o6) if (dhcpv4_over_dhcpv6 && (packet->dhcp4o6_response != NULL)) { snprintf (msgbuf, sizeof msgbuf, "DHCP4o6 DHCPRELEASE of %s from %s %s%s%svia " "%s (%sfound)", cstr, (packet -> raw -> htype ? print_hw_addr (packet -> raw -> htype, packet -> raw -> hlen, packet -> raw -> chaddr) : (lease ? print_hex_1(lease->uid_len, lease->uid, 60) : "")), s ? "(" : "", s ? s : "", s ? ") " : "", piaddr(packet->client_addr), lease ? "" : "not "); } else #endif snprintf (msgbuf, sizeof msgbuf, "DHCPRELEASE of %s from %s %s%s%svia %s (%sfound)", cstr, (packet -> raw -> htype ? print_hw_addr (packet -> raw -> htype, packet -> raw -> hlen, packet -> raw -> chaddr) : (lease ? print_hex_1(lease->uid_len, lease->uid, 60) : "")), s ? "(" : "", s ? s : "", s ? ") " : "", packet -> raw -> giaddr.s_addr ? inet_ntoa (packet -> raw -> giaddr) : packet -> interface -> name, lease ? "" : "not "); #if defined (FAILOVER_PROTOCOL) if (lease && lease -> pool && lease -> pool -> failover_peer) { dhcp_failover_state_t *peer = lease -> pool -> failover_peer; if (peer -> service_state == not_responding || peer -> service_state == service_startup) { log_info ("%s: ignored%s", peer -> name, peer -> nrr); goto out; } /* DHCPRELEASE messages are unicast, so if the client sent the DHCPRELEASE to us, it's not going to send it to the peer. Not sure why this would happen, and if it does happen I think we still have to change the lease state, so that's what we're doing. XXX See what it says in the draft about this. */ } #endif /* If we found a lease, release it. */ if (lease && lease -> ends > cur_time) { release_lease (lease, packet); } log_info ("%s", msgbuf); #if defined(FAILOVER_PROTOCOL) out: #endif if (lease) lease_dereference (&lease, MDL); } void dhcpdecline (packet, ms_nulltp) struct packet *packet; int ms_nulltp; { struct lease *lease = (struct lease *)0; struct option_state *options = (struct option_state *)0; int ignorep = 0; int i; const char *status; const char *s; char msgbuf [1024]; /* XXX */ struct iaddr cip; struct option_cache *oc; struct data_string data; /* DHCPDECLINE must specify address. */ if (!(oc = lookup_option (&dhcp_universe, packet -> options, DHO_DHCP_REQUESTED_ADDRESS))) return; memset (&data, 0, sizeof data); if (!evaluate_option_cache (&data, packet, (struct lease *)0, (struct client_state *)0, packet -> options, (struct option_state *)0, &global_scope, oc, MDL)) return; cip.len = 4; memcpy (cip.iabuf, data.data, 4); data_string_forget (&data, MDL); find_lease_by_ip_addr (&lease, cip, MDL); if (lease && lease -> client_hostname) { if ((strlen (lease -> client_hostname) <= 64) && db_printable((unsigned char *)lease->client_hostname)) s = lease -> client_hostname; else s = "Hostname Unsuitable for Printing"; } else s = (char *)0; /* %Audit% This is log output. %2004.06.17,Safe% * If we truncate we hope the user can get a hint from the log. */ #if defined(DHCPv6) && defined(DHCP4o6) if (dhcpv4_over_dhcpv6 && (packet->dhcp4o6_response != NULL)) { snprintf (msgbuf, sizeof msgbuf, "DHCP4o6 DHCPDECLINE of %s from %s %s%s%svia %s", piaddr (cip), (packet -> raw -> htype ? print_hw_addr (packet -> raw -> htype, packet -> raw -> hlen, packet -> raw -> chaddr) : (lease ? print_hex_1(lease->uid_len, lease->uid, 60) : "")), s ? "(" : "", s ? s : "", s ? ") " : "", piaddr(packet->client_addr)); } else #endif snprintf (msgbuf, sizeof msgbuf, "DHCPDECLINE of %s from %s %s%s%svia %s", piaddr (cip), (packet -> raw -> htype ? print_hw_addr (packet -> raw -> htype, packet -> raw -> hlen, packet -> raw -> chaddr) : (lease ? print_hex_1(lease->uid_len, lease->uid, 60) : "")), s ? "(" : "", s ? s : "", s ? ") " : "", packet -> raw -> giaddr.s_addr ? inet_ntoa (packet -> raw -> giaddr) : packet -> interface -> name); option_state_allocate (&options, MDL); /* Execute statements in scope starting with the subnet scope. */ if (lease) execute_statements_in_scope(NULL, packet, NULL, NULL, packet->options, options, &global_scope, lease->subnet->group, NULL, NULL); /* Execute statements in the class scopes. */ for (i = packet -> class_count; i > 0; i--) { execute_statements_in_scope (NULL, packet, NULL, NULL, packet->options, options, &global_scope, packet->classes[i - 1]->group, lease ? lease->subnet->group : NULL, NULL); } /* Drop the request if dhcpdeclines are being ignored. */ oc = lookup_option (&server_universe, options, SV_DECLINES); if (!oc || evaluate_boolean_option_cache (&ignorep, packet, lease, (struct client_state *)0, packet -> options, options, &lease -> scope, oc, MDL)) { /* If we found a lease, mark it as unusable and complain. */ if (lease) { #if defined (FAILOVER_PROTOCOL) if (lease -> pool && lease -> pool -> failover_peer) { dhcp_failover_state_t *peer = lease -> pool -> failover_peer; if (peer -> service_state == not_responding || peer -> service_state == service_startup) { if (!ignorep) log_info ("%s: ignored%s", peer -> name, peer -> nrr); goto out; } /* DHCPDECLINE messages are broadcast, so we can safely ignore the DHCPDECLINE if the peer has the lease. XXX Of course, at this point that information has been lost. */ } #endif abandon_lease (lease, "declined."); status = "abandoned"; } else { status = "not found"; } } else status = "ignored"; if (!ignorep) log_info ("%s: %s", msgbuf, status); #if defined(FAILOVER_PROTOCOL) out: #endif if (options) option_state_dereference (&options, MDL); if (lease) lease_dereference (&lease, MDL); } #if defined(RELAY_PORT) u_int16_t dhcp_check_relayport(packet) struct packet *packet; { if (lookup_option(&agent_universe, packet->options, RAI_RELAY_PORT) != NULL) { return (packet->client_port); } return (0); } #endif void dhcpinform (packet, ms_nulltp) struct packet *packet; int ms_nulltp; { char msgbuf[1024], *addr_type; struct data_string d1, prl, fixed_addr; struct option_cache *oc; struct option_state *options = NULL; struct dhcp_packet raw; struct packet outgoing; unsigned char dhcpack = DHCPACK; struct subnet *subnet = NULL; struct iaddr cip, gip, sip; unsigned i; int nulltp; struct sockaddr_in to; struct in_addr from; isc_boolean_t zeroed_ciaddr; struct interface_info *interface; int result, h_m_client_ip = 0; struct host_decl *host = NULL, *hp = NULL, *h; #if defined(RELAY_PORT) u_int16_t relay_port = 0; #endif #if defined (DEBUG_INFORM_HOST) int h_w_fixed_addr = 0; #endif /* The client should set ciaddr to its IP address, but apparently it's common for clients not to do this, so we'll use their IP source address if they didn't set ciaddr. */ if (!packet->raw->ciaddr.s_addr) { zeroed_ciaddr = ISC_TRUE; /* With DHCPv4-over-DHCPv6 it can be an IPv6 address so we check its length. */ if (packet->client_addr.len == 4) { cip.len = 4; memcpy(cip.iabuf, &packet->client_addr.iabuf, 4); addr_type = "source"; } else { cip.len = 0; memset(cip.iabuf, 0, 4); addr_type = "v4o6"; } } else { zeroed_ciaddr = ISC_FALSE; cip.len = 4; memcpy(cip.iabuf, &packet->raw->ciaddr, 4); addr_type = "client"; } sip.len = 4; memcpy(sip.iabuf, cip.iabuf, 4); if (packet->raw->giaddr.s_addr) { gip.len = 4; memcpy(gip.iabuf, &packet->raw->giaddr, 4); if (zeroed_ciaddr == ISC_TRUE) { addr_type = "relay"; memcpy(sip.iabuf, gip.iabuf, 4); } } else gip.len = 0; /* %Audit% This is log output. %2004.06.17,Safe% * If we truncate we hope the user can get a hint from the log. */ #if defined(DHCPv6) && defined(DHCP4o6) if (dhcpv4_over_dhcpv6 && (packet->dhcp4o6_response != NULL)) { snprintf(msgbuf, sizeof(msgbuf), "DHCP4o6 DHCPINFORM from %s via %s", piaddr(cip), piaddr(packet->client_addr)); } else #endif snprintf(msgbuf, sizeof(msgbuf), "DHCPINFORM from %s via %s", piaddr(cip), packet->raw->giaddr.s_addr ? inet_ntoa(packet->raw->giaddr) : packet->interface->name); /* If the IP source address is zero, don't respond. */ if (!memcmp(cip.iabuf, "\0\0\0", 4)) { log_info("%s: ignored (null source address).", msgbuf); return; } #if defined(RELAY_PORT) relay_port = dhcp_check_relayport(packet); #endif /* Find the subnet that the client is on. * CC: Do the link selection / subnet selection */ option_state_allocate(&options, MDL); if ((oc = lookup_option(&agent_universe, packet->options, RAI_LINK_SELECT)) == NULL) oc = lookup_option(&dhcp_universe, packet->options, DHO_SUBNET_SELECTION); memset(&d1, 0, sizeof d1); if (oc && evaluate_option_cache(&d1, packet, NULL, NULL, packet->options, NULL, &global_scope, oc, MDL)) { struct option_cache *noc = NULL; if (d1.len != 4) { log_info("%s: ignored (invalid subnet selection option).", msgbuf); option_state_dereference(&options, MDL); return; } memcpy(sip.iabuf, d1.data, 4); data_string_forget(&d1, MDL); /* Make a copy of the data. */ if (option_cache_allocate(&noc, MDL)) { if (oc->data.len) data_string_copy(&noc->data, &oc->data, MDL); if (oc->expression) expression_reference(&noc->expression, oc->expression, MDL); if (oc->option) option_reference(&(noc->option), oc->option, MDL); } save_option(&dhcp_universe, options, noc); option_cache_dereference(&noc, MDL); if ((zeroed_ciaddr == ISC_TRUE) && (gip.len != 0)) addr_type = "relay link select"; else addr_type = "selected"; } find_subnet(&subnet, sip, MDL); if (subnet == NULL) { log_info("%s: unknown subnet for %s address %s", msgbuf, addr_type, piaddr(sip)); option_state_dereference(&options, MDL); return; } /* We don't respond to DHCPINFORM packets if we're not authoritative. It would be nice if a per-host value could override this, but there's overhead involved in checking this, so let's see how people react first. */ if (!subnet->group->authoritative) { static int eso = 0; log_info("%s: not authoritative for subnet %s", msgbuf, piaddr (subnet -> net)); if (!eso) { log_info("If this DHCP server is authoritative for%s", " that subnet,"); log_info("please write an `authoritative;' directi%s", "ve either in the"); log_info("subnet declaration or in some scope that%s", " encloses the"); log_info("subnet declaration - for example, write %s", "it at the top"); log_info("of the dhcpd.conf file."); } if (eso++ == 100) eso = 0; subnet_dereference(&subnet, MDL); option_state_dereference(&options, MDL); return; } memset(&outgoing, 0, sizeof outgoing); memset(&raw, 0, sizeof raw); outgoing.raw = &raw; maybe_return_agent_options(packet, options); /* Execute statements network statements starting at the subnet level */ execute_statements_in_scope(NULL, packet, NULL, NULL, packet->options, options, &global_scope, subnet->group, NULL, NULL); /* If we have ciaddr, find its lease so we can find its pool. */ if (zeroed_ciaddr == ISC_FALSE) { struct lease* cip_lease = NULL; find_lease_by_ip_addr (&cip_lease, cip, MDL); /* Overlay with pool options if ciaddr mapped to a lease. */ if (cip_lease) { if (cip_lease->pool && cip_lease->pool->group) { execute_statements_in_scope( NULL, packet, NULL, NULL, packet->options, options, &global_scope, cip_lease->pool->group, cip_lease->pool->shared_network->group, NULL); } lease_dereference (&cip_lease, MDL); } } /* Execute statements in the class scopes. */ for (i = packet->class_count; i > 0; i--) { execute_statements_in_scope(NULL, packet, NULL, NULL, packet->options, options, &global_scope, packet->classes[i - 1]->group, subnet->group, NULL); } /* * Process host declarations during DHCPINFORM, * Try to find a matching host declaration by cli ID or HW addr. * * Look through the host decls for one that matches the * client identifer or the hardware address. The preference * order is: * client id with matching ip address * hardware address with matching ip address * client id without a ip fixed address * hardware address without a fixed ip address * If found, set host to use its option definitions. */ oc = lookup_option(&dhcp_universe, packet->options, DHO_DHCP_CLIENT_IDENTIFIER); memset(&d1, 0, sizeof(d1)); if (oc && evaluate_option_cache(&d1, packet, NULL, NULL, packet->options, NULL, &global_scope, oc, MDL)) { find_hosts_by_uid(&hp, d1.data, d1.len, MDL); data_string_forget(&d1, MDL); #if defined (DEBUG_INFORM_HOST) if (hp) log_debug ("dhcpinform: found host by ID " "-- checking fixed-address match"); #endif /* check if we have one with fixed-address * matching the client ip first */ for (h = hp; !h_m_client_ip && h; h = h->n_ipaddr) { if (!h->fixed_addr) continue; memset(&fixed_addr, 0, sizeof(fixed_addr)); if (!evaluate_option_cache (&fixed_addr, NULL, NULL, NULL, NULL, NULL, &global_scope, h->fixed_addr, MDL)) continue; #if defined (DEBUG_INFORM_HOST) h_w_fixed_addr++; #endif for (i = 0; (i + cip.len) <= fixed_addr.len; i += cip.len) { if (memcmp(fixed_addr.data + i, cip.iabuf, cip.len) == 0) { #if defined (DEBUG_INFORM_HOST) log_debug ("dhcpinform: found " "host with matching " "fixed-address by ID"); #endif host_reference(&host, h, MDL); h_m_client_ip = 1; break; } } data_string_forget(&fixed_addr, MDL); } /* fallback to a host without fixed-address */ for (h = hp; !host && h; h = h->n_ipaddr) { if (h->fixed_addr) continue; #if defined (DEBUG_INFORM_HOST) log_debug ("dhcpinform: found host " "without fixed-address by ID"); #endif host_reference(&host, h, MDL); break; } if (hp) host_dereference (&hp, MDL); } if (!host || !h_m_client_ip) { find_hosts_by_haddr(&hp, packet->raw->htype, packet->raw->chaddr, packet->raw->hlen, MDL); #if defined (DEBUG_INFORM_HOST) if (hp) log_debug ("dhcpinform: found host by HW " "-- checking fixed-address match"); #endif /* check if we have one with fixed-address * matching the client ip first */ for (h = hp; !h_m_client_ip && h; h = h->n_ipaddr) { if (!h->fixed_addr) continue; memset (&fixed_addr, 0, sizeof(fixed_addr)); if (!evaluate_option_cache (&fixed_addr, NULL, NULL, NULL, NULL, NULL, &global_scope, h->fixed_addr, MDL)) continue; #if defined (DEBUG_INFORM_HOST) h_w_fixed_addr++; #endif for (i = 0; (i + cip.len) <= fixed_addr.len; i += cip.len) { if (memcmp(fixed_addr.data + i, cip.iabuf, cip.len) == 0) { #if defined (DEBUG_INFORM_HOST) log_debug ("dhcpinform: found " "host with matching " "fixed-address by HW"); #endif /* * Hmm.. we've found one * without IP by ID and now * (better) one with IP by HW. */ if(host) host_dereference(&host, MDL); host_reference(&host, h, MDL); h_m_client_ip = 1; break; } } data_string_forget(&fixed_addr, MDL); } /* fallback to a host without fixed-address */ for (h = hp; !host && h; h = h->n_ipaddr) { if (h->fixed_addr) continue; #if defined (DEBUG_INFORM_HOST) log_debug ("dhcpinform: found host without " "fixed-address by HW"); #endif host_reference (&host, h, MDL); break; } if (hp) host_dereference (&hp, MDL); } #if defined (DEBUG_INFORM_HOST) /* Hmm..: what when there is a host with a fixed-address, * that matches by hw or id, but the fixed-addresses * didn't match client ip? */ if (h_w_fixed_addr && !h_m_client_ip) { log_info ("dhcpinform: matching host with " "fixed-address different than " "client IP detected?!"); } #endif /* If we have a host_decl structure, run the options * associated with its group. Whether the host decl * struct is old or not. */ if (host) { #if defined (DEBUG_INFORM_HOST) log_info ("dhcpinform: applying host (group) options"); #endif execute_statements_in_scope(NULL, packet, NULL, NULL, packet->options, options, &global_scope, host->group, subnet->group, NULL); host_dereference (&host, MDL); } /* CC: end of host entry processing.... */ /* Figure out the filename. */ memset (&d1, 0, sizeof d1); oc = lookup_option (&server_universe, options, SV_FILENAME); if (oc && evaluate_option_cache (&d1, packet, (struct lease *)0, (struct client_state *)0, packet -> options, (struct option_state *)0, &global_scope, oc, MDL)) { i = d1.len; if (i >= sizeof(raw.file)) { log_info("file name longer than packet field " "truncated - field: %lu name: %d %.*s", (unsigned long)sizeof(raw.file), i, (int)i, d1.data); i = sizeof(raw.file); } else raw.file[i] = 0; memcpy (raw.file, d1.data, i); data_string_forget (&d1, MDL); } /* Choose a server name as above. */ oc = lookup_option (&server_universe, options, SV_SERVER_NAME); if (oc && evaluate_option_cache (&d1, packet, (struct lease *)0, (struct client_state *)0, packet -> options, (struct option_state *)0, &global_scope, oc, MDL)) { i = d1.len; if (i >= sizeof(raw.sname)) { log_info("server name longer than packet field " "truncated - field: %lu name: %d %.*s", (unsigned long)sizeof(raw.sname), i, (int)i, d1.data); i = sizeof(raw.sname); } else raw.sname[i] = 0; memcpy (raw.sname, d1.data, i); data_string_forget (&d1, MDL); } /* Set a flag if this client is a lame Microsoft client that NUL terminates string options and expects us to do likewise. */ nulltp = 0; if ((oc = lookup_option (&dhcp_universe, packet -> options, DHO_HOST_NAME))) { if (!oc->expression) nulltp = oc->flags & OPTION_HAD_NULLS; } /* Put in DHCP-specific options. */ i = DHO_DHCP_MESSAGE_TYPE; oc = (struct option_cache *)0; if (option_cache_allocate (&oc, MDL)) { if (make_const_data (&oc -> expression, &dhcpack, 1, 0, 0, MDL)) { option_code_hash_lookup(&oc->option, dhcp_universe.code_hash, &i, 0, MDL); save_option (&dhcp_universe, options, oc); } option_cache_dereference (&oc, MDL); } get_server_source_address(&from, options, options, packet); /* Use the subnet mask from the subnet declaration if no other mask has been provided. */ i = DHO_SUBNET_MASK; if (subnet && !lookup_option (&dhcp_universe, options, i)) { oc = (struct option_cache *)0; if (option_cache_allocate (&oc, MDL)) { if (make_const_data (&oc -> expression, subnet -> netmask.iabuf, subnet -> netmask.len, 0, 0, MDL)) { option_code_hash_lookup(&oc->option, dhcp_universe.code_hash, &i, 0, MDL); save_option (&dhcp_universe, options, oc); } option_cache_dereference (&oc, MDL); } } /* If a site option space has been specified, use that for site option codes. */ i = SV_SITE_OPTION_SPACE; if ((oc = lookup_option (&server_universe, options, i)) && evaluate_option_cache (&d1, packet, (struct lease *)0, (struct client_state *)0, packet -> options, options, &global_scope, oc, MDL)) { struct universe *u = (struct universe *)0; if (!universe_hash_lookup (&u, universe_hash, (const char *)d1.data, d1.len, MDL)) { log_error ("unknown option space %s.", d1.data); option_state_dereference (&options, MDL); if (subnet) subnet_dereference (&subnet, MDL); return; } options -> site_universe = u -> index; options->site_code_min = find_min_site_code(u); data_string_forget (&d1, MDL); } else { options -> site_universe = dhcp_universe.index; options -> site_code_min = 0; /* Trust me, it works. */ } memset (&prl, 0, sizeof prl); /* Use the parameter list from the scope if there is one. */ oc = lookup_option (&dhcp_universe, options, DHO_DHCP_PARAMETER_REQUEST_LIST); /* Otherwise, if the client has provided a list of options that it wishes returned, use it to prioritize. Otherwise, prioritize based on the default priority list. */ if (!oc) oc = lookup_option (&dhcp_universe, packet -> options, DHO_DHCP_PARAMETER_REQUEST_LIST); if (oc) evaluate_option_cache (&prl, packet, (struct lease *)0, (struct client_state *)0, packet -> options, options, &global_scope, oc, MDL); #ifdef DEBUG_PACKET dump_packet (packet); dump_raw ((unsigned char *)packet -> raw, packet -> packet_length); #endif log_info ("%s", msgbuf); /* Figure out the address of the boot file server. */ if ((oc = lookup_option (&server_universe, options, SV_NEXT_SERVER))) { if (evaluate_option_cache (&d1, packet, (struct lease *)0, (struct client_state *)0, packet -> options, options, &global_scope, oc, MDL)) { /* If there was more than one answer, take the first. */ if (d1.len >= 4 && d1.data) memcpy (&raw.siaddr, d1.data, 4); data_string_forget (&d1, MDL); } } /* * Remove any time options, per section 3.4 RFC 2131 */ delete_option(&dhcp_universe, options, DHO_DHCP_LEASE_TIME); delete_option(&dhcp_universe, options, DHO_DHCP_RENEWAL_TIME); delete_option(&dhcp_universe, options, DHO_DHCP_REBINDING_TIME); /* Set up the option buffer... */ outgoing.packet_length = cons_options (packet, outgoing.raw, (struct lease *)0, (struct client_state *)0, 0, packet -> options, options, &global_scope, 0, nulltp, 0, prl.len ? &prl : (struct data_string *)0, (char *)0); option_state_dereference (&options, MDL); data_string_forget (&prl, MDL); /* Make sure that the packet is at least as big as a BOOTP packet. */ if (outgoing.packet_length < BOOTP_MIN_LEN) outgoing.packet_length = BOOTP_MIN_LEN; raw.giaddr = packet -> raw -> giaddr; raw.ciaddr = packet -> raw -> ciaddr; memcpy (raw.chaddr, packet -> raw -> chaddr, sizeof raw.chaddr); raw.hlen = packet -> raw -> hlen; raw.htype = packet -> raw -> htype; raw.xid = packet -> raw -> xid; raw.secs = packet -> raw -> secs; raw.flags = packet -> raw -> flags; raw.hops = packet -> raw -> hops; raw.op = BOOTREPLY; #ifdef DEBUG_PACKET dump_packet (&outgoing); dump_raw ((unsigned char *)&raw, outgoing.packet_length); #endif #if defined(DHCPv6) && defined(DHCP4o6) if (dhcpv4_over_dhcpv6 && (packet->dhcp4o6_response != NULL)) { /* Report what we're sending. */ snprintf(msgbuf, sizeof msgbuf, "DHCP4o6 DHCPACK to %s (%s) via", piaddr(cip), (packet->raw->htype && packet->raw->hlen) ? print_hw_addr(packet->raw->htype, packet->raw->hlen, packet->raw->chaddr) : ""); log_info("%s %s", msgbuf, piaddr(packet->client_addr)); /* fill dhcp4o6_response */ packet->dhcp4o6_response->len = outgoing.packet_length; packet->dhcp4o6_response->buffer = NULL; if (!buffer_allocate(&packet->dhcp4o6_response->buffer, outgoing.packet_length, MDL)) { log_fatal("No memory to store DHCP4o6 reply."); } packet->dhcp4o6_response->data = packet->dhcp4o6_response->buffer->data; memcpy(packet->dhcp4o6_response->buffer->data, outgoing.raw, outgoing.packet_length); /* done */ if (subnet) subnet_dereference (&subnet, MDL); return; } #endif /* Set up the common stuff... */ to.sin_family = AF_INET; #ifdef HAVE_SA_LEN to.sin_len = sizeof to; #endif memset (to.sin_zero, 0, sizeof to.sin_zero); /* RFC2131 states the server SHOULD unicast to ciaddr. * There are two wrinkles - relays, and when ciaddr is zero. * There's actually no mention of relays at all in rfc2131 in * regard to DHCPINFORM, except to say we might get packets from * clients via them. Note: relays unicast to clients to the * "yiaddr" address, which servers are forbidden to set when * answering an inform. * * The solution: If ciaddr is zero, and giaddr is set, go via the * relay with the broadcast flag set to help the relay (with no * yiaddr and very likely no chaddr, it will have no idea where to * send the packet). * * If the ciaddr is zero and giaddr is not set, go via the source * IP address (but you are permitted to barf on their shoes). * * If ciaddr is not zero, send the packet there always. */ if (!raw.ciaddr.s_addr && gip.len) { memcpy(&to.sin_addr, gip.iabuf, 4); #if defined(RELAY_PORT) to.sin_port = relay_port ? relay_port : local_port; #else to.sin_port = local_port; #endif raw.flags |= htons(BOOTP_BROADCAST); } else { gip.len = 0; memcpy(&to.sin_addr, cip.iabuf, 4); to.sin_port = remote_port; } /* Report what we're sending. */ snprintf(msgbuf, sizeof msgbuf, "DHCPACK to %s (%s) via", piaddr(cip), (packet->raw->htype && packet->raw->hlen) ? print_hw_addr(packet->raw->htype, packet->raw->hlen, packet->raw->chaddr) : ""); log_info("%s %s", msgbuf, gip.len ? piaddr(gip) : packet->interface->name); errno = 0; interface = (fallback_interface ? fallback_interface : packet -> interface); result = send_packet(interface, &outgoing, &raw, outgoing.packet_length, from, &to, NULL); if (result < 0) { log_error ("%s:%d: Failed to send %d byte long packet over %s " "interface.", MDL, outgoing.packet_length, interface->name); } if (subnet) subnet_dereference (&subnet, MDL); } /*! * \brief Constructs and sends a DHCP Nak * * In order to populate options such as dhcp-server-id and * dhcp-client-identifier, the function creates a temporary option cache * and evaluates options based on the packet's shared-network or the * network_group in its absence, as well as the packet->clasess (if any). * * \param packet inbound packet received from the client * \param cip address requested by the client * \param network_group optional scope for use in setting up options */ void nak_lease (packet, cip, network_group) struct packet *packet; struct iaddr *cip; struct group *network_group; /* scope to use for options */ { struct sockaddr_in to; struct in_addr from; int result; struct dhcp_packet raw; unsigned char nak = DHCPNAK; struct packet outgoing; unsigned i; #if defined(RELAY_PORT) u_int16_t relay_port = 0; #endif struct option_state *options = (struct option_state *)0; struct option_cache *oc = (struct option_cache *)0; struct option_state *eval_options = NULL; option_state_allocate (&options, MDL); memset (&outgoing, 0, sizeof outgoing); memset (&raw, 0, sizeof raw); outgoing.raw = &raw; /* Set DHCP_MESSAGE_TYPE to DHCPNAK */ if (!option_cache_allocate (&oc, MDL)) { log_error ("No memory for DHCPNAK message type."); option_state_dereference (&options, MDL); return; } if (!make_const_data (&oc -> expression, &nak, sizeof nak, 0, 0, MDL)) { log_error ("No memory for expr_const expression."); option_cache_dereference (&oc, MDL); option_state_dereference (&options, MDL); return; } i = DHO_DHCP_MESSAGE_TYPE; option_code_hash_lookup(&oc->option, dhcp_universe.code_hash, &i, 0, MDL); save_option (&dhcp_universe, options, oc); option_cache_dereference (&oc, MDL); #if defined(RELAY_PORT) relay_port = dhcp_check_relayport(packet); #endif /* Set DHCP_MESSAGE to whatever the message is */ if (!option_cache_allocate (&oc, MDL)) { log_error ("No memory for DHCPNAK message type."); option_state_dereference (&options, MDL); return; } if (!make_const_data (&oc -> expression, (unsigned char *)dhcp_message, strlen (dhcp_message), 1, 0, MDL)) { log_error ("No memory for expr_const expression."); option_cache_dereference (&oc, MDL); option_state_dereference (&options, MDL); return; } i = DHO_DHCP_MESSAGE; option_code_hash_lookup(&oc->option, dhcp_universe.code_hash, &i, 0, MDL); save_option (&dhcp_universe, options, oc); option_cache_dereference (&oc, MDL); /* Setup the options at the global and subnet scopes. These * may be used to locate sever id option if enabled as well * for echo-client-id further on. (This allocates eval_options). */ eval_network_statements(&eval_options, packet, network_group); #if defined(SERVER_ID_FOR_NAK) /* Pass in the evaluated options so they can be searched for * server-id, otherwise source address comes from the interface * address. */ get_server_source_address(&from, eval_options, options, packet); #else /* Get server source address from the interface address */ get_server_source_address(&from, NULL, options, packet); #endif /* if defined(SERVER_ID_FOR_NAK) */ /* If there were agent options in the incoming packet, return * them. We do not check giaddr to detect the presence of a * relay, as this excludes "l2" relay agents which have no * giaddr to set. */ if (packet->options->universe_count > agent_universe.index && packet->options->universes [agent_universe.index]) { option_chain_head_reference ((struct option_chain_head **) &(options -> universes [agent_universe.index]), (struct option_chain_head *) packet -> options -> universes [agent_universe.index], MDL); } /* echo-client-id can specified at the class level so add class-scoped * options into eval_options. */ for (i = packet->class_count; i > 0; i--) { execute_statements_in_scope(NULL, packet, NULL, NULL, packet->options, eval_options, &global_scope, packet->classes[i - 1]->group, NULL, NULL); } /* Echo client id if we received and it's enabled */ echo_client_id(packet, NULL, eval_options, options); option_state_dereference (&eval_options, MDL); /* Do not use the client's requested parameter list. */ delete_option (&dhcp_universe, packet -> options, DHO_DHCP_PARAMETER_REQUEST_LIST); /* Set up the option buffer... */ outgoing.packet_length = cons_options (packet, outgoing.raw, (struct lease *)0, (struct client_state *)0, 0, packet -> options, options, &global_scope, 0, 0, 0, (struct data_string *)0, (char *)0); option_state_dereference (&options, MDL); /* memset (&raw.ciaddr, 0, sizeof raw.ciaddr);*/ raw.giaddr = packet -> raw -> giaddr; memcpy (raw.chaddr, packet -> raw -> chaddr, sizeof raw.chaddr); raw.hlen = packet -> raw -> hlen; raw.htype = packet -> raw -> htype; raw.xid = packet -> raw -> xid; raw.secs = packet -> raw -> secs; raw.flags = packet -> raw -> flags | htons (BOOTP_BROADCAST); raw.hops = packet -> raw -> hops; raw.op = BOOTREPLY; /* Make sure that the packet is at least as big as a BOOTP packet. */ if (outgoing.packet_length < BOOTP_MIN_LEN) outgoing.packet_length = BOOTP_MIN_LEN; /* Report what we're sending... */ #if defined(DHCPv6) && defined(DHCP4o6) if (dhcpv4_over_dhcpv6 && (packet->dhcp4o6_response != NULL)) { log_info ("DHCP4o6 DHCPNAK on %s to %s via %s", piaddr (*cip), print_hw_addr (packet -> raw -> htype, packet -> raw -> hlen, packet -> raw -> chaddr), piaddr(packet->client_addr)); } else #endif log_info ("DHCPNAK on %s to %s via %s", piaddr (*cip), print_hw_addr (packet -> raw -> htype, packet -> raw -> hlen, packet -> raw -> chaddr), packet -> raw -> giaddr.s_addr ? inet_ntoa (packet -> raw -> giaddr) : packet -> interface -> name); #ifdef DEBUG_PACKET dump_packet (packet); dump_raw ((unsigned char *)packet -> raw, packet -> packet_length); dump_packet (&outgoing); dump_raw ((unsigned char *)&raw, outgoing.packet_length); #endif #if defined(DHCPv6) && defined(DHCP4o6) if (dhcpv4_over_dhcpv6 && (packet->dhcp4o6_response != NULL)) { /* fill dhcp4o6_response */ packet->dhcp4o6_response->len = outgoing.packet_length; packet->dhcp4o6_response->buffer = NULL; if (!buffer_allocate(&packet->dhcp4o6_response->buffer, outgoing.packet_length, MDL)) { log_fatal("No memory to store DHCP4o6 reply."); } packet->dhcp4o6_response->data = packet->dhcp4o6_response->buffer->data; memcpy(packet->dhcp4o6_response->buffer->data, outgoing.raw, outgoing.packet_length); return; } #endif /* Set up the common stuff... */ to.sin_family = AF_INET; #ifdef HAVE_SA_LEN to.sin_len = sizeof to; #endif memset (to.sin_zero, 0, sizeof to.sin_zero); /* If this was gatewayed, send it back to the gateway. Otherwise, broadcast it on the local network. */ if (raw.giaddr.s_addr) { to.sin_addr = raw.giaddr; if (raw.giaddr.s_addr != htonl (INADDR_LOOPBACK)) #if defined(RELAY_PORT) to.sin_port = relay_port ? relay_port : local_port; #else to.sin_port = local_port; #endif else to.sin_port = remote_port; /* for testing. */ if (fallback_interface) { result = send_packet(fallback_interface, packet, &raw, outgoing.packet_length, from, &to, NULL); if (result < 0) { log_error ("%s:%d: Failed to send %d byte long " "packet over %s interface.", MDL, outgoing.packet_length, fallback_interface->name); } return; } } else { to.sin_addr = limited_broadcast; to.sin_port = remote_port; } errno = 0; result = send_packet(packet->interface, packet, &raw, outgoing.packet_length, from, &to, NULL); if (result < 0) { log_error ("%s:%d: Failed to send %d byte long packet over %s " "interface.", MDL, outgoing.packet_length, packet->interface->name); } } /*! * \brief Adds a dhcp-client-id option to a set of options * Given a set of input options, it searches for echo-client-id. If it is * defined and enabled, the given packet is searched for dhcp-client-id. If * the option is found it is replicated into the given set of output options. * This allows us to provide compliance with RFC 6842. It is called when we ack * or nak a lease. In the latter case we may or may not have created the * requisite scope to lookup echo-client-id. * * Note the flag packet.sv_echo_client_id is set to reflect the configuration * option. This bypases inaccessiblity of server_universe in cons_options() * which must amend the PRL (when not empty) if echoing is enabled. * * \param packet inbound packet received from the client * \param lease lease associated with this client (if one) * \param in_options options in which to search for echo-client-id * \param out_options options to which to save the client-id */ void echo_client_id(packet, lease, in_options, out_options) struct packet *packet; struct lease *lease; struct option_state *in_options; struct option_state *out_options; { struct option_cache *oc; int ignorep; /* Check if echo-client-id is enabled */ oc = lookup_option(&server_universe, in_options, SV_ECHO_CLIENT_ID); if (oc && evaluate_boolean_option_cache(&ignorep, packet, lease, NULL, packet->options, in_options, (lease ? &lease->scope : NULL), oc, MDL)) { struct data_string client_id; unsigned int opcode = DHO_DHCP_CLIENT_IDENTIFIER; /* Save knowledge that echo is enabled to the packet */ packet->sv_echo_client_id = ISC_TRUE; /* Now see if inbound packet contains client-id */ oc = lookup_option(&dhcp_universe, packet->options, opcode); memset(&client_id, 0, sizeof client_id); if (oc && evaluate_option_cache(&client_id, packet, NULL, NULL, packet->options, NULL, (lease ? &lease->scope : NULL), oc, MDL)) { /* Packet contained client-id, add it to out_options. */ oc = NULL; if (option_cache_allocate(&oc, MDL)) { if (make_const_data(&oc->expression, client_id.data, client_id.len, 1, 0, MDL)) { option_code_hash_lookup(&oc->option, dhcp_universe. code_hash, &opcode, 0, MDL); save_option(&dhcp_universe, out_options, oc); } option_cache_dereference(&oc, MDL); } } } } void check_pool_threshold (packet, lease, state) struct packet *packet; struct lease *lease; struct lease_state *state; { struct pool *pool = lease->pool; int used, count, high_threshold, poolhigh = 0, poollow = 0; char *shared_name = "no name"; if (pool == NULL) return; /* get a pointer to the name if we have one */ if ((pool->shared_network != NULL) && (pool->shared_network->name != NULL)) { shared_name = pool->shared_network->name; } count = pool->lease_count; used = count - (pool->free_leases + pool->backup_leases); /* The logged flag indicates if we have already crossed the high * threshold and emitted a log message. If it is set we check to * see if we have re-crossed the low threshold and need to reset * things. When we cross the high threshold we determine what * the low threshold is and save it into the low_threshold value. * When we cross that threshold we reset the logged flag and * the low_threshold to 0 which allows the high threshold message * to be emitted once again. * if we haven't recrossed the boundry we don't need to do anything. */ if (pool->logged !=0) { if (used <= pool->low_threshold) { pool->low_threshold = 0; pool->logged = 0; log_error("Pool threshold reset - shared subnet: %s; " "address: %s; low threshold %d/%d.", shared_name, piaddr(lease->ip_addr), used, count); } return; } /* find the high threshold */ if (get_option_int(&poolhigh, &server_universe, packet, lease, NULL, packet->options, state->options, state->options, &lease->scope, SV_LOG_THRESHOLD_HIGH, MDL) == 0) { /* no threshold bail out */ return; } /* We do have a threshold for this pool, see if its valid */ if ((poolhigh <= 0) || (poolhigh > 100)) { /* not valid */ return; } /* we have a valid value, have we exceeded it */ high_threshold = FIND_PERCENT(count, poolhigh); if (used < high_threshold) { /* nope, no more to do */ return; } /* we've exceeded it, output a message */ log_error("Pool threshold exceeded - shared subnet: %s; " "address: %s; high threshold %d%% %d/%d.", shared_name, piaddr(lease->ip_addr), poolhigh, used, count); /* handle the low threshold now, if we don't * have a valid one we default to 0. */ if ((get_option_int(&poollow, &server_universe, packet, lease, NULL, packet->options, state->options, state->options, &lease->scope, SV_LOG_THRESHOLD_LOW, MDL) == 0) || (poollow > 100)) { poollow = 0; } /* * If the low theshold is higher than the high threshold we continue to log * If it isn't then we set the flag saying we already logged and determine * what the reset threshold is. */ if (poollow < poolhigh) { pool->logged = 1; pool->low_threshold = FIND_PERCENT(count, poollow); } } void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp) struct packet *packet; struct lease *lease; unsigned int offer; TIME when; char *msg; int ms_nulltp; struct host_decl *hp; { struct lease *lt; struct lease_state *state; struct lease *next; struct host_decl *host = (struct host_decl *)0; TIME lease_time; TIME offered_lease_time; struct data_string d1; TIME min_lease_time; TIME max_lease_time; TIME default_lease_time; struct option_cache *oc; isc_result_t result; TIME ping_timeout; TIME lease_cltt; struct in_addr from; TIME remaining_time; struct iaddr cip; #if defined(DELAYED_ACK) /* By default we don't do the enqueue */ isc_boolean_t enqueue = ISC_FALSE; #endif int use_old_lease = 0; unsigned i, j; int s1; int ignorep; struct timeval tv; /* If we're already acking this lease, don't do it again. */ if (lease -> state) return; /* Save original cltt for comparison later. */ lease_cltt = lease->cltt; /* If the lease carries a host record, remember it. */ if (hp) host_reference (&host, hp, MDL); else if (lease -> host) host_reference (&host, lease -> host, MDL); /* Allocate a lease state structure... */ state = new_lease_state (MDL); if (!state) log_fatal ("unable to allocate lease state!"); state -> got_requested_address = packet -> got_requested_address; shared_network_reference (&state -> shared_network, packet -> interface -> shared_network, MDL); /* See if we got a server identifier option. */ if (lookup_option (&dhcp_universe, packet -> options, DHO_DHCP_SERVER_IDENTIFIER)) state -> got_server_identifier = 1; maybe_return_agent_options(packet, state->options); /* If we are offering a lease that is still currently valid, preserve the events. We need to do this because if the client does not REQUEST our offer, it will expire in 2 minutes, overriding the expire time in the currently in force lease. We want the expire events to be executed at that point. */ if (lease->ends <= cur_time && offer != DHCPOFFER) { /* Get rid of any old expiry or release statements - by executing the statements below, we will be inserting new ones if there are any to insert. */ if (lease->on_star.on_expiry) executable_statement_dereference (&lease->on_star.on_expiry, MDL); if (lease->on_star.on_commit) executable_statement_dereference (&lease->on_star.on_commit, MDL); if (lease->on_star.on_release) executable_statement_dereference (&lease->on_star.on_release, MDL); } /* Execute statements in scope starting with the subnet scope. */ execute_statements_in_scope (NULL, packet, lease, NULL, packet->options, state->options, &lease->scope, lease->subnet->group, NULL, NULL); /* If the lease is from a pool, run the pool scope. */ if (lease->pool) (execute_statements_in_scope(NULL, packet, lease, NULL, packet->options, state->options, &lease->scope, lease->pool->group, lease->pool-> shared_network->group, NULL)); /* Execute statements from class scopes. */ for (i = packet -> class_count; i > 0; i--) { execute_statements_in_scope(NULL, packet, lease, NULL, packet->options, state->options, &lease->scope, packet->classes[i - 1]->group, (lease->pool ? lease->pool->group : lease->subnet->group), NULL); } /* See if the client is only supposed to have one lease at a time, and if so, find its other leases and release them. We can only do this on DHCPREQUEST. It's a little weird to do this before looking at permissions, because the client might not actually _get_ a lease after we've done the permission check, but the assumption for this option is that the client has exactly one network interface, and will only ever remember one lease. So if it sends a DHCPREQUEST, and doesn't get the lease, it's already forgotten about its old lease, so we can too. */ if (packet -> packet_type == DHCPREQUEST && (oc = lookup_option (&server_universe, state -> options, SV_ONE_LEASE_PER_CLIENT)) && evaluate_boolean_option_cache (&ignorep, packet, lease, (struct client_state *)0, packet -> options, state -> options, &lease -> scope, oc, MDL)) { struct lease *seek; if (lease -> uid_len) { do { seek = (struct lease *)0; find_lease_by_uid (&seek, lease -> uid, lease -> uid_len, MDL); if (!seek) break; if (seek == lease && !seek -> n_uid) { lease_dereference (&seek, MDL); break; } next = (struct lease *)0; /* Don't release expired leases, and don't release the lease we're going to assign. */ next = (struct lease *)0; while (seek) { if (seek -> n_uid) lease_reference (&next, seek -> n_uid, MDL); if (seek != lease && seek -> binding_state != FTS_RELEASED && seek -> binding_state != FTS_EXPIRED && seek -> binding_state != FTS_RESET && seek -> binding_state != FTS_FREE && seek -> binding_state != FTS_BACKUP) break; lease_dereference (&seek, MDL); if (next) { lease_reference (&seek, next, MDL); lease_dereference (&next, MDL); } } if (next) lease_dereference (&next, MDL); if (seek) { release_lease (seek, packet); lease_dereference (&seek, MDL); } else break; } while (1); } if (!lease -> uid_len || (host && !host -> client_identifier.len && (oc = lookup_option (&server_universe, state -> options, SV_DUPLICATES)) && !evaluate_boolean_option_cache (&ignorep, packet, lease, (struct client_state *)0, packet -> options, state -> options, &lease -> scope, oc, MDL))) { do { seek = (struct lease *)0; find_lease_by_hw_addr (&seek, lease -> hardware_addr.hbuf, lease -> hardware_addr.hlen, MDL); if (!seek) break; if (seek == lease && !seek -> n_hw) { lease_dereference (&seek, MDL); break; } next = (struct lease *)0; while (seek) { if (seek -> n_hw) lease_reference (&next, seek -> n_hw, MDL); if (seek != lease && seek -> binding_state != FTS_RELEASED && seek -> binding_state != FTS_EXPIRED && seek -> binding_state != FTS_RESET && seek -> binding_state != FTS_FREE && seek -> binding_state != FTS_BACKUP) break; lease_dereference (&seek, MDL); if (next) { lease_reference (&seek, next, MDL); lease_dereference (&next, MDL); } } if (next) lease_dereference (&next, MDL); if (seek) { release_lease (seek, packet); lease_dereference (&seek, MDL); } else break; } while (1); } } /* Make sure this packet satisfies the configured minimum number of seconds. */ memset (&d1, 0, sizeof d1); if (offer == DHCPOFFER && (oc = lookup_option (&server_universe, state -> options, SV_MIN_SECS))) { if (evaluate_option_cache (&d1, packet, lease, (struct client_state *)0, packet -> options, state -> options, &lease -> scope, oc, MDL)) { if (d1.len && ntohs (packet -> raw -> secs) < d1.data [0]) { log_info("%s: configured min-secs value (%d) " "is greater than secs field (%d). " "message dropped.", msg, d1.data[0], ntohs(packet->raw->secs)); data_string_forget (&d1, MDL); free_lease_state (state, MDL); if (host) host_dereference (&host, MDL); return; } data_string_forget (&d1, MDL); } } /* Try to find a matching host declaration for this lease. */ if (!host) { struct host_decl *hp = (struct host_decl *)0; struct host_decl *h; /* Try to find a host_decl that matches the client identifier or hardware address on the packet, and has no fixed IP address. If there is one, hang it off the lease so that its option definitions can be used. */ oc = lookup_option (&dhcp_universe, packet -> options, DHO_DHCP_CLIENT_IDENTIFIER); if (oc && evaluate_option_cache (&d1, packet, lease, (struct client_state *)0, packet -> options, state -> options, &lease -> scope, oc, MDL)) { find_hosts_by_uid (&hp, d1.data, d1.len, MDL); data_string_forget (&d1, MDL); for (h = hp; h; h = h -> n_ipaddr) { if (!h -> fixed_addr) break; } if (h) host_reference (&host, h, MDL); if (hp != NULL) host_dereference(&hp, MDL); } if (!host) { find_hosts_by_haddr (&hp, packet -> raw -> htype, packet -> raw -> chaddr, packet -> raw -> hlen, MDL); for (h = hp; h; h = h -> n_ipaddr) { if (!h -> fixed_addr) break; } if (h) host_reference (&host, h, MDL); if (hp != NULL) host_dereference(&hp, MDL); } if (!host) { find_hosts_by_option(&hp, packet, packet->options, MDL); for (h = hp; h; h = h -> n_ipaddr) { if (!h -> fixed_addr) break; } if (h) host_reference (&host, h, MDL); if (hp != NULL) host_dereference(&hp, MDL); } } /* If we have a host_decl structure, run the options associated with its group. Whether the host decl struct is old or not. */ if (host) execute_statements_in_scope (NULL, packet, lease, NULL, packet->options, state->options, &lease->scope, host->group, (lease->pool ? lease->pool->group : lease->subnet->group), NULL); /* Drop the request if it's not allowed for this client. By default, unknown clients are allowed. */ if (!host && (oc = lookup_option (&server_universe, state -> options, SV_BOOT_UNKNOWN_CLIENTS)) && !evaluate_boolean_option_cache (&ignorep, packet, lease, (struct client_state *)0, packet -> options, state -> options, &lease -> scope, oc, MDL)) { if (!ignorep) log_info ("%s: unknown client", msg); free_lease_state (state, MDL); if (host) host_dereference (&host, MDL); return; } /* Drop the request if it's not allowed for this client. */ if (!offer && (oc = lookup_option (&server_universe, state -> options, SV_ALLOW_BOOTP)) && !evaluate_boolean_option_cache (&ignorep, packet, lease, (struct client_state *)0, packet -> options, state -> options, &lease -> scope, oc, MDL)) { if (!ignorep) log_info ("%s: bootp disallowed", msg); free_lease_state (state, MDL); if (host) host_dereference (&host, MDL); return; } /* Drop the request if booting is specifically denied. */ oc = lookup_option (&server_universe, state -> options, SV_ALLOW_BOOTING); if (oc && !evaluate_boolean_option_cache (&ignorep, packet, lease, (struct client_state *)0, packet -> options, state -> options, &lease -> scope, oc, MDL)) { if (!ignorep) log_info ("%s: booting disallowed", msg); free_lease_state (state, MDL); if (host) host_dereference (&host, MDL); return; } /* If we are configured to do per-class billing, do it. */ if (have_billing_classes && !(lease -> flags & STATIC_LEASE)) { /* See if the lease is currently being billed to a class, and if so, whether or not it can continue to be billed to that class. */ if (lease -> billing_class) { for (i = 0; i < packet -> class_count; i++) if (packet -> classes [i] == lease -> billing_class) break; if (i == packet -> class_count) { unbill_class(lease); /* Active lease billing change negates reuse */ if (lease->binding_state == FTS_ACTIVE) { lease->cannot_reuse = 1; } } } /* If we don't have an active billing, see if we need one, and if we do, try to do so. */ if (lease->billing_class == NULL) { char *cname = ""; int bill = 0; for (i = 0; i < packet->class_count; i++) { struct class *billclass, *subclass; billclass = packet->classes[i]; if (billclass->lease_limit) { bill++; if (bill_class(lease, billclass)) break; subclass = billclass->superclass; if (subclass == NULL) cname = subclass->name; else cname = billclass->name; } } if (bill != 0 && i == packet->class_count) { log_info("%s: no available billing: lease " "limit reached in all matching " "classes (last: '%s')", msg, cname); free_lease_state(state, MDL); if (host) host_dereference(&host, MDL); return; } /* * If this is an offer, undo the billing. We go * through all the steps above to bill a class so * we can hit the 'no available billing' mark and * abort without offering. But it just doesn't make * sense to permanently bill a class for a non-active * lease. This means on REQUEST, we will bill this * lease again (if there is a REQUEST). */ if (offer == DHCPOFFER && lease->billing_class != NULL && lease->binding_state != FTS_ACTIVE) unbill_class(lease); /* Lease billing change negates reuse */ if (lease->billing_class != NULL) { lease->cannot_reuse = 1; } } } /* Figure out the filename. */ oc = lookup_option (&server_universe, state -> options, SV_FILENAME); if (oc) evaluate_option_cache (&state -> filename, packet, lease, (struct client_state *)0, packet -> options, state -> options, &lease -> scope, oc, MDL); /* Choose a server name as above. */ oc = lookup_option (&server_universe, state -> options, SV_SERVER_NAME); if (oc) evaluate_option_cache (&state -> server_name, packet, lease, (struct client_state *)0, packet -> options, state -> options, &lease -> scope, oc, MDL); /* At this point, we have a lease that we can offer the client. Now we construct a lease structure that contains what we want, and call supersede_lease to do the right thing with it. */ lt = (struct lease *)0; result = lease_allocate (<, MDL); if (result != ISC_R_SUCCESS) { log_info ("%s: can't allocate temporary lease structure: %s", msg, isc_result_totext (result)); free_lease_state (state, MDL); if (host) host_dereference (&host, MDL); return; } /* Use the ip address of the lease that we finally found in the database. */ lt -> ip_addr = lease -> ip_addr; /* Start now. */ lt -> starts = cur_time; /* Figure out how long a lease to assign. If this is a dynamic BOOTP lease, its duration must be infinite. */ if (offer) { lt->flags &= ~BOOTP_LEASE; default_lease_time = DEFAULT_DEFAULT_LEASE_TIME; if ((oc = lookup_option (&server_universe, state -> options, SV_DEFAULT_LEASE_TIME))) { if (evaluate_option_cache (&d1, packet, lease, (struct client_state *)0, packet -> options, state -> options, &lease -> scope, oc, MDL)) { if (d1.len == sizeof (u_int32_t)) default_lease_time = getULong (d1.data); data_string_forget (&d1, MDL); } } if ((oc = lookup_option (&dhcp_universe, packet -> options, DHO_DHCP_LEASE_TIME))) s1 = evaluate_option_cache (&d1, packet, lease, (struct client_state *)0, packet -> options, state -> options, &lease -> scope, oc, MDL); else s1 = 0; if (s1 && (d1.len == 4)) { u_int32_t ones = 0xffffffff; /* One potential use of reserved leases is to allow * clients to signal reservation of their lease. They * can kinda sorta do this, if you squint hard enough, * by supplying an 'infinite' requested-lease-time * option. This is generally bad practice...you want * clients to return to the server on at least some * period (days, months, years) to get up-to-date * config state. So; * * 1) A client requests 0xffffffff lease-time. * 2) The server reserves the lease, and assigns a * <= max_lease_time lease-time to the client, which * we presume is much smaller than 0xffffffff. * 3) The client ultimately fails to renew its lease * (all clients go offline at some point). * 4) The server retains the reservation, although * the lease expires and passes through those states * as normal, it's placed in the 'reserved' queue, * and is under no circumstances allocated to any * clients. * * Whether the client knows its reserving its lease or * not, this can be a handy tool for a sysadmin. */ if ((memcmp(d1.data, &ones, 4) == 0) && (oc = lookup_option(&server_universe, state->options, SV_RESERVE_INFINITE)) && evaluate_boolean_option_cache(&ignorep, packet, lease, NULL, packet->options, state->options, &lease->scope, oc, MDL)) { lt->flags |= RESERVED_LEASE; if (!ignorep) log_info("Infinite-leasetime " "reservation made on %s.", piaddr(lt->ip_addr)); } lease_time = getULong (d1.data); } else lease_time = default_lease_time; if (s1) data_string_forget(&d1, MDL); /* See if there's a maximum lease time. */ max_lease_time = DEFAULT_MAX_LEASE_TIME; if ((oc = lookup_option (&server_universe, state -> options, SV_MAX_LEASE_TIME))) { if (evaluate_option_cache (&d1, packet, lease, (struct client_state *)0, packet -> options, state -> options, &lease -> scope, oc, MDL)) { if (d1.len == sizeof (u_int32_t)) max_lease_time = getULong (d1.data); data_string_forget (&d1, MDL); } } /* Enforce the maximum lease length. */ if (lease_time < 0 /* XXX */ || lease_time > max_lease_time) lease_time = max_lease_time; min_lease_time = DEFAULT_MIN_LEASE_TIME; if (min_lease_time > max_lease_time) min_lease_time = max_lease_time; if ((oc = lookup_option (&server_universe, state -> options, SV_MIN_LEASE_TIME))) { if (evaluate_option_cache (&d1, packet, lease, (struct client_state *)0, packet -> options, state -> options, &lease -> scope, oc, MDL)) { if (d1.len == sizeof (u_int32_t)) min_lease_time = getULong (d1.data); data_string_forget (&d1, MDL); } } /* CC: If there are less than adaptive-lease-time-threshold % free leases, hand out only short term leases */ memset(&d1, 0, sizeof(d1)); if (lease->pool && (oc = lookup_option(&server_universe, state->options, SV_ADAPTIVE_LEASE_TIME_THRESHOLD)) && evaluate_option_cache(&d1, packet, lease, NULL, packet->options, state->options, &lease->scope, oc, MDL)) { if (d1.len == 1 && d1.data[0] > 0 && d1.data[0] < 100) { TIME adaptive_time; int poolfilled, total, count; if (min_lease_time) adaptive_time = min_lease_time; else adaptive_time = DEFAULT_MIN_LEASE_TIME; /* Allow the client to keep its lease. */ if (lease->ends - cur_time > adaptive_time) adaptive_time = lease->ends - cur_time; count = lease->pool->lease_count; total = count - (lease->pool->free_leases + lease->pool->backup_leases); poolfilled = (total > (INT_MAX / 100)) ? total / (count / 100) : (total * 100) / count; log_debug("Adap-lease: Total: %d, Free: %d, " "Ends: %d, Adaptive: %d, Fill: %d, " "Threshold: %d", lease->pool->lease_count, lease->pool->free_leases, (int)(lease->ends - cur_time), (int)adaptive_time, poolfilled, d1.data[0]); if (poolfilled >= d1.data[0] && lease_time > adaptive_time) { log_info("Pool over threshold, time " "for %s reduced from %d to " "%d.", piaddr(lease->ip_addr), (int)lease_time, (int)adaptive_time); lease_time = adaptive_time; } } data_string_forget(&d1, MDL); } /* * If this is an ack check to see if we have used enough of * the pool to want to log a message */ if (offer == DHCPACK) check_pool_threshold(packet, lease, state); /* a client requests an address which is not yet active*/ if (lease->pool && lease->pool->valid_from && cur_time < lease->pool->valid_from) { /* NAK leases before pool activation date */ cip.len = 4; memcpy (cip.iabuf, <->ip_addr.iabuf, 4); nak_lease(packet, &cip, lease->subnet->group); free_lease_state (state, MDL); lease_dereference (<, MDL); if (host) host_dereference (&host, MDL); return; } /* CC: a) NAK current lease if past the expiration date b) extend lease only up to the expiration date, but not below min-lease-time Setting min-lease-time is essential for this to work! The value of min-lease-time determines the length of the transition window: A client renewing a second before the deadline will get a min-lease-time lease. Since the current ip might not be routable after the deadline, the client will be offline until it DISCOVERS again. Otherwise it will receive a NAK at T/2. A min-lease-time of 6 seconds effectively switches over all clients in this pool very quickly. */ if (lease->pool && lease->pool->valid_until) { if (cur_time >= lease->pool->valid_until) { /* NAK leases after pool expiration date */ cip.len = 4; memcpy (cip.iabuf, <->ip_addr.iabuf, 4); nak_lease(packet, &cip, lease->subnet->group); free_lease_state (state, MDL); lease_dereference (<, MDL); if (host) host_dereference (&host, MDL); return; } remaining_time = lease->pool->valid_until - cur_time; if (lease_time > remaining_time) lease_time = remaining_time; } if (lease_time < min_lease_time) { if (min_lease_time) lease_time = min_lease_time; else lease_time = default_lease_time; } #if defined (FAILOVER_PROTOCOL) /* Okay, we know the lease duration. Now check the failover state, if any. */ if (lease -> pool && lease -> pool -> failover_peer) { TIME new_lease_time = lease_time; dhcp_failover_state_t *peer = lease -> pool -> failover_peer; /* Copy previous lease failover ack-state. */ lt->tsfp = lease->tsfp; lt->atsfp = lease->atsfp; /* cltt set below */ /* Lease times less than MCLT are not a concern. */ if (lease_time > peer->mclt) { /* Each server can only offer a lease time * that is either equal to MCLT (at least), * or up to TSFP+MCLT. Only if the desired * lease time falls within TSFP+MCLT, can * the server allow it. */ if (lt->tsfp <= cur_time) new_lease_time = peer->mclt; else if ((cur_time + lease_time) > (lt->tsfp + peer->mclt)) new_lease_time = (lt->tsfp - cur_time) + peer->mclt; } /* Update potential expiry. Allow for the desired * lease time plus one half the actual (whether * modified downward or not) lease time, which is * actually an estimate of when the client will * renew. This way, the client will be able to get * the desired lease time upon renewal. */ if (offer == DHCPACK) { if (lease_time == INFINITE_TIME) { lt->tstp = MAX_TIME; } else { lt->tstp = leaseTimeCheck( (cur_time + lease_time + (new_lease_time / 2)), MAX_TIME - 1); } /* If we reduced the potential expiry time, * make sure we don't offer an old-expiry-time * lease for this lease before the change is * ack'd. */ if (lt->tstp < lt->tsfp) lt->tsfp = lt->tstp; } else lt->tstp = lease->tstp; /* Use failover-modified lease time. */ lease_time = new_lease_time; } #endif /* FAILOVER_PROTOCOL */ if (lease_time == INFINITE_TIME) { state->offered_expiry = MAX_TIME; } else { /* If the lease duration causes the time value to wrap, use the maximum expiry time. */ state->offered_expiry = leaseTimeCheck(cur_time + lease_time, MAX_TIME - 1); } if (when) lt -> ends = when; else lt -> ends = state -> offered_expiry; /* Don't make lease active until we actually get a DHCPREQUEST. */ if (offer == DHCPACK) lt -> next_binding_state = FTS_ACTIVE; else lt -> next_binding_state = lease -> binding_state; } else { lt->flags |= BOOTP_LEASE; lease_time = MAX_TIME - cur_time; if ((oc = lookup_option (&server_universe, state -> options, SV_BOOTP_LEASE_LENGTH))) { if (evaluate_option_cache (&d1, packet, lease, (struct client_state *)0, packet -> options, state -> options, &lease -> scope, oc, MDL)) { if (d1.len == sizeof (u_int32_t)) lease_time = getULong (d1.data); data_string_forget (&d1, MDL); } } if ((oc = lookup_option (&server_universe, state -> options, SV_BOOTP_LEASE_CUTOFF))) { if (evaluate_option_cache (&d1, packet, lease, (struct client_state *)0, packet -> options, state -> options, &lease -> scope, oc, MDL)) { if (d1.len == sizeof (u_int32_t)) lease_time = (getULong (d1.data) - cur_time); data_string_forget (&d1, MDL); } } lt -> ends = state -> offered_expiry = cur_time + lease_time; lt -> next_binding_state = FTS_ACTIVE; } /* Update Client Last Transaction Time. */ lt->cltt = cur_time; /* See if we want to record the uid for this client */ oc = lookup_option(&server_universe, state->options, SV_IGNORE_CLIENT_UIDS); if ((oc == NULL) || !evaluate_boolean_option_cache(&ignorep, packet, lease, NULL, packet->options, state->options, &lease->scope, oc, MDL)) { /* Record the uid, if given... */ oc = lookup_option (&dhcp_universe, packet -> options, DHO_DHCP_CLIENT_IDENTIFIER); if (oc && evaluate_option_cache(&d1, packet, lease, NULL, packet->options, state->options, &lease->scope, oc, MDL)) { if (d1.len <= sizeof(lt->uid_buf)) { memcpy(lt->uid_buf, d1.data, d1.len); lt->uid = lt->uid_buf; lt->uid_max = sizeof(lt->uid_buf); lt->uid_len = d1.len; } else { unsigned char *tuid; lt->uid_max = d1.len; lt->uid_len = d1.len; tuid = (unsigned char *)dmalloc(lt->uid_max, MDL); /* XXX inelegant */ if (!tuid) log_fatal ("no memory for large uid."); memcpy(tuid, d1.data, lt->uid_len); lt->uid = tuid; } data_string_forget (&d1, MDL); } } if (host) { host_reference (< -> host, host, MDL); host_dereference (&host, MDL); } if (lease -> subnet) subnet_reference (< -> subnet, lease -> subnet, MDL); if (lease -> billing_class) class_reference (< -> billing_class, lease -> billing_class, MDL); /* Set a flag if this client is a broken client that NUL terminates string options and expects us to do likewise. */ if (ms_nulltp) lease -> flags |= MS_NULL_TERMINATION; else lease -> flags &= ~MS_NULL_TERMINATION; /* Save any bindings. */ if (lease -> scope) { binding_scope_reference (< -> scope, lease -> scope, MDL); binding_scope_dereference (&lease -> scope, MDL); } if (lease -> agent_options) option_chain_head_reference (< -> agent_options, lease -> agent_options, MDL); /* Save the vendor-class-identifier for DHCPLEASEQUERY. */ oc = lookup_option(&dhcp_universe, packet->options, DHO_VENDOR_CLASS_IDENTIFIER); if (oc != NULL && evaluate_option_cache(&d1, packet, NULL, NULL, packet->options, NULL, <->scope, oc, MDL)) { if (d1.len != 0) { bind_ds_value(<->scope, "vendor-class-identifier", &d1); } data_string_forget(&d1, MDL); } /* If we got relay agent information options from the packet, then * cache them for renewal in case the relay agent can't supply them * when the client unicasts. The options may be from an addressed * "l3" relay, or from an unaddressed "l2" relay which does not set * giaddr. */ if (!packet->agent_options_stashed && (packet->options != NULL) && packet->options->universe_count > agent_universe.index && packet->options->universes[agent_universe.index] != NULL) { oc = lookup_option (&server_universe, state -> options, SV_STASH_AGENT_OPTIONS); if (!oc || evaluate_boolean_option_cache (&ignorep, packet, lease, (struct client_state *)0, packet -> options, state -> options, &lease -> scope, oc, MDL)) { if (lt -> agent_options) option_chain_head_dereference (< -> agent_options, MDL); option_chain_head_reference (< -> agent_options, (struct option_chain_head *) packet -> options -> universes [agent_universe.index], MDL); } } /* Replace the old lease hostname with the new one, if it's changed. */ oc = lookup_option (&dhcp_universe, packet -> options, DHO_HOST_NAME); if (oc) s1 = evaluate_option_cache (&d1, packet, (struct lease *)0, (struct client_state *)0, packet -> options, (struct option_state *)0, &global_scope, oc, MDL); else s1 = 0; if (oc && s1 && lease -> client_hostname && strlen (lease -> client_hostname) == d1.len && !memcmp (lease -> client_hostname, d1.data, d1.len)) { /* Hasn't changed. */ data_string_forget (&d1, MDL); lt -> client_hostname = lease -> client_hostname; lease -> client_hostname = (char *)0; } else if (oc && s1) { lt -> client_hostname = dmalloc (d1.len + 1, MDL); if (!lt -> client_hostname) log_error ("no memory for client hostname."); else { memcpy (lt -> client_hostname, d1.data, d1.len); lt -> client_hostname [d1.len] = 0; } data_string_forget (&d1, MDL); /* hostname changed, can't reuse lease */ lease->cannot_reuse = 1; } /* Record the hardware address, if given... */ lt -> hardware_addr.hlen = packet -> raw -> hlen + 1; lt -> hardware_addr.hbuf [0] = packet -> raw -> htype; memcpy (< -> hardware_addr.hbuf [1], packet -> raw -> chaddr, sizeof packet -> raw -> chaddr); /* * If client has requested the lease become infinite, then it * doens't qualify for reuse even if it's younger than the * dhcp-cache-threshold. */ if ((lt->flags & RESERVED_LEASE) && !(lease->flags & RESERVED_LEASE)) { log_debug ("Cannot reuse: lease is changing to RESERVED"); lease->cannot_reuse = 1; } lt->flags |= lease->flags & ~PERSISTENT_FLAGS; /* If there are statements to execute when the lease is committed, execute them. */ if (lease->on_star.on_commit && (!offer || offer == DHCPACK)) { execute_statements (NULL, packet, lt, NULL, packet->options, state->options, <->scope, lease->on_star.on_commit, NULL); if (lease->on_star.on_commit) executable_statement_dereference (&lease->on_star.on_commit, MDL); } #ifdef NSUPDATE /* Perform DDNS updates, if configured to. */ if ((!offer || offer == DHCPACK) && (!(oc = lookup_option (&server_universe, state -> options, SV_DDNS_UPDATES)) || evaluate_boolean_option_cache (&ignorep, packet, lt, (struct client_state *)0, packet -> options, state -> options, < -> scope, oc, MDL))) { ddns_updates(packet, lt, lease, NULL, NULL, state->options); } #endif /* NSUPDATE */ /* Don't call supersede_lease on a mocked-up lease. */ if (lease -> flags & STATIC_LEASE) { /* Copy the hardware address into the static lease structure. */ lease -> hardware_addr.hlen = packet -> raw -> hlen + 1; lease -> hardware_addr.hbuf [0] = packet -> raw -> htype; memcpy (&lease -> hardware_addr.hbuf [1], packet -> raw -> chaddr, sizeof packet -> raw -> chaddr); /* XXX */ } else { int commit = (!offer || (offer == DHCPACK)); /* If dhcp-cache-threshold is enabled, see if "lease" can * be reused. */ use_old_lease = reuse_lease(packet, lt, lease, state, offer); if (use_old_lease == 1) { commit = 0; } #if !defined(DELAYED_ACK) /* Install the new information on 'lt' onto the lease at * 'lease'. If this is a DHCPOFFER, it is a 'soft' promise, * if it is a DHCPACK, it is a 'hard' binding, so it needs * to be recorded and propogated immediately. If the update * fails, don't ACK it (or BOOTREPLY) either; we may give * the same lease to another client later, and that would be * a conflict. */ if ((use_old_lease == 0) && !supersede_lease(lease, lt, commit, offer == DHCPACK, offer == DHCPACK, 0)) { #else /* defined(DELAYED_ACK) */ /* * If there already isn't a need for a lease commit, and we * can just answer right away, set a flag to indicate this. */ if (commit) enqueue = ISC_TRUE; /* Install the new information on 'lt' onto the lease at * 'lease'. We will not 'commit' this information to disk * yet (fsync()), we will 'propogate' the information if * this is BOOTP or a DHCPACK, but we will not 'pimmediate'ly * transmit failover binding updates (this is delayed until * after the fsync()). If the update fails, don't ACK it (or * BOOTREPLY either); we may give the same lease out to a * different client, and that would be a conflict. */ if ((use_old_lease == 0) && !supersede_lease(lease, lt, 0, !offer || offer == DHCPACK, 0, 0)) { #endif log_info ("%s: database update failed", msg); free_lease_state (state, MDL); lease_dereference (<, MDL); return; } } lease_dereference (<, MDL); /* Remember the interface on which the packet arrived. */ state -> ip = packet -> interface; /* Remember the giaddr, xid, secs, flags and hops. */ state -> giaddr = packet -> raw -> giaddr; state -> ciaddr = packet -> raw -> ciaddr; state -> xid = packet -> raw -> xid; state -> secs = packet -> raw -> secs; state -> bootp_flags = packet -> raw -> flags; state -> hops = packet -> raw -> hops; state -> offer = offer; /* If we're always supposed to broadcast to this client, set the broadcast bit in the bootp flags field. */ if ((oc = lookup_option (&server_universe, state -> options, SV_ALWAYS_BROADCAST)) && evaluate_boolean_option_cache (&ignorep, packet, lease, (struct client_state *)0, packet -> options, state -> options, &lease -> scope, oc, MDL)) state -> bootp_flags |= htons (BOOTP_BROADCAST); /* Get the Maximum Message Size option from the packet, if one was sent. */ oc = lookup_option (&dhcp_universe, packet -> options, DHO_DHCP_MAX_MESSAGE_SIZE); if (oc && evaluate_option_cache (&d1, packet, lease, (struct client_state *)0, packet -> options, state -> options, &lease -> scope, oc, MDL)) { if (d1.len == sizeof (u_int16_t)) state -> max_message_size = getUShort (d1.data); data_string_forget (&d1, MDL); } else { oc = lookup_option (&dhcp_universe, state -> options, DHO_DHCP_MAX_MESSAGE_SIZE); if (oc && evaluate_option_cache (&d1, packet, lease, (struct client_state *)0, packet -> options, state -> options, &lease -> scope, oc, MDL)) { if (d1.len == sizeof (u_int16_t)) state -> max_message_size = getUShort (d1.data); data_string_forget (&d1, MDL); } } /* Get the Subnet Selection option from the packet, if one was sent. */ if ((oc = lookup_option (&dhcp_universe, packet -> options, DHO_SUBNET_SELECTION))) { /* Make a copy of the data. */ struct option_cache *noc = (struct option_cache *)0; if (option_cache_allocate (&noc, MDL)) { if (oc -> data.len) data_string_copy (&noc -> data, &oc -> data, MDL); if (oc -> expression) expression_reference (&noc -> expression, oc -> expression, MDL); if (oc -> option) option_reference(&(noc->option), oc->option, MDL); save_option (&dhcp_universe, state -> options, noc); option_cache_dereference (&noc, MDL); } } /* Now, if appropriate, put in DHCP-specific options that override those. */ if (state -> offer) { i = DHO_DHCP_MESSAGE_TYPE; oc = (struct option_cache *)0; if (option_cache_allocate (&oc, MDL)) { if (make_const_data (&oc -> expression, &state -> offer, 1, 0, 0, MDL)) { option_code_hash_lookup(&oc->option, dhcp_universe.code_hash, &i, 0, MDL); save_option (&dhcp_universe, state -> options, oc); } option_cache_dereference (&oc, MDL); } get_server_source_address(&from, state->options, state->options, packet); memcpy(state->from.iabuf, &from, sizeof(from)); state->from.len = sizeof(from); offered_lease_time = state -> offered_expiry - cur_time; putULong(state->expiry, (u_int32_t)offered_lease_time); i = DHO_DHCP_LEASE_TIME; oc = (struct option_cache *)0; if (option_cache_allocate (&oc, MDL)) { if (make_const_data(&oc->expression, state->expiry, 4, 0, 0, MDL)) { option_code_hash_lookup(&oc->option, dhcp_universe.code_hash, &i, 0, MDL); save_option (&dhcp_universe, state -> options, oc); } option_cache_dereference (&oc, MDL); } /* * Validate any configured renew or rebinding times against * the determined lease time. Do rebinding first so that * the renew time can be validated against the rebind time. */ if ((oc = lookup_option(&dhcp_universe, state->options, DHO_DHCP_REBINDING_TIME)) != NULL && evaluate_option_cache(&d1, packet, lease, NULL, packet->options, state->options, &lease->scope, oc, MDL)) { TIME rebind_time = getULong(d1.data); /* Drop the configured (invalid) rebinding time. */ if (rebind_time >= offered_lease_time) delete_option(&dhcp_universe, state->options, DHO_DHCP_REBINDING_TIME); else /* XXX: variable is reused. */ offered_lease_time = rebind_time; data_string_forget(&d1, MDL); } if ((oc = lookup_option(&dhcp_universe, state->options, DHO_DHCP_RENEWAL_TIME)) != NULL && evaluate_option_cache(&d1, packet, lease, NULL, packet->options, state->options, &lease->scope, oc, MDL)) { if (getULong(d1.data) >= offered_lease_time) delete_option(&dhcp_universe, state->options, DHO_DHCP_RENEWAL_TIME); data_string_forget(&d1, MDL); } } else { /* XXXSK: should we use get_server_source_address() here? */ if (state -> ip -> address_count) { state -> from.len = sizeof state -> ip -> addresses [0]; memcpy (state -> from.iabuf, &state -> ip -> addresses [0], state -> from.len); } } /* Figure out the address of the boot file server. */ memset (&state -> siaddr, 0, sizeof state -> siaddr); if ((oc = lookup_option (&server_universe, state -> options, SV_NEXT_SERVER))) { if (evaluate_option_cache (&d1, packet, lease, (struct client_state *)0, packet -> options, state -> options, &lease -> scope, oc, MDL)) { /* If there was more than one answer, take the first. */ if (d1.len >= 4 && d1.data) memcpy (&state -> siaddr, d1.data, 4); data_string_forget (&d1, MDL); } } /* Use the subnet mask from the subnet declaration if no other mask has been provided. */ i = DHO_SUBNET_MASK; if (!lookup_option (&dhcp_universe, state -> options, i)) { oc = (struct option_cache *)0; if (option_cache_allocate (&oc, MDL)) { if (make_const_data (&oc -> expression, lease -> subnet -> netmask.iabuf, lease -> subnet -> netmask.len, 0, 0, MDL)) { option_code_hash_lookup(&oc->option, dhcp_universe.code_hash, &i, 0, MDL); save_option (&dhcp_universe, state -> options, oc); } option_cache_dereference (&oc, MDL); } } /* Use the name of the host declaration if there is one and no hostname has otherwise been provided, and if the use-host-decl-name flag is set. */ use_host_decl_name(packet, lease, state->options); /* Send client_id back if we received it and echo-client-id is on. */ echo_client_id(packet, lease, state->options, state->options); /* If we don't have a hostname yet, and we've been asked to do a reverse lookup to find the hostname, do it. */ i = DHO_HOST_NAME; j = SV_GET_LEASE_HOSTNAMES; if (!lookup_option(&dhcp_universe, state->options, i) && evaluate_boolean_option_cache (&ignorep, packet, lease, NULL, packet->options, state->options, &lease->scope, lookup_option (&server_universe, state->options, j), MDL)) { struct in_addr ia; struct hostent *h; memcpy (&ia, lease -> ip_addr.iabuf, 4); h = gethostbyaddr ((char *)&ia, sizeof ia, AF_INET); if (!h) log_error ("No hostname for %s", inet_ntoa (ia)); else { oc = (struct option_cache *)0; if (option_cache_allocate (&oc, MDL)) { if (make_const_data (&oc -> expression, ((unsigned char *) h -> h_name), strlen (h -> h_name) + 1, 1, 1, MDL)) { option_code_hash_lookup(&oc->option, dhcp_universe.code_hash, &i, 0, MDL); save_option (&dhcp_universe, state -> options, oc); } option_cache_dereference (&oc, MDL); } } } /* If so directed, use the leased IP address as the router address. This supposedly makes Win95 machines ARP for all IP addresses, so if the local router does proxy arp, you win. */ if (evaluate_boolean_option_cache (&ignorep, packet, lease, (struct client_state *)0, packet -> options, state -> options, &lease -> scope, lookup_option (&server_universe, state -> options, SV_USE_LEASE_ADDR_FOR_DEFAULT_ROUTE), MDL)) { i = DHO_ROUTERS; oc = lookup_option (&dhcp_universe, state -> options, i); if (!oc) { oc = (struct option_cache *)0; if (option_cache_allocate (&oc, MDL)) { if (make_const_data (&oc -> expression, lease -> ip_addr.iabuf, lease -> ip_addr.len, 0, 0, MDL)) { option_code_hash_lookup(&oc->option, dhcp_universe.code_hash, &i, 0, MDL); save_option (&dhcp_universe, state -> options, oc); } option_cache_dereference (&oc, MDL); } } } /* If a site option space has been specified, use that for site option codes. */ i = SV_SITE_OPTION_SPACE; if ((oc = lookup_option (&server_universe, state -> options, i)) && evaluate_option_cache (&d1, packet, lease, (struct client_state *)0, packet -> options, state -> options, &lease -> scope, oc, MDL)) { struct universe *u = (struct universe *)0; if (!universe_hash_lookup (&u, universe_hash, (const char *)d1.data, d1.len, MDL)) { log_error ("unknown option space %s.", d1.data); return; } state -> options -> site_universe = u -> index; state->options->site_code_min = find_min_site_code(u); data_string_forget (&d1, MDL); } else { state -> options -> site_code_min = 0; state -> options -> site_universe = dhcp_universe.index; } /* If the client has provided a list of options that it wishes returned, use it to prioritize. If there's a parameter request list in scope, use that in preference. Otherwise use the default priority list. */ oc = lookup_option (&dhcp_universe, state -> options, DHO_DHCP_PARAMETER_REQUEST_LIST); if (!oc) oc = lookup_option (&dhcp_universe, packet -> options, DHO_DHCP_PARAMETER_REQUEST_LIST); if (oc) evaluate_option_cache (&state -> parameter_request_list, packet, lease, (struct client_state *)0, packet -> options, state -> options, &lease -> scope, oc, MDL); #ifdef DEBUG_PACKET dump_packet (packet); dump_raw ((unsigned char *)packet -> raw, packet -> packet_length); #endif lease -> state = state; log_info ("%s", msg); /* Hang the packet off the lease state. */ packet_reference (&lease -> state -> packet, packet, MDL); /* If this is a DHCPOFFER, ping the lease address before actually sending the offer. */ if (offer == DHCPOFFER && !(lease -> flags & STATIC_LEASE) && (((cur_time - lease_cltt) > 60) || (lease->binding_state == FTS_ABANDONED)) && (!(oc = lookup_option (&server_universe, state -> options, SV_PING_CHECKS)) || evaluate_boolean_option_cache (&ignorep, packet, lease, (struct client_state *)0, packet -> options, state -> options, &lease -> scope, oc, MDL))) { icmp_echorequest (&lease -> ip_addr); /* Determine whether to use configured or default ping timeout. */ if ((oc = lookup_option (&server_universe, state -> options, SV_PING_TIMEOUT)) && evaluate_option_cache (&d1, packet, lease, NULL, packet -> options, state -> options, &lease -> scope, oc, MDL)) { if (d1.len == sizeof (u_int32_t)) ping_timeout = getULong (d1.data); else ping_timeout = DEFAULT_PING_TIMEOUT; data_string_forget (&d1, MDL); } else ping_timeout = DEFAULT_PING_TIMEOUT; #ifdef DEBUG log_debug ("Ping timeout: %ld", (long)ping_timeout); #endif /* * Set a timeout for 'ping-timeout' seconds from NOW, including * current microseconds. As ping-timeout defaults to 1, the * exclusion of current microseconds causes a value somewhere * /between/ zero and one. */ tv.tv_sec = cur_tv.tv_sec + ping_timeout; tv.tv_usec = cur_tv.tv_usec; add_timeout (&tv, lease_ping_timeout, lease, (tvref_t)lease_reference, (tvunref_t)lease_dereference); ++outstanding_pings; } else { lease->cltt = cur_time; #if defined(DELAYED_ACK) if (enqueue) delayed_ack_enqueue(lease); else #endif dhcp_reply(lease); } } #if defined(DELAYED_ACK) /* * CC: queue single ACK: * - write the lease (but do not fsync it yet) * - add to double linked list * - commit if more than xx ACKs pending * - if necessary set the max timer and bump the next timer * but only up to the max timer value. */ static void delayed_ack_enqueue(struct lease *lease) { struct leasequeue *q; if (!write_lease(lease)) return; if (free_ackqueue) { q = free_ackqueue; free_ackqueue = q->next; } else { q = ((struct leasequeue *) dmalloc(sizeof(struct leasequeue), MDL)); if (!q) log_fatal("delayed_ack_enqueue: no memory!"); } memset(q, 0, sizeof *q); /* prepend to ackqueue*/ lease_reference(&q->lease, lease, MDL); q->next = ackqueue_head; ackqueue_head = q; if (!ackqueue_tail) ackqueue_tail = q; else q->next->prev = q; outstanding_acks++; if (outstanding_acks > max_outstanding_acks) { /* Cancel any pending timeout and call handler directly */ cancel_timeout(delayed_acks_timer, NULL); delayed_acks_timer(NULL); } else { struct timeval next_fsync; if (max_fsync.tv_sec == 0 && max_fsync.tv_usec == 0) { /* set the maximum time we'll wait */ max_fsync.tv_sec = cur_tv.tv_sec + max_ack_delay_secs; max_fsync.tv_usec = cur_tv.tv_usec + max_ack_delay_usecs; if (max_fsync.tv_usec >= 1000000) { max_fsync.tv_sec++; max_fsync.tv_usec -= 1000000; } } /* Set the timeout */ next_fsync.tv_sec = cur_tv.tv_sec; next_fsync.tv_usec = cur_tv.tv_usec + min_ack_delay_usecs; if (next_fsync.tv_usec >= 1000000) { next_fsync.tv_sec++; next_fsync.tv_usec -= 1000000; } /* but not more than the max */ if ((next_fsync.tv_sec > max_fsync.tv_sec) || ((next_fsync.tv_sec == max_fsync.tv_sec) && (next_fsync.tv_usec > max_fsync.tv_usec))) { next_fsync.tv_sec = max_fsync.tv_sec; next_fsync.tv_usec = max_fsync.tv_usec; } add_timeout(&next_fsync, delayed_acks_timer, NULL, (tvref_t) NULL, (tvunref_t) NULL); } } /* Processes any delayed acks: * Commits the leases and then for each delayed ack: * - Update the failover peer if we're in failover * - Send the REPLY to the client */ static void delayed_acks_timer(void *foo) { struct leasequeue *ack, *p; /* Reset max fsync */ memset(&max_fsync, 0, sizeof(max_fsync)); if (!outstanding_acks) { /* Nothing to do, so punt, shouldn't happen? */ return; } /* Commit the leases first */ commit_leases(); /* Now process the delayed ACKs - update failover peer - send out the ACK packets - move the queue slots to the free list */ /* process from bottom to retain packet order */ for (ack = ackqueue_tail ; ack ; ack = p) { p = ack->prev; #if defined(FAILOVER_PROTOCOL) /* If we're in failover we need to send any deferred * bind updates as well as the replies */ if (ack->lease->pool) { dhcp_failover_state_t *fpeer; fpeer = ack->lease->pool->failover_peer; if (fpeer && fpeer->link_to_peer) { dhcp_failover_send_updates(fpeer); } } #endif /* dhcp_reply() requires that the reply state still be valid */ if (ack->lease->state == NULL) log_error("delayed ack for %s has gone stale", piaddr(ack->lease->ip_addr)); else { dhcp_reply(ack->lease); } lease_dereference(&ack->lease, MDL); ack->next = free_ackqueue; free_ackqueue = ack; } ackqueue_head = NULL; ackqueue_tail = NULL; outstanding_acks = 0; } #if defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) void relinquish_ackqueue(void) { struct leasequeue *q, *n; for (q = ackqueue_head ; q ; q = n) { n = q->next; dfree(q, MDL); } for (q = free_ackqueue ; q ; q = n) { n = q->next; dfree(q, MDL); } } #endif #endif /* defined(DELAYED_ACK) */ void dhcp_reply (lease) struct lease *lease; { int bufs = 0; unsigned packet_length; struct dhcp_packet raw; struct sockaddr_in to; struct in_addr from; struct hardware hto; int result; struct lease_state *state = lease -> state; int nulltp, bootpp, unicastp = 1; #if defined(RELAY_PORT) u_int16_t relay_port = 0; #endif struct data_string d1; const char *s; if (!state) log_fatal ("dhcp_reply was supplied lease with no state!"); /* Compose a response for the client... */ memset (&raw, 0, sizeof raw); memset (&d1, 0, sizeof d1); /* Copy in the filename if given; otherwise, flag the filename buffer as available for options. */ if (state -> filename.len && state -> filename.data) { memcpy (raw.file, state -> filename.data, state -> filename.len > sizeof raw.file ? sizeof raw.file : state -> filename.len); if (sizeof raw.file > state -> filename.len) memset (&raw.file [state -> filename.len], 0, (sizeof raw.file) - state -> filename.len); else log_info("file name longer than packet field " "truncated - field: %lu name: %d %.*s", (unsigned long)sizeof(raw.file), state->filename.len, (int)state->filename.len, state->filename.data); } else bufs |= 1; /* Copy in the server name if given; otherwise, flag the server_name buffer as available for options. */ if (state -> server_name.len && state -> server_name.data) { memcpy (raw.sname, state -> server_name.data, state -> server_name.len > sizeof raw.sname ? sizeof raw.sname : state -> server_name.len); if (sizeof raw.sname > state -> server_name.len) memset (&raw.sname [state -> server_name.len], 0, (sizeof raw.sname) - state -> server_name.len); else log_info("server name longer than packet field " "truncated - field: %lu name: %d %.*s", (unsigned long)sizeof(raw.sname), state->server_name.len, (int)state->server_name.len, state->server_name.data); } else bufs |= 2; /* XXX */ memcpy (raw.chaddr, &lease -> hardware_addr.hbuf [1], sizeof raw.chaddr); raw.hlen = lease -> hardware_addr.hlen - 1; raw.htype = lease -> hardware_addr.hbuf [0]; /* See if this is a Microsoft client that NUL-terminates its strings and expects us to do likewise... */ if (lease -> flags & MS_NULL_TERMINATION) nulltp = 1; else nulltp = 0; /* See if this is a bootp client... */ if (state -> offer) bootpp = 0; else bootpp = 1; /* Insert such options as will fit into the buffer. */ packet_length = cons_options (state -> packet, &raw, lease, (struct client_state *)0, state -> max_message_size, state -> packet -> options, state -> options, &global_scope, bufs, nulltp, bootpp, &state -> parameter_request_list, (char *)0); memcpy (&raw.ciaddr, &state -> ciaddr, sizeof raw.ciaddr); memcpy (&raw.yiaddr, lease -> ip_addr.iabuf, 4); raw.siaddr = state -> siaddr; raw.giaddr = state -> giaddr; raw.xid = state -> xid; raw.secs = state -> secs; raw.flags = state -> bootp_flags; raw.hops = state -> hops; raw.op = BOOTREPLY; if (lease -> client_hostname) { if ((strlen (lease -> client_hostname) <= 64) && db_printable((unsigned char *)lease->client_hostname)) s = lease -> client_hostname; else s = "Hostname Unsuitable for Printing"; } else s = (char *)0; /* Make sure outgoing packets are at least as big as a BOOTP packet. */ if (packet_length < BOOTP_MIN_LEN) packet_length = BOOTP_MIN_LEN; #if defined(DHCPv6) && defined(DHCP4o6) if (dhcpv4_over_dhcpv6 && (state->packet->dhcp4o6_response != NULL)) { /* Say what we're doing... */ log_info ("DHCP4o6 %s on %s to %s %s%s%svia %s", (state -> offer ? (state -> offer == DHCPACK ? "DHCPACK" : "DHCPOFFER") : "BOOTREPLY"), piaddr (lease -> ip_addr), (lease -> hardware_addr.hlen ? print_hw_addr (lease -> hardware_addr.hbuf [0], lease -> hardware_addr.hlen - 1, &lease -> hardware_addr.hbuf [1]) : print_hex_1(lease->uid_len, lease->uid, 60)), s ? "(" : "", s ? s : "", s ? ") " : "", piaddr(state->packet->client_addr)); /* fill dhcp4o6_response */ state->packet->dhcp4o6_response->len = packet_length; state->packet->dhcp4o6_response->buffer = NULL; if (!buffer_allocate(&state->packet->dhcp4o6_response->buffer, packet_length, MDL)) { log_fatal("No memory to store DHCP4o6 reply."); } state->packet->dhcp4o6_response->data = state->packet->dhcp4o6_response->buffer->data; memcpy(state->packet->dhcp4o6_response->buffer->data, &raw, packet_length); /* done */ free_lease_state (state, MDL); lease -> state = (struct lease_state *)0; return; } #endif /* Say what we're doing... */ log_info ("%s on %s to %s %s%s%svia %s", (state -> offer ? (state -> offer == DHCPACK ? "DHCPACK" : "DHCPOFFER") : "BOOTREPLY"), piaddr (lease -> ip_addr), (lease -> hardware_addr.hlen ? print_hw_addr (lease -> hardware_addr.hbuf [0], lease -> hardware_addr.hlen - 1, &lease -> hardware_addr.hbuf [1]) : print_hex_1(lease->uid_len, lease->uid, 60)), s ? "(" : "", s ? s : "", s ? ") " : "", (state -> giaddr.s_addr ? inet_ntoa (state -> giaddr) : state -> ip -> name)); #ifdef DEBUG_PACKET dump_raw ((unsigned char *)&raw, packet_length); #endif /* Set up the hardware address... */ hto.hlen = lease -> hardware_addr.hlen; memcpy (hto.hbuf, lease -> hardware_addr.hbuf, hto.hlen); to.sin_family = AF_INET; #ifdef HAVE_SA_LEN to.sin_len = sizeof to; #endif memset (to.sin_zero, 0, sizeof to.sin_zero); #if defined(RELAY_PORT) relay_port = dhcp_check_relayport(state->packet); #endif /* If this was gatewayed, send it back to the gateway... */ if (raw.giaddr.s_addr) { to.sin_addr = raw.giaddr; if (raw.giaddr.s_addr != htonl (INADDR_LOOPBACK)) #if defined(RELAY_PORT) to.sin_port = relay_port ? relay_port : local_port; #else to.sin_port = local_port; #endif else to.sin_port = remote_port; /* For debugging. */ if (fallback_interface) { result = send_packet(fallback_interface, NULL, &raw, packet_length, raw.siaddr, &to, NULL); if (result < 0) { log_error ("%s:%d: Failed to send %d byte long " "packet over %s interface.", MDL, packet_length, fallback_interface->name); } free_lease_state (state, MDL); lease -> state = (struct lease_state *)0; return; } /* If the client is RENEWING, unicast to the client using the regular IP stack. Some clients, particularly those that follow RFC1541, are buggy, and send both ciaddr and server identifier. We deal with this situation by assuming that if we got both dhcp-server-identifier and ciaddr, and giaddr was not set, then the client is on the local network, and we can therefore unicast or broadcast to it successfully. A client in REQUESTING state on another network that's making this mistake will have set giaddr, and will therefore get a relayed response from the above code. */ } else if (raw.ciaddr.s_addr && !((state -> got_server_identifier || (raw.flags & htons (BOOTP_BROADCAST))) && /* XXX This won't work if giaddr isn't zero, but it is: */ (state -> shared_network == lease -> subnet -> shared_network)) && state -> offer == DHCPACK) { to.sin_addr = raw.ciaddr; to.sin_port = remote_port; if (fallback_interface) { result = send_packet(fallback_interface, NULL, &raw, packet_length, raw.siaddr, &to, NULL); if (result < 0) { log_error("%s:%d: Failed to send %d byte long" " packet over %s interface.", MDL, packet_length, fallback_interface->name); } free_lease_state (state, MDL); lease -> state = (struct lease_state *)0; return; } /* If it comes from a client that already knows its address and is not requesting a broadcast response, and we can unicast to a client without using the ARP protocol, sent it directly to that client. */ } else if (!(raw.flags & htons (BOOTP_BROADCAST)) && can_unicast_without_arp (state -> ip)) { to.sin_addr = raw.yiaddr; to.sin_port = remote_port; /* Otherwise, broadcast it on the local network. */ } else { to.sin_addr = limited_broadcast; to.sin_port = remote_port; if (!(lease -> flags & UNICAST_BROADCAST_HACK)) unicastp = 0; } memcpy (&from, state -> from.iabuf, sizeof from); result = send_packet(state->ip, NULL, &raw, packet_length, from, &to, unicastp ? &hto : NULL); if (result < 0) { log_error ("%s:%d: Failed to send %d byte long " "packet over %s interface.", MDL, packet_length, state->ip->name); } /* Free all of the entries in the option_state structure now that we're done with them. */ free_lease_state (state, MDL); lease -> state = (struct lease_state *)0; } int find_lease (struct lease **lp, struct packet *packet, struct shared_network *share, int *ours, int *peer_has_leases, struct lease *ip_lease_in, const char *file, int line) { struct lease *uid_lease = (struct lease *)0; struct lease *ip_lease = (struct lease *)0; struct lease *hw_lease = (struct lease *)0; struct lease *lease = (struct lease *)0; struct iaddr cip; struct host_decl *hp = (struct host_decl *)0; struct host_decl *host = (struct host_decl *)0; struct lease *fixed_lease = (struct lease *)0; struct lease *next = (struct lease *)0; struct option_cache *oc; struct data_string d1; int have_client_identifier = 0; struct data_string client_identifier; struct hardware h; #if defined(FAILOVER_PROTOCOL) /* Quick check to see if the peer has leases. */ if (peer_has_leases) { struct pool *pool; for (pool = share->pools ; pool ; pool = pool->next) { dhcp_failover_state_t *peer = pool->failover_peer; if (peer && ((peer->i_am == primary && pool->backup_leases) || (peer->i_am == secondary && pool->free_leases))) { *peer_has_leases = 1; break; } } } #endif /* FAILOVER_PROTOCOL */ if (packet -> raw -> ciaddr.s_addr) { cip.len = 4; memcpy (cip.iabuf, &packet -> raw -> ciaddr, 4); } else { /* Look up the requested address. */ oc = lookup_option (&dhcp_universe, packet -> options, DHO_DHCP_REQUESTED_ADDRESS); memset (&d1, 0, sizeof d1); if (oc && evaluate_option_cache (&d1, packet, (struct lease *)0, (struct client_state *)0, packet -> options, (struct option_state *)0, &global_scope, oc, MDL)) { packet -> got_requested_address = 1; cip.len = 4; memcpy (cip.iabuf, d1.data, cip.len); data_string_forget (&d1, MDL); } else cip.len = 0; } /* Try to find a host or lease that's been assigned to the specified unique client identifier. */ oc = lookup_option (&dhcp_universe, packet -> options, DHO_DHCP_CLIENT_IDENTIFIER); memset (&client_identifier, 0, sizeof client_identifier); if (oc && evaluate_option_cache (&client_identifier, packet, (struct lease *)0, (struct client_state *)0, packet -> options, (struct option_state *)0, &global_scope, oc, MDL)) { /* Remember this for later. */ have_client_identifier = 1; /* First, try to find a fixed host entry for the specified client identifier... */ if (find_hosts_by_uid (&hp, client_identifier.data, client_identifier.len, MDL)) { /* Remember if we know of this client. */ packet -> known = 1; mockup_lease (&fixed_lease, packet, share, hp); } #if defined (DEBUG_FIND_LEASE) if (fixed_lease) { log_info ("Found host for client identifier: %s.", piaddr (fixed_lease -> ip_addr)); } #endif if (hp) { if (!fixed_lease) /* Save the host if we found one. */ host_reference (&host, hp, MDL); host_dereference (&hp, MDL); } find_lease_by_uid (&uid_lease, client_identifier.data, client_identifier.len, MDL); } /* If we didn't find a fixed lease using the uid, try doing it with the hardware address... */ if (!fixed_lease && !host) { if (find_hosts_by_haddr (&hp, packet -> raw -> htype, packet -> raw -> chaddr, packet -> raw -> hlen, MDL)) { /* Remember if we know of this client. */ packet -> known = 1; if (host) host_dereference (&host, MDL); host_reference (&host, hp, MDL); host_dereference (&hp, MDL); mockup_lease (&fixed_lease, packet, share, host); #if defined (DEBUG_FIND_LEASE) if (fixed_lease) { log_info ("Found host for link address: %s.", piaddr (fixed_lease -> ip_addr)); } #endif } } /* Finally, if we haven't found anything yet try again with the * host-identifier option ... */ if (!fixed_lease && !host) { if (find_hosts_by_option(&hp, packet, packet->options, MDL) == 1) { packet->known = 1; if (host) host_dereference(&host, MDL); host_reference(&host, hp, MDL); host_dereference(&hp, MDL); mockup_lease (&fixed_lease, packet, share, host); #if defined (DEBUG_FIND_LEASE) if (fixed_lease) { log_info ("Found host via host-identifier"); } #endif } } /* If fixed_lease is present but does not match the requested IP address, and this is a DHCPREQUEST, then we can't return any other lease, so we might as well return now. */ if (packet -> packet_type == DHCPREQUEST && fixed_lease && (fixed_lease -> ip_addr.len != cip.len || memcmp (fixed_lease -> ip_addr.iabuf, cip.iabuf, cip.len))) { if (ours) *ours = 1; strcpy (dhcp_message, "requested address is incorrect"); #if defined (DEBUG_FIND_LEASE) log_info ("Client's fixed-address %s doesn't match %s%s", piaddr (fixed_lease -> ip_addr), "request ", print_dotted_quads (cip.len, cip.iabuf)); #endif goto out; } /* * If we found leases matching the client identifier, loop through * the n_uid pointer looking for one that's actually valid. We * can't do this until we get here because we depend on * packet -> known, which may be set by either the uid host * lookup or the haddr host lookup. * * Note that the n_uid lease chain is sorted in order of * preference, so the first one is the best one. */ while (uid_lease) { isc_boolean_t do_release = !packet->raw->ciaddr.s_addr; #if defined (DEBUG_FIND_LEASE) log_info ("trying next lease matching client id: %s", piaddr (uid_lease -> ip_addr)); #endif #if defined (FAILOVER_PROTOCOL) /* * When we lookup a lease by uid, we know the client identifier * matches the lease's record. If it is active, or was last * active with the same client, we can trivially extend it. * If is not or was not active, we can allocate it to this * client if it matches the usual free/backup criteria (which * is contained in lease_mine_to_reallocate()). */ if (uid_lease->binding_state != FTS_ACTIVE && uid_lease->rewind_binding_state != FTS_ACTIVE && !lease_mine_to_reallocate(uid_lease)) { #if defined (DEBUG_FIND_LEASE) log_info("not active or not mine to allocate: %s", piaddr(uid_lease->ip_addr)); #endif goto n_uid; } #endif if (uid_lease -> subnet -> shared_network != share) { #if defined (DEBUG_FIND_LEASE) log_info ("wrong network segment: %s", piaddr (uid_lease -> ip_addr)); #endif /* Allow multiple leases using the same UID on different subnetworks. */ do_release = ISC_FALSE; goto n_uid; } if ((uid_lease -> pool -> prohibit_list && permitted (packet, uid_lease -> pool -> prohibit_list)) || (uid_lease -> pool -> permit_list && !permitted (packet, uid_lease -> pool -> permit_list))) { #if defined (DEBUG_FIND_LEASE) log_info ("not permitted: %s", piaddr (uid_lease -> ip_addr)); #endif n_uid: if (uid_lease -> n_uid) lease_reference (&next, uid_lease -> n_uid, MDL); if (do_release) release_lease (uid_lease, packet); lease_dereference (&uid_lease, MDL); if (next) { lease_reference (&uid_lease, next, MDL); lease_dereference (&next, MDL); } continue; } break; } #if defined (DEBUG_FIND_LEASE) if (uid_lease) log_info ("Found lease for client id: %s.", piaddr (uid_lease -> ip_addr)); #endif /* Find a lease whose hardware address matches, whose client * identifier matches (or equally doesn't have one), that's * permitted, and that's on the correct subnet. * * Note that the n_hw chain is sorted in order of preference, so * the first one found is the best one. */ h.hlen = packet -> raw -> hlen + 1; h.hbuf [0] = packet -> raw -> htype; memcpy (&h.hbuf [1], packet -> raw -> chaddr, packet -> raw -> hlen); find_lease_by_hw_addr (&hw_lease, h.hbuf, h.hlen, MDL); while (hw_lease) { #if defined (DEBUG_FIND_LEASE) log_info ("trying next lease matching hw addr: %s", piaddr (hw_lease -> ip_addr)); #endif #if defined (FAILOVER_PROTOCOL) /* * When we lookup a lease by chaddr, we know the MAC address * matches the lease record (we will check if the lease has a * client-id the client does not next). If the lease is * currently active or was last active with this client, we can * trivially extend it. Otherwise, there are a set of rules * that govern if we can reallocate this lease to any client * ("lease_mine_to_reallocate()") including this one. */ if (hw_lease->binding_state != FTS_ACTIVE && hw_lease->rewind_binding_state != FTS_ACTIVE && !lease_mine_to_reallocate(hw_lease)) { #if defined (DEBUG_FIND_LEASE) log_info("not active or not mine to allocate: %s", piaddr(hw_lease->ip_addr)); #endif goto n_hw; } #endif /* * This conditional skips "potentially active" leases (leases * we think are expired may be extended by the peer, etc) that * may be assigned to a differently /client-identified/ client * with the same MAC address. */ if (hw_lease -> binding_state != FTS_FREE && hw_lease -> binding_state != FTS_BACKUP && hw_lease -> uid && (!have_client_identifier || hw_lease -> uid_len != client_identifier.len || memcmp (hw_lease -> uid, client_identifier.data, hw_lease -> uid_len))) { #if defined (DEBUG_FIND_LEASE) log_info ("wrong client identifier: %s", piaddr (hw_lease -> ip_addr)); #endif goto n_hw; } if (hw_lease -> subnet -> shared_network != share) { #if defined (DEBUG_FIND_LEASE) log_info ("wrong network segment: %s", piaddr (hw_lease -> ip_addr)); #endif goto n_hw; } if ((hw_lease -> pool -> prohibit_list && permitted (packet, hw_lease -> pool -> prohibit_list)) || (hw_lease -> pool -> permit_list && !permitted (packet, hw_lease -> pool -> permit_list))) { #if defined (DEBUG_FIND_LEASE) log_info ("not permitted: %s", piaddr (hw_lease -> ip_addr)); #endif if (!packet -> raw -> ciaddr.s_addr) release_lease (hw_lease, packet); n_hw: if (hw_lease -> n_hw) lease_reference (&next, hw_lease -> n_hw, MDL); lease_dereference (&hw_lease, MDL); if (next) { lease_reference (&hw_lease, next, MDL); lease_dereference (&next, MDL); } continue; } break; } #if defined (DEBUG_FIND_LEASE) if (hw_lease) log_info ("Found lease for hardware address: %s.", piaddr (hw_lease -> ip_addr)); #endif /* Try to find a lease that's been allocated to the client's IP address. */ if (ip_lease_in) lease_reference (&ip_lease, ip_lease_in, MDL); else if (cip.len) find_lease_by_ip_addr (&ip_lease, cip, MDL); #if defined (DEBUG_FIND_LEASE) if (ip_lease) log_info ("Found lease for requested address: %s.", piaddr (ip_lease -> ip_addr)); #endif /* If ip_lease is valid at this point, set ours to one, so that even if we choose a different lease, we know that the address the client was requesting was ours, and thus we can NAK it. */ if (ip_lease && ours) *ours = 1; /* If the requested IP address isn't on the network the packet came from, don't use it. Allow abandoned leases to be matched here - if the client is requesting it, there's a decent chance that it's because the lease database got trashed and a client that thought it had this lease answered an ARP or PING, causing the lease to be abandoned. If so, this request probably came from that client. */ if (ip_lease && (ip_lease -> subnet -> shared_network != share)) { if (ours) *ours = 1; #if defined (DEBUG_FIND_LEASE) log_info ("...but it was on the wrong shared network."); #endif strcpy (dhcp_message, "requested address on bad subnet"); lease_dereference (&ip_lease, MDL); } /* * If the requested address is in use (or potentially in use) by * a different client, it can't be granted. * * This first conditional only detects if the lease is currently * identified to a different client (client-id and/or chaddr * mismatch). In this case we may not want to give the client the * lease, if doing so may potentially be an addressing conflict. */ if (ip_lease && (ip_lease -> uid ? (!have_client_identifier || ip_lease -> uid_len != client_identifier.len || memcmp (ip_lease -> uid, client_identifier.data, ip_lease -> uid_len)) : (ip_lease -> hardware_addr.hbuf [0] != packet -> raw -> htype || ip_lease -> hardware_addr.hlen != packet -> raw -> hlen + 1 || memcmp (&ip_lease -> hardware_addr.hbuf [1], packet -> raw -> chaddr, (unsigned)(ip_lease -> hardware_addr.hlen - 1))))) { /* * A lease is unavailable for allocation to a new client if * it is not in the FREE or BACKUP state. There may be * leases that are in the expired state with a rewinding * state that is free or backup, but these will be processed * into the free or backup states by expiration processes, so * checking for them here is superfluous. */ if (ip_lease -> binding_state != FTS_FREE && ip_lease -> binding_state != FTS_BACKUP) { #if defined (DEBUG_FIND_LEASE) log_info ("rejecting lease for requested address."); #endif /* If we're rejecting it because the peer has it, don't set "ours", because we shouldn't NAK. */ if (ours && ip_lease -> binding_state != FTS_ACTIVE) *ours = 0; lease_dereference (&ip_lease, MDL); } } /* * If we got an ip_lease and a uid_lease or hw_lease, and ip_lease * is/was not active, and is not ours to reallocate, forget about it. */ if (ip_lease && (uid_lease || hw_lease) && ip_lease->binding_state != FTS_ACTIVE && ip_lease->rewind_binding_state != FTS_ACTIVE && #if defined(FAILOVER_PROTOCOL) !lease_mine_to_reallocate(ip_lease) && #endif packet->packet_type == DHCPDISCOVER) { #if defined (DEBUG_FIND_LEASE) log_info("ip lease not active or not ours to offer."); #endif lease_dereference(&ip_lease, MDL); } /* If for some reason the client has more than one lease on the subnet that matches its uid, pick the one that it asked for and (if we can) free the other. */ if (ip_lease && ip_lease->binding_state == FTS_ACTIVE && ip_lease->uid && ip_lease != uid_lease) { if (have_client_identifier && (ip_lease -> uid_len == client_identifier.len) && !memcmp (client_identifier.data, ip_lease -> uid, ip_lease -> uid_len)) { if (uid_lease) { if (uid_lease->binding_state == FTS_ACTIVE) { log_error ("client %s has duplicate%s on %s", (print_hw_addr (packet -> raw -> htype, packet -> raw -> hlen, packet -> raw -> chaddr)), " leases", (ip_lease -> subnet -> shared_network -> name)); /* If the client is REQUESTing the lease, it shouldn't still be using the old one, so we can free it for allocation. */ if (uid_lease && uid_lease->binding_state == FTS_ACTIVE && !packet -> raw -> ciaddr.s_addr && (share == uid_lease -> subnet -> shared_network) && packet -> packet_type == DHCPREQUEST) release_lease (uid_lease, packet); } lease_dereference (&uid_lease, MDL); lease_reference (&uid_lease, ip_lease, MDL); } } /* If we get to here and fixed_lease is not null, that means that there are both a dynamic lease and a fixed-address declaration for the same IP address. */ if (packet -> packet_type == DHCPREQUEST && fixed_lease) { lease_dereference (&fixed_lease, MDL); db_conflict: log_error ("Dynamic and static leases present for %s.", piaddr (cip)); log_error ("Remove host declaration %s or remove %s", (fixed_lease && fixed_lease -> host ? (fixed_lease -> host -> name ? fixed_lease -> host -> name : piaddr (cip)) : piaddr (cip)), piaddr (cip)); log_error ("from the dynamic address pool for %s", ip_lease -> subnet -> shared_network -> name ); if (fixed_lease) lease_dereference (&ip_lease, MDL); strcpy (dhcp_message, "database conflict - call for help!"); } if (ip_lease && ip_lease != uid_lease) { #if defined (DEBUG_FIND_LEASE) log_info ("requested address not available."); #endif lease_dereference (&ip_lease, MDL); } } /* If we get to here with both fixed_lease and ip_lease not null, then we have a configuration file bug. */ if (packet -> packet_type == DHCPREQUEST && fixed_lease && ip_lease) goto db_conflict; /* Toss extra pointers to the same lease... */ if (hw_lease && hw_lease == uid_lease) { #if defined (DEBUG_FIND_LEASE) log_info ("hardware lease and uid lease are identical."); #endif lease_dereference (&hw_lease, MDL); } if (ip_lease && ip_lease == hw_lease) { lease_dereference (&hw_lease, MDL); #if defined (DEBUG_FIND_LEASE) log_info ("hardware lease and ip lease are identical."); #endif } if (ip_lease && ip_lease == uid_lease) { lease_dereference (&uid_lease, MDL); #if defined (DEBUG_FIND_LEASE) log_info ("uid lease and ip lease are identical."); #endif } /* Make sure the client is permitted to use the requested lease. */ if (ip_lease && ((ip_lease -> pool -> prohibit_list && permitted (packet, ip_lease -> pool -> prohibit_list)) || (ip_lease -> pool -> permit_list && !permitted (packet, ip_lease -> pool -> permit_list)))) { if (!packet->raw->ciaddr.s_addr && (ip_lease->binding_state == FTS_ACTIVE)) release_lease (ip_lease, packet); lease_dereference (&ip_lease, MDL); } if (uid_lease && ((uid_lease -> pool -> prohibit_list && permitted (packet, uid_lease -> pool -> prohibit_list)) || (uid_lease -> pool -> permit_list && !permitted (packet, uid_lease -> pool -> permit_list)))) { if (!packet -> raw -> ciaddr.s_addr) release_lease (uid_lease, packet); lease_dereference (&uid_lease, MDL); } if (hw_lease && ((hw_lease -> pool -> prohibit_list && permitted (packet, hw_lease -> pool -> prohibit_list)) || (hw_lease -> pool -> permit_list && !permitted (packet, hw_lease -> pool -> permit_list)))) { if (!packet -> raw -> ciaddr.s_addr) release_lease (hw_lease, packet); lease_dereference (&hw_lease, MDL); } /* If we've already eliminated the lease, it wasn't there to begin with. If we have come up with a matching lease, set the message to bad network in case we have to throw it out. */ if (!ip_lease) { strcpy (dhcp_message, "requested address not available"); } /* If this is a DHCPREQUEST, make sure the lease we're going to return matches the requested IP address. If it doesn't, don't return a lease at all. */ if (packet -> packet_type == DHCPREQUEST && !ip_lease && !fixed_lease) { #if defined (DEBUG_FIND_LEASE) log_info ("no applicable lease found for DHCPREQUEST."); #endif goto out; } /* At this point, if fixed_lease is nonzero, we can assign it to this client. */ if (fixed_lease) { lease_reference (&lease, fixed_lease, MDL); lease_dereference (&fixed_lease, MDL); #if defined (DEBUG_FIND_LEASE) log_info ("choosing fixed address."); #endif } /* If we got a lease that matched the ip address and don't have a better offer, use that; otherwise, release it. */ if (ip_lease) { if (lease) { if (!packet -> raw -> ciaddr.s_addr) release_lease (ip_lease, packet); #if defined (DEBUG_FIND_LEASE) log_info ("not choosing requested address (!)."); #endif lease_dereference (&ip_lease, MDL); } else { #if defined (DEBUG_FIND_LEASE) log_info ("choosing lease on requested address."); #endif lease_reference (&lease, ip_lease, MDL); if (lease -> host) host_dereference (&lease -> host, MDL); } } /* If we got a lease that matched the client identifier, we may want to use it, but if we already have a lease we like, we must free the lease that matched the client identifier. */ if (uid_lease) { if (lease) { log_error("uid lease %s for client %s is duplicate " "on %s", piaddr(uid_lease->ip_addr), print_hw_addr(packet->raw->htype, packet->raw->hlen, packet->raw->chaddr), uid_lease->subnet->shared_network->name); if (!packet -> raw -> ciaddr.s_addr && packet -> packet_type == DHCPREQUEST && uid_lease -> binding_state == FTS_ACTIVE) release_lease(uid_lease, packet); #if defined (DEBUG_FIND_LEASE) log_info ("not choosing uid lease."); #endif } else { lease_reference (&lease, uid_lease, MDL); if (lease -> host) host_dereference (&lease -> host, MDL); #if defined (DEBUG_FIND_LEASE) log_info ("choosing uid lease."); #endif } lease_dereference (&uid_lease, MDL); } /* The lease that matched the hardware address is treated likewise. */ if (hw_lease) { if (lease) { #if defined (DEBUG_FIND_LEASE) log_info ("not choosing hardware lease."); #endif } else { /* We're a little lax here - if the client didn't send a client identifier and it's a bootp client, but the lease has a client identifier, we still let the client have a lease. */ if (!hw_lease -> uid_len || (have_client_identifier ? (hw_lease -> uid_len == client_identifier.len && !memcmp (hw_lease -> uid, client_identifier.data, client_identifier.len)) : packet -> packet_type == 0)) { lease_reference (&lease, hw_lease, MDL); if (lease -> host) host_dereference (&lease -> host, MDL); #if defined (DEBUG_FIND_LEASE) log_info ("choosing hardware lease."); #endif } else { #if defined (DEBUG_FIND_LEASE) log_info ("not choosing hardware lease: %s.", "uid mismatch"); #endif } } lease_dereference (&hw_lease, MDL); } /* * If we found a host_decl but no matching address, try to * find a host_decl that has no address, and if there is one, * hang it off the lease so that we can use the supplied * options. */ if (lease && host && !lease->host) { struct host_decl *p = NULL; struct host_decl *n = NULL; host_reference(&p, host, MDL); while (p != NULL) { if (!p->fixed_addr) { /* * If the lease is currently active, then it * must be allocated to the present client. * We store a reference to the host record on * the lease to save a lookup later (in * ack_lease()). We mustn't refer to the host * record on non-active leases because the * client may be denied later. * * XXX: Not having this reference (such as in * DHCPDISCOVER/INIT) means ack_lease will have * to perform this lookup a second time. This * hopefully isn't a problem as DHCPREQUEST is * more common than DHCPDISCOVER. */ if (lease->binding_state == FTS_ACTIVE) host_reference(&lease->host, p, MDL); host_dereference(&p, MDL); break; } if (p->n_ipaddr != NULL) host_reference(&n, p->n_ipaddr, MDL); host_dereference(&p, MDL); if (n != NULL) { host_reference(&p, n, MDL); host_dereference(&n, MDL); } } } /* If we find an abandoned lease, but it's the one the client requested, we assume that previous bugginess on the part of the client, or a server database loss, caused the lease to be abandoned, so we reclaim it and let the client have it. */ if (lease && (lease -> binding_state == FTS_ABANDONED) && lease == ip_lease && packet -> packet_type == DHCPREQUEST) { log_error ("Reclaiming REQUESTed abandoned IP address %s.", piaddr (lease -> ip_addr)); } else if (lease && (lease -> binding_state == FTS_ABANDONED)) { /* Otherwise, if it's not the one the client requested, we do not return it - instead, we claim it's ours, causing a DHCPNAK to be sent if this lookup is for a DHCPREQUEST, and force the client to go back through the allocation process. */ if (ours) *ours = 1; lease_dereference (&lease, MDL); } out: if (have_client_identifier) data_string_forget (&client_identifier, MDL); if (fixed_lease) lease_dereference (&fixed_lease, MDL); if (hw_lease) lease_dereference (&hw_lease, MDL); if (uid_lease) lease_dereference (&uid_lease, MDL); if (ip_lease) lease_dereference (&ip_lease, MDL); if (host) host_dereference (&host, MDL); if (lease) { #if defined (DEBUG_FIND_LEASE) log_info ("Returning lease: %s.", piaddr (lease -> ip_addr)); #endif lease_reference (lp, lease, file, line); lease_dereference (&lease, MDL); return 1; } #if defined (DEBUG_FIND_LEASE) log_info ("Not returning a lease."); #endif return 0; } /* Search the provided host_decl structure list for an address that's on the specified shared network. If one is found, mock up and return a lease structure for it; otherwise return the null pointer. */ int mockup_lease (struct lease **lp, struct packet *packet, struct shared_network *share, struct host_decl *hp) { struct lease *lease = (struct lease *)0; struct host_decl *rhp = (struct host_decl *)0; if (lease_allocate (&lease, MDL) != ISC_R_SUCCESS) return 0; if (host_reference (&rhp, hp, MDL) != ISC_R_SUCCESS) { lease_dereference (&lease, MDL); return 0; } if (!find_host_for_network (&lease -> subnet, &rhp, &lease -> ip_addr, share)) { lease_dereference (&lease, MDL); host_dereference (&rhp, MDL); return 0; } host_reference (&lease -> host, rhp, MDL); if (rhp -> client_identifier.len > sizeof lease -> uid_buf) lease -> uid = dmalloc (rhp -> client_identifier.len, MDL); else lease -> uid = lease -> uid_buf; if (!lease -> uid) { lease_dereference (&lease, MDL); host_dereference (&rhp, MDL); return 0; } memcpy (lease -> uid, rhp -> client_identifier.data, rhp -> client_identifier.len); lease -> uid_len = rhp -> client_identifier.len; lease -> hardware_addr = rhp -> interface; lease -> starts = lease -> cltt = lease -> ends = MIN_TIME; lease -> flags = STATIC_LEASE; lease -> binding_state = FTS_FREE; lease_reference (lp, lease, MDL); lease_dereference (&lease, MDL); host_dereference (&rhp, MDL); return 1; } /* Look through all the pools in a list starting with the specified pool for a free lease. We try to find a virgin lease if we can. If we don't find a virgin lease, we try to find a non-virgin lease that's free. If we can't find one of those, we try to reclaim an abandoned lease. If all of these possibilities fail to pan out, we don't return a lease at all. */ int allocate_lease (struct lease **lp, struct packet *packet, struct pool *pool, int *peer_has_leases) { struct lease *lease = NULL; struct lease *candl = NULL; for (; pool ; pool = pool -> next) { if ((pool -> prohibit_list && permitted (packet, pool -> prohibit_list)) || (pool -> permit_list && !permitted (packet, pool -> permit_list))) continue; #if defined (FAILOVER_PROTOCOL) /* Peer_has_leases just says that we found at least one free lease. If no free lease is returned, the caller can deduce that this means the peer is hogging all the free leases, so we can print a better error message. */ /* XXX Do we need code here to ignore PEER_IS_OWNER and * XXX just check tstp if we're in, e.g., PARTNER_DOWN? * XXX Where do we deal with CONFLICT_DETECTED, et al? */ /* XXX This should be handled by the lease binding "state * XXX machine" - that is, when we get here, if a lease * XXX could be allocated, it will have the correct * XXX binding state so that the following code will * XXX result in its being allocated. */ /* Skip to the most expired lease in the pool that is not * owned by a failover peer. */ if (pool->failover_peer != NULL) { struct lease *peerl = NULL; if (pool->failover_peer->i_am == primary) { candl = LEASE_GET_FIRST(pool->free); /* * In normal operation, we never want to touch * the peer's leases. In partner-down * operation, we need to be able to pick up * the peer's leases after STOS+MCLT. */ peerl = LEASE_GET_FIRST(pool->backup); if (peerl != NULL) { if (((candl == NULL) || (candl->ends > peerl->ends)) && lease_mine_to_reallocate(peerl)) { candl = peerl; } else { *peer_has_leases = 1; } } } else { candl = LEASE_GET_FIRST(pool->backup); peerl = LEASE_GET_FIRST(pool->free); if (peerl != NULL) { if (((candl == NULL) || (candl->ends > peerl->ends)) && lease_mine_to_reallocate(peerl)) { candl = peerl; } else { *peer_has_leases = 1; } } } /* Try abandoned leases as a last resort. */ peerl = LEASE_GET_FIRST(pool->abandoned); if ((candl == NULL) && (peerl != NULL) && lease_mine_to_reallocate(peerl)) candl = peerl; } else #endif { if (LEASE_NOT_EMPTY(pool->free)) candl = LEASE_GET_FIRST(pool->free); else candl = LEASE_GET_FIRST(pool->abandoned); } /* * XXX: This may not match with documented expectation. * It's expected that when we OFFER a lease, we set its * ends time forward 2 minutes so that it gets sorted to * the end of its free list (avoiding a similar allocation * to another client). It is not expected that we issue a * "no free leases" error when the last lease has been * offered, but it's not exactly broken either. */ if (!candl || (candl->binding_state != FTS_ABANDONED && (candl->ends > cur_time))) { continue; } if (!lease) { lease = candl; continue; } /* * There are tiers of lease state preference, listed here in * reverse order (least to most preferential): * * ABANDONED * FREE/BACKUP * * If the selected lease and candidate are both of the same * state, select the oldest (longest ago) expiration time * between the two. If the candidate lease is of a higher * preferred grade over the selected lease, use it. */ if ((lease -> binding_state == FTS_ABANDONED) && ((candl -> binding_state != FTS_ABANDONED) || (candl -> ends < lease -> ends))) { lease = candl; continue; } else if (candl -> binding_state == FTS_ABANDONED) continue; if ((lease -> uid_len || lease -> hardware_addr.hlen) && ((!candl -> uid_len && !candl -> hardware_addr.hlen) || (candl -> ends < lease -> ends))) { lease = candl; continue; } else if (candl -> uid_len || candl -> hardware_addr.hlen) continue; if (candl -> ends < lease -> ends) lease = candl; } if (lease != NULL) { if (lease->binding_state == FTS_ABANDONED) log_error("Reclaiming abandoned lease %s.", piaddr(lease->ip_addr)); /* * XXX: For reliability, we go ahead and remove the host * record and try to move on. For correctness, if there * are any other stale host vectors, we want to find them. */ if (lease->host != NULL) { log_debug("soft impossible condition (%s:%d): stale " "host \"%s\" found on lease %s", MDL, lease->host->name, piaddr(lease->ip_addr)); host_dereference(&lease->host, MDL); } lease_reference (lp, lease, MDL); return 1; } return 0; } /* Determine whether or not a permit exists on a particular permit list that matches the specified packet, returning nonzero if so, zero if not. */ int permitted (packet, permit_list) struct packet *packet; struct permit *permit_list; { struct permit *p; int i; for (p = permit_list; p; p = p -> next) { switch (p -> type) { case permit_unknown_clients: if (!packet -> known) return 1; break; case permit_known_clients: if (packet -> known) return 1; break; case permit_authenticated_clients: if (packet -> authenticated) return 1; break; case permit_unauthenticated_clients: if (!packet -> authenticated) return 1; break; case permit_all_clients: return 1; case permit_dynamic_bootp_clients: if (!packet -> options_valid || !packet -> packet_type) return 1; break; case permit_class: for (i = 0; i < packet -> class_count; i++) { if (p -> class == packet -> classes [i]) return 1; if (packet -> classes [i] && packet -> classes [i] -> superclass && (packet -> classes [i] -> superclass == p -> class)) return 1; } break; case permit_after: if (cur_time > p->after) return 1; break; } } return 0; } #if defined(DHCPv6) && defined(DHCP4o6) static int locate_network6 (packet) struct packet *packet; { const struct packet *chk_packet; const struct in6_addr *link_addr, *first_link_addr; struct iaddr ia; struct data_string data; struct subnet *subnet = NULL; struct option_cache *oc; /* from locate_network() */ /* See if there's a Relay Agent Link Selection Option, or a * Subnet Selection Option. The Link-Select and Subnet-Select * are formatted and used precisely the same, but we must prefer * the link-select over the subnet-select. * BTW in DHCPv4 over DHCPv6 no cross version relay was specified * so it is unlikely to see a link-select. */ if ((oc = lookup_option(&agent_universe, packet->options, RAI_LINK_SELECT)) == NULL) oc = lookup_option(&dhcp_universe, packet->options, DHO_SUBNET_SELECTION); /* If there's an option indicating link connection or subnet * selection, and it's valid, use it to figure out the subnet. * If it's not valid, fail. */ if (oc) { memset(&data, 0, sizeof data); if (!evaluate_option_cache(&data, packet, NULL, NULL, packet->options, NULL, &global_scope, oc, MDL)) { return (0); } if (data.len == 0) { return (0); } if (data.len != 4) { data_string_forget(&data, MDL); return (0); } ia.len = 4; memcpy(ia.iabuf, data.data, 4); data_string_forget(&data, MDL); if (find_subnet(&subnet, ia, MDL)) { shared_network_reference(&packet->shared_network, subnet->shared_network, MDL); subnet_dereference(&subnet, MDL); return (1); } return (0); } /* See if there is a giaddr (still unlikely), if there is one * use it to figure out the subnet. If it's not valid, fail. */ if (packet->raw->giaddr.s_addr) { ia.len = 4; memcpy(ia.iabuf, &packet->raw->giaddr, 4); if (find_subnet(&subnet, ia, MDL)) { shared_network_reference(&packet->shared_network, subnet->shared_network, MDL); subnet_dereference(&subnet, MDL); return (1); } return (0); } /* from shared_network_from_packet6() */ /* First, find the link address where the packet from the client * first appeared (if this packet was relayed). */ first_link_addr = NULL; chk_packet = packet->dhcpv6_container_packet; while (chk_packet != NULL) { link_addr = &chk_packet->dhcpv6_link_address; if (!IN6_IS_ADDR_UNSPECIFIED(link_addr) && !IN6_IS_ADDR_LINKLOCAL(link_addr)) { first_link_addr = link_addr; break; } chk_packet = chk_packet->dhcpv6_container_packet; } /* If there is a relayed link address, find the subnet associated * with that, and use that to get the appropriate shared_network. */ if (first_link_addr != NULL) { ia.len = sizeof(*first_link_addr); memcpy(ia.iabuf, first_link_addr, sizeof(*first_link_addr)); if (find_subnet (&subnet, ia, MDL)) { shared_network_reference(&packet->shared_network, subnet->shared_network, MDL); subnet_dereference(&subnet, MDL); return (1); } return (0); } /* If there is no link address, we will use the interface * that this packet came in on to pick the shared_network. */ if (packet->interface != NULL) { if (packet->interface->shared_network == NULL) return (0); shared_network_reference(&packet->shared_network, packet->interface->shared_network, MDL); return (1); } /* We shouldn't be able to get here but if there is no link * address and no interface we don't know where to get the * shared_network from, log an error and return an error. */ log_error("No interface and no link address " "can't determine DHCP4o6 shared network"); return (0); } #endif int locate_network (packet) struct packet *packet; { struct iaddr ia; struct data_string data; struct subnet *subnet = (struct subnet *)0; struct option_cache *oc; #if defined(DHCPv6) && defined(DHCP4o6) if (dhcpv4_over_dhcpv6 && (packet->dhcp4o6_response != NULL)) { return (locate_network6 (packet)); } #endif /* See if there's a Relay Agent Link Selection Option, or a * Subnet Selection Option. The Link-Select and Subnet-Select * are formatted and used precisely the same, but we must prefer * the link-select over the subnet-select. */ if ((oc = lookup_option(&agent_universe, packet->options, RAI_LINK_SELECT)) == NULL) oc = lookup_option(&dhcp_universe, packet->options, DHO_SUBNET_SELECTION); /* If there's no SSO and no giaddr, then use the shared_network from the interface, if there is one. If not, fail. */ if (!oc && !packet -> raw -> giaddr.s_addr) { if (packet -> interface -> shared_network) { shared_network_reference (&packet -> shared_network, packet -> interface -> shared_network, MDL); return 1; } return 0; } /* If there's an option indicating link connection, and it's valid, * use it to figure out the subnet. If it's not valid, fail. */ if (oc) { memset (&data, 0, sizeof data); if (!evaluate_option_cache (&data, packet, (struct lease *)0, (struct client_state *)0, packet -> options, (struct option_state *)0, &global_scope, oc, MDL)) { return 0; } if (data.len == 0) { return 0; } if (data.len != 4) { data_string_forget (&data, MDL); return 0; } ia.len = 4; memcpy (ia.iabuf, data.data, 4); data_string_forget (&data, MDL); } else { ia.len = 4; memcpy (ia.iabuf, &packet -> raw -> giaddr, 4); } /* If we know the subnet on which the IP address lives, use it. */ if (find_subnet (&subnet, ia, MDL)) { shared_network_reference (&packet -> shared_network, subnet -> shared_network, MDL); subnet_dereference (&subnet, MDL); return 1; } /* Otherwise, fail. */ return 0; } /* * Try to figure out the source address to send packets from. * * from is the address structure we use to return any address * we find. * * options is the option cache to search. This may include * options from the incoming packet and configuration information. * * out_options is the outgoing option cache. This cache * may be the same as options. If out_options isn't NULL * we may save the server address option into it. We do so * if out_options is different than options or if the option * wasn't in options and we needed to find the address elsewhere. * * packet is the state structure for the incoming packet * * When finding the address we first check to see if it is * in the options list. If it isn't we use the first address * from the interface. * * While this is slightly more complicated than I'd like it allows * us to use the same code in several different places. ack, * inform and lease query use it to find the address and fill * in the options if we get the address from the interface. * nack uses it to find the address and copy it to the outgoing * cache. dhcprequest uses it to find the address for comparison * and doesn't need to add it to an outgoing list. */ void get_server_source_address(struct in_addr *from, struct option_state *options, struct option_state *out_options, struct packet *packet) { unsigned option_num; struct option_cache *oc = NULL; struct data_string d; struct in_addr *a = NULL; isc_boolean_t found = ISC_FALSE; int allocate = 0; memset(&d, 0, sizeof(d)); memset(from, 0, sizeof(*from)); option_num = DHO_DHCP_SERVER_IDENTIFIER; oc = lookup_option(&dhcp_universe, options, option_num); if (oc != NULL) { if (evaluate_option_cache(&d, packet, NULL, NULL, packet->options, options, &global_scope, oc, MDL)) { if (d.len == sizeof(*from)) { found = ISC_TRUE; memcpy(from, d.data, sizeof(*from)); /* * Arrange to save a copy of the data * to the outgoing list. */ if ((out_options != NULL) && (options != out_options)) { a = from; allocate = 1; } } data_string_forget(&d, MDL); } oc = NULL; } if ((found == ISC_FALSE) && (packet->interface->address_count > 0)) { *from = packet->interface->addresses[0]; if (out_options != NULL) { a = &packet->interface->addresses[0]; } } if ((a != NULL) && (option_cache_allocate(&oc, MDL))) { if (make_const_data(&oc->expression, (unsigned char *)a, sizeof(*a), 0, allocate, MDL)) { option_code_hash_lookup(&oc->option, dhcp_universe.code_hash, &option_num, 0, MDL); save_option(&dhcp_universe, out_options, oc); } option_cache_dereference(&oc, MDL); } return; } /*! * \brief Builds option set from statements at the global and network scope * * Set up an option state list based on the global and network scopes. * These are primarily used by NAK logic to locate dhcp-server-id and * echo-client-id. * * We don't go through all possible options - in particualr we skip the hosts * and we don't include the lease to avoid making changes to it. This means * that using these, we won't get the correct server id if the admin puts them * on hosts or builds the server id with information from the lease. * * As this is a fallback function (used to handle NAKs or sort out server id * mismatch in failover) and requires configuration by the admin, it should be * okay. * * \param network_options option_state to which options will be added. If it * refers to NULL, it will be allocated. Caller is responsible to delete it. * \param packet inbound packet * \param network_group scope group to use if packet->shared_network is null. */ void eval_network_statements(struct option_state **network_options, struct packet *packet, struct group *network_group) { if (*network_options == NULL) { option_state_allocate (network_options, MDL); } /* Use the packet's shared_network if it has one. If not use * network_group and if it is null then use global scope. */ if (packet->shared_network != NULL) { /* * If we have a subnet and group start with that else start * with the shared network group. The first will recurse and * include the second. */ if ((packet->shared_network->subnets != NULL) && (packet->shared_network->subnets->group != NULL)) { execute_statements_in_scope(NULL, packet, NULL, NULL, packet->options, *network_options, &global_scope, packet->shared_network->subnets->group, NULL, NULL); } else { execute_statements_in_scope(NULL, packet, NULL, NULL, packet->options, *network_options, &global_scope, packet->shared_network->group, NULL, NULL); } /* do the pool if there is one */ if (packet->shared_network->pools != NULL) { execute_statements_in_scope(NULL, packet, NULL, NULL, packet->options, *network_options, &global_scope, packet->shared_network->pools->group, packet->shared_network->group, NULL); } } else if (network_group != NULL) { execute_statements_in_scope(NULL, packet, NULL, NULL, packet->options, *network_options, &global_scope, network_group, NULL, NULL); } else { execute_statements_in_scope(NULL, packet, NULL, NULL, packet->options, *network_options, &global_scope, root_group, NULL, NULL); } } /* * Look for the lowest numbered site code number and * apply a log warning if it is less than 224. Do not * permit site codes less than 128 (old code never did). * * Note that we could search option codes 224 down to 128 * on the hash table, but the table is (probably) smaller * than that if it was declared as a standalone table with * defaults. So we traverse the option code hash. */ static int find_min_site_code(struct universe *u) { if (u->site_code_min) return u->site_code_min; /* * Note that site_code_min has to be global as we can't pass an * argument through hash_foreach(). The value 224 is taken from * RFC 3942. */ site_code_min = 224; option_code_hash_foreach(u->code_hash, lowest_site_code); if (site_code_min < 224) { log_error("WARNING: site-local option codes less than 224 have " "been deprecated by RFC3942. You have options " "listed in site local space %s that number as low as " "%d. Please investigate if these should be declared " "as regular options rather than site-local options, " "or migrated up past 224.", u->name, site_code_min); } /* * don't even bother logging, this is just silly, and never worked * on any old version of software. */ if (site_code_min < 128) site_code_min = 128; /* * Cache the determined minimum site code on the universe structure. * Note that due to the < 128 check above, a value of zero is * impossible. */ u->site_code_min = site_code_min; return site_code_min; } static isc_result_t lowest_site_code(const void *key, unsigned len, void *object) { struct option *option = object; if (option->code < site_code_min) site_code_min = option->code; return ISC_R_SUCCESS; } static void maybe_return_agent_options(struct packet *packet, struct option_state *options) { /* If there were agent options in the incoming packet, return * them. Do not return the agent options if they were stashed * on the lease. We do not check giaddr to detect the presence of * a relay, as this excludes "l2" relay agents which have no giaddr * to set. * * XXX: If the user configures options for the relay agent information * (state->options->universes[agent_universe.index] is not NULL), * we're still required to duplicate other values provided by the * relay agent. So we need to merge the old values not configured * by the user into the new state, not just give up. */ if (!packet->agent_options_stashed && (packet->options != NULL) && packet->options->universe_count > agent_universe.index && packet->options->universes[agent_universe.index] != NULL && (options->universe_count <= agent_universe.index || options->universes[agent_universe.index] == NULL)) { option_chain_head_reference ((struct option_chain_head **) &(options->universes[agent_universe.index]), (struct option_chain_head *) packet->options->universes[agent_universe.index], MDL); if (options->universe_count <= agent_universe.index) options->universe_count = agent_universe.index + 1; } } /*! * \brief Adds hostname option when use-host-decl-names is enabled. * * Constructs a hostname option from the name of the host declaration if * there is one and no hostname has otherwise been provided and the * use-host-decl-names flag is set, then adds the new option to the given * option_state. This funciton is used for both bootp and dhcp. * * \param packet inbound packet received from the client * \param lease lease associated with the client * \param options option state to search and update */ void use_host_decl_name(struct packet* packet, struct lease *lease, struct option_state *options) { unsigned int ocode = SV_USE_HOST_DECL_NAMES; if ((lease->host && lease->host->name) && !lookup_option(&dhcp_universe, options, DHO_HOST_NAME) && (evaluate_boolean_option_cache(NULL, packet, lease, NULL, packet->options, options, &lease->scope, lookup_option(&server_universe, options, ocode), MDL))) { struct option_cache *oc = NULL; if (option_cache_allocate (&oc, MDL)) { if (make_const_data(&oc -> expression, ((unsigned char*)lease->host->name), strlen(lease->host->name), 1, 0, MDL)) { ocode = DHO_HOST_NAME; option_code_hash_lookup(&oc->option, dhcp_universe.code_hash, &ocode, 0, MDL); save_option(&dhcp_universe, options, oc); } option_cache_dereference(&oc, MDL); } } } /*! * \brief Checks and preps for lease resuse based on dhcp-cache-threshold * * If dhcp-cache-threshold is enabled (i.e. greater than zero), this function * determines if the current lease is young enough to be reused. If the lease * can be resused the function returns 1, O if not. This function is called * by ack_lease when responding to both DISCOVERs and REQUESTS. * * The current lease can be reused only if all of the following are true: * a. dhcp-cache-threshold is > 0 * b. The current lease is active * c. The lease "age" is less than that allowed by the threshold * d. DNS updates are not being performed on the new lease. * e. Lease has not been otherwise disqualified for reuse (Ex: billing class * or hostname changed) * f. The host declaration has changed (either a new one was added * or an older one was found due to something like a change in the uid) * g. The UID or hardware address have changed. * * Clients may renew leases using full DORA cycles or just RAs. This means * that reusability must be checked when acking both DISCOVERs and REQUESTs. * When a lease cannot be reused, ack_lease() calls supersede_lease() which * updates the lease start time (among other things). If this occurs on the * DISCOVER, then the lease will virtually always be seen as young enough to * reuse on the ensuing REQUEST and the lease updates will not get committed * to the lease file. The lease.cannot_reuse flag is used to handle this * this situation. * * \param packet inbound packet received from the client * \param new_lease candidate new lease to associate with the client * \param lease current lease associated with the client * \param options option state to search and update * * \return 1 if the lease can be reused. */ int reuse_lease (struct packet* packet, struct lease* new_lease, struct lease* lease, struct lease_state *state, int offer) { int reusable = 0; /* To even consider reuse all of the following must be true: * 1 - reuse hasn't already disqualified * 2 - current lease is active * 3 - DNS info hasn't changed * 4 - the host declaration hasn't changed * 5 - the uid hasn't changed * 6 - the hardware address hasn't changed */ if ((lease->cannot_reuse == 0) && (lease->binding_state == FTS_ACTIVE) && (new_lease->ddns_cb == NULL) && (lease->host == new_lease->host) && (lease->uid_len == new_lease->uid_len) && (memcmp(lease->uid, new_lease->uid, lease->uid_len) == 0) && (lease->hardware_addr.hlen == new_lease->hardware_addr.hlen) && (memcmp(&lease->hardware_addr.hbuf[0], &new_lease->hardware_addr.hbuf[0], lease->hardware_addr.hlen) == 0)) { int thresh = DEFAULT_CACHE_THRESHOLD; struct option_cache* oc = NULL; struct data_string d1; /* Look up threshold value */ memset(&d1, 0, sizeof(struct data_string)); if ((oc = lookup_option(&server_universe, state->options, SV_CACHE_THRESHOLD)) && (evaluate_option_cache(&d1, packet, new_lease, NULL, packet->options, state->options, &new_lease->scope, oc, MDL))) { if (d1.len == 1 && (d1.data[0] < 100)) thresh = d1.data[0]; data_string_forget(&d1, MDL); } /* If threshold is enabled, check lease age */ if (thresh > 0) { int limit = 0; int lease_length = 0; long lease_age = 0; /* Calculate limit in seconds */ lease_length = lease->ends - lease->starts; if (lease_length <= (INT_MAX / thresh)) limit = lease_length * thresh / 100; else limit = lease_length / 100 * thresh; /* Note new_lease->starts is really just cur_time */ lease_age = new_lease->starts - lease->starts; /* Is the lease young enough to reuse? */ if (lease_age <= limit) { /* Restore expiry to its original value */ state->offered_expiry = lease->ends; /* Restore bindings. This fixes 37368. */ if (new_lease->scope != NULL) { if (lease->scope != NULL) { binding_scope_dereference( &lease->scope, MDL); } binding_scope_reference(&lease->scope, new_lease->scope, MDL); } /* restore client hostname, fixes 42849. */ if (new_lease->client_hostname) { lease->client_hostname = new_lease->client_hostname; new_lease->client_hostname = NULL; } /* We're cleared to reuse it */ log_debug("reuse_lease: lease age %ld (secs)" " under %d%% threshold, reply with " "unaltered, existing lease for %s", lease_age, thresh, piaddr(lease->ip_addr)); reusable = 1; } } } /* If we can't reuse it and this is an offer disqualify reuse for * ensuing REQUEST, otherwise clear the flag. */ lease->cannot_reuse = (!reusable && offer == DHCPOFFER); return (reusable); } /* \brief Validates a proposed value for use as a lease time * * Convenience function used for catching calculeated lease * times that overflow 4-byte times used in v4 protocol. * * We use variables of type TIME in lots of places, which on * 64-bit systems is 8 bytes while on 32-bit OSs it is int32_t, * so we have all sorts of fun places to mess things up. * This function checks a calculated lease time for and if it * is unsuitable for use as a lease time, the given alternate * value is returned. * \param calculated * \param alternate * * \returen either the calculated value if it is valid, or * the alternate value supplied */ TIME leaseTimeCheck(TIME calculated, TIME alternate) { if ((sizeof(TIME) > 4 && calculated >= INFINITE_TIME) || (calculated < cur_time)) { return (alternate); } return (calculated); } dhcp-4.4.1/server/dhcpd.8000644 000765 000024 00000071722 13243301226 015405 0ustar00tmarkstaff000000 000000 .\" dhcpd.8 .\" .\" Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") .\" Copyright (c) 1996-2003 by Internet Software Consortium .\" .\" 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/. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT .\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" Internet Systems Consortium, Inc. .\" 950 Charter Street .\" Redwood City, CA 94063 .\" .\" https://www.isc.org/ .\" .\" This software has been written for Internet Systems Consortium .\" by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. .\" .\" Support and other services are available for ISC products - see .\" https://www.isc.org for more information or to learn more about ISC. .\" .\" $Id: dhcpd.8,v 1.35 2011/05/20 13:48:33 tomasz Exp $ .\" .TH dhcpd 8 .SH NAME dhcpd - Dynamic Host Configuration Protocol Server .SH SYNOPSIS .B dhcpd [ .B -p .I port ] [ .B -f ] [ .B -d ] [ .B -q ] [ .B -t | .B -T ] [ .B -4 | .B -6 ] [ .B -4o6 .I port ] [ .B -s .I server ] [ .B -cf .I config-file ] [ .B -lf .I lease-file ] [ .B -pf .I pid-file ] [ .B --no-pid ] [ .B -user .I user ] [ .B -group .I group ] [ .B -chroot .I dir ] [ .B -tf .I trace-output-file ] [ .B -play .I trace-playback-file ] [ .I if0 [ .I ...ifN ] ] .B dhcpd --version .SH DESCRIPTION The Internet Systems Consortium DHCP Server, dhcpd, implements the Dynamic Host Configuration Protocol (DHCP) and the Internet Bootstrap Protocol (BOOTP). DHCP allows hosts on a TCP/IP network to request and be assigned IP addresses, and also to discover information about the network to which they are attached. BOOTP provides similar functionality, with certain restrictions. .SH OPERATION .PP The DHCP protocol allows a host which is unknown to the network administrator to be automatically assigned a new IP address out of a pool of IP addresses for its network. In order for this to work, the network administrator allocates address pools in each subnet and enters them into the dhcpd.conf(5) file. .PP There are two versions of the DHCP protocol DHCPv4 and DHCPv6. At startup the server may be started for one or the other via the .B -4 or .B -6 arguments. .PP On startup, dhcpd reads the .IR dhcpd.conf file and stores a list of available addresses on each subnet in memory. When a client requests an address using the DHCP protocol, dhcpd allocates an address for it. Each client is assigned a lease, which expires after an amount of time chosen by the administrator (by default, one day). Before leases expire, the clients to which leases are assigned are expected to renew them in order to continue to use the addresses. Once a lease has expired, the client to which that lease was assigned is no longer permitted to use the leased IP address. .PP In order to keep track of leases across system reboots and server restarts, dhcpd keeps a list of leases it has assigned in the dhcpd.leases(5) file. Before dhcpd grants a lease to a host, it records the lease in this file and makes sure that the contents of the file are flushed to disk. This ensures that even in the event of a system crash, dhcpd will not forget about a lease that it has assigned. On startup, after reading the dhcpd.conf file, dhcpd reads the dhcpd.leases file to refresh its memory about what leases have been assigned. .PP New leases are appended to the end of the dhcpd.leases file. In order to prevent the file from becoming arbitrarily large, from time to time dhcpd creates a new dhcpd.leases file from its in-core lease database. Once this file has been written to disk, the old file is renamed .IR dhcpd.leases~ , and the new file is renamed dhcpd.leases. If the system crashes in the middle of this process, whichever dhcpd.leases file remains will contain all the lease information, so there is no need for a special crash recovery process. .PP BOOTP support is also provided by this server. Unlike DHCP, the BOOTP protocol does not provide a protocol for recovering dynamically-assigned addresses once they are no longer needed. It is still possible to dynamically assign addresses to BOOTP clients, but some administrative process for reclaiming addresses is required. By default, leases are granted to BOOTP clients in perpetuity, although the network administrator may set an earlier cutoff date or a shorter lease length for BOOTP leases if that makes sense. .PP BOOTP clients may also be served in the old standard way, which is to simply provide a declaration in the dhcpd.conf file for each BOOTP client, permanently assigning an address to each client. .PP Whenever changes are made to the dhcpd.conf file, dhcpd must be restarted. To restart dhcpd, send a SIGTERM (signal 15) to the process ID contained in .IR RUNDIR/dhcpd.pid , and then re-invoke dhcpd. Because the DHCP server database is not as lightweight as a BOOTP database, dhcpd does not automatically restart itself when it sees a change to the dhcpd.conf file. .PP Note: We get a lot of complaints about this. We realize that it would be nice if one could send a SIGHUP to the server and have it reload the database. This is not technically impossible, but it would require a great deal of work, our resources are extremely limited, and they can be better spent elsewhere. So please don't complain about this on the mailing list unless you're prepared to fund a project to implement this feature, or prepared to do it yourself. .SH COMMAND LINE .PP The names of the network interfaces on which dhcpd should listen for broadcasts may be specified on the command line. This should be done on systems where dhcpd is unable to identify non-broadcast interfaces, but should not be required on other systems. If no interface names are specified on the command line dhcpd will identify all network interfaces which are up, eliminating non-broadcast interfaces if possible, and listen for DHCP broadcasts on each interface. .PP .SH COMMAND LINE OPTIONS .TP .BI \-4 Run as a DHCP server. This is the default and cannot be combined with \fB\-6\fR. .TP .BI \-6 Run as a DHCPv6 server. This cannot be combined with \fB\-4\fR. .TP .BI \-4o6 \ port Participate in the DHCPv4 over DHCPv6 protocol specified by RFC 7341. This associates a DHCPv4 and a DHCPv6 server to allow the v4 server to receive v4 requests that were encapsulated in a v6 packet. Communication between the two servers is done on a pair of UDP sockets bound to ::1 \fIport\fR and \fIport + 1\fR. Both servers must be launched using the same \fIport\fR argument. .TP .BI \-p \ port The UDP port number on which .B dhcpd should listen. If unspecified .B dhcpd uses the default port of 67. This is mostly useful for debugging purposes. .TP .BI \-s \ address Specify an address or host name to which .B dhcpd should send replies rather than the broadcast address (255.255.255.255). This option is only supported in IPv4. .TP .BI \-f Force .B dhcpd to run as a foreground process instead of as a daemon in the background. This is useful when running .B dhcpd under a debugger, or when running it out of inittab on System V systems. .TP .BI \-d Send log messages to the standard error descriptor. This can be useful for debugging, and also at sites where a complete log of all dhcp activity must be kept but syslogd is not reliable or otherwise cannot be used. Normally, .B dhcpd will log all output using the \fBsyslog(3)\fR function with the log facility set to LOG_DAEMON. Note that \fB\-d\fR implies \fB\-f\fR (the daemon will not fork itself into the background). .TP .BI \-q Be quiet at startup. This suppresses the printing of the entire copyright message during startup. This might be desirable when starting .B dhcpd from a system startup script (e.g., /etc/rc). .TP .BI \-t Test the configuration file. The server tests the configuration file for correct syntax, but will not attempt to perform any network operations. This can be used to test a new configuration file automatically before installing it. .TP .BI \-T Test the lease file. The server tests the lease file for correct syntax, but will not attempt to perform any network operations. In addition to reading the lease file it will also write the leases to a temporary lease file. The current lease file will not be modified and the temporary lease file will be removed upon completion of the test. This can be used to test a new lease file automatically before installing it. .TP .BI \-user \ user Setuid to user after completing privileged operations, such as creating sockets that listen on privileged ports. This also causes the lease file to be owned by user. This option is only available if the code was compiled with the PARANOIA patch (./configure --enable-paranoia). .TP .BI \-group \ group Setgid to group after completing privileged operations, such as creating sockets that listen on privileged ports. This also causes the lease file to use group. This option is only available if the code was compiled with the PARANOIA patch (./configure --enable-paranoia). .TP .BI \-chroot \ dir Chroot to directory. This may occur before or after reading the configuration files depending on whether the code was compiled with the EARLY_CHROOT option enabled (./configure --enable-early-chroot). This option is only available if the code was compiled with the PARANOIA patch (./configure --enable-paranoia). .TP .BI \-tf \ tracefile Specify a file into which the entire startup state of the server and all the transactions it processes are logged. This can be useful in submitting bug reports - if you are getting a core dump every so often, you can start the server with the \fB-tf\fR option and then, when the server dumps core, the trace file will contain all the transactions that led up to it dumping core, so that the problem can be easily debugged with \fB-play\fR. .TP .BI \-play \ playfile Specify a file from which the entire startup state of the server and all the transactions it processed are read. The \fB-play\fR option must be specified with an alternate lease file, using the \fB-lf\fR switch, so that the DHCP server doesn't wipe out your existing lease file with its test data. The DHCP server will refuse to operate in playback mode unless you specify an alternate lease file. .TP .BI --version Print version number and exit. .PP .I Modifying default file locations: The following options can be used to modify the locations .B dhcpd uses for its files. Because of the importance of using the same lease database at all times when running dhcpd in production, these options should be used \fBonly\fR for testing lease files or database files in a non-production environment. .TP .BI \-cf \ config-file Path to alternate configuration file. .TP .BI \-lf \ lease-file Path to alternate lease file. .TP .BI \-pf \ pid-file Path to alternate pid file. .TP .BI \--no-pid Option to disable writing pid files. By default the program will write a pid file. If the program is invoked with this option it will not check for an existing server process. .PP .SH PORTS During operations the server may use multiple UDP and TCP ports to provide different functions. Which ports are opened depends on both the way you compiled your code and the configuration you supply. The following should provide you an idea of what ports may be in use. Normally a DHCPv4 server will open a raw UDP socket to receive and send most DHCPv4 packets. It also opens a fallback UDP socket for use in sending unicast packets. Normally these will both use the well known port number for BOOTPS. For each DHCPv4 failover peer you list in the configuration file there will be a TCP socket listening for connections on the ports specified in the configuration file. When the peer connects there will be another socket for the established connection. For the established connection the side (primary or secondary) opening the connection will use a random port. For DHCPv6 the server opens a UDP socket on the well known dhcpv6-server port. The server opens an icmp socket for doing ping requests to check if addresses are in use. If you have included an omapi-port statement in your configuration file then the server will open a TCP socket on that port to listen for OMPAI connections. When something connects another port will be used for the established connection. When DDNS is enabled at compile time (see includes/site.h) the server will open both a v4 and a v6 UDP socket on random ports, unless DDNS updates are globally disabled by setting ddns-update-style to none in the configuration file. .PP .SH CONFIGURATION The syntax of the dhcpd.conf(5) file is discussed separately. This section should be used as an overview of the configuration process, and the dhcpd.conf(5) documentation should be consulted for detailed reference information. .PP .SH Subnets dhcpd needs to know the subnet numbers and netmasks of all subnets for which it will be providing service. In addition, in order to dynamically allocate addresses, it must be assigned one or more ranges of addresses on each subnet which it can in turn assign to client hosts as they boot. Thus, a very simple configuration providing DHCP support might look like this: .nf .sp 1 subnet 239.252.197.0 netmask 255.255.255.0 { range 239.252.197.10 239.252.197.250; } .fi .PP Multiple address ranges may be specified like this: .nf .sp 1 subnet 239.252.197.0 netmask 255.255.255.0 { range 239.252.197.10 239.252.197.107; range 239.252.197.113 239.252.197.250; } .fi .PP If a subnet will only be provided with BOOTP service and no dynamic address assignment, the range clause can be left out entirely, but the subnet statement must appear. .PP .SH Lease Lengths DHCP leases can be assigned almost any length from zero seconds to infinity. What lease length makes sense for any given subnet, or for any given installation, will vary depending on the kinds of hosts being served. .PP For example, in an office environment where systems are added from time to time and removed from time to time, but move relatively infrequently, it might make sense to allow lease times of a month or more. In a final test environment on a manufacturing floor, it may make more sense to assign a maximum lease length of 30 minutes - enough time to go through a simple test procedure on a network appliance before packaging it up for delivery. .PP It is possible to specify two lease lengths: the default length that will be assigned if a client doesn't ask for any particular lease length, and a maximum lease length. These are specified as clauses to the subnet command: .nf .sp 1 subnet 239.252.197.0 netmask 255.255.255.0 { range 239.252.197.10 239.252.197.107; default-lease-time 600; max-lease-time 7200; } .fi .PP This particular subnet declaration specifies a default lease time of 600 seconds (ten minutes), and a maximum lease time of 7200 seconds (two hours). Other common values would be 86400 (one day), 604800 (one week) and 2592000 (30 days). .PP Each subnet need not have the same lease\(emin the case of an office environment and a manufacturing environment served by the same DHCP server, it might make sense to have widely disparate values for default and maximum lease times on each subnet. .SH BOOTP Support Each BOOTP client must be explicitly declared in the dhcpd.conf file. A very basic client declaration will specify the client network interface's hardware address and the IP address to assign to that client. If the client needs to be able to load a boot file from the server, that file's name must be specified. A simple bootp client declaration might look like this: .nf .sp 1 host haagen { hardware ethernet 08:00:2b:4c:59:23; fixed-address 239.252.197.9; filename "/tftpboot/haagen.boot"; } .fi .SH Options DHCP (and also BOOTP with Vendor Extensions) provide a mechanism whereby the server can provide the client with information about how to configure its network interface (e.g., subnet mask), and also how the client can access various network services (e.g., DNS, IP routers, and so on). .PP These options can be specified on a per-subnet basis, and, for BOOTP clients, also on a per-client basis. In the event that a BOOTP client declaration specifies options that are also specified in its subnet declaration, the options specified in the client declaration take precedence. A reasonably complete DHCP configuration might look something like this: .nf .sp 1 subnet 239.252.197.0 netmask 255.255.255.0 { range 239.252.197.10 239.252.197.250; default-lease-time 600; max-lease-time 7200; option subnet-mask 255.255.255.0; option broadcast-address 239.252.197.255; option routers 239.252.197.1; option domain-name-servers 239.252.197.2, 239.252.197.3; option domain-name "isc.org"; } .fi .PP A bootp host on that subnet that needs to be in a different domain and use a different name server might be declared as follows: .nf .sp 1 host haagen { hardware ethernet 08:00:2b:4c:59:23; fixed-address 239.252.197.9; filename "/tftpboot/haagen.boot"; option domain-name-servers 192.5.5.1; option domain-name "example.com"; } .fi .PP A more complete description of the dhcpd.conf file syntax is provided in dhcpd.conf(5). .SH OMAPI The DHCP server provides the capability to modify some of its configuration while it is running, without stopping it, modifying its database files, and restarting it. This capability is currently provided using OMAPI - an API for manipulating remote objects. OMAPI clients connect to the server using TCP/IP, authenticate, and can then examine the server's current status and make changes to it. .PP Rather than implementing the underlying OMAPI protocol directly, user programs should use the dhcpctl API or OMAPI itself. Dhcpctl is a wrapper that handles some of the housekeeping chores that OMAPI does not do automatically. Dhcpctl and OMAPI are documented in \fBdhcpctl(3)\fR and \fBomapi(3)\fR. .PP OMAPI exports objects, which can then be examined and modified. The DHCP server exports the following objects: lease, host, failover-state and group. Each object has a number of methods that are provided: lookup, create, and destroy. In addition, it is possible to look at attributes that are stored on objects, and in some cases to modify those attributes. .SH THE LEASE OBJECT Leases can't currently be created or destroyed, but they can be looked up to examine and modify their state. .PP Leases have the following attributes: .PP .B state \fIinteger\fR lookup, examine .RS 0.5i .nf 1 = free 2 = active 3 = expired 4 = released 5 = abandoned 6 = reset 7 = backup 8 = reserved 9 = bootp .fi .RE .PP .B ip-address \fIdata\fR lookup, examine .RS 0.5i The IP address of the lease. .RE .PP .B dhcp-client-identifier \fIdata\fR lookup, examine, update .RS 0.5i The client identifier that the client used when it acquired the lease. Not all clients send client identifiers, so this may be empty. .RE .PP .B client-hostname \fIdata\fR examine, update .RS 0.5i The value the client sent in the host-name option. .RE .PP .B host \fIhandle\fR examine .RS 0.5i the host declaration associated with this lease, if any. .RE .PP .B subnet \fIhandle\fR examine .RS 0.5i the subnet object associated with this lease (the subnet object is not currently supported). .RE .PP .B pool \fIhandle\fR examine .RS 0.5i the pool object associated with this lease (the pool object is not currently supported). .RE .PP .B billing-class \fIhandle\fR examine .RS 0.5i the handle to the class to which this lease is currently billed, if any (the class object is not currently supported). .RE .PP .B hardware-address \fIdata\fR examine, update .RS 0.5i the hardware address (chaddr) field sent by the client when it acquired its lease. .RE .PP .B hardware-type \fIinteger\fR examine, update .RS 0.5i the type of the network interface that the client reported when it acquired its lease. .RE .PP .B ends \fItime\fR examine .RS 0.5i the time when the lease's current state ends, as understood by the client. .RE .PP .B tstp \fItime\fR examine .RS 0.5i the time when the lease's current state ends, as understood by the server. .RE .B tsfp \fItime\fR examine .RS 0.5i the adjusted time when the lease's current state ends, as understood by the failover peer (if there is no failover peer, this value is undefined). Generally this value is only adjusted for expired, released, or reset leases while the server is operating in partner-down state, and otherwise is simply the value supplied by the peer. .RE .B atsfp \fItime\fR examine .RS 0.5i the actual tsfp value sent from the peer. This value is forgotten when a lease binding state change is made, to facilitate retransmission logic. .RE .PP .B cltt \fItime\fR examine .RS 0.5i The time of the last transaction with the client on this lease. .RE .SH THE HOST OBJECT Hosts can be created, destroyed, looked up, examined and modified. If a host declaration is created or deleted using OMAPI, that information will be recorded in the dhcpd.leases file. It is permissible to delete host declarations that are declared in the dhcpd.conf file. .PP Hosts have the following attributes: .PP .B name \fIdata\fR lookup, examine, modify .RS 0.5i the name of the host declaration. This name must be unique among all host declarations. .RE .PP .B group \fIhandle\fR examine, modify .RS 0.5i the named group associated with the host declaration, if there is one. .RE .PP .B hardware-address \fIdata\fR lookup, examine, modify .RS 0.5i the link-layer address that will be used to match the client, if any. Only valid if hardware-type is also present. .RE .PP .B hardware-type \fIinteger\fR lookup, examine, modify .RS 0.5i the type of the network interface that will be used to match the client, if any. Only valid if hardware-address is also present. .RE .PP .B dhcp-client-identifier \fIdata\fR lookup, examine, modify .RS 0.5i the dhcp-client-identifier option that will be used to match the client, if any. .RE .PP .B ip-address \fIdata\fR examine, modify .RS 0.5i a fixed IP address which is reserved for a DHCP client that matches this host declaration. The IP address will only be assigned to the client if it is valid for the network segment to which the client is connected. .RE .PP .B statements \fIdata\fR modify .RS 0.5i a list of statements in the format of the dhcpd.conf file that will be executed whenever a message from the client is being processed. .RE .PP .B known \fIinteger\fR examine, modify .RS 0.5i if nonzero, indicates that a client matching this host declaration will be treated as \fIknown\fR in pool permit lists. If zero, the client will not be treated as known. .RE .SH THE GROUP OBJECT Named groups can be created, destroyed, looked up, examined and modified. If a group declaration is created or deleted using OMAPI, that information will be recorded in the dhcpd.leases file. It is permissible to delete group declarations that are declared in the dhcpd.conf file. .PP Named groups currently can only be associated with hosts - this allows one set of statements to be efficiently attached to more than one host declaration. .PP Groups have the following attributes: .PP .B name \fIdata\fR .RS 0.5i the name of the group. All groups that are created using OMAPI must have names, and the names must be unique among all groups. .RE .PP .B statements \fIdata\fR .RS 0.5i a list of statements in the format of the dhcpd.conf file that will be executed whenever a message from a client whose host declaration references this group is processed. .RE .SH THE CONTROL OBJECT The control object allows you to shut the server down. If the server is doing failover with another peer, it will make a clean transition into the shutdown state and notify its peer, so that the peer can go into partner down, and then record the "recover" state in the lease file so that when the server is restarted, it will automatically resynchronize with its peer. .PP On shutdown the server will also attempt to cleanly shut down all OMAPI connections. If these connections do not go down cleanly after five seconds, they are shut down preemptively. It can take as much as 25 seconds from the beginning of the shutdown process to the time that the server actually exits. .PP To shut the server down, open its control object and set the state attribute to 2. .SH THE FAILOVER-STATE OBJECT The failover-state object is the object that tracks the state of the failover protocol as it is being managed for a given failover peer. The failover object has the following attributes (please see .B dhcpd.conf (5) for explanations about what these attributes mean): .PP .B name \fIdata\fR examine .RS 0.5i Indicates the name of the failover peer relationship, as described in the server's \fBdhcpd.conf\fR file. .RE .PP .B partner-address \fIdata\fR examine .RS 0.5i Indicates the failover partner's IP address. .RE .PP .B local-address \fIdata\fR examine .RS 0.5i Indicates the IP address that is being used by the DHCP server for this failover pair. .RE .PP .B partner-port \fIdata\fR examine .RS 0.5i Indicates the TCP port on which the failover partner is listening for failover protocol connections. .RE .PP .B local-port \fIdata\fR examine .RS 0.5i Indicates the TCP port on which the DHCP server is listening for failover protocol connections for this failover pair. .RE .PP .B max-outstanding-updates \fIinteger\fR examine .RS 0.5i Indicates the number of updates that can be outstanding and unacknowledged at any given time, in this failover relationship. .RE .PP .B mclt \fIinteger\fR examine .RS 0.5i Indicates the maximum client lead time in this failover relationship. .RE .PP .B load-balance-max-secs \fIinteger\fR examine .RS 0.5i Indicates the maximum value for the secs field in a client request before load balancing is bypassed. .RE .PP .B load-balance-hba \fIdata\fR examine .RS 0.5i Indicates the load balancing hash bucket array for this failover relationship. .RE .PP .B local-state \fIinteger\fR examine, modify .RS 0.5i Indicates the present state of the DHCP server in this failover relationship. Possible values for state are: .RE .RS 1i .PP .nf 1 - startup 2 - normal 3 - communications interrupted 4 - partner down 5 - potential conflict 6 - recover 7 - paused 8 - shutdown 9 - recover done 10 - resolution interrupted 11 - conflict done 254 - recover wait .fi .RE .PP .RS 0.5i (Note that some of the above values have changed since DHCP 3.0.x.) .RE .PP .RS 0.5i In general it is not a good idea to make changes to this state. However, in the case that the failover partner is known to be down, it can be useful to set the DHCP server's failover state to partner down. At this point the DHCP server will take over service of the failover partner's leases as soon as possible, and will give out normal leases, not leases that are restricted by MCLT. If you do put the DHCP server into the partner-down when the other DHCP server is not in the partner-down state, but is not reachable, IP address assignment conflicts are possible, even likely. Once a server has been put into partner-down mode, its failover partner must not be brought back online until communication is possible between the two servers. .RE .PP .B partner-state \fIinteger\fR examine .RS 0.5i Indicates the present state of the failover partner. .RE .PP .B local-stos \fIinteger\fR examine .RS 0.5i Indicates the time at which the DHCP server entered its present state in this failover relationship. .RE .PP .B partner-stos \fIinteger\fR examine .RS 0.5i Indicates the time at which the failover partner entered its present state. .RE .PP .B hierarchy \fIinteger\fR examine .RS 0.5i Indicates whether the DHCP server is primary (0) or secondary (1) in this failover relationship. .RE .PP .B last-packet-sent \fIinteger\fR examine .RS 0.5i Indicates the time at which the most recent failover packet was sent by this DHCP server to its failover partner. .RE .PP .B last-timestamp-received \fIinteger\fR examine .RS 0.5i Indicates the timestamp that was on the failover message most recently received from the failover partner. .RE .PP .B skew \fIinteger\fR examine .RS 0.5i Indicates the skew between the failover partner's clock and this DHCP server's clock .RE .PP .B max-response-delay \fIinteger\fR examine .RS 0.5i Indicates the time in seconds after which, if no message is received from the failover partner, the partner is assumed to be out of communication. .RE .PP .B cur-unacked-updates \fIinteger\fR examine .RS 0.5i Indicates the number of update messages that have been received from the failover partner but not yet processed. .RE .SH FILES .B ETCDIR/dhcpd.conf, DBDIR/dhcpd.leases, RUNDIR/dhcpd.pid, .B DBDIR/dhcpd.leases~. .SH SEE ALSO dhclient(8), dhcrelay(8), dhcpd.conf(5), dhcpd.leases(5) .SH AUTHOR .B dhcpd(8) was originally written by Ted Lemon under a contract with Vixie Labs. Funding for this project was provided by Internet Systems Consortium. Version 3 of the DHCP server was funded by Nominum, Inc. Information about Internet Systems Consortium is available at .B https://www.isc.org/\fR. dhcp-4.4.1/server/dhcpd.c000644 000765 000024 00000142245 13243301226 015457 0ustar00tmarkstaff000000 000000 /* dhcpd.c DHCP Server Daemon. */ /* * Copyright (c) 2004-2018 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1996-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ static const char copyright[] = "Copyright 2004-2018 Internet Systems Consortium."; static const char arr [] = "All rights reserved."; static const char message [] = "Internet Systems Consortium DHCP Server"; static const char url [] = "For info, please visit https://www.isc.org/software/dhcp/"; #include "dhcpd.h" #include #include #include #include #include #include #include #include #if defined (PARANOIA) # include # include # include /* get around the ISC declaration of group */ # define group real_group # include # undef group /* global values so db.c can look at them */ uid_t set_uid = 0; gid_t set_gid = 0; #endif /* PARANOIA */ struct class unknown_class; struct class known_class; struct iaddr server_identifier; int server_identifier_matched; #if defined (NSUPDATE) /* This stuff is always executed to figure the default values for certain ddns variables. */ char std_nsupdate [] = " \n\ option server.ddns-hostname = \n\ pick (option fqdn.hostname, option host-name, config-option host-name); \n\ option server.ddns-domainname = config-option domain-name; \n\ option server.ddns-rev-domainname = \"in-addr.arpa.\";"; /* Stores configured DDNS conflict detection flags */ u_int16_t ddns_conflict_mask; #endif /* NSUPDATE */ int ddns_update_style; int dont_use_fsync = 0; /* 0 = default, use fsync, 1 = don't use fsync */ int server_id_check = 0; /* 0 = default, don't check server id, 1 = do check */ #ifdef DHCPv6 int prefix_length_mode = PLM_PREFER; int do_release_on_roam = 0; /* 0 = default, do not release v6 leases on roam */ #endif #ifdef EUI_64 int persist_eui64 = 1; /* 1 = write EUI64 leases to disk, 0 = don't */ #endif int authoring_byte_order = 0; /* 0 = not set */ int lease_id_format = TOKEN_OCTAL; /* octal by default */ u_int32_t abandon_lease_time = DEFAULT_ABANDON_LEASE_TIME; const char *path_dhcpd_conf = _PATH_DHCPD_CONF; const char *path_dhcpd_db = _PATH_DHCPD_DB; const char *path_dhcpd_pid = _PATH_DHCPD_PID; /* False (default) => we write and use a pid file */ isc_boolean_t no_pid_file = ISC_FALSE; int dhcp_max_agent_option_packet_length = DHCP_MTU_MAX; static omapi_auth_key_t *omapi_key = (omapi_auth_key_t *)0; int omapi_port; #if defined (TRACING) trace_type_t *trace_srandom; #endif char *progname; static isc_result_t verify_addr (omapi_object_t *l, omapi_addr_t *addr) { return ISC_R_SUCCESS; } static isc_result_t verify_auth (omapi_object_t *p, omapi_auth_key_t *a) { if (a != omapi_key) return DHCP_R_INVALIDKEY; return ISC_R_SUCCESS; } static void omapi_listener_start (void *foo) { omapi_object_t *listener; isc_result_t result; struct timeval tv; listener = (omapi_object_t *)0; result = omapi_generic_new (&listener, MDL); if (result != ISC_R_SUCCESS) log_fatal ("Can't allocate new generic object: %s", isc_result_totext (result)); result = omapi_protocol_listen (listener, (unsigned)omapi_port, 1); if (result == ISC_R_SUCCESS && omapi_key) result = omapi_protocol_configure_security (listener, verify_addr, verify_auth); if (result != ISC_R_SUCCESS) { log_error ("Can't start OMAPI protocol: %s", isc_result_totext (result)); tv.tv_sec = cur_tv.tv_sec + 5; tv.tv_usec = cur_tv.tv_usec; add_timeout (&tv, omapi_listener_start, 0, 0, 0); } omapi_object_dereference (&listener, MDL); } #ifndef UNIT_TEST #define DHCPD_USAGE0 \ "[-p ] [-f] [-d] [-q] [-t|-T]\n" #ifdef DHCPv6 #ifdef DHCP4o6 #define DHCPD_USAGE1 \ " [-4|-6] [-4o6 ]\n" \ " [-cf config-file] [-lf lease-file]\n" #else /* DHCP4o6 */ #define DHCPD_USAGE1 \ " [-4|-6] [-cf config-file] [-lf lease-file]\n" #endif /* DHCP4o6 */ #else /* !DHCPv6 */ #define DHCPD_USAGE1 \ " [-cf config-file] [-lf lease-file]\n" #endif /* DHCPv6 */ #if defined (PARANOIA) #define DHCPD_USAGEP \ " [-user user] [-group group] [-chroot dir]\n" #else #define DHCPD_USAGEP "" #endif /* PARANOIA */ #if defined (TRACING) #define DHCPD_USAGET \ " [-tf trace-output-file]\n" \ " [-play trace-input-file]\n" #else #define DHCPD_USAGET "" #endif /* TRACING */ #define DHCPD_USAGEC \ " [-pf pid-file] [--no-pid] [-s server]\n" \ " [if0 [...ifN]]" #define DHCPD_USAGEH "{--version|--help|-h}" /*! * * \brief Print the generic usage message * * If the user has provided an incorrect command line print out * the description of the command line. The arguments provide * a way for the caller to request more specific information about * the error be printed as well. Mostly this will be that some * comamnd doesn't include its argument. * * \param sfmt - The basic string and format for the specific error * \param sarg - Generally the offending argument from the comamnd line. * * \return Nothing */ static char use_noarg[] = "No argument for command: %s "; static void usage(const char *sfmt, const char *sarg) { log_info("%s %s", message, PACKAGE_VERSION); log_info(copyright); log_info(arr); log_info(url); /* If desired print out the specific error message */ #ifdef PRINT_SPECIFIC_CL_ERRORS if (sfmt != NULL) log_error(sfmt, sarg); #endif log_fatal("Usage: %s %s%s%s%s%s\n %s %s", isc_file_basename(progname), DHCPD_USAGE0, DHCPD_USAGE1, DHCPD_USAGEP, DHCPD_USAGET, DHCPD_USAGEC, isc_file_basename(progname), DHCPD_USAGEH); } /* Note: If we add unit tests to test setup_chroot it will * need to be moved to be outside the ifndef UNIT_TEST block. */ #if defined (PARANOIA) /* to be used in one of two possible scenarios */ static void setup_chroot (char *chroot_dir) { if (geteuid()) log_fatal ("you must be root to use chroot"); if (chroot(chroot_dir)) { log_fatal ("chroot(\"%s\"): %m", chroot_dir); } if (chdir ("/")) { /* probably permission denied */ log_fatal ("chdir(\"/\"): %m"); } } #endif /* PARANOIA */ int main(int argc, char **argv) { int fd; int i, status; struct servent *ent; char *s; int cftest = 0; int lftest = 0; int pid; char pbuf [20]; #ifndef DEBUG int daemon = 1; int dfd[2] = { -1, -1 }; #endif int quiet = 0; char *server = (char *)0; isc_result_t result; unsigned seed; struct interface_info *ip; #if defined (NSUPDATE) struct parse *parse; int lose; #endif int have_dhcpd_conf = 0; int have_dhcpd_db = 0; int have_dhcpd_pid = 0; #ifdef DHCPv6 int local_family_set = 0; #ifdef DHCP4o6 u_int16_t dhcp4o6_port = 0; #endif /* DHCP4o6 */ #endif /* DHCPv6 */ #if defined (TRACING) char *traceinfile = (char *)0; char *traceoutfile = (char *)0; #endif #if defined (PARANOIA) char *set_user = 0; char *set_group = 0; char *set_chroot = 0; #endif /* PARANOIA */ #ifdef OLD_LOG_NAME progname = "dhcpd"; #else progname = argv[0]; #endif /* Make sure that file descriptors 0 (stdin), 1, (stdout), and 2 (stderr) are open. To do this, we assume that when we open a file the lowest available file descriptor is used. */ fd = open("/dev/null", O_RDWR); if (fd == 0) fd = open("/dev/null", O_RDWR); if (fd == 1) fd = open("/dev/null", O_RDWR); if (fd == 2) log_perror = 0; /* No sense logging to /dev/null. */ else if (fd != -1) close(fd); /* Parse arguments changing daemon */ for (i = 1; i < argc; i++) { if (!strcmp (argv [i], "-f")) { #ifndef DEBUG daemon = 0; #endif } else if (!strcmp (argv [i], "-d")) { #ifndef DEBUG daemon = 0; #endif } else if (!strcmp (argv [i], "-t")) { #ifndef DEBUG daemon = 0; #endif } else if (!strcmp (argv [i], "-T")) { #ifndef DEBUG daemon = 0; #endif } else if (!strcmp (argv [i], "--version")) { const char vstring[] = "isc-dhcpd-"; IGNORE_RET(write(STDERR_FILENO, vstring, strlen(vstring))); IGNORE_RET(write(STDERR_FILENO, PACKAGE_VERSION, strlen(PACKAGE_VERSION))); IGNORE_RET(write(STDERR_FILENO, "\n", 1)); exit (0); } else if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "-h")) { const char *pname = isc_file_basename(progname); IGNORE_RET(write(STDERR_FILENO, "Usage: ", 7)); IGNORE_RET(write(STDERR_FILENO, pname, strlen(pname))); IGNORE_RET(write(STDERR_FILENO, " ", 1)); IGNORE_RET(write(STDERR_FILENO, DHCPD_USAGE0, strlen(DHCPD_USAGE0))); IGNORE_RET(write(STDERR_FILENO, DHCPD_USAGE1, strlen(DHCPD_USAGE1))); #if defined (PARANOIA) IGNORE_RET(write(STDERR_FILENO, DHCPD_USAGEP, strlen(DHCPD_USAGEP))); #endif #if defined (TRACING) IGNORE_RET(write(STDERR_FILENO, DHCPD_USAGET, strlen(DHCPD_USAGET))); #endif IGNORE_RET(write(STDERR_FILENO, DHCPD_USAGEC, strlen(DHCPD_USAGEC))); IGNORE_RET(write(STDERR_FILENO, "\n", 1)); IGNORE_RET(write(STDERR_FILENO, " ", 7)); IGNORE_RET(write(STDERR_FILENO, pname, strlen(pname))); IGNORE_RET(write(STDERR_FILENO, " ", 1)); IGNORE_RET(write(STDERR_FILENO, DHCPD_USAGEH, strlen(DHCPD_USAGEH))); IGNORE_RET(write(STDERR_FILENO, "\n", 1)); exit(0); #ifdef TRACING } else if (!strcmp (argv [i], "-play")) { #ifndef DEBUG daemon = 0; #endif #endif } } #ifndef DEBUG /* When not forbidden prepare to become a daemon */ if (daemon) { if (pipe(dfd) == -1) log_fatal("Can't get pipe: %m"); if ((pid = fork ()) < 0) log_fatal("Can't fork daemon: %m"); if (pid != 0) { /* Parent: wait for the child to start */ int n; (void) close(dfd[1]); do { char buf; n = read(dfd[0], &buf, 1); if (n == 1) _exit((int)buf); } while (n == -1 && errno == EINTR); _exit(1); } /* Child */ (void) close(dfd[0]); } #endif /* Set up the isc and dns library managers */ status = dhcp_context_create(DHCP_CONTEXT_PRE_DB, NULL, NULL); if (status != ISC_R_SUCCESS) log_fatal("Can't initialize context: %s", isc_result_totext(status)); /* Set up the client classification system. */ classification_setup (); /* Initialize the omapi system. */ result = omapi_init (); if (result != ISC_R_SUCCESS) log_fatal ("Can't initialize OMAPI: %s", isc_result_totext (result)); /* Set up the OMAPI wrappers for common objects. */ dhcp_db_objects_setup (); /* Set up the OMAPI wrappers for various server database internal objects. */ dhcp_common_objects_setup (); /* Initially, log errors to stderr as well as to syslogd. */ openlog (isc_file_basename(progname), DHCP_LOG_OPTIONS, DHCPD_LOG_FACILITY); for (i = 1; i < argc; i++) { if (!strcmp (argv [i], "-p")) { if (++i == argc) usage(use_noarg, argv[i-1]); local_port = validate_port (argv [i]); log_debug ("binding to user-specified port %d", ntohs (local_port)); } else if (!strcmp (argv [i], "-f")) { #ifndef DEBUG /* daemon = 0; */ #endif } else if (!strcmp (argv [i], "-d")) { #ifndef DEBUG /* daemon = 0; */ #endif log_perror = -1; } else if (!strcmp (argv [i], "-s")) { if (++i == argc) usage(use_noarg, argv[i-1]); server = argv [i]; #if defined (PARANOIA) } else if (!strcmp (argv [i], "-user")) { if (++i == argc) usage(use_noarg, argv[i-1]); set_user = argv [i]; } else if (!strcmp (argv [i], "-group")) { if (++i == argc) usage(use_noarg, argv[i-1]); set_group = argv [i]; } else if (!strcmp (argv [i], "-chroot")) { if (++i == argc) usage(use_noarg, argv[i-1]); set_chroot = argv [i]; #endif /* PARANOIA */ } else if (!strcmp (argv [i], "-cf")) { if (++i == argc) usage(use_noarg, argv[i-1]); path_dhcpd_conf = argv [i]; have_dhcpd_conf = 1; } else if (!strcmp (argv [i], "-lf")) { if (++i == argc) usage(use_noarg, argv[i-1]); path_dhcpd_db = argv [i]; have_dhcpd_db = 1; } else if (!strcmp (argv [i], "-pf")) { if (++i == argc) usage(use_noarg, argv[i-1]); path_dhcpd_pid = argv [i]; have_dhcpd_pid = 1; } else if (!strcmp(argv[i], "--no-pid")) { no_pid_file = ISC_TRUE; } else if (!strcmp (argv [i], "-t")) { /* test configurations only */ #ifndef DEBUG /* daemon = 0; */ #endif cftest = 1; log_perror = -1; } else if (!strcmp (argv [i], "-T")) { /* test configurations and lease file only */ #ifndef DEBUG /* daemon = 0; */ #endif cftest = 1; lftest = 1; log_perror = -1; } else if (!strcmp (argv [i], "-q")) { quiet = 1; quiet_interface_discovery = 1; #ifdef DHCPv6 } else if (!strcmp(argv[i], "-4")) { if (local_family_set && (local_family != AF_INET)) { log_fatal("Server cannot run in both IPv4 and " "IPv6 mode at the same time."); } local_family = AF_INET; local_family_set = 1; } else if (!strcmp(argv[i], "-6")) { if (local_family_set && (local_family != AF_INET6)) { log_fatal("Server cannot run in both IPv4 and " "IPv6 mode at the same time."); } local_family = AF_INET6; local_family_set = 1; #ifdef DHCP4o6 } else if (!strcmp(argv[i], "-4o6")) { if (++i == argc) usage(use_noarg, argv[i-1]); dhcp4o6_port = validate_port_pair(argv[i]); log_debug("DHCPv4 over DHCPv6 over ::1 port %d and %d", ntohs(dhcp4o6_port), ntohs(dhcp4o6_port) + 1); dhcpv4_over_dhcpv6 = 1; #endif /* DHCP4o6 */ #endif /* DHCPv6 */ #if defined (TRACING) } else if (!strcmp (argv [i], "-tf")) { if (++i == argc) usage(use_noarg, argv[i-1]); traceoutfile = argv [i]; } else if (!strcmp (argv [i], "-play")) { if (++i == argc) usage(use_noarg, argv[i-1]); traceinfile = argv [i]; trace_replay_init (); #endif /* TRACING */ } else if (argv [i][0] == '-') { usage("Unknown command %s", argv[i]); } else { struct interface_info *tmp = (struct interface_info *)0; if (strlen(argv[i]) >= sizeof(tmp->name)) log_fatal("%s: interface name too long " "(is %ld)", argv[i], (long)strlen(argv[i])); result = interface_allocate (&tmp, MDL); if (result != ISC_R_SUCCESS) log_fatal ("Insufficient memory to %s %s: %s", "record interface", argv [i], isc_result_totext (result)); strcpy (tmp -> name, argv [i]); if (interfaces) { interface_reference (&tmp -> next, interfaces, MDL); interface_dereference (&interfaces, MDL); } interface_reference (&interfaces, tmp, MDL); tmp -> flags = INTERFACE_REQUESTED; } } #if defined(DHCPv6) && defined(DHCP4o6) if (dhcpv4_over_dhcpv6) { if (!local_family_set) log_error("please specify the address family " "with DHPv4 over DHCPv6 [-4|-6]."); if ((local_family == AF_INET) && (interfaces != NULL)) log_fatal("DHCPv4 server in DHPv4 over DHCPv6 " "mode with command line specified " "interfaces."); } #endif /* DHCPv6 && DHCP4o6 */ if (!have_dhcpd_conf && (s = getenv ("PATH_DHCPD_CONF"))) { path_dhcpd_conf = s; } #ifdef DHCPv6 if (local_family == AF_INET6) { /* DHCPv6: override DHCPv4 lease and pid filenames */ if (!have_dhcpd_db) { if ((s = getenv ("PATH_DHCPD6_DB"))) path_dhcpd_db = s; else path_dhcpd_db = _PATH_DHCPD6_DB; } if (!have_dhcpd_pid) { if ((s = getenv ("PATH_DHCPD6_PID"))) path_dhcpd_pid = s; else path_dhcpd_pid = _PATH_DHCPD6_PID; } } else #endif /* DHCPv6 */ { if (!have_dhcpd_db && (s = getenv ("PATH_DHCPD_DB"))) { path_dhcpd_db = s; have_dhcpd_db = 1; } if (!have_dhcpd_pid && (s = getenv ("PATH_DHCPD_PID"))) { path_dhcpd_pid = s; have_dhcpd_pid = 1; } } /* * convert relative path names to absolute, for files that need * to be reopened after chdir() has been called */ if (have_dhcpd_db && path_dhcpd_db[0] != '/') { path_dhcpd_db = absolute_path(path_dhcpd_db); } if (!quiet) { log_info("%s %s", message, PACKAGE_VERSION); log_info (copyright); log_info (arr); log_info (url); } else { log_perror = 0; } #if defined (TRACING) trace_init (set_time, MDL); if (traceoutfile) { result = trace_begin (traceoutfile, MDL); if (result != ISC_R_SUCCESS) log_fatal ("Unable to begin trace: %s", isc_result_totext (result)); } interface_trace_setup (); parse_trace_setup (); trace_srandom = trace_type_register ("random-seed", (void *)0, trace_seed_input, trace_seed_stop, MDL); #if defined (NSUPDATE) trace_ddns_init(); #endif /* NSUPDATE */ #endif #if defined (PARANOIA) /* get user and group info if those options were given */ if (set_user) { struct passwd *tmp_pwd; if (geteuid()) log_fatal ("you must be root to set user"); if (!(tmp_pwd = getpwnam(set_user))) log_fatal ("no such user: %s", set_user); set_uid = tmp_pwd->pw_uid; /* use the user's group as the default gid */ if (!set_group) set_gid = tmp_pwd->pw_gid; } if (set_group) { /* get around the ISC declaration of group */ #define group real_group struct group *tmp_grp; if (geteuid()) log_fatal ("you must be root to set group"); if (!(tmp_grp = getgrnam(set_group))) log_fatal ("no such group: %s", set_group); set_gid = tmp_grp->gr_gid; #undef group } # if defined (EARLY_CHROOT) if (set_chroot) setup_chroot (set_chroot); # endif /* EARLY_CHROOT */ #endif /* PARANOIA */ /* Default to the DHCP/BOOTP port. */ if (!local_port) { if ((s = getenv ("DHCPD_PORT"))) { local_port = validate_port (s); log_debug ("binding to environment-specified port %d", ntohs (local_port)); } else { if (local_family == AF_INET) { ent = getservbyname("dhcp", "udp"); if (ent == NULL) { local_port = htons(67); } else { local_port = ent->s_port; } } else { /* INSIST(local_family == AF_INET6); */ ent = getservbyname("dhcpv6-server", "udp"); if (ent == NULL) { local_port = htons(547); } else { local_port = ent->s_port; } } #ifndef __CYGWIN32__ /* XXX */ endservent (); #endif } } if (local_family == AF_INET) { remote_port = htons(ntohs(local_port) + 1); } else { /* INSIST(local_family == AF_INET6); */ ent = getservbyname("dhcpv6-client", "udp"); if (ent == NULL) { remote_port = htons(546); } else { remote_port = ent->s_port; } } if (server) { if (local_family != AF_INET) { log_fatal("You can only specify address to send " "replies to when running an IPv4 server."); } if (!inet_aton (server, &limited_broadcast)) { struct hostent *he; he = gethostbyname (server); if (he) { memcpy (&limited_broadcast, he -> h_addr_list [0], sizeof limited_broadcast); } else limited_broadcast.s_addr = INADDR_BROADCAST; } } else { limited_broadcast.s_addr = INADDR_BROADCAST; } /* Get the current time... */ gettimeofday(&cur_tv, NULL); /* Set up the initial dhcp option universe. */ initialize_common_option_spaces (); initialize_server_option_spaces (); /* Add the ddns update style enumeration prior to parsing. */ add_enumeration (&ddns_styles); add_enumeration (&syslog_enum); #if defined (LDAP_CONFIGURATION) add_enumeration (&ldap_methods); #if defined (LDAP_USE_SSL) add_enumeration (&ldap_ssl_usage_enum); add_enumeration (&ldap_tls_reqcert_enum); add_enumeration (&ldap_tls_crlcheck_enum); #endif #endif if (!group_allocate (&root_group, MDL)) log_fatal ("Can't allocate root group!"); root_group -> authoritative = 0; /* Set up various hooks. */ dhcp_interface_setup_hook = dhcpd_interface_setup_hook; bootp_packet_handler = do_packet; #ifdef DHCPv6 add_enumeration (&prefix_length_modes); dhcpv6_packet_handler = do_packet6; #endif /* DHCPv6 */ #if defined (NSUPDATE) /* Set up the standard name service updater routine. */ parse = NULL; status = new_parse(&parse, -1, std_nsupdate, sizeof(std_nsupdate) - 1, "standard name service update routine", 0); if (status != ISC_R_SUCCESS) log_fatal ("can't begin parsing name service updater!"); if (parse != NULL) { lose = 0; if (!(parse_executable_statements(&root_group->statements, parse, &lose, context_any))) { end_parse(&parse); log_fatal("can't parse standard name service updater!"); } end_parse(&parse); } #endif /* Initialize icmp support... */ if (!cftest && !lftest) icmp_startup (1, lease_pinged); #if defined (TRACING) if (traceinfile) { if (!have_dhcpd_db) { log_error ("%s", ""); log_error ("** You must specify a lease file with -lf."); log_error (" Dhcpd will not overwrite your default"); log_fatal (" lease file when playing back a trace. **"); } trace_file_replay (traceinfile); #if defined (DEBUG_MEMORY_LEAKAGE) && \ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) free_everything (); omapi_print_dmalloc_usage_by_caller (); #endif exit (0); } #endif #ifdef DHCPv6 /* set up DHCPv6 hashes */ if (!ia_new_hash(&ia_na_active, DEFAULT_HASH_SIZE, MDL)) { log_fatal("Out of memory creating hash for active IA_NA."); } if (!ia_new_hash(&ia_ta_active, DEFAULT_HASH_SIZE, MDL)) { log_fatal("Out of memory creating hash for active IA_TA."); } if (!ia_new_hash(&ia_pd_active, DEFAULT_HASH_SIZE, MDL)) { log_fatal("Out of memory creating hash for active IA_PD."); } #endif /* DHCPv6 */ /* Read the dhcpd.conf file... */ if (readconf () != ISC_R_SUCCESS) log_fatal ("Configuration file errors encountered -- exiting"); postconf_initialization (quiet); #if defined (FAILOVER_PROTOCOL) dhcp_failover_sanity_check(); #endif #if defined(DHCPv6) && defined(DHCP4o6) if (dhcpv4_over_dhcpv6) { if ((local_family == AF_INET) && (interfaces != NULL)) log_fatal("DHCPv4 server in DHPv4 over DHCPv6 " "mode with config file specified " "interfaces."); } #endif /* DHCPv6 && DHCP4o6 */ #if defined (PARANOIA) && !defined (EARLY_CHROOT) if (set_chroot) setup_chroot (set_chroot); #endif /* PARANOIA && !EARLY_CHROOT */ #ifdef DHCPv6 /* log info about ipv6_ponds with large address ranges */ report_jumbo_ranges(); #endif /* test option should cause an early exit */ if (cftest && !lftest) { exit(0); } /* * First part of dealing with pid files. Check to see if * we should continue running or not. We run if: * - we are testing the lease file out * - we don't have a pid file to check * - there is no other process running */ if ((lftest == 0) && (no_pid_file == ISC_FALSE)) { /*Read previous pid file. */ if ((i = open(path_dhcpd_pid, O_RDONLY)) >= 0) { status = read(i, pbuf, (sizeof pbuf) - 1); close(i); if (status > 0) { pbuf[status] = 0; pid = atoi(pbuf); /* * If there was a previous server process and * it is still running, abort */ if (!pid || (pid != getpid() && kill(pid, 0) == 0)) log_fatal("There's already a " "DHCP server running."); } } } group_write_hook = group_writer; /* Start up the database... */ db_startup (lftest); if (lftest) exit (0); /* Discover all the network interfaces and initialize them. */ #if defined(DHCPv6) && defined(DHCP4o6) if (dhcpv4_over_dhcpv6) { int real_family = local_family; local_family = AF_INET6; /* The DHCPv4 side of DHCPv4-over-DHCPv6 service uses a specific discovery which doesn't register DHCPv6 sockets. */ if (real_family == AF_INET) discover_interfaces(DISCOVER_SERVER46); else discover_interfaces(DISCOVER_SERVER); local_family = real_family; } else #endif /* DHCPv6 && DHCP4o6 */ discover_interfaces(DISCOVER_SERVER); #ifdef DHCPv6 /* * Remove addresses from our pools that we should not issue * to clients. * * We currently have no support for this in IPv4. It is not * as important in IPv4, as making pools with ranges that * leave out interfaces and hosts is fairly straightforward * using range notation, but not so handy with CIDR notation. */ if (local_family == AF_INET6) { mark_hosts_unavailable(); mark_phosts_unavailable(); mark_interfaces_unavailable(); } #endif /* DHCPv6 */ /* Make up a seed for the random number generator from current time plus the sum of the last four bytes of each interface's hardware address interpreted as an integer. Not much entropy, but we're booting, so we're not likely to find anything better. */ seed = 0; for (ip = interfaces; ip; ip = ip -> next) { int junk; memcpy (&junk, &ip -> hw_address.hbuf [ip -> hw_address.hlen - sizeof seed], sizeof seed); seed += junk; } srandom (seed + cur_time); #if defined (TRACING) trace_seed_stash (trace_srandom, seed + cur_time); #endif postdb_startup (); #ifdef DHCPv6 /* * Set server DHCPv6 identifier - we go in order: * dhcp6.server-id in the config file * server-duid from the lease file * server-duid from the config file (the config file is read first * and the lease file overwrites the config file information) * generate a new one from the interface hardware addresses. * In all cases we write it out to the lease file. * See dhcpv6.c for discussion of setting DUID. */ if ((set_server_duid_from_option() != ISC_R_SUCCESS) && (!server_duid_isset()) && (generate_new_server_duid() != ISC_R_SUCCESS)) { log_fatal("Unable to set server identifier."); } write_server_duid(); #ifdef DHCP4o6 if (dhcpv4_over_dhcpv6) dhcp4o6_setup(dhcp4o6_port); #endif /* DHCP4o6 */ #endif /* DHCPv6 */ #ifndef DEBUG /* * Second part of dealing with pid files. Now * that we have forked we can write our pid if * appropriate. */ if (no_pid_file == ISC_FALSE) { i = open(path_dhcpd_pid, O_WRONLY|O_CREAT|O_TRUNC, 0644); if (i >= 0) { sprintf(pbuf, "%d\n", (int) getpid()); IGNORE_RET(write(i, pbuf, strlen(pbuf))); close(i); } else { log_error("Can't create PID file %s: %m.", path_dhcpd_pid); } } #if defined (PARANOIA) /* change uid to the specified one */ if (set_gid) { if (setgroups (0, (void *)0)) log_fatal ("setgroups: %m"); if (setgid (set_gid)) log_fatal ("setgid(%d): %m", (int) set_gid); } if (set_uid) { if (setuid (set_uid)) log_fatal ("setuid(%d): %m", (int) set_uid); } #endif /* PARANOIA */ /* If we were requested to log to stdout on the command line, keep doing so; otherwise, stop. */ if (log_perror == -1) log_perror = 1; else log_perror = 0; if (daemon) { if (dfd[0] != -1 && dfd[1] != -1) { char buf = 0; if (write(dfd[1], &buf, 1) != 1) log_fatal("write to parent: %m"); (void) close(dfd[1]); dfd[0] = dfd[1] = -1; } /* Become session leader and get pid... */ (void) setsid(); /* Close standard I/O descriptors. */ (void) close(0); (void) close(1); (void) close(2); /* Reopen them on /dev/null. */ (void) open("/dev/null", O_RDWR); (void) open("/dev/null", O_RDWR); (void) open("/dev/null", O_RDWR); log_perror = 0; /* No sense logging to /dev/null. */ IGNORE_RET (chdir("/")); } #endif /* !DEBUG */ #if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) || \ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) dmalloc_cutoff_generation = dmalloc_generation; dmalloc_longterm = dmalloc_outstanding; dmalloc_outstanding = 0; #endif omapi_set_int_value ((omapi_object_t *)dhcp_control_object, (omapi_object_t *)0, "state", server_running); #if defined(ENABLE_GENTLE_SHUTDOWN) /* no signal handlers until we deal with the side effects */ /* install signal handlers */ signal(SIGINT, dhcp_signal_handler); /* control-c */ signal(SIGTERM, dhcp_signal_handler); /* kill */ #endif /* Log that we are about to start working */ log_info("Server starting service."); /* * Receive packets and dispatch them... * dispatch() will never return. */ dispatch (); /* Let's return status code */ return 0; } #endif /* !UNIT_TEST */ void postconf_initialization (int quiet) { struct option_state *options = NULL; struct data_string db; struct option_cache *oc; char *s; isc_result_t result; int tmp; #if defined (NSUPDATE) struct in_addr local4, *local4_ptr = NULL; struct in6_addr local6, *local6_ptr = NULL; #endif /* Now try to get the lease file name. */ option_state_allocate(&options, MDL); execute_statements_in_scope(NULL, NULL, NULL, NULL, NULL, options, &global_scope, root_group, NULL, NULL); memset(&db, 0, sizeof db); oc = lookup_option(&server_universe, options, SV_LEASE_FILE_NAME); if (oc && evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL, &global_scope, oc, MDL)) { s = dmalloc(db.len + 1, MDL); if (!s) log_fatal("no memory for lease db filename."); memcpy(s, db.data, db.len); s[db.len] = 0; data_string_forget(&db, MDL); path_dhcpd_db = s; } oc = lookup_option(&server_universe, options, SV_PID_FILE_NAME); if (oc && evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL, &global_scope, oc, MDL)) { s = dmalloc(db.len + 1, MDL); if (!s) log_fatal("no memory for pid filename."); memcpy(s, db.data, db.len); s[db.len] = 0; data_string_forget(&db, MDL); path_dhcpd_pid = s; } #ifdef DHCPv6 if (local_family == AF_INET6) { /* * Override lease file name with dhcpv6 lease file name, * if it was set; then, do the same with the pid file name */ oc = lookup_option(&server_universe, options, SV_DHCPV6_LEASE_FILE_NAME); if (oc && evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL, &global_scope, oc, MDL)) { s = dmalloc(db.len + 1, MDL); if (!s) log_fatal("no memory for lease db filename."); memcpy(s, db.data, db.len); s[db.len] = 0; data_string_forget(&db, MDL); path_dhcpd_db = s; } oc = lookup_option(&server_universe, options, SV_DHCPV6_PID_FILE_NAME); if (oc && evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL, &global_scope, oc, MDL)) { s = dmalloc(db.len + 1, MDL); if (!s) log_fatal("no memory for pid filename."); memcpy(s, db.data, db.len); s[db.len] = 0; data_string_forget(&db, MDL); path_dhcpd_pid = s; } oc = lookup_option(&server_universe, options, SV_LOCAL_ADDRESS6); if (oc && evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL, &global_scope, oc, MDL)) { if (db.len == 16) { memcpy(&local_address6, db.data, 16); } else log_fatal("invalid local address " "data length"); data_string_forget(&db, MDL); } oc = lookup_option(&server_universe, options, SV_BIND_LOCAL_ADDRESS6); if (oc && evaluate_boolean_option_cache(NULL, NULL, NULL, NULL, options, NULL, &global_scope, oc, MDL)) { bind_local_address6 = 1; } } #endif /* DHCPv6 */ omapi_port = -1; oc = lookup_option(&server_universe, options, SV_OMAPI_PORT); if (oc && evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL, &global_scope, oc, MDL)) { if (db.len == 2) { omapi_port = getUShort(db.data); } else log_fatal("invalid omapi port data length"); data_string_forget(&db, MDL); } oc = lookup_option(&server_universe, options, SV_OMAPI_KEY); if (oc && evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL, &global_scope, oc, MDL)) { s = dmalloc(db.len + 1, MDL); if (!s) log_fatal("no memory for OMAPI key filename."); memcpy(s, db.data, db.len); s[db.len] = 0; data_string_forget(&db, MDL); result = omapi_auth_key_lookup_name(&omapi_key, s); dfree(s, MDL); if (result != ISC_R_SUCCESS) log_fatal("OMAPI key %s: %s", s, isc_result_totext (result)); } oc = lookup_option(&server_universe, options, SV_LOCAL_PORT); if (oc && evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL, &global_scope, oc, MDL)) { if (db.len == 2) { local_port = htons(getUShort (db.data)); } else log_fatal("invalid local port data length"); data_string_forget(&db, MDL); } oc = lookup_option(&server_universe, options, SV_REMOTE_PORT); if (oc && evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL, &global_scope, oc, MDL)) { if (db.len == 2) { remote_port = htons(getUShort (db.data)); } else log_fatal("invalid remote port data length"); data_string_forget(&db, MDL); } oc = lookup_option(&server_universe, options, SV_LIMITED_BROADCAST_ADDRESS); if (oc && evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL, &global_scope, oc, MDL)) { if (db.len == 4) { memcpy(&limited_broadcast, db.data, 4); } else log_fatal("invalid broadcast address data length"); data_string_forget(&db, MDL); } oc = lookup_option(&server_universe, options, SV_LOCAL_ADDRESS); if (oc && evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL, &global_scope, oc, MDL)) { if (db.len == 4) { memcpy(&local_address, db.data, 4); } else log_fatal("invalid local address data length"); data_string_forget(&db, MDL); } oc = lookup_option(&server_universe, options, SV_DDNS_UPDATE_STYLE); if (oc) { if (evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL, &global_scope, oc, MDL)) { if (db.len == 1) { ddns_update_style = db.data[0]; } else log_fatal("invalid dns update type"); data_string_forget(&db, MDL); } } else { ddns_update_style = DDNS_UPDATE_STYLE_NONE; } #if defined (NSUPDATE) /* We no longer support ad_hoc, tell the user */ if (ddns_update_style == DDNS_UPDATE_STYLE_AD_HOC) { log_fatal("ddns-update-style ad_hoc no longer supported"); } oc = lookup_option(&server_universe, options, SV_DDNS_LOCAL_ADDRESS4); if (oc) { if (evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL, &global_scope, oc, MDL)) { if (db.len == 4) { memcpy(&local4, db.data, 4); local4_ptr = &local4; } data_string_forget(&db, MDL); } } oc = lookup_option(&server_universe, options, SV_DDNS_LOCAL_ADDRESS6); if (oc) { if (evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL, &global_scope, oc, MDL)) { if (db.len == 16) { memcpy(&local6, db.data, 16); local6_ptr = &local6; } data_string_forget(&db, MDL); } } /* Don't init DNS client if update style is none. This avoids * listening ports that aren't needed. We don't use ddns-udpates * as that has multiple levels of scope. */ if (ddns_update_style != DDNS_UPDATE_STYLE_NONE) { if (dhcp_context_create(DHCP_CONTEXT_POST_DB, local4_ptr, local6_ptr) != ISC_R_SUCCESS) { log_fatal("Unable to complete ddns initialization"); } } /* Set the conflict detection flag mask based on globally * defined DDNS configuration params. This mask should be * to init ddns_cb::flags before for every DDNS transaction. */ ddns_conflict_mask = get_conflict_mask(options); #else /* If we don't have support for updates compiled in tell the user */ if (ddns_update_style != DDNS_UPDATE_STYLE_NONE) { log_fatal("Support for ddns-update-style not compiled in"); } #endif if (!quiet) { log_info ("Config file: %s", path_dhcpd_conf); log_info ("Database file: %s", path_dhcpd_db); log_info ("PID file: %s", path_dhcpd_pid); } oc = lookup_option(&server_universe, options, SV_LOG_FACILITY); if (oc) { if (evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL, &global_scope, oc, MDL)) { if (db.len == 1) { closelog (); openlog(isc_file_basename(progname), DHCP_LOG_OPTIONS, db.data[0]); /* Log the startup banner into the new log file. */ /* Don't log to stderr twice. */ tmp = log_perror; log_perror = 0; log_info("%s %s", message, PACKAGE_VERSION); log_info(copyright); log_info(arr); log_info(url); log_perror = tmp; } else log_fatal("invalid log facility"); data_string_forget(&db, MDL); } } #if defined(DELAYED_ACK) oc = lookup_option(&server_universe, options, SV_DELAYED_ACK); if (oc && evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL, &global_scope, oc, MDL)) { if (db.len == 2) { max_outstanding_acks = htons(getUShort(db.data)); } else { log_fatal("invalid max delayed ACK count "); } data_string_forget(&db, MDL); } #if defined(DHCP4o6) /* Delayed acks and DHCPv4-over-DHCPv6 are incompatible */ if (dhcpv4_over_dhcpv6) { if (max_outstanding_acks > 0) { log_debug("DHCP4o6 enabled, " "setting delayed-ack to zero (incompatible)"); } max_outstanding_acks = 0; } #endif oc = lookup_option(&server_universe, options, SV_MAX_ACK_DELAY); if (oc && evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL, &global_scope, oc, MDL)) { u_int32_t timeval; if (db.len != 4) log_fatal("invalid max ack delay configuration"); timeval = getULong(db.data); max_ack_delay_secs = timeval / 1000000; max_ack_delay_usecs = timeval % 1000000; data_string_forget(&db, MDL); } #endif oc = lookup_option(&server_universe, options, SV_DONT_USE_FSYNC); if ((oc != NULL) && evaluate_boolean_option_cache(NULL, NULL, NULL, NULL, options, NULL, &global_scope, oc, MDL)) { dont_use_fsync = 1; log_error("Not using fsync() to flush lease writes"); } oc = lookup_option(&server_universe, options, SV_SERVER_ID_CHECK); if ((oc != NULL) && evaluate_boolean_option_cache(NULL, NULL, NULL, NULL, options, NULL, &global_scope, oc, MDL)) { log_info("Setting server-id-check true"); server_id_check = 1; } #ifdef DHCPv6 oc = lookup_option(&server_universe, options, SV_PREFIX_LEN_MODE); if ((oc != NULL) && evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL, &global_scope, oc, MDL)) { if (db.len == 1) { prefix_length_mode = db.data[0]; } else { log_fatal("invalid prefix-len-mode"); } data_string_forget(&db, MDL); } #endif // Set global abandon-lease-time option. oc = lookup_option (&server_universe, options, SV_ABANDON_LEASE_TIME); if ((oc != NULL) && evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL, &global_scope, oc, MDL)) { if (db.len == sizeof (u_int32_t)) { abandon_lease_time = getULong (db.data); } else { log_fatal("invalid abandon-lease-time"); } data_string_forget (&db, MDL); } #if defined (FAILOVER_PROTOCOL) oc = lookup_option(&server_universe, options, SV_CHECK_SECS_BYTE_ORDER); if ((oc != NULL) && evaluate_boolean_option_cache(NULL, NULL, NULL, NULL, options, NULL, &global_scope, oc, MDL)) { check_secs_byte_order = 1; } #endif #ifdef EUI_64 oc = lookup_option(&server_universe, options, SV_PERSIST_EUI_64_LEASES); if (oc != NULL) { persist_eui64 = evaluate_boolean_option_cache(NULL, NULL, NULL, NULL, options, NULL, &global_scope, oc, MDL); } if (!persist_eui64) { log_info("EUI64 leases will not be written to lease file"); } #endif #ifdef DHCPv6 oc = lookup_option(&server_universe, options, SV_RELEASE_ON_ROAM); if (oc != NULL) { do_release_on_roam = evaluate_boolean_option_cache(NULL, NULL, NULL, NULL, options, NULL, &global_scope, oc, MDL); } #endif #if defined (BINARY_LEASES) if (local_family == AF_INET) { log_info("Source compiled to use binary-leases"); } #endif /* Don't need the options anymore. */ option_state_dereference(&options, MDL); } void postdb_startup (void) { /* Initialize the omapi listener state. */ if (omapi_port != -1) { omapi_listener_start (0); } #if defined (FAILOVER_PROTOCOL) /* Initialize the failover listener state. */ dhcp_failover_startup (); #endif /* * Begin our lease timeout background task. */ schedule_all_ipv6_lease_timeouts(); } void lease_pinged (from, packet, length) struct iaddr from; u_int8_t *packet; int length; { struct lease *lp; /* Don't try to look up a pinged lease if we aren't trying to ping one - otherwise somebody could easily make us churn by just forging repeated ICMP EchoReply packets for us to look up. */ if (!outstanding_pings) return; lp = (struct lease *)0; if (!find_lease_by_ip_addr (&lp, from, MDL)) { log_debug ("unexpected ICMP Echo Reply from %s", piaddr (from)); return; } if (!lp -> state) { #if defined (FAILOVER_PROTOCOL) if (!lp -> pool || !lp -> pool -> failover_peer) #endif log_debug ("ICMP Echo Reply for %s late or spurious.", piaddr (from)); goto out; } if (lp -> ends > cur_time) { log_debug ("ICMP Echo reply while lease %s valid.", piaddr (from)); } /* At this point it looks like we pinged a lease and got a response, which shouldn't have happened. */ data_string_forget (&lp -> state -> parameter_request_list, MDL); free_lease_state (lp -> state, MDL); lp -> state = (struct lease_state *)0; abandon_lease (lp, "pinged before offer"); cancel_timeout (lease_ping_timeout, lp); --outstanding_pings; out: lease_dereference (&lp, MDL); } void lease_ping_timeout (vlp) void *vlp; { struct lease *lp = vlp; #if defined (DEBUG_MEMORY_LEAKAGE) unsigned long previous_outstanding = dmalloc_outstanding; #endif --outstanding_pings; dhcp_reply (lp); #if defined (DEBUG_MEMORY_LEAKAGE) log_info ("generation %ld: %ld new, %ld outstanding, %ld long-term", dmalloc_generation, dmalloc_outstanding - previous_outstanding, dmalloc_outstanding, dmalloc_longterm); #endif #if defined (DEBUG_MEMORY_LEAKAGE) dmalloc_dump_outstanding (); #endif } int dhcpd_interface_setup_hook (struct interface_info *ip, struct iaddr *ia) { struct subnet *subnet; struct shared_network *share; isc_result_t status; /* Special case for fallback network - not sure why this is necessary. */ if (!ia) { const char *fnn = "fallback-net"; status = shared_network_allocate (&ip -> shared_network, MDL); if (status != ISC_R_SUCCESS) log_fatal ("No memory for shared subnet: %s", isc_result_totext (status)); ip -> shared_network -> name = dmalloc (strlen (fnn) + 1, MDL); if (!ip -> shared_network -> name) log_fatal("no memory for shared network"); strcpy (ip -> shared_network -> name, fnn); return 1; } /* If there's a registered subnet for this address, connect it together... */ subnet = (struct subnet *)0; if (find_subnet (&subnet, *ia, MDL)) { /* If this interface has multiple aliases on the same subnet, ignore all but the first we encounter. */ if (!subnet -> interface) { interface_reference (&subnet -> interface, ip, MDL); subnet -> interface_address = *ia; } else if (subnet -> interface != ip) { log_error ("Multiple interfaces match the %s: %s %s", "same subnet", subnet -> interface -> name, ip -> name); } share = subnet -> shared_network; if (ip -> shared_network && ip -> shared_network != share) { log_fatal ("Interface %s matches multiple shared %s", ip -> name, "networks"); } else { if (!ip -> shared_network) shared_network_reference (&ip -> shared_network, share, MDL); } if (!share -> interface) { interface_reference (&share -> interface, ip, MDL); } else if (share -> interface != ip) { log_error ("Multiple interfaces match the %s: %s %s", "same shared network", share -> interface -> name, ip -> name); } subnet_dereference (&subnet, MDL); } return 1; } static TIME shutdown_time; static int omapi_connection_count; enum dhcp_shutdown_state shutdown_state; isc_result_t dhcp_io_shutdown (omapi_object_t *obj, void *foo) { /* Shut down all listeners. */ if (shutdown_state == shutdown_listeners && obj -> type == omapi_type_listener && obj -> inner && obj -> inner -> type == omapi_type_protocol_listener) { omapi_listener_destroy (obj, MDL); return ISC_R_SUCCESS; } /* Shut down all existing omapi connections. */ if (obj -> type == omapi_type_connection && obj -> inner && obj -> inner -> type == omapi_type_protocol) { if (shutdown_state == shutdown_drop_omapi_connections) { omapi_disconnect (obj, 1); } omapi_connection_count++; if (shutdown_state == shutdown_omapi_connections) { omapi_disconnect (obj, 0); return ISC_R_SUCCESS; } } /* Shutdown all DHCP interfaces. */ if (obj -> type == dhcp_type_interface && shutdown_state == shutdown_dhcp) { dhcp_interface_remove (obj, (omapi_object_t *)0); return ISC_R_SUCCESS; } return ISC_R_SUCCESS; } static isc_result_t dhcp_io_shutdown_countdown (void *vlp) { #if defined (FAILOVER_PROTOCOL) dhcp_failover_state_t *state; int failover_connection_count = 0; #endif struct timeval tv; oncemore: if (shutdown_state == shutdown_listeners || shutdown_state == shutdown_omapi_connections || shutdown_state == shutdown_drop_omapi_connections || shutdown_state == shutdown_dhcp) { omapi_connection_count = 0; omapi_io_state_foreach (dhcp_io_shutdown, 0); } if ((shutdown_state == shutdown_listeners || shutdown_state == shutdown_omapi_connections || shutdown_state == shutdown_drop_omapi_connections) && omapi_connection_count == 0) { shutdown_state = shutdown_dhcp; shutdown_time = cur_time; goto oncemore; } else if (shutdown_state == shutdown_listeners && cur_time - shutdown_time > 4) { shutdown_state = shutdown_omapi_connections; shutdown_time = cur_time; } else if (shutdown_state == shutdown_omapi_connections && cur_time - shutdown_time > 4) { shutdown_state = shutdown_drop_omapi_connections; shutdown_time = cur_time; } else if (shutdown_state == shutdown_drop_omapi_connections && cur_time - shutdown_time > 4) { shutdown_state = shutdown_dhcp; shutdown_time = cur_time; goto oncemore; } else if (shutdown_state == shutdown_dhcp && cur_time - shutdown_time > 4) { shutdown_state = shutdown_done; shutdown_time = cur_time; } #if defined (FAILOVER_PROTOCOL) /* Set all failover peers into the shutdown state. */ if (shutdown_state == shutdown_dhcp) { for (state = failover_states; state; state = state -> next) { if (state -> me.state == normal) { dhcp_failover_set_state (state, shut_down); failover_connection_count++; } if (state -> me.state == shut_down && state -> partner.state != partner_down) failover_connection_count++; } } if (shutdown_state == shutdown_done) { for (state = failover_states; state; state = state -> next) { if (state -> me.state == shut_down) { if (state -> link_to_peer) dhcp_failover_link_dereference (&state -> link_to_peer, MDL); dhcp_failover_set_state (state, recover); } } #if defined (DEBUG_MEMORY_LEAKAGE) && \ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) free_everything (); omapi_print_dmalloc_usage_by_caller (); #endif if (no_pid_file == ISC_FALSE) (void) unlink(path_dhcpd_pid); exit (0); } #else if (shutdown_state == shutdown_done) { #if defined (DEBUG_MEMORY_LEAKAGE) && \ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) free_everything (); omapi_print_dmalloc_usage_by_caller (); #endif if (no_pid_file == ISC_FALSE) (void) unlink(path_dhcpd_pid); exit (0); } #endif if (shutdown_state == shutdown_dhcp && #if defined(FAILOVER_PROTOCOL) !failover_connection_count && #endif ISC_TRUE) { shutdown_state = shutdown_done; shutdown_time = cur_time; goto oncemore; } tv.tv_sec = cur_tv.tv_sec + 1; tv.tv_usec = cur_tv.tv_usec; add_timeout (&tv, (void (*)(void *))dhcp_io_shutdown_countdown, 0, 0, 0); return ISC_R_SUCCESS; } isc_result_t dhcp_set_control_state (control_object_state_t oldstate, control_object_state_t newstate) { struct timeval tv; if (newstate != server_shutdown) return DHCP_R_INVALIDARG; /* Re-entry. */ if (shutdown_signal == SIGUSR1) return ISC_R_SUCCESS; shutdown_time = cur_time; shutdown_state = shutdown_listeners; /* Called by user. */ if (shutdown_signal == 0) { shutdown_signal = SIGUSR1; dhcp_io_shutdown_countdown (0); return ISC_R_SUCCESS; } /* Called on signal. */ log_info("Received signal %d, initiating shutdown.", shutdown_signal); shutdown_signal = SIGUSR1; /* * Prompt the shutdown event onto the timer queue * and return to the dispatch loop. */ tv.tv_sec = cur_tv.tv_sec; tv.tv_usec = cur_tv.tv_usec + 1; add_timeout(&tv, (void (*)(void *))dhcp_io_shutdown_countdown, 0, 0, 0); return ISC_R_SUCCESS; } dhcp-4.4.1/server/dhcpd.conf.5000644 000765 000024 00000426164 13243301226 016332 0ustar00tmarkstaff000000 000000 .\" dhcpd.conf.5 .\" .\" Copyright (c) 2004-2018 by Internet Systems Consortium, Inc. ("ISC") .\" Copyright (c) 1996-2003 by Internet Software Consortium .\" .\" 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/. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT .\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" Internet Systems Consortium, Inc. .\" 950 Charter Street .\" Redwood City, CA 94063 .\" .\" https://www.isc.org/ .\" .\" This software has been written for Internet Systems Consortium .\" by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. .\" .\" Support and other services are available for ISC products - see .\" https://www.isc.org for more information or to learn more about ISC. .\" .\" $Id: dhcpd.conf.5,v 1.114 2012/04/02 22:47:35 sar Exp $ .\" .TH dhcpd.conf 5 .SH NAME dhcpd.conf - dhcpd configuration file .SH DESCRIPTION The dhcpd.conf file contains configuration information for .IR dhcpd, the Internet Systems Consortium DHCP Server. .PP The dhcpd.conf file is a free-form ASCII text file. It is parsed by the recursive-descent parser built into dhcpd. The file may contain extra tabs and newlines for formatting purposes. Keywords in the file are case-insensitive. Comments may be placed anywhere within the file (except within quotes). Comments begin with the # character and end at the end of the line. .PP The file essentially consists of a list of statements. Statements fall into two broad categories - parameters and declarations. .PP Parameter statements either say how to do something (e.g., how long a lease to offer), whether to do something (e.g., should dhcpd provide addresses to unknown clients), or what parameters to provide to the client (e.g., use gateway 220.177.244.7). .PP Declarations are used to describe the topology of the network, to describe clients on the network, to provide addresses that can be assigned to clients, or to apply a group of parameters to a group of declarations. In any group of parameters and declarations, all parameters must be specified before any declarations which depend on those parameters may be specified. .PP Declarations about network topology include the \fIshared-network\fR and the \fIsubnet\fR declarations. If clients on a subnet are to be assigned addresses dynamically, a \fIrange\fR declaration must appear within the \fIsubnet\fR declaration. For clients with statically assigned addresses, or for installations where only known clients will be served, each such client must have a \fIhost\fR declaration. If parameters are to be applied to a group of declarations which are not related strictly on a per-subnet basis, the \fIgroup\fR declaration can be used. .PP For every subnet which will be served, and for every subnet to which the dhcp server is connected, there must be one \fIsubnet\fR declaration, which tells dhcpd how to recognize that an address is on that subnet. A \fIsubnet\fR declaration is required for each subnet even if no addresses will be dynamically allocated on that subnet. .PP Some installations have physical networks on which more than one IP subnet operates. For example, if there is a site-wide requirement that 8-bit subnet masks be used, but a department with a single physical ethernet network expands to the point where it has more than 254 nodes, it may be necessary to run two 8-bit subnets on the same ethernet until such time as a new physical network can be added. In this case, the \fIsubnet\fR declarations for these two networks must be enclosed in a \fIshared-network\fR declaration. .PP Note that even when the \fIshared-network\fR declaration is absent, an empty one is created by the server to contain the \fIsubnet\fR (and any scoped parameters included in the \fIsubnet\fR). For practical purposes, this means that "stateless" DHCP clients, which are not tied to addresses (and therefore subnets) will receive the same configuration as stateful ones. .PP Some sites may have departments which have clients on more than one subnet, but it may be desirable to offer those clients a uniform set of parameters which are different than what would be offered to clients from other departments on the same subnet. For clients which will be declared explicitly with \fIhost\fR declarations, these declarations can be enclosed in a \fIgroup\fR declaration along with the parameters which are common to that department. For clients whose addresses will be dynamically assigned, class declarations and conditional declarations may be used to group parameter assignments based on information the client sends. .PP When a client is to be booted, its boot parameters are determined by consulting that client's \fIhost\fR declaration (if any), and then consulting any \fIclass\fR declarations matching the client, followed by the \fIpool\fR, \fIsubnet\fR and \fIshared-network\fR declarations for the IP address assigned to the client. Each of these declarations itself appears within a lexical scope, and all declarations at less specific lexical scopes are also consulted for client option declarations. Scopes are never considered twice, and if parameters are declared in more than one scope, the parameter declared in the most specific scope is the one that is used. .PP When dhcpd tries to find a \fIhost\fR declaration for a client, it first looks for a \fIhost\fR declaration which has a \fIfixed-address\fR declaration that lists an IP address that is valid for the subnet or shared network on which the client is booting. If it doesn't find any such entry, it tries to find an entry which has no \fIfixed-address\fR declaration. .SH EXAMPLES .PP A typical dhcpd.conf file will look something like this: .nf .I global parameters... subnet 204.254.239.0 netmask 255.255.255.224 { \fIsubnet-specific parameters...\fR range 204.254.239.10 204.254.239.30; } subnet 204.254.239.32 netmask 255.255.255.224 { \fIsubnet-specific parameters...\fR range 204.254.239.42 204.254.239.62; } subnet 204.254.239.64 netmask 255.255.255.224 { \fIsubnet-specific parameters...\fR range 204.254.239.74 204.254.239.94; } group { \fIgroup-specific parameters...\fR host zappo.test.isc.org { \fIhost-specific parameters...\fR } host beppo.test.isc.org { \fIhost-specific parameters...\fR } host harpo.test.isc.org { \fIhost-specific parameters...\fR } } .ce 1 Figure 1 .fi .PP Notice that at the beginning of the file, there's a place for global parameters. These might be things like the organization's domain name, the addresses of the name servers (if they are common to the entire organization), and so on. So, for example: .nf option domain-name "isc.org"; option domain-name-servers ns1.isc.org, ns2.isc.org; .ce 1 Figure 2 .fi .PP As you can see in Figure 2, you can specify host addresses in parameters using their domain names rather than their numeric IP addresses. If a given hostname resolves to more than one IP address (for example, if that host has two ethernet interfaces), then where possible, both addresses are supplied to the client. .PP The most obvious reason for having subnet-specific parameters as shown in Figure 1 is that each subnet, of necessity, has its own router. So for the first subnet, for example, there should be something like: .nf option routers 204.254.239.1; .fi .PP Note that the address here is specified numerically. This is not required - if you have a different domain name for each interface on your router, it's perfectly legitimate to use the domain name for that interface instead of the numeric address. However, in many cases there may be only one domain name for all of a router's IP addresses, and it would not be appropriate to use that name here. .PP In Figure 1 there is also a \fIgroup\fR statement, which provides common parameters for a set of three hosts - zappo, beppo and harpo. As you can see, these hosts are all in the test.isc.org domain, so it might make sense for a group-specific parameter to override the domain name supplied to these hosts: .nf option domain-name "test.isc.org"; .fi .PP Also, given the domain they're in, these are probably test machines. If we wanted to test the DHCP leasing mechanism, we might set the lease timeout somewhat shorter than the default: .nf max-lease-time 120; default-lease-time 120; .fi .PP You may have noticed that while some parameters start with the \fIoption\fR keyword, some do not. Parameters starting with the \fIoption\fR keyword correspond to actual DHCP options, while parameters that do not start with the option keyword either control the behavior of the DHCP server (e.g., how long a lease dhcpd will give out), or specify client parameters that are not optional in the DHCP protocol (for example, server-name and filename). .PP In Figure 1, each host had \fIhost-specific parameters\fR. These could include such things as the \fIhostname\fR option, the name of a file to upload (the \fIfilename\fR parameter) and the address of the server from which to upload the file (the \fInext-server\fR parameter). In general, any parameter can appear anywhere that parameters are allowed, and will be applied according to the scope in which the parameter appears. .PP Imagine that you have a site with a lot of NCD X-Terminals. These terminals come in a variety of models, and you want to specify the boot files for each model. One way to do this would be to have host declarations for each server and group them by model: .nf group { filename "Xncd19r"; next-server ncd-booter; host ncd1 { hardware ethernet 0:c0:c3:49:2b:57; } host ncd4 { hardware ethernet 0:c0:c3:80:fc:32; } host ncd8 { hardware ethernet 0:c0:c3:22:46:81; } } group { filename "Xncd19c"; next-server ncd-booter; host ncd2 { hardware ethernet 0:c0:c3:88:2d:81; } host ncd3 { hardware ethernet 0:c0:c3:00:14:11; } } group { filename "XncdHMX"; next-server ncd-booter; host ncd1 { hardware ethernet 0:c0:c3:11:90:23; } host ncd4 { hardware ethernet 0:c0:c3:91:a7:8; } host ncd8 { hardware ethernet 0:c0:c3:cc:a:8f; } } .fi .SH ADDRESS POOLS .PP The \fBpool\fR and \fBpool6\fR declarations can be used to specify a pool of addresses that will be treated differently than another pool of addresses, even on the same network segment or subnet. For example, you may want to provide a large set of addresses that can be assigned to DHCP clients that are registered to your DHCP server, while providing a smaller set of addresses, possibly with short lease times, that are available for unknown clients. If you have a firewall, you may be able to arrange for addresses from one pool to be allowed access to the Internet, while addresses in another pool are not, thus encouraging users to register their DHCP clients. To do this, you would set up a pair of pool declarations: .PP .nf subnet 10.0.0.0 netmask 255.255.255.0 { option routers 10.0.0.254; # Unknown clients get this pool. pool { option domain-name-servers bogus.example.com; max-lease-time 300; range 10.0.0.200 10.0.0.253; allow unknown-clients; } # Known clients get this pool. pool { option domain-name-servers ns1.example.com, ns2.example.com; max-lease-time 28800; range 10.0.0.5 10.0.0.199; deny unknown-clients; } } .fi .PP It is also possible to set up entirely different subnets for known and unknown clients - address pools exist at the level of shared networks, so address ranges within pool declarations can be on different subnets. .PP As you can see in the preceding example, pools can have permit lists that control which clients are allowed access to the pool and which aren't. Each entry in a pool's permit list is introduced with the .I allow or \fIdeny\fR keyword. If a pool has a permit list, then only those clients that match specific entries on the permit list will be eligible to be assigned addresses from the pool. If a pool has a deny list, then only those clients that do not match any entries on the deny list will be eligible. If both permit and deny lists exist for a pool, then only clients that match the permit list and do not match the deny list will be allowed access. .PP The \fBpool6\fR declaration is similar to the \fBpool\fR declaration. Currently it is only allowed within a \fBsubnet6\fR declaration, and may not be included directly in a shared network declaration. In addition to the \fBrange6\fR statement it allows the \fBprefix6\fR statement to be included. You may include \fBrange6\fR statements for both NA and TA and \fBprefixy6\fR statements in a single \fBpool6\fR statement. .SH DYNAMIC ADDRESS ALLOCATION Address allocation is actually only done when a client is in the INIT state and has sent a DHCPDISCOVER message. If the client thinks it has a valid lease and sends a DHCPREQUEST to initiate or renew that lease, the server has only three choices - it can ignore the DHCPREQUEST, send a DHCPNAK to tell the client it should stop using the address, or send a DHCPACK, telling the client to go ahead and use the address for a while. .PP If the server finds the address the client is requesting, and that address is available to the client, the server will send a DHCPACK. If the address is no longer available, or the client isn't permitted to have it, the server will send a DHCPNAK. If the server knows nothing about the address, it will remain silent, unless the address is incorrect for the network segment to which the client has been attached and the server is authoritative for that network segment, in which case the server will send a DHCPNAK even though it doesn't know about the address. .PP There may be a host declaration matching the client's identification. If that host declaration contains a fixed-address declaration that lists an IP address that is valid for the network segment to which the client is connected, the DHCP server will never do dynamic address allocation. In this case, the client is \fIrequired\fR to take the address specified in the host declaration. If the client sends a DHCPREQUEST for some other address, the server will respond with a DHCPNAK. .PP When the DHCP server allocates a new address for a client (remember, this only happens if the client has sent a DHCPDISCOVER), it first looks to see if the client already has a valid lease on an IP address, or if there is an old IP address the client had before that hasn't yet been reassigned. In that case, the server will take that address and check it to see if the client is still permitted to use it. If the client is no longer permitted to use it, the lease is freed if the server thought it was still in use - the fact that the client has sent a DHCPDISCOVER proves to the server that the client is no longer using the lease. .PP If no existing lease is found, or if the client is forbidden to receive the existing lease, then the server will look in the list of address pools for the network segment to which the client is attached for a lease that is not in use and that the client is permitted to have. It looks through each pool declaration in sequence (all .I range declarations that appear outside of pool declarations are grouped into a single pool with no permit list). If the permit list for the pool allows the client to be allocated an address from that pool, the pool is examined to see if there is an address available. If so, then the client is tentatively assigned that address. Otherwise, the next pool is tested. If no addresses are found that can be assigned to the client, no response is sent to the client. .PP If an address is found that the client is permitted to have, and that has never been assigned to any client before, the address is immediately allocated to the client. If the address is available for allocation but has been previously assigned to a different client, the server will keep looking in hopes of finding an address that has never before been assigned to a client. .PP The DHCP server generates the list of available IP addresses from a hash table. This means that the addresses are not sorted in any particular order, and so it is not possible to predict the order in which the DHCP server will allocate IP addresses. Users of previous versions of the ISC DHCP server may have become accustomed to the DHCP server allocating IP addresses in ascending order, but this is no longer possible, and there is no way to configure this behavior with version 3 of the ISC DHCP server. .SH IP ADDRESS CONFLICT PREVENTION The DHCP server checks IP addresses to see if they are in use before allocating them to clients. It does this by sending an ICMP Echo request message to the IP address being allocated. If no ICMP Echo reply is received within a second, the address is assumed to be free. This is only done for leases that have been specified in range statements, and only when the lease is thought by the DHCP server to be free - i.e., the DHCP server or its failover peer has not listed the lease as in use. .PP If a response is received to an ICMP Echo request, the DHCP server assumes that there is a configuration error - the IP address is in use by some host on the network that is not a DHCP client. It marks the address as abandoned, and will not assign it to clients. The lease will remain abandoned for a minimum of abandon-lease-time seconds. .PP If a DHCP client tries to get an IP address, but none are available, but there are abandoned IP addresses, then the DHCP server will attempt to reclaim an abandoned IP address. It marks one IP address as free, and then does the same ICMP Echo request check described previously. If there is no answer to the ICMP Echo request, the address is assigned to the client. .PP The DHCP server does not cycle through abandoned IP addresses if the first IP address it tries to reclaim is free. Rather, when the next DHCPDISCOVER comes in from the client, it will attempt a new allocation using the same method described here, and will typically try a new IP address. .SH DHCP FAILOVER This version of the ISC DHCP server supports the DHCP failover protocol as documented in draft-ietf-dhc-failover-12.txt. This is not a final protocol document, and we have not done interoperability testing with other vendors' implementations of this protocol, so you must not assume that this implementation conforms to the standard. If you wish to use the failover protocol, make sure that both failover peers are running the same version of the ISC DHCP server. .PP The failover protocol allows two DHCP servers (and no more than two) to share a common address pool. Each server will have about half of the available IP addresses in the pool at any given time for allocation. If one server fails, the other server will continue to renew leases out of the pool, and will allocate new addresses out of the roughly half of available addresses that it had when communications with the other server were lost. .PP It is possible during a prolonged failure to tell the remaining server that the other server is down, in which case the remaining server will (over time) reclaim all the addresses the other server had available for allocation, and begin to reuse them. This is called putting the server into the PARTNER-DOWN state. .PP You can put the server into the PARTNER-DOWN state either by using the .B omshell (1) command or by stopping the server, editing the last failover state declaration in the lease file, and restarting the server. If you use this last method, change the "my state" line to: .PP .nf .B failover peer "\fIname\fB" state { .B my state partner-down;. .B peer state \fIstate\fB at \fIdate\fB; .B } .fi .PP It is only required to change "my state" as shown above. .PP When the other server comes back online, it should automatically detect that it has been offline and request a complete update from the server that was running in the PARTNER-DOWN state, and then both servers will resume processing together. .PP It is possible to get into a dangerous situation: if you put one server into the PARTNER-DOWN state, and then *that* server goes down, and the other server comes back up, the other server will not know that the first server was in the PARTNER-DOWN state, and may issue addresses previously issued by the other server to different clients, resulting in IP address conflicts. Before putting a server into PARTNER-DOWN state, therefore, make .I sure that the other server will not restart automatically. .PP The failover protocol defines a primary server role and a secondary server role. There are some differences in how primaries and secondaries act, but most of the differences simply have to do with providing a way for each peer to behave in the opposite way from the other. So one server must be configured as primary, and the other must be configured as secondary, and it doesn't matter too much which one is which. .SH FAILOVER STARTUP When a server starts that has not previously communicated with its failover peer, it must establish communications with its failover peer and synchronize with it before it can serve clients. This can happen either because you have just configured your DHCP servers to perform failover for the first time, or because one of your failover servers has failed catastrophically and lost its database. .PP The initial recovery process is designed to ensure that when one failover peer loses its database and then resynchronizes, any leases that the failed server gave out before it failed will be honored. When the failed server starts up, it notices that it has no saved failover state, and attempts to contact its peer. .PP When it has established contact, it asks the peer for a complete copy its peer's lease database. The peer then sends its complete database, and sends a message indicating that it is done. The failed server then waits until MCLT has passed, and once MCLT has passed both servers make the transition back into normal operation. This waiting period ensures that any leases the failed server may have given out while out of contact with its partner will have expired. .PP While the failed server is recovering, its partner remains in the partner-down state, which means that it is serving all clients. The failed server provides no service at all to DHCP clients until it has made the transition into normal operation. .PP In the case where both servers detect that they have never before communicated with their partner, they both come up in this recovery state and follow the procedure we have just described. In this case, no service will be provided to DHCP clients until MCLT has expired. .SH CONFIGURING FAILOVER In order to configure failover, you need to write a peer declaration that configures the failover protocol, and you need to write peer references in each pool declaration for which you want to do failover. You do not have to do failover for all pools on a given network segment. You must not tell one server it's doing failover on a particular address pool and tell the other it is not. You must not have any common address pools on which you are not doing failover. A pool declaration that utilizes failover would look like this: .PP .nf pool { failover peer "foo"; \fIpool specific parameters\fR }; .fi .PP The server currently does very little sanity checking, so if you configure it wrong, it will just fail in odd ways. I would recommend therefore that you either do failover or don't do failover, but don't do any mixed pools. Also, use the same master configuration file for both servers, and have a separate file that contains the peer declaration and includes the master file. This will help you to avoid configuration mismatches. As our implementation evolves, this will become less of a problem. A basic sample dhcpd.conf file for a primary server might look like this: .PP .nf failover peer "foo" { primary; address anthrax.rc.example.com; port 519; peer address trantor.rc.example.com; peer port 520; max-response-delay 60; max-unacked-updates 10; mclt 3600; split 128; load balance max seconds 3; } include "/etc/dhcpd.master"; .fi .PP The statements in the peer declaration are as follows: .PP The .I primary and .I secondary statements .RS 0.25i .PP [ \fBprimary\fR | \fBsecondary\fR ]\fB;\fR .PP This determines whether the server is primary or secondary, as described earlier under DHCP FAILOVER. .RE .PP The .I address statement .RS 0.25i .PP .B address \fIaddress\fR\fB;\fR .PP The \fBaddress\fR statement declares the IP address or DNS name on which the server should listen for connections from its failover peer, and also the value to use for the DHCP Failover Protocol server identifier. Because this value is used as an identifier, it may not be omitted. .RE .PP The .I peer address statement .RS 0.25i .PP .B peer address \fIaddress\fR\fB;\fR .PP The \fBpeer address\fR statement declares the IP address or DNS name to which the server should connect to reach its failover peer for failover messages. .RE .PP The .I port statement .RS 0.25i .PP .B port \fIport-number\fR\fB;\fR .PP The \fBport\fR statement declares the TCP port on which the server should listen for connections from its failover peer. This statement may be omitted, in which case the IANA assigned port number 647 will be used by default. .RE .PP The .I peer port statement .RS 0.25i .PP .B peer port \fIport-number\fR\fB;\fR .PP The \fBpeer port\fR statement declares the TCP port to which the server should connect to reach its failover peer for failover messages. This statement may be omitted, in which case the IANA assigned port number 647 will be used by default. .RE .PP The .I max-response-delay statement .RS 0.25i .PP .B max-response-delay \fIseconds\fR\fB;\fR .PP The \fBmax-response-delay\fR statement tells the DHCP server how many seconds may pass without receiving a message from its failover peer before it assumes that connection has failed. This number should be small enough that a transient network failure that breaks the connection will not result in the servers being out of communication for a long time, but large enough that the server isn't constantly making and breaking connections. This parameter must be specified. .RE .PP The .I max-unacked-updates statement .RS 0.25i .PP .B max-unacked-updates \fIcount\fR\fB;\fR .PP The \fBmax-unacked-updates\fR statement tells the remote DHCP server how many BNDUPD messages it can send before it receives a BNDACK from the local system. We don't have enough operational experience to say what a good value for this is, but 10 seems to work. This parameter must be specified. .RE .PP The .I mclt statement .RS 0.25i .PP .B mclt \fIseconds\fR\fB;\fR .PP The \fBmclt\fR statement defines the Maximum Client Lead Time. It must be specified on the primary, and may not be specified on the secondary. This is the length of time for which a lease may be renewed by either failover peer without contacting the other. The longer you set this, the longer it will take for the running server to recover IP addresses after moving into PARTNER-DOWN state. The shorter you set it, the more load your servers will experience when they are not communicating. A value of something like 3600 is probably reasonable, but again bear in mind that we have no real operational experience with this. .RE .PP The .I split statement .RS 0.25i .PP .B split \fIbits\fR\fB;\fR .PP The split statement specifies the split between the primary and secondary for the purposes of load balancing. Whenever a client makes a DHCP request, the DHCP server runs a hash on the client identification, resulting in value from 0 to 255. This is used as an index into a 256 bit field. If the bit at that index is set, the primary is responsible. If the bit at that index is not set, the secondary is responsible. The \fBsplit\fR value determines how many of the leading bits are set to one. So, in practice, higher split values will cause the primary to serve more clients than the secondary. Lower split values, the converse. Legal values are between 0 and 256 inclusive, of which the most reasonable is 128. Note that a value of 0 makes the secondary responsible for all clients and a value of 256 makes the primary responsible for all clients. .RE .PP The .I hba statement .RS 0.25i .PP .B hba \fIcolon-separated-hex-list\fB;\fR .PP The hba statement specifies the split between the primary and secondary as a bitmap rather than a cutoff, which theoretically allows for finer-grained control. In practice, there is probably no need for such fine-grained control, however. An example hba statement: .PP .nf hba ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff: 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00; .fi .PP This is equivalent to a \fBsplit 128;\fR statement, and identical. The following two examples are also equivalent to a \fBsplit\fR of 128, but are not identical: .PP .nf hba aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa: aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa; hba 55:55:55:55:55:55:55:55:55:55:55:55:55:55:55:55: 55:55:55:55:55:55:55:55:55:55:55:55:55:55:55:55; .fi .PP They are equivalent, because half the bits are set to 0, half are set to 1 (0xa and 0x5 are 1010 and 0101 binary respectively) and consequently this would roughly divide the clients equally between the servers. They are not identical, because the actual peers this would load balance to each server are different for each example. .PP You must only have \fBsplit\fR or \fBhba\fR defined, never both. For most cases, the fine-grained control that \fBhba\fR offers isn't necessary, and \fBsplit\fR should be used. .RE .PP The .I load balance max seconds statement .RS 0.25i .PP .B load balance max seconds \fIseconds\fR\fB;\fR .PP This statement allows you to configure a cutoff after which load balancing is disabled. The cutoff is based on the number of seconds since the client sent its first DHCPDISCOVER or DHCPREQUEST message, and only works with clients that correctly implement the \fIsecs\fR field - fortunately most clients do. We recommend setting this to something like 3 or 5. The effect of this is that if one of the failover peers gets into a state where it is responding to failover messages but not responding to some client requests, the other failover peer will take over its client load automatically as the clients retry. .PP It is possible to disable load balancing between peers by setting this value to 0 on both peers. Bear in mind that this means both peers will respond to all DHCPDISCOVERs or DHCPREQUESTs. .RE .PP The .I auto-partner-down statement .RS 0.25i .PP .B auto-partner-down \fIseconds\fR\fB;\fR .PP This statement instructs the server to initiate a timed delay upon entering the communications-interrupted state (any situation of being out-of-contact with the remote failover peer). At the conclusion of the timer, the server will automatically enter the partner-down state. This permits the server to allocate leases from the partner's free lease pool after an STOS+MCLT timer expires, which can be dangerous if the partner is in fact operating at the time (the two servers will give conflicting bindings). .PP Think very carefully before enabling this feature. The partner-down and communications-interrupted states are intentionally segregated because there do exist situations where a failover server can fail to communicate with its peer, but still has the ability to receive and reply to requests from DHCP clients. In general, this feature should only be used in those deployments where the failover servers are directly connected to one another, such as by a dedicated hardwired link ("a heartbeat cable"). .PP A zero value disables the auto-partner-down feature (also the default), and any positive value indicates the time in seconds to wait before automatically entering partner-down. .RE .PP The Failover pool balance statements. .RS 0.25i .PP \fBmax-lease-misbalance \fIpercentage\fR\fB;\fR \fBmax-lease-ownership \fIpercentage\fR\fB;\fR \fBmin-balance \fIseconds\fR\fB;\fR \fBmax-balance \fIseconds\fR\fB;\fR .PP This version of the DHCP Server evaluates pool balance on a schedule, rather than on demand as leases are allocated. The latter approach proved to be slightly klunky when pool misbalanced reach total saturation \(em when any server ran out of leases to assign, it also lost its ability to notice it had run dry. .PP In order to understand pool balance, some elements of its operation first need to be defined. First, there are \'free\' and \'backup\' leases. Both of these are referred to as \'free state leases\'. \'free\' and \'backup\' are \'the free states\' for the purpose of this document. The difference is that only the primary may allocate from \'free\' leases unless under special circumstances, and only the secondary may allocate \'backup\' leases. .PP When pool balance is performed, the only plausible expectation is to provide a 50/50 split of the free state leases between the two servers. This is because no one can predict which server will fail, regardless of the relative load placed upon the two servers, so giving each server half the leases gives both servers the same amount of \'failure endurance\'. Therefore, there is no way to configure any different behaviour, outside of some very small windows we will describe shortly. .PP The first thing calculated on any pool balance run is a value referred to as \'lts\', or "Leases To Send". This, simply, is the difference in the count of free and backup leases, divided by two. For the secondary, it is the difference in the backup and free leases, divided by two. The resulting value is signed: if it is positive, the local server is expected to hand out leases to retain a 50/50 balance. If it is negative, the remote server would need to send leases to balance the pool. Once the lts value reaches zero, the pool is perfectly balanced (give or take one lease in the case of an odd number of total free state leases). .PP The current approach is still something of a hybrid of the old approach, marked by the presence of the \fBmax-lease-misbalance\fR statement. This parameter configures what used to be a 10% fixed value in previous versions: if lts is less than free+backup * \fBmax-lease-misbalance\fR percent, then the server will skip balancing a given pool (it won't bother moving any leases, even if some leases "should" be moved). The meaning of this value is also somewhat overloaded, however, in that it also governs the estimation of when to attempt to balance the pool (which may then also be skipped over). The oldest leases in the free and backup states are examined. The time they have resided in their respective queues is used as an estimate to indicate how much time it is probable it would take before the leases at the top of the list would be consumed (and thus, how long it would take to use all leases in that state). This percentage is directly multiplied by this time, and fit into the schedule if it falls within the \fBmin-balance\fR and \fBmax-balance\fR configured values. The scheduled pool check time is only moved in a downwards direction, it is never increased. Lastly, if the lts is more than double this number in the negative direction, the local server will \'panic\' and transmit a Failover protocol POOLREQ message, in the hopes that the remote system will be woken up into action. .PP Once the lts value exceeds the \fBmax-lease-misbalance\fR percentage of total free state leases as described above, leases are moved to the remote server. This is done in two passes. .PP In the first pass, only leases whose most recent bound client would have been served by the remote server - according to the Load Balance Algorithm (see above \fBsplit\fR and \fBhba\fR configuration statements) - are given away to the peer. This first pass will happily continue to give away leases, decrementing the lts value by one for each, until the lts value has reached the negative of the total number of leases multiplied by the \fBmax-lease-ownership\fR percentage. So it is through this value that you can permit a small misbalance of the lease pools - for the purpose of giving the peer more than a 50/50 share of leases in the hopes that their clients might some day return and be allocated by the peer (operating normally). This process is referred to as \'MAC Address Affinity\', but this is somewhat misnamed: it applies equally to DHCP Client Identifier options. Note also that affinity is applied to leases when they enter the state \'free\' from \'expired\' or \'released\'. In this case also, leases will not be moved from free to backup if the secondary already has more than its share. .PP The second pass is only entered into if the first pass fails to reduce the lts underneath the total number of free state leases multiplied by the \fBmax-lease-ownership\fR percentage. In this pass, the oldest leases are given over to the peer without second thought about the Load Balance Algorithm, and this continues until the lts falls under this value. In this way, the local server will also happily keep a small percentage of the leases that would normally load balance to itself. .PP So, the \fBmax-lease-misbalance\fR value acts as a behavioural gate. Smaller values will cause more leases to transition states to balance the pools over time, higher values will decrease the amount of change (but may lead to pool starvation if there's a run on leases). .PP The \fBmax-lease-ownership\fR value permits a small (percentage) skew in the lease balance of a percentage of the total number of free state leases. .PP Finally, the \fBmin-balance\fR and \fBmax-balance\fR make certain that a scheduled rebalance event happens within a reasonable timeframe (not to be thrown off by, for example, a 7 year old free lease). .PP Plausible values for the percentages lie between 0 and 100, inclusive, but values over 50 are indistinguishable from one another (once lts exceeds 50% of the free state leases, one server must therefore have 100% of the leases in its respective free state). It is recommended to select a \fBmax-lease-ownership\fR value that is lower than the value selected for the \fBmax-lease-misbalance\fR value. \fBmax-lease-ownership\fR defaults to 10, and \fBmax-lease-misbalance\fR defaults to 15. .PP Plausible values for the \fBmin-balance\fR and \fBmax-balance\fR times also range from 0 to (2^32)-1 (or the limit of your local time_t value), but default to values 60 and 3600 respectively (to place balance events between 1 minute and 1 hour). .RE .SH CLIENT CLASSING Clients can be separated into classes, and treated differently depending on what class they are in. This separation can be done either with a conditional statement, or with a match statement within the class declaration. It is possible to specify a limit on the total number of clients within a particular class or subclass that may hold leases at one time, and it is possible to specify automatic subclassing based on the contents of the client packet. .PP Classing support for DHCPv6 clients was added in 4.3.0. It follows the same rules as for DHCPv4 except that support for billing classes has not been added yet. .PP To add clients to classes based on conditional evaluation, you can specify a matching expression in the class statement: .PP .nf class "ras-clients" { match if substring (option dhcp-client-identifier, 1, 3) = "RAS"; } .fi .PP Please note that the values used in match expressions may only come from data or options that are part of the client packet. It is not possible to use values constructed through one or more executable statements. This stems from the fact that client classification occurs before any statements are executed. Attempting to do so will yield indeterminate results. .PP Note that whether you use matching expressions or add statements (or both) to classify clients, you must always write a class declaration for any class that you use. If there will be no match statement and no in-scope statements for a class, the declaration should look like this: .PP .nf class "ras-clients" { } .fi .SH SUBCLASSES .PP In addition to classes, it is possible to declare subclasses. A subclass is a class with the same name as a regular class, but with a specific submatch expression which is hashed for quick matching. This is essentially a speed hack - the main difference between five classes with match expressions and one class with five subclasses is that it will be quicker to find the subclasses. Subclasses work as follows: .PP .nf class "allocation-class-1" { match pick-first-value (option dhcp-client-identifier, hardware); } class "allocation-class-2" { match pick-first-value (option dhcp-client-identifier, hardware); } subclass "allocation-class-1" 1:8:0:2b:4c:39:ad; subclass "allocation-class-2" 1:8:0:2b:a9:cc:e3; subclass "allocation-class-1" 1:0:0:c4:aa:29:44; subnet 10.0.0.0 netmask 255.255.255.0 { pool { allow members of "allocation-class-1"; range 10.0.0.11 10.0.0.50; } pool { allow members of "allocation-class-2"; range 10.0.0.51 10.0.0.100; } } .fi .PP The data following the class name in the subclass declaration is a constant value to use in matching the match expression for the class. When class matching is done, the server will evaluate the match expression and then look the result up in the hash table. If it finds a match, the client is considered a member of both the class and the subclass. .PP Subclasses can be declared with or without scope. In the above example, the sole purpose of the subclass is to allow some clients access to one address pool, while other clients are given access to the other pool, so these subclasses are declared without scopes. If part of the purpose of the subclass were to define different parameter values for some clients, you might want to declare some subclasses with scopes. .PP In the above example, if you had a single client that needed some configuration parameters, while most didn't, you might write the following subclass declaration for that client: .PP .nf subclass "allocation-class-2" 1:08:00:2b:a1:11:31 { option root-path "samsara:/var/diskless/alphapc"; filename "/tftpboot/netbsd.alphapc-diskless"; } .fi .PP In this example, we've used subclassing as a way to control address allocation on a per-client basis. However, it's also possible to use subclassing in ways that are not specific to clients - for example, to use the value of the vendor-class-identifier option to determine what values to send in the vendor-encapsulated-options option. An example of this is shown under the VENDOR ENCAPSULATED OPTIONS head in the .B dhcp-options(5) manual page. .SH PER-CLASS LIMITS ON DYNAMIC ADDRESS ALLOCATION .PP You may specify a limit to the number of clients in a class that can be assigned leases. The effect of this will be to make it difficult for a new client in a class to get an address. Once a class with such a limit has reached its limit, the only way a new client in that class can get a lease is for an existing client to relinquish its lease, either by letting it expire, or by sending a DHCPRELEASE packet. Classes with lease limits are specified as follows: .PP .nf class "limited-1" { lease limit 4; } .fi .PP This will produce a class in which a maximum of four members may hold a lease at one time. .SH SPAWNING CLASSES .PP It is possible to declare a .I spawning class\fR. A spawning class is a class that automatically produces subclasses based on what the client sends. The reason that spawning classes were created was to make it possible to create lease-limited classes on the fly. The envisioned application is a cable-modem environment where the ISP wishes to provide clients at a particular site with more than one IP address, but does not wish to provide such clients with their own subnet, nor give them an unlimited number of IP addresses from the network segment to which they are connected. .PP Many cable modem head-end systems can be configured to add a Relay Agent Information option to DHCP packets when relaying them to the DHCP server. These systems typically add a circuit ID or remote ID option that uniquely identifies the customer site. To take advantage of this, you can write a class declaration as follows: .PP .nf class "customer" { spawn with option agent.circuit-id; lease limit 4; } .fi .PP Now whenever a request comes in from a customer site, the circuit ID option will be checked against the class\'s hash table. If a subclass is found that matches the circuit ID, the client will be classified in that subclass and treated accordingly. If no subclass is found matching the circuit ID, a new one will be created and logged in the .B dhcpd.leases file, and the client will be classified in this new class. Once the client has been classified, it will be treated according to the rules of the class, including, in this case, being subject to the per-site limit of four leases. .PP The use of the subclass spawning mechanism is not restricted to relay agent options - this particular example is given only because it is a fairly straightforward one. .SH COMBINING MATCH, MATCH IF AND SPAWN WITH .PP In some cases, it may be useful to use one expression to assign a client to a particular class, and a second expression to put it into a subclass of that class. This can be done by combining the \fBmatch if\fR and \fBspawn with\fR statements, or the \fBmatch if\fR and \fBmatch\fR statements. For example: .PP .nf class "jr-cable-modems" { match if option dhcp-vendor-identifier = "jrcm"; spawn with option agent.circuit-id; lease limit 4; } class "dv-dsl-modems" { match if option dhcp-vendor-identifier = "dvdsl"; spawn with option agent.circuit-id; lease limit 16; } .fi .PP This allows you to have two classes that both have the same \fBspawn with\fR expression without getting the clients in the two classes confused with each other. .SH DYNAMIC DNS UPDATES .PP The DHCP server has the ability to dynamically update the Domain Name System. Within the configuration files, you can define how you want the Domain Name System to be updated. These updates are RFC 2136 compliant so any DNS server supporting RFC 2136 should be able to accept updates from the DHCP server. .PP There are two DNS schemes implemented. The interim option is based on draft revisions of the DDNS documents while the standard option is based on the RFCs for DHCP-DNS interaction and DHCIDs. A third option, ad-hoc, was deprecated and has now been removed from the code base. The DHCP server must be configured to use one of the two currently-supported methods, or not to do DNS updates. .PP New installations should use the standard option. Older installations may want to continue using the interim option for backwards compatibility with the DNS database until the database can be updated. This can be done with the .I ddns-update-style configuration parameter. .SH THE DNS UPDATE SCHEME the interim and standard DNS update schemes operate mostly according to work from the IETF. The interim version was based on the drafts in progress at the time while the standard is based on the completed RFCs. The standard RFCs are: .PP .nf .ce 3 RFC 4701 (updated by RF5494) RFC 4702 RFC 4703 .fi .PP And the corresponding drafts were: .PP .nf .ce 3 draft-ietf-dnsext-dhcid-rr-??.txt draft-ietf-dhc-fqdn-option-??.txt draft-ietf-dhc-ddns-resolution-??.txt .fi .PP The basic framework for the two schemes is similar with the main material difference being that a DHCID RR is used in the standard version while the interim versions uses a TXT RR. The format of the TXT record bears a resemblance to the DHCID RR but it is not equivalent (MD5 vs SHA2, field length differences etc). .PP In these two schemes the DHCP server does not necessarily always update both the A and the PTR records. The FQDN option includes a flag which, when sent by the client, indicates that the client wishes to update its own A record. In that case, the server can be configured either to honor the client\'s intentions or ignore them. This is done with the statement \fIallow client-updates;\fR or the statement \fIignore client-updates;\fR. By default, client updates are allowed. .PP If the server is configured to allow client updates, then if the client sends a fully-qualified domain name in the FQDN option, the server will use that name the client sent in the FQDN option to update the PTR record. For example, let us say that the client is a visitor from the "radish.org" domain, whose hostname is "jschmoe". The server is for the "example.org" domain. The DHCP client indicates in the FQDN option that its FQDN is "jschmoe.radish.org.". It also indicates that it wants to update its own A record. The DHCP server therefore does not attempt to set up an A record for the client, but does set up a PTR record for the IP address that it assigns the client, pointing at jschmoe.radish.org. Once the DHCP client has an IP address, it can update its own A record, assuming that the "radish.org" DNS server will allow it to do so. .PP If the server is configured not to allow client updates, or if the client doesn\'t want to do its own update, the server will simply choose a name for the client. By default, the server will choose from the following three values: .PP 1. \fBfqdn\fR option (if present) 2. hostname option (if present) 3. Configured hostname option (if defined). .PP If these defaults for choosing the host name are not appropriate you can write your own statement to set the ddns-hostname variable as you wish. If none of the above are found the server will use the host declaration name (if one) and use-host-decl-names is on. .PP It will use its own domain name for the client. It will then update both the A and PTR record, using the name that it chose for the client. If the client sends a fully-qualified domain name in the \fBfqdn\fR option, the server uses only the leftmost part of the domain name - in the example above, "jschmoe" instead of "jschmoe.radish.org". .PP Further, if the \fIignore client-updates;\fR directive is used, then the server will in addition send a response in the DHCP packet, using the FQDN Option, that implies to the client that it should perform its own updates if it chooses to do so. With \fIdeny client-updates;\fR, a response is sent which indicates the client may not perform updates. .PP Both the standard and interim options also include a method to allow more than one DHCP server to update the DNS database without accidentally deleting A records that shouldn\'t be deleted nor failing to add A records that should be added. For the standard option the method works as follows: .PP When the DHCP server issues a client a new lease, it creates a text string that is an SHA hash over the DHCP client\'s identification (see RFCs 4701 & 4702 for details). The update attempts to add an A record with the name the server chose and a DHCID record containing the hashed identifier string (hashid). If this update succeeds, the server is done. .PP If the update fails because the A record already exists, then the DHCP server attempts to add the A record with the prerequisite that there must be a DHCID record in the same name as the new A record, and that DHCID record\'s contents must be equal to hashid. If this update succeeds, then the client has its A record and PTR record. If it fails, then the name the client has been assigned (or requested) is in use, and can\'t be used by the client. At this point the DHCP server gives up trying to do a DNS update for the client until the client chooses a new name. .PP The server also does not update very aggressively. Because each DNS update involves a round trip to the DNS server, there is a cost associated with doing updates even if they do not actually modify the DNS database. So the DHCP server tracks whether or not it has updated the record in the past (this information is stored on the lease) and does not attempt to update records that it thinks it has already updated. .PP This can lead to cases where the DHCP server adds a record, and then the record is deleted through some other mechanism, but the server never again updates the DNS because it thinks the data is already there. In this case the data can be removed from the lease through operator intervention, and once this has been done, the DNS will be updated the next time the client renews. .PP The interim DNS update scheme was written before the RFCs were finalized and does not quite follow them. The RFCs call for a new DHCID RRtype while the interim DNS update scheme uses a TXT record. In addition the ddns-resolution draft called for the DHCP server to put a DHCID RR on the PTR record, but the \fIinterim\fR update method does not do this. In the final RFC this requirement was relaxed such that a server may add a DHCID RR to the PTR record. .PP .SH DDNS IN DUAL STACK ENVIRONMENTS As described in RFC 4703, section 5.2, in order to perform DDNS in dual stack environments, both IPv4 and IPv6 servers would need to be configured to use the standard update style and participating IPv4 clients MUST convey DUIDs as described in RFC 4361, section 6.1., in their dhcp-client-identifiers. .PP In a nutshell, this mechanism is intended to use globally unique DUIDs to idenfity both IPv4 and IPv6 clients, and where a device has both IPv4 and IPv6 leases it is identified by the same DUID. This allows a dual stack client to use the same FQDN for both mappings, while being protected from updates for other clients by the rules of conflict detection. .PP However, not all IPv4 clients implement this behavior which makes supporting them dual stack environments problematic. In order to address this issue ISC DHCP (as of 4.4.0) supports a new mode of DDNS conflict resolution referred to as Dual Stack Mixed Mode (DSMM). .PP The concept behind DSMM is relatively simple. All dhcp servers of one protocol (IPv4 or v6) use one ddns-update-style (interim or standard) while all servers of the "other" protocol will use the "other" ddns-udpate-style. In this way, all servers of a given protocol are using the same record type (TXT or DHCID) for their DHCID RR entries. This allows conflict detection to be enforced within each protocol without interferring with the other's entries. .PP DSMM modifications now ensure that IPv4 DSMM servers only ever modify A records, their associated PTR records and DHCID records, while DSMM IPv6 severs only modify AAAA records, their associated PTR records, and DHCID records. .PP Note that DSMM is not a perfect solution, it is a compromise that can work well provided all participating DNS updaters play by DSMM rules. As with anything else in life, it only works as well as those who particpate behave. .PP While conflict detection is enabled by default, DSMM is not. To enable DSMM, both update-conflict-detection and ddns-dual-stack-mixed-mode must be true. .PP .SH PROTECTING DNS ENTRIES FOR STATIC CLIENTS Built into conflict resolution is the protection of manually made entries for static clients. Per the rules of conflict resolution, a DNS updater may not alter forward DNS entries unless there is a DHCID RR which matches for whom the update is being made. Therefore, any forward DNS entries without a corresponding DHCID RR cannot be altered by such an updater. .PP In some environments, it may be desirable to use only this aspect of conflict resolution and allow DNS updaters to overwrite entries for dynamic clients regardless of what client owns them. In other words, the presence or lack of a DHCID RR is used to determine whether entries may or may not be overwritten. Whether or not the client matches the data value of the DHCID RR is irrelevant. This behavior, off by default, can be configured through the parameter, ddns-guard-id-must-match. As with DSMM, this behavior is can only be enabled if conflict resolution is enabled. This behavior should be considered carefully before electing to use it. .PP There is an additional parameter that can be used with DSMM ddns-other-guard-is-dynamic. When enabled along with DSMM, a server will regard the presence of a DHCID RR of the other style type as indicating that the forward DNS entries for that FQDN should be dynamic and may be overwritten. For example, such a server using interim style could overwrite the DNS entries for an FQDN if there is only a DHDID type DHDID RR for the FQDN. Essentially, if there are dynamic entries for one protocol, that is enough to overcome the static protection of entries for the other protocol. This behavior warrants careful consideration before electing to use it. .PP .SH DYNAMIC DNS UPDATE SECURITY .PP When you set your DNS server up to allow updates from the DHCP server, you may be exposing it to unauthorized updates. To avoid this, you should use TSIG signatures - a method of cryptographically signing updates using a shared secret key. As long as you protect the secrecy of this key, your updates should also be secure. Note, however, that the DHCP protocol itself provides no security, and that clients can therefore provide information to the DHCP server which the DHCP server will then use in its updates, with the constraints described previously. .PP The DNS server must be configured to allow updates for any zone that the DHCP server will be updating. For example, let us say that clients in the sneedville.edu domain will be assigned addresses on the 10.10.17.0/24 subnet. In that case, you will need a key declaration for the TSIG key you will be using, and also two zone declarations - one for the zone containing A records that will be updates and one for the zone containing PTR records - for ISC BIND, something like this: .PP .nf key DHCP_UPDATER { algorithm HMAC-MD5.SIG-ALG.REG.INT; secret pRP5FapFoJ95JEL06sv4PQ==; }; zone "example.org" { type master; file "example.org.db"; allow-update { key DHCP_UPDATER; }; }; zone "17.10.10.in-addr.arpa" { type master; file "10.10.17.db"; allow-update { key DHCP_UPDATER; }; }; .fi .PP You will also have to configure your DHCP server to do updates to these zones. To do so, you need to add something like this to your dhcpd.conf file: .PP .nf key DHCP_UPDATER { algorithm HMAC-MD5.SIG-ALG.REG.INT; secret pRP5FapFoJ95JEL06sv4PQ==; }; zone EXAMPLE.ORG. { primary 127.0.0.1; key DHCP_UPDATER; } zone 17.127.10.in-addr.arpa. { primary 127.0.0.1; key DHCP_UPDATER; } .fi .PP The \fIprimary\fR statement specifies the IP address of the name server whose zone information is to be updated. In addition to the \fIprimary\fR statement there are also the \fIprimary6\fR , \fIsecondary\fR and \fIsecondary6\fR statements. The \fIprimary6\fR statement specifies an IPv6 address for the name server. The secondaries provide for additional addresses for name servers to be used if the primary does not respond. The number of name servers the DDNS code will attempt to use before giving up is limited and is currently set to three. .PP Note that the zone declarations have to correspond to authority records in your name server - in the above example, there must be an SOA record for "example.org." and for "17.10.10.in-addr.arpa.". For example, if there were a subdomain "foo.example.org" with no separate SOA, you could not write a zone declaration for "foo.example.org." Also keep in mind that zone names in your DHCP configuration should end in a "."; this is the preferred syntax. If you do not end your zone name in a ".", the DHCP server will figure it out. Also note that in the DHCP configuration, zone names are not encapsulated in quotes where there are in the DNS configuration. .PP You should choose your own secret key, of course. The ISC BIND 9 distribution comes with a program for generating secret keys called dnssec-keygen. If you are using BIND 9\'s dnssec-keygen, the above key would be created as follows: .PP .nf dnssec-keygen -a HMAC-MD5 -b 128 -n USER DHCP_UPDATER .fi .PP The key name, algorithm, and secret must match that being used by the DNS server. The DHCP server currently supports the following algorithms: .nf HMAC-MD5 HMAC-SHA1 HMAC-SHA224 HMAC-SHA256 HMAC-SHA384 HMAC-SHA512 .fi .PP You may wish to enable logging of DNS updates on your DNS server. To do so, you might write a logging statement like the following: .PP .nf logging { channel update_debug { file "/var/log/update-debug.log"; severity debug 3; print-category yes; print-severity yes; print-time yes; }; channel security_info { file "/var/log/named-auth.info"; severity info; print-category yes; print-severity yes; print-time yes; }; category update { update_debug; }; category security { security_info; }; }; .fi .PP You must create the /var/log/named-auth.info and /var/log/update-debug.log files before starting the name server. For more information on configuring ISC BIND, consult the documentation that accompanies it. .SH REFERENCE: EVENTS .PP There are three kinds of events that can happen regarding a lease, and it is possible to declare statements that occur when any of these events happen. These events are the commit event, when the server has made a commitment of a certain lease to a client, the release event, when the client has released the server from its commitment, and the expiry event, when the commitment expires. .PP To declare a set of statements to execute when an event happens, you must use the \fBon\fR statement, followed by the name of the event, followed by a series of statements to execute when the event happens, enclosed in braces. .SH REFERENCE: DECLARATIONS .PP .B The .I include .B statement .PP .nf \fBinclude\fR \fI"filename"\fR\fB;\fR .fi .PP The \fIinclude\fR statement is used to read in a named file, and process the contents of that file as though it were entered in place of the include statement. .PP .B The .I shared-network .B statement .PP .nf \fBshared-network\fR \fIname\fR \fB{\fR [ \fIparameters\fR ] [ \fIdeclarations\fR ] \fB}\fR .fi .PP The \fIshared-network\fR statement is used to inform the DHCP server that some IP subnets actually share the same physical network. Any subnets in a shared network should be declared within a \fIshared-network\fR statement. Parameters specified in the \fIshared-network\fR statement will be used when booting clients on those subnets unless parameters provided at the subnet or host level override them. If any subnet in a shared network has addresses available for dynamic allocation, those addresses are collected into a common pool for that shared network and assigned to clients as needed. There is no way to distinguish on which subnet of a shared network a client should boot. .PP .I Name should be the name of the shared network. This name is used when printing debugging messages, so it should be descriptive for the shared network. The name may have the syntax of a valid domain name (although it will never be used as such), or it may be any arbitrary name, enclosed in quotes. .PP .B The .I subnet .B statement .PP .nf \fBsubnet\fR \fIsubnet-number\fR \fBnetmask\fR \fInetmask\fR \fB{\fR [ \fIparameters\fR ] [ \fIdeclarations\fR ] \fB}\fR .fi .PP The \fIsubnet\fR statement is used to provide dhcpd with enough information to tell whether or not an IP address is on that subnet. It may also be used to provide subnet-specific parameters and to specify what addresses may be dynamically allocated to clients booting on that subnet. Such addresses are specified using the \fIrange\fR declaration. .PP The .I subnet-number should be an IP address or domain name which resolves to the subnet number of the subnet being described. The .I netmask should be an IP address or domain name which resolves to the subnet mask of the subnet being described. The subnet number, together with the netmask, are sufficient to determine whether any given IP address is on the specified subnet. .PP Although a netmask must be given with every subnet declaration, it is recommended that if there is any variance in subnet masks at a site, a subnet-mask option statement be used in each subnet declaration to set the desired subnet mask, since any subnet-mask option statement will override the subnet mask declared in the subnet statement. .PP .B The .I subnet6 .B statement .PP .nf \fBsubnet6\fR \fIsubnet6-number\fR \fB{\fR [ \fIparameters\fR ] [ \fIdeclarations\fR ] \fB}\fR .fi .PP The \fIsubnet6\fR statement is used to provide dhcpd with enough information to tell whether or not an IPv6 address is on that subnet6. It may also be used to provide subnet-specific parameters and to specify what addresses may be dynamically allocated to clients booting on that subnet. .PP The .I subnet6-number should be an IPv6 network identifier, specified as ip6-address/bits. .PP .B The .I range .B statement .PP .nf .B range\fR [ \fBdynamic-bootp\fR ] \fIlow-address\fR [ \fIhigh-address\fR]\fB;\fR .fi .PP For any subnet on which addresses will be assigned dynamically, there must be at least one \fIrange\fR statement. The range statement gives the lowest and highest IP addresses in a range. All IP addresses in the range should be in the subnet in which the \fIrange\fR statement is declared. The \fIdynamic-bootp\fR flag may be specified if addresses in the specified range may be dynamically assigned to BOOTP clients as well as DHCP clients. When specifying a single address, \fIhigh-address\fR can be omitted. .PP .B The .I range6 .B statement .PP .nf .B range6\fR \fIlow-address\fR \fIhigh-address\fR\fB;\fR .B range6\fR \fIsubnet6-number\fR\fB;\fR .B range6\fR \fIsubnet6-number\fR \fBtemporary\fR\fB;\fR .B range6\fR \fIaddress\fR \fBtemporary\fR\fB;\fR .fi .PP For any IPv6 subnet6 on which addresses will be assigned dynamically, there must be at least one \fIrange6\fR statement. The \fIrange6\fR statement can either be the lowest and highest IPv6 addresses in a \fIrange6\fR, or use CIDR notation, specified as ip6-address/bits. All IP addresses in the \fIrange6\fR should be in the subnet6 in which the \fIrange6\fR statement is declared. .PP The \fItemporary\fR variant makes the prefix (by default on 64 bits) available for temporary (RFC 4941) addresses. A new address per prefix in the shared network is computed at each request with an IA_TA option. Release and Confirm ignores temporary addresses. .PP Any IPv6 addresses given to hosts with \fIfixed-address6\fR are excluded from the \fIrange6\fR, as are IPv6 addresses on the server itself. .PP .PP .B The .I prefix6 .B statement .PP .nf .B prefix6\fR \fIlow-address\fR \fIhigh-address\fR \fB/\fR \fIbits\fR\fB;\fR .fi .PP The \fIprefix6\fR is the \fIrange6\fR equivalent for Prefix Delegation (RFC 3633). Prefixes of \fIbits\fR length are assigned between \fIlow-address\fR and \fIhigh-address\fR. .PP Any IPv6 prefixes given to static entries (hosts) with \fIfixed-prefix6\fR are excluded from the \fIprefix6\fR. .PP This statement is currently global but it should have a shared-network scope. .PP .B The .I host .B statement .PP .nf \fBhost\fR \fIhostname\fR { [ \fIparameters\fR ] [ \fIdeclarations\fR ] \fB}\fR .fi .PP The .B host declaration provides a way for the DHCP server to identify a DHCP or BOOTP client. This allows the server to provide configuration information including fixed addresses or, in DHCPv6, fixed prefixes for a specific client. .PP If it is desirable to be able to boot a DHCP or BOOTP client on more than one subnet with fixed v4 addresses, more than one address may be specified in the .I fixed-address declaration, or more than one .B host statement may be specified matching the same client. .PP The .I fixed-address6 declaration is used for v6 addresses. At this time it only works with a single address. For multiple addresses specify multiple .B host statements. .PP If client-specific boot parameters must change based on the network to which the client is attached, then multiple .B host declarations should be used. The .B host declarations will only match a client if one of their .I fixed-address statements is viable on the subnet (or shared network) where the client is attached. Conversely, for a .B host declaration to match a client being allocated a dynamic address, it must not have any .I fixed-address statements. You may therefore need a mixture of .B host declarations for any given client...some having .I fixed-address statements, others without. .PP .I hostname should be a name identifying the host. If a \fIhostname\fR option is not specified for the host, \fIhostname\fR is used. .PP \fIHost\fR declarations are matched to actual DHCP or BOOTP clients by matching the \fRdhcp-client-identifier\fR option specified in the \fIhost\fR declaration to the one supplied by the client, or, if the \fIhost\fR declaration or the client does not provide a \fRdhcp-client-identifier\fR option, by matching the \fIhardware\fR parameter in the \fIhost\fR declaration to the network hardware address supplied by the client. BOOTP clients do not normally provide a \fIdhcp-client-identifier\fR, so the hardware address must be used for all clients that may boot using the BOOTP protocol. .PP DHCPv6 servers can use the \fIhost-identifier option\fR parameter in the \fIhost\fR declaration, and specify any option with a fixed value to identify hosts. .PP Please be aware that .B only the \fIdhcp-client-identifier\fR option and the hardware address can be used to match a host declaration, or the \fIhost-identifier option\fR parameter for DHCPv6 servers. For example, it is not possible to match a host declaration to a \fIhost-name\fR option. This is because the host-name option cannot be guaranteed to be unique for any given client, whereas both the hardware address and \fIdhcp-client-identifier\fR option are at least theoretically guaranteed to be unique to a given client. .PP .B The .I group .B statement .PP .nf \fBgroup\fR { [ \fIparameters\fR ] [ \fIdeclarations\fR ] \fB}\fR .fi .PP The group statement is used simply to apply one or more parameters to a group of declarations. It can be used to group hosts, shared networks, subnets, or even other groups. .SH REFERENCE: ALLOW AND DENY The .I allow and .I deny statements can be used to control the response of the DHCP server to various sorts of requests. The allow and deny keywords actually have different meanings depending on the context. In a pool context, these keywords can be used to set up access lists for address allocation pools. In other contexts, the keywords simply control general server behavior with respect to clients based on scope. In a non-pool context, the .I ignore keyword can be used in place of the .I deny keyword to prevent logging of denied requests. .PP .SH ALLOW DENY AND IGNORE IN SCOPE The following usages of allow and deny will work in any scope, although it is not recommended that they be used in pool declarations. .PP .B The .I unknown-clients .B keyword .PP \fBallow unknown-clients;\fR \fBdeny unknown-clients;\fR \fBignore unknown-clients;\fR .PP The \fBunknown-clients\fR flag is used to tell dhcpd whether or not to dynamically assign addresses to unknown clients. Dynamic address assignment to unknown clients is \fBallow\fRed by default. An unknown client is simply a client that has no host declaration. .PP The use of this option is now \fIdeprecated\fR. If you are trying to restrict access on your network to known clients, you should use \fBdeny unknown-clients;\fR inside of your address pool, as described under the heading ALLOW AND DENY WITHIN POOL DECLARATIONS. .PP .B The .I bootp .B keyword .PP \fBallow bootp;\fR \fBdeny bootp;\fR \fBignore bootp;\fR .PP The \fBbootp\fR flag is used to tell dhcpd whether or not to respond to bootp queries. Bootp queries are \fBallow\fRed by default. .PP .B The .I booting .B keyword .PP \fBallow booting;\fR \fBdeny booting;\fR \fBignore booting;\fR .PP The \fBbooting\fR flag is used to tell dhcpd whether or not to respond to queries from a particular client. This keyword only has meaning when it appears in a host declaration. By default, booting is \fBallow\fRed, but if it is disabled for a particular client, then that client will not be able to get an address from the DHCP server. .PP .B The .I duplicates .B keyword .PP \fBallow duplicates;\fR \fBdeny duplicates;\fR .PP Host declarations can match client messages based on the DHCP Client Identifier option or based on the client's network hardware type and MAC address. If the MAC address is used, the host declaration will match any client with that MAC address - even clients with different client identifiers. This doesn't normally happen, but is possible when one computer has more than one operating system installed on it - for example, Microsoft Windows and NetBSD or Linux. .PP The \fBduplicates\fR flag tells the DHCP server that if a request is received from a client that matches the MAC address of a host declaration, any other leases matching that MAC address should be discarded by the server, even if the UID is not the same. This is a violation of the DHCP protocol, but can prevent clients whose client identifiers change regularly from holding many leases at the same time. By default, duplicates are \fBallow\fRed. .PP .B The .I declines .B keyword .PP \fBallow declines;\fR \fBdeny declines;\fR \fBignore declines;\fR .PP The DHCPDECLINE message is used by DHCP clients to indicate that the lease the server has offered is not valid. When the server receives a DHCPDECLINE for a particular address, it normally abandons that address, assuming that some unauthorized system is using it. Unfortunately, a malicious or buggy client can, using DHCPDECLINE messages, completely exhaust the DHCP server's allocation pool. The server will eventually reclaim these leases, but not while the client is running through the pool. This may cause serious thrashing in the DNS, and it will also cause the DHCP server to forget old DHCP client address allocations. .PP The \fBdeclines\fR flag tells the DHCP server whether or not to honor DHCPDECLINE messages. If it is set to \fBdeny\fR or \fBignore\fR in a particular scope, the DHCP server will not respond to DHCPDECLINE messages. .PP The \fBdeclines\fR flag is only supported by DHCPv4 servers. Given the large IPv6 address space and the internal limits imposed by the server's address generation mechanism we don't think it is necessary for DHCPv6 servers at this time. .PP Currently, abandoned IPv6 addresses are reclaimed in one of two ways: a) Client renews a specific address: If a client using a given DUID submits a DHCP REQUEST containing the last address abandoned by that DUID, the address will be reassigned to that client. b) Upon the second restart following an address abandonment. When an address is abandoned it is both recorded as such in the lease file and retained as abandoned in server memory until the server is restarted. Upon restart, the server will process the lease file and all addresses whose last known state is abandoned will be retained as such in memory but not rewritten to the lease file. This means that a subsequent restart of the server will not see the abandoned addresses in the lease file and therefore have no record of them as abandoned in memory and as such perceive them as free for assignment. .PP The total number addresses in a pool, available for a given DUID value, is internally limited by the server's address generation mechanism. If through mistaken configuration, multiple clients are using the same DUID they will competing for the same addresses causing the server to reach this internal limit rather quickly. The internal limit isolates this type of activity such that address range is not exhausted for other DUID values. The appearance of the following error log, can be an indication of this condition: "Best match for DUID is an abandoned address, This may be a result of multiple clients attempting to use this DUID" where is an actual DUID value depicted as colon separated string of bytes in hexadecimal values. .PP .B The .I client-updates .B keyword .PP \fBallow client-updates;\fR \fBdeny client-updates;\fR .PP The \fBclient-updates\fR flag tells the DHCP server whether or not to honor the client's intention to do its own update of its A record. See the documentation under the heading THE DNS UPDATE SCHEME for details. .PP .B The .I leasequery .B keyword .PP \fBallow leasequery;\fR \fBdeny leasequery;\fR .PP The \fBleasequery\fR flag tells the DHCP server whether or not to answer DHCPLEASEQUERY packets. The answer to a DHCPLEASEQUERY packet includes information about a specific lease, such as when it was issued and when it will expire. By default, the server will not respond to these packets. .SH ALLOW AND DENY WITHIN POOL DECLARATIONS .PP The uses of the allow and deny keywords shown in the previous section work pretty much the same way whether the client is sending a DHCPDISCOVER or a DHCPREQUEST message - an address will be allocated to the client (either the old address it's requesting, or a new address) and then that address will be tested to see if it's okay to let the client have it. If the client requested it, and it's not okay, the server will send a DHCPNAK message. Otherwise, the server will simply not respond to the client. If it is okay to give the address to the client, the server will send a DHCPACK message. .PP The primary motivation behind pool declarations is to have address allocation pools whose allocation policies are different. A client may be denied access to one pool, but allowed access to another pool on the same network segment. In order for this to work, access control has to be done during address allocation, not after address allocation is done. .PP When a DHCPREQUEST message is processed, address allocation simply consists of looking up the address the client is requesting and seeing if it's still available for the client. If it is, then the DHCP server checks both the address pool permit lists and the relevant in-scope allow and deny statements to see if it's okay to give the lease to the client. In the case of a DHCPDISCOVER message, the allocation process is done as described previously in the ADDRESS ALLOCATION section. .PP When declaring permit lists for address allocation pools, the following syntaxes are recognized following the allow or deny keywords: .PP \fBknown-clients;\fR .PP If specified, this statement either allows or prevents allocation from this pool to any client that has a host declaration (i.e., is known). A client is known if it has a host declaration in \fIany\fR scope, not just the current scope. .PP \fBunknown-clients;\fR .PP If specified, this statement either allows or prevents allocation from this pool to any client that has no host declaration (i.e., is not known). .PP \fBmembers of "\fRclass\fB";\fR .PP If specified, this statement either allows or prevents allocation from this pool to any client that is a member of the named class. .PP \fBdynamic bootp clients;\fR .PP If specified, this statement either allows or prevents allocation from this pool to any bootp client. .PP \fBauthenticated clients;\fR .PP If specified, this statement either allows or prevents allocation from this pool to any client that has been authenticated using the DHCP authentication protocol. This is not yet supported. .PP \fBunauthenticated clients;\fR .PP If specified, this statement either allows or prevents allocation from this pool to any client that has not been authenticated using the DHCP authentication protocol. This is not yet supported. .PP \fBall clients;\fR .PP If specified, this statement either allows or prevents allocation from this pool to all clients. This can be used when you want to write a pool declaration for some reason, but hold it in reserve, or when you want to renumber your network quickly, and thus want the server to force all clients that have been allocated addresses from this pool to obtain new addresses immediately when they next renew. .PP \fBafter \fItime\fR\fB;\fR .PP If specified, this statement either allows or prevents allocation from this pool after a given date. This can be used when you want to move clients from one pool to another. The server adjusts the regular lease time so that the latest expiry time is at the given time+min-lease-time. A short min-lease-time enforces a step change, whereas a longer min-lease-time allows for a gradual change. \fItime\fR is either second since epoch, or a UTC time string e.g. 4 2007/08/24 09:14:32 or a string with time zone offset in seconds e.g. 4 2007/08/24 11:14:32 -7200 .SH REFERENCE: PARAMETERS The .I abandon-lease-time statement .RS 0.25i .PP .B abandon-lease-time \fItime\fR\fB;\fR .PP .I Time should be the maximum amount of time (in seconds) that an abandoned IPv4 lease remains unavailable for assignment to a client. Abandoned leases will only be offered to clients if there are no free leases. If not defined, the default abandon lease time is 86400 seconds (24 hours). Note the abandoned lease time for a given lease is preserved across server restarts. The parameter may only be set at the global scope and is evaluated only once during server startup. .PP Values less than sixty seconds are not recommended as this is below the ping check threshold and can cause leases once abandoned but since returned to the free state to not be pinged before being offered. If the requested time is larger than 0x7FFFFFFF - 1 or the sum of the current time plus the abandoned time isgreater than 0x7FFFFFFF it is treated as infinite. .RE .PP The .I adaptive-lease-time-threshold statement .RS 0.25i .PP .B adaptive-lease-time-threshold \fIpercentage\fR\fB;\fR .PP When the number of allocated leases within a pool rises above the \fIpercentage\fR given in this statement, the DHCP server decreases the lease length for new clients within this pool to \fImin-lease-time\fR seconds. Clients renewing an already valid (long) leases get at least the remaining time from the current lease. Since the leases expire faster, the server may either recover more quickly or avoid pool exhaustion entirely. Once the number of allocated leases drop below the threshold, the server reverts back to normal lease times. Valid percentages are between 1 and 99. .RE .PP The .I always-broadcast statement .RS 0.25i .PP .B always-broadcast \fIflag\fR\fB;\fR .PP The DHCP and BOOTP protocols both require DHCP and BOOTP clients to set the broadcast bit in the flags field of the BOOTP message header. Unfortunately, some DHCP and BOOTP clients do not do this, and therefore may not receive responses from the DHCP server. The DHCP server can be made to always broadcast its responses to clients by setting this flag to \'on\' for the relevant scope; relevant scopes would be inside a conditional statement, as a parameter for a class, or as a parameter for a host declaration. To avoid creating excess broadcast traffic on your network, we recommend that you restrict the use of this option to as few clients as possible. For example, the Microsoft DHCP client is known not to have this problem, as are the OpenTransport and ISC DHCP clients. .RE .PP The .I always-reply-rfc1048 statement .RS 0.25i .PP .B always-reply-rfc1048 \fIflag\fR\fB;\fR .PP Some BOOTP clients expect RFC1048-style responses, but do not follow RFC1048 when sending their requests. You can tell that a client is having this problem if it is not getting the options you have configured for it and if you see in the server log the message "(non-rfc1048)" printed with each BOOTREQUEST that is logged. .PP If you want to send rfc1048 options to such a client, you can set the .B always-reply-rfc1048 option in that client's host declaration, and the DHCP server will respond with an RFC-1048-style vendor options field. This flag can be set in any scope, and will affect all clients covered by that scope. .RE .PP The .I authoritative statement .RS 0.25i .PP .B authoritative; .PP .B not authoritative; .PP The DHCP server will normally assume that the configuration information about a given network segment is not known to be correct and is not authoritative. This is so that if a naive user installs a DHCP server not fully understanding how to configure it, it does not send spurious DHCPNAK messages to clients that have obtained addresses from a legitimate DHCP server on the network. .PP Network administrators setting up authoritative DHCP servers for their networks should always write \fBauthoritative;\fR at the top of their configuration file to indicate that the DHCP server \fIshould\fR send DHCPNAK messages to misconfigured clients. If this is not done, clients will be unable to get a correct IP address after changing subnets until their old lease has expired, which could take quite a long time. .PP Usually, writing \fBauthoritative;\fR at the top level of the file should be sufficient. However, if a DHCP server is to be set up so that it is aware of some networks for which it is authoritative and some networks for which it is not, it may be more appropriate to declare authority on a per-network-segment basis. .PP Note that the most specific scope for which the concept of authority makes any sense is the physical network segment - either a shared-network statement or a subnet statement that is not contained within a shared-network statement. It is not meaningful to specify that the server is authoritative for some subnets within a shared network, but not authoritative for others, nor is it meaningful to specify that the server is authoritative for some host declarations and not others. .RE .PP The \fIboot-unknown-clients\fR statement .RS 0.25i .PP .B boot-unknown-clients \fIflag\fB;\fR .PP If the \fIboot-unknown-clients\fR statement is present and has a value of \fIfalse\fR or \fIoff\fR, then clients for which there is no .I host declaration will not be allowed to obtain IP addresses. If this statement is not present or has a value of \fItrue\fR or \fIon\fR, then clients without host declarations will be allowed to obtain IP addresses, as long as those addresses are not restricted by .I allow and \fIdeny\fR statements within their \fIpool\fR declarations. .RE .PP The \fIcheck-secs-byte-order\fR statement .RS 0.25i .PP .B check-secs-byte-order \fIflag\fB;\fR .PP When \fIcheck-secs-byte-order\fR is enabled, the server will check for DHCPv4 clients that do the byte ordering on the secs field incorrectly. This field should be in network byte order but some clients get it wrong. When this parameter is enabled the server will examine the secs field and if it looks wrong (high byte non zero and low byte zero) swap the bytes. The default is disabled. This parameter is only useful when doing load balancing within failover. (Formerly, this behavior had to be enabled during compilation configuration via --enable-secs-byteorder). .PP The \fIdb-time-format\fR statement .RS 0.25i .PP .B db-time-format \fR[ \fIdefault\fR | \fIlocal\fR ] \fB;\fR .PP The DHCP server software outputs several timestamps when writing leases to persistent storage. This configuration parameter selects one of two output formats. The \fIdefault\fR format prints the day, date, and time in UTC, while the \fIlocal\fR format prints the system seconds-since-epoch, and helpfully provides the day and time in the system timezone in a comment. The time formats are described in detail in the dhcpd.leases(5) manpage. .RE .PP The \fIddns-hostname\fR statement .RS 0.25i .PP .B ddns-hostname \fIname\fB;\fR .PP The \fIname\fR parameter should be the hostname that will be used in setting up the client's A and PTR records. If no \fIddns-hostname\fR is specified in scope, then the server will derive the hostname automatically, using an algorithm that varies for each of the different update methods. .RE .PP The \fIddns-domainname\fR statement .RS 0.25i .PP .B ddns-domainname \fIname\fB;\fR .PP The \fIname\fR parameter should be the domain name that will be appended to the client's hostname to form a fully-qualified domain-name (FQDN). .RE .PP The \fIddns-dual-stack-mixed-mode\fR statement .RS 0.25i .PP .B ddns-dual-stack-mixed-mode \fIflag\fB;\fR .PP The \fIddns-dual-stack-mixed-mode\fR parameter controls whether or not the server applies Dual Stack Mixed Mode rules during DDNS conflict resolution. This parameter is off by default, has no effect unless update-conflict-detection is enabled, and may only be specified at the global scope. .RE .PP The \fIddns-guard-id-must-match\fR statement .RS 0.25i .PP .B ddns-guard-id-must-match \fIflag\fB;\fR .PP The \fIddns-guard-id-must-match\fR parameter controls whether or not a the client id within a DHCID RR must match that of the DNS update's client to permit DNS entries associated with that DHCID RR to be ovewritten. Proper conflict resolution requires ID matching and should only be disabled after careful consideration. When disabled, it is allows any DNS updater to replace DNS entries that have an associated DHCID RR, regardless of client identity. This parameter is on by default, has no effect unless update-conflict-detection is enabled, and may only be specified at the global scope. .RE .PP The \fddns-local-address4\fR and \fddns-local-address6\fR statements .RS 0.25i .PP .B ddns-local-address4 \fIaddress\fB;\fR .PP .B ddns-local-address6 \fIaddress\fB;\fR .PP The \fIaddress\fR parameter should be the local IPv4 or IPv6 address the server should use as the from address when sending DDNS update requests. .RE .PP The \fIddns-other-guard-is-dynamic\fR statement .RS 0.25i .PP .B ddns-other-guard-is-dynamic \fIflag\fB;\fR .PP The \fIddns-other-guard-is-dynamic\fR parameter controls whether or not a a server running DSMM will consider the presence of the other update style DHCID RR as an indcation that a DNS entries may be overwritten. It should only be enabled after careful study as it allows DNS entries that would otherwise be protected as static, to be overwritten in certain cases. This paramater is off by default, has no effect unless ddns-dual-stack-mixed-mode is enabled, and may only be specified at the global scope. .RE .PP The \fIddns-rev-domainname\fR statement .RS 0.25i .PP .B ddns-rev-domainname \fIname\fB;\fR .PP The \fIname\fR parameter should be the domain name that will be appended to the client's reversed IP address to produce a name for use in the client's PTR record. By default, this is "in-addr.arpa.", but the default can be overridden here. .PP The reversed IP address to which this domain name is appended is always the IP address of the client, in dotted quad notation, reversed - for example, if the IP address assigned to the client is 10.17.92.74, then the reversed IP address is 74.92.17.10. So a client with that IP address would, by default, be given a PTR record of 10.17.92.74.in-addr.arpa. .RE .PP The \fIddns-update-style\fR parameter .RS 0.25i .PP .B ddns-update-style \fIstyle\fB;\fR .PP The .I style parameter must be one of \fBstandard\fR, \fBinterim\fR or \fBnone\fR. The \fIddns-update-style\fR statement is only meaningful in the outer scope - it is evaluated once after reading the dhcpd.conf file, rather than each time a client is assigned an IP address, so there is no way to use different DNS update styles for different clients. The default is \fBnone\fR. .RE .PP .B The .I ddns-updates .B statement .RS 0.25i .PP \fBddns-updates \fIflag\fR\fB;\fR .PP The \fIddns-updates\fR parameter controls whether or not the server will attempt to do a DNS update when a lease is confirmed. Set this to \fIoff\fR if the server should not attempt to do updates within a certain scope. The \fIddns-updates\fR parameter is on by default. To disable DNS updates in all scopes, it is preferable to use the \fIddns-update-style\fR statement, setting the style to \fInone\fR. .RE .PP The .I default-lease-time statement .RS 0.25i .PP .B default-lease-time \fItime\fR\fB;\fR .PP .I Time should be the length in seconds that will be assigned to a lease if the client requesting the lease does not ask for a specific expiration time. This is used for both DHCPv4 and DHCPv6 leases (it is also known as the "valid lifetime" in DHCPv6). The default is 43200 seconds. .RE .PP The .I delayed-ack and .I max-ack-delay statements .RS 0.25i .PP .B delayed-ack \fIcount\fR\fB;\fR .PP .B max-ack-delay \fImicroseconds\fR\fB;\fR .PP .I Count should be an integer value from zero to 2^16-1 and defaults to 0, which means that the feature is disabled. Otherwise, 28 may be a sensible starting point for many configurations (SO_SNDBUF size / 576 bytes.) The count represents how many DHCPv4 replies maximum will be queued pending transmission until after a database commit event. If this number is reached, a database commit event (commonly resulting in fsync() and representing a performance penalty) will be made, and the reply packets will be transmitted in a batch afterwards. This preserves the RFC2131 direction that "stable storage" be updated prior to replying to clients. Should the DHCPv4 sockets "go dry" (select() returns immediately with no read sockets), the commit is made and any queued packets are transmitted. .PP Similarly, \fImicroseconds\fR indicates how many microseconds are permitted to pass inbetween queuing a packet pending an fsync, and performing the fsync. Valid values range from 0 to 2^32-1, and defaults to 250,000 (1/4 of a second). .PP The delayed-ack feature is compiled in by default, but can be disabled at compile time with \'./configure --disable-delayed-ack\'. Please note that the delayed-ack feature is not currently compatible with support for DHPCv4-over-DHCPv6 so when a 4to6 port ommand line argument enables this in the server the delayed-ack value is reset to 0. .RE .PP The .I dhcp-cache-threshold statement .RS 0.25i .PP .B dhcp-cache-threshold \fIpercentage\fB;\fR .PP The \fIdhcp-cache-threshold\fR statement takes one integer parameter with allowed values between 0 and 100. The default value is 25 (25% of the lease time). This parameter expresses the percentage of the total lease time, measured from the beginning, during which a client's attempt to renew its lease will result in getting the already assigned lease, rather than an extended lease. This feature is supported for both IPv4 and IPv6 and down to the pool level and for IPv6 all three pool types: NA, TA and PD. .PP Clients that attempt renewal frequently can cause the server to update and write the database frequently resulting in a performance impact on the server. The \fIdhcp-cache-threshold\fR statement instructs the DHCP server to avoid updating leases too frequently thus avoiding this behavior. Instead the server replies with the same lease (i.e. reuses it) with no modifications except for CLTT (Client Last Transmission Time) and for IPv4: the lease time sent to the client is shortened by the age of the lease while for IPv6: the preferred and valid lifetimes sent to the client are shortened by the age of the lease. None of these changes require writing the lease to disk. .PP When an existing lease is matched to a renewing client, it will be reused if all of the following conditions are true: .nf 1. The dhcp-cache-threshold is larger than zero 2. The current lease is active 3. The percentage of the lease time that has elapsed is less than dhcp-cache-threshold 4. The client information provided in the renewal does not alter any of the following: a. DNS information and DNS updates are enabled b. Billing class to which the lease is associated (IPv4 only) c. The host declaration associated with the lease (IPv4 only) d. The client id - this may happen if a client boots without a client id and then starts using one in subsequent requests. (IPv4 only) .fi .PP While lease data is not written to disk when a lease is reused, the server will still execute any on-commit statements. .PP Note that the lease can be reused if the options the client or relay agent sends are changed. These changes will not be recorded in the in-memory or on-disk databases until the client renews after the threshold time is reached. .RE .PP The .I do-forward-updates statement .RS 0.25i .PP .B do-forward-updates \fIflag\fB;\fR .PP The \fIdo-forward-updates\fR statement instructs the DHCP server as to whether it should attempt to update a DHCP client\'s A record when the client acquires or renews a lease. This statement has no effect unless DNS updates are enabled. Forward updates are enabled by default. If this statement is used to disable forward updates, the DHCP server will never attempt to update the client\'s A record, and will only ever attempt to update the client\'s PTR record if the client supplies an FQDN that should be placed in the PTR record using the \fBfqdn\fR option. If forward updates are enabled, the DHCP server will still honor the setting of the \fBclient-updates\fR flag. .RE .PP The .I dont-use-fsync statement .RS 0.25i .PP .B dont-use-fsync \fIflag\fB;\fR .PP The \fIdont-use-fsync\fR statement instructs the DHCP server if it should call fsync() when writing leases to the lease file. By default and if the flag is set to false the server \fBwill\fR call fsync(). Suppressing the call to fsync() may increase the performance of the server but it also adds a risk that a lease will not be properly written to the disk after it has been issued to a client and before the server stops. This can lead to duplicate leases being issued to different clients. Using this option is \fBnot recommended\FR. .RE .PP The .I dynamic-bootp-lease-cutoff statement .RS 0.25i .PP .B dynamic-bootp-lease-cutoff \fIdate\fB;\fR .PP The \fIdynamic-bootp-lease-cutoff\fR statement sets the ending time for all leases assigned dynamically to BOOTP clients. Because BOOTP clients do not have any way of renewing leases, and don't know that their leases could expire, by default dhcpd assigns infinite leases to all BOOTP clients. However, it may make sense in some situations to set a cutoff date for all BOOTP leases - for example, the end of a school term, or the time at night when a facility is closed and all machines are required to be powered off. .PP .I Date should be the date on which all assigned BOOTP leases will end. The date is specified in the form: .PP .ce 1 W YYYY/MM/DD HH:MM:SS .PP W is the day of the week expressed as a number from zero (Sunday) to six (Saturday). YYYY is the year, including the century. MM is the month expressed as a number from 1 to 12. DD is the day of the month, counting from 1. HH is the hour, from zero to 23. MM is the minute and SS is the second. The time is always in Coordinated Universal Time (UTC), not local time. .RE .PP The .I dynamic-bootp-lease-length statement .RS 0.25i .PP .B dynamic-bootp-lease-length\fR \fIlength\fR\fB;\fR .PP The \fIdynamic-bootp-lease-length\fR statement is used to set the length of leases dynamically assigned to BOOTP clients. At some sites, it may be possible to assume that a lease is no longer in use if its holder has not used BOOTP or DHCP to get its address within a certain time period. The period is specified in \fIlength\fR as a number of seconds. If a client reboots using BOOTP during the timeout period, the lease duration is reset to \fIlength\fR, so a BOOTP client that boots frequently enough will never lose its lease. Needless to say, this parameter should be adjusted with extreme caution. .RE .PP The .I echo-client-id statement .RS 0.25i .PP .B echo-client-id\fR \fIflag\fR\fB;\fR .PP The \fIecho-client-id\fR statement is used to enable or disable RFC 6842 compliant behavior. If the echo-client-id statement is present and has a value of true or on, and a DHCP DISCOVER or REQUEST is received which contains the client identifier option (Option code 61), the server will copy the option into its response (DHCP ACK or NAK) per RFC 6842. In other words if the client sends the option it will receive it back. By default, this flag is off and client identifiers will not echoed back to the client. .RE .PP The .I filename statement .RS 0.25i .PP .B filename\fR \fB"\fR\fIfilename\fR\fB";\fR .PP The \fIfilename\fR statement can be used to specify the name of the initial boot file which is to be loaded by a client. The .I filename should be a filename recognizable to whatever file transfer protocol the client can be expected to use to load the file. .RE .PP The .I fixed-address declaration .RS 0.25i .PP .B fixed-address address\fR [\fB,\fR \fIaddress\fR ... ]\fB;\fR .PP The \fIfixed-address\fR declaration is used to assign one or more fixed IP addresses to a client. It should only appear in a \fIhost\fR declaration. If more than one address is supplied, then when the client boots, it will be assigned the address that corresponds to the network on which it is booting. If none of the addresses in the \fIfixed-address\fR statement are valid for the network to which the client is connected, that client will not match the \fIhost\fR declaration containing that \fIfixed-address\fR declaration. Each \fIaddress\fR in the \fIfixed-address\fR declaration should be either an IP address or a domain name that resolves to one or more IP addresses. .RE .PP The .I fixed-address6 declaration .RS 0.25i .PP .B fixed-address6 ip6-address\fR ;\fR .PP The \fIfixed-address6\fR declaration is used to assign a fixed IPv6 addresses to a client. It should only appear in a \fIhost\fR declaration. .RE .PP The .I fixed-prefix6 declaration .RS 0.25i .PP .B fixed-prefix6\fR \fIlow-address\fR \fB/\fR \fIbits\fR\fB;\fR .PP The \fIfixed-prefix6\fR declaration is used to assign a fixed IPv6 prefix to a client. It should only appear in a \fIhost\fR declaration, but multiple \fIfixed-prefix6\fR statements may appear in a single \fIhost\fR declaration. .PP The \fIlow-address\fR specifies the start of the prefix and the \fIbits\fR specifies the size of the prefix in bits. .PP If there are multiple prefixes for a given host entry the server will choose one that matches the requested prefix size or, if none match, the first one. .PP If there are multiple \fIhost\fR declarations the server will try to choose a declaration where the \fIfixed-address6\fR matches the client's subnet. If none match it will choose one that doesn't have a \fIfixed-address6\fR statement. .PP Note Well: Unlike the fixed address the fixed prefix does not need to match a subnet in order to be served. This allows you to provide a prefix to a client that is outside of the subnet on which the client makes the request to the the server. .RE .PP The .I get-lease-hostnames statement .RS 0.25i .PP .B get-lease-hostnames\fR \fIflag\fR\fB;\fR .PP The \fIget-lease-hostnames\fR statement is used to tell dhcpd whether or not to look up the domain name corresponding to the IP address of each address in the lease pool and use that address for the DHCP \fIhostname\fR option. If \fIflag\fR is true, then this lookup is done for all addresses in the current scope. By default, or if \fIflag\fR is false, no lookups are done. .RE .PP The .I hardware statement .RS 0.25i .PP .B hardware \fIhardware-type hardware-address\fB;\fR .PP In order for a BOOTP client to be recognized, its network hardware address must be declared using a \fIhardware\fR clause in the .I host statement. .I hardware-type must be the name of a physical hardware interface type. Currently, only the .B ethernet and .B token-ring types are recognized, although support for a .B fddi hardware type (and others) would also be desirable. The .I hardware-address should be a set of hexadecimal octets (numbers from 0 through ff) separated by colons. The \fIhardware\fR statement may also be used for DHCP clients. .RE .PP The .I host-identifier option statement .RS 0.25i .PP .B host-identifier option \fIoption-name option-data\fB;\fR .PP or .PP .B host-identifier v6relopt \fInumber option-name option-data\fB;\fR .PP This identifies a DHCPv6 client in a .I host statement. .I option-name is any option, and .I option-data is the value for the option that the client will send. The .I option-data must be a constant value. In the v6relopts case the additional number is the relay to examine for the specified option name and value. The values are the same as for the v6relay option. 0 is a no-op, 1 is the relay closest to the client, 2 the next one in and so on. Values that are larger than the maximum number of relays (currently 32) indicate the relay closest to the server independent of number. .RE .PP The .I ignore-client-uids statement .RS 0.25i .PP .B ignore-client-uids \fIflag\fB;\fR .PP If the \fIignore-client-uids\fR statement is present and has a value of \fItrue\fR or \fIon\fR, the UID for clients will not be recorded. If this statement is not present or has a value of \fIfalse\fR or \fIoff\fR, then client UIDs will be recorded. .RE .PP The .I infinite-is-reserved statement .RS 0.25i .PP .B infinite-is-reserved \fIflag\fB;\fR .PP ISC DHCP now supports \'reserved\' leases. See the section on RESERVED LEASES below. If this \fIflag\fR is on, the server will automatically reserve leases allocated to clients which requested an infinite (0xffffffff) lease-time. .PP The default is off. .RE .PP The .I lease-file-name statement .RS 0.25i .PP .B lease-file-name \fIname\fB;\fR .PP .I Name Where \fIname\fR is the name of the DHCP server's lease file. By default, this is DBDIR/dhcpd.leases. This statement \fBmust\fR appear in the outer scope of the configuration file - if it appears in some other scope, it will have no effect. The value must be the absolute path of the file to use. The order of precedence the server uses for the lease file name is: .PP 1. \fBlease-file-name\fR configuration file statement. 2. \fB-lf\fR command line flag. 3. \fBPATH_DHCPD_DB\fR environment variable. .RE .PP The .I dhcpv6-lease-file-name statement .RS 0.25i .PP .B dhcpv6-lease-file-name \fIname\fB;\fR .PP Where \fIname\fR is the name of the DHCP server's lease file when the server is running DHCPv6. By default, this is DBDIR/dhcpd6.leases. This statement \fBmust\fR appear in the outer scope of the configuration file - if it appears in some other scope, it will have no effect. The value must be the absolute path of the file to use. The order of precedence the server uses for the lease file name is: .PP 1. \fBdhcpv6-lease-file-name\fR configuration file statement. 2. \fB-lf\fR command line flag. 3. \fBPATH_DHCPD6_DB\fR environment variable. .RE .PP The .I lease-id-format parameter .RS 0.25i .PP .B lease-id-format \fIformat\fB;\fR .PP The \fIformat\fR parameter must be either \fBoctal\fR or \fBhex\fR. This parameter governs the format used to write certain values to lease files. With the default format, octal, values are written as quoted strings in which non-printable characters are represented as octal escapes - a backslash character followed by three octal digits. When the hex format is specified, values are written as an unquoted series of pairs of hexadecimal digits, separated by colons. Currently, the values written out based on lease-id-format are the server-duid, the uid (DHCPv4 leases), and the IAID_DUID (DHCPv6 leases). Note the server automatically reads the values in either format. .RE .PP The .I limit-addrs-per-ia statement .RS 0.25i .PP .B limit-addrs-per-ia \fInumber\fB;\fR .PP By default, the DHCPv6 server will limit clients to one IAADDR per IA option, meaning one address. If you wish to permit clients to hang onto multiple addresses at a time, configure a larger \fInumber\fR here. .PP Note that there is no present method to configure the server to forcibly configure the client with one IP address per each subnet on a shared network. This is left to future work. .RE .PP The .I local-port statement .RS 0.25i .PP .B local-port \fIport\fB;\fR .PP This statement causes the DHCP server to listen for DHCP requests on the UDP port specified in \fIport\fR, rather than on port 67. .RE .PP The .I local-address statement .RS 0.25i .PP .B local-address \fIaddress\fB;\fR .PP This statement causes the DHCP server to listen for DHCP requests sent to the specified \fIaddress\fR, rather than requests sent to all addresses. Since serving directly attached DHCP clients implies that the server must respond to requests sent to the all-ones IP address, this option cannot be used if clients are on directly attached networks; it is only realistically useful for a server whose only clients are reached via unicasts, such as via DHCP relay agents. .PP Note: This statement is only effective if the server was compiled using the USE_SOCKETS #define statement, which is default on a small number of operating systems, and must be explicitly chosen at compile-time for all others. You can be sure if your server is compiled with USE_SOCKETS if you see lines of this format at startup: .PP Listening on Socket/eth0 .PP Note also that since this bind()s all DHCP sockets to the specified address, that only one address may be supported in a daemon at a given time. .RE .PP The .I local-address6 and .I bind-local-address6 statements .RS 0.25i .PP .B local-address6 \fIaddress\fB;\fR .PP .B bind-local-address6 \fIflag\fB;\fR .PP The \fIlocal-address6\fR statement causes the DHCP server to send IPv6 packets as originating from the specified IPv6 \fIaddress\fR, rather than leaving the kernel to fill in the source address field. .PP When \fIbind-local-address6\fR is present and has a value of true or on, service sockets are bound to \fIaddress\fR too. .PP By default \fIaddress\fR is the undefined address and the \fIbind-local-address6\fR is disabled, both may only be set at the global scope. .RE .PP The .I log-facility statement .RS 0.25i .PP .B log-facility \fIfacility\fB;\fR .PP This statement causes the DHCP server to do all of its logging on the specified log facility once the dhcpd.conf file has been read. By default the DHCP server logs to the daemon facility. Possible log facilities include auth, authpriv, cron, daemon, ftp, kern, lpr, mail, mark, news, ntp, security, syslog, user, uucp, and local0 through local7. Not all of these facilities are available on all systems, and there may be other facilities available on other systems. .PP In addition to setting this value, you may need to modify your .I syslog.conf file to configure logging of the DHCP server. For example, you might add a line like this: .PP .nf local7.debug /var/log/dhcpd.log .fi .PP The syntax of the \fIsyslog.conf\fR file may be different on some operating systems - consult the \fIsyslog.conf\fR manual page to be sure. To get syslog to start logging to the new file, you must first create the file with correct ownership and permissions (usually, the same owner and permissions of your /var/log/messages or /usr/adm/messages file should be fine) and send a SIGHUP to syslogd. Some systems support log rollover using a shell script or program called newsyslog or logrotate, and you may be able to configure this as well so that your log file doesn't grow uncontrollably. .PP Because the \fIlog-facility\fR setting is controlled by the dhcpd.conf file, log messages printed while parsing the dhcpd.conf file or before parsing it are logged to the default log facility. To prevent this, see the README file included with this distribution, which describes BUG: where is that mentioned in README? how to change the default log facility. When this parameter is used, the DHCP server prints its startup message a second time after parsing the configuration file, so that the log will be as complete as possible. .RE .PP The .I log-threshold-high and .I log-threshold-low statements .RS 0.25i .PP .B log-threshold-high \fIpercentage\fB;\fR .PP .B log-threshold-low \fIpercentage\fB;\fR .PP The \fIlog-threshold-low\fR and \fIlog-threshold-high\fR statements are used to control when a message is output about pool usage. The value for both of them is the percentage of the pool in use. If the high threshold is 0 or has not been specified, no messages will be produced. If a high threshold is given, a message is output once the pool usage passes that level. After that, no more messages will be output until the pool usage falls below the low threshold. If the low threshold is not given, it default to a value of zero. .PP A special case occurs when the low threshold is set to be higer than the high threshold. In this case, a message will be generated each time a lease is acknowledged when the pool usage is above the high threshold. .PP Note that threshold logging will be automatically disabled for shared subnets whose total number of addresses is larger than (2^64)-1. The server will emit a log statement at startup when threshold logging is disabled as shown below: "Threshold logging disabled for shared subnet of ranges: " This is likely to have no practical runtime effect as CPUs are unlikely to support a server actually reaching such a large number of leases. .RE .PP The .I max-lease-time statement .RS 0.25i .PP .B max-lease-time \fItime\fR\fB;\fR .PP .I Time should be the maximum length in seconds that will be assigned to a lease. If not defined, the default maximum lease time is 86400. The only exception to this is that Dynamic BOOTP lease lengths, which are not specified by the client, are not limited by this maximum. .RE .PP The .I min-lease-time statement .RS 0.25i .PP .B min-lease-time \fItime\fR\fB;\fR .PP .I Time should be the minimum length in seconds that will be assigned to a lease. The default is the minimum of 300 seconds or \fBmax-lease-time\fR. .RE .PP The .I min-secs statement .RS 0.25i .PP .B min-secs \fIseconds\fR\fB;\fR .PP .I Seconds should be the minimum number of seconds since a client began trying to acquire a new lease before the DHCP server will respond to its request. The number of seconds is based on what the client reports, and the maximum value that the client can report is 255 seconds. Generally, setting this to one will result in the DHCP server not responding to the client's first request, but always responding to its second request. .PP This can be used to set up a secondary DHCP server which never offers an address to a client until the primary server has been given a chance to do so. If the primary server is down, the client will bind to the secondary server, but otherwise clients should always bind to the primary. Note that this does not, by itself, permit a primary server and a secondary server to share a pool of dynamically-allocatable addresses. .RE .PP The .I next-server statement .RS 0.25i .PP .B next-server\fR \fIserver-name\fR\fB;\fR .PP The \fInext-server\fR statement is used to specify the host address of the server from which the initial boot file (specified in the \fIfilename\fR statement) is to be loaded. \fIServer-name\fR should be a numeric IP address or a domain name. .RE .PP The .I omapi-port statement .RS 0.25i .PP .B omapi-port\fR \fIport\fR\fB;\fR .PP The \fIomapi-port\fR statement causes the DHCP server to listen for OMAPI connections on the specified port. This statement is required to enable the OMAPI protocol, which is used to examine and modify the state of the DHCP server as it is running. .RE .PP The .I one-lease-per-client statement .RS 0.25i .PP .B one-lease-per-client \fIflag\fR\fB;\fR .PP If this flag is enabled, whenever a client sends a DHCPREQUEST for a particular lease, the server will automatically free any other leases the client holds. This presumes that when the client sends a DHCPREQUEST, it has forgotten any lease not mentioned in the DHCPREQUEST - i.e., the client has only a single network interface .I and it does not remember leases it's holding on networks to which it is not currently attached. Neither of these assumptions are guaranteed or provable, so we urge caution in the use of this statement. .RE .PP The .I persist-eui-64-leases statement .RS 0.25i .PP .B persist-eui-64-leases \fIflag\fR\fB;\fR .PP When this flag is enabled, the server will write EUI-64 based leases to the leases file. Since such leases can only, ever be valid for a single DUID value it can be argued that writing them to the leases file isn't essential and not doing so may have perfomance advantages. See \fIuse-eui-64\fR statement for more details on EUI-64 based address allocation. The flag is enabled by default and may only be set at the global scope. .RE .PP The .I pid-file-name statement .RS 0.25i .PP .B pid-file-name .I name\fR\fB;\fR .PP .I Name should be the name of the DHCP server's process ID file. This is the file in which the DHCP server's process ID is stored when the server starts. By default, this is RUNDIR/dhcpd.pid. Like the \fIlease-file-name\fR statement, this statement must appear in the outer scope of the configuration file. The order of precedence used by the server is: .PP 1. \fBpid-file-name\fR configuration file statement. 2. \fB-lf\fR command line flag. 3. \fBPATH_DHCPD_PID\fR environment variable. .PP The .I dhcpv6-pid-file-name statement .RS 0.25i .PP .B dhcpv6-pid-file-name \fIname\fB;\fR .PP .I Name is the name of the pid file to use if and only if the server is running in DHCPv6 mode. By default, this is DBDIR/dhcpd6.pid. This statement, like \fIpid-file-name\fr, \fBmust\fR appear in the outer scope of the configuration file. The order of precedence used by the server is: .PP 1. \fBdhcpv6-pid-file-name\fR configuration file statement. 2. \fB-lf\fR command line flag. 3. \fBPATH_DHCPD6_PID\fR environment variable. .PP .RE .PP The .I ping-check statement .RS 0.25i .PP .B ping-check .I flag\fR\fB;\fR .PP When the DHCP server is considering dynamically allocating an IP address to a client, it first sends an ICMP Echo request (a \fIping\fR) to the address being assigned. It waits for a second, and if no ICMP Echo response has been heard, it assigns the address. If a response \fIis\fR heard, the lease is abandoned, and the server does not respond to the client. The lease will remain abandoned for a minimum of abandon-lease-time seconds. .PP If a there are no free addressses but there are abandoned IP addresses, the DHCP server will attempt to reclaim an abandoned IP address regardless of the value of abandon-lease-time. .PP This \fIping check\fR introduces a default one-second delay in responding to DHCPDISCOVER messages, which can be a problem for some clients. The default delay of one second may be configured using the ping-timeout parameter. The ping-check configuration parameter can be used to control checking - if its value is false, no ping check is done. .RE .PP The .I ping-timeout statement .RS 0.25i .PP .B ping-timeout .I seconds\fR\fB;\fR .PP If the DHCP server determined it should send an ICMP echo request (a \fIping\fR) because the ping-check statement is true, ping-timeout allows you to configure how many seconds the DHCP server should wait for an ICMP Echo response to be heard, if no ICMP Echo response has been received before the timeout expires, it assigns the address. If a response \fIis\fR heard, the lease is abandoned, and the server does not respond to the client. If no value is set, ping-timeout defaults to 1 second. .RE .PP The .I preferred-lifetime statement .RS 0.25i .PP .B preferred-lifetime .I seconds\fR\fB;\fR .PP IPv6 addresses have \'valid\' and \'preferred\' lifetimes. The valid lifetime determines at what point at lease might be said to have expired, and is no longer useable. A preferred lifetime is an advisory condition to help applications move off of the address and onto currently valid addresses (should there still be any open TCP sockets or similar). .PP The preferred lifetime defaults to 5/8 the default lease time. .RE .PP The .I prefix-length-mode statement .RS 0.25i .PP .B prefix-length-mode .I mode\fR\fB;\fR .PP According to RFC 3633, DHCPv6 clients may specify preferences when soliciting prefixes by including an IA_PD Prefix option within the IA_PD option. Among the preferences that may be conveyed is the "prefix-length". When non-zero it indicates a client's desired length for offered prefixes. The RFC states that servers "MAY choose to use the information...to select prefix(es)" but does not specify any particular rules for doing so. The prefix-length-mode statement can be used to set the prefix selection rules employed by the server, when clients send a non-zero prefix-length value. The mode parameter must be one of \fBignore\fR, \fBprefer\fR, \fBexact\fR, \fBminimum\fR, or \fBmaximum\fR where: .PP 1. ignore - The requested length is ignored. The server will offer the first available prefix. .PP 2. prefer - The server will offer the first available prefix with the same length as the requested length. If none are found then it will offer the first available prefix of any length. This is the default behavior. .PP 3. exact - The server will offer the first available prefix with the same length as the requested length. If none are found, it will return a status indicating no prefixes available. .PP 4. minimum - The server will offer the first available prefix with the same length as the requested length. If none are found, it will return the first available prefix whose length is greater than (e.g. longer than), the requested value. If none of those are found, it will return a status indicating no prefixes available. For example, if client requests a length of /60, and the server has available prefixes of lengths /56 and /64, it will offer prefix of length /64. .PP 5. maximum - The server will offer the first available prefix with the same length as the requested length. If none are found, it will return the first available prefix whose length is less than (e.g. shorter than), the requested value. If none of those are found, it will return a status indicating no prefixes available. For example, if client requests a length of /60, and the server has available prefixes of lengths /56 and /64, it will offer a prefix of length /56. .PP In general "first available" is determined by the order in which pools are defined in the server's configuration. For example, if a subnet is defined with three prefix pools A,B, and C: .PP .nf subnet 3000::/64 { # pool A pool6 { : } # pool B pool6 { : } # pool C pool6 { : } } .fi .PP then the pools will be checked in the order A, B, C. For modes \fBprefer\fR, \fBminimum\fR, and \fBmaximum\fR this may mean checking the pools in that order twice. A first pass through is made looking for an available prefix of exactly the preferred length. If none are found, then a second pass is performed starting with pool A but with appropriately adjusted length criteria. .RE .PP The .I release-on-roam statement .RS 0.25i .PP .B release-on-roam \fIflag\fB;\fR .PP When enabled and the dhcpd server detects that a DHCPv6 client (IAID+DUID) has roamed to a new network, it will release the pre-existing leases on the old network and emit a log statement similiar to the following: "Client: roamed to new network, releasing lease:
" The server will carry out all of the same steps that would normally occur when a client explicitly releases a lease. When release-on-roam is disabled (the default) the server makes such leases unavailable until they expire or the server is restarted. Clients that need leases in multiple networks must supply a unique IAID in each IA. This parameter may only be specified at the global level. .RE .PP The .I remote-port statement .RS 0.25i .PP .B remote-port \fIport\fB;\fR .PP This statement causes the DHCP server to transmit DHCP responses to DHCP clients upon the UDP port specified in \fIport\fR, rather than on port 68. In the event that the UDP response is transmitted to a DHCP Relay, the server generally uses the \fBlocal-port\fR configuration value. Should the DHCP Relay happen to be addressed as 127.0.0.1, however, the DHCP Server transmits its response to the \fBremote-port\fR configuration value. This is generally only useful for testing purposes, and this configuration value should generally not be used. .RE .PP The .I server-identifier statement .RS 0.25i .PP .B server-identifier \fIhostname\fR\fB;\fR .PP The server-identifier statement can be used to define the value that is sent in the DHCP Server Identifier option for a given scope. The value specified \fBmust\fR be an IP address for the DHCP server, and must be reachable by all clients served by a particular scope. .PP The use of the server-identifier statement is not recommended - the only reason to use it is to force a value other than the default value to be sent on occasions where the default value would be incorrect. The default value is the first IP address associated with the physical network interface on which the request arrived. .PP The usual case where the \fIserver-identifier\fR statement needs to be sent is when a physical interface has more than one IP address, and the one being sent by default isn't appropriate for some or all clients served by that interface. Another common case is when an alias is defined for the purpose of having a consistent IP address for the DHCP server, and it is desired that the clients use this IP address when contacting the server. .PP Supplying a value for the dhcp-server-identifier option is equivalent to using the server-identifier statement. .RE .PP The .I server-id-check statement .RS 0.25i .PP .B server-id-check \fIflag\fR\fB;\fR .PP The server-id-check statement is used to control whether or not a server, participating in failover, verifies that the value of the dhcp-server-identifier option in received DHCP REQUESTs match the server's id before processing the request. Server id checking is disabled by default. Setting this flag enables id checking and thereafter the server will only process requests that match. Note the flag setting should be consistent between failover partners. .PP Unless overridden by use of the server-identifier statement, the value the server uses as its id will be the first IP address associated with the physical network interface on which the request arrived. .PP In order to reduce runtime overhead the server only checks for a server id option in the global and subnet scopes. Complicated configurations may result in different server ids for this check and when the server id for a reply packet is determined, which would prohibit the server from responding. .PP The primary use for this option is when a client broadcasts a request but requires that the response come from a specific failover peer. An example of this would be when a client reboots while its lease is still active - in this case both servers will normally respond. Most of the time the client won't check the server id and can use either of the responses. However if the client does check the server id it may reject the response if it came from the wrong peer. If the timing is such that the "wrong" peer responds first most of the time the client may not get an address for some time. .PP Care should be taken before enabling this option. .PP .RE .PP The .I server-duid statement .RS 0.25i .PP .B server-duid \fILLT\fR [ \fIhardware-type\fR \fItimestamp\fR \fIhardware-address\fR ] \fB;\fR .B server-duid \fIEN\fR \fIenterprise-number\fR \fIenterprise-identifier\fR \fB;\fR .B server-duid \fILL\fR [ \fIhardware-type\fR \fIhardware-address\fR ] \fB;\fR .PP The server-duid statement configures the server DUID. You may pick either LLT (link local address plus time), EN (enterprise), or LL (link local). .PP If you choose LLT or LL, you may specify the exact contents of the DUID. Otherwise the server will generate a DUID of the specified type. .PP If you choose EN, you must include the enterprise number and the enterprise-identifier. .PP If there is a server-duid statement in the lease file it will take precedence over the server-duid statement from the config file and a dhcp6.server-id option in the config file will override both. .PP The default server-duid type is LLT. .RE .PP The .I server-name statement .RS 0.25i .PP .B server-name "\fIname\fB";\fR .PP The \fIserver-name\fR statement can be used to inform the client of the name of the server from which it is booting. \fIName\fR should be the name that will be provided to the client. .RE .PP The .I dhcpv6-set-tee-times statement .RS 0.25i .PP .B dhcpv6-set-tee-times\fR \fIflag\fR\fB;\fR .PP The \fIdhcpv6-set-tee-times\fR statement enables setting T1 and T2 to the values recommended in RFC 3315 (Section 22.4). When setting T1 and T2, the server will use dhcp-renewal-time and dhcp-rebinding-time, respectively. A value of zero tells the client it may choose its own value. When those options are not defined then values will be set to zero unless the global \fIdhcpv6-set-tee-times\fR is enabled. When this option is enabled the times are calculated as recommended by RFC 3315, Section 22.4: T1 will be set to 0.5 times the shortest preferred lifetime in the reply. If the "shortest" preferred lifetime is 0xFFFFFFFF, T1 will set to 0xFFFFFFFF. T2 will be set to 0.8 times the shortest preferred lifetime in the reply. If the "shortest" preferred lifetime is 0xFFFFFFFF, T2 will set to 0xFFFFFFFF. Keep in mind that given sufficiently small lease lifetimes, the above calculations will result in the two values being equal. For example, a 9 second lease lifetime would yield T1 = T2 = 4 seconds, which would cause clients to issue rebinds only. In such a case it would likely be better to explicitly define the values. Note that dhcpv6-set-tee-times is intended to be transitional and will likely be removed in a future release. Once removed the behavior will be to use the configured values when present or calculate them per the RFC. If you want zeros, define them as zeros. .RE .PP The .I site-option-space statement .RS 0.25i .PP .B site-option-space "\fIname\fB";\fR .PP The \fIsite-option-space\fR statement can be used to determine from what option space site-local options will be taken. This can be used in much the same way as the \fIvendor-option-space\fR statement. Site-local options in DHCP are those options whose numeric codes are greater than 224. These options are intended for site-specific uses, but are frequently used by vendors of embedded hardware that contains DHCP clients. Because site-specific options are allocated on an ad hoc basis, it is quite possible that one vendor's DHCP client might use the same option code that another vendor's client uses, for different purposes. The \fIsite-option-space\fR option can be used to assign a different set of site-specific options for each such vendor, using conditional evaluation (see \fBdhcp-eval (5)\fR for details). .RE .PP The .I stash-agent-options statement .RS 0.25i .PP .B stash-agent-options \fIflag\fB;\fR .PP If the \fIstash-agent-options\fR parameter is true for a given client, the server will record the relay agent information options sent during the client's initial DHCPREQUEST message when the client was in the SELECTING state and behave as if those options are included in all subsequent DHCPREQUEST messages sent in the RENEWING state. This works around a problem with relay agent information options, which is that they usually not appear in DHCPREQUEST messages sent by the client in the RENEWING state, because such messages are unicast directly to the server and not sent through a relay agent. .RE .PP The .I update-conflict-detection statement .RS 0.25i .PP .B update-conflict-detection \fIflag\fB;\fR .PP If the \fIupdate-conflict-detection\fR parameter is true, the server will perform standard DHCID multiple-client, one-name conflict detection. If the parameter has been set false, the server will skip this check and instead simply tear down any previous bindings to install the new binding without question. The default is true and this parameter may only be specified at the global scope. .RE .PP The .I update-optimization statement .RS 0.25i .PP .B update-optimization \fIflag\fB;\fR .PP If the \fIupdate-optimization\fR parameter is false for a given client, the server will attempt a DNS update for that client each time the client renews its lease, rather than only attempting an update when it appears to be necessary. This will allow the DNS to heal from database inconsistencies more easily, but the cost is that the DHCP server must do many more DNS updates. We recommend leaving this option enabled, which is the default. If this parameter is not specified, or is true, the DHCP server will only update when the client information changes, the client gets a different lease, or the client's lease expires. .RE .PP The .I update-static-leases statement .RS 0.25i .PP .B update-static-leases \fIflag\fB;\fR .PP The \fIupdate-static-leases\fR flag, if enabled, causes the DHCP server to do DNS updates for clients even if those clients are being assigned their IP address using a \fIfixed-address\fR or \fIfixed-address6\fR statement - that is, the client is being given a static assignment. It is not recommended because the DHCP server has no way to tell that the update has been done, and therefore will not delete the record when it is not in use. Also, the server must attempt the update each time the client renews its lease, which could have a significant performance impact in environments that place heavy demands on the DHCP server. This feature is supported for both DHCPv4 and DHCPv6, and update modes standard or interim. It is disabled by default. .RE .PP The .I use-eui-64 statement .RS 0.25i .PP .B use-eui-64 \fIflag\fB;\fR .PP (Support for this must be enabled at compile time, see EUI_64 in includes/site.h) The \fIuse-eui-64\fR flag, if enabled, instructs the server to construct an address using the client's EUI-64 DUID (Type 3, HW Type EUI-64), rather than creating an address using the dynamic algorithm. This means that a given DUID will always generate the same address for a given pool and further that the address is guaranteed to be unique to that DUID. The IPv6 address will be calculated from the EUI-64 link layer address, conforming to RFC 2373, unless there is a host declaration for the client-id. The range6 statement for EUI-64 must define full /64 bit ranges. Invalid ranges will be flagged during configuration parsing as errors. See the following example: subnet6 fc00:e4::/64 { use-eui-64 true; range6 fc00:e4::/64; } The statement may be specified down to the pool level, allowing a mixture of dynamic and EUI-64 based pools. During lease file parsing, any leases which map to an EUI-64 pool, that have a non-EUI-64 DUID or for which the lease address is not the EUI-64 address for that DUID in that pool, will be discarded. If a host declaration exists for the DUID, the server grants the address (fixed-prefix6, fixed-address6) according to the host declaration, regardless of the DUID type of the client (even for EUI-64 DUIDs). If a client request's an EUI-64 lease for a given network, and the resultant address conflicts with a fixed address reservation, the server will send the client a "no addresses available" response. Any client with a non-conforming DUID (not type 3 or not hw type EUI-64) that is not linked to a host declaration, which requests an address from an EUI-64 enabled pool will be ignored and the event will be logged. Pools that are configured for EUI-64 will be skipped for dynamic allocation. If there are no pools in the shared network from which to allocate, the client will get back a no addresses available status. On an EUI-64 enabled pool, any client with a DUID 3, HW Type EUI-64, requesting a solicit/renew and including IA_NA that do not match the EUI-64 policy, they will be treated as though they are "outside" the subnet for a given client message: Solicit - Server will advertise with EUI-64 ia suboption, but with rapid commit off Request - Server will send "an address not on link status", and no ia suboption Renew/Rebind - Server will send the requested address ia suboption with lifetimes of 0, plus an EUI-64 ia Whether or not EUI-64 based leases are written out to the lease database may be controlled by \fIpersist-eui-64-leases\fR statement. .RE .PP The .I use-host-decl-names statement .RS 0.25i .PP .B use-host-decl-names \fIflag\fB;\fR .PP If the \fIuse-host-decl-names\fR parameter is true in a given scope, then for every host declaration within that scope, the name provided for the host declaration will be supplied to the client as its hostname. So, for example, .PP .nf group { use-host-decl-names on; host joe { hardware ethernet 08:00:2b:4c:29:32; fixed-address joe.example.com; } } is equivalent to host joe { hardware ethernet 08:00:2b:4c:29:32; fixed-address joe.example.com; option host-name "joe"; } .fi .PP Additionally, enabling use-host-decl-names instructs the server to use the host declaration name in the the forward DNS name, if no other values are available. This value selection process is discussed in more detail under DNS updates. .PP An \fIoption host-name\fR statement within a host declaration will override the use of the name in the host declaration. .PP It should be noted here that most DHCP clients completely ignore the host-name option sent by the DHCP server, and there is no way to configure them not to do this. So you generally have a choice of either not having any hostname to client IP address mapping that the client will recognize, or doing DNS updates. It is beyond the scope of this document to describe how to make this determination. .RE .PP The .I use-lease-addr-for-default-route statement .RS 0.25i .PP .B use-lease-addr-for-default-route \fIflag\fR\fB;\fR .PP If the \fIuse-lease-addr-for-default-route\fR parameter is true in a given scope, then instead of sending the value specified in the routers option (or sending no value at all), the IP address of the lease being assigned is sent to the client. This supposedly causes Win95 machines to ARP for all IP addresses, which can be helpful if your router is configured for proxy ARP. The use of this feature is not recommended, because it won't work for many DHCP clients. .RE .PP The .I vendor-option-space statement .RS 0.25i .PP .B vendor-option-space \fIstring\fR\fB;\fR .PP The \fIvendor-option-space\fR parameter determines from what option space vendor options are taken. The use of this configuration parameter is illustrated in the \fBdhcp-options(5)\fR manual page, in the \fIVENDOR ENCAPSULATED OPTIONS\fR section. .RE .SH SETTING PARAMETER VALUES USING EXPRESSIONS Sometimes it's helpful to be able to set the value of a DHCP server parameter based on some value that the client has sent. To do this, you can use expression evaluation. The .B dhcp-eval(5) manual page describes how to write expressions. To assign the result of an evaluation to an option, define the option as follows: .nf .sp 1 \fImy-parameter \fB= \fIexpression \fB;\fR .fi .PP For example: .nf .sp 1 ddns-hostname = binary-to-ascii (16, 8, "-", substring (hardware, 1, 6)); .fi .RE .SH RESERVED LEASES It's often useful to allocate a single address to a single client, in approximate perpetuity. Host statements with \fBfixed-address\fR clauses exist to a certain extent to serve this purpose, but because host statements are intended to approximate \'static configuration\', they suffer from not being referenced in a littany of other Server Services, such as dynamic DNS, failover, \'on events\' and so forth. .PP If a standard dynamic lease, as from any range statement, is marked \'reserved\', then the server will only allocate this lease to the client it is identified by (be that by client identifier or hardware address). .PP In practice, this means that the lease follows the normal state engine, enters ACTIVE state when the client is bound to it, expires, or is released, and any events or services that would normally be supplied during these events are processed normally, as with any other dynamic lease. The only difference is that failover servers treat reserved leases as special when they enter the FREE or BACKUP states - each server applies the lease into the state it may allocate from - and the leases are not placed on the queue for allocation to other clients. Instead they may only be \'found\' by client identity. The result is that the lease is only offered to the returning client. .PP Care should probably be taken to ensure that the client only has one lease within a given subnet that it is identified by. .PP Leases may be set \'reserved\' either through OMAPI, or through the \'infinite-is-reserved\' configuration option (if this is applicable to your environment and mixture of clients). .PP It should also be noted that leases marked \'reserved\' are effectively treated the same as leases marked \'bootp\'. .RE .SH REFERENCE: OPTION STATEMENTS DHCP option statements are documented in the .B dhcp-options(5) manual page. .SH REFERENCE: EXPRESSIONS Expressions used in DHCP option statements and elsewhere are documented in the .B dhcp-eval(5) manual page. .SH SEE ALSO dhcpd(8), dhcpd.leases(5), dhcp-options(5), dhcp-eval(5), RFC2132, RFC2131. .SH AUTHOR .B dhcpd.conf(5) is maintained by ISC. Information about Internet Systems Consortium can be found at .B https://www.isc.org. dhcp-4.4.1/server/dhcpd.conf.example000644 000765 000024 00000006302 13243301226 017605 0ustar00tmarkstaff000000 000000 # dhcpd.conf # # Sample configuration file for ISC dhcpd # # option definitions common to all supported networks... option domain-name "example.org"; option domain-name-servers ns1.example.org, ns2.example.org; default-lease-time 600; max-lease-time 7200; # Use this to enble / disable dynamic dns updates globally. #ddns-update-style none; # If this DHCP server is the official DHCP server for the local # network, the authoritative directive should be uncommented. #authoritative; # Use this to send dhcp log messages to a different log file (you also # have to hack syslog.conf to complete the redirection). log-facility local7; # No service will be given on this subnet, but declaring it helps the # DHCP server to understand the network topology. subnet 10.152.187.0 netmask 255.255.255.0 { } # This is a very basic subnet declaration. subnet 10.254.239.0 netmask 255.255.255.224 { range 10.254.239.10 10.254.239.20; option routers rtr-239-0-1.example.org, rtr-239-0-2.example.org; } # This declaration allows BOOTP clients to get dynamic addresses, # which we don't really recommend. subnet 10.254.239.32 netmask 255.255.255.224 { range dynamic-bootp 10.254.239.40 10.254.239.60; option broadcast-address 10.254.239.31; option routers rtr-239-32-1.example.org; } # A slightly different configuration for an internal subnet. subnet 10.5.5.0 netmask 255.255.255.224 { range 10.5.5.26 10.5.5.30; option domain-name-servers ns1.internal.example.org; option domain-name "internal.example.org"; option routers 10.5.5.1; option broadcast-address 10.5.5.31; default-lease-time 600; max-lease-time 7200; } # Hosts which require special configuration options can be listed in # host statements. If no address is specified, the address will be # allocated dynamically (if possible), but the host-specific information # will still come from the host declaration. host passacaglia { hardware ethernet 0:0:c0:5d:bd:95; filename "vmunix.passacaglia"; server-name "toccata.example.com"; } # Fixed IP addresses can also be specified for hosts. These addresses # should not also be listed as being available for dynamic assignment. # Hosts for which fixed IP addresses have been specified can boot using # BOOTP or DHCP. Hosts for which no fixed address is specified can only # be booted with DHCP, unless there is an address range on the subnet # to which a BOOTP client is connected which has the dynamic-bootp flag # set. host fantasia { hardware ethernet 08:00:07:26:c0:a5; fixed-address fantasia.example.com; } # You can declare a class of clients and then do address allocation # based on that. The example below shows a case where all clients # in a certain class get addresses on the 10.17.224/24 subnet, and all # other clients get addresses on the 10.0.29/24 subnet. class "foo" { match if substring (option vendor-class-identifier, 0, 4) = "SUNW"; } shared-network 224-29 { subnet 10.17.224.0 netmask 255.255.255.0 { option routers rtr-224.example.org; } subnet 10.0.29.0 netmask 255.255.255.0 { option routers rtr-29.example.org; } pool { allow members of "foo"; range 10.17.224.10 10.17.224.250; } pool { deny members of "foo"; range 10.0.29.10 10.0.29.230; } } dhcp-4.4.1/server/dhcpd.leases.5000644 000765 000024 00000040367 13243301226 016656 0ustar00tmarkstaff000000 000000 .\" dhcpd.leases.5 .\" .\" Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") .\" Copyright (c) 1996-2003 by Internet Software Consortium .\" .\" 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/. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT .\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" Internet Systems Consortium, Inc. .\" 950 Charter Street .\" Redwood City, CA 94063 .\" .\" https://www.isc.org/ .\" .\" $Id: dhcpd.leases.5,v 1.17 2011/09/19 00:24:50 sar Exp $ .\" .TH dhcpd.leases 5 .SH NAME dhcpd.leases - DHCP client lease database .SH DESCRIPTION The Internet Systems Consortium DHCP Server keeps a persistent database of leases that it has assigned. This database is a free-form ASCII file containing a series of lease declarations. Every time a lease is acquired, renewed or released, its new value is recorded at the end of the lease file. So if more than one declaration appears for a given lease, the last one in the file is the current one. .PP When dhcpd is first installed, there is no lease database. However, dhcpd requires that a lease database be present before it will start. To make the initial lease database, just create an empty file called DBDIR/dhcpd.leases. You can do this with: .PP .nf touch DBDIR/dhcpd.leases .fi .PP In order to prevent the lease database from growing without bound, the file is rewritten from time to time. First, a temporary lease database is created and all known leases are dumped to it. Then, the old lease database is renamed DBDIR/dhcpd.leases~. Finally, the newly written lease database is moved into place. .PP In order to process both DHCPv4 and DHCPv6 messages you will need to run two separate instances of the dhcpd process. Each of these instances will need it's own lease file. You can use the \fI-lf\fR option on the server's command line to specify a different lease file name for one or both servers. .SH FORMAT Lease descriptions are stored in a format that is parsed by the same recursive descent parser used to read the .B dhcpd.conf(5) and .B dhclient.conf(5) files. Lease files can contain lease declarations, and also group and subgroup declarations, host declarations and failover state declarations. Group, subgroup and host declarations are used to record objects created using the OMAPI protocol. .PP The lease file is a log-structured file - whenever a lease changes, the contents of that lease are written to the end of the file. This means that it is entirely possible and quite reasonable for there to be two or more declarations of the same lease in the lease file at the same time. In that case, the instance of that particular lease that appears last in the file is the one that is in effect. .PP Group, subgroup and host declarations in the lease file are handled in the same manner, except that if any of these objects are deleted, a \fIrubout\fR is written to the lease file. This is just the same declaration, with \fB{ deleted; }\fR in the scope of the declaration. When the lease file is rewritten, any such rubouts that can be eliminated are eliminated. It is possible to delete a declaration in the \fBdhcpd.conf\fR file; in this case, the rubout can never be eliminated from the \fBdhcpd.leases\fR file. .SH COMMON STATEMENTS FOR LEASE DECLARATIONS While the lease file formats for DHCPv4 and DHCPv6 are different they share many common statements and structures. This section describes the common statements while the succeeding sections describe the protocol specific statements. .PP .B Dates .PP A \fIdate\fR is specified in two ways, depending on the configuration value for the \fBdb-time-format\fR parameter. If it was set to \fIdefault\fR, then the \fIdate\fR fields appear as follows: .PP .I weekday year\fB/\fImonth\fB/\fIday hour\fB:\fIminute\fB:\fIsecond\fR .PP The weekday is present to make it easy for a human to tell when a lease expires - it's specified as a number from zero to six, with zero being Sunday. The day of week is ignored on input. The year is specified with the century, so it should generally be four digits except for really long leases. The month is specified as a number starting with 1 for January. The day of the month is likewise specified starting with 1. The hour is a number between 0 and 23, the minute a number between 0 and 59, and the second also a number between 0 and 59. .PP Lease times are specified in Universal Coordinated Time (UTC), not in the local time zone. There is probably nowhere in the world where the times recorded on a lease are always the same as wall clock times. On most unix machines, you can display the current time in UTC by typing \fBdate -u\fR. .PP If the \fBdb-time-format\fR was configured to \fIlocal\fR, then the \fIdate\fR fields appear as follows: .PP \fBepoch\fR \fI\fR\fB; #\fR \fI \fR\fB:\fR\fI\fR\fB:\fR\fI \fR .PP The \fIseconds-since-epoch\fR is as according to the system's local clock (often referred to as "unix time"). The \fB#\fR symbol supplies a comment that describes what actual time this is as according to the system's configured timezone, at the time the value was written. It is provided only for human inspection. .PP If a lease will never expire, \fIdate\fR is \fBnever\fR instead of an actual date. .PP .B General Variables .PP As part of the processing of a lease information may be attached to the lease structure, for example the DDNS information or if you specify a variable in your configuration file. Some of these, like the DDNS information, have specific descriptions below. For others, such as any you might define, a generic line of the following will be included. .PP .B set \fIvariable\fB = \fIvalue\fB; .PP The \fBset\fR statement sets the value of a variable on the lease. For general information on variables, see the \fBdhcp-eval(5)\fR manual page. .PP .B DDNS Variables .PP .nf .B The \fIddns-text\fB and \fIddns-dhcid\fB variables .PP These variables are used to record the value of the client's identification record when the server has updated DNS for a particular lease. The text record is used with the interim DDNS update style while the dhcid record is used for the standard DDNS update style. .PP .B The \fIddns-fwd-name\fB variable .PP This variable records the value of the name used in updating the client's A record if a DDNS update has been successfully done by the server. The server may also have used this name to update the client's PTR record. .PP .B The \fIddns-client-fqdn\fB variable .PP If the server is configured both to use the interim or standard DDNS update style, and to allow clients to update their own FQDNs, then if the client did in fact update its own FQDN, the \fIddns-client-fqdn\fR variable records the name that the client has indicated it is using. This is the name that the server will have used to update the client's PTR record in this case. .PP .B The \fIddns-rev-name\fB variable .PP If the server successfully updates the client's PTR record, this variable will record the name that the DHCP server used for the PTR record. The name to which the PTR record points will be either the \fIddns-fwd-name\fR or the \fIddns-client-fqdn\fR. .PP .B Executable Statements .PP .B on \fIevents\fB { \fIstatements...\fB } The \fBon\fR statement records a list of statements to execute if a certain event occurs. The possible events that can occur for an active lease are \fBrelease\fR and \fBexpiry\fR. More than one event can be specified - if so, the events are separated by '|' characters. .PP The \fIauthoring-byte-order\fR statement .RS 0.25i .PP .B authoring-byte-order \fR[ \fIbig-endian\fR | \fIlittle-endian\fR ] \fB;\fR .PP This statement is automatically added to the top of new lease files by the server. It indicates the internal byte order of the server. This permits lease files generated on a server with one form of byte order to be read by a server with a different form. Lease files which do not contain this entry are simply treated as having the same byte order as the server reading them. If you are migrating lease files generated by a server that predates this statement and is of a different byte order than the your destination server, you can manually add this statement. It must proceed any lease entries. Valid values for this parameter are \fIlittle-endian\fR and \fIbig-endian\fR. .RE .PP .SH THE DHCPv4 LEASE DECLARATION .PP .B lease \fIip-address\fB { \fIstatements...\fB } .PP Each lease declaration includes the single IP address that has been leased to the client. The statements within the braces define the duration of the lease and to whom it is assigned. .PP .nf .B starts \fIdate\fB;\fR .B ends \fIdate\fB;\fR .B tstp \fIdate\fB;\fR .B tsfp \fIdate\fB;\fR .B atsfp \fIdate\fB;\fR .B cltt \fIdate\fB;\fR .fi .PP The start and end time of a lease are recorded using the \fBstarts\fR and \fBends\fR statements. The \fBtstp\fR statement is present if the failover protocol is being used, and indicates what time the peer has been told the lease expires. The \fBtsfp\fR statement is also present if the failover protocol is being used, and indicates the lease expiry time that the peer has acknowledged. The \fBatsfp\fR statement is the actual time sent from the failover partner. The \fBcltt\fR statement is the client's last transaction time. .PP See the description of dates in the section on common structures. .PP .B hardware \fIhardware-type mac-address\fB;\fR .PP The hardware statement records the MAC address of the network interface on which the lease will be used. It is specified as a series of hexadecimal octets, separated by colons. .PP .B uid \fIclient-identifier\fB;\fR .PP The \fBuid\fR statement records the client identifier used by the client to acquire the lease. Clients are not required to send client identifiers, and this statement only appears if the client did in fact send one. Client identifiers are normally an ARP type (1 for ethernet) followed by the MAC address, just like in the \fBhardware\fI statement, but this is not required. .PP The client identifier is recorded as a colon-separated hexadecimal list or as a quoted string. If it is recorded as a quoted string and it contains one or more non-printable characters, those characters are represented as octal escapes - a backslash character followed by three octal digits. The format used is determined by the lease-id-format parameter, which defaults to octal. .PP .B client-hostname "\fIhostname\fB";\fR .PP Most DHCP clients will send their hostname in the \fIhost-name\fR option. If a client sends its hostname in this way, the hostname is recorded on the lease with a \fBclient-hostname\fR statement. This is not required by the protocol, however, so many specialized DHCP clients do not send a host-name option. .PP .nf .B binding state \fIstate\fB; .B next binding state \fIstate\fB; .fi .PP The \fBbinding state\fR statement declares the lease's binding state. When the DHCP server is not configured to use the failover protocol, a lease's binding state may be \fBactive\fR, \fBfree\fR or \fBabandoned\fR. The failover protocol adds some additional transitional states, as well as the \fBbackup\fR state, which indicates that the lease is available for allocation by the failover secondary. Please see the \fBdhcpd.conf(5)\fR manual page for more information about abandoned leases. .PP The \fBnext binding state\fR statement indicates what state the lease will move to when the current state expires. The time when the current state expires is specified in the \fIends\fR statement. .PP .B rewind binding state \fIstate\fB; .PP This statement is part of an optimization for use with failover. This helps a server rewind a lease to the state most recently transmitted to its peer. .PP .nf .B option agent.circuit-id \fIstring\fR; .B option agent.remote-id \fIstring\fR; .fi .PP These statements are used to record the circuit ID and remote ID options sent by the relay agent, if the relay agent uses the \fIrelay agent information option\fR. This allows these options to be used consistently in conditional evaluations even when the client is contacting the server directly rather than through its relay agent. .PP .B The \fIvendor-class-identifier\fB variable .PP The server retains the client-supplied Vendor Class Identifier option for informational purposes, and to render them in DHCPLEASEQUERY responses. .PP .nf .B bootp; .B reserved; .fi .PP If present, they indicate that the BOOTP and RESERVED failover flags (respectively) should be set. BOOTP and RESERVED dynamic leases are treated differently than normal dynamic leases, as they may only be used by the client to which they are currently allocated. .PP .B Other Additional options or executable statements may be included, see the description of them in the section on common structures. .RE .PP .SH THE DHCPv6 LEASE (IA) DECLARATION .PP .nf .B ia_ta \fI IAID_DUID\fB { \fIstatements...\fB } .B ia_na \fI IAID_DUID\fB { \fIstatements...\fB } .B ia_pd \fI IAID_DUID\fB { \fIstatements...\fB } .fi .PP Each lease declaration starts with a tag indicating the type of the lease. ia_ta is for temporary addresses, ia_na is for non-temporary addresses and ia_pd is for prefix delegation. Following this tag is the combined IAID and DUID from the client for this lease. .PP The IAID_DUID value is recorded as a colon-separated hexadecimal list or as a quoted string. If it is recorded as a quoted string and it contains one or more non-printable characters, those characters are represented as octal escapes - a backslash character followed by three octal digits. The format used is governed by the lease-id-format parameter, which defaults to octal. .PP .B cltt \fIdate\fB;\fR .PP The \fBcltt\fR statement is the client's last transaction time. .PP See the description of dates in the section on common structures. .PP .nf .B iaaddr \fIipv6-address\fB { \fIstatements...\fB } .B iaprefix \fIipv6-address/prefix-length\fB { \fIstatements...\fB } .PP Within a given lease there can be multiple iaaddr and iaprefix statements. Each will have either an IPv6 address or an IPv6 prefix (an address and a prefix length indicating a CIDR style block of addresses). The following statements may occur Within each iaaddr or iaprefix. .PP .B binding state \fIstate\fB; .PP The \fBbinding state\fR statement declares the lease's binding state. In DHCPv6 you will normally see this as \fIactive\fR or \fIexpired\fR. .PP .B preferred-life \fIlifetime\fB; .PP The IPv6 preferred lifetime associated with this address, in seconds. .PP .B max-life \fIlifetime\fB; .PP The valid lifetime associated with this address, in seconds. .PP .B ends \fIdate\fB;\fR .PP The end time of the lease. See the description of dates in the section on common structures. .PP Additional options or executable statements may be included. See the description of them in the section on common structures. .PP .RE .SH THE FAILOVER PEER STATE DECLARATION The state of any failover peering arrangements is also recorded in the lease file, using the \fBfailover peer\fR statement: .PP .nf .B failover peer "\fIname\fB" state { .B my state \fIstate\fB at \fIdate\fB; .B peer state \fIstate\fB at \fIdate\fB; .B } .fi .PP The states of the peer named \fIname\fR is being recorded. Both the state of the running server (\fBmy state\fR) and the other failover partner (\fIpeer state\fR) are recorded. The following states are possible: \fBunknown-state\fR, \fBpartner-down\fR, \fBnormal\fR, \fBcommunications-interrupted\fR, \fBresolution-interrupted\fR, \fBpotential-conflict\fR, \fBrecover\fR, \fBrecover-done\fR, \fBshutdown\fR, \fBpaused\fR, and \fBstartup\fR. .RE .SH FILES .B DBDIR/dhcpd.leases DBDIR/dhcpd.leases~ .SH SEE ALSO dhcpd(8), dhcp-options(5), dhcp-eval(5), dhcpd.conf(5), RFC2132, RFC2131. .SH AUTHOR .B dhcpd(8) is maintained by ISC. Information about Internet Systems Consortium can be found at: .B https://www.isc.org/ dhcp-4.4.1/server/dhcpleasequery.c000644 000765 000024 00000076206 13243301226 017416 0ustar00tmarkstaff000000 000000 /* * Copyright (C) 2006-2017 by 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include "dhcpd.h" /* * TODO: RFC4388 specifies that the server SHOULD return the same * options it would for a DHCREQUEST message, if no Parameter * Request List option (option 55) is passed. We do not do that. * * TODO: RFC4388 specifies the creation of a "non-sensitive options" * configuration list, and that these SHOULD be returned. We * have no such list. * * TODO: RFC4388 says the server SHOULD use RFC3118, "Authentication * for DHCP Messages". * * TODO: RFC4388 specifies that you SHOULD insure that you cannot be * DoS'ed by DHCPLEASEQUERY message. */ /* * If you query by hardware address or by client ID, then you may have * more than one IP address for your query argument. We need to do two * things: * * 1. Find the most recent lease. * 2. Find all additional IP addresses for the query argument. * * We do this by looking through all of the leases associated with a * given hardware address or client ID. We use the cltt (client last * transaction time) of the lease, which only has a resolution of one * second, so we might not actually give the very latest IP. */ static struct lease* next_hw(const struct lease *lease) { /* INSIST(lease != NULL); */ return lease->n_hw; } static struct lease* next_uid(const struct lease *lease) { /* INSIST(lease != NULL); */ return lease->n_uid; } void get_newest_lease(struct lease **retval, struct lease *lease, struct lease *(*next)(const struct lease *)) { struct lease *p; struct lease *newest; /* INSIST(newest != NULL); */ /* INSIST(next != NULL); */ *retval = NULL; if (lease == NULL) { return; } newest = lease; for (p=next(lease); p != NULL; p=next(p)) { if (newest->binding_state == FTS_ACTIVE) { if ((p->binding_state == FTS_ACTIVE) && (p->cltt > newest->cltt)) { newest = p; } } else { if (p->ends > newest->ends) { newest = p; } } } lease_reference(retval, newest, MDL); } static int get_associated_ips(const struct lease *lease, struct lease *(*next)(const struct lease *), const struct lease *newest, u_int32_t *associated_ips, unsigned int associated_ips_size) { const struct lease *p; int cnt; /* INSIST(next != NULL); */ /* INSIST(associated_ips != NULL); */ if (lease == NULL) { return 0; } cnt = 0; for (p=lease; p != NULL; p=next(p)) { if ((p->binding_state == FTS_ACTIVE) && (p != newest)) { if (cnt < associated_ips_size) { memcpy(&associated_ips[cnt], p->ip_addr.iabuf, sizeof(associated_ips[cnt])); } cnt++; } } return cnt; } void dhcpleasequery(struct packet *packet, int ms_nulltp) { char msgbuf[256]; char dbg_info[128]; struct iaddr cip; struct iaddr gip; struct data_string uid; struct hardware h; struct lease *tmp_lease; struct lease *lease; int want_associated_ip; int assoc_ip_cnt; u_int32_t assoc_ips[40]; /* XXXSK: arbitrary maximum number of IPs */ const int nassoc_ips = sizeof(assoc_ips) / sizeof(assoc_ips[0]); unsigned char dhcpMsgType; const char *dhcp_msg_type_name; struct subnet *subnet; struct group *relay_group; struct option_state *options; struct option_cache *oc; int allow_leasequery; int ignorep; u_int32_t lease_duration; u_int32_t time_renewal; u_int32_t time_rebinding; u_int32_t time_expiry; u_int32_t client_last_transaction_time; #if defined(RELAY_PORT) u_int16_t relay_port = 0; #endif struct sockaddr_in to; struct in_addr siaddr; struct data_string prl; struct data_string *prl_ptr; int i; struct interface_info *interface; /* INSIST(packet != NULL); */ /* * Prepare log information. */ snprintf(msgbuf, sizeof(msgbuf), "DHCPLEASEQUERY from %s", inet_ntoa(packet->raw->giaddr)); /* * We can't reply if there is no giaddr field. */ /* * Note: this makes DHCPv4-over-DHCPv6 always fail but it should not * really be a problem because it is not a specified use case * (or even one that makes sense). */ if (!packet->raw->giaddr.s_addr) { log_info("%s: missing giaddr, ciaddr is %s, no reply sent", msgbuf, inet_ntoa(packet->raw->ciaddr)); return; } /* * Initially we use the 'giaddr' subnet options scope to determine if * the giaddr-identified relay agent is permitted to perform a * leasequery. The subnet is not required, and may be omitted, in * which case we are essentially interrogating the root options class * to find a globally permit. */ gip.len = sizeof(packet->raw->giaddr); memcpy(gip.iabuf, &packet->raw->giaddr, sizeof(packet->raw->giaddr)); subnet = NULL; find_subnet(&subnet, gip, MDL); if (subnet != NULL) relay_group = subnet->group; else relay_group = root_group; subnet_dereference(&subnet, MDL); options = NULL; if (!option_state_allocate(&options, MDL)) { log_error("No memory for option state."); log_info("%s: out of memory, no reply sent", msgbuf); return; } execute_statements_in_scope(NULL, packet, NULL, NULL, packet->options, options, &global_scope, relay_group, NULL, NULL); for (i=packet->class_count-1; i>=0; i--) { execute_statements_in_scope(NULL, packet, NULL, NULL, packet->options, options, &global_scope, packet->classes[i]->group, relay_group, NULL); } /* * Because LEASEQUERY has some privacy concerns, default to deny. */ allow_leasequery = 0; /* * See if we are authorized to do LEASEQUERY. */ oc = lookup_option(&server_universe, options, SV_LEASEQUERY); if (oc != NULL) { allow_leasequery = evaluate_boolean_option_cache(&ignorep, packet, NULL, NULL, packet->options, options, &global_scope, oc, MDL); } if (!allow_leasequery) { log_info("%s: LEASEQUERY not allowed, query ignored", msgbuf); option_state_dereference(&options, MDL); return; } /* * Copy out the client IP address. */ cip.len = sizeof(packet->raw->ciaddr); memcpy(cip.iabuf, &packet->raw->ciaddr, sizeof(packet->raw->ciaddr)); /* * If the client IP address is valid (not all zero), then we * are looking for information about that IP address. */ assoc_ip_cnt = 0; lease = tmp_lease = NULL; if (memcmp(cip.iabuf, "\0\0\0", 4)) { want_associated_ip = 0; snprintf(dbg_info, sizeof(dbg_info), "IP %s", piaddr(cip)); find_lease_by_ip_addr(&lease, cip, MDL); } else { want_associated_ip = 1; /* * If the client IP address is all zero, then we will * either look up by the client identifier (if we have * one), or by the MAC address. */ memset(&uid, 0, sizeof(uid)); if (get_option(&uid, &dhcp_universe, packet, NULL, NULL, packet->options, NULL, packet->options, &global_scope, DHO_DHCP_CLIENT_IDENTIFIER, MDL)) { snprintf(dbg_info, sizeof(dbg_info), "client-id %s", print_hex_1(uid.len, uid.data, 60)); find_lease_by_uid(&tmp_lease, uid.data, uid.len, MDL); data_string_forget(&uid, MDL); get_newest_lease(&lease, tmp_lease, next_uid); assoc_ip_cnt = get_associated_ips(tmp_lease, next_uid, lease, assoc_ips, nassoc_ips); } else { if (packet->raw->hlen+1 > sizeof(h.hbuf)) { log_info("%s: hardware length too long, " "no reply sent", msgbuf); option_state_dereference(&options, MDL); return; } h.hlen = packet->raw->hlen + 1; h.hbuf[0] = packet->raw->htype; memcpy(&h.hbuf[1], packet->raw->chaddr, packet->raw->hlen); snprintf(dbg_info, sizeof(dbg_info), "MAC address %s", print_hw_addr(h.hbuf[0], h.hlen - 1, &h.hbuf[1])); find_lease_by_hw_addr(&tmp_lease, h.hbuf, h.hlen, MDL); get_newest_lease(&lease, tmp_lease, next_hw); assoc_ip_cnt = get_associated_ips(tmp_lease, next_hw, lease, assoc_ips, nassoc_ips); } lease_dereference(&tmp_lease, MDL); if (lease != NULL) { memcpy(&packet->raw->ciaddr, lease->ip_addr.iabuf, sizeof(packet->raw->ciaddr)); } /* * Log if we have too many IP addresses associated * with this client. */ if (want_associated_ip && (assoc_ip_cnt > nassoc_ips)) { log_info("%d IP addresses associated with %s, " "only %d sent in reply.", assoc_ip_cnt, dbg_info, nassoc_ips); } } /* * We now know the query target too, so can report this in * our log message. */ snprintf(msgbuf, sizeof(msgbuf), "DHCPLEASEQUERY from %s for %s", inet_ntoa(packet->raw->giaddr), dbg_info); /* * Figure our our return type. */ if (lease == NULL) { dhcpMsgType = DHCPLEASEUNKNOWN; dhcp_msg_type_name = "DHCPLEASEUNKNOWN"; } else { if (lease->binding_state == FTS_ACTIVE) { dhcpMsgType = DHCPLEASEACTIVE; dhcp_msg_type_name = "DHCPLEASEACTIVE"; } else { dhcpMsgType = DHCPLEASEUNASSIGNED; dhcp_msg_type_name = "DHCPLEASEUNASSIGNED"; } } /* * Set options that only make sense if we have an active lease. */ if (dhcpMsgType == DHCPLEASEACTIVE) { /* * RFC 4388 uses the PRL to request options for the agent to * receive that are "about" the client. It is confusing * because in some cases it wants to know what was sent to * the client (lease times, adjusted), and in others it wants * to know information the client sent. You're supposed to * know this on a case-by-case basis. * * "Name servers", "domain name", and the like from the relay * agent's scope seems less than useful. Our options are to * restart the option cache from the lease's best point of view * (execute statements from the lease pool's group), or to * simply restart the option cache from empty. * * I think restarting the option cache from empty best * approaches RFC 4388's intent; specific options are included. */ option_state_dereference(&options, MDL); if (!option_state_allocate(&options, MDL)) { log_error("%s: out of memory, no reply sent", msgbuf); lease_dereference(&lease, MDL); return; } /* * Set the hardware address fields. */ packet->raw->hlen = lease->hardware_addr.hlen - 1; packet->raw->htype = lease->hardware_addr.hbuf[0]; memcpy(packet->raw->chaddr, &lease->hardware_addr.hbuf[1], sizeof(packet->raw->chaddr)); /* * Set client identifier option. */ if (lease->uid_len > 0) { if (!add_option(options, DHO_DHCP_CLIENT_IDENTIFIER, lease->uid, lease->uid_len)) { option_state_dereference(&options, MDL); lease_dereference(&lease, MDL); log_info("%s: out of memory, no reply sent", msgbuf); return; } } /* * Calculate T1 and T2, the times when the client * tries to extend its lease on its networking * address. * These seem to be hard-coded in ISC DHCP, to 0.5 and * 0.875 of the lease time. */ lease_duration = lease->ends - lease->starts; time_renewal = lease->starts + (lease_duration / 2); time_rebinding = lease->starts + (lease_duration / 2) + (lease_duration / 4) + (lease_duration / 8); if (time_renewal > cur_time) { time_renewal = htonl(time_renewal - cur_time); if (!add_option(options, DHO_DHCP_RENEWAL_TIME, &time_renewal, sizeof(time_renewal))) { option_state_dereference(&options, MDL); lease_dereference(&lease, MDL); log_info("%s: out of memory, no reply sent", msgbuf); return; } } if (time_rebinding > cur_time) { time_rebinding = htonl(time_rebinding - cur_time); if (!add_option(options, DHO_DHCP_REBINDING_TIME, &time_rebinding, sizeof(time_rebinding))) { option_state_dereference(&options, MDL); lease_dereference(&lease, MDL); log_info("%s: out of memory, no reply sent", msgbuf); return; } } if (lease->ends > cur_time) { time_expiry = htonl(lease->ends - cur_time); if (!add_option(options, DHO_DHCP_LEASE_TIME, &time_expiry, sizeof(time_expiry))) { option_state_dereference(&options, MDL); lease_dereference(&lease, MDL); log_info("%s: out of memory, no reply sent", msgbuf); return; } } /* Supply the Vendor-Class-Identifier. */ if (lease->scope != NULL) { struct data_string vendor_class; memset(&vendor_class, 0, sizeof(vendor_class)); if (find_bound_string(&vendor_class, lease->scope, "vendor-class-identifier")) { if (!add_option(options, DHO_VENDOR_CLASS_IDENTIFIER, (void *)vendor_class.data, vendor_class.len)) { option_state_dereference(&options, MDL); lease_dereference(&lease, MDL); log_error("%s: error adding vendor " "class identifier, no reply " "sent", msgbuf); data_string_forget(&vendor_class, MDL); return; } data_string_forget(&vendor_class, MDL); } } /* * Set the relay agent info. * * Note that because agent info is appended without regard * to the PRL in cons_options(), this will be sent as the * last option in the packet whether it is listed on PRL or * not. */ if (lease->agent_options != NULL) { int idx = agent_universe.index; struct option_chain_head **tmp1 = (struct option_chain_head **) &(options->universes[idx]); struct option_chain_head *tmp2 = (struct option_chain_head *) lease->agent_options; option_chain_head_reference(tmp1, tmp2, MDL); } /* * Set the client last transaction time. * We check to make sure we have a timestamp. For * lease files that were saved before running a * timestamp-aware version of the server, this may * not be set. */ if (lease->cltt != MIN_TIME) { if (cur_time > lease->cltt) { client_last_transaction_time = htonl(cur_time - lease->cltt); } else { client_last_transaction_time = htonl(0); } if (!add_option(options, DHO_CLIENT_LAST_TRANSACTION_TIME, &client_last_transaction_time, sizeof(client_last_transaction_time))) { option_state_dereference(&options, MDL); lease_dereference(&lease, MDL); log_info("%s: out of memory, no reply sent", msgbuf); return; } } /* * Set associated IPs, if requested and there are some. */ if (want_associated_ip && (assoc_ip_cnt > 0)) { if (!add_option(options, DHO_ASSOCIATED_IP, assoc_ips, assoc_ip_cnt * sizeof(assoc_ips[0]))) { option_state_dereference(&options, MDL); lease_dereference(&lease, MDL); log_info("%s: out of memory, no reply sent", msgbuf); return; } } } /* * Set the message type. */ packet->raw->op = BOOTREPLY; /* * Set DHCP message type. */ if (!add_option(options, DHO_DHCP_MESSAGE_TYPE, &dhcpMsgType, sizeof(dhcpMsgType))) { option_state_dereference(&options, MDL); lease_dereference(&lease, MDL); log_info("%s: error adding option, no reply sent", msgbuf); return; } /* * Log the message we've received. */ log_info("%s", msgbuf); /* * Figure out which address to use to send from. */ get_server_source_address(&siaddr, options, options, packet); /* * Set up the option buffer. */ memset(&prl, 0, sizeof(prl)); oc = lookup_option(&dhcp_universe, options, DHO_DHCP_PARAMETER_REQUEST_LIST); if (oc != NULL) { evaluate_option_cache(&prl, packet, NULL, NULL, packet->options, options, &global_scope, oc, MDL); } if (prl.len > 0) { prl_ptr = &prl; } else { prl_ptr = NULL; } packet->packet_length = cons_options(packet, packet->raw, lease, NULL, 0, packet->options, options, &global_scope, 0, 0, 0, prl_ptr, NULL); data_string_forget(&prl, MDL); /* SK: safe, even if empty */ option_state_dereference(&options, MDL); lease_dereference(&lease, MDL); to.sin_family = AF_INET; #ifdef HAVE_SA_LEN to.sin_len = sizeof(to); #endif memset(to.sin_zero, 0, sizeof(to.sin_zero)); #if defined(RELAY_PORT) relay_port = dhcp_check_relayport(packet); #endif /* * Leasequery packets are be sent to the gateway address. */ to.sin_addr = packet->raw->giaddr; if (packet->raw->giaddr.s_addr != htonl(INADDR_LOOPBACK)) { #if defined(RELAY_PORT) to.sin_port = relay_port ? relay_port : local_port; #else to.sin_port = local_port; #endif } else { to.sin_port = remote_port; /* XXXSK: For debugging. */ } /* * The fallback_interface lets us send with a real IP * address. The packet interface sends from all-zeros. */ if (fallback_interface != NULL) { interface = fallback_interface; } else { interface = packet->interface; } /* * Report what we're sending. */ log_info("%s to %s for %s (%d associated IPs)", dhcp_msg_type_name, inet_ntoa(to.sin_addr), dbg_info, assoc_ip_cnt); send_packet(interface, NULL, packet->raw, packet->packet_length, siaddr, &to, NULL); } #ifdef DHCPv6 /* * TODO: RFC5007 query-by-clientid. * * TODO: RFC5007 look at the pools according to the link-address. * * TODO: get fixed leases too. * * TODO: RFC5007 ORO in query-options. * * TODO: RFC5007 lq-relay-data. * * TODO: RFC5007 lq-client-link. * * Note: the code is still nearly compliant and usable for the target * case with these missing features! */ /* * The structure to handle a leasequery. */ struct lq6_state { struct packet *packet; struct data_string client_id; struct data_string server_id; struct data_string lq_query; uint8_t query_type; struct in6_addr link_addr; struct option_state *query_opts; struct option_state *reply_opts; unsigned cursor; union reply_buffer { unsigned char data[65536]; struct dhcpv6_packet reply; } buf; }; /* * Options that we want to send. */ static const int required_opts_lq[] = { D6O_CLIENTID, D6O_SERVERID, D6O_STATUS_CODE, D6O_CLIENT_DATA, D6O_LQ_RELAY_DATA, D6O_LQ_CLIENT_LINK, 0 }; static const int required_opt_CLIENT_DATA[] = { D6O_CLIENTID, D6O_IAADDR, D6O_IAPREFIX, D6O_CLT_TIME, 0 }; /* * Get the lq-query option from the packet. */ static isc_result_t get_lq_query(struct lq6_state *lq) { struct data_string *lq_query = &lq->lq_query; struct packet *packet = lq->packet; struct option_cache *oc; /* * Verify our lq_query structure is empty. */ if ((lq_query->data != NULL) || (lq_query->len != 0)) { return DHCP_R_INVALIDARG; } oc = lookup_option(&dhcpv6_universe, packet->options, D6O_LQ_QUERY); if (oc == NULL) { return ISC_R_NOTFOUND; } if (!evaluate_option_cache(lq_query, packet, NULL, NULL, packet->options, NULL, &global_scope, oc, MDL)) { return ISC_R_FAILURE; } return ISC_R_SUCCESS; } /* * Message validation, RFC 5007 section 4.2.1: * dhcpv6.c:valid_client_msg() - unicast + lq-query option. */ static int valid_query_msg(struct lq6_state *lq) { struct packet *packet = lq->packet; int ret_val = 0; struct option_cache *oc; /* INSIST((lq != NULL) || (packet != NULL)); */ switch (get_client_id(packet, &lq->client_id)) { case ISC_R_SUCCESS: break; case ISC_R_NOTFOUND: log_debug("Discarding %s from %s; " "client identifier missing", dhcpv6_type_names[packet->dhcpv6_msg_type], piaddr(packet->client_addr)); goto exit; default: log_error("Error processing %s from %s; " "unable to evaluate Client Identifier", dhcpv6_type_names[packet->dhcpv6_msg_type], piaddr(packet->client_addr)); goto exit; } oc = lookup_option(&dhcpv6_universe, packet->options, D6O_SERVERID); if (oc != NULL) { if (evaluate_option_cache(&lq->server_id, packet, NULL, NULL, packet->options, NULL, &global_scope, oc, MDL)) { log_debug("Discarding %s from %s; " "server identifier found " "(CLIENTID %s, SERVERID %s)", dhcpv6_type_names[packet->dhcpv6_msg_type], piaddr(packet->client_addr), print_hex_1(lq->client_id.len, lq->client_id.data, 60), print_hex_2(lq->server_id.len, lq->server_id.data, 60)); } else { log_debug("Discarding %s from %s; " "server identifier found " "(CLIENTID %s)", dhcpv6_type_names[packet->dhcpv6_msg_type], print_hex_1(lq->client_id.len, lq->client_id.data, 60), piaddr(packet->client_addr)); } goto exit; } switch (get_lq_query(lq)) { case ISC_R_SUCCESS: break; case ISC_R_NOTFOUND: log_debug("Discarding %s from %s; lq-query missing", dhcpv6_type_names[packet->dhcpv6_msg_type], piaddr(packet->client_addr)); goto exit; default: log_error("Error processing %s from %s; " "unable to evaluate LQ-Query", dhcpv6_type_names[packet->dhcpv6_msg_type], piaddr(packet->client_addr)); goto exit; } /* looks good */ ret_val = 1; exit: if (!ret_val) { if (lq->client_id.len > 0) { data_string_forget(&lq->client_id, MDL); } if (lq->server_id.len > 0) { data_string_forget(&lq->server_id, MDL); } if (lq->lq_query.len > 0) { data_string_forget(&lq->lq_query, MDL); } } return ret_val; } /* * Set an error in a status-code option (from set_status_code). */ static int set_error(struct lq6_state *lq, u_int16_t code, const char *message) { struct data_string d; int ret_val; memset(&d, 0, sizeof(d)); d.len = sizeof(code) + strlen(message); if (!buffer_allocate(&d.buffer, d.len, MDL)) { log_fatal("set_error: no memory for status code."); } d.data = d.buffer->data; putUShort(d.buffer->data, code); memcpy(d.buffer->data + sizeof(code), message, d.len - sizeof(code)); if (!save_option_buffer(&dhcpv6_universe, lq->reply_opts, d.buffer, (unsigned char *)d.data, d.len, D6O_STATUS_CODE, 0)) { log_error("set_error: error saving status code."); ret_val = 0; } else { ret_val = 1; } data_string_forget(&d, MDL); return ret_val; } /* * Process a by-address lease query. */ static int process_lq_by_address(struct lq6_state *lq) { struct packet *packet = lq->packet; struct option_cache *oc; struct ipv6_pool *pool = NULL; struct data_string data; struct in6_addr addr; struct iasubopt *iaaddr = NULL; struct option_state *opt_state = NULL; u_int32_t lifetime; unsigned opt_cursor; int ret_val = 0; /* * Get the IAADDR. */ oc = lookup_option(&dhcpv6_universe, lq->query_opts, D6O_IAADDR); if (oc == NULL) { if (!set_error(lq, STATUS_MalformedQuery, "No OPTION_IAADDR.")) { log_error("process_lq_by_address: unable " "to set MalformedQuery status code."); return 0; } return 1; } memset(&data, 0, sizeof(data)); if (!evaluate_option_cache(&data, packet, NULL, NULL, lq->query_opts, NULL, &global_scope, oc, MDL) || (data.len < IAADDR_OFFSET)) { log_error("process_lq_by_address: error evaluating IAADDR."); goto exit; } memcpy(&addr, data.data, sizeof(addr)); data_string_forget(&data, MDL); /* * Find the lease. * Note the RFC 5007 says to use the link-address to find the link * or the ia-aadr when it is :: but in any case the ia-addr has * to be on the link, so we ignore the link-address here. */ if (find_ipv6_pool(&pool, D6O_IA_NA, &addr) != ISC_R_SUCCESS) { if (!set_error(lq, STATUS_NotConfigured, "Address not in a pool.")) { log_error("process_lq_by_address: unable " "to set NotConfigured status code."); goto exit; } ret_val = 1; goto exit; } if (iasubopt_hash_lookup(&iaaddr, pool->leases, &addr, sizeof(addr), MDL) == 0) { ret_val = 1; goto exit; } if ((iaaddr == NULL) || (iaaddr->state != FTS_ACTIVE) || (iaaddr->ia == NULL) || (iaaddr->ia->iaid_duid.len <= 4)) { ret_val = 1; goto exit; } /* * Build the client-data option (with client-id, ia-addr and clt-time). */ if (!option_state_allocate(&opt_state, MDL)) { log_error("process_lq_by_address: " "no memory for option state."); goto exit; } data_string_copy(&data, &iaaddr->ia->iaid_duid, MDL); data.data += 4; data.len -= 4; if (!save_option_buffer(&dhcpv6_universe, opt_state, NULL, (unsigned char *)data.data, data.len, D6O_CLIENTID, 0)) { log_error("process_lq_by_address: error saving client ID."); goto exit; } data_string_forget(&data, MDL); data.len = IAADDR_OFFSET; if (!buffer_allocate(&data.buffer, data.len, MDL)) { log_error("process_lq_by_address: no memory for ia-addr."); goto exit; } data.data = data.buffer->data; memcpy(data.buffer->data, &iaaddr->addr, 16); lifetime = iaaddr->prefer; putULong(data.buffer->data + 16, lifetime); lifetime = iaaddr->valid; putULong(data.buffer->data + 20, lifetime); if (!save_option_buffer(&dhcpv6_universe, opt_state, NULL, (unsigned char *)data.data, data.len, D6O_IAADDR, 0)) { log_error("process_lq_by_address: error saving ia-addr."); goto exit; } data_string_forget(&data, MDL); lifetime = htonl(iaaddr->ia->cltt); if (!save_option_buffer(&dhcpv6_universe, opt_state, NULL, (unsigned char *)&lifetime, 4, D6O_CLT_TIME, 0)) { log_error("process_lq_by_address: error saving clt time."); goto exit; } /* * Store the client-data option. */ opt_cursor = lq->cursor; putUShort(lq->buf.data + lq->cursor, (unsigned)D6O_CLIENT_DATA); lq->cursor += 2; /* Skip option length. */ lq->cursor += 2; lq->cursor += store_options6((char *)lq->buf.data + lq->cursor, sizeof(lq->buf) - lq->cursor, opt_state, lq->packet, required_opt_CLIENT_DATA, NULL); /* Reset the length. */ putUShort(lq->buf.data + opt_cursor + 2, lq->cursor - (opt_cursor + 4)); /* Done. */ ret_val = 1; exit: if (data.data != NULL) data_string_forget(&data, MDL); if (pool != NULL) ipv6_pool_dereference(&pool, MDL); if (iaaddr != NULL) iasubopt_dereference(&iaaddr, MDL); if (opt_state != NULL) option_state_dereference(&opt_state, MDL); return ret_val; } /* * Process a lease query. */ void dhcpv6_leasequery(struct data_string *reply_ret, struct packet *packet) { static struct lq6_state lq; struct option_cache *oc; int allow_lq; /* * Initialize the lease query state. */ lq.packet = NULL; memset(&lq.client_id, 0, sizeof(lq.client_id)); memset(&lq.server_id, 0, sizeof(lq.server_id)); memset(&lq.lq_query, 0, sizeof(lq.lq_query)); lq.query_opts = NULL; lq.reply_opts = NULL; packet_reference(&lq.packet, packet, MDL); /* * Validate our input. */ if (!valid_query_msg(&lq)) { goto exit; } /* * Prepare our reply. */ if (!option_state_allocate(&lq.reply_opts, MDL)) { log_error("dhcpv6_leasequery: no memory for option state."); goto exit; } execute_statements_in_scope(NULL, lq.packet, NULL, NULL, lq.packet->options, lq.reply_opts, &global_scope, root_group, NULL, NULL); lq.buf.reply.msg_type = DHCPV6_LEASEQUERY_REPLY; memcpy(lq.buf.reply.transaction_id, lq.packet->dhcpv6_transaction_id, sizeof(lq.buf.reply.transaction_id)); /* * Because LEASEQUERY has some privacy concerns, default to deny. */ allow_lq = 0; /* * See if we are authorized to do LEASEQUERY. */ oc = lookup_option(&server_universe, lq.reply_opts, SV_LEASEQUERY); if (oc != NULL) { allow_lq = evaluate_boolean_option_cache(NULL, lq.packet, NULL, NULL, lq.packet->options, lq.reply_opts, &global_scope, oc, MDL); } if (!allow_lq) { log_info("dhcpv6_leasequery: not allowed, query ignored."); goto exit; } /* * Same than transmission of REPLY message in RFC 3315: * server-id * client-id */ oc = lookup_option(&dhcpv6_universe, lq.reply_opts, D6O_SERVERID); if (oc == NULL) { /* If not already in options, get from query then global. */ if (lq.server_id.data == NULL) copy_server_duid(&lq.server_id, MDL); if (!save_option_buffer(&dhcpv6_universe, lq.reply_opts, NULL, (unsigned char *)lq.server_id.data, lq.server_id.len, D6O_SERVERID, 0)) { log_error("dhcpv6_leasequery: " "error saving server identifier."); goto exit; } } if (!save_option_buffer(&dhcpv6_universe, lq.reply_opts, lq.client_id.buffer, (unsigned char *)lq.client_id.data, lq.client_id.len, D6O_CLIENTID, 0)) { log_error("dhcpv6_leasequery: " "error saving client identifier."); goto exit; } lq.cursor = 4; /* * Decode the lq-query option. */ if (lq.lq_query.len <= LQ_QUERY_OFFSET) { if (!set_error(&lq, STATUS_MalformedQuery, "OPTION_LQ_QUERY too short.")) { log_error("dhcpv6_leasequery: unable " "to set MalformedQuery status code."); goto exit; } goto done; } lq.query_type = lq.lq_query.data [0]; memcpy(&lq.link_addr, lq.lq_query.data + 1, sizeof(lq.link_addr)); switch (lq.query_type) { case LQ6QT_BY_ADDRESS: break; case LQ6QT_BY_CLIENTID: if (!set_error(&lq, STATUS_UnknownQueryType, "QUERY_BY_CLIENTID not supported.")) { log_error("dhcpv6_leasequery: unable to " "set UnknownQueryType status code."); goto exit; } goto done; default: if (!set_error(&lq, STATUS_UnknownQueryType, "Unknown query-type.")) { log_error("dhcpv6_leasequery: unable to " "set UnknownQueryType status code."); goto exit; } goto done; } if (!option_state_allocate(&lq.query_opts, MDL)) { log_error("dhcpv6_leasequery: no memory for option state."); goto exit; } if (!parse_option_buffer(lq.query_opts, lq.lq_query.data + LQ_QUERY_OFFSET, lq.lq_query.len - LQ_QUERY_OFFSET, &dhcpv6_universe)) { log_error("dhcpv6_leasequery: error parsing query-options."); if (!set_error(&lq, STATUS_MalformedQuery, "Bad query-options.")) { log_error("dhcpv6_leasequery: unable " "to set MalformedQuery status code."); goto exit; } goto done; } /* Do it. */ if (!process_lq_by_address(&lq)) goto exit; done: /* Store the options. */ lq.cursor += store_options6((char *)lq.buf.data + lq.cursor, sizeof(lq.buf) - lq.cursor, lq.reply_opts, lq.packet, required_opts_lq, NULL); /* Return our reply to the caller. */ reply_ret->len = lq.cursor; reply_ret->buffer = NULL; if (!buffer_allocate(&reply_ret->buffer, lq.cursor, MDL)) { log_fatal("dhcpv6_leasequery: no memory to store Reply."); } memcpy(reply_ret->buffer->data, lq.buf.data, lq.cursor); reply_ret->data = reply_ret->buffer->data; exit: /* Cleanup. */ if (lq.packet != NULL) packet_dereference(&lq.packet, MDL); if (lq.client_id.data != NULL) data_string_forget(&lq.client_id, MDL); if (lq.server_id.data != NULL) data_string_forget(&lq.server_id, MDL); if (lq.lq_query.data != NULL) data_string_forget(&lq.lq_query, MDL); if (lq.query_opts != NULL) option_state_dereference(&lq.query_opts, MDL); if (lq.reply_opts != NULL) option_state_dereference(&lq.reply_opts, MDL); } #endif /* DHCPv6 */ dhcp-4.4.1/server/dhcpv6.c000644 000765 000024 00000737671 13243301226 015604 0ustar00tmarkstaff000000 000000 /* * Copyright (C) 2006-2017 by 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /*! \file server/dhcpv6.c */ #include "dhcpd.h" #ifdef DHCPv6 #ifdef DHCP4o6 static void forw_dhcpv4_query(struct packet *packet); static void send_dhcpv4_response(struct data_string *raw); static void recv_dhcpv4_query(struct data_string *raw); static void dhcp4o6_dhcpv4_query(struct data_string *reply_ret, struct packet *packet); struct udp_data4o6 { u_int16_t src_port; u_int8_t rsp_opt_exist; u_int8_t reserved; }; static int offset_data4o6 = 36; /* 16+16+4 */ #endif /* * We use print_hex_1() to output DUID values. We could actually output * the DUID with more information... MAC address if using type 1 or 3, * and so on. However, RFC 3315 contains Grave Warnings against actually * attempting to understand a DUID. */ /* * TODO: gettext() or other method of localization for the messages * for status codes (and probably for log formats eventually) * TODO: refactoring (simplify, simplify, simplify) * TODO: support multiple shared_networks on each interface (this * will allow the server to issue multiple IPv6 addresses to * a single interface) */ /* * DHCPv6 Reply workflow assist. A Reply packet is built by various * different functions; this gives us one location where we keep state * regarding a reply. */ struct reply_state { /* root level persistent state */ struct shared_network *shared; struct host_decl *host; struct subnet *subnet; /* Used to match fixed-addrs to subnet scopes. */ struct option_state *opt_state; struct packet *packet; struct data_string client_id; /* IA level persistent state */ unsigned ia_count; unsigned pd_count; unsigned client_resources; isc_boolean_t resources_included; isc_boolean_t static_lease; unsigned static_prefixes; struct ia_xx *ia; struct ia_xx *old_ia; struct option_state *reply_ia; struct data_string fixed; struct iaddrcidrnet fixed_pref; /* static prefix for logging */ /* IAADDR/PREFIX level persistent state */ struct iasubopt *lease; /* * "t1", "t2", preferred, and valid lifetimes records for calculating * t1 and t2 (min/max). */ u_int32_t renew, rebind, min_prefer, min_valid; /* Client-requested valid and preferred lifetimes. */ u_int32_t client_valid, client_prefer; /* Chosen values to transmit for valid and preferred lifetimes. */ u_int32_t send_valid, send_prefer; /* Preferred prefix length (-1 is any). */ int preflen; /* Index into the data field that has been consumed. */ unsigned cursor; /* Space for the on commit statements for a fixed host */ struct on_star on_star; union reply_buffer { unsigned char data[65536]; struct dhcpv6_packet reply; } buf; }; /* * Prototypes local to this file. */ static int get_encapsulated_IA_state(struct option_state **enc_opt_state, struct data_string *enc_opt_data, struct packet *packet, struct option_cache *oc, int offset); static void build_dhcpv6_reply(struct data_string *, struct packet *); static isc_result_t shared_network_from_packet6(struct shared_network **shared, struct packet *packet); static void seek_shared_host(struct host_decl **hp, struct shared_network *shared); static isc_boolean_t fixed_matches_shared(struct host_decl *host, struct shared_network *shared); static isc_result_t reply_process_ia_na(struct reply_state *reply, struct option_cache *ia); static isc_result_t reply_process_ia_ta(struct reply_state *reply, struct option_cache *ia); static isc_result_t reply_process_addr(struct reply_state *reply, struct option_cache *addr); static isc_boolean_t address_is_owned(struct reply_state *reply, struct iaddr *addr); static isc_boolean_t temporary_is_available(struct reply_state *reply, struct iaddr *addr); static isc_result_t find_client_temporaries(struct reply_state *reply); static isc_result_t reply_process_try_addr(struct reply_state *reply, struct iaddr *addr); static isc_result_t find_client_address(struct reply_state *reply); static isc_result_t reply_process_is_addressed(struct reply_state *reply, struct binding_scope **scope, struct group *group); static isc_result_t reply_process_send_addr(struct reply_state *reply, struct iaddr *addr); static struct iasubopt *lease_compare(struct iasubopt *alpha, struct iasubopt *beta); static isc_result_t reply_process_ia_pd(struct reply_state *reply, struct option_cache *ia_pd); static struct group *find_group_by_prefix(struct reply_state *reply); static isc_result_t reply_process_prefix(struct reply_state *reply, struct option_cache *pref); static isc_boolean_t prefix_is_owned(struct reply_state *reply, struct iaddrcidrnet *pref); static isc_result_t find_client_prefix(struct reply_state *reply); static isc_result_t reply_process_try_prefix(struct reply_state *reply, struct iaddrcidrnet *pref); static isc_result_t reply_process_is_prefixed(struct reply_state *reply, struct binding_scope **scope, struct group *group); static isc_result_t reply_process_send_prefix(struct reply_state *reply, struct iaddrcidrnet *pref); static struct iasubopt *prefix_compare(struct reply_state *reply, struct iasubopt *alpha, struct iasubopt *beta); static void schedule_lease_timeout_reply(struct reply_state *reply); static int eval_prefix_mode(int thislen, int preflen, int prefix_mode); static isc_result_t pick_v6_prefix_helper(struct reply_state *reply, int prefix_mode); static void unicast_reject(struct data_string *reply_ret, struct packet *packet, const struct data_string *client_id, const struct data_string *server_id); static isc_boolean_t is_unicast_option_defined(struct packet *packet); static isc_result_t shared_network_from_requested_addr (struct shared_network **shared, struct packet* packet); static isc_result_t get_first_ia_addr_val (struct packet* packet, int addr_type, struct iaddr* iaddr); static void set_reply_tee_times(struct reply_state* reply, unsigned ia_cursor); static const char *iasubopt_plen_str(struct iasubopt *lease); static int release_on_roam(struct reply_state *reply); static int reuse_lease6(struct reply_state *reply, struct iasubopt *lease); static void shorten_lifetimes(struct reply_state *reply, struct iasubopt *lease, time_t age, int threshold); static void write_to_packet(struct reply_state *reply, unsigned ia_cursor); static const char *iasubopt_plen_str(struct iasubopt *lease); #ifdef NSUPDATE static void ddns_update_static6(struct reply_state* reply); #endif #ifdef DHCP4o6 /* * \brief Omapi I/O handler * * The inter-process communication receive handler. * Get the message, put it into the raw data_string * and call \ref send_dhcpv4_response() (DHCPv6 side) or * \ref recv_dhcpv4_query() (DHCPv4 side) * * \param h the OMAPI object * \return a result for I/O success or error (used by the I/O subsystem) */ isc_result_t dhcpv4o6_handler(omapi_object_t *h) { char buf[65536]; struct data_string raw; int cc; if (h->type != dhcp4o6_type) return DHCP_R_INVALIDARG; cc = recv(dhcp4o6_fd, buf, sizeof(buf), 0); if (cc < DHCP_FIXED_NON_UDP + offset_data4o6) return ISC_R_UNEXPECTED; memset(&raw, 0, sizeof(raw)); if (!buffer_allocate(&raw.buffer, cc, MDL)) { log_error("dhcpv4o6_handler: no memory buffer."); return ISC_R_NOMEMORY; } raw.data = raw.buffer->data; raw.len = cc; memcpy(raw.buffer->data, buf, cc); if (local_family == AF_INET6) { send_dhcpv4_response(&raw); } else { recv_dhcpv4_query(&raw); } data_string_forget(&raw, MDL); return ISC_R_SUCCESS; } /* * \brief Send the DHCPv4-response back to the DHCPv6 side * (DHCPv6 server function) * * Format: interface:16 + address:16 + udp:4 + DHCPv6 DHCPv4-response message * * \param raw the IPC message content */ static void send_dhcpv4_response(struct data_string *raw) { struct interface_info *ip; char name[16 + 1]; struct sockaddr_in6 to_addr; char pbuf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; struct udp_data4o6 udp_data; int send_ret; memset(name, 0, sizeof(name)); memcpy(name, raw->data, 16); for (ip = interfaces; ip != NULL; ip = ip->next) { if (!strcmp(name, ip->name)) break; } if (ip == NULL) { log_error("send_dhcpv4_response: can't find interface %s.", name); return; } memset(&to_addr, 0, sizeof(to_addr)); to_addr.sin6_family = AF_INET6; memcpy(&to_addr.sin6_addr, raw->data + 16, 16); memset(&udp_data, 0, sizeof(udp_data)); memcpy(&udp_data, raw->data + 32, 4); if ((raw->data[36] == DHCPV6_RELAY_FORW) || (raw->data[36] == DHCPV6_RELAY_REPL)) { if (udp_data.rsp_opt_exist) { to_addr.sin6_port = udp_data.src_port; } else { to_addr.sin6_port = local_port; } } else { to_addr.sin6_port = remote_port; } log_info("send_dhcpv4_response(): sending %s on %s to %s port %d", dhcpv6_type_names[raw->data[36]], name, inet_ntop(AF_INET6, raw->data + 16, pbuf, sizeof(pbuf)), ntohs(to_addr.sin6_port)); send_ret = send_packet6(ip, raw->data + 36, raw->len - 36, &to_addr); if (send_ret < 0) { log_error("send_dhcpv4_response: send_packet6(): %m"); } else if (send_ret != raw->len - 36) { log_error("send_dhcpv4_response: send_packet6() " "sent %d of %d bytes", send_ret, raw->len - 36); } } #endif /* DHCP4o6 */ /* * Schedule lease timeouts for all of the iasubopts in the reply. * This is currently used to schedule timeouts for soft leases. */ static void schedule_lease_timeout_reply(struct reply_state *reply) { struct iasubopt *tmp; int i; /* sanity check the reply */ if ((reply == NULL) || (reply->ia == NULL) || (reply->ia->iasubopt == NULL)) return; /* walk through the list, scheduling as we go */ for (i = 0 ; i < reply->ia->num_iasubopt ; i++) { tmp = reply->ia->iasubopt[i]; schedule_lease_timeout(tmp->ipv6_pool); } } /* * This function returns the time since DUID time start for the * given time_t value. */ static u_int32_t duid_time(time_t when) { /* * This time is modulo 2^32. */ while ((when - DUID_TIME_EPOCH) > 4294967295u) { /* use 2^31 to avoid spurious compiler warnings */ when -= 2147483648u; when -= 2147483648u; } return when - DUID_TIME_EPOCH; } /* * Server DUID. * * This must remain the same for the lifetime of this server, because * clients return the server DUID that we sent them in Request packets. * * We pick the server DUID like this: * * 1. Check dhcpd.conf - any value the administrator has configured * overrides any possible values. * 2. Check the leases.txt - we want to use the previous value if * possible. * 3. Check if dhcpd.conf specifies a type of server DUID to use, * and generate that type. * 4. Generate a type 1 (time + hardware address) DUID. */ static struct data_string server_duid; /* * Check if the server_duid has been set. */ isc_boolean_t server_duid_isset(void) { return (server_duid.data != NULL); } /* * Return the server_duid. */ void copy_server_duid(struct data_string *ds, const char *file, int line) { data_string_copy(ds, &server_duid, file, line); } /* * Set the server DUID to a specified value. This is used when * the server DUID is stored in persistent memory (basically the * leases.txt file). */ void set_server_duid(struct data_string *new_duid) { /* INSIST(new_duid != NULL); */ /* INSIST(new_duid->data != NULL); */ if (server_duid_isset()) { data_string_forget(&server_duid, MDL); } data_string_copy(&server_duid, new_duid, MDL); } /* * Set the server DUID based on the D6O_SERVERID option. This handles * the case where the administrator explicitly put it in the dhcpd.conf * file. */ isc_result_t set_server_duid_from_option(void) { struct option_state *opt_state; struct option_cache *oc; struct data_string option_duid; isc_result_t ret_val; opt_state = NULL; if (!option_state_allocate(&opt_state, MDL)) { log_fatal("No memory for server DUID."); } execute_statements_in_scope(NULL, NULL, NULL, NULL, NULL, opt_state, &global_scope, root_group, NULL, NULL); oc = lookup_option(&dhcpv6_universe, opt_state, D6O_SERVERID); if (oc == NULL) { ret_val = ISC_R_NOTFOUND; } else { memset(&option_duid, 0, sizeof(option_duid)); if (!evaluate_option_cache(&option_duid, NULL, NULL, NULL, opt_state, NULL, &global_scope, oc, MDL)) { ret_val = ISC_R_UNEXPECTED; } else { set_server_duid(&option_duid); data_string_forget(&option_duid, MDL); ret_val = ISC_R_SUCCESS; } } option_state_dereference(&opt_state, MDL); return ret_val; } /* * DUID layout, as defined in RFC 3315, section 9. * * We support type 1 (hardware address plus time) and type 3 (hardware * address). * * We can support type 2 for specific vendors in the future, if they * publish the specification. And of course there may be additional * types later. */ static int server_duid_type = DUID_LLT; /* * Set the DUID type. */ void set_server_duid_type(int type) { server_duid_type = type; } /* * Generate a new server DUID. This is done if there was no DUID in * the leases.txt or in the dhcpd.conf file. */ isc_result_t generate_new_server_duid(void) { struct interface_info *p; u_int32_t time_val; struct data_string generated_duid; /* * Verify we have a type that we support. */ if ((server_duid_type != DUID_LL) && (server_duid_type != DUID_LLT)) { log_error("Invalid DUID type %d specified, " "only LL and LLT types supported", server_duid_type); return DHCP_R_INVALIDARG; } /* * Find an interface with a hardware address. * Any will do. :) */ for (p = interfaces; p != NULL; p = p->next) { if (p->hw_address.hlen > 0) { break; } } if (p == NULL) { return ISC_R_UNEXPECTED; } /* * Build our DUID. */ memset(&generated_duid, 0, sizeof(generated_duid)); if (server_duid_type == DUID_LLT) { time_val = duid_time(time(NULL)); generated_duid.len = 8 + p->hw_address.hlen - 1; if (!buffer_allocate(&generated_duid.buffer, generated_duid.len, MDL)) { log_fatal("No memory for server DUID."); } generated_duid.data = generated_duid.buffer->data; putUShort(generated_duid.buffer->data, DUID_LLT); putUShort(generated_duid.buffer->data + 2, p->hw_address.hbuf[0]); putULong(generated_duid.buffer->data + 4, time_val); memcpy(generated_duid.buffer->data + 8, p->hw_address.hbuf+1, p->hw_address.hlen-1); } else if (server_duid_type == DUID_LL) { generated_duid.len = 4 + p->hw_address.hlen - 1; if (!buffer_allocate(&generated_duid.buffer, generated_duid.len, MDL)) { log_fatal("No memory for server DUID."); } generated_duid.data = generated_duid.buffer->data; putUShort(generated_duid.buffer->data, DUID_LL); putUShort(generated_duid.buffer->data + 2, p->hw_address.hbuf[0]); memcpy(generated_duid.buffer->data + 4, p->hw_address.hbuf+1, p->hw_address.hlen-1); } else { log_fatal("Unsupported server DUID type %d.", server_duid_type); } set_server_duid(&generated_duid); data_string_forget(&generated_duid, MDL); return ISC_R_SUCCESS; } /* * Get the client identifier from the packet. */ isc_result_t get_client_id(struct packet *packet, struct data_string *client_id) { struct option_cache *oc; /* * Verify our client_id structure is empty. */ if ((client_id->data != NULL) || (client_id->len != 0)) { return DHCP_R_INVALIDARG; } oc = lookup_option(&dhcpv6_universe, packet->options, D6O_CLIENTID); if (oc == NULL) { return ISC_R_NOTFOUND; } if (!evaluate_option_cache(client_id, packet, NULL, NULL, packet->options, NULL, &global_scope, oc, MDL)) { return ISC_R_FAILURE; } return ISC_R_SUCCESS; } /* * Message validation, defined in RFC 3315, sections 15.2, 15.5, 15.7: * * Servers MUST discard any Solicit messages that do not include a * Client Identifier option or that do include a Server Identifier * option. */ int valid_client_msg(struct packet *packet, struct data_string *client_id) { int ret_val; struct option_cache *oc; struct data_string data; ret_val = 0; memset(client_id, 0, sizeof(*client_id)); memset(&data, 0, sizeof(data)); switch (get_client_id(packet, client_id)) { case ISC_R_SUCCESS: break; case ISC_R_NOTFOUND: log_debug("Discarding %s from %s; " "client identifier missing", dhcpv6_type_names[packet->dhcpv6_msg_type], piaddr(packet->client_addr)); goto exit; default: log_error("Error processing %s from %s; " "unable to evaluate Client Identifier", dhcpv6_type_names[packet->dhcpv6_msg_type], piaddr(packet->client_addr)); goto exit; } /* * Required by RFC 3315, section 15. */ if (packet->unicast) { log_debug("Discarding %s from %s; packet sent unicast " "(CLIENTID %s)", dhcpv6_type_names[packet->dhcpv6_msg_type], piaddr(packet->client_addr), print_hex_1(client_id->len, client_id->data, 60)); goto exit; } oc = lookup_option(&dhcpv6_universe, packet->options, D6O_SERVERID); if (oc != NULL) { if (evaluate_option_cache(&data, packet, NULL, NULL, packet->options, NULL, &global_scope, oc, MDL)) { log_debug("Discarding %s from %s; " "server identifier found " "(CLIENTID %s, SERVERID %s)", dhcpv6_type_names[packet->dhcpv6_msg_type], piaddr(packet->client_addr), print_hex_1(client_id->len, client_id->data, 60), print_hex_2(data.len, data.data, 60)); } else { log_debug("Discarding %s from %s; " "server identifier found " "(CLIENTID %s)", dhcpv6_type_names[packet->dhcpv6_msg_type], print_hex_1(client_id->len, client_id->data, 60), piaddr(packet->client_addr)); } goto exit; } /* looks good */ ret_val = 1; exit: if (data.len > 0) { data_string_forget(&data, MDL); } if (!ret_val) { if (client_id->len > 0) { data_string_forget(client_id, MDL); } } return ret_val; } /* * Response validation, defined in RFC 3315, sections 15.4, 15.6, 15.8, * 15.9 (slightly different wording, but same meaning): * * Servers MUST discard any received Request message that meet any of * the following conditions: * * - the message does not include a Server Identifier option. * - the contents of the Server Identifier option do not match the * server's DUID. * - the message does not include a Client Identifier option. */ int valid_client_resp(struct packet *packet, struct data_string *client_id, struct data_string *server_id) { int ret_val; struct option_cache *oc; /* INSIST((duid.data != NULL) && (duid.len > 0)); */ ret_val = 0; memset(client_id, 0, sizeof(*client_id)); memset(server_id, 0, sizeof(*server_id)); switch (get_client_id(packet, client_id)) { case ISC_R_SUCCESS: break; case ISC_R_NOTFOUND: log_debug("Discarding %s from %s; " "client identifier missing", dhcpv6_type_names[packet->dhcpv6_msg_type], piaddr(packet->client_addr)); goto exit; default: log_error("Error processing %s from %s; " "unable to evaluate Client Identifier", dhcpv6_type_names[packet->dhcpv6_msg_type], piaddr(packet->client_addr)); goto exit; } oc = lookup_option(&dhcpv6_universe, packet->options, D6O_SERVERID); if (oc == NULL) { log_debug("Discarding %s from %s: " "server identifier missing (CLIENTID %s)", dhcpv6_type_names[packet->dhcpv6_msg_type], piaddr(packet->client_addr), print_hex_1(client_id->len, client_id->data, 60)); goto exit; } if (!evaluate_option_cache(server_id, packet, NULL, NULL, packet->options, NULL, &global_scope, oc, MDL)) { log_error("Error processing %s from %s; " "unable to evaluate Server Identifier (CLIENTID %s)", dhcpv6_type_names[packet->dhcpv6_msg_type], piaddr(packet->client_addr), print_hex_1(client_id->len, client_id->data, 60)); goto exit; } if ((server_duid.len != server_id->len) || (memcmp(server_duid.data, server_id->data, server_duid.len) != 0)) { log_debug("Discarding %s from %s; " "not our server identifier " "(CLIENTID %s, SERVERID %s, server DUID %s)", dhcpv6_type_names[packet->dhcpv6_msg_type], piaddr(packet->client_addr), print_hex_1(client_id->len, client_id->data, 60), print_hex_2(server_id->len, server_id->data, 60), print_hex_3(server_duid.len, server_duid.data, 60)); goto exit; } /* looks good */ ret_val = 1; exit: if (!ret_val) { if (server_id->len > 0) { data_string_forget(server_id, MDL); } if (client_id->len > 0) { data_string_forget(client_id, MDL); } } return ret_val; } /* * Information request validation, defined in RFC 3315, section 15.12: * * Servers MUST discard any received Information-request message that * meets any of the following conditions: * * - The message includes a Server Identifier option and the DUID in * the option does not match the server's DUID. * * - The message includes an IA option. */ int valid_client_info_req(struct packet *packet, struct data_string *server_id) { int ret_val; struct option_cache *oc; struct data_string client_id; char client_id_str[80]; /* print_hex_1() uses maximum 60 characters, plus a few more for extra information */ ret_val = 0; memset(server_id, 0, sizeof(*server_id)); memset(&client_id, 0, sizeof(client_id)); /* * Make a string that we can print out to give more * information about the client if we need to. * * By RFC 3315, Section 18.1.5 clients SHOULD have a * client-id on an Information-request packet, but it * is not strictly necessary. */ if (get_client_id(packet, &client_id) == ISC_R_SUCCESS) { snprintf(client_id_str, sizeof(client_id_str), " (CLIENTID %s)", print_hex_1(client_id.len, client_id.data, 60)); data_string_forget(&client_id, MDL); } else { client_id_str[0] = '\0'; } /* * Required by RFC 3315, section 15. */ if (packet->unicast) { log_debug("Discarding %s from %s; packet sent unicast%s", dhcpv6_type_names[packet->dhcpv6_msg_type], piaddr(packet->client_addr), client_id_str); goto exit; } oc = lookup_option(&dhcpv6_universe, packet->options, D6O_IA_NA); if (oc != NULL) { log_debug("Discarding %s from %s; " "IA_NA option present%s", dhcpv6_type_names[packet->dhcpv6_msg_type], piaddr(packet->client_addr), client_id_str); goto exit; } oc = lookup_option(&dhcpv6_universe, packet->options, D6O_IA_TA); if (oc != NULL) { log_debug("Discarding %s from %s; " "IA_TA option present%s", dhcpv6_type_names[packet->dhcpv6_msg_type], piaddr(packet->client_addr), client_id_str); goto exit; } oc = lookup_option(&dhcpv6_universe, packet->options, D6O_IA_PD); if (oc != NULL) { log_debug("Discarding %s from %s; " "IA_PD option present%s", dhcpv6_type_names[packet->dhcpv6_msg_type], piaddr(packet->client_addr), client_id_str); goto exit; } oc = lookup_option(&dhcpv6_universe, packet->options, D6O_SERVERID); if (oc != NULL) { if (!evaluate_option_cache(server_id, packet, NULL, NULL, packet->options, NULL, &global_scope, oc, MDL)) { log_error("Error processing %s from %s; " "unable to evaluate Server Identifier%s", dhcpv6_type_names[packet->dhcpv6_msg_type], piaddr(packet->client_addr), client_id_str); goto exit; } if ((server_duid.len != server_id->len) || (memcmp(server_duid.data, server_id->data, server_duid.len) != 0)) { log_debug("Discarding %s from %s; " "not our server identifier " "(SERVERID %s, server DUID %s)%s", dhcpv6_type_names[packet->dhcpv6_msg_type], piaddr(packet->client_addr), print_hex_1(server_id->len, server_id->data, 60), print_hex_2(server_duid.len, server_duid.data, 60), client_id_str); goto exit; } } /* looks good */ ret_val = 1; exit: if (!ret_val) { if (server_id->len > 0) { data_string_forget(server_id, MDL); } } return ret_val; } /* * Options that we want to send, in addition to what was requested * via the ORO. */ static const int required_opts[] = { D6O_CLIENTID, D6O_SERVERID, D6O_STATUS_CODE, D6O_PREFERENCE, 0 }; static const int required_opts_solicit[] = { D6O_CLIENTID, D6O_SERVERID, D6O_IA_NA, D6O_IA_TA, D6O_IA_PD, D6O_RAPID_COMMIT, D6O_STATUS_CODE, D6O_RECONF_ACCEPT, D6O_PREFERENCE, 0 }; static const int required_opts_agent[] = { D6O_INTERFACE_ID, #if defined(RELAY_PORT) D6O_RELAY_SOURCE_PORT, #endif D6O_RELAY_MSG, 0 }; static const int required_opts_IA[] = { D6O_IAADDR, D6O_STATUS_CODE, 0 }; static const int required_opts_IA_PD[] = { D6O_IAPREFIX, D6O_STATUS_CODE, 0 }; static const int required_opts_STATUS_CODE[] = { D6O_STATUS_CODE, 0 }; #ifdef DHCP4o6 static const int required_opts_4o6[] = { D6O_DHCPV4_MSG, 0 }; #endif static const int unicast_reject_opts[] = { D6O_CLIENTID, D6O_SERVERID, D6O_STATUS_CODE, 0 }; /* * Extracts from packet contents an IA_* option, storing the IA structure * in its entirety in enc_opt_data, and storing any decoded DHCPv6 options * in enc_opt_state for later lookup and evaluation. The 'offset' indicates * where in the IA_* the DHCPv6 options commence. */ static int get_encapsulated_IA_state(struct option_state **enc_opt_state, struct data_string *enc_opt_data, struct packet *packet, struct option_cache *oc, int offset) { /* * Get the raw data for the encapsulated options. */ memset(enc_opt_data, 0, sizeof(*enc_opt_data)); if (!evaluate_option_cache(enc_opt_data, packet, NULL, NULL, packet->options, NULL, &global_scope, oc, MDL)) { log_error("get_encapsulated_IA_state: " "error evaluating raw option."); return 0; } if (enc_opt_data->len < offset) { log_error("get_encapsulated_IA_state: raw option too small."); data_string_forget(enc_opt_data, MDL); return 0; } /* * Now create the option state structure, and pass it to the * function that parses options. */ *enc_opt_state = NULL; if (!option_state_allocate(enc_opt_state, MDL)) { log_error("get_encapsulated_IA_state: no memory for options."); data_string_forget(enc_opt_data, MDL); return 0; } if (!parse_option_buffer(*enc_opt_state, enc_opt_data->data + offset, enc_opt_data->len - offset, &dhcpv6_universe)) { log_error("get_encapsulated_IA_state: error parsing options."); option_state_dereference(enc_opt_state, MDL); data_string_forget(enc_opt_data, MDL); return 0; } return 1; } static int set_status_code(u_int16_t status_code, const char *status_message, struct option_state *opt_state) { struct data_string d; int ret_val; memset(&d, 0, sizeof(d)); d.len = sizeof(status_code) + strlen(status_message); if (!buffer_allocate(&d.buffer, d.len, MDL)) { log_fatal("set_status_code: no memory for status code."); } d.data = d.buffer->data; putUShort(d.buffer->data, status_code); memcpy(d.buffer->data + sizeof(status_code), status_message, d.len - sizeof(status_code)); if (!save_option_buffer(&dhcpv6_universe, opt_state, d.buffer, (unsigned char *)d.data, d.len, D6O_STATUS_CODE, 0)) { log_error("set_status_code: error saving status code."); ret_val = 0; } else { ret_val = 1; } data_string_forget(&d, MDL); return ret_val; } void check_pool6_threshold(struct reply_state *reply, struct iasubopt *lease) { struct ipv6_pond *pond; isc_uint64_t used, count, high_threshold; int poolhigh = 0, poollow = 0; char *shared_name = "no name"; char tmp_addr[INET6_ADDRSTRLEN]; if ((lease->ipv6_pool == NULL) || (lease->ipv6_pool->ipv6_pond == NULL)) return; pond = lease->ipv6_pool->ipv6_pond; /* If the address range is too large to track, just skip all this. */ if (pond->jumbo_range == 1) { return; } count = pond->num_total; used = pond->num_active; /* get network name for logging */ if ((pond->shared_network != NULL) && (pond->shared_network->name != NULL)) { shared_name = pond->shared_network->name; } /* The logged flag indicates if we have already crossed the high * threshold and emitted a log message. If it is set we check to * see if we have re-crossed the low threshold and need to reset * things. When we cross the high threshold we determine what * the low threshold is and save it into the low_threshold value. * When we cross that threshold we reset the logged flag and * the low_threshold to 0 which allows the high threshold message * to be emitted once again. * if we haven't recrossed the boundry we don't need to do anything. */ if (pond->logged !=0) { if (used <= pond->low_threshold) { pond->low_threshold = 0; pond->logged = 0; log_error("Pool threshold reset - shared subnet: %s; " "address: %s; low threshold %llu/%llu.", shared_name, inet_ntop(AF_INET6, &lease->addr, tmp_addr, sizeof(tmp_addr)), used, count); } return; } /* find the high threshold */ if (get_option_int(&poolhigh, &server_universe, reply->packet, NULL, NULL, reply->packet->options, reply->opt_state, reply->opt_state, &lease->scope, SV_LOG_THRESHOLD_HIGH, MDL) == 0) { /* no threshold bail out */ return; } /* We do have a threshold for this pool, see if its valid */ if ((poolhigh <= 0) || (poolhigh > 100)) { /* not valid */ return; } /* we have a valid value, have we exceeded it */ high_threshold = FIND_POND6_PERCENT(count, poolhigh); if (used < high_threshold) { /* nope, no more to do */ return; } /* we've exceeded it, output a message */ log_error("Pool threshold exceeded - shared subnet: %s; " "address: %s; high threshold %d%% %llu/%llu.", shared_name, inet_ntop(AF_INET6, &lease->addr, tmp_addr, sizeof(tmp_addr)), poolhigh, used, count); /* handle the low threshold now, if we don't * have one we default to 0. */ if ((get_option_int(&poollow, &server_universe, reply->packet, NULL, NULL, reply->packet->options, reply->opt_state, reply->opt_state, &lease->scope, SV_LOG_THRESHOLD_LOW, MDL) == 0) || (poollow > 100)) { poollow = 0; } /* * If the low theshold is higher than the high threshold we continue to log * If it isn't then we set the flag saying we already logged and determine * what the reset threshold is. */ if (poollow < poolhigh) { pond->logged = 1; pond->low_threshold = FIND_POND6_PERCENT(count, poollow); } } /* * We have a set of operations we do to set up the reply packet, which * is the same for many message types. */ static int start_reply(struct packet *packet, const struct data_string *client_id, const struct data_string *server_id, struct option_state **opt_state, struct dhcpv6_packet *reply) { struct option_cache *oc; const unsigned char *server_id_data; int server_id_len; /* * Build our option state for reply. */ *opt_state = NULL; if (!option_state_allocate(opt_state, MDL)) { log_error("start_reply: no memory for option_state."); return 0; } execute_statements_in_scope(NULL, packet, NULL, NULL, packet->options, *opt_state, &global_scope, root_group, NULL, NULL); /* * A small bit of special handling for Solicit messages. * * We could move the logic into a flag, but for now just check * explicitly. */ if (packet->dhcpv6_msg_type == DHCPV6_SOLICIT) { reply->msg_type = DHCPV6_ADVERTISE; /* * If: * - this message type supports rapid commit (Solicit), and * - the server is configured to supply a rapid commit, and * - the client requests a rapid commit, * Then we add a rapid commit option, and send Reply (instead * of an Advertise). */ oc = lookup_option(&dhcpv6_universe, *opt_state, D6O_RAPID_COMMIT); if (oc != NULL) { oc = lookup_option(&dhcpv6_universe, packet->options, D6O_RAPID_COMMIT); if (oc != NULL) { /* Rapid-commit in action. */ reply->msg_type = DHCPV6_REPLY; } else { /* Don't want a rapid-commit in advertise. */ delete_option(&dhcpv6_universe, *opt_state, D6O_RAPID_COMMIT); } } } else { reply->msg_type = DHCPV6_REPLY; /* Delete the rapid-commit from the sent options. */ oc = lookup_option(&dhcpv6_universe, *opt_state, D6O_RAPID_COMMIT); if (oc != NULL) { delete_option(&dhcpv6_universe, *opt_state, D6O_RAPID_COMMIT); } } /* * Use the client's transaction identifier for the reply. */ memcpy(reply->transaction_id, packet->dhcpv6_transaction_id, sizeof(reply->transaction_id)); /* * RFC 3315, section 18.2 says we need server identifier and * client identifier. * * If the server ID is defined via the configuration file, then * it will already be present in the option state at this point, * so we don't need to set it. * * If we have a server ID passed in from the caller, * use that, otherwise use the global DUID. */ oc = lookup_option(&dhcpv6_universe, *opt_state, D6O_SERVERID); if (oc == NULL) { if (server_id == NULL) { server_id_data = server_duid.data; server_id_len = server_duid.len; } else { server_id_data = server_id->data; server_id_len = server_id->len; } if (!save_option_buffer(&dhcpv6_universe, *opt_state, NULL, (unsigned char *)server_id_data, server_id_len, D6O_SERVERID, 0)) { log_error("start_reply: " "error saving server identifier."); return 0; } } if (client_id->buffer != NULL) { if (!save_option_buffer(&dhcpv6_universe, *opt_state, client_id->buffer, (unsigned char *)client_id->data, client_id->len, D6O_CLIENTID, 0)) { log_error("start_reply: error saving " "client identifier."); return 0; } } /* * If the client accepts reconfiguration, let it know that we * will send them. * * Note: we don't actually do this yet, but DOCSIS requires we * claim to. */ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_RECONF_ACCEPT); if (oc != NULL) { if (!save_option_buffer(&dhcpv6_universe, *opt_state, NULL, (unsigned char *)"", 0, D6O_RECONF_ACCEPT, 0)) { log_error("start_reply: " "error saving RECONF_ACCEPT option."); option_state_dereference(opt_state, MDL); return 0; } } return 1; } /* * Try to get the IPv6 address the client asked for from the * pool. * * addr is the result (should be a pointer to NULL on entry) * pool is the pool to search in * requested_addr is the address the client wants */ static isc_result_t try_client_v6_address(struct iasubopt **addr, struct ipv6_pool *pool, const struct data_string *requested_addr) { struct in6_addr tmp_addr; isc_result_t result; if (requested_addr->len < sizeof(tmp_addr)) { return DHCP_R_INVALIDARG; } memcpy(&tmp_addr, requested_addr->data, sizeof(tmp_addr)); if (IN6_IS_ADDR_UNSPECIFIED(&tmp_addr)) { return ISC_R_FAILURE; } /* * The address is not covered by this (or possibly any) dynamic * range. */ if (!ipv6_in_pool(&tmp_addr, pool)) { return ISC_R_ADDRNOTAVAIL; } if (lease6_exists(pool, &tmp_addr)) { return ISC_R_ADDRINUSE; } result = iasubopt_allocate(addr, MDL); if (result != ISC_R_SUCCESS) { return result; } (*addr)->addr = tmp_addr; (*addr)->plen = 0; /* Default is soft binding for 2 minutes. */ result = add_lease6(pool, *addr, cur_time + 120); if (result != ISC_R_SUCCESS) { iasubopt_dereference(addr, MDL); } return result; } /*! * * \brief Get an IPv6 address for the client. * * Attempt to find a usable address for the client. We walk through * the ponds checking for permit and deny then through the pools * seeing if they have an available address. * * \param reply = the state structure for the current work on this request * if we create a lease we return it using reply->lease * * \return * ISC_R_SUCCESS = we were able to find an address and are returning a * pointer to the lease * ISC_R_NORESOURCES = there don't appear to be any free addresses. This * is probabalistic. We don't exhaustively try the * address range, instead we hash the duid and if * the address derived from the hash is in use we * hash the address. After a number of failures we * conclude the pool is basically full. */ static isc_result_t pick_v6_address(struct reply_state *reply) { struct ipv6_pool *p = NULL; struct ipv6_pond *pond; int i; int start_pool; unsigned int attempts; char tmp_buf[INET6_ADDRSTRLEN]; struct iasubopt **addr = &reply->lease; isc_uint64_t total = 0; isc_uint64_t active = 0; isc_uint64_t abandoned = 0; int jumbo_range = 0; char *shared_name = (reply->shared->name ? reply->shared->name : "(no name)"); /* * Do a quick walk through of the ponds and pools * to see if we have any NA address pools */ for (pond = reply->shared->ipv6_pond; pond != NULL; pond = pond->next) { if (pond->ipv6_pools == NULL) continue; for (i = 0; (p = pond->ipv6_pools[i]) != NULL; i++) { if (p->pool_type == D6O_IA_NA) break; } if (p != NULL) break; } /* If we get here and p is NULL we have no useful pools */ if (p == NULL) { log_debug("Unable to pick client address: " "no IPv6 pools on this shared network"); return ISC_R_NORESOURCES; } /* * We have at least one pool that could provide an address * Now we walk through the ponds and pools again and check * to see if the client is permitted and if an address is * available * * Within a given pond we start looking at the last pool we * allocated from, unless it had a collision trying to allocate * an address. This will tend to move us into less-filled pools. */ for (pond = reply->shared->ipv6_pond; pond != NULL; pond = pond->next) { isc_result_t result = ISC_R_FAILURE; if (((pond->prohibit_list != NULL) && (permitted(reply->packet, pond->prohibit_list))) || ((pond->permit_list != NULL) && (!permitted(reply->packet, pond->permit_list)))) continue; #ifdef EUI_64 /* If pond is EUI-64 but client duid isn't a valid EUI-64 * id, then skip this pond */ if (pond->use_eui_64 && !valid_eui_64_duid(&reply->ia->iaid_duid, IAID_LEN)) { continue; } #endif start_pool = pond->last_ipv6_pool; i = start_pool; do { p = pond->ipv6_pools[i]; if (p->pool_type == D6O_IA_NA) { #ifdef EUI_64 if (pond->use_eui_64) { result = create_lease6_eui_64(p, addr, &reply->ia->iaid_duid, cur_time + 120); } else #endif { result = create_lease6(p, addr, &attempts, &reply->ia->iaid_duid, cur_time + 120); } if (result == ISC_R_SUCCESS) { /* * Record the pool used (or next one if * there was a collision). */ if (attempts > 1) { i++; if (pond->ipv6_pools[i] == NULL) { i = 0; } } pond->last_ipv6_pool = i; log_debug("Picking pool address %s", inet_ntop(AF_INET6, &((*addr)->addr), tmp_buf, sizeof(tmp_buf))); return (ISC_R_SUCCESS); } } i++; if (pond->ipv6_pools[i] == NULL) { i = 0; } } while (i != start_pool); if (result == ISC_R_NORESOURCES) { jumbo_range += pond->jumbo_range; total += pond->num_total; active += pond->num_active; abandoned += pond->num_abandoned; } } /* * If we failed to pick an IPv6 address from any of the subnets. * Presumably that means we have no addresses for the client. */ if (jumbo_range != 0) { log_debug("Unable to pick client address: " "no addresses available - shared network %s: " " 2^64-1 < total, %llu active, %llu abandoned", shared_name, active - abandoned, abandoned); } else { log_debug("Unable to pick client address: " "no addresses available - shared network %s: " "%llu total, %llu active, %llu abandoned", shared_name, total, active - abandoned, abandoned); } return ISC_R_NORESOURCES; } /* * Try to get the IPv6 prefix the client asked for from the * prefix pool. * * pref is the result (should be a pointer to NULL on entry) * pool is the prefix pool to search in * requested_pref is the address the client wants */ static isc_result_t try_client_v6_prefix(struct iasubopt **pref, struct ipv6_pool *pool, const struct data_string *requested_pref) { u_int8_t tmp_plen; struct in6_addr tmp_pref; struct iaddr ia; isc_result_t result; if (requested_pref->len < sizeof(tmp_plen) + sizeof(tmp_pref)) { return DHCP_R_INVALIDARG; } tmp_plen = (int) requested_pref->data[0]; if ((tmp_plen < 3) || (tmp_plen > 128)) { return ISC_R_FAILURE; } memcpy(&tmp_pref, requested_pref->data + 1, sizeof(tmp_pref)); if (IN6_IS_ADDR_UNSPECIFIED(&tmp_pref)) { return ISC_R_FAILURE; } ia.len = 16; memcpy(&ia.iabuf, &tmp_pref, 16); if (!is_cidr_mask_valid(&ia, (int) tmp_plen)) { return ISC_R_FAILURE; } if (!ipv6_in_pool(&tmp_pref, pool) || ((int)tmp_plen != pool->units)) { return ISC_R_ADDRNOTAVAIL; } if (prefix6_exists(pool, &tmp_pref, tmp_plen)) { return ISC_R_ADDRINUSE; } result = iasubopt_allocate(pref, MDL); if (result != ISC_R_SUCCESS) { return result; } (*pref)->addr = tmp_pref; (*pref)->plen = tmp_plen; /* Default is soft binding for 2 minutes. */ result = add_lease6(pool, *pref, cur_time + 120); if (result != ISC_R_SUCCESS) { iasubopt_dereference(pref, MDL); } return result; } /*! * * \brief Get an IPv6 prefix for the client. * * Attempt to find a usable prefix for the client. Based upon the prefix * length mode and the plen supplied by the client (if one), we make one * or more calls to pick_v6_prefix_helper() to find a prefix as follows: * * PLM_IGNORE or client specifies a plen of zero, use the first available * prefix regardless of it's length. * * PLM_PREFER – look for an exact match to client's plen first, if none * found, use the first available prefix of any length * * PLM_EXACT – look for an exact match first, if none found then fail. This * is the default behavior. * * PLM_MAXIMUM - look for an exact match first, then the first available whose * prefix length is less than client's plen, otherwise fail. * * PLM_MINIMUM - look for an exact match first, then the first available whose * prefix length is greater than client's plen, otherwise fail. * * Note that the selection mode is configurable at the global scope only via * prefix-len-mode. * * \param reply = the state structure for the current work on this request * if we create a lease we return it using reply->lease * * \return * ISC_R_SUCCESS = we were able to find an prefix and are returning a * pointer to the lease * ISC_R_NORESOURCES = there don't appear to be any free addresses. This * is probabalistic. We don't exhaustively try the * address range, instead we hash the duid and if * the address derived from the hash is in use we * hash the address. After a number of failures we * conclude the pool is basically full. */ static isc_result_t pick_v6_prefix(struct reply_state *reply) { struct ipv6_pool *p = NULL; struct ipv6_pond *pond; int i; isc_result_t result; /* * Do a quick walk through of the ponds and pools * to see if we have any prefix pools */ for (pond = reply->shared->ipv6_pond; pond != NULL; pond = pond->next) { if (pond->ipv6_pools == NULL) continue; for (i = 0; (p = pond->ipv6_pools[i]) != NULL; i++) { if (p->pool_type == D6O_IA_PD) break; } if (p != NULL) break; } /* If we get here and p is NULL we have no useful pools */ if (p == NULL) { log_debug("Unable to pick client prefix: " "no IPv6 pools on this shared network"); return ISC_R_NORESOURCES; } if (reply->preflen <= 0) { /* If we didn't get a plen (-1) or client plen is 0, then just * select first available (same as PLM_INGORE) */ result = pick_v6_prefix_helper(reply, PLM_IGNORE); } else { switch (prefix_length_mode) { case PLM_PREFER: /* First we look for an exact match, if not found * then first available */ result = pick_v6_prefix_helper(reply, PLM_EXACT); if (result != ISC_R_SUCCESS) { result = pick_v6_prefix_helper(reply, PLM_IGNORE); } break; case PLM_EXACT: /* Match exactly or fail */ result = pick_v6_prefix_helper(reply, PLM_EXACT); break; case PLM_MINIMUM: case PLM_MAXIMUM: /* First we look for an exact match, if not found * then first available by mode */ result = pick_v6_prefix_helper(reply, PLM_EXACT); if (result != ISC_R_SUCCESS) { result = pick_v6_prefix_helper(reply, prefix_length_mode); } break; default: /* First available */ result = pick_v6_prefix_helper(reply, PLM_IGNORE); break; } } if (result == ISC_R_SUCCESS) { char tmp_buf[INET6_ADDRSTRLEN]; log_debug("Picking pool prefix %s/%u", inet_ntop(AF_INET6, &(reply->lease->addr), tmp_buf, sizeof(tmp_buf)), (unsigned)(reply->lease->plen)); return (ISC_R_SUCCESS); } /* * If we failed to pick an IPv6 prefix * Presumably that means we have no prefixes for the client. */ log_debug("Unable to pick client prefix: no prefixes available"); return ISC_R_NORESOURCES; } /*! * * \brief Get an IPv6 prefix for the client based upon selection mode. * * We walk through the ponds checking for permit and deny. If a pond is * permissable to use, loop through its PD pools checking prefix lengths * against the client plen based on the prefix length mode, looking for * available prefixes. * * \param reply = the state structure for the current work on this request * if we create a lease we return it using reply->lease * \prefix_mode = selection mode to use * * \return * ISC_R_SUCCESS = we were able to find a prefix and are returning a * pointer to the lease * ISC_R_NORESOURCES = there don't appear to be any free addresses. This * is probabalistic. We don't exhaustively try the * address range, instead we hash the duid and if * the address derived from the hash is in use we * hash the address. After a number of failures we * conclude the pool is basically full. */ isc_result_t pick_v6_prefix_helper(struct reply_state *reply, int prefix_mode) { struct ipv6_pool *p = NULL; struct ipv6_pond *pond; int i; unsigned int attempts; struct iasubopt **pref = &reply->lease; for (pond = reply->shared->ipv6_pond; pond != NULL; pond = pond->next) { if (((pond->prohibit_list != NULL) && (permitted(reply->packet, pond->prohibit_list))) || ((pond->permit_list != NULL) && (!permitted(reply->packet, pond->permit_list)))) continue; for (i = 0; (p = pond->ipv6_pools[i]) != NULL; i++) { if ((p->pool_type == D6O_IA_PD) && (eval_prefix_mode(p->units, reply->preflen, prefix_mode) == 1) && (create_prefix6(p, pref, &attempts, &reply->ia->iaid_duid, cur_time + 120) == ISC_R_SUCCESS)) { return (ISC_R_SUCCESS); } } } return ISC_R_NORESOURCES; } /*! * * \brief Test a prefix length against another based on prefix length mode * * \param len - prefix length to test * \param preflen - preferred prefix length against which to test * \param prefix_mode - prefix selection mode with which to test * * Note that the case of preferred length of 0 is not short-cut here as it * is assumed to be done at a higher level. * * \return 1 if the given length is usable based upon mode and a preferred * length, 0 if not. */ int eval_prefix_mode(int len, int preflen, int prefix_mode) { int use_it = 1; switch (prefix_mode) { case PLM_EXACT: use_it = (len == preflen); break; case PLM_MINIMUM: /* they asked for a prefix length no "shorter" than preflen */ use_it = (len >= preflen); break; case PLM_MAXIMUM: /* they asked for a prefix length no "longer" than preflen */ use_it = (len <= preflen); break; default: /* otherwise use it */ break; } return (use_it); } /* *! \file server/dhcpv6.c * * \brief construct a reply containing information about a client's lease * * lease_to_client() is called from several messages to construct a * reply that contains all that we know about the client's correct lease * (or projected lease). * * Solicit - "Soft" binding, ignore unknown addresses or bindings, just * send what we "may" give them on a request. * * Request - "Hard" binding, but ignore supplied addresses (just provide what * the client should really use). * * Renew - "Hard" binding, but client-supplied addresses are 'real'. Error * Rebind out any "wrong" addresses the client sends. This means we send * an empty IA_NA with a status code of NoBinding or NotOnLink or * possibly send the address with zeroed lifetimes. * * Information-Request - No binding. * * The basic structure is to traverse the client-supplied data first, and * validate and echo back any contents that can be. If the client-supplied * data does not error out (on renew/rebind as above), but we did not send * any addresses, attempt to allocate one. * * At the end of the this function we call commit_leases_timed() to * fsync and rotate the file as necessary. commit_leases_timed() will * check that we have written at least one lease to the file and that * some time has passed before doing any fsync or file rewrite so we * don't bother tracking if we did a write_ia during this function. */ /* TODO: look at client hints for lease times */ static void lease_to_client(struct data_string *reply_ret, struct packet *packet, const struct data_string *client_id, const struct data_string *server_id) { static struct reply_state reply; struct option_cache *oc; struct data_string packet_oro; int i; memset(&packet_oro, 0, sizeof(packet_oro)); /* Locate the client. */ if (shared_network_from_packet6(&reply.shared, packet) != ISC_R_SUCCESS) goto exit; /* * Initialize the reply. */ packet_reference(&reply.packet, packet, MDL); data_string_copy(&reply.client_id, client_id, MDL); if (!start_reply(packet, client_id, server_id, &reply.opt_state, &reply.buf.reply)) goto exit; /* Set the write cursor to just past the reply header. */ reply.cursor = REPLY_OPTIONS_INDEX; /* * Get the ORO from the packet, if any. */ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_ORO); if (oc != NULL) { if (!evaluate_option_cache(&packet_oro, packet, NULL, NULL, packet->options, NULL, &global_scope, oc, MDL)) { log_error("lease_to_client: error evaluating ORO."); goto exit; } } /* * Find a host record that matches the packet, if any, and is * valid for the shared network the client is on. */ if (find_hosts6(&reply.host, packet, client_id, MDL)) { packet->known = 1; seek_shared_host(&reply.host, reply.shared); } /* Process the client supplied IA's onto the reply buffer. */ reply.ia_count = 0; oc = lookup_option(&dhcpv6_universe, packet->options, D6O_IA_NA); for (; oc != NULL ; oc = oc->next) { isc_result_t status; /* Start counting resources (addresses) offered. */ reply.client_resources = 0; reply.resources_included = ISC_FALSE; status = reply_process_ia_na(&reply, oc); /* * We continue to try other IA's whether we can address * this one or not. Any other result is an immediate fail. */ if ((status != ISC_R_SUCCESS) && (status != ISC_R_NORESOURCES)) goto exit; } oc = lookup_option(&dhcpv6_universe, packet->options, D6O_IA_TA); for (; oc != NULL ; oc = oc->next) { isc_result_t status; /* Start counting resources (addresses) offered. */ reply.client_resources = 0; reply.resources_included = ISC_FALSE; status = reply_process_ia_ta(&reply, oc); /* * We continue to try other IA's whether we can address * this one or not. Any other result is an immediate fail. */ if ((status != ISC_R_SUCCESS) && (status != ISC_R_NORESOURCES)) goto exit; } /* Same for IA_PD's. */ reply.pd_count = 0; oc = lookup_option(&dhcpv6_universe, packet->options, D6O_IA_PD); for (; oc != NULL ; oc = oc->next) { isc_result_t status; /* Start counting resources (prefixes) offered. */ reply.client_resources = 0; reply.resources_included = ISC_FALSE; status = reply_process_ia_pd(&reply, oc); /* * We continue to try other IA_PD's whether we can address * this one or not. Any other result is an immediate fail. */ if ((status != ISC_R_SUCCESS) && (status != ISC_R_NORESOURCES)) goto exit; } /* * Make no reply if we gave no resources and is not * for Information-Request. */ if ((reply.ia_count == 0) && (reply.pd_count == 0)) { if (reply.packet->dhcpv6_msg_type != DHCPV6_INFORMATION_REQUEST) goto exit; /* * Because we only execute statements on a per-IA basis, * we need to execute statements in any non-IA reply to * source configuration. */ execute_statements_in_scope(NULL, reply.packet, NULL, NULL, reply.packet->options, reply.opt_state, &global_scope, reply.shared->group, root_group, NULL); /* Execute statements from class scopes. */ for (i = reply.packet->class_count; i > 0; i--) { execute_statements_in_scope(NULL, reply.packet, NULL, NULL, reply.packet->options, reply.opt_state, &global_scope, reply.packet->classes[i - 1]->group, reply.shared->group, NULL); } /* Bring in any configuration from a host record. */ if (reply.host != NULL) execute_statements_in_scope(NULL, reply.packet, NULL, NULL, reply.packet->options, reply.opt_state, &global_scope, reply.host->group, reply.shared->group, NULL); } /* * RFC3315 section 17.2.2 (Solicit): * * If the server will not assign any addresses to any IAs in a * subsequent Request from the client, the server MUST send an * Advertise message to the client that includes only a Status * Code option with code NoAddrsAvail and a status message for * the user, a Server Identifier option with the server's DUID, * and a Client Identifier option with the client's DUID. * * This has been updated by an errata such that the server * can always send an IA. * * Section 18.2.1 (Request): * * If the server cannot assign any addresses to an IA in the * message from the client, the server MUST include the IA in * the Reply message with no addresses in the IA and a Status * Code option in the IA containing status code NoAddrsAvail. * * Section 18.1.8 (Client Behavior): * * Leave unchanged any information about addresses the client has * recorded in the IA but that were not included in the IA from * the server. * Sends a Renew/Rebind if the IA is not in the Reply message. */ /* * Having stored the client's IA's, store any options that * will fit in the remaining space. */ reply.cursor += store_options6((char *)reply.buf.data + reply.cursor, sizeof(reply.buf) - reply.cursor, reply.opt_state, reply.packet, required_opts_solicit, &packet_oro); /* Return our reply to the caller. */ reply_ret->len = reply.cursor; reply_ret->buffer = NULL; if (!buffer_allocate(&reply_ret->buffer, reply.cursor, MDL)) { log_fatal("No memory to store Reply."); } memcpy(reply_ret->buffer->data, reply.buf.data, reply.cursor); reply_ret->data = reply_ret->buffer->data; /* If appropriate commit and rotate the lease file */ (void) commit_leases_timed(); exit: /* Cleanup. */ if (reply.shared != NULL) shared_network_dereference(&reply.shared, MDL); if (reply.host != NULL) host_dereference(&reply.host, MDL); if (reply.opt_state != NULL) option_state_dereference(&reply.opt_state, MDL); if (reply.packet != NULL) packet_dereference(&reply.packet, MDL); if (reply.client_id.data != NULL) data_string_forget(&reply.client_id, MDL); if (packet_oro.buffer != NULL) data_string_forget(&packet_oro, MDL); reply.renew = reply.rebind = reply.min_prefer = reply.min_valid = 0; reply.cursor = 0; } /* Process a client-supplied IA_NA. This may append options to the tail of * the reply packet being built in the reply_state structure. */ static isc_result_t reply_process_ia_na(struct reply_state *reply, struct option_cache *ia) { isc_result_t status = ISC_R_SUCCESS; u_int32_t iaid; unsigned ia_cursor; struct option_state *packet_ia; struct option_cache *oc; struct data_string ia_data, data; /* Initialize values that will get cleaned up on return. */ packet_ia = NULL; memset(&ia_data, 0, sizeof(ia_data)); memset(&data, 0, sizeof(data)); /* * Note that find_client_address() may set reply->lease. */ /* Make sure there is at least room for the header. */ if ((reply->cursor + IA_NA_OFFSET + 4) > sizeof(reply->buf)) { log_error("reply_process_ia_na: Reply too long for IA."); return ISC_R_NOSPACE; } /* Fetch the IA_NA contents. */ if (!get_encapsulated_IA_state(&packet_ia, &ia_data, reply->packet, ia, IA_NA_OFFSET)) { log_error("reply_process_ia_na: error evaluating ia"); status = ISC_R_FAILURE; goto cleanup; } /* Extract IA_NA header contents. */ iaid = getULong(ia_data.data); reply->renew = getULong(ia_data.data + 4); reply->rebind = getULong(ia_data.data + 8); /* Create an IA_NA structure. */ if (ia_allocate(&reply->ia, iaid, (char *)reply->client_id.data, reply->client_id.len, MDL) != ISC_R_SUCCESS) { log_error("reply_process_ia_na: no memory for ia."); status = ISC_R_NOMEMORY; goto cleanup; } reply->ia->ia_type = D6O_IA_NA; /* Cache pre-existing IA, if any. */ ia_hash_lookup(&reply->old_ia, ia_na_active, (unsigned char *)reply->ia->iaid_duid.data, reply->ia->iaid_duid.len, MDL); /* * Create an option cache to carry the IA_NA option contents, and * execute any user-supplied values into it. */ if (!option_state_allocate(&reply->reply_ia, MDL)) { status = ISC_R_NOMEMORY; goto cleanup; } /* Check & cache the fixed host record. */ if ((reply->host != NULL) && (reply->host->fixed_addr != NULL)) { struct iaddr tmp_addr; if (!evaluate_option_cache(&reply->fixed, NULL, NULL, NULL, NULL, NULL, &global_scope, reply->host->fixed_addr, MDL)) { log_error("reply_process_ia_na: unable to evaluate " "fixed address."); status = ISC_R_FAILURE; goto cleanup; } if (reply->fixed.len < 16) { log_error("reply_process_ia_na: invalid fixed address."); status = DHCP_R_INVALIDARG; goto cleanup; } /* Find the static lease's subnet. */ tmp_addr.len = 16; memcpy(tmp_addr.iabuf, reply->fixed.data, 16); if (find_grouped_subnet(&reply->subnet, reply->shared, tmp_addr, MDL) == 0) log_fatal("Impossible condition at %s:%d.", MDL); reply->static_lease = ISC_TRUE; } else reply->static_lease = ISC_FALSE; /* * Save the cursor position at the start of the IA, so we can * set length and adjust t1/t2 values later. We write a temporary * header out now just in case we decide to adjust the packet * within sub-process functions. */ ia_cursor = reply->cursor; /* Initialize the IA_NA header. First the code. */ putUShort(reply->buf.data + reply->cursor, (unsigned)D6O_IA_NA); reply->cursor += 2; /* Then option length. */ putUShort(reply->buf.data + reply->cursor, 0x0Cu); reply->cursor += 2; /* Then IA_NA header contents; IAID. */ putULong(reply->buf.data + reply->cursor, iaid); reply->cursor += 4; /* We store the client's t1 for now, and may over-ride it later. */ putULong(reply->buf.data + reply->cursor, reply->renew); reply->cursor += 4; /* We store the client's t2 for now, and may over-ride it later. */ putULong(reply->buf.data + reply->cursor, reply->rebind); reply->cursor += 4; /* * For each address in this IA_NA, decide what to do about it. * * Guidelines: * * The client leaves unchanged any information about addresses * it has recorded but are not included ("cancel/break" below). * A not included IA ("cleanup" below) could give a Renew/Rebind. */ oc = lookup_option(&dhcpv6_universe, packet_ia, D6O_IAADDR); reply->min_valid = reply->min_prefer = INFINITE_TIME; reply->client_valid = reply->client_prefer = 0; for (; oc != NULL ; oc = oc->next) { status = reply_process_addr(reply, oc); /* * Canceled means we did not allocate addresses to the * client, but we're "done" with this IA - we set a status * code. So transmit this reply, e.g., move on to the next * IA. */ if (status == ISC_R_CANCELED) break; if ((status != ISC_R_SUCCESS) && (status != ISC_R_ADDRINUSE) && (status != ISC_R_ADDRNOTAVAIL)) goto cleanup; } reply->ia_count++; /* * If we fell through the above and never gave the client * an address, give it one now. */ if ((status != ISC_R_CANCELED) && (reply->client_resources == 0)) { status = find_client_address(reply); if (status == ISC_R_NORESOURCES) { switch (reply->packet->dhcpv6_msg_type) { case DHCPV6_SOLICIT: /* * No address for any IA is handled * by the caller. */ /* FALL THROUGH */ case DHCPV6_REQUEST: /* Section 18.2.1 (Request): * * If the server cannot assign any addresses to * an IA in the message from the client, the * server MUST include the IA in the Reply * message with no addresses in the IA and a * Status Code option in the IA containing * status code NoAddrsAvail. */ option_state_dereference(&reply->reply_ia, MDL); if (!option_state_allocate(&reply->reply_ia, MDL)) { log_error("reply_process_ia_na: No " "memory for option state " "wipe."); status = ISC_R_NOMEMORY; goto cleanup; } if (!set_status_code(STATUS_NoAddrsAvail, "No addresses available " "for this interface.", reply->reply_ia)) { log_error("reply_process_ia_na: Unable " "to set NoAddrsAvail status " "code."); status = ISC_R_FAILURE; goto cleanup; } status = ISC_R_SUCCESS; break; default: /* * RFC 3315 does not tell us to emit a status * code in this condition, or anything else. * * If we included non-allocated addresses * (zeroed lifetimes) in an IA, then the client * will deconfigure them. * * So we want to include the IA even if we * can't give it a new address if it includes * zeroed lifetime addresses. * * We don't want to include the IA if we * provide zero addresses including zeroed * lifetimes. */ if (reply->resources_included) status = ISC_R_SUCCESS; else goto cleanup; break; } } if (status != ISC_R_SUCCESS) goto cleanup; } /* * yes, goto's aren't the best but we also want to avoid extra * indents */ if (status == ISC_R_CANCELED) { /* We're replying with a status code so we still need to * write it out in wire-format to the outbound buffer */ write_to_packet(reply, ia_cursor); goto cleanup; } /* * Handle static leases, we always log stuff and if it's * a hard binding we run any commit statements that we have */ if (reply->static_lease) { char tmp_addr[INET6_ADDRSTRLEN]; log_info("%s NA: address %s to client with duid %s iaid = %d " "static", dhcpv6_type_names[reply->buf.reply.msg_type], inet_ntop(AF_INET6, reply->fixed.data, tmp_addr, sizeof(tmp_addr)), print_hex_1(reply->client_id.len, reply->client_id.data, 60), iaid); /* Write the lease out in wire-format to the outbound buffer */ write_to_packet(reply, ia_cursor); #ifdef NSUPDATE /* Performs DDNS updates if we're configured to do them */ ddns_update_static6(reply); #endif if ((reply->buf.reply.msg_type == DHCPV6_REPLY) && (reply->on_star.on_commit != NULL)) { execute_statements(NULL, reply->packet, NULL, NULL, reply->packet->options, reply->opt_state, NULL, reply->on_star.on_commit, NULL); executable_statement_dereference (&reply->on_star.on_commit, MDL); } goto cleanup; } /* * If we have any addresses log what we are doing. */ if (reply->ia->num_iasubopt != 0) { struct iasubopt *tmp; int i; char tmp_addr[INET6_ADDRSTRLEN]; for (i = 0 ; i < reply->ia->num_iasubopt ; i++) { tmp = reply->ia->iasubopt[i]; log_info("%s NA: address %s to client with duid %s " "iaid = %d valid for %u seconds", dhcpv6_type_names[reply->buf.reply.msg_type], inet_ntop(AF_INET6, &tmp->addr, tmp_addr, sizeof(tmp_addr)), print_hex_1(reply->client_id.len, reply->client_id.data, 60), iaid, tmp->valid); } } /* * If this is not a 'soft' binding, consume the new changes into * the database (if any have been attached to the ia_na). * * Loop through the assigned dynamic addresses, referencing the * leases onto this IA_NA rather than any old ones, and updating * pool timers for each (if any). * * Note that we must do ddns_updates() before we test for lease * reuse (so we'll know if DNS entries are different). To ensure * we don't break any configs, we run on_commit statements before * we do ddns_updates() just in case the former affects the later. * This is symetrical with v4 logic. We always run on_commit and * ddns_udpates() whether a lease is reused or renewed. */ if ((reply->ia->num_iasubopt != 0) && (reply->buf.reply.msg_type == DHCPV6_REPLY)) { int must_commit = 0; struct iasubopt *tmp; struct data_string *ia_id; int i; for (i = 0 ; i < reply->ia->num_iasubopt ; i++) { tmp = reply->ia->iasubopt[i]; if (tmp->ia != NULL) { ia_dereference(&tmp->ia, MDL); } ia_reference(&tmp->ia, reply->ia, MDL); /* If we have anything to do on commit do it now */ if (tmp->on_star.on_commit != NULL) { execute_statements(NULL, reply->packet, NULL, NULL, reply->packet->options, reply->opt_state, &tmp->scope, tmp->on_star.on_commit, &tmp->on_star); executable_statement_dereference (&tmp->on_star.on_commit, MDL); } #if defined (NSUPDATE) /* Perform ddns updates */ oc = lookup_option(&server_universe, reply->opt_state, SV_DDNS_UPDATES); if ((oc == NULL) || evaluate_boolean_option_cache(NULL, reply->packet, NULL, NULL, reply->packet->options, reply->opt_state, &tmp->scope, oc, MDL)) { ddns_updates(reply->packet, NULL, NULL, tmp, NULL, reply->opt_state); } #endif if (!reuse_lease6(reply, tmp)) { /* Commit 'hard' bindings. */ must_commit = 1; renew_lease6(tmp->ipv6_pool, tmp); schedule_lease_timeout(tmp->ipv6_pool); /* Do our threshold check. */ check_pool6_threshold(reply, tmp); } } /* write the IA_NA in wire-format to the outbound buffer */ write_to_packet(reply, ia_cursor); /* Remove any old ia from the hash. */ if (reply->old_ia != NULL) { if (!release_on_roam(reply)) { ia_id = &reply->old_ia->iaid_duid; ia_hash_delete(ia_na_active, (unsigned char *)ia_id->data, ia_id->len, MDL); } ia_dereference(&reply->old_ia, MDL); } /* Put new ia into the hash. */ reply->ia->cltt = cur_time; ia_id = &reply->ia->iaid_duid; ia_hash_add(ia_na_active, (unsigned char *)ia_id->data, ia_id->len, reply->ia, MDL); /* If we couldn't reuse all of the iasubopts, we * must update udpate the lease db */ if (must_commit) { write_ia(reply->ia); } } else { /* write the IA_NA in wire-format to the outbound buffer */ write_to_packet(reply, ia_cursor); schedule_lease_timeout_reply(reply); } cleanup: if (packet_ia != NULL) option_state_dereference(&packet_ia, MDL); if (reply->reply_ia != NULL) option_state_dereference(&reply->reply_ia, MDL); if (ia_data.data != NULL) data_string_forget(&ia_data, MDL); if (data.data != NULL) data_string_forget(&data, MDL); if (reply->ia != NULL) ia_dereference(&reply->ia, MDL); if (reply->old_ia != NULL) ia_dereference(&reply->old_ia, MDL); if (reply->lease != NULL) iasubopt_dereference(&reply->lease, MDL); if (reply->fixed.data != NULL) data_string_forget(&reply->fixed, MDL); if (reply->subnet != NULL) subnet_dereference(&reply->subnet, MDL); if (reply->on_star.on_expiry != NULL) executable_statement_dereference (&reply->on_star.on_expiry, MDL); if (reply->on_star.on_release != NULL) executable_statement_dereference (&reply->on_star.on_release, MDL); /* * ISC_R_CANCELED is a status code used by the addr processing to * indicate we're replying with a status code. This is still a * success at higher layers. */ return((status == ISC_R_CANCELED) ? ISC_R_SUCCESS : status); } /* * Writes the populated IA_xx in wire format to the reply buffer */ void write_to_packet(struct reply_state *reply, unsigned ia_cursor) { reply->cursor += store_options6((char *)reply->buf.data + reply->cursor, sizeof(reply->buf) - reply->cursor, reply->reply_ia, reply->packet, (reply->ia->ia_type != D6O_IA_PD ? required_opts_IA : required_opts_IA_PD), NULL); /* Reset the length of this IA to match what was just written. */ putUShort(reply->buf.data + ia_cursor + 2, reply->cursor - (ia_cursor + 4)); if (reply->ia->ia_type != D6O_IA_TA) { /* Calculate T1/T2 and stuff them in the reply */ set_reply_tee_times(reply, ia_cursor); } } /* * Process an IAADDR within a given IA_xA, storing any IAADDR reply contents * into the reply's current ia-scoped option cache. Returns ISC_R_CANCELED * in the event we are replying with a status code and do not wish to process * more IAADDRs within this IA. */ static isc_result_t reply_process_addr(struct reply_state *reply, struct option_cache *addr) { u_int32_t pref_life, valid_life; struct binding_scope **scope; struct group *group; struct subnet *subnet; struct iaddr tmp_addr; struct option_cache *oc; struct data_string iaaddr, data; isc_result_t status = ISC_R_SUCCESS; #ifdef EUI_64 int invalid_for_eui_64 = 0; #endif /* Initializes values that will be cleaned up. */ memset(&iaaddr, 0, sizeof(iaaddr)); memset(&data, 0, sizeof(data)); /* Note that reply->lease may be set by address_is_owned() */ /* * There is no point trying to process an incoming address if there * is no room for an outgoing address. */ if ((reply->cursor + 28) > sizeof(reply->buf)) { log_error("reply_process_addr: Out of room for address."); return ISC_R_NOSPACE; } /* Extract this IAADDR option. */ if (!evaluate_option_cache(&iaaddr, reply->packet, NULL, NULL, reply->packet->options, NULL, &global_scope, addr, MDL) || (iaaddr.len < IAADDR_OFFSET)) { log_error("reply_process_addr: error evaluating IAADDR."); status = ISC_R_FAILURE; goto cleanup; } /* The first 16 bytes are the IPv6 address. */ pref_life = getULong(iaaddr.data + 16); valid_life = getULong(iaaddr.data + 20); if ((reply->client_valid == 0) || (reply->client_valid > valid_life)) reply->client_valid = valid_life; if ((reply->client_prefer == 0) || (reply->client_prefer > pref_life)) reply->client_prefer = pref_life; /* * Clients may choose to send :: as an address, with the idea to give * hints about preferred-lifetime or valid-lifetime. */ tmp_addr.len = 16; memset(tmp_addr.iabuf, 0, 16); if (!memcmp(iaaddr.data, tmp_addr.iabuf, 16)) { /* Status remains success; we just ignore this one. */ goto cleanup; } /* tmp_addr len remains 16 */ memcpy(tmp_addr.iabuf, iaaddr.data, 16); /* * Verify that this address is on the client's network. */ for (subnet = reply->shared->subnets ; subnet != NULL ; subnet = subnet->next_sibling) { if (addr_eq(subnet_number(tmp_addr, subnet->netmask), subnet->net)) break; } #ifdef EUI_64 if (subnet) { /* If the requested address falls into an EUI-64 pool, then * we need to verify if it has EUI-64 duid AND the requested * address is correct for that duid. If not we treat it just * like an not-on-link request. */ struct ipv6_pool* pool = NULL; struct in6_addr* addr = (struct in6_addr*)(iaaddr.data); if ((find_ipv6_pool(&pool, D6O_IA_NA, addr) == ISC_R_SUCCESS) && (pool->ipv6_pond->use_eui_64) && (!valid_for_eui_64_pool(pool, &reply->client_id, 0, addr))) { log_debug ("Requested address: %s," " not valid for EUI-64 pool", pin6_addr(addr)); invalid_for_eui_64 = 1; } } #endif /* Address not found on shared network. */ #ifdef EUI_64 if ((subnet == NULL) || invalid_for_eui_64) { #else if (subnet == NULL) { #endif /* Ignore this address on 'soft' bindings. */ if (reply->packet->dhcpv6_msg_type == DHCPV6_SOLICIT) { /* disable rapid commit */ reply->buf.reply.msg_type = DHCPV6_ADVERTISE; delete_option(&dhcpv6_universe, reply->opt_state, D6O_RAPID_COMMIT); /* status remains success */ goto cleanup; } /* * RFC3315 section 18.2.1: * * If the server finds that the prefix on one or more IP * addresses in any IA in the message from the client is not * appropriate for the link to which the client is connected, * the server MUST return the IA to the client with a Status * Code option with the value NotOnLink. */ if (reply->packet->dhcpv6_msg_type == DHCPV6_REQUEST) { /* Rewind the IA_NA to empty. */ option_state_dereference(&reply->reply_ia, MDL); if (!option_state_allocate(&reply->reply_ia, MDL)) { log_error("reply_process_addr: No memory for " "option state wipe."); status = ISC_R_NOMEMORY; goto cleanup; } /* Append a NotOnLink status code. */ if (!set_status_code(STATUS_NotOnLink, "Address not for use on this " "link.", reply->reply_ia)) { log_error("reply_process_addr: Failure " "setting status code."); status = ISC_R_FAILURE; goto cleanup; } /* Fin (no more IAADDRs). */ status = ISC_R_CANCELED; goto cleanup; } /* * RFC3315 sections 18.2.3 and 18.2.4 have identical language: * * If the server finds that any of the addresses are not * appropriate for the link to which the client is attached, * the server returns the address to the client with lifetimes * of 0. */ if ((reply->packet->dhcpv6_msg_type != DHCPV6_RENEW) && (reply->packet->dhcpv6_msg_type != DHCPV6_REBIND)) { log_error("It is impossible to lease a client that is " "not sending a solicit, request, renew, or " "rebind."); status = ISC_R_FAILURE; goto cleanup; } reply->send_prefer = reply->send_valid = 0; goto send_addr; } /* Verify the address belongs to the client. */ if (!address_is_owned(reply, &tmp_addr)) { /* * For solicit and request, any addresses included are * 'requested' addresses. For rebind, we actually have * no direction on what to do from 3315 section 18.2.4! * So I think the best bet is to try and give it out, and if * we can't, zero lifetimes. */ if ((reply->packet->dhcpv6_msg_type == DHCPV6_SOLICIT) || (reply->packet->dhcpv6_msg_type == DHCPV6_REQUEST) || (reply->packet->dhcpv6_msg_type == DHCPV6_REBIND)) { status = reply_process_try_addr(reply, &tmp_addr); /* * If the address is in use, or isn't in any dynamic * range, continue as normal. If any other error was * found, error out. */ if ((status != ISC_R_SUCCESS) && (status != ISC_R_ADDRINUSE) && (status != ISC_R_ADDRNOTAVAIL)) goto cleanup; /* * If we didn't honor this lease, for solicit and * request we simply omit it from our answer. For * rebind, we send it with zeroed lifetimes. */ if (reply->lease == NULL) { if (reply->packet->dhcpv6_msg_type == DHCPV6_REBIND) { reply->send_prefer = 0; reply->send_valid = 0; goto send_addr; } /* status remains success - ignore */ goto cleanup; } /* * RFC3315 section 18.2.3: * * If the server cannot find a client entry for the IA the * server returns the IA containing no addresses with a Status * Code option set to NoBinding in the Reply message. * * On mismatch we (ab)use this pretending we have not the IA * as soon as we have not an address. */ } else if (reply->packet->dhcpv6_msg_type == DHCPV6_RENEW) { /* Rewind the IA_NA to empty. */ option_state_dereference(&reply->reply_ia, MDL); if (!option_state_allocate(&reply->reply_ia, MDL)) { log_error("reply_process_addr: No memory for " "option state wipe."); status = ISC_R_NOMEMORY; goto cleanup; } /* Append a NoBinding status code. */ if (!set_status_code(STATUS_NoBinding, "Address not bound to this " "interface.", reply->reply_ia)) { log_error("reply_process_addr: Unable to " "attach status code."); status = ISC_R_FAILURE; goto cleanup; } /* Fin (no more IAADDRs). */ status = ISC_R_CANCELED; goto cleanup; } else { log_error("It is impossible to lease a client that is " "not sending a solicit, request, renew, or " "rebind message."); status = ISC_R_FAILURE; goto cleanup; } } if (reply->static_lease) { if (reply->host == NULL) log_fatal("Impossible condition at %s:%d.", MDL); scope = &global_scope; group = reply->subnet->group; } else { if (reply->lease == NULL) log_fatal("Impossible condition at %s:%d.", MDL); scope = &reply->lease->scope; group = reply->lease->ipv6_pool->ipv6_pond->group; } /* * If client_resources is nonzero, then the reply_process_is_addressed * function has executed configuration state into the reply option * cache. We will use that valid cache to derive configuration for * whether or not to engage in additional addresses, and similar. */ if (reply->client_resources != 0) { unsigned limit = 1; /* * Does this client have "enough" addresses already? Default * to one. Everybody gets one, and one should be enough for * anybody. */ oc = lookup_option(&server_universe, reply->opt_state, SV_LIMIT_ADDRS_PER_IA); if (oc != NULL) { if (!evaluate_option_cache(&data, reply->packet, NULL, NULL, reply->packet->options, reply->opt_state, scope, oc, MDL) || (data.len != 4)) { log_error("reply_process_addr: unable to " "evaluate addrs-per-ia value."); status = ISC_R_FAILURE; goto cleanup; } limit = getULong(data.data); data_string_forget(&data, MDL); } /* * If we wish to limit the client to a certain number of * addresses, then omit the address from the reply. */ if (reply->client_resources >= limit) goto cleanup; } status = reply_process_is_addressed(reply, scope, group); if (status != ISC_R_SUCCESS) goto cleanup; send_addr: status = reply_process_send_addr(reply, &tmp_addr); cleanup: if (iaaddr.data != NULL) data_string_forget(&iaaddr, MDL); if (data.data != NULL) data_string_forget(&data, MDL); if (reply->lease != NULL) iasubopt_dereference(&reply->lease, MDL); return status; } /* * Verify the address belongs to the client. If we've got a host * record with a fixed address, it has to be the assigned address * (fault out all else). Otherwise it's a dynamic address, so lookup * that address and make sure it belongs to this DUID:IAID pair. */ static isc_boolean_t address_is_owned(struct reply_state *reply, struct iaddr *addr) { int i; struct ipv6_pond *pond; /* * This faults out addresses that don't match fixed addresses. */ if (reply->static_lease) { if (reply->fixed.data == NULL) log_fatal("Impossible condition at %s:%d.", MDL); if (memcmp(addr->iabuf, reply->fixed.data, 16) == 0) return (ISC_TRUE); return (ISC_FALSE); } if ((reply->old_ia == NULL) || (reply->old_ia->num_iasubopt == 0)) return (ISC_FALSE); for (i = 0 ; i < reply->old_ia->num_iasubopt ; i++) { struct iasubopt *tmp; tmp = reply->old_ia->iasubopt[i]; if (memcmp(addr->iabuf, &tmp->addr, 16) == 0) { if (lease6_usable(tmp) == ISC_FALSE) { return (ISC_FALSE); } pond = tmp->ipv6_pool->ipv6_pond; if (((pond->prohibit_list != NULL) && (permitted(reply->packet, pond->prohibit_list))) || ((pond->permit_list != NULL) && (!permitted(reply->packet, pond->permit_list)))) return (ISC_FALSE); iasubopt_reference(&reply->lease, tmp, MDL); return (ISC_TRUE); } } return (ISC_FALSE); } /* Process a client-supplied IA_TA. This may append options to the tail of * the reply packet being built in the reply_state structure. */ static isc_result_t reply_process_ia_ta(struct reply_state *reply, struct option_cache *ia) { isc_result_t status = ISC_R_SUCCESS; u_int32_t iaid; unsigned ia_cursor; struct option_state *packet_ia; struct option_cache *oc; struct data_string ia_data, data; struct data_string iaaddr; u_int32_t pref_life, valid_life; struct iaddr tmp_addr; /* Initialize values that will get cleaned up on return. */ packet_ia = NULL; memset(&ia_data, 0, sizeof(ia_data)); memset(&data, 0, sizeof(data)); memset(&iaaddr, 0, sizeof(iaaddr)); /* Make sure there is at least room for the header. */ if ((reply->cursor + IA_TA_OFFSET + 4) > sizeof(reply->buf)) { log_error("reply_process_ia_ta: Reply too long for IA."); return ISC_R_NOSPACE; } /* Fetch the IA_TA contents. */ if (!get_encapsulated_IA_state(&packet_ia, &ia_data, reply->packet, ia, IA_TA_OFFSET)) { log_error("reply_process_ia_ta: error evaluating ia"); status = ISC_R_FAILURE; goto cleanup; } /* Extract IA_TA header contents. */ iaid = getULong(ia_data.data); /* Create an IA_TA structure. */ if (ia_allocate(&reply->ia, iaid, (char *)reply->client_id.data, reply->client_id.len, MDL) != ISC_R_SUCCESS) { log_error("reply_process_ia_ta: no memory for ia."); status = ISC_R_NOMEMORY; goto cleanup; } reply->ia->ia_type = D6O_IA_TA; /* Cache pre-existing IA, if any. */ ia_hash_lookup(&reply->old_ia, ia_ta_active, (unsigned char *)reply->ia->iaid_duid.data, reply->ia->iaid_duid.len, MDL); /* * Create an option cache to carry the IA_TA option contents, and * execute any user-supplied values into it. */ if (!option_state_allocate(&reply->reply_ia, MDL)) { status = ISC_R_NOMEMORY; goto cleanup; } /* * Temporary leases are dynamic by definition. */ reply->static_lease = ISC_FALSE; /* * Save the cursor position at the start of the IA, so we can * set length later. We write a temporary * header out now just in case we decide to adjust the packet * within sub-process functions. */ ia_cursor = reply->cursor; /* Initialize the IA_TA header. First the code. */ putUShort(reply->buf.data + reply->cursor, (unsigned)D6O_IA_TA); reply->cursor += 2; /* Then option length. */ putUShort(reply->buf.data + reply->cursor, 0x04u); reply->cursor += 2; /* Then IA_TA header contents; IAID. */ putULong(reply->buf.data + reply->cursor, iaid); reply->cursor += 4; /* * Deal with an IAADDR for lifetimes. * For all or none, process IAADDRs as hints. */ reply->min_valid = reply->min_prefer = INFINITE_TIME; reply->client_valid = reply->client_prefer = 0; oc = lookup_option(&dhcpv6_universe, packet_ia, D6O_IAADDR); for (; oc != NULL; oc = oc->next) { memset(&iaaddr, 0, sizeof(iaaddr)); if (!evaluate_option_cache(&iaaddr, reply->packet, NULL, NULL, reply->packet->options, NULL, &global_scope, oc, MDL) || (iaaddr.len < IAADDR_OFFSET)) { log_error("reply_process_ia_ta: error " "evaluating IAADDR."); status = ISC_R_FAILURE; goto cleanup; } /* The first 16 bytes are the IPv6 address. */ pref_life = getULong(iaaddr.data + 16); valid_life = getULong(iaaddr.data + 20); if ((reply->client_valid == 0) || (reply->client_valid > valid_life)) reply->client_valid = valid_life; if ((reply->client_prefer == 0) || (reply->client_prefer > pref_life)) reply->client_prefer = pref_life; /* Nothing more if something has failed. */ if (status == ISC_R_CANCELED) continue; tmp_addr.len = 16; memcpy(tmp_addr.iabuf, iaaddr.data, 16); if (!temporary_is_available(reply, &tmp_addr)) goto bad_temp; status = reply_process_is_addressed(reply, &reply->lease->scope, reply->lease->ipv6_pool->ipv6_pond->group); if (status != ISC_R_SUCCESS) goto bad_temp; status = reply_process_send_addr(reply, &tmp_addr); if (status != ISC_R_SUCCESS) goto bad_temp; if (reply->lease != NULL) iasubopt_dereference(&reply->lease, MDL); continue; bad_temp: /* Rewind the IA_TA to empty. */ option_state_dereference(&reply->reply_ia, MDL); if (!option_state_allocate(&reply->reply_ia, MDL)) { status = ISC_R_NOMEMORY; goto cleanup; } status = ISC_R_CANCELED; reply->client_resources = 0; reply->resources_included = ISC_FALSE; if (reply->lease != NULL) iasubopt_dereference(&reply->lease, MDL); } reply->ia_count++; /* * Give the client temporary addresses. */ if (reply->client_resources != 0) goto store; status = find_client_temporaries(reply); if (status == ISC_R_NORESOURCES) { switch (reply->packet->dhcpv6_msg_type) { case DHCPV6_SOLICIT: /* * No address for any IA is handled * by the caller. */ /* FALL THROUGH */ case DHCPV6_REQUEST: /* Section 18.2.1 (Request): * * If the server cannot assign any addresses to * an IA in the message from the client, the * server MUST include the IA in the Reply * message with no addresses in the IA and a * Status Code option in the IA containing * status code NoAddrsAvail. */ option_state_dereference(&reply->reply_ia, MDL); if (!option_state_allocate(&reply->reply_ia, MDL)) { log_error("reply_process_ia_ta: No " "memory for option state wipe."); status = ISC_R_NOMEMORY; goto cleanup; } if (!set_status_code(STATUS_NoAddrsAvail, "No addresses available " "for this interface.", reply->reply_ia)) { log_error("reply_process_ia_ta: Unable " "to set NoAddrsAvail status code."); status = ISC_R_FAILURE; goto cleanup; } status = ISC_R_SUCCESS; break; default: /* * We don't want to include the IA if we * provide zero addresses including zeroed * lifetimes. */ if (reply->resources_included) status = ISC_R_SUCCESS; else goto cleanup; break; } } else if (status != ISC_R_SUCCESS) goto cleanup; store: /* * yes, goto's aren't the best but we also want to avoid extra * indents */ if (status == ISC_R_CANCELED) { /* We're replying with a status code so we still need to * write it out in wire-format to the outbound buffer */ write_to_packet(reply, ia_cursor); goto cleanup; } /* * If we have any addresses log what we are doing. */ if (reply->ia->num_iasubopt != 0) { struct iasubopt *tmp; int i; char tmp_addr[INET6_ADDRSTRLEN]; for (i = 0 ; i < reply->ia->num_iasubopt ; i++) { tmp = reply->ia->iasubopt[i]; log_info("%s TA: address %s to client with duid %s " "iaid = %d valid for %u seconds", dhcpv6_type_names[reply->buf.reply.msg_type], inet_ntop(AF_INET6, &tmp->addr, tmp_addr, sizeof(tmp_addr)), print_hex_1(reply->client_id.len, reply->client_id.data, 60), iaid, tmp->valid); } } /* * For hard bindings we consume the new changes into * the database (if any have been attached to the ia_ta). * * Loop through the assigned dynamic addresses, referencing the * leases onto this IA_TA rather than any old ones, and updating * pool timers for each (if any). */ if ((reply->ia->num_iasubopt != 0) && (reply->buf.reply.msg_type == DHCPV6_REPLY)) { int must_commit = 0; struct iasubopt *tmp; struct data_string *ia_id; int i; for (i = 0 ; i < reply->ia->num_iasubopt ; i++) { tmp = reply->ia->iasubopt[i]; if (tmp->ia != NULL) ia_dereference(&tmp->ia, MDL); ia_reference(&tmp->ia, reply->ia, MDL); /* If we have anything to do on commit do it now */ if (tmp->on_star.on_commit != NULL) { execute_statements(NULL, reply->packet, NULL, NULL, reply->packet->options, reply->opt_state, &tmp->scope, tmp->on_star.on_commit, &tmp->on_star); executable_statement_dereference (&tmp->on_star.on_commit, MDL); } #if defined (NSUPDATE) /* * Perform ddns updates. */ oc = lookup_option(&server_universe, reply->opt_state, SV_DDNS_UPDATES); if ((oc == NULL) || evaluate_boolean_option_cache(NULL, reply->packet, NULL, NULL, reply->packet->options, reply->opt_state, &tmp->scope, oc, MDL)) { ddns_updates(reply->packet, NULL, NULL, tmp, NULL, reply->opt_state); } #endif if (!reuse_lease6(reply, tmp)) { /* Commit 'hard' bindings. */ must_commit = 1; renew_lease6(tmp->ipv6_pool, tmp); schedule_lease_timeout(tmp->ipv6_pool); /* Do our threshold check. */ check_pool6_threshold(reply, tmp); } } /* write the IA_TA in wire-format to the outbound buffer */ write_to_packet(reply, ia_cursor); /* Remove any old ia from the hash. */ if (reply->old_ia != NULL) { if (!release_on_roam(reply)) { ia_id = &reply->old_ia->iaid_duid; ia_hash_delete(ia_ta_active, (unsigned char *)ia_id->data, ia_id->len, MDL); } ia_dereference(&reply->old_ia, MDL); } /* Put new ia into the hash. */ reply->ia->cltt = cur_time; ia_id = &reply->ia->iaid_duid; ia_hash_add(ia_ta_active, (unsigned char *)ia_id->data, ia_id->len, reply->ia, MDL); /* If we couldn't reuse all of the iasubopts, we * must update udpate the lease db */ if (must_commit) { write_ia(reply->ia); } } else { /* write the IA_TA in wire-format to the outbound buffer */ write_to_packet(reply, ia_cursor); schedule_lease_timeout_reply(reply); } cleanup: if (packet_ia != NULL) option_state_dereference(&packet_ia, MDL); if (iaaddr.data != NULL) data_string_forget(&iaaddr, MDL); if (reply->reply_ia != NULL) option_state_dereference(&reply->reply_ia, MDL); if (ia_data.data != NULL) data_string_forget(&ia_data, MDL); if (data.data != NULL) data_string_forget(&data, MDL); if (reply->ia != NULL) ia_dereference(&reply->ia, MDL); if (reply->old_ia != NULL) ia_dereference(&reply->old_ia, MDL); if (reply->lease != NULL) iasubopt_dereference(&reply->lease, MDL); /* * ISC_R_CANCELED is a status code used by the addr processing to * indicate we're replying with other addresses. This is still a * success at higher layers. */ return((status == ISC_R_CANCELED) ? ISC_R_SUCCESS : status); } /* * Determines if a lease (iasubopt) can be reused without extending it. * If dhcp-cache-threshold is greater than zero (i.e enabled) then * a lease may be reused without going through a full renewal if * it meets all the requirements. In short it must be active, younger * than the threshold, and not have DNS changes. * * If it is determined that it can be reused, that a call to * shorten_lifetimes() is made to reduce the valid and preferred lifetimes * sent to the client by the age of the lease. * * Returns 1 if lease can be reused, 0 otherwise */ int reuse_lease6(struct reply_state *reply, struct iasubopt *lease) { int threshold = DEFAULT_CACHE_THRESHOLD; struct option_cache* oc = NULL; struct data_string d1; time_t age; time_t limit; int reuse_it = 0; /* In order to even qualify for reuse consideration: * 1. Lease must be active * 2. It must have been accepted at least once * 3. DNS info must not have changed */ if ((lease->state != FTS_ACTIVE) || (lease->hard_lifetime_end_time == 0) || (lease->ddns_cb != NULL)) { return (0); } /* Look up threshold value */ memset(&d1, 0, sizeof(struct data_string)); oc = lookup_option(&server_universe, reply->opt_state, SV_CACHE_THRESHOLD); if (oc && evaluate_option_cache(&d1, reply->packet, NULL, NULL, reply->packet->options, reply->opt_state, &lease->scope, oc, MDL)) { if (d1.len == 1 && (d1.data[0] < 100)) { threshold = d1.data[0]; } data_string_forget(&d1, MDL); } if (threshold <= 0) { return (0); } if (lease->valid >= MAX_TIME) { /* Infinite leases are always reused. We have to make * a choice because we cannot determine when they actually * began, so we either always reuse them or we never do. */ log_debug ("reusing infinite lease for: %s%s", pin6_addr(&lease->addr), iasubopt_plen_str(lease)); return (1); } age = cur_tv.tv_sec - (lease->hard_lifetime_end_time - lease->valid); if (lease->valid <= (INT_MAX / threshold)) limit = lease->valid * threshold / 100; else limit = lease->valid / 100 * threshold; if (age < limit) { /* Reduce valid/preferred going to the client by age */ shorten_lifetimes(reply, lease, age, threshold); reuse_it = 1; } return (reuse_it); } /* * Reduces the valid and preferred lifetimes for a given lease (iasubopt) * * We cannot determine until after a iasubopt has been added to * the reply if the lease can be reused. Therefore, when we do reuse a * lease we need a way to alter the lifetimes that will be sent to the client. * That's where this function comes in handy: * * Locate the iasubopt by it's address within the reply the reduce both * the preferred and valid lifetimes by the given number of seconds. * * Note that this function, by necessity, works directly with the * option_cache data. Sort of a no-no but I don't have any better ideas. */ void shorten_lifetimes(struct reply_state *reply, struct iasubopt *lease, time_t age, int threshold) { struct option_cache* oc = NULL; int subopt_type; int addr_offset; int pref_offset; int val_offset; int exp_length; if (reply->ia->ia_type != D6O_IA_PD) { subopt_type = D6O_IAADDR; addr_offset = IASUBOPT_NA_ADDR_OFFSET; pref_offset = IASUBOPT_NA_PREF_OFFSET; val_offset = IASUBOPT_NA_VALID_OFFSET; exp_length = IASUBOPT_NA_LEN; } else { subopt_type = D6O_IAPREFIX; addr_offset = IASUBOPT_PD_PREFIX_OFFSET; pref_offset = IASUBOPT_PD_PREF_OFFSET; val_offset = IASUBOPT_PD_VALID_OFFSET; exp_length = IASUBOPT_PD_LEN; } // loop through the iasubopts for the one that matches this lease oc = lookup_option(&dhcpv6_universe, reply->reply_ia, subopt_type); for (; oc != NULL ; oc = oc->next) { if (oc->data.data == NULL || oc->data.len != exp_length) { /* shouldn't happen */ continue; } /* If address matches (and for PDs the prefix len matches) * we assume this is our subopt, so update the lifetimes */ if (!memcmp(oc->data.data + addr_offset, &lease->addr, 16) && (subopt_type != D6O_IAPREFIX || (oc->data.data[IASUBOPT_PD_PREFLEN_OFFSET] == lease->plen))) { u_int32_t pref_life = getULong(oc->data.data + pref_offset); u_int32_t valid_life = getULong(oc->data.data + val_offset); if (pref_life < MAX_TIME && pref_life > age) { pref_life -= age; putULong((unsigned char*)(oc->data.data) + pref_offset, pref_life); if (reply->min_prefer > pref_life) { reply->min_prefer = pref_life; } } if (valid_life < MAX_TIME && valid_life > age) { valid_life -= age; putULong((unsigned char*)(oc->data.data) + val_offset, valid_life); if (reply->min_valid > reply->send_valid) { reply->min_valid = valid_life; } } log_debug ("Reusing lease for: %s%s, " "age %ld secs < %d%%," " sending shortened lifetimes -" " preferred: %u, valid %u", pin6_addr(&lease->addr), iasubopt_plen_str(lease), (long)age, threshold, pref_life, valid_life); break; } } } /* * Verify the temporary address is available. */ static isc_boolean_t temporary_is_available(struct reply_state *reply, struct iaddr *addr) { struct in6_addr tmp_addr; struct subnet *subnet; struct ipv6_pool *pool = NULL; struct ipv6_pond *pond = NULL; int i; memcpy(&tmp_addr, addr->iabuf, sizeof(tmp_addr)); /* * Clients may choose to send :: as an address, with the idea to give * hints about preferred-lifetime or valid-lifetime. * So this is not a request for this address. */ if (IN6_IS_ADDR_UNSPECIFIED(&tmp_addr)) return ISC_FALSE; /* * Verify that this address is on the client's network. */ for (subnet = reply->shared->subnets ; subnet != NULL ; subnet = subnet->next_sibling) { if (addr_eq(subnet_number(*addr, subnet->netmask), subnet->net)) break; } /* Address not found on shared network. */ if (subnet == NULL) return ISC_FALSE; /* * Check if this address is owned (must be before next step). */ if (address_is_owned(reply, addr)) return ISC_TRUE; /* * Verify that this address is in a temporary pool and try to get it. */ for (pond = reply->shared->ipv6_pond; pond != NULL; pond = pond->next) { if (((pond->prohibit_list != NULL) && (permitted(reply->packet, pond->prohibit_list))) || ((pond->permit_list != NULL) && (!permitted(reply->packet, pond->permit_list)))) continue; for (i = 0 ; (pool = pond->ipv6_pools[i]) != NULL ; i++) { if (pool->pool_type != D6O_IA_TA) continue; if (ipv6_in_pool(&tmp_addr, pool)) break; } if (pool != NULL) break; } if (pool == NULL) return ISC_FALSE; if (lease6_exists(pool, &tmp_addr)) return ISC_FALSE; if (iasubopt_allocate(&reply->lease, MDL) != ISC_R_SUCCESS) return ISC_FALSE; reply->lease->addr = tmp_addr; reply->lease->plen = 0; /* Default is soft binding for 2 minutes. */ if (add_lease6(pool, reply->lease, cur_time + 120) != ISC_R_SUCCESS) return ISC_FALSE; return ISC_TRUE; } /* * Get a temporary address per prefix. */ static isc_result_t find_client_temporaries(struct reply_state *reply) { int i; struct ipv6_pool *p = NULL; struct ipv6_pond *pond; isc_result_t status = ISC_R_NORESOURCES;; unsigned int attempts; struct iaddr send_addr; /* * Do a quick walk through of the ponds and pools * to see if we have any prefix pools */ for (pond = reply->shared->ipv6_pond; pond != NULL; pond = pond->next) { if (pond->ipv6_pools == NULL) continue; for (i = 0; (p = pond->ipv6_pools[i]) != NULL; i++) { if (p->pool_type == D6O_IA_TA) break; } if (p != NULL) break; } /* If we get here and p is NULL we have no useful pools */ if (p == NULL) { log_debug("Unable to get client addresses: " "no IPv6 pools on this shared network"); return ISC_R_NORESOURCES; } /* * We have at least one pool that could provide an address * Now we walk through the ponds and pools again and check * to see if the client is permitted and if an address is * available */ for (pond = reply->shared->ipv6_pond; pond != NULL; pond = pond->next) { if (((pond->prohibit_list != NULL) && (permitted(reply->packet, pond->prohibit_list))) || ((pond->permit_list != NULL) && (!permitted(reply->packet, pond->permit_list)))) continue; for (i = 0; (p = pond->ipv6_pools[i]) != NULL; i++) { if (p->pool_type != D6O_IA_TA) { continue; } /* * Get an address in this temporary pool. */ status = create_lease6(p, &reply->lease, &attempts, &reply->client_id, cur_time + 120); if (status != ISC_R_SUCCESS) { log_debug("Unable to get a temporary address."); goto cleanup; } status = reply_process_is_addressed(reply, &reply->lease->scope, pond->group); if (status != ISC_R_SUCCESS) { goto cleanup; } send_addr.len = 16; memcpy(send_addr.iabuf, &reply->lease->addr, 16); status = reply_process_send_addr(reply, &send_addr); if (status != ISC_R_SUCCESS) { goto cleanup; } /* * reply->lease can't be null as we use it above * add check if that changes */ iasubopt_dereference(&reply->lease, MDL); } } cleanup: if (reply->lease != NULL) { iasubopt_dereference(&reply->lease, MDL); } return status; } /* * This function only returns failure on 'hard' failures. If it succeeds, * it will leave a lease structure behind. */ static isc_result_t reply_process_try_addr(struct reply_state *reply, struct iaddr *addr) { isc_result_t status = ISC_R_ADDRNOTAVAIL; struct ipv6_pool *pool = NULL; struct ipv6_pond *pond = NULL; int i; struct data_string data_addr; if ((reply == NULL) || (reply->shared == NULL) || (addr == NULL) || (reply->lease != NULL)) return (DHCP_R_INVALIDARG); /* * Do a quick walk through of the ponds and pools * to see if we have any NA address pools */ for (pond = reply->shared->ipv6_pond; pond != NULL; pond = pond->next) { if (pond->ipv6_pools == NULL) continue; for (i = 0; ; i++) { pool = pond->ipv6_pools[i]; if ((pool == NULL) || (pool->pool_type == D6O_IA_NA)) break; } if (pool != NULL) break; } /* If we get here and p is NULL we have no useful pools */ if (pool == NULL) { return (ISC_R_ADDRNOTAVAIL); } memset(&data_addr, 0, sizeof(data_addr)); data_addr.len = addr->len; data_addr.data = addr->iabuf; /* * We have at least one pool that could provide an address * Now we walk through the ponds and pools again and check * to see if the client is permitted and if an address is * available * * Within a given pond we start looking at the last pool we * allocated from, unless it had a collision trying to allocate * an address. This will tend to move us into less-filled pools. */ for (pond = reply->shared->ipv6_pond; pond != NULL; pond = pond->next) { if (((pond->prohibit_list != NULL) && (permitted(reply->packet, pond->prohibit_list))) || ((pond->permit_list != NULL) && (!permitted(reply->packet, pond->permit_list)))) continue; for (i = 0 ; (pool = pond->ipv6_pools[i]) != NULL ; i++) { if (pool->pool_type != D6O_IA_NA) continue; status = try_client_v6_address(&reply->lease, pool, &data_addr); if (status == ISC_R_SUCCESS) break; } if (status == ISC_R_SUCCESS) break; } /* Note that this is just pedantry. There is no allocation to free. */ data_string_forget(&data_addr, MDL); /* Return just the most recent status... */ return (status); } /* Look around for an address to give the client. First, look through the * old IA for addresses we can extend. Second, try to allocate a new address. * Finally, actually add that address into the current reply IA. */ static isc_result_t find_client_address(struct reply_state *reply) { struct iaddr send_addr; isc_result_t status = ISC_R_NORESOURCES; struct iasubopt *lease, *best_lease = NULL; struct binding_scope **scope; struct group *group; int i; if (reply->static_lease) { if (reply->host == NULL) return DHCP_R_INVALIDARG; send_addr.len = 16; memcpy(send_addr.iabuf, reply->fixed.data, 16); scope = &global_scope; group = reply->subnet->group; goto send_addr; } if (reply->old_ia != NULL) { for (i = 0 ; i < reply->old_ia->num_iasubopt ; i++) { struct shared_network *candidate_shared; struct ipv6_pond *pond; lease = reply->old_ia->iasubopt[i]; candidate_shared = lease->ipv6_pool->shared_network; pond = lease->ipv6_pool->ipv6_pond; /* * Look for the best lease on the client's shared * network, that is still permitted */ if ((candidate_shared != reply->shared) || (lease6_usable(lease) != ISC_TRUE)) continue; if (((pond->prohibit_list != NULL) && (permitted(reply->packet, pond->prohibit_list))) || ((pond->permit_list != NULL) && (!permitted(reply->packet, pond->permit_list)))) continue; best_lease = lease_compare(lease, best_lease); } } /* Try to pick a new address if we didn't find one, or if we found an * abandoned lease. */ if ((best_lease == NULL) || (best_lease->state == FTS_ABANDONED)) { status = pick_v6_address(reply); } else if (best_lease != NULL) { iasubopt_reference(&reply->lease, best_lease, MDL); status = ISC_R_SUCCESS; } /* Pick the abandoned lease as a last resort. */ if ((status == ISC_R_NORESOURCES) && (best_lease != NULL)) { /* I don't see how this is supposed to be done right now. */ log_error("Best match for DUID %s is an abandoned address," " This may be a result of multiple clients attempting" " to use this DUID", print_hex_1(reply->client_id.len, reply->client_id.data, 60)); /* iasubopt_reference(&reply->lease, best_lease, MDL); */ } /* Give up now if we didn't find a lease. */ if (status != ISC_R_SUCCESS) return status; if (reply->lease == NULL) log_fatal("Impossible condition at %s:%d.", MDL); /* Draw binding scopes from the lease's binding scope, and config * from the lease's containing subnet and higher. Note that it may * be desirable to place the group attachment directly in the pool. */ scope = &reply->lease->scope; group = reply->lease->ipv6_pool->ipv6_pond->group; send_addr.len = 16; memcpy(send_addr.iabuf, &reply->lease->addr, 16); send_addr: status = reply_process_is_addressed(reply, scope, group); if (status != ISC_R_SUCCESS) return status; status = reply_process_send_addr(reply, &send_addr); return status; } /* Once an address is found for a client, perform several common functions; * Calculate and store valid and preferred lease times, draw client options * into the option state. */ static isc_result_t reply_process_is_addressed(struct reply_state *reply, struct binding_scope **scope, struct group *group) { isc_result_t status = ISC_R_SUCCESS; struct data_string data; struct option_cache *oc; struct option_state *tmp_options = NULL; struct on_star *on_star; int i; /* Initialize values we will cleanup. */ memset(&data, 0, sizeof(data)); /* * Find the proper on_star block to use. We use the * one in the lease if we have a lease or the one in * the reply if we don't have a lease because this is * a static instance */ if (reply->lease) { on_star = &reply->lease->on_star; } else { on_star = &reply->on_star; } /* * Bring in the root configuration. We only do this to bring * in the on * statements, as we didn't have the lease available * we did it the first time. */ option_state_allocate(&tmp_options, MDL); execute_statements_in_scope(NULL, reply->packet, NULL, NULL, reply->packet->options, tmp_options, &global_scope, root_group, NULL, on_star); if (tmp_options != NULL) { option_state_dereference(&tmp_options, MDL); } /* * Bring configured options into the root packet level cache - start * with the lease's closest enclosing group (passed in by the caller * as 'group'). */ execute_statements_in_scope(NULL, reply->packet, NULL, NULL, reply->packet->options, reply->opt_state, scope, group, root_group, on_star); /* Execute statements from class scopes. */ for (i = reply->packet->class_count; i > 0; i--) { execute_statements_in_scope(NULL, reply->packet, NULL, NULL, reply->packet->options, reply->opt_state, scope, reply->packet->classes[i - 1]->group, group, on_star); } /* * If there is a host record, over-ride with values configured there, * without re-evaluating configuration from the previously executed * group or its common enclosers. */ if (reply->host != NULL) execute_statements_in_scope(NULL, reply->packet, NULL, NULL, reply->packet->options, reply->opt_state, scope, reply->host->group, group, on_star); /* Determine valid lifetime. */ if (reply->client_valid == 0) reply->send_valid = DEFAULT_DEFAULT_LEASE_TIME; else reply->send_valid = reply->client_valid; oc = lookup_option(&server_universe, reply->opt_state, SV_DEFAULT_LEASE_TIME); if (oc != NULL) { if (!evaluate_option_cache(&data, reply->packet, NULL, NULL, reply->packet->options, reply->opt_state, scope, oc, MDL) || (data.len != 4)) { log_error("reply_process_is_addressed: unable to " "evaluate default lease time"); status = ISC_R_FAILURE; goto cleanup; } reply->send_valid = getULong(data.data); data_string_forget(&data, MDL); } /* Check to see if the lease time would cause us to wrap * in which case we make it infinite. * The following doesn't work on at least some systems: * (cur_time + reply->send_valid < cur_time) */ if (reply->send_valid != INFINITE_TIME) { time_t test_time = cur_time + reply->send_valid; if (test_time < cur_time) reply->send_valid = INFINITE_TIME; } if (reply->client_prefer == 0) reply->send_prefer = reply->send_valid; else reply->send_prefer = reply->client_prefer; if ((reply->send_prefer >= reply->send_valid) && (reply->send_valid != INFINITE_TIME)) reply->send_prefer = (reply->send_valid / 2) + (reply->send_valid / 8); oc = lookup_option(&server_universe, reply->opt_state, SV_PREFER_LIFETIME); if (oc != NULL) { if (!evaluate_option_cache(&data, reply->packet, NULL, NULL, reply->packet->options, reply->opt_state, scope, oc, MDL) || (data.len != 4)) { log_error("reply_process_is_addressed: unable to " "evaluate preferred lease time"); status = ISC_R_FAILURE; goto cleanup; } reply->send_prefer = getULong(data.data); data_string_forget(&data, MDL); } /* Note lowest values for later calculation of renew/rebind times. */ if (reply->min_prefer > reply->send_prefer) reply->min_prefer = reply->send_prefer; if (reply->min_valid > reply->send_valid) reply->min_valid = reply->send_valid; #if 0 /* * XXX: Old 4.0.0 alpha code would change the host {} record * XXX: uid upon lease assignment. This was intended to cover the * XXX: case where a client first identifies itself using vendor * XXX: options in a solicit, or request, but later neglects to include * XXX: these options in a Renew or Rebind. It is not clear that this * XXX: is required, and has some startling ramifications (such as * XXX: how to recover this dynamic host {} state across restarts). */ if (reply->host != NULL) change_host_uid(host, reply->client_id->data, reply->client_id->len); #endif /* 0 */ /* Perform dynamic lease related update work. */ if (reply->lease != NULL) { /* Cached lifetimes */ reply->lease->prefer = reply->send_prefer; reply->lease->valid = reply->send_valid; /* Advance (or rewind) the valid lifetime. * In the protocol 0xFFFFFFFF is infinite * when connecting to the lease file MAX_TIME is */ if (reply->buf.reply.msg_type == DHCPV6_REPLY) { if (reply->send_valid == INFINITE_TIME) { reply->lease->soft_lifetime_end_time = MAX_TIME; } else { reply->lease->soft_lifetime_end_time = cur_time + reply->send_valid; } /* Wait before renew! */ } status = ia_add_iasubopt(reply->ia, reply->lease, MDL); if (status != ISC_R_SUCCESS) { log_fatal("reply_process_is_addressed: Unable to " "attach lease to new IA: %s", isc_result_totext(status)); } /* * If this is a new lease, make sure it is attached somewhere. */ if (reply->lease->ia == NULL) { ia_reference(&reply->lease->ia, reply->ia, MDL); } } /* Bring a copy of the relevant options into the IA scope. */ execute_statements_in_scope(NULL, reply->packet, NULL, NULL, reply->packet->options, reply->reply_ia, scope, group, root_group, NULL); /* Execute statements from class scopes. */ for (i = reply->packet->class_count; i > 0; i--) { execute_statements_in_scope(NULL, reply->packet, NULL, NULL, reply->packet->options, reply->reply_ia, scope, reply->packet->classes[i - 1]->group, group, NULL); } /* * And bring in host record configuration, if any, but not to overlap * the previous group or its common enclosers. */ if (reply->host != NULL) execute_statements_in_scope(NULL, reply->packet, NULL, NULL, reply->packet->options, reply->reply_ia, scope, reply->host->group, group, NULL); cleanup: if (data.data != NULL) data_string_forget(&data, MDL); if (status == ISC_R_SUCCESS) reply->client_resources++; return status; } /* Simply send an IAADDR within the IA scope as described. */ static isc_result_t reply_process_send_addr(struct reply_state *reply, struct iaddr *addr) { isc_result_t status = ISC_R_SUCCESS; struct data_string data; memset(&data, 0, sizeof(data)); /* Now append the lease. */ data.len = IAADDR_OFFSET; if (!buffer_allocate(&data.buffer, data.len, MDL)) { log_error("reply_process_send_addr: out of memory" "allocating new IAADDR buffer."); status = ISC_R_NOMEMORY; goto cleanup; } data.data = data.buffer->data; memcpy(data.buffer->data, addr->iabuf, 16); putULong(data.buffer->data + 16, reply->send_prefer); putULong(data.buffer->data + 20, reply->send_valid); if (!append_option_buffer(&dhcpv6_universe, reply->reply_ia, data.buffer, data.buffer->data, data.len, D6O_IAADDR, 0)) { log_error("reply_process_send_addr: unable " "to save IAADDR option"); status = ISC_R_FAILURE; goto cleanup; } reply->resources_included = ISC_TRUE; cleanup: if (data.data != NULL) data_string_forget(&data, MDL); return status; } /* Choose the better of two leases. */ static struct iasubopt * lease_compare(struct iasubopt *alpha, struct iasubopt *beta) { if (alpha == NULL) return beta; if (beta == NULL) return alpha; switch(alpha->state) { case FTS_ACTIVE: switch(beta->state) { case FTS_ACTIVE: /* Choose the lease with the longest lifetime (most * likely the most recently allocated). */ if (alpha->hard_lifetime_end_time < beta->hard_lifetime_end_time) return beta; else return alpha; case FTS_EXPIRED: case FTS_ABANDONED: return alpha; default: log_fatal("Impossible condition at %s:%d.", MDL); } break; case FTS_EXPIRED: switch (beta->state) { case FTS_ACTIVE: return beta; case FTS_EXPIRED: /* Choose the most recently expired lease. */ if (alpha->hard_lifetime_end_time < beta->hard_lifetime_end_time) return beta; else if ((alpha->hard_lifetime_end_time == beta->hard_lifetime_end_time) && (alpha->soft_lifetime_end_time < beta->soft_lifetime_end_time)) return beta; else return alpha; case FTS_ABANDONED: return alpha; default: log_fatal("Impossible condition at %s:%d.", MDL); } break; case FTS_ABANDONED: switch (beta->state) { case FTS_ACTIVE: case FTS_EXPIRED: return alpha; case FTS_ABANDONED: /* Choose the lease that was abandoned longest ago. */ if (alpha->hard_lifetime_end_time < beta->hard_lifetime_end_time) return alpha; else return beta; default: log_fatal("Impossible condition at %s:%d.", MDL); } break; default: log_fatal("Impossible condition at %s:%d.", MDL); } log_fatal("Triple impossible condition at %s:%d.", MDL); return NULL; } /* Process a client-supplied IA_PD. This may append options to the tail of * the reply packet being built in the reply_state structure. */ static isc_result_t reply_process_ia_pd(struct reply_state *reply, struct option_cache *ia) { isc_result_t status = ISC_R_SUCCESS; u_int32_t iaid; unsigned ia_cursor; struct option_state *packet_ia; struct option_cache *oc; struct data_string ia_data, data; /* Initialize values that will get cleaned up on return. */ packet_ia = NULL; memset(&ia_data, 0, sizeof(ia_data)); memset(&data, 0, sizeof(data)); /* * Note that find_client_prefix() may set reply->lease. */ /* Make sure there is at least room for the header. */ if ((reply->cursor + IA_PD_OFFSET + 4) > sizeof(reply->buf)) { log_error("reply_process_ia_pd: Reply too long for IA."); return ISC_R_NOSPACE; } /* Fetch the IA_PD contents. */ if (!get_encapsulated_IA_state(&packet_ia, &ia_data, reply->packet, ia, IA_PD_OFFSET)) { log_error("reply_process_ia_pd: error evaluating ia"); status = ISC_R_FAILURE; goto cleanup; } /* Extract IA_PD header contents. */ iaid = getULong(ia_data.data); reply->renew = getULong(ia_data.data + 4); reply->rebind = getULong(ia_data.data + 8); /* Create an IA_PD structure. */ if (ia_allocate(&reply->ia, iaid, (char *)reply->client_id.data, reply->client_id.len, MDL) != ISC_R_SUCCESS) { log_error("reply_process_ia_pd: no memory for ia."); status = ISC_R_NOMEMORY; goto cleanup; } reply->ia->ia_type = D6O_IA_PD; /* Cache pre-existing IA_PD, if any. */ ia_hash_lookup(&reply->old_ia, ia_pd_active, (unsigned char *)reply->ia->iaid_duid.data, reply->ia->iaid_duid.len, MDL); /* * Create an option cache to carry the IA_PD option contents, and * execute any user-supplied values into it. */ if (!option_state_allocate(&reply->reply_ia, MDL)) { status = ISC_R_NOMEMORY; goto cleanup; } /* Check & count the fixed prefix host records. */ reply->static_prefixes = 0; if ((reply->host != NULL) && (reply->host->fixed_prefix != NULL)) { struct iaddrcidrnetlist *fp; for (fp = reply->host->fixed_prefix; fp != NULL; fp = fp->next) { reply->static_prefixes += 1; } } /* * Save the cursor position at the start of the IA_PD, so we can * set length and adjust t1/t2 values later. We write a temporary * header out now just in case we decide to adjust the packet * within sub-process functions. */ ia_cursor = reply->cursor; /* Initialize the IA_PD header. First the code. */ putUShort(reply->buf.data + reply->cursor, (unsigned)D6O_IA_PD); reply->cursor += 2; /* Then option length. */ putUShort(reply->buf.data + reply->cursor, 0x0Cu); reply->cursor += 2; /* Then IA_PD header contents; IAID. */ putULong(reply->buf.data + reply->cursor, iaid); reply->cursor += 4; /* We store the client's t1 for now, and may over-ride it later. */ putULong(reply->buf.data + reply->cursor, reply->renew); reply->cursor += 4; /* We store the client's t2 for now, and may over-ride it later. */ putULong(reply->buf.data + reply->cursor, reply->rebind); reply->cursor += 4; /* * For each prefix in this IA_PD, decide what to do about it. */ oc = lookup_option(&dhcpv6_universe, packet_ia, D6O_IAPREFIX); reply->min_valid = reply->min_prefer = INFINITE_TIME; reply->client_valid = reply->client_prefer = 0; reply->preflen = -1; for (; oc != NULL ; oc = oc->next) { status = reply_process_prefix(reply, oc); /* * Canceled means we did not allocate prefixes to the * client, but we're "done" with this IA - we set a status * code. So transmit this reply, e.g., move on to the next * IA. */ if (status == ISC_R_CANCELED) break; if ((status != ISC_R_SUCCESS) && (status != ISC_R_ADDRINUSE) && (status != ISC_R_ADDRNOTAVAIL)) goto cleanup; } reply->pd_count++; /* * If we fell through the above and never gave the client * a prefix, give it one now. */ if ((status != ISC_R_CANCELED) && (reply->client_resources == 0)) { status = find_client_prefix(reply); if (status == ISC_R_NORESOURCES) { switch (reply->packet->dhcpv6_msg_type) { case DHCPV6_SOLICIT: /* * No prefix for any IA is handled * by the caller. */ /* FALL THROUGH */ case DHCPV6_REQUEST: /* Same than for addresses. */ option_state_dereference(&reply->reply_ia, MDL); if (!option_state_allocate(&reply->reply_ia, MDL)) { log_error("reply_process_ia_pd: No " "memory for option state " "wipe."); status = ISC_R_NOMEMORY; goto cleanup; } if (!set_status_code(STATUS_NoPrefixAvail, "No prefixes available " "for this interface.", reply->reply_ia)) { log_error("reply_process_ia_pd: " "Unable to set " "NoPrefixAvail status " "code."); status = ISC_R_FAILURE; goto cleanup; } status = ISC_R_SUCCESS; break; default: if (reply->resources_included) status = ISC_R_SUCCESS; else goto cleanup; break; } } if (status != ISC_R_SUCCESS) goto cleanup; } /* * yes, goto's aren't the best but we also want to avoid extra * indents */ if (status == ISC_R_CANCELED) { /* We're replying with a status code so we still need to * write it out in wire-format to the outbound buffer */ write_to_packet(reply, ia_cursor); goto cleanup; } /* * Handle static prefixes, we always log stuff and if it's * a hard binding we run any commit statements that we have */ if (reply->static_prefixes != 0) { char tmp_addr[INET6_ADDRSTRLEN]; log_info("%s PD: address %s/%d to client with duid %s " "iaid = %d static", dhcpv6_type_names[reply->buf.reply.msg_type], inet_ntop(AF_INET6, reply->fixed_pref.lo_addr.iabuf, tmp_addr, sizeof(tmp_addr)), reply->fixed_pref.bits, print_hex_1(reply->client_id.len, reply->client_id.data, 60), iaid); /* Write the lease out in wire-format to the outbound buffer */ write_to_packet(reply, ia_cursor); if ((reply->buf.reply.msg_type == DHCPV6_REPLY) && (reply->on_star.on_commit != NULL)) { execute_statements(NULL, reply->packet, NULL, NULL, reply->packet->options, reply->opt_state, NULL, reply->on_star.on_commit, NULL); executable_statement_dereference (&reply->on_star.on_commit, MDL); } goto cleanup; } /* * If we have any addresses log what we are doing. */ if (reply->ia->num_iasubopt != 0) { struct iasubopt *tmp; int i; char tmp_addr[INET6_ADDRSTRLEN]; for (i = 0 ; i < reply->ia->num_iasubopt ; i++) { tmp = reply->ia->iasubopt[i]; log_info("%s PD: address %s/%d to client with duid %s" " iaid = %d valid for %u seconds", dhcpv6_type_names[reply->buf.reply.msg_type], inet_ntop(AF_INET6, &tmp->addr, tmp_addr, sizeof(tmp_addr)), (int)tmp->plen, print_hex_1(reply->client_id.len, reply->client_id.data, 60), iaid, tmp->valid); } } /* * If this is not a 'soft' binding, consume the new changes into * the database (if any have been attached to the ia_pd). * * Loop through the assigned dynamic prefixes, referencing the * prefixes onto this IA_PD rather than any old ones, and updating * prefix pool timers for each (if any). * * If a lease can be reused we skip renewing it or checking the * pool threshold. If it can't we flag that the IA must be commited * to the db and do the renewal and pool check. */ if ((reply->buf.reply.msg_type == DHCPV6_REPLY) && (reply->ia->num_iasubopt != 0)) { int must_commit = 0; struct iasubopt *tmp; struct data_string *ia_id; int i; for (i = 0 ; i < reply->ia->num_iasubopt ; i++) { tmp = reply->ia->iasubopt[i]; if (tmp->ia != NULL) ia_dereference(&tmp->ia, MDL); ia_reference(&tmp->ia, reply->ia, MDL); /* If we have anything to do on commit do it now */ if (tmp->on_star.on_commit != NULL) { execute_statements(NULL, reply->packet, NULL, NULL, reply->packet->options, reply->opt_state, &tmp->scope, tmp->on_star.on_commit, &tmp->on_star); executable_statement_dereference (&tmp->on_star.on_commit, MDL); } if (!reuse_lease6(reply, tmp)) { /* Commit 'hard' bindings. */ must_commit = 1; renew_lease6(tmp->ipv6_pool, tmp); schedule_lease_timeout(tmp->ipv6_pool); /* Do our threshold check. */ check_pool6_threshold(reply, tmp); } } /* write the IA_PD in wire-format to the outbound buffer */ write_to_packet(reply, ia_cursor); /* Remove any old ia from the hash. */ if (reply->old_ia != NULL) { if (!release_on_roam(reply)) { ia_id = &reply->old_ia->iaid_duid; ia_hash_delete(ia_pd_active, (unsigned char *)ia_id->data, ia_id->len, MDL); } ia_dereference(&reply->old_ia, MDL); } /* Put new ia into the hash. */ reply->ia->cltt = cur_time; ia_id = &reply->ia->iaid_duid; ia_hash_add(ia_pd_active, (unsigned char *)ia_id->data, ia_id->len, reply->ia, MDL); /* If we couldn't reuse all of the iasubopts, we * must udpate the lease db */ if (must_commit) { write_ia(reply->ia); } } else { /* write the IA_PD in wire-format to the outbound buffer */ write_to_packet(reply, ia_cursor); schedule_lease_timeout_reply(reply); } cleanup: if (packet_ia != NULL) option_state_dereference(&packet_ia, MDL); if (reply->reply_ia != NULL) option_state_dereference(&reply->reply_ia, MDL); if (ia_data.data != NULL) data_string_forget(&ia_data, MDL); if (data.data != NULL) data_string_forget(&data, MDL); if (reply->ia != NULL) ia_dereference(&reply->ia, MDL); if (reply->old_ia != NULL) ia_dereference(&reply->old_ia, MDL); if (reply->lease != NULL) iasubopt_dereference(&reply->lease, MDL); if (reply->on_star.on_expiry != NULL) executable_statement_dereference (&reply->on_star.on_expiry, MDL); if (reply->on_star.on_release != NULL) executable_statement_dereference (&reply->on_star.on_release, MDL); /* * ISC_R_CANCELED is a status code used by the prefix processing to * indicate we're replying with a status code. This is still a * success at higher layers. */ return((status == ISC_R_CANCELED) ? ISC_R_SUCCESS : status); } /*! * * \brief Find the proper scoping group for use with a v6 static prefix. * * We start by trying to find a subnet based on the given prefix and * the shared network. If we don't find one then the prefix has been * declared outside of any subnets. If there is a static address * associated with the host we use it to try and find a subnet (this * should succeed). If there isn't a static address we fall back * to the shared subnet itself. * Once we have a subnet we extract the group from it and return it. * * \param reply - the reply structure we use to collect information * we will use the fields shared, fixed_pref and host * from the structure * * \return a pointer to the group structure to use for scoping */ static struct group * find_group_by_prefix(struct reply_state *reply) { /* default group if we don't find anything better */ struct group *group = reply->shared->group; struct subnet *subnet = NULL; struct iaddr tmp_addr; struct data_string fixed_addr; /* Try with the prefix first */ if (find_grouped_subnet(&subnet, reply->shared, reply->fixed_pref.lo_addr, MDL) != 0) { group = subnet->group; subnet_dereference(&subnet, MDL); return (group); } /* Didn't find a subnet via prefix, what about fixed address */ /* The caller has already tested reply->host != NULL */ memset(&fixed_addr, 0, sizeof(fixed_addr)); if ((reply->host->fixed_addr != NULL) && (evaluate_option_cache(&fixed_addr, NULL, NULL, NULL, NULL, NULL, &global_scope, reply->host->fixed_addr, MDL))) { if (fixed_addr.len >= 16) { tmp_addr.len = 16; memcpy(tmp_addr.iabuf, fixed_addr.data, 16); if (find_grouped_subnet(&subnet, reply->shared, tmp_addr, MDL) != 0) { group = subnet->group; subnet_dereference(&subnet, MDL); } } data_string_forget(&fixed_addr, MDL); } /* return whatever we got */ return (group); } /* * Process an IAPREFIX within a given IA_PD, storing any IAPREFIX reply * contents into the reply's current ia_pd-scoped option cache. Returns * ISC_R_CANCELED in the event we are replying with a status code and do * not wish to process more IAPREFIXes within this IA_PD. */ static isc_result_t reply_process_prefix(struct reply_state *reply, struct option_cache *pref) { u_int32_t pref_life, valid_life; struct binding_scope **scope; struct iaddrcidrnet tmp_pref; struct option_cache *oc; struct data_string iapref, data; isc_result_t status = ISC_R_SUCCESS; struct group *group; /* Initializes values that will be cleaned up. */ memset(&iapref, 0, sizeof(iapref)); memset(&data, 0, sizeof(data)); /* Note that reply->lease may be set by prefix_is_owned() */ /* * There is no point trying to process an incoming prefix if there * is no room for an outgoing prefix. */ if ((reply->cursor + 29) > sizeof(reply->buf)) { log_error("reply_process_prefix: Out of room for prefix."); return ISC_R_NOSPACE; } /* Extract this IAPREFIX option. */ if (!evaluate_option_cache(&iapref, reply->packet, NULL, NULL, reply->packet->options, NULL, &global_scope, pref, MDL) || (iapref.len < IAPREFIX_OFFSET)) { log_error("reply_process_prefix: error evaluating IAPREFIX."); status = ISC_R_FAILURE; goto cleanup; } /* * Layout: preferred and valid lifetimes followed by the prefix * length and the IPv6 address. */ pref_life = getULong(iapref.data); valid_life = getULong(iapref.data + 4); if ((reply->client_valid == 0) || (reply->client_valid > valid_life)) reply->client_valid = valid_life; if ((reply->client_prefer == 0) || (reply->client_prefer > pref_life)) reply->client_prefer = pref_life; /* * Clients may choose to send ::/0 as a prefix, with the idea to give * hints about preferred-lifetime or valid-lifetime. */ tmp_pref.lo_addr.len = 16; memset(tmp_pref.lo_addr.iabuf, 0, 16); if ((iapref.data[8] == 0) && (memcmp(iapref.data + 9, tmp_pref.lo_addr.iabuf, 16) == 0)) { /* Status remains success; we just ignore this one. */ goto cleanup; } /* * Clients may choose to send ::/X as a prefix to specify a * preferred/requested prefix length. Note X is never zero here. */ tmp_pref.bits = (int) iapref.data[8]; if (reply->preflen < 0) { /* Cache the first preferred prefix length. */ reply->preflen = tmp_pref.bits; } if (memcmp(iapref.data + 9, tmp_pref.lo_addr.iabuf, 16) == 0) { goto cleanup; } memcpy(tmp_pref.lo_addr.iabuf, iapref.data + 9, 16); /* Verify the prefix belongs to the client. */ if (!prefix_is_owned(reply, &tmp_pref)) { /* Same than for addresses. */ if ((reply->packet->dhcpv6_msg_type == DHCPV6_SOLICIT) || (reply->packet->dhcpv6_msg_type == DHCPV6_REQUEST) || (reply->packet->dhcpv6_msg_type == DHCPV6_REBIND)) { status = reply_process_try_prefix(reply, &tmp_pref); /* Either error out or skip this prefix. */ if ((status != ISC_R_SUCCESS) && (status != ISC_R_ADDRINUSE) && (status != ISC_R_ADDRNOTAVAIL)) goto cleanup; if (reply->lease == NULL) { if (reply->packet->dhcpv6_msg_type == DHCPV6_REBIND) { reply->send_prefer = 0; reply->send_valid = 0; goto send_pref; } /* status remains success - ignore */ goto cleanup; } /* * RFC3633 section 18.2.3: * * If the delegating router cannot find a binding * for the requesting router's IA_PD the delegating * router returns the IA_PD containing no prefixes * with a Status Code option set to NoBinding in the * Reply message. * * On mismatch we (ab)use this pretending we have not the IA * as soon as we have not a prefix. */ } else if (reply->packet->dhcpv6_msg_type == DHCPV6_RENEW) { /* Rewind the IA_PD to empty. */ option_state_dereference(&reply->reply_ia, MDL); if (!option_state_allocate(&reply->reply_ia, MDL)) { log_error("reply_process_prefix: No memory " "for option state wipe."); status = ISC_R_NOMEMORY; goto cleanup; } /* Append a NoBinding status code. */ if (!set_status_code(STATUS_NoBinding, "Prefix not bound to this " "interface.", reply->reply_ia)) { log_error("reply_process_prefix: Unable to " "attach status code."); status = ISC_R_FAILURE; goto cleanup; } /* Fin (no more IAPREFIXes). */ status = ISC_R_CANCELED; goto cleanup; } else { log_error("It is impossible to lease a client that is " "not sending a solicit, request, renew, or " "rebind message."); status = ISC_R_FAILURE; goto cleanup; } } if (reply->static_prefixes > 0) { if (reply->host == NULL) log_fatal("Impossible condition at %s:%d.", MDL); scope = &global_scope; /* Copy the static prefix for logging and finding the group */ memcpy(&reply->fixed_pref, &tmp_pref, sizeof(tmp_pref)); /* Try to find a group for the static prefix */ group = find_group_by_prefix(reply); } else { if (reply->lease == NULL) log_fatal("Impossible condition at %s:%d.", MDL); scope = &reply->lease->scope; group = reply->lease->ipv6_pool->ipv6_pond->group; } /* * If client_resources is nonzero, then the reply_process_is_prefixed * function has executed configuration state into the reply option * cache. We will use that valid cache to derive configuration for * whether or not to engage in additional prefixes, and similar. */ if (reply->client_resources != 0) { unsigned limit = 1; /* * Does this client have "enough" prefixes already? Default * to one. Everybody gets one, and one should be enough for * anybody. */ oc = lookup_option(&server_universe, reply->opt_state, SV_LIMIT_PREFS_PER_IA); if (oc != NULL) { if (!evaluate_option_cache(&data, reply->packet, NULL, NULL, reply->packet->options, reply->opt_state, scope, oc, MDL) || (data.len != 4)) { log_error("reply_process_prefix: unable to " "evaluate prefs-per-ia value."); status = ISC_R_FAILURE; goto cleanup; } limit = getULong(data.data); data_string_forget(&data, MDL); } /* * If we wish to limit the client to a certain number of * prefixes, then omit the prefix from the reply. */ if (reply->client_resources >= limit) goto cleanup; } status = reply_process_is_prefixed(reply, scope, group); if (status != ISC_R_SUCCESS) goto cleanup; send_pref: status = reply_process_send_prefix(reply, &tmp_pref); cleanup: if (iapref.data != NULL) data_string_forget(&iapref, MDL); if (data.data != NULL) data_string_forget(&data, MDL); if (reply->lease != NULL) iasubopt_dereference(&reply->lease, MDL); return status; } /* * Verify the prefix belongs to the client. If we've got a host * record with fixed prefixes, it has to be an assigned prefix * (fault out all else). Otherwise it's a dynamic prefix, so lookup * that prefix and make sure it belongs to this DUID:IAID pair. */ static isc_boolean_t prefix_is_owned(struct reply_state *reply, struct iaddrcidrnet *pref) { struct iaddrcidrnetlist *l; int i; struct ipv6_pond *pond; /* * This faults out prefixes that don't match fixed prefixes. */ if (reply->static_prefixes > 0) { for (l = reply->host->fixed_prefix; l != NULL; l = l->next) { if ((pref->bits == l->cidrnet.bits) && (memcmp(pref->lo_addr.iabuf, l->cidrnet.lo_addr.iabuf, 16) == 0)) return (ISC_TRUE); } return (ISC_FALSE); } if ((reply->old_ia == NULL) || (reply->old_ia->num_iasubopt == 0)) return (ISC_FALSE); for (i = 0 ; i < reply->old_ia->num_iasubopt ; i++) { struct iasubopt *tmp; tmp = reply->old_ia->iasubopt[i]; if ((pref->bits == (int) tmp->plen) && (memcmp(pref->lo_addr.iabuf, &tmp->addr, 16) == 0)) { if (lease6_usable(tmp) == ISC_FALSE) { return (ISC_FALSE); } pond = tmp->ipv6_pool->ipv6_pond; if (((pond->prohibit_list != NULL) && (permitted(reply->packet, pond->prohibit_list))) || ((pond->permit_list != NULL) && (!permitted(reply->packet, pond->permit_list)))) return (ISC_FALSE); iasubopt_reference(&reply->lease, tmp, MDL); return (ISC_TRUE); } } return (ISC_FALSE); } /* * This function only returns failure on 'hard' failures. If it succeeds, * it will leave a prefix structure behind. */ static isc_result_t reply_process_try_prefix(struct reply_state *reply, struct iaddrcidrnet *pref) { isc_result_t status = ISC_R_ADDRNOTAVAIL; struct ipv6_pool *pool = NULL; struct ipv6_pond *pond = NULL; int i; struct data_string data_pref; if ((reply == NULL) || (reply->shared == NULL) || (pref == NULL) || (reply->lease != NULL)) return (DHCP_R_INVALIDARG); /* * Do a quick walk through of the ponds and pools * to see if we have any prefix pools */ for (pond = reply->shared->ipv6_pond; pond != NULL; pond = pond->next) { if (pond->ipv6_pools == NULL) continue; for (i = 0; (pool = pond->ipv6_pools[i]) != NULL; i++) { if (pool->pool_type == D6O_IA_PD) break; } if (pool != NULL) break; } /* If we get here and p is NULL we have no useful pools */ if (pool == NULL) { return (ISC_R_ADDRNOTAVAIL); } memset(&data_pref, 0, sizeof(data_pref)); data_pref.len = 17; if (!buffer_allocate(&data_pref.buffer, data_pref.len, MDL)) { log_error("reply_process_try_prefix: out of memory."); return (ISC_R_NOMEMORY); } data_pref.data = data_pref.buffer->data; data_pref.buffer->data[0] = (u_int8_t) pref->bits; memcpy(data_pref.buffer->data + 1, pref->lo_addr.iabuf, 16); /* * We have at least one pool that could provide a prefix * Now we walk through the ponds and pools again and check * to see if the client is permitted and if an prefix is * available * */ for (pond = reply->shared->ipv6_pond; pond != NULL; pond = pond->next) { if (((pond->prohibit_list != NULL) && (permitted(reply->packet, pond->prohibit_list))) || ((pond->permit_list != NULL) && (!permitted(reply->packet, pond->permit_list)))) continue; for (i = 0; (pool = pond->ipv6_pools[i]) != NULL; i++) { if (pool->pool_type != D6O_IA_PD) { continue; } status = try_client_v6_prefix(&reply->lease, pool, &data_pref); /* If we found it in this pool (either in use or available), there is no need to look further. */ if ( (status == ISC_R_SUCCESS) || (status == ISC_R_ADDRINUSE) ) break; } if ( (status == ISC_R_SUCCESS) || (status == ISC_R_ADDRINUSE) ) break; } data_string_forget(&data_pref, MDL); /* Return just the most recent status... */ return (status); } /* Look around for a prefix to give the client. First, look through the old * IA_PD for prefixes we can extend. Second, try to allocate a new prefix. * Finally, actually add that prefix into the current reply IA_PD. */ static isc_result_t find_client_prefix(struct reply_state *reply) { struct iaddrcidrnet send_pref; isc_result_t status = ISC_R_NORESOURCES; struct iasubopt *prefix, *best_prefix = NULL; struct binding_scope **scope; int i; struct group *group; if (reply->static_prefixes > 0) { struct iaddrcidrnetlist *l; if (reply->host == NULL) return DHCP_R_INVALIDARG; for (l = reply->host->fixed_prefix; l != NULL; l = l->next) { if (l->cidrnet.bits == reply->preflen) break; } if (l == NULL) { /* * If no fixed prefix has the preferred length, * get the first one. */ l = reply->host->fixed_prefix; } memcpy(&send_pref, &l->cidrnet, sizeof(send_pref)); scope = &global_scope; /* Copy the prefix for logging purposes */ memcpy(&reply->fixed_pref, &l->cidrnet, sizeof(send_pref)); /* Try to find a group for the static prefix */ group = find_group_by_prefix(reply); goto send_pref; } if (reply->old_ia != NULL) { for (i = 0 ; i < reply->old_ia->num_iasubopt ; i++) { struct shared_network *candidate_shared; struct ipv6_pond *pond; prefix = reply->old_ia->iasubopt[i]; candidate_shared = prefix->ipv6_pool->shared_network; pond = prefix->ipv6_pool->ipv6_pond; /* * Consider this prefix if it is in a global pool or * if it is scoped in a pool under the client's shared * network. */ if (((candidate_shared != NULL) && (candidate_shared != reply->shared)) || (lease6_usable(prefix) != ISC_TRUE)) continue; /* * And check if the prefix is still permitted */ if (((pond->prohibit_list != NULL) && (permitted(reply->packet, pond->prohibit_list))) || ((pond->permit_list != NULL) && (!permitted(reply->packet, pond->permit_list)))) continue; best_prefix = prefix_compare(reply, prefix, best_prefix); } /* * If we have prefix length hint and we're not igoring them, * then toss the best match if it doesn't match the hint, * unless this is in response to a rebind. In the latter * case we're supposed to return it with zero lifetimes. * (See rt45780) */ if (best_prefix && (reply->preflen > 0) && (prefix_length_mode != PLM_IGNORE) && (reply->preflen != best_prefix->plen) && (reply->packet->dhcpv6_msg_type != DHCPV6_REBIND)) { best_prefix = NULL; } } /* Try to pick a new prefix if we didn't find one, or if we found an * abandoned prefix. */ if ((best_prefix == NULL) || (best_prefix->state == FTS_ABANDONED)) { status = pick_v6_prefix(reply); } else if (best_prefix != NULL) { iasubopt_reference(&reply->lease, best_prefix, MDL); status = ISC_R_SUCCESS; } /* Pick the abandoned prefix as a last resort. */ if ((status == ISC_R_NORESOURCES) && (best_prefix != NULL)) { /* I don't see how this is supposed to be done right now. */ log_error("Reclaiming abandoned prefixes is not yet " "supported. Treating this as an out of space " "condition."); /* iasubopt_reference(&reply->lease, best_prefix, MDL); */ } /* Give up now if we didn't find a prefix. */ if (status != ISC_R_SUCCESS) return status; if (reply->lease == NULL) log_fatal("Impossible condition at %s:%d.", MDL); scope = &reply->lease->scope; group = reply->lease->ipv6_pool->ipv6_pond->group; send_pref.lo_addr.len = 16; memcpy(send_pref.lo_addr.iabuf, &reply->lease->addr, 16); send_pref.bits = (int) reply->lease->plen; send_pref: status = reply_process_is_prefixed(reply, scope, group); if (status != ISC_R_SUCCESS) return status; status = reply_process_send_prefix(reply, &send_pref); return status; } /* Once a prefix is found for a client, perform several common functions; * Calculate and store valid and preferred prefix times, draw client options * into the option state. */ static isc_result_t reply_process_is_prefixed(struct reply_state *reply, struct binding_scope **scope, struct group *group) { isc_result_t status = ISC_R_SUCCESS; struct data_string data; struct option_cache *oc; struct option_state *tmp_options = NULL; struct on_star *on_star; int i; /* Initialize values we will cleanup. */ memset(&data, 0, sizeof(data)); /* * Find the proper on_star block to use. We use the * one in the lease if we have a lease or the one in * the reply if we don't have a lease because this is * a static instance */ if (reply->lease) { on_star = &reply->lease->on_star; } else { on_star = &reply->on_star; } /* * Bring in the root configuration. We only do this to bring * in the on * statements, as we didn't have the lease available * we we did it the first time. */ option_state_allocate(&tmp_options, MDL); execute_statements_in_scope(NULL, reply->packet, NULL, NULL, reply->packet->options, tmp_options, &global_scope, root_group, NULL, on_star); if (tmp_options != NULL) { option_state_dereference(&tmp_options, MDL); } /* * Bring configured options into the root packet level cache - start * with the lease's closest enclosing group (passed in by the caller * as 'group'). */ execute_statements_in_scope(NULL, reply->packet, NULL, NULL, reply->packet->options, reply->opt_state, scope, group, root_group, on_star); /* Execute statements from class scopes. */ for (i = reply->packet->class_count; i > 0; i--) { execute_statements_in_scope(NULL, reply->packet, NULL, NULL, reply->packet->options, reply->opt_state, scope, reply->packet->classes[i - 1]->group, group, on_star); } /* * If there is a host record, over-ride with values configured there, * without re-evaluating configuration from the previously executed * group or its common enclosers. */ if (reply->host != NULL) execute_statements_in_scope(NULL, reply->packet, NULL, NULL, reply->packet->options, reply->opt_state, scope, reply->host->group, group, on_star); /* Determine valid lifetime. */ if (reply->client_valid == 0) reply->send_valid = DEFAULT_DEFAULT_LEASE_TIME; else reply->send_valid = reply->client_valid; oc = lookup_option(&server_universe, reply->opt_state, SV_DEFAULT_LEASE_TIME); if (oc != NULL) { if (!evaluate_option_cache(&data, reply->packet, NULL, NULL, reply->packet->options, reply->opt_state, scope, oc, MDL) || (data.len != 4)) { log_error("reply_process_is_prefixed: unable to " "evaluate default prefix time"); status = ISC_R_FAILURE; goto cleanup; } reply->send_valid = getULong(data.data); data_string_forget(&data, MDL); } /* Check to see if the lease time would cause us to wrap * in which case we make it infinite. * The following doesn't work on at least some systems: * (cur_time + reply->send_valid < cur_time) */ if (reply->send_valid != INFINITE_TIME) { time_t test_time = cur_time + reply->send_valid; if (test_time < cur_time) reply->send_valid = INFINITE_TIME; } if (reply->client_prefer == 0) reply->send_prefer = reply->send_valid; else reply->send_prefer = reply->client_prefer; if ((reply->send_prefer >= reply->send_valid) && (reply->send_valid != INFINITE_TIME)) reply->send_prefer = (reply->send_valid / 2) + (reply->send_valid / 8); oc = lookup_option(&server_universe, reply->opt_state, SV_PREFER_LIFETIME); if (oc != NULL) { if (!evaluate_option_cache(&data, reply->packet, NULL, NULL, reply->packet->options, reply->opt_state, scope, oc, MDL) || (data.len != 4)) { log_error("reply_process_is_prefixed: unable to " "evaluate preferred prefix time"); status = ISC_R_FAILURE; goto cleanup; } reply->send_prefer = getULong(data.data); data_string_forget(&data, MDL); } /* Note lowest values for later calculation of renew/rebind times. */ if (reply->min_prefer > reply->send_prefer) reply->min_prefer = reply->send_prefer; if (reply->min_valid > reply->send_valid) reply->min_valid = reply->send_valid; /* Perform dynamic prefix related update work. */ if (reply->lease != NULL) { /* Cached lifetimes */ reply->lease->prefer = reply->send_prefer; reply->lease->valid = reply->send_valid; /* Advance (or rewind) the valid lifetime. * In the protocol 0xFFFFFFFF is infinite * when connecting to the lease file MAX_TIME is */ if (reply->buf.reply.msg_type == DHCPV6_REPLY) { if (reply->send_valid == INFINITE_TIME) { reply->lease->soft_lifetime_end_time = MAX_TIME; } else { reply->lease->soft_lifetime_end_time = cur_time + reply->send_valid; } /* Wait before renew! */ } status = ia_add_iasubopt(reply->ia, reply->lease, MDL); if (status != ISC_R_SUCCESS) { log_fatal("reply_process_is_prefixed: Unable to " "attach prefix to new IA_PD: %s", isc_result_totext(status)); } /* * If this is a new prefix, make sure it is attached somewhere. */ if (reply->lease->ia == NULL) { ia_reference(&reply->lease->ia, reply->ia, MDL); } } /* Bring a copy of the relevant options into the IA_PD scope. */ execute_statements_in_scope(NULL, reply->packet, NULL, NULL, reply->packet->options, reply->reply_ia, scope, group, root_group, NULL); /* Execute statements from class scopes. */ for (i = reply->packet->class_count; i > 0; i--) { execute_statements_in_scope(NULL, reply->packet, NULL, NULL, reply->packet->options, reply->reply_ia, scope, reply->packet->classes[i - 1]->group, group, NULL); } /* * And bring in host record configuration, if any, but not to overlap * the previous group or its common enclosers. */ if (reply->host != NULL) execute_statements_in_scope(NULL, reply->packet, NULL, NULL, reply->packet->options, reply->reply_ia, scope, reply->host->group, group, NULL); cleanup: if (data.data != NULL) data_string_forget(&data, MDL); if (status == ISC_R_SUCCESS) reply->client_resources++; return status; } /* Simply send an IAPREFIX within the IA_PD scope as described. */ static isc_result_t reply_process_send_prefix(struct reply_state *reply, struct iaddrcidrnet *pref) { isc_result_t status = ISC_R_SUCCESS; struct data_string data; memset(&data, 0, sizeof(data)); /* Now append the prefix. */ data.len = IAPREFIX_OFFSET; if (!buffer_allocate(&data.buffer, data.len, MDL)) { log_error("reply_process_send_prefix: out of memory" "allocating new IAPREFIX buffer."); status = ISC_R_NOMEMORY; goto cleanup; } data.data = data.buffer->data; putULong(data.buffer->data, reply->send_prefer); putULong(data.buffer->data + 4, reply->send_valid); data.buffer->data[8] = pref->bits; memcpy(data.buffer->data + 9, pref->lo_addr.iabuf, 16); if (!append_option_buffer(&dhcpv6_universe, reply->reply_ia, data.buffer, data.buffer->data, data.len, D6O_IAPREFIX, 0)) { log_error("reply_process_send_prefix: unable " "to save IAPREFIX option"); status = ISC_R_FAILURE; goto cleanup; } reply->resources_included = ISC_TRUE; cleanup: if (data.data != NULL) data_string_forget(&data, MDL); return status; } /* Choose the better of two prefixes. */ static struct iasubopt * prefix_compare(struct reply_state *reply, struct iasubopt *alpha, struct iasubopt *beta) { if (alpha == NULL) return beta; if (beta == NULL) return alpha; if (reply->preflen >= 0) { if ((alpha->plen == reply->preflen) && (beta->plen != reply->preflen)) return alpha; if ((beta->plen == reply->preflen) && (alpha->plen != reply->preflen)) return beta; } switch(alpha->state) { case FTS_ACTIVE: switch(beta->state) { case FTS_ACTIVE: /* Choose the prefix with the longest lifetime (most * likely the most recently allocated). */ if (alpha->hard_lifetime_end_time < beta->hard_lifetime_end_time) return beta; else return alpha; case FTS_EXPIRED: case FTS_ABANDONED: return alpha; default: log_fatal("Impossible condition at %s:%d.", MDL); } break; case FTS_EXPIRED: switch (beta->state) { case FTS_ACTIVE: return beta; case FTS_EXPIRED: /* Choose the most recently expired prefix. */ if (alpha->hard_lifetime_end_time < beta->hard_lifetime_end_time) return beta; else if ((alpha->hard_lifetime_end_time == beta->hard_lifetime_end_time) && (alpha->soft_lifetime_end_time < beta->soft_lifetime_end_time)) return beta; else return alpha; case FTS_ABANDONED: return alpha; default: log_fatal("Impossible condition at %s:%d.", MDL); } break; case FTS_ABANDONED: switch (beta->state) { case FTS_ACTIVE: case FTS_EXPIRED: return alpha; case FTS_ABANDONED: /* Choose the prefix that was abandoned longest ago. */ if (alpha->hard_lifetime_end_time < beta->hard_lifetime_end_time) return alpha; else return beta; default: log_fatal("Impossible condition at %s:%d.", MDL); } break; default: log_fatal("Impossible condition at %s:%d.", MDL); } log_fatal("Triple impossible condition at %s:%d.", MDL); return NULL; } /* * Solicit is how a client starts requesting addresses. * * If the client asks for rapid commit, and we support it, we will * allocate the addresses and reply. * * Otherwise we will send an advertise message. */ static void dhcpv6_solicit(struct data_string *reply_ret, struct packet *packet) { struct data_string client_id; /* * Validate our input. */ if (!valid_client_msg(packet, &client_id)) { return; } lease_to_client(reply_ret, packet, &client_id, NULL); /* * Clean up. */ data_string_forget(&client_id, MDL); } /* * Request is how a client actually requests addresses. * * Very similar to Solicit handling, except the server DUID is required. */ static void dhcpv6_request(struct data_string *reply_ret, struct packet *packet) { struct data_string client_id; struct data_string server_id; /* * Validate our input. */ if (!valid_client_resp(packet, &client_id, &server_id)) { return; } /* If the REQUEST arrived via unicast and unicast option isn't set, * reject it per RFC 3315, Sec 18.2.1 */ if (packet->unicast == ISC_TRUE && is_unicast_option_defined(packet) == ISC_FALSE) { unicast_reject(reply_ret, packet, &client_id, &server_id); } else { /* * Issue our lease. */ lease_to_client(reply_ret, packet, &client_id, &server_id); } /* * Cleanup. */ data_string_forget(&client_id, MDL); data_string_forget(&server_id, MDL); } /* Find a DHCPv6 packet's shared network from hints in the packet. */ static isc_result_t shared_network_from_packet6(struct shared_network **shared, struct packet *packet) { const struct packet *chk_packet; const struct in6_addr *link_addr, *first_link_addr; struct iaddr tmp_addr; struct subnet *subnet; isc_result_t status; if ((shared == NULL) || (*shared != NULL) || (packet == NULL)) return DHCP_R_INVALIDARG; /* * First, find the link address where the packet from the client * first appeared (if this packet was relayed). */ first_link_addr = NULL; chk_packet = packet->dhcpv6_container_packet; while (chk_packet != NULL) { link_addr = &chk_packet->dhcpv6_link_address; if (!IN6_IS_ADDR_UNSPECIFIED(link_addr) && !IN6_IS_ADDR_LINKLOCAL(link_addr)) { first_link_addr = link_addr; break; } chk_packet = chk_packet->dhcpv6_container_packet; } /* * If there is a relayed link address, find the subnet associated * with that, and use that to get the appropriate * shared_network. */ if (first_link_addr != NULL) { tmp_addr.len = sizeof(*first_link_addr); memcpy(tmp_addr.iabuf, first_link_addr, sizeof(*first_link_addr)); subnet = NULL; if (!find_subnet(&subnet, tmp_addr, MDL)) { log_debug("No subnet found for link-address %s.", piaddr(tmp_addr)); return ISC_R_NOTFOUND; } status = shared_network_reference(shared, subnet->shared_network, MDL); subnet_dereference(&subnet, MDL); /* * If there is no link address, we will use the interface * that this packet came in on to pick the shared_network. */ } else if (packet->interface != NULL) { status = shared_network_reference(shared, packet->interface->shared_network, MDL); if (packet->dhcpv6_container_packet != NULL) { log_info("[L2 Relay] No link address in relay packet " "assuming L2 relay and using receiving " "interface"); } } else { /* * We shouldn't be able to get here but if there is no link * address and no interface we don't know where to get the * pool from log an error and return an error. */ log_error("No interface and no link address " "can't determine pool"); status = DHCP_R_INVALIDARG; } return status; } /* * When a client thinks it might be on a new link, it sends a * Confirm message. * * From RFC3315 section 18.2.2: * * When the server receives a Confirm message, the server determines * whether the addresses in the Confirm message are appropriate for the * link to which the client is attached. If all of the addresses in the * Confirm message pass this test, the server returns a status of * Success. If any of the addresses do not pass this test, the server * returns a status of NotOnLink. If the server is unable to perform * this test (for example, the server does not have information about * prefixes on the link to which the client is connected), or there were * no addresses in any of the IAs sent by the client, the server MUST * NOT send a reply to the client. */ static void dhcpv6_confirm(struct data_string *reply_ret, struct packet *packet) { struct shared_network *shared; struct subnet *subnet; struct option_cache *ia, *ta, *oc; struct data_string cli_enc_opt_data, iaaddr, client_id, packet_oro; struct option_state *cli_enc_opt_state, *opt_state; struct iaddr cli_addr; int pass; isc_boolean_t inappropriate, has_addrs; char reply_data[65536]; struct dhcpv6_packet *reply = (struct dhcpv6_packet *)reply_data; int reply_ofs = (int)(offsetof(struct dhcpv6_packet, options)); /* * Basic client message validation. */ memset(&client_id, 0, sizeof(client_id)); if (!valid_client_msg(packet, &client_id)) { return; } /* * Do not process Confirms that do not have IA's we do not recognize. */ ia = lookup_option(&dhcpv6_universe, packet->options, D6O_IA_NA); ta = lookup_option(&dhcpv6_universe, packet->options, D6O_IA_TA); if ((ia == NULL) && (ta == NULL)) return; /* * IA_PD's are simply ignored. */ delete_option(&dhcpv6_universe, packet->options, D6O_IA_PD); /* * Bit of variable initialization. */ opt_state = cli_enc_opt_state = NULL; memset(&cli_enc_opt_data, 0, sizeof(cli_enc_opt_data)); memset(&iaaddr, 0, sizeof(iaaddr)); memset(&packet_oro, 0, sizeof(packet_oro)); /* Determine what shared network the client is connected to. We * must not respond if we don't have any information about the * network the client is on. */ shared = NULL; if ((shared_network_from_packet6(&shared, packet) != ISC_R_SUCCESS) || (shared == NULL)) goto exit; /* If there are no recorded subnets, then we have no * information about this subnet - ignore Confirms. */ subnet = shared->subnets; if (subnet == NULL) goto exit; /* Are the addresses in all the IA's appropriate for that link? */ has_addrs = inappropriate = ISC_FALSE; pass = D6O_IA_NA; while(!inappropriate) { /* If we've reached the end of the IA_NA pass, move to the * IA_TA pass. */ if ((pass == D6O_IA_NA) && (ia == NULL)) { pass = D6O_IA_TA; ia = ta; } /* If we've reached the end of all passes, we're done. */ if (ia == NULL) break; if (((pass == D6O_IA_NA) && !get_encapsulated_IA_state(&cli_enc_opt_state, &cli_enc_opt_data, packet, ia, IA_NA_OFFSET)) || ((pass == D6O_IA_TA) && !get_encapsulated_IA_state(&cli_enc_opt_state, &cli_enc_opt_data, packet, ia, IA_TA_OFFSET))) { goto exit; } oc = lookup_option(&dhcpv6_universe, cli_enc_opt_state, D6O_IAADDR); for ( ; oc != NULL ; oc = oc->next) { if (!evaluate_option_cache(&iaaddr, packet, NULL, NULL, packet->options, NULL, &global_scope, oc, MDL) || (iaaddr.len < IAADDR_OFFSET)) { log_error("dhcpv6_confirm: " "error evaluating IAADDR."); goto exit; } /* Copy out the IPv6 address for processing. */ cli_addr.len = 16; memcpy(cli_addr.iabuf, iaaddr.data, 16); data_string_forget(&iaaddr, MDL); /* Record that we've processed at least one address. */ has_addrs = ISC_TRUE; /* Find out if any subnets cover this address. */ for (subnet = shared->subnets ; subnet != NULL ; subnet = subnet->next_sibling) { if (addr_eq(subnet_number(cli_addr, subnet->netmask), subnet->net)) break; } /* If we reach the end of the subnet list, and no * subnet matches the client address, then it must * be inappropriate to the link (so far as our * configuration says). Once we've found one * inappropriate address, there is no reason to * continue searching. */ if (subnet == NULL) { inappropriate = ISC_TRUE; break; } } option_state_dereference(&cli_enc_opt_state, MDL); data_string_forget(&cli_enc_opt_data, MDL); /* Advance to the next IA_*. */ ia = ia->next; } /* If the client supplied no addresses, do not reply. */ if (!has_addrs) goto exit; /* * Set up reply. */ if (!start_reply(packet, &client_id, NULL, &opt_state, reply)) { goto exit; } /* * Set our status. */ if (inappropriate) { if (!set_status_code(STATUS_NotOnLink, "Some of the addresses are not on link.", opt_state)) { goto exit; } } else { if (!set_status_code(STATUS_Success, "All addresses still on link.", opt_state)) { goto exit; } } /* * Only one option: add it. */ reply_ofs += store_options6(reply_data+reply_ofs, sizeof(reply_data)-reply_ofs, opt_state, packet, required_opts, &packet_oro); /* * Return our reply to the caller. */ reply_ret->len = reply_ofs; reply_ret->buffer = NULL; if (!buffer_allocate(&reply_ret->buffer, reply_ofs, MDL)) { log_fatal("No memory to store reply."); } reply_ret->data = reply_ret->buffer->data; memcpy(reply_ret->buffer->data, reply, reply_ofs); exit: /* Cleanup any stale data strings. */ if (cli_enc_opt_data.buffer != NULL) data_string_forget(&cli_enc_opt_data, MDL); if (iaaddr.buffer != NULL) data_string_forget(&iaaddr, MDL); if (client_id.buffer != NULL) data_string_forget(&client_id, MDL); if (packet_oro.buffer != NULL) data_string_forget(&packet_oro, MDL); /* Release any stale option states. */ if (cli_enc_opt_state != NULL) option_state_dereference(&cli_enc_opt_state, MDL); if (opt_state != NULL) option_state_dereference(&opt_state, MDL); } /* * Renew is when a client wants to extend its lease/prefix, at time T1. * * We handle this the same as if the client wants a new lease/prefix, * except for the error code of when addresses don't match. */ static void dhcpv6_renew(struct data_string *reply, struct packet *packet) { struct data_string client_id; struct data_string server_id; /* * Validate the request. */ if (!valid_client_resp(packet, &client_id, &server_id)) { return; } /* If the RENEW arrived via unicast and unicast option isn't set, * reject it per RFC 3315, Sec 18.2.3 */ if (packet->unicast == ISC_TRUE && is_unicast_option_defined(packet) == ISC_FALSE) { unicast_reject(reply, packet, &client_id, &server_id); } else { /* * Renew our lease. */ lease_to_client(reply, packet, &client_id, &server_id); } /* * Cleanup. */ data_string_forget(&server_id, MDL); data_string_forget(&client_id, MDL); } /* * Rebind is when a client wants to extend its lease, at time T2. * * We handle this the same as if the client wants a new lease, except * for the error code of when addresses don't match. */ static void dhcpv6_rebind(struct data_string *reply, struct packet *packet) { struct data_string client_id; if (!valid_client_msg(packet, &client_id)) { return; } lease_to_client(reply, packet, &client_id, NULL); data_string_forget(&client_id, MDL); } static void ia_na_match_decline(const struct data_string *client_id, const struct data_string *iaaddr, struct iasubopt *lease) { char tmp_addr[INET6_ADDRSTRLEN]; log_error("Client %s reports address %s is " "already in use by another host!", print_hex_1(client_id->len, client_id->data, 60), inet_ntop(AF_INET6, iaaddr->data, tmp_addr, sizeof(tmp_addr))); if (lease != NULL) { decline_lease6(lease->ipv6_pool, lease); lease->ia->cltt = cur_time; write_ia(lease->ia); } } static void ia_na_nomatch_decline(const struct data_string *client_id, const struct data_string *iaaddr, u_int32_t *ia_na_id, struct packet *packet, char *reply_data, int *reply_ofs, int reply_len) { char tmp_addr[INET6_ADDRSTRLEN]; struct option_state *host_opt_state; int len; log_info("Client %s declines address %s, which is not offered to it.", print_hex_1(client_id->len, client_id->data, 60), inet_ntop(AF_INET6, iaaddr->data, tmp_addr, sizeof(tmp_addr))); /* * Create state for this IA_NA. */ host_opt_state = NULL; if (!option_state_allocate(&host_opt_state, MDL)) { log_error("ia_na_nomatch_decline: out of memory " "allocating option_state."); goto exit; } if (!set_status_code(STATUS_NoBinding, "Decline for unknown address.", host_opt_state)) { goto exit; } /* * Insure we have enough space */ if (reply_len < (*reply_ofs + 16)) { log_error("ia_na_nomatch_decline: " "out of space for reply packet."); goto exit; } /* * Put our status code into the reply packet. */ len = store_options6(reply_data+(*reply_ofs)+16, reply_len-(*reply_ofs)-16, host_opt_state, packet, required_opts_STATUS_CODE, NULL); /* * Store the non-encapsulated option data for this * IA_NA into our reply packet. Defined in RFC 3315, * section 22.4. */ /* option number */ putUShort((unsigned char *)reply_data+(*reply_ofs), D6O_IA_NA); /* option length */ putUShort((unsigned char *)reply_data+(*reply_ofs)+2, len + 12); /* IA_NA, copied from the client */ memcpy(reply_data+(*reply_ofs)+4, ia_na_id, 4); /* t1 and t2, odd that we need them, but here it is */ putULong((unsigned char *)reply_data+(*reply_ofs)+8, 0); putULong((unsigned char *)reply_data+(*reply_ofs)+12, 0); /* * Get ready for next IA_NA. */ *reply_ofs += (len + 16); exit: option_state_dereference(&host_opt_state, MDL); } static void iterate_over_ia_na(struct data_string *reply_ret, struct packet *packet, const struct data_string *client_id, const struct data_string *server_id, const char *packet_type, void (*ia_na_match)(), void (*ia_na_nomatch)()) { struct option_state *opt_state; struct host_decl *packet_host; struct option_cache *ia; struct option_cache *oc; /* cli_enc_... variables come from the IA_NA/IA_TA options */ struct data_string cli_enc_opt_data; struct option_state *cli_enc_opt_state; struct host_decl *host; struct data_string iaaddr; struct data_string fixed_addr; char reply_data[65536]; struct dhcpv6_packet *reply = (struct dhcpv6_packet *)reply_data; int reply_ofs = (int)(offsetof(struct dhcpv6_packet, options)); char status_msg[32]; struct iasubopt *lease; struct ia_xx *existing_ia_na; int i; struct data_string key; u_int32_t iaid; /* * Initialize to empty values, in case we have to exit early. */ opt_state = NULL; memset(&cli_enc_opt_data, 0, sizeof(cli_enc_opt_data)); cli_enc_opt_state = NULL; memset(&iaaddr, 0, sizeof(iaaddr)); memset(&fixed_addr, 0, sizeof(fixed_addr)); lease = NULL; /* * Find the host record that matches from the packet, if any. */ packet_host = NULL; find_hosts6(&packet_host, packet, client_id, MDL); /* * Set our reply information. */ reply->msg_type = DHCPV6_REPLY; memcpy(reply->transaction_id, packet->dhcpv6_transaction_id, sizeof(reply->transaction_id)); /* * Build our option state for reply. */ opt_state = NULL; if (!option_state_allocate(&opt_state, MDL)) { log_error("iterate_over_ia_na: no memory for option_state."); goto exit; } execute_statements_in_scope(NULL, packet, NULL, NULL, packet->options, opt_state, &global_scope, root_group, NULL, NULL); /* * RFC 3315, section 18.2.7 tells us which options to include. */ oc = lookup_option(&dhcpv6_universe, opt_state, D6O_SERVERID); if (oc == NULL) { if (!save_option_buffer(&dhcpv6_universe, opt_state, NULL, (unsigned char *)server_duid.data, server_duid.len, D6O_SERVERID, 0)) { log_error("iterate_over_ia_na: " "error saving server identifier."); goto exit; } } if (!save_option_buffer(&dhcpv6_universe, opt_state, client_id->buffer, (unsigned char *)client_id->data, client_id->len, D6O_CLIENTID, 0)) { log_error("iterate_over_ia_na: " "error saving client identifier."); goto exit; } snprintf(status_msg, sizeof(status_msg), "%s received.", packet_type); if (!set_status_code(STATUS_Success, status_msg, opt_state)) { goto exit; } /* * Add our options that are not associated with any IA_NA or IA_TA. */ reply_ofs += store_options6(reply_data+reply_ofs, sizeof(reply_data)-reply_ofs, opt_state, packet, required_opts, NULL); /* * Loop through the IA_NA reported by the client, and deal with * addresses reported as already in use. */ for (ia = lookup_option(&dhcpv6_universe, packet->options, D6O_IA_NA); ia != NULL; ia = ia->next) { if (!get_encapsulated_IA_state(&cli_enc_opt_state, &cli_enc_opt_data, packet, ia, IA_NA_OFFSET)) { goto exit; } iaid = getULong(cli_enc_opt_data.data); /* * XXX: It is possible that we can get multiple addresses * sent by the client. We don't send multiple * addresses, so this indicates a client error. * We should check for multiple IAADDR options, log * if found, and set as an error. */ oc = lookup_option(&dhcpv6_universe, cli_enc_opt_state, D6O_IAADDR); if (oc == NULL) { /* no address given for this IA, ignore */ option_state_dereference(&cli_enc_opt_state, MDL); data_string_forget(&cli_enc_opt_data, MDL); continue; } memset(&iaaddr, 0, sizeof(iaaddr)); if (!evaluate_option_cache(&iaaddr, packet, NULL, NULL, packet->options, NULL, &global_scope, oc, MDL)) { log_error("iterate_over_ia_na: " "error evaluating IAADDR."); goto exit; } /* * Now we need to figure out which host record matches * this IA_NA and IAADDR (encapsulated option contents * matching a host record by option). * * XXX: We don't currently track IA_NA separately, but * we will need to do this! */ host = NULL; if (!find_hosts_by_option(&host, packet, cli_enc_opt_state, MDL)) { if (packet_host != NULL) { host = packet_host; } else { host = NULL; } } while (host != NULL) { if (host->fixed_addr != NULL) { if (!evaluate_option_cache(&fixed_addr, NULL, NULL, NULL, NULL, NULL, &global_scope, host->fixed_addr, MDL)) { log_error("iterate_over_ia_na: error " "evaluating host address."); goto exit; } if ((iaaddr.len >= 16) && !memcmp(fixed_addr.data, iaaddr.data, 16)) { data_string_forget(&fixed_addr, MDL); break; } data_string_forget(&fixed_addr, MDL); } host = host->n_ipaddr; } if ((host == NULL) && (iaaddr.len >= IAADDR_OFFSET)) { /* * Find existing IA_NA. */ if (ia_make_key(&key, iaid, (char *)client_id->data, client_id->len, MDL) != ISC_R_SUCCESS) { log_fatal("iterate_over_ia_na: no memory for " "key."); } existing_ia_na = NULL; if (ia_hash_lookup(&existing_ia_na, ia_na_active, (unsigned char *)key.data, key.len, MDL)) { /* * Make sure this address is in the IA_NA. */ for (i=0; inum_iasubopt; i++) { struct iasubopt *tmp; struct in6_addr *in6_addr; tmp = existing_ia_na->iasubopt[i]; in6_addr = &tmp->addr; if (memcmp(in6_addr, iaaddr.data, 16) == 0) { iasubopt_reference(&lease, tmp, MDL); break; } } } data_string_forget(&key, MDL); } if ((host != NULL) || (lease != NULL)) { ia_na_match(client_id, &iaaddr, lease); } else { ia_na_nomatch(client_id, &iaaddr, (u_int32_t *)cli_enc_opt_data.data, packet, reply_data, &reply_ofs, sizeof(reply_data)); } if (lease != NULL) { iasubopt_dereference(&lease, MDL); } data_string_forget(&iaaddr, MDL); option_state_dereference(&cli_enc_opt_state, MDL); data_string_forget(&cli_enc_opt_data, MDL); } /* * Return our reply to the caller. */ reply_ret->len = reply_ofs; reply_ret->buffer = NULL; if (!buffer_allocate(&reply_ret->buffer, reply_ofs, MDL)) { log_fatal("No memory to store reply."); } reply_ret->data = reply_ret->buffer->data; memcpy(reply_ret->buffer->data, reply, reply_ofs); exit: if (lease != NULL) { iasubopt_dereference(&lease, MDL); } if (fixed_addr.buffer != NULL) { data_string_forget(&fixed_addr, MDL); } if (iaaddr.buffer != NULL) { data_string_forget(&iaaddr, MDL); } if (cli_enc_opt_state != NULL) { option_state_dereference(&cli_enc_opt_state, MDL); } if (cli_enc_opt_data.buffer != NULL) { data_string_forget(&cli_enc_opt_data, MDL); } if (opt_state != NULL) { option_state_dereference(&opt_state, MDL); } } /* * Decline means a client has detected that something else is using an * address we gave it. * * Since we're only dealing with fixed leases for now, there's not * much we can do, other that log the occurrence. * * When we start issuing addresses from pools, then we will have to * record our declined addresses and issue another. In general with * IPv6 there is no worry about DoS by clients exhausting space, but * we still need to be aware of this possibility. */ /* TODO: IA_TA */ static void dhcpv6_decline(struct data_string *reply, struct packet *packet) { struct data_string client_id; struct data_string server_id; /* * Validate our input. */ if (!valid_client_resp(packet, &client_id, &server_id)) { return; } /* If the DECLINE arrived via unicast and unicast option isn't set, * reject it per RFC 3315, Sec 18.2.7 */ if (packet->unicast == ISC_TRUE && is_unicast_option_defined(packet) == ISC_FALSE) { unicast_reject(reply, packet, &client_id, &server_id); } else { /* * Undefined for IA_PD. */ delete_option(&dhcpv6_universe, packet->options, D6O_IA_PD); /* * And operate on each IA_NA in this packet. */ iterate_over_ia_na(reply, packet, &client_id, &server_id, "Decline", ia_na_match_decline, ia_na_nomatch_decline); } data_string_forget(&server_id, MDL); data_string_forget(&client_id, MDL); } static void ia_na_match_release(const struct data_string *client_id, const struct data_string *iaaddr, struct iasubopt *lease) { char tmp_addr[INET6_ADDRSTRLEN]; log_info("Client %s releases address %s", print_hex_1(client_id->len, client_id->data, 60), inet_ntop(AF_INET6, iaaddr->data, tmp_addr, sizeof(tmp_addr))); if (lease != NULL) { release_lease6(lease->ipv6_pool, lease); lease->ia->cltt = cur_time; write_ia(lease->ia); } } static void ia_na_nomatch_release(const struct data_string *client_id, const struct data_string *iaaddr, u_int32_t *ia_na_id, struct packet *packet, char *reply_data, int *reply_ofs, int reply_len) { char tmp_addr[INET6_ADDRSTRLEN]; struct option_state *host_opt_state; int len; log_info("Client %s releases address %s, which is not leased to it.", print_hex_1(client_id->len, client_id->data, 60), inet_ntop(AF_INET6, iaaddr->data, tmp_addr, sizeof(tmp_addr))); /* * Create state for this IA_NA. */ host_opt_state = NULL; if (!option_state_allocate(&host_opt_state, MDL)) { log_error("ia_na_nomatch_release: out of memory " "allocating option_state."); goto exit; } if (!set_status_code(STATUS_NoBinding, "Release for non-leased address.", host_opt_state)) { goto exit; } /* * Insure we have enough space */ if (reply_len < (*reply_ofs + 16)) { log_error("ia_na_nomatch_release: " "out of space for reply packet."); goto exit; } /* * Put our status code into the reply packet. */ len = store_options6(reply_data+(*reply_ofs)+16, reply_len-(*reply_ofs)-16, host_opt_state, packet, required_opts_STATUS_CODE, NULL); /* * Store the non-encapsulated option data for this * IA_NA into our reply packet. Defined in RFC 3315, * section 22.4. */ /* option number */ putUShort((unsigned char *)reply_data+(*reply_ofs), D6O_IA_NA); /* option length */ putUShort((unsigned char *)reply_data+(*reply_ofs)+2, len + 12); /* IA_NA, copied from the client */ memcpy(reply_data+(*reply_ofs)+4, ia_na_id, 4); /* t1 and t2, odd that we need them, but here it is */ putULong((unsigned char *)reply_data+(*reply_ofs)+8, 0); putULong((unsigned char *)reply_data+(*reply_ofs)+12, 0); /* * Get ready for next IA_NA. */ *reply_ofs += (len + 16); exit: option_state_dereference(&host_opt_state, MDL); } static void ia_pd_match_release(const struct data_string *client_id, const struct data_string *iapref, struct iasubopt *prefix) { char tmp_addr[INET6_ADDRSTRLEN]; log_info("Client %s releases prefix %s/%u", print_hex_1(client_id->len, client_id->data, 60), inet_ntop(AF_INET6, iapref->data + 9, tmp_addr, sizeof(tmp_addr)), (unsigned) getUChar(iapref->data + 8)); if (prefix != NULL) { release_lease6(prefix->ipv6_pool, prefix); prefix->ia->cltt = cur_time; write_ia(prefix->ia); } } static void ia_pd_nomatch_release(const struct data_string *client_id, const struct data_string *iapref, u_int32_t *ia_pd_id, struct packet *packet, char *reply_data, int *reply_ofs, int reply_len) { char tmp_addr[INET6_ADDRSTRLEN]; struct option_state *host_opt_state; int len; log_info("Client %s releases prefix %s/%u, which is not leased to it.", print_hex_1(client_id->len, client_id->data, 60), inet_ntop(AF_INET6, iapref->data + 9, tmp_addr, sizeof(tmp_addr)), (unsigned) getUChar(iapref->data + 8)); /* * Create state for this IA_PD. */ host_opt_state = NULL; if (!option_state_allocate(&host_opt_state, MDL)) { log_error("ia_pd_nomatch_release: out of memory " "allocating option_state."); goto exit; } if (!set_status_code(STATUS_NoBinding, "Release for non-leased prefix.", host_opt_state)) { goto exit; } /* * Insure we have enough space */ if (reply_len < (*reply_ofs + 16)) { log_error("ia_pd_nomatch_release: " "out of space for reply packet."); goto exit; } /* * Put our status code into the reply packet. */ len = store_options6(reply_data+(*reply_ofs)+16, reply_len-(*reply_ofs)-16, host_opt_state, packet, required_opts_STATUS_CODE, NULL); /* * Store the non-encapsulated option data for this * IA_PD into our reply packet. Defined in RFC 3315, * section 22.4. */ /* option number */ putUShort((unsigned char *)reply_data+(*reply_ofs), D6O_IA_PD); /* option length */ putUShort((unsigned char *)reply_data+(*reply_ofs)+2, len + 12); /* IA_PD, copied from the client */ memcpy(reply_data+(*reply_ofs)+4, ia_pd_id, 4); /* t1 and t2, odd that we need them, but here it is */ putULong((unsigned char *)reply_data+(*reply_ofs)+8, 0); putULong((unsigned char *)reply_data+(*reply_ofs)+12, 0); /* * Get ready for next IA_PD. */ *reply_ofs += (len + 16); exit: option_state_dereference(&host_opt_state, MDL); } static void iterate_over_ia_pd(struct data_string *reply_ret, struct packet *packet, const struct data_string *client_id, const struct data_string *server_id, const char *packet_type, void (*ia_pd_match)(), void (*ia_pd_nomatch)()) { struct data_string reply_new; int reply_len; struct option_state *opt_state; struct host_decl *packet_host; struct option_cache *ia; struct option_cache *oc; /* cli_enc_... variables come from the IA_PD options */ struct data_string cli_enc_opt_data; struct option_state *cli_enc_opt_state; struct host_decl *host; struct data_string iaprefix; char reply_data[65536]; int reply_ofs; struct iasubopt *prefix; struct ia_xx *existing_ia_pd; int i; struct data_string key; u_int32_t iaid; /* * Initialize to empty values, in case we have to exit early. */ memset(&reply_new, 0, sizeof(reply_new)); opt_state = NULL; memset(&cli_enc_opt_data, 0, sizeof(cli_enc_opt_data)); cli_enc_opt_state = NULL; memset(&iaprefix, 0, sizeof(iaprefix)); prefix = NULL; /* * Compute the available length for the reply. */ reply_len = sizeof(reply_data) - reply_ret->len; reply_ofs = 0; /* * Find the host record that matches from the packet, if any. */ packet_host = NULL; find_hosts6(&packet_host, packet, client_id, MDL); /* * Build our option state for reply. */ opt_state = NULL; if (!option_state_allocate(&opt_state, MDL)) { log_error("iterate_over_ia_pd: no memory for option_state."); goto exit; } execute_statements_in_scope(NULL, packet, NULL, NULL, packet->options, opt_state, &global_scope, root_group, NULL, NULL); /* * Loop through the IA_PD reported by the client, and deal with * prefixes reported as already in use. */ for (ia = lookup_option(&dhcpv6_universe, packet->options, D6O_IA_PD); ia != NULL; ia = ia->next) { if (!get_encapsulated_IA_state(&cli_enc_opt_state, &cli_enc_opt_data, packet, ia, IA_PD_OFFSET)) { goto exit; } iaid = getULong(cli_enc_opt_data.data); oc = lookup_option(&dhcpv6_universe, cli_enc_opt_state, D6O_IAPREFIX); if (oc == NULL) { /* no prefix given for this IA_PD, ignore */ option_state_dereference(&cli_enc_opt_state, MDL); data_string_forget(&cli_enc_opt_data, MDL); continue; } for (; oc != NULL; oc = oc->next) { memset(&iaprefix, 0, sizeof(iaprefix)); if (!evaluate_option_cache(&iaprefix, packet, NULL, NULL, packet->options, NULL, &global_scope, oc, MDL)) { log_error("iterate_over_ia_pd: " "error evaluating IAPREFIX."); goto exit; } /* * Now we need to figure out which host record matches * this IA_PD and IAPREFIX (encapsulated option contents * matching a host record by option). * * XXX: We don't currently track IA_PD separately, but * we will need to do this! */ host = NULL; if (!find_hosts_by_option(&host, packet, cli_enc_opt_state, MDL)) { if (packet_host != NULL) { host = packet_host; } else { host = NULL; } } while (host != NULL) { if (host->fixed_prefix != NULL) { struct iaddrcidrnetlist *l; int plen = (int) getUChar(iaprefix.data + 8); for (l = host->fixed_prefix; l != NULL; l = l->next) { if (plen != l->cidrnet.bits) continue; if (memcmp(iaprefix.data + 9, l->cidrnet.lo_addr.iabuf, 16) == 0) break; } if ((l != NULL) && (iaprefix.len >= 17)) break; } host = host->n_ipaddr; } if ((host == NULL) && (iaprefix.len >= IAPREFIX_OFFSET)) { /* * Find existing IA_PD. */ if (ia_make_key(&key, iaid, (char *)client_id->data, client_id->len, MDL) != ISC_R_SUCCESS) { log_fatal("iterate_over_ia_pd: no memory for " "key."); } existing_ia_pd = NULL; if (ia_hash_lookup(&existing_ia_pd, ia_pd_active, (unsigned char *)key.data, key.len, MDL)) { /* * Make sure this prefix is in the IA_PD. */ for (i = 0; i < existing_ia_pd->num_iasubopt; i++) { struct iasubopt *tmp; u_int8_t plen; plen = getUChar(iaprefix.data + 8); tmp = existing_ia_pd->iasubopt[i]; if ((tmp->plen == plen) && (memcmp(&tmp->addr, iaprefix.data + 9, 16) == 0)) { iasubopt_reference(&prefix, tmp, MDL); break; } } } data_string_forget(&key, MDL); } if ((host != NULL) || (prefix != NULL)) { ia_pd_match(client_id, &iaprefix, prefix); } else { ia_pd_nomatch(client_id, &iaprefix, (u_int32_t *)cli_enc_opt_data.data, packet, reply_data, &reply_ofs, reply_len - reply_ofs); } if (prefix != NULL) { iasubopt_dereference(&prefix, MDL); } data_string_forget(&iaprefix, MDL); } option_state_dereference(&cli_enc_opt_state, MDL); data_string_forget(&cli_enc_opt_data, MDL); } /* * Return our reply to the caller. * The IA_NA routine has already filled at least the header. */ reply_new.len = reply_ret->len + reply_ofs; if (!buffer_allocate(&reply_new.buffer, reply_new.len, MDL)) { log_fatal("No memory to store reply."); } reply_new.data = reply_new.buffer->data; memcpy(reply_new.buffer->data, reply_ret->buffer->data, reply_ret->len); memcpy(reply_new.buffer->data + reply_ret->len, reply_data, reply_ofs); data_string_forget(reply_ret, MDL); data_string_copy(reply_ret, &reply_new, MDL); data_string_forget(&reply_new, MDL); exit: if (prefix != NULL) { iasubopt_dereference(&prefix, MDL); } if (iaprefix.buffer != NULL) { data_string_forget(&iaprefix, MDL); } if (cli_enc_opt_state != NULL) { option_state_dereference(&cli_enc_opt_state, MDL); } if (cli_enc_opt_data.buffer != NULL) { data_string_forget(&cli_enc_opt_data, MDL); } if (opt_state != NULL) { option_state_dereference(&opt_state, MDL); } } /* * Release means a client is done with the leases. */ static void dhcpv6_release(struct data_string *reply, struct packet *packet) { struct data_string client_id; struct data_string server_id; /* * Validate our input. */ if (!valid_client_resp(packet, &client_id, &server_id)) { return; } /* If the RELEASE arrived via unicast and unicast option isn't set, * reject it per RFC 3315, Sec 18.2.6 */ if (packet->unicast == ISC_TRUE && is_unicast_option_defined(packet) == ISC_FALSE) { unicast_reject(reply, packet, &client_id, &server_id); } else { /* * And operate on each IA_NA in this packet. */ iterate_over_ia_na(reply, packet, &client_id, &server_id, "Release", ia_na_match_release, ia_na_nomatch_release); /* * And operate on each IA_PD in this packet. */ iterate_over_ia_pd(reply, packet, &client_id, &server_id, "Release", ia_pd_match_release, ia_pd_nomatch_release); } data_string_forget(&server_id, MDL); data_string_forget(&client_id, MDL); } /* * Information-Request is used by clients who have obtained an address * from other means, but want configuration information from the server. */ static void dhcpv6_information_request(struct data_string *reply, struct packet *packet) { struct data_string client_id; struct data_string server_id; /* * Validate our input. */ if (!valid_client_info_req(packet, &server_id)) { return; } /* * Get our client ID, if there is one. */ memset(&client_id, 0, sizeof(client_id)); if (get_client_id(packet, &client_id) != ISC_R_SUCCESS) { data_string_forget(&client_id, MDL); } /* * Use the lease_to_client() function. This will work fine, * because the valid_client_info_req() insures that we * don't have any IA that would cause us to allocate * resources to the client. */ lease_to_client(reply, packet, &client_id, server_id.data != NULL ? &server_id : NULL); /* * Cleanup. */ if (client_id.data != NULL) { data_string_forget(&client_id, MDL); } data_string_forget(&server_id, MDL); } /* * The Relay-forw message is sent by relays. It typically contains a * single option, which encapsulates an entire packet. * * We need to build an encapsulated reply. */ /* XXX: this is very, very similar to do_packet6(), and should probably be combined in a clever way */ /* DHCPv6 server side */ static void dhcpv6_relay_forw(struct data_string *reply_ret, struct packet *packet) { struct option_cache *oc; struct data_string enc_opt_data; struct packet *enc_packet; unsigned char msg_type; const struct dhcpv6_packet *msg; const struct dhcpv6_relay_packet *relay; struct data_string enc_reply; char link_addr[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; char peer_addr[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; struct data_string a_opt, packet_ero; struct option_state *opt_state; static char reply_data[65536]; struct dhcpv6_relay_packet *reply; int reply_ofs; /* * Initialize variables for early exit. */ opt_state = NULL; memset(&a_opt, 0, sizeof(a_opt)); memset(&packet_ero, 0, sizeof(packet_ero)); memset(&enc_reply, 0, sizeof(enc_reply)); memset(&enc_opt_data, 0, sizeof(enc_opt_data)); enc_packet = NULL; /* * Get our encapsulated relay message. */ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_RELAY_MSG); if (oc == NULL) { inet_ntop(AF_INET6, &packet->dhcpv6_link_address, link_addr, sizeof(link_addr)); inet_ntop(AF_INET6, &packet->dhcpv6_peer_address, peer_addr, sizeof(peer_addr)); log_info("Relay-forward from %s with link address=%s and " "peer address=%s missing Relay Message option.", piaddr(packet->client_addr), link_addr, peer_addr); goto exit; } if (!evaluate_option_cache(&enc_opt_data, NULL, NULL, NULL, NULL, NULL, &global_scope, oc, MDL)) { /* should be dhcpv6_relay_forw */ log_error("dhcpv6_forw_relay: error evaluating " "relayed message."); goto exit; } if (!packet6_len_okay((char *)enc_opt_data.data, enc_opt_data.len)) { /* should be dhcpv6_relay_forw */ log_error("dhcpv6_forw_relay: encapsulated packet too short."); goto exit; } /* * Build a packet structure from this encapsulated packet. */ enc_packet = NULL; if (!packet_allocate(&enc_packet, MDL)) { /* should be dhcpv6_relay_forw */ log_error("dhcpv6_forw_relay: " "no memory for encapsulated packet."); goto exit; } if (!option_state_allocate(&enc_packet->options, MDL)) { /* should be dhcpv6_relay_forw */ log_error("dhcpv6_forw_relay: " "no memory for encapsulated packet's options."); goto exit; } enc_packet->client_port = packet->client_port; enc_packet->client_addr = packet->client_addr; interface_reference(&enc_packet->interface, packet->interface, MDL); enc_packet->dhcpv6_container_packet = packet; msg_type = enc_opt_data.data[0]; if ((msg_type == DHCPV6_RELAY_FORW) || (msg_type == DHCPV6_RELAY_REPL)) { int relaylen = (int)(offsetof(struct dhcpv6_relay_packet, options)); relay = (struct dhcpv6_relay_packet *)enc_opt_data.data; enc_packet->dhcpv6_msg_type = relay->msg_type; /* relay-specific data */ enc_packet->dhcpv6_hop_count = relay->hop_count; memcpy(&enc_packet->dhcpv6_link_address, relay->link_address, sizeof(relay->link_address)); memcpy(&enc_packet->dhcpv6_peer_address, relay->peer_address, sizeof(relay->peer_address)); if (!parse_option_buffer(enc_packet->options, relay->options, enc_opt_data.len - relaylen, &dhcpv6_universe)) { /* no logging here, as parse_option_buffer() logs all cases where it fails */ goto exit; } } else if ((msg_type == DHCPV6_DHCPV4_QUERY) || (msg_type == DHCPV6_DHCPV4_RESPONSE)) { #ifdef DHCP4o6 if (!dhcpv4_over_dhcpv6 || (msg_type == DHCPV6_DHCPV4_RESPONSE)) { log_error("dhcpv6_relay_forw: " "unsupported %s message type.", dhcpv6_type_names[msg_type]); goto exit; } forw_dhcpv4_query(packet); goto exit; #else /* DHCP4o6 */ log_error("dhcpv6_relay_forw: unsupported %s message type.", dhcpv6_type_names[msg_type]); goto exit; #endif /* DHCP4o6 */ } else { int msglen = (int)(offsetof(struct dhcpv6_packet, options)); msg = (struct dhcpv6_packet *)enc_opt_data.data; enc_packet->dhcpv6_msg_type = msg->msg_type; /* message-specific data */ memcpy(enc_packet->dhcpv6_transaction_id, msg->transaction_id, sizeof(enc_packet->dhcpv6_transaction_id)); if (!parse_option_buffer(enc_packet->options, msg->options, enc_opt_data.len - msglen, &dhcpv6_universe)) { /* no logging here, as parse_option_buffer() logs all cases where it fails */ goto exit; } } /* * This is recursive. It is possible to exceed maximum packet size. * XXX: This will cause the packet send to fail. */ build_dhcpv6_reply(&enc_reply, enc_packet); /* * If we got no encapsulated data, then it is discarded, and * our reply-forw is also discarded. */ if (enc_reply.data == NULL) { goto exit; } /* * Now we can use the reply_data buffer. * Packet header stuff all comes from the forward message. */ reply = (struct dhcpv6_relay_packet *)reply_data; reply->msg_type = DHCPV6_RELAY_REPL; reply->hop_count = packet->dhcpv6_hop_count; memcpy(reply->link_address, &packet->dhcpv6_link_address, sizeof(reply->link_address)); memcpy(reply->peer_address, &packet->dhcpv6_peer_address, sizeof(reply->peer_address)); reply_ofs = (int)(offsetof(struct dhcpv6_relay_packet, options)); /* * Get the reply option state. */ opt_state = NULL; if (!option_state_allocate(&opt_state, MDL)) { log_error("dhcpv6_relay_forw: no memory for option state."); goto exit; } /* * Append the interface-id if present. */ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_INTERFACE_ID); if (oc != NULL) { if (!evaluate_option_cache(&a_opt, packet, NULL, NULL, packet->options, NULL, &global_scope, oc, MDL)) { log_error("dhcpv6_relay_forw: error evaluating " "Interface ID."); goto exit; } if (!save_option_buffer(&dhcpv6_universe, opt_state, NULL, (unsigned char *)a_opt.data, a_opt.len, D6O_INTERFACE_ID, 0)) { log_error("dhcpv6_relay_forw: error saving " "Interface ID."); goto exit; } data_string_forget(&a_opt, MDL); } #if defined(RELAY_PORT) /* * Append the relay_source_port option if present. */ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_RELAY_SOURCE_PORT); if (oc != NULL) { if (!evaluate_option_cache(&a_opt, packet, NULL, NULL, packet->options, NULL, &global_scope, oc, MDL)) { log_error("dhcpv6_relay_forw: error evaluating " "Relay Source Port."); goto exit; } if (!save_option_buffer(&dhcpv6_universe, opt_state, NULL, (unsigned char *)a_opt.data, a_opt.len, D6O_RELAY_SOURCE_PORT, 0)) { log_error("dhcpv6_relay_forw: error saving " "Relay Source Port."); goto exit; } data_string_forget(&a_opt, MDL); packet->relay_source_port = ISC_TRUE; } #endif /* * Append our encapsulated stuff for caller. */ if (!save_option_buffer(&dhcpv6_universe, opt_state, NULL, (unsigned char *)enc_reply.data, enc_reply.len, D6O_RELAY_MSG, 0)) { log_error("dhcpv6_relay_forw: error saving Relay MSG."); goto exit; } /* * Get the ERO if any. */ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_ERO); if (oc != NULL) { unsigned req; int i; if (!evaluate_option_cache(&packet_ero, packet, NULL, NULL, packet->options, NULL, &global_scope, oc, MDL) || (packet_ero.len & 1)) { log_error("dhcpv6_relay_forw: error evaluating ERO."); goto exit; } /* Decode and apply the ERO. */ for (i = 0; i < packet_ero.len; i += 2) { req = getUShort(packet_ero.data + i); /* Already in the reply? */ oc = lookup_option(&dhcpv6_universe, opt_state, req); if (oc != NULL) continue; /* Get it from the packet if present. */ oc = lookup_option(&dhcpv6_universe, packet->options, req); if (oc == NULL) continue; if (!evaluate_option_cache(&a_opt, packet, NULL, NULL, packet->options, NULL, &global_scope, oc, MDL)) { log_error("dhcpv6_relay_forw: error " "evaluating option %u.", req); goto exit; } if (!save_option_buffer(&dhcpv6_universe, opt_state, NULL, (unsigned char *)a_opt.data, a_opt.len, req, 0)) { log_error("dhcpv6_relay_forw: error saving " "option %u.", req); goto exit; } data_string_forget(&a_opt, MDL); } } reply_ofs += store_options6(reply_data + reply_ofs, sizeof(reply_data) - reply_ofs, opt_state, packet, required_opts_agent, &packet_ero); /* * Return our reply to the caller. */ reply_ret->len = reply_ofs; reply_ret->buffer = NULL; if (!buffer_allocate(&reply_ret->buffer, reply_ret->len, MDL)) { log_fatal("No memory to store reply."); } reply_ret->data = reply_ret->buffer->data; memcpy(reply_ret->buffer->data, reply_data, reply_ofs); exit: if (opt_state != NULL) option_state_dereference(&opt_state, MDL); if (a_opt.data != NULL) { data_string_forget(&a_opt, MDL); } if (packet_ero.data != NULL) { data_string_forget(&packet_ero, MDL); } if (enc_reply.data != NULL) { data_string_forget(&enc_reply, MDL); } if (enc_opt_data.data != NULL) { data_string_forget(&enc_opt_data, MDL); } if (enc_packet != NULL) { packet_dereference(&enc_packet, MDL); } } #ifdef DHCP4o6 /* \brief Internal processing of a relayed DHCPv4-query * (DHCPv4 server side) * * Code copied from \ref dhcpv6_relay_forw() which itself is * from \ref do_packet6(). * * \param reply_ret pointer to the response * \param packet the query */ static void dhcp4o6_relay_forw(struct data_string *reply_ret, struct packet *packet) { struct option_cache *oc; struct data_string enc_opt_data; struct packet *enc_packet; unsigned char msg_type; const struct dhcpv6_relay_packet *relay; const struct dhcpv4_over_dhcpv6_packet *msg; struct data_string enc_reply; char link_addr[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; char peer_addr[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; struct data_string a_opt, packet_ero; struct option_state *opt_state; static char reply_data[65536]; struct dhcpv6_relay_packet *reply; int reply_ofs; /* * Initialize variables for early exit. */ opt_state = NULL; memset(&a_opt, 0, sizeof(a_opt)); memset(&packet_ero, 0, sizeof(packet_ero)); memset(&enc_reply, 0, sizeof(enc_reply)); memset(&enc_opt_data, 0, sizeof(enc_opt_data)); enc_packet = NULL; /* * Get our encapsulated relay message. */ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_RELAY_MSG); if (oc == NULL) { inet_ntop(AF_INET6, &packet->dhcpv6_link_address, link_addr, sizeof(link_addr)); inet_ntop(AF_INET6, &packet->dhcpv6_peer_address, peer_addr, sizeof(peer_addr)); log_info("Relay-forward from %s with link address=%s and " "peer address=%s missing Relay Message option.", piaddr(packet->client_addr), link_addr, peer_addr); goto exit; } if (!evaluate_option_cache(&enc_opt_data, NULL, NULL, NULL, NULL, NULL, &global_scope, oc, MDL)) { log_error("dhcp4o6_relay_forw: error evaluating " "relayed message."); goto exit; } if (!packet6_len_okay((char *)enc_opt_data.data, enc_opt_data.len)) { log_error("dhcp4o6_relay_forw: " "encapsulated packet too short."); goto exit; } /* * Build a packet structure from this encapsulated packet. */ if (!packet_allocate(&enc_packet, MDL)) { log_error("dhcp4o6_relay_forw: " "no memory for encapsulated packet."); goto exit; } if (!option_state_allocate(&enc_packet->options, MDL)) { log_error("dhcp4o6_relay_forw: " "no memory for encapsulated packet's options."); goto exit; } enc_packet->client_port = packet->client_port; enc_packet->client_addr = packet->client_addr; interface_reference(&enc_packet->interface, packet->interface, MDL); enc_packet->dhcpv6_container_packet = packet; msg_type = enc_opt_data.data[0]; if ((msg_type == DHCPV6_RELAY_FORW) || (msg_type == DHCPV6_RELAY_REPL)) { int relaylen = (int)(offsetof(struct dhcpv6_relay_packet, options)); relay = (struct dhcpv6_relay_packet *)enc_opt_data.data; enc_packet->dhcpv6_msg_type = relay->msg_type; /* relay-specific data */ enc_packet->dhcpv6_hop_count = relay->hop_count; memcpy(&enc_packet->dhcpv6_link_address, relay->link_address, sizeof(relay->link_address)); memcpy(&enc_packet->dhcpv6_peer_address, relay->peer_address, sizeof(relay->peer_address)); if (!parse_option_buffer(enc_packet->options, relay->options, enc_opt_data.len - relaylen, &dhcpv6_universe)) { /* no logging here, as parse_option_buffer() logs all cases where it fails */ goto exit; } } else if ((msg_type == DHCPV6_DHCPV4_QUERY) || (msg_type == DHCPV6_DHCPV4_RESPONSE)) { int msglen = (int)(offsetof(struct dhcpv4_over_dhcpv6_packet, options)); msg = (struct dhcpv4_over_dhcpv6_packet *)enc_opt_data.data; enc_packet->dhcpv6_msg_type = msg->msg_type; /* message-specific data */ memcpy(enc_packet->dhcp4o6_flags, msg->flags, sizeof(enc_packet->dhcp4o6_flags)); if (!parse_option_buffer(enc_packet->options, msg->options, enc_opt_data.len - msglen, &dhcpv6_universe)) { /* no logging here, as parse_option_buffer() logs all cases where it fails */ goto exit; } } else { log_error("dhcp4o6_relay_forw: unexpected message of type %d.", (int)msg_type); goto exit; } /* * This is recursive. It is possible to exceed maximum packet size. * XXX: This will cause the packet send to fail. */ build_dhcpv6_reply(&enc_reply, enc_packet); /* * If we got no encapsulated data, then it is discarded, and * our reply-forw is also discarded. */ if (enc_reply.data == NULL) { goto exit; } /* * Now we can use the reply_data buffer. * Packet header stuff all comes from the forward message. */ reply = (struct dhcpv6_relay_packet *)reply_data; reply->msg_type = DHCPV6_RELAY_REPL; reply->hop_count = packet->dhcpv6_hop_count; memcpy(reply->link_address, &packet->dhcpv6_link_address, sizeof(reply->link_address)); memcpy(reply->peer_address, &packet->dhcpv6_peer_address, sizeof(reply->peer_address)); reply_ofs = (int)(offsetof(struct dhcpv6_relay_packet, options)); /* * Get the reply option state. */ if (!option_state_allocate(&opt_state, MDL)) { log_error("dhcp4o6_relay_forw: no memory for option state."); goto exit; } /* * Append the interface-id if present. */ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_INTERFACE_ID); if (oc != NULL) { if (!evaluate_option_cache(&a_opt, packet, NULL, NULL, packet->options, NULL, &global_scope, oc, MDL)) { log_error("dhcp4o6_relay_forw: error evaluating " "Interface ID."); goto exit; } if (!save_option_buffer(&dhcpv6_universe, opt_state, NULL, (unsigned char *)a_opt.data, a_opt.len, D6O_INTERFACE_ID, 0)) { log_error("dhcp4o6_relay_forw: error saving " "Interface ID."); goto exit; } data_string_forget(&a_opt, MDL); } #if defined(RELAY_PORT) /* * Append the relay_source_port option if present. */ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_RELAY_SOURCE_PORT); if (oc != NULL) { if (!evaluate_option_cache(&a_opt, packet, NULL, NULL, packet->options, NULL, &global_scope, oc, MDL)) { log_error("dhcpv4o6_relay_forw: error evaluating " "Relay Source Port."); goto exit; } if (!save_option_buffer(&dhcpv6_universe, opt_state, NULL, (unsigned char *)a_opt.data, a_opt.len, D6O_RELAY_SOURCE_PORT, 0)) { log_error("dhcpv4o6_relay_forw: error saving " "Relay Source Port."); goto exit; } data_string_forget(&a_opt, MDL); packet->relay_source_port = ISC_TRUE; } #endif /* * Append our encapsulated stuff for caller. */ if (!save_option_buffer(&dhcpv6_universe, opt_state, NULL, (unsigned char *)enc_reply.data, enc_reply.len, D6O_RELAY_MSG, 0)) { log_error("dhcp4o6_relay_forw: error saving Relay MSG."); goto exit; } /* * Get the ERO if any. */ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_ERO); if (oc != NULL) { unsigned req; int i; if (!evaluate_option_cache(&packet_ero, packet, NULL, NULL, packet->options, NULL, &global_scope, oc, MDL) || (packet_ero.len & 1)) { log_error("dhcp4o6_relay_forw: error evaluating ERO."); goto exit; } /* Decode and apply the ERO. */ for (i = 0; i < packet_ero.len; i += 2) { req = getUShort(packet_ero.data + i); /* Already in the reply? */ oc = lookup_option(&dhcpv6_universe, opt_state, req); if (oc != NULL) continue; /* Get it from the packet if present. */ oc = lookup_option(&dhcpv6_universe, packet->options, req); if (oc == NULL) continue; if (!evaluate_option_cache(&a_opt, packet, NULL, NULL, packet->options, NULL, &global_scope, oc, MDL)) { log_error("dhcp4o6_relay_forw: error " "evaluating option %u.", req); goto exit; } if (!save_option_buffer(&dhcpv6_universe, opt_state, NULL, (unsigned char *)a_opt.data, a_opt.len, req, 0)) { log_error("dhcp4o6_relay_forw: error saving " "option %u.", req); goto exit; } data_string_forget(&a_opt, MDL); } } reply_ofs += store_options6(reply_data + reply_ofs, sizeof(reply_data) - reply_ofs, opt_state, packet, required_opts_agent, &packet_ero); /* * Return our reply to the caller. */ reply_ret->len = reply_ofs; reply_ret->buffer = NULL; if (!buffer_allocate(&reply_ret->buffer, reply_ret->len, MDL)) { log_fatal("No memory to store reply."); } reply_ret->data = reply_ret->buffer->data; memcpy(reply_ret->buffer->data, reply_data, reply_ofs); exit: if (opt_state != NULL) option_state_dereference(&opt_state, MDL); if (a_opt.data != NULL) { data_string_forget(&a_opt, MDL); } if (packet_ero.data != NULL) { data_string_forget(&packet_ero, MDL); } if (enc_reply.data != NULL) { data_string_forget(&enc_reply, MDL); } if (enc_opt_data.data != NULL) { data_string_forget(&enc_opt_data, MDL); } if (enc_packet != NULL) { packet_dereference(&enc_packet, MDL); } } /* * \brief Internal processing of a DHCPv4-query * (DHCPv4 server function) * * Code copied from \ref do_packet(). * * \param reply_ret pointer to the response * \param packet the query */ static void dhcp4o6_dhcpv4_query(struct data_string *reply_ret, struct packet *packet) { struct option_cache *oc; struct data_string enc_opt_data; struct packet *enc_packet; struct data_string enc_response; struct option_state *opt_state; static char response_data[65536]; struct dhcpv4_over_dhcpv6_packet *response; int response_ofs; /* * Initialize variables for early exit. */ opt_state = NULL; memset(&enc_response, 0, sizeof(enc_response)); memset(&enc_opt_data, 0, sizeof(enc_opt_data)); enc_packet = NULL; /* * Get our encapsulated relay message. */ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_DHCPV4_MSG); if (oc == NULL) { log_info("DHCPv4-query from %s missing DHCPv4 Message option.", piaddr(packet->client_addr)); goto exit; } if (!evaluate_option_cache(&enc_opt_data, NULL, NULL, NULL, NULL, NULL, &global_scope, oc, MDL)) { log_error("dhcp4o6_dhcpv4_query: error evaluating " "DHCPv4 message."); goto exit; } if (enc_opt_data.len < DHCP_FIXED_NON_UDP) { log_error("dhcp4o6_dhcpv4_query: DHCPv4 packet too short."); goto exit; } /* * Build a packet structure from this encapsulated packet. */ if (!packet_allocate(&enc_packet, MDL)) { log_error("dhcp4o6_dhcpv4_query: " "no memory for encapsulated packet."); goto exit; } enc_packet->raw = (struct dhcp_packet *)enc_opt_data.data; enc_packet->packet_length = enc_opt_data.len; enc_packet->dhcp4o6_response = &enc_response; enc_packet->client_port = packet->client_port; enc_packet->client_addr = packet->client_addr; interface_reference(&enc_packet->interface, packet->interface, MDL); enc_packet->dhcpv6_container_packet = packet; if (packet->dhcp4o6_flags[0] & DHCP4O6_QUERY_UNICAST) enc_packet->unicast = 1; if (enc_packet->raw->hlen > sizeof(enc_packet->raw->chaddr)) { log_info("dhcp4o6_dhcpv4_query: " "discarding packet with bogus hlen."); goto exit; } /* Allocate packet->options now so it is non-null for all packets */ if (!option_state_allocate (&enc_packet->options, MDL)) { log_error("dhcp4o6_dhcpv4_query: no memory for options."); goto exit; } /* If there's an option buffer, try to parse it. */ if (enc_packet->packet_length >= DHCP_FIXED_NON_UDP + 4) { struct option_cache *op; if (!parse_options(enc_packet)) { if (enc_packet->options) option_state_dereference (&enc_packet->options, MDL); packet_dereference (&enc_packet, MDL); goto exit; } if (enc_packet->options_valid && (op = lookup_option(&dhcp_universe, enc_packet->options, DHO_DHCP_MESSAGE_TYPE))) { struct data_string dp; memset(&dp, 0, sizeof dp); evaluate_option_cache(&dp, enc_packet, NULL, NULL, enc_packet->options, NULL, NULL, op, MDL); if (dp.len > 0) enc_packet->packet_type = dp.data[0]; else enc_packet->packet_type = 0; data_string_forget(&dp, MDL); } } if (validate_packet(enc_packet) != 0) { if (enc_packet->packet_type) dhcp(enc_packet); else bootp(enc_packet); } /* If the caller kept the packet, they'll have upped the refcnt. */ packet_dereference(&enc_packet, MDL); /* * If we got no response data, then it is discarded, and * our DHCPv4-response is also discarded. */ if (enc_response.data == NULL) { goto exit; } /* * Now we can use the response_data buffer. */ response = (struct dhcpv4_over_dhcpv6_packet *)response_data; response->msg_type = DHCPV6_DHCPV4_RESPONSE; response->flags[0] = response->flags[1] = response->flags[2] = 0; response_ofs = (int)(offsetof(struct dhcpv4_over_dhcpv6_packet, options)); /* * Get the response option state. */ if (!option_state_allocate(&opt_state, MDL)) { log_error("dhcp4o6_dhcpv4_query: no memory for option state."); goto exit; } /* * Append our encapsulated stuff for caller. */ if (!save_option_buffer(&dhcpv6_universe, opt_state, NULL, (unsigned char *)enc_response.data, enc_response.len, D6O_DHCPV4_MSG, 0)) { log_error("dhcp4o6_dhcpv4_query: error saving DHCPv4 MSG."); goto exit; } response_ofs += store_options6(response_data + response_ofs, sizeof(response_data) - response_ofs, opt_state, packet, required_opts_4o6, NULL); /* * Return our response to the caller. */ reply_ret->len = response_ofs; reply_ret->buffer = NULL; if (!buffer_allocate(&reply_ret->buffer, reply_ret->len, MDL)) { log_fatal("dhcp4o6_dhcpv4_query: no memory to store reply."); } reply_ret->data = reply_ret->buffer->data; memcpy(reply_ret->buffer->data, response_data, response_ofs); exit: if (opt_state != NULL) option_state_dereference(&opt_state, MDL); if (enc_response.data != NULL) { data_string_forget(&enc_response, MDL); } if (enc_opt_data.data != NULL) { data_string_forget(&enc_opt_data, MDL); } if (enc_packet != NULL) { packet_dereference(&enc_packet, MDL); } } /* * \brief Forward a DHCPv4-query message to the DHCPv4 side * (DHCPv6 server function) * * Format: interface:16 + address:16 + udp:4 + DHCPv6 DHCPv4-query message * * \brief packet the DHCPv6 DHCPv4-query message */ static void forw_dhcpv4_query(struct packet *packet) { struct data_string ds; struct udp_data4o6 udp_data; unsigned len; int cc; /* Get the initial message. */ while (packet->dhcpv6_container_packet != NULL) packet = packet->dhcpv6_container_packet; /* Check the initial message. */ if ((packet->raw == NULL) || (packet->client_addr.len != 16) || (packet->interface == NULL)) { log_error("forw_dhcpv4_query: can't find initial message."); return; } /* Get a buffer. */ len = packet->packet_length + 36; memset(&ds, 0, sizeof(ds)); if (!buffer_allocate(&ds.buffer, len, MDL)) { log_error("forw_dhcpv4_query: " "no memory for encapsulating packet."); return; } ds.data = ds.buffer->data; ds.len = len; /* Fill the buffer. */ strncpy((char *)ds.buffer->data, packet->interface->name, 16); memcpy(ds.buffer->data + 16, packet->client_addr.iabuf, 16); memset(&udp_data, 0, sizeof(udp_data)); udp_data.src_port = packet->client_port; memcpy(ds.buffer->data + 32, &udp_data, 4); memcpy(ds.buffer->data + 36, (unsigned char *)packet->raw, packet->packet_length); /* Forward to the DHCPv4 server. */ cc = send(dhcp4o6_fd, ds.data, ds.len, 0); if (cc < 0) log_error("forw_dhcpv4_query: send(): %m"); data_string_forget(&ds, MDL); } #endif static void dhcpv6_discard(struct packet *packet) { /* INSIST(packet->msg_type > 0); */ /* INSIST(packet->msg_type < dhcpv6_type_name_max); */ log_debug("Discarding %s from %s; message type not handled by server", dhcpv6_type_names[packet->dhcpv6_msg_type], piaddr(packet->client_addr)); } static void build_dhcpv6_reply(struct data_string *reply, struct packet *packet) { memset(reply, 0, sizeof(*reply)); /* I would like to classify the client once here, but * as I don't want to classify all of the incoming packets * I need to do it before handling specific types. * We don't need to classify if we are tossing the packet * or if it is a relay - the classification step will get * done when we process the inner client packet. */ switch (packet->dhcpv6_msg_type) { case DHCPV6_SOLICIT: classify_client(packet); dhcpv6_solicit(reply, packet); break; case DHCPV6_ADVERTISE: dhcpv6_discard(packet); break; case DHCPV6_REQUEST: classify_client(packet); dhcpv6_request(reply, packet); break; case DHCPV6_CONFIRM: classify_client(packet); dhcpv6_confirm(reply, packet); break; case DHCPV6_RENEW: classify_client(packet); dhcpv6_renew(reply, packet); break; case DHCPV6_REBIND: classify_client(packet); dhcpv6_rebind(reply, packet); break; case DHCPV6_REPLY: dhcpv6_discard(packet); break; case DHCPV6_RELEASE: classify_client(packet); dhcpv6_release(reply, packet); break; case DHCPV6_DECLINE: classify_client(packet); dhcpv6_decline(reply, packet); break; case DHCPV6_RECONFIGURE: dhcpv6_discard(packet); break; case DHCPV6_INFORMATION_REQUEST: classify_client(packet); dhcpv6_information_request(reply, packet); break; case DHCPV6_RELAY_FORW: #ifdef DHCP4o6 if (dhcpv4_over_dhcpv6 && (local_family == AF_INET)) dhcp4o6_relay_forw(reply, packet); else #endif /* DHCP4o6 */ dhcpv6_relay_forw(reply, packet); break; case DHCPV6_RELAY_REPL: dhcpv6_discard(packet); break; case DHCPV6_LEASEQUERY: classify_client(packet); dhcpv6_leasequery(reply, packet); break; case DHCPV6_LEASEQUERY_REPLY: dhcpv6_discard(packet); break; case DHCPV6_DHCPV4_QUERY: #ifdef DHCP4o6 if (dhcpv4_over_dhcpv6) { if (local_family == AF_INET6) { forw_dhcpv4_query(packet); } else { dhcp4o6_dhcpv4_query(reply, packet); } } else #endif /* DHCP4o6 */ dhcpv6_discard(packet); break; case DHCPV6_DHCPV4_RESPONSE: dhcpv6_discard(packet); break; default: /* XXX: would be nice if we had "notice" level, as syslog, for this */ log_info("Discarding unknown DHCPv6 message type %d " "from %s", packet->dhcpv6_msg_type, piaddr(packet->client_addr)); } } static void log_packet_in(const struct packet *packet) { struct data_string s; u_int32_t tid; char tmp_addr[INET6_ADDRSTRLEN]; const void *addr; memset(&s, 0, sizeof(s)); if (packet->dhcpv6_msg_type < dhcpv6_type_name_max) { data_string_sprintfa(&s, "%s message from %s port %d", dhcpv6_type_names[packet->dhcpv6_msg_type], piaddr(packet->client_addr), ntohs(packet->client_port)); } else { data_string_sprintfa(&s, "Unknown message type %d from %s port %d", packet->dhcpv6_msg_type, piaddr(packet->client_addr), ntohs(packet->client_port)); } if ((packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) || (packet->dhcpv6_msg_type == DHCPV6_RELAY_REPL)) { addr = &packet->dhcpv6_link_address; data_string_sprintfa(&s, ", link address %s", inet_ntop(AF_INET6, addr, tmp_addr, sizeof(tmp_addr))); addr = &packet->dhcpv6_peer_address; data_string_sprintfa(&s, ", peer address %s", inet_ntop(AF_INET6, addr, tmp_addr, sizeof(tmp_addr))); } else if ((packet->dhcpv6_msg_type != DHCPV6_DHCPV4_QUERY) && (packet->dhcpv6_msg_type != DHCPV6_DHCPV4_RESPONSE)) { tid = 0; memcpy(((char *)&tid)+1, packet->dhcpv6_transaction_id, 3); data_string_sprintfa(&s, ", transaction ID 0x%06X", tid); /* oc = lookup_option(&dhcpv6_universe, packet->options, D6O_CLIENTID); if (oc != NULL) { memset(&tmp_ds, 0, sizeof(tmp_ds_)); if (!evaluate_option_cache(&tmp_ds, packet, NULL, NULL, packet->options, NULL, &global_scope, oc, MDL)) { log_error("Error evaluating Client Identifier"); } else { data_strint_sprintf(&s, ", client ID %s", data_string_forget(&tmp_ds, MDL); } } */ } log_info("%s", s.data); data_string_forget(&s, MDL); } void dhcpv6(struct packet *packet) { struct data_string reply; struct sockaddr_in6 to_addr; int send_ret; /* * Log a message that we received this packet. */ log_packet_in(packet); /* * Build our reply packet. */ build_dhcpv6_reply(&reply, packet); if (reply.data != NULL) { /* * Send our reply, if we have one. */ memset(&to_addr, 0, sizeof(to_addr)); to_addr.sin6_family = AF_INET6; if ((packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) || (packet->dhcpv6_msg_type == DHCPV6_RELAY_REPL)) { to_addr.sin6_port = local_port; } else { to_addr.sin6_port = remote_port; } #if defined (REPLY_TO_SOURCE_PORT) /* * This appears to have been included for testing so we would * not need a root client, but was accidently left in the * final code. We continue to include it in case * some users have come to rely upon it, but leave * it off by default as it's a bad idea. */ to_addr.sin6_port = packet->client_port; #endif #if defined(RELAY_PORT) /* * Check relay source port. */ if (packet->relay_source_port) { to_addr.sin6_port = packet->client_port; } #endif memcpy(&to_addr.sin6_addr, packet->client_addr.iabuf, sizeof(to_addr.sin6_addr)); log_info("Sending %s to %s port %d", dhcpv6_type_names[reply.data[0]], piaddr(packet->client_addr), ntohs(to_addr.sin6_port)); send_ret = send_packet6(packet->interface, reply.data, reply.len, &to_addr); if (send_ret != reply.len) { log_error("dhcpv6: send_packet6() sent %d of %d bytes", send_ret, reply.len); } data_string_forget(&reply, MDL); } } #ifdef DHCP4o6 /* * \brief Receive a DHCPv4-query message from the DHCPv6 side * (DHCPv4 server function) * * Receive a message with a DHCPv4-query inside from the DHCPv6 server. * (code copied from \ref do_packet6() \ref and dhcpv6()) * * Format: interface:16 + address:16 + udp:4 + DHCPv6 DHCPv4-query message * * \param raw the DHCPv6 DHCPv4-query message raw content */ static void recv_dhcpv4_query(struct data_string *raw) { struct interface_info *ip; char name[16 + 1]; struct iaddr iaddr; struct packet *packet; unsigned char msg_type; const struct dhcpv6_relay_packet *relay; const struct dhcpv4_over_dhcpv6_packet *msg; struct data_string reply; struct data_string ds; struct udp_data4o6 udp_data; unsigned len; int cc; memset(name, 0, sizeof(name)); memcpy(name, raw->data, 16); for (ip = interfaces; ip != NULL; ip = ip->next) { if (!strcmp(name, ip->name)) break; } if (ip == NULL) { log_error("recv_dhcpv4_query: can't find interface %s.", name); return; } iaddr.len = 16; memcpy(iaddr.iabuf, raw->data + 16, 16); memset(&udp_data, 0, sizeof(udp_data)); memcpy(&udp_data, raw->data + 32, 4); /* * From do_packet6(). */ if (!packet6_len_okay((char *)raw->data + 36, raw->len - 36)) { log_error("recv_dhcpv4_query: " "short packet from %s, len %d, dropped", piaddr(iaddr), raw->len - 36); return; } /* * Build a packet structure. */ packet = NULL; if (!packet_allocate(&packet, MDL)) { log_error("recv_dhcpv4_query: no memory for packet."); return; } if (!option_state_allocate(&packet->options, MDL)) { log_error("recv_dhcpv4_query: no memory for options."); packet_dereference(&packet, MDL); return; } packet->raw = (struct dhcp_packet *)(raw->data + 36); packet->packet_length = raw->len - 36; packet->client_port = udp_data.src_port; packet->client_addr = iaddr; interface_reference(&packet->interface, ip, MDL); msg_type = raw->data[36]; if ((msg_type == DHCPV6_RELAY_FORW) || (msg_type == DHCPV6_RELAY_REPL)) { int relaylen = (int)(offsetof(struct dhcpv6_relay_packet, options)); relay = (const struct dhcpv6_relay_packet *)(raw->data + 36); packet->dhcpv6_msg_type = relay->msg_type; /* relay-specific data */ packet->dhcpv6_hop_count = relay->hop_count; memcpy(&packet->dhcpv6_link_address, relay->link_address, sizeof(relay->link_address)); memcpy(&packet->dhcpv6_peer_address, relay->peer_address, sizeof(relay->peer_address)); if (!parse_option_buffer(packet->options, relay->options, raw->len - 36 - relaylen, &dhcpv6_universe)) { /* no logging here, as parse_option_buffer() logs all cases where it fails */ packet_dereference(&packet, MDL); return; } } else if ((msg_type == DHCPV6_DHCPV4_QUERY) || (msg_type == DHCPV6_DHCPV4_RESPONSE)) { int msglen = (int)(offsetof(struct dhcpv4_over_dhcpv6_packet, options)); msg = (struct dhcpv4_over_dhcpv6_packet *)(raw->data + 36); packet->dhcpv6_msg_type = msg->msg_type; /* message-specific data */ memcpy(packet->dhcp4o6_flags, msg->flags, sizeof(packet->dhcp4o6_flags)); if (!parse_option_buffer(packet->options, msg->options, raw->len - 36 - msglen, &dhcpv6_universe)) { /* no logging here, as parse_option_buffer() logs all cases where it fails */ packet_dereference(&packet, MDL); return; } } else { log_error("recv_dhcpv4_query: unexpected message of type %d.", (int)msg_type); packet_dereference(&packet, MDL); return; } /* * From dhcpv6(). */ /* * Log a message that we received this packet. */ /* log_packet_in(packet); */ memset(&ds, 0, sizeof(ds)); if (packet->dhcpv6_msg_type < dhcpv6_type_name_max) { data_string_sprintfa(&ds, "%s message from %s", dhcpv6_type_names[packet->dhcpv6_msg_type], piaddr(packet->client_addr)); } else { data_string_sprintfa(&ds, "Unknown message type %d from %s", packet->dhcpv6_msg_type, piaddr(packet->client_addr)); } if ((packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) || (packet->dhcpv6_msg_type == DHCPV6_RELAY_REPL)) { char tmp_addr[INET6_ADDRSTRLEN]; const void *addr; addr = &packet->dhcpv6_link_address; data_string_sprintfa(&ds, ", link address %s", inet_ntop(AF_INET6, addr, tmp_addr, sizeof(tmp_addr))); addr = &packet->dhcpv6_peer_address; data_string_sprintfa(&ds, ", peer address %s", inet_ntop(AF_INET6, addr, tmp_addr, sizeof(tmp_addr))); } else if ((packet->dhcpv6_msg_type != DHCPV6_DHCPV4_QUERY) && (packet->dhcpv6_msg_type != DHCPV6_DHCPV4_RESPONSE)) { u_int32_t tid = 0; memcpy(((char *)&tid)+1, packet->dhcpv6_transaction_id, 3); data_string_sprintfa(&ds, ", transaction ID 0x%06X", tid); } log_info("%s", ds.data); data_string_forget(&ds, MDL); /* * Build our reply packet. */ build_dhcpv6_reply(&reply, packet); if (reply.data == NULL) { packet_dereference(&packet, MDL); return; } /* * Forward the response. */ len = reply.len + 36; memset(&ds, 0, sizeof(ds)); if (!buffer_allocate(&ds.buffer, len, MDL)) { log_error("recv_dhcpv4_query: no memory."); packet_dereference(&packet, MDL); return; } ds.data = ds.buffer->data; ds.len = len; memcpy(ds.buffer->data, name, 16); memcpy(ds.buffer->data + 16, iaddr.iabuf, 16); udp_data.rsp_opt_exist = packet->relay_source_port ? 1 : 0; memcpy(ds.buffer->data + 32, &udp_data, 4); memcpy(ds.buffer->data + 36, reply.data, reply.len); /* * Now we can release the packet. */ packet_dereference(&packet, MDL); cc = send(dhcp4o6_fd, ds.data, ds.len, 0); if (cc < 0) log_error("recv_dhcpv4_query: send(): %m"); data_string_forget(&ds, MDL); } #endif /* DHCP4o6 */ static void seek_shared_host(struct host_decl **hp, struct shared_network *shared) { struct host_decl *nofixed = NULL; struct host_decl *seek, *hold = NULL; /* * Seek forward through fixed addresses for the right link. * * Note: how to do this for fixed prefixes??? */ host_reference(&hold, *hp, MDL); host_dereference(hp, MDL); seek = hold; while (seek != NULL) { if (seek->fixed_addr == NULL) nofixed = seek; else if (fixed_matches_shared(seek, shared)) break; seek = seek->n_ipaddr; } if ((seek == NULL) && (nofixed != NULL)) seek = nofixed; if (seek != NULL) host_reference(hp, seek, MDL); } static isc_boolean_t fixed_matches_shared(struct host_decl *host, struct shared_network *shared) { struct subnet *subnet; struct data_string addr; isc_boolean_t matched; struct iaddr fixed; if (host->fixed_addr == NULL) return ISC_FALSE; memset(&addr, 0, sizeof(addr)); if (!evaluate_option_cache(&addr, NULL, NULL, NULL, NULL, NULL, &global_scope, host->fixed_addr, MDL)) return ISC_FALSE; if (addr.len < 16) { data_string_forget(&addr, MDL); return ISC_FALSE; } fixed.len = 16; memcpy(fixed.iabuf, addr.data, 16); matched = ISC_FALSE; for (subnet = shared->subnets ; subnet != NULL ; subnet = subnet->next_sibling) { if (addr_eq(subnet_number(fixed, subnet->netmask), subnet->net)) { matched = ISC_TRUE; break; } } data_string_forget(&addr, MDL); return matched; } /*! * * \brief Constructs a REPLY with status of UseMulticast to a given packet * * Per RFC 3315 Secs 18.2.1,3,6 & 7, when a server rejects a client's * unicast-sent packet, the response must only contain the client id, * server id, and a status code option of 5 (UseMulticast). This function * constructs such a packet and returns it as a data_string. * * \param reply_ret = data_string which will receive the newly constructed * reply * \param packet = client request which is being rejected * \param client_id = data_string which contains the client id * \param server_id = data_string which which contains the server id * */ void unicast_reject(struct data_string *reply_ret, struct packet *packet, const struct data_string *client_id, const struct data_string *server_id) { struct reply_state reply; memset(&reply, 0x0, sizeof(struct reply_state)); /* Locate the client. */ if (shared_network_from_packet6(&reply.shared, packet) != ISC_R_SUCCESS) { log_error("unicast_reject: could not locate client."); return; } /* Initialize the reply. */ packet_reference(&reply.packet, packet, MDL); data_string_copy(&reply.client_id, client_id, MDL); if (start_reply(packet, client_id, server_id, &reply.opt_state, &reply.buf.reply)) { /* Set the UseMulticast status code. */ if (!set_status_code(STATUS_UseMulticast, "Unicast not allowed by server.", reply.opt_state)) { log_error("unicast_reject: Unable to set status code."); } else { /* Set write cursor to just past the reply header. */ reply.cursor = REPLY_OPTIONS_INDEX; reply.cursor += store_options6(((char *)reply.buf.data + reply.cursor), (sizeof(reply.buf) - reply.cursor), reply.opt_state, reply.packet, unicast_reject_opts, NULL); /* Return our reply to the caller. */ reply_ret->len = reply.cursor; reply_ret->buffer = NULL; if (!buffer_allocate(&reply_ret->buffer, reply.cursor, MDL)) { log_fatal("unicast_reject:" "No memory to store Reply."); } memcpy(reply_ret->buffer->data, reply.buf.data, reply.cursor); reply_ret->data = reply_ret->buffer->data; } } /* Cleanup. */ if (reply.shared != NULL) shared_network_dereference(&reply.shared, MDL); if (reply.opt_state != NULL) option_state_dereference(&reply.opt_state, MDL); if (reply.packet != NULL) packet_dereference(&reply.packet, MDL); if (reply.client_id.data != NULL) data_string_forget(&reply.client_id, MDL); } /*! * * \brief Checks if the dhcp6.unicast option has been defined * * Scans the option space for the presence of the dhcp6.unicast option. The * function attempts to map the inbound packet to a shared network first * by an ip address specified via an D6O_IA_XX option and if that fails then * by the packet's source information (e.g. relay link, link, or interace). * Once the packet is mapped to a shared network, the function executes all * statements from the network's group outward into a local option cache. * The option cache is then scanned for the presence of unicast option. If * the packet cannot be mapped to a shared network, the function returns * ISC_FALSE. * \param packet inbound packet from the client * * \return ISC_TRUE if the dhcp6.unicast option is defined, false otherwise. * */ isc_boolean_t is_unicast_option_defined(struct packet *packet) { isc_boolean_t is_defined = ISC_FALSE; struct option_state *opt_state = NULL; struct option_cache *oc = NULL; struct shared_network *shared = NULL; if (!option_state_allocate(&opt_state, MDL)) { log_fatal("is_unicast_option_defined:" "No memory for option state."); } /* We try to map the packet to a network first by an IA_XX value. * If that fails, we try by packet source. */ if (((shared_network_from_requested_addr(&shared, packet) != ISC_R_SUCCESS) && (shared_network_from_packet6(&shared, packet) != ISC_R_SUCCESS)) || (shared == NULL)) { /* @todo what would this really mean? I think wrong network * logic will catch it */ log_error("is_unicast_option_defined:" "cannot attribute packet to a network."); return (ISC_FALSE); } /* Now that we've mapped it to a network, execute statments to that * scope, looking for the unicast option. We don't care about the * value of the option, only whether or not it is defined. */ execute_statements_in_scope(NULL, NULL, NULL, NULL, NULL, opt_state, &global_scope, shared->group, NULL, NULL); oc = lookup_option(&dhcpv6_universe, opt_state, D6O_UNICAST); is_defined = (oc != NULL ? ISC_TRUE : ISC_FALSE); log_debug("is_unicast_option_defined: option found : %d", is_defined); if (shared != NULL) { shared_network_dereference(&shared, MDL); } if (opt_state != NULL) { option_state_dereference(&opt_state, MDL); } return (is_defined); } /*! * * \brief Maps a packet to a shared network based on the requested IP address * * The function attempts to find a subnet that matches the first requested IP * address contained within the given packet. Note that it looks first for * D6O_IA_NAs, then D6O_IA_PDs and lastly D6O_IA_TAs. If a matching network is * found, a reference to it is returned in the parameter, shared. * * \param shared shared_network pointer which will receive the matching network * \param packet inbound packet from the client * * \return ISC_R_SUCCESS if the packet can be mapped to a shared_network. * */ static isc_result_t shared_network_from_requested_addr (struct shared_network **shared, struct packet* packet) { struct iaddr iaddr; struct subnet* subnet = NULL; isc_result_t status = ISC_R_FAILURE; /* Try to match first IA_ address or prefix we find to a subnet. In * theory all IA_ values in a given request are supposed to be in the * same subnet so we only need to try one right? */ if ((get_first_ia_addr_val(packet, D6O_IA_NA, &iaddr) != ISC_R_SUCCESS) && (get_first_ia_addr_val(packet, D6O_IA_PD, &iaddr) != ISC_R_SUCCESS) && (get_first_ia_addr_val(packet, D6O_IA_TA, &iaddr) != ISC_R_SUCCESS)) { /* we found nothing to match against */ log_debug("share_network_from_request_addr: nothing to match"); return (ISC_R_FAILURE); } if (!find_subnet(&subnet, iaddr, MDL)) { log_debug("shared_network_from_requested_addr:" "No subnet found for addr %s.", piaddr(iaddr)); } else { status = shared_network_reference(shared, subnet->shared_network, MDL); subnet_dereference(&subnet, MDL); log_debug("shared_network_from_requested_addr:" " found shared network %s for address %s.", ((*shared)->name ? (*shared)->name : "unnamed"), piaddr(iaddr)); return (status); } return (ISC_R_FAILURE); } /*! * * \brief Retrieves the first IP address from a given packet of a given type * * Search a packet for options of a given type (D6O_IA_AN, D6O_IA_PD, or * D6O_IA_TA) for the first non-blank IA_XX value and return its IP address * component. * * \param packet packet received from the client * \param addr_type the address option type (D6O_IA_NA , D6O_IA_PD, or * D6O_IP_TA) to look for within the packet. * \param iaddr pointer to the iaddr structure which will receive the extracted * address. * * \return ISC_R_SUCCESS if an address was succesfully extracted, ISC_R_FALURE * otherwise. * */ static isc_result_t get_first_ia_addr_val (struct packet* packet, int addr_type, struct iaddr* iaddr) { struct option_cache *ia; struct option_cache *oc = NULL; struct data_string cli_enc_opt_data; struct option_state *cli_enc_opt_state; int addr_opt_offset; int addr_opt; int addr_opt_data_len; int ip_addr_offset; isc_result_t status = ISC_R_FAILURE; memset(iaddr, 0, sizeof(struct iaddr)); /* Set up address type specifics */ switch (addr_type) { case D6O_IA_NA: addr_opt_offset = IA_NA_OFFSET; addr_opt = D6O_IAADDR; addr_opt_data_len = 24; ip_addr_offset = 0; break; case D6O_IA_TA: addr_opt_offset = IA_TA_OFFSET; addr_opt = D6O_IAADDR; addr_opt_data_len = 24; ip_addr_offset = 0; break; case D6O_IA_PD: addr_opt_offset = IA_PD_OFFSET; addr_opt = D6O_IAPREFIX; addr_opt_data_len = 25; ip_addr_offset = 9; break; default: /* shouldn't be here */ log_error ("get_first_ia_addr_val: invalid opt type %d", addr_type); return (ISC_R_FAILURE); } /* Find the first, non-blank IA_XX value within an D6O_IA_XX option. */ for (ia = lookup_option(&dhcpv6_universe, packet->options, addr_type); ia != NULL && oc == NULL; ia = ia->next) { if (!get_encapsulated_IA_state(&cli_enc_opt_state, &cli_enc_opt_data, packet, ia, addr_opt_offset)) { log_debug ("get_first_ia_addr_val:" " couldn't unroll enclosing option"); return (ISC_R_FAILURE); } oc = lookup_option(&dhcpv6_universe, cli_enc_opt_state, addr_opt); if (oc == NULL) { /* no address given for this IA, ignore */ option_state_dereference(&cli_enc_opt_state, MDL); data_string_forget(&cli_enc_opt_data, MDL); } } /* If we found a non-blank IA_XX then extract its ip address. */ if (oc != NULL) { struct data_string iaddr_str; memset(&iaddr_str, 0, sizeof(iaddr_str)); if (!evaluate_option_cache(&iaddr_str, packet, NULL, NULL, packet->options, NULL, &global_scope, oc, MDL)) { log_error("get_first_ia_addr_val: " "error evaluating IA_XX option."); } else { if (iaddr_str.len != addr_opt_data_len) { log_error("shared_network_from_requested_addr:" " invalid length %d, expected %d", iaddr_str.len, addr_opt_data_len); } else { iaddr->len = 16; memcpy (iaddr->iabuf, iaddr_str.data + ip_addr_offset, 16); status = ISC_R_SUCCESS; } data_string_forget(&iaddr_str, MDL); } option_state_dereference(&cli_enc_opt_state, MDL); data_string_forget(&cli_enc_opt_data, MDL); } return (status); } /* * \brief Calculates the reply T1/T2 times and stuffs them in outbound buffer * * T1/T2 time selection is kind of weird. We actually use DHCP * (v4) scoped * options, dhcp-renewal-time and dhcp-rebinding-time, as handy existing places * where these can be configured by an administrator. A value of zero tells the * client it may choose its own value. * * When those options are not defined, the values will be set to zero unless * the global option, dhcpv6-set-tee-times is enabled. When this option is * enabled the values are calculated as recommended by RFC 3315, Section 22.4: * * T1 will be set to 0.5 times the shortest preferred lifetime * in the IA_XX option. If the "shortest" preferred lifetime is * 0xFFFFFFFF, T1 will set to 0xFFFFFFFF. * * T2 will be set to 0.8 times the shortest preferred lifetime * in the IA_XX option. If the "shortest" preferred lifetime is * 0xFFFFFFFF, T2 will set to 0xFFFFFFFF. * * Note that dhcpv6-set-tee-times is intended to be transitional and will * likely be removed in 4.4.0, leaving the behavior as getting the values * either from the configured parameters (if you want zeros, define them as * zeros) or by calculating them per the RFC. * * \param reply - pointer to the reply_state structure * \param ia_cursor - offset of the beginning of the IA_XX option within the * reply's outbound data buffer */ static void set_reply_tee_times(struct reply_state* reply, unsigned ia_cursor) { struct option_cache *oc; int set_tee_times; /* Found out if calculated values are enabled. */ oc = lookup_option(&server_universe, reply->opt_state, SV_DHCPV6_SET_TEE_TIMES); set_tee_times = (oc && evaluate_boolean_option_cache(NULL, reply->packet, NULL, NULL, reply->packet->options, reply->opt_state, &global_scope, oc, MDL)); oc = lookup_option(&dhcp_universe, reply->opt_state, DHO_DHCP_RENEWAL_TIME); if (oc != NULL) { /* dhcp-renewal-time is defined, use it */ struct data_string data; memset(&data, 0x00, sizeof(data)); if (!evaluate_option_cache(&data, reply->packet, NULL, NULL, reply->packet->options, reply->opt_state, &global_scope, oc, MDL) || (data.len != 4)) { log_error("Invalid renewal time."); reply->renew = 0; } else { reply->renew = getULong(data.data); } if (data.data != NULL) data_string_forget(&data, MDL); } else if (set_tee_times) { /* Setting them is enabled so T1 is either infinite or * 0.5 * the shortest preferred lifetime in the IA_XX */ if (reply->min_prefer == INFINITE_TIME) reply->renew = INFINITE_TIME; else reply->renew = reply->min_prefer / 2; } else { /* Default is to let the client choose */ reply->renew = 0; } putULong(reply->buf.data + ia_cursor + 8, reply->renew); /* Now T2. */ oc = lookup_option(&dhcp_universe, reply->opt_state, DHO_DHCP_REBINDING_TIME); if (oc != NULL) { /* dhcp-rebinding-time is defined, use it */ struct data_string data; memset(&data, 0x00, sizeof(data)); if (!evaluate_option_cache(&data, reply->packet, NULL, NULL, reply->packet->options, reply->opt_state, &global_scope, oc, MDL) || (data.len != 4)) { log_error("Invalid rebinding time."); reply->rebind = 0; } else { reply->rebind = getULong(data.data); } if (data.data != NULL) data_string_forget(&data, MDL); } else if (set_tee_times) { /* Setting them is enabled so T2 is either infinite or * 0.8 * the shortest preferred lifetime in the reply */ if (reply->min_prefer == INFINITE_TIME) reply->rebind = INFINITE_TIME; else reply->rebind = (reply->min_prefer / 5) * 4; } else { /* Default is to let the client choose */ reply->rebind = 0; } putULong(reply->buf.data + ia_cursor + 12, reply->rebind); } /* * Releases the iasubopts in the pre-existing IA, if they are not in * the same shared-network as the new IA. * * returns 1 if the release was done, 0 otherwise */ int release_on_roam(struct reply_state* reply) { struct ia_xx* old_ia = reply->old_ia; struct iasubopt *lease = NULL; int i; if ((!do_release_on_roam) || old_ia == NULL || old_ia->num_iasubopt <= 0) { return(0); } /* If the old shared-network and new are the same, client hasn't * roamed, nothing to do. We only check the first one because you * cannot have iasubopts on different shared-networks within a * single ia. */ lease = old_ia->iasubopt[0]; if (lease->ipv6_pool->shared_network == reply->shared) { return (0); } /* Old and new are on different shared networks so the client must * roamed. Release the old leases. */ for (i = 0; i < old_ia->num_iasubopt; i++) { lease = old_ia->iasubopt[i]; log_info("Client: %s roamed to new network," " releasing lease: %s%s", print_hex_1(reply->client_id.len, reply->client_id.data, 60), pin6_addr(&lease->addr), iasubopt_plen_str(lease)); release_lease6(lease->ipv6_pool, lease); lease->ia->cltt = cur_time; write_ia(lease->ia); } return (1); } /* * Convenience function which returns a string (static buffer) * containing either a "/" followed by the prefix length or an * empty string depending on the lease type */ const char *iasubopt_plen_str(struct iasubopt *lease) { static char prefix_buf[16]; *prefix_buf = 0; if ((lease->ia) && (lease->ia->ia_type == D6O_IA_PD)) { sprintf(prefix_buf, "/%-d", lease->plen); } return (prefix_buf); } #ifdef NSUPDATE /* * Initiates DDNS updates for static v6 leases if configured to do so. * * The function, which must be called after the IA has been written to the * packet, adds an iasubopt to the IA for static lease. This is done so we * have an iasubopt to pass into ddns_updates(). A reference to the IA is * added to the DDNS control block to ensure it and it's iasubopt remain in * scope until the update is complete. * */ void ddns_update_static6(struct reply_state* reply) { struct iasubopt *iasub = NULL; struct binding_scope *scope = NULL; struct option_cache *oc = NULL; oc = lookup_option(&server_universe, reply->opt_state, SV_DDNS_UPDATES); if ((oc != NULL) && (evaluate_boolean_option_cache(NULL, reply->packet, NULL, NULL, reply->packet->options, reply->opt_state, NULL, oc, MDL) == 0)) { return; } oc = lookup_option(&server_universe, reply->opt_state, SV_UPDATE_STATIC_LEASES); if ((oc == NULL) || (evaluate_boolean_option_cache(NULL, reply->packet, NULL, NULL, reply->packet->options, reply->opt_state, NULL, oc, MDL) == 0)) { return; } if (iasubopt_allocate(&iasub, MDL) != ISC_R_SUCCESS) { log_fatal("No memory for iasubopt."); } if (ia_add_iasubopt(reply->ia, iasub, MDL) != ISC_R_SUCCESS) { log_fatal("Could not add iasubopt."); } ia_reference(&iasub->ia, reply->ia, MDL); memcpy(iasub->addr.s6_addr, reply->fixed.data, 16); iasub->plen = 0; iasub->prefer = MAX_TIME; iasub->valid = MAX_TIME; iasub->static_lease = 1; if (!binding_scope_allocate(&scope, MDL)) { log_fatal("Out of memory for binding scope."); } binding_scope_reference(&iasub->scope, scope, MDL); ddns_updates(reply->packet, NULL, NULL, iasub, NULL, reply->opt_state); } #endif /* NSUPDATE */ #endif /* DHCPv6 */ dhcp-4.4.1/server/failover.c000644 000765 000024 00000567326 13243301226 016217 0ustar00tmarkstaff000000 000000 /* failover.c Failover protocol support code... */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1999-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "cdefs.h" #include "dhcpd.h" #include #if defined (FAILOVER_PROTOCOL) dhcp_failover_state_t *failover_states; static isc_result_t do_a_failover_option (omapi_object_t *, dhcp_failover_link_t *); dhcp_failover_listener_t *failover_listeners; static isc_result_t failover_message_reference (failover_message_t **, failover_message_t *, const char *file, int line); static isc_result_t failover_message_dereference (failover_message_t **, const char *file, int line); static void dhcp_failover_pool_balance(dhcp_failover_state_t *state); static void dhcp_failover_pool_reqbalance(dhcp_failover_state_t *state); static int dhcp_failover_pool_dobalance(dhcp_failover_state_t *state, isc_boolean_t *sendreq); static inline int secondary_not_hoarding(dhcp_failover_state_t *state, struct pool *p); static void scrub_lease(struct lease* lease, const char *file, int line); int check_secs_byte_order = 0; /* enables byte order check of secs field if 1 */ /*! * \brief Performs a "pre-flight" sanity check of failover configuration * * Provides an opportunity to do post-parse pre-startup sanity checking * of failover configuration. This allows checks to be done under test * mode (-T), without requiring full startup for validation. * * Currently, it enforces all failover peers be used in at lease one * pool. This logic was formerly located in dhcp_failover_startup. * * On failure, a fatal error is logged. * */ void dhcp_failover_sanity_check() { dhcp_failover_state_t *state; int fail_count = 0; for (state = failover_states; state; state = state->next) { if (state->pool_count == 0) { log_error ("ERROR: Failover peer, %s, has no referring" " pools. You must refer to each peer in at" " least one pool declaration.", state->name); fail_count++; } if (state->load_balance_max_secs == 0) { log_info ("WARNING: load balancing will be disabled " "for failover peer, %s, " "because its load balance max secs is 0", state->name); } } if (fail_count) { log_fatal ("Failover configuration sanity check failed"); } } void dhcp_failover_startup () { dhcp_failover_state_t *state; isc_result_t status; struct timeval tv; for (state = failover_states; state; state = state -> next) { dhcp_failover_state_transition (state, "startup"); /* In case the peer is already running, immediately try to establish a connection with it. */ status = dhcp_failover_link_initiate ((omapi_object_t *)state); if (status != ISC_R_SUCCESS && status != DHCP_R_INCOMPLETE) { #if defined (DEBUG_FAILOVER_TIMING) log_info ("add_timeout +90 dhcp_failover_reconnect"); #endif tv . tv_sec = cur_time + 90; tv . tv_usec = 0; add_timeout (&tv, dhcp_failover_reconnect, state, (tvref_t) dhcp_failover_state_reference, (tvunref_t) dhcp_failover_state_dereference); log_error ("failover peer %s: %s", state -> name, isc_result_totext (status)); } status = (dhcp_failover_listen ((omapi_object_t *)state)); if (status != ISC_R_SUCCESS) { #if defined (DEBUG_FAILOVER_TIMING) log_info ("add_timeout +90 %s", "dhcp_failover_listener_restart"); #endif tv . tv_sec = cur_time + 90; tv . tv_usec = 0; add_timeout (&tv, dhcp_failover_listener_restart, state, (tvref_t)omapi_object_reference, (tvunref_t)omapi_object_dereference); } } } int dhcp_failover_write_all_states () { dhcp_failover_state_t *state; for (state = failover_states; state; state = state -> next) { if (!write_failover_state (state)) return 0; } return 1; } isc_result_t enter_failover_peer (peer) dhcp_failover_state_t *peer; { dhcp_failover_state_t *dup = (dhcp_failover_state_t *)0; isc_result_t status; status = find_failover_peer (&dup, peer -> name, MDL); if (status == ISC_R_NOTFOUND) { if (failover_states) { dhcp_failover_state_reference (&peer -> next, failover_states, MDL); dhcp_failover_state_dereference (&failover_states, MDL); } dhcp_failover_state_reference (&failover_states, peer, MDL); return ISC_R_SUCCESS; } dhcp_failover_state_dereference (&dup, MDL); if (status == ISC_R_SUCCESS) return ISC_R_EXISTS; return status; } isc_result_t find_failover_peer (peer, name, file, line) dhcp_failover_state_t **peer; const char *name; const char *file; int line; { dhcp_failover_state_t *p; for (p = failover_states; p; p = p -> next) if (!strcmp (name, p -> name)) break; if (p) return dhcp_failover_state_reference (peer, p, file, line); return ISC_R_NOTFOUND; } /* The failover protocol has three objects associated with it. For each failover partner declaration in the dhcpd.conf file, primary or secondary, there is a failover_state object. For any primary or secondary state object that has a connection to its peer, there is also a failover_link object, which has its own input state separate from the failover protocol state for managing the actual bytes coming in off the wire. Finally, there will be one listener object for every distinct port number associated with a secondary failover_state object. Normally all secondary failover_state objects are expected to listen on the same port number, so there need be only one listener object, but if different port numbers are specified for each failover object, there could be as many as one listener object for each secondary failover_state object. */ /* This, then, is the implementation of the failover link object. */ isc_result_t dhcp_failover_link_initiate (omapi_object_t *h) { isc_result_t status; dhcp_failover_link_t *obj; dhcp_failover_state_t *state; omapi_object_t *o; int i; struct data_string ds; omapi_addr_list_t *addrs = (omapi_addr_list_t *)0; omapi_addr_t local_addr; /* Find the failover state in the object chain. */ for (o = h; o -> outer; o = o -> outer) ; for (; o; o = o -> inner) { if (o -> type == dhcp_type_failover_state) break; } if (!o) return DHCP_R_INVALIDARG; state = (dhcp_failover_state_t *)o; obj = (dhcp_failover_link_t *)0; status = dhcp_failover_link_allocate (&obj, MDL); if (status != ISC_R_SUCCESS) return status; option_cache_reference (&obj -> peer_address, state -> partner.address, MDL); obj -> peer_port = state -> partner.port; dhcp_failover_state_reference (&obj -> state_object, state, MDL); memset (&ds, 0, sizeof ds); if (!evaluate_option_cache (&ds, (struct packet *)0, (struct lease *)0, (struct client_state *)0, (struct option_state *)0, (struct option_state *)0, &global_scope, obj -> peer_address, MDL)) { dhcp_failover_link_dereference (&obj, MDL); return ISC_R_UNEXPECTED; } /* Make an omapi address list out of a buffer containing zero or more IPv4 addresses. */ status = omapi_addr_list_new (&addrs, ds.len / 4, MDL); if (status != ISC_R_SUCCESS) { dhcp_failover_link_dereference (&obj, MDL); return status; } for (i = 0; i < addrs -> count; i++) { addrs -> addresses [i].addrtype = AF_INET; addrs -> addresses [i].addrlen = sizeof (struct in_addr); memcpy (addrs -> addresses [i].address, &ds.data [i * 4], sizeof (struct in_addr)); addrs -> addresses [i].port = obj -> peer_port; } data_string_forget (&ds, MDL); /* Now figure out the local address that we're supposed to use. */ if (!state -> me.address || !evaluate_option_cache (&ds, (struct packet *)0, (struct lease *)0, (struct client_state *)0, (struct option_state *)0, (struct option_state *)0, &global_scope, state -> me.address, MDL)) { memset (&local_addr, 0, sizeof local_addr); local_addr.addrtype = AF_INET; local_addr.addrlen = sizeof (struct in_addr); if (!state -> server_identifier.len) { log_fatal ("failover peer %s: no local address.", state -> name); } } else { if (ds.len != sizeof (struct in_addr)) { log_error("failover peer %s: 'address' parameter " "fails to resolve to an IPv4 address", state->name); data_string_forget (&ds, MDL); dhcp_failover_link_dereference (&obj, MDL); omapi_addr_list_dereference (&addrs, MDL); return DHCP_R_INVALIDARG; } local_addr.addrtype = AF_INET; local_addr.addrlen = ds.len; memcpy (local_addr.address, ds.data, ds.len); if (!state -> server_identifier.len) data_string_copy (&state -> server_identifier, &ds, MDL); data_string_forget (&ds, MDL); local_addr.port = 0; /* Let the O.S. choose. */ } status = omapi_connect_list ((omapi_object_t *)obj, addrs, &local_addr); omapi_addr_list_dereference (&addrs, MDL); dhcp_failover_link_dereference (&obj, MDL); return status; } isc_result_t dhcp_failover_link_signal (omapi_object_t *h, const char *name, va_list ap) { isc_result_t status; dhcp_failover_link_t *link; omapi_object_t *c; dhcp_failover_state_t *s, *state = (dhcp_failover_state_t *)0; char *sname; int slen; struct timeval tv; if (h -> type != dhcp_type_failover_link) { /* XXX shouldn't happen. Put an assert here? */ return ISC_R_UNEXPECTED; } link = (dhcp_failover_link_t *)h; if (!strcmp (name, "connect")) { if (link -> state_object -> i_am == primary) { status = dhcp_failover_send_connect (h); if (status != ISC_R_SUCCESS) { log_info ("dhcp_failover_send_connect: %s", isc_result_totext (status)); omapi_disconnect (h -> outer, 1); } } else status = ISC_R_SUCCESS; /* Allow the peer fifteen seconds to send us a startup message. */ #if defined (DEBUG_FAILOVER_TIMING) log_info ("add_timeout +15 %s", "dhcp_failover_link_startup_timeout"); #endif tv . tv_sec = cur_time + 15; tv . tv_usec = 0; add_timeout (&tv, dhcp_failover_link_startup_timeout, link, (tvref_t)dhcp_failover_link_reference, (tvunref_t)dhcp_failover_link_dereference); return status; } if (!strcmp (name, "disconnect")) { if (link -> state_object) { dhcp_failover_state_reference (&state, link -> state_object, MDL); link -> state = dhcp_flink_disconnected; /* Make the transition. */ if (state->link_to_peer == link) dhcp_failover_state_transition(link->state_object, name); /* Schedule an attempt to reconnect. */ #if defined (DEBUG_FAILOVER_TIMING) log_info("add_timeout +5 dhcp_failover_reconnect"); #endif tv.tv_sec = cur_time + 5; tv.tv_usec = cur_tv.tv_usec; add_timeout(&tv, dhcp_failover_reconnect, state, (tvref_t)dhcp_failover_state_reference, (tvunref_t)dhcp_failover_state_dereference); dhcp_failover_state_dereference (&state, MDL); } return ISC_R_SUCCESS; } if (!strcmp (name, "status")) { if (link -> state_object) { isc_result_t status; status = va_arg(ap, isc_result_t); if ((status == ISC_R_HOSTUNREACH) || (status == ISC_R_TIMEDOUT)) { dhcp_failover_state_reference (&state, link -> state_object, MDL); link -> state = dhcp_flink_disconnected; /* Make the transition. */ dhcp_failover_state_transition (link -> state_object, "disconnect"); /* Start trying to reconnect. */ #if defined (DEBUG_FAILOVER_TIMING) log_info ("add_timeout +5 %s", "dhcp_failover_reconnect"); #endif tv . tv_sec = cur_time + 5; tv . tv_usec = 0; add_timeout (&tv, dhcp_failover_reconnect, state, (tvref_t)dhcp_failover_state_reference, (tvunref_t)dhcp_failover_state_dereference); } dhcp_failover_state_dereference (&state, MDL); } return ISC_R_SUCCESS; } /* Not a signal we recognize? */ if (strcmp (name, "ready")) { if (h -> inner && h -> inner -> type -> signal_handler) return (*(h -> inner -> type -> signal_handler)) (h -> inner, name, ap); return ISC_R_NOTFOUND; } if (!h -> outer || h -> outer -> type != omapi_type_connection) return DHCP_R_INVALIDARG; c = h -> outer; /* We get here because we requested that we be woken up after some number of bytes were read, and that number of bytes has in fact been read. */ switch (link -> state) { case dhcp_flink_start: link -> state = dhcp_flink_message_length_wait; if ((omapi_connection_require (c, 2)) != ISC_R_SUCCESS) break; case dhcp_flink_message_length_wait: next_message: link -> state = dhcp_flink_message_wait; link -> imsg = dmalloc (sizeof (failover_message_t), MDL); if (!link -> imsg) { status = ISC_R_NOMEMORY; dhcp_flink_fail: if (link -> imsg) { failover_message_dereference (&link->imsg, MDL); } link -> state = dhcp_flink_disconnected; log_info ("message length wait: %s", isc_result_totext (status)); omapi_disconnect (c, 1); /* XXX just blow away the protocol state now? XXX or will disconnect blow it away? */ return ISC_R_UNEXPECTED; } memset (link -> imsg, 0, sizeof (failover_message_t)); link -> imsg -> refcnt = 1; /* Get the length: */ omapi_connection_get_uint16 (c, &link -> imsg_len); link -> imsg_count = 0; /* Bytes read. */ /* Ensure the message is of valid length. */ if (link->imsg_len < DHCP_FAILOVER_MIN_MESSAGE_SIZE || link->imsg_len > DHCP_FAILOVER_MAX_MESSAGE_SIZE) { status = ISC_R_UNEXPECTED; goto dhcp_flink_fail; } if ((omapi_connection_require (c, link -> imsg_len - 2U)) != ISC_R_SUCCESS) break; case dhcp_flink_message_wait: /* Read in the message. At this point we have the entire message in the input buffer. For each incoming value ID, set a bit in the bitmask indicating that we've gotten it. Maybe flag an error message if the bit is already set. Once we're done reading, we can check the bitmask to make sure that the required fields for each message have been included. */ link -> imsg_count += 2; /* Count the length as read. */ /* Get message type. */ omapi_connection_copyout (&link -> imsg -> type, c, 1); link -> imsg_count++; /* Get message payload offset. */ omapi_connection_copyout (&link -> imsg_payoff, c, 1); link -> imsg_count++; /* Get message time. */ omapi_connection_get_uint32 (c, &link -> imsg -> time); link -> imsg_count += 4; /* Get transaction ID. */ omapi_connection_get_uint32 (c, &link -> imsg -> xid); link -> imsg_count += 4; #if defined (DEBUG_FAILOVER_MESSAGES) # if !defined(DEBUG_FAILOVER_CONTACT_MESSAGES) if (link->imsg->type == FTM_CONTACT) goto skip_contact; # endif log_info ("link: message %s payoff %d time %ld xid %ld", dhcp_failover_message_name (link -> imsg -> type), link -> imsg_payoff, (unsigned long)link -> imsg -> time, (unsigned long)link -> imsg -> xid); # if !defined(DEBUG_FAILOVER_CONTACT_MESSAGES) skip_contact: # endif #endif /* Skip over any portions of the message header that we don't understand. */ if (link -> imsg_payoff - link -> imsg_count) { omapi_connection_copyout ((unsigned char *)0, c, (link -> imsg_payoff - link -> imsg_count)); link -> imsg_count = link -> imsg_payoff; } /* Now start sucking options off the wire. */ while (link -> imsg_count < link -> imsg_len) { status = do_a_failover_option (c, link); if (status != ISC_R_SUCCESS) goto dhcp_flink_fail; } /* If it's a connect message, try to associate it with a state object. */ /* XXX this should be authenticated! */ if (link -> imsg -> type == FTM_CONNECT) { const char *errmsg; int reason; if (!(link->imsg->options_present & FTB_RELATIONSHIP_NAME)) { errmsg = "missing relationship-name"; reason = FTR_INVALID_PARTNER; goto badconnect; } /* See if we can find a failover_state object that matches this connection. This message should only be received by a secondary from a primary. */ for (s = failover_states; s; s = s -> next) { if (dhcp_failover_state_match_by_name(s, &link->imsg->relationship_name)) state = s; } /* If we can't find a failover protocol state for this remote host, drop the connection */ if (!state) { errmsg = "unknown failover relationship name"; reason = FTR_INVALID_PARTNER; badconnect: /* XXX Send a refusal message first? XXX Look in protocol spec for guidance. */ if (state != NULL) { sname = state->name; slen = strlen(sname); } else if (link->imsg->options_present & FTB_RELATIONSHIP_NAME) { sname = (char *)link->imsg-> relationship_name.data; slen = link->imsg->relationship_name.count; } else { sname = "unknown"; slen = strlen(sname); } log_error("Failover CONNECT from %.*s: %s", slen, sname, errmsg); dhcp_failover_send_connectack ((omapi_object_t *)link, state, reason, errmsg); log_info ("failover: disconnect: %s", errmsg); omapi_disconnect (c, 0); link -> state = dhcp_flink_disconnected; return ISC_R_SUCCESS; } if ((cur_time > link -> imsg -> time && cur_time - link -> imsg -> time > 60) || (cur_time < link -> imsg -> time && link -> imsg -> time - cur_time > 60)) { errmsg = "time offset too large"; reason = FTR_TIMEMISMATCH; goto badconnect; } if (!(link -> imsg -> options_present & FTB_HBA) || link -> imsg -> hba.count != 32) { errmsg = "invalid HBA"; reason = FTR_HBA_CONFLICT; /* XXX */ goto badconnect; } if (state -> hba) dfree (state -> hba, MDL); state -> hba = dmalloc (32, MDL); if (!state -> hba) { errmsg = "no memory"; reason = FTR_MISC_REJECT; goto badconnect; } memcpy (state -> hba, link -> imsg -> hba.data, 32); if (!link -> state_object) dhcp_failover_state_reference (&link -> state_object, state, MDL); if (!link -> peer_address) option_cache_reference (&link -> peer_address, state -> partner.address, MDL); } /* If we don't have a state object at this point, it's some kind of bogus situation, so just drop the connection. */ if (!link -> state_object) { log_info ("failover: connect: no matching state."); omapi_disconnect (c, 1); link -> state = dhcp_flink_disconnected; return DHCP_R_INVALIDARG; } /* Once we have the entire message, and we've validated it as best we can here, pass it to the parent. */ omapi_signal ((omapi_object_t *)link -> state_object, "message", link); link -> state = dhcp_flink_message_length_wait; if (link -> imsg) failover_message_dereference (&link -> imsg, MDL); /* XXX This is dangerous because we could get into a tight XXX loop reading input without servicing any other stuff. XXX There needs to be a way to relinquish control but XXX get it back immediately if there's no other work to XXX do. */ if ((omapi_connection_require (c, 2)) == ISC_R_SUCCESS) goto next_message; break; default: log_fatal("Impossible case at %s:%d.", MDL); break; } return ISC_R_SUCCESS; } static isc_result_t do_a_failover_option (c, link) omapi_object_t *c; dhcp_failover_link_t *link; { u_int16_t option_code; u_int16_t option_len; unsigned char *op; unsigned op_size; unsigned op_count; int i; if (link -> imsg_count + 2 > link -> imsg_len) { log_error ("FAILOVER: message overflow at option code."); return DHCP_R_PROTOCOLERROR; } if (link->imsg->type > FTM_MAX) { log_error ("FAILOVER: invalid message type: %d", link->imsg->type); return DHCP_R_PROTOCOLERROR; } /* Get option code. */ omapi_connection_get_uint16 (c, &option_code); link -> imsg_count += 2; if (link -> imsg_count + 2 > link -> imsg_len) { log_error ("FAILOVER: message overflow at length."); return DHCP_R_PROTOCOLERROR; } /* Get option length. */ omapi_connection_get_uint16 (c, &option_len); link -> imsg_count += 2; if (link -> imsg_count + option_len > link -> imsg_len) { log_error ("FAILOVER: message overflow at data."); return DHCP_R_PROTOCOLERROR; } /* If it's an unknown code, skip over it. */ if ((option_code > FTO_MAX) || (ft_options[option_code].type == FT_UNDEF)) { #if defined (DEBUG_FAILOVER_MESSAGES) log_debug (" option code %d (%s) len %d (not recognized)", option_code, dhcp_failover_option_name (option_code), option_len); #endif omapi_connection_copyout ((unsigned char *)0, c, option_len); link -> imsg_count += option_len; return ISC_R_SUCCESS; } /* If it's the digest, do it now. */ if (ft_options [option_code].type == FT_DIGEST) { link -> imsg_count += option_len; if (link -> imsg_count != link -> imsg_len) { log_error ("FAILOVER: digest not at end of message"); return DHCP_R_PROTOCOLERROR; } #if defined (DEBUG_FAILOVER_MESSAGES) log_debug (" option %s len %d", ft_options [option_code].name, option_len); #endif /* For now, just dump it. */ omapi_connection_copyout ((unsigned char *)0, c, option_len); return ISC_R_SUCCESS; } /* Only accept an option once. */ if (link -> imsg -> options_present & ft_options [option_code].bit) { log_error ("FAILOVER: duplicate option %s", ft_options [option_code].name); return DHCP_R_PROTOCOLERROR; } /* Make sure the option is appropriate for this type of message. Really, any option is generally allowed for any message, and the cases where this is not true are too complicated to represent in this way - what this code is doing is to just avoid saving the value of an option we don't have any way to use, which allows us to make the failover_message structure smaller. */ if (ft_options [option_code].bit && !(fto_allowed [link -> imsg -> type] & ft_options [option_code].bit)) { omapi_connection_copyout ((unsigned char *)0, c, option_len); link -> imsg_count += option_len; return ISC_R_SUCCESS; } /* Figure out how many elements, how big they are, and where to store them. */ if (ft_options [option_code].num_present) { /* If this option takes a fixed number of elements, we expect the space for them to be preallocated, and we can just read the data in. */ op = ((unsigned char *)link -> imsg) + ft_options [option_code].offset; op_size = ft_sizes [ft_options [option_code].type]; op_count = ft_options [option_code].num_present; if (option_len != op_size * op_count) { log_error ("FAILOVER: option size (%d:%d), option %s", option_len, (ft_sizes [ft_options [option_code].type] * ft_options [option_code].num_present), ft_options [option_code].name); return DHCP_R_PROTOCOLERROR; } } else { failover_option_t *fo; /* FT_DDNS* are special - one or two bytes of status followed by the client FQDN. */ /* Note: FT_DDNS* option support appears to be incomplete. ISC-Bugs #36996 has been opened to address this. */ if (ft_options [option_code].type == FT_DDNS || ft_options [option_code].type == FT_DDNS1) { ddns_fqdn_t *ddns = ((ddns_fqdn_t *) (((char *)link -> imsg) + ft_options [option_code].offset)); op_count = (ft_options [option_code].type == FT_DDNS1 ? 1 : 2); omapi_connection_copyout (&ddns -> codes [0], c, op_count); link -> imsg_count += op_count; if (op_count == 1) ddns -> codes [1] = 0; op_size = 1; op_count = option_len - op_count; ddns -> length = op_count; ddns -> data = dmalloc (op_count, MDL); if (!ddns -> data) { log_error ("FAILOVER: no memory getting%s(%d)", " DNS data ", op_count); /* Actually, NO_MEMORY, but if we lose here we have to drop the connection. */ return DHCP_R_PROTOCOLERROR; } omapi_connection_copyout (ddns -> data, c, op_count); goto out; } /* A zero for num_present means that any number of elements can appear, so we have to figure out how many we got from the length of the option, and then fill out a failover_option structure describing the data. */ op_size = ft_sizes [ft_options [option_code].type]; /* Make sure that option data length is a multiple of the size of the data type being sent. */ if (op_size > 1 && option_len % op_size) { log_error ("FAILOVER: option_len %d not %s%d", option_len, "multiple of ", op_size); return DHCP_R_PROTOCOLERROR; } op_count = option_len / op_size; fo = ((failover_option_t *) (((char *)link -> imsg) + ft_options [option_code].offset)); fo -> count = op_count; fo -> data = dmalloc (option_len, MDL); if (!fo -> data) { log_error ("FAILOVER: no memory getting %s (%d)", "option data", op_count); return DHCP_R_PROTOCOLERROR; } op = fo -> data; } /* For single-byte message values and multi-byte values that don't need swapping, just read them in all at once. */ if (op_size == 1 || ft_options [option_code].type == FT_IPADDR) { omapi_connection_copyout ((unsigned char *)op, c, option_len); link -> imsg_count += option_len; /* * As of 3.1.0, many option codes were changed to conform to * draft revision 12 (which alphabetized, then renumbered all * the option codes without preserving the version option code * nor bumping its value). As it turns out, the message codes * for CONNECT and CONNECTACK turn out the same, so it tries * its darndest to connect, and falls short (when TLS_REQUEST * comes up size 2 rather than size 1 as draft revision 12 also * mandates). * * The VENDOR_CLASS code in 3.0.x was 11, which is now the HBA * code. Both work out to be arbitrarily long text-or-byte * strings, so they pass parsing. * * Note that it is possible (or intentional), if highly * improbable, for the HBA bit array to exactly match * isc-V3.0.x. Warning here is not an issue; if it really is * 3.0.x, there will be a protocol error later on. If it isn't * actually 3.0.x, then I guess the lucky user will have to * live with a weird warning. */ if ((option_code == 11) && (option_len > 9) && (strncmp((const char *)op, "isc-V3.0.", 9) == 0)) { log_error("WARNING: failover as of versions 3.1.0 and " "on are not reverse compatible with " "versions 3.0.x."); } goto out; } /* For values that require swapping, read them in one at a time using routines that swap bytes. */ for (i = 0; i < op_count; i++) { switch (ft_options [option_code].type) { case FT_UINT32: omapi_connection_get_uint32 (c, (u_int32_t *)op); op += 4; link -> imsg_count += 4; break; case FT_UINT16: omapi_connection_get_uint16 (c, (u_int16_t *)op); op += 2; link -> imsg_count += 2; break; default: /* Everything else should have been handled already. */ log_error ("FAILOVER: option %s: bad type %d", ft_options [option_code].name, ft_options [option_code].type); return DHCP_R_PROTOCOLERROR; } } out: /* Remember that we got this option. */ link -> imsg -> options_present |= ft_options [option_code].bit; return ISC_R_SUCCESS; } isc_result_t dhcp_failover_link_set_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_typed_data_t *value) { if (h -> type != omapi_type_protocol) return DHCP_R_INVALIDARG; /* Never valid to set these. */ if (!omapi_ds_strcmp (name, "link-port") || !omapi_ds_strcmp (name, "link-name") || !omapi_ds_strcmp (name, "link-state")) return ISC_R_NOPERM; if (h -> inner && h -> inner -> type -> set_value) return (*(h -> inner -> type -> set_value)) (h -> inner, id, name, value); return ISC_R_NOTFOUND; } isc_result_t dhcp_failover_link_get_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_value_t **value) { dhcp_failover_link_t *link; if (h -> type != omapi_type_protocol) return DHCP_R_INVALIDARG; link = (dhcp_failover_link_t *)h; if (!omapi_ds_strcmp (name, "link-port")) { return omapi_make_int_value (value, name, (int)link -> peer_port, MDL); } else if (!omapi_ds_strcmp (name, "link-state")) { if (link -> state >= dhcp_flink_state_max) return omapi_make_string_value (value, name, "invalid link state", MDL); return omapi_make_string_value (value, name, dhcp_flink_state_names [link -> state], MDL); } if (h -> inner && h -> inner -> type -> get_value) return (*(h -> inner -> type -> get_value)) (h -> inner, id, name, value); return ISC_R_NOTFOUND; } isc_result_t dhcp_failover_link_destroy (omapi_object_t *h, const char *file, int line) { dhcp_failover_link_t *link; if (h -> type != dhcp_type_failover_link) return DHCP_R_INVALIDARG; link = (dhcp_failover_link_t *)h; if (link -> peer_address) option_cache_dereference (&link -> peer_address, file, line); if (link -> imsg) failover_message_dereference (&link -> imsg, file, line); if (link -> state_object) dhcp_failover_state_dereference (&link -> state_object, file, line); return ISC_R_SUCCESS; } /* Write all the published values associated with the object through the specified connection. */ isc_result_t dhcp_failover_link_stuff_values (omapi_object_t *c, omapi_object_t *id, omapi_object_t *l) { dhcp_failover_link_t *link; isc_result_t status; if (l -> type != dhcp_type_failover_link) return DHCP_R_INVALIDARG; link = (dhcp_failover_link_t *)l; status = omapi_connection_put_name (c, "link-port"); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_uint32 (c, sizeof (int)); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_uint32 (c, link -> peer_port); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_name (c, "link-state"); if (status != ISC_R_SUCCESS) return status; if (link -> state >= dhcp_flink_state_max) status = omapi_connection_put_string (c, "invalid link state"); else status = (omapi_connection_put_string (c, dhcp_flink_state_names [link -> state])); if (status != ISC_R_SUCCESS) return status; if (link -> inner && link -> inner -> type -> stuff_values) return (*(link -> inner -> type -> stuff_values)) (c, id, link -> inner); return ISC_R_SUCCESS; } /* Set up a listener for the omapi protocol. The handle stored points to a listener object, not a protocol object. */ isc_result_t dhcp_failover_listen (omapi_object_t *h) { isc_result_t status; dhcp_failover_listener_t *obj, *l; omapi_value_t *value = (omapi_value_t *)0; omapi_addr_t local_addr; unsigned long port; status = omapi_get_value_str (h, (omapi_object_t *)0, "local-port", &value); if (status != ISC_R_SUCCESS) return status; if (!value -> value) { omapi_value_dereference (&value, MDL); return DHCP_R_INVALIDARG; } status = omapi_get_int_value (&port, value -> value); omapi_value_dereference (&value, MDL); if (status != ISC_R_SUCCESS) return status; local_addr.port = port; status = omapi_get_value_str (h, (omapi_object_t *)0, "local-address", &value); if (status != ISC_R_SUCCESS) return status; if (!value -> value) { nogood: omapi_value_dereference (&value, MDL); return DHCP_R_INVALIDARG; } if (value -> value -> type != omapi_datatype_data || value -> value -> u.buffer.len != sizeof (struct in_addr)) goto nogood; memcpy (local_addr.address, value -> value -> u.buffer.value, value -> value -> u.buffer.len); local_addr.addrlen = value -> value -> u.buffer.len; local_addr.addrtype = AF_INET; omapi_value_dereference (&value, MDL); /* Are we already listening on this port and address? */ for (l = failover_listeners; l; l = l -> next) { if (l -> address.port == local_addr.port && l -> address.addrtype == local_addr.addrtype && l -> address.addrlen == local_addr.addrlen && !memcmp (l -> address.address, local_addr.address, local_addr.addrlen)) break; } /* Already listening. */ if (l) return ISC_R_SUCCESS; obj = (dhcp_failover_listener_t *)0; status = dhcp_failover_listener_allocate (&obj, MDL); if (status != ISC_R_SUCCESS) return status; obj -> address = local_addr; status = omapi_listen_addr ((omapi_object_t *)obj, &obj -> address, 1); if (status != ISC_R_SUCCESS) return status; status = omapi_object_reference (&h -> outer, (omapi_object_t *)obj, MDL); if (status != ISC_R_SUCCESS) { dhcp_failover_listener_dereference (&obj, MDL); return status; } status = omapi_object_reference (&obj -> inner, h, MDL); if (status != ISC_R_SUCCESS) { dhcp_failover_listener_dereference (&obj, MDL); return status; } /* Put this listener on the list. */ if (failover_listeners) { dhcp_failover_listener_reference (&obj -> next, failover_listeners, MDL); dhcp_failover_listener_dereference (&failover_listeners, MDL); } dhcp_failover_listener_reference (&failover_listeners, obj, MDL); return dhcp_failover_listener_dereference (&obj, MDL); } /* Signal handler for protocol listener - if we get a connect signal, create a new protocol connection, otherwise pass the signal down. */ isc_result_t dhcp_failover_listener_signal (omapi_object_t *o, const char *name, va_list ap) { isc_result_t status; omapi_connection_object_t *c; dhcp_failover_link_t *obj; dhcp_failover_listener_t *p; dhcp_failover_state_t *s, *state = (dhcp_failover_state_t *)0; if (!o || o -> type != dhcp_type_failover_listener) return DHCP_R_INVALIDARG; p = (dhcp_failover_listener_t *)o; /* Not a signal we recognize? */ if (strcmp (name, "connect")) { if (p -> inner && p -> inner -> type -> signal_handler) return (*(p -> inner -> type -> signal_handler)) (p -> inner, name, ap); return ISC_R_NOTFOUND; } c = va_arg (ap, omapi_connection_object_t *); if (!c || c -> type != omapi_type_connection) return DHCP_R_INVALIDARG; /* See if we can find a failover_state object that matches this connection. */ for (s = failover_states; s; s = s -> next) { if (dhcp_failover_state_match (s, (u_int8_t *)&c -> remote_addr.sin_addr, sizeof c -> remote_addr.sin_addr)) { state = s; break; } } if (!state) { log_info ("failover: listener: no matching state"); omapi_disconnect ((omapi_object_t *)c, 1); return(ISC_R_NOTFOUND); } obj = (dhcp_failover_link_t *)0; status = dhcp_failover_link_allocate (&obj, MDL); if (status != ISC_R_SUCCESS) return status; obj -> peer_port = ntohs (c -> remote_addr.sin_port); status = omapi_object_reference (&obj -> outer, (omapi_object_t *)c, MDL); if (status != ISC_R_SUCCESS) { lose: dhcp_failover_link_dereference (&obj, MDL); log_info ("failover: listener: picayune failure."); omapi_disconnect ((omapi_object_t *)c, 1); return status; } status = omapi_object_reference (&c -> inner, (omapi_object_t *)obj, MDL); if (status != ISC_R_SUCCESS) goto lose; status = dhcp_failover_state_reference (&obj -> state_object, state, MDL); if (status != ISC_R_SUCCESS) goto lose; omapi_signal_in ((omapi_object_t *)obj, "connect"); return dhcp_failover_link_dereference (&obj, MDL); } isc_result_t dhcp_failover_listener_set_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_typed_data_t *value) { if (h -> type != dhcp_type_failover_listener) return DHCP_R_INVALIDARG; if (h -> inner && h -> inner -> type -> set_value) return (*(h -> inner -> type -> set_value)) (h -> inner, id, name, value); return ISC_R_NOTFOUND; } isc_result_t dhcp_failover_listener_get_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_value_t **value) { if (h -> type != dhcp_type_failover_listener) return DHCP_R_INVALIDARG; if (h -> inner && h -> inner -> type -> get_value) return (*(h -> inner -> type -> get_value)) (h -> inner, id, name, value); return ISC_R_NOTFOUND; } isc_result_t dhcp_failover_listener_destroy (omapi_object_t *h, const char *file, int line) { dhcp_failover_listener_t *l; if (h -> type != dhcp_type_failover_listener) return DHCP_R_INVALIDARG; l = (dhcp_failover_listener_t *)h; if (l -> next) dhcp_failover_listener_dereference (&l -> next, file, line); return ISC_R_SUCCESS; } /* Write all the published values associated with the object through the specified connection. */ isc_result_t dhcp_failover_listener_stuff (omapi_object_t *c, omapi_object_t *id, omapi_object_t *p) { if (p -> type != dhcp_type_failover_listener) return DHCP_R_INVALIDARG; if (p -> inner && p -> inner -> type -> stuff_values) return (*(p -> inner -> type -> stuff_values)) (c, id, p -> inner); return ISC_R_SUCCESS; } /* Set up master state machine for the failover protocol. */ isc_result_t dhcp_failover_register (omapi_object_t *h) { isc_result_t status; dhcp_failover_state_t *obj; unsigned long port; omapi_value_t *value = (omapi_value_t *)0; status = omapi_get_value_str (h, (omapi_object_t *)0, "local-port", &value); if (status != ISC_R_SUCCESS) return status; if (!value -> value) { omapi_value_dereference (&value, MDL); return DHCP_R_INVALIDARG; } status = omapi_get_int_value (&port, value -> value); omapi_value_dereference (&value, MDL); if (status != ISC_R_SUCCESS) return status; obj = (dhcp_failover_state_t *)0; dhcp_failover_state_allocate (&obj, MDL); obj -> me.port = port; status = omapi_listen ((omapi_object_t *)obj, port, 1); if (status != ISC_R_SUCCESS) { dhcp_failover_state_dereference (&obj, MDL); return status; } status = omapi_object_reference (&h -> outer, (omapi_object_t *)obj, MDL); if (status != ISC_R_SUCCESS) { dhcp_failover_state_dereference (&obj, MDL); return status; } status = omapi_object_reference (&obj -> inner, h, MDL); dhcp_failover_state_dereference (&obj, MDL); return status; } /* Signal handler for protocol state machine. */ isc_result_t dhcp_failover_state_signal (omapi_object_t *o, const char *name, va_list ap) { isc_result_t status; dhcp_failover_state_t *state; dhcp_failover_link_t *link; struct timeval tv; if (!o || o -> type != dhcp_type_failover_state) return DHCP_R_INVALIDARG; state = (dhcp_failover_state_t *)o; /* Not a signal we recognize? */ if (strcmp (name, "disconnect") && strcmp (name, "message")) { if (state -> inner && state -> inner -> type -> signal_handler) return (*(state -> inner -> type -> signal_handler)) (state -> inner, name, ap); return ISC_R_NOTFOUND; } /* Handle connect signals by seeing what state we're in and potentially doing a state transition. */ if (!strcmp (name, "disconnect")) { link = va_arg (ap, dhcp_failover_link_t *); dhcp_failover_link_dereference (&state -> link_to_peer, MDL); dhcp_failover_state_transition (state, "disconnect"); if (state -> i_am == primary) { #if defined (DEBUG_FAILOVER_TIMING) log_info ("add_timeout +90 %s", "dhcp_failover_reconnect"); #endif tv . tv_sec = cur_time + 90; tv . tv_usec = 0; add_timeout (&tv, dhcp_failover_reconnect, state, (tvref_t)dhcp_failover_state_reference, (tvunref_t) dhcp_failover_state_dereference); } } else if (!strcmp (name, "message")) { link = va_arg (ap, dhcp_failover_link_t *); if (link -> imsg -> type == FTM_CONNECT) { /* If we already have a link to the peer, it must be dead, so drop it. XXX Is this the right thing to do? XXX Probably not - what if both peers start at XXX the same time? */ if (state -> link_to_peer) { dhcp_failover_send_connectack ((omapi_object_t *)link, state, FTR_DUP_CONNECTION, "already connected"); omapi_disconnect (link -> outer, 1); return ISC_R_SUCCESS; } if (!(link -> imsg -> options_present & FTB_MCLT)) { dhcp_failover_send_connectack ((omapi_object_t *)link, state, FTR_INVALID_MCLT, "no MCLT provided"); omapi_disconnect (link -> outer, 1); return ISC_R_SUCCESS; } dhcp_failover_link_reference (&state -> link_to_peer, link, MDL); status = (dhcp_failover_send_connectack ((omapi_object_t *)link, state, 0, 0)); if (status != ISC_R_SUCCESS) { dhcp_failover_link_dereference (&state -> link_to_peer, MDL); log_info ("dhcp_failover_send_connectack: %s", isc_result_totext (status)); omapi_disconnect (link -> outer, 1); return ISC_R_SUCCESS; } if (link -> imsg -> options_present & FTB_MAX_UNACKED) state -> partner.max_flying_updates = link -> imsg -> max_unacked; if (link -> imsg -> options_present & FTB_RECEIVE_TIMER) state -> partner.max_response_delay = link -> imsg -> receive_timer; state -> mclt = link -> imsg -> mclt; dhcp_failover_send_state (state); cancel_timeout (dhcp_failover_link_startup_timeout, link); } else if (link -> imsg -> type == FTM_CONNECTACK) { const char *errmsg; char errbuf[1024]; int reason; cancel_timeout (dhcp_failover_link_startup_timeout, link); if (!(link->imsg->options_present & FTB_RELATIONSHIP_NAME)) { errmsg = "missing relationship-name"; reason = FTR_INVALID_PARTNER; goto badconnectack; } if (link->imsg->options_present & FTB_REJECT_REASON) { /* XXX: add message option to text output. */ log_error ("Failover CONNECT to %s rejected: %s", state ? state->name : "unknown", (dhcp_failover_reject_reason_print (link -> imsg -> reject_reason))); /* XXX print message from peer if peer sent message. */ omapi_disconnect (link -> outer, 1); return ISC_R_SUCCESS; } if (!dhcp_failover_state_match_by_name(state, &link->imsg->relationship_name)) { /* XXX: Overflow results in log truncation, safe. */ snprintf(errbuf, sizeof(errbuf), "remote failover " "relationship name %.*s does not match", (int)link->imsg->relationship_name.count, link->imsg->relationship_name.data); errmsg = errbuf; reason = FTR_INVALID_PARTNER; badconnectack: log_error("Failover CONNECTACK from %s: %s", state->name, errmsg); dhcp_failover_send_disconnect ((omapi_object_t *)link, reason, errmsg); omapi_disconnect (link -> outer, 0); return ISC_R_SUCCESS; } if (state -> link_to_peer) { errmsg = "already connected"; reason = FTR_DUP_CONNECTION; goto badconnectack; } if ((cur_time > link -> imsg -> time && cur_time - link -> imsg -> time > 60) || (cur_time < link -> imsg -> time && link -> imsg -> time - cur_time > 60)) { errmsg = "time offset too large"; reason = FTR_TIMEMISMATCH; goto badconnectack; } dhcp_failover_link_reference (&state -> link_to_peer, link, MDL); #if 0 /* XXX This is probably the right thing to do, but XXX for release three, to make the smallest possible XXX change, we are doing this when the peer state XXX changes instead. */ if (state -> me.state == startup) dhcp_failover_set_state (state, state -> saved_state); else #endif dhcp_failover_send_state (state); if (link -> imsg -> options_present & FTB_MAX_UNACKED) state -> partner.max_flying_updates = link -> imsg -> max_unacked; if (link -> imsg -> options_present & FTB_RECEIVE_TIMER) state -> partner.max_response_delay = link -> imsg -> receive_timer; #if defined (DEBUG_FAILOVER_CONTACT_TIMING) log_info ("add_timeout +%d %s", (int)state -> partner.max_response_delay / 3, "dhcp_failover_send_contact"); #endif tv . tv_sec = cur_time + (int)state -> partner.max_response_delay / 3; tv . tv_usec = 0; add_timeout (&tv, dhcp_failover_send_contact, state, (tvref_t)dhcp_failover_state_reference, (tvunref_t)dhcp_failover_state_dereference); #if defined (DEBUG_FAILOVER_CONTACT_TIMING) log_info ("add_timeout +%d %s", (int)state -> me.max_response_delay, "dhcp_failover_timeout"); #endif tv . tv_sec = cur_time + (int)state -> me.max_response_delay; tv . tv_usec = 0; add_timeout (&tv, dhcp_failover_timeout, state, (tvref_t)dhcp_failover_state_reference, (tvunref_t)dhcp_failover_state_dereference); } else if (link -> imsg -> type == FTM_DISCONNECT) { if (link -> imsg -> reject_reason) { log_error ("Failover DISCONNECT from %s: %s", state ? state->name : "unknown", (dhcp_failover_reject_reason_print (link -> imsg -> reject_reason))); } omapi_disconnect (link -> outer, 1); } else if (link -> imsg -> type == FTM_BNDUPD) { dhcp_failover_process_bind_update (state, link -> imsg); } else if (link -> imsg -> type == FTM_BNDACK) { dhcp_failover_process_bind_ack (state, link -> imsg); } else if (link -> imsg -> type == FTM_UPDREQ) { dhcp_failover_process_update_request (state, link -> imsg); } else if (link -> imsg -> type == FTM_UPDREQALL) { dhcp_failover_process_update_request_all (state, link -> imsg); } else if (link -> imsg -> type == FTM_UPDDONE) { dhcp_failover_process_update_done (state, link -> imsg); } else if (link -> imsg -> type == FTM_POOLREQ) { dhcp_failover_pool_reqbalance(state); } else if (link -> imsg -> type == FTM_POOLRESP) { log_info ("pool response: %ld leases", (unsigned long) link -> imsg -> addresses_transferred); } else if (link -> imsg -> type == FTM_STATE) { dhcp_failover_peer_state_changed (state, link -> imsg); } /* Add a timeout so that if the partner doesn't send another message for the maximum transmit idle time plus a grace of one second, we close the connection. */ if (state -> link_to_peer && state -> link_to_peer == link && state -> link_to_peer -> state != dhcp_flink_disconnected) { #if defined (DEBUG_FAILOVER_CONTACT_TIMING) log_info ("add_timeout +%d %s", (int)state -> me.max_response_delay, "dhcp_failover_timeout"); #endif tv . tv_sec = cur_time + (int)state -> me.max_response_delay; tv . tv_usec = 0; add_timeout (&tv, dhcp_failover_timeout, state, (tvref_t)dhcp_failover_state_reference, (tvunref_t)dhcp_failover_state_dereference); } } /* Handle all the events we care about... */ return ISC_R_SUCCESS; } isc_result_t dhcp_failover_state_transition (dhcp_failover_state_t *state, const char *name) { isc_result_t status; /* XXX Check these state transitions against the spec! */ if (!strcmp (name, "disconnect")) { if (state -> link_to_peer) { log_info ("peer %s: disconnected", state -> name); if (state -> link_to_peer -> state_object) dhcp_failover_state_dereference (&state -> link_to_peer -> state_object, MDL); dhcp_failover_link_dereference (&state -> link_to_peer, MDL); } cancel_timeout (dhcp_failover_send_contact, state); cancel_timeout (dhcp_failover_timeout, state); cancel_timeout (dhcp_failover_startup_timeout, state); switch (state -> me.state == startup ? state -> saved_state : state -> me.state) { /* In these situations, we remain in the current * state, or if in startup enter those states. */ case conflict_done: /* As the peer may not have received or may have * lost track of updates we sent previously we * rescind them, causing us to retransmit them * on an update request. */ dhcp_failover_rescind_updates(state); /* fall through */ case communications_interrupted: case partner_down: case paused: case recover: case recover_done: case recover_wait: case resolution_interrupted: case shut_down: /* Already in the right state? */ if (state -> me.state == startup) return (dhcp_failover_set_state (state, state -> saved_state)); return ISC_R_SUCCESS; case potential_conflict: return dhcp_failover_set_state (state, resolution_interrupted); case normal: return dhcp_failover_set_state (state, communications_interrupted); case unknown_state: return dhcp_failover_set_state (state, resolution_interrupted); default: log_fatal("Impossible case at %s:%d.", MDL); break; /* can't happen. */ } } else if (!strcmp (name, "connect")) { switch (state -> me.state) { case communications_interrupted: status = dhcp_failover_set_state (state, normal); dhcp_failover_send_updates (state); return status; case resolution_interrupted: return dhcp_failover_set_state (state, potential_conflict); case conflict_done: case partner_down: case potential_conflict: case normal: case recover: case shut_down: case paused: case unknown_state: case recover_done: case startup: case recover_wait: return dhcp_failover_send_state (state); default: log_fatal("Impossible case at %s:%d.", MDL); break; } } else if (!strcmp (name, "startup")) { dhcp_failover_set_state (state, startup); return ISC_R_SUCCESS; } else if (!strcmp (name, "connect-timeout")) { switch (state -> me.state) { case communications_interrupted: case partner_down: case resolution_interrupted: case paused: case startup: case shut_down: case conflict_done: return ISC_R_SUCCESS; case normal: case recover: case recover_wait: case recover_done: case unknown_state: return dhcp_failover_set_state (state, communications_interrupted); case potential_conflict: return dhcp_failover_set_state (state, resolution_interrupted); default: log_fatal("Impossible case at %s:%d.", MDL); break; } } return DHCP_R_INVALIDARG; } isc_result_t dhcp_failover_set_service_state (dhcp_failover_state_t *state) { switch (state -> me.state) { case unknown_state: state -> service_state = not_responding; state -> nrr = " (my state unknown)"; break; case partner_down: state -> service_state = service_partner_down; state -> nrr = ""; break; case normal: state -> service_state = cooperating; state -> nrr = ""; break; case communications_interrupted: state -> service_state = not_cooperating; state -> nrr = ""; break; case resolution_interrupted: case potential_conflict: case conflict_done: state -> service_state = not_responding; state -> nrr = " (resolving conflicts)"; break; case recover: state -> service_state = not_responding; state -> nrr = " (recovering)"; break; case shut_down: state -> service_state = not_responding; state -> nrr = " (shut down)"; break; case paused: state -> service_state = not_responding; state -> nrr = " (paused)"; break; case recover_wait: state -> service_state = not_responding; state -> nrr = " (recover wait)"; break; case recover_done: state -> service_state = not_responding; state -> nrr = " (recover done)"; break; case startup: state -> service_state = service_startup; state -> nrr = " (startup)"; break; default: log_fatal("Impossible case at %s:%d.\n", MDL); break; } /* Some peer states can require us not to respond, even if our state doesn't. */ /* XXX hm. I suspect this isn't true anymore. */ if (state -> service_state != not_responding) { switch (state -> partner.state) { case partner_down: state -> service_state = not_responding; state -> nrr = " (peer demands: recovering)"; break; case potential_conflict: case conflict_done: case resolution_interrupted: state -> service_state = not_responding; state -> nrr = " (peer demands: resolving conflicts)"; break; /* Other peer states don't affect our behaviour. */ default: break; } } return ISC_R_SUCCESS; } /*! * \brief Return any leases on the ack queue back to the update queue * * Re-schedule any pending updates by moving them from the ack queue * (update sent awaiting response) back to the update queue (need to * send an update for this lease). This will result in a retransmission * of the update. * * \param state is the state block for the failover connection we are * updating. */ void dhcp_failover_rescind_updates (dhcp_failover_state_t *state) { struct lease *lp; if (state->ack_queue_tail == NULL) return; /* Zap the flags. */ for (lp = state->ack_queue_head; lp; lp = lp->next_pending) lp->flags = ((lp->flags & ~ON_ACK_QUEUE) | ON_UPDATE_QUEUE); /* Now hook the ack queue to the beginning of the update queue. */ if (state->update_queue_head) { lease_reference(&state->ack_queue_tail->next_pending, state->update_queue_head, MDL); lease_dereference(&state->update_queue_head, MDL); } lease_reference(&state->update_queue_head, state->ack_queue_head, MDL); if (!state->update_queue_tail) { #if defined (POINTER_DEBUG) if (state->ack_queue_tail->next_pending) { log_error("next pending on ack queue tail."); abort(); } #endif lease_reference(&state->update_queue_tail, state->ack_queue_tail, MDL); } lease_dereference(&state->ack_queue_tail, MDL); lease_dereference(&state->ack_queue_head, MDL); state->cur_unacked_updates = 0; } isc_result_t dhcp_failover_set_state (dhcp_failover_state_t *state, enum failover_state new_state) { enum failover_state saved_state; TIME saved_stos; struct pool *p; struct shared_network *s; struct lease *l; struct timeval tv; /* If we're in certain states where we're sending updates, and the peer * state changes, we need to re-schedule any pending updates just to * be on the safe side. This results in retransmission. */ switch (state -> me.state) { case normal: case potential_conflict: case partner_down: /* Move the ack queue to the update queue */ dhcp_failover_rescind_updates(state); /* We will re-queue a timeout later, if applicable. */ cancel_timeout (dhcp_failover_keepalive, state); break; default: break; } /* Tentatively make the transition. */ saved_state = state -> me.state; saved_stos = state -> me.stos; /* Keep the old stos if we're going into recover_wait or if we're coming into or out of startup. */ if (new_state != recover_wait && new_state != startup && saved_state != startup) state -> me.stos = cur_time; /* If we're in shutdown, peer is in partner_down, and we're moving to recover, we can skip waiting for MCLT to expire. This happens when a server is moved administratively into shutdown prior to actually shutting down. Of course, if there are any updates pending we can't actually do this. */ if (new_state == recover && saved_state == shut_down && state -> partner.state == partner_down && !state -> update_queue_head && !state -> ack_queue_head) state -> me.stos = cur_time - state -> mclt; state -> me.state = new_state; if (new_state == startup && saved_state != startup) state -> saved_state = saved_state; /* If we can't record the new state, we can't make a state transition. */ if (!write_failover_state (state) || !commit_leases ()) { log_error ("Unable to record current failover state for %s", state -> name); state -> me.state = saved_state; state -> me.stos = saved_stos; return ISC_R_IOERROR; } log_info ("failover peer %s: I move from %s to %s", state -> name, dhcp_failover_state_name_print (saved_state), dhcp_failover_state_name_print (state -> me.state)); /* If both servers are now normal log it */ if ((state->me.state == normal) && (state->partner.state == normal)) log_info("failover peer %s: Both servers normal", state->name); /* If we were in startup and we just left it, cancel the timeout. */ if (new_state != startup && saved_state == startup) cancel_timeout (dhcp_failover_startup_timeout, state); /* * If the state changes for any reason, cancel 'delayed auto state * changes' (currently there is just the one). */ cancel_timeout(dhcp_failover_auto_partner_down, state); /* Set our service state. */ dhcp_failover_set_service_state (state); /* Tell the peer about it. */ if (state -> link_to_peer) dhcp_failover_send_state (state); switch (new_state) { case communications_interrupted: /* * There is an optional feature to automatically enter partner * down after a timer expires, upon entering comms-interrupted. * This feature is generally not safe except in specific * circumstances. * * A zero value (also the default) disables it. */ if (state->auto_partner_down == 0) break; #if defined (DEBUG_FAILOVER_TIMING) log_info("add_timeout +%lu dhcp_failover_auto_partner_down", (unsigned long)state->auto_partner_down); #endif tv.tv_sec = cur_time + state->auto_partner_down; tv.tv_usec = 0; add_timeout(&tv, dhcp_failover_auto_partner_down, state, (tvref_t)omapi_object_reference, (tvunref_t)omapi_object_dereference); break; case normal: /* Upon entering normal state, the server is expected to retransmit * all pending binding updates. This is a good opportunity to * rebalance the pool (potentially making new pending updates), * which also schedules the next pool rebalance. */ dhcp_failover_pool_balance(state); dhcp_failover_generate_update_queue(state, 0); if (state->update_queue_tail != NULL) { dhcp_failover_send_updates(state); log_info("Sending updates to %s.", state->name); } break; case potential_conflict: if ((state->i_am == primary) || ((state->i_am == secondary) && (state->partner.state == conflict_done))) dhcp_failover_send_update_request (state); break; case startup: #if defined (DEBUG_FAILOVER_TIMING) log_info ("add_timeout +15 %s", "dhcp_failover_startup_timeout"); #endif tv . tv_sec = cur_time + 15; tv . tv_usec = 0; add_timeout (&tv, dhcp_failover_startup_timeout, state, (tvref_t)omapi_object_reference, (tvunref_t) omapi_object_dereference); break; /* If we come back in recover_wait and there's still waiting to do, set a timeout. */ case recover_wait: if (state -> me.stos + state -> mclt > cur_time) { #if defined (DEBUG_FAILOVER_TIMING) log_info ("add_timeout +%d %s", (int)(cur_time - state -> me.stos + state -> mclt), "dhcp_failover_startup_timeout"); #endif tv . tv_sec = (int)(state -> me.stos + state -> mclt); tv . tv_usec = 0; add_timeout (&tv, dhcp_failover_recover_done, state, (tvref_t)omapi_object_reference, (tvunref_t) omapi_object_dereference); } else dhcp_failover_recover_done (state); break; case recover: /* XXX: We're supposed to calculate if updreq or updreqall is * needed. In theory, we should only have to updreqall if we * are positive we lost our stable storage. */ if (state -> link_to_peer) dhcp_failover_send_update_request_all (state); break; case partner_down: /* For every expired lease, set a timeout for it to become free. */ for (s = shared_networks; s; s = s->next) { for (p = s->pools; p; p = p->next) { #if defined (BINARY_LEASES) long int tiebreaker = 0; #endif if (p->failover_peer == state) { for (l = LEASE_GET_FIRST(p->expired); l != NULL; l = LEASE_GET_NEXT(p->expired, l)) { l->tsfp = state->me.stos + state->mclt; l->sort_time = (l->tsfp > l->ends) ? l->tsfp : l->ends; #if defined (BINARY_LEASES) /* If necessary fix up the tiebreaker so the leases * maintain proper sort order. */ l->sort_tiebreaker = tiebreaker; if (tiebreaker != LONG_MAX) tiebreaker++; #endif } l = LEASE_GET_FIRST(p->expired); if (l && (l->sort_time < p->next_event_time)) { p->next_event_time = l->sort_time; #if defined (DEBUG_FAILOVER_TIMING) log_info ("add_timeout +%d %s", (int)(cur_time - p->next_event_time), "pool_timer"); #endif tv.tv_sec = p->next_event_time; tv.tv_usec = 0; add_timeout(&tv, pool_timer, p, (tvref_t)pool_reference, (tvunref_t)pool_dereference); } } } } break; default: break; } return ISC_R_SUCCESS; } isc_result_t dhcp_failover_peer_state_changed (dhcp_failover_state_t *state, failover_message_t *msg) { enum failover_state previous_state = state -> partner.state; enum failover_state new_state; int startupp; new_state = msg -> server_state; startupp = (msg -> server_flags & FTF_SERVER_STARTUP) ? 1 : 0; if (state -> partner.state == new_state && state -> me.state) { switch (state -> me.state) { case startup: /* * If we have a peer state we must be connected. * If so we should move to potential_conflict * instead of resolution_interrupted, otherwise * back to whereever we were before we stopped. */ if (state->saved_state == resolution_interrupted) dhcp_failover_set_state(state, potential_conflict); else dhcp_failover_set_state(state, state->saved_state); return ISC_R_SUCCESS; case unknown_state: case normal: case potential_conflict: case recover_done: case shut_down: case paused: case recover_wait: return ISC_R_SUCCESS; /* If we get a peer state change when we're disconnected, we always process it. */ case partner_down: case communications_interrupted: case resolution_interrupted: case recover: case conflict_done: break; default: log_fatal("Impossible case at %s:%d.", MDL); break; } } state -> partner.state = new_state; state -> partner.stos = cur_time; log_info ("failover peer %s: peer moves from %s to %s", state -> name, dhcp_failover_state_name_print (previous_state), dhcp_failover_state_name_print (state -> partner.state)); /* If both servers are now normal log it */ if ((state->me.state == normal) && (state->partner.state == normal)) log_info("failover peer %s: Both servers normal", state->name); if (!write_failover_state (state) || !commit_leases ()) { /* This is bad, but it's not fatal. Of course, if we can't write to the lease database, we're not going to get much done anyway. */ log_error ("Unable to record current failover state for %s", state -> name); } /* Quickly validate the new state as being one of the 13 known * states. */ switch (new_state) { case unknown_state: case startup: case normal: case communications_interrupted: case partner_down: case potential_conflict: case recover: case paused: case shut_down: case recover_done: case resolution_interrupted: case conflict_done: case recover_wait: break; default: log_error("failover peer %s: Invalid state: %d", state->name, new_state); dhcp_failover_set_state(state, shut_down); return ISC_R_SUCCESS; } /* Do any state transitions that are required as a result of the peer's state transition. */ switch (state -> me.state == startup ? state -> saved_state : state -> me.state) { case normal: switch (new_state) { case normal: dhcp_failover_state_pool_check (state); break; case partner_down: if (state -> me.state == startup) dhcp_failover_set_state (state, recover); else dhcp_failover_set_state (state, potential_conflict); break; case potential_conflict: case resolution_interrupted: case conflict_done: /* None of these transitions should ever occur. */ log_error("Peer %s: Invalid state transition %s " "to %s.", state->name, dhcp_failover_state_name_print(previous_state), dhcp_failover_state_name_print(new_state)); dhcp_failover_set_state (state, shut_down); break; case recover: case shut_down: dhcp_failover_set_state (state, partner_down); break; case paused: dhcp_failover_set_state (state, communications_interrupted); break; default: /* recover_wait, recover_done, unknown_state, startup, * communications_interrupted */ break; } break; case recover: switch (new_state) { case recover: log_info ("failover peer %s: requesting %s", state -> name, "full update from peer"); /* Don't send updreqall if we're really in the startup state, because that will result in two being sent. */ if (state -> me.state == recover) dhcp_failover_send_update_request_all (state); break; case potential_conflict: case resolution_interrupted: case conflict_done: case normal: dhcp_failover_set_state (state, potential_conflict); break; case partner_down: case communications_interrupted: /* We're supposed to send an update request at this point. */ /* XXX we don't currently have code here to do any XXX clever detection of when we should send an XXX UPDREQALL message rather than an UPDREQ XXX message. What to do, what to do? */ /* Currently when we enter recover state, no matter * the reason, we send an UPDREQALL. So, it makes * the most sense to stick to that until something * better is done. * Furthermore, we only want to send the update * request if we are not in startup state. */ if (state -> me.state == recover) dhcp_failover_send_update_request_all (state); break; case shut_down: /* XXX We're not explicitly told what to do in this XXX case, but this transition is consistent with XXX what is elsewhere in the draft. */ dhcp_failover_set_state (state, partner_down); break; /* We can't really do anything in this case. */ default: /* paused, recover_done, recover_wait, unknown_state, * startup. */ break; } break; case potential_conflict: switch (new_state) { case normal: /* This is an illegal transition. */ log_error("Peer %s moves to normal during conflict " "resolution - panic, shutting down.", state->name); dhcp_failover_set_state(state, shut_down); break; case conflict_done: if (previous_state == potential_conflict) dhcp_failover_send_update_request (state); else log_error("Peer %s: Unexpected move to " "conflict-done.", state->name); break; case recover_done: case recover_wait: case potential_conflict: case partner_down: case communications_interrupted: case resolution_interrupted: case paused: break; case recover: dhcp_failover_set_state (state, recover); break; case shut_down: dhcp_failover_set_state (state, partner_down); break; default: /* unknown_state, startup */ break; } break; case conflict_done: switch (new_state) { case normal: case shut_down: dhcp_failover_set_state(state, new_state); break; case potential_conflict: case resolution_interrupted: /* * This can happen when the connection is lost and * recovered after the primary has moved to * conflict-done but the secondary is still in * potential-conflict. In that case, we have to * remain in conflict-done. */ break; default: log_fatal("Peer %s: Invalid attempt to move from %s " "to %s while local state is conflict-done.", state->name, dhcp_failover_state_name_print(previous_state), dhcp_failover_state_name_print(new_state)); } break; case partner_down: /* Take no action if other server is starting up. */ if (startupp) break; switch (new_state) { /* This is where we should be. */ case recover: case recover_wait: break; case recover_done: dhcp_failover_set_state (state, normal); break; case normal: case potential_conflict: case partner_down: case communications_interrupted: case resolution_interrupted: case conflict_done: dhcp_failover_set_state (state, potential_conflict); break; default: /* shut_down, paused, unknown_state, startup */ break; } break; case communications_interrupted: switch (new_state) { case paused: /* Stick with the status quo. */ break; /* If we're in communications-interrupted and an amnesic peer connects, go to the partner_down state immediately. */ case recover: dhcp_failover_set_state (state, partner_down); break; case normal: case communications_interrupted: case recover_done: case recover_wait: /* XXX so we don't need to do this specially in XXX the CONNECT and CONNECTACK handlers. */ dhcp_failover_send_updates (state); dhcp_failover_set_state (state, normal); break; case potential_conflict: case partner_down: case resolution_interrupted: case conflict_done: dhcp_failover_set_state (state, potential_conflict); break; case shut_down: dhcp_failover_set_state (state, partner_down); break; default: /* unknown_state, startup */ break; } break; case resolution_interrupted: switch (new_state) { case normal: case recover: case potential_conflict: case partner_down: case communications_interrupted: case resolution_interrupted: case conflict_done: case recover_done: case recover_wait: dhcp_failover_set_state (state, potential_conflict); break; case shut_down: dhcp_failover_set_state (state, partner_down); break; default: /* paused, unknown_state, startup */ break; } break; /* Make no transitions while in recover_wait...just wait. */ case recover_wait: break; case recover_done: switch (new_state) { case recover_done: log_error("Both servers have entered recover-done!"); /* Fall through and tranistion to normal anyway */ case normal: dhcp_failover_set_state (state, normal); break; case shut_down: dhcp_failover_set_state (state, partner_down); break; default: /* potential_conflict, partner_down, * communications_interrupted, resolution_interrupted, * paused, recover, recover_wait, unknown_state, * startup. */ break; } break; /* We are essentially dead in the water when we're in either shut_down or paused states, and do not do any automatic state transitions. */ case shut_down: case paused: break; /* XXX: Shouldn't this be a fatal condition? */ case unknown_state: break; default: log_fatal("Impossible condition at %s:%d.", MDL); break; } /* If we didn't make a transition out of startup as a result of the peer's state change, do it now as a result of the fact that we got a state change from the peer. */ if (state -> me.state == startup && state -> saved_state != startup) dhcp_failover_set_state (state, state -> saved_state); /* For now, just set the service state based on the peer's state if necessary. */ dhcp_failover_set_service_state (state); return ISC_R_SUCCESS; } /* * Balance operation manual entry; startup, entrance to normal state. No * sense sending a POOLREQ at this stage; the peer is likely about to schedule * their own rebalance event upon entering normal themselves. */ static void dhcp_failover_pool_balance(dhcp_failover_state_t *state) { /* Cancel pending event. */ cancel_timeout(dhcp_failover_pool_rebalance, state); state->sched_balance = 0; dhcp_failover_pool_dobalance(state, NULL); } /* * Balance operation entry from timer event. Once per timer interval is * the only time we want to emit POOLREQs (asserting an interrupt in our * peer). */ void dhcp_failover_pool_rebalance(void *failover_state) { dhcp_failover_state_t *state; isc_boolean_t sendreq = ISC_FALSE; state = (dhcp_failover_state_t *)failover_state; /* Clear scheduled event indicator. */ state->sched_balance = 0; if (dhcp_failover_pool_dobalance(state, &sendreq)) dhcp_failover_send_updates(state); if (sendreq) dhcp_failover_send_poolreq(state); } /* * Balance operation entry from POOLREQ protocol message. Do not permit a * POOLREQ to send back a POOLREQ. Ping pong. */ static void dhcp_failover_pool_reqbalance(dhcp_failover_state_t *state) { int queued; /* Cancel pending event. */ cancel_timeout(dhcp_failover_pool_rebalance, state); state->sched_balance = 0; queued = dhcp_failover_pool_dobalance(state, NULL); dhcp_failover_send_poolresp(state, queued); if (queued) dhcp_failover_send_updates(state); else log_info("peer %s: Got POOLREQ, answering negatively! " "Peer may be out of leases or database inconsistent.", state->name); } /* * Do the meat of the work common to all forms of pool rebalance. If the * caller deems it appropriate to transmit POOLREQ messages, it can use the * sendreq pointer to pass in the address of a FALSE value which this function * will conditionally turn TRUE if a POOLREQ is determined to be necessary. * A NULL value may be passed, in which case no action is taken. */ static int dhcp_failover_pool_dobalance(dhcp_failover_state_t *state, isc_boolean_t *sendreq) { int lts, total, thresh, hold, panic, pass; int leases_queued = 0; struct lease *lp = NULL; struct lease *next = NULL; struct lease *ltemp = NULL; struct shared_network *s; struct pool *p; binding_state_t peer_lease_state; /* binding_state_t my_lease_state; */ /* XXX Why is this my_lease_state never used? */ LEASE_STRUCT_PTR lq; int (*log_func)(const char *, ...); const char *result, *reqlog; if (state -> me.state != normal) return 0; state->last_balance = cur_time; for (s = shared_networks ; s ; s = s->next) { for (p = s->pools ; p ; p = p->next) { if (p->failover_peer != state) continue; /* Right now we're giving the peer half of the free leases. If we have more leases than the peer (i.e., more than half), then the number of leases we have, less the number of leases the peer has, will be how many more leases we have than the peer has. So if we send half that number to the peer, we should be even. */ if (p->failover_peer->i_am == primary) { lts = (p->free_leases - p->backup_leases) / 2; peer_lease_state = FTS_BACKUP; /* my_lease_state = FTS_FREE; */ lq = &p->free; } else { lts = (p->backup_leases - p->free_leases) / 2; peer_lease_state = FTS_FREE; /* my_lease_state = FTS_BACKUP; */ lq = &p->backup; } total = p->backup_leases + p->free_leases; thresh = ((total * state->max_lease_misbalance) + 50) / 100; hold = ((total * state->max_lease_ownership) + 50) / 100; /* * If we need leases (so lts is negative) more than negative * double the thresh%, panic and send poolreq to hopefully wake * up the peer (but more likely the db is inconsistent). But, * if this comes out zero, switch to -1 so that the POOLREQ is * sent on lts == -2 rather than right away at -1. * * Note that we do not subtract -1 from panic all the time * because thresh% and hold% may come out to the same number, * and that is correct operation...where thresh% and hold% are * both -1, we want to send poolreq when lts reaches -3. So, * "-3 < -2", lts < panic. */ panic = thresh * -2; if (panic == 0) panic = -1; if ((sendreq != NULL) && (lts < panic)) { reqlog = " (requesting peer rebalance!)"; *sendreq = ISC_TRUE; } else reqlog = ""; log_info("balancing pool %lx %s total %d free %d " "backup %d lts %d max-own (+/-)%d%s", (unsigned long)p, (p->shared_network ? p->shared_network->name : ""), p->lease_count, p->free_leases, p->backup_leases, lts, hold, reqlog); /* In the first pass, try to allocate leases to the * peer which it would normally be responsible for (if * the lease has a hardware address or client-identifier, * and the load-balance-algorithm chooses the peer to * answer that address), up to a hold% excess in the peer's * favor. In the second pass, just send the oldest (first * on the list) leases up to a hold% excess in our favor. * * This could make for additional pool rebalance * events, but preserving MAC possession should be * worth it. */ pass = 0; lease_reference(&lp, LEASE_GET_FIRSTP(lq), MDL); while (lp) { if (next) lease_dereference(&next, MDL); ltemp = LEASE_GET_NEXTP(lq, lp); if (ltemp != NULL) lease_reference(&next, ltemp, MDL); /* * Stop if the pool is 'balanced enough.' * * The pool is balanced enough if: * * 1) We're on the first run through and the peer has * its fair share of leases already (lts reaches * -hold). * 2) We're on the second run through, we are shifting * never-used leases, and there is a perfectly even * balance (lts reaches zero). * 3) Second run through, we are shifting previously * used leases, and the local system has its fair * share but no more (lts reaches hold). * * Note that this is implemented below in 3,2,1 order. */ if (pass) { if (lp->ends) { if (lts <= hold) break; } else { if (lts <= 0) break; } } else if (lts <= -hold) break; if (pass || peer_wants_lease(lp)) { --lts; ++leases_queued; lp->next_binding_state = peer_lease_state; lp->tstp = cur_time; lp->starts = cur_time; scrub_lease(lp, MDL); if (!supersede_lease(lp, NULL, 0, 1, 0, 0) || !write_lease(lp)) log_error("can't commit lease %s on " "giveaway", piaddr(lp->ip_addr)); } lease_dereference(&lp, MDL); if (next) lease_reference(&lp, next, MDL); else if (!pass) { pass = 1; lease_reference(&lp, LEASE_GET_FIRSTP(lq), MDL); } } if (next) lease_dereference(&next, MDL); if (lp) lease_dereference(&lp, MDL); if (lts > thresh) { result = "IMBALANCED"; log_func = log_error; } else { result = "balanced"; log_func = log_info; } log_func("%s pool %lx %s total %d free %d backup %d " "lts %d max-misbal %d", result, (unsigned long)p, (p->shared_network ? p->shared_network->name : ""), p->lease_count, p->free_leases, p->backup_leases, lts, thresh); /* Recalculate next rebalance event timer. */ dhcp_failover_pool_check(p); } } if (leases_queued) commit_leases(); return leases_queued; } /* dhcp_failover_pool_check: Called whenever FREE or BACKUP leases change * states, on both servers. Check the scheduled time to rebalance the pool * and lower it if applicable. */ void dhcp_failover_pool_check(struct pool *pool) { dhcp_failover_state_t *peer; TIME est1, est2; struct timeval tv; struct lease *ltemp; peer = pool->failover_peer; if(!peer || peer->me.state != normal) return; /* Estimate the time left until lease exhaustion. * The first lease on the backup or free lists is also the oldest * lease. It is reasonable to guess that it will take at least * as much time for a pool to run out of leases, as the present * age of the oldest lease (seconds since it expired). * * Note that this isn't so sane of an assumption if the oldest * lease is a virgin (ends = 0), we wind up sending this against * the max_balance bounds check. */ ltemp = LEASE_GET_FIRST(pool->free); if(ltemp && ltemp->ends < cur_time) est1 = cur_time - ltemp->ends; else est1 = 0; ltemp = LEASE_GET_FIRST(pool->backup); if(ltemp && ltemp->ends < cur_time) est2 = cur_time - ltemp->ends; else est2 = 0; /* We don't want to schedule rebalance for when we think we'll run * out of leases, we want to schedule the rebalance for when we think * the disparity will be 'large enough' to warrant action. */ est1 = ((est1 * peer->max_lease_misbalance) + 50) / 100; est2 = ((est2 * peer->max_lease_misbalance) + 50) / 100; /* Guess when the local system will begin issuing POOLREQ panic * attacks because "max_lease_misbalance*2" has been exceeded. */ if(peer->i_am == primary) est1 *= 2; else est2 *= 2; /* Select the smallest time. */ if(est1 > est2) est1 = est2; /* Bounded by the maximum configured value. */ if(est1 > peer->max_balance) est1 = peer->max_balance; /* Project this time into the future. */ est1 += cur_time; /* Do not move the time down under the minimum. */ est2 = peer->last_balance + peer->min_balance; if(peer->last_balance && (est1 < est2)) est1 = est2; /* Introduce a random delay. */ est1 += random() % 5; /* Do not move the time forward, or reset to the same time. */ if(peer->sched_balance) { if (est1 >= peer->sched_balance) return; /* We are about to schedule the time down, cancel the * current timeout. */ cancel_timeout(dhcp_failover_pool_rebalance, peer); } /* The time is different, and lower, use it. */ peer->sched_balance = est1; #if defined(DEBUG_FAILOVER_TIMING) log_info("add_timeout +%d dhcp_failover_pool_rebalance", (int)(est1 - cur_time)); #endif tv.tv_sec = est1; tv.tv_usec = 0; add_timeout(&tv, dhcp_failover_pool_rebalance, peer, (tvref_t)dhcp_failover_state_reference, (tvunref_t)dhcp_failover_state_dereference); } int dhcp_failover_state_pool_check (dhcp_failover_state_t *state) { struct shared_network *s; struct pool *p; for (s = shared_networks; s; s = s -> next) { for (p = s -> pools; p; p = p -> next) { if (p -> failover_peer != state) continue; dhcp_failover_pool_check (p); } } return 0; } isc_result_t dhcp_failover_send_updates (dhcp_failover_state_t *state) { struct lease *lp = (struct lease *)0; isc_result_t status; /* Can't update peer if we're not talking to it! */ if (!state -> link_to_peer) return ISC_R_SUCCESS; /* If there are acks pending, transmit them prior to potentially * sending new updates for the same lease. */ if (state->toack_queue_head != NULL) dhcp_failover_send_acks(state); while ((state -> partner.max_flying_updates > state -> cur_unacked_updates) && state -> update_queue_head) { /* Grab the head of the update queue. */ lease_reference (&lp, state -> update_queue_head, MDL); /* Send the update to the peer. */ status = dhcp_failover_send_bind_update (state, lp); if (status != ISC_R_SUCCESS) { lease_dereference (&lp, MDL); return status; } lp -> flags &= ~ON_UPDATE_QUEUE; /* Take it off the head of the update queue and put the next item in the update queue at the head. */ lease_dereference (&state -> update_queue_head, MDL); if (lp -> next_pending) { lease_reference (&state -> update_queue_head, lp -> next_pending, MDL); lease_dereference (&lp -> next_pending, MDL); } else { lease_dereference (&state -> update_queue_tail, MDL); } if (state -> ack_queue_head) { lease_reference (&state -> ack_queue_tail -> next_pending, lp, MDL); lease_dereference (&state -> ack_queue_tail, MDL); } else { lease_reference (&state -> ack_queue_head, lp, MDL); } #if defined (POINTER_DEBUG) if (lp -> next_pending) { log_error ("ack_queue_tail: lp -> next_pending"); abort (); } #endif lease_reference (&state -> ack_queue_tail, lp, MDL); lp -> flags |= ON_ACK_QUEUE; lease_dereference (&lp, MDL); /* Count the object as an unacked update. */ state -> cur_unacked_updates++; } return ISC_R_SUCCESS; } /* Queue an update for a lease. Always returns 1 at this point - it's not an error for this to be called on a lease for which there's no failover peer. */ int dhcp_failover_queue_update (struct lease *lease, int immediate) { dhcp_failover_state_t *state; if (!lease -> pool || !lease -> pool -> failover_peer) return 1; /* If it's already on the update queue, leave it there. */ if (lease -> flags & ON_UPDATE_QUEUE) return 1; /* Get the failover state structure for this lease. */ state = lease -> pool -> failover_peer; /* If it's on the ack queue, take it off. */ if (lease -> flags & ON_ACK_QUEUE) dhcp_failover_ack_queue_remove (state, lease); if (state -> update_queue_head) { lease_reference (&state -> update_queue_tail -> next_pending, lease, MDL); lease_dereference (&state -> update_queue_tail, MDL); } else { lease_reference (&state -> update_queue_head, lease, MDL); } #if defined (POINTER_DEBUG) if (lease -> next_pending) { log_error ("next pending on update queue lease."); #if defined (DEBUG_RC_HISTORY) dump_rc_history (lease); #endif abort (); } #endif lease_reference (&state -> update_queue_tail, lease, MDL); lease -> flags |= ON_UPDATE_QUEUE; if (immediate) dhcp_failover_send_updates (state); return 1; } int dhcp_failover_send_acks (dhcp_failover_state_t *state) { failover_message_t *msg = (failover_message_t *)0; /* Must commit all leases prior to acking them. */ if (!commit_leases ()) return 0; while (state -> toack_queue_head) { failover_message_reference (&msg, state -> toack_queue_head, MDL); failover_message_dereference (&state -> toack_queue_head, MDL); if (msg -> next) { failover_message_reference (&state -> toack_queue_head, msg -> next, MDL); } dhcp_failover_send_bind_ack (state, msg, 0, (const char *)0); failover_message_dereference (&msg, MDL); } if (state -> toack_queue_tail) failover_message_dereference (&state -> toack_queue_tail, MDL); state -> pending_acks = 0; return 1; } void dhcp_failover_toack_queue_timeout (void *vs) { dhcp_failover_state_t *state = vs; #if defined (DEBUG_FAILOVER_TIMING) log_info ("dhcp_failover_toack_queue_timeout"); #endif dhcp_failover_send_acks (state); } /* Queue an ack for a message. There is currently no way to queue a negative ack -- these need to be sent directly. */ int dhcp_failover_queue_ack (dhcp_failover_state_t *state, failover_message_t *msg) { struct timeval tv; if (state -> toack_queue_head) { failover_message_reference (&state -> toack_queue_tail -> next, msg, MDL); failover_message_dereference (&state -> toack_queue_tail, MDL); } else { failover_message_reference (&state -> toack_queue_head, msg, MDL); } failover_message_reference (&state -> toack_queue_tail, msg, MDL); state -> pending_acks++; /* Flush the toack queue whenever we exceed half the number of allowed unacked updates. */ if (state -> pending_acks >= state -> partner.max_flying_updates / 2) { dhcp_failover_send_acks (state); } /* Schedule a timeout to flush the ack queue. */ if (state -> pending_acks > 0) { #if defined (DEBUG_FAILOVER_TIMING) log_info ("add_timeout +2 %s", "dhcp_failover_toack_queue_timeout"); #endif tv . tv_sec = cur_time + 2; tv . tv_usec = 0; add_timeout (&tv, dhcp_failover_toack_queue_timeout, state, (tvref_t)dhcp_failover_state_reference, (tvunref_t)dhcp_failover_state_dereference); } return 1; } void dhcp_failover_ack_queue_remove (dhcp_failover_state_t *state, struct lease *lease) { struct lease *lp; if (!(lease -> flags & ON_ACK_QUEUE)) return; if (state -> ack_queue_head == lease) { lease_dereference (&state -> ack_queue_head, MDL); if (lease -> next_pending) { lease_reference (&state -> ack_queue_head, lease -> next_pending, MDL); lease_dereference (&lease -> next_pending, MDL); } else { lease_dereference (&state -> ack_queue_tail, MDL); } } else { for (lp = state -> ack_queue_head; lp && lp -> next_pending != lease; lp = lp -> next_pending) ; if (!lp) return; lease_dereference (&lp -> next_pending, MDL); if (lease -> next_pending) { lease_reference (&lp -> next_pending, lease -> next_pending, MDL); lease_dereference (&lease -> next_pending, MDL); } else { lease_dereference (&state -> ack_queue_tail, MDL); if (lp -> next_pending) { log_error ("state -> ack_queue_tail"); abort (); } lease_reference (&state -> ack_queue_tail, lp, MDL); } } lease -> flags &= ~ON_ACK_QUEUE; /* Multiple acks on one XID is an error and may cause badness. */ lease->last_xid = 0; /* XXX: this violates draft-failover. We can't send another * update just because we forgot about an old one that hasn't * been acked yet. */ state -> cur_unacked_updates--; /* * When updating leases as a result of an ack, we defer the commit * for performance reasons. When there are no more acks pending, * do a commit. */ if (state -> cur_unacked_updates == 0) { commit_leases(); } } isc_result_t dhcp_failover_state_set_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_typed_data_t *value) { isc_result_t status; if (h -> type != dhcp_type_failover_state) return DHCP_R_INVALIDARG; /* This list of successful returns is completely wrong, but the fastest way to make dhcpctl do something vaguely sane when you try to change the local state. */ if (!omapi_ds_strcmp (name, "name")) { return ISC_R_SUCCESS; } else if (!omapi_ds_strcmp (name, "partner-address")) { return ISC_R_SUCCESS; } else if (!omapi_ds_strcmp (name, "local-address")) { return ISC_R_SUCCESS; } else if (!omapi_ds_strcmp (name, "partner-port")) { return ISC_R_SUCCESS; } else if (!omapi_ds_strcmp (name, "local-port")) { return ISC_R_SUCCESS; } else if (!omapi_ds_strcmp (name, "max-outstanding-updates")) { return ISC_R_SUCCESS; } else if (!omapi_ds_strcmp (name, "mclt")) { return ISC_R_SUCCESS; } else if (!omapi_ds_strcmp (name, "load-balance-max-secs")) { return ISC_R_SUCCESS; } else if (!omapi_ds_strcmp (name, "load-balance-hba")) { return ISC_R_SUCCESS; } else if (!omapi_ds_strcmp (name, "partner-state")) { return ISC_R_SUCCESS; } else if (!omapi_ds_strcmp (name, "local-state")) { unsigned long l; status = omapi_get_int_value (&l, value); if (status != ISC_R_SUCCESS) return status; return dhcp_failover_set_state ((dhcp_failover_state_t *)h, l); } else if (!omapi_ds_strcmp (name, "partner-stos")) { return ISC_R_SUCCESS; } else if (!omapi_ds_strcmp (name, "local-stos")) { return ISC_R_SUCCESS; } else if (!omapi_ds_strcmp (name, "hierarchy")) { return ISC_R_SUCCESS; } else if (!omapi_ds_strcmp (name, "last-packet-sent")) { return ISC_R_SUCCESS; } else if (!omapi_ds_strcmp (name, "last-timestamp-received")) { return ISC_R_SUCCESS; } else if (!omapi_ds_strcmp (name, "skew")) { return ISC_R_SUCCESS; } else if (!omapi_ds_strcmp (name, "max-response-delay")) { return ISC_R_SUCCESS; } else if (!omapi_ds_strcmp (name, "cur-unacked-updates")) { return ISC_R_SUCCESS; } if (h -> inner && h -> inner -> type -> set_value) return (*(h -> inner -> type -> set_value)) (h -> inner, id, name, value); return ISC_R_NOTFOUND; } void dhcp_failover_keepalive (void *vs) { } void dhcp_failover_reconnect (void *vs) { dhcp_failover_state_t *state = vs; isc_result_t status; struct timeval tv; #if defined (DEBUG_FAILOVER_TIMING) log_info ("dhcp_failover_reconnect"); #endif /* If we already connected the other way, let the connection recovery code initiate any retry that may be required. */ if (state -> link_to_peer) return; status = dhcp_failover_link_initiate ((omapi_object_t *)state); if (status != ISC_R_SUCCESS && status != DHCP_R_INCOMPLETE) { log_info ("failover peer %s: %s", state -> name, isc_result_totext (status)); #if defined (DEBUG_FAILOVER_TIMING) log_info("add_timeout +90 dhcp_failover_reconnect"); #endif tv . tv_sec = cur_time + 90; tv . tv_usec = 0; add_timeout(&tv, dhcp_failover_reconnect, state, (tvref_t)dhcp_failover_state_reference, (tvunref_t)dhcp_failover_state_dereference); } } void dhcp_failover_startup_timeout (void *vs) { dhcp_failover_state_t *state = vs; #if defined (DEBUG_FAILOVER_TIMING) log_info ("dhcp_failover_startup_timeout"); #endif dhcp_failover_state_transition (state, "disconnect"); } void dhcp_failover_link_startup_timeout (void *vl) { dhcp_failover_link_t *link = vl; omapi_object_t *p; for (p = (omapi_object_t *)link; p -> inner; p = p -> inner) ; for (; p; p = p -> outer) if (p -> type == omapi_type_connection) break; if (p) { log_info ("failover: link startup timeout"); omapi_disconnect (p, 1); } } void dhcp_failover_listener_restart (void *vs) { dhcp_failover_state_t *state = vs; isc_result_t status; struct timeval tv; #if defined (DEBUG_FAILOVER_TIMING) log_info ("dhcp_failover_listener_restart"); #endif status = dhcp_failover_listen ((omapi_object_t *)state); if (status != ISC_R_SUCCESS) { log_info ("failover peer %s: %s", state -> name, isc_result_totext (status)); #if defined (DEBUG_FAILOVER_TIMING) log_info ("add_timeout +90 %s", "dhcp_failover_listener_restart"); #endif tv . tv_sec = cur_time + 90; tv . tv_usec = 0; add_timeout (&tv, dhcp_failover_listener_restart, state, (tvref_t)dhcp_failover_state_reference, (tvunref_t)dhcp_failover_state_dereference); } } void dhcp_failover_auto_partner_down(void *vs) { dhcp_failover_state_t *state = vs; #if defined (DEBUG_FAILOVER_TIMING) log_info("dhcp_failover_auto_partner_down"); #endif dhcp_failover_set_state(state, partner_down); } isc_result_t dhcp_failover_state_get_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_value_t **value) { dhcp_failover_state_t *s; struct option_cache *oc; struct data_string ds; isc_result_t status; if (h -> type != dhcp_type_failover_state) return DHCP_R_INVALIDARG; s = (dhcp_failover_state_t *)h; if (!omapi_ds_strcmp (name, "name")) { if (s -> name) return omapi_make_string_value (value, name, s -> name, MDL); return ISC_R_NOTFOUND; } else if (!omapi_ds_strcmp (name, "partner-address")) { oc = s -> partner.address; getaddr: memset (&ds, 0, sizeof ds); if (!evaluate_option_cache (&ds, (struct packet *)0, (struct lease *)0, (struct client_state *)0, (struct option_state *)0, (struct option_state *)0, &global_scope, oc, MDL)) { return ISC_R_NOTFOUND; } status = omapi_make_const_value (value, name, ds.data, ds.len, MDL); /* Disgusting kludge: */ if (oc == s -> me.address && !s -> server_identifier.len) data_string_copy (&s -> server_identifier, &ds, MDL); data_string_forget (&ds, MDL); return status; } else if (!omapi_ds_strcmp (name, "local-address")) { oc = s -> me.address; goto getaddr; } else if (!omapi_ds_strcmp (name, "partner-port")) { return omapi_make_int_value (value, name, s -> partner.port, MDL); } else if (!omapi_ds_strcmp (name, "local-port")) { return omapi_make_int_value (value, name, s -> me.port, MDL); } else if (!omapi_ds_strcmp (name, "max-outstanding-updates")) { return omapi_make_uint_value (value, name, s -> me.max_flying_updates, MDL); } else if (!omapi_ds_strcmp (name, "mclt")) { return omapi_make_uint_value (value, name, s -> mclt, MDL); } else if (!omapi_ds_strcmp (name, "load-balance-max-secs")) { return omapi_make_int_value (value, name, s -> load_balance_max_secs, MDL); } else if (!omapi_ds_strcmp (name, "load-balance-hba")) { if (s -> hba) return omapi_make_const_value (value, name, s -> hba, 32, MDL); return ISC_R_NOTFOUND; } else if (!omapi_ds_strcmp (name, "partner-state")) { return omapi_make_uint_value (value, name, s -> partner.state, MDL); } else if (!omapi_ds_strcmp (name, "local-state")) { return omapi_make_uint_value (value, name, s -> me.state, MDL); } else if (!omapi_ds_strcmp (name, "partner-stos")) { return omapi_make_int_value (value, name, s -> partner.stos, MDL); } else if (!omapi_ds_strcmp (name, "local-stos")) { return omapi_make_int_value (value, name, s -> me.stos, MDL); } else if (!omapi_ds_strcmp (name, "hierarchy")) { return omapi_make_uint_value (value, name, s -> i_am, MDL); } else if (!omapi_ds_strcmp (name, "last-packet-sent")) { return omapi_make_int_value (value, name, s -> last_packet_sent, MDL); } else if (!omapi_ds_strcmp (name, "last-timestamp-received")) { return omapi_make_int_value (value, name, s -> last_timestamp_received, MDL); } else if (!omapi_ds_strcmp (name, "skew")) { return omapi_make_int_value (value, name, s -> skew, MDL); } else if (!omapi_ds_strcmp (name, "max-response-delay")) { return omapi_make_uint_value (value, name, s -> me.max_response_delay, MDL); } else if (!omapi_ds_strcmp (name, "cur-unacked-updates")) { return omapi_make_int_value (value, name, s -> cur_unacked_updates, MDL); } if (h -> inner && h -> inner -> type -> get_value) return (*(h -> inner -> type -> get_value)) (h -> inner, id, name, value); return ISC_R_NOTFOUND; } isc_result_t dhcp_failover_state_destroy (omapi_object_t *h, const char *file, int line) { dhcp_failover_state_t *s; if (h -> type != dhcp_type_failover_state) return DHCP_R_INVALIDARG; s = (dhcp_failover_state_t *)h; if (s -> link_to_peer) dhcp_failover_link_dereference (&s -> link_to_peer, file, line); if (s -> name) { dfree (s -> name, MDL); s -> name = (char *)0; } if (s -> partner.address) option_cache_dereference (&s -> partner.address, file, line); if (s -> me.address) option_cache_dereference (&s -> me.address, file, line); if (s -> hba) { dfree (s -> hba, file, line); s -> hba = (u_int8_t *)0; } if (s -> update_queue_head) lease_dereference (&s -> update_queue_head, file, line); if (s -> update_queue_tail) lease_dereference (&s -> update_queue_tail, file, line); if (s -> ack_queue_head) lease_dereference (&s -> ack_queue_head, file, line); if (s -> ack_queue_tail) lease_dereference (&s -> ack_queue_tail, file, line); if (s -> send_update_done) lease_dereference (&s -> send_update_done, file, line); if (s -> toack_queue_head) failover_message_dereference (&s -> toack_queue_head, file, line); if (s -> toack_queue_tail) failover_message_dereference (&s -> toack_queue_tail, file, line); return ISC_R_SUCCESS; } /* Write all the published values associated with the object through the specified connection. */ isc_result_t dhcp_failover_state_stuff (omapi_object_t *c, omapi_object_t *id, omapi_object_t *h) { /* In this function c should be a (omapi_connection_object_t *) */ dhcp_failover_state_t *s; isc_result_t status; if (c -> type != omapi_type_connection) return DHCP_R_INVALIDARG; if (h -> type != dhcp_type_failover_state) return DHCP_R_INVALIDARG; s = (dhcp_failover_state_t *)h; status = omapi_connection_put_name (c, "name"); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_string (c, s -> name); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_name (c, "partner-address"); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_uint32 (c, sizeof s -> partner.address); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_copyin (c, (u_int8_t *)&s -> partner.address, sizeof s -> partner.address); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_name (c, "partner-port"); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_uint32 (c, sizeof (u_int32_t)); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_uint32 (c, (u_int32_t)s -> partner.port); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_name (c, "local-address"); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_uint32 (c, sizeof s -> me.address); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_copyin (c, (u_int8_t *)&s -> me.address, sizeof s -> me.address); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_name (c, "local-port"); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_uint32 (c, sizeof (u_int32_t)); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_uint32 (c, (u_int32_t)s -> me.port); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_name (c, "max-outstanding-updates"); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_uint32 (c, sizeof (u_int32_t)); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_uint32 (c, s -> me.max_flying_updates); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_name (c, "mclt"); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_uint32 (c, sizeof (u_int32_t)); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_uint32 (c, s -> mclt); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_name (c, "load-balance-max-secs"); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_uint32 (c, sizeof (u_int32_t)); if (status != ISC_R_SUCCESS) return status; status = (omapi_connection_put_uint32 (c, (u_int32_t)s -> load_balance_max_secs)); if (status != ISC_R_SUCCESS) return status; if (s -> hba) { status = omapi_connection_put_name (c, "load-balance-hba"); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_uint32 (c, 32); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_copyin (c, s -> hba, 32); if (status != ISC_R_SUCCESS) return status; } status = omapi_connection_put_name (c, "partner-state"); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_uint32 (c, sizeof (u_int32_t)); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_uint32 (c, s -> partner.state); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_name (c, "local-state"); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_uint32 (c, sizeof (u_int32_t)); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_uint32 (c, s -> me.state); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_name (c, "partner-stos"); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_uint32 (c, sizeof (u_int32_t)); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_uint32 (c, (u_int32_t)s -> partner.stos); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_name (c, "local-stos"); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_uint32 (c, sizeof (u_int32_t)); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_uint32 (c, (u_int32_t)s -> me.stos); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_name (c, "hierarchy"); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_uint32 (c, sizeof (u_int32_t)); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_uint32 (c, s -> i_am); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_name (c, "last-packet-sent"); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_uint32 (c, sizeof (u_int32_t)); if (status != ISC_R_SUCCESS) return status; status = (omapi_connection_put_uint32 (c, (u_int32_t)s -> last_packet_sent)); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_name (c, "last-timestamp-received"); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_uint32 (c, sizeof (u_int32_t)); if (status != ISC_R_SUCCESS) return status; status = (omapi_connection_put_uint32 (c, (u_int32_t)s -> last_timestamp_received)); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_name (c, "skew"); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_uint32 (c, sizeof (u_int32_t)); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_uint32 (c, (u_int32_t)s -> skew); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_name (c, "max-response-delay"); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_uint32 (c, sizeof (u_int32_t)); if (status != ISC_R_SUCCESS) return status; status = (omapi_connection_put_uint32 (c, (u_int32_t)s -> me.max_response_delay)); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_name (c, "cur-unacked-updates"); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_uint32 (c, sizeof (u_int32_t)); if (status != ISC_R_SUCCESS) return status; status = (omapi_connection_put_uint32 (c, (u_int32_t)s -> cur_unacked_updates)); if (status != ISC_R_SUCCESS) return status; if (h -> inner && h -> inner -> type -> stuff_values) return (*(h -> inner -> type -> stuff_values)) (c, id, h -> inner); return ISC_R_SUCCESS; } isc_result_t dhcp_failover_state_lookup (omapi_object_t **sp, omapi_object_t *id, omapi_object_t *ref) { omapi_value_t *tv = (omapi_value_t *)0; isc_result_t status; dhcp_failover_state_t *s; if (!ref) return DHCP_R_NOKEYS; /* First see if we were sent a handle. */ status = omapi_get_value_str (ref, id, "handle", &tv); if (status == ISC_R_SUCCESS) { status = omapi_handle_td_lookup (sp, tv -> value); omapi_value_dereference (&tv, MDL); if (status != ISC_R_SUCCESS) return status; /* Don't return the object if the type is wrong. */ if ((*sp) -> type != dhcp_type_failover_state) { omapi_object_dereference (sp, MDL); return DHCP_R_INVALIDARG; } } /* Look the failover state up by peer name. */ status = omapi_get_value_str (ref, id, "name", &tv); if (status == ISC_R_SUCCESS) { for (s = failover_states; s; s = s -> next) { unsigned l = strlen (s -> name); if (l == tv -> value -> u.buffer.len && !memcmp (s -> name, tv -> value -> u.buffer.value, l)) break; } omapi_value_dereference (&tv, MDL); /* If we already have a lease, and it's not the same one, then the query was invalid. */ if (*sp && *sp != (omapi_object_t *)s) { omapi_object_dereference (sp, MDL); return DHCP_R_KEYCONFLICT; } else if (!s) { if (*sp) omapi_object_dereference (sp, MDL); return ISC_R_NOTFOUND; } else if (!*sp) /* XXX fix so that hash lookup itself creates XXX the reference. */ omapi_object_reference (sp, (omapi_object_t *)s, MDL); } /* If we get to here without finding a lease, no valid key was specified. */ if (!*sp) return DHCP_R_NOKEYS; return ISC_R_SUCCESS; } isc_result_t dhcp_failover_state_create (omapi_object_t **sp, omapi_object_t *id) { return ISC_R_NOTIMPLEMENTED; } isc_result_t dhcp_failover_state_remove (omapi_object_t *sp, omapi_object_t *id) { return ISC_R_NOTIMPLEMENTED; } int dhcp_failover_state_match (dhcp_failover_state_t *state, u_int8_t *addr, unsigned addrlen) { struct data_string ds; int i; memset (&ds, 0, sizeof ds); if (evaluate_option_cache (&ds, (struct packet *)0, (struct lease *)0, (struct client_state *)0, (struct option_state *)0, (struct option_state *)0, &global_scope, state -> partner.address, MDL)) { for (i = 0; i + addrlen - 1 < ds.len; i += addrlen) { if (!memcmp (&ds.data [i], addr, addrlen)) { data_string_forget (&ds, MDL); return 1; } } data_string_forget (&ds, MDL); } return 0; } int dhcp_failover_state_match_by_name(state, name) dhcp_failover_state_t *state; failover_option_t *name; { if ((strlen(state->name) == name->count) && (memcmp(state->name, name->data, name->count) == 0)) return 1; return 0; } const char *dhcp_failover_reject_reason_print (int reason) { static char resbuf[sizeof("Undefined-255: This reason code is not defined " "in the protocol standard.")]; if ((reason > 0xff) || (reason < 0)) return "Reason code out of range."; switch (reason) { case FTR_ILLEGAL_IP_ADDR: return "Illegal IP address (not part of any address pool)."; case FTR_FATAL_CONFLICT: return "Fatal conflict exists: address in use by other client."; case FTR_MISSING_BINDINFO: return "Missing binding information."; case FTR_TIMEMISMATCH: return "Connection rejected, time mismatch too great."; case FTR_INVALID_MCLT: return "Connection rejected, invalid MCLT."; case FTR_MISC_REJECT: return "Connection rejected, unknown reason."; case FTR_DUP_CONNECTION: return "Connection rejected, duplicate connection."; case FTR_INVALID_PARTNER: return "Connection rejected, invalid failover partner."; case FTR_TLS_UNSUPPORTED: return "TLS not supported."; case FTR_TLS_UNCONFIGURED: return "TLS supported but not configured."; case FTR_TLS_REQUIRED: return "TLS required but not supported by partner."; case FTR_DIGEST_UNSUPPORTED: return "Message digest not supported."; case FTR_DIGEST_UNCONFIGURED: return "Message digest not configured."; case FTR_VERSION_MISMATCH: return "Protocol version mismatch."; case FTR_OUTDATED_BIND_INFO: return "Outdated binding information."; case FTR_LESS_CRIT_BIND_INFO: return "Less critical binding information."; case FTR_NO_TRAFFIC: return "No traffic within sufficient time."; case FTR_HBA_CONFLICT: return "Hash bucket assignment conflict."; case FTR_IP_NOT_RESERVED: return "IP not reserved on this server."; case FTR_IP_DIGEST_FAILURE: return "Message digest failed to compare."; case FTR_IP_MISSING_DIGEST: return "Missing message digest."; case FTR_UNKNOWN: return "Unknown Error."; default: sprintf(resbuf, "Undefined-%d: This reason code is not defined in the " "protocol standard.", reason); return resbuf; } } const char *dhcp_failover_state_name_print (enum failover_state state) { switch (state) { default: case unknown_state: return "unknown-state"; case partner_down: return "partner-down"; case normal: return "normal"; case conflict_done: return "conflict-done"; case communications_interrupted: return "communications-interrupted"; case resolution_interrupted: return "resolution-interrupted"; case potential_conflict: return "potential-conflict"; case recover: return "recover"; case recover_done: return "recover-done"; case recover_wait: return "recover-wait"; case shut_down: return "shutdown"; case paused: return "paused"; case startup: return "startup"; } } const char *dhcp_failover_message_name (unsigned type) { static char messbuf[sizeof("unknown-message-255")]; if (type > 0xff) return "invalid-message"; switch (type) { case FTM_POOLREQ: return "pool-request"; case FTM_POOLRESP: return "pool-response"; case FTM_BNDUPD: return "bind-update"; case FTM_BNDACK: return "bind-ack"; case FTM_CONNECT: return "connect"; case FTM_CONNECTACK: return "connect-ack"; case FTM_UPDREQ: return "update-request"; case FTM_UPDDONE: return "update-done"; case FTM_UPDREQALL: return "update-request-all"; case FTM_STATE: return "state"; case FTM_CONTACT: return "contact"; case FTM_DISCONNECT: return "disconnect"; default: sprintf(messbuf, "unknown-message-%u", type); return messbuf; } } const char *dhcp_failover_option_name (unsigned type) { static char optbuf[sizeof("unknown-option-65535")]; if (type > 0xffff) return "invalid-option"; switch (type) { case FTO_ADDRESSES_TRANSFERRED: return "addresses-transferred"; case FTO_ASSIGNED_IP_ADDRESS: return "assigned-ip-address"; case FTO_BINDING_STATUS: return "binding-status"; case FTO_CLIENT_IDENTIFIER: return "client-identifier"; case FTO_CHADDR: return "chaddr"; case FTO_CLTT: return "cltt"; case FTO_DDNS: return "ddns"; case FTO_DELAYED_SERVICE: return "delayed-service"; case FTO_HBA: return "hba"; case FTO_IP_FLAGS: return "ip-flags"; case FTO_LEASE_EXPIRY: return "lease-expiry"; case FTO_MAX_UNACKED: return "max-unacked"; case FTO_MCLT: return "mclt"; case FTO_MESSAGE: return "message"; case FTO_MESSAGE_DIGEST: return "message-digest"; case FTO_POTENTIAL_EXPIRY: return "potential-expiry"; case FTO_PROTOCOL_VERSION: return "protocol-version"; case FTO_RECEIVE_TIMER: return "receive-timer"; case FTO_REJECT_REASON: return "reject-reason"; case FTO_RELATIONSHIP_NAME: return "relationship-name"; case FTO_REPLY_OPTIONS: return "reply-options"; case FTO_REQUEST_OPTIONS: return "request-options"; case FTO_SERVER_FLAGS: return "server-flags"; case FTO_SERVER_STATE: return "server-state"; case FTO_STOS: return "stos"; case FTO_TLS_REPLY: return "tls-reply"; case FTO_TLS_REQUEST: return "tls-request"; case FTO_VENDOR_CLASS: return "vendor-class"; case FTO_VENDOR_OPTIONS: return "vendor-options"; default: sprintf(optbuf, "unknown-option-%u", type); return optbuf; } } failover_option_t *dhcp_failover_option_printf (unsigned code, char *obuf, unsigned *obufix, unsigned obufmax, const char *fmt, ...) { va_list va; char tbuf [256]; /* %Audit% Truncation causes panic. %2004.06.17,Revisit% * It is unclear what the effects of truncation here are, or * how that condition should be handled. It seems that this * function is used for formatting messages in the failover * command channel. For now the safest thing is for * overflow-truncation to cause a fatal log. */ va_start (va, fmt); if (vsnprintf (tbuf, sizeof tbuf, fmt, va) >= sizeof tbuf) log_fatal ("%s: vsnprintf would truncate", "dhcp_failover_make_option"); va_end (va); return dhcp_failover_make_option (code, obuf, obufix, obufmax, strlen (tbuf), tbuf); } failover_option_t *dhcp_failover_make_option (unsigned code, char *obuf, unsigned *obufix, unsigned obufmax, ...) { va_list va; struct failover_option_info *info; int i; unsigned size, count; unsigned val; u_int8_t *iaddr; unsigned ilen = 0; u_int8_t *bval; char *txt = NULL; #if defined (DEBUG_FAILOVER_MESSAGES) char tbuf [256]; #endif /* Note that the failover_option structure is used differently on input than on output - on input, count is an element count, and on output it's the number of bytes total in the option, including the option code and option length. */ failover_option_t option, *op; /* Bogus option code? */ if (code < 1 || code > FTO_MAX || ft_options [code].type == FT_UNDEF) { return &null_failover_option; } info = &ft_options [code]; va_start (va, obufmax); /* Get the number of elements and the size of the buffer we need to allocate. */ if (info -> type == FT_DDNS || info -> type == FT_DDNS1) { count = info -> type == FT_DDNS ? 1 : 2; size = va_arg (va, int) + count; } else { /* Find out how many items in this list. */ if (info -> num_present) count = info -> num_present; else count = va_arg (va, int); /* Figure out size. */ switch (info -> type) { case FT_UINT8: case FT_BYTES: case FT_DIGEST: size = count; break; case FT_TEXT_OR_BYTES: case FT_TEXT: txt = va_arg (va, char *); size = count; break; case FT_IPADDR: ilen = va_arg (va, unsigned); size = count * ilen; break; case FT_UINT32: size = count * 4; break; case FT_UINT16: size = count * 2; break; default: /* shouldn't get here. */ log_fatal ("bogus type in failover_make_option: %d", info -> type); return &null_failover_option; } } size += 4; /* Allocate a buffer for the option. */ option.count = size; option.data = dmalloc (option.count, MDL); if (!option.data) { va_end (va); return &null_failover_option; } /* Put in the option code and option length. */ putUShort (option.data, code); putUShort (&option.data [2], size - 4); #if defined (DEBUG_FAILOVER_MESSAGES) /* %Audit% Truncation causes panic. %2004.06.17,Revisit% * It is unclear what the effects of truncation here are, or * how that condition should be handled. It seems that this * message may be sent over the failover command channel. * For now the safest thing is for overflow-truncation to cause * a fatal log. */ if (snprintf (tbuf, sizeof tbuf, " (%s<%d>", info -> name, option.count) >= sizeof tbuf) log_fatal ("dhcp_failover_make_option: tbuf overflow"); failover_print (obuf, obufix, obufmax, tbuf); #endif /* Now put in the data. */ switch (info -> type) { case FT_UINT8: for (i = 0; i < count; i++) { val = va_arg (va, unsigned); #if defined (DEBUG_FAILOVER_MESSAGES) /* %Audit% Cannot exceed 24 bytes. %2004.06.17,Safe% */ sprintf (tbuf, " %d", val); failover_print (obuf, obufix, obufmax, tbuf); #endif option.data [i + 4] = val; } break; case FT_IPADDR: for (i = 0; i < count; i++) { iaddr = va_arg (va, u_int8_t *); if (ilen != 4) { dfree (option.data, MDL); log_error ("IP addrlen=%d, should be 4.", ilen); va_end (va); return &null_failover_option; } #if defined (DEBUG_FAILOVER_MESSAGES) /*%Audit% Cannot exceed 17 bytes. %2004.06.17,Safe%*/ sprintf (tbuf, " %u.%u.%u.%u", iaddr [0], iaddr [1], iaddr [2], iaddr [3]); failover_print (obuf, obufix, obufmax, tbuf); #endif memcpy (&option.data [4 + i * ilen], iaddr, ilen); } break; case FT_UINT32: for (i = 0; i < count; i++) { val = va_arg (va, unsigned); #if defined (DEBUG_FAILOVER_MESSAGES) /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/ sprintf (tbuf, " %d", val); failover_print (obuf, obufix, obufmax, tbuf); #endif putULong (&option.data [4 + i * 4], val); } break; case FT_BYTES: case FT_DIGEST: bval = va_arg (va, u_int8_t *); #if defined (DEBUG_FAILOVER_MESSAGES) for (i = 0; i < count; i++) { /* 23 bytes plus nul, safe. */ sprintf (tbuf, " %d", bval [i]); failover_print (obuf, obufix, obufmax, tbuf); } #endif memcpy (&option.data [4], bval, count); break; /* On output, TEXT_OR_BYTES is _always_ text, and always NUL terminated. Note that the caller should be careful not to provide a format and data that amount to more than 256 bytes of data, since it will cause a fatal error. */ case FT_TEXT_OR_BYTES: case FT_TEXT: #if defined (DEBUG_FAILOVER_MESSAGES) /* %Audit% Truncation causes panic. %2004.06.17,Revisit% * It is unclear what the effects of truncation here are, or * how that condition should be handled. It seems that this * function is used for formatting messages in the failover * command channel. For now the safest thing is for * overflow-truncation to cause a fatal log. */ if (snprintf (tbuf, sizeof tbuf, "\"%s\"", txt) >= sizeof tbuf) log_fatal ("dhcp_failover_make_option: tbuf overflow"); failover_print (obuf, obufix, obufmax, tbuf); #endif memcpy (&option.data [4], txt, count); break; case FT_DDNS: case FT_DDNS1: option.data [4] = va_arg (va, unsigned); if (count == 2) option.data [5] = va_arg (va, unsigned); bval = va_arg (va, u_int8_t *); memcpy (&option.data [4 + count], bval, size - count - 4); #if defined (DEBUG_FAILOVER_MESSAGES) for (i = 4; i < size; i++) { /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/ sprintf (tbuf, " %d", option.data [i]); failover_print (obuf, obufix, obufmax, tbuf); } #endif break; case FT_UINT16: for (i = 0; i < count; i++) { val = va_arg (va, u_int32_t); #if defined (DEBUG_FAILOVER_MESSAGES) /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/ sprintf (tbuf, " %d", val); failover_print (obuf, obufix, obufmax, tbuf); #endif putUShort (&option.data [4 + i * 2], val); } break; case FT_UNDEF: default: break; } #if defined DEBUG_FAILOVER_MESSAGES failover_print (obuf, obufix, obufmax, ")"); #endif va_end (va); /* Now allocate a place to store what we just set up. */ op = dmalloc (sizeof (failover_option_t), MDL); if (!op) { dfree (option.data, MDL); return &null_failover_option; } *op = option; return op; } /* Send a failover message header. */ isc_result_t dhcp_failover_put_message (dhcp_failover_link_t *link, omapi_object_t *connection, int msg_type, u_int32_t xid, ...) { unsigned size = 0; int bad_option = 0; int opix = 0; va_list list; failover_option_t *option; unsigned char *opbuf; isc_result_t status = ISC_R_SUCCESS; unsigned char cbuf; struct timeval tv; /* Run through the argument list once to compute the length of the option portion of the message. */ va_start (list, xid); while ((option = va_arg (list, failover_option_t *))) { if (option != &skip_failover_option) size += option -> count; if (option == &null_failover_option) bad_option = 1; } va_end (list); /* Allocate an option buffer, unless we got an error. */ if (!bad_option && size) { opbuf = dmalloc (size, MDL); if (!opbuf) status = ISC_R_NOMEMORY; } else opbuf = (unsigned char *)0; va_start (list, xid); while ((option = va_arg (list, failover_option_t *))) { if (option == &skip_failover_option) continue; if (!bad_option && opbuf) memcpy (&opbuf [opix], option -> data, option -> count); if (option != &null_failover_option && option != &skip_failover_option) { opix += option -> count; dfree (option -> data, MDL); dfree (option, MDL); } } va_end(list); if (bad_option) return DHCP_R_INVALIDARG; /* Now send the message header. */ /* Message length. */ status = omapi_connection_put_uint16 (connection, size + 12); if (status != ISC_R_SUCCESS) goto err; /* Message type. */ cbuf = msg_type; status = omapi_connection_copyin (connection, &cbuf, 1); if (status != ISC_R_SUCCESS) goto err; /* Payload offset. */ cbuf = 12; status = omapi_connection_copyin (connection, &cbuf, 1); if (status != ISC_R_SUCCESS) goto err; /* Current time. */ status = omapi_connection_put_uint32 (connection, (u_int32_t)cur_time); if (status != ISC_R_SUCCESS) goto err; /* Transaction ID. */ status = omapi_connection_put_uint32(connection, xid); if (status != ISC_R_SUCCESS) goto err; /* Payload. */ if (opbuf) { status = omapi_connection_copyin (connection, opbuf, size); if (status != ISC_R_SUCCESS) goto err; dfree (opbuf, MDL); } if (link -> state_object && link -> state_object -> link_to_peer == link) { #if defined (DEBUG_FAILOVER_CONTACT_TIMING) log_info ("add_timeout +%d %s", (int)(link -> state_object -> partner.max_response_delay) / 3, "dhcp_failover_send_contact"); #endif tv . tv_sec = cur_time + (int)(link -> state_object -> partner.max_response_delay) / 3; tv . tv_usec = 0; add_timeout (&tv, dhcp_failover_send_contact, link -> state_object, (tvref_t)dhcp_failover_state_reference, (tvunref_t)dhcp_failover_state_dereference); } return status; err: if (opbuf) dfree (opbuf, MDL); log_info ("dhcp_failover_put_message: something went wrong."); omapi_disconnect (connection, 1); return status; } void dhcp_failover_timeout (void *vstate) { dhcp_failover_state_t *state = vstate; dhcp_failover_link_t *link; #if defined (DEBUG_FAILOVER_TIMING) log_info ("dhcp_failover_timeout"); #endif if (!state || state -> type != dhcp_type_failover_state) return; link = state -> link_to_peer; if (!link || !link -> outer || link -> outer -> type != omapi_type_connection) return; log_error ("timeout waiting for failover peer %s", state -> name); /* If we haven't gotten a timely response, blow away the connection. This will cause the state to change automatically. */ omapi_disconnect (link -> outer, 1); } void dhcp_failover_send_contact (void *vstate) { dhcp_failover_state_t *state = vstate; dhcp_failover_link_t *link; isc_result_t status; #if defined(DEBUG_FAILOVER_MESSAGES) && \ defined(DEBUG_FAILOVER_CONTACT_MESSAGES) char obuf [64]; unsigned obufix = 0; failover_print(obuf, &obufix, sizeof(obuf), "(contact"); #endif #if defined (DEBUG_FAILOVER_CONTACT_TIMING) log_info ("dhcp_failover_send_contact"); #endif if (!state || state -> type != dhcp_type_failover_state) return; link = state -> link_to_peer; if (!link || !link -> outer || link -> outer -> type != omapi_type_connection) return; status = (dhcp_failover_put_message (link, link -> outer, FTM_CONTACT, link->xid++, (failover_option_t *)0)); #if defined(DEBUG_FAILOVER_MESSAGES) && \ defined(DEBUG_FAILOVER_CONTACT_MESSAGES) if (status != ISC_R_SUCCESS) failover_print(obuf, &obufix, sizeof(obuf), " (failed)"); failover_print(obuf, &obufix, sizeof(obuf), ")"); if (obufix) { log_debug ("%s", obuf); } #else IGNORE_UNUSED(status); #endif return; } isc_result_t dhcp_failover_send_state (dhcp_failover_state_t *state) { dhcp_failover_link_t *link; isc_result_t status; #if defined (DEBUG_FAILOVER_MESSAGES) char obuf [64]; unsigned obufix = 0; # define FMA obuf, &obufix, sizeof obuf failover_print (FMA, "(state"); #else # define FMA (char *)0, (unsigned *)0, 0 #endif if (!state || state -> type != dhcp_type_failover_state) return DHCP_R_INVALIDARG; link = state -> link_to_peer; if (!link || !link -> outer || link -> outer -> type != omapi_type_connection) return DHCP_R_INVALIDARG; status = (dhcp_failover_put_message (link, link -> outer, FTM_STATE, link->xid++, dhcp_failover_make_option (FTO_SERVER_STATE, FMA, (state -> me.state == startup ? state -> saved_state : state -> me.state)), dhcp_failover_make_option (FTO_SERVER_FLAGS, FMA, (state -> service_state == service_startup ? FTF_SERVER_STARTUP : 0)), dhcp_failover_make_option (FTO_STOS, FMA, state -> me.stos), (failover_option_t *)0)); #if defined (DEBUG_FAILOVER_MESSAGES) if (status != ISC_R_SUCCESS) failover_print (FMA, " (failed)"); failover_print (FMA, ")"); if (obufix) { log_debug ("%s", obuf); } #else IGNORE_UNUSED(status); #endif return ISC_R_SUCCESS; } /* Send a connect message. */ isc_result_t dhcp_failover_send_connect (omapi_object_t *l) { dhcp_failover_link_t *link; dhcp_failover_state_t *state; isc_result_t status; #if defined (DEBUG_FAILOVER_MESSAGES) char obuf [64]; unsigned obufix = 0; # define FMA obuf, &obufix, sizeof obuf failover_print (FMA, "(connect"); #else # define FMA (char *)0, (unsigned *)0, 0 #endif if (!l || l -> type != dhcp_type_failover_link) return DHCP_R_INVALIDARG; link = (dhcp_failover_link_t *)l; state = link -> state_object; if (!l -> outer || l -> outer -> type != omapi_type_connection) return DHCP_R_INVALIDARG; status = (dhcp_failover_put_message (link, l -> outer, FTM_CONNECT, link->xid++, dhcp_failover_make_option(FTO_RELATIONSHIP_NAME, FMA, strlen(state->name), state->name), dhcp_failover_make_option (FTO_MAX_UNACKED, FMA, state -> me.max_flying_updates), dhcp_failover_make_option (FTO_RECEIVE_TIMER, FMA, state -> me.max_response_delay), dhcp_failover_option_printf(FTO_VENDOR_CLASS, FMA, "isc-%s", PACKAGE_VERSION), dhcp_failover_make_option (FTO_PROTOCOL_VERSION, FMA, DHCP_FAILOVER_VERSION), dhcp_failover_make_option (FTO_TLS_REQUEST, FMA, 0, 0), dhcp_failover_make_option (FTO_MCLT, FMA, state -> mclt), (state -> hba ? dhcp_failover_make_option (FTO_HBA, FMA, 32, state -> hba) : &skip_failover_option), (failover_option_t *)0)); #if defined (DEBUG_FAILOVER_MESSAGES) if (status != ISC_R_SUCCESS) failover_print (FMA, " (failed)"); failover_print (FMA, ")"); if (obufix) { log_debug ("%s", obuf); } #endif return status; } isc_result_t dhcp_failover_send_connectack (omapi_object_t *l, dhcp_failover_state_t *state, int reason, const char *errmsg) { dhcp_failover_link_t *link; isc_result_t status; #if defined (DEBUG_FAILOVER_MESSAGES) char obuf [64]; unsigned obufix = 0; # define FMA obuf, &obufix, sizeof obuf failover_print (FMA, "(connectack"); #else # define FMA (char *)0, (unsigned *)0, 0 #endif if (!l || l -> type != dhcp_type_failover_link) return DHCP_R_INVALIDARG; link = (dhcp_failover_link_t *)l; if (!l -> outer || l -> outer -> type != omapi_type_connection) return DHCP_R_INVALIDARG; status = (dhcp_failover_put_message (link, l -> outer, FTM_CONNECTACK, link->imsg->xid, state ? dhcp_failover_make_option(FTO_RELATIONSHIP_NAME, FMA, strlen(state->name), state->name) : (link->imsg->options_present & FTB_RELATIONSHIP_NAME) ? dhcp_failover_make_option(FTO_RELATIONSHIP_NAME, FMA, link->imsg->relationship_name.count, link->imsg->relationship_name.data) : &skip_failover_option, state ? dhcp_failover_make_option (FTO_MAX_UNACKED, FMA, state -> me.max_flying_updates) : &skip_failover_option, state ? dhcp_failover_make_option (FTO_RECEIVE_TIMER, FMA, state -> me.max_response_delay) : &skip_failover_option, dhcp_failover_option_printf(FTO_VENDOR_CLASS, FMA, "isc-%s", PACKAGE_VERSION), dhcp_failover_make_option (FTO_PROTOCOL_VERSION, FMA, DHCP_FAILOVER_VERSION), (link->imsg->options_present & FTB_TLS_REQUEST) ? dhcp_failover_make_option(FTO_TLS_REPLY, FMA, 0, 0) : &skip_failover_option, reason ? dhcp_failover_make_option (FTO_REJECT_REASON, FMA, reason) : &skip_failover_option, (reason && errmsg) ? dhcp_failover_make_option (FTO_MESSAGE, FMA, strlen (errmsg), errmsg) : &skip_failover_option, (failover_option_t *)0)); #if defined (DEBUG_FAILOVER_MESSAGES) if (status != ISC_R_SUCCESS) failover_print (FMA, " (failed)"); failover_print (FMA, ")"); if (obufix) { log_debug ("%s", obuf); } #endif return status; } isc_result_t dhcp_failover_send_disconnect (omapi_object_t *l, int reason, const char *message) { dhcp_failover_link_t *link; isc_result_t status; #if defined (DEBUG_FAILOVER_MESSAGES) char obuf [64]; unsigned obufix = 0; # define FMA obuf, &obufix, sizeof obuf failover_print (FMA, "(disconnect"); #else # define FMA (char *)0, (unsigned *)0, 0 #endif if (!l || l -> type != dhcp_type_failover_link) return DHCP_R_INVALIDARG; link = (dhcp_failover_link_t *)l; if (!l -> outer || l -> outer -> type != omapi_type_connection) return DHCP_R_INVALIDARG; if (!message && reason) message = dhcp_failover_reject_reason_print (reason); status = (dhcp_failover_put_message (link, l -> outer, FTM_DISCONNECT, link->xid++, dhcp_failover_make_option (FTO_REJECT_REASON, FMA, reason), (message ? dhcp_failover_make_option (FTO_MESSAGE, FMA, strlen (message), message) : &skip_failover_option), (failover_option_t *)0)); #if defined (DEBUG_FAILOVER_MESSAGES) if (status != ISC_R_SUCCESS) failover_print (FMA, " (failed)"); failover_print (FMA, ")"); if (obufix) { log_debug ("%s", obuf); } #endif return status; } /* Send a Bind Update message. */ isc_result_t dhcp_failover_send_bind_update (dhcp_failover_state_t *state, struct lease *lease) { dhcp_failover_link_t *link; isc_result_t status; int flags = 0; binding_state_t transmit_state; #if defined (DEBUG_FAILOVER_MESSAGES) char obuf [64]; unsigned obufix = 0; # define FMA obuf, &obufix, sizeof obuf failover_print (FMA, "(bndupd"); #else # define FMA (char *)0, (unsigned *)0, 0 #endif if (!state -> link_to_peer || state -> link_to_peer -> type != dhcp_type_failover_link) return DHCP_R_INVALIDARG; link = (dhcp_failover_link_t *)state -> link_to_peer; if (!link -> outer || link -> outer -> type != omapi_type_connection) return DHCP_R_INVALIDARG; transmit_state = lease->desired_binding_state; if (lease->flags & RESERVED_LEASE) { /* If we are listing an allocable (not yet ACTIVE etc) lease * as reserved, toggle to the peer's 'free state', per the * draft. This gives the peer permission to alloc it to the * chaddr/uid-named client. */ if ((state->i_am == primary) && (transmit_state == FTS_FREE)) transmit_state = FTS_BACKUP; else if ((state->i_am == secondary) && (transmit_state == FTS_BACKUP)) transmit_state = FTS_FREE; flags |= FTF_IP_FLAG_RESERVE; } if (lease->flags & BOOTP_LEASE) flags |= FTF_IP_FLAG_BOOTP; /* last_xid == 0 is illegal, seek past zero if we hit it. */ if (link->xid == 0) link->xid = 1; lease->last_xid = link->xid++; /* * Our very next action is to transmit a binding update relating to * this lease over the wire, and although there is a BNDACK, there is * no BNDACKACK or BNDACKACKACK...the basic issue as we send a BNDUPD, * we may not receive a BNDACK. This non-reception does not imply the * peer did not receive and process the BNDUPD. So at this point, we * must divest any state that would be dangerous to retain under the * impression the peer has been updated. Normally state changes like * this are processed in supersede_lease(), but in this case we need a * very late binding. * * In failover rules, a server is permitted to work forward in certain * directions from a given lease's state; active leases may be * extended, so forth. There is an 'optimization' in the failover * draft that permits a server to 'rewind' any work they have not * informed the peer. Since we can't know if the peer received our * update but was unable to acknowledge it, we make this change on * transmit rather than upon receiving the acknowledgement. * * XXX: Frequent lease commits are undesirable. This should hopefully * only trigger when a server is sending a lease /state change/, and * not merely an update such as with a renewal. */ if (lease->rewind_binding_state != lease->binding_state) { lease->rewind_binding_state = lease->binding_state; write_lease(lease); commit_leases(); } /* Send the update. */ status = (dhcp_failover_put_message (link, link -> outer, FTM_BNDUPD, lease->last_xid, dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS, FMA, lease -> ip_addr.len, lease -> ip_addr.iabuf), dhcp_failover_make_option (FTO_BINDING_STATUS, FMA, lease -> desired_binding_state), lease -> uid_len ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER, FMA, lease -> uid_len, lease -> uid) : &skip_failover_option, lease -> hardware_addr.hlen ? dhcp_failover_make_option (FTO_CHADDR, FMA, lease -> hardware_addr.hlen, lease -> hardware_addr.hbuf) : &skip_failover_option, dhcp_failover_make_option (FTO_LEASE_EXPIRY, FMA, lease -> ends), dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY, FMA, lease -> tstp), dhcp_failover_make_option (FTO_STOS, FMA, lease -> starts), (lease->cltt != 0) ? dhcp_failover_make_option(FTO_CLTT, FMA, lease->cltt) : &skip_failover_option, /* No CLTT */ flags ? dhcp_failover_make_option(FTO_IP_FLAGS, FMA, flags) : &skip_failover_option, /* No IP_FLAGS */ &skip_failover_option, /* XXX DDNS */ &skip_failover_option, /* XXX request options */ &skip_failover_option, /* XXX reply options */ (failover_option_t *)0)); #if defined (DEBUG_FAILOVER_MESSAGES) if (status != ISC_R_SUCCESS) failover_print (FMA, " (failed)"); failover_print (FMA, ")"); if (obufix) { log_debug ("%s", obuf); } #endif return status; } /* Send a Bind ACK message. */ isc_result_t dhcp_failover_send_bind_ack (dhcp_failover_state_t *state, failover_message_t *msg, int reason, const char *message) { dhcp_failover_link_t *link; isc_result_t status; #if defined (DEBUG_FAILOVER_MESSAGES) char obuf [64]; unsigned obufix = 0; # define FMA obuf, &obufix, sizeof obuf failover_print (FMA, "(bndack"); #else # define FMA (char *)0, (unsigned *)0, 0 #endif if (!state -> link_to_peer || state -> link_to_peer -> type != dhcp_type_failover_link) return DHCP_R_INVALIDARG; link = (dhcp_failover_link_t *)state -> link_to_peer; if (!link -> outer || link -> outer -> type != omapi_type_connection) return DHCP_R_INVALIDARG; if (!message && reason) message = dhcp_failover_reject_reason_print (reason); /* Send the update. */ status = (dhcp_failover_put_message (link, link -> outer, FTM_BNDACK, msg->xid, dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS, FMA, sizeof msg -> assigned_addr, &msg -> assigned_addr), #ifdef DO_BNDACK_SHOULD_NOT dhcp_failover_make_option (FTO_BINDING_STATUS, FMA, msg -> binding_status), (msg -> options_present & FTB_CLIENT_IDENTIFIER) ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER, FMA, msg -> client_identifier.count, msg -> client_identifier.data) : &skip_failover_option, (msg -> options_present & FTB_CHADDR) ? dhcp_failover_make_option (FTO_CHADDR, FMA, msg -> chaddr.count, msg -> chaddr.data) : &skip_failover_option, dhcp_failover_make_option (FTO_LEASE_EXPIRY, FMA, msg -> expiry), dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY, FMA, msg -> potential_expiry), dhcp_failover_make_option (FTO_STOS, FMA, msg -> stos), (msg->options_present & FTB_CLTT) ? dhcp_failover_make_option(FTO_CLTT, FMA, msg->cltt) : &skip_failover_option, /* No CLTT in the msg to ack. */ ((msg->options_present & FTB_IP_FLAGS) && msg->ip_flags) ? dhcp_failover_make_option(FTO_IP_FLAGS, FMA, msg->ip_flags) : &skip_failover_option, #endif /* DO_BNDACK_SHOULD_NOT */ reason ? dhcp_failover_make_option(FTO_REJECT_REASON, FMA, reason) : &skip_failover_option, (reason && message) ? dhcp_failover_make_option (FTO_MESSAGE, FMA, strlen (message), message) : &skip_failover_option, #ifdef DO_BNDACK_SHOULD_NOT &skip_failover_option, /* XXX DDNS */ &skip_failover_option, /* XXX request options */ &skip_failover_option, /* XXX reply options */ #endif /* DO_BNDACK_SHOULD_NOT */ (failover_option_t *)0)); #if defined (DEBUG_FAILOVER_MESSAGES) if (status != ISC_R_SUCCESS) failover_print (FMA, " (failed)"); failover_print (FMA, ")"); if (obufix) { log_debug ("%s", obuf); } #endif return status; } isc_result_t dhcp_failover_send_poolreq (dhcp_failover_state_t *state) { dhcp_failover_link_t *link; isc_result_t status; #if defined (DEBUG_FAILOVER_MESSAGES) char obuf [64]; unsigned obufix = 0; # define FMA obuf, &obufix, sizeof obuf failover_print (FMA, "(poolreq"); #else # define FMA (char *)0, (unsigned *)0, 0 #endif if (!state -> link_to_peer || state -> link_to_peer -> type != dhcp_type_failover_link) return DHCP_R_INVALIDARG; link = (dhcp_failover_link_t *)state -> link_to_peer; if (!link -> outer || link -> outer -> type != omapi_type_connection) return DHCP_R_INVALIDARG; status = (dhcp_failover_put_message (link, link -> outer, FTM_POOLREQ, link->xid++, (failover_option_t *)0)); #if defined (DEBUG_FAILOVER_MESSAGES) if (status != ISC_R_SUCCESS) failover_print (FMA, " (failed)"); failover_print (FMA, ")"); if (obufix) { log_debug ("%s", obuf); } #endif return status; } isc_result_t dhcp_failover_send_poolresp (dhcp_failover_state_t *state, int leases) { dhcp_failover_link_t *link; isc_result_t status; #if defined (DEBUG_FAILOVER_MESSAGES) char obuf [64]; unsigned obufix = 0; # define FMA obuf, &obufix, sizeof obuf failover_print (FMA, "(poolresp"); #else # define FMA (char *)0, (unsigned *)0, 0 #endif if (!state -> link_to_peer || state -> link_to_peer -> type != dhcp_type_failover_link) return DHCP_R_INVALIDARG; link = (dhcp_failover_link_t *)state -> link_to_peer; if (!link -> outer || link -> outer -> type != omapi_type_connection) return DHCP_R_INVALIDARG; status = (dhcp_failover_put_message (link, link -> outer, FTM_POOLRESP, link->imsg->xid, dhcp_failover_make_option (FTO_ADDRESSES_TRANSFERRED, FMA, leases), (failover_option_t *)0)); #if defined (DEBUG_FAILOVER_MESSAGES) if (status != ISC_R_SUCCESS) failover_print (FMA, " (failed)"); failover_print (FMA, ")"); if (obufix) { log_debug ("%s", obuf); } #endif return status; } isc_result_t dhcp_failover_send_update_request (dhcp_failover_state_t *state) { dhcp_failover_link_t *link; isc_result_t status; #if defined (DEBUG_FAILOVER_MESSAGES) char obuf [64]; unsigned obufix = 0; # define FMA obuf, &obufix, sizeof obuf failover_print (FMA, "(updreq"); #else # define FMA (char *)0, (unsigned *)0, 0 #endif if (!state->link_to_peer || state->link_to_peer->type != dhcp_type_failover_link) return (DHCP_R_INVALIDARG); link = (dhcp_failover_link_t *)state->link_to_peer; if (!link->outer || link->outer->type != omapi_type_connection) return (DHCP_R_INVALIDARG); /* We allow an update to be restarted in case we requested an update * and were interrupted by something. If we had an ALL going we need * to restart that. Otherwise we simply continue with the request */ if (state->curUPD == FTM_UPDREQALL) { return (dhcp_failover_send_update_request_all(state)); } status = (dhcp_failover_put_message(link, link->outer, FTM_UPDREQ, link->xid++, NULL)); state->curUPD = FTM_UPDREQ; #if defined (DEBUG_FAILOVER_MESSAGES) if (status != ISC_R_SUCCESS) failover_print(FMA, " (failed)"); failover_print(FMA, ")"); if (obufix) { log_debug("%s", obuf); } #endif if (status == ISC_R_SUCCESS) { log_info("Sent update request message to %s", state->name); } else { log_error("Failed to send update request all message to %s: %s", state->name, isc_result_totext(status)); } return (status); } isc_result_t dhcp_failover_send_update_request_all (dhcp_failover_state_t *state) { dhcp_failover_link_t *link; isc_result_t status; #if defined (DEBUG_FAILOVER_MESSAGES) char obuf [64]; unsigned obufix = 0; # define FMA obuf, &obufix, sizeof obuf failover_print (FMA, "(updreqall"); #else # define FMA (char *)0, (unsigned *)0, 0 #endif if (!state->link_to_peer || state->link_to_peer->type != dhcp_type_failover_link) return (DHCP_R_INVALIDARG); link = (dhcp_failover_link_t *)state->link_to_peer; if (!link->outer || link->outer->type != omapi_type_connection) return (DHCP_R_INVALIDARG); /* We allow an update to be restarted in case we requested an update * and were interrupted by something. */ status = (dhcp_failover_put_message(link, link->outer, FTM_UPDREQALL, link->xid++, NULL)); state->curUPD = FTM_UPDREQALL; #if defined (DEBUG_FAILOVER_MESSAGES) if (status != ISC_R_SUCCESS) failover_print(FMA, " (failed)"); failover_print(FMA, ")"); if (obufix) { log_debug("%s", obuf); } #endif if (status == ISC_R_SUCCESS) { log_info("Sent update request all message to %s", state->name); } else { log_error("Failed to send update request all message to %s: %s", state->name, isc_result_totext(status)); } return (status); } isc_result_t dhcp_failover_send_update_done (dhcp_failover_state_t *state) { dhcp_failover_link_t *link; isc_result_t status; #if defined (DEBUG_FAILOVER_MESSAGES) char obuf [64]; unsigned obufix = 0; # define FMA obuf, &obufix, sizeof obuf failover_print (FMA, "(upddone"); #else # define FMA (char *)0, (unsigned *)0, 0 #endif if (!state -> link_to_peer || state -> link_to_peer -> type != dhcp_type_failover_link) return DHCP_R_INVALIDARG; link = (dhcp_failover_link_t *)state -> link_to_peer; if (!link -> outer || link -> outer -> type != omapi_type_connection) return DHCP_R_INVALIDARG; status = (dhcp_failover_put_message (link, link -> outer, FTM_UPDDONE, state->updxid, (failover_option_t *)0)); #if defined (DEBUG_FAILOVER_MESSAGES) if (status != ISC_R_SUCCESS) failover_print (FMA, " (failed)"); failover_print (FMA, ")"); if (obufix) { log_debug ("%s", obuf); } #endif log_info ("Sent update done message to %s", state -> name); state->updxid--; /* Paranoia, just so it mismatches. */ /* There may be uncommitted leases at this point (since dhcp_failover_process_bind_ack() doesn't commit leases); commit the lease file. */ commit_leases(); return status; } /* * failover_lease_is_better() compares the binding update in 'msg' with * the current lease in 'lease'. If the determination is that the binding * update shouldn't be allowed to update/crush more critical binding info * on the lease, the lease is preferred. A value of true is returned if the * local lease is preferred, or false if the remote binding update is * preferred. * * For now this function is hopefully simplistic and trivial. It may be that * a more detailed system of preferences is required, so this is something we * should monitor as we gain experience with these dueling events. */ static isc_boolean_t failover_lease_is_better(dhcp_failover_state_t *state, struct lease *lease, failover_message_t *msg) { binding_state_t local_state; TIME msg_cltt; if (lease->binding_state != lease->desired_binding_state) local_state = lease->desired_binding_state; else local_state = lease->binding_state; if ((msg->options_present & FTB_CLTT) != 0) msg_cltt = msg->cltt; else msg_cltt = 0; switch(local_state) { case FTS_ACTIVE: if (msg->binding_status == FTS_ACTIVE) { if (msg_cltt < lease->cltt) return ISC_TRUE; else if (msg_cltt > lease->cltt) return ISC_FALSE; else if (state->i_am == primary) return ISC_TRUE; else return ISC_FALSE; } else if (msg->binding_status == FTS_EXPIRED) { return ISC_FALSE; } /* FALL THROUGH */ case FTS_FREE: case FTS_BACKUP: case FTS_EXPIRED: case FTS_RELEASED: case FTS_ABANDONED: case FTS_RESET: if (msg->binding_status == FTS_ACTIVE) return ISC_FALSE; else if (state->i_am == primary) return ISC_TRUE; else return ISC_FALSE; /* FALL THROUGH to impossible condition */ default: log_fatal("Impossible condition at %s:%d.", MDL); } log_fatal("Impossible condition at %s:%d.", MDL); /* Silence compiler warning. */ return ISC_FALSE; } isc_result_t dhcp_failover_process_bind_update (dhcp_failover_state_t *state, failover_message_t *msg) { struct lease *lt = NULL, *lease = NULL; struct iaddr ia; int reason = FTR_MISC_REJECT; const char *message; int new_binding_state; int send_to_backup = 0; int required_options; isc_boolean_t chaddr_changed = ISC_FALSE; isc_boolean_t ident_changed = ISC_FALSE; /* Validate the binding update. */ required_options = FTB_ASSIGNED_IP_ADDRESS | FTB_BINDING_STATUS; if ((msg->options_present & required_options) != required_options) { message = "binding update lacks required options"; reason = FTR_MISSING_BINDINFO; goto bad; } ia.len = sizeof msg -> assigned_addr; memcpy (ia.iabuf, &msg -> assigned_addr, ia.len); if (!find_lease_by_ip_addr (&lease, ia, MDL)) { message = "unknown IP address"; reason = FTR_ILLEGAL_IP_ADDR; goto bad; } /* * If this lease is covered by a different failover peering * relationship, assert an error. */ if ((lease->pool == NULL) || (lease->pool->failover_peer == NULL) || (lease->pool->failover_peer != state)) { message = "IP address is covered by a different failover " "relationship state"; reason = FTR_ILLEGAL_IP_ADDR; goto bad; } /* * Dueling updates: This happens when both servers send a BNDUPD * at the same time. We want the best update to win, which means * we reject if we think ours is better, or cancel if we think the * peer's is better. We only assert a problem if the lease is on * the ACK queue, not on the UPDATE queue. This means that after * accepting this server's BNDUPD, we will send our own BNDUPD * /after/ sending the BNDACK (this order was recently enforced in * queue processing). */ if ((lease->flags & ON_ACK_QUEUE) != 0) { if (failover_lease_is_better(state, lease, msg)) { message = "incoming update is less critical than " "outgoing update"; reason = FTR_LESS_CRIT_BIND_INFO; goto bad; } else { /* This makes it so we ignore any spurious ACKs. */ dhcp_failover_ack_queue_remove(state, lease); } } /* Install the new info. Start by taking a copy to markup. */ if (!lease_copy (<, lease, MDL)) { message = "no memory"; goto bad; } if (msg -> options_present & FTB_CHADDR) { if (msg->binding_status == FTS_ABANDONED) { message = "BNDUPD to ABANDONED with a CHADDR"; goto bad; } if (msg -> chaddr.count > sizeof lt -> hardware_addr.hbuf) { message = "chaddr too long"; goto bad; } if ((lt->hardware_addr.hlen != msg->chaddr.count) || (memcmp(lt->hardware_addr.hbuf, msg->chaddr.data, msg->chaddr.count) != 0)) chaddr_changed = ISC_TRUE; lt -> hardware_addr.hlen = msg -> chaddr.count; memcpy (lt -> hardware_addr.hbuf, msg -> chaddr.data, msg -> chaddr.count); } else if (msg->binding_status == FTS_ACTIVE || msg->binding_status == FTS_EXPIRED || msg->binding_status == FTS_RELEASED) { message = "BNDUPD without CHADDR"; reason = FTR_MISSING_BINDINFO; goto bad; } else if (msg->binding_status == FTS_ABANDONED) { chaddr_changed = ISC_TRUE; lt->hardware_addr.hlen = 0; if (lt->scope) binding_scope_dereference(<->scope, MDL); } /* There is no explicit message content to indicate that the client * supplied no client-identifier. So if we don't hear of a value, * we discard the last one. */ if (msg->options_present & FTB_CLIENT_IDENTIFIER) { if (msg->binding_status == FTS_ABANDONED) { message = "BNDUPD to ABANDONED with client-id"; goto bad; } if ((lt->uid_len != msg->client_identifier.count) || (lt->uid == NULL) || /* Sanity; should never happen. */ (memcmp(lt->uid, msg->client_identifier.data, lt->uid_len) != 0)) ident_changed = ISC_TRUE; lt->uid_len = msg->client_identifier.count; /* Allocate the lt->uid buffer if we haven't already, or * re-allocate the lt-uid buffer if we have one that is not * large enough. Otherwise, just use the extant buffer. */ if (!lt->uid || lt->uid == lt->uid_buf || lt->uid_len > lt->uid_max) { if (lt->uid && lt->uid != lt->uid_buf) dfree(lt->uid, MDL); if (lt->uid_len > sizeof(lt->uid_buf)) { lt->uid_max = lt->uid_len; lt->uid = dmalloc(lt->uid_len, MDL); if (!lt->uid) { message = "no memory"; goto bad; } } else { lt->uid_max = sizeof(lt->uid_buf); lt->uid = lt->uid_buf; } } memcpy (lt -> uid, msg -> client_identifier.data, lt -> uid_len); } else if (lt->uid && msg->binding_status != FTS_RESET && msg->binding_status != FTS_FREE && msg->binding_status != FTS_BACKUP) { ident_changed = ISC_TRUE; if (lt->uid != lt->uid_buf) dfree (lt->uid, MDL); lt->uid = NULL; lt->uid_max = lt->uid_len = 0; } /* * A server's configuration can assign a 'binding scope'; * * set var = "value"; * * The problem with these binding scopes is that they are refreshed * when the server processes a client's DHCP packet. A local binding * scope is trash, then, when the lease has been assigned by the * partner server. There is no real way to detect this, a peer may * be updating us (as through potential conflict) with a binding we * sent them, but we can trivially detect the /problematic/ case; * * lease is free. * primary allocates lease to client A, assigns ddns name A. * primary fails. * secondary enters partner down. * lease expires, and is set free. * lease is allocated to client B and given ddns name B. * primary recovers. * * The binding update in this case will be active->active, but the * client identification on the lease will have changed. The ddns * update on client A will have leaked if we just remove the binding * scope blindly. */ if (msg->binding_status == FTS_ACTIVE && (chaddr_changed || ident_changed)) { #if defined (NSUPDATE) (void) ddns_removals(lease, NULL, NULL, ISC_FALSE); #endif /* NSUPDATE */ if (lease->scope != NULL) binding_scope_dereference(&lease->scope, MDL); } /* XXX Times may need to be adjusted based on clock skew! */ if (msg -> options_present & FTB_STOS) { lt -> starts = msg -> stos; } if (msg -> options_present & FTB_LEASE_EXPIRY) { lt -> ends = msg -> expiry; } if (msg->options_present & FTB_POTENTIAL_EXPIRY) { lt->atsfp = lt->tsfp = msg->potential_expiry; } if (msg->options_present & FTB_IP_FLAGS) { if (msg->ip_flags & FTF_IP_FLAG_RESERVE) { if ((((state->i_am == primary) && (lease->binding_state == FTS_FREE)) || ((state->i_am == secondary) && (lease->binding_state == FTS_BACKUP))) && !(lease->flags & RESERVED_LEASE)) { message = "Address is not reserved."; reason = FTR_IP_NOT_RESERVED; goto bad; } lt->flags |= RESERVED_LEASE; } else lt->flags &= ~RESERVED_LEASE; if (msg->ip_flags & FTF_IP_FLAG_BOOTP) { if ((((state->i_am == primary) && (lease->binding_state == FTS_FREE)) || ((state->i_am == secondary) && (lease->binding_state == FTS_BACKUP))) && !(lease->flags & BOOTP_LEASE)) { message = "Address is not allocated to BOOTP."; goto bad; } lt->flags |= BOOTP_LEASE; } else lt->flags &= ~BOOTP_LEASE; if (msg->ip_flags & ~(FTF_IP_FLAG_RESERVE | FTF_IP_FLAG_BOOTP)) log_info("Unknown IP-flags set in BNDUPD (0x%x).", msg->ip_flags); } else /* Flags may only not appear if the values are zero. */ lt->flags &= ~(RESERVED_LEASE | BOOTP_LEASE); #if defined (DEBUG_LEASE_STATE_TRANSITIONS) log_info ("processing state transition for %s: %s to %s", piaddr (lease -> ip_addr), binding_state_print (lease -> binding_state), binding_state_print (msg -> binding_status)); #endif /* If we're in normal state, make sure the state transition we got is valid. */ if (state -> me.state == normal) { new_binding_state = (normal_binding_state_transition_check (lease, state, msg -> binding_status, msg -> potential_expiry)); /* XXX if the transition the peer asked for isn't XXX allowed, maybe we should make the transition XXX into potential-conflict at this point. */ } else { new_binding_state = (conflict_binding_state_transition_check (lease, state, msg -> binding_status, msg -> potential_expiry)); } if (new_binding_state != msg -> binding_status) { char outbuf [100]; if (snprintf (outbuf, sizeof outbuf, "%s: invalid state transition: %s to %s", piaddr (lease -> ip_addr), binding_state_print (lease -> binding_state), binding_state_print (msg -> binding_status)) >= sizeof outbuf) log_fatal ("%s: impossible outbuf overflow", "dhcp_failover_process_bind_update"); dhcp_failover_send_bind_ack (state, msg, FTR_FATAL_CONFLICT, outbuf); goto out; } if (new_binding_state == FTS_EXPIRED || new_binding_state == FTS_RELEASED || new_binding_state == FTS_RESET) { lt -> next_binding_state = FTS_FREE; /* Mac address affinity. Assign the lease to * BACKUP state if we are the primary and the * peer is more likely to reallocate this lease * to a returning client. */ if ((state->i_am == primary) && !(lt->flags & (RESERVED_LEASE | BOOTP_LEASE))) send_to_backup = peer_wants_lease(lt); } else { lt -> next_binding_state = new_binding_state; } msg -> binding_status = lt -> next_binding_state; /* * If we accept a peer's binding update, then we can't rewind a * lease behind the peer's state. */ lease->rewind_binding_state = lt->next_binding_state; /* Try to install the new information. */ if (!supersede_lease (lease, lt, 0, 0, 0, 0) || !write_lease (lease)) { message = "database update failed"; bad: dhcp_failover_send_bind_ack (state, msg, reason, message); goto out; } else { dhcp_failover_queue_ack (state, msg); } /* If it is probably wise, assign lease to backup state if the peer * is not already hoarding leases. */ if (send_to_backup && secondary_not_hoarding(state, lease->pool)) { lease->next_binding_state = FTS_BACKUP; lease->tstp = cur_time; lease->starts = cur_time; if (!supersede_lease(lease, NULL, 0, 1, 0, 0) || !write_lease(lease)) log_error("can't commit lease %s for mac addr " "affinity", piaddr(lease->ip_addr)); dhcp_failover_send_updates(state); } out: if (lt) lease_dereference (<, MDL); if (lease) lease_dereference (&lease, MDL); return ISC_R_SUCCESS; } /* This was hairy enough I didn't want to do it all in an if statement. * * Returns: Truth is the secondary is allowed to get more leases based upon * MAC address affinity. False otherwise. */ static inline int secondary_not_hoarding(dhcp_failover_state_t *state, struct pool *p) { int total; int hold; int lts; total = p->free_leases + p->backup_leases; /* How many leases is one side or the other allowed to "hold"? */ hold = ((total * state->max_lease_ownership) + 50) / 100; /* If we were to send leases (or if the secondary were to send us * leases in the negative direction), how many would that be? */ lts = (p->free_leases - p->backup_leases) / 2; /* The peer is not hoarding leases if we would send them more leases * (or they would take fewer leases) than the maximum they are allowed * to hold (the negative hold). */ return(lts > -hold); } isc_result_t dhcp_failover_process_bind_ack (dhcp_failover_state_t *state, failover_message_t *msg) { struct lease *lease = NULL; struct iaddr ia; const char *message = "no memory"; u_int32_t pot_expire; int send_to_backup = ISC_FALSE; struct timeval tv; ia.len = sizeof msg -> assigned_addr; memcpy (ia.iabuf, &msg -> assigned_addr, ia.len); if (!find_lease_by_ip_addr (&lease, ia, MDL)) { message = "no such lease"; goto bad; } /* XXX check for conflicts. */ if (msg -> options_present & FTB_REJECT_REASON) { log_error ("bind update on %s from %s rejected: %.*s", piaddr (ia), state -> name, (int)((msg -> options_present & FTB_MESSAGE) ? msg -> message.count : strlen (dhcp_failover_reject_reason_print (msg -> reject_reason))), (msg -> options_present & FTB_MESSAGE) ? (const char *)(msg -> message.data) : (dhcp_failover_reject_reason_print (msg -> reject_reason))); goto unqueue; } /* Silently discard acks for leases we did not update (or multiple * acks). */ if (!lease->last_xid) goto unqueue; if (lease->last_xid != msg->xid) { message = "xid mismatch"; goto bad; } /* XXX Times may need to be adjusted based on clock skew! */ if (msg->options_present & FTO_POTENTIAL_EXPIRY) pot_expire = msg->potential_expiry; else pot_expire = lease->tstp; /* If the lease was desired to enter a binding state, we set * such a value upon transmitting a bndupd. We do not clear it * if we receive a bndupd in the meantime (or change the state * of the lease again ourselves), but we do set binding_state * if we get a bndupd. * * So desired_binding_state tells us what we sent a bndupd for, * and binding_state tells us what we have since determined in * the meantime. */ if (lease->desired_binding_state == FTS_EXPIRED || lease->desired_binding_state == FTS_RESET || lease->desired_binding_state == FTS_RELEASED) { /* It is not a problem to do this directly as we call * supersede_lease immediately after: the lease is requeued * even if its sort order (tsfp) has changed. */ lease->atsfp = lease->tsfp = pot_expire; if ((state->i_am == secondary) && (lease->flags & RESERVED_LEASE)) lease->next_binding_state = FTS_BACKUP; else lease->next_binding_state = FTS_FREE; /* Clear this condition for the next go-round. */ lease->desired_binding_state = lease->next_binding_state; /* The peer will have made this state change, so set rewind. */ lease->rewind_binding_state = lease->next_binding_state; supersede_lease(lease, NULL, 0, 0, 0, 0); write_lease(lease); /* Lease has returned to FREE state from the * transitional states. If the lease 'belongs' * to a client that would be served by the * peer, process a binding update now to send * the lease to backup state. But not if we * think we already have. */ if (state->i_am == primary && !(lease->flags & (RESERVED_LEASE | BOOTP_LEASE)) && peer_wants_lease(lease)) send_to_backup = ISC_TRUE; if (!send_to_backup && state->me.state == normal) commit_leases(); } else { /* XXX It could be a problem to do this directly if the lease * XXX is sorted by tsfp. */ lease->atsfp = lease->tsfp = pot_expire; if (lease->desired_binding_state != lease->binding_state) { lease->next_binding_state = lease->desired_binding_state; supersede_lease(lease, NULL, 0, 0, 0, 0); } write_lease(lease); /* Commit the lease only after a two-second timeout, so that if we get a bunch of acks in quick succession (e.g., when stealing leases from the secondary), we do not do an immediate commit for each one. */ tv.tv_sec = cur_time + 2; tv.tv_usec = 0; add_timeout(&tv, commit_leases_timeout, (void *)0, 0, 0); } unqueue: dhcp_failover_ack_queue_remove (state, lease); /* If we are supposed to send an update done after we send this lease, go ahead and send it. */ if (state -> send_update_done == lease) { lease_dereference (&state -> send_update_done, MDL); dhcp_failover_send_update_done (state); } /* Now that the lease is off the ack queue, consider putting it * back on the update queue for mac address affinity. */ if (send_to_backup && secondary_not_hoarding(state, lease->pool)) { lease->next_binding_state = FTS_BACKUP; lease->tstp = lease->starts = cur_time; if (!supersede_lease(lease, NULL, 0, 1, 0, 0) || !write_lease(lease)) log_error("can't commit lease %s for " "client affinity", piaddr(lease->ip_addr)); if (state->me.state == normal) commit_leases(); } /* If there are updates pending, we've created space to send at least one. */ dhcp_failover_send_updates (state); out: lease_dereference (&lease, MDL); return ISC_R_SUCCESS; bad: log_info ("bind update on %s got ack from %s: %s.", piaddr (ia), state -> name, message); goto out; } isc_result_t dhcp_failover_generate_update_queue (dhcp_failover_state_t *state, int everythingp) { struct shared_network *s; struct pool *p; struct lease *l; int i; #define FREE_LEASES 0 #define ACTIVE_LEASES 1 #define EXPIRED_LEASES 2 #define ABANDONED_LEASES 3 #define BACKUP_LEASES 4 #define RESERVED_LEASES 5 LEASE_STRUCT_PTR lptr[RESERVED_LEASES+1]; /* Loop through each pool in each shared network and call the expiry routine on the pool. */ for (s = shared_networks; s; s = s -> next) { for (p = s -> pools; p; p = p -> next) { if (p->failover_peer != state) continue; lptr[FREE_LEASES] = &p->free; lptr[ACTIVE_LEASES] = &p->active; lptr[EXPIRED_LEASES] = &p->expired; lptr[ABANDONED_LEASES] = &p->abandoned; lptr[BACKUP_LEASES] = &p->backup; lptr[RESERVED_LEASES] = &p->reserved; for (i = FREE_LEASES; i <= RESERVED_LEASES; i++) { for (l = LEASE_GET_FIRSTP(lptr[i]); l != NULL; l = LEASE_GET_NEXTP(lptr[i], l)) { if ((l->flags & ON_QUEUE) == 0 && (everythingp || (l->tstp > l->atsfp) || (i == EXPIRED_LEASES))) { l -> desired_binding_state = l -> binding_state; dhcp_failover_queue_update (l, 0); } } } } } return ISC_R_SUCCESS; } isc_result_t dhcp_failover_process_update_request (dhcp_failover_state_t *state, failover_message_t *msg) { if (state->send_update_done) { log_info("Received update request while old update still " "flying! Silently discarding old request."); lease_dereference(&state->send_update_done, MDL); } /* Generate a fresh update queue. */ dhcp_failover_generate_update_queue (state, 0); state->updxid = msg->xid; /* If there's anything on the update queue (there shouldn't be anything on the ack queue), trigger an update done message when we get an ack for that lease. */ if (state -> update_queue_tail) { lease_reference (&state -> send_update_done, state -> update_queue_tail, MDL); dhcp_failover_send_updates (state); log_info ("Update request from %s: sending update", state -> name); } else { /* Otherwise, there are no updates to send, so we can just send an UPDDONE message immediately. */ dhcp_failover_send_update_done (state); log_info ("Update request from %s: nothing pending", state -> name); } return ISC_R_SUCCESS; } isc_result_t dhcp_failover_process_update_request_all (dhcp_failover_state_t *state, failover_message_t *msg) { if (state->send_update_done) { log_info("Received update request while old update still " "flying! Silently discarding old request."); lease_dereference(&state->send_update_done, MDL); } /* Generate a fresh update queue that includes every lease. */ dhcp_failover_generate_update_queue (state, 1); state->updxid = msg->xid; if (state -> update_queue_tail) { lease_reference (&state -> send_update_done, state -> update_queue_tail, MDL); dhcp_failover_send_updates (state); log_info ("Update request all from %s: sending update", state -> name); } else { /* This should really never happen, but it could happen on a server that currently has no leases configured. */ dhcp_failover_send_update_done (state); log_info ("Update request all from %s: nothing pending", state -> name); } return ISC_R_SUCCESS; } isc_result_t dhcp_failover_process_update_done (dhcp_failover_state_t *state, failover_message_t *msg) { struct timeval tv; log_info ("failover peer %s: peer update completed.", state -> name); state -> curUPD = 0; switch (state -> me.state) { case unknown_state: case partner_down: case normal: case communications_interrupted: case resolution_interrupted: case shut_down: case paused: case recover_done: case startup: case recover_wait: break; /* shouldn't happen. */ /* We got the UPDDONE, so we can go into normal state! */ case potential_conflict: if (state->partner.state == conflict_done) { if (state->i_am == secondary) { dhcp_failover_set_state (state, normal); } else { log_error("Secondary is in conflict_done " "state after conflict resolution, " "this is illegal."); dhcp_failover_set_state (state, shut_down); } } else { if (state->i_am == primary) dhcp_failover_set_state (state, conflict_done); else log_error("Spurious update-done message."); } break; case conflict_done: log_error("Spurious update-done message."); break; case recover: /* Wait for MCLT to expire before moving to recover_done, except that if both peers come up in recover, there is no point in waiting for MCLT to expire - this probably indicates the initial startup of a newly-configured failover pair. */ if (state -> me.stos + state -> mclt > cur_time && state -> partner.state != recover && state -> partner.state != recover_done) { dhcp_failover_set_state (state, recover_wait); #if defined (DEBUG_FAILOVER_TIMING) log_info ("add_timeout +%d %s", (int)(cur_time - state -> me.stos + state -> mclt), "dhcp_failover_recover_done"); #endif tv . tv_sec = (int)(state -> me.stos + state -> mclt); tv . tv_usec = 0; add_timeout (&tv, dhcp_failover_recover_done, state, (tvref_t)omapi_object_reference, (tvunref_t) omapi_object_dereference); } else dhcp_failover_recover_done (state); } return ISC_R_SUCCESS; } void dhcp_failover_recover_done (void *sp) { dhcp_failover_state_t *state = sp; #if defined (DEBUG_FAILOVER_TIMING) log_info ("dhcp_failover_recover_done"); #endif dhcp_failover_set_state (state, recover_done); } #if defined (DEBUG_FAILOVER_MESSAGES) /* Print hunks of failover messages, doing line breaks as appropriate. Note that this assumes syslog is being used, rather than, e.g., the Windows NT logging facility, where just dumping the whole message in one hunk would be more appropriate. */ void failover_print (char *obuf, unsigned *obufix, unsigned obufmax, const char *s) { int len = strlen (s); while (len + *obufix + 1 >= obufmax) { log_debug ("%s", obuf); if (!*obufix) { log_debug ("%s", s); *obufix = 0; return; } *obufix = 0; } strcpy (&obuf [*obufix], s); *obufix += len; } #endif /* defined (DEBUG_FAILOVER_MESSAGES) */ /* Taken from draft-ietf-dhc-loadb-01.txt: */ /* A "mixing table" of 256 distinct values, in pseudo-random order. */ unsigned char loadb_mx_tbl[256] = { 251, 175, 119, 215, 81, 14, 79, 191, 103, 49, 181, 143, 186, 157, 0, 232, 31, 32, 55, 60, 152, 58, 17, 237, 174, 70, 160, 144, 220, 90, 57, 223, 59, 3, 18, 140, 111, 166, 203, 196, 134, 243, 124, 95, 222, 179, 197, 65, 180, 48, 36, 15, 107, 46, 233, 130, 165, 30, 123, 161, 209, 23, 97, 16, 40, 91, 219, 61, 100, 10, 210, 109, 250, 127, 22, 138, 29, 108, 244, 67, 207, 9, 178, 204, 74, 98, 126, 249, 167, 116, 34, 77, 193, 200, 121, 5, 20, 113, 71, 35, 128, 13, 182, 94, 25, 226, 227, 199, 75, 27, 41, 245, 230, 224, 43, 225, 177, 26, 155, 150, 212, 142, 218, 115, 241, 73, 88, 105, 39, 114, 62, 255, 192, 201, 145, 214, 168, 158, 221, 148, 154, 122, 12, 84, 82, 163, 44, 139, 228, 236, 205, 242, 217, 11, 187, 146, 159, 64, 86, 239, 195, 42, 106, 198, 118, 112, 184, 172, 87, 2, 173, 117, 176, 229, 247, 253, 137, 185, 99, 164, 102, 147, 45, 66, 231, 52, 141, 211, 194, 206, 246, 238, 56, 110, 78, 248, 63, 240, 189, 93, 92, 51, 53, 183, 19, 171, 72, 50, 33, 104, 101, 69, 8, 252, 83, 120, 76, 135, 85, 54, 202, 125, 188, 213, 96, 235, 136, 208, 162, 129, 190, 132, 156, 38, 47, 1, 7, 254, 24, 4, 216, 131, 89, 21, 28, 133, 37, 153, 149, 80, 170, 68, 6, 169, 234, 151 }; static unsigned char loadb_p_hash (const unsigned char *, unsigned); static unsigned char loadb_p_hash (const unsigned char *key, unsigned len) { unsigned char hash = len; int i; for(i = len; i > 0; ) hash = loadb_mx_tbl [hash ^ (key [--i])]; return hash; } int load_balance_mine (struct packet *packet, dhcp_failover_state_t *state) { struct option_cache *oc; struct data_string ds; unsigned char hbaix; int hm; u_int16_t ec; ec = ntohs(packet->raw->secs); /* * If desired check to see if the secs field may have been byte * swapped. We assume it has if the high order byte isn't cleared * while the low order byte is cleared. In this case we swap the * bytes and continue processing. */ if ((check_secs_byte_order == 1) && ((ec > 255) && ((ec & 0xff) == 0))) { ec = (ec >> 8) | (ec << 8); } if ((state->load_balance_max_secs == 0) || (state->load_balance_max_secs < ec)) { return (1); } /* If we don't have a hash bucket array, we can't tell if this one's ours, so we assume it's not. */ if (!state->hba) return (0); oc = lookup_option(&dhcp_universe, packet->options, DHO_DHCP_CLIENT_IDENTIFIER); memset(&ds, 0, sizeof ds); if (oc && evaluate_option_cache(&ds, packet, NULL, NULL, packet->options, NULL, &global_scope, oc, MDL)) { hbaix = loadb_p_hash(ds.data, ds.len); data_string_forget(&ds, MDL); } else { hbaix = loadb_p_hash(packet->raw->chaddr, packet->raw->hlen); } hm = state->hba[(hbaix >> 3) & 0x1F] & (1 << (hbaix & 0x07)); if (state->i_am == primary) return (hm); else return (!hm); } /* The inverse of load_balance_mine ("load balance theirs"). We can't * use the regular load_balance_mine() and invert it because of the case * where there might not be an HBA, and we want to indicate false here * in this case only. */ int peer_wants_lease(struct lease *lp) { dhcp_failover_state_t *state; unsigned char hbaix; int hm; if (!lp->pool) return 0; state = lp->pool->failover_peer; if (!state || !state->hba) return 0; if (lp->uid_len) hbaix = loadb_p_hash(lp->uid, lp->uid_len); else if (lp->hardware_addr.hlen > 1) /* Skip the first byte, which is the hardware type, and is * not included during actual load balancing checks above * since it is separate from the packet header chaddr field. * The remainder of the hardware address should be identical * to the chaddr contents. */ hbaix = loadb_p_hash(lp->hardware_addr.hbuf + 1, lp->hardware_addr.hlen - 1); else /* impossible to categorize into LBA */ return 0; hm = state->hba[(hbaix >> 3) & 0x1F] & (1 << (hbaix & 0x07)); if (state->i_am == primary) return !hm; else return hm; } /* This deals with what to do with bind updates when we're in the normal state Note that tsfp had better be set from the latest bind update _before_ this function is called! */ binding_state_t normal_binding_state_transition_check (struct lease *lease, dhcp_failover_state_t *state, binding_state_t binding_state, u_int32_t tsfp) { binding_state_t new_state; /* If there is no transition, it's no problem. */ if (binding_state == lease -> binding_state) return binding_state; switch (lease -> binding_state) { case FTS_FREE: case FTS_ABANDONED: switch (binding_state) { case FTS_ACTIVE: case FTS_ABANDONED: case FTS_BACKUP: case FTS_EXPIRED: case FTS_RELEASED: case FTS_RESET: /* If the lease was free, and our peer is primary, then it can make it active, or abandoned, or backup. Abandoned is treated like free in this case. */ if (state -> i_am == secondary) return binding_state; /* Otherwise, it can't legitimately do any sort of state transition. Because the lease was free, and the error has already been made, we allow the peer to change its state anyway, but log a warning message in hopes that the error will be fixed. */ case FTS_FREE: /* for compiler */ new_state = binding_state; goto out; default: log_fatal ("Impossible case at %s:%d.", MDL); return FTS_RESET; } case FTS_ACTIVE: /* The secondary can't change the state of an active lease. */ if (state -> i_am == primary) { /* Except that the client may send the DHCPRELEASE to the secondary. We also allow for when the secondary gets a DECLINE and the primary does not.*/ if ((binding_state == FTS_RELEASED) || (binding_state == FTS_ABANDONED)) return binding_state; new_state = lease -> binding_state; goto out; } /* So this is only for transitions made by the primary: */ switch (binding_state) { case FTS_FREE: case FTS_BACKUP: /* Can't set a lease to free or backup until the peer agrees that it's expired. */ if (tsfp > cur_time) { new_state = lease -> binding_state; goto out; } return binding_state; case FTS_EXPIRED: /* XXX 65 should be the clock skew between the peers XXX plus a fudge factor. This code will result XXX in problems if MCLT is really short or the XXX max-lease-time is really short (less than the XXX fudge factor. */ if (lease -> ends - 65 > cur_time) { new_state = lease -> binding_state; goto out; } case FTS_RELEASED: case FTS_ABANDONED: case FTS_RESET: case FTS_ACTIVE: return binding_state; default: log_fatal ("Impossible case at %s:%d.", MDL); return FTS_RESET; } break; case FTS_EXPIRED: switch (binding_state) { case FTS_BACKUP: case FTS_FREE: /* Can't set a lease to free or backup until the peer agrees that it's expired. */ if (tsfp > cur_time) { new_state = lease -> binding_state; goto out; } return binding_state; case FTS_ACTIVE: case FTS_RELEASED: case FTS_ABANDONED: case FTS_RESET: case FTS_EXPIRED: return binding_state; default: log_fatal ("Impossible case at %s:%d.", MDL); return FTS_RESET; } case FTS_RELEASED: switch (binding_state) { case FTS_FREE: case FTS_BACKUP: /* These are invalid state transitions - should we prevent them? */ case FTS_EXPIRED: case FTS_ABANDONED: case FTS_RESET: case FTS_ACTIVE: case FTS_RELEASED: return binding_state; default: log_fatal ("Impossible case at %s:%d.", MDL); return FTS_RESET; } case FTS_RESET: switch (binding_state) { case FTS_FREE: case FTS_BACKUP: /* Can't set a lease to free or backup until the peer agrees that it's expired. */ if (tsfp > cur_time) { new_state = lease -> binding_state; goto out; } return binding_state; case FTS_ACTIVE: case FTS_EXPIRED: case FTS_RELEASED: case FTS_ABANDONED: case FTS_RESET: return binding_state; default: log_fatal ("Impossible case at %s:%d.", MDL); return FTS_RESET; } case FTS_BACKUP: switch (binding_state) { case FTS_ACTIVE: case FTS_ABANDONED: case FTS_EXPIRED: case FTS_RELEASED: case FTS_RESET: /* If the lease was in backup, and our peer is secondary, then it can make it active or abandoned. */ if (state -> i_am == primary) return binding_state; /* Either the primary or the secondary can reasonably move a lease from the backup state to the free state. */ case FTS_FREE: return binding_state; case FTS_BACKUP: new_state = lease -> binding_state; goto out; default: log_fatal ("Impossible case at %s:%d.", MDL); return FTS_RESET; } default: log_fatal ("Impossible case at %s:%d.", MDL); return FTS_RESET; } out: return new_state; } /* Determine whether the state transition is okay when we're potentially in conflict with the peer. */ binding_state_t conflict_binding_state_transition_check (struct lease *lease, dhcp_failover_state_t *state, binding_state_t binding_state, u_int32_t tsfp) { binding_state_t new_state; /* If there is no transition, it's no problem. */ if (binding_state == lease -> binding_state) new_state = binding_state; else { switch (lease -> binding_state) { /* If we think the lease is not in use, then the state into which the partner put it is just fine, whatever it is. */ case FTS_FREE: case FTS_ABANDONED: case FTS_EXPIRED: case FTS_RELEASED: case FTS_RESET: case FTS_BACKUP: new_state = binding_state; break; /* If we think the lease *is* in use, then we're not going to take the partner's change if the partner thinks it's free. */ case FTS_ACTIVE: switch (binding_state) { case FTS_FREE: case FTS_BACKUP: new_state = lease -> binding_state; break; case FTS_EXPIRED: /* If we don't agree about expiry, it's * invalid. 65 should allow for max * clock skew (60) plus some fudge. * XXX: should we refetch cur_time? */ if ((lease->ends - 65) > cur_time) new_state = lease->binding_state; else new_state = binding_state; break; /* RELEASED, RESET, and ABANDONED indicate * that our partner has information about * this lease that we did not witness. Our * partner wins. */ case FTS_RELEASED: case FTS_RESET: case FTS_ABANDONED: new_state = binding_state; break; default: log_fatal ("Impossible case at %s:%d.", MDL); return FTS_RESET; } break; default: log_fatal ("Impossible case at %s:%d.", MDL); return FTS_RESET; } } return new_state; } /* We can reallocate a lease under the following circumstances: (1) It belongs to us - it's FTS_FREE, and we're primary, or it's FTS_BACKUP, and we're secondary. (2) We're in partner_down, and the lease is not active, and we can be sure that the other server didn't make it active. We can only be sure that the server didn't make it active when we are in the partner_down state and one of the following two conditions holds: (a) in the case that the time sent from the peer is earlier than the time we entered the partner_down state, at least MCLT has gone by since we entered partner_down, or (b) in the case that the time sent from the peer is later than the time when we entered partner_down, the current time is later than the time sent from the peer by at least MCLT. */ int lease_mine_to_reallocate (struct lease *lease) { dhcp_failover_state_t *peer; if (lease && lease->pool && (peer = lease->pool->failover_peer)) { /* * In addition to the normal rules governing wether a server * is allowed to operate changes on a lease, the server is * allowed to operate on a lease from the standpoint of the * most conservative guess of the peer's state for this lease. */ switch (lease->binding_state) { case FTS_ACTIVE: /* ACTIVE leases may not be reallocated. */ return 0; case FTS_FREE: case FTS_ABANDONED: /* FREE leases may only be allocated by the primary, * unless the secondary is acting in partner_down * state and stos+mclt or tsfp+mclt has expired, * whichever is greater. * * ABANDONED are treated the same as FREE for all * purposes here. Note that servers will only try * for ABANDONED leases as a last resort anyway. */ if (peer -> i_am == primary) return 1; return(peer->service_state == service_partner_down && ((lease->tsfp < peer->me.stos) ? (peer->me.stos + peer->mclt < cur_time) : (lease->tsfp + peer->mclt < cur_time))); case FTS_RELEASED: case FTS_EXPIRED: /* * These leases are generally untouchable until the * peer acknowledges their state change. However, as * this is impossible if the peer is offline, the * failover protocol permits an 'optimization' to * rewind the lease to a previous state that the server * is allowed to operate on, if that was the state that * was last acknowledged by the peer. * * So if a lease was free, was allocated by this * server, and expired without ever being transmitted * to the peer, it can be returned to free and given * to any new client legally. */ if ((peer->i_am == primary) && (lease->rewind_binding_state == FTS_FREE)) return 1; if ((peer->i_am == secondary) && (lease->rewind_binding_state == FTS_BACKUP)) return 1; /* FALL THROUGH (released, expired, reset) */ case FTS_RESET: /* * Released, expired, and reset leases go onto the * 'expired' queue all together. Upon entry into * partner-down state, this queue of leases has their * tsfp values modified to equal stos+mclt, the point * at which the server is allowed to remove them from * these transitional states. * * Note that although tsfp has been possibly extended * past the actual tsfp we received from the peer, we * don't have to take any special action. Since tsfp * will be equal to the current time when the lease * transitions to free, tsfp will not be used to grant * lease-times longer than the MCLT to clients, which * is the only danger for this sort of modification. */ return((peer->service_state == service_partner_down) && (lease->tsfp < cur_time)); case FTS_BACKUP: /* Only the secondary may allocate BACKUP leases, * unless in partner_down state in which case at * least TSFP+MCLT or STOS+MCLT must have expired, * whichever is greater. */ if (peer->i_am == secondary) return 1; return((peer->service_state == service_partner_down) && ((lease->tsfp < peer->me.stos) ? (peer->me.stos + peer->mclt < cur_time) : (lease->tsfp + peer->mclt < cur_time))); default: /* All lease states appear above. */ log_fatal("Impossible case at %s:%d.", MDL); break; } return 0; } if (lease) return(lease->binding_state == FTS_FREE || lease->binding_state == FTS_BACKUP); else return 0; } static isc_result_t failover_message_reference (failover_message_t **mp, failover_message_t *m, const char *file, int line) { *mp = m; m -> refcnt++; return ISC_R_SUCCESS; } static isc_result_t failover_message_dereference (failover_message_t **mp, const char *file, int line) { failover_message_t *m; m = (*mp); m -> refcnt--; if (m -> refcnt == 0) { if (m -> next) failover_message_dereference (&m -> next, file, line); if (m -> chaddr.data) dfree (m -> chaddr.data, file, line); if (m -> client_identifier.data) dfree (m -> client_identifier.data, file, line); if (m -> hba.data) dfree (m -> hba.data, file, line); if (m -> message.data) dfree (m -> message.data, file, line); if (m -> relationship_name.data) dfree (m -> relationship_name.data, file, line); if (m -> reply_options.data) dfree (m -> reply_options.data, file, line); if (m -> request_options.data) dfree (m -> request_options.data, file, line); if (m -> vendor_class.data) dfree (m -> vendor_class.data, file, line); if (m -> vendor_options.data) dfree (m -> vendor_options.data, file, line); if (m -> ddns.data) dfree (m -> ddns.data, file, line); dfree (*mp, file, line); } *mp = 0; return ISC_R_SUCCESS; } OMAPI_OBJECT_ALLOC (dhcp_failover_state, dhcp_failover_state_t, dhcp_type_failover_state) OMAPI_OBJECT_ALLOC (dhcp_failover_listener, dhcp_failover_listener_t, dhcp_type_failover_listener) OMAPI_OBJECT_ALLOC (dhcp_failover_link, dhcp_failover_link_t, dhcp_type_failover_link) #endif /* defined (FAILOVER_PROTOCOL) */ const char *binding_state_print (enum failover_state state) { switch (state) { case FTS_FREE: return "free"; break; case FTS_ACTIVE: return "active"; break; case FTS_EXPIRED: return "expired"; break; case FTS_RELEASED: return "released"; break; case FTS_ABANDONED: return "abandoned"; break; case FTS_RESET: return "reset"; break; case FTS_BACKUP: return "backup"; break; default: return "unknown"; break; } } /*! * \brief Given a char pointer, return always return a printable value * * This function is intended to be used in within log statements, such that * its invocation only occurs if the logging level is enabled. * * \param value pointer the character to print * * \return If value is null, returns the string "", if it contains * non-printable bytes, returns the string "", * otherwise it returns a const pointer to value */ const char *printable(const char* value) { const char *print_value = ""; if (value) { if ((strlen (value) <= 64) && db_printable((unsigned char*)value)) { print_value = value; } else { print_value = ""; } } return (print_value); } /*! * \brief Remove information from a prior use of a lease * * Remove information from a lease that is not germain to lease affinity * * \param lease the lease to scrub */ void scrub_lease(struct lease* lease, const char *file, int line) { log_debug ("%s(%d):scrubbing lease for %s, hostname: %s", file, line, piaddr(lease->ip_addr), printable(lease->client_hostname)); if (lease->client_hostname) { dfree (lease->client_hostname, MDL); lease->client_hostname = (char *)0; } } dhcp-4.4.1/server/ldap.c000644 000765 000024 00000253373 13243301226 015322 0ustar00tmarkstaff000000 000000 /* ldap.c Routines for reading the configuration from LDAP */ /* * Copyright (c) 2010-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 2003-2006 Ntelos, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of The Internet Software Consortium nor the names * of its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * This LDAP module was written by Brian Masney . Its * development was sponsored by Ntelos, Inc. (www.ntelos.com). */ #include "dhcpd.h" #if defined(LDAP_CONFIGURATION) #include #include #include #include #include #if defined(HAVE_IFADDRS_H) #include #endif #include #if defined(LDAP_CASA_AUTH) #include "ldap_casa.h" #endif #if defined(LDAP_USE_GSSAPI) #include #include "ldap_krb_helper.h" #endif static LDAP * ld = NULL; static char *ldap_server = NULL, *ldap_username = NULL, *ldap_password = NULL, *ldap_base_dn = NULL, *ldap_dhcp_server_cn = NULL, *ldap_debug_file = NULL; static int ldap_port = LDAP_PORT, ldap_method = LDAP_METHOD_DYNAMIC, ldap_referrals = -1, ldap_debug_fd = -1, ldap_enable_retry = -1, ldap_init_retry = -1; #if defined (LDAP_USE_SSL) static int ldap_use_ssl = -1, /* try TLS if possible */ ldap_tls_reqcert = -1, ldap_tls_crlcheck = -1; static char *ldap_tls_ca_file = NULL, *ldap_tls_ca_dir = NULL, *ldap_tls_cert = NULL, *ldap_tls_key = NULL, *ldap_tls_ciphers = NULL, *ldap_tls_randfile = NULL; #endif #if defined (LDAP_USE_GSSAPI) static char *ldap_gssapi_keytab = NULL, *ldap_gssapi_principal = NULL; struct ldap_sasl_instance { char *sasl_mech; char *sasl_realm; char *sasl_authz_id; char *sasl_authc_id; char *sasl_password; }; static struct ldap_sasl_instance *ldap_sasl_inst = NULL; static int _ldap_sasl_interact(LDAP *ld, unsigned flags, void *defaults, void *sin) ; #endif static struct ldap_config_stack *ldap_stack = NULL; typedef struct ldap_dn_node { struct ldap_dn_node *next; size_t refs; char *dn; } ldap_dn_node; static ldap_dn_node *ldap_service_dn_head = NULL; static ldap_dn_node *ldap_service_dn_tail = NULL; static int ldap_read_function (struct parse *cfile); static struct parse * x_parser_init(const char *name) { struct parse *cfile; isc_result_t res; char *inbuf; inbuf = dmalloc (LDAP_BUFFER_SIZE, MDL); if (inbuf == NULL) return NULL; cfile = (struct parse *) NULL; res = new_parse (&cfile, -1, inbuf, LDAP_BUFFER_SIZE, name, 0); if (res != ISC_R_SUCCESS) { dfree(inbuf, MDL); return NULL; } /* the buffer is still empty */ cfile->bufsiz = LDAP_BUFFER_SIZE; cfile->buflen = cfile->bufix = 0; /* attach ldap read function */ cfile->read_function = ldap_read_function; return cfile; } static isc_result_t x_parser_free(struct parse **cfile) { if (cfile && *cfile) { if ((*cfile)->inbuf) dfree((*cfile)->inbuf, MDL); (*cfile)->inbuf = NULL; (*cfile)->bufsiz = 0; return end_parse(cfile); } return ISC_R_SUCCESS; } static int x_parser_resize(struct parse *cfile, size_t len) { size_t size; char * temp; /* grow by len rounded up at LDAP_BUFFER_SIZE */ size = cfile->bufsiz + (len | (LDAP_BUFFER_SIZE-1)) + 1; /* realloc would be better, but there isn't any */ if ((temp = dmalloc (size, MDL)) != NULL) { #if defined (DEBUG_LDAP) log_info ("Reallocated %s buffer from %zu to %zu", cfile->tlname, cfile->bufsiz, size); #endif memcpy(temp, cfile->inbuf, cfile->bufsiz); dfree(cfile->inbuf, MDL); cfile->inbuf = temp; cfile->bufsiz = size; return 1; } /* * Hmm... what is worser, consider it as fatal error and * bail out completely or discard config data in hope it * is "only" an option in dynamic host lookup? */ log_error("Unable to reallocated %s buffer from %zu to %zu", cfile->tlname, cfile->bufsiz, size); return 0; } static char * x_parser_strcat(struct parse *cfile, const char *str) { size_t cur = strlen(cfile->inbuf); size_t len = strlen(str); size_t cnt; if (cur + len >= cfile->bufsiz && !x_parser_resize(cfile, len)) return NULL; cnt = cfile->bufsiz > cur ? cfile->bufsiz - cur - 1 : 0; return strncat(cfile->inbuf, str, cnt); } static inline void x_parser_reset(struct parse *cfile) { cfile->inbuf[0] = '\0'; cfile->bufix = cfile->buflen = 0; } static inline size_t x_parser_length(struct parse *cfile) { cfile->buflen = strlen(cfile->inbuf); return cfile->buflen; } static char * x_strxform(char *dst, const char *src, size_t dst_size, int (*xform)(int)) { if(dst && src && dst_size) { size_t len, pos; len = strlen(src); for(pos=0; pos < len && pos + 1 < dst_size; pos++) dst[pos] = xform((int)src[pos]); dst[pos] = '\0'; return dst; } return NULL; } static int get_host_entry(char *fqdnname, size_t fqdnname_size, char *hostaddr, size_t hostaddr_size) { #if defined(MAXHOSTNAMELEN) char hname[MAXHOSTNAMELEN+1]; #else char hname[65]; #endif struct hostent *hp; if (NULL == fqdnname || 1 >= fqdnname_size) return -1; memset(hname, 0, sizeof(hname)); if (gethostname(hname, sizeof(hname)-1)) return -1; if (NULL == (hp = gethostbyname(hname))) return -1; strncpy(fqdnname, hp->h_name, fqdnname_size-1); fqdnname[fqdnname_size-1] = '\0'; if (hostaddr != NULL) { if (hp->h_addr != NULL) { struct in_addr *aptr = (struct in_addr *)hp->h_addr; #if defined(HAVE_INET_NTOP) if (hostaddr_size >= INET_ADDRSTRLEN && inet_ntop(AF_INET, aptr, hostaddr, hostaddr_size) != NULL) { return 0; } #else char *astr = inet_ntoa(*aptr); size_t alen = strlen(astr); if (astr && alen > 0 && hostaddr_size > alen) { strncpy(hostaddr, astr, hostaddr_size-1); hostaddr[hostaddr_size-1] = '\0'; return 0; } #endif } return -1; } return 0; } #if defined(HAVE_IFADDRS_H) static int is_iface_address(struct ifaddrs *addrs, struct in_addr *addr) { struct ifaddrs *ia; struct sockaddr_in *sa; int num = 0; if(addrs == NULL || addr == NULL) return -1; for (ia = addrs; ia != NULL; ia = ia->ifa_next) { ++num; if (ia->ifa_addr && (ia->ifa_flags & IFF_UP) && ia->ifa_addr->sa_family == AF_INET) { sa = (struct sockaddr_in *)(ia->ifa_addr); if (addr->s_addr == sa->sin_addr.s_addr) return num; } } return 0; } static int get_host_address(const char *hostname, char *hostaddr, size_t hostaddr_size, struct ifaddrs *addrs) { if (hostname && *hostname && hostaddr && hostaddr_size) { struct in_addr addr; #if defined(HAVE_INET_PTON) if (inet_pton(AF_INET, hostname, &addr) == 1) #else if (inet_aton(hostname, &addr) != 0) #endif { /* it is already IP address string */ if(strlen(hostname) < hostaddr_size) { strncpy(hostaddr, hostname, hostaddr_size-1); hostaddr[hostaddr_size-1] = '\0'; if (addrs != NULL && is_iface_address (addrs, &addr) > 0) return 1; else return 0; } } else { struct hostent *hp; if ((hp = gethostbyname(hostname)) != NULL && hp->h_addr != NULL) { struct in_addr *aptr = (struct in_addr *)hp->h_addr; int mret = 0; if (addrs != NULL) { char **h; for (h=hp->h_addr_list; *h; h++) { struct in_addr *haddr = (struct in_addr *)*h; if (is_iface_address (addrs, haddr) > 0) { aptr = haddr; mret = 1; } } } #if defined(HAVE_INET_NTOP) if (hostaddr_size >= INET_ADDRSTRLEN && inet_ntop(AF_INET, aptr, hostaddr, hostaddr_size) != NULL) { return mret; } #else char *astr = inet_ntoa(*aptr); size_t alen = strlen(astr); if (astr && alen > 0 && alen < hostaddr_size) { strncpy(hostaddr, astr, hostaddr_size-1); hostaddr[hostaddr_size-1] = '\0'; return mret; } #endif } } } return -1; } #endif /* HAVE_IFADDRS_H */ static void ldap_parse_class (struct ldap_config_stack *item, struct parse *cfile) { struct berval **tempbv; if ((tempbv = ldap_get_values_len (ld, item->ldent, "cn")) == NULL || tempbv[0] == NULL) { if (tempbv != NULL) ldap_value_free_len (tempbv); return; } x_parser_strcat (cfile, "class \""); x_parser_strcat (cfile, tempbv[0]->bv_val); x_parser_strcat (cfile, "\" {\n"); item->close_brace = 1; ldap_value_free_len (tempbv); } static int is_hex_string(const char *str) { int colon = 1; int xdigit = 0; size_t i; if (!str) return 0; if (*str == '-') str++; for (i=0; str[i]; ++i) { if (str[i] == ':') { xdigit = 0; if(++colon > 1) return 0; } else if(isxdigit((unsigned char)str[i])) { colon = 0; if (++xdigit > 2) return 0; } else return 0; } return i > 0 && !colon; } static void ldap_parse_subclass (struct ldap_config_stack *item, struct parse *cfile) { struct berval **tempbv, **classdata; char *tmp; if ((tempbv = ldap_get_values_len (ld, item->ldent, "cn")) == NULL || tempbv[0] == NULL) { if (tempbv != NULL) ldap_value_free_len (tempbv); return; } if ((classdata = ldap_get_values_len (ld, item->ldent, "dhcpClassData")) == NULL || classdata[0] == NULL) { if (classdata != NULL) ldap_value_free_len (classdata); ldap_value_free_len (tempbv); return; } x_parser_strcat (cfile, "subclass \""); x_parser_strcat (cfile, classdata[0]->bv_val); if (is_hex_string(tempbv[0]->bv_val)) { x_parser_strcat (cfile, "\" "); x_parser_strcat (cfile, tempbv[0]->bv_val); x_parser_strcat (cfile, " {\n"); } else { tmp = quotify_string(tempbv[0]->bv_val, MDL); x_parser_strcat (cfile, "\" \""); x_parser_strcat (cfile, tmp); x_parser_strcat (cfile, "\" {\n"); dfree(tmp, MDL); } item->close_brace = 1; ldap_value_free_len (tempbv); ldap_value_free_len (classdata); } static void ldap_parse_host (struct ldap_config_stack *item, struct parse *cfile) { struct berval **tempbv, **hwaddr; if ((tempbv = ldap_get_values_len (ld, item->ldent, "cn")) == NULL || tempbv[0] == NULL) { if (tempbv != NULL) ldap_value_free_len (tempbv); return; } hwaddr = ldap_get_values_len (ld, item->ldent, "dhcpHWAddress"); x_parser_strcat (cfile, "host "); x_parser_strcat (cfile, tempbv[0]->bv_val); x_parser_strcat (cfile, " {\n"); if (hwaddr != NULL) { if (hwaddr[0] != NULL) { x_parser_strcat (cfile, "hardware "); x_parser_strcat (cfile, hwaddr[0]->bv_val); x_parser_strcat (cfile, ";\n"); } ldap_value_free_len (hwaddr); } item->close_brace = 1; ldap_value_free_len (tempbv); } static void ldap_parse_shared_network (struct ldap_config_stack *item, struct parse *cfile) { struct berval **tempbv; if ((tempbv = ldap_get_values_len (ld, item->ldent, "cn")) == NULL || tempbv[0] == NULL) { if (tempbv != NULL) ldap_value_free_len (tempbv); return; } x_parser_strcat (cfile, "shared-network \""); x_parser_strcat (cfile, tempbv[0]->bv_val); x_parser_strcat (cfile, "\" {\n"); item->close_brace = 1; ldap_value_free_len (tempbv); } static void parse_netmask (int netmask, char *netmaskbuf) { unsigned long nm; int i; nm = 0; for (i=1; i <= netmask; i++) { nm |= 1 << (32 - i); } sprintf (netmaskbuf, "%d.%d.%d.%d", (int) (nm >> 24) & 0xff, (int) (nm >> 16) & 0xff, (int) (nm >> 8) & 0xff, (int) nm & 0xff); } static void ldap_parse_subnet (struct ldap_config_stack *item, struct parse *cfile) { struct berval **tempbv, **netmaskstr; char netmaskbuf[sizeof("255.255.255.255")]; int i; if ((tempbv = ldap_get_values_len (ld, item->ldent, "cn")) == NULL || tempbv[0] == NULL) { if (tempbv != NULL) ldap_value_free_len (tempbv); return; } if ((netmaskstr = ldap_get_values_len (ld, item->ldent, "dhcpNetmask")) == NULL || netmaskstr[0] == NULL) { if (netmaskstr != NULL) ldap_value_free_len (netmaskstr); ldap_value_free_len (tempbv); return; } x_parser_strcat (cfile, "subnet "); x_parser_strcat (cfile, tempbv[0]->bv_val); x_parser_strcat (cfile, " netmask "); parse_netmask (strtol (netmaskstr[0]->bv_val, NULL, 10), netmaskbuf); x_parser_strcat (cfile, netmaskbuf); x_parser_strcat (cfile, " {\n"); ldap_value_free_len (tempbv); ldap_value_free_len (netmaskstr); if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpRange")) != NULL) { for (i=0; tempbv[i] != NULL; i++) { x_parser_strcat (cfile, "range"); x_parser_strcat (cfile, " "); x_parser_strcat (cfile, tempbv[i]->bv_val); x_parser_strcat (cfile, ";\n"); } ldap_value_free_len (tempbv); } item->close_brace = 1; } static void ldap_parse_subnet6 (struct ldap_config_stack *item, struct parse *cfile) { struct berval **tempbv; int i; if ((tempbv = ldap_get_values_len (ld, item->ldent, "cn")) == NULL || tempbv[0] == NULL) { if (tempbv != NULL) ldap_value_free_len (tempbv); return; } x_parser_strcat (cfile, "subnet6 "); x_parser_strcat (cfile, tempbv[0]->bv_val); x_parser_strcat (cfile, " {\n"); ldap_value_free_len (tempbv); if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpRange6")) != NULL) { for (i=0; tempbv[i] != NULL; i++) { x_parser_strcat (cfile, "range6"); x_parser_strcat (cfile, " "); x_parser_strcat (cfile, tempbv[i]->bv_val); x_parser_strcat (cfile, ";\n"); } ldap_value_free_len (tempbv); } if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpPermitList")) != NULL) { for (i=0; tempbv[i] != NULL; i++) { x_parser_strcat (cfile, tempbv[i]->bv_val); x_parser_strcat (cfile, ";\n"); } ldap_value_free_len (tempbv); } item->close_brace = 1; } static void ldap_parse_pool (struct ldap_config_stack *item, struct parse *cfile) { struct berval **tempbv; int i; x_parser_strcat (cfile, "pool {\n"); if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpRange")) != NULL) { x_parser_strcat (cfile, "range"); for (i=0; tempbv[i] != NULL; i++) { x_parser_strcat (cfile, " "); x_parser_strcat (cfile, tempbv[i]->bv_val); } x_parser_strcat (cfile, ";\n"); ldap_value_free_len (tempbv); } if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpPermitList")) != NULL) { for (i=0; tempbv[i] != NULL; i++) { x_parser_strcat (cfile, tempbv[i]->bv_val); x_parser_strcat (cfile, ";\n"); } ldap_value_free_len (tempbv); } item->close_brace = 1; } static void ldap_parse_pool6 (struct ldap_config_stack *item, struct parse *cfile) { struct berval **tempbv; int i; x_parser_strcat (cfile, "pool6 {\n"); if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpRange6")) != NULL) { x_parser_strcat (cfile, "range6"); for (i=0; tempbv[i] != NULL; i++) { x_parser_strcat (cfile, " "); x_parser_strcat (cfile, tempbv[i]->bv_val); } x_parser_strcat (cfile, ";\n"); ldap_value_free_len (tempbv); } if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpPermitList")) != NULL) { for (i=0; tempbv[i] != NULL; i++) { x_parser_strcat(cfile, tempbv[i]->bv_val); x_parser_strcat (cfile, ";\n"); } ldap_value_free_len (tempbv); } item->close_brace = 1; } static void ldap_parse_group (struct ldap_config_stack *item, struct parse *cfile) { x_parser_strcat (cfile, "group {\n"); item->close_brace = 1; } static void ldap_parse_key (struct ldap_config_stack *item, struct parse *cfile) { struct berval **tempbv; if ((tempbv = ldap_get_values_len (ld, item->ldent, "cn")) != NULL) { x_parser_strcat (cfile, "key "); x_parser_strcat (cfile, tempbv[0]->bv_val); x_parser_strcat (cfile, " {\n"); ldap_value_free_len (tempbv); } if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpKeyAlgorithm")) != NULL) { x_parser_strcat (cfile, "algorithm "); x_parser_strcat (cfile, tempbv[0]->bv_val); x_parser_strcat (cfile, ";\n"); ldap_value_free_len (tempbv); } if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpKeySecret")) != NULL) { x_parser_strcat (cfile, "secret "); x_parser_strcat (cfile, tempbv[0]->bv_val); x_parser_strcat (cfile, ";\n"); ldap_value_free_len (tempbv); } item->close_brace = 1; } static void ldap_parse_zone (struct ldap_config_stack *item, struct parse *cfile) { char *cnFindStart, *cnFindEnd; struct berval **tempbv; char *keyCn; size_t len; if ((tempbv = ldap_get_values_len (ld, item->ldent, "cn")) != NULL) { x_parser_strcat (cfile, "zone "); x_parser_strcat (cfile, tempbv[0]->bv_val); x_parser_strcat (cfile, " {\n"); ldap_value_free_len (tempbv); } if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpDnsZoneServer")) != NULL) { x_parser_strcat (cfile, "primary "); x_parser_strcat (cfile, tempbv[0]->bv_val); x_parser_strcat (cfile, ";\n"); ldap_value_free_len (tempbv); } if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpKeyDN")) != NULL) { cnFindStart = strchr(tempbv[0]->bv_val,'='); if (cnFindStart != NULL) cnFindEnd = strchr(++cnFindStart,','); else cnFindEnd = NULL; if (cnFindEnd != NULL && cnFindEnd > cnFindStart) { len = cnFindEnd - cnFindStart; keyCn = dmalloc (len + 1, MDL); } else { len = 0; keyCn = NULL; } if (keyCn != NULL) { strncpy (keyCn, cnFindStart, len); keyCn[len] = '\0'; x_parser_strcat (cfile, "key "); x_parser_strcat (cfile, keyCn); x_parser_strcat (cfile, ";\n"); dfree (keyCn, MDL); } ldap_value_free_len (tempbv); } item->close_brace = 1; } #if defined(HAVE_IFADDRS_H) static void ldap_parse_failover (struct ldap_config_stack *item, struct parse *cfile) { struct berval **tempbv, **peername; struct ifaddrs *addrs = NULL; char srvaddr[2][64] = {"\0", "\0"}; int primary, split = 0, match; if ((peername = ldap_get_values_len (ld, item->ldent, "cn")) == NULL || peername[0] == NULL) { if (peername != NULL) ldap_value_free_len (peername); // ldap with disabled schema checks? fail to avoid syntax error. log_error("Unable to find mandatory failover peering name attribute"); return; } /* Get all interface addresses */ getifaddrs(&addrs); /* ** when dhcpFailOverPrimaryServer or dhcpFailOverSecondaryServer ** matches one of our IP address, the following valiables are set: ** - primary is 1 when we are primary or 0 when we are secondary ** - srvaddr[0] contains ip address of the primary ** - srvaddr[1] contains ip address of the secondary */ primary = -1; if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpFailOverPrimaryServer")) != NULL && tempbv[0] != NULL) { match = get_host_address (tempbv[0]->bv_val, srvaddr[0], sizeof(srvaddr[0]), addrs); if (match >= 0) { /* we are the primary */ if (match > 0) primary = 1; } else { log_info("Can't resolve address of the primary failover '%s' server %s", peername[0]->bv_val, tempbv[0]->bv_val); ldap_value_free_len (tempbv); ldap_value_free_len (peername); if (addrs) freeifaddrs(addrs); return; } } if (tempbv != NULL) ldap_value_free_len (tempbv); if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpFailOverSecondaryServer")) != NULL && tempbv[0] != NULL) { match = get_host_address (tempbv[0]->bv_val, srvaddr[1], sizeof(srvaddr[1]), addrs); if (match >= 0) { if (match > 0) { if (primary == 1) { log_info("Both, primary and secondary failover '%s' server" " attributes match our local address", peername[0]->bv_val); ldap_value_free_len (tempbv); ldap_value_free_len (peername); if (addrs) freeifaddrs(addrs); return; } /* we are the secondary */ primary = 0; } } else { log_info("Can't resolve address of the secondary failover '%s' server %s", peername[0]->bv_val, tempbv[0]->bv_val); ldap_value_free_len (tempbv); ldap_value_free_len (peername); if (addrs) freeifaddrs(addrs); return; } } if (tempbv != NULL) ldap_value_free_len (tempbv); if (primary == -1 || srvaddr[0] == '\0' || srvaddr[1] == '\0') { log_error("Could not decide if the server type is primary" " or secondary for failover peering '%s'.", peername[0]->bv_val); ldap_value_free_len (peername); if (addrs) freeifaddrs(addrs); return; } x_parser_strcat (cfile, "failover peer \""); x_parser_strcat (cfile, peername[0]->bv_val); x_parser_strcat (cfile, "\" {\n"); if (primary) x_parser_strcat (cfile, "primary;\n"); else x_parser_strcat (cfile, "secondary;\n"); x_parser_strcat (cfile, "address "); if (primary) x_parser_strcat (cfile, srvaddr[0]); else x_parser_strcat (cfile, srvaddr[1]); x_parser_strcat (cfile, ";\n"); x_parser_strcat (cfile, "peer address "); if (primary) x_parser_strcat (cfile, srvaddr[1]); else x_parser_strcat (cfile, srvaddr[0]); x_parser_strcat (cfile, ";\n"); if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpFailOverPrimaryPort")) != NULL && tempbv[0] != NULL) { if (primary) x_parser_strcat (cfile, "port "); else x_parser_strcat (cfile, "peer port "); x_parser_strcat (cfile, tempbv[0]->bv_val); x_parser_strcat (cfile, ";\n"); } if (tempbv != NULL) ldap_value_free_len (tempbv); if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpFailOverSecondaryPort")) != NULL && tempbv[0] != NULL) { if (primary) x_parser_strcat (cfile, "peer port "); else x_parser_strcat (cfile, "port "); x_parser_strcat (cfile, tempbv[0]->bv_val); x_parser_strcat (cfile, ";\n"); } if (tempbv != NULL) ldap_value_free_len (tempbv); if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpFailOverResponseDelay")) != NULL && tempbv[0] != NULL) { x_parser_strcat (cfile, "max-response-delay "); x_parser_strcat (cfile, tempbv[0]->bv_val); x_parser_strcat (cfile, ";\n"); } if (tempbv != NULL) ldap_value_free_len (tempbv); if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpFailOverUnackedUpdates")) != NULL && tempbv[0] != NULL) { x_parser_strcat (cfile, "max-unacked-updates "); x_parser_strcat (cfile, tempbv[0]->bv_val); x_parser_strcat (cfile, ";\n"); } if (tempbv != NULL) ldap_value_free_len (tempbv); if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpFailOverLoadBalanceTime")) != NULL && tempbv[0] != NULL) { x_parser_strcat (cfile, "load balance max seconds "); x_parser_strcat (cfile, tempbv[0]->bv_val); x_parser_strcat (cfile, ";\n"); } if (tempbv != NULL) ldap_value_free_len (tempbv); tempbv = NULL; if (primary && (tempbv = ldap_get_values_len (ld, item->ldent, "dhcpMaxClientLeadTime")) != NULL && tempbv[0] != NULL) { x_parser_strcat (cfile, "mclt "); x_parser_strcat (cfile, tempbv[0]->bv_val); x_parser_strcat (cfile, ";\n"); } if (tempbv != NULL) ldap_value_free_len (tempbv); tempbv = NULL; if (primary && (tempbv = ldap_get_values_len (ld, item->ldent, "dhcpFailOverSplit")) != NULL && tempbv[0] != NULL) { x_parser_strcat (cfile, "split "); x_parser_strcat (cfile, tempbv[0]->bv_val); x_parser_strcat (cfile, ";\n"); split = 1; } if (tempbv != NULL) ldap_value_free_len (tempbv); tempbv = NULL; if (primary && !split && (tempbv = ldap_get_values_len (ld, item->ldent, "dhcpFailOverHashBucketAssignment")) != NULL && tempbv[0] != NULL) { x_parser_strcat (cfile, "hba "); x_parser_strcat (cfile, tempbv[0]->bv_val); x_parser_strcat (cfile, ";\n"); } if (tempbv != NULL) ldap_value_free_len (tempbv); item->close_brace = 1; } #endif /* HAVE_IFADDRS_H */ static void add_to_config_stack (LDAPMessage * res, LDAPMessage * ent) { struct ldap_config_stack *ns; ns = dmalloc (sizeof (*ns), MDL); if (!ns) { log_fatal ("no memory for add_to_config_stack()"); } ns->res = res; ns->ldent = ent; ns->close_brace = 0; ns->processed = 0; ns->next = ldap_stack; ldap_stack = ns; } static void ldap_stop() { struct sigaction old, new; if (ld == NULL) return; /* ** ldap_unbind after a LDAP_SERVER_DOWN result ** causes a SIGPIPE and dhcpd gets terminated, ** since it doesn't handle it... */ new.sa_flags = 0; new.sa_handler = SIG_IGN; sigemptyset (&new.sa_mask); sigaction (SIGPIPE, &new, &old); ldap_unbind_ext_s (ld, NULL, NULL); ld = NULL; sigaction (SIGPIPE, &old, &new); } static char * _do_lookup_dhcp_string_option (struct option_state *options, int option_name) { struct option_cache *oc; struct data_string db; char *ret; memset (&db, 0, sizeof (db)); oc = lookup_option (&server_universe, options, option_name); if (oc && evaluate_option_cache (&db, (struct packet*) NULL, (struct lease *) NULL, (struct client_state *) NULL, options, (struct option_state *) NULL, &global_scope, oc, MDL) && db.data != NULL && *db.data != '\0') { ret = dmalloc (db.len + 1, MDL); if (ret == NULL) log_fatal ("no memory for ldap option %d value", option_name); memcpy (ret, db.data, db.len); ret[db.len] = 0; data_string_forget (&db, MDL); } else ret = NULL; return (ret); } static int _do_lookup_dhcp_int_option (struct option_state *options, int option_name) { struct option_cache *oc; struct data_string db; int ret; memset (&db, 0, sizeof (db)); oc = lookup_option (&server_universe, options, option_name); if (oc && evaluate_option_cache (&db, (struct packet*) NULL, (struct lease *) NULL, (struct client_state *) NULL, options, (struct option_state *) NULL, &global_scope, oc, MDL) && db.data != NULL && *db.data != '\0') { ret = strtol ((const char *) db.data, NULL, 10); data_string_forget (&db, MDL); } else ret = 0; return (ret); } static int _do_lookup_dhcp_enum_option (struct option_state *options, int option_name) { struct option_cache *oc; struct data_string db; int ret = -1; memset (&db, 0, sizeof (db)); oc = lookup_option (&server_universe, options, option_name); if (oc && evaluate_option_cache (&db, (struct packet*) NULL, (struct lease *) NULL, (struct client_state *) NULL, options, (struct option_state *) NULL, &global_scope, oc, MDL) && db.data != NULL && *db.data != '\0') { if (db.len == 1) ret = db.data [0]; else log_fatal ("invalid option name %d", option_name); data_string_forget (&db, MDL); } else ret = 0; return (ret); } int ldap_rebind_cb (LDAP *ld, LDAP_CONST char *url, ber_tag_t request, ber_int_t msgid, void *parms) { int ret; LDAPURLDesc *ldapurl = NULL; char *who = NULL; struct berval creds; log_info("LDAP rebind to '%s'", url); if ((ret = ldap_url_parse(url, &ldapurl)) != LDAP_SUCCESS) { log_error ("Error: Can not parse ldap rebind url '%s': %s", url, ldap_err2string(ret)); return ret; } #if defined (LDAP_USE_SSL) if (strcasecmp(ldapurl->lud_scheme, "ldaps") == 0) { int opt = LDAP_OPT_X_TLS_HARD; if ((ret = ldap_set_option (ld, LDAP_OPT_X_TLS, &opt)) != LDAP_SUCCESS) { log_error ("Error: Cannot init LDAPS session to %s:%d: %s", ldapurl->lud_host, ldapurl->lud_port, ldap_err2string (ret)); ldap_free_urldesc(ldapurl); return ret; } else { log_info ("LDAPS session successfully enabled to %s", ldap_server); } } else if (strcasecmp(ldapurl->lud_scheme, "ldap") == 0 && ldap_use_ssl != LDAP_SSL_OFF) { if ((ret = ldap_start_tls_s (ld, NULL, NULL)) != LDAP_SUCCESS) { log_error ("Error: Cannot start TLS session to %s:%d: %s", ldapurl->lud_host, ldapurl->lud_port, ldap_err2string (ret)); ldap_free_urldesc(ldapurl); return ret; } else { log_info ("TLS session successfully started to %s:%d", ldapurl->lud_host, ldapurl->lud_port); } } #endif #if defined(LDAP_USE_GSSAPI) if (ldap_gssapi_principal != NULL) { krb5_get_tgt(ldap_gssapi_principal, ldap_gssapi_keytab); if ((ret = ldap_sasl_interactive_bind_s(ld, NULL, ldap_sasl_inst->sasl_mech, NULL, NULL, LDAP_SASL_AUTOMATIC, _ldap_sasl_interact, ldap_sasl_inst) ) != LDAP_SUCCESS) { log_error ("Error: Cannot SASL bind to ldap server %s:%d: %s", ldap_server, ldap_port, ldap_err2string (ret)); char *msg=NULL; ldap_get_option( ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void*)&msg); log_error ("\tAdditional info: %s", msg); ldap_memfree(msg); ldap_stop(); } ldap_free_urldesc(ldapurl); return ret; } #endif if (ldap_username != NULL && *ldap_username != '\0' && ldap_password != NULL) { who = ldap_username; creds.bv_val = strdup(ldap_password); if (creds.bv_val == NULL) log_fatal ("Error: Unable to allocate memory to duplicate ldap_password"); creds.bv_len = strlen(ldap_password); if ((ret = ldap_sasl_bind_s (ld, who, LDAP_SASL_SIMPLE, &creds, NULL, NULL, NULL)) != LDAP_SUCCESS) { log_error ("Error: Cannot login into ldap server %s:%d: %s", ldapurl->lud_host, ldapurl->lud_port, ldap_err2string (ret)); } if (creds.bv_val) free(creds.bv_val); } ldap_free_urldesc(ldapurl); return ret; } static int _do_ldap_retry(int ret, const char *server, int port) { static int inform = 1; if (ldap_enable_retry > 0 && ret == LDAP_SERVER_DOWN && ldap_init_retry > 0) { if (inform || (ldap_init_retry % 10) == 0) { inform = 0; log_info ("Can't contact LDAP server %s:%d: retrying for %d sec", server, port, ldap_init_retry); } sleep(1); return ldap_init_retry--; } return 0; } static struct berval * _do_ldap_str2esc_filter_bv(const char *str, ber_len_t len, struct berval *bv_o) { struct berval bv_i; if (!str || !bv_o || (ber_str2bv(str, len, 0, &bv_i) == NULL) || (ldap_bv2escaped_filter_value(&bv_i, bv_o) != 0)) return NULL; return bv_o; } static void ldap_start (void) { struct option_state *options; int ret, version; char *uri = NULL; struct berval creds; #if defined(LDAP_USE_GSSAPI) char *gssapi_realm = NULL; char *gssapi_user = NULL; char *running = NULL; const char *gssapi_delim = "@"; #endif if (ld != NULL) return; if (ldap_server == NULL) { options = NULL; option_state_allocate (&options, MDL); execute_statements_in_scope (NULL, NULL, NULL, NULL, NULL, options, &global_scope, root_group, NULL, NULL); ldap_server = _do_lookup_dhcp_string_option (options, SV_LDAP_SERVER); ldap_dhcp_server_cn = _do_lookup_dhcp_string_option (options, SV_LDAP_DHCP_SERVER_CN); ldap_port = _do_lookup_dhcp_int_option (options, SV_LDAP_PORT); ldap_base_dn = _do_lookup_dhcp_string_option (options, SV_LDAP_BASE_DN); ldap_method = _do_lookup_dhcp_enum_option (options, SV_LDAP_METHOD); ldap_debug_file = _do_lookup_dhcp_string_option (options, SV_LDAP_DEBUG_FILE); ldap_referrals = _do_lookup_dhcp_enum_option (options, SV_LDAP_REFERRALS); ldap_init_retry = _do_lookup_dhcp_int_option (options, SV_LDAP_INIT_RETRY); #if defined (LDAP_USE_SSL) ldap_use_ssl = _do_lookup_dhcp_enum_option (options, SV_LDAP_SSL); if( ldap_use_ssl != LDAP_SSL_OFF) { ldap_tls_reqcert = _do_lookup_dhcp_enum_option (options, SV_LDAP_TLS_REQCERT); ldap_tls_ca_file = _do_lookup_dhcp_string_option (options, SV_LDAP_TLS_CA_FILE); ldap_tls_ca_dir = _do_lookup_dhcp_string_option (options, SV_LDAP_TLS_CA_DIR); ldap_tls_cert = _do_lookup_dhcp_string_option (options, SV_LDAP_TLS_CERT); ldap_tls_key = _do_lookup_dhcp_string_option (options, SV_LDAP_TLS_KEY); ldap_tls_crlcheck = _do_lookup_dhcp_enum_option (options, SV_LDAP_TLS_CRLCHECK); ldap_tls_ciphers = _do_lookup_dhcp_string_option (options, SV_LDAP_TLS_CIPHERS); ldap_tls_randfile = _do_lookup_dhcp_string_option (options, SV_LDAP_TLS_RANDFILE); } #endif #if defined (LDAP_USE_GSSAPI) ldap_gssapi_principal = _do_lookup_dhcp_string_option (options, SV_LDAP_GSSAPI_PRINCIPAL); if (ldap_gssapi_principal == NULL) { log_error("ldap_gssapi_principal is not set," "GSSAPI Authentication for LDAP will not be used"); } else { ldap_gssapi_keytab = _do_lookup_dhcp_string_option (options, SV_LDAP_GSSAPI_KEYTAB); if (ldap_gssapi_keytab == NULL) { log_fatal("ldap_gssapi_keytab must be specified"); } running = strdup(ldap_gssapi_principal); if (running == NULL) log_fatal("Could not allocate memory to duplicate gssapi principal"); gssapi_user = strtok(running, gssapi_delim); if (!gssapi_user || strlen(gssapi_user) == 0) { log_fatal ("GSSAPI principal must specify user: user@realm"); } gssapi_realm = strtok(NULL, gssapi_delim); if (!gssapi_realm || strlen(gssapi_realm) == 0) { log_fatal ("GSSAPI principal must specify realm: user@realm"); } ldap_sasl_inst = malloc(sizeof(struct ldap_sasl_instance)); if (ldap_sasl_inst == NULL) log_fatal("Could not allocate memory for sasl instance! Can not run!"); ldap_sasl_inst->sasl_mech = ber_strdup("GSSAPI"); if (ldap_sasl_inst->sasl_mech == NULL) log_fatal("Could not allocate memory to duplicate gssapi mechanism"); ldap_sasl_inst->sasl_realm = ber_strdup(gssapi_realm); if (ldap_sasl_inst->sasl_realm == NULL) log_fatal("Could not allocate memory to duplicate gssapi realm"); ldap_sasl_inst->sasl_authz_id = ber_strdup(gssapi_user); if (ldap_sasl_inst->sasl_authz_id == NULL) log_fatal("Could not allocate memory to duplicate gssapi user"); ldap_sasl_inst->sasl_authc_id = NULL; ldap_sasl_inst->sasl_password = NULL; //"" before free(running); } #endif #if defined (LDAP_CASA_AUTH) if (!load_uname_pwd_from_miCASA(&ldap_username,&ldap_password)) { #if defined (DEBUG_LDAP) log_info ("Authentication credential taken from file"); #endif #endif ldap_username = _do_lookup_dhcp_string_option (options, SV_LDAP_USERNAME); ldap_password = _do_lookup_dhcp_string_option (options, SV_LDAP_PASSWORD); #if defined (LDAP_CASA_AUTH) } #endif option_state_dereference (&options, MDL); } if (ldap_server == NULL || ldap_base_dn == NULL) { log_info ("Not searching LDAP since ldap-server, ldap-port and ldap-base-dn were not specified in the config file"); ldap_method = LDAP_METHOD_STATIC; return; } if (ldap_debug_file != NULL && ldap_debug_fd == -1) { if ((ldap_debug_fd = open (ldap_debug_file, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR)) < 0) log_error ("Error opening debug LDAP log file %s: %s", ldap_debug_file, strerror (errno)); } #if defined (DEBUG_LDAP) log_info ("Connecting to LDAP server %s:%d", ldap_server, ldap_port); #endif #if defined (LDAP_USE_SSL) if (ldap_use_ssl == -1) { /* ** There was no "ldap-ssl" option in dhcpd.conf (also not "off"). ** Let's try, if we can use an anonymous TLS session without to ** verify the server certificate -- if not continue without TLS. */ int opt = LDAP_OPT_X_TLS_ALLOW; if ((ret = ldap_set_option (NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &opt)) != LDAP_SUCCESS) { log_error ("Warning: Cannot set LDAP TLS require cert option to 'allow': %s", ldap_err2string (ret)); } } if (ldap_use_ssl != LDAP_SSL_OFF) { if (ldap_tls_reqcert != -1) { if ((ret = ldap_set_option (NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &ldap_tls_reqcert)) != LDAP_SUCCESS) { log_error ("Cannot set LDAP TLS require cert option: %s", ldap_err2string (ret)); } } if( ldap_tls_ca_file != NULL) { if ((ret = ldap_set_option (NULL, LDAP_OPT_X_TLS_CACERTFILE, ldap_tls_ca_file)) != LDAP_SUCCESS) { log_error ("Cannot set LDAP TLS CA certificate file %s: %s", ldap_tls_ca_file, ldap_err2string (ret)); } } if( ldap_tls_ca_dir != NULL) { if ((ret = ldap_set_option (NULL, LDAP_OPT_X_TLS_CACERTDIR, ldap_tls_ca_dir)) != LDAP_SUCCESS) { log_error ("Cannot set LDAP TLS CA certificate dir %s: %s", ldap_tls_ca_dir, ldap_err2string (ret)); } } if( ldap_tls_cert != NULL) { if ((ret = ldap_set_option (NULL, LDAP_OPT_X_TLS_CERTFILE, ldap_tls_cert)) != LDAP_SUCCESS) { log_error ("Cannot set LDAP TLS client certificate file %s: %s", ldap_tls_cert, ldap_err2string (ret)); } } if( ldap_tls_key != NULL) { if ((ret = ldap_set_option (NULL, LDAP_OPT_X_TLS_KEYFILE, ldap_tls_key)) != LDAP_SUCCESS) { log_error ("Cannot set LDAP TLS certificate key file %s: %s", ldap_tls_key, ldap_err2string (ret)); } } if( ldap_tls_crlcheck != -1) { int opt = ldap_tls_crlcheck; if ((ret = ldap_set_option (NULL, LDAP_OPT_X_TLS_CRLCHECK, &opt)) != LDAP_SUCCESS) { log_error ("Cannot set LDAP TLS crl check option: %s", ldap_err2string (ret)); } } if( ldap_tls_ciphers != NULL) { if ((ret = ldap_set_option (NULL, LDAP_OPT_X_TLS_CIPHER_SUITE, ldap_tls_ciphers)) != LDAP_SUCCESS) { log_error ("Cannot set LDAP TLS cipher suite %s: %s", ldap_tls_ciphers, ldap_err2string (ret)); } } if( ldap_tls_randfile != NULL) { if ((ret = ldap_set_option (NULL, LDAP_OPT_X_TLS_RANDOM_FILE, ldap_tls_randfile)) != LDAP_SUCCESS) { log_error ("Cannot set LDAP TLS random file %s: %s", ldap_tls_randfile, ldap_err2string (ret)); } } } #endif /* enough for 'ldap://+ + hostname + ':' + port number */ uri = malloc(strlen(ldap_server) + 16); if (uri == NULL) { log_error ("Cannot build ldap init URI %s:%d", ldap_server, ldap_port); return; } sprintf(uri, "ldap://%s:%d", ldap_server, ldap_port); ldap_initialize(&ld, uri); if (ld == NULL) { log_error ("Cannot init ldap session to %s:%d", ldap_server, ldap_port); return; } free(uri); version = LDAP_VERSION3; if ((ret = ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, &version)) != LDAP_OPT_SUCCESS) { log_error ("Cannot set LDAP version to %d: %s", version, ldap_err2string (ret)); } if (ldap_referrals != -1) { if ((ret = ldap_set_option (ld, LDAP_OPT_REFERRALS, ldap_referrals ? LDAP_OPT_ON : LDAP_OPT_OFF)) != LDAP_OPT_SUCCESS) { log_error ("Cannot %s LDAP referrals option: %s", (ldap_referrals ? "enable" : "disable"), ldap_err2string (ret)); } } if ((ret = ldap_set_rebind_proc(ld, ldap_rebind_cb, NULL)) != LDAP_SUCCESS) { log_error ("Warning: Cannot set ldap rebind procedure: %s", ldap_err2string (ret)); } #if defined (LDAP_USE_SSL) if (ldap_use_ssl == LDAP_SSL_LDAPS || (ldap_use_ssl == LDAP_SSL_ON && ldap_port == LDAPS_PORT)) { int opt = LDAP_OPT_X_TLS_HARD; if ((ret = ldap_set_option (ld, LDAP_OPT_X_TLS, &opt)) != LDAP_SUCCESS) { log_error ("Error: Cannot init LDAPS session to %s:%d: %s", ldap_server, ldap_port, ldap_err2string (ret)); ldap_stop(); return; } else { log_info ("LDAPS session successfully enabled to %s:%d", ldap_server, ldap_port); } } else if (ldap_use_ssl != LDAP_SSL_OFF) { do { ret = ldap_start_tls_s (ld, NULL, NULL); } while(_do_ldap_retry(ret, ldap_server, ldap_port) > 0); if (ret != LDAP_SUCCESS) { log_error ("Error: Cannot start TLS session to %s:%d: %s", ldap_server, ldap_port, ldap_err2string (ret)); ldap_stop(); return; } else { log_info ("TLS session successfully started to %s:%d", ldap_server, ldap_port); } } #endif #if defined(LDAP_USE_GSSAPI) if (ldap_gssapi_principal != NULL) { krb5_get_tgt(ldap_gssapi_principal, ldap_gssapi_keytab); if ((ret = ldap_sasl_interactive_bind_s(ld, NULL, ldap_sasl_inst->sasl_mech, NULL, NULL, LDAP_SASL_AUTOMATIC, _ldap_sasl_interact, ldap_sasl_inst) ) != LDAP_SUCCESS) { log_error ("Error: Cannot SASL bind to ldap server %s:%d: %s", ldap_server, ldap_port, ldap_err2string (ret)); char *msg=NULL; ldap_get_option( ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void*)&msg); log_error ("\tAdditional info: %s", msg); ldap_memfree(msg); ldap_stop(); return; } } else #endif if (ldap_username != NULL && *ldap_username != '\0' && ldap_password != NULL) { creds.bv_val = strdup(ldap_password); if (creds.bv_val == NULL) log_fatal ("Error: Unable to allocate memory to duplicate ldap_password"); creds.bv_len = strlen(ldap_password); do { ret = ldap_sasl_bind_s (ld, ldap_username, LDAP_SASL_SIMPLE, &creds, NULL, NULL, NULL); } while(_do_ldap_retry(ret, ldap_server, ldap_port) > 0); free(creds.bv_val); if (ret != LDAP_SUCCESS) { log_error ("Error: Cannot login into ldap server %s:%d: %s", ldap_server, ldap_port, ldap_err2string (ret)); ldap_stop(); return; } } #if defined (DEBUG_LDAP) log_info ("Successfully logged into LDAP server %s", ldap_server); #endif } static void parse_external_dns (LDAPMessage * ent) { char *search[] = {"dhcpOptionsDN", "dhcpSharedNetworkDN", "dhcpSubnetDN", "dhcpGroupDN", "dhcpHostDN", "dhcpClassesDN", "dhcpPoolDN", "dhcpZoneDN", "dhcpFailOverPeerDN", NULL}; /* TODO: dhcpKeyDN can't be added. It is referenced in dhcpDnsZone to retrive the key name (cn). Adding keyDN will reflect adding a key declaration inside the zone configuration. dhcpSubClassesDN cant be added. It is also similar to the above. Needs schema change. */ LDAPMessage * newres, * newent; struct berval **tempbv; int i, j, ret; #if defined (DEBUG_LDAP) char *dn; dn = ldap_get_dn (ld, ent); if (dn != NULL) { log_info ("Parsing external DNs for '%s'", dn); ldap_memfree (dn); } #endif if (ld == NULL) ldap_start (); if (ld == NULL) return; for (i=0; search[i] != NULL; i++) { if ((tempbv = ldap_get_values_len (ld, ent, search[i])) == NULL) continue; for (j=0; tempbv[j] != NULL; j++) { if (*tempbv[j]->bv_val == '\0') continue; if ((ret = ldap_search_ext_s(ld, tempbv[j]->bv_val, LDAP_SCOPE_BASE, "objectClass=*", NULL, 0, NULL, NULL, NULL, 0, &newres)) != LDAP_SUCCESS) { ldap_value_free_len (tempbv); ldap_stop(); return; } #if defined (DEBUG_LDAP) log_info ("Adding contents of subtree '%s' to config stack from '%s' reference", tempbv[j]->bv_val, search[i]); #endif for (newent = ldap_first_entry (ld, newres); newent != NULL; newent = ldap_next_entry (ld, newent)) { #if defined (DEBUG_LDAP) dn = ldap_get_dn (ld, newent); if (dn != NULL) { log_info ("Adding LDAP result set starting with '%s' to config stack", dn); ldap_memfree (dn); } #endif add_to_config_stack (newres, newent); /* don't free newres here */ } } ldap_value_free_len (tempbv); } } static void free_stack_entry (struct ldap_config_stack *item) { struct ldap_config_stack *look_ahead_pointer = item; int may_free_msg = 1; while (look_ahead_pointer->next != NULL) { look_ahead_pointer = look_ahead_pointer->next; if (look_ahead_pointer->res == item->res) { may_free_msg = 0; break; } } if (may_free_msg) ldap_msgfree (item->res); dfree (item, MDL); } static void next_ldap_entry (struct parse *cfile) { struct ldap_config_stack *temp_stack; if (ldap_stack != NULL && ldap_stack->close_brace) { x_parser_strcat (cfile, "}\n"); ldap_stack->close_brace = 0; } while (ldap_stack != NULL && (ldap_stack->ldent == NULL || ( ldap_stack->processed && (ldap_stack->ldent = ldap_next_entry (ld, ldap_stack->ldent)) == NULL))) { if (ldap_stack->close_brace) { x_parser_strcat (cfile, "}\n"); ldap_stack->close_brace = 0; } temp_stack = ldap_stack; ldap_stack = ldap_stack->next; free_stack_entry (temp_stack); } if (ldap_stack != NULL && ldap_stack->close_brace) { x_parser_strcat (cfile, "}\n"); ldap_stack->close_brace = 0; } } static char check_statement_end (const char *statement) { char *ptr; if (statement == NULL || *statement == '\0') return ('\0'); /* ** check if it ends with "}", e.g.: ** "zone my.domain. { ... }" ** optionally followed by spaces */ ptr = strrchr (statement, '}'); if (ptr != NULL) { /* skip following white-spaces */ for (++ptr; isspace ((int)*ptr); ptr++); /* check if we reached the end */ if (*ptr == '\0') return ('}'); /* yes, block end */ else return (*ptr); } /* ** this should not happen, but... ** check if it ends with ";", e.g.: ** "authoritative;" ** optionally followed by spaces */ ptr = strrchr (statement, ';'); if (ptr != NULL) { /* skip following white-spaces */ for (++ptr; isspace ((int)*ptr); ptr++); /* check if we reached the end */ if (*ptr == '\0') return (';'); /* ends with a ; */ else return (*ptr); } return ('\0'); } static isc_result_t ldap_parse_entry_options (LDAPMessage *ent, struct parse *cfile, int *lease_limit) { struct berval **tempbv; int i; if (ent == NULL || cfile == NULL) return (ISC_R_FAILURE); if ((tempbv = ldap_get_values_len (ld, ent, "dhcpStatements")) != NULL) { for (i=0; tempbv[i] != NULL; i++) { if (lease_limit != NULL && strncasecmp ("lease limit ", tempbv[i]->bv_val, 12) == 0) { *lease_limit = (int) strtol ((tempbv[i]->bv_val) + 12, NULL, 10); continue; } x_parser_strcat (cfile, tempbv[i]->bv_val); switch((int) check_statement_end (tempbv[i]->bv_val)) { case '}': case ';': x_parser_strcat (cfile, "\n"); break; default: x_parser_strcat (cfile, ";\n"); break; } } ldap_value_free_len (tempbv); } if ((tempbv = ldap_get_values_len (ld, ent, "dhcpOption")) != NULL) { for (i=0; tempbv[i] != NULL; i++) { x_parser_strcat (cfile, "option "); x_parser_strcat (cfile, tempbv[i]->bv_val); switch ((int) check_statement_end (tempbv[i]->bv_val)) { case ';': x_parser_strcat (cfile, "\n"); break; default: x_parser_strcat (cfile, ";\n"); break; } } ldap_value_free_len (tempbv); } return (ISC_R_SUCCESS); } static void ldap_generate_config_string (struct parse *cfile) { struct berval **objectClass; char *dn; struct ldap_config_stack *entry; LDAPMessage * ent, * res, *entfirst, *resfirst; int i, ignore, found; int ret, parsedn = 1; size_t len = cfile->buflen; if (ld == NULL) ldap_start (); if (ld == NULL) return; entry = ldap_stack; if ((objectClass = ldap_get_values_len (ld, entry->ldent, "objectClass")) == NULL) return; entry->processed = 1; ignore = 0; found = 1; for (i=0; objectClass[i] != NULL; i++) { if (strcasecmp (objectClass[i]->bv_val, "dhcpSharedNetwork") == 0) ldap_parse_shared_network (entry, cfile); else if (strcasecmp (objectClass[i]->bv_val, "dhcpClass") == 0) ldap_parse_class (entry, cfile); else if (strcasecmp (objectClass[i]->bv_val, "dhcpSubnet") == 0) ldap_parse_subnet (entry, cfile); else if (strcasecmp (objectClass[i]->bv_val, "dhcpSubnet6") == 0) ldap_parse_subnet6 (entry, cfile); else if (strcasecmp (objectClass[i]->bv_val, "dhcpPool") == 0) ldap_parse_pool (entry, cfile); else if (strcasecmp (objectClass[i]->bv_val, "dhcpPool6") == 0) ldap_parse_pool6 (entry, cfile); else if (strcasecmp (objectClass[i]->bv_val, "dhcpGroup") == 0) ldap_parse_group (entry, cfile); else if (strcasecmp (objectClass[i]->bv_val, "dhcpTSigKey") == 0) ldap_parse_key (entry, cfile); else if (strcasecmp (objectClass[i]->bv_val, "dhcpDnsZone") == 0) ldap_parse_zone (entry, cfile); #if defined(HAVE_IFADDRS_H) else if (strcasecmp (objectClass[i]->bv_val, "dhcpFailOverPeer") == 0) ldap_parse_failover (entry, cfile); #endif else if (strcasecmp (objectClass[i]->bv_val, "dhcpHost") == 0) { if (ldap_method == LDAP_METHOD_STATIC) ldap_parse_host (entry, cfile); else { ignore = 1; break; } } else if (strcasecmp (objectClass[i]->bv_val, "dhcpSubClass") == 0) { if (ldap_method == LDAP_METHOD_STATIC) ldap_parse_subclass (entry, cfile); else { ignore = 1; break; } } else found = 0; if (found && x_parser_length(cfile) <= len) { ignore = 1; break; } } ldap_value_free_len (objectClass); if (ignore) { next_ldap_entry (cfile); return; } ldap_parse_entry_options(entry->ldent, cfile, NULL); dn = ldap_get_dn (ld, entry->ldent); if (dn == NULL) { ldap_stop(); return; } #if defined(DEBUG_LDAP) log_info ("Found LDAP entry '%s'", dn); #endif if ((ret = ldap_search_ext_s (ld, dn, LDAP_SCOPE_ONELEVEL, "(!(|(|(objectClass=dhcpTSigKey)(objectClass=dhcpClass)) (objectClass=dhcpFailOverPeer)))", NULL, 0, NULL, NULL, NULL, 0, &res)) != LDAP_SUCCESS) { ldap_memfree (dn); ldap_stop(); return; } if ((ret = ldap_search_ext_s (ld, dn, LDAP_SCOPE_ONELEVEL, "(|(|(objectClass=dhcpTSigKey)(objectClass=dhcpClass)) (objectClass=dhcpFailOverPeer))", NULL, 0, NULL, NULL, NULL, 0, &resfirst)) != LDAP_SUCCESS) { ldap_memfree (dn); ldap_msgfree (res); ldap_stop(); return; } ldap_memfree (dn); ent = ldap_first_entry(ld, res); entfirst = ldap_first_entry(ld, resfirst); if (ent == NULL && entfirst == NULL) { parse_external_dns (entry->ldent); next_ldap_entry (cfile); } if (ent != NULL) { add_to_config_stack (res, ent); parse_external_dns (entry->ldent); parsedn = 0; } else ldap_msgfree (res); if (entfirst != NULL) { add_to_config_stack (resfirst, entfirst); if(parsedn) parse_external_dns (entry->ldent); } else ldap_msgfree (resfirst); } static void ldap_close_debug_fd() { if (ldap_debug_fd != -1) { close (ldap_debug_fd); ldap_debug_fd = -1; } } static void ldap_write_debug (const void *buff, size_t size) { if (ldap_debug_fd != -1) { if (write (ldap_debug_fd, buff, size) < 0) { log_error ("Error writing to LDAP debug file %s: %s." " Disabling log file.", ldap_debug_file, strerror (errno)); ldap_close_debug_fd(); } } } static int ldap_read_function (struct parse *cfile) { size_t len; /* append when in saved state */ if (cfile->saved_state == NULL) { cfile->inbuf[0] = '\0'; cfile->bufix = 0; cfile->buflen = 0; } len = cfile->buflen; while (ldap_stack != NULL && x_parser_length(cfile) <= len) ldap_generate_config_string (cfile); if (x_parser_length(cfile) <= len && ldap_stack == NULL) return (EOF); if (cfile->buflen > len) ldap_write_debug (cfile->inbuf + len, cfile->buflen - len); #if defined (DEBUG_LDAP) log_info ("Sending config portion '%s'", cfile->inbuf + len); #endif return (cfile->inbuf[cfile->bufix++]); } static char * ldap_get_host_name (LDAPMessage * ent) { struct berval **name; char *ret; ret = NULL; if ((name = ldap_get_values_len (ld, ent, "cn")) == NULL || name[0] == NULL) { if (name != NULL) ldap_value_free_len (name); #if defined (DEBUG_LDAP) ret = ldap_get_dn (ld, ent); if (ret != NULL) { log_info ("Cannot get cn attribute for LDAP entry %s", ret); ldap_memfree(ret); } #endif return (NULL); } ret = dmalloc (strlen (name[0]->bv_val) + 1, MDL); strcpy (ret, name[0]->bv_val); ldap_value_free_len (name); return (ret); } isc_result_t ldap_read_config (void) { LDAPMessage * ldres, * hostres, * ent, * hostent; char hfilter[1024], sfilter[1024], fqdn[257]; char *hostdn; ldap_dn_node *curr = NULL; struct parse *cfile; struct utsname unme; isc_result_t res; size_t length; int ret, cnt; struct berval **tempbv = NULL; struct berval bv_o[2]; cfile = x_parser_init("LDAP"); if (cfile == NULL) return (ISC_R_NOMEMORY); ldap_enable_retry = 1; if (ld == NULL) ldap_start (); ldap_enable_retry = 0; if (ld == NULL) { x_parser_free(&cfile); return (ldap_server == NULL ? ISC_R_SUCCESS : ISC_R_FAILURE); } uname (&unme); if (ldap_dhcp_server_cn != NULL) { if (_do_ldap_str2esc_filter_bv(ldap_dhcp_server_cn, 0, &bv_o[0]) == NULL) { log_error ("Cannot escape ldap filter value %s: %m", ldap_dhcp_server_cn); x_parser_free(&cfile); return (ISC_R_FAILURE); } snprintf (hfilter, sizeof (hfilter), "(&(objectClass=dhcpServer)(cn=%s))", bv_o[0].bv_val); ber_memfree(bv_o[0].bv_val); } else { if (_do_ldap_str2esc_filter_bv(unme.nodename, 0, &bv_o[0]) == NULL) { log_error ("Cannot escape ldap filter value %s: %m", unme.nodename); x_parser_free(&cfile); return (ISC_R_FAILURE); } *fqdn ='\0'; if(0 == get_host_entry(fqdn, sizeof(fqdn), NULL, 0)) { if (_do_ldap_str2esc_filter_bv(fqdn, 0, &bv_o[1]) == NULL) { log_error ("Cannot escape ldap filter value %s: %m", fqdn); ber_memfree(bv_o[0].bv_val); x_parser_free(&cfile); return (ISC_R_FAILURE); } } // If we have fqdn and it isn't the same as nodename, use it in filter // otherwise just use nodename if ((*fqdn) && (strcmp(unme.nodename, fqdn))) { snprintf (hfilter, sizeof (hfilter), "(&(objectClass=dhcpServer)(|(cn=%s)(cn=%s)))", bv_o[0].bv_val, bv_o[1].bv_val); ber_memfree(bv_o[1].bv_val); } else { snprintf (hfilter, sizeof (hfilter), "(&(objectClass=dhcpServer)(cn=%s))", bv_o[0].bv_val); } ber_memfree(bv_o[0].bv_val); } ldap_enable_retry = 1; do { hostres = NULL; ret = ldap_search_ext_s (ld, ldap_base_dn, LDAP_SCOPE_SUBTREE, hfilter, NULL, 0, NULL, NULL, NULL, 0, &hostres); } while(_do_ldap_retry(ret, ldap_server, ldap_port) > 0); ldap_enable_retry = 0; if(ret != LDAP_SUCCESS) { log_error ("Cannot find host LDAP entry %s %s", ((ldap_dhcp_server_cn == NULL)?(unme.nodename):(ldap_dhcp_server_cn)), hfilter); if(NULL != hostres) ldap_msgfree (hostres); ldap_stop(); x_parser_free(&cfile); return (ISC_R_FAILURE); } if ((hostent = ldap_first_entry (ld, hostres)) == NULL) { log_error ("Error: Cannot find LDAP entry matching %s", hfilter); ldap_msgfree (hostres); ldap_stop(); x_parser_free(&cfile); return (ISC_R_FAILURE); } hostdn = ldap_get_dn (ld, hostent); #if defined(DEBUG_LDAP) if (hostdn != NULL) log_info ("Found dhcpServer LDAP entry '%s'", hostdn); #endif if (hostdn == NULL || (tempbv = ldap_get_values_len (ld, hostent, "dhcpServiceDN")) == NULL || tempbv[0] == NULL) { log_error ("Error: No dhcp service is associated with the server %s %s", (hostdn ? "dn" : "name"), (hostdn ? hostdn : (ldap_dhcp_server_cn ? ldap_dhcp_server_cn : unme.nodename))); if (tempbv != NULL) ldap_value_free_len (tempbv); if (hostdn) ldap_memfree (hostdn); ldap_msgfree (hostres); ldap_stop(); x_parser_free(&cfile); return (ISC_R_FAILURE); } #if defined(DEBUG_LDAP) log_info ("LDAP: Parsing dhcpServer options '%s' ...", hostdn); #endif res = ldap_parse_entry_options(hostent, cfile, NULL); if (res != ISC_R_SUCCESS) { ldap_value_free_len (tempbv); ldap_msgfree (hostres); ldap_memfree (hostdn); ldap_stop(); x_parser_free(&cfile); return res; } if (x_parser_length(cfile) > 0) { ldap_write_debug(cfile->inbuf, cfile->buflen); res = conf_file_subparse (cfile, root_group, ROOT_GROUP); if (res != ISC_R_SUCCESS) { log_error ("LDAP: cannot parse dhcpServer entry '%s'", hostdn); ldap_value_free_len (tempbv); ldap_msgfree (hostres); ldap_memfree (hostdn); ldap_stop(); x_parser_free(&cfile); return res; } x_parser_reset(cfile); } ldap_msgfree (hostres); res = ISC_R_SUCCESS; for (cnt=0; tempbv[cnt] != NULL; cnt++) { if (_do_ldap_str2esc_filter_bv(hostdn, 0, &bv_o[0]) == NULL) { log_error ("Cannot escape ldap filter value %s: %m", hostdn); res = ISC_R_FAILURE; break; } snprintf(sfilter, sizeof(sfilter), "(&(objectClass=dhcpService)" "(|(|(dhcpPrimaryDN=%s)(dhcpSecondaryDN=%s))(dhcpServerDN=%s)))", bv_o[0].bv_val, bv_o[0].bv_val, bv_o[0].bv_val); ber_memfree(bv_o[0].bv_val); ldres = NULL; if ((ret = ldap_search_ext_s (ld, tempbv[cnt]->bv_val, LDAP_SCOPE_BASE, sfilter, NULL, 0, NULL, NULL, NULL, 0, &ldres)) != LDAP_SUCCESS) { log_error ("Error searching for dhcpServiceDN '%s': %s. Please update the LDAP entry '%s'", tempbv[cnt]->bv_val, ldap_err2string (ret), hostdn); if(NULL != ldres) ldap_msgfree(ldres); res = ISC_R_FAILURE; break; } if ((ent = ldap_first_entry (ld, ldres)) == NULL) { log_error ("Error: Cannot find dhcpService DN '%s' with server reference. Please update the LDAP server entry '%s'", tempbv[cnt]->bv_val, hostdn); ldap_msgfree(ldres); res = ISC_R_FAILURE; break; } /* ** FIXME: how to free the remembered dn's on exit? ** This should be OK if dmalloc registers the ** memory it allocated and frees it on exit.. */ curr = dmalloc (sizeof (*curr), MDL); if (curr != NULL) { length = strlen (tempbv[cnt]->bv_val); curr->dn = dmalloc (length + 1, MDL); if (curr->dn == NULL) { dfree (curr, MDL); curr = NULL; } else strcpy (curr->dn, tempbv[cnt]->bv_val); } if (curr != NULL) { curr->refs++; /* append to service-dn list */ if (ldap_service_dn_tail != NULL) ldap_service_dn_tail->next = curr; else ldap_service_dn_head = curr; ldap_service_dn_tail = curr; } else log_fatal ("no memory to remember ldap service dn"); #if defined (DEBUG_LDAP) log_info ("LDAP: Parsing dhcpService DN '%s' ...", tempbv[cnt]->bv_val); #endif add_to_config_stack (ldres, ent); res = conf_file_subparse (cfile, root_group, ROOT_GROUP); if (res != ISC_R_SUCCESS) { log_error ("LDAP: cannot parse dhcpService entry '%s'", tempbv[cnt]->bv_val); break; } } x_parser_free(&cfile); ldap_close_debug_fd(); ldap_memfree (hostdn); ldap_value_free_len (tempbv); if (res != ISC_R_SUCCESS) { struct ldap_config_stack *temp_stack; while ((curr = ldap_service_dn_head) != NULL) { ldap_service_dn_head = curr->next; dfree (curr->dn, MDL); dfree (curr, MDL); } ldap_service_dn_tail = NULL; while ((temp_stack = ldap_stack) != NULL) { ldap_stack = temp_stack->next; free_stack_entry (temp_stack); } ldap_stop(); } /* Unbind from ldap immediately after reading config in static mode. */ if (ldap_method == LDAP_METHOD_STATIC) ldap_stop(); return (res); } /* This function will parse the dhcpOption and dhcpStatements field in the LDAP entry if it exists. Right now, type will be either HOST_DECL or CLASS_DECL. If we are parsing a HOST_DECL, this always returns 0. If we are parsing a CLASS_DECL, this will return what the current lease limit is in LDAP. If there is no lease limit specified, we return 0 */ static int ldap_parse_options (LDAPMessage * ent, struct group *group, int type, struct host_decl *host, struct class **class) { int declaration, lease_limit; enum dhcp_token token; struct parse *cfile; isc_result_t res; const char *val; lease_limit = 0; cfile = x_parser_init(type == HOST_DECL ? "LDAP-HOST" : "LDAP-SUBCLASS"); if (cfile == NULL) return (lease_limit); /* This block of code will try to find the parent of the host, and if it is a group object, fetch the options and apply to the host. */ if (type == HOST_DECL) { char *hostdn, *basedn, *temp1, *temp2, filter[1024]; LDAPMessage *groupdn, *entry; int ret; hostdn = ldap_get_dn (ld, ent); if( hostdn != NULL) { basedn = NULL; temp1 = strchr (hostdn, '='); if (temp1 != NULL) temp1 = strchr (++temp1, '='); if (temp1 != NULL) temp2 = strchr (++temp1, ','); else temp2 = NULL; if (temp2 != NULL) { struct berval bv_o; if (_do_ldap_str2esc_filter_bv(temp1, (temp2 - temp1), &bv_o) == NULL) { log_error ("Cannot escape ldap filter value %.*s: %m", (int)(temp2 - temp1), temp1); filter[0] = '\0'; } else { snprintf (filter, sizeof(filter), "(&(cn=%s)(objectClass=dhcpGroup))", bv_o.bv_val); ber_memfree(bv_o.bv_val); } basedn = strchr (temp1, ','); if (basedn != NULL) ++basedn; } if (basedn != NULL && *basedn != '\0' && filter[0] != '\0') { ret = ldap_search_ext_s (ld, basedn, LDAP_SCOPE_SUBTREE, filter, NULL, 0, NULL, NULL, NULL, 0, &groupdn); if (ret == LDAP_SUCCESS) { if ((entry = ldap_first_entry (ld, groupdn)) != NULL) { res = ldap_parse_entry_options (entry, cfile, &lease_limit); if (res != ISC_R_SUCCESS) { /* reset option buffer discarding any results */ x_parser_reset(cfile); lease_limit = 0; } } ldap_msgfree( groupdn); } } ldap_memfree( hostdn); } } res = ldap_parse_entry_options (ent, cfile, &lease_limit); if (res != ISC_R_SUCCESS) { x_parser_free(&cfile); return (lease_limit); } if (x_parser_length(cfile) == 0) { x_parser_free(&cfile); return (lease_limit); } declaration = 0; do { token = peek_token (&val, NULL, cfile); if (token == END_OF_FILE) break; declaration = parse_statement (cfile, group, type, host, declaration); } while (1); x_parser_free(&cfile); return (lease_limit); } int find_haddr_in_ldap (struct host_decl **hp, int htype, unsigned hlen, const unsigned char *haddr, const char *file, int line) { char buf[128], *type_str; LDAPMessage * res, *ent; struct host_decl * host; isc_result_t status; ldap_dn_node *curr; char up_hwaddr[20]; char lo_hwaddr[20]; int ret; struct berval bv_o[2]; *hp = NULL; if (ldap_method == LDAP_METHOD_STATIC) return (0); if (ld == NULL) ldap_start (); if (ld == NULL) return (0); switch (htype) { case HTYPE_ETHER: type_str = "ethernet"; break; case HTYPE_IEEE802: type_str = "token-ring"; break; case HTYPE_FDDI: type_str = "fddi"; break; default: log_info ("Ignoring unknown type %d", htype); return (0); } /* ** FIXME: It is not guaranteed, that the dhcpHWAddress attribute ** contains _exactly_ "type addr" with one space between! */ snprintf(lo_hwaddr, sizeof(lo_hwaddr), "%s", print_hw_addr (htype, hlen, haddr)); x_strxform(up_hwaddr, lo_hwaddr, sizeof(up_hwaddr), toupper); if (_do_ldap_str2esc_filter_bv(lo_hwaddr, 0, &bv_o[0]) == NULL) { log_error ("Cannot escape ldap filter value %s: %m", lo_hwaddr); return (0); } if (_do_ldap_str2esc_filter_bv(up_hwaddr, 0, &bv_o[1]) == NULL) { log_error ("Cannot escape ldap filter value %s: %m", up_hwaddr); ber_memfree(bv_o[0].bv_val); return (0); } snprintf (buf, sizeof (buf), "(&(objectClass=dhcpHost)(|(dhcpHWAddress=%s %s)(dhcpHWAddress=%s %s)))", type_str, bv_o[0].bv_val, type_str, bv_o[1].bv_val); ber_memfree(bv_o[0].bv_val); ber_memfree(bv_o[1].bv_val); res = ent = NULL; for (curr = ldap_service_dn_head; curr != NULL && *curr->dn != '\0'; curr = curr->next) { #if defined (DEBUG_LDAP) log_info ("Searching for %s in LDAP tree %s", buf, curr->dn); #endif ret = ldap_search_ext_s (ld, curr->dn, LDAP_SCOPE_SUBTREE, buf, NULL, 0, NULL, NULL, NULL, 0, &res); if(ret == LDAP_SERVER_DOWN) { log_info ("LDAP server was down, trying to reconnect..."); ldap_stop(); ldap_start(); if(ld == NULL) { log_info ("LDAP reconnect failed - try again later..."); return (0); } ret = ldap_search_ext_s (ld, curr->dn, LDAP_SCOPE_SUBTREE, buf, NULL, 0, NULL, NULL, NULL, 0, &res); } if (ret == LDAP_SUCCESS) { ent = ldap_first_entry (ld, res); #if defined (DEBUG_LDAP) if (ent == NULL) { log_info ("No host entry for %s in LDAP tree %s", buf, curr->dn); } #endif while (ent != NULL) { #if defined (DEBUG_LDAP) char *dn = ldap_get_dn (ld, ent); if (dn != NULL) { log_info ("Found dhcpHWAddress LDAP entry %s", dn); ldap_memfree(dn); } #endif host = (struct host_decl *)0; status = host_allocate (&host, MDL); if (status != ISC_R_SUCCESS) { log_fatal ("can't allocate host decl struct: %s", isc_result_totext (status)); ldap_msgfree (res); return (0); } host->name = ldap_get_host_name (ent); if (host->name == NULL) { host_dereference (&host, MDL); ldap_msgfree (res); return (0); } if (!clone_group (&host->group, root_group, MDL)) { log_fatal ("can't clone group for host %s", host->name); host_dereference (&host, MDL); ldap_msgfree (res); return (0); } ldap_parse_options (ent, host->group, HOST_DECL, host, NULL); host->n_ipaddr = *hp; *hp = host; ent = ldap_next_entry (ld, ent); } if(res) { ldap_msgfree (res); res = NULL; } return (*hp != NULL); } else { if(res) { ldap_msgfree (res); res = NULL; } if (ret != LDAP_NO_SUCH_OBJECT && ret != LDAP_SUCCESS) { log_error ("Cannot search for %s in LDAP tree %s: %s", buf, curr->dn, ldap_err2string (ret)); ldap_stop(); return (0); } #if defined (DEBUG_LDAP) else { log_info ("ldap_search_ext_s returned %s when searching for %s in %s", ldap_err2string (ret), buf, curr->dn); } #endif } } return (0); } int find_subclass_in_ldap (struct class *class, struct class **newclass, struct data_string *data) { LDAPMessage * res, * ent; int ret, lease_limit; isc_result_t status; ldap_dn_node *curr; char buf[2048]; struct berval bv_class; struct berval bv_cdata; char *hex_1; if (ldap_method == LDAP_METHOD_STATIC) return (0); if (ld == NULL) ldap_start (); if (ld == NULL) return (0); hex_1 = print_hex_1 (data->len, data->data, 1024); if (*hex_1 == '"') { /* result is a quotted not hex string: ldap escape the original string */ if (_do_ldap_str2esc_filter_bv((const char*)data->data, data->len, &bv_cdata) == NULL) { log_error ("Cannot escape ldap filter value %s: %m", hex_1); return (0); } hex_1 = NULL; } if (_do_ldap_str2esc_filter_bv(class->name, strlen (class->name), &bv_class) == NULL) { log_error ("Cannot escape ldap filter value %s: %m", class->name); if (hex_1 == NULL) ber_memfree(bv_cdata.bv_val); return (0); } snprintf (buf, sizeof (buf), "(&(objectClass=dhcpSubClass)(cn=%s)(dhcpClassData=%s))", (hex_1 == NULL ? bv_cdata.bv_val : hex_1), bv_class.bv_val); if (hex_1 == NULL) ber_memfree(bv_cdata.bv_val); ber_memfree(bv_class.bv_val); #if defined (DEBUG_LDAP) log_info ("Searching LDAP for %s", buf); #endif res = ent = NULL; for (curr = ldap_service_dn_head; curr != NULL && *curr->dn != '\0'; curr = curr->next) { #if defined (DEBUG_LDAP) log_info ("Searching for %s in LDAP tree %s", buf, curr->dn); #endif ret = ldap_search_ext_s (ld, curr->dn, LDAP_SCOPE_SUBTREE, buf, NULL, 0, NULL, NULL, NULL, 0, &res); if(ret == LDAP_SERVER_DOWN) { log_info ("LDAP server was down, trying to reconnect..."); ldap_stop(); ldap_start(); if(ld == NULL) { log_info ("LDAP reconnect failed - try again later..."); return (0); } ret = ldap_search_ext_s (ld, curr->dn, LDAP_SCOPE_SUBTREE, buf, NULL, 0, NULL, NULL, NULL, 0, &res); } if (ret == LDAP_SUCCESS) { if( (ent = ldap_first_entry (ld, res)) != NULL) break; /* search OK and have entry */ #if defined (DEBUG_LDAP) log_info ("No subclass entry for %s in LDAP tree %s", buf, curr->dn); #endif if(res) { ldap_msgfree (res); res = NULL; } } else { if(res) { ldap_msgfree (res); res = NULL; } if (ret != LDAP_NO_SUCH_OBJECT && ret != LDAP_SUCCESS) { log_error ("Cannot search for %s in LDAP tree %s: %s", buf, curr->dn, ldap_err2string (ret)); ldap_stop(); return (0); } #if defined (DEBUG_LDAP) else { log_info ("ldap_search_ext_s returned %s when searching for %s in %s", ldap_err2string (ret), buf, curr->dn); } #endif } } if (res && ent) { #if defined (DEBUG_LDAP) char *dn = ldap_get_dn (ld, ent); if (dn != NULL) { log_info ("Found subclass LDAP entry %s", dn); ldap_memfree(dn); } #endif status = class_allocate (newclass, MDL); if (status != ISC_R_SUCCESS) { log_error ("Cannot allocate memory for a new class"); ldap_msgfree (res); return (0); } group_reference (&(*newclass)->group, class->group, MDL); class_reference (&(*newclass)->superclass, class, MDL); lease_limit = ldap_parse_options (ent, (*newclass)->group, CLASS_DECL, NULL, newclass); if (lease_limit == 0) (*newclass)->lease_limit = class->lease_limit; else class->lease_limit = lease_limit; if ((*newclass)->lease_limit) { (*newclass)->billed_leases = dmalloc ((*newclass)->lease_limit * sizeof (struct lease *), MDL); if (!(*newclass)->billed_leases) { log_error ("no memory for billing"); class_dereference (newclass, MDL); ldap_msgfree (res); return (0); } memset ((*newclass)->billed_leases, 0, ((*newclass)->lease_limit * sizeof (struct lease *))); } data_string_copy (&(*newclass)->hash_string, data, MDL); ldap_msgfree (res); return (1); } if(res) ldap_msgfree (res); return (0); } int find_client_in_ldap (struct host_decl **hp, struct packet *packet, struct option_state *state, const char *file, int line) { LDAPMessage * res, * ent; ldap_dn_node *curr; struct host_decl * host; isc_result_t status; struct data_string client_id; char buf[1024], buf1[1024]; int ret; if (ldap_method == LDAP_METHOD_STATIC) return (0); if (ld == NULL) ldap_start (); if (ld == NULL) return (0); memset(&client_id, 0, sizeof(client_id)); if (get_client_id(packet, &client_id) != ISC_R_SUCCESS) return (0); snprintf(buf, sizeof(buf), "(&(objectClass=dhcpHost)(dhcpClientId=%s))", print_hw_addr(0, client_id.len, client_id.data)); /* log_info ("Searching LDAP for %s (%s)", buf, packet->interface->shared_network->name); */ res = ent = NULL; for (curr = ldap_service_dn_head; curr != NULL && *curr->dn != '\0'; curr = curr->next) { snprintf(buf1, sizeof(buf1), "cn=%s,%s", packet->interface->shared_network->name, curr->dn); #if defined (DEBUG_LDAP) log_info ("Searching for %s in LDAP tree %s", buf, buf1); #endif ret = ldap_search_ext_s (ld, buf1, LDAP_SCOPE_SUBTREE, buf, NULL, 0, NULL, NULL, NULL, 0, &res); if(ret == LDAP_SERVER_DOWN) { log_info ("LDAP server was down, trying to reconnect..."); ldap_stop(); ldap_start(); if(ld == NULL) { log_info ("LDAP reconnect failed - try again later..."); return (0); } ret = ldap_search_ext_s (ld, buf1, LDAP_SCOPE_SUBTREE, buf, NULL, 0, NULL, NULL, NULL, 0, &res); } if (ret == LDAP_SUCCESS) { if( (ent = ldap_first_entry (ld, res)) != NULL) { log_info ("found entry in search %s", buf1); break; /* search OK and have entry */ } #if defined (DEBUG_LDAP) log_info ("No subclass entry for %s in LDAP tree %s", buf, curr->dn); #endif if(res) { ldap_msgfree (res); res = NULL; } } else { if(res) { ldap_msgfree (res); res = NULL; } if (ret != LDAP_NO_SUCH_OBJECT && ret != LDAP_SUCCESS) { log_error ("Cannot search for %s in LDAP tree %s: %s", buf, curr->dn, ldap_err2string (ret)); ldap_stop(); return (0); } else { log_info ("did not find: %s", buf); } } } if (res && ent) { #if defined (DEBUG_LDAP) log_info ("ldap_get_dn %s", curr->dn); char *dn = ldap_get_dn (ld, ent); if (dn != NULL) { log_info ("Found subclass LDAP entry %s", dn); ldap_memfree(dn); } else { log_info ("DN is null %s", dn); } #endif host = (struct host_decl *)0; status = host_allocate (&host, MDL); if (status != ISC_R_SUCCESS) { log_fatal ("can't allocate host decl struct: %s", isc_result_totext (status)); ldap_msgfree (res); return (0); } host->name = ldap_get_host_name (ent); if (host->name == NULL) { host_dereference (&host, MDL); ldap_msgfree (res); return (0); } /* log_info ("Host name %s", host->name); */ if (!clone_group (&host->group, root_group, MDL)) { log_fatal ("can't clone group for host %s", host->name); host_dereference (&host, MDL); ldap_msgfree (res); return (0); } ldap_parse_options (ent, host->group, HOST_DECL, host, NULL); *hp = host; ldap_msgfree (res); return (1); } else { log_info ("did not find clientid: %s", buf); } if(res) ldap_msgfree (res); return (0); } #if defined(LDAP_USE_GSSAPI) static int _ldap_sasl_interact(LDAP *ld, unsigned flags, void *defaults, void *sin) { sasl_interact_t *in; struct ldap_sasl_instance *ldap_inst = defaults; int ret = LDAP_OTHER; size_t size; if (ld == NULL || sin == NULL) return LDAP_PARAM_ERROR; log_info("doing interactive bind"); for (in = sin; in != NULL && in->id != SASL_CB_LIST_END; in++) { switch (in->id) { case SASL_CB_USER: log_info("got request for SASL_CB_USER %s", ldap_inst->sasl_authz_id); size = strlen(ldap_inst->sasl_authz_id); in->result = ldap_inst->sasl_authz_id; in->len = size; ret = LDAP_SUCCESS; break; case SASL_CB_GETREALM: log_info("got request for SASL_CB_GETREALM %s", ldap_inst->sasl_realm); size = strlen(ldap_inst->sasl_realm); in->result = ldap_inst->sasl_realm; in->len = size; ret = LDAP_SUCCESS; break; case SASL_CB_AUTHNAME: log_info("got request for SASL_CB_AUTHNAME %s", ldap_inst->sasl_authc_id); size = strlen(ldap_inst->sasl_authc_id); in->result = ldap_inst->sasl_authc_id; in->len = size; ret = LDAP_SUCCESS; break; case SASL_CB_PASS: log_info("got request for SASL_CB_PASS %s", ldap_inst->sasl_password); size = strlen(ldap_inst->sasl_password); in->result = ldap_inst->sasl_password; in->len = size; ret = LDAP_SUCCESS; break; default: goto cleanup; } } return ret; cleanup: in->result = NULL; in->len = 0; return LDAP_OTHER; } #endif /* LDAP_USE_GSSAPI */ #endif dhcp-4.4.1/server/ldap_casa.c000644 000765 000024 00000012600 13243301226 016273 0ustar00tmarkstaff000000 000000 /* ldap_casa.c CASA routines for DHCPD... */ /* Copyright (c) 2006 Novell, Inc. * All rights reserved. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * 1.Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2.Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3.Neither the name of ISC, ISC DHCP, nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * THIS SOFTWARE IS PROVIDED BY INTERNET SYSTEMS CONSORTIUM AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ISC OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * This file was written by S Kalyanasundaram */ /* * Copyright (c) 2004-2010 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1995-2003 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ */ #if defined(LDAP_CASA_AUTH) #include "dhcpd.h" #include "ldap_casa.h" #include #include int load_casa (void) { if( !(casaIDK = dlopen(MICASA_LIB,RTLD_LAZY))) return 0; p_miCASAGetCredential = (CASA_GetCredential_T) dlsym(casaIDK, "miCASAGetCredential"); p_miCASASetCredential = (CASA_SetCredential_T) dlsym(casaIDK, "miCASASetCredential"); p_miCASARemoveCredential = (CASA_RemoveCredential_T) dlsym(casaIDK, "miCASARemoveCredential"); if((p_miCASAGetCredential == NULL) || (p_miCASASetCredential == NULL) || (p_miCASARemoveCredential == NULL)) { if(casaIDK) dlclose(casaIDK); casaIDK = NULL; p_miCASAGetCredential = NULL; p_miCASASetCredential = NULL; p_miCASARemoveCredential = NULL; return 0; } else return 1; } static void release_casa(void) { if(casaIDK) { dlclose(casaIDK); casaIDK = NULL; } p_miCASAGetCredential = NULL; p_miCASASetCredential = NULL; p_miCASARemoveCredential = NULL; } int load_uname_pwd_from_miCASA (char **ldap_username, char **ldap_password) { int result = 0; uint32_t credentialtype = SSCS_CRED_TYPE_SERVER_F; SSCS_BASIC_CREDENTIAL credential; SSCS_SECRET_ID_T applicationSecretId; char *tempVar = NULL; const char applicationName[10] = "dhcp-ldap"; if ( load_casa() ) { memset(&credential, 0, sizeof(SSCS_BASIC_CREDENTIAL)); memset(&applicationSecretId, 0, sizeof(SSCS_SECRET_ID_T)); applicationSecretId.len = strlen(applicationName) + 1; memcpy (applicationSecretId.id, applicationName, applicationSecretId.len); credential.unFlags = USERNAME_TYPE_CN_F; result = p_miCASAGetCredential (0, &applicationSecretId,NULL,&credentialtype, &credential,NULL); if(credential.unLen) { tempVar = dmalloc (credential.unLen + 1, MDL); if (!tempVar) log_fatal ("no memory for ldap_username"); memcpy(tempVar , credential.username, credential.unLen); *ldap_username = tempVar; tempVar = dmalloc (credential.pwordLen + 1, MDL); if (!tempVar) log_fatal ("no memory for ldap_password"); memcpy(tempVar, credential.password, credential.pwordLen); *ldap_password = tempVar; #if defined (DEBUG_LDAP) log_info ("Authentication credential taken from CASA"); #endif release_casa(); return 1; } else { release_casa(); return 0; } } else return 0; //casa libraries not loaded } #endif /* LDAP_CASA_AUTH */ dhcp-4.4.1/server/ldap_krb_helper.c000644 000765 000024 00000016524 13243301226 017512 0ustar00tmarkstaff000000 000000 /* ldap_krb_helper.c Helper routings for allowing LDAP to read configuration with GSSAPI/krb auth */ /* * Copyright (c) 2015-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 2014 William B. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of The Internet Software Consortium nor the names * of its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * This helper was written by William Brown , * inspired by krb5_helper.c from bind-dyndb-ldap by Simo Sorce (Redhat) */ #if defined(LDAP_USE_GSSAPI) #include "dhcpd.h" #include "ldap_krb_helper.h" #include #include #include #include #define KRB_DEFAULT_KEYTAB "FILE:/etc/dhcp/dhcp.keytab" #define KRB_MIN_TIME 300 #define CHECK_KRB5(ctx, err, msg, ...) \ do { \ if (err) { \ const char * errmsg = krb5_get_error_message(ctx, err); \ log_error("Err: %s -> %s\n", msg, errmsg); \ result = ISC_R_FAILURE; \ goto cleanup; \ } \ } while (0) #define CHECK(ret_code, msg) \ if (ret_code != 0) { \ log_error("Error, %i %s\n", ret_code, msg); \ goto cleanup; \ } static isc_result_t check_credentials(krb5_context context, krb5_ccache ccache, krb5_principal service) { char *realm = NULL; krb5_creds creds; krb5_creds mcreds; krb5_error_code krberr; krb5_timestamp now; isc_result_t result = ISC_R_FAILURE; memset(&mcreds, 0, sizeof(mcreds)); memset(&creds, 0, sizeof(creds)); krberr = krb5_get_default_realm(context, &realm); CHECK_KRB5(context, krberr, "Failed to retrieve default realm"); krberr = krb5_build_principal(context, &mcreds.server, strlen(realm), realm, "krbtgt", realm, NULL); CHECK_KRB5(context, krberr, "Failed to build 'krbtgt/REALM' principal"); mcreds.client = service; krberr = krb5_cc_retrieve_cred(context, ccache, 0, &mcreds, &creds); if (krberr) { const char * errmsg = krb5_get_error_message(context, krberr); log_error("Credentials are not present in cache (%s)\n", errmsg); krb5_free_error_message(context, errmsg); result = ISC_R_FAILURE; goto cleanup; } CHECK_KRB5(context, krberr, "Credentials are not present in cache "); krberr = krb5_timeofday(context, &now); CHECK_KRB5(context, krberr, "Failed to get time of day"); if (now > (creds.times.endtime + KRB_MIN_TIME)) { log_error("Credentials cache expired"); result = ISC_R_FAILURE; goto cleanup; } else { char buf[255]; char fill = ' '; krb5_timestamp_to_sfstring(creds.times.endtime, buf, 16, &fill); log_info("Credentials valid til %s\n", buf); } result = ISC_R_SUCCESS; cleanup: krb5_free_cred_contents(context, &creds); if (mcreds.server) krb5_free_principal(context, mcreds.server); if (realm) krb5_free_default_realm(context, realm); return result; } isc_result_t krb5_get_tgt(const char *principal, const char *keyfile) { isc_result_t result = ISC_R_FAILURE; char *ccname = NULL; krb5_context context = NULL; krb5_error_code krberr; krb5_ccache ccache = NULL; krb5_principal kprincpw = NULL; krb5_creds my_creds; krb5_creds * my_creds_ptr = NULL; krb5_get_init_creds_opt options; krb5_keytab keytab = NULL; int ret; if (keyfile == NULL || keyfile[0] == '\0') { keyfile = KRB_DEFAULT_KEYTAB; log_info("Using default keytab %s\n", keyfile); } else { if (strncmp(keyfile, "FILE:", 5) != 0) { log_error("Unknown keytab path format: Does it start with FILE:?\n"); return ISC_R_FAILURE; } } krberr = krb5_init_context(&context); CHECK_KRB5(NULL, krberr, "Kerberos context initialization failed"); result = ISC_R_SUCCESS; ccname = "MEMORY:dhcp_ld_krb5_cc"; log_info("Using ccache %s\n" , ccname); ret = setenv("KRB5CCNAME", ccname, 1); if (ret == -1) { log_error("Failed to setup environment\n"); result = ISC_R_FAILURE; goto cleanup; } krberr = krb5_cc_resolve(context, ccname, &ccache); CHECK_KRB5(context, krberr, "Couldnt resolve ccache '%s'", ccname); krberr = krb5_parse_name(context, principal, &kprincpw); CHECK_KRB5(context, krberr, "Failed to parse princ '%s'", princpal); result = check_credentials(context, ccache, kprincpw); if (result == ISC_R_SUCCESS) { log_info("Found valid kerberos credentials\n"); goto cleanup; } else { log_error("No valid krb5 credentials\n"); } krberr = krb5_kt_resolve(context, keyfile, &keytab); CHECK_KRB5(context, krberr, "Failed to resolve kt files '%s'\n", keyfile); memset(&my_creds, 0, sizeof(my_creds)); memset(&options, 0, sizeof(options)); krb5_get_init_creds_opt_set_tkt_life(&options, KRB_MIN_TIME * 2); krb5_get_init_creds_opt_set_address_list(&options, NULL); krb5_get_init_creds_opt_set_forwardable(&options, 0); krb5_get_init_creds_opt_set_proxiable(&options, 0); krberr = krb5_get_init_creds_keytab(context, &my_creds, kprincpw, keytab, 0, NULL, &options); CHECK_KRB5(context, krberr, "Failed to get initial credentials TGT\n"); my_creds_ptr = &my_creds; krberr = krb5_cc_initialize(context, ccache, kprincpw); CHECK_KRB5(context, krberr, "Failed to init ccache\n"); krberr = krb5_cc_store_cred(context, ccache, &my_creds); CHECK_KRB5(context, krberr, "Failed to store credentials\n"); result = ISC_R_SUCCESS; log_info("Successfully init krb tgt %s", principal); cleanup: if (ccache) krb5_cc_close(context, ccache); if (keytab) krb5_kt_close(context, keytab); if (kprincpw) krb5_free_principal(context, kprincpw); if (my_creds_ptr) krb5_free_cred_contents(context, &my_creds); if (context) krb5_free_context(context); return result; } #endif /* defined(LDAP_USE_GSSAPI) */ dhcp-4.4.1/server/leasechain.c000644 000765 000024 00000045573 13243301226 016477 0ustar00tmarkstaff000000 000000 /* leasechain.c Additional support for in-memory database support */ /* * Copyright (c) 2015-2017 by 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ /*! \file server\leasechaing.c * * \page leasechain structures overview * * A brief description of the leasechain structures * * This file provides additional data structures for a leasecain to * provide faster access to leases on the queues associated with a pool * than a linear walk. Each pool has a set of queues: active, free, backup, * expired and abandoned to track leases as they are handed out and returned. * The original code use a simply linear list for each of those pools but * this can present performance issues if the pool is large and the lists are * long. * This code adds an array on top of the list allowing us to search the list * in a binary fashion instead of a linear walk. * * \verbatim * leasechain * +------------+ +-------+-------+-------+-------+ * | lease list |--> | lease | lease | lease | lease |.... * | start | | ptr | ptr | ptr | ptr | * | end | +-------+-------+-------+-------+ * | max | | | * +------------+ V V * +-------+ +-------+ * | lease | | lease | * | | | | * | next |->| next |->NULL * NULL<- | prev |<-| prev | * +-------+ +-------+ * * The linked list is maintained in an ordered state. Inserting an entry is * accomplished by doing a binary search on the array to find the proper place * in the list and then updating the pointers in the linked list to include the * new entry. The entry is added into the array by copying the remainder of * the array to provide space for the new entry. * Removing an entry is the reverse. * The arrays for the queues will be pre-allocated but not all of them will be * large enough to hold all of the leases. If additional space is required the * array will be grown. */ #include "dhcpd.h" #if defined (BINARY_LEASES) /* Default number number of lease pointers to add to the leasechain array * everytime it grows beyond the current size */ #define LC_GROWTH_DELTA 256 /*! * * \brief Check if leasechain isn't empty * * \param lc The leasechain to check * * \return 1 if leasechain isn't empty */ int lc_not_empty( struct leasechain *lc ) { #if defined (DEBUG_BINARY_LEASES) log_debug("LC empty check %s:%d", MDL); INSIST(lc != NULL); #endif return (lc->nelem > 0 ? 1 : 0); } /*! * * \brief Get the first lease from a leasechain * * \param lc The leasechain to check * * \return A pointer to the first lease from a lease chain, or NULL if none found */ struct lease * lc_get_first_lease(struct leasechain *lc) { #if defined (DEBUG_BINARY_LEASES) log_debug("LC Get first %s:%d", MDL); INSIST(lc != NULL); INSIST(lc->total >= lc->nelem); #endif if (lc->nelem > 0) { return (lc->list)[0]; } return (NULL); } /*! * * \brief Get the next lease from the chain, based on the lease passed in. * * \param lc The leasechain to check * \param lp The lease to start from * * \return The next lease in the ordered list after lp */ struct lease * lc_get_next(struct leasechain *lc, struct lease *lp) { #if defined (DEBUG_BINARY_LEASES) log_debug("LC Get next %s:%d", MDL); INSIST(lc != NULL); INSIST(lp != NULL); #endif return lp->next; } /*! * * \brief Find the best position for inserting a lease * * Given a potential range of the array to insert the lease into this routine * will recursively examine the range to find the proper place in which to * insert the lease. * * \param lc The leasechain to add the lease to * \param lp The lease to insert * \param min The minium index of the potential range for insertion * \param max The maximum index of the potential range for insertion * * \return The index of the array entry to insert the lease */ size_t lc_binary_search_insert_point(struct leasechain *lc, struct lease *lp, size_t min, size_t max) { size_t mid_index = ((max - min)/2) + min; if ((lc->list[mid_index]->sort_time > lp->sort_time) || ((lc->list[mid_index]->sort_time == lp->sort_time) && (lc->list[mid_index]->sort_tiebreaker > lp->sort_tiebreaker))) { if (mid_index == min) { /* insert in the min position, as sort_time is larger */ return (min); } /* try again with lower half of list */ return (lc_binary_search_insert_point(lc, lp, min, mid_index - 1)); } else if ((lc->list[mid_index]->sort_time < lp->sort_time) || ((lc->list[mid_index]->sort_time == lp->sort_time) && (lc->list[mid_index]->sort_tiebreaker < lp->sort_tiebreaker))) { if (mid_index == max) { /* insert in mid_index + 1 as sort_time is smaller */ return (mid_index+1); } /* try again with upper half of list */ return (lc_binary_search_insert_point(lc, lp, mid_index + 1, max)); } /* sort_time and sort_tiebreaker match, so insert in this position */ return (mid_index); } /*! * * \brief Find an exact match for a lease * * Given a potential range of the array to search this routine * will recursively examine the range to find the proper lease * * \param lc The leasechain to check * \param lp The lease to find * \param min The minium index of the search range * \param max The maximum index of the search range * * \return The index of the array entry for the lease, SIZE_MAX if the lease * wasn't found */ size_t lc_binary_search_lease(struct leasechain *lc, struct lease *lp, size_t min, size_t max) { size_t mid_index; size_t i; if (max < min) { /* lease not found */ return (SIZE_MAX); } mid_index = ((max - min)/2) + min; if ((lc->list[mid_index]->sort_time > lp->sort_time) || ((lc->list[mid_index]->sort_time == lp->sort_time) && (lc->list[mid_index]->sort_tiebreaker > lp->sort_tiebreaker))) { if (mid_index == min) { /* lease not found */ return (SIZE_MAX); } /* try the lower half of the list */ return (lc_binary_search_lease(lc, lp, min, mid_index - 1)); } else if ((lc->list[mid_index]->sort_time < lp->sort_time) || ((lc->list[mid_index]->sort_time == lp->sort_time) && (lc->list[mid_index]->sort_tiebreaker < lp->sort_tiebreaker))) { /* try the upper half of the list */ return (lc_binary_search_lease(lc, lp, mid_index + 1, max)); } /* * As sort_time/sort_tiebreaker may not be unique in the list, once we * find a match, we need to look before and after from this position * for all matching sort_time/sort_tiebreaker until we find the exact * lease or until no matching lease is found */ if (lp == lc->list[mid_index]) { return (mid_index); } /* Check out entries below the mid_index */ if (mid_index > min) { /* We will break out of the loop if we either go past the * canddiates or hit the end of the range when i == min. As * i is unsigned we can't check it in the for loop itself. */ for (i = mid_index - 1; ; i--) { if (lp == lc->list[i]) { return (i); } /* Are we done with this range? */ if ((i == min) || ((lc->list[i]->sort_time != lp->sort_time) || ((lc->list[i]->sort_time == lp->sort_time) && (lc->list[i]->sort_tiebreaker != lp->sort_tiebreaker)))) { break; } } } /* Check out entries above the mid_index */ if (mid_index < max) { /* We will break out of the loop if we either go past the * canddiates or hit the end of the range when i == max. */ for (i = mid_index + 1; i <= max; i++) { if (lp == lc->list[i]) { return (i); } if ((lc->list[i]->sort_time != lp->sort_time) || ((lc->list[i]->sort_time == lp->sort_time) && (lc->list[i]->sort_tiebreaker != lp->sort_tiebreaker))) { break; } } } /* Lease not found */ return (SIZE_MAX); } /*! * * \brief Increase the size of the array for the lease chain * * \param lc The leasechain to expand * * If we are unable to allocate memory we log a fatal error. There's * not much else to do as we can't figure out where to put the lease. * * If we can allocate memory we copy the old lease chain to the new * lease chain and free the old. */ void lc_grow_chain(struct leasechain *lc) { #if defined (DEBUG_BINARY_LEASES) log_debug("LC grow lease chain max was %zu, %s:%d", lc->total, MDL); #endif void *p; size_t temp_size; if (lc->growth == 0) temp_size = lc->total + LC_GROWTH_DELTA; else temp_size = lc->total + lc->growth; /* try to allocate the memory */ p = dmalloc(sizeof(struct lease *) * temp_size, MDL); if (p == NULL) { log_fatal("LC grow, unable to allocated memory %s:%d", MDL); } /* Success, copy the lease chain and install the new one */ if (lc->list != NULL) { memcpy(p, lc->list, sizeof(struct lease *) * lc->nelem); dfree(lc->list, MDL); } lc->list = (struct lease **) p; lc->total = temp_size; return; } /*! * * \brief Link a lease to a lease chain position * * This function may increase the size of the lease chain if necessary and will * probably need to move entries in the lease chain around. * * \param lc The leasechain to update * \param lp The lease to insert * \param n The position in which to insert the lease * */ void lc_link_lcp(struct leasechain *lc, struct lease *lp, size_t n) { #if defined (DEBUG_BINARY_LEASES) log_debug("LC link lcp %s:%d", MDL); INSIST (lc != NULL); INSIST (lp != NULL); #endif if (lc->nelem == lc->total) { lc_grow_chain(lc); } #if defined (DEBUG_BINARY_LEASES) log_debug("LC Link lcp position %zu, elem %zu, %s:%d", n, lc->nelem, MDL); #endif /* create room for the new pointer */ if (n < lc->nelem) { #if defined (DEBUG_BINARY_LEASES) log_debug("LC link lcp moving position %zu, moving %zu. %s:%d", n, (lc->nelem-n), MDL); #endif memmove(lc->list + n + 1, lc->list + n, sizeof(struct lease *) * (lc->nelem-n)); } /* clean any stale pointer info from this position before calling * lease_reference as it won't work if pointer is not NULL */ lc->list[n] = NULL; lease_reference(&(lc->list[n]), lp, MDL); lc->nelem++; lp->lc = lc; return; } /*! * * \brief Insert the lease at the specified position in both the lease chain * and the linked list * * This function may increase the size of the lease chain if necessary and will * probably need to move entries in the lease chain around. * \param lc The leasechain to update * \param lp The lease to insert * \param n The position in which to insert the lease * */ void lc_add_lease_pos(struct leasechain *lc, struct lease *lp, size_t pos) { #if defined (DEBUG_BINARY_LEASES) log_debug("LC Add lease position %zu, %s:%d", pos, MDL); INSIST (lc != NULL); INSIST (lp != NULL); #endif lc_link_lcp(lc, lp, pos); #if 0 /* this shoudln't be necessary, if we still have pointers on * the lease being inserted things are broken */ if (lp->prev) { lease_dereference(&lp->prev, MDL); } if (lp->next) { lease_dereference(&lp->next, MDL); } #endif /* not the first element? */ if (pos > 0) { if (lc->list[pos-1]->next) { lease_dereference(&(lc->list[pos-1]->next), MDL); } lease_reference(&(lc->list[pos-1]->next), lp, MDL); lease_reference(&lp->prev, lc->list[pos-1], MDL ); } /* not the last element? we've already bumped nelem when linking * into the lease chain so nelem should never be zero here */ if (pos < (lc->nelem-1)) { if (lc->list[pos+1]->prev) { lease_dereference(&(lc->list[pos+1]->prev), MDL); } lease_reference(&(lc->list[pos+1]->prev), lp, MDL); lease_reference(&lp->next, lc->list[pos+1], MDL); } return; } #ifdef POINTER_DEBUG /*! * * \brief Debug only code, check the lease to verify it is sorted * * \param lc The leasechain to verify * * Calls log_fatal if the leasechain is not properly sorted */ void lc_check_lc_sort_order(struct leasechain *lc) { size_t i; TIME t = 0; long int tiebreak = 0; log_debug("LC check sort %s:%d", MDL); for (i = 0; i < lc->nelem; i++ ) { if ((lc->list[i]->sort_time < t) || ((lc->list[i]->sort_time == t) && (lc->list[i]->tiebreaker < tiebreaker))) { if (i > 0) { print_lease(lc->list[i-1]); } print_lease(lc->list[i]); if (i < lc->nelem - 1) { print_lease(lc->list[i+1]); } log_fatal("lc[%p] not sorted properly", lc); } t = lc->list[i]->sort_time; tiebreak = lc->list[i]->sort_tiebreaker; } } #endif /*! * * \brief Add a lease into the sorted lease and lease chain * The sort_time is set by the caller while the sort_tiebreaker is set here * The value doesn't much matter as long as it prvoides a way to have different * values in most of the leases. * * When choosing a value for tiebreak we choose: * 0 for the first lease in the queue * 0 if the lease is going to the end of the queue with a sort_time greater * than that of the current last lease * previous tiebreaker + 1 if it is going to the end of the queue with a * sort_time equal to that of the current last lease * random if none of the above fit * * During startup when we can take advantage of the fact that leases may already * be sorted and so check the end of the list to see if we can simply add the * lease to the end. * * \param lc The leasechain in which to insert the lease * \param lp The lease to insert * */ void lc_add_sorted_lease(struct leasechain *lc, struct lease *lp) { size_t pos; #if defined (DEBUG_BINARY_LEASES) log_debug("LC add sorted %s:%d", MDL); INSIST (lc != NULL); INSIST (lp != NULL); #endif if (lc->nelem == 0) { /* The first lease start with a tiebreak of 0 and add it at * the first position */ lp->sort_tiebreaker = 0; lc_add_lease_pos(lc, lp, 0); /* log_debug("LC add sorted done, %s:%d", MDL); */ return; } if (lp->sort_time > lc->list[lc->nelem-1]->sort_time) { /* Adding to end of queue, with a different sort time */ lp->sort_tiebreaker = 0; pos = lc->nelem; } else if (lp->sort_time == lc->list[lc->nelem-1]->sort_time) { /* Adding to end of queue, with the same sort time */ if (lc->list[lc->nelem-1]->sort_tiebreaker < LONG_MAX) lp->sort_tiebreaker = lc->list[lc->nelem-1]->sort_tiebreaker+1; else lp->sort_tiebreaker = LONG_MAX; pos = lc->nelem; } else { /* Adding somewhere in the queue, just pick a random value */ lp->sort_tiebreaker = random(); pos = lc_binary_search_insert_point(lc, lp, 0, lc->nelem - 1); } /* Finally add it to the queue */ lc_add_lease_pos(lc, lp, pos); #if defined (DEBUG_BINARY_LEASES) log_debug("LC add sorted complete position %zu, elements %zu, %s:%d", pos, lc->nelem, MDL); #endif #ifdef POINTER_DEBUG lc_check_lc_sort_order(lc); #endif } /*! * * \brief Remove the Nth pointer from a leasechain structure and update counters. * The pointers in the array will be moved to fill in the hole if necessary. * * \param lc The lease chain to update * \param n the entry to remove from the lease chain */ void lc_unlink_lcp(struct leasechain *lc, size_t n) { #if defined (DEBUG_BINARY_LEASES) log_debug("LC unlink lcp %s:%d", MDL); /* element index to remove must be less than the number of elements present */ INSIST(n < lc->nelem); #endif /* Clear the pointer from the lease back to the LC */ lc->list[n]->lc = NULL; /* Clear the pointer from the LC to the lease */ lease_dereference(&(lc->list[n]), MDL); /* memove unless we are removing the last element */ if ((lc->nelem-1) > n) { memmove(lc->list + n, lc->list + n + 1, sizeof(struct lease *) * (lc->nelem-1-n)); } lc->nelem--; } /*! * * \brief Remove a lease from a specific position. This will first unlink * the lease from the lease chain and then update the linked list. * * \param lc The lease chain to update * \param pos the entry to remove from the lease chain */ void lc_unlink_lease_pos(struct leasechain *lc, size_t pos) { #if defined (DEBUG_BINARY_LEASES) INSIST(lc != NULL); #endif struct lease *lp = NULL; lease_reference(&lp, lc->list[pos], MDL); /* unlink from lease chain list */ lc_unlink_lcp(lc, pos); /* unlink from the linked list */ if (lp->next) { lease_dereference(&lp->next->prev, MDL); if (lp->prev) lease_reference(&lp->next->prev, lp->prev, MDL); } if (lp->prev) { lease_dereference(&lp->prev->next, MDL); if (lp->next) lease_reference(&lp->prev->next, lp->next, MDL); lease_dereference(&lp->prev, MDL); } if (lp->next) { lease_dereference(&lp->next, MDL); } lease_dereference(&lp, MDL); } /*! * * \brief Find a lease in the lease chain and then remove it * If we can't find the lease on the given lease chain it's a fatal error. * * \param lc The lease chain to update * \param lp The lease to remove */ void lc_unlink_lease(struct leasechain *lc, struct lease *lp) { #if defined (DEBUG_BINARY_LEASES) log_debug("LC unlink lease %s:%d", MDL); INSIST(lc != NULL); INSIST(lc->list != NULL); INSIST(lp != NULL ); INSIST(lp->lc != NULL ); INSIST(lp->lc == lc ); #endif size_t pos = lc_binary_search_lease(lc, lp, 0, lc->nelem-1); if (pos == SIZE_MAX) { /* fatal, lease not found in leasechain */ log_fatal("Lease with binding state %s not on its queue.", (lp->binding_state < 1 || lp->binding_state > FTS_LAST) ? "unknown" : binding_state_names[lp->binding_state - 1]); } lc_unlink_lease_pos(lc, pos); } /*! * * \brief Unlink all the leases in the lease chain and free the * lease chain structure. The leases will be freed if and when * any other references to them are cleared. * * \param lc the lease chain to clear */ void lc_delete_all(struct leasechain *lc) { size_t i; if (lc->nelem > 0) { /* better to delete from the last one, to avoid the memmove */ for (i = lc->nelem - 1; ; i--) { lc_unlink_lease_pos(lc, i); if (i == 0) { break; } } } /* and then get rid of the list itself */ if (lc->list != NULL) { dfree(lc->list, MDL); lc->list = NULL; } lc->total = 0; lc->nelem = 0; } /*! * * \brief Set the growth value. This is the number of elements to * add to the array whenever it needs to grow. * * \param lc the lease chain to set up * \param growth the growth value to use */ void lc_init_growth(struct leasechain *lc, size_t growth) { lc->growth = growth; } #endif /* #if defined (BINARY_LEASES) */ dhcp-4.4.1/server/Makefile.am000644 000765 000024 00000001726 13243301226 016263 0ustar00tmarkstaff000000 000000 # We want to build this directory first, before descending into tests subdir. # The reason is that ideally the tests should link existing objects from this # directory. That eliminates any discrepancies between tested code and # production code. Sadly, we are not there yet. SUBDIRS = . tests AM_CPPFLAGS = -I.. -DLOCALSTATEDIR='"@localstatedir@"' dist_sysconf_DATA = dhcpd.conf.example sbin_PROGRAMS = dhcpd dhcpd_SOURCES = dhcpd.c dhcp.c bootp.c confpars.c db.c class.c failover.c \ omapi.c mdb.c stables.c salloc.c ddns.c dhcpleasequery.c \ dhcpv6.c mdb6.c ldap.c ldap_casa.c leasechain.c ldap_krb_helper.c dhcpd_CFLAGS = $(LDAP_CFLAGS) dhcpd_LDADD = ../common/libdhcp.@A@ ../omapip/libomapi.@A@ \ ../dhcpctl/libdhcpctl.@A@ \ $(BINDLIBIRSDIR)/libirs.@A@ \ $(BINDLIBDNSDIR)/libdns.@A@ \ $(BINDLIBISCCFGDIR)/libisccfg.@A@ \ $(BINDLIBISCDIR)/libisc.@A@ $(LDAP_LIBS) man_MANS = dhcpd.8 dhcpd.conf.5 dhcpd.leases.5 EXTRA_DIST = $(man_MANS) dhcp-4.4.1/server/mdb.c000644 000765 000024 00000267602 13243301226 015144 0ustar00tmarkstaff000000 000000 /* mdb.c Server-specific in-memory database support. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1996-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #include "omapip/hash.h" struct subnet *subnets; struct shared_network *shared_networks; host_hash_t *host_hw_addr_hash; host_hash_t *host_uid_hash; host_hash_t *host_name_hash; lease_id_hash_t *lease_uid_hash; lease_ip_hash_t *lease_ip_addr_hash; lease_id_hash_t *lease_hw_addr_hash; /* * We allow users to specify any option as a host identifier. * * Any host is uniquely identified by the combination of * option type & option data. * * We expect people will only use a few types of options as host * identifier. Because of this, we store a list with an entry for * each option type. Each of these has a hash table, which contains * hash of the option data. * * For v6 we also include a relay count - this specifies which * relay to check for the requested option. As each different * value of relays creates a new instance admins should use the * same value across each option for all host-identifers. * A value of 0 indicates that we aren't doing relay options * and should simply look in the current option list. */ typedef struct host_id_info { struct option *option; host_hash_t *values_hash; int relays; struct host_id_info *next; } host_id_info_t; static host_id_info_t *host_id_info = NULL; int numclasseswritten; omapi_object_type_t *dhcp_type_host; isc_result_t enter_class(cd, dynamicp, commit) struct class *cd; int dynamicp; int commit; { if (!collections -> classes) { /* A subclass with no parent is invalid. */ if (cd->name == NULL) return DHCP_R_INVALIDARG; class_reference (&collections -> classes, cd, MDL); } else if (cd->name != NULL) { /* regular class */ struct class *c = 0; if (find_class(&c, cd->name, MDL) != ISC_R_NOTFOUND) { class_dereference(&c, MDL); return ISC_R_EXISTS; } /* Find the tail. */ for (c = collections -> classes; c -> nic; c = c -> nic) /* nothing */ ; class_reference (&c -> nic, cd, MDL); } if (dynamicp && commit) { const char *name = cd->name; if (name == NULL) { name = cd->superclass->name; } write_named_billing_class ((const unsigned char *)name, 0, cd); if (!commit_leases ()) return ISC_R_IOERROR; } return ISC_R_SUCCESS; } /* Variable to check if we're starting the server. The server will init as * starting - but just to be safe start out as false to avoid triggering new * special-case code * XXX: There is actually a server_startup state...which is never entered... */ #define SS_NOSYNC 1 #define SS_QFOLLOW 2 static int server_starting = 0; static int find_uid_statement (struct executable_statement *esp, void *vp, int condp) { struct executable_statement **evp = vp; if (esp -> op == supersede_option_statement && esp -> data.option && (esp -> data.option -> option -> universe == &dhcp_universe) && (esp -> data.option -> option -> code == DHO_DHCP_CLIENT_IDENTIFIER)) { if (condp) { log_error ("dhcp client identifier may not be %s", "specified conditionally."); } else if (!(*evp)) { executable_statement_reference (evp, esp, MDL); return 1; } else { log_error ("only one dhcp client identifier may be %s", "specified"); } } return 0; } static host_id_info_t * find_host_id_info(unsigned int option_code, int relays) { host_id_info_t *p; for (p = host_id_info; p != NULL; p = p->next) { if ((p->option->code == option_code) && (p->relays == relays)) { break; } } return p; } /* Debugging code */ #if 0 isc_result_t print_host(const void *name, unsigned len, void *value) { struct host_decl *h; printf("--------------\n"); printf("name:'%s'\n", print_hex_1(len, name, 60)); printf("len:%d\n", len); h = (struct host_decl *)value; printf("host @%p is '%s'\n", h, h->name); return ISC_R_SUCCESS; } void hash_print_hosts(struct hash_table *h) { hash_foreach(h, print_host); printf("--------------\n"); } #endif /* 0 */ void change_host_uid(struct host_decl *host, const char *uid, int len) { /* XXX: should consolidate this type of code throughout */ if (host_uid_hash == NULL) { if (!host_new_hash(&host_uid_hash, HOST_HASH_SIZE, MDL)) { log_fatal("Can't allocate host/uid hash"); } } /* * Remove the old entry, if one exists. */ if (host->client_identifier.data != NULL) { host_hash_delete(host_uid_hash, host->client_identifier.data, host->client_identifier.len, MDL); data_string_forget(&host->client_identifier, MDL); } /* * Set our new value. */ memset(&host->client_identifier, 0, sizeof(host->client_identifier)); host->client_identifier.len = len; if (!buffer_allocate(&host->client_identifier.buffer, len, MDL)) { log_fatal("Can't allocate uid buffer"); } host->client_identifier.data = host->client_identifier.buffer->data; memcpy((char *)host->client_identifier.data, uid, len); /* * And add to hash. */ host_hash_add(host_uid_hash, host->client_identifier.data, host->client_identifier.len, host, MDL); } isc_result_t enter_host (hd, dynamicp, commit) struct host_decl *hd; int dynamicp; int commit; { struct host_decl *hp = (struct host_decl *)0; struct host_decl *np = (struct host_decl *)0; struct executable_statement *esp; host_id_info_t *h_id_info; if (!host_name_hash) { if (!host_new_hash(&host_name_hash, HOST_HASH_SIZE, MDL)) log_fatal ("Can't allocate host name hash"); host_hash_add (host_name_hash, (unsigned char *)hd -> name, strlen (hd -> name), hd, MDL); } else { host_hash_lookup (&hp, host_name_hash, (unsigned char *)hd -> name, strlen (hd -> name), MDL); /* If it's deleted, we can supersede it. */ if (hp && (hp -> flags & HOST_DECL_DELETED)) { host_hash_delete (host_name_hash, (unsigned char *)hd -> name, strlen (hd -> name), MDL); /* If the old entry wasn't dynamic, then we always have to keep the deletion. */ if (hp -> flags & HOST_DECL_STATIC) { hd -> flags |= HOST_DECL_STATIC; } host_dereference (&hp, MDL); } /* If we are updating an existing host declaration, we can just delete it and add it again. */ if (hp && hp == hd) { host_dereference (&hp, MDL); delete_host (hd, 0); if (!write_host (hd)) return ISC_R_IOERROR; hd -> flags &= ~HOST_DECL_DELETED; } /* If there isn't already a host decl matching this address, add it to the hash table. */ if (!hp) { host_hash_add (host_name_hash, (unsigned char *)hd -> name, strlen (hd -> name), hd, MDL); } else { /* XXX actually, we have to delete the old one XXX carefully and replace it. Not done yet. */ host_dereference (&hp, MDL); return ISC_R_EXISTS; } } if (hd -> n_ipaddr) host_dereference (&hd -> n_ipaddr, MDL); if (!hd -> type) hd -> type = dhcp_type_host; if (hd -> interface.hlen) { if (!host_hw_addr_hash) { if (!host_new_hash(&host_hw_addr_hash, HOST_HASH_SIZE, MDL)) log_fatal ("Can't allocate host/hw hash"); } else { /* If there isn't already a host decl matching this address, add it to the hash table. */ host_hash_lookup (&hp, host_hw_addr_hash, hd -> interface.hbuf, hd -> interface.hlen, MDL); } if (!hp) host_hash_add (host_hw_addr_hash, hd -> interface.hbuf, hd -> interface.hlen, hd, MDL); else { /* If there was already a host declaration for this hardware address, add this one to the end of the list. */ for (np = hp; np -> n_ipaddr; np = np -> n_ipaddr) ; host_reference (&np -> n_ipaddr, hd, MDL); host_dereference (&hp, MDL); } } /* See if there's a statement that sets the client identifier. This is a kludge - the client identifier really shouldn't be set with an executable statement. */ esp = NULL; if (executable_statement_foreach (hd->group->statements, find_uid_statement, &esp, 0)) { (void) evaluate_option_cache (&hd->client_identifier, NULL, NULL, NULL, NULL, NULL, &global_scope, esp->data.option, MDL); } /* If we got a client identifier, hash this entry by client identifier. */ if (hd -> client_identifier.len) { /* If there's no uid hash, make one; otherwise, see if there's already an entry in the hash for this host. */ if (!host_uid_hash) { if (!host_new_hash(&host_uid_hash, HOST_HASH_SIZE, MDL)) log_fatal ("Can't allocate host/uid hash"); host_hash_add (host_uid_hash, hd -> client_identifier.data, hd -> client_identifier.len, hd, MDL); } else { /* If there's already a host declaration for this client identifier, add this one to the end of the list. Otherwise, add it to the hash table. */ if (host_hash_lookup (&hp, host_uid_hash, hd -> client_identifier.data, hd -> client_identifier.len, MDL)) { /* Don't link it in twice... */ if (!np) { for (np = hp; np -> n_ipaddr; np = np -> n_ipaddr) { if (hd == np) break; } if (hd != np) host_reference (&np -> n_ipaddr, hd, MDL); } host_dereference (&hp, MDL); } else { host_hash_add (host_uid_hash, hd -> client_identifier.data, hd -> client_identifier.len, hd, MDL); } } } /* * If we use an option as our host identifier, record it here. */ if (hd->host_id_option != NULL) { /* * Look for the host identifier information for this option, * and create a new entry if there is none. */ h_id_info = find_host_id_info(hd->host_id_option->code, hd->relays); if (h_id_info == NULL) { h_id_info = dmalloc(sizeof(*h_id_info), MDL); if (h_id_info == NULL) { log_fatal("No memory for host-identifier " "option information."); } option_reference(&h_id_info->option, hd->host_id_option, MDL); if (!host_new_hash(&h_id_info->values_hash, HOST_HASH_SIZE, MDL)) { log_fatal("No memory for host-identifier " "option hash."); } h_id_info->relays = hd->relays; h_id_info->next = host_id_info; host_id_info = h_id_info; } if (host_hash_lookup(&hp, h_id_info->values_hash, hd->host_id.data, hd->host_id.len, MDL)) { /* * If this option is already present, then add * this host to the list in n_ipaddr, unless * we have already done so previously. * * XXXSK: This seems scary to me, but I don't * fully understand how these are used. * Shouldn't there be multiple lists, or * maybe we should just forbid duplicates? */ if (np == NULL) { np = hp; while (np->n_ipaddr != NULL) { np = np->n_ipaddr; } if (hd != np) { host_reference(&np->n_ipaddr, hd, MDL); } } host_dereference(&hp, MDL); } else { host_hash_add(h_id_info->values_hash, hd->host_id.data, hd->host_id.len, hd, MDL); } } if (dynamicp && commit) { if (!write_host (hd)) return ISC_R_IOERROR; if (!commit_leases ()) return ISC_R_IOERROR; } return ISC_R_SUCCESS; } isc_result_t delete_class (cp, commit) struct class *cp; int commit; { cp->flags |= CLASS_DECL_DELETED; /* do the write first as we won't be leaving it in any data structures, unlike the host objects */ if (commit) { write_named_billing_class ((unsigned char *)cp->name, 0, cp); if (!commit_leases ()) return ISC_R_IOERROR; } /* * If this is a subclass remove it from the class's hash table */ if (cp->superclass) { class_hash_delete(cp->superclass->hash, (const char *)cp->hash_string.data, cp->hash_string.len, MDL); } /* remove from collections */ unlink_class(&cp); return ISC_R_SUCCESS; } isc_result_t delete_host (hd, commit) struct host_decl *hd; int commit; { struct host_decl *hp = (struct host_decl *)0; struct host_decl *np = (struct host_decl *)0; struct host_decl *foo; int hw_head = 0, uid_head = 1; /* Don't need to do it twice. */ if (hd -> flags & HOST_DECL_DELETED) return ISC_R_SUCCESS; /* But we do need to do it once! :') */ hd -> flags |= HOST_DECL_DELETED; if (hd -> interface.hlen) { if (host_hw_addr_hash) { if (host_hash_lookup (&hp, host_hw_addr_hash, hd -> interface.hbuf, hd -> interface.hlen, MDL)) { if (hp == hd) { host_hash_delete (host_hw_addr_hash, hd -> interface.hbuf, hd -> interface.hlen, MDL); hw_head = 1; } else { np = (struct host_decl *)0; foo = (struct host_decl *)0; host_reference (&foo, hp, MDL); while (foo) { if (foo == hd) break; if (np) host_dereference (&np, MDL); host_reference (&np, foo, MDL); host_dereference (&foo, MDL); if (np -> n_ipaddr) host_reference (&foo, np -> n_ipaddr, MDL); } if (foo) { host_dereference (&np -> n_ipaddr, MDL); if (hd -> n_ipaddr) host_reference (&np -> n_ipaddr, hd -> n_ipaddr, MDL); host_dereference (&foo, MDL); } if (np) host_dereference (&np, MDL); } host_dereference (&hp, MDL); } } } /* If we got a client identifier, hash this entry by client identifier. */ if (hd -> client_identifier.len) { if (host_uid_hash) { if (host_hash_lookup (&hp, host_uid_hash, hd -> client_identifier.data, hd -> client_identifier.len, MDL)) { if (hp == hd) { host_hash_delete (host_uid_hash, hd -> client_identifier.data, hd -> client_identifier.len, MDL); uid_head = 1; } else { np = (struct host_decl *)0; foo = (struct host_decl *)0; host_reference (&foo, hp, MDL); while (foo) { if (foo == hd) break; if (np) host_dereference (&np, MDL); host_reference (&np, foo, MDL); host_dereference (&foo, MDL); if (np -> n_ipaddr) host_reference (&foo, np -> n_ipaddr, MDL); } if (foo) { host_dereference (&np -> n_ipaddr, MDL); if (hd -> n_ipaddr) host_reference (&np -> n_ipaddr, hd -> n_ipaddr, MDL); host_dereference (&foo, MDL); } if (np) host_dereference (&np, MDL); } host_dereference (&hp, MDL); } } } if (hd->host_id_option != NULL) { option_dereference(&hd->host_id_option, MDL); data_string_forget(&hd->host_id, MDL); } if (hd -> n_ipaddr) { if (uid_head && hd -> n_ipaddr -> client_identifier.len) { host_hash_add (host_uid_hash, hd -> n_ipaddr -> client_identifier.data, hd -> n_ipaddr -> client_identifier.len, hd -> n_ipaddr, MDL); } if (hw_head && hd -> n_ipaddr -> interface.hlen) { host_hash_add (host_hw_addr_hash, hd -> n_ipaddr -> interface.hbuf, hd -> n_ipaddr -> interface.hlen, hd -> n_ipaddr, MDL); } host_dereference (&hd -> n_ipaddr, MDL); } if (host_name_hash) { if (host_hash_lookup (&hp, host_name_hash, (unsigned char *)hd -> name, strlen (hd -> name), MDL)) { if (hp == hd && !(hp -> flags & HOST_DECL_STATIC)) { host_hash_delete (host_name_hash, (unsigned char *)hd -> name, strlen (hd -> name), MDL); } host_dereference (&hp, MDL); } } if (commit) { if (!write_host (hd)) return ISC_R_IOERROR; if (!commit_leases ()) return ISC_R_IOERROR; } return ISC_R_SUCCESS; } int find_hosts_by_haddr (struct host_decl **hp, int htype, const unsigned char *haddr, unsigned hlen, const char *file, int line) { struct hardware h; #if defined(LDAP_CONFIGURATION) int ret; if ((ret = find_haddr_in_ldap (hp, htype, hlen, haddr, file, line))) return ret; #endif h.hlen = hlen + 1; h.hbuf [0] = htype; memcpy (&h.hbuf [1], haddr, hlen); return host_hash_lookup (hp, host_hw_addr_hash, h.hbuf, h.hlen, file, line); } int find_hosts_by_uid (struct host_decl **hp, const unsigned char *data, unsigned len, const char *file, int line) { return host_hash_lookup (hp, host_uid_hash, data, len, file, line); } int find_hosts_by_option(struct host_decl **hp, struct packet *packet, struct option_state *opt_state, const char *file, int line) { host_id_info_t *p; struct option_cache *oc; struct data_string data; int found; struct packet *relay_packet; struct option_state *relay_state; #if defined(LDAP_CONFIGURATION) if ((found = find_client_in_ldap (hp, packet, opt_state, file, line))) return found; #endif for (p = host_id_info; p != NULL; p = p->next) { relay_packet = packet; relay_state = opt_state; /* If this option block is for a relay (relays != 0) * and we are processing the main options and not * options from the IA (packet->options == opt_state) * try to find the proper relay */ if ((p->relays != 0) && (packet->options == opt_state)) { int i = p->relays; while ((i != 0) && (relay_packet->dhcpv6_container_packet != NULL)) { relay_packet = relay_packet->dhcpv6_container_packet; i--; } /* We wanted a specific relay but were * unable to find it */ if ((p->relays <= MAX_V6RELAY_HOPS) && (i != 0)) continue; relay_state = relay_packet->options; } oc = lookup_option(p->option->universe, relay_state, p->option->code); if (oc != NULL) { memset(&data, 0, sizeof(data)); if (!evaluate_option_cache(&data, relay_packet, NULL, NULL, relay_state, NULL, &global_scope, oc, MDL)) { log_error("Error evaluating option cache"); return 0; } found = host_hash_lookup(hp, p->values_hash, data.data, data.len, file, line); data_string_forget(&data, MDL); if (found) { return 1; } } } return 0; } /* More than one host_decl can be returned by find_hosts_by_haddr or find_hosts_by_uid, and each host_decl can have multiple addresses. Loop through the list of hosts, and then for each host, through the list of addresses, looking for an address that's in the same shared network as the one specified. Store the matching address through the addr pointer, update the host pointer to point at the host_decl that matched, and return the subnet that matched. */ int find_host_for_network (struct subnet **sp, struct host_decl **host, struct iaddr *addr, struct shared_network *share) { int i; struct iaddr ip_address; struct host_decl *hp; struct data_string fixed_addr; memset (&fixed_addr, 0, sizeof fixed_addr); for (hp = *host; hp; hp = hp -> n_ipaddr) { if (!hp -> fixed_addr) continue; if (!evaluate_option_cache (&fixed_addr, (struct packet *)0, (struct lease *)0, (struct client_state *)0, (struct option_state *)0, (struct option_state *)0, &global_scope, hp -> fixed_addr, MDL)) continue; for (i = 0; i < fixed_addr.len; i += 4) { ip_address.len = 4; memcpy (ip_address.iabuf, fixed_addr.data + i, 4); if (find_grouped_subnet (sp, share, ip_address, MDL)) { struct host_decl *tmp = (struct host_decl *)0; *addr = ip_address; /* This is probably not necessary, but just in case *host is the only reference to that host declaration, make a temporary reference so that dereferencing it doesn't dereference hp out from under us. */ host_reference (&tmp, *host, MDL); host_dereference (host, MDL); host_reference (host, hp, MDL); host_dereference (&tmp, MDL); data_string_forget (&fixed_addr, MDL); return 1; } } data_string_forget (&fixed_addr, MDL); } return 0; } void new_address_range (cfile, low, high, subnet, pool, lpchain) struct parse *cfile; struct iaddr low, high; struct subnet *subnet; struct pool *pool; struct lease **lpchain; { #if defined(COMPACT_LEASES) struct lease *address_range; unsigned s; #endif unsigned min, max, i, num_addrs; char lowbuf [16], highbuf [16], netbuf [16]; struct shared_network *share = subnet -> shared_network; struct lease *lt = (struct lease *)0; #if !defined(COMPACT_LEASES) isc_result_t status; #endif /* All subnets should have attached shared network structures. */ if (!share) { strcpy (netbuf, piaddr (subnet -> net)); log_fatal ("No shared network for network %s (%s)", netbuf, piaddr (subnet -> netmask)); } /* Initialize the hash table if it hasn't been done yet. */ if (!lease_uid_hash) { if (!lease_id_new_hash(&lease_uid_hash, LEASE_HASH_SIZE, MDL)) log_fatal ("Can't allocate lease/uid hash"); } if (!lease_ip_addr_hash) { if (!lease_ip_new_hash(&lease_ip_addr_hash, LEASE_HASH_SIZE, MDL)) log_fatal ("Can't allocate lease/ip hash"); } if (!lease_hw_addr_hash) { if (!lease_id_new_hash(&lease_hw_addr_hash, LEASE_HASH_SIZE, MDL)) log_fatal ("Can't allocate lease/hw hash"); } /* Make sure that high and low addresses are in this subnet. */ if (!addr_eq(subnet->net, subnet_number(low, subnet->netmask))) { strcpy(lowbuf, piaddr(low)); strcpy(netbuf, piaddr(subnet->net)); log_fatal("bad range, address %s not in subnet %s netmask %s", lowbuf, netbuf, piaddr(subnet->netmask)); } if (!addr_eq(subnet->net, subnet_number(high, subnet->netmask))) { strcpy(highbuf, piaddr(high)); strcpy(netbuf, piaddr(subnet->net)); log_fatal("bad range, address %s not in subnet %s netmask %s", highbuf, netbuf, piaddr(subnet->netmask)); } /* Get the high and low host addresses... */ max = host_addr (high, subnet -> netmask); min = host_addr (low, subnet -> netmask); /* Allow range to be specified high-to-low as well as low-to-high. */ if (min > max) { max = min; min = host_addr (high, subnet -> netmask); } /* get the number of addresses we want, and add it to the pool info * this value is only for use when setting up lease chains and will * be overwritten when expire_all_pools is run */ num_addrs = max - min + 1; #if defined (BINARY_LEASES) pool->lease_count += num_addrs; #endif /* Get a lease structure for each address in the range. */ #if defined (COMPACT_LEASES) s = (num_addrs + 1) * sizeof (struct lease); /* Check unsigned overflow in new_leases(). With 304 byte lease structure (x64_86), this happens at range 10.0.0.0 10.215.148.52; */ if (((s % sizeof (struct lease)) != 0) || ((s / sizeof (struct lease)) != (num_addrs + 1))) { strcpy (lowbuf, piaddr (low)); strcpy (highbuf, piaddr (high)); parse_warn (cfile, "%s-%s is an overly large address range.", lowbuf, highbuf); log_fatal ("Memory overflow."); } address_range = new_leases (num_addrs, MDL); if (!address_range) { strcpy (lowbuf, piaddr (low)); strcpy (highbuf, piaddr (high)); log_fatal ("No memory for address range %s-%s.", lowbuf, highbuf); } #endif /* Fill out the lease structures with some minimal information. */ for (i = 0; i < num_addrs; i++) { struct lease *lp = (struct lease *)0; #if defined (COMPACT_LEASES) omapi_object_initialize ((omapi_object_t *)&address_range [i], dhcp_type_lease, 0, sizeof (struct lease), MDL); lease_reference (&lp, &address_range [i], MDL); #else status = lease_allocate (&lp, MDL); if (status != ISC_R_SUCCESS) log_fatal ("No memory for lease %s: %s", piaddr (ip_addr (subnet -> net, subnet -> netmask, i + min)), isc_result_totext (status)); #endif lp->ip_addr = ip_addr(subnet->net, subnet->netmask, i + min); lp->starts = MIN_TIME; lp->ends = MIN_TIME; subnet_reference(&lp->subnet, subnet, MDL); pool_reference(&lp->pool, pool, MDL); lp->binding_state = FTS_FREE; lp->next_binding_state = FTS_FREE; lp->rewind_binding_state = FTS_FREE; lp->flags = 0; /* Remember the lease in the IP address hash. */ if (find_lease_by_ip_addr (<, lp -> ip_addr, MDL)) { if (lt -> pool) { parse_warn (cfile, "lease %s is declared twice!", piaddr (lp -> ip_addr)); } else pool_reference (< -> pool, pool, MDL); lease_dereference (<, MDL); } else lease_ip_hash_add(lease_ip_addr_hash, lp->ip_addr.iabuf, lp->ip_addr.len, lp, MDL); /* Put the lease on the chain for the caller. */ if (lpchain) { if (*lpchain) { lease_reference (&lp -> next, *lpchain, MDL); lease_dereference (lpchain, MDL); } lease_reference (lpchain, lp, MDL); } lease_dereference (&lp, MDL); } } int find_subnet (struct subnet **sp, struct iaddr addr, const char *file, int line) { struct subnet *rv; for (rv = subnets; rv; rv = rv -> next_subnet) { #if defined(DHCP4o6) if (addr.len != rv->netmask.len) continue; #endif if (addr_eq (subnet_number (addr, rv -> netmask), rv -> net)) { if (subnet_reference (sp, rv, file, line) != ISC_R_SUCCESS) return 0; return 1; } } return 0; } int find_grouped_subnet (struct subnet **sp, struct shared_network *share, struct iaddr addr, const char *file, int line) { struct subnet *rv; for (rv = share -> subnets; rv; rv = rv -> next_sibling) { #if defined(DHCP4o6) if (addr.len != rv->netmask.len) continue; #endif if (addr_eq (subnet_number (addr, rv -> netmask), rv -> net)) { if (subnet_reference (sp, rv, file, line) != ISC_R_SUCCESS) return 0; return 1; } } return 0; } /* XXX: could speed up if everyone had a prefix length */ int subnet_inner_than(const struct subnet *subnet, const struct subnet *scan, int warnp) { #if defined(DHCP4o6) if (subnet->net.len != scan->net.len) return 0; #endif if (addr_eq(subnet_number(subnet->net, scan->netmask), scan->net) || addr_eq(subnet_number(scan->net, subnet->netmask), subnet->net)) { char n1buf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255")]; int i, j; for (i = 0; i < 128; i++) if (subnet->netmask.iabuf[3 - (i >> 3)] & (1 << (i & 7))) break; for (j = 0; j < 128; j++) if (scan->netmask.iabuf[3 - (j >> 3)] & (1 << (j & 7))) break; if (warnp) { strcpy(n1buf, piaddr(subnet->net)); log_error("Warning: subnet %s/%d overlaps subnet %s/%d", n1buf, 32 - i, piaddr(scan->net), 32 - j); } if (i < j) return 1; } return 0; } /* Enter a new subnet into the subnet list. */ void enter_subnet (subnet) struct subnet *subnet; { struct subnet *scan = (struct subnet *)0; struct subnet *next = (struct subnet *)0; struct subnet *prev = (struct subnet *)0; /* Check for duplicates... */ if (subnets) subnet_reference (&next, subnets, MDL); while (next) { subnet_reference (&scan, next, MDL); subnet_dereference (&next, MDL); /* When we find a conflict, make sure that the subnet with the narrowest subnet mask comes first. */ if (subnet_inner_than (subnet, scan, 1)) { if (prev) { if (prev -> next_subnet) subnet_dereference (&prev -> next_subnet, MDL); subnet_reference (&prev -> next_subnet, subnet, MDL); subnet_dereference (&prev, MDL); } else { subnet_dereference (&subnets, MDL); subnet_reference (&subnets, subnet, MDL); } subnet_reference (&subnet -> next_subnet, scan, MDL); subnet_dereference (&scan, MDL); return; } subnet_reference (&prev, scan, MDL); subnet_dereference (&scan, MDL); } if (prev) subnet_dereference (&prev, MDL); /* XXX use the BSD radix tree code instead of a linked list. */ if (subnets) { subnet_reference (&subnet -> next_subnet, subnets, MDL); subnet_dereference (&subnets, MDL); } subnet_reference (&subnets, subnet, MDL); } /* Enter a new shared network into the shared network list. */ void enter_shared_network (share) struct shared_network *share; { if (shared_networks) { shared_network_reference (&share -> next, shared_networks, MDL); shared_network_dereference (&shared_networks, MDL); } shared_network_reference (&shared_networks, share, MDL); } void new_shared_network_interface (cfile, share, name) struct parse *cfile; struct shared_network *share; const char *name; { struct interface_info *ip; isc_result_t status; if (share -> interface) { parse_warn (cfile, "A subnet or shared network can't be connected %s", "to two interfaces."); return; } for (ip = interfaces; ip; ip = ip -> next) if (!strcmp (ip -> name, name)) break; if (!ip) { status = interface_allocate (&ip, MDL); if (status != ISC_R_SUCCESS) log_fatal ("new_shared_network_interface %s: %s", name, isc_result_totext (status)); if (strlen (name) > sizeof ip -> name) { memcpy (ip -> name, name, (sizeof ip -> name) - 1); ip -> name [(sizeof ip -> name) - 1] = 0; } else strcpy (ip -> name, name); if (interfaces) { interface_reference (&ip -> next, interfaces, MDL); interface_dereference (&interfaces, MDL); } interface_reference (&interfaces, ip, MDL); ip -> flags = INTERFACE_REQUESTED; /* XXX this is a reference loop. */ shared_network_reference (&ip -> shared_network, share, MDL); interface_reference (&share -> interface, ip, MDL); } } /* Enter a lease into the system. This is called by the parser each time it reads in a new lease. If the subnet for that lease has already been read in (usually the case), just update that lease; otherwise, allocate temporary storage for the lease and keep it around until we're done reading in the config file. */ void enter_lease (lease) struct lease *lease; { struct lease *comp = (struct lease *)0; if (find_lease_by_ip_addr (&comp, lease -> ip_addr, MDL)) { if (!comp -> pool) { log_error ("undeclared lease found in database: %s", piaddr (lease -> ip_addr)); } else pool_reference (&lease -> pool, comp -> pool, MDL); if (comp -> subnet) subnet_reference (&lease -> subnet, comp -> subnet, MDL); lease_ip_hash_delete(lease_ip_addr_hash, lease->ip_addr.iabuf, lease->ip_addr.len, MDL); lease_dereference (&comp, MDL); } /* The only way a lease can get here without a subnet is if it's in the lease file, but not in the dhcpd.conf file. In this case, we *should* keep it around until it's expired, but never reallocate it or renew it. Currently, to maintain consistency, we are not doing this. XXX fix this so that the lease is kept around until it expires. XXX this will be important in IPv6 with addresses that become XXX non-renewable as a result of a renumbering event. */ if (!lease -> subnet) { log_error ("lease %s: no subnet.", piaddr (lease -> ip_addr)); return; } lease_ip_hash_add(lease_ip_addr_hash, lease->ip_addr.iabuf, lease->ip_addr.len, lease, MDL); } /* Replace the data in an existing lease with the data in a new lease; adjust hash tables to suit, and insertion sort the lease into the list of leases by expiry time so that we can always find the oldest lease. */ int supersede_lease (comp, lease, commit, propogate, pimmediate, from_pool) struct lease *comp, *lease; int commit; int propogate; int pimmediate; int from_pool; { LEASE_STRUCT_PTR lq; struct timeval tv; #if defined (FAILOVER_PROTOCOL) int do_pool_check = 0; /* We must commit leases before sending updates regarding them to failover peers. It is, therefore, an error to set pimmediate and not commit. */ if (pimmediate && !commit) return 0; #endif /* If there is no sample lease, just do the move. */ if (!lease) goto just_move_it; /* Static leases are not currently kept in the database... */ if (lease -> flags & STATIC_LEASE) return 1; /* If the existing lease hasn't expired and has a different unique identifier or, if it doesn't have a unique identifier, a different hardware address, then the two leases are in conflict. If the existing lease has a uid and the new one doesn't, but they both have the same hardware address, and dynamic bootp is allowed on this lease, then we allow that, in case a dynamic BOOTP lease is requested *after* a DHCP lease has been assigned. */ if (lease -> binding_state != FTS_ABANDONED && lease -> next_binding_state != FTS_ABANDONED && comp -> binding_state == FTS_ACTIVE && (((comp -> uid && lease -> uid) && (comp -> uid_len != lease -> uid_len || memcmp (comp -> uid, lease -> uid, comp -> uid_len))) || (!comp -> uid && ((comp -> hardware_addr.hlen != lease -> hardware_addr.hlen) || memcmp (comp -> hardware_addr.hbuf, lease -> hardware_addr.hbuf, comp -> hardware_addr.hlen))))) { log_error ("Lease conflict at %s", piaddr (comp -> ip_addr)); } /* If there's a Unique ID, dissociate it from the hash table and free it if necessary. */ if (comp->uid) { uid_hash_delete(comp); if (comp->uid != comp->uid_buf) { dfree(comp->uid, MDL); comp->uid_max = 0; comp->uid_len = 0; } comp -> uid = (unsigned char *)0; } /* If there's a hardware address, remove the lease from its * old position in the hash bucket's ordered list. */ if (comp->hardware_addr.hlen) hw_hash_delete(comp); /* If the lease has been billed to a class, remove the billing. */ if (comp -> billing_class != lease -> billing_class) { if (comp->billing_class) unbill_class(comp); if (lease -> billing_class) bill_class (comp, lease -> billing_class); } /* Copy the data files, but not the linkages. */ comp -> starts = lease -> starts; if (lease -> uid) { if (lease -> uid_len <= sizeof (lease -> uid_buf)) { memcpy (comp -> uid_buf, lease -> uid, lease -> uid_len); comp -> uid = &comp -> uid_buf [0]; comp -> uid_max = sizeof comp -> uid_buf; comp -> uid_len = lease -> uid_len; } else if (lease -> uid != &lease -> uid_buf [0]) { comp -> uid = lease -> uid; comp -> uid_max = lease -> uid_max; lease -> uid = (unsigned char *)0; lease -> uid_max = 0; comp -> uid_len = lease -> uid_len; lease -> uid_len = 0; } else { log_fatal ("corrupt lease uid."); /* XXX */ } } else { comp -> uid = (unsigned char *)0; comp -> uid_len = comp -> uid_max = 0; } if (comp -> host) host_dereference (&comp -> host, MDL); host_reference (&comp -> host, lease -> host, MDL); comp -> hardware_addr = lease -> hardware_addr; if (comp -> scope) binding_scope_dereference (&comp -> scope, MDL); if (lease -> scope) { binding_scope_reference (&comp -> scope, lease -> scope, MDL); binding_scope_dereference (&lease -> scope, MDL); } if (comp -> agent_options) option_chain_head_dereference (&comp -> agent_options, MDL); if (lease -> agent_options) { /* Only retain the agent options if the lease is still affirmatively associated with a client. */ if (lease -> next_binding_state == FTS_ACTIVE || lease -> next_binding_state == FTS_EXPIRED) option_chain_head_reference (&comp -> agent_options, lease -> agent_options, MDL); option_chain_head_dereference (&lease -> agent_options, MDL); } /* Record the hostname information in the lease. */ if (comp -> client_hostname) dfree (comp -> client_hostname, MDL); comp -> client_hostname = lease -> client_hostname; lease -> client_hostname = (char *)0; if (lease->on_star.on_expiry) { if (comp->on_star.on_expiry) executable_statement_dereference (&comp->on_star.on_expiry, MDL); executable_statement_reference (&comp->on_star.on_expiry, lease->on_star.on_expiry, MDL); } if (lease->on_star.on_commit) { if (comp->on_star.on_commit) executable_statement_dereference (&comp->on_star.on_commit, MDL); executable_statement_reference (&comp->on_star.on_commit, lease->on_star.on_commit, MDL); } if (lease->on_star.on_release) { if (comp->on_star.on_release) executable_statement_dereference (&comp->on_star.on_release, MDL); executable_statement_reference (&comp->on_star.on_release, lease->on_star.on_release, MDL); } /* Record the lease in the uid hash if necessary. */ if (comp->uid) uid_hash_add(comp); /* Record it in the hardware address hash if necessary. */ if (comp->hardware_addr.hlen) hw_hash_add(comp); comp->cltt = lease->cltt; #if defined (FAILOVER_PROTOCOL) comp->tstp = lease->tstp; comp->tsfp = lease->tsfp; comp->atsfp = lease->atsfp; #endif /* FAILOVER_PROTOCOL */ comp->ends = lease->ends; comp->next_binding_state = lease->next_binding_state; /* * If we have a control block pointer copy it in. * We don't zero out an older ponter as it is still * in use. We shouldn't need to overwrite an * old pointer with a new one as the old transaction * should have been cancelled before getting here. */ if (lease->ddns_cb != NULL) comp->ddns_cb = lease->ddns_cb; just_move_it: #if defined (FAILOVER_PROTOCOL) /* * Atsfp should be cleared upon any state change that implies * propagation whether supersede_lease was given a copy lease * structure or not (often from the pool_timer()). */ if (propogate) comp->atsfp = 0; #endif /* FAILOVER_PROTOCOL */ if (!comp -> pool) { log_error ("Supersede_lease: lease %s with no pool.", piaddr (comp -> ip_addr)); return 0; } /* Figure out which queue it's on. */ switch (comp -> binding_state) { case FTS_FREE: if (comp->flags & RESERVED_LEASE) lq = &comp->pool->reserved; else { lq = &comp->pool->free; comp->pool->free_leases--; } #if defined(FAILOVER_PROTOCOL) do_pool_check = 1; #endif break; case FTS_ACTIVE: lq = &comp -> pool -> active; break; case FTS_EXPIRED: case FTS_RELEASED: case FTS_RESET: lq = &comp -> pool -> expired; break; case FTS_ABANDONED: lq = &comp -> pool -> abandoned; break; case FTS_BACKUP: if (comp->flags & RESERVED_LEASE) lq = &comp->pool->reserved; else { lq = &comp->pool->backup; comp->pool->backup_leases--; } #if defined(FAILOVER_PROTOCOL) do_pool_check = 1; #endif break; default: log_error ("Lease with bogus binding state: %d", comp -> binding_state); #if defined (BINDING_STATE_DEBUG) abort (); #endif return 0; } /* Remove the lease from its current place in its current timer sequence. */ LEASE_REMOVEP(lq, comp); /* Now that we've done the flag-affected queue removal * we can update the new lease's flags, if there's an * existing lease */ if (lease) { comp->flags = ((lease->flags & ~PERSISTENT_FLAGS) | (comp->flags & ~EPHEMERAL_FLAGS)); } /* Make the state transition. */ if (commit || !pimmediate) make_binding_state_transition (comp); /* Put the lease back on the appropriate queue. If the lease is corrupt (as detected by lease_enqueue), don't go any farther. */ if (!lease_enqueue (comp)) return 0; /* If this is the next lease that will timeout on the pool, zap the old timeout and set the timeout on this pool to the time that the lease's next event will happen. We do not actually set the timeout unless commit is true - we don't want to thrash the timer queue when reading the lease database. Instead, the database code calls the expiry event on each pool after reading in the lease file, and the expiry code sets the timer if there's anything left to expire after it's run any outstanding expiry events on the pool. */ if ((commit || !pimmediate) && comp -> sort_time != MIN_TIME && comp -> sort_time > cur_time && (comp -> sort_time < comp -> pool -> next_event_time || comp -> pool -> next_event_time == MIN_TIME)) { comp -> pool -> next_event_time = comp -> sort_time; tv . tv_sec = comp -> pool -> next_event_time; tv . tv_usec = 0; add_timeout (&tv, pool_timer, comp -> pool, (tvref_t)pool_reference, (tvunref_t)pool_dereference); } if (commit) { #if defined(FAILOVER_PROTOCOL) /* * If commit and propogate are set, then we can save a * possible fsync later in BNDUPD socket transmission by * stepping the rewind state forward to the new state, in * case it has changed. This is only worth doing if the * failover connection is currently connected, as in this * case it is likely we will be transmitting to the peer very * shortly. */ if (propogate && (comp->pool->failover_peer != NULL) && ((comp->pool->failover_peer->service_state == cooperating) || (comp->pool->failover_peer->service_state == not_responding))) comp->rewind_binding_state = comp->binding_state; #endif if (!write_lease (comp)) return 0; if ((server_starting & SS_NOSYNC) == 0) { if (!commit_leases ()) return 0; } } #if defined (FAILOVER_PROTOCOL) if (propogate) { comp -> desired_binding_state = comp -> binding_state; if (!dhcp_failover_queue_update (comp, pimmediate)) return 0; } if (do_pool_check && comp->pool->failover_peer) dhcp_failover_pool_check(comp->pool); #endif /* If the current binding state has already expired and we haven't * been called from pool_timer, do an expiry event right now. */ /* XXX At some point we should optimize this so that we don't XXX write the lease twice, but this is a safe way to fix the XXX problem for 3.0 (I hope!). */ if ((from_pool == 0) && (commit || !pimmediate) && (comp->sort_time < cur_time) && (comp->next_binding_state != comp->binding_state)) pool_timer(comp->pool); return 1; } void make_binding_state_transition (struct lease *lease) { #if defined (FAILOVER_PROTOCOL) dhcp_failover_state_t *peer; if (lease -> pool && lease -> pool -> failover_peer) peer = lease -> pool -> failover_peer; else peer = (dhcp_failover_state_t *)0; #endif /* If the lease was active and is now no longer active, but isn't released, then it just expired, so do the expiry event. */ if (lease -> next_binding_state != lease -> binding_state && (( #if defined (FAILOVER_PROTOCOL) peer && (lease->binding_state == FTS_EXPIRED || lease->binding_state == FTS_ACTIVE) && (lease->next_binding_state == FTS_FREE || lease->next_binding_state == FTS_BACKUP)) || (!peer && #endif lease -> binding_state == FTS_ACTIVE && lease -> next_binding_state != FTS_RELEASED))) { #if defined (NSUPDATE) (void) ddns_removals(lease, NULL, NULL, ISC_TRUE); #endif if (lease->on_star.on_expiry) { execute_statements(NULL, NULL, lease, NULL, NULL, NULL, &lease->scope, lease->on_star.on_expiry, NULL); if (lease->on_star.on_expiry) executable_statement_dereference (&lease->on_star.on_expiry, MDL); } /* No sense releasing a lease after it's expired. */ if (lease->on_star.on_release) executable_statement_dereference (&lease->on_star.on_release, MDL); /* Get rid of client-specific bindings that are only correct when the lease is active. */ if (lease->billing_class) unbill_class(lease); if (lease -> agent_options) option_chain_head_dereference (&lease -> agent_options, MDL); if (lease -> client_hostname) { dfree (lease -> client_hostname, MDL); lease -> client_hostname = (char *)0; } if (lease -> host) host_dereference (&lease -> host, MDL); /* Send the expiry time to the peer. */ lease -> tstp = lease -> ends; } /* If the lease was active and is now released, do the release event. */ if (lease -> next_binding_state != lease -> binding_state && (( #if defined (FAILOVER_PROTOCOL) peer && lease -> binding_state == FTS_RELEASED && (lease -> next_binding_state == FTS_FREE || lease -> next_binding_state == FTS_BACKUP)) || (!peer && #endif lease -> binding_state == FTS_ACTIVE && lease -> next_binding_state == FTS_RELEASED))) { #if defined (NSUPDATE) /* * Note: ddns_removals() is also iterated when the lease * enters state 'released' in 'release_lease()'. The below * is caught when a peer receives a BNDUPD from a failover * peer; it may not have received the client's release (it * may have been offline). * * We could remove the call from release_lease() because * it will also catch here on the originating server after the * peer acknowledges the state change. However, there could * be many hours inbetween, and in this case we /know/ the * client is no longer using the lease when we receive the * release message. This is not true of expiry, where the * peer may have extended the lease. */ (void) ddns_removals(lease, NULL, NULL, ISC_TRUE); #endif if (lease->on_star.on_release) { execute_statements(NULL, NULL, lease, NULL, NULL, NULL, &lease->scope, lease->on_star.on_release, NULL); executable_statement_dereference (&lease->on_star.on_release, MDL); } /* A released lease can't expire. */ if (lease->on_star.on_expiry) executable_statement_dereference (&lease->on_star.on_expiry, MDL); /* Get rid of client-specific bindings that are only correct when the lease is active. */ if (lease->billing_class) unbill_class(lease); if (lease -> agent_options) option_chain_head_dereference (&lease -> agent_options, MDL); if (lease -> client_hostname) { dfree (lease -> client_hostname, MDL); lease -> client_hostname = (char *)0; } if (lease -> host) host_dereference (&lease -> host, MDL); /* Send the release time (should be == cur_time) to the peer. */ lease -> tstp = lease -> ends; } #if defined (DEBUG_LEASE_STATE_TRANSITIONS) log_debug ("lease %s moves from %s to %s", piaddr (lease -> ip_addr), binding_state_print (lease -> binding_state), binding_state_print (lease -> next_binding_state)); #endif lease -> binding_state = lease -> next_binding_state; switch (lease -> binding_state) { case FTS_ACTIVE: #if defined (FAILOVER_PROTOCOL) if (lease -> pool && lease -> pool -> failover_peer) lease -> next_binding_state = FTS_EXPIRED; else #endif lease -> next_binding_state = FTS_FREE; break; case FTS_EXPIRED: case FTS_RELEASED: case FTS_ABANDONED: case FTS_RESET: lease->next_binding_state = FTS_FREE; #if defined(FAILOVER_PROTOCOL) /* If we are not in partner_down, leases don't go from EXPIRED to FREE on a timeout - only on an update. If we're in partner_down, they expire at mclt past the time we entered partner_down. */ if ((lease->pool != NULL) && (lease->pool->failover_peer != NULL) && (lease->pool->failover_peer->me.state == partner_down)) lease->tsfp = (lease->pool->failover_peer->me.stos + lease->pool->failover_peer->mclt); #endif /* FAILOVER_PROTOCOL */ break; case FTS_FREE: case FTS_BACKUP: lease -> next_binding_state = lease -> binding_state; break; } #if defined (DEBUG_LEASE_STATE_TRANSITIONS) log_debug ("lease %s: next binding state %s", piaddr (lease -> ip_addr), binding_state_print (lease -> next_binding_state)); #endif } /* Copy the contents of one lease into another, correctly maintaining reference counts. */ int lease_copy (struct lease **lp, struct lease *lease, const char *file, int line) { struct lease *lt = (struct lease *)0; isc_result_t status; status = lease_allocate (<, MDL); if (status != ISC_R_SUCCESS) return 0; lt -> ip_addr = lease -> ip_addr; lt -> starts = lease -> starts; lt -> ends = lease -> ends; lt -> uid_len = lease -> uid_len; lt -> uid_max = lease -> uid_max; if (lease -> uid == lease -> uid_buf) { lt -> uid = lt -> uid_buf; memcpy (lt -> uid_buf, lease -> uid_buf, sizeof lt -> uid_buf); } else if (!lease -> uid_max) { lt -> uid = (unsigned char *)0; } else { lt -> uid = dmalloc (lt -> uid_max, MDL); if (!lt -> uid) { lease_dereference (<, MDL); return 0; } memcpy (lt -> uid, lease -> uid, lease -> uid_max); } if (lease -> client_hostname) { lt -> client_hostname = dmalloc (strlen (lease -> client_hostname) + 1, MDL); if (!lt -> client_hostname) { lease_dereference (<, MDL); return 0; } strcpy (lt -> client_hostname, lease -> client_hostname); } if (lease -> scope) binding_scope_reference (< -> scope, lease -> scope, MDL); if (lease -> agent_options) option_chain_head_reference (< -> agent_options, lease -> agent_options, MDL); host_reference (< -> host, lease -> host, file, line); subnet_reference (< -> subnet, lease -> subnet, file, line); pool_reference (< -> pool, lease -> pool, file, line); class_reference (< -> billing_class, lease -> billing_class, file, line); lt -> hardware_addr = lease -> hardware_addr; if (lease->on_star.on_expiry) executable_statement_reference (<->on_star.on_expiry, lease->on_star.on_expiry, file, line); if (lease->on_star.on_commit) executable_statement_reference (<->on_star.on_commit, lease->on_star.on_commit, file, line); if (lease->on_star.on_release) executable_statement_reference (<->on_star.on_release, lease->on_star.on_release, file, line); lt->flags = lease->flags; lt->tstp = lease->tstp; lt->tsfp = lease->tsfp; lt->atsfp = lease->atsfp; lt->cltt = lease -> cltt; lt->binding_state = lease->binding_state; lt->next_binding_state = lease->next_binding_state; lt->rewind_binding_state = lease->rewind_binding_state; status = lease_reference(lp, lt, file, line); lease_dereference(<, MDL); return status == ISC_R_SUCCESS; } /* Release the specified lease and re-hash it as appropriate. */ void release_lease (lease, packet) struct lease *lease; struct packet *packet; { /* If there are statements to execute when the lease is released, execute them. */ #if defined (NSUPDATE) (void) ddns_removals(lease, NULL, NULL, ISC_FALSE); #endif if (lease->on_star.on_release) { execute_statements (NULL, packet, lease, NULL, packet->options, NULL, &lease->scope, lease->on_star.on_release, NULL); if (lease->on_star.on_release) executable_statement_dereference (&lease->on_star.on_release, MDL); } /* We do either the on_release or the on_expiry events, but not both (it's possible that they could be the same, in any case). */ if (lease->on_star.on_expiry) executable_statement_dereference (&lease->on_star.on_expiry, MDL); if (lease -> binding_state != FTS_FREE && lease -> binding_state != FTS_BACKUP && lease -> binding_state != FTS_RELEASED && lease -> binding_state != FTS_EXPIRED && lease -> binding_state != FTS_RESET) { if (lease->on_star.on_commit) executable_statement_dereference (&lease->on_star.on_commit, MDL); /* Blow away any bindings. */ if (lease -> scope) binding_scope_dereference (&lease -> scope, MDL); /* Set sort times to the present. */ lease -> ends = cur_time; /* Lower layers of muckery set tstp to ->ends. But we send * protocol messages before this. So it is best to set * tstp now anyway. */ lease->tstp = cur_time; #if defined (FAILOVER_PROTOCOL) if (lease -> pool && lease -> pool -> failover_peer) { dhcp_failover_state_t *peer = NULL; if (lease->pool != NULL) peer = lease->pool->failover_peer; if ((peer->service_state == not_cooperating) && (((peer->i_am == primary) && (lease->rewind_binding_state == FTS_FREE)) || ((peer->i_am == secondary) && (lease->rewind_binding_state == FTS_BACKUP)))) { lease->next_binding_state = lease->rewind_binding_state; } else lease -> next_binding_state = FTS_RELEASED; } else { lease -> next_binding_state = FTS_FREE; } #else lease -> next_binding_state = FTS_FREE; #endif supersede_lease(lease, NULL, 1, 1, 1, 0); } } /* Abandon the specified lease (set its timeout to infinity and its particulars to zero, and re-hash it as appropriate. */ void abandon_lease (lease, message) struct lease *lease; const char *message; { struct lease *lt = NULL; #if defined (NSUPDATE) (void) ddns_removals(lease, NULL, NULL, ISC_FALSE); #endif if (!lease_copy(<, lease, MDL)) { return; } if (lt->scope) { binding_scope_dereference(<->scope, MDL); } /* Calculate the abandone expiry time. If it wraps, * use the maximum expiry time. */ lt->ends = cur_time + abandon_lease_time; if (lt->ends < cur_time || lt->ends > MAX_TIME) { lt->ends = MAX_TIME; } lt->next_binding_state = FTS_ABANDONED; log_error ("Abandoning IP address %s: %s", piaddr(lease->ip_addr), message); lt->hardware_addr.hlen = 0; if (lt->uid && lt->uid != lt->uid_buf) { dfree(lt->uid, MDL); } lt->uid = NULL; lt->uid_len = 0; lt->uid_max = 0; supersede_lease(lease, lt, 1, 1, 1, 0); lease_dereference(<, MDL); } #if 0 /* * This doesn't appear to be in use for anything anymore. * I'm ifdeffing it now and if there are no complaints in * the future it will be removed. * SAR */ /* Abandon the specified lease (set its timeout to infinity and its particulars to zero, and re-hash it as appropriate. */ void dissociate_lease (lease) struct lease *lease; { struct lease *lt = (struct lease *)0; #if defined (NSUPDATE) (void) ddns_removals(lease, NULL, NULL, ISC_FALSE); #endif if (!lease_copy (<, lease, MDL)) return; #if defined (FAILOVER_PROTOCOL) if (lease -> pool && lease -> pool -> failover_peer) { lt -> next_binding_state = FTS_RESET; } else { lt -> next_binding_state = FTS_FREE; } #else lt -> next_binding_state = FTS_FREE; #endif lt -> ends = cur_time; /* XXX */ lt -> hardware_addr.hlen = 0; if (lt -> uid && lt -> uid != lt -> uid_buf) dfree (lt -> uid, MDL); lt -> uid = (unsigned char *)0; lt -> uid_len = 0; lt -> uid_max = 0; supersede_lease (lease, lt, 1, 1, 1, 0); lease_dereference (<, MDL); } #endif /* Timer called when a lease in a particular pool expires. */ void pool_timer (vpool) void *vpool; { struct pool *pool; struct lease *next = NULL; struct lease *lease = NULL; struct lease *ltemp = NULL; #define FREE_LEASES 0 #define ACTIVE_LEASES 1 #define EXPIRED_LEASES 2 #define ABANDONED_LEASES 3 #define BACKUP_LEASES 4 #define RESERVED_LEASES 5 LEASE_STRUCT_PTR lptr[RESERVED_LEASES+1]; TIME next_expiry = MAX_TIME; int i; struct timeval tv; pool = (struct pool *)vpool; lptr[FREE_LEASES] = &pool->free; lptr[ACTIVE_LEASES] = &pool->active; lptr[EXPIRED_LEASES] = &pool->expired; lptr[ABANDONED_LEASES] = &pool->abandoned; lptr[BACKUP_LEASES] = &pool->backup; lptr[RESERVED_LEASES] = &pool->reserved; for (i = FREE_LEASES; i <= RESERVED_LEASES; i++) { /* If there's nothing on the queue, skip it. */ if (!(LEASE_NOT_EMPTYP(lptr[i]))) continue; #if defined (FAILOVER_PROTOCOL) if (pool->failover_peer && pool->failover_peer->me.state != partner_down) { /* * Normally the secondary doesn't initiate expiration * events (unless in partner-down), but rather relies * on the primary to expire the lease. However, when * disconnected from its peer, the server is allowed to * rewind a lease to the previous state that the peer * would have recorded it. This means there may be * opportunities for active->free or active->backup * expirations while out of contact. * * Q: Should we limit this expiration to * comms-interrupt rather than not-normal? */ if ((i == ACTIVE_LEASES) && (pool->failover_peer->i_am == secondary) && (pool->failover_peer->me.state == normal)) continue; /* Leases in an expired state don't move to free because of a timeout unless we're in partner_down. */ if (i == EXPIRED_LEASES) continue; } #endif lease_reference(&lease, LEASE_GET_FIRSTP(lptr[i]), MDL); while (lease) { /* Remember the next lease in the list. */ if (next) lease_dereference(&next, MDL); ltemp = LEASE_GET_NEXTP(lptr[i], lease); if (ltemp) lease_reference(&next, ltemp, MDL); /* If we've run out of things to expire on this list, stop. */ if (lease->sort_time > cur_time) { if (lease->sort_time < next_expiry) next_expiry = lease->sort_time; break; } /* If there is a pending state change, and this lease has gotten to the time when the state change should happen, just call supersede_lease on it to make the change happen. */ if (lease->next_binding_state != lease->binding_state) { #if defined(FAILOVER_PROTOCOL) dhcp_failover_state_t *peer = NULL; if (lease->pool != NULL) peer = lease->pool->failover_peer; /* Can we rewind the lease to a free state? */ if (peer != NULL && peer->service_state == not_cooperating && lease->next_binding_state == FTS_EXPIRED && ((peer->i_am == primary && lease->rewind_binding_state == FTS_FREE) || (peer->i_am == secondary && lease->rewind_binding_state == FTS_BACKUP))) lease->next_binding_state = lease->rewind_binding_state; #endif supersede_lease(lease, NULL, 1, 1, 1, 1); } lease_dereference(&lease, MDL); if (next) lease_reference(&lease, next, MDL); } if (next) lease_dereference(&next, MDL); if (lease) lease_dereference(&lease, MDL); } /* If we found something to expire and its expiration time * is either less than the current expiration time or the * current expiration time is already expired update the * timer. */ if ((next_expiry != MAX_TIME) && ((pool->next_event_time > next_expiry) || (pool->next_event_time <= cur_time))) { pool->next_event_time = next_expiry; tv.tv_sec = pool->next_event_time; tv.tv_usec = 0; add_timeout (&tv, pool_timer, pool, (tvref_t)pool_reference, (tvunref_t)pool_dereference); } else pool->next_event_time = MIN_TIME; } /* Locate the lease associated with a given IP address... */ int find_lease_by_ip_addr (struct lease **lp, struct iaddr addr, const char *file, int line) { return lease_ip_hash_lookup(lp, lease_ip_addr_hash, addr.iabuf, addr.len, file, line); } int find_lease_by_uid (struct lease **lp, const unsigned char *uid, unsigned len, const char *file, int line) { if (len == 0) return 0; return lease_id_hash_lookup (lp, lease_uid_hash, uid, len, file, line); } int find_lease_by_hw_addr (struct lease **lp, const unsigned char *hwaddr, unsigned hwlen, const char *file, int line) { if (hwlen == 0) return (0); /* * If it's an infiniband address don't bother * as we don't have a useful address to hash. */ if ((hwlen == 1) && (hwaddr[0] == HTYPE_INFINIBAND)) return (0); return (lease_id_hash_lookup(lp, lease_hw_addr_hash, hwaddr, hwlen, file, line)); } /* If the lease is preferred over the candidate, return truth. The * 'cand' and 'lease' names are retained to read more clearly against * the 'uid_hash_add' and 'hw_hash_add' functions (this is common logic * to those two functions). * * 1) ACTIVE leases are preferred. The active lease with * the longest lifetime is preferred over shortest. * 2) "transitional states" are next, this time with the * most recent CLTT. * 3) free/backup/etc states are next, again with CLTT. In truth we * should never see reset leases for this. * 4) Abandoned leases are always dead last. */ static isc_boolean_t client_lease_preferred(struct lease *cand, struct lease *lease) { if (cand->binding_state == FTS_ACTIVE) { if (lease->binding_state == FTS_ACTIVE && lease->ends >= cand->ends) return ISC_TRUE; } else if (cand->binding_state == FTS_EXPIRED || cand->binding_state == FTS_RELEASED) { if (lease->binding_state == FTS_ACTIVE) return ISC_TRUE; if ((lease->binding_state == FTS_EXPIRED || lease->binding_state == FTS_RELEASED) && lease->cltt >= cand->cltt) return ISC_TRUE; } else if (cand->binding_state != FTS_ABANDONED) { if (lease->binding_state == FTS_ACTIVE || lease->binding_state == FTS_EXPIRED || lease->binding_state == FTS_RELEASED) return ISC_TRUE; if (lease->binding_state != FTS_ABANDONED && lease->cltt >= cand->cltt) return ISC_TRUE; } else /* (cand->binding_state == FTS_ABANDONED) */ { if (lease->binding_state != FTS_ABANDONED || lease->cltt >= cand->cltt) return ISC_TRUE; } return ISC_FALSE; } /* Add the specified lease to the uid hash. */ void uid_hash_add(struct lease *lease) { struct lease *head = NULL; struct lease *cand = NULL; struct lease *prev = NULL; struct lease *next = NULL; /* If it's not in the hash, just add it. */ if (!find_lease_by_uid (&head, lease -> uid, lease -> uid_len, MDL)) lease_id_hash_add(lease_uid_hash, lease->uid, lease->uid_len, lease, MDL); else { /* Otherwise, insert it into the list in order of its * preference for "resuming allocation to the client." * * Because we don't have control of the hash bucket index * directly, we have to remove and re-insert the client * id into the hash if we're inserting onto the head. */ lease_reference(&cand, head, MDL); while (cand != NULL) { if (client_lease_preferred(cand, lease)) break; if (prev != NULL) lease_dereference(&prev, MDL); lease_reference(&prev, cand, MDL); if (cand->n_uid != NULL) lease_reference(&next, cand->n_uid, MDL); lease_dereference(&cand, MDL); if (next != NULL) { lease_reference(&cand, next, MDL); lease_dereference(&next, MDL); } } /* If we want to insert 'before cand', and prev is NULL, * then it was the head of the list. Assume that position. */ if (prev == NULL) { lease_reference(&lease->n_uid, head, MDL); lease_id_hash_delete(lease_uid_hash, lease->uid, lease->uid_len, MDL); lease_id_hash_add(lease_uid_hash, lease->uid, lease->uid_len, lease, MDL); } else /* (prev != NULL) */ { if(prev->n_uid != NULL) { lease_reference(&lease->n_uid, prev->n_uid, MDL); lease_dereference(&prev->n_uid, MDL); } lease_reference(&prev->n_uid, lease, MDL); lease_dereference(&prev, MDL); } if (cand != NULL) lease_dereference(&cand, MDL); lease_dereference(&head, MDL); } } /* Delete the specified lease from the uid hash. */ void uid_hash_delete (lease) struct lease *lease; { struct lease *head = (struct lease *)0; struct lease *scan; /* If it's not in the hash, we have no work to do. */ if (!find_lease_by_uid (&head, lease -> uid, lease -> uid_len, MDL)) { if (lease -> n_uid) lease_dereference (&lease -> n_uid, MDL); return; } /* If the lease we're freeing is at the head of the list, remove the hash table entry and add a new one with the next lease on the list (if there is one). */ if (head == lease) { lease_id_hash_delete(lease_uid_hash, lease->uid, lease->uid_len, MDL); if (lease -> n_uid) { lease_id_hash_add(lease_uid_hash, lease->n_uid->uid, lease->n_uid->uid_len, lease->n_uid, MDL); lease_dereference (&lease -> n_uid, MDL); } } else { /* Otherwise, look for the lease in the list of leases attached to the hash table entry, and remove it if we find it. */ for (scan = head; scan -> n_uid; scan = scan -> n_uid) { if (scan -> n_uid == lease) { lease_dereference (&scan -> n_uid, MDL); if (lease -> n_uid) { lease_reference (&scan -> n_uid, lease -> n_uid, MDL); lease_dereference (&lease -> n_uid, MDL); } break; } } } lease_dereference (&head, MDL); } /* Add the specified lease to the hardware address hash. */ /* We don't add leases with infiniband addresses to the * hash as there isn't any address to hash on. */ void hw_hash_add(struct lease *lease) { struct lease *head = NULL; struct lease *cand = NULL; struct lease *prev = NULL; struct lease *next = NULL; /* * If it's an infiniband address don't bother * as we don't have a useful address to hash. */ if ((lease->hardware_addr.hlen == 1) && (lease->hardware_addr.hbuf[0] == HTYPE_INFINIBAND)) return; /* If it's not in the hash, just add it. */ if (!find_lease_by_hw_addr (&head, lease -> hardware_addr.hbuf, lease -> hardware_addr.hlen, MDL)) lease_id_hash_add(lease_hw_addr_hash, lease->hardware_addr.hbuf, lease->hardware_addr.hlen, lease, MDL); else { /* Otherwise, insert it into the list in order of its * preference for "resuming allocation to the client." * * Because we don't have control of the hash bucket index * directly, we have to remove and re-insert the client * id into the hash if we're inserting onto the head. */ lease_reference(&cand, head, MDL); while (cand != NULL) { if (client_lease_preferred(cand, lease)) break; if (prev != NULL) lease_dereference(&prev, MDL); lease_reference(&prev, cand, MDL); if (cand->n_hw != NULL) lease_reference(&next, cand->n_hw, MDL); lease_dereference(&cand, MDL); if (next != NULL) { lease_reference(&cand, next, MDL); lease_dereference(&next, MDL); } } /* If we want to insert 'before cand', and prev is NULL, * then it was the head of the list. Assume that position. */ if (prev == NULL) { lease_reference(&lease->n_hw, head, MDL); lease_id_hash_delete(lease_hw_addr_hash, lease->hardware_addr.hbuf, lease->hardware_addr.hlen, MDL); lease_id_hash_add(lease_hw_addr_hash, lease->hardware_addr.hbuf, lease->hardware_addr.hlen, lease, MDL); } else /* (prev != NULL) */ { if(prev->n_hw != NULL) { lease_reference(&lease->n_hw, prev->n_hw, MDL); lease_dereference(&prev->n_hw, MDL); } lease_reference(&prev->n_hw, lease, MDL); lease_dereference(&prev, MDL); } if (cand != NULL) lease_dereference(&cand, MDL); lease_dereference(&head, MDL); } } /* Delete the specified lease from the hardware address hash. */ void hw_hash_delete (lease) struct lease *lease; { struct lease *head = (struct lease *)0; struct lease *next = (struct lease *)0; /* * If it's an infiniband address don't bother * as we don't have a useful address to hash. */ if ((lease->hardware_addr.hlen == 1) && (lease->hardware_addr.hbuf[0] == HTYPE_INFINIBAND)) return; /* If it's not in the hash, we have no work to do. */ if (!find_lease_by_hw_addr (&head, lease -> hardware_addr.hbuf, lease -> hardware_addr.hlen, MDL)) { if (lease -> n_hw) lease_dereference (&lease -> n_hw, MDL); return; } /* If the lease we're freeing is at the head of the list, remove the hash table entry and add a new one with the next lease on the list (if there is one). */ if (head == lease) { lease_id_hash_delete(lease_hw_addr_hash, lease->hardware_addr.hbuf, lease->hardware_addr.hlen, MDL); if (lease->n_hw) { lease_id_hash_add(lease_hw_addr_hash, lease->n_hw->hardware_addr.hbuf, lease->n_hw->hardware_addr.hlen, lease->n_hw, MDL); lease_dereference(&lease->n_hw, MDL); } } else { /* Otherwise, look for the lease in the list of leases attached to the hash table entry, and remove it if we find it. */ while (head -> n_hw) { if (head -> n_hw == lease) { lease_dereference (&head -> n_hw, MDL); if (lease -> n_hw) { lease_reference (&head -> n_hw, lease -> n_hw, MDL); lease_dereference (&lease -> n_hw, MDL); } break; } lease_reference (&next, head -> n_hw, MDL); lease_dereference (&head, MDL); lease_reference (&head, next, MDL); lease_dereference (&next, MDL); } } if (head) lease_dereference (&head, MDL); } /* Write v4 leases to permanent storage. */ int write_leases4(void) { struct lease *l; struct shared_network *s; struct pool *p; LEASE_STRUCT_PTR lptr[RESERVED_LEASES+1]; int num_written = 0, i; /* Write all the leases. */ for (s = shared_networks; s; s = s->next) { for (p = s->pools; p; p = p->next) { lptr[FREE_LEASES] = &p->free; lptr[ACTIVE_LEASES] = &p->active; lptr[EXPIRED_LEASES] = &p->expired; lptr[ABANDONED_LEASES] = &p->abandoned; lptr[BACKUP_LEASES] = &p->backup; lptr[RESERVED_LEASES] = &p->reserved; for (i = FREE_LEASES; i <= RESERVED_LEASES; i++) { for (l = LEASE_GET_FIRSTP(lptr[i]); l != NULL; l = LEASE_GET_NEXTP(lptr[i], l)) { #if !defined (DEBUG_DUMP_ALL_LEASES) if (l->hardware_addr.hlen != 0 || l->uid_len != 0 || l->tsfp != 0 || l->binding_state != FTS_FREE) #endif { if (write_lease(l) == 0) return (0); num_written++; } } } } } log_info ("Wrote %d leases to leases file.", num_written); return (1); } /* Write all interesting leases to permanent storage. */ int write_leases () { struct host_decl *hp; struct group_object *gp; struct hash_bucket *hb; struct class *cp; struct collection *colp; int i; int num_written; /* write all the dynamically-created class declarations. */ if (collections->classes) { numclasseswritten = 0; for (colp = collections ; colp ; colp = colp->next) { for (cp = colp->classes ; cp ; cp = cp->nic) { write_named_billing_class( (unsigned char *)cp->name, 0, cp); } } /* XXXJAB this number doesn't include subclasses... */ log_info ("Wrote %d class decls to leases file.", numclasseswritten); } /* Write all the dynamically-created group declarations. */ if (group_name_hash) { num_written = 0; for (i = 0; i < group_name_hash -> hash_count; i++) { for (hb = group_name_hash -> buckets [i]; hb; hb = hb -> next) { gp = (struct group_object *)hb -> value; if ((gp -> flags & GROUP_OBJECT_DYNAMIC) || ((gp -> flags & GROUP_OBJECT_STATIC) && (gp -> flags & GROUP_OBJECT_DELETED))) { if (!write_group (gp)) return 0; ++num_written; } } } log_info ("Wrote %d group decls to leases file.", num_written); } /* Write all the deleted host declarations. */ if (host_name_hash) { num_written = 0; for (i = 0; i < host_name_hash -> hash_count; i++) { for (hb = host_name_hash -> buckets [i]; hb; hb = hb -> next) { hp = (struct host_decl *)hb -> value; if (((hp -> flags & HOST_DECL_STATIC) && (hp -> flags & HOST_DECL_DELETED))) { if (!write_host (hp)) return 0; ++num_written; } } } log_info ("Wrote %d deleted host decls to leases file.", num_written); } /* Write all the new, dynamic host declarations. */ if (host_name_hash) { num_written = 0; for (i = 0; i < host_name_hash -> hash_count; i++) { for (hb = host_name_hash -> buckets [i]; hb; hb = hb -> next) { hp = (struct host_decl *)hb -> value; if ((hp -> flags & HOST_DECL_DYNAMIC)) { if (!write_host (hp)) ++num_written; } } } log_info ("Wrote %d new dynamic host decls to leases file.", num_written); } #if defined (FAILOVER_PROTOCOL) /* Write all the failover states. */ if (!dhcp_failover_write_all_states ()) return 0; #endif switch (local_family) { case AF_INET: if (write_leases4() == 0) return (0); break; #ifdef DHCPv6 case AF_INET6: if (write_leases6() == 0) return (0); break; #endif /* DHCPv6 */ } if (commit_leases() == 0) return (0); return (1); } #if !defined (BINARY_LEASES) /* Unlink all the leases in the queue. */ void lease_remove_all(struct lease **lq) { struct lease *lp, *ln = NULL; /* nothing to do */ if (*lq == NULL) return; /* We simply derefernce the first item in the list. When * it's reference counter goes to zero it will be cleaned * and the reference counter * * Get a pointer to the first item in the list and then * drop the reference from the queue pointer */ lease_reference(&lp, *lq, MDL); lease_dereference(lq, MDL); do { /* if we have a next save a pointer to it and unlink it */ if (lp->next) { lease_reference(&ln, lp->next, MDL); lease_dereference(&lp->next, MDL); } /* get rid of what we currently have */ lease_dereference(&lp, MDL); /* move the next to the current and loop */ lp = ln; ln = NULL; } while (lp != NULL); } /* * This routine walks through a given lease queue (lq) looking * for comp. If it doesn't find the lease it is a fatal error * as it should be on the given queue. Once we find the lease * we can remove it from this list. */ void lease_remove(struct lease **lq, struct lease *comp) { struct lease *prev, *lp; prev = NULL; for (lp = *lq; lp != NULL; lp = lp->next) { if (lp == comp) break; prev = lp; } if (!lp) { log_fatal("Lease with binding state %s not on its queue.", (comp->binding_state < 1 || comp->binding_state > FTS_LAST) ? "unknown" : binding_state_names[comp->binding_state - 1]); } if (prev) { lease_dereference(&prev->next, MDL); if (comp->next) { lease_reference(&prev->next, comp->next, MDL); lease_dereference (&comp->next, MDL); } } else { lease_dereference(lq, MDL); if (comp->next) { lease_reference(lq, comp->next, MDL); lease_dereference(&comp->next, MDL); } } } /* This routine inserts comp into lq in a sorted fashion. * The sort key is comp->sort_time, smaller values are * placed earlier in the list. */ void lease_insert(struct lease **lq, struct lease *comp) { struct lease *prev, *lp; static struct lease **last_lq = NULL; static struct lease *last_insert_point = NULL; /* This only works during server startup: during runtime, the last * lease may be dequeued in between calls. If the queue is the same * as was used previously, and the lease structure isn't (this is not * a re-queue), use that as a starting point for the insertion-sort. */ if ((server_starting & SS_QFOLLOW) && (lq == last_lq) && (comp != last_insert_point) && (last_insert_point->sort_time <= comp->sort_time)) { prev = last_insert_point; lp = prev->next; } else { prev = NULL; lp = *lq; } /* Insertion sort the lease onto the appropriate queue. */ for (; lp != NULL ; lp = lp->next) { if (lp->sort_time >= comp->sort_time) break; prev = lp; } if (prev) { if (prev->next) { lease_reference(&comp->next, prev->next, MDL); lease_dereference(&prev->next, MDL); } lease_reference(&prev->next, comp, MDL); } else { if (*lq) { lease_reference (&comp->next, *lq, MDL); lease_dereference(lq, MDL); } lease_reference(lq, comp, MDL); } last_insert_point = comp; last_lq = lq; return; } #endif /* In addition to placing this lease upon a lease queue depending on its * state, it also keeps track of the number of FREE and BACKUP leases in * existence, and sets the sort_time on the lease. * * Sort_time is used in pool_timer() to determine when the lease will * bubble to the top of the list and be supersede_lease()'d into its next * state (possibly, if all goes well). Example, ACTIVE leases move to * EXPIRED state when the 'ends' value is reached, so that is its sort * time. Most queues are sorted by 'ends', since it is generally best * practice to re-use the oldest lease, to reduce address collision * chances. */ int lease_enqueue (struct lease *comp) { LEASE_STRUCT_PTR lq; /* No queue to put it on? */ if (!comp -> pool) return 0; /* Figure out which queue it's going to. */ switch (comp -> binding_state) { case FTS_FREE: if (comp->flags & RESERVED_LEASE) { lq = &comp->pool->reserved; } else { lq = &comp->pool->free; comp->pool->free_leases++; } comp -> sort_time = comp -> ends; break; case FTS_ACTIVE: lq = &comp -> pool -> active; comp -> sort_time = comp -> ends; break; case FTS_EXPIRED: case FTS_RELEASED: case FTS_RESET: lq = &comp -> pool -> expired; #if defined(FAILOVER_PROTOCOL) /* In partner_down, tsfp is the time at which the lease * may be reallocated (stos+mclt). We can do that with * lease_mine_to_reallocate() anywhere between tsfp and * ends. But we prefer to wait until ends before doing it * automatically (choose the greater of the two). Note * that 'ends' is usually a historic timestamp in the * case of expired leases, is really only in the future * on released leases, and if we know a lease to be released * the peer might still know it to be active...in which case * it's possible the peer has renewed this lease, so avoid * doing that. */ if (comp->pool->failover_peer && comp->pool->failover_peer->me.state == partner_down) comp->sort_time = (comp->tsfp > comp->ends) ? comp->tsfp : comp->ends; else #endif comp->sort_time = comp->ends; break; case FTS_ABANDONED: lq = &comp -> pool -> abandoned; comp -> sort_time = comp -> ends; break; case FTS_BACKUP: if (comp->flags & RESERVED_LEASE) { lq = &comp->pool->reserved; } else { lq = &comp->pool->backup; comp->pool->backup_leases++; } comp -> sort_time = comp -> ends; break; default: log_error ("Lease with bogus binding state: %d", comp -> binding_state); #if defined (BINDING_STATE_DEBUG) abort (); #endif return 0; } LEASE_INSERTP(lq, comp); return 1; } /* For a given lease, sort it onto the right list in its pool and put it in each appropriate hash, understanding that it's already by definition in lease_ip_addr_hash. */ isc_result_t lease_instantiate(const void *key, unsigned len, void *object) { struct lease *lease = object; struct class *class; /* XXX If the lease doesn't have a pool at this point, it's an XXX orphan, which we *should* keep around until it expires, XXX but which right now we just forget. */ if (!lease -> pool) { lease_ip_hash_delete(lease_ip_addr_hash, lease->ip_addr.iabuf, lease->ip_addr.len, MDL); return ISC_R_SUCCESS; } #if defined (FAILOVER_PROTOCOL) /* If the lease is in FTS_BACKUP but there is no peer, then the * pool must have been formerly configured for failover and * is now configured as standalone. This means we need to * move the lease to FTS_FREE to make it available. */ if ((lease->binding_state == FTS_BACKUP) && (lease->pool->failover_peer == NULL)) { #else /* We aren't compiled for failover, so just move to FTS_FREE */ if (lease->binding_state == FTS_BACKUP) { #endif lease->binding_state = FTS_FREE; lease->next_binding_state = FTS_FREE; lease->rewind_binding_state = FTS_FREE; } /* Put the lease on the right queue. Failure to queue is probably * due to a bogus binding state. In such a case, we claim success, * so that later leases in a hash_foreach are processed, but we * return early as we really don't want hw address hash entries or * other cruft to surround such a bogus entry. */ if (!lease_enqueue(lease)) return ISC_R_SUCCESS; /* Record the lease in the uid hash if possible. */ if (lease -> uid) { uid_hash_add (lease); } /* Record it in the hardware address hash if possible. */ if (lease -> hardware_addr.hlen) { hw_hash_add (lease); } /* If the lease has a billing class, set up the billing. */ if (lease -> billing_class) { class = (struct class *)0; class_reference (&class, lease -> billing_class, MDL); class_dereference (&lease -> billing_class, MDL); /* If the lease is available for allocation, the billing is invalid, so we don't keep it. */ if (lease -> binding_state == FTS_ACTIVE || lease -> binding_state == FTS_EXPIRED || lease -> binding_state == FTS_RELEASED || lease -> binding_state == FTS_RESET) bill_class (lease, class); class_dereference (&class, MDL); } return ISC_R_SUCCESS; } /* Run expiry events on every pool. This is called on startup so that any expiry events that occurred after the server stopped and before it was restarted can be run. At the same time, if failover support is compiled in, we compute the balance of leases for the pool. */ void expire_all_pools () { struct shared_network *s; struct pool *p; int i; struct lease *l; LEASE_STRUCT_PTR lptr[RESERVED_LEASES+1]; /* Indicate that we are in the startup phase */ server_starting = SS_NOSYNC | SS_QFOLLOW; #if defined (BINARY_LEASES) /* set up the growth factors for the binary leases. * We use 100% for free, 50% for active and backup * 20% for expired, abandoned and reserved * but no less than 100, 50, and 20. */ for (s = shared_networks; s; s = s -> next) { for (p = s -> pools; p != NULL; p = p -> next) { size_t num_f = 100, num_a = 50, num_e = 20; if (p->lease_count > 100) { num_f = p->lease_count; num_a = num_f / 2; num_e = num_f / 5; } lc_init_growth(&p->free, num_f); lc_init_growth(&p->active, num_a); lc_init_growth(&p->expired, num_a); lc_init_growth(&p->abandoned, num_e); lc_init_growth(&p->backup, num_e); lc_init_growth(&p->reserved, num_e); } } #endif /* First, go over the hash list and actually put all the leases on the appropriate lists. */ lease_ip_hash_foreach(lease_ip_addr_hash, lease_instantiate); /* Loop through each pool in each shared network and call the * expiry routine on the pool. It is no longer safe to follow * the queue insertion point, as expiration of a lease can move * it between queues (and this may be the lease that function * points at). */ server_starting &= ~SS_QFOLLOW; for (s = shared_networks; s; s = s -> next) { for (p = s -> pools; p; p = p -> next) { pool_timer (p); p -> lease_count = 0; p -> free_leases = 0; p -> backup_leases = 0; lptr [FREE_LEASES] = &p -> free; lptr [ACTIVE_LEASES] = &p -> active; lptr [EXPIRED_LEASES] = &p -> expired; lptr [ABANDONED_LEASES] = &p -> abandoned; lptr [BACKUP_LEASES] = &p -> backup; lptr [RESERVED_LEASES] = &p->reserved; for (i = FREE_LEASES; i <= RESERVED_LEASES; i++) { for (l = LEASE_GET_FIRSTP(lptr[i]); l != NULL; l = LEASE_GET_NEXTP(lptr[i], l)) { p -> lease_count++; if (l -> ends <= cur_time) { if (l->binding_state == FTS_FREE) { if (i == FREE_LEASES) p->free_leases++; else if (i != RESERVED_LEASES) log_fatal("Impossible case " "at %s:%d.", MDL); } else if (l->binding_state == FTS_BACKUP) { if (i == BACKUP_LEASES) p->backup_leases++; else if (i != RESERVED_LEASES) log_fatal("Impossible case " "at %s:%d.", MDL); } } #if defined (FAILOVER_PROTOCOL) if (p -> failover_peer && l -> tstp > l -> atsfp && !(l -> flags & ON_UPDATE_QUEUE)) { l -> desired_binding_state = l -> binding_state; dhcp_failover_queue_update (l, 1); } #endif } } } } /* turn off startup phase */ server_starting = 0; } void dump_subnets () { struct lease *l; struct shared_network *s; struct subnet *n; struct pool *p; LEASE_STRUCT_PTR lptr[RESERVED_LEASES+1]; int i; log_info ("Subnets:"); for (n = subnets; n; n = n -> next_subnet) { log_debug (" Subnet %s", piaddr (n -> net)); log_debug (" netmask %s", piaddr (n -> netmask)); } log_info ("Shared networks:"); for (s = shared_networks; s; s = s -> next) { log_info (" %s", s -> name); for (p = s -> pools; p; p = p -> next) { lptr [FREE_LEASES] = &p -> free; lptr [ACTIVE_LEASES] = &p -> active; lptr [EXPIRED_LEASES] = &p -> expired; lptr [ABANDONED_LEASES] = &p -> abandoned; lptr [BACKUP_LEASES] = &p -> backup; lptr [RESERVED_LEASES] = &p->reserved; for (i = FREE_LEASES; i <= RESERVED_LEASES; i++) { for (l = LEASE_GET_FIRSTP(lptr[i]); l != NULL; l = LEASE_GET_NEXTP(lptr[i], l)) { print_lease (l); } } } } } HASH_FUNCTIONS(lease_ip, const unsigned char *, struct lease, lease_ip_hash_t, lease_reference, lease_dereference, do_ip4_hash) HASH_FUNCTIONS(lease_id, const unsigned char *, struct lease, lease_id_hash_t, lease_reference, lease_dereference, do_id_hash) HASH_FUNCTIONS (host, const unsigned char *, struct host_decl, host_hash_t, host_reference, host_dereference, do_string_hash) HASH_FUNCTIONS (class, const char *, struct class, class_hash_t, class_reference, class_dereference, do_string_hash) #if defined (DEBUG_MEMORY_LEAKAGE) && \ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) extern struct hash_table *dns_zone_hash; extern struct interface_info **interface_vector; extern int interface_count; dhcp_control_object_t *dhcp_control_object; extern struct hash_table *auth_key_hash; struct hash_table *universe_hash; struct universe **universes; int universe_count, universe_max; #if 0 extern int end; #endif #if defined (COMPACT_LEASES) extern struct lease *lease_hunks; #endif void free_everything(void) { struct subnet *sc = (struct subnet *)0, *sn = (struct subnet *)0; struct shared_network *nc = (struct shared_network *)0, *nn = (struct shared_network *)0; struct pool *pc = (struct pool *)0, *pn = (struct pool *)0; struct lease *lc = NULL, *ln = NULL, *ltemp = NULL; struct interface_info *ic = (struct interface_info *)0, *in = (struct interface_info *)0; struct class *cc = (struct class *)0, *cn = (struct class *)0; struct collection *lp; int i; /* Get rid of all the hash tables. */ if (host_hw_addr_hash) host_free_hash_table (&host_hw_addr_hash, MDL); host_hw_addr_hash = 0; if (host_uid_hash) host_free_hash_table (&host_uid_hash, MDL); host_uid_hash = 0; if (lease_uid_hash) lease_id_free_hash_table (&lease_uid_hash, MDL); lease_uid_hash = 0; if (lease_ip_addr_hash) lease_ip_free_hash_table (&lease_ip_addr_hash, MDL); lease_ip_addr_hash = 0; if (lease_hw_addr_hash) lease_id_free_hash_table (&lease_hw_addr_hash, MDL); lease_hw_addr_hash = 0; if (host_name_hash) host_free_hash_table (&host_name_hash, MDL); host_name_hash = 0; if (dns_zone_hash) dns_zone_free_hash_table (&dns_zone_hash, MDL); dns_zone_hash = 0; while (host_id_info != NULL) { host_id_info_t *tmp; option_dereference(&host_id_info->option, MDL); host_free_hash_table(&host_id_info->values_hash, MDL); tmp = host_id_info->next; dfree(host_id_info, MDL); host_id_info = tmp; } #if 0 if (auth_key_hash) auth_key_free_hash_table (&auth_key_hash, MDL); #endif auth_key_hash = 0; omapi_object_dereference ((omapi_object_t **)&dhcp_control_object, MDL); for (lp = collections; lp; lp = lp -> next) { if (lp -> classes) { class_reference (&cn, lp -> classes, MDL); do { if (cn) { class_reference (&cc, cn, MDL); class_dereference (&cn, MDL); } if (cc -> nic) { class_reference (&cn, cc -> nic, MDL); class_dereference (&cc -> nic, MDL); } group_dereference (&cc -> group, MDL); if (cc -> hash) { class_free_hash_table (&cc -> hash, MDL); cc -> hash = (struct hash_table *)0; } class_dereference (&cc, MDL); } while (cn); class_dereference (&lp -> classes, MDL); } } if (interface_vector) { for (i = 0; i < interface_count; i++) { if (interface_vector [i]) interface_dereference (&interface_vector [i], MDL); } dfree (interface_vector, MDL); interface_vector = 0; } if (interfaces) { interface_reference (&in, interfaces, MDL); do { if (in) { interface_reference (&ic, in, MDL); interface_dereference (&in, MDL); } if (ic -> next) { interface_reference (&in, ic -> next, MDL); interface_dereference (&ic -> next, MDL); } omapi_unregister_io_object ((omapi_object_t *)ic); if (ic -> shared_network) { if (ic -> shared_network -> interface) interface_dereference (&ic -> shared_network -> interface, MDL); shared_network_dereference (&ic -> shared_network, MDL); } interface_dereference (&ic, MDL); } while (in); interface_dereference (&interfaces, MDL); } /* Subnets are complicated because of the extra links. */ if (subnets) { subnet_reference (&sn, subnets, MDL); do { if (sn) { subnet_reference (&sc, sn, MDL); subnet_dereference (&sn, MDL); } if (sc -> next_subnet) { subnet_reference (&sn, sc -> next_subnet, MDL); subnet_dereference (&sc -> next_subnet, MDL); } if (sc -> next_sibling) subnet_dereference (&sc -> next_sibling, MDL); if (sc -> shared_network) shared_network_dereference (&sc -> shared_network, MDL); group_dereference (&sc -> group, MDL); if (sc -> interface) interface_dereference (&sc -> interface, MDL); subnet_dereference (&sc, MDL); } while (sn); subnet_dereference (&subnets, MDL); } /* So are shared networks. */ /* XXX: this doesn't work presently, but i'm ok just filtering * it out of the noise (you get a bigger spike on the real leaks). * It would be good to fix this, but it is not a "real bug," so not * today. This hack is incomplete, it doesn't trim out sub-values. */ if (shared_networks) { shared_network_dereference (&shared_networks, MDL); /* This is the old method (tries to free memory twice, broken) */ } else if (0) { shared_network_reference (&nn, shared_networks, MDL); do { if (nn) { shared_network_reference (&nc, nn, MDL); shared_network_dereference (&nn, MDL); } if (nc -> next) { shared_network_reference (&nn, nc -> next, MDL); shared_network_dereference (&nc -> next, MDL); } /* As are pools. */ if (nc -> pools) { pool_reference (&pn, nc -> pools, MDL); do { LEASE_STRUCT_PTR lptr[RESERVED_LEASES+1]; if (pn) { pool_reference (&pc, pn, MDL); pool_dereference (&pn, MDL); } if (pc -> next) { pool_reference (&pn, pc -> next, MDL); pool_dereference (&pc -> next, MDL); } lptr [FREE_LEASES] = &pc -> free; lptr [ACTIVE_LEASES] = &pc -> active; lptr [EXPIRED_LEASES] = &pc -> expired; lptr [ABANDONED_LEASES] = &pc -> abandoned; lptr [BACKUP_LEASES] = &pc -> backup; lptr [RESERVED_LEASES] = &pc->reserved; /* As (sigh) are leases. */ for (i = FREE_LEASES ; i <= RESERVED_LEASES ; i++) { if (LEASE_NOT_EMPTYP(lptr[i])) { lease_reference(&ln, LEASE_GET_FIRSTP(lptr[i]), MDL); do { /* save a pointer to the current lease */ lease_reference (&lc, ln, MDL); lease_dereference (&ln, MDL); /* get the next lease if there is one */ ltemp = LEASE_GET_NEXTP(lptr[i], lc); if (ltemp != NULL) { lease_reference(&ln, ltemp, MDL); } /* remove the current lease from the queue */ LEASE_REMOVEP(lptr[i], lc); if (lc -> billing_class) class_dereference (&lc -> billing_class, MDL); if (lc -> state) free_lease_state (lc -> state, MDL); lc -> state = (struct lease_state *)0; if (lc -> n_hw) lease_dereference (&lc -> n_hw, MDL); if (lc -> n_uid) lease_dereference (&lc -> n_uid, MDL); lease_dereference (&lc, MDL); } while (ln); } } if (pc -> group) group_dereference (&pc -> group, MDL); if (pc -> shared_network) shared_network_dereference (&pc -> shared_network, MDL); pool_dereference (&pc, MDL); } while (pn); pool_dereference (&nc -> pools, MDL); } /* Because of a circular reference, we need to nuke this manually. */ group_dereference (&nc -> group, MDL); shared_network_dereference (&nc, MDL); } while (nn); shared_network_dereference (&shared_networks, MDL); } cancel_all_timeouts (); relinquish_timeouts (); #if defined(DELAYED_ACK) relinquish_ackqueue(); #endif trace_free_all (); group_dereference (&root_group, MDL); executable_statement_dereference (&default_classification_rules, MDL); shutdown_state = shutdown_drop_omapi_connections; omapi_io_state_foreach (dhcp_io_shutdown, 0); shutdown_state = shutdown_listeners; omapi_io_state_foreach (dhcp_io_shutdown, 0); shutdown_state = shutdown_dhcp; omapi_io_state_foreach (dhcp_io_shutdown, 0); omapi_object_dereference ((omapi_object_t **)&icmp_state, MDL); universe_free_hash_table (&universe_hash, MDL); for (i = 0; i < universe_count; i++) { #if 0 union { const char *c; char *s; } foo; #endif if (universes [i]) { if (universes[i]->name_hash) option_name_free_hash_table( &universes[i]->name_hash, MDL); if (universes[i]->code_hash) option_code_free_hash_table( &universes[i]->code_hash, MDL); #if 0 if (universes [i] -> name > (char *)&end) { foo.c = universes [i] -> name; dfree (foo.s, MDL); } if (universes [i] > (struct universe *)&end) dfree (universes [i], MDL); #endif } } dfree (universes, MDL); relinquish_free_lease_states (); relinquish_free_pairs (); relinquish_free_expressions (); relinquish_free_binding_values (); relinquish_free_option_caches (); relinquish_free_packets (); #if defined(COMPACT_LEASES) relinquish_lease_hunks (); #endif relinquish_hash_bucket_hunks (); omapi_type_relinquish (); } #endif /* DEBUG_MEMORY_LEAKAGE_ON_EXIT */ dhcp-4.4.1/server/mdb6.c000644 000765 000024 00000245644 13243301226 015234 0ustar00tmarkstaff000000 000000 /* * Copyright (C) 2007-2017 by 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /*! * \todo assert() * \todo simplify functions, as pool is now in iaaddr */ /*! \file server/mdb6.c * * \page ipv6structures IPv6 Structures Overview * * A brief description of the IPv6 structures as reverse engineered. * * There are four major data structures in the lease configuraion. * * - shared_network - The shared network is the outer enclosing scope for a * network region that shares a broadcast domain. It is * composed of one or more subnets all of which are valid * in the given region. The share network may be * explicitly defined or implicitly created if there is * only a subnet statement. This structrure is shared * with v4. Each shared network statment or naked subnet * will map to one of these structures * * - subnet - The subnet structure mostly specifies the address range * that could be valid in a given region. This structute * doesn't include the addresses that the server can delegate * those are in the ipv6_pool. This structure is also shared * with v4. Each subnet statement will map to one of these * structures. * * - ipv6_pond - The pond structure is a grouping of the address and prefix * information via the pointers to the ipv6_pool and the * allowability of this pool for given clinets via the permit * lists and the valid TIMEs. This is equivilent to the v4 * pool structure and would have been named ip6_pool except * that the name was already in use. Generally each pool6 * statement will map to one of these structures. In addition * there may be one or for each group of naked range6 and * prefix6 statements within a shared network that share * the same group of statements. * * - ipv6_pool - this contains information about a pool of addresses or prefixes * that the server is using. This includes a hash table that * tracks the active items and a pair of heap tables one for * active items and one for non-active items. The heap tables * are used to determine the next items to be modified due to * timing events (expire mostly). * * The linkages then look like this: * \verbatim *+--------------+ +-------------+ *|Shared Network| | ipv6_pond | *| group | | group | *| | | permit info | *| | | next ----> *| ponds ---->| | *| |<---- shared | *| Subnets | | pools | *+-----|--------+ +------|------+ * | ^ | ^ * | | v | * | | +-----------|-+ * | | | ipv6_pool | | * | | | type | | * | | | ipv6_pond | * | | | | * | | | next ----> * | | | | * | | | subnet | * | | +-----|-------+ * | | | * | | v * | | +-------------+ * | | | subnet | * | +---------- shared | * +----------->| | * | group | * +-------------+ * * The shared network contains a list of all the subnets that are on a broadcast * doamin. These can be used to determine if an address makes sense in a given * domain, but the subnets do not contain the addresses the server can delegate. * Those are stored in the ponds and pools. * * In the simple case to find an acceptable address the server would first find * the shared network the client is on based on either the interface used to * receive the request or the relay agent's information. From the shared * network the server will walk through it's list of ponds. For each pond it * will evaluate the permit information against the (already done) classification. * If it finds an acceptable pond it will then walk through the pools for that * pond. The server first checks the type of the pool (NA, TA and PD) agaisnt the * request and if they match it attemps to find an address within that pool. On * success the address is used, on failure the server steps to the next pool and * if necessary to the next pond. * * When the server is successful in finding an address it will execute any * statements assocaited with the pond, then the subnet, then the shared * network the group field is for in the above picture). * * In configurations that don't include either a shared network or a pool6 * statement (or both) the missing pieces are created. * * * There are three major data structuress involved in the lease database: * * - ipv6_pool - see above * - ia_xx - this contains information about a single IA from a request * normally it will contain one pointer to a lease for the client * but it may contain more in some circumstances. There are 3 * hash tables to aid in accessing these one each for NA, TA and PD. * - iasubopt - the v6 lease structure. These are created dynamically when * a client asks for something and will eventually be destroyed * if the client doesn't re-ask for that item. A lease has space * for backpointers to the IA and to the pool to which it belongs. * The pool backpointer is always filled, the IA pointer may not be. * * In normal use we then have something like this: * * \verbatim * ia hash tables * ia_na_active +----------------+ * ia_ta_active +------------+ | pool | * ia_pd_active | iasubopt |<--| active hash | * +-----------------+ | aka lease |<--| active heap | * | ia_xx | | pool ptr |-->| | * | iasubopt array |<---| iaptr |<--| inactive heap | * | lease ptr |--->| | | | * +-----------------+ +------------+ +----------------+ * \endverbatim * * For the pool either the inactive heap will have a pointer * or both the active heap and the active hash will have pointers. * * I think there are several major items to notice. The first is * that as a lease moves around it will be added to and removed * from the address hash table in the pool and between the active * and inactive hash tables. The hash table and the active heap * are used when the lease is either active or abandoned. The * inactive heap is used for all other states. In particular a * lease that has expired or been released will be cleaned * (DDNS removal etc) and then moved to the inactive heap. After * some time period (currently 1 hour) it will be freed. * * The second is that when a client requests specific addresses, * either because it previously owned them or if the server supplied * them as part of a solicit, the server will try to lookup the ia_xx * associated with the client and find the addresses there. If it * does find appropriate leases it moves them from the old IA to * a new IA and eventually replaces the old IA with the new IA * in the IA hash tables. * */ #include "config.h" #include #include #include #include #include "dhcpd.h" #include "omapip/omapip.h" #include "omapip/hash.h" #include HASH_FUNCTIONS(ia, unsigned char *, struct ia_xx, ia_hash_t, ia_reference, ia_dereference, do_string_hash) ia_hash_t *ia_na_active; ia_hash_t *ia_ta_active; ia_hash_t *ia_pd_active; HASH_FUNCTIONS(iasubopt, struct in6_addr *, struct iasubopt, iasubopt_hash_t, iasubopt_reference, iasubopt_dereference, do_string_hash) struct ipv6_pool **pools; int num_pools; /* * Create a new IAADDR/PREFIX structure. * * - iasubopt must be a pointer to a (struct iasubopt *) pointer previously * initialized to NULL */ isc_result_t iasubopt_allocate(struct iasubopt **iasubopt, const char *file, int line) { struct iasubopt *tmp; if (iasubopt == NULL) { log_error("%s(%d): NULL pointer reference", file, line); return DHCP_R_INVALIDARG; } if (*iasubopt != NULL) { log_error("%s(%d): non-NULL pointer", file, line); return DHCP_R_INVALIDARG; } tmp = dmalloc(sizeof(*tmp), file, line); if (tmp == NULL) { return ISC_R_NOMEMORY; } tmp->refcnt = 1; tmp->state = FTS_FREE; tmp->active_index = 0; tmp->inactive_index = 0; tmp->plen = 255; *iasubopt = tmp; return ISC_R_SUCCESS; } /* * Reference an IAADDR/PREFIX structure. * * - iasubopt must be a pointer to a (struct iasubopt *) pointer previously * initialized to NULL */ isc_result_t iasubopt_reference(struct iasubopt **iasubopt, struct iasubopt *src, const char *file, int line) { if (iasubopt == NULL) { log_error("%s(%d): NULL pointer reference", file, line); return DHCP_R_INVALIDARG; } if (*iasubopt != NULL) { log_error("%s(%d): non-NULL pointer", file, line); return DHCP_R_INVALIDARG; } if (src == NULL) { log_error("%s(%d): NULL pointer reference", file, line); return DHCP_R_INVALIDARG; } *iasubopt = src; src->refcnt++; return ISC_R_SUCCESS; } /* * Dereference an IAADDR/PREFIX structure. * * If it is the last reference, then the memory for the * structure is freed. */ isc_result_t iasubopt_dereference(struct iasubopt **iasubopt, const char *file, int line) { struct iasubopt *tmp; if ((iasubopt == NULL) || (*iasubopt == NULL)) { log_error("%s(%d): NULL pointer", file, line); return DHCP_R_INVALIDARG; } tmp = *iasubopt; *iasubopt = NULL; tmp->refcnt--; if (tmp->refcnt < 0) { log_error("%s(%d): negative refcnt", file, line); tmp->refcnt = 0; } if (tmp->refcnt == 0) { if (tmp->ia != NULL) { ia_dereference(&(tmp->ia), file, line); } if (tmp->ipv6_pool != NULL) { ipv6_pool_dereference(&(tmp->ipv6_pool), file, line); } if (tmp->scope != NULL) { binding_scope_dereference(&tmp->scope, file, line); } if (tmp->on_star.on_expiry != NULL) { executable_statement_dereference (&tmp->on_star.on_expiry, MDL); } if (tmp->on_star.on_commit != NULL) { executable_statement_dereference (&tmp->on_star.on_commit, MDL); } if (tmp->on_star.on_release != NULL) { executable_statement_dereference (&tmp->on_star.on_release, MDL); } dfree(tmp, file, line); } return ISC_R_SUCCESS; } /* * Make the key that we use for IA. */ isc_result_t ia_make_key(struct data_string *key, u_int32_t iaid, const char *duid, unsigned int duid_len, const char *file, int line) { memset(key, 0, sizeof(*key)); key->len = duid_len + sizeof(iaid); if (!buffer_allocate(&(key->buffer), key->len, file, line)) { return ISC_R_NOMEMORY; } key->data = key->buffer->data; memcpy((char *)key->data, &iaid, sizeof(iaid)); memcpy((char *)key->data + sizeof(iaid), duid, duid_len); return ISC_R_SUCCESS; } /* * Create a new IA structure. * * - ia must be a pointer to a (struct ia_xx *) pointer previously * initialized to NULL * - iaid and duid are values from the client * * XXXsk: we don't concern ourself with the byte order of the IAID, * which might be a problem if we transfer this structure * between machines of different byte order */ isc_result_t ia_allocate(struct ia_xx **ia, u_int32_t iaid, const char *duid, unsigned int duid_len, const char *file, int line) { struct ia_xx *tmp; if (ia == NULL) { log_error("%s(%d): NULL pointer reference", file, line); return DHCP_R_INVALIDARG; } if (*ia != NULL) { log_error("%s(%d): non-NULL pointer", file, line); return DHCP_R_INVALIDARG; } tmp = dmalloc(sizeof(*tmp), file, line); if (tmp == NULL) { return ISC_R_NOMEMORY; } if (ia_make_key(&tmp->iaid_duid, iaid, duid, duid_len, file, line) != ISC_R_SUCCESS) { dfree(tmp, file, line); return ISC_R_NOMEMORY; } tmp->refcnt = 1; *ia = tmp; return ISC_R_SUCCESS; } /* * Reference an IA structure. * * - ia must be a pointer to a (struct ia_xx *) pointer previously * initialized to NULL */ isc_result_t ia_reference(struct ia_xx **ia, struct ia_xx *src, const char *file, int line) { if (ia == NULL) { log_error("%s(%d): NULL pointer reference", file, line); return DHCP_R_INVALIDARG; } if (*ia != NULL) { log_error("%s(%d): non-NULL pointer", file, line); return DHCP_R_INVALIDARG; } if (src == NULL) { log_error("%s(%d): NULL pointer reference", file, line); return DHCP_R_INVALIDARG; } *ia = src; src->refcnt++; return ISC_R_SUCCESS; } /* * Dereference an IA structure. * * If it is the last reference, then the memory for the * structure is freed. */ isc_result_t ia_dereference(struct ia_xx **ia, const char *file, int line) { struct ia_xx *tmp; int i; if ((ia == NULL) || (*ia == NULL)) { log_error("%s(%d): NULL pointer", file, line); return DHCP_R_INVALIDARG; } tmp = *ia; *ia = NULL; tmp->refcnt--; if (tmp->refcnt < 0) { log_error("%s(%d): negative refcnt", file, line); tmp->refcnt = 0; } if (tmp->refcnt == 0) { if (tmp->iasubopt != NULL) { for (i=0; inum_iasubopt; i++) { iasubopt_dereference(&(tmp->iasubopt[i]), file, line); } dfree(tmp->iasubopt, file, line); } data_string_forget(&(tmp->iaid_duid), file, line); dfree(tmp, file, line); } return ISC_R_SUCCESS; } /* * Add an IAADDR/PREFIX entry to an IA structure. */ isc_result_t ia_add_iasubopt(struct ia_xx *ia, struct iasubopt *iasubopt, const char *file, int line) { int max; struct iasubopt **new; /* * Grow our array if we need to. * * Note: we pick 4 as the increment, as that seems a reasonable * guess as to how many addresses/prefixes we might expect * on an interface. */ if (ia->max_iasubopt <= ia->num_iasubopt) { max = ia->max_iasubopt + 4; new = dmalloc(max * sizeof(struct iasubopt *), file, line); if (new == NULL) { return ISC_R_NOMEMORY; } memcpy(new, ia->iasubopt, ia->num_iasubopt * sizeof(struct iasubopt *)); ia->iasubopt = new; ia->max_iasubopt = max; } iasubopt_reference(&(ia->iasubopt[ia->num_iasubopt]), iasubopt, file, line); ia->num_iasubopt++; return ISC_R_SUCCESS; } /* * Remove an IAADDR/PREFIX entry to an IA structure. * * Note: if a suboption appears more than once, then only ONE will be removed. */ void ia_remove_iasubopt(struct ia_xx *ia, struct iasubopt *iasubopt, const char *file, int line) { int i, j; if (ia == NULL || iasubopt == NULL) return; for (i=0; inum_iasubopt; i++) { if (ia->iasubopt[i] == iasubopt) { /* remove this sub option */ iasubopt_dereference(&(ia->iasubopt[i]), file, line); /* move remaining suboption pointers down one */ for (j=i+1; j < ia->num_iasubopt; j++) { ia->iasubopt[j-1] = ia->iasubopt[j]; } /* decrease our total count */ /* remove the back-reference in the suboption itself */ ia_dereference(&iasubopt->ia, file, line); ia->num_iasubopt--; return; } } log_error("%s(%d): IAADDR/PREFIX not in IA", file, line); } /* * Remove all addresses/prefixes from an IA. */ void ia_remove_all_lease(struct ia_xx *ia, const char *file, int line) { int i; for (i=0; inum_iasubopt; i++) { ia_dereference(&(ia->iasubopt[i]->ia), file, line); iasubopt_dereference(&(ia->iasubopt[i]), file, line); } ia->num_iasubopt = 0; } /* * Compare two IA. */ isc_boolean_t ia_equal(const struct ia_xx *a, const struct ia_xx *b) { isc_boolean_t found; int i, j; /* * Handle cases where one or both of the inputs is NULL. */ if (a == NULL) { if (b == NULL) { return ISC_TRUE; } else { return ISC_FALSE; } } /* * Check the type is the same. */ if (a->ia_type != b->ia_type) { return ISC_FALSE; } /* * Check the DUID is the same. */ if (a->iaid_duid.len != b->iaid_duid.len) { return ISC_FALSE; } if (memcmp(a->iaid_duid.data, b->iaid_duid.data, a->iaid_duid.len) != 0) { return ISC_FALSE; } /* * Make sure we have the same number of addresses/prefixes in each. */ if (a->num_iasubopt != b->num_iasubopt) { return ISC_FALSE; } /* * Check that each address/prefix is present in both. */ for (i=0; inum_iasubopt; i++) { found = ISC_FALSE; for (j=0; jnum_iasubopt; j++) { if (a->iasubopt[i]->plen != b->iasubopt[i]->plen) continue; if (memcmp(&(a->iasubopt[i]->addr), &(b->iasubopt[j]->addr), sizeof(struct in6_addr)) == 0) { found = ISC_TRUE; break; } } if (!found) { return ISC_FALSE; } } /* * These are the same in every way we care about. */ return ISC_TRUE; } /* * Helper function for lease heaps. * Makes the top of the heap the oldest lease. */ static isc_boolean_t lease_older(void *a, void *b) { struct iasubopt *la = (struct iasubopt *)a; struct iasubopt *lb = (struct iasubopt *)b; if (la->hard_lifetime_end_time == lb->hard_lifetime_end_time) { return difftime(la->soft_lifetime_end_time, lb->soft_lifetime_end_time) < 0; } else { return difftime(la->hard_lifetime_end_time, lb->hard_lifetime_end_time) < 0; } } /* * Helper functions for lease address/prefix heaps. * Callback when an address's position in the heap changes. */ static void active_changed(void *iasubopt, unsigned int new_heap_index) { ((struct iasubopt *)iasubopt)->active_index = new_heap_index; } static void inactive_changed(void *iasubopt, unsigned int new_heap_index) { ((struct iasubopt *)iasubopt)->inactive_index = new_heap_index; } /*! * * \brief Create a new IPv6 lease pool structure * * Allocate space for a new ipv6_pool structure and return a reference * to it, includes setting the reference count to 1. * * \param pool = space for returning a referenced pointer to the pool. * This must point to a space that has been initialzied * to NULL by the caller. * \param[in] type = The type of the pool NA, TA or PD * \param[in] start_addr = The first address in the range for the pool * \param[in] bits = The contiguous bits of the pool * * \return * ISC_R_SUCCESS = The pool was successfully created, pool points to it. * DHCP_R_INVALIDARG = One of the arugments was invalid, pool has not been * modified * ISC_R_NOMEMORY = The system wasn't able to allocate memory, pool has * not been modified. */ isc_result_t ipv6_pool_allocate(struct ipv6_pool **pool, u_int16_t type, const struct in6_addr *start_addr, int bits, int units, const char *file, int line) { struct ipv6_pool *tmp; if (pool == NULL) { log_error("%s(%d): NULL pointer reference", file, line); return DHCP_R_INVALIDARG; } if (*pool != NULL) { log_error("%s(%d): non-NULL pointer", file, line); return DHCP_R_INVALIDARG; } tmp = dmalloc(sizeof(*tmp), file, line); if (tmp == NULL) { return ISC_R_NOMEMORY; } tmp->refcnt = 1; tmp->pool_type = type; tmp->start_addr = *start_addr; tmp->bits = bits; tmp->units = units; if (!iasubopt_new_hash(&tmp->leases, DEFAULT_HASH_SIZE, file, line)) { dfree(tmp, file, line); return ISC_R_NOMEMORY; } if (isc_heap_create(dhcp_gbl_ctx.mctx, lease_older, active_changed, 0, &(tmp->active_timeouts)) != ISC_R_SUCCESS) { iasubopt_free_hash_table(&(tmp->leases), file, line); dfree(tmp, file, line); return ISC_R_NOMEMORY; } if (isc_heap_create(dhcp_gbl_ctx.mctx, lease_older, inactive_changed, 0, &(tmp->inactive_timeouts)) != ISC_R_SUCCESS) { isc_heap_destroy(&(tmp->active_timeouts)); iasubopt_free_hash_table(&(tmp->leases), file, line); dfree(tmp, file, line); return ISC_R_NOMEMORY; } *pool = tmp; return ISC_R_SUCCESS; } /*! * * \brief reference an IPv6 pool structure. * * This function genreates a reference to an ipv6_pool structure * and increments the reference count on the structure. * * \param[out] pool = space for returning a referenced pointer to the pool. * This must point to a space that has been initialzied * to NULL by the caller. * \param[in] src = A pointer to the pool to reference. This must not be * NULL. * * \return * ISC_R_SUCCESS = The pool was successfully referenced, pool now points * to src. * DHCP_R_INVALIDARG = One of the arugments was invalid, pool has not been * modified. */ isc_result_t ipv6_pool_reference(struct ipv6_pool **pool, struct ipv6_pool *src, const char *file, int line) { if (pool == NULL) { log_error("%s(%d): NULL pointer reference", file, line); return DHCP_R_INVALIDARG; } if (*pool != NULL) { log_error("%s(%d): non-NULL pointer", file, line); return DHCP_R_INVALIDARG; } if (src == NULL) { log_error("%s(%d): NULL pointer reference", file, line); return DHCP_R_INVALIDARG; } *pool = src; src->refcnt++; return ISC_R_SUCCESS; } /* * Note: Each IAADDR/PREFIX in a pool is referenced by the pool. This is needed * to prevent the lease from being garbage collected out from under the * pool. * * The references are made from the hash and from the heap. The following * helper functions dereference these when a pool is destroyed. */ /* * Helper function for pool cleanup. * Dereference each of the hash entries in a pool. */ static isc_result_t dereference_hash_entry(const void *name, unsigned len, void *value) { struct iasubopt *iasubopt = (struct iasubopt *)value; iasubopt_dereference(&iasubopt, MDL); return ISC_R_SUCCESS; } /* * Helper function for pool cleanup. * Dereference each of the heap entries in a pool. */ static void dereference_heap_entry(void *value, void *dummy) { struct iasubopt *iasubopt = (struct iasubopt *)value; iasubopt_dereference(&iasubopt, MDL); } /*! * * \brief de-reference an IPv6 pool structure. * * This function decrements the reference count in an ipv6_pool structure. * If this was the last reference then the memory for the structure is * freed. * * \param[in] pool = A pointer to the pointer to the pool that should be * de-referenced. On success the pointer to the pool * is cleared. It must not be NULL and must not point * to NULL. * * \return * ISC_R_SUCCESS = The pool was successfully de-referenced, pool now points * to NULL * DHCP_R_INVALIDARG = One of the arugments was invalid, pool has not been * modified. */ isc_result_t ipv6_pool_dereference(struct ipv6_pool **pool, const char *file, int line) { struct ipv6_pool *tmp; if ((pool == NULL) || (*pool == NULL)) { log_error("%s(%d): NULL pointer", file, line); return DHCP_R_INVALIDARG; } tmp = *pool; *pool = NULL; tmp->refcnt--; if (tmp->refcnt < 0) { log_error("%s(%d): negative refcnt", file, line); tmp->refcnt = 0; } if (tmp->refcnt == 0) { iasubopt_hash_foreach(tmp->leases, dereference_hash_entry); iasubopt_free_hash_table(&(tmp->leases), file, line); isc_heap_foreach(tmp->active_timeouts, dereference_heap_entry, NULL); isc_heap_destroy(&(tmp->active_timeouts)); isc_heap_foreach(tmp->inactive_timeouts, dereference_heap_entry, NULL); isc_heap_destroy(&(tmp->inactive_timeouts)); dfree(tmp, file, line); } return ISC_R_SUCCESS; } /* * Create an address by hashing the input, and using that for * the non-network part. */ static void build_address6(struct in6_addr *addr, const struct in6_addr *net_start_addr, int net_bits, const struct data_string *input) { isc_md5_t ctx; int net_bytes; int i; char *str; const char *net_str; /* * Use MD5 to get a nice 128 bit hash of the input. * Yes, we know MD5 isn't cryptographically sound. * No, we don't care. */ isc_md5_init(&ctx); isc_md5_update(&ctx, input->data, input->len); isc_md5_final(&ctx, (unsigned char *)addr); /* * Copy the [0..128] network bits over. */ str = (char *)addr; net_str = (const char *)net_start_addr; net_bytes = net_bits / 8; for (i = 0; i < net_bytes; i++) { str[i] = net_str[i]; } switch (net_bits % 8) { case 1: str[i] = (str[i] & 0x7F) | (net_str[i] & 0x80); break; case 2: str[i] = (str[i] & 0x3F) | (net_str[i] & 0xC0); break; case 3: str[i] = (str[i] & 0x1F) | (net_str[i] & 0xE0); break; case 4: str[i] = (str[i] & 0x0F) | (net_str[i] & 0xF0); break; case 5: str[i] = (str[i] & 0x07) | (net_str[i] & 0xF8); break; case 6: str[i] = (str[i] & 0x03) | (net_str[i] & 0xFC); break; case 7: str[i] = (str[i] & 0x01) | (net_str[i] & 0xFE); break; } /* * Set the universal/local bit ("u bit") to zero for /64s. The * individual/group bit ("g bit") is unchanged, because the g-bit * has no meaning when the u-bit is cleared. */ if (net_bits == 64) str[8] &= ~0x02; } #ifdef EUI_64 int valid_eui_64_duid(const struct data_string* uid, int offset) { if (uid->len == (offset + EUI_64_ID_LEN)) { const unsigned char* duid = uid->data + offset; return (((duid[0] == 0x00 && duid[1] == 0x03) && (duid[2] == 0x00 && duid[3] == 0x1b))); } return(0); } /* * Create an EUI-64 address */ static isc_result_t build_address6_eui_64(struct in6_addr *addr, const struct in6_addr *net_start_addr, int net_bits, const struct data_string *iaid_duid, int duid_beg) { if (net_bits != 64) { log_error("build_address_eui_64: network is not 64 bits"); return (ISC_R_FAILURE); } if (valid_eui_64_duid(iaid_duid, duid_beg)) { const unsigned char *duid = iaid_duid->data + duid_beg; /* copy network prefix to the high 64 bits */ memcpy(addr->s6_addr, net_start_addr->s6_addr, 8); /* copy Link-layer address to low 64 bits */ memcpy(addr->s6_addr + 8, duid + 4, 8); /* RFC-3315 Any address assigned by a server that is based * on an EUI-64 identifier MUST include an interface identifier * with the "u" (universal/local) and "g" (individual/group) * bits of the interface identifier set appropriately, as * indicated in section 2.5.1 of RFC 2373 [5]. */ addr->s6_addr[8] |= 0x02; return (ISC_R_SUCCESS); } log_error("build_address_eui_64: iaid_duid not a valid EUI-64: %s", print_hex_1(iaid_duid->len, iaid_duid->data, 60)); return (ISC_R_FAILURE); } int valid_for_eui_64_pool(struct ipv6_pool* pool, struct data_string* uid, int duid_beg, struct in6_addr* ia_addr) { struct in6_addr test_addr; /* If it's not an EUI-64 pool bail */ if (!pool->ipv6_pond->use_eui_64) { return (0); } if (!valid_eui_64_duid(uid, duid_beg)) { /* Dynamic lease in a now eui_64 pond, toss it*/ return (0); } /* Call build_address6_eui_64() and compare it's result to * this lease and see if they match. */ memset (&test_addr, 0, sizeof(test_addr)); build_address6_eui_64(&test_addr, &pool->start_addr, pool->bits, uid, duid_beg); return (!memcmp(ia_addr, &test_addr, sizeof(test_addr))); } #endif /* * Create a temporary address by a variant of RFC 4941 algo. * Note: this should not be used for prefixes shorter than 64 bits. */ static void build_temporary6(struct in6_addr *addr, const struct in6_addr *net_start_addr, int net_bits, const struct data_string *input) { static u_int32_t history[2]; static u_int32_t counter = 0; isc_md5_t ctx; unsigned char md[16]; /* * First time/time to reseed. * Please use a good pseudo-random generator here! */ if (counter == 0) { isc_random_get(&history[0]); isc_random_get(&history[1]); } /* * Use MD5 as recommended by RFC 4941. */ isc_md5_init(&ctx); isc_md5_update(&ctx, (unsigned char *)&history[0], 8UL); isc_md5_update(&ctx, input->data, input->len); isc_md5_final(&ctx, md); /* * Build the address. */ if (net_bits == 64) { memcpy(&addr->s6_addr[0], &net_start_addr->s6_addr[0], 8); memcpy(&addr->s6_addr[8], md, 8); addr->s6_addr[8] &= ~0x02; } else { int net_bytes; int i; char *str; const char *net_str; /* * Copy the [0..128] network bits over. */ str = (char *)addr; net_str = (const char *)net_start_addr; net_bytes = net_bits / 8; for (i = 0; i < net_bytes; i++) { str[i] = net_str[i]; } memcpy(str + net_bytes, md, 16 - net_bytes); switch (net_bits % 8) { case 1: str[i] = (str[i] & 0x7F) | (net_str[i] & 0x80); break; case 2: str[i] = (str[i] & 0x3F) | (net_str[i] & 0xC0); break; case 3: str[i] = (str[i] & 0x1F) | (net_str[i] & 0xE0); break; case 4: str[i] = (str[i] & 0x0F) | (net_str[i] & 0xF0); break; case 5: str[i] = (str[i] & 0x07) | (net_str[i] & 0xF8); break; case 6: str[i] = (str[i] & 0x03) | (net_str[i] & 0xFC); break; case 7: str[i] = (str[i] & 0x01) | (net_str[i] & 0xFE); break; } } /* * Save history for the next call. */ memcpy((unsigned char *)&history[0], md + 8, 8); counter++; } /* Reserved Subnet Router Anycast ::0:0:0:0. */ static struct in6_addr rtany; /* Reserved Subnet Anycasts ::fdff:ffff:ffff:ff80-::fdff:ffff:ffff:ffff. */ static struct in6_addr resany; /* * Create a lease for the given address and client duid. * * - pool must be a pointer to a (struct ipv6_pool *) pointer previously * initialized to NULL * * Right now we simply hash the DUID, and if we get a collision, we hash * again until we find a free address. We try this a fixed number of times, * to avoid getting stuck in a loop (this is important on small pools * where we can run out of space). * * We return the number of attempts that it took to find an available * lease. This tells callers when a pool is are filling up, as * well as an indication of how full the pool is; statistically the * more full a pool is the more attempts must be made before finding * a free lease. Realistically this will only happen in very full * pools. * * We probably want different algorithms depending on the network size, in * the long term. */ isc_result_t create_lease6(struct ipv6_pool *pool, struct iasubopt **addr, unsigned int *attempts, const struct data_string *uid, time_t soft_lifetime_end_time) { struct data_string ds; struct in6_addr tmp; struct iasubopt *test_iaaddr; struct data_string new_ds; struct iasubopt *iaaddr; isc_result_t result; isc_boolean_t reserved_iid; static isc_boolean_t init_resiid = ISC_FALSE; /* * Fill the reserved IIDs. */ if (!init_resiid) { memset(&rtany, 0, 16); memset(&resany, 0, 8); resany.s6_addr[8] = 0xfd; memset(&resany.s6_addr[9], 0xff, 6); init_resiid = ISC_TRUE; } /* * Use the UID as our initial seed for the hash */ memset(&ds, 0, sizeof(ds)); data_string_copy(&ds, (struct data_string *)uid, MDL); *attempts = 0; for (;;) { /* * Give up at some point. */ if (++(*attempts) > 100) { data_string_forget(&ds, MDL); return ISC_R_NORESOURCES; } /* * Build a resource. */ switch (pool->pool_type) { case D6O_IA_NA: /* address */ build_address6(&tmp, &pool->start_addr, pool->bits, &ds); break; case D6O_IA_TA: /* temporary address */ build_temporary6(&tmp, &pool->start_addr, pool->bits, &ds); break; case D6O_IA_PD: /* prefix */ log_error("create_lease6: prefix pool."); return DHCP_R_INVALIDARG; default: log_error("create_lease6: untyped pool."); return DHCP_R_INVALIDARG; } /* * Avoid reserved interface IDs. (cf. RFC 5453) */ reserved_iid = ISC_FALSE; if (memcmp(&tmp.s6_addr[8], &rtany.s6_addr[8], 8) == 0) { reserved_iid = ISC_TRUE; } if (!reserved_iid && (memcmp(&tmp.s6_addr[8], &resany.s6_addr[8], 7) == 0) && ((tmp.s6_addr[15] & 0x80) == 0x80)) { reserved_iid = ISC_TRUE; } /* * If this address is not in use, we're happy with it */ test_iaaddr = NULL; if (!reserved_iid && (iasubopt_hash_lookup(&test_iaaddr, pool->leases, &tmp, sizeof(tmp), MDL) == 0)) { break; } if (test_iaaddr != NULL) iasubopt_dereference(&test_iaaddr, MDL); /* * Otherwise, we create a new input, adding the address */ memset(&new_ds, 0, sizeof(new_ds)); new_ds.len = ds.len + sizeof(tmp); if (!buffer_allocate(&new_ds.buffer, new_ds.len, MDL)) { data_string_forget(&ds, MDL); return ISC_R_NOMEMORY; } new_ds.data = new_ds.buffer->data; memcpy(new_ds.buffer->data, ds.data, ds.len); memcpy(new_ds.buffer->data + ds.len, &tmp, sizeof(tmp)); data_string_forget(&ds, MDL); data_string_copy(&ds, &new_ds, MDL); data_string_forget(&new_ds, MDL); } data_string_forget(&ds, MDL); /* * We're happy with the address, create an IAADDR * to hold it. */ iaaddr = NULL; result = iasubopt_allocate(&iaaddr, MDL); if (result != ISC_R_SUCCESS) { return result; } iaaddr->plen = 0; memcpy(&iaaddr->addr, &tmp, sizeof(iaaddr->addr)); /* * Add the lease to the pool (note state is free, not active?!). */ result = add_lease6(pool, iaaddr, soft_lifetime_end_time); if (result == ISC_R_SUCCESS) { iasubopt_reference(addr, iaaddr, MDL); } iasubopt_dereference(&iaaddr, MDL); return result; } #ifdef EUI_64 /*! * \brief Assign an EUI-64 address from a pool for a given iaid-duid * * \param pool - pool from which the address is assigned * \param iaddr - pointer to the iasubopt to contain the assigned address is * \param uid - data_string containing the iaid-duid tuple * \param soft_lifetime_end_time - lifetime of the lease for a solicit? * * \return status indicating success or nature of the failure */ isc_result_t create_lease6_eui_64(struct ipv6_pool *pool, struct iasubopt **addr, const struct data_string *uid, time_t soft_lifetime_end_time) { struct in6_addr tmp; struct iasubopt *test_iaaddr; struct iasubopt *iaaddr; isc_result_t result; static isc_boolean_t init_resiid = ISC_FALSE; /* Fill the reserved IIDs. */ if (!init_resiid) { memset(&rtany, 0, 16); memset(&resany, 0, 8); resany.s6_addr[8] = 0xfd; memset(&resany.s6_addr[9], 0xff, 6); init_resiid = ISC_TRUE; } /* Pool must be IA_NA */ if (pool->pool_type != D6O_IA_NA) { log_error("create_lease6_eui_64: pool type is not IA_NA."); return (DHCP_R_INVALIDARG); } /* Attempt to build the address */ if (build_address6_eui_64 (&tmp, &pool->start_addr, pool->bits, uid, IAID_LEN) != ISC_R_SUCCESS) { log_error("create_lease6_eui_64: build_address6_eui_64 failed"); return (ISC_R_FAILURE); } /* Avoid reserved interface IDs. (cf. RFC 5453) */ if ((memcmp(&tmp.s6_addr[8], &rtany.s6_addr[8], 8) == 0) || ((memcmp(&tmp.s6_addr[8], &resany.s6_addr[8], 7) == 0) && ((tmp.s6_addr[15] & 0x80) == 0x80))) { log_error("create_lease6_eui_64: " "address conflicts with reserved IID"); return (ISC_R_FAILURE); } /* If this address is not in use, we're happy with it */ test_iaaddr = NULL; if (iasubopt_hash_lookup(&test_iaaddr, pool->leases, &tmp, sizeof(tmp), MDL) != 0) { /* See if it's ours. Static leases won't have an ia */ int ours = 0; if (!test_iaaddr->ia) { log_error("create_lease6_eui_64: " "address %s is assigned to static lease", pin6_addr(&test_iaaddr->addr)); } else { /* Not sure if this can actually happen */ struct data_string* found = &test_iaaddr->ia->iaid_duid; ours = ((found->len == uid->len) && (!memcmp(found->data, uid->data, uid->len))); log_error("create_lease6_eui_64: " "address %s belongs to %s", pin6_addr(&test_iaaddr->addr), print_hex_1(found->len, found->data, 60)); } iasubopt_dereference(&test_iaaddr, MDL); if (!ours) { /* Cant' use it */ return (ISC_R_FAILURE); } } /* We're happy with the address, create an IAADDR to hold it. */ iaaddr = NULL; result = iasubopt_allocate(&iaaddr, MDL); if (result != ISC_R_SUCCESS) { log_error("create_lease6_eui_64: could not allocate iasubop"); return result; } iaaddr->plen = 0; memcpy(&iaaddr->addr, &tmp, sizeof(iaaddr->addr)); /* Add the lease to the pool and the reply */ result = add_lease6(pool, iaaddr, soft_lifetime_end_time); if (result == ISC_R_SUCCESS) { iasubopt_reference(addr, iaaddr, MDL); } iasubopt_dereference(&iaaddr, MDL); return result; } #endif /*! * * \brief Cleans up leases when reading from a lease file * * This function is only expected to be run when reading leases in from a file. * It checks to see if a lease already exists for the new leases's address. * We don't add expired leases to the structures when reading a lease file * which limits what can happen. We have two variables the owners of the leases * being the same or different and the new lease being active or non-active: * Owners active * same no remove old lease and its connections * same yes nothing to do, other code will update the structures. * diff no nothing to do * diff yes this combination shouldn't happen, we should only have a * single active lease per address at a time and that lease * should move to non-active before any other lease can * become active for that address. * Currently we delete the previous lease and pass an error * to the caller who should log an error. * * When we remove a lease we remove it from the hash table and active heap * (remember only active leases are in the structures at this time) for the * pool, and from the IA's array. If, after we've removed the pointer from * IA's array to the lease, the IA has no more pointers we remove it from * the appropriate hash table as well. * * \param[in] ia_table = the hash table for the IA * \param[in] pool = the pool to update * \param[in] lease = the new lease we want to add * \param[in] ia = the new ia we are building * * \return * ISC_R_SUCCESS = the incoming lease and any previous lease were in * an expected state - one of the first 3 options above. * If necessary the old lease was removed. * ISC_R_FAILURE = there is already an active lease for the address in * the incoming lease. This shouldn't happen if it does * flag an error for the caller to log. */ isc_result_t cleanup_lease6(ia_hash_t *ia_table, struct ipv6_pool *pool, struct iasubopt *lease, struct ia_xx *ia) { struct iasubopt *test_iasubopt, *tmp_iasubopt; struct ia_xx *old_ia; isc_result_t status = ISC_R_SUCCESS; test_iasubopt = NULL; old_ia = NULL; /* * Look up the address - if we don't find a lease * we don't need to do anything. */ if (iasubopt_hash_lookup(&test_iasubopt, pool->leases, &lease->addr, sizeof(lease->addr), MDL) == 0) { return (ISC_R_SUCCESS); } if (test_iasubopt->ia == NULL) { /* no old ia, no work to do */ iasubopt_dereference(&test_iasubopt, MDL); return (status); } ia_reference(&old_ia, test_iasubopt->ia, MDL); if ((old_ia->iaid_duid.len == ia->iaid_duid.len) && (memcmp((unsigned char *)ia->iaid_duid.data, (unsigned char *)old_ia->iaid_duid.data, ia->iaid_duid.len) == 0)) { /* same IA */ if ((lease->state == FTS_ACTIVE) || (lease->state == FTS_ABANDONED)) { /* still active, no need to delete */ goto cleanup; } } else { /* different IA */ if ((lease->state != FTS_ACTIVE) && (lease->state != FTS_ABANDONED)) { /* new lease isn't active, no work */ goto cleanup; } /* * We appear to have two active leases, this shouldn't happen. * Before a second lease can be set to active the first lease * should be set to inactive (released, expired etc). For now * delete the previous lease and indicate a failure to the * caller so it can generate a warning. * In the future we may try and determine which is the better * lease to keep. */ status = ISC_R_FAILURE; } /* * Remove the old lease from the active heap and from the hash table * then remove the lease from the IA and clean up the IA if necessary. */ isc_heap_delete(pool->active_timeouts, test_iasubopt->active_index); pool->num_active--; if (pool->ipv6_pond) pool->ipv6_pond->num_active--; if (lease->state == FTS_ABANDONED) { pool->num_abandoned--; if (pool->ipv6_pond) pool->ipv6_pond->num_abandoned--; } iasubopt_hash_delete(pool->leases, &test_iasubopt->addr, sizeof(test_iasubopt->addr), MDL); ia_remove_iasubopt(old_ia, test_iasubopt, MDL); if (old_ia->num_iasubopt <= 0) { ia_hash_delete(ia_table, (unsigned char *)old_ia->iaid_duid.data, old_ia->iaid_duid.len, MDL); } /* * We derefenrece the subopt here as we've just removed it from * the hash table in the pool. We need to make a copy as we * need to derefernece it again later. */ tmp_iasubopt = test_iasubopt; iasubopt_dereference(&tmp_iasubopt, MDL); cleanup: ia_dereference(&old_ia, MDL); /* * Clean up the reference, this is in addition to the deference * above after removing the entry from the hash table */ iasubopt_dereference(&test_iasubopt, MDL); return (status); } /* * Put a lease in the pool directly. This is intended to be used when * loading leases from the file. */ isc_result_t add_lease6(struct ipv6_pool *pool, struct iasubopt *lease, time_t valid_lifetime_end_time) { isc_result_t insert_result; struct iasubopt *test_iasubopt; struct iasubopt *tmp_iasubopt; /* If a state was not assigned by the caller, assume active. */ if (lease->state == 0) lease->state = FTS_ACTIVE; ipv6_pool_reference(&lease->ipv6_pool, pool, MDL); /* * If this IAADDR/PREFIX is already in our structures, remove the * old one. */ test_iasubopt = NULL; if (iasubopt_hash_lookup(&test_iasubopt, pool->leases, &lease->addr, sizeof(lease->addr), MDL)) { /* XXX: we should probably ask the lease what heap it is on * (as a consistency check). * XXX: we should probably have one function to "put this lease * on its heap" rather than doing these if's everywhere. If * you add more states to this list, don't. */ if ((test_iasubopt->state == FTS_ACTIVE) || (test_iasubopt->state == FTS_ABANDONED)) { isc_heap_delete(pool->active_timeouts, test_iasubopt->active_index); pool->num_active--; if (pool->ipv6_pond) pool->ipv6_pond->num_active--; if (test_iasubopt->state == FTS_ABANDONED) { pool->num_abandoned--; if (pool->ipv6_pond) pool->ipv6_pond->num_abandoned--; } } else { isc_heap_delete(pool->inactive_timeouts, test_iasubopt->inactive_index); pool->num_inactive--; } iasubopt_hash_delete(pool->leases, &test_iasubopt->addr, sizeof(test_iasubopt->addr), MDL); /* * We're going to do a bit of evil trickery here. * * We need to dereference the entry once to remove our * current reference (in test_iasubopt), and then one * more time to remove the reference left when the * address was added to the pool before. */ tmp_iasubopt = test_iasubopt; iasubopt_dereference(&test_iasubopt, MDL); iasubopt_dereference(&tmp_iasubopt, MDL); } /* * Add IAADDR/PREFIX to our structures. */ tmp_iasubopt = NULL; iasubopt_reference(&tmp_iasubopt, lease, MDL); if ((tmp_iasubopt->state == FTS_ACTIVE) || (tmp_iasubopt->state == FTS_ABANDONED)) { tmp_iasubopt->hard_lifetime_end_time = valid_lifetime_end_time; iasubopt_hash_add(pool->leases, &tmp_iasubopt->addr, sizeof(tmp_iasubopt->addr), lease, MDL); insert_result = isc_heap_insert(pool->active_timeouts, tmp_iasubopt); if (insert_result == ISC_R_SUCCESS) { pool->num_active++; if (pool->ipv6_pond) pool->ipv6_pond->num_active++; if (tmp_iasubopt->state == FTS_ABANDONED) { pool->num_abandoned++; if (pool->ipv6_pond) pool->ipv6_pond->num_abandoned++; } } } else { tmp_iasubopt->soft_lifetime_end_time = valid_lifetime_end_time; insert_result = isc_heap_insert(pool->inactive_timeouts, tmp_iasubopt); if (insert_result == ISC_R_SUCCESS) pool->num_inactive++; } if (insert_result != ISC_R_SUCCESS) { iasubopt_hash_delete(pool->leases, &lease->addr, sizeof(lease->addr), MDL); iasubopt_dereference(&tmp_iasubopt, MDL); return insert_result; } /* * Note: we intentionally leave tmp_iasubopt referenced; there * is a reference in the heap/hash, after all. */ return ISC_R_SUCCESS; } /* * Determine if an address is present in a pool or not. */ isc_boolean_t lease6_exists(const struct ipv6_pool *pool, const struct in6_addr *addr) { struct iasubopt *test_iaaddr; test_iaaddr = NULL; if (iasubopt_hash_lookup(&test_iaaddr, pool->leases, (void *)addr, sizeof(*addr), MDL)) { iasubopt_dereference(&test_iaaddr, MDL); return ISC_TRUE; } else { return ISC_FALSE; } } /*! * * \brief Check if address is available to a lease * * Determine if the address in the lease is available to that * lease. Either the address isn't in use or it is in use * but by that lease. * * \param[in] lease = lease to check * * \return * ISC_TRUE = The lease is allowed to use that address * ISC_FALSE = The lease isn't allowed to use that address */ isc_boolean_t lease6_usable(struct iasubopt *lease) { struct iasubopt *test_iaaddr; isc_boolean_t status = ISC_TRUE; test_iaaddr = NULL; if (iasubopt_hash_lookup(&test_iaaddr, lease->ipv6_pool->leases, (void *)&lease->addr, sizeof(lease->addr), MDL)) { if (test_iaaddr != lease) { status = ISC_FALSE; } iasubopt_dereference(&test_iaaddr, MDL); } return (status); } /* * Put the lease on our active pool. */ static isc_result_t move_lease_to_active(struct ipv6_pool *pool, struct iasubopt *lease) { isc_result_t insert_result; insert_result = isc_heap_insert(pool->active_timeouts, lease); if (insert_result == ISC_R_SUCCESS) { iasubopt_hash_add(pool->leases, &lease->addr, sizeof(lease->addr), lease, MDL); isc_heap_delete(pool->inactive_timeouts, lease->inactive_index); pool->num_active++; pool->num_inactive--; lease->state = FTS_ACTIVE; if (pool->ipv6_pond) pool->ipv6_pond->num_active++; } return insert_result; } /*! * * \brief Renew a lease in the pool. * * The hard_lifetime_end_time of the lease should be set to * the current expiration time. * The soft_lifetime_end_time of the lease should be set to * the desired expiration time. * * This routine will compare the two and call the correct * heap routine to move the lease. If the lease is active * and the new expiration time is greater (the normal case) * then we call isc_heap_decreased() as a larger time is a * lower priority. If the new expiration time is less then * we call isc_heap_increased(). * * If the lease is abandoned then it will be on the active list * and we will always call isc_heap_increased() as the previous * expiration would have been all 1s (as close as we can get * to infinite). * * If the lease is moving to active we call that routine * which will move it from the inactive list to the active list. * * \param pool = a pool the lease belongs to * \param lease = the lease to be renewed * * \return result of the renew operation (ISC_R_SUCCESS if successful, ISC_R_NOMEMORY when run out of memory) */ isc_result_t renew_lease6(struct ipv6_pool *pool, struct iasubopt *lease) { time_t old_end_time = lease->hard_lifetime_end_time; lease->hard_lifetime_end_time = lease->soft_lifetime_end_time; lease->soft_lifetime_end_time = 0; if (lease->state == FTS_ACTIVE) { if (old_end_time <= lease->hard_lifetime_end_time) { isc_heap_decreased(pool->active_timeouts, lease->active_index); } else { isc_heap_increased(pool->active_timeouts, lease->active_index); } return ISC_R_SUCCESS; } else if (lease->state == FTS_ABANDONED) { char tmp_addr[INET6_ADDRSTRLEN]; lease->state = FTS_ACTIVE; isc_heap_increased(pool->active_timeouts, lease->active_index); log_info("Reclaiming previously abandoned address %s", inet_ntop(AF_INET6, &(lease->addr), tmp_addr, sizeof(tmp_addr))); pool->num_abandoned--; if (pool->ipv6_pond) pool->ipv6_pond->num_abandoned--; return ISC_R_SUCCESS; } else { return move_lease_to_active(pool, lease); } } /* * Put the lease on our inactive pool, with the specified state. */ static isc_result_t move_lease_to_inactive(struct ipv6_pool *pool, struct iasubopt *lease, binding_state_t state) { isc_result_t insert_result; insert_result = isc_heap_insert(pool->inactive_timeouts, lease); if (insert_result == ISC_R_SUCCESS) { /* * Handle expire and release statements * To get here we must be active and have done a commit so * we should run the proper statements if they exist, though * that will change when we remove the inactive heap. * In addition we get rid of the references for both as we * can only do one (expire or release) on a lease */ if (lease->on_star.on_expiry != NULL) { if (state == FTS_EXPIRED) { execute_statements(NULL, NULL, NULL, NULL, NULL, NULL, &lease->scope, lease->on_star.on_expiry, &lease->on_star); } executable_statement_dereference (&lease->on_star.on_expiry, MDL); } if (lease->on_star.on_release != NULL) { if (state == FTS_RELEASED) { execute_statements(NULL, NULL, NULL, NULL, NULL, NULL, &lease->scope, lease->on_star.on_release, &lease->on_star); } executable_statement_dereference (&lease->on_star.on_release, MDL); } #if defined (NSUPDATE) /* Process events upon expiration. */ if (pool->pool_type != D6O_IA_PD) { (void) ddns_removals(NULL, lease, NULL, ISC_FALSE); } #endif /* Binding scopes are no longer valid after expiry or * release. */ if (lease->scope != NULL) { binding_scope_dereference(&lease->scope, MDL); } iasubopt_hash_delete(pool->leases, &lease->addr, sizeof(lease->addr), MDL); isc_heap_delete(pool->active_timeouts, lease->active_index); lease->state = state; pool->num_active--; pool->num_inactive++; if (pool->ipv6_pond) pool->ipv6_pond->num_active--; if (lease->state == FTS_ABANDONED) { pool->num_abandoned--; if (pool->ipv6_pond) pool->ipv6_pond->num_abandoned--; } } return insert_result; } /* * Expire the oldest lease if it's lifetime_end_time is * older than the given time. * * - leasep must be a pointer to a (struct iasubopt *) pointer previously * initialized to NULL * * On return leasep has a reference to the removed entry. It is left * pointing to NULL if the oldest lease has not expired. */ isc_result_t expire_lease6(struct iasubopt **leasep, struct ipv6_pool *pool, time_t now) { struct iasubopt *tmp; isc_result_t result; if (leasep == NULL) { log_error("%s(%d): NULL pointer reference", MDL); return DHCP_R_INVALIDARG; } if (*leasep != NULL) { log_error("%s(%d): non-NULL pointer", MDL); return DHCP_R_INVALIDARG; } if (pool->num_active > 0) { tmp = (struct iasubopt *) isc_heap_element(pool->active_timeouts, 1); if (now > tmp->hard_lifetime_end_time) { result = move_lease_to_inactive(pool, tmp, FTS_EXPIRED); if (result == ISC_R_SUCCESS) { iasubopt_reference(leasep, tmp, MDL); } return result; } } return ISC_R_SUCCESS; } /* * For a declined lease, leave it on the "active" pool, but mark * it as declined. Give it an infinite (well, really long) life. */ isc_result_t decline_lease6(struct ipv6_pool *pool, struct iasubopt *lease) { isc_result_t result; if ((lease->state != FTS_ACTIVE) && (lease->state != FTS_ABANDONED)) { result = move_lease_to_active(pool, lease); if (result != ISC_R_SUCCESS) { return result; } } lease->state = FTS_ABANDONED; pool->num_abandoned++; if (pool->ipv6_pond) pool->ipv6_pond->num_abandoned++; lease->hard_lifetime_end_time = MAX_TIME; isc_heap_decreased(pool->active_timeouts, lease->active_index); return ISC_R_SUCCESS; } /* * Put the returned lease on our inactive pool. */ isc_result_t release_lease6(struct ipv6_pool *pool, struct iasubopt *lease) { if (lease->state == FTS_ACTIVE) { return move_lease_to_inactive(pool, lease, FTS_RELEASED); } else { return ISC_R_SUCCESS; } } /* * Create a prefix by hashing the input, and using that for * the part subject to allocation. */ void build_prefix6(struct in6_addr *pref, const struct in6_addr *net_start_pref, int pool_bits, int pref_bits, const struct data_string *input) { isc_md5_t ctx; int net_bytes; int i; char *str; const char *net_str; /* * Use MD5 to get a nice 128 bit hash of the input. * Yes, we know MD5 isn't cryptographically sound. * No, we don't care. */ isc_md5_init(&ctx); isc_md5_update(&ctx, input->data, input->len); isc_md5_final(&ctx, (unsigned char *)pref); /* * Copy the network bits over. */ str = (char *)pref; net_str = (const char *)net_start_pref; net_bytes = pool_bits / 8; for (i = 0; i < net_bytes; i++) { str[i] = net_str[i]; } i = net_bytes; switch (pool_bits % 8) { case 1: str[i] = (str[i] & 0x7F) | (net_str[i] & 0x80); break; case 2: str[i] = (str[i] & 0x3F) | (net_str[i] & 0xC0); break; case 3: str[i] = (str[i] & 0x1F) | (net_str[i] & 0xE0); break; case 4: str[i] = (str[i] & 0x0F) | (net_str[i] & 0xF0); break; case 5: str[i] = (str[i] & 0x07) | (net_str[i] & 0xF8); break; case 6: str[i] = (str[i] & 0x03) | (net_str[i] & 0xFC); break; case 7: str[i] = (str[i] & 0x01) | (net_str[i] & 0xFE); break; } /* * Zero the remaining bits. */ net_bytes = pref_bits / 8; for (i=net_bytes+1; i<16; i++) { str[i] = 0; } i = net_bytes; switch (pref_bits % 8) { case 0: str[i] &= 0; break; case 1: str[i] &= 0x80; break; case 2: str[i] &= 0xC0; break; case 3: str[i] &= 0xE0; break; case 4: str[i] &= 0xF0; break; case 5: str[i] &= 0xF8; break; case 6: str[i] &= 0xFC; break; case 7: str[i] &= 0xFE; break; } } /* * Create a lease for the given prefix and client duid. * * - pool must be a pointer to a (struct ipv6_pool *) pointer previously * initialized to NULL * * Right now we simply hash the DUID, and if we get a collision, we hash * again until we find a free prefix. We try this a fixed number of times, * to avoid getting stuck in a loop (this is important on small pools * where we can run out of space). * * We return the number of attempts that it took to find an available * prefix. This tells callers when a pool is are filling up, as * well as an indication of how full the pool is; statistically the * more full a pool is the more attempts must be made before finding * a free prefix. Realistically this will only happen in very full * pools. * * We probably want different algorithms depending on the network size, in * the long term. */ isc_result_t create_prefix6(struct ipv6_pool *pool, struct iasubopt **pref, unsigned int *attempts, const struct data_string *uid, time_t soft_lifetime_end_time) { struct data_string ds; struct in6_addr tmp; struct iasubopt *test_iapref; struct data_string new_ds; struct iasubopt *iapref; isc_result_t result; /* * Use the UID as our initial seed for the hash */ memset(&ds, 0, sizeof(ds)); data_string_copy(&ds, (struct data_string *)uid, MDL); *attempts = 0; for (;;) { /* * Give up at some point. */ if (++(*attempts) > 10) { data_string_forget(&ds, MDL); return ISC_R_NORESOURCES; } /* * Build a prefix */ build_prefix6(&tmp, &pool->start_addr, pool->bits, pool->units, &ds); /* * If this prefix is not in use, we're happy with it */ test_iapref = NULL; if (iasubopt_hash_lookup(&test_iapref, pool->leases, &tmp, sizeof(tmp), MDL) == 0) { break; } iasubopt_dereference(&test_iapref, MDL); /* * Otherwise, we create a new input, adding the prefix */ memset(&new_ds, 0, sizeof(new_ds)); new_ds.len = ds.len + sizeof(tmp); if (!buffer_allocate(&new_ds.buffer, new_ds.len, MDL)) { data_string_forget(&ds, MDL); return ISC_R_NOMEMORY; } new_ds.data = new_ds.buffer->data; memcpy(new_ds.buffer->data, ds.data, ds.len); memcpy(new_ds.buffer->data + ds.len, &tmp, sizeof(tmp)); data_string_forget(&ds, MDL); data_string_copy(&ds, &new_ds, MDL); data_string_forget(&new_ds, MDL); } data_string_forget(&ds, MDL); /* * We're happy with the prefix, create an IAPREFIX * to hold it. */ iapref = NULL; result = iasubopt_allocate(&iapref, MDL); if (result != ISC_R_SUCCESS) { return result; } iapref->plen = (u_int8_t)pool->units; memcpy(&iapref->addr, &tmp, sizeof(iapref->addr)); /* * Add the prefix to the pool (note state is free, not active?!). */ result = add_lease6(pool, iapref, soft_lifetime_end_time); if (result == ISC_R_SUCCESS) { iasubopt_reference(pref, iapref, MDL); } iasubopt_dereference(&iapref, MDL); return result; } /* * Determine if a prefix is present in a pool or not. */ isc_boolean_t prefix6_exists(const struct ipv6_pool *pool, const struct in6_addr *pref, u_int8_t plen) { struct iasubopt *test_iapref; if ((int)plen != pool->units) return ISC_FALSE; test_iapref = NULL; if (iasubopt_hash_lookup(&test_iapref, pool->leases, (void *)pref, sizeof(*pref), MDL)) { iasubopt_dereference(&test_iapref, MDL); return ISC_TRUE; } else { return ISC_FALSE; } } /* * Mark an IPv6 address/prefix as unavailable from a pool. * * This is used for host entries and the addresses of the server itself. */ isc_result_t mark_lease_unavailable(struct ipv6_pool *pool, const struct in6_addr *addr) { struct iasubopt *dummy_iasubopt; isc_result_t result; dummy_iasubopt = NULL; result = iasubopt_allocate(&dummy_iasubopt, MDL); if (result == ISC_R_SUCCESS) { dummy_iasubopt->addr = *addr; iasubopt_hash_add(pool->leases, &dummy_iasubopt->addr, sizeof(*addr), dummy_iasubopt, MDL); } return result; } /* * Add a pool. */ isc_result_t add_ipv6_pool(struct ipv6_pool *pool) { struct ipv6_pool **new_pools; new_pools = dmalloc(sizeof(struct ipv6_pool *) * (num_pools+1), MDL); if (new_pools == NULL) { return ISC_R_NOMEMORY; } if (num_pools > 0) { memcpy(new_pools, pools, sizeof(struct ipv6_pool *) * num_pools); dfree(pools, MDL); } pools = new_pools; pools[num_pools] = NULL; ipv6_pool_reference(&pools[num_pools], pool, MDL); num_pools++; return ISC_R_SUCCESS; } static void cleanup_old_expired(struct ipv6_pool *pool) { struct iasubopt *tmp; struct ia_xx *ia; struct ia_xx *ia_active; unsigned char *tmpd; time_t timeout; while (pool->num_inactive > 0) { tmp = (struct iasubopt *) isc_heap_element(pool->inactive_timeouts, 1); if (tmp->hard_lifetime_end_time != 0) { timeout = tmp->hard_lifetime_end_time; timeout += EXPIRED_IPV6_CLEANUP_TIME; } else { timeout = tmp->soft_lifetime_end_time; } if (cur_time < timeout) { break; } isc_heap_delete(pool->inactive_timeouts, tmp->inactive_index); pool->num_inactive--; if (tmp->ia != NULL) { /* * Check to see if this IA is in an active list, * but has no remaining resources. If so, remove it * from the active list. */ ia = NULL; ia_reference(&ia, tmp->ia, MDL); ia_remove_iasubopt(ia, tmp, MDL); ia_active = NULL; tmpd = (unsigned char *)ia->iaid_duid.data; if ((ia->ia_type == D6O_IA_NA) && (ia->num_iasubopt <= 0) && (ia_hash_lookup(&ia_active, ia_na_active, tmpd, ia->iaid_duid.len, MDL) == 0) && (ia_active == ia)) { ia_hash_delete(ia_na_active, tmpd, ia->iaid_duid.len, MDL); } if ((ia->ia_type == D6O_IA_TA) && (ia->num_iasubopt <= 0) && (ia_hash_lookup(&ia_active, ia_ta_active, tmpd, ia->iaid_duid.len, MDL) == 0) && (ia_active == ia)) { ia_hash_delete(ia_ta_active, tmpd, ia->iaid_duid.len, MDL); } if ((ia->ia_type == D6O_IA_PD) && (ia->num_iasubopt <= 0) && (ia_hash_lookup(&ia_active, ia_pd_active, tmpd, ia->iaid_duid.len, MDL) == 0) && (ia_active == ia)) { ia_hash_delete(ia_pd_active, tmpd, ia->iaid_duid.len, MDL); } ia_dereference(&ia, MDL); } iasubopt_dereference(&tmp, MDL); } } static void lease_timeout_support(void *vpool) { struct ipv6_pool *pool; struct iasubopt *lease; pool = (struct ipv6_pool *)vpool; for (;;) { /* * Get the next lease scheduled to expire. * * Note that if there are no leases in the pool, * expire_lease6() will return ISC_R_SUCCESS with * a NULL lease. * * expire_lease6() will call move_lease_to_inactive() which * calls ddns_removals() do we want that on the standard * expiration timer or a special 'depref' timer? Original * query from DH, moved here by SAR. */ lease = NULL; if (expire_lease6(&lease, pool, cur_time) != ISC_R_SUCCESS) { break; } if (lease == NULL) { break; } write_ia(lease->ia); iasubopt_dereference(&lease, MDL); } /* * If appropriate commit and rotate the lease file * As commit_leases_timed() checks to see if we've done any writes * we don't bother tracking if this function called write _ia */ (void) commit_leases_timed(); /* * Do some cleanup of our expired leases. */ cleanup_old_expired(pool); /* * Schedule next round of expirations. */ schedule_lease_timeout(pool); } /* * For a given pool, add a timer that will remove the next * lease to expire. */ void schedule_lease_timeout(struct ipv6_pool *pool) { struct iasubopt *tmp; time_t timeout; time_t next_timeout; struct timeval tv; next_timeout = MAX_TIME; if (pool->num_active > 0) { tmp = (struct iasubopt *) isc_heap_element(pool->active_timeouts, 1); if (tmp->hard_lifetime_end_time < next_timeout) { next_timeout = tmp->hard_lifetime_end_time + 1; } } if (pool->num_inactive > 0) { tmp = (struct iasubopt *) isc_heap_element(pool->inactive_timeouts, 1); if (tmp->hard_lifetime_end_time != 0) { timeout = tmp->hard_lifetime_end_time; timeout += EXPIRED_IPV6_CLEANUP_TIME; } else { timeout = tmp->soft_lifetime_end_time + 1; } if (timeout < next_timeout) { next_timeout = timeout; } } if (next_timeout < MAX_TIME) { tv.tv_sec = next_timeout; tv.tv_usec = 0; add_timeout(&tv, lease_timeout_support, pool, (tvref_t)ipv6_pool_reference, (tvunref_t)ipv6_pool_dereference); } } /* * Schedule timeouts across all pools. */ void schedule_all_ipv6_lease_timeouts(void) { int i; for (i=0; i 128)) { log_fatal("ipv6_network_portion: bits %d not between 0 and 128", bits); } /* * Copy our address portion. */ *result = *addr; addrp = ((unsigned char *)result) + 15; /* * Zero out masked portion. */ mask_bits = 128 - bits; bytes = mask_bits / 8; extra_bits = mask_bits % 8; for (i=0; ibits); if (memcmp(&tmp, &pool->start_addr, sizeof(tmp)) == 0) { return ISC_TRUE; } else { return ISC_FALSE; } } /* * Find the pool that contains the given address. * * - pool must be a pointer to a (struct ipv6_pool *) pointer previously * initialized to NULL */ isc_result_t find_ipv6_pool(struct ipv6_pool **pool, u_int16_t type, const struct in6_addr *addr) { int i; if (pool == NULL) { log_error("%s(%d): NULL pointer reference", MDL); return DHCP_R_INVALIDARG; } if (*pool != NULL) { log_error("%s(%d): non-NULL pointer", MDL); return DHCP_R_INVALIDARG; } for (i=0; ipool_type != type) continue; if (ipv6_in_pool(addr, pools[i])) { ipv6_pool_reference(pool, pools[i], MDL); return ISC_R_SUCCESS; } } return ISC_R_NOTFOUND; } /* * Helper function for the various functions that act across all * pools. */ static isc_result_t change_leases(struct ia_xx *ia, isc_result_t (*change_func)(struct ipv6_pool *, struct iasubopt *)) { isc_result_t retval; isc_result_t renew_retval; struct ipv6_pool *pool; struct in6_addr *addr; int i; retval = ISC_R_SUCCESS; for (i=0; inum_iasubopt; i++) { pool = NULL; addr = &ia->iasubopt[i]->addr; if (find_ipv6_pool(&pool, ia->ia_type, addr) == ISC_R_SUCCESS) { renew_retval = change_func(pool, ia->iasubopt[i]); if (renew_retval != ISC_R_SUCCESS) { retval = renew_retval; } } /* XXXsk: should we warn if we don't find a pool? */ } return retval; } /* * Renew all leases in an IA from all pools. * * The new lifetime should be in the soft_lifetime_end_time * and will be moved to hard_lifetime_end_time by renew_lease6. */ isc_result_t renew_leases(struct ia_xx *ia) { return change_leases(ia, renew_lease6); } /* * Release all leases in an IA from all pools. */ isc_result_t release_leases(struct ia_xx *ia) { return change_leases(ia, release_lease6); } /* * Decline all leases in an IA from all pools. */ isc_result_t decline_leases(struct ia_xx *ia) { return change_leases(ia, decline_lease6); } #ifdef DHCPv6 /* * Helper function to output leases. */ static int write_error; static isc_result_t write_ia_leases(const void *name, unsigned len, void *value) { struct ia_xx *ia = (struct ia_xx *)value; if (!write_error) { if (!write_ia(ia)) { write_error = 1; } } return ISC_R_SUCCESS; } /* * Write all DHCPv6 information. */ int write_leases6(void) { int nas, tas, pds; write_error = 0; write_server_duid(); nas = ia_hash_foreach(ia_na_active, write_ia_leases); if (write_error) { return 0; } tas = ia_hash_foreach(ia_ta_active, write_ia_leases); if (write_error) { return 0; } pds = ia_hash_foreach(ia_pd_active, write_ia_leases); if (write_error) { return 0; } log_info("Wrote %d NA, %d TA, %d PD leases to lease file.", nas, tas, pds); return 1; } #endif /* DHCPv6 */ static isc_result_t mark_hosts_unavailable_support(const void *name, unsigned len, void *value) { struct host_decl *h; struct data_string fixed_addr; struct in6_addr addr; struct ipv6_pool *p; h = (struct host_decl *)value; /* * If the host has no address, we don't need to mark anything. */ if (h->fixed_addr == NULL) { return ISC_R_SUCCESS; } /* * Evaluate the fixed address. */ memset(&fixed_addr, 0, sizeof(fixed_addr)); if (!evaluate_option_cache(&fixed_addr, NULL, NULL, NULL, NULL, NULL, &global_scope, h->fixed_addr, MDL)) { log_error("mark_hosts_unavailable: " "error evaluating host address."); return ISC_R_SUCCESS; } if (fixed_addr.len != 16) { log_error("mark_hosts_unavailable: " "host address is not 128 bits."); return ISC_R_SUCCESS; } memcpy(&addr, fixed_addr.data, 16); data_string_forget(&fixed_addr, MDL); /* * Find the pool holding this host, and mark the address. * (I suppose it is arguably valid to have a host that does not * sit in any pool.) */ p = NULL; if (find_ipv6_pool(&p, D6O_IA_NA, &addr) == ISC_R_SUCCESS) { mark_lease_unavailable(p, &addr); ipv6_pool_dereference(&p, MDL); } if (find_ipv6_pool(&p, D6O_IA_TA, &addr) == ISC_R_SUCCESS) { mark_lease_unavailable(p, &addr); ipv6_pool_dereference(&p, MDL); } return ISC_R_SUCCESS; } void mark_hosts_unavailable(void) { hash_foreach(host_name_hash, mark_hosts_unavailable_support); } static isc_result_t mark_phosts_unavailable_support(const void *name, unsigned len, void *value) { struct host_decl *h; struct iaddrcidrnetlist *l; struct in6_addr pref; struct ipv6_pool *p; h = (struct host_decl *)value; /* * If the host has no prefix, we don't need to mark anything. */ if (h->fixed_prefix == NULL) { return ISC_R_SUCCESS; } /* * Get the fixed prefixes. */ for (l = h->fixed_prefix; l != NULL; l = l->next) { if (l->cidrnet.lo_addr.len != 16) { continue; } memcpy(&pref, l->cidrnet.lo_addr.iabuf, 16); /* * Find the pool holding this host, and mark the prefix. * (I suppose it is arguably valid to have a host that does not * sit in any pool.) */ p = NULL; if (find_ipv6_pool(&p, D6O_IA_PD, &pref) != ISC_R_SUCCESS) { continue; } if (l->cidrnet.bits != p->units) { ipv6_pool_dereference(&p, MDL); continue; } mark_lease_unavailable(p, &pref); ipv6_pool_dereference(&p, MDL); } return ISC_R_SUCCESS; } void mark_phosts_unavailable(void) { hash_foreach(host_name_hash, mark_phosts_unavailable_support); } void mark_interfaces_unavailable(void) { struct interface_info *ip; int i; struct ipv6_pool *p; ip = interfaces; while (ip != NULL) { for (i=0; iv6address_count; i++) { p = NULL; if (find_ipv6_pool(&p, D6O_IA_NA, &ip->v6addresses[i]) == ISC_R_SUCCESS) { mark_lease_unavailable(p, &ip->v6addresses[i]); ipv6_pool_dereference(&p, MDL); } if (find_ipv6_pool(&p, D6O_IA_TA, &ip->v6addresses[i]) == ISC_R_SUCCESS) { mark_lease_unavailable(p, &ip->v6addresses[i]); ipv6_pool_dereference(&p, MDL); } } ip = ip->next; } } /*! * \brief Create a new IPv6 pond structure. * * Allocate space for a new ipv6_pond structure and return a reference * to it, includes setting the reference count to 1. * * \param pond = space for returning a referenced pointer to the pond. * This must point to a space that has been initialzied * to NULL by the caller. * * \return * ISC_R_SUCCESS = The pond was successfully created, pond points to it. * DHCP_R_INVALIDARG = One of the arugments was invalid, pond has not been * modified * ISC_R_NOMEMORY = The system wasn't able to allocate memory, pond has * not been modified. */ isc_result_t ipv6_pond_allocate(struct ipv6_pond **pond, const char *file, int line) { struct ipv6_pond *tmp; if (pond == NULL) { log_error("%s(%d): NULL pointer reference", file, line); return DHCP_R_INVALIDARG; } if (*pond != NULL) { log_error("%s(%d): non-NULL pointer", file, line); return DHCP_R_INVALIDARG; } tmp = dmalloc(sizeof(*tmp), file, line); if (tmp == NULL) { return ISC_R_NOMEMORY; } tmp->refcnt = 1; *pond = tmp; return ISC_R_SUCCESS; } /*! * * \brief reference an IPv6 pond structure. * * This function genreates a reference to an ipv6_pond structure * and increments the reference count on the structure. * * \param[out] pond = space for returning a referenced pointer to the pond. * This must point to a space that has been initialzied * to NULL by the caller. * \param[in] src = A pointer to the pond to reference. This must not be * NULL. * * \return * ISC_R_SUCCESS = The pond was successfully referenced, pond now points * to src. * DHCP_R_INVALIDARG = One of the arugments was invalid, pond has not been * modified. */ isc_result_t ipv6_pond_reference(struct ipv6_pond **pond, struct ipv6_pond *src, const char *file, int line) { if (pond == NULL) { log_error("%s(%d): NULL pointer reference", file, line); return DHCP_R_INVALIDARG; } if (*pond != NULL) { log_error("%s(%d): non-NULL pointer", file, line); return DHCP_R_INVALIDARG; } if (src == NULL) { log_error("%s(%d): NULL pointer reference", file, line); return DHCP_R_INVALIDARG; } *pond = src; src->refcnt++; return ISC_R_SUCCESS; } /*! * * \brief de-reference an IPv6 pond structure. * * This function decrements the reference count in an ipv6_pond structure. * If this was the last reference then the memory for the structure is * freed. * * \param[in] pond = A pointer to the pointer to the pond that should be * de-referenced. On success the pointer to the pond * is cleared. It must not be NULL and must not point * to NULL. * * \return * ISC_R_SUCCESS = The pond was successfully de-referenced, pond now points * to NULL * DHCP_R_INVALIDARG = One of the arugments was invalid, pond has not been * modified. */ isc_result_t ipv6_pond_dereference(struct ipv6_pond **pond, const char *file, int line) { struct ipv6_pond *tmp; if ((pond == NULL) || (*pond == NULL)) { log_error("%s(%d): NULL pointer", file, line); return DHCP_R_INVALIDARG; } tmp = *pond; *pond = NULL; tmp->refcnt--; if (tmp->refcnt < 0) { log_error("%s(%d): negative refcnt", file, line); tmp->refcnt = 0; } if (tmp->refcnt == 0) { dfree(tmp, file, line); } return ISC_R_SUCCESS; } #ifdef EUI_64 /* * Enables/disables EUI-64 address assignment for a pond * * Excecutes statements down to the pond's scope and sets the pond's * use_eui_64 flag accordingly. In addition it iterates over the * pond's pools ensuring they are all /64. Anything else is deemed * invalid for EUI-64. It returns the number of invalid pools * detected. This is done post-parsing as use-eui-64 can be set * down to the pool scope and we can't reliably do it until the * entire configuration has been parsed. */ int set_eui_64(struct ipv6_pond *pond) { int invalid_cnt = 0; struct option_state* options = NULL; struct option_cache *oc = NULL; option_state_allocate(&options, MDL); execute_statements_in_scope(NULL, NULL, NULL, NULL, NULL, options, &global_scope, pond->group, NULL, NULL); pond->use_eui_64 = ((oc = lookup_option(&server_universe, options, SV_USE_EUI_64)) && (evaluate_boolean_option_cache (NULL, NULL, NULL, NULL, options, NULL, &global_scope, oc, MDL))); if (pond->use_eui_64) { // Check all pools are valid int i = 0; struct ipv6_pool* p; while((p = pond->ipv6_pools[i++]) != NULL) { if (p->bits != 64) { log_error("Pool %s/%d cannot use EUI-64," " prefix must 64", pin6_addr(&p->start_addr), p->bits); invalid_cnt++; } else { log_debug("Pool: %s/%d - will use EUI-64", pin6_addr(&p->start_addr), p->bits); } } } /* Don't need the options anymore. */ option_state_dereference(&options, MDL); return (invalid_cnt); } #endif /* * Emits a log for each pond that has been flagged as being a "jumbo range" * A pond is considered a "jumbo range" when the total number of elements * exceeds the maximum value of POND_TRACK_MAX (currently maximum value * that can be stored by ipv6_pond.num_total). Since we disable threshold * logging for jumbo ranges, we need to report this to the user. This * function allows us to report jumbo ponds after config parsing, so the * logs can be seen both on the console (-T) and the log facility (i.e syslog). * * Note, threshold logging is done at the pond level, so we need emit a list * of the addresses ranges of the pools in the pond affected. */ void report_jumbo_ranges() { struct shared_network* s; char log_buf[1084]; #ifdef EUI_64 int invalid_cnt = 0; #endif /* Loop thru all the networks looking for jumbo range ponds */ for (s = shared_networks; s; s = s -> next) { struct ipv6_pond* pond = s->ipv6_pond; while (pond) { #ifdef EUI_64 /* while we're here, set the pond's use_eui_64 flag */ invalid_cnt += set_eui_64(pond); #endif /* if its a jumbo and has pools(sanity check) */ if (pond->jumbo_range == 1 && (pond->ipv6_pools)) { struct ipv6_pool* pool; char *bufptr = log_buf; size_t space_left = sizeof(log_buf) - 1; int i = 0; int used = 0; /* Build list containing the start-address/CIDR * of each pool */ *bufptr = '\0'; while ((pool = pond->ipv6_pools[i++]) && (space_left > (INET6_ADDRSTRLEN + 6))) { /* more than one so add a comma */ if (i > 1) { *bufptr++ = ','; *bufptr++ = ' '; *bufptr = '\0'; space_left -= 2; } /* add the address */ inet_ntop(AF_INET6, &pool->start_addr, bufptr, INET6_ADDRSTRLEN); used = strlen(bufptr); bufptr += used; space_left -= used; /* add the CIDR */ sprintf (bufptr, "/%d",pool->bits); used = strlen(bufptr); bufptr += used; space_left -= used; *bufptr = '\0'; } log_info("Threshold logging disabled for shared" " subnet of ranges: %s", log_buf); } pond = pond->next; } } #ifdef EUI_64 if (invalid_cnt) { log_fatal ("%d pool(s) are invalid for EUI-64 use", invalid_cnt); } #endif } /* * \brief Tests that 16-bit hardware type is less than 256 * * XXX: DHCPv6 gives a 16-bit field for the htype. DHCPv4 gives an * 8-bit field. To change the semantics of the generic 'hardware' * structure, we would have to adjust many DHCPv4 sources (from * interface to DHCPv4 lease code), and we would have to update the * 'hardware' config directive (probably being reverse compatible and * providing a new upgrade/replacement primitive). This is a little * too much to change for now. Hopefully we will revisit this before * hardware types exceeding 8 bits are assigned. * * Uses a static variable to limit log occurence to once per startup * * \param htype hardware type value to test * * \return returns 0 if the value is too large * */ int htype_bounds_check(uint16_t htype) { static int log_once = 0; if (htype & 0xFF00) { if (!log_once) { log_error("Attention: At least one client advertises a " "hardware type of %d, which exceeds the software " "limitation of 255.", htype); log_once = 1; } return(0); } return(1); } /*! * \brief Look for hosts by MAC address if it's available * * Checks the inbound packet against host declarations which specified: * * "hardware ethernet ;" * * For directly connected clients, the function will use the MAC address * contained in packet:haddr if it's populated. \TODO - While the logic is in * place for this search, the socket layer does not yet populate packet:haddr, * this is to be done under rt41523. * * For relayed clients, the function will use the MAC address from the * client-linklayer-address option if it has been supplied by the relay * directly connected to the client. * * \param hp[out] - pointer to storage for the host delcaration if found * \param packet - received packet * \param opt_state - option state to search * \param file - source file * \param line - line number * * \return non-zero if a matching host was found, zero otherwise */ int find_hosts_by_haddr6(struct host_decl **hp, struct packet *packet, struct option_state *opt_state, const char *file, int line) { int found = 0; int htype; int hlen; /* For directly connected clients, use packet:haddr if populated */ if (packet->dhcpv6_container_packet == NULL) { if (packet->haddr) { htype = packet->haddr->hbuf[0]; hlen = packet->haddr->hlen - 1, log_debug("find_hosts_by_haddr6: using packet->haddr," " type: %d, len: %d", htype, hlen); found = find_hosts_by_haddr (hp, htype, &packet->haddr->hbuf[1], hlen, MDL); } } else { /* The first container packet is the from the relay directly * connected to the client. Per RFC 6939, that is only relay * that may supply the client linklayer address option. */ struct packet *relay_packet = packet->dhcpv6_container_packet; struct option_state *relay_state = relay_packet->options; struct data_string rel_addr; struct option_cache *oc; /* Look for the option in the first relay packet */ oc = lookup_option(&dhcpv6_universe, relay_state, D6O_CLIENT_LINKLAYER_ADDR); if (!oc) { /* Not there, so bail */ return (0); } /* The option is present, fetch the address data */ memset(&rel_addr, 0, sizeof(rel_addr)); if (!evaluate_option_cache(&rel_addr, relay_packet, NULL, NULL, relay_state, NULL, &global_scope, oc, MDL)) { log_error("find_hosts_by_add6:" "Error evaluating option cache"); return (0); } /* The relay address data should be: * byte 0 - 1 = hardware type * bytes 2 - hlen = hardware address * where hlen ( hardware address len) is option data len - 2 */ hlen = rel_addr.len - 2; if (hlen > 0 && hlen <= HARDWARE_ADDR_LEN) { htype = getUShort(rel_addr.data); if (htype_bounds_check(htype)) { /* Looks valid, let's search with it */ log_debug("find_hosts_by_haddr6:" "using relayed haddr" " type: %d, len: %d", htype, hlen); found = find_hosts_by_haddr (hp, htype, &rel_addr.data[2], hlen, MDL); } } data_string_forget(&rel_addr, MDL); } return (found); } /* * find_host_by_duid_chaddr() synthesizes a DHCPv4-like 'hardware' * parameter from a DHCPv6 supplied DUID (client-identifier option), * and may seek to use client or relay supplied hardware addresses. */ int find_hosts_by_duid_chaddr(struct host_decl **host, const struct data_string *client_id) { int htype, hlen; const unsigned char *chaddr; /* * The DUID-LL and DUID-LLT must have a 2-byte DUID type and 2-byte * htype. */ if (client_id->len < 4) return 0; /* * The third and fourth octets of the DUID-LL and DUID-LLT * is the hardware type, but in 16 bits. */ htype = getUShort(client_id->data + 2); hlen = 0; chaddr = NULL; /* The first two octets of the DUID identify the type. */ switch(getUShort(client_id->data)) { case DUID_LLT: if (client_id->len > 8) { hlen = client_id->len - 8; chaddr = client_id->data + 8; } break; case DUID_LL: /* * Note that client_id->len must be greater than or equal * to four to get to this point in the function. */ hlen = client_id->len - 4; chaddr = client_id->data + 4; break; default: break; } if ((hlen == 0) || (hlen > HARDWARE_ADDR_LEN) || !htype_bounds_check(htype)) { return (0); } return find_hosts_by_haddr(host, htype, chaddr, hlen, MDL); } /* * \brief Finds a host record that matches the packet, if any * * This function centralizes the logic for matching v6 client * packets to host declarations. We check in the following order * for matches with: * * 1. client_id if specified * 2. MAC address when explicitly available * 3. packet option * 4. synthesized hardware address - this is done last as some * synthesis methods are not consided to be reliable * * \param[out] host - pointer to storage for the located host * \param packet - inbound client packet * \param client_id - client identifier (if one) * \param file - source file * \param line - source file line number * \return non-zero if a host is found, zero otherwise */ int find_hosts6(struct host_decl** host, struct packet* packet, const struct data_string* client_id, char* file, int line) { return (find_hosts_by_uid(host, client_id->data, client_id->len, MDL) || find_hosts_by_haddr6(host, packet, packet->options, MDL) || find_hosts_by_option(host, packet, packet->options, MDL) || find_hosts_by_duid_chaddr(host, client_id)); } /* unittest moved to server/tests/mdb6_unittest.c */ dhcp-4.4.1/server/omapi.c000644 000765 000024 00000216327 13243301226 015505 0ustar00tmarkstaff000000 000000 /* omapi.c OMAPI object interfaces for the DHCP server. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1999-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ /* Many, many thanks to Brian Murrell and BCtel for this code - BCtel provided the funding that resulted in this code and the entire OMAPI support library being written, and Brian helped brainstorm and refine the requirements. To the extent that this code is useful, you have Brian and BCtel to thank. Any limitations in the code are a result of mistakes on my part. -- Ted Lemon */ #include "dhcpd.h" #include static isc_result_t class_lookup (omapi_object_t **, omapi_object_t *, omapi_object_t *, omapi_object_type_t *); static isc_result_t update_lease_flags(struct lease* lease, omapi_typed_data_t *value); omapi_object_type_t *dhcp_type_lease; omapi_object_type_t *dhcp_type_pool; omapi_object_type_t *dhcp_type_class; omapi_object_type_t *dhcp_type_subclass; omapi_object_type_t *dhcp_type_host; #if defined (FAILOVER_PROTOCOL) omapi_object_type_t *dhcp_type_failover_state; omapi_object_type_t *dhcp_type_failover_link; omapi_object_type_t *dhcp_type_failover_listener; #endif void dhcp_db_objects_setup () { isc_result_t status; status = omapi_object_type_register (&dhcp_type_lease, "lease", dhcp_lease_set_value, dhcp_lease_get_value, dhcp_lease_destroy, dhcp_lease_signal_handler, dhcp_lease_stuff_values, dhcp_lease_lookup, dhcp_lease_create, dhcp_lease_remove, #if defined (COMPACT_LEASES) dhcp_lease_free, dhcp_lease_get, #else 0, 0, #endif 0, sizeof (struct lease), 0, RC_LEASE); if (status != ISC_R_SUCCESS) log_fatal ("Can't register lease object type: %s", isc_result_totext (status)); status = omapi_object_type_register (&dhcp_type_class, "class", dhcp_class_set_value, dhcp_class_get_value, dhcp_class_destroy, dhcp_class_signal_handler, dhcp_class_stuff_values, dhcp_class_lookup, dhcp_class_create, dhcp_class_remove, 0, 0, 0, sizeof (struct class), 0, RC_MISC); if (status != ISC_R_SUCCESS) log_fatal ("Can't register class object type: %s", isc_result_totext (status)); status = omapi_object_type_register (&dhcp_type_subclass, "subclass", dhcp_subclass_set_value, dhcp_subclass_get_value, dhcp_class_destroy, dhcp_subclass_signal_handler, dhcp_subclass_stuff_values, dhcp_subclass_lookup, dhcp_subclass_create, dhcp_subclass_remove, 0, 0, 0, sizeof (struct class), 0, RC_MISC); if (status != ISC_R_SUCCESS) log_fatal ("Can't register subclass object type: %s", isc_result_totext (status)); status = omapi_object_type_register (&dhcp_type_pool, "pool", dhcp_pool_set_value, dhcp_pool_get_value, dhcp_pool_destroy, dhcp_pool_signal_handler, dhcp_pool_stuff_values, dhcp_pool_lookup, dhcp_pool_create, dhcp_pool_remove, 0, 0, 0, sizeof (struct pool), 0, RC_MISC); if (status != ISC_R_SUCCESS) log_fatal ("Can't register pool object type: %s", isc_result_totext (status)); status = omapi_object_type_register (&dhcp_type_host, "host", dhcp_host_set_value, dhcp_host_get_value, dhcp_host_destroy, dhcp_host_signal_handler, dhcp_host_stuff_values, dhcp_host_lookup, dhcp_host_create, dhcp_host_remove, 0, 0, 0, sizeof (struct host_decl), 0, RC_MISC); if (status != ISC_R_SUCCESS) log_fatal ("Can't register host object type: %s", isc_result_totext (status)); #if defined (FAILOVER_PROTOCOL) status = omapi_object_type_register (&dhcp_type_failover_state, "failover-state", dhcp_failover_state_set_value, dhcp_failover_state_get_value, dhcp_failover_state_destroy, dhcp_failover_state_signal, dhcp_failover_state_stuff, dhcp_failover_state_lookup, dhcp_failover_state_create, dhcp_failover_state_remove, 0, 0, 0, sizeof (dhcp_failover_state_t), 0, RC_MISC); if (status != ISC_R_SUCCESS) log_fatal ("Can't register failover state object type: %s", isc_result_totext (status)); status = omapi_object_type_register (&dhcp_type_failover_link, "failover-link", dhcp_failover_link_set_value, dhcp_failover_link_get_value, dhcp_failover_link_destroy, dhcp_failover_link_signal, dhcp_failover_link_stuff_values, 0, 0, 0, 0, 0, 0, sizeof (dhcp_failover_link_t), 0, RC_MISC); if (status != ISC_R_SUCCESS) log_fatal ("Can't register failover link object type: %s", isc_result_totext (status)); status = omapi_object_type_register (&dhcp_type_failover_listener, "failover-listener", dhcp_failover_listener_set_value, dhcp_failover_listener_get_value, dhcp_failover_listener_destroy, dhcp_failover_listener_signal, dhcp_failover_listener_stuff, 0, 0, 0, 0, 0, 0, sizeof (dhcp_failover_listener_t), 0, RC_MISC); if (status != ISC_R_SUCCESS) log_fatal ("Can't register failover listener object type: %s", isc_result_totext (status)); #endif /* FAILOVER_PROTOCOL */ } isc_result_t dhcp_lease_set_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_typed_data_t *value) { struct lease *lease; isc_result_t status; if (h -> type != dhcp_type_lease) return DHCP_R_INVALIDARG; lease = (struct lease *)h; /* We're skipping a lot of things it might be interesting to set - for now, we just make it possible to whack the state. */ if (!omapi_ds_strcmp (name, "state")) { unsigned long bar; const char *ols, *nls; status = omapi_get_int_value (&bar, value); if (status != ISC_R_SUCCESS) return status; if (bar < 1 || bar > FTS_LAST) return DHCP_R_INVALIDARG; nls = binding_state_names [bar - 1]; if (lease -> binding_state >= 1 && lease -> binding_state <= FTS_LAST) ols = binding_state_names [lease -> binding_state - 1]; else ols = "unknown state"; if (lease -> binding_state != bar) { lease -> next_binding_state = bar; if (supersede_lease (lease, NULL, 1, 1, 1, 0)) { log_info ("lease %s state changed from %s to %s", piaddr(lease->ip_addr), ols, nls); return ISC_R_SUCCESS; } log_info ("lease %s state change from %s to %s failed.", piaddr (lease -> ip_addr), ols, nls); return ISC_R_IOERROR; } return DHCP_R_UNCHANGED; } else if (!omapi_ds_strcmp (name, "ip-address")) { return ISC_R_NOPERM; } else if (!omapi_ds_strcmp (name, "dhcp-client-identifier")) { return DHCP_R_UNCHANGED; /* XXX take change. */ } else if (!omapi_ds_strcmp (name, "hostname")) { return DHCP_R_UNCHANGED; /* XXX take change. */ } else if (!omapi_ds_strcmp (name, "client-hostname")) { return DHCP_R_UNCHANGED; /* XXX take change. */ } else if (!omapi_ds_strcmp (name, "host")) { return DHCP_R_UNCHANGED; /* XXX take change. */ } else if (!omapi_ds_strcmp (name, "subnet")) { return DHCP_R_INVALIDARG; } else if (!omapi_ds_strcmp (name, "pool")) { return ISC_R_NOPERM; } else if (!omapi_ds_strcmp (name, "starts")) { return ISC_R_NOPERM; } else if (!omapi_ds_strcmp (name, "ends")) { unsigned long lease_end, old_lease_end; status = omapi_get_int_value (&lease_end, value); if (status != ISC_R_SUCCESS) return status; old_lease_end = lease->ends; lease->ends = lease_end; if (supersede_lease (lease, NULL, 1, 1, 1, 0)) { log_info ("lease %s end changed from %lu to %lu", piaddr(lease->ip_addr), old_lease_end, lease_end); return ISC_R_SUCCESS; } log_info ("lease %s end change from %lu to %lu failed", piaddr(lease->ip_addr), old_lease_end, lease_end); return ISC_R_IOERROR; } else if (!omapi_ds_strcmp(name, "flags")) { return (update_lease_flags(lease, value)); } else if (!omapi_ds_strcmp (name, "billing-class")) { return DHCP_R_UNCHANGED; /* XXX carefully allow change. */ } else if (!omapi_ds_strcmp (name, "hardware-address")) { return DHCP_R_UNCHANGED; /* XXX take change. */ } else if (!omapi_ds_strcmp (name, "hardware-type")) { return DHCP_R_UNCHANGED; /* XXX take change. */ } else if (lease -> scope) { status = binding_scope_set_value (lease -> scope, 0, name, value); if (status == ISC_R_SUCCESS) { if (write_lease (lease) && commit_leases ()) return ISC_R_SUCCESS; return ISC_R_IOERROR; } } /* Try to find some inner object that can take the value. */ if (h -> inner && h -> inner -> type -> set_value) { status = ((*(h -> inner -> type -> set_value)) (h -> inner, id, name, value)); if (status == ISC_R_SUCCESS || status == DHCP_R_UNCHANGED) return status; } if (!lease -> scope) { if (!binding_scope_allocate (&lease -> scope, MDL)) return ISC_R_NOMEMORY; } status = binding_scope_set_value (lease -> scope, 1, name, value); if (status != ISC_R_SUCCESS) return status; if (write_lease (lease) && commit_leases ()) return ISC_R_SUCCESS; return ISC_R_IOERROR; } /* * \brief Updates the lease's flags to a given value * * In order to update the lease's flags, we make a copy of the * lease, and update the copy's flags with the new value. * We then use the updated copy as the second parameter to a * call to supersede_lease(). This ensures that the lease * moves between queues correctly. This is critical when * the RESERVED_LEASE flag is being changed. * * Note that only the EPHEMERAL flags are permitted to be changed. * * \param lease - pointer to the lease to update * \param value - omapi data value containing the new flags value * * \return ISC_R_SUCCESS if the lease was successfully updated, * DHCP_R_UNCHANGED if new value would result in no change to the * lease's flags, or an appropriate status on other errors */ static isc_result_t update_lease_flags(struct lease* lease, omapi_typed_data_t *value) { u_int8_t oldflags; u_int8_t newflags; struct lease* lupdate = NULL; isc_result_t status; /* Grab the requested flags value. We (the server) send flags * out as 1-byte, so we expect clients to do the same. However * omshell, will send a network-ordered 4 byte integer if the * input is "set flags = ", so we'll accomdate that too. */ if (value->u.buffer.len == 1) { newflags = value->u.buffer.value[0]; } else { unsigned long tmp; status = omapi_get_int_value (&tmp, value); if (status != ISC_R_SUCCESS) { return (status); } newflags = (u_int8_t)tmp; } /* Save off the current flags value. */ oldflags = lease->flags; /* The new value must preserve all PERSISTANT_FLAGS */ newflags = ((lease->flags & ~EPHEMERAL_FLAGS) | (newflags & EPHEMERAL_FLAGS)); /* If there's no net change, we're done */ if (oldflags == newflags) { return (DHCP_R_UNCHANGED); } /* Make a copy of the lease. */ if (!lease_copy(&lupdate, lease, MDL)) { return (ISC_R_FAILURE); } /* Set the copy's flags to the new value */ lupdate->flags = newflags; /* Attempt to update the lease */ if (!supersede_lease(lease, lupdate, 1, 1, 1, 0)) { log_error("Failed to update flags for lease %s.", piaddr(lease->ip_addr)); status = ISC_R_FAILURE; } else { log_debug ("lease flags changed from %x to %x for lease %s.", oldflags, newflags, piaddr(lease->ip_addr)); status = ISC_R_SUCCESS; } lease_dereference(&lupdate, MDL); return (status); } isc_result_t dhcp_lease_get_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_value_t **value) { struct lease *lease; isc_result_t status; if (h -> type != dhcp_type_lease) return DHCP_R_INVALIDARG; lease = (struct lease *)h; if (!omapi_ds_strcmp (name, "state")) return omapi_make_int_value (value, name, (int)lease -> binding_state, MDL); else if (!omapi_ds_strcmp (name, "ip-address")) return omapi_make_const_value (value, name, lease -> ip_addr.iabuf, lease -> ip_addr.len, MDL); else if (!omapi_ds_strcmp (name, "dhcp-client-identifier")) { return omapi_make_const_value (value, name, lease -> uid, lease -> uid_len, MDL); } else if (!omapi_ds_strcmp (name, "client-hostname")) { if (lease -> client_hostname) return omapi_make_string_value (value, name, lease -> client_hostname, MDL); return ISC_R_NOTFOUND; } else if (!omapi_ds_strcmp (name, "host")) { if (lease -> host) return omapi_make_handle_value (value, name, ((omapi_object_t *)lease -> host), MDL); } else if (!omapi_ds_strcmp (name, "subnet")) return omapi_make_handle_value (value, name, ((omapi_object_t *) lease -> subnet), MDL); else if (!omapi_ds_strcmp (name, "pool")) return omapi_make_handle_value (value, name, ((omapi_object_t *) lease -> pool), MDL); else if (!omapi_ds_strcmp (name, "billing-class")) { if (lease -> billing_class) return omapi_make_handle_value (value, name, ((omapi_object_t *)lease -> billing_class), MDL); return ISC_R_NOTFOUND; } else if (!omapi_ds_strcmp (name, "hardware-address")) { if (lease -> hardware_addr.hlen) return omapi_make_const_value (value, name, &lease -> hardware_addr.hbuf [1], (unsigned)(lease -> hardware_addr.hlen - 1), MDL); return ISC_R_NOTFOUND; } else if (!omapi_ds_strcmp (name, "hardware-type")) { if (lease -> hardware_addr.hlen) return omapi_make_int_value (value, name, lease -> hardware_addr.hbuf [0], MDL); return ISC_R_NOTFOUND; } else if (lease -> scope) { status = binding_scope_get_value (value, lease -> scope, name); if (status != ISC_R_NOTFOUND) return status; } /* Try to find some inner object that can take the value. */ if (h -> inner && h -> inner -> type -> get_value) { status = ((*(h -> inner -> type -> get_value)) (h -> inner, id, name, value)); if (status == ISC_R_SUCCESS) return status; } return DHCP_R_UNKNOWNATTRIBUTE; } isc_result_t dhcp_lease_destroy (omapi_object_t *h, const char *file, int line) { struct lease *lease; if (h->type != dhcp_type_lease) return DHCP_R_INVALIDARG; lease = (struct lease *)h; if (lease-> uid) uid_hash_delete (lease); hw_hash_delete (lease); if (lease->on_star.on_release) executable_statement_dereference (&lease->on_star.on_release, file, line); if (lease->on_star.on_expiry) executable_statement_dereference (&lease->on_star.on_expiry, file, line); if (lease->on_star.on_commit) executable_statement_dereference (&lease->on_star.on_commit, file, line); if (lease->scope) binding_scope_dereference (&lease->scope, file, line); if (lease->agent_options) option_chain_head_dereference (&lease->agent_options, file, line); if (lease->uid && lease->uid != lease->uid_buf) { dfree (lease->uid, MDL); lease->uid = &lease->uid_buf [0]; lease->uid_len = 0; } if (lease->client_hostname) { dfree (lease->client_hostname, MDL); lease->client_hostname = (char *)0; } if (lease->host) host_dereference (&lease->host, file, line); if (lease->subnet) subnet_dereference (&lease->subnet, file, line); if (lease->pool) pool_dereference (&lease->pool, file, line); if (lease->state) { free_lease_state (lease->state, file, line); lease->state = (struct lease_state *)0; cancel_timeout (lease_ping_timeout, lease); --outstanding_pings; /* XXX */ } if (lease->billing_class) class_dereference (&lease->billing_class, file, line); /* We no longer check for a next pointer as that should * be cleared when we destroy the pool and as before we * should only ever be doing that on exit. if (lease->next) lease_dereference (&lease->next, file, line); */ if (lease->n_hw) lease_dereference (&lease->n_hw, file, line); if (lease->n_uid) lease_dereference (&lease->n_uid, file, line); if (lease->next_pending) lease_dereference (&lease->next_pending, file, line); return ISC_R_SUCCESS; } isc_result_t dhcp_lease_signal_handler (omapi_object_t *h, const char *name, va_list ap) { /* h should point to (struct lease *) */ isc_result_t status; if (h -> type != dhcp_type_lease) return DHCP_R_INVALIDARG; if (!strcmp (name, "updated")) return ISC_R_SUCCESS; /* Try to find some inner object that can take the value. */ if (h -> inner && h -> inner -> type -> signal_handler) { status = ((*(h -> inner -> type -> signal_handler)) (h -> inner, name, ap)); if (status == ISC_R_SUCCESS) return status; } return ISC_R_NOTFOUND; } isc_result_t dhcp_lease_stuff_values (omapi_object_t *c, omapi_object_t *id, omapi_object_t *h) { u_int32_t bouncer; struct lease *lease; isc_result_t status; u_int8_t flagbuf; if (h -> type != dhcp_type_lease) return DHCP_R_INVALIDARG; lease = (struct lease *)h; /* Write out all the values. */ status = omapi_connection_put_named_uint32(c, "state", lease->binding_state); if (status != ISC_R_SUCCESS) return (status); status = omapi_connection_put_name (c, "ip-address"); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_uint32 (c, lease -> ip_addr.len); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_copyin (c, lease -> ip_addr.iabuf, lease -> ip_addr.len); if (status != ISC_R_SUCCESS) return status; if (lease -> uid_len) { status = omapi_connection_put_name (c, "dhcp-client-identifier"); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_uint32 (c, lease -> uid_len); if (status != ISC_R_SUCCESS) return status; if (lease -> uid_len) { status = omapi_connection_copyin (c, lease -> uid, lease -> uid_len); if (status != ISC_R_SUCCESS) return status; } } if (lease -> client_hostname) { status = omapi_connection_put_name (c, "client-hostname"); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_string (c, lease -> client_hostname); if (status != ISC_R_SUCCESS) return status; } if (lease -> host) { status = omapi_connection_put_name (c, "host"); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_handle (c, (omapi_object_t *) lease -> host); if (status != ISC_R_SUCCESS) return status; } status = omapi_connection_put_name (c, "subnet"); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_handle (c, (omapi_object_t *)lease -> subnet); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_name (c, "pool"); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_handle (c, (omapi_object_t *)lease -> pool); if (status != ISC_R_SUCCESS) return status; if (lease -> billing_class) { status = omapi_connection_put_name (c, "billing-class"); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_handle (c, (omapi_object_t *)lease -> billing_class); if (status != ISC_R_SUCCESS) return status; } if (lease -> hardware_addr.hlen) { status = omapi_connection_put_name (c, "hardware-address"); if (status != ISC_R_SUCCESS) return status; status = (omapi_connection_put_uint32 (c, (unsigned long)(lease -> hardware_addr.hlen - 1))); if (status != ISC_R_SUCCESS) return status; status = (omapi_connection_copyin (c, &lease -> hardware_addr.hbuf [1], (unsigned long)(lease -> hardware_addr.hlen - 1))); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_named_uint32(c, "hardware-type", lease->hardware_addr.hbuf[0]); if (status != ISC_R_SUCCESS) return (status); } /* TIME values may be 64-bit, depending on system architecture. * OMAPI must be system independent, both in terms of transmitting * bytes on the wire in network byte order, and in terms of being * readable and usable by both systems. * * XXX: In a future feature release, a put_int64() should be made * to exist, and perhaps a put_time() wrapper that selects which * to use based upon sizeof(TIME). In the meantime, use existing, * 32-bit, code. */ bouncer = (u_int32_t)lease->ends; status = omapi_connection_put_named_uint32(c, "ends", bouncer); if (status != ISC_R_SUCCESS) return (status); bouncer = (u_int32_t)lease->starts; status = omapi_connection_put_named_uint32(c, "starts", bouncer); if (status != ISC_R_SUCCESS) return (status); bouncer = (u_int32_t)lease->tstp; status = omapi_connection_put_named_uint32(c, "tstp", bouncer); if (status != ISC_R_SUCCESS) return (status); bouncer = (u_int32_t)lease->tsfp; status = omapi_connection_put_named_uint32(c, "tsfp", bouncer); if (status != ISC_R_SUCCESS) return status; bouncer = (u_int32_t)lease->atsfp; status = omapi_connection_put_named_uint32(c, "atsfp", bouncer); if (status != ISC_R_SUCCESS) return status; bouncer = (u_int32_t)lease->cltt; status = omapi_connection_put_named_uint32(c, "cltt", bouncer); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_name (c, "flags"); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_uint32(c, sizeof(flagbuf)); if (status != ISC_R_SUCCESS) return status; flagbuf = lease->flags & EPHEMERAL_FLAGS; status = omapi_connection_copyin(c, &flagbuf, sizeof(flagbuf)); if (status != ISC_R_SUCCESS) return status; if (lease -> scope) { status = binding_scope_stuff_values (c, lease -> scope); if (status != ISC_R_SUCCESS) return status; } /* Write out the inner object, if any. */ if (h -> inner && h -> inner -> type -> stuff_values) { status = ((*(h -> inner -> type -> stuff_values)) (c, id, h -> inner)); if (status == ISC_R_SUCCESS) return status; } return ISC_R_SUCCESS; } isc_result_t dhcp_lease_lookup (omapi_object_t **lp, omapi_object_t *id, omapi_object_t *ref) { omapi_value_t *tv = (omapi_value_t *)0; isc_result_t status; struct lease *lease; if (!ref) return DHCP_R_NOKEYS; /* First see if we were sent a handle. */ status = omapi_get_value_str (ref, id, "handle", &tv); if (status == ISC_R_SUCCESS) { status = omapi_handle_td_lookup (lp, tv -> value); omapi_value_dereference (&tv, MDL); if (status != ISC_R_SUCCESS) return status; /* Don't return the object if the type is wrong. */ if ((*lp) -> type != dhcp_type_lease) { omapi_object_dereference (lp, MDL); return DHCP_R_INVALIDARG; } } /* Now look for an IP address. */ status = omapi_get_value_str (ref, id, "ip-address", &tv); if (status == ISC_R_SUCCESS) { lease = (struct lease *)0; lease_ip_hash_lookup(&lease, lease_ip_addr_hash, tv->value->u.buffer.value, tv->value->u.buffer.len, MDL); omapi_value_dereference (&tv, MDL); /* If we already have a lease, and it's not the same one, then the query was invalid. */ if (*lp && *lp != (omapi_object_t *)lease) { omapi_object_dereference (lp, MDL); lease_dereference (&lease, MDL); return DHCP_R_KEYCONFLICT; } else if (!lease) { if (*lp) omapi_object_dereference (lp, MDL); return ISC_R_NOTFOUND; } else if (!*lp) { /* XXX fix so that hash lookup itself creates XXX the reference. */ omapi_object_reference (lp, (omapi_object_t *)lease, MDL); lease_dereference (&lease, MDL); } } /* Now look for a client identifier. */ status = omapi_get_value_str (ref, id, "dhcp-client-identifier", &tv); if (status == ISC_R_SUCCESS) { lease = (struct lease *)0; lease_id_hash_lookup(&lease, lease_uid_hash, tv->value->u.buffer.value, tv->value->u.buffer.len, MDL); omapi_value_dereference (&tv, MDL); if (*lp && *lp != (omapi_object_t *)lease) { omapi_object_dereference (lp, MDL); lease_dereference (&lease, MDL); return DHCP_R_KEYCONFLICT; } else if (!lease) { if (*lp) omapi_object_dereference (lp, MDL); return ISC_R_NOTFOUND; } else if (lease -> n_uid) { if (*lp) omapi_object_dereference (lp, MDL); return DHCP_R_MULTIPLE; } else if (!*lp) { /* XXX fix so that hash lookup itself creates XXX the reference. */ omapi_object_reference (lp, (omapi_object_t *)lease, MDL); lease_dereference (&lease, MDL); } } /* Now look for a hardware address. */ status = omapi_get_value_str (ref, id, "hardware-address", &tv); if (status == ISC_R_SUCCESS) { unsigned char *haddr; unsigned int len; len = tv -> value -> u.buffer.len + 1; haddr = dmalloc (len, MDL); if (!haddr) { omapi_value_dereference (&tv, MDL); return ISC_R_NOMEMORY; } memcpy (haddr + 1, tv -> value -> u.buffer.value, len - 1); omapi_value_dereference (&tv, MDL); status = omapi_get_value_str (ref, id, "hardware-type", &tv); if (status == ISC_R_SUCCESS) { if (tv -> value -> type == omapi_datatype_data) { if ((tv -> value -> u.buffer.len != 4) || (tv -> value -> u.buffer.value[0] != 0) || (tv -> value -> u.buffer.value[1] != 0) || (tv -> value -> u.buffer.value[2] != 0)) { omapi_value_dereference (&tv, MDL); dfree (haddr, MDL); return DHCP_R_INVALIDARG; } haddr[0] = tv -> value -> u.buffer.value[3]; } else if (tv -> value -> type == omapi_datatype_int) { haddr[0] = (unsigned char) tv -> value -> u.integer; } else { omapi_value_dereference (&tv, MDL); dfree (haddr, MDL); return DHCP_R_INVALIDARG; } omapi_value_dereference (&tv, MDL); } else { /* If no hardware-type is specified, default to ethernet. This may or may not be a good idea, but Telus is currently relying on this behavior. - DPN */ haddr[0] = HTYPE_ETHER; } lease = (struct lease *)0; lease_id_hash_lookup(&lease, lease_hw_addr_hash, haddr, len, MDL); dfree (haddr, MDL); if (*lp && *lp != (omapi_object_t *)lease) { omapi_object_dereference (lp, MDL); lease_dereference (&lease, MDL); return DHCP_R_KEYCONFLICT; } else if (!lease) { if (*lp) omapi_object_dereference (lp, MDL); return ISC_R_NOTFOUND; } else if (lease -> n_hw) { if (*lp) omapi_object_dereference (lp, MDL); lease_dereference (&lease, MDL); return DHCP_R_MULTIPLE; } else if (!*lp) { /* XXX fix so that hash lookup itself creates XXX the reference. */ omapi_object_reference (lp, (omapi_object_t *)lease, MDL); lease_dereference (&lease, MDL); } } /* If we get to here without finding a lease, no valid key was specified. */ if (!*lp) return DHCP_R_NOKEYS; return ISC_R_SUCCESS; } isc_result_t dhcp_lease_create (omapi_object_t **lp, omapi_object_t *id) { return ISC_R_NOTIMPLEMENTED; } isc_result_t dhcp_lease_remove (omapi_object_t *lp, omapi_object_t *id) { return ISC_R_NOTIMPLEMENTED; } isc_result_t dhcp_host_set_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_typed_data_t *value) { struct host_decl *host; isc_result_t status; if (h -> type != dhcp_type_host) return DHCP_R_INVALIDARG; host = (struct host_decl *)h; /* XXX For now, we can only set these values on new host objects. XXX Soon, we need to be able to update host objects. */ if (!omapi_ds_strcmp (name, "name")) { if (host -> name) return ISC_R_EXISTS; if (value && (value -> type == omapi_datatype_data || value -> type == omapi_datatype_string)) { host -> name = dmalloc (value -> u.buffer.len + 1, MDL); if (!host -> name) return ISC_R_NOMEMORY; memcpy (host -> name, value -> u.buffer.value, value -> u.buffer.len); host -> name [value -> u.buffer.len] = 0; } else return DHCP_R_INVALIDARG; return ISC_R_SUCCESS; } if (!omapi_ds_strcmp (name, "group")) { if (value && (value -> type == omapi_datatype_data || value -> type == omapi_datatype_string)) { struct group_object *group; group = (struct group_object *)0; group_hash_lookup (&group, group_name_hash, (char *)value -> u.buffer.value, value -> u.buffer.len, MDL); if (!group || (group -> flags & GROUP_OBJECT_DELETED)) return ISC_R_NOTFOUND; if (host -> group) group_dereference (&host -> group, MDL); group_reference (&host -> group, group -> group, MDL); if (host -> named_group) group_object_dereference (&host -> named_group, MDL); group_object_reference (&host -> named_group, group, MDL); group_object_dereference (&group, MDL); } else return DHCP_R_INVALIDARG; return ISC_R_SUCCESS; } if (!omapi_ds_strcmp (name, "hardware-address")) { if (host -> interface.hlen) return ISC_R_EXISTS; if (value && (value -> type == omapi_datatype_data || value -> type == omapi_datatype_string)) { if (value -> u.buffer.len > (sizeof host -> interface.hbuf) - 1) return DHCP_R_INVALIDARG; memcpy (&host -> interface.hbuf [1], value -> u.buffer.value, value -> u.buffer.len); host -> interface.hlen = value -> u.buffer.len + 1; } else return DHCP_R_INVALIDARG; return ISC_R_SUCCESS; } if (!omapi_ds_strcmp (name, "hardware-type")) { int type; if ((value != NULL) && ((value->type == omapi_datatype_data) && (value->u.buffer.len == sizeof(type)))) { if (value->u.buffer.len > sizeof(type)) return (DHCP_R_INVALIDARG); memcpy(&type, value->u.buffer.value, value->u.buffer.len); type = ntohl(type); } else if ((value != NULL) && (value->type == omapi_datatype_int)) type = value->u.integer; else return (DHCP_R_INVALIDARG); host->interface.hbuf[0] = type; return (ISC_R_SUCCESS); } if (!omapi_ds_strcmp (name, "dhcp-client-identifier")) { if (host -> client_identifier.data) return ISC_R_EXISTS; if (value && (value -> type == omapi_datatype_data || value -> type == omapi_datatype_string)) { if (!buffer_allocate (&host -> client_identifier.buffer, value -> u.buffer.len, MDL)) return ISC_R_NOMEMORY; host -> client_identifier.data = &host -> client_identifier.buffer -> data [0]; memcpy (host -> client_identifier.buffer -> data, value -> u.buffer.value, value -> u.buffer.len); host -> client_identifier.len = value -> u.buffer.len; } else return DHCP_R_INVALIDARG; return ISC_R_SUCCESS; } if (!omapi_ds_strcmp (name, "ip-address")) { if (host -> fixed_addr) option_cache_dereference (&host -> fixed_addr, MDL); if (!value) return ISC_R_SUCCESS; if (value && (value -> type == omapi_datatype_data || value -> type == omapi_datatype_string)) { struct data_string ds; memset (&ds, 0, sizeof ds); ds.len = value -> u.buffer.len; if (!buffer_allocate (&ds.buffer, ds.len, MDL)) return ISC_R_NOMEMORY; ds.data = (&ds.buffer -> data [0]); memcpy (ds.buffer -> data, value -> u.buffer.value, ds.len); if (!option_cache (&host -> fixed_addr, &ds, (struct expression *)0, (struct option *)0, MDL)) { data_string_forget (&ds, MDL); return ISC_R_NOMEMORY; } data_string_forget (&ds, MDL); } else return DHCP_R_INVALIDARG; return ISC_R_SUCCESS; } if (!omapi_ds_strcmp (name, "statements")) { if (!host -> group) { if (!clone_group (&host -> group, root_group, MDL)) return ISC_R_NOMEMORY; } else { if (host -> group -> statements && (!host -> named_group || host -> group != host -> named_group -> group) && host -> group != root_group) return ISC_R_EXISTS; if (!clone_group (&host -> group, host -> group, MDL)) return ISC_R_NOMEMORY; } if (!host -> group) return ISC_R_NOMEMORY; if (value && (value -> type == omapi_datatype_data || value -> type == omapi_datatype_string)) { struct parse *parse; int lose = 0; parse = (struct parse *)0; status = new_parse(&parse, -1, (char *) value->u.buffer.value, value->u.buffer.len, "network client", 0); if (status != ISC_R_SUCCESS || parse == NULL) return status; if (!(parse_executable_statements (&host -> group -> statements, parse, &lose, context_any))) { end_parse (&parse); return DHCP_R_BADPARSE; } end_parse (&parse); } else return DHCP_R_INVALIDARG; return ISC_R_SUCCESS; } /* The "known" flag isn't supported in the database yet, but it's legitimate. */ if (!omapi_ds_strcmp (name, "known")) { return ISC_R_SUCCESS; } /* Try to find some inner object that can take the value. */ if (h -> inner && h -> inner -> type -> set_value) { status = ((*(h -> inner -> type -> set_value)) (h -> inner, id, name, value)); if (status == ISC_R_SUCCESS || status == DHCP_R_UNCHANGED) return status; } return DHCP_R_UNKNOWNATTRIBUTE; } isc_result_t dhcp_host_get_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_value_t **value) { struct host_decl *host; isc_result_t status; struct data_string ip_addrs; if (h -> type != dhcp_type_host) return DHCP_R_INVALIDARG; host = (struct host_decl *)h; if (!omapi_ds_strcmp (name, "ip-addresses")) { memset (&ip_addrs, 0, sizeof ip_addrs); if (host -> fixed_addr && evaluate_option_cache (&ip_addrs, (struct packet *)0, (struct lease *)0, (struct client_state *)0, (struct option_state *)0, (struct option_state *)0, &global_scope, host -> fixed_addr, MDL)) { status = omapi_make_const_value (value, name, ip_addrs.data, ip_addrs.len, MDL); data_string_forget (&ip_addrs, MDL); return status; } return ISC_R_NOTFOUND; } if (!omapi_ds_strcmp (name, "dhcp-client-identifier")) { if (!host -> client_identifier.len) return ISC_R_NOTFOUND; return omapi_make_const_value (value, name, host -> client_identifier.data, host -> client_identifier.len, MDL); } if (!omapi_ds_strcmp (name, "name")) return omapi_make_string_value (value, name, host -> name, MDL); if (!omapi_ds_strcmp (name, "hardware-address")) { if (!host -> interface.hlen) return ISC_R_NOTFOUND; return (omapi_make_const_value (value, name, &host -> interface.hbuf [1], (unsigned long)(host -> interface.hlen - 1), MDL)); } if (!omapi_ds_strcmp (name, "hardware-type")) { if (!host -> interface.hlen) return ISC_R_NOTFOUND; return omapi_make_int_value (value, name, host -> interface.hbuf [0], MDL); } /* Try to find some inner object that can take the value. */ if (h -> inner && h -> inner -> type -> get_value) { status = ((*(h -> inner -> type -> get_value)) (h -> inner, id, name, value)); if (status == ISC_R_SUCCESS) return status; } return DHCP_R_UNKNOWNATTRIBUTE; } isc_result_t dhcp_host_destroy (omapi_object_t *h, const char *file, int line) { if (h -> type != dhcp_type_host) return DHCP_R_INVALIDARG; struct host_decl *host = (struct host_decl *)h; if (host -> n_ipaddr) host_dereference (&host -> n_ipaddr, file, line); if (host -> n_dynamic) host_dereference (&host -> n_dynamic, file, line); if (host -> name) { dfree (host -> name, file, line); host -> name = (char *)0; } data_string_forget (&host -> client_identifier, file, line); if (host -> fixed_addr) option_cache_dereference (&host -> fixed_addr, file, line); if (host -> group) group_dereference (&host -> group, file, line); if (host -> named_group) omapi_object_dereference ((omapi_object_t **) &host -> named_group, file, line); data_string_forget (&host -> auth_key_id, file, line); return ISC_R_SUCCESS; } isc_result_t dhcp_host_signal_handler (omapi_object_t *h, const char *name, va_list ap) { struct host_decl *host; isc_result_t status; int updatep = 0; if (h -> type != dhcp_type_host) return DHCP_R_INVALIDARG; host = (struct host_decl *)h; if (!strcmp (name, "updated")) { /* There must be a client identifier of some sort. */ if (host -> interface.hlen == 0 && !host -> client_identifier.len) return DHCP_R_INVALIDARG; if (!host -> name) { char hnbuf [64]; sprintf (hnbuf, "nh%08lx%08lx", (unsigned long)cur_time, (unsigned long)host); host -> name = dmalloc (strlen (hnbuf) + 1, MDL); if (!host -> name) return ISC_R_NOMEMORY; strcpy (host -> name, hnbuf); } #ifdef DEBUG_OMAPI log_debug ("OMAPI added host %s", host -> name); #endif status = enter_host (host, 1, 1); if (status != ISC_R_SUCCESS) return status; updatep = 1; } /* Try to find some inner object that can take the value. */ if (h -> inner && h -> inner -> type -> signal_handler) { status = ((*(h -> inner -> type -> signal_handler)) (h -> inner, name, ap)); if (status == ISC_R_SUCCESS) return status; } if (updatep) return ISC_R_SUCCESS; return ISC_R_NOTFOUND; } isc_result_t dhcp_host_stuff_values (omapi_object_t *c, omapi_object_t *id, omapi_object_t *h) { struct host_decl *host; isc_result_t status; struct data_string ip_addrs; if (h -> type != dhcp_type_host) return DHCP_R_INVALIDARG; host = (struct host_decl *)h; /* Write out all the values. */ memset (&ip_addrs, 0, sizeof ip_addrs); if (host -> fixed_addr && evaluate_option_cache (&ip_addrs, (struct packet *)0, (struct lease *)0, (struct client_state *)0, (struct option_state *)0, (struct option_state *)0, &global_scope, host -> fixed_addr, MDL)) { status = omapi_connection_put_name (c, "ip-address"); if (status != ISC_R_SUCCESS) { data_string_forget (&ip_addrs, MDL); return status; } status = omapi_connection_put_uint32 (c, ip_addrs.len); if (status != ISC_R_SUCCESS) { data_string_forget (&ip_addrs, MDL); return status; } status = omapi_connection_copyin (c, ip_addrs.data, ip_addrs.len); if (status != ISC_R_SUCCESS) { data_string_forget (&ip_addrs, MDL); return status; } data_string_forget (&ip_addrs, MDL); } if (host -> client_identifier.len) { status = omapi_connection_put_name (c, "dhcp-client-identifier"); if (status != ISC_R_SUCCESS) return status; status = (omapi_connection_put_uint32 (c, host -> client_identifier.len)); if (status != ISC_R_SUCCESS) return status; status = (omapi_connection_copyin (c, host -> client_identifier.data, host -> client_identifier.len)); if (status != ISC_R_SUCCESS) return status; } if (host -> name) { status = omapi_connection_put_name (c, "name"); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_string (c, host -> name); if (status != ISC_R_SUCCESS) return status; } if (host -> interface.hlen) { status = omapi_connection_put_name (c, "hardware-address"); if (status != ISC_R_SUCCESS) return status; status = (omapi_connection_put_uint32 (c, (unsigned long)(host -> interface.hlen - 1))); if (status != ISC_R_SUCCESS) return status; status = (omapi_connection_copyin (c, &host -> interface.hbuf [1], (unsigned long)(host -> interface.hlen - 1))); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_named_uint32(c, "hardware-type", host->interface.hbuf[0]); if (status != ISC_R_SUCCESS) return status; } /* Write out the inner object, if any. */ if (h -> inner && h -> inner -> type -> stuff_values) { status = ((*(h -> inner -> type -> stuff_values)) (c, id, h -> inner)); if (status == ISC_R_SUCCESS) return status; } return ISC_R_SUCCESS; } isc_result_t dhcp_host_lookup (omapi_object_t **lp, omapi_object_t *id, omapi_object_t *ref) { omapi_value_t *tv = (omapi_value_t *)0; isc_result_t status; struct host_decl *host; if (!ref) return DHCP_R_NOKEYS; /* First see if we were sent a handle. */ status = omapi_get_value_str (ref, id, "handle", &tv); if (status == ISC_R_SUCCESS) { status = omapi_handle_td_lookup (lp, tv -> value); omapi_value_dereference (&tv, MDL); if (status != ISC_R_SUCCESS) return status; /* Don't return the object if the type is wrong. */ if ((*lp) -> type != dhcp_type_host) { omapi_object_dereference (lp, MDL); return DHCP_R_INVALIDARG; } if (((struct host_decl *)(*lp)) -> flags & HOST_DECL_DELETED) { omapi_object_dereference (lp, MDL); } } /* Now look for a client identifier. */ status = omapi_get_value_str (ref, id, "dhcp-client-identifier", &tv); if (status == ISC_R_SUCCESS) { host = (struct host_decl *)0; host_hash_lookup (&host, host_uid_hash, tv -> value -> u.buffer.value, tv -> value -> u.buffer.len, MDL); omapi_value_dereference (&tv, MDL); if (*lp && *lp != (omapi_object_t *)host) { omapi_object_dereference (lp, MDL); if (host) host_dereference (&host, MDL); return DHCP_R_KEYCONFLICT; } else if (!host || (host -> flags & HOST_DECL_DELETED)) { if (*lp) omapi_object_dereference (lp, MDL); if (host) host_dereference (&host, MDL); return ISC_R_NOTFOUND; } else if (!*lp) { /* XXX fix so that hash lookup itself creates XXX the reference. */ omapi_object_reference (lp, (omapi_object_t *)host, MDL); host_dereference (&host, MDL); } } /* Now look for a hardware address. */ status = omapi_get_value_str (ref, id, "hardware-address", &tv); if (status == ISC_R_SUCCESS) { unsigned char *haddr; unsigned int len; len = tv -> value -> u.buffer.len + 1; haddr = dmalloc (len, MDL); if (!haddr) { omapi_value_dereference (&tv, MDL); return ISC_R_NOMEMORY; } memcpy (haddr + 1, tv -> value -> u.buffer.value, len - 1); omapi_value_dereference (&tv, MDL); status = omapi_get_value_str (ref, id, "hardware-type", &tv); if (status == ISC_R_SUCCESS) { if (tv -> value -> type == omapi_datatype_data) { if ((tv -> value -> u.buffer.len != 4) || (tv -> value -> u.buffer.value[0] != 0) || (tv -> value -> u.buffer.value[1] != 0) || (tv -> value -> u.buffer.value[2] != 0)) { omapi_value_dereference (&tv, MDL); dfree (haddr, MDL); return DHCP_R_INVALIDARG; } haddr[0] = tv -> value -> u.buffer.value[3]; } else if (tv -> value -> type == omapi_datatype_int) { haddr[0] = (unsigned char) tv -> value -> u.integer; } else { omapi_value_dereference (&tv, MDL); dfree (haddr, MDL); return DHCP_R_INVALIDARG; } omapi_value_dereference (&tv, MDL); } else { /* If no hardware-type is specified, default to ethernet. This may or may not be a good idea, but Telus is currently relying on this behavior. - DPN */ haddr[0] = HTYPE_ETHER; } host = (struct host_decl *)0; host_hash_lookup (&host, host_hw_addr_hash, haddr, len, MDL); dfree (haddr, MDL); if (*lp && *lp != (omapi_object_t *)host) { omapi_object_dereference (lp, MDL); if (host) host_dereference (&host, MDL); return DHCP_R_KEYCONFLICT; } else if (!host || (host -> flags & HOST_DECL_DELETED)) { if (*lp) omapi_object_dereference (lp, MDL); if (host) host_dereference (&host, MDL); return ISC_R_NOTFOUND; } else if (!*lp) { /* XXX fix so that hash lookup itself creates XXX the reference. */ omapi_object_reference (lp, (omapi_object_t *)host, MDL); host_dereference (&host, MDL); } } /* Now look for an ip address. */ status = omapi_get_value_str (ref, id, "ip-address", &tv); if (status == ISC_R_SUCCESS) { struct lease *l; /* first find the lease for this ip address */ l = (struct lease *)0; lease_ip_hash_lookup(&l, lease_ip_addr_hash, tv->value->u.buffer.value, tv->value->u.buffer.len, MDL); omapi_value_dereference (&tv, MDL); if (!l && !*lp) return ISC_R_NOTFOUND; if (l) { /* now use that to get a host */ host = (struct host_decl *)0; host_hash_lookup (&host, host_hw_addr_hash, l -> hardware_addr.hbuf, l -> hardware_addr.hlen, MDL); if (host && *lp && *lp != (omapi_object_t *)host) { omapi_object_dereference (lp, MDL); if (host) host_dereference (&host, MDL); return DHCP_R_KEYCONFLICT; } else if (!host || (host -> flags & HOST_DECL_DELETED)) { if (host) host_dereference (&host, MDL); if (!*lp) return ISC_R_NOTFOUND; } else if (!*lp) { /* XXX fix so that hash lookup itself creates XXX the reference. */ omapi_object_reference (lp, (omapi_object_t *)host, MDL); host_dereference (&host, MDL); } lease_dereference (&l, MDL); } } /* Now look for a name. */ status = omapi_get_value_str (ref, id, "name", &tv); if (status == ISC_R_SUCCESS) { host = (struct host_decl *)0; host_hash_lookup (&host, host_name_hash, tv -> value -> u.buffer.value, tv -> value -> u.buffer.len, MDL); omapi_value_dereference (&tv, MDL); if (*lp && *lp != (omapi_object_t *)host) { omapi_object_dereference (lp, MDL); if (host) host_dereference (&host, MDL); return DHCP_R_KEYCONFLICT; } else if (!host || (host -> flags & HOST_DECL_DELETED)) { if (host) host_dereference (&host, MDL); return ISC_R_NOTFOUND; } else if (!*lp) { /* XXX fix so that hash lookup itself creates XXX the reference. */ omapi_object_reference (lp, (omapi_object_t *)host, MDL); host_dereference (&host, MDL); } } /* If we get to here without finding a host, no valid key was specified. */ if (!*lp) return DHCP_R_NOKEYS; return ISC_R_SUCCESS; } isc_result_t dhcp_host_create (omapi_object_t **lp, omapi_object_t *id) { struct host_decl *hp; isc_result_t status; hp = (struct host_decl *)0; status = host_allocate (&hp, MDL); if (status != ISC_R_SUCCESS) return status; group_reference (&hp -> group, root_group, MDL); hp -> flags = HOST_DECL_DYNAMIC; status = omapi_object_reference (lp, (omapi_object_t *)hp, MDL); host_dereference (&hp, MDL); return status; } isc_result_t dhcp_host_remove (omapi_object_t *lp, omapi_object_t *id) { struct host_decl *hp; if (lp -> type != dhcp_type_host) return DHCP_R_INVALIDARG; hp = (struct host_decl *)lp; #ifdef DEBUG_OMAPI log_debug ("OMAPI delete host %s", hp -> name); #endif delete_host (hp, 1); return ISC_R_SUCCESS; } isc_result_t dhcp_pool_set_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_typed_data_t *value) { /* h should point to (struct pool *) */ isc_result_t status; if (h -> type != dhcp_type_pool) return DHCP_R_INVALIDARG; /* No values to set yet. */ /* Try to find some inner object that can take the value. */ if (h -> inner && h -> inner -> type -> set_value) { status = ((*(h -> inner -> type -> set_value)) (h -> inner, id, name, value)); if (status == ISC_R_SUCCESS || status == DHCP_R_UNCHANGED) return status; } return DHCP_R_UNKNOWNATTRIBUTE; } isc_result_t dhcp_pool_get_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_value_t **value) { /* h should point to (struct pool *) */ isc_result_t status; if (h -> type != dhcp_type_pool) return DHCP_R_INVALIDARG; /* No values to get yet. */ /* Try to find some inner object that can provide the value. */ if (h -> inner && h -> inner -> type -> get_value) { status = ((*(h -> inner -> type -> get_value)) (h -> inner, id, name, value)); if (status == ISC_R_SUCCESS) return status; } return DHCP_R_UNKNOWNATTRIBUTE; } isc_result_t dhcp_pool_destroy (omapi_object_t *h, const char *file, int line) { struct permit *pc, *pn; if (h -> type != dhcp_type_pool) return DHCP_R_INVALIDARG; struct pool *pool = (struct pool *)h; if (pool -> next) pool_dereference (&pool -> next, file, line); if (pool -> group) group_dereference (&pool -> group, file, line); if (pool -> shared_network) shared_network_dereference (&pool -> shared_network, file, line); POOL_DESTROYP(&pool->active); POOL_DESTROYP(&pool->expired); POOL_DESTROYP(&pool->free); POOL_DESTROYP(&pool->backup); POOL_DESTROYP(&pool->abandoned); POOL_DESTROYP(&pool->reserved); #if defined (FAILOVER_PROTOCOL) if (pool -> failover_peer) dhcp_failover_state_dereference (&pool -> failover_peer, file, line); #endif for (pc = pool -> permit_list; pc; pc = pn) { pn = pc -> next; free_permit (pc, file, line); } pool -> permit_list = (struct permit *)0; for (pc = pool -> prohibit_list; pc; pc = pn) { pn = pc -> next; free_permit (pc, file, line); } pool -> prohibit_list = (struct permit *)0; return ISC_R_SUCCESS; } isc_result_t dhcp_pool_signal_handler (omapi_object_t *h, const char *name, va_list ap) { /* h should point to (struct pool *) */ isc_result_t status; if (h -> type != dhcp_type_pool) return DHCP_R_INVALIDARG; /* Can't write pools yet. */ /* Try to find some inner object that can take the value. */ if (h -> inner && h -> inner -> type -> signal_handler) { status = ((*(h -> inner -> type -> signal_handler)) (h -> inner, name, ap)); if (status == ISC_R_SUCCESS) return status; } return ISC_R_NOTFOUND; } isc_result_t dhcp_pool_stuff_values (omapi_object_t *c, omapi_object_t *id, omapi_object_t *h) { struct pool *pool; isc_result_t status; if (h->type != dhcp_type_pool) return (DHCP_R_INVALIDARG); pool = (struct pool *)h; /* * I don't think we can actually find a pool yet * but include the output of interesting values * for when we do */ status = omapi_connection_put_named_uint32(c, "lease-count", ((u_int32_t) pool->lease_count)); if (status != ISC_R_SUCCESS) return (status); status = omapi_connection_put_named_uint32(c, "free-leases", ((u_int32_t) pool->free_leases)); if (status != ISC_R_SUCCESS) return (status); status = omapi_connection_put_named_uint32(c, "backup-leases", ((u_int32_t) pool->backup_leases)); if (status != ISC_R_SUCCESS) return (status); /* we could add time stamps but lets wait on those */ /* Write out the inner object, if any. */ if (h->inner && h->inner->type->stuff_values) { status = ((*(h->inner->type->stuff_values)) (c, id, h->inner)); if (status == ISC_R_SUCCESS) return (status); } return (ISC_R_SUCCESS); } isc_result_t dhcp_pool_lookup (omapi_object_t **lp, omapi_object_t *id, omapi_object_t *ref) { /* Can't look up pools yet. */ /* If we get to here without finding a pool, no valid key was specified. */ if (!*lp) return DHCP_R_NOKEYS; return ISC_R_SUCCESS; } isc_result_t dhcp_pool_create (omapi_object_t **lp, omapi_object_t *id) { return ISC_R_NOTIMPLEMENTED; } isc_result_t dhcp_pool_remove (omapi_object_t *lp, omapi_object_t *id) { return ISC_R_NOTIMPLEMENTED; } static isc_result_t class_set_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_typed_data_t *value) { struct class *class; struct class *superclass = 0; isc_result_t status; int issubclass = (h -> type == dhcp_type_subclass); class = (struct class *)h; if (!omapi_ds_strcmp(name, "name")) { if (class->name) return ISC_R_EXISTS; if (issubclass) { char tname[value->u.buffer.len + 1]; memcpy(tname, value->u.buffer.value, value->u.buffer.len); tname[sizeof(tname)-1] = '\0'; status = find_class(&superclass, tname, MDL); if (status == ISC_R_NOTFOUND) return status; if (class->superclass != NULL) class_dereference(&class->superclass, MDL); class_reference(&class->superclass, superclass, MDL); if (class->group != NULL) group_dereference(&class->group, MDL); group_reference(&class->group, superclass->group, MDL); class->lease_limit = superclass->lease_limit; if (class->lease_limit != 0) { class->billed_leases = dmalloc(class->lease_limit * sizeof(struct lease *), MDL); if (class->billed_leases == NULL) { return ISC_R_NOMEMORY; } } } else if (value->type == omapi_datatype_data || value->type == omapi_datatype_string) { class->name = dmalloc(value->u.buffer.len + 1, MDL); if (!class->name) return ISC_R_NOMEMORY; /* class->name is null-terminated from dmalloc() */ memcpy(class->name, value->u.buffer.value, value->u.buffer.len); } else return DHCP_R_INVALIDARG; return ISC_R_SUCCESS; } if (issubclass && !omapi_ds_strcmp(name, "hashstring")) { if (class->hash_string.data) return ISC_R_EXISTS; if (value->type == omapi_datatype_data || value->type == omapi_datatype_string) { if (!buffer_allocate(&class->hash_string.buffer, value->u.buffer.len, MDL)) return ISC_R_NOMEMORY; class->hash_string.data = class->hash_string.buffer->data; memcpy(class->hash_string.buffer->data, value->u.buffer.value, value->u.buffer.len); class->hash_string.len = value->u.buffer.len; } else return DHCP_R_INVALIDARG; return ISC_R_SUCCESS; } if (!omapi_ds_strcmp(name, "group")) { if (value->type == omapi_datatype_data || value->type == omapi_datatype_string) { struct group_object *group = NULL; group_hash_lookup(&group, group_name_hash, (char *)value->u.buffer.value, value->u.buffer.len, MDL); if (!group || (group->flags & GROUP_OBJECT_DELETED)) return ISC_R_NOTFOUND; if (class->group) group_dereference(&class->group, MDL); group_reference(&class->group, group->group, MDL); group_object_dereference(&group, MDL); } else return DHCP_R_INVALIDARG; return ISC_R_SUCCESS; } /* note we do not support full expressions via omapi because the expressions parser needs to be re-done to support parsing from strings and not just files. */ if (!omapi_ds_strcmp(name, "match")) { if (value->type == omapi_datatype_data || value->type == omapi_datatype_string) { unsigned minlen = (value->u.buffer.len > 8 ? 8 : value->u.buffer.len); if (!strncmp("hardware", (char *)value->u.buffer.value, minlen)) { if (!expression_allocate(&class->submatch, MDL)) return ISC_R_NOMEMORY; class->submatch->op = expr_hardware; } else return DHCP_R_INVALIDARG; } else return DHCP_R_INVALIDARG; return ISC_R_SUCCESS; } if (!omapi_ds_strcmp(name, "option")) { if (value->type == omapi_datatype_data || value->type == omapi_datatype_string) { /* XXXJAB support 'options' here. */ /* XXXJAB specifically 'bootfile-name' */ return DHCP_R_INVALIDARG; /* XXX tmp */ } else return DHCP_R_INVALIDARG; /* * Currently no way to get here, if we update the above * code so that we do get here this return needs to be * uncommented. * return ISC_R_SUCCESS; */ } /* Try to find some inner object that can take the value. */ if (h->inner && h->inner->type->set_value) { status = ((*(h->inner->type->set_value)) (h->inner, id, name, value)); if (status == ISC_R_SUCCESS || status == DHCP_R_UNCHANGED) return status; } return DHCP_R_UNKNOWNATTRIBUTE; } isc_result_t dhcp_class_set_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_typed_data_t *value) { if (h -> type != dhcp_type_class) return DHCP_R_INVALIDARG; return class_set_value(h, id, name, value); } isc_result_t dhcp_class_get_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_value_t **value) { struct class *class; isc_result_t status; if (h -> type != dhcp_type_class) return DHCP_R_INVALIDARG; class = (struct class *)h; if (!omapi_ds_strcmp (name, "name")) return omapi_make_string_value (value, name, class -> name, MDL); /* Try to find some inner object that can provide the value. */ if (h -> inner && h -> inner -> type -> get_value) { status = ((*(h -> inner -> type -> get_value)) (h -> inner, id, name, value)); if (status == ISC_R_SUCCESS) return status; } return DHCP_R_UNKNOWNATTRIBUTE; } isc_result_t dhcp_class_destroy (omapi_object_t *h, const char *file, int line) { if (h -> type != dhcp_type_class && h -> type != dhcp_type_subclass) return DHCP_R_INVALIDARG; struct class *class = (struct class *)h; if (class -> nic) class_dereference (&class -> nic, file, line); if (class -> superclass) class_dereference (&class -> superclass, file, line); if (class -> name) { dfree (class -> name, file, line); class -> name = (char *)0; } if (class -> billed_leases) { int i; for (i = 0; i < class -> lease_limit; i++) { if (class -> billed_leases [i]) { lease_dereference (&class -> billed_leases [i], file, line); } } dfree (class -> billed_leases, file, line); class -> billed_leases = (struct lease **)0; } if (class -> hash) { class_free_hash_table (&class -> hash, file, line); class -> hash = (class_hash_t *)0; } data_string_forget (&class -> hash_string, file, line); if (class -> expr) expression_dereference (&class -> expr, file, line); if (class -> submatch) expression_dereference (&class -> submatch, file, line); if (class -> group) group_dereference (&class -> group, file, line); if (class -> statements) executable_statement_dereference (&class -> statements, file, line); if (class -> superclass) class_dereference (&class -> superclass, file, line); return ISC_R_SUCCESS; } static isc_result_t class_signal_handler(omapi_object_t *h, const char *name, va_list ap) { struct class *class = (struct class *)h; isc_result_t status; int updatep = 0; int issubclass; issubclass = (h->type == dhcp_type_subclass); if (!strcmp (name, "updated")) { if (!issubclass) { if (class->name == 0 || strlen(class->name) == 0) { return DHCP_R_INVALIDARG; } } else { if (class->superclass == 0) { return DHCP_R_INVALIDARG; /* didn't give name */ } if (class->hash_string.data == NULL) { return DHCP_R_INVALIDARG; } } if (issubclass) { if (!class->superclass->hash) class_new_hash(&class->superclass->hash, SCLASS_HASH_SIZE, MDL); class_hash_add(class->superclass->hash, (const char *)class->hash_string.data, class->hash_string.len, (void *)class, MDL); } #ifdef DEBUG_OMAPI if (issubclass) { log_debug ("OMAPI added subclass %s", class->superclass->name); } else { log_debug ("OMAPI added class %s", class->name); } #endif status = enter_class (class, 1, 1); if (status != ISC_R_SUCCESS) return status; updatep = 1; } /* Try to find some inner object that can take the value. */ if (h->inner && h->inner->type->signal_handler) { status = ((*(h->inner->type->signal_handler)) (h->inner, name, ap)); if (status == ISC_R_SUCCESS) return status; } if (updatep) return ISC_R_SUCCESS; return ISC_R_NOTFOUND; } isc_result_t dhcp_class_signal_handler (omapi_object_t *h, const char *name, va_list ap) { if (h -> type != dhcp_type_class) return DHCP_R_INVALIDARG; return class_signal_handler(h, name, ap); } /* * Routine to put out generic class & subclass information */ isc_result_t class_stuff_values (omapi_object_t *c, omapi_object_t *id, omapi_object_t *h) { struct class *class; isc_result_t status; class = (struct class *)h; status = omapi_connection_put_named_uint32(c, "lease-limit", ((u_int32_t) class->lease_limit)); if (status != ISC_R_SUCCESS) return (status); status = omapi_connection_put_named_uint32(c, "leases-used", ((u_int32_t) class->leases_consumed)); if (status != ISC_R_SUCCESS) return (status); /* Write out the inner object, if any. */ if (h->inner && h->inner->type->stuff_values) { status = ((*(h->inner->type->stuff_values)) (c, id, h->inner)); if (status == ISC_R_SUCCESS) return (status); } return (ISC_R_SUCCESS); } isc_result_t dhcp_class_stuff_values (omapi_object_t *c, omapi_object_t *id, omapi_object_t *h) { if (h->type != dhcp_type_class) return (DHCP_R_INVALIDARG); /* add any class specific items here */ return (class_stuff_values(c, id, h)); } static isc_result_t class_lookup (omapi_object_t **lp, omapi_object_t *id, omapi_object_t *ref, omapi_object_type_t *typewanted) { omapi_value_t *nv = NULL; omapi_value_t *hv = NULL; isc_result_t status; struct class *class = 0; struct class *subclass = 0; *lp = NULL; if (ref == NULL) return (DHCP_R_NOKEYS); /* see if we have a name */ status = omapi_get_value_str(ref, id, "name", &nv); if (status == ISC_R_SUCCESS) { char *name = dmalloc(nv->value->u.buffer.len + 1, MDL); if (name == NULL) return (ISC_R_NOMEMORY); memcpy (name, nv->value->u.buffer.value, nv->value->u.buffer.len); omapi_value_dereference(&nv, MDL); find_class(&class, name, MDL); dfree(name, MDL); if (class == NULL) { return (ISC_R_NOTFOUND); } if (typewanted == dhcp_type_subclass) { status = omapi_get_value_str(ref, id, "hashstring", &hv); if (status != ISC_R_SUCCESS) { class_dereference(&class, MDL); return (DHCP_R_NOKEYS); } if (hv->value->type != omapi_datatype_data && hv->value->type != omapi_datatype_string) { class_dereference(&class, MDL); omapi_value_dereference(&hv, MDL); return (DHCP_R_NOKEYS); } class_hash_lookup(&subclass, class->hash, (const char *) hv->value->u.buffer.value, hv->value->u.buffer.len, MDL); omapi_value_dereference(&hv, MDL); class_dereference(&class, MDL); if (subclass == NULL) { return (ISC_R_NOTFOUND); } class_reference(&class, subclass, MDL); class_dereference(&subclass, MDL); } /* Don't return the object if the type is wrong. */ if (class->type != typewanted) { class_dereference(&class, MDL); return (DHCP_R_INVALIDARG); } if (class->flags & CLASS_DECL_DELETED) { class_dereference(&class, MDL); return (ISC_R_NOTFOUND); } omapi_object_reference(lp, (omapi_object_t *)class, MDL); class_dereference(&class, MDL); return (ISC_R_SUCCESS); } return (DHCP_R_NOKEYS); } isc_result_t dhcp_class_lookup (omapi_object_t **lp, omapi_object_t *id, omapi_object_t *ref) { return class_lookup(lp, id, ref, dhcp_type_class); } isc_result_t dhcp_class_create (omapi_object_t **lp, omapi_object_t *id) { struct class *cp = 0; isc_result_t status; status = class_allocate(&cp, MDL); if (status != ISC_R_SUCCESS) return (status); if (clone_group(&cp->group, root_group, MDL) == 0) return (ISC_R_NOMEMORY); cp->flags = CLASS_DECL_DYNAMIC; status = omapi_object_reference(lp, (omapi_object_t *)cp, MDL); class_dereference(&cp, MDL); return (status); } isc_result_t dhcp_class_remove (omapi_object_t *lp, omapi_object_t *id) { struct class *cp; if (lp -> type != dhcp_type_class) return DHCP_R_INVALIDARG; cp = (struct class *)lp; #ifdef DEBUG_OMAPI log_debug ("OMAPI delete class %s", cp -> name); #endif delete_class (cp, 1); return ISC_R_SUCCESS; } isc_result_t dhcp_subclass_set_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_typed_data_t *value) { if (h -> type != dhcp_type_subclass) return DHCP_R_INVALIDARG; return class_set_value(h, id, name, value); } isc_result_t dhcp_subclass_get_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_value_t **value) { struct class *subclass; isc_result_t status; if (h -> type != dhcp_type_class) return DHCP_R_INVALIDARG; subclass = (struct class *)h; if (subclass -> name != 0) return DHCP_R_INVALIDARG; /* XXXJAB No values to get yet. */ /* Try to find some inner object that can provide the value. */ if (h -> inner && h -> inner -> type -> get_value) { status = ((*(h -> inner -> type -> get_value)) (h -> inner, id, name, value)); if (status == ISC_R_SUCCESS) return status; } return DHCP_R_UNKNOWNATTRIBUTE; } isc_result_t dhcp_subclass_signal_handler (omapi_object_t *h, const char *name, va_list ap) { if (h -> type != dhcp_type_subclass) return DHCP_R_INVALIDARG; return class_signal_handler(h, name, ap); } isc_result_t dhcp_subclass_stuff_values (omapi_object_t *c, omapi_object_t *id, omapi_object_t *h) { struct class *subclass; if (h->type != dhcp_type_subclass) return (DHCP_R_INVALIDARG); subclass = (struct class *)h; if (subclass->name != 0) return (DHCP_R_INVALIDARG); /* add any subclass specific items here */ return (class_stuff_values(c, id, h)); } isc_result_t dhcp_subclass_lookup (omapi_object_t **lp, omapi_object_t *id, omapi_object_t *ref) { return class_lookup(lp, id, ref, dhcp_type_subclass); } isc_result_t dhcp_subclass_create (omapi_object_t **lp, omapi_object_t *id) { struct class *cp = 0; isc_result_t status; status = subclass_allocate(&cp, MDL); if (status != ISC_R_SUCCESS) return status; group_reference (&cp->group, root_group, MDL); cp->flags = CLASS_DECL_DYNAMIC; status = omapi_object_reference (lp, (omapi_object_t *)cp, MDL); subclass_dereference (&cp, MDL); return status; } isc_result_t dhcp_subclass_remove (omapi_object_t *lp, omapi_object_t *id) { struct class *cp; if (lp -> type != dhcp_type_subclass) return DHCP_R_INVALIDARG; cp = (struct class *)lp; #ifdef DEBUG_OMAPI log_debug ("OMAPI delete subclass %s", cp -> name); #endif delete_class (cp, 1); return ISC_R_SUCCESS; } isc_result_t binding_scope_set_value (struct binding_scope *scope, int createp, omapi_data_string_t *name, omapi_typed_data_t *value) { struct binding *bp; char *nname; struct binding_value *nv; nname = dmalloc (name -> len + 1, MDL); if (!nname) return ISC_R_NOMEMORY; memcpy (nname, name -> value, name -> len); nname [name -> len] = 0; bp = find_binding (scope, nname); if (!bp && !createp) { dfree (nname, MDL); return DHCP_R_UNKNOWNATTRIBUTE; } if (!value) { dfree (nname, MDL); if (!bp) return DHCP_R_UNKNOWNATTRIBUTE; binding_value_dereference (&bp -> value, MDL); return ISC_R_SUCCESS; } nv = (struct binding_value *)0; if (!binding_value_allocate (&nv, MDL)) { dfree (nname, MDL); return ISC_R_NOMEMORY; } switch (value -> type) { case omapi_datatype_int: nv -> type = binding_numeric; nv -> value.intval = value -> u.integer; break; case omapi_datatype_string: case omapi_datatype_data: if (!buffer_allocate (&nv -> value.data.buffer, value -> u.buffer.len, MDL)) { binding_value_dereference (&nv, MDL); dfree (nname, MDL); return ISC_R_NOMEMORY; } memcpy (&nv -> value.data.buffer -> data [1], value -> u.buffer.value, value -> u.buffer.len); nv -> value.data.len = value -> u.buffer.len; break; case omapi_datatype_object: binding_value_dereference (&nv, MDL); dfree (nname, MDL); return DHCP_R_INVALIDARG; } if (!bp) { bp = dmalloc (sizeof *bp, MDL); if (!bp) { binding_value_dereference (&nv, MDL); dfree (nname, MDL); return ISC_R_NOMEMORY; } memset (bp, 0, sizeof *bp); bp -> name = nname; bp -> next = scope -> bindings; scope -> bindings = bp; } else { if (bp -> value) binding_value_dereference (&bp -> value, MDL); dfree (nname, MDL); } binding_value_reference (&bp -> value, nv, MDL); binding_value_dereference (&nv, MDL); return ISC_R_SUCCESS; } isc_result_t binding_scope_get_value (omapi_value_t **value, struct binding_scope *scope, omapi_data_string_t *name) { struct binding *bp; omapi_typed_data_t *td; isc_result_t status; char *nname; nname = dmalloc (name -> len + 1, MDL); if (!nname) return ISC_R_NOMEMORY; memcpy (nname, name -> value, name -> len); nname [name -> len] = 0; bp = find_binding (scope, nname); dfree (nname, MDL); if (!bp) return DHCP_R_UNKNOWNATTRIBUTE; if (!bp -> value) return DHCP_R_UNKNOWNATTRIBUTE; switch (bp -> value -> type) { case binding_boolean: td = (omapi_typed_data_t *)0; status = omapi_typed_data_new (MDL, &td, omapi_datatype_int, bp -> value -> value.boolean); break; case binding_numeric: td = (omapi_typed_data_t *)0; status = omapi_typed_data_new (MDL, &td, omapi_datatype_int, (int) bp -> value -> value.intval); break; case binding_data: td = (omapi_typed_data_t *)0; status = omapi_typed_data_new (MDL, &td, omapi_datatype_data, bp -> value -> value.data.len); if (status != ISC_R_SUCCESS) return status; memcpy (&td -> u.buffer.value [0], bp -> value -> value.data.data, bp -> value -> value.data.len); break; /* Can't return values for these two (yet?). */ case binding_dns: case binding_function: return DHCP_R_INVALIDARG; default: log_fatal ("Impossible case at %s:%d.", MDL); return ISC_R_FAILURE; } if (status != ISC_R_SUCCESS) return status; status = omapi_value_new (value, MDL); if (status != ISC_R_SUCCESS) { omapi_typed_data_dereference (&td, MDL); return status; } omapi_data_string_reference (&(*value) -> name, name, MDL); omapi_typed_data_reference (&(*value) -> value, td, MDL); omapi_typed_data_dereference (&td, MDL); return ISC_R_SUCCESS; } isc_result_t binding_scope_stuff_values (omapi_object_t *c, struct binding_scope *scope) { struct binding *bp; unsigned len; isc_result_t status; for (bp = scope -> bindings; bp; bp = bp -> next) { if (bp -> value) { if (bp -> value -> type == binding_dns || bp -> value -> type == binding_function) continue; /* Stuff the name. */ len = strlen (bp -> name); status = omapi_connection_put_uint16 (c, len); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_copyin (c, (unsigned char *)bp -> name, len); if (status != ISC_R_SUCCESS) return status; switch (bp -> value -> type) { case binding_boolean: status = omapi_connection_put_uint32 (c, sizeof (u_int32_t)); if (status != ISC_R_SUCCESS) return status; status = (omapi_connection_put_uint32 (c, ((u_int32_t)(bp -> value -> value.boolean)))); if (status != ISC_R_SUCCESS) return status; break; case binding_data: status = (omapi_connection_put_uint32 (c, bp -> value -> value.data.len)); if (status != ISC_R_SUCCESS) return status; if (bp -> value -> value.data.len) { status = (omapi_connection_copyin (c, bp -> value -> value.data.data, bp -> value -> value.data.len)); if (status != ISC_R_SUCCESS) return status; } break; case binding_numeric: status = (omapi_connection_put_uint32 (c, sizeof (u_int32_t))); if (status != ISC_R_SUCCESS) return status; status = (omapi_connection_put_uint32 (c, ((u_int32_t) (bp -> value -> value.intval)))); if (status != ISC_R_SUCCESS) return status; break; /* NOTREACHED */ case binding_dns: case binding_function: break; } } } return ISC_R_SUCCESS; } /* vim: set tabstop=8: */ dhcp-4.4.1/server/salloc.c000644 000765 000024 00000014475 13243301226 015655 0ustar00tmarkstaff000000 000000 /* salloc.c Memory allocation for the DHCP server... */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1996-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #include #if defined (COMPACT_LEASES) struct lease *free_leases; #if defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) struct lease *lease_hunks; void relinquish_lease_hunks () { struct lease *c, *n, **p; int i; /* Account for all the leases on the free list. */ for (n = lease_hunks; n; n = n->next) { for (i = 1; i < n->starts + 1; i++) { p = &free_leases; for (c = free_leases; c; c = c->next) { if (c == &n[i]) { *p = c->next; n->ends++; break; } p = &c->next; } if (!c) { log_info("lease %s refcnt %d", piaddr (n[i].ip_addr), n[i].refcnt); #if defined (DEBUG_RC_HISTORY) dump_rc_history(&n[i]); #endif } } } for (c = lease_hunks; c; c = n) { n = c->next; if (c->ends != c->starts) { log_info("lease hunk %lx leases %ld free %ld", (unsigned long)c, (unsigned long)(c->starts), (unsigned long)(c->ends)); } dfree(c, MDL); } /* Free all the rogue leases. */ for (c = free_leases; c; c = n) { n = c->next; dfree(c, MDL); } } #endif struct lease *new_leases (n, file, line) unsigned n; const char *file; int line; { struct lease *rval; #if defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) rval = dmalloc ((n + 1) * sizeof (struct lease), file, line); if (rval != NULL) { memset (rval, 0, sizeof (struct lease)); rval -> starts = n; rval -> next = lease_hunks; lease_hunks = rval; rval++; } #else rval = dmalloc (n * sizeof (struct lease), file, line); #endif return rval; } /* If we are allocating leases in aggregations, there's really no way to free one, although perhaps we can maintain a free list. */ isc_result_t dhcp_lease_free (omapi_object_t *lo, const char *file, int line) { struct lease *lease; if (lo -> type != dhcp_type_lease) return DHCP_R_INVALIDARG; lease = (struct lease *)lo; memset (lease, 0, sizeof (struct lease)); lease -> next = free_leases; free_leases = lease; return ISC_R_SUCCESS; } isc_result_t dhcp_lease_get (omapi_object_t **lp, const char *file, int line) { struct lease **lease = (struct lease **)lp; struct lease *lt; if (free_leases) { lt = free_leases; free_leases = lt -> next; *lease = lt; return ISC_R_SUCCESS; } return ISC_R_NOMEMORY; } #endif /* COMPACT_LEASES */ OMAPI_OBJECT_ALLOC (lease, struct lease, dhcp_type_lease) OMAPI_OBJECT_ALLOC (class, struct class, dhcp_type_class) OMAPI_OBJECT_ALLOC (subclass, struct class, dhcp_type_subclass) OMAPI_OBJECT_ALLOC (pool, struct pool, dhcp_type_pool) #if !defined (NO_HOST_FREES) /* Scary debugging mode - don't enable! */ OMAPI_OBJECT_ALLOC (host, struct host_decl, dhcp_type_host) #else isc_result_t host_allocate (struct host_decl **p, const char *file, int line) { return omapi_object_allocate ((omapi_object_t **)p, dhcp_type_host, 0, file, line); } isc_result_t host_reference (struct host_decl **pptr, struct host_decl *ptr, const char *file, int line) { return omapi_object_reference ((omapi_object_t **)pptr, (omapi_object_t *)ptr, file, line); } isc_result_t host_dereference (struct host_decl **ptr, const char *file, int line) { if ((*ptr) -> refcnt == 1) { log_error ("host dereferenced with refcnt == 1."); #if defined (DEBUG_RC_HISTORY) dump_rc_history (); #endif abort (); } return omapi_object_dereference ((omapi_object_t **)ptr, file, line); } #endif struct lease_state *free_lease_states; struct lease_state *new_lease_state (file, line) const char *file; int line; { struct lease_state *rval; if (free_lease_states) { rval = free_lease_states; free_lease_states = (struct lease_state *)(free_lease_states -> next); dmalloc_reuse (rval, file, line, 0); } else { rval = dmalloc (sizeof (struct lease_state), file, line); if (!rval) return rval; } memset (rval, 0, sizeof *rval); if (!option_state_allocate (&rval -> options, file, line)) { free_lease_state (rval, file, line); return (struct lease_state *)0; } return rval; } void free_lease_state (ptr, file, line) struct lease_state *ptr; const char *file; int line; { if (ptr -> options) option_state_dereference (&ptr -> options, file, line); if (ptr -> packet) packet_dereference (&ptr -> packet, file, line); if (ptr -> shared_network) shared_network_dereference (&ptr -> shared_network, file, line); data_string_forget (&ptr -> parameter_request_list, file, line); data_string_forget (&ptr -> filename, file, line); data_string_forget (&ptr -> server_name, file, line); ptr -> next = free_lease_states; free_lease_states = ptr; dmalloc_reuse (free_lease_states, (char *)0, 0, 0); } #if defined (DEBUG_MEMORY_LEAKAGE) || \ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) void relinquish_free_lease_states () { struct lease_state *cs, *ns; for (cs = free_lease_states; cs; cs = ns) { ns = cs -> next; dfree (cs, MDL); } free_lease_states = (struct lease_state *)0; } #endif struct permit *new_permit (file, line) const char *file; int line; { struct permit *permit = ((struct permit *) dmalloc (sizeof (struct permit), file, line)); if (!permit) return permit; memset (permit, 0, sizeof *permit); return permit; } void free_permit (permit, file, line) struct permit *permit; const char *file; int line; { if (permit -> type == permit_class) class_dereference (&permit -> class, MDL); dfree (permit, file, line); } dhcp-4.4.1/server/stables.c000644 000765 000024 00000050664 13243301226 016035 0ustar00tmarkstaff000000 000000 /* stables.c Tables of information only used by server... */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1995-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #include #if defined (FAILOVER_PROTOCOL) /* This is used to indicate some kind of failure when generating a failover option. */ failover_option_t null_failover_option = { 0, 0 }; failover_option_t skip_failover_option = { 0, 0 }; /* Information about failover options, for printing, encoding and decoding. */ struct failover_option_info ft_options [] = { { 0, "unused", FT_UNDEF, 0, 0, 0 }, { FTO_ADDRESSES_TRANSFERRED, "addresses-transferred", FT_UINT32, 1, FM_OFFSET(addresses_transferred), FTB_ADDRESSES_TRANSFERRED }, { FTO_ASSIGNED_IP_ADDRESS, "assigned-IP-address", FT_IPADDR, 1, FM_OFFSET(assigned_addr), FTB_ASSIGNED_IP_ADDRESS }, { FTO_BINDING_STATUS, "binding-status", FT_UINT8, 1, FM_OFFSET(binding_status), FTB_BINDING_STATUS }, { FTO_CLIENT_IDENTIFIER, "client-identifier", FT_BYTES, 0, FM_OFFSET(client_identifier), FTB_CLIENT_IDENTIFIER }, { FTO_CHADDR, "client-hardware-address", FT_BYTES, 0, FM_OFFSET(chaddr), FTB_CHADDR }, { FTO_CLTT, "client-last-transaction-time", FT_UINT32, 1, FM_OFFSET(cltt), FTB_CLTT }, { FTO_REPLY_OPTIONS, "client-reply-options", FT_BYTES, 0, FM_OFFSET(reply_options), FTB_REPLY_OPTIONS }, { FTO_REQUEST_OPTIONS, "client-request-options", FT_BYTES, 0, FM_OFFSET(request_options), FTB_REQUEST_OPTIONS }, { FTO_DDNS, "DDNS", FT_DDNS, 1, FM_OFFSET(ddns), FTB_DDNS }, { FTO_DELAYED_SERVICE, "delayed-service", FT_UINT8, 1, FM_OFFSET(delayed_service), FTB_DELAYED_SERVICE }, { FTO_HBA, "hash-bucket-assignment", FT_BYTES, 0, FM_OFFSET(hba), FTB_HBA }, { FTO_IP_FLAGS, "IP-flags", FT_UINT16, 1, FM_OFFSET(ip_flags), FTB_IP_FLAGS }, { FTO_LEASE_EXPIRY, "lease-expiration-time", FT_UINT32, 1, FM_OFFSET(expiry), FTB_LEASE_EXPIRY }, { FTO_MAX_UNACKED, "max-unacked-bndupd", FT_UINT32, 1, FM_OFFSET(max_unacked), FTB_MAX_UNACKED }, { FTO_MCLT, "MCLT", FT_UINT32, 1, FM_OFFSET(mclt), FTB_MCLT }, { FTO_MESSAGE, "message", FT_TEXT, 0, FM_OFFSET(message), FTB_MESSAGE }, { FTO_MESSAGE_DIGEST, "message-digest", FT_BYTES, 0, FM_OFFSET(message_digest), FTB_MESSAGE_DIGEST }, { FTO_POTENTIAL_EXPIRY, "potential-expiration-time", FT_UINT32, 1, FM_OFFSET(potential_expiry), FTB_POTENTIAL_EXPIRY }, { FTO_RECEIVE_TIMER, "receive-timer", FT_UINT32, 1, FM_OFFSET(receive_timer), FTB_RECEIVE_TIMER }, { FTO_PROTOCOL_VERSION, "protocol-version", FT_UINT8, 1, FM_OFFSET(protocol_version), FTB_PROTOCOL_VERSION }, { FTO_REJECT_REASON, "reject-reason", FT_UINT8, 1, FM_OFFSET(reject_reason), FTB_REJECT_REASON }, { FTO_RELATIONSHIP_NAME, "relationship-name", FT_BYTES, 0, FM_OFFSET(relationship_name), FTB_RELATIONSHIP_NAME }, { FTO_SERVER_FLAGS, "server-flags", FT_UINT8, 1, FM_OFFSET(server_flags), FTB_SERVER_FLAGS }, { FTO_SERVER_STATE, "server-state", FT_UINT8, 1, FM_OFFSET(server_state), FTB_SERVER_STATE }, { FTO_STOS, "start-time-of-state", FT_UINT32, 1, FM_OFFSET(stos), FTB_STOS }, { FTO_TLS_REPLY, "TLS-reply", FT_UINT8, 1, FM_OFFSET(tls_reply), FTB_TLS_REPLY }, { FTO_TLS_REQUEST, "TLS-request", FT_UINT8, 1, FM_OFFSET(tls_request), FTB_TLS_REQUEST }, { FTO_VENDOR_CLASS, "vendor-class-identifier", FT_BYTES, 0, FM_OFFSET(vendor_class), FTB_VENDOR_CLASS }, { FTO_VENDOR_OPTIONS, "vendor-specific-options", FT_BYTES, 0, FM_OFFSET(vendor_options), FTB_VENDOR_OPTIONS } }; /* These are really options that make sense for a particular request - if some other option comes in, we're not going to use it, so we can just discard it. Note that the message-digest option is allowed for all message types, but is not saved - it's just used to validate the message and then discarded - so it's not mentioned here. */ u_int32_t fto_allowed [] = { 0, /* 0 unused */ 0, /* 1 POOLREQ */ FTB_ADDRESSES_TRANSFERRED, /* 2 POOLRESP */ (FTB_ASSIGNED_IP_ADDRESS | FTB_BINDING_STATUS | FTB_CLIENT_IDENTIFIER | FTB_CHADDR | FTB_DDNS | FTB_IP_FLAGS | FTB_LEASE_EXPIRY | FTB_POTENTIAL_EXPIRY | FTB_STOS | FTB_CLTT | FTB_REQUEST_OPTIONS | FTB_REPLY_OPTIONS), /* 3 BNDUPD */ (FTB_ASSIGNED_IP_ADDRESS | FTB_BINDING_STATUS | FTB_CLIENT_IDENTIFIER | FTB_CHADDR | FTB_DDNS | FTB_IP_FLAGS | FTB_LEASE_EXPIRY | FTB_POTENTIAL_EXPIRY | FTB_STOS | FTB_CLTT | FTB_REQUEST_OPTIONS | FTB_REPLY_OPTIONS | FTB_REJECT_REASON | FTB_MESSAGE), /* 4 BNDACK */ (FTB_RELATIONSHIP_NAME | FTB_MAX_UNACKED | FTB_RECEIVE_TIMER | FTB_VENDOR_CLASS | FTB_PROTOCOL_VERSION | FTB_TLS_REQUEST | FTB_MCLT | FTB_HBA), /* 5 CONNECT */ (FTB_RELATIONSHIP_NAME | FTB_MAX_UNACKED | FTB_RECEIVE_TIMER | FTB_VENDOR_CLASS | FTB_PROTOCOL_VERSION | FTB_TLS_REPLY | FTB_REJECT_REASON | FTB_MESSAGE), /* CONNECTACK */ 0, /* 7 UPDREQALL */ 0, /* 8 UPDDONE */ 0, /* 9 UPDREQ */ (FTB_SERVER_STATE | FTB_SERVER_FLAGS | FTB_STOS), /* 10 STATE */ 0, /* 11 CONTACT */ (FTB_REJECT_REASON | FTB_MESSAGE) /* 12 DISCONNECT */ }; /* Sizes of the various types. */ int ft_sizes [] = { 1, /* FT_UINT8 */ 4, /* FT_IPADDR */ 4, /* FT_UINT32 */ 1, /* FT_BYTES */ 1, /* FT_TEXT_OR_BYTES */ 0, /* FT_DDNS */ 0, /* FT_DDNS1 */ 2, /* FT_UINT16 */ 1, /* FT_TEXT */ 0, /* FT_UNDEF */ 0, /* FT_DIGEST */ }; /* Names of the various failover link states. */ const char *dhcp_flink_state_names [] = { "invalid state 0", "startup", "message length wait", "message wait", "disconnected" }; #endif /* FAILOVER_PROTOCOL */ /* Failover binding state names. These are used even if there is no failover protocol support. */ const char *binding_state_names [] = { "free", "active", "expired", "released", "abandoned", "reset", "backup" }; struct universe agent_universe; static struct option agent_options[] = { { "circuit-id", "X", &agent_universe, 1, 1 }, { "remote-id", "X", &agent_universe, 2, 1 }, { "agent-id", "I", &agent_universe, 3, 1 }, { "DOCSIS-device-class", "L", &agent_universe, 4, 1 }, { "link-selection", "I", &agent_universe, 5, 1 }, { "relay-port", "Z", &agent_universe, 19, 1 }, { NULL, NULL, NULL, 0, 0 } }; struct universe server_universe; static struct option server_options[] = { { "default-lease-time", "T", &server_universe, 1, 1 }, { "max-lease-time", "T", &server_universe, 2, 1 }, { "min-lease-time", "T", &server_universe, 3, 1 }, { "dynamic-bootp-lease-cutoff", "T", &server_universe, 4, 1 }, { "dynamic-bootp-lease-length", "L", &server_universe, 5, 1 }, { "boot-unknown-clients", "f", &server_universe, 6, 1 }, { "dynamic-bootp", "f", &server_universe, 7, 1 }, { "allow-bootp", "f", &server_universe, 8, 1 }, { "allow-booting", "f", &server_universe, 9, 1 }, { "one-lease-per-client", "f", &server_universe, 10, 1 }, { "get-lease-hostnames", "f", &server_universe, 11, 1 }, { "use-host-decl-names", "f", &server_universe, 12, 1 }, { "use-lease-addr-for-default-route", "f", &server_universe, 13, 1 }, { "min-secs", "B", &server_universe, 14, 1 }, { "filename", "t", &server_universe, 15, 1 }, { "server-name", "t", &server_universe, 16, 1 }, { "next-server", "I", &server_universe, 17, 1 }, { "authoritative", "f", &server_universe, 18, 1 }, { "vendor-option-space", "U", &server_universe, 19, 1 }, { "always-reply-rfc1048", "f", &server_universe, 20, 1 }, { "site-option-space", "X", &server_universe, 21, 1 }, { "always-broadcast", "f", &server_universe, 22, 1 }, { "ddns-domainname", "t", &server_universe, 23, 1 }, { "ddns-hostname", "t", &server_universe, 24, 1 }, { "ddns-rev-domainname", "t", &server_universe, 25, 1 }, { "lease-file-name", "t", &server_universe, 26, 1 }, { "pid-file-name", "t", &server_universe, 27, 1 }, { "duplicates", "f", &server_universe, 28, 1 }, { "declines", "f", &server_universe, 29, 1 }, { "ddns-updates", "f", &server_universe, 30, 1 }, { "omapi-port", "S", &server_universe, 31, 1 }, { "local-port", "S", &server_universe, 32, 1 }, { "limited-broadcast-address", "I", &server_universe, 33, 1 }, { "remote-port", "S", &server_universe, 34, 1 }, { "local-address", "I", &server_universe, 35, 1 }, { "omapi-key", "d", &server_universe, 36, 1 }, { "stash-agent-options", "f", &server_universe, 37, 1 }, { "ddns-ttl", "T", &server_universe, 38, 1 }, { "ddns-update-style", "Nddns-styles.", &server_universe, 39, 1 }, { "client-updates", "f", &server_universe, 40, 1 }, { "update-optimization", "f", &server_universe, 41, 1 }, { "ping-check", "f", &server_universe, 42, 1 }, { "update-static-leases", "f", &server_universe, 43, 1 }, { "log-facility", "Nsyslog-facilities.", &server_universe, 44, 1 }, { "do-forward-updates", "f", &server_universe, 45, 1 }, { "ping-timeout", "T", &server_universe, 46, 1 }, { "infinite-is-reserved", "f", &server_universe, 47, 1 }, { "update-conflict-detection", "f", &server_universe, 48, 1 }, { "leasequery", "f", &server_universe, 49, 1 }, { "adaptive-lease-time-threshold", "B", &server_universe, 50, 1 }, { "do-reverse-updates", "f", &server_universe, 51, 1 }, { "fqdn-reply", "f", &server_universe, 52, 1 }, { "preferred-lifetime", "T", &server_universe, 53, 1 }, { "dhcpv6-lease-file-name", "t", &server_universe, 54, 1 }, { "dhcpv6-pid-file-name", "t", &server_universe, 55, 1 }, { "limit-addrs-per-ia", "L", &server_universe, 56, 1 }, { "limit-prefs-per-ia", "L", &server_universe, 57, 1 }, /* Assert a configuration parsing error if delayed-ack isn't compiled in. */ #if defined(DELAYED_ACK) { "delayed-ack", "S", &server_universe, 58, 1 }, { "max-ack-delay", "L", &server_universe, 59, 1 }, #endif #if defined(LDAP_CONFIGURATION) { "ldap-server", "t", &server_universe, 60, 1 }, { "ldap-port", "d", &server_universe, 61, 1 }, { "ldap-username", "t", &server_universe, 62, 1 }, { "ldap-password", "t", &server_universe, 63, 1 }, { "ldap-base-dn", "t", &server_universe, 64, 1 }, { "ldap-method", "Nldap-methods.", &server_universe, 65, 1 }, { "ldap-debug-file", "t", &server_universe, 66, 1 }, { "ldap-dhcp-server-cn", "t", &server_universe, 67, 1 }, { "ldap-referrals", "f", &server_universe, 68, 1 }, #if defined(LDAP_USE_SSL) { "ldap-ssl", "Nldap-ssl-usage.", &server_universe, 69, 1 }, { "ldap-tls-reqcert", "Nldap-tls-reqcert.", &server_universe, 70, 1 }, { "ldap-tls-ca-file", "t", &server_universe, 71, 1 }, { "ldap-tls-ca-dir", "t", &server_universe, 72, 1 }, { "ldap-tls-cert", "t", &server_universe, 73, 1 }, { "ldap-tls-key", "t", &server_universe, 74, 1 }, { "ldap-tls-crlcheck", "Nldap-tls-crlcheck.", &server_universe, 75, 1 }, { "ldap-tls-ciphers", "t", &server_universe, 76, 1 }, { "ldap-tls-randfile", "t", &server_universe, 77, 1 }, { "ldap-init-retry", "d", &server_universe, SV_LDAP_INIT_RETRY, 1 }, #endif /* LDAP_USE_SSL */ #if defined(LDAP_USE_GSSAPI) { "ldap-gssapi-keytab", "t", &server_universe, SV_LDAP_GSSAPI_KEYTAB, 1}, { "ldap-gssapi-principal", "t", &server_universe, SV_LDAP_GSSAPI_PRINCIPAL, 1}, #endif /* LDAP_USE_GSSAPI */ #endif /* LDAP_CONFIGURATION */ { "dhcp-cache-threshold", "B", &server_universe, 78, 1 }, { "dont-use-fsync", "f", &server_universe, 79, 1 }, { "ddns-local-address4", "I", &server_universe, 80, 1 }, { "ddns-local-address6", "6", &server_universe, 81, 1 }, { "ignore-client-uids", "f", &server_universe, 82, 1 }, { "log-threshold-low", "B", &server_universe, 83, 1 }, { "log-threshold-high", "B", &server_universe, 84, 1 }, { "echo-client-id", "f", &server_universe, SV_ECHO_CLIENT_ID, 1 }, { "server-id-check", "f", &server_universe, SV_SERVER_ID_CHECK, 1 }, { "prefix-length-mode", "Nprefix_length_modes.", &server_universe, SV_PREFIX_LEN_MODE, 1 }, { "dhcpv6-set-tee-times", "f", &server_universe, SV_DHCPV6_SET_TEE_TIMES, 1 }, { "abandon-lease-time", "T", &server_universe, SV_ABANDON_LEASE_TIME, 1 }, #ifdef EUI_64 { "use-eui-64", "f", &server_universe, SV_USE_EUI_64, 1 }, { "persist-eui-64-leases", "f", &server_universe, SV_PERSIST_EUI_64_LEASES, 1 }, #endif #if defined (FAILOVER_PROTOCOL) { "check-secs-byte-order", "f", &server_universe, SV_CHECK_SECS_BYTE_ORDER, 1 }, #endif { "ddns-dual-stack-mixed-mode", "f", &server_universe, SV_DDNS_DUAL_STACK_MIXED_MODE, 1 }, { "ddns-guard-id-must-match", "f", &server_universe, SV_DDNS_GUARD_ID_MUST_MATCH, 1 }, { "ddns-other-guard-is-dynamic", "f", &server_universe, SV_DDNS_OTHER_GUARD_IS_DYNAMIC, 1 }, { "release-on-roam", "f", &server_universe, SV_RELEASE_ON_ROAM, 1 }, { "local-address6", "6", &server_universe, SV_LOCAL_ADDRESS6, 1 }, { "bind-local-address6", "f", &server_universe, SV_BIND_LOCAL_ADDRESS6, 1 }, { NULL, NULL, NULL, 0, 0 } }; #if defined(LDAP_CONFIGURATION) struct enumeration_value ldap_values [] = { { "static", LDAP_METHOD_STATIC }, { "dynamic", LDAP_METHOD_DYNAMIC }, { (char *) 0, 0 } }; struct enumeration ldap_methods = { (struct enumeration *)0, "ldap-methods", 1, ldap_values }; #if defined(LDAP_USE_SSL) struct enumeration_value ldap_ssl_usage_values [] = { { "off", LDAP_SSL_OFF }, { "on",LDAP_SSL_ON }, { "ldaps", LDAP_SSL_LDAPS }, { "start_tls", LDAP_SSL_TLS }, { (char *) 0, 0 } }; struct enumeration ldap_ssl_usage_enum = { (struct enumeration *)0, "ldap-ssl-usage", 1, ldap_ssl_usage_values }; struct enumeration_value ldap_tls_reqcert_values [] = { { "never", LDAP_OPT_X_TLS_NEVER }, { "hard", LDAP_OPT_X_TLS_HARD }, { "demand", LDAP_OPT_X_TLS_DEMAND}, { "allow", LDAP_OPT_X_TLS_ALLOW }, { "try", LDAP_OPT_X_TLS_TRY }, { (char *) 0, 0 } }; struct enumeration ldap_tls_reqcert_enum = { (struct enumeration *)0, "ldap-tls-reqcert", 1, ldap_tls_reqcert_values }; struct enumeration_value ldap_tls_crlcheck_values [] = { { "none", LDAP_OPT_X_TLS_CRL_NONE}, { "peer", LDAP_OPT_X_TLS_CRL_PEER}, { "all", LDAP_OPT_X_TLS_CRL_ALL }, { (char *) 0, 0 } }; struct enumeration ldap_tls_crlcheck_enum = { (struct enumeration *)0, "ldap-tls-crlcheck", 1, ldap_tls_crlcheck_values }; #endif #endif struct enumeration_value ddns_styles_values [] = { { "none", 0 }, { "ad-hoc", 1 }, { "interim", 2 }, { "standard", 3 }, { (char *)0, 0 } }; struct enumeration ddns_styles = { (struct enumeration *)0, "ddns-styles", 1, ddns_styles_values }; struct enumeration_value prefix_length_modes_values[] = { { "ignore", PLM_IGNORE }, { "prefer", PLM_PREFER }, { "exact", PLM_EXACT }, { "minimum", PLM_MINIMUM }, { "maximum", PLM_MAXIMUM }, { (char *)0, 0 } }; struct enumeration prefix_length_modes = { (struct enumeration *)0, "prefix_length_modes", 1, prefix_length_modes_values }; struct enumeration_value syslog_values [] = { #if defined (LOG_KERN) { "kern", LOG_KERN }, #endif #if defined (LOG_USER) { "user", LOG_USER }, #endif #if defined (LOG_MAIL) { "mail", LOG_MAIL }, #endif #if defined (LOG_DAEMON) { "daemon", LOG_DAEMON }, #endif #if defined (LOG_AUTH) { "auth", LOG_AUTH }, #endif #if defined (LOG_SYSLOG) { "syslog", LOG_SYSLOG }, #endif #if defined (LOG_LPR) { "lpr", LOG_LPR }, #endif #if defined (LOG_NEWS) { "news", LOG_NEWS }, #endif #if defined (LOG_UUCP) { "uucp", LOG_UUCP }, #endif #if defined (LOG_CRON) { "cron", LOG_CRON }, #endif #if defined (LOG_AUTHPRIV) { "authpriv", LOG_AUTHPRIV }, #endif #if defined (LOG_FTP) { "ftp", LOG_FTP }, #endif #if defined (LOG_LOCAL0) { "local0", LOG_LOCAL0 }, #endif #if defined (LOG_LOCAL1) { "local1", LOG_LOCAL1 }, #endif #if defined (LOG_LOCAL2) { "local2", LOG_LOCAL2 }, #endif #if defined (LOG_LOCAL3) { "local3", LOG_LOCAL3 }, #endif #if defined (LOG_LOCAL4) { "local4", LOG_LOCAL4 }, #endif #if defined (LOG_LOCAL5) { "local5", LOG_LOCAL5 }, #endif #if defined (LOG_LOCAL6) { "local6", LOG_LOCAL6 }, #endif #if defined (LOG_LOCAL7) { "local7", LOG_LOCAL7 }, #endif { (char *)0, 0 } }; struct enumeration syslog_enum = { (struct enumeration *)0, "syslog-facilities", 1, syslog_values }; void initialize_server_option_spaces() { int i; unsigned code; /* Set up the Relay Agent Information Option suboption space... */ agent_universe.name = "agent"; agent_universe.concat_duplicates = 0; agent_universe.option_state_dereference = linked_option_state_dereference; agent_universe.lookup_func = lookup_linked_option; agent_universe.save_func = save_linked_option; agent_universe.delete_func = delete_linked_option; agent_universe.encapsulate = linked_option_space_encapsulate; agent_universe.foreach = linked_option_space_foreach; agent_universe.decode = parse_option_buffer; agent_universe.index = universe_count++; agent_universe.length_size = 1; agent_universe.tag_size = 1; agent_universe.get_tag = getUChar; agent_universe.store_tag = putUChar; agent_universe.get_length = getUChar; agent_universe.store_length = putUChar; agent_universe.site_code_min = 0; agent_universe.end = 0; universes [agent_universe.index] = &agent_universe; if (!option_name_new_hash(&agent_universe.name_hash, AGENT_HASH_SIZE, MDL) || !option_code_new_hash(&agent_universe.code_hash, AGENT_HASH_SIZE, MDL)) log_fatal ("Can't allocate agent option hash table."); for (i = 0 ; agent_options[i].name ; i++) { option_code_hash_add(agent_universe.code_hash, &agent_options[i].code, 0, &agent_options[i], MDL); option_name_hash_add(agent_universe.name_hash, agent_options[i].name, 0, &agent_options[i], MDL); } #if defined(REPORT_HASH_PERFORMANCE) log_info("Relay Agent name hash: %s", option_name_hash_report(agent_universe.name_hash)); log_info("Relay Agent code hash: %s", option_code_hash_report(agent_universe.code_hash)); #endif code = DHO_DHCP_AGENT_OPTIONS; option_code_hash_lookup(&agent_universe.enc_opt, dhcp_universe.code_hash, &code, 0, MDL); /* Set up the server option universe... */ server_universe.name = "server"; server_universe.concat_duplicates = 0; server_universe.lookup_func = lookup_hashed_option; server_universe.option_state_dereference = hashed_option_state_dereference; server_universe.save_func = save_hashed_option; server_universe.delete_func = delete_hashed_option; server_universe.encapsulate = hashed_option_space_encapsulate; server_universe.foreach = hashed_option_space_foreach; server_universe.length_size = 1; /* Never used ... */ server_universe.tag_size = 4; server_universe.store_tag = putUChar; server_universe.store_length = putUChar; server_universe.site_code_min = 0; server_universe.end = 0; server_universe.index = universe_count++; universes [server_universe.index] = &server_universe; if (!option_name_new_hash(&server_universe.name_hash, SERVER_HASH_SIZE, MDL) || !option_code_new_hash(&server_universe.code_hash, SERVER_HASH_SIZE, MDL)) log_fatal ("Can't allocate server option hash table."); for (i = 0 ; server_options[i].name ; i++) { option_code_hash_add(server_universe.code_hash, &server_options[i].code, 0, &server_options[i], MDL); option_name_hash_add(server_universe.name_hash, server_options[i].name, 0, &server_options[i], MDL); } #if defined(REPORT_HASH_PERFORMANCE) log_info("Server-Config Option name hash: %s", option_name_hash_report(server_universe.name_hash)); log_info("Server-Config Option code hash: %s", option_code_hash_report(server_universe.code_hash)); #endif /* Add the server and agent option spaces to the option space hash. */ universe_hash_add (universe_hash, agent_universe.name, 0, &agent_universe, MDL); universe_hash_add (universe_hash, server_universe.name, 0, &server_universe, MDL); /* Make the server universe the configuration option universe. */ config_universe = &server_universe; code = SV_VENDOR_OPTION_SPACE; option_code_hash_lookup(&vendor_cfg_option, server_universe.code_hash, &code, 0, MDL); } dhcp-4.4.1/server/tests/000755 000765 000024 00000000000 13243313034 015363 5ustar00tmarkstaff000000 000000 dhcp-4.4.1/server/tests/Atffile000644 000765 000024 00000000145 13243301226 016660 0ustar00tmarkstaff000000 000000 Content-Type: application/X-atf-atffile; version="1" prop: test-suite = dhcp4 tp-glob: *_unittests dhcp-4.4.1/server/tests/hash_unittest.c000644 000765 000024 00000056554 13243301226 020430 0ustar00tmarkstaff000000 000000 /* * Copyright (c) 2012-2017 by 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "config.h" #include #include #include "dhcpd.h" /* * The following structures are kept here for reference only. As hash functions * are somewhat convoluted, they are copied here for the reference. Original * location is specified. Keep in mind that it may change over time: * * copied from server/omapi.c:49 * * omapi_object_type_t *dhcp_type_lease; * omapi_object_type_t *dhcp_type_pool; * omapi_object_type_t *dhcp_type_class; * omapi_object_type_t *dhcp_type_subclass; * omapi_object_type_t *dhcp_type_host; * * copied from server/salloc.c:138 * OMAPI_OBJECT_ALLOC (lease, struct lease, dhcp_type_lease) * OMAPI_OBJECT_ALLOC (class, struct class, dhcp_type_class) * OMAPI_OBJECT_ALLOC (subclass, struct class, dhcp_type_subclass) * OMAPI_OBJECT_ALLOC (pool, struct pool, dhcp_type_pool) * OMAPI_OBJECT_ALLOC (host, struct host_decl, dhcp_type_host) * * copied from server/mdb.c:2686 * HASH_FUNCTIONS(lease_ip, const unsigned char *, struct lease, lease_ip_hash_t, * lease_reference, lease_dereference, do_ip4_hash) * HASH_FUNCTIONS(lease_id, const unsigned char *, struct lease, lease_id_hash_t, * lease_reference, lease_dereference, do_id_hash) * HASH_FUNCTIONS (host, const unsigned char *, struct host_decl, host_hash_t, * host_reference, host_dereference, do_string_hash) * HASH_FUNCTIONS (class, const char *, struct class, class_hash_t, * class_reference, class_dereference, do_string_hash) * * copied from server/mdb.c:46 * host_hash_t *host_hw_addr_hash; * host_hash_t *host_uid_hash; * host_hash_t *host_name_hash; * lease_id_hash_t *lease_uid_hash; * lease_ip_hash_t *lease_ip_addr_hash; * lease_id_hash_t *lease_hw_addr_hash; */ /** * @brief sets client-id field in host declaration * * @param host pointer to host declaration * @param uid pointer to client-id data * @param uid_len length of the client-id data * * @return 1 if successful, 0 otherwise */ int lease_set_clientid(struct host_decl *host, const unsigned char *uid, int uid_len) { /* clean-up this mess and set client-identifier in a sane way */ int real_len = uid_len; if (uid_len == 0) { real_len = strlen((const char *)uid) + 1; } memset(&host->client_identifier, 0, sizeof(host->client_identifier)); host->client_identifier.len = uid_len; if (!buffer_allocate(&host->client_identifier.buffer, real_len, MDL)) { return 0; } host->client_identifier.data = host->client_identifier.buffer->data; memcpy((char *)host->client_identifier.data, uid, real_len); return 1; } /// @brief executes uid hash test for specified client-ids (2 hosts) /// /// Creates two host structures, adds first host to the uid hash, /// then adds second host to the hash, then removes first host, /// then removed the second. Many checks are performed during all /// operations. /// /// @param clientid1 client-id of the first host /// @param clientid1_len client-id1 length (may be 0 for strings) /// @param clientid2 client-id of the second host /// @param clientid2_len client-id2 length (may be 0 for strings) void lease_hash_test_2hosts(unsigned char clientid1[], size_t clientid1_len, unsigned char clientid2[], size_t clientid2_len) { printf("Checking hash operation for 2 hosts: clientid1-len=%lu" "clientid2-len=%lu\n", (unsigned long) clientid1_len, (unsigned long) clientid2_len); dhcp_db_objects_setup (); dhcp_common_objects_setup (); /* check that there is actually zero hosts in the hash */ /* @todo: host_hash_for_each() */ struct host_decl *host1 = 0, *host2 = 0; struct host_decl *check = 0; /* === step 1: allocate hosts === */ ATF_CHECK_MSG(host_allocate(&host1, MDL) == ISC_R_SUCCESS, "Failed to allocate host"); ATF_CHECK_MSG(host_allocate(&host2, MDL) == ISC_R_SUCCESS, "Failed to allocate host"); ATF_CHECK_MSG(host_new_hash(&host_uid_hash, HOST_HASH_SIZE, MDL) != 0, "Unable to create new hash"); ATF_CHECK_MSG(buffer_allocate(&host1->client_identifier.buffer, clientid1_len, MDL) != 0, "Can't allocate uid buffer for host1"); ATF_CHECK_MSG(buffer_allocate(&host2->client_identifier.buffer, clientid2_len, MDL) != 0, "Can't allocate uid buffer for host2"); /* setting up host1->client_identifier is actually not needed */ /* ATF_CHECK_MSG(lease_set_clientid(host1, clientid1, actual1_len) != 0, "Failed to set client-id for host1"); ATF_CHECK_MSG(lease_set_clientid(host2, clientid2, actual2_len) != 0, "Failed to set client-id for host2"); */ ATF_CHECK_MSG(host1->refcnt == 1, "Invalid refcnt for host1"); ATF_CHECK_MSG(host2->refcnt == 1, "Invalid refcnt for host2"); /* verify that our hosts are not in the hash yet */ ATF_CHECK_MSG(host_hash_lookup(&check, host_uid_hash, clientid1, clientid1_len, MDL) == 0, "Host1 is not supposed to be in the uid_hash."); ATF_CHECK_MSG(!check, "Host1 is not supposed to be in the uid_hash."); ATF_CHECK_MSG(host_hash_lookup(&check, host_uid_hash, clientid2, clientid2_len, MDL) == 0, "Host2 is not supposed to be in the uid_hash."); ATF_CHECK_MSG(!check, "Host2 is not supposed to be in the uid_hash."); /* === step 2: add first host to the hash === */ host_hash_add(host_uid_hash, clientid1, clientid1_len, host1, MDL); /* 2 pointers expected: ours (host1) and the one stored in hash */ ATF_CHECK_MSG(host1->refcnt == 2, "Invalid refcnt for host1"); /* 1 pointer expected: just ours (host2) */ ATF_CHECK_MSG(host2->refcnt == 1, "Invalid refcnt for host2"); /* verify that host1 is really in the hash and the we can find it */ ATF_CHECK_MSG(host_hash_lookup(&check, host_uid_hash, clientid1, clientid1_len, MDL), "Host1 was supposed to be in the uid_hash."); ATF_CHECK_MSG(check, "Host1 was supposed to be in the uid_hash."); /* Hey! That's not the host we were looking for! */ ATF_CHECK_MSG(check == host1, "Wrong host returned by host_hash_lookup"); /* 3 pointers: host1, (stored in hash), check */ ATF_CHECK_MSG(host1->refcnt == 3, "Invalid refcnt for host1"); /* reference count should be increased because we not have a pointer */ host_dereference(&check, MDL); /* we don't need it now */ ATF_CHECK_MSG(check == NULL, "check pointer is supposed to be NULL"); /* 2 pointers: host1, (stored in hash) */ ATF_CHECK_MSG(host1->refcnt == 2, "Invalid refcnt for host1"); /* verify that host2 is not in the hash */ ATF_CHECK_MSG(host_hash_lookup(&check, host_uid_hash, clientid2, clientid2_len, MDL) == 0, "Host2 was not supposed to be in the uid_hash[2]."); ATF_CHECK_MSG(check == NULL, "Host2 was not supposed to be in the hash."); /* === step 3: add second hot to the hash === */ host_hash_add(host_uid_hash, clientid2, clientid2_len, host2, MDL); /* 2 pointers expected: ours (host1) and the one stored in hash */ ATF_CHECK_MSG(host2->refcnt == 2, "Invalid refcnt for host2"); ATF_CHECK_MSG(host_hash_lookup(&check, host_uid_hash, clientid2, clientid2_len, MDL), "Host2 was supposed to be in the uid_hash."); ATF_CHECK_MSG(check, "Host2 was supposed to be in the uid_hash."); /* Hey! That's not the host we were looking for! */ ATF_CHECK_MSG(check == host2, "Wrong host returned by host_hash_lookup"); /* 3 pointers: host1, (stored in hash), check */ ATF_CHECK_MSG(host2->refcnt == 3, "Invalid refcnt for host1"); host_dereference(&check, MDL); /* we don't need it now */ /* now we have 2 hosts in the hash */ /* verify that host1 is still in the hash and the we can find it */ ATF_CHECK_MSG(host_hash_lookup(&check, host_uid_hash, clientid1, clientid1_len, MDL), "Host1 was supposed to be in the uid_hash."); ATF_CHECK_MSG(check, "Host1 was supposed to be in the uid_hash."); /* Hey! That's not the host we were looking for! */ ATF_CHECK_MSG(check == host1, "Wrong host returned by host_hash_lookup"); /* 3 pointers: host1, (stored in hash), check */ ATF_CHECK_MSG(host1->refcnt == 3, "Invalid refcnt for host1"); host_dereference(&check, MDL); /* we don't need it now */ /** * @todo check that there is actually two hosts in the hash. * Use host_hash_for_each() for that. */ /* === step 4: remove first host from the hash === */ /* delete host from hash */ host_hash_delete(host_uid_hash, clientid1, clientid1_len, MDL); ATF_CHECK_MSG(host1->refcnt == 1, "Invalid refcnt for host1"); ATF_CHECK_MSG(host2->refcnt == 2, "Invalid refcnt for host2"); /* verify that host1 is no longer in the hash */ ATF_CHECK_MSG(host_hash_lookup(&check, host_uid_hash, clientid1, clientid1_len, MDL) == 0, "Host1 is not supposed to be in the uid_hash."); ATF_CHECK_MSG(!check, "Host1 is not supposed to be in the uid_hash."); /* host2 should be still there, though */ ATF_CHECK_MSG(host_hash_lookup(&check, host_uid_hash, clientid2, clientid2_len, MDL), "Host2 was supposed to still be in the uid_hash."); host_dereference(&check, MDL); /* === step 5: remove second host from the hash === */ host_hash_delete(host_uid_hash, clientid2, clientid2_len, MDL); ATF_CHECK_MSG(host1->refcnt == 1, "Invalid refcnt for host1"); ATF_CHECK_MSG(host2->refcnt == 1, "Invalid refcnt for host2"); ATF_CHECK_MSG(host_hash_lookup(&check, host_uid_hash, clientid2, clientid2_len, MDL) == 0, "Host2 was not supposed to be in the uid_hash anymore."); host_dereference(&host1, MDL); host_dereference(&host2, MDL); /* * No easy way to check if the host object were actually released. * We could run it in valgrind and check for memory leaks. */ #if defined (DEBUG_MEMORY_LEAKAGE) && defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) /* @todo: Should be called in cleanup */ free_everything (); #endif } /// @brief executes uid hash test for specified client-ids (3 hosts) /// /// Creates three host structures, adds first host to the uid hash, /// then adds second host to the hash, then removes first host, /// then removed the second. Many checks are performed during all /// operations. /// /// @param clientid1 client-id of the first host /// @param clientid1_len client-id1 length (may be 0 for strings) /// @param clientid2 client-id of the second host /// @param clientid2_len client-id2 length (may be 0 for strings) /// @param clientid3 client-id of the second host /// @param clientid3_len client-id2 length (may be 0 for strings) void lease_hash_test_3hosts(unsigned char clientid1[], size_t clientid1_len, unsigned char clientid2[], size_t clientid2_len, unsigned char clientid3[], size_t clientid3_len) { printf("Checking hash operation for 3 hosts: clientid1-len=%lu" " clientid2-len=%lu clientid3-len=%lu\n", (unsigned long) clientid1_len, (unsigned long) clientid2_len, (unsigned long) clientid3_len); dhcp_db_objects_setup (); dhcp_common_objects_setup (); /* check that there is actually zero hosts in the hash */ /* @todo: host_hash_for_each() */ struct host_decl *host1 = 0, *host2 = 0, *host3 = 0; struct host_decl *check = 0; /* === step 1: allocate hosts === */ ATF_CHECK_MSG(host_allocate(&host1, MDL) == ISC_R_SUCCESS, "Failed to allocate host"); ATF_CHECK_MSG(host_allocate(&host2, MDL) == ISC_R_SUCCESS, "Failed to allocate host"); ATF_CHECK_MSG(host_allocate(&host3, MDL) == ISC_R_SUCCESS, "Failed to allocate host"); ATF_CHECK_MSG(host_new_hash(&host_uid_hash, HOST_HASH_SIZE, MDL) != 0, "Unable to create new hash"); ATF_CHECK_MSG(buffer_allocate(&host1->client_identifier.buffer, clientid1_len, MDL) != 0, "Can't allocate uid buffer for host1"); ATF_CHECK_MSG(buffer_allocate(&host2->client_identifier.buffer, clientid2_len, MDL) != 0, "Can't allocate uid buffer for host2"); ATF_CHECK_MSG(buffer_allocate(&host3->client_identifier.buffer, clientid3_len, MDL) != 0, "Can't allocate uid buffer for host3"); /* verify that our hosts are not in the hash yet */ ATF_CHECK_MSG(host_hash_lookup(&check, host_uid_hash, clientid1, clientid1_len, MDL) == 0, "Host1 is not supposed to be in the uid_hash."); ATF_CHECK_MSG(!check, "Host1 is not supposed to be in the uid_hash."); ATF_CHECK_MSG(host_hash_lookup(&check, host_uid_hash, clientid2, clientid2_len, MDL) == 0, "Host2 is not supposed to be in the uid_hash."); ATF_CHECK_MSG(!check, "Host2 is not supposed to be in the uid_hash."); ATF_CHECK_MSG(host_hash_lookup(&check, host_uid_hash, clientid3, clientid3_len, MDL) == 0, "Host3 is not supposed to be in the uid_hash."); ATF_CHECK_MSG(!check, "Host3 is not supposed to be in the uid_hash."); /* === step 2: add hosts to the hash === */ host_hash_add(host_uid_hash, clientid1, clientid1_len, host1, MDL); host_hash_add(host_uid_hash, clientid2, clientid2_len, host2, MDL); host_hash_add(host_uid_hash, clientid3, clientid3_len, host3, MDL); ATF_CHECK_MSG(host_hash_lookup(&check, host_uid_hash, clientid1, clientid1_len, MDL), "Host1 was supposed to be in the uid_hash."); /* Hey! That's not the host we were looking for! */ ATF_CHECK_MSG(check == host1, "Wrong host returned by host_hash_lookup"); host_dereference(&check, MDL); /* we don't need it now */ ATF_CHECK_MSG(host_hash_lookup(&check, host_uid_hash, clientid2, clientid2_len, MDL), "Host2 was supposed to be in the uid_hash."); ATF_CHECK_MSG(check, "Host2 was supposed to be in the uid_hash."); /* Hey! That's not the host we were looking for! */ ATF_CHECK_MSG(check == host2, "Wrong host returned by host_hash_lookup"); host_dereference(&check, MDL); /* we don't need it now */ ATF_CHECK_MSG(host_hash_lookup(&check, host_uid_hash, clientid3, clientid3_len, MDL), "Host3 was supposed to be in the uid_hash."); ATF_CHECK_MSG(check, "Host3 was supposed to be in the uid_hash."); /* Hey! That's not the host we were looking for! */ ATF_CHECK_MSG(check == host3, "Wrong host returned by host_hash_lookup"); host_dereference(&check, MDL); /* we don't need it now */ /* === step 4: remove first host from the hash === */ /* delete host from hash */ host_hash_delete(host_uid_hash, clientid1, clientid1_len, MDL); /* verify that host1 is no longer in the hash */ ATF_CHECK_MSG(host_hash_lookup(&check, host_uid_hash, clientid1, clientid1_len, MDL) == 0, "Host1 is not supposed to be in the uid_hash."); ATF_CHECK_MSG(!check, "Host1 is not supposed to be in the uid_hash."); /* host2 and host3 should be still there, though */ ATF_CHECK_MSG(host_hash_lookup(&check, host_uid_hash, clientid2, clientid2_len, MDL), "Host2 was supposed to still be in the uid_hash."); host_dereference(&check, MDL); ATF_CHECK_MSG(host_hash_lookup(&check, host_uid_hash, clientid3, clientid3_len, MDL), "Host3 was supposed to still be in the uid_hash."); host_dereference(&check, MDL); /* === step 5: remove second host from the hash === */ host_hash_delete(host_uid_hash, clientid2, clientid2_len, MDL); ATF_CHECK_MSG(host_hash_lookup(&check, host_uid_hash, clientid2, clientid2_len, MDL) == 0, "Host2 was not supposed to be in the uid_hash anymore."); ATF_CHECK_MSG(host_hash_lookup(&check, host_uid_hash, clientid3, clientid3_len, MDL), "Host3 was supposed to still be in the uid_hash."); host_dereference(&check, MDL); /* === step 6: remove the last (third) host from the hash === */ host_hash_delete(host_uid_hash, clientid3, clientid3_len, MDL); ATF_CHECK_MSG(host_hash_lookup(&check, host_uid_hash, clientid3, clientid3_len, MDL) == 0, "Host3 was not supposed to be in the uid_hash anymore."); host_dereference(&check, MDL); host_dereference(&host1, MDL); host_dereference(&host2, MDL); host_dereference(&host3, MDL); /* * No easy way to check if the host object were actually released. * We could run it in valgrind and check for memory leaks. */ #if defined (DEBUG_MEMORY_LEAKAGE) && defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) /* @todo: Should be called in cleanup */ free_everything (); #endif } ATF_TC(lease_hash_basic_2hosts); ATF_TC_HEAD(lease_hash_basic_2hosts, tc) { atf_tc_set_md_var(tc, "descr", "Basic lease hash tests"); /* * The following functions are tested: * host_allocate(), host_new_hash(), buffer_allocate(), host_hash_lookup() * host_hash_add(), host_hash_delete() */ } ATF_TC_BODY(lease_hash_basic_2hosts, tc) { unsigned char clientid1[] = { 0x1, 0x2, 0x3 }; unsigned char clientid2[] = { 0xff, 0xfe }; lease_hash_test_2hosts(clientid1, sizeof(clientid1), clientid2, sizeof(clientid2)); } ATF_TC(lease_hash_string_2hosts); ATF_TC_HEAD(lease_hash_string_2hosts, tc) { atf_tc_set_md_var(tc, "descr", "string-based lease hash tests"); /* * The following functions are tested: * host_allocate(), host_new_hash(), buffer_allocate(), host_hash_lookup() * host_hash_add(), host_hash_delete() */ } ATF_TC_BODY(lease_hash_string_2hosts, tc) { unsigned char clientid1[] = "Alice"; unsigned char clientid2[] = "Bob"; lease_hash_test_2hosts(clientid1, 0, clientid2, 0); } ATF_TC(lease_hash_negative1); ATF_TC_HEAD(lease_hash_negative1, tc) { atf_tc_set_md_var(tc, "descr", "Negative tests for lease hash"); } ATF_TC_BODY(lease_hash_negative1, tc) { unsigned char clientid1[] = { 0x1 }; unsigned char clientid2[] = { 0x0 }; lease_hash_test_2hosts(clientid1, 0, clientid2, 1); } ATF_TC(lease_hash_string_3hosts); ATF_TC_HEAD(lease_hash_string_3hosts, tc) { atf_tc_set_md_var(tc, "descr", "string-based lease hash tests"); /* * The following functions are tested: * host_allocate(), host_new_hash(), buffer_allocate(), host_hash_lookup() * host_hash_add(), host_hash_delete() */ } ATF_TC_BODY(lease_hash_string_3hosts, tc) { unsigned char clientid1[] = "Alice"; unsigned char clientid2[] = "Bob"; unsigned char clientid3[] = "Charlie"; lease_hash_test_3hosts(clientid1, 0, clientid2, 0, clientid3, 0); } ATF_TC(lease_hash_basic_3hosts); ATF_TC_HEAD(lease_hash_basic_3hosts, tc) { atf_tc_set_md_var(tc, "descr", "Basic lease hash tests"); /* * The following functions are tested: * host_allocate(), host_new_hash(), buffer_allocate(), host_hash_lookup() * host_hash_add(), host_hash_delete() */ } ATF_TC_BODY(lease_hash_basic_3hosts, tc) { unsigned char clientid1[] = { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9 }; unsigned char clientid2[] = { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8 }; unsigned char clientid3[] = { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7 }; lease_hash_test_3hosts(clientid1, sizeof(clientid1), clientid2, sizeof(clientid2), clientid3, sizeof(clientid3)); } #if 0 /* This test is disabled as we solved the issue by prohibiting the code from using an improper client id earlier and restoring the hash code to its previous state. As we may choose to redo the hash code again this test hasn't been deleted. */ /* this test is a direct reproduction of 29851 issue */ ATF_TC(uid_hash_rt29851); ATF_TC_HEAD(uid_hash_rt29851, tc) { atf_tc_set_md_var(tc, "descr", "Uid hash tests"); /* * this test should last less than millisecond. If its execution * is longer than 3 second, we hit infinite loop. */ atf_tc_set_md_var(tc, "timeout", "3"); } ATF_TC_BODY(uid_hash_rt29851, tc) { unsigned char clientid1[] = { 0x0 }; unsigned char clientid2[] = { 0x0 }; unsigned char clientid3[] = { 0x0 }; int clientid1_len = 1; int clientid2_len = 1; int clientid3_len = 0; struct lease *lease1 = 0, *lease2 = 0, *lease3 = 0; dhcp_db_objects_setup (); dhcp_common_objects_setup (); ATF_CHECK(lease_id_new_hash(&lease_uid_hash, LEASE_HASH_SIZE, MDL)); ATF_CHECK(lease_allocate (&lease1, MDL) == ISC_R_SUCCESS); ATF_CHECK(lease_allocate (&lease2, MDL) == ISC_R_SUCCESS); ATF_CHECK(lease_allocate (&lease3, MDL) == ISC_R_SUCCESS); lease1->uid = clientid1; lease2->uid = clientid2; lease3->uid = clientid3; lease1->uid_len = clientid1_len; lease2->uid_len = clientid2_len; lease3->uid_len = clientid3_len; uid_hash_add(lease1); /* uid_hash_delete(lease2); // not necessary for actual issue repro */ uid_hash_add(lease3); /* lease2->uid_len = 0; // not necessary for actual issue repro */ /* uid_hash_delete(lease2); // not necessary for actual issue repro */ /* uid_hash_delete(lease3); // not necessary for actual issue repro */ uid_hash_delete(lease1); /* lease2->uid_len = 1; // not necessary for actual issue repro */ uid_hash_add(lease1); uid_hash_delete(lease2); } #endif ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, lease_hash_basic_2hosts); ATF_TP_ADD_TC(tp, lease_hash_basic_3hosts); ATF_TP_ADD_TC(tp, lease_hash_string_2hosts); ATF_TP_ADD_TC(tp, lease_hash_string_3hosts); ATF_TP_ADD_TC(tp, lease_hash_negative1); #if 0 /* see comment in function */ ATF_TP_ADD_TC(tp, uid_hash_rt29851); #endif return (atf_no_error()); } dhcp-4.4.1/server/tests/Kyuafile000644 000765 000024 00000000363 13243301226 017061 0ustar00tmarkstaff000000 000000 syntax(2) test_suite('isc-dhcp') atf_test_program{name='dhcpd_unittests'} atf_test_program{name='hash_unittests'} atf_test_program{name='leaseq_unittests'} atf_test_program{name='legacy_unittests'} atf_test_program{name='load_bal_unittests'} dhcp-4.4.1/server/tests/leaseq_unittest.c000644 000765 000024 00000033562 13243301226 020751 0ustar00tmarkstaff000000 000000 /* * Copyright (C) 2015-2017 by 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include #include "dhcpd.h" #include /* * Test the lease queue code. These tests will verify that we can * add, find and remove leases from the lease queue code * * Thoughout the tests * lq will be the lease queue for which we add or removing leases * test_leaseX will be the leases we add or remove * We only care about the sort_time in the lease structure for these * tests but need to do a lease_reference in order to keep the ref * count positive to avoid the omapi code trying to free the object. * We can't use lease_allocate easily as we haven't set up the omapi * object information in the test. */ #if defined (BINARY_LEASES) #define INIT_LQ(LQ) memset(&(LQ), 0, sizeof(struct leasechain)) #else #define INIT_LQ(LQ) lq = NULL #endif /* Test basic leaseq functions with a single lease */ /*- empty, add, get, and remove */ ATF_TC(leaseq_basic); ATF_TC_HEAD(leaseq_basic, tc) { atf_tc_set_md_var(tc, "descr", "Verify basic functions"); } ATF_TC_BODY(leaseq_basic, tc) { LEASE_STRUCT lq; struct lease test_lease1, *check_lease; INIT_LQ(lq); memset(&test_lease1, 0, sizeof(struct lease)); test_lease1.sort_time = 10; check_lease = NULL; lease_reference(&check_lease, &test_lease1, MDL); /* Check that the lq is empty */ if ((LEASE_NOT_EMPTY(lq)) || (LEASE_NOT_EMPTYP(&lq))) atf_tc_fail("lq not empty at start."); /* And that getting the first lease is okay when queue is empty */ if ((LEASE_GET_FIRST(lq) != NULL) || (LEASE_GET_FIRSTP(&lq) != NULL)) atf_tc_fail("lease not null"); /* Add a lease */ LEASE_INSERTP(&lq, &test_lease1); /* lq shouldn't be empty anymore */ if (!(LEASE_NOT_EMPTY(lq)) || !(LEASE_NOT_EMPTYP(&lq))) atf_tc_fail("lq empty after insertion."); /* We should have the same lease we inserted */ check_lease = LEASE_GET_FIRST(lq); if (check_lease != &test_lease1) atf_tc_fail("leases don't match"); /* We should have the same lease we inserted */ check_lease = LEASE_GET_FIRSTP(&lq); if (check_lease != &test_lease1) atf_tc_fail("leases don't match"); /* Check that doing a get on the last lease returns NULL */ if ((LEASE_GET_NEXT(lq, check_lease) != NULL) || (LEASE_GET_NEXTP(&lq, check_lease) != NULL)) { atf_tc_fail("Next not null"); } /* Remove the lease */ LEASE_REMOVEP(&lq, &test_lease1); /* and verify the lease queue is empty again */ if ((LEASE_NOT_EMPTY(lq)) || (LEASE_NOT_EMPTYP(&lq))) atf_tc_fail("lq not empty afer removal"); /* And that getting the first lease is okay when queue is empty */ if ((LEASE_GET_FIRST(lq) != NULL) || (LEASE_GET_FIRSTP(&lq) != NULL)) atf_tc_fail("lease not null"); } /* Test if we can add leases to the end of the list and remove them * from the end */ ATF_TC(leaseq_add_to_end); ATF_TC_HEAD(leaseq_add_to_end, tc) { atf_tc_set_md_var(tc, "descr", "Verify adding to end of list"); } ATF_TC_BODY(leaseq_add_to_end, tc) { LEASE_STRUCT lq; struct lease test_lease[3], *check_lease; int i; INIT_LQ(lq); /* create and add 3 leases */ for (i = 0; i < 3; i++) { memset(&test_lease[i], 0, sizeof(struct lease)); test_lease[i].sort_time = i; check_lease = NULL; lease_reference(&check_lease, &test_lease[i], MDL); LEASE_INSERTP(&lq, &test_lease[i]); } /* check ordering of leases */ check_lease = LEASE_GET_FIRST(lq); for (i = 0; i < 3; i++) { if (check_lease != &test_lease[i]) atf_tc_fail("leases don't match, %d", i); check_lease = LEASE_GET_NEXT(lq, check_lease); } if (check_lease != NULL) atf_tc_fail("lease not null"); /* Remove the last lease and check the list */ LEASE_REMOVEP(&lq, &test_lease[2]); check_lease = LEASE_GET_FIRST(lq); if (check_lease != &test_lease[0]) atf_tc_fail("wrong lease after remove, 1"); check_lease = LEASE_GET_NEXT(lq, check_lease); if (check_lease != &test_lease[1]) atf_tc_fail("wrong lease after remove, 2"); LEASE_REMOVEP(&lq, &test_lease[1]); check_lease = LEASE_GET_FIRST(lq); if (check_lease != &test_lease[0]) atf_tc_fail("wrong lease after remove, 3"); LEASE_REMOVEP(&lq, check_lease); /* The lease queue should now be empty */ if (LEASE_NOT_EMPTY(lq)) atf_tc_fail("lq not empty afer removal"); } /* Test if we can add leases to the start of the list and remove them * from the start */ ATF_TC(leaseq_add_to_start); ATF_TC_HEAD(leaseq_add_to_start, tc) { atf_tc_set_md_var(tc, "descr", "Verify adding to start of list"); } ATF_TC_BODY(leaseq_add_to_start, tc) { LEASE_STRUCT lq; struct lease test_lease[3], *check_lease; int i; INIT_LQ(lq); /* create 3 leases */ for (i = 0; i < 3; i++) { memset(&test_lease[i], 0, sizeof(struct lease)); test_lease[i].sort_time = i; check_lease = NULL; lease_reference(&check_lease, &test_lease[i], MDL); } /* Add leases */ LEASE_INSERTP(&lq, &test_lease[2]); LEASE_INSERTP(&lq, &test_lease[1]); LEASE_INSERTP(&lq, &test_lease[0]); /* check ordering of leases */ check_lease = LEASE_GET_FIRST(lq); for (i = 0; i < 3; i++) { if (check_lease != &test_lease[i]) atf_tc_fail("leases don't match, %d", i); check_lease = LEASE_GET_NEXT(lq, check_lease); } if (check_lease != NULL) atf_tc_fail("lease not null"); /* Remove the first lease and check the next one */ check_lease = LEASE_GET_FIRST(lq); LEASE_REMOVEP(&lq, check_lease); check_lease = LEASE_GET_FIRST(lq); if (check_lease != &test_lease[1]) atf_tc_fail("wrong lease after remove, 1"); check_lease = LEASE_GET_NEXT(lq, check_lease); if (check_lease != &test_lease[2]) atf_tc_fail("wrong lease after remove, 2"); check_lease = LEASE_GET_FIRST(lq); LEASE_REMOVEP(&lq, check_lease); check_lease = LEASE_GET_FIRST(lq); if (check_lease != &test_lease[2]) atf_tc_fail("wrong lease after remove, 3"); LEASE_REMOVEP(&lq, check_lease); /* The lease queue should now be empty */ if (LEASE_NOT_EMPTY(lq)) atf_tc_fail("lq not empty afer removal"); } /* Test if we can add leases to the middle of the list and remove them * from the middle */ ATF_TC(leaseq_add_to_middle); ATF_TC_HEAD(leaseq_add_to_middle, tc) { atf_tc_set_md_var(tc, "descr", "Verify adding to end of list"); } ATF_TC_BODY(leaseq_add_to_middle, tc) { LEASE_STRUCT lq; struct lease test_lease[3], *check_lease; int i; INIT_LQ(lq); /* create 3 leases */ for (i = 0; i < 3; i++) { memset(&test_lease[i], 0, sizeof(struct lease)); test_lease[i].sort_time = i; check_lease = NULL; lease_reference(&check_lease, &test_lease[i], MDL); } /* Add leases */ LEASE_INSERTP(&lq, &test_lease[0]); LEASE_INSERTP(&lq, &test_lease[2]); LEASE_INSERTP(&lq, &test_lease[1]); /* check ordering of leases */ check_lease = LEASE_GET_FIRST(lq); for (i = 0; i < 3; i++) { if (check_lease != &test_lease[i]) atf_tc_fail("leases don't match, %d", i); check_lease = LEASE_GET_NEXT(lq, check_lease); } if (check_lease != NULL) atf_tc_fail("lease not null"); /* Remove the middle lease and check the list */ LEASE_REMOVEP(&lq, &test_lease[1]); check_lease = LEASE_GET_FIRST(lq); if (check_lease != &test_lease[0]) atf_tc_fail("wrong lease after remove, 1"); check_lease = LEASE_GET_NEXT(lq, check_lease); if (check_lease != &test_lease[2]) atf_tc_fail("wrong lease after remove, 2"); LEASE_REMOVEP(&lq, &test_lease[0]); check_lease = LEASE_GET_FIRST(lq); if (check_lease != &test_lease[2]) atf_tc_fail("wrong lease after remove, 3"); LEASE_REMOVEP(&lq, check_lease); /* The lease queue should now be empty */ if (LEASE_NOT_EMPTY(lq)) atf_tc_fail("lq not empty afer removal"); } /* Test if we can cycle the leases we add three leases then remove * the first one, update it's sort time and re add it */ ATF_TC(leaseq_cycle); ATF_TC_HEAD(leaseq_cycle, tc) { atf_tc_set_md_var(tc, "descr", "Verify cycling the list"); } ATF_TC_BODY(leaseq_cycle, tc) { LEASE_STRUCT lq; struct lease test_lease[3], *check_lease; int i; INIT_LQ(lq); /* create and add 3 leases */ for (i = 0; i < 3; i++) { memset(&test_lease[i], 0, sizeof(struct lease)); test_lease[i].sort_time = i; check_lease = NULL; lease_reference(&check_lease, &test_lease[i], MDL); LEASE_INSERTP(&lq, &test_lease[i]); } /* Remove first lease, update it and re-insert it */ LEASE_REMOVEP(&lq, &test_lease[0]); test_lease[0].sort_time = 4; LEASE_INSERTP(&lq, &test_lease[0]); /* check ordering of leases */ check_lease = LEASE_GET_FIRST(lq); if (check_lease != &test_lease[1]) atf_tc_fail("leases don't match, 1"); check_lease = LEASE_GET_NEXT(lq, check_lease); if (check_lease != &test_lease[2]) atf_tc_fail("leases don't match, 2"); check_lease = LEASE_GET_NEXT(lq, check_lease); if (check_lease != &test_lease[0]) atf_tc_fail("leases don't match, 3"); /* Remove first lease, update it and re-insert it */ LEASE_REMOVEP(&lq, &test_lease[1]); test_lease[1].sort_time = 5; LEASE_INSERTP(&lq, &test_lease[1]); /* check ordering of leases */ check_lease = LEASE_GET_FIRST(lq); if (check_lease != &test_lease[2]) atf_tc_fail("leases don't match, 4"); check_lease = LEASE_GET_NEXT(lq, check_lease); if (check_lease != &test_lease[0]) atf_tc_fail("leases don't match, 5"); check_lease = LEASE_GET_NEXT(lq, check_lease); if (check_lease != &test_lease[1]) atf_tc_fail("leases don't match, 6"); /* Remove first lease, update it and re-insert it */ LEASE_REMOVEP(&lq, &test_lease[2]); test_lease[2].sort_time = 6; LEASE_INSERTP(&lq, &test_lease[2]); /* check ordering of leases */ check_lease = LEASE_GET_FIRST(lq); if (check_lease != &test_lease[0]) atf_tc_fail("leases don't match, 7"); check_lease = LEASE_GET_NEXT(lq, check_lease); if (check_lease != &test_lease[1]) atf_tc_fail("leases don't match, 8"); check_lease = LEASE_GET_NEXT(lq, check_lease); if (check_lease != &test_lease[2]) atf_tc_fail("leases don't match, 9"); } /* Test what happens if we set the growth factor to a smallish number * and add enough leases to require it to grow multiple times * Mostly this is for the binary leases case but we can most of the * test for both. */ ATF_TC(leaseq_long); ATF_TC_HEAD(leaseq_long, tc) { atf_tc_set_md_var(tc, "descr", "Verify a long list"); } ATF_TC_BODY(leaseq_long, tc) { LEASE_STRUCT lq; struct lease test_lease[20], *check_lease; int i; INIT_LQ(lq); #if defined (BINARY_LEASES) lc_init_growth(&lq, 5); #endif /* create and add 10 leases */ for (i = 0; i < 10; i++) { memset(&test_lease[i], 0, sizeof(struct lease)); test_lease[i].sort_time = i; check_lease = NULL; lease_reference(&check_lease, &test_lease[i], MDL); LEASE_INSERTP(&lq, &test_lease[i]); } /* check ordering of leases */ check_lease = LEASE_GET_FIRST(lq); for (i = 0; i < 10; i++) { if (check_lease != &test_lease[i]) atf_tc_fail("leases don't match, %d", i); check_lease = LEASE_GET_NEXT(lq, check_lease); } /* Remove first 5 */ for (i = 0; i < 5; i++) { LEASE_REMOVEP(&lq, &test_lease[i]); } /* Add 10 more */ for (i = 10; i < 20; i++) { memset(&test_lease[i], 0, sizeof(struct lease)); test_lease[i].sort_time = i; check_lease = NULL; lease_reference(&check_lease, &test_lease[i], MDL); LEASE_INSERTP(&lq, &test_lease[i]); } /* and the first 5 */ for (i = 0; i < 5; i++) { LEASE_INSERTP(&lq, &test_lease[i]); } /* and check them all out */ check_lease = LEASE_GET_FIRST(lq); for (i = 0; i < 20; i++) { if (check_lease != &test_lease[i]) atf_tc_fail("leases don't match, %d", i); check_lease = LEASE_GET_NEXT(lq, check_lease); } } /* Test if we can add leases that all have the same sort time */ ATF_TC(leaseq_same_time); ATF_TC_HEAD(leaseq_same_time, tc) { atf_tc_set_md_var(tc, "descr", "Verify adding leases with same time"); } ATF_TC_BODY(leaseq_same_time, tc) { LEASE_STRUCT lq; struct lease test_lease[20], *check_lease; int i; INIT_LQ(lq); /* create and add 20 leases */ for (i = 0; i < 20; i++) { memset(&test_lease[i], 0, sizeof(struct lease)); if (i < 10) test_lease[i].sort_time = 10; else test_lease[i].sort_time = 20; check_lease = NULL; lease_reference(&check_lease, &test_lease[i], MDL); LEASE_INSERTP(&lq, &test_lease[i]); } #if defined (BINARY_LEASES) /* the ordering isn't well defined for either but we check * to see what happens in the binary case. At the start * the leases should stay in the order they were added. */ /* check ordering of leases */ check_lease = LEASE_GET_FIRST(lq); for (i = 0; i < 20; i++) { if (check_lease != &test_lease[i]) atf_tc_fail("leases don't match 1, %d", i); check_lease = LEASE_GET_NEXT(lq, check_lease); } if (check_lease != NULL) atf_tc_fail("lease not null"); #endif /* Remove first 10, update their time, but still before * the previous 10 and re add them */ for (i = 0; i < 10; i++) { LEASE_REMOVEP(&lq, &test_lease[i]); test_lease[i].sort_time = 15; LEASE_INSERTP(&lq, &test_lease[i]); } /* The ordering becomes random at this point so we don't * check it. We try to remove them all and see that * we got to an empty queue. */ for (i = 0; i < 20; i++) { LEASE_REMOVEP(&lq, &test_lease[i]); } if (LEASE_NOT_EMPTY(lq)) atf_tc_fail("queue not empty"); } ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, leaseq_basic); ATF_TP_ADD_TC(tp, leaseq_add_to_end); ATF_TP_ADD_TC(tp, leaseq_add_to_start); ATF_TP_ADD_TC(tp, leaseq_add_to_middle); ATF_TP_ADD_TC(tp, leaseq_cycle); ATF_TP_ADD_TC(tp, leaseq_long); ATF_TP_ADD_TC(tp, leaseq_same_time); return (atf_no_error()); } dhcp-4.4.1/server/tests/load_bal_unittest.c000644 000765 000024 00000012773 13243301226 021235 0ustar00tmarkstaff000000 000000 /* * Copyright (C) 2012-2017 by 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include #include "dhcpd.h" #include /* * Test the load balancing code. * * The two main variables are: * packet => the "packet" being processed * state => the "state" of the failover peer * We only fill in the fields necessary for our testing * packet->raw->secs => amount of time the client has been trying * packet->raw->hlen => the length of the mac address of the client * packet->raw->chaddr => the mac address of the client * To simplify the tests the mac address will be only 1 byte long and * not really matter. Instead the hba will be all 1s and the tests * will use the primary/secondary flag to change the expected result. * * state->i_am => primary or secondary * state->load_balance_max_secs => maxixum time for a client to be trying * before the other peer responds * set to 5 for these tests * state->hba = array of hash buckets assigning the hash to primary or secondary * set to all ones (all primary) for theses tests */ ATF_TC(load_balance); ATF_TC_HEAD(load_balance, tc) { atf_tc_set_md_var(tc, "descr", "This test case checks that " "load balancing works."); } ATF_TC_BODY(load_balance, tc) { #if defined(FAILOVER_PROTOCOL) struct packet packet; struct dhcp_packet raw; dhcp_failover_state_t pstate, sstate; u_int8_t hba[256]; memset(&packet, 0, sizeof(struct packet)); memset(&raw, 0, sizeof(struct dhcp_packet)); packet.raw = &raw; raw.hlen = 1; raw.chaddr[0] = 14; memset(hba, 0xFF, 256); /* primary state */ memset(&pstate, 0, sizeof(dhcp_failover_state_t)); pstate.i_am = primary; pstate.load_balance_max_secs = 5; pstate.hba = hba; /* secondary state, we can reuse the hba as it doesn't change */ memset(&sstate, 0, sizeof(dhcp_failover_state_t)); sstate.i_am = secondary; sstate.load_balance_max_secs = 5; sstate.hba = hba; /* Basic check, primary accepted, secondary not */ raw.secs = htons(0); if (load_balance_mine(&packet, &pstate) != 1) { atf_tc_fail("ERROR: primary not accepted %s:%d", MDL); } if (load_balance_mine(&packet, &sstate) != 0) { atf_tc_fail("ERROR: secondary accepted %s:%d", MDL); } /* Timeout not exceeded, primary accepted, secondary not */ raw.secs = htons(2); if (load_balance_mine(&packet, &pstate) != 1) { atf_tc_fail("ERROR: primary not accepted %s:%d", MDL); } if (load_balance_mine(&packet, &sstate) != 0) { atf_tc_fail("ERROR: secondary accepted %s:%d", MDL); } /* Timeout exceeded, both accepted */ raw.secs = htons(6); if (load_balance_mine(&packet, &pstate) != 1) { atf_tc_fail("ERROR: primary not accepted %s:%d", MDL); } if (load_balance_mine(&packet, &sstate) != 1) { atf_tc_fail("ERROR: secondary not accepted %s:%d", MDL); } /* Timeout exeeded with a large value, both accepted */ raw.secs = htons(257); if (load_balance_mine(&packet, &pstate) != 1) { atf_tc_fail("ERROR: primary not accepted %s:%d", MDL); } if (load_balance_mine(&packet, &sstate) != 1) { atf_tc_fail("ERROR: secondary not accepted %s:%d", MDL); } #else atf_tc_skip("failover is disabled"); #endif } ATF_TC(load_balance_swap); ATF_TC_HEAD(load_balance_swap, tc) { atf_tc_set_md_var(tc, "descr", "This test case checks that " "load balancing works with byteswapping."); } ATF_TC_BODY(load_balance_swap, tc) { #if defined(FAILOVER_PROTOCOL) struct packet packet; struct dhcp_packet raw; dhcp_failover_state_t pstate, sstate; u_int8_t hba[256]; check_secs_byte_order = 1; memset(&packet, 0, sizeof(struct packet)); memset(&raw, 0, sizeof(struct dhcp_packet)); packet.raw = &raw; raw.hlen = 1; raw.chaddr[0] = 14; memset(hba, 0xFF, 256); /* primary state */ memset(&pstate, 0, sizeof(dhcp_failover_state_t)); pstate.i_am = primary; pstate.load_balance_max_secs = 5; pstate.hba = hba; /* secondary state, we can reuse the hba as it doesn't change */ memset(&sstate, 0, sizeof(dhcp_failover_state_t)); sstate.i_am = secondary; sstate.load_balance_max_secs = 5; sstate.hba = hba; /* Small byteswapped timeout, primary accepted, secondary not*/ raw.secs = htons(256); if (load_balance_mine(&packet, &pstate) != 1) { atf_tc_fail("ERROR: primary not accepted %s:%d", MDL); } if (load_balance_mine(&packet, &sstate) != 0) { atf_tc_fail("ERROR: secondary accepted %s:%d", MDL); } /* Large byteswapped timeout, both accepted*/ raw.secs = htons(256 * 6); if (load_balance_mine(&packet, &pstate) != 1) { atf_tc_fail("ERROR: primary not accepted %s:%d", MDL); } if (load_balance_mine(&packet, &sstate) != 1) { atf_tc_fail("ERROR: secondary not accepted %s:%d", MDL); } #else atf_tc_skip("failover is disabled"); #endif } ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, load_balance); ATF_TP_ADD_TC(tp, load_balance_swap); return (atf_no_error()); } dhcp-4.4.1/server/tests/Makefile.am000644 000765 000024 00000004064 13243301226 017423 0ustar00tmarkstaff000000 000000 SUBDIRS = . AM_CPPFLAGS = $(ATF_CFLAGS) -DUNIT_TEST -I$(top_srcdir)/includes AM_CPPFLAGS += -I@BINDDIR@/include -I$(top_srcdir) AM_CPPFLAGS += -DLOCALSTATEDIR='"."' EXTRA_DIST = Atffile Kyuafile # for autotools debugging only info: @echo "ATF_CFLAGS=$(ATF_CFLAGS)" @echo "ATF_LDFLAGS=$(ATF_LDFLAGS)" @echo "ATF_LIBS=$(ATF_LIBS)" DHCPSRC = ../dhcp.c ../bootp.c ../confpars.c ../db.c ../class.c \ ../failover.c ../omapi.c ../mdb.c ../stables.c ../salloc.c \ ../ddns.c ../dhcpleasequery.c ../dhcpv6.c ../mdb6.c \ ../ldap.c ../ldap_casa.c ../dhcpd.c ../leasechain.c DHCPLIBS = $(top_builddir)/common/libdhcp.@A@ \ $(top_builddir)/omapip/libomapi.@A@ \ $(top_builddir)/dhcpctl/libdhcpctl.@A@ \ $(BINDLIBIRSDIR)/libirs.@A@ \ $(BINDLIBDNSDIR)/libdns.@A@ \ $(BINDLIBISCCFGDIR)/libisccfg.@A@ \ $(BINDLIBISCDIR)/libisc.@A@ ATF_TESTS = if HAVE_ATF ATF_TESTS += dhcpd_unittests legacy_unittests hash_unittests load_bal_unittests leaseq_unittests dhcpd_unittests_SOURCES = $(DHCPSRC) dhcpd_unittests_SOURCES += simple_unittest.c dhcpd_unittests_LDADD = $(ATF_LDFLAGS) dhcpd_unittests_LDADD += $(DHCPLIBS) dhcpd_unittests_LDFLAGS = $(AM_LDFLAGS) $(ATF_LDFLAGS) hash_unittests_SOURCES = $(DHCPSRC) hash_unittest.c hash_unittests_LDADD = $(DHCPLIBS) $(ATF_LDFLAGS) # This is a legacy unittest. It replaces main() with something that was in mdb6.c legacy_unittests_SOURCES = $(DHCPSRC) mdb6_unittest.c legacy_unittests_LDADD = $(DHCPLIBS) $(ATF_LDFLAGS) load_bal_unittests_SOURCES = $(DHCPSRC) load_bal_unittest.c load_bal_unittests_LDADD = $(DHCPLIBS) $(ATF_LDFLAGS) leaseq_unittests_SOURCES = $(DHCPSRC) leaseq_unittest.c leaseq_unittests_LDADD = $(DHCPLIBS) $(ATF_LDFLAGS) check: $(ATF_TESTS) @if test $(top_srcdir) != ${top_builddir}; then \ cp $(top_srcdir)/server/tests/Atffile Atffile; \ cp $(top_srcdir)/server/tests/Kyuafile Kyuafile; \ fi sh ${top_builddir}/tests/unittest.sh distclean-local: @if test $(top_srcdir) != ${top_builddir}; then \ rm -f Atffile Kyuafile; \ fi endif check_PROGRAMS = $(ATF_TESTS) dhcp-4.4.1/server/tests/mdb6_unittest.c000644 000765 000024 00000076004 13243301226 020325 0ustar00tmarkstaff000000 000000 /* * Copyright (C) 2007-2017 by 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include "config.h" #include #include #include #include #include "dhcpd.h" #include "omapip/omapip.h" #include "omapip/hash.h" #include #include #include void build_prefix6(struct in6_addr *pref, const struct in6_addr *net_start_pref, int pool_bits, int pref_bits, const struct data_string *input); /* * Basic iaaddr manipulation. * Verify construction and referencing of an iaaddr. */ ATF_TC(iaaddr_basic); ATF_TC_HEAD(iaaddr_basic, tc) { atf_tc_set_md_var(tc, "descr", "This test case checks that basic " "IAADDR manipulation is possible."); } ATF_TC_BODY(iaaddr_basic, tc) { struct iasubopt *iaaddr; struct iasubopt *iaaddr_copy; /* set up dhcp globals */ dhcp_context_create(DHCP_CONTEXT_PRE_DB | DHCP_CONTEXT_POST_DB, NULL, NULL); /* and other common arguments */ iaaddr = NULL; iaaddr_copy = NULL; /* tests */ if (iasubopt_allocate(&iaaddr, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: iasubopt_allocate() %s:%d", MDL); } if (iaaddr->state != FTS_FREE) { atf_tc_fail("ERROR: bad state %s:%d", MDL); } if (iaaddr->active_index != 0) { atf_tc_fail("ERROR: bad active_index :%d %s:%d", iaaddr->active_index, MDL); } if (iaaddr->inactive_index != 0) { atf_tc_fail("ERROR: bad inactive_index %d %s:%d", iaaddr->inactive_index, MDL); } if (iasubopt_reference(&iaaddr_copy, iaaddr, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: iasubopt_reference() %s:%d", MDL); } if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: iasubopt_reference() %s:%d", MDL); } if (iasubopt_dereference(&iaaddr_copy, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: iasubopt_reference() %s:%d", MDL); } } /* * Basic iaaddr sanity checks. * Verify that the iaaddr code does some sanity checking. */ ATF_TC(iaaddr_negative); ATF_TC_HEAD(iaaddr_negative, tc) { atf_tc_set_md_var(tc, "descr", "This test case checks that IAADDR " "option code can handle various negative scenarios."); } ATF_TC_BODY(iaaddr_negative, tc) { struct iasubopt *iaaddr; struct iasubopt *iaaddr_copy; /* set up dhcp globals */ dhcp_context_create(DHCP_CONTEXT_PRE_DB | DHCP_CONTEXT_POST_DB, NULL, NULL); /* tests */ /* bogus allocate arguments */ if (iasubopt_allocate(NULL, MDL) != DHCP_R_INVALIDARG) { atf_tc_fail("ERROR: iasubopt_allocate() %s:%d", MDL); } iaaddr = (struct iasubopt *)1; if (iasubopt_allocate(&iaaddr, MDL) != DHCP_R_INVALIDARG) { atf_tc_fail("ERROR: iasubopt_allocate() %s:%d", MDL); } /* bogus reference arguments */ iaaddr = NULL; if (iasubopt_allocate(&iaaddr, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: iasubopt_allocate() %s:%d", MDL); } if (iasubopt_reference(NULL, iaaddr, MDL) != DHCP_R_INVALIDARG) { atf_tc_fail("ERROR: iasubopt_reference() %s:%d", MDL); } iaaddr_copy = (struct iasubopt *)1; if (iasubopt_reference(&iaaddr_copy, iaaddr, MDL) != DHCP_R_INVALIDARG) { atf_tc_fail("ERROR: iasubopt_reference() %s:%d", MDL); } iaaddr_copy = NULL; if (iasubopt_reference(&iaaddr_copy, NULL, MDL) != DHCP_R_INVALIDARG) { atf_tc_fail("ERROR: iasubopt_reference() %s:%d", MDL); } if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: iasubopt_reference() %s:%d", MDL); } /* bogus dereference arguments */ if (iasubopt_dereference(NULL, MDL) != DHCP_R_INVALIDARG) { atf_tc_fail("ERROR: iasubopt_dereference() %s:%d", MDL); } iaaddr = NULL; if (iasubopt_dereference(&iaaddr, MDL) != DHCP_R_INVALIDARG) { atf_tc_fail("ERROR: iasubopt_dereference() %s:%d", MDL); } } /* * Basic ia_na manipulation. */ ATF_TC(ia_na_basic); ATF_TC_HEAD(ia_na_basic, tc) { atf_tc_set_md_var(tc, "descr", "This test case checks that IA_NA code can " "handle various basic scenarios."); } ATF_TC_BODY(ia_na_basic, tc) { uint32_t iaid; struct ia_xx *ia_na; struct ia_xx *ia_na_copy; struct iasubopt *iaaddr; /* set up dhcp globals */ dhcp_context_create(DHCP_CONTEXT_PRE_DB | DHCP_CONTEXT_POST_DB, NULL, NULL); /* and other common arguments */ iaid = 666; ia_na = NULL; ia_na_copy = NULL; iaaddr = NULL; /* tests */ if (ia_allocate(&ia_na, iaid, "TestDUID", 8, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: ia_allocate() %s:%d", MDL); } if (memcmp(ia_na->iaid_duid.data, &iaid, sizeof(iaid)) != 0) { atf_tc_fail("ERROR: bad IAID_DUID %s:%d", MDL); } if (memcmp(ia_na->iaid_duid.data+sizeof(iaid), "TestDUID", 8) != 0) { atf_tc_fail("ERROR: bad IAID_DUID %s:%d", MDL); } if (ia_na->num_iasubopt != 0) { atf_tc_fail("ERROR: bad num_iasubopt %s:%d", MDL); } if (ia_reference(&ia_na_copy, ia_na, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: ia_reference() %s:%d", MDL); } if (iasubopt_allocate(&iaaddr, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: iasubopt_allocate() %s:%d", MDL); } if (ia_add_iasubopt(ia_na, iaaddr, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: ia_add_iasubopt() %s:%d", MDL); } ia_remove_iasubopt(ia_na, iaaddr, MDL); if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: iasubopt_reference() %s:%d", MDL); } if (ia_dereference(&ia_na, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: ia_dereference() %s:%d", MDL); } if (ia_dereference(&ia_na_copy, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: ia_dereference() %s:%d", MDL); } } /* * Lots of iaaddr in our ia_na. * Create many iaaddrs and attach them to an ia_na * then clean up by removing them one at a time and * all at once by dereferencing the ia_na. */ ATF_TC(ia_na_manyaddrs); ATF_TC_HEAD(ia_na_manyaddrs, tc) { atf_tc_set_md_var(tc, "descr", "This test case checks that IA_NA can " "handle lots of addresses."); } ATF_TC_BODY(ia_na_manyaddrs, tc) { uint32_t iaid; struct ia_xx *ia_na; struct iasubopt *iaaddr; int i; /* set up dhcp globals */ dhcp_context_create(DHCP_CONTEXT_PRE_DB | DHCP_CONTEXT_POST_DB, NULL, NULL); /* tests */ /* lots of iaaddr that we delete */ iaid = 666; ia_na = NULL; if (ia_allocate(&ia_na, iaid, "TestDUID", 8, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: ia_allocate() %s:%d", MDL); } for (i=0; i<100; i++) { iaaddr = NULL; if (iasubopt_allocate(&iaaddr, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: iasubopt_allocate() %s:%d", MDL); } if (ia_add_iasubopt(ia_na, iaaddr, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: ia_add_iasubopt() %s:%d", MDL); } if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: iasubopt_reference() %s:%d", MDL); } } #if 0 for (i=0; i<100; i++) { iaaddr = ia_na->iasubopt[random() % ia_na->num_iasubopt]; ia_remove_iasubopt(ia_na, iaaddr, MDL); /* TODO: valgrind reports problem here: Invalid read of size 8 * Address 0x51e6258 is 56 bytes inside a block of size 88 free'd */ } #endif if (ia_dereference(&ia_na, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: ia_dereference() %s:%d", MDL); } /* lots of iaaddr, let dereference cleanup */ iaid = 666; ia_na = NULL; if (ia_allocate(&ia_na, iaid, "TestDUID", 8, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: ia_allocate() %s:%d", MDL); } for (i=0; i<100; i++) { iaaddr = NULL; if (iasubopt_allocate(&iaaddr, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: iasubopt_allocate() %s:%d", MDL); } if (ia_add_iasubopt(ia_na, iaaddr, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: ia_add_iasubopt() %s:%d", MDL); } if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: iasubopt_reference() %s:%d", MDL); } } if (ia_dereference(&ia_na, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: ia_dereference() %s:%d", MDL); } } /* * Basic ia_na sanity checks. * Verify that the ia_na code does some sanity checking. */ ATF_TC(ia_na_negative); ATF_TC_HEAD(ia_na_negative, tc) { atf_tc_set_md_var(tc, "descr", "This test case checks that IA_NA option " "code can handle various negative scenarios."); } ATF_TC_BODY(ia_na_negative, tc) { uint32_t iaid; struct ia_xx *ia_na; struct ia_xx *ia_na_copy; /* set up dhcp globals */ dhcp_context_create(DHCP_CONTEXT_PRE_DB | DHCP_CONTEXT_POST_DB, NULL, NULL); /* tests */ /* bogus allocate arguments */ if (ia_allocate(NULL, 123, "", 0, MDL) != DHCP_R_INVALIDARG) { atf_tc_fail("ERROR: ia_allocate() %s:%d", MDL); } ia_na = (struct ia_xx *)1; if (ia_allocate(&ia_na, 456, "", 0, MDL) != DHCP_R_INVALIDARG) { atf_tc_fail("ERROR: ia_allocate() %s:%d", MDL); } /* bogus reference arguments */ iaid = 666; ia_na = NULL; if (ia_allocate(&ia_na, iaid, "TestDUID", 8, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: ia_allocate() %s:%d", MDL); } if (ia_reference(NULL, ia_na, MDL) != DHCP_R_INVALIDARG) { atf_tc_fail("ERROR: ia_reference() %s:%d", MDL); } ia_na_copy = (struct ia_xx *)1; if (ia_reference(&ia_na_copy, ia_na, MDL) != DHCP_R_INVALIDARG) { atf_tc_fail("ERROR: ia_reference() %s:%d", MDL); } ia_na_copy = NULL; if (ia_reference(&ia_na_copy, NULL, MDL) != DHCP_R_INVALIDARG) { atf_tc_fail("ERROR: ia_reference() %s:%d", MDL); } if (ia_dereference(&ia_na, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: ia_dereference() %s:%d", MDL); } /* bogus dereference arguments */ if (ia_dereference(NULL, MDL) != DHCP_R_INVALIDARG) { atf_tc_fail("ERROR: ia_dereference() %s:%d", MDL); } /* bogus remove */ iaid = 666; ia_na = NULL; if (ia_allocate(&ia_na, iaid, "TestDUID", 8, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: ia_allocate() %s:%d", MDL); } ia_remove_iasubopt(ia_na, NULL, MDL); if (ia_dereference(&ia_na, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: ia_dereference() %s:%d", MDL); } } /* * Basic ipv6_pool manipulation. * Verify that basic pool operations work properly. * The operations include creating a pool and creating, * renewing, expiring, releasing and declining addresses. */ ATF_TC(ipv6_pool_basic); ATF_TC_HEAD(ipv6_pool_basic, tc) { atf_tc_set_md_var(tc, "descr", "This test case checks that IPv6 pool " "manipulation is possible."); } ATF_TC_BODY(ipv6_pool_basic, tc) { struct iasubopt *iaaddr; struct in6_addr addr; struct ipv6_pool *pool; struct ipv6_pool *pool_copy; char addr_buf[INET6_ADDRSTRLEN]; char *uid; struct data_string ds; struct iasubopt *expired_iaaddr; unsigned int attempts; /* set up dhcp globals */ dhcp_context_create(DHCP_CONTEXT_PRE_DB | DHCP_CONTEXT_POST_DB, NULL, NULL); /* and other common arguments */ inet_pton(AF_INET6, "1:2:3:4::", &addr); uid = "client0"; memset(&ds, 0, sizeof(ds)); ds.len = strlen(uid); if (!buffer_allocate(&ds.buffer, ds.len, MDL)) { atf_tc_fail("Out of memory"); } ds.data = ds.buffer->data; memcpy((char *)ds.data, uid, ds.len); /* tests */ /* allocate, reference */ pool = NULL; if (ipv6_pool_allocate(&pool, D6O_IA_NA, &addr, 64, 128, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: ipv6_pool_allocate() %s:%d", MDL); } if (pool->num_active != 0) { atf_tc_fail("ERROR: bad num_active %s:%d", MDL); } if (pool->bits != 64) { atf_tc_fail("ERROR: bad bits %s:%d", MDL); } inet_ntop(AF_INET6, &pool->start_addr, addr_buf, sizeof(addr_buf)); if (strcmp(inet_ntop(AF_INET6, &pool->start_addr, addr_buf, sizeof(addr_buf)), "1:2:3:4::") != 0) { atf_tc_fail("ERROR: bad start_addr %s:%d", MDL); } pool_copy = NULL; if (ipv6_pool_reference(&pool_copy, pool, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: ipv6_pool_reference() %s:%d", MDL); } /* create_lease6, renew_lease6, expire_lease6 */ iaaddr = NULL; if (create_lease6(pool, &iaaddr, &attempts, &ds, 1) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: create_lease6() %s:%d", MDL); } if (pool->num_inactive != 1) { atf_tc_fail("ERROR: bad num_inactive %s:%d", MDL); } if (renew_lease6(pool, iaaddr) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: renew_lease6() %s:%d", MDL); } if (pool->num_active != 1) { atf_tc_fail("ERROR: bad num_active %s:%d", MDL); } expired_iaaddr = NULL; if (expire_lease6(&expired_iaaddr, pool, 0) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: expire_lease6() %s:%d", MDL); } if (expired_iaaddr != NULL) { atf_tc_fail("ERROR: should not have expired a lease %s:%d", MDL); } if (pool->num_active != 1) { atf_tc_fail("ERROR: bad num_active %s:%d", MDL); } if (expire_lease6(&expired_iaaddr, pool, 1000) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: expire_lease6() %s:%d", MDL); } if (expired_iaaddr == NULL) { atf_tc_fail("ERROR: should have expired a lease %s:%d", MDL); } if (iasubopt_dereference(&expired_iaaddr, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: iasubopt_dereference() %s:%d", MDL); } if (pool->num_active != 0) { atf_tc_fail("ERROR: bad num_active %s:%d", MDL); } if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: iasubopt_dereference() %s:%d", MDL); } /* release_lease6, decline_lease6 */ if (create_lease6(pool, &iaaddr, &attempts, &ds, 1) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: create_lease6() %s:%d", MDL); } if (renew_lease6(pool, iaaddr) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: renew_lease6() %s:%d", MDL); } if (pool->num_active != 1) { atf_tc_fail("ERROR: bad num_active %s:%d", MDL); } if (release_lease6(pool, iaaddr) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: decline_lease6() %s:%d", MDL); } if (pool->num_active != 0) { atf_tc_fail("ERROR: bad num_active %s:%d", MDL); } if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: iasubopt_dereference() %s:%d", MDL); } if (create_lease6(pool, &iaaddr, &attempts, &ds, 1) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: create_lease6() %s:%d", MDL); } if (renew_lease6(pool, iaaddr) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: renew_lease6() %s:%d", MDL); } if (pool->num_active != 1) { atf_tc_fail("ERROR: bad num_active %s:%d", MDL); } if (decline_lease6(pool, iaaddr) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: decline_lease6() %s:%d", MDL); } if (pool->num_active != 1) { atf_tc_fail("ERROR: bad num_active %s:%d", MDL); } if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: iasubopt_dereference() %s:%d", MDL); } /* dereference */ if (ipv6_pool_dereference(&pool, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: ipv6_pool_reference() %s:%d", MDL); } if (ipv6_pool_dereference(&pool_copy, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: ipv6_pool_reference() %s:%d", MDL); } } /* * Basic ipv6_pool sanity checks. * Verify that the ipv6_pool code does some sanity checking. */ ATF_TC(ipv6_pool_negative); ATF_TC_HEAD(ipv6_pool_negative, tc) { atf_tc_set_md_var(tc, "descr", "This test case checks that IPv6 pool " "can handle negative cases."); } ATF_TC_BODY(ipv6_pool_negative, tc) { struct in6_addr addr; struct ipv6_pool *pool; struct ipv6_pool *pool_copy; /* set up dhcp globals */ dhcp_context_create(DHCP_CONTEXT_PRE_DB | DHCP_CONTEXT_POST_DB, NULL, NULL); /* and other common arguments */ inet_pton(AF_INET6, "1:2:3:4::", &addr); /* tests */ if (ipv6_pool_allocate(NULL, D6O_IA_NA, &addr, 64, 128, MDL) != DHCP_R_INVALIDARG) { atf_tc_fail("ERROR: ipv6_pool_allocate() %s:%d", MDL); } pool = (struct ipv6_pool *)1; if (ipv6_pool_allocate(&pool, D6O_IA_NA, &addr, 64, 128, MDL) != DHCP_R_INVALIDARG) { atf_tc_fail("ERROR: ipv6_pool_allocate() %s:%d", MDL); } if (ipv6_pool_reference(NULL, pool, MDL) != DHCP_R_INVALIDARG) { atf_tc_fail("ERROR: ipv6_pool_reference() %s:%d", MDL); } pool_copy = (struct ipv6_pool *)1; if (ipv6_pool_reference(&pool_copy, pool, MDL) != DHCP_R_INVALIDARG) { atf_tc_fail("ERROR: ipv6_pool_reference() %s:%d", MDL); } pool_copy = NULL; if (ipv6_pool_reference(&pool_copy, NULL, MDL) != DHCP_R_INVALIDARG) { atf_tc_fail("ERROR: ipv6_pool_reference() %s:%d", MDL); } if (ipv6_pool_dereference(NULL, MDL) != DHCP_R_INVALIDARG) { atf_tc_fail("ERROR: ipv6_pool_dereference() %s:%d", MDL); } if (ipv6_pool_dereference(&pool_copy, MDL) != DHCP_R_INVALIDARG) { atf_tc_fail("ERROR: ipv6_pool_dereference() %s:%d", MDL); } } /* * Order of expiration. * Add several addresses to a pool and check that * they expire in the proper order. */ ATF_TC(expire_order); ATF_TC_HEAD(expire_order, tc) { atf_tc_set_md_var(tc, "descr", "This test case checks that order " "of lease expiration is handled properly."); } ATF_TC_BODY(expire_order, tc) { struct iasubopt *iaaddr; struct ipv6_pool *pool; struct in6_addr addr; int i; char *uid; struct data_string ds; struct iasubopt *expired_iaaddr; unsigned int attempts; /* set up dhcp globals */ dhcp_context_create(DHCP_CONTEXT_PRE_DB | DHCP_CONTEXT_POST_DB, NULL, NULL); /* and other common arguments */ inet_pton(AF_INET6, "1:2:3:4::", &addr); uid = "client0"; memset(&ds, 0, sizeof(ds)); ds.len = strlen(uid); if (!buffer_allocate(&ds.buffer, ds.len, MDL)) { atf_tc_fail("Out of memory"); } ds.data = ds.buffer->data; memcpy((char *)ds.data, uid, ds.len); iaaddr = NULL; expired_iaaddr = NULL; /* tests */ pool = NULL; if (ipv6_pool_allocate(&pool, D6O_IA_NA, &addr, 64, 128, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: ipv6_pool_allocate() %s:%d", MDL); } for (i=10; i<100; i+=10) { if (create_lease6(pool, &iaaddr, &attempts, &ds, i) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: create_lease6() %s:%d", MDL); } if (renew_lease6(pool, iaaddr) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: renew_lease6() %s:%d", MDL); } if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: iasubopt_dereference() %s:%d", MDL); } if (pool->num_active != (i / 10)) { atf_tc_fail("ERROR: bad num_active %s:%d", MDL); } } if (pool->num_active != 9) { atf_tc_fail("ERROR: bad num_active %s:%d", MDL); } for (i=10; i<100; i+=10) { if (expire_lease6(&expired_iaaddr, pool, 1000) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: expire_lease6() %s:%d", MDL); } if (expired_iaaddr == NULL) { atf_tc_fail("ERROR: should have expired a lease %s:%d", MDL); } if (pool->num_active != (9 - (i / 10))) { atf_tc_fail("ERROR: bad num_active %s:%d", MDL); } if (expired_iaaddr->hard_lifetime_end_time != i) { atf_tc_fail("ERROR: bad hard_lifetime_end_time %s:%d", MDL); } if (iasubopt_dereference(&expired_iaaddr, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: iasubopt_dereference() %s:%d", MDL); } } if (pool->num_active != 0) { atf_tc_fail("ERROR: bad num_active %s:%d", MDL); } expired_iaaddr = NULL; if (expire_lease6(&expired_iaaddr, pool, 1000) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: expire_lease6() %s:%d", MDL); } if (ipv6_pool_dereference(&pool, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: ipv6_pool_dereference() %s:%d", MDL); } } /* * Reduce the expiration period of a lease. * This test reduces the expiration period of * a lease to verify we process reductions * properly. */ ATF_TC(expire_order_reduce); ATF_TC_HEAD(expire_order_reduce, tc) { atf_tc_set_md_var(tc, "descr", "This test case checks that reducing " "the expiration time of a lease works properly."); } ATF_TC_BODY(expire_order_reduce, tc) { struct iasubopt *iaaddr1, *iaaddr2; struct ipv6_pool *pool; struct in6_addr addr; char *uid; struct data_string ds; struct iasubopt *expired_iaaddr; unsigned int attempts; /* set up dhcp globals */ dhcp_context_create(DHCP_CONTEXT_PRE_DB | DHCP_CONTEXT_POST_DB, NULL, NULL); /* and other common arguments */ inet_pton(AF_INET6, "1:2:3:4::", &addr); uid = "client0"; memset(&ds, 0, sizeof(ds)); ds.len = strlen(uid); if (!buffer_allocate(&ds.buffer, ds.len, MDL)) { atf_tc_fail("Out of memory"); } ds.data = ds.buffer->data; memcpy((char *)ds.data, uid, ds.len); pool = NULL; iaaddr1 = NULL; iaaddr2 = NULL; expired_iaaddr = NULL; /* * Add two leases iaaddr1 with expire time of 200 * and iaaddr2 with expire time of 300. Then update * iaaddr2 to expire in 100 instead. This should cause * iaaddr2 to move with the hash list. */ /* create pool and add iaaddr1 and iaaddr2 */ if (ipv6_pool_allocate(&pool, D6O_IA_NA, &addr, 64, 128, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: ipv6_pool_allocate() %s:%d", MDL); } if (create_lease6(pool, &iaaddr1, &attempts, &ds, 200) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: create_lease6() %s:%d", MDL); } if (renew_lease6(pool, iaaddr1) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: renew_lease6() %s:%d", MDL); } if (create_lease6(pool, &iaaddr2, &attempts, &ds, 300) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: create_lease6() %s:%d", MDL); } if (renew_lease6(pool, iaaddr2) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: renew_lease6() %s:%d", MDL); } /* verify pool */ if (pool->num_active != 2) { atf_tc_fail("ERROR: bad num_active %s:%d", MDL); } /* reduce lease for iaaddr2 */ iaaddr2->soft_lifetime_end_time = 100; if (renew_lease6(pool, iaaddr2) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: renew_lease6() %s:%d", MDL); } /* expire a lease, it should be iaaddr2 with an expire time of 100 */ if (expire_lease6(&expired_iaaddr, pool, 1000) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: expire_lease6() %s:%d", MDL); } if (expired_iaaddr == NULL) { atf_tc_fail("ERROR: should have expired a lease %s:%d", MDL); } if (expired_iaaddr != iaaddr2) { atf_tc_fail("Error: incorrect lease expired %s:%d", MDL); } if (expired_iaaddr->hard_lifetime_end_time != 100) { atf_tc_fail("ERROR: bad hard_lifetime_end_time %s:%d", MDL); } if (iasubopt_dereference(&expired_iaaddr, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: iasubopt_dereference() %s:%d", MDL); } /* expire a lease, it should be iaaddr1 with an expire time of 200 */ if (expire_lease6(&expired_iaaddr, pool, 1000) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: expire_lease6() %s:%d", MDL); } if (expired_iaaddr == NULL) { atf_tc_fail("ERROR: should have expired a lease %s:%d", MDL); } if (expired_iaaddr != iaaddr1) { atf_tc_fail("Error: incorrect lease expired %s:%d", MDL); } if (expired_iaaddr->hard_lifetime_end_time != 200) { atf_tc_fail("ERROR: bad hard_lifetime_end_time %s:%d", MDL); } if (iasubopt_dereference(&expired_iaaddr, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: iasubopt_dereference() %s:%d", MDL); } /* cleanup */ if (iasubopt_dereference(&iaaddr1, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: iasubopt_dereference() %s:%d", MDL); } if (iasubopt_dereference(&iaaddr2, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: iasubopt_dereference() %s:%d", MDL); } if (ipv6_pool_dereference(&pool, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: ipv6_pool_dereference() %s:%d", MDL); } } /* * Small pool. * check that a small pool behaves properly. */ ATF_TC(small_pool); ATF_TC_HEAD(small_pool, tc) { atf_tc_set_md_var(tc, "descr", "This test case checks that small pool " "is handled properly."); } ATF_TC_BODY(small_pool, tc) { struct in6_addr addr; struct ipv6_pool *pool; struct iasubopt *iaaddr; char *uid; struct data_string ds; unsigned int attempts; /* set up dhcp globals */ dhcp_context_create(DHCP_CONTEXT_PRE_DB | DHCP_CONTEXT_POST_DB, NULL, NULL); /* and other common arguments */ inet_pton(AF_INET6, "1:2:3:4::", &addr); addr.s6_addr[14] = 0x81; uid = "client0"; memset(&ds, 0, sizeof(ds)); ds.len = strlen(uid); if (!buffer_allocate(&ds.buffer, ds.len, MDL)) { atf_tc_fail("Out of memory"); } ds.data = ds.buffer->data; memcpy((char *)ds.data, uid, ds.len); pool = NULL; iaaddr = NULL; /* tests */ if (ipv6_pool_allocate(&pool, D6O_IA_NA, &addr, 127, 128, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: ipv6_pool_allocate() %s:%d", MDL); } if (create_lease6(pool, &iaaddr, &attempts, &ds, 42) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: create_lease6() %s:%d", MDL); } if (renew_lease6(pool, iaaddr) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: renew_lease6() %s:%d", MDL); } if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: iasubopt_dereference() %s:%d", MDL); } if (create_lease6(pool, &iaaddr, &attempts, &ds, 11) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: create_lease6() %s:%d", MDL); } if (renew_lease6(pool, iaaddr) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: renew_lease6() %s:%d", MDL); } if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: iasubopt_dereference() %s:%d", MDL); } if (create_lease6(pool, &iaaddr, &attempts, &ds, 11) != ISC_R_NORESOURCES) { atf_tc_fail("ERROR: create_lease6() %s:%d", MDL); } if (ipv6_pool_dereference(&pool, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: ipv6_pool_dereference() %s:%d", MDL); } } /* * Address to pool mapping. * Verify that we find the proper pool for an address * or don't find a pool if we don't have one for the given * address. */ ATF_TC(many_pools); ATF_TC_HEAD(many_pools, tc) { atf_tc_set_md_var(tc, "descr", "This test case checks that functions " "across all pools are working correctly."); } ATF_TC_BODY(many_pools, tc) { struct in6_addr addr; struct ipv6_pool *pool; /* set up dhcp globals */ dhcp_context_create(DHCP_CONTEXT_PRE_DB | DHCP_CONTEXT_POST_DB, NULL, NULL); /* and other common arguments */ inet_pton(AF_INET6, "1:2:3:4::", &addr); /* tests */ pool = NULL; if (ipv6_pool_allocate(&pool, D6O_IA_NA, &addr, 64, 128, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: ipv6_pool_allocate() %s:%d", MDL); } if (add_ipv6_pool(pool) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: add_ipv6_pool() %s:%d", MDL); } if (ipv6_pool_dereference(&pool, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: ipv6_pool_dereference() %s:%d", MDL); } pool = NULL; if (find_ipv6_pool(&pool, D6O_IA_NA, &addr) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: find_ipv6_pool() %s:%d", MDL); } if (ipv6_pool_dereference(&pool, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: ipv6_pool_dereference() %s:%d", MDL); } inet_pton(AF_INET6, "1:2:3:4:ffff:ffff:ffff:ffff", &addr); pool = NULL; if (find_ipv6_pool(&pool, D6O_IA_NA, &addr) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: find_ipv6_pool() %s:%d", MDL); } if (ipv6_pool_dereference(&pool, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: ipv6_pool_dereference() %s:%d", MDL); } inet_pton(AF_INET6, "1:2:3:5::", &addr); pool = NULL; if (find_ipv6_pool(&pool, D6O_IA_NA, &addr) != ISC_R_NOTFOUND) { atf_tc_fail("ERROR: find_ipv6_pool() %s:%d", MDL); } inet_pton(AF_INET6, "1:2:3:3:ffff:ffff:ffff:ffff", &addr); pool = NULL; if (find_ipv6_pool(&pool, D6O_IA_NA, &addr) != ISC_R_NOTFOUND) { atf_tc_fail("ERROR: find_ipv6_pool() %s:%d", MDL); } /* iaid = 666; ia_na = NULL; if (ia_allocate(&ia_na, iaid, "TestDUID", 8, MDL) != ISC_R_SUCCESS) { atf_tc_fail("ERROR: ia_allocate() %s:%d", MDL); }*/ { struct in6_addr r; struct data_string ds; u_char data[16]; char buf[64]; int i, j; memset(&ds, 0, sizeof(ds)); memset(data, 0xaa, sizeof(data)); ds.len = 16; ds.data = data; inet_pton(AF_INET6, "3ffe:501:ffff:100::", &addr); for (i = 32; i < 42; i++) for (j = i + 1; j < 49; j++) { memset(&r, 0, sizeof(r)); memset(buf, 0, 64); build_prefix6(&r, &addr, i, j, &ds); inet_ntop(AF_INET6, &r, buf, 64); printf("%d,%d-> %s/%d\n", i, j, buf, j); } } } ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, iaaddr_basic); ATF_TP_ADD_TC(tp, iaaddr_negative); ATF_TP_ADD_TC(tp, ia_na_basic); ATF_TP_ADD_TC(tp, ia_na_manyaddrs); ATF_TP_ADD_TC(tp, ia_na_negative); ATF_TP_ADD_TC(tp, ipv6_pool_basic); ATF_TP_ADD_TC(tp, ipv6_pool_negative); ATF_TP_ADD_TC(tp, expire_order); ATF_TP_ADD_TC(tp, expire_order_reduce); ATF_TP_ADD_TC(tp, small_pool); ATF_TP_ADD_TC(tp, many_pools); return (atf_no_error()); } dhcp-4.4.1/server/tests/simple_unittest.c000644 000765 000024 00000007620 13243301226 020764 0ustar00tmarkstaff000000 000000 /* * Copyright (C) 2012-2017 by 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include #include #include /* That is an example ATF test case, tailored to ISC DHCP sources. For detailed description with examples, see man 3 atf-c-api. */ /* this macro defines a name of a test case. Typical test case constists of an initial test declaration (ATF_TC()) followed by 3 phases: - Initialization: ATF_TC_HEAD() - Main body: ATF_TC_BODY() - Cleanup: ATF_TC_CLEANUP() In many cases initialization or cleanup are not needed. Use ATF_TC_WITHOUT_HEAD() or ATF_TC_WITH_CLEANUP() as needed. */ ATF_TC(simple_test_case); ATF_TC_HEAD(simple_test_case, tc) { atf_tc_set_md_var(tc, "descr", "This test case is a simple DHCP test."); } ATF_TC_BODY(simple_test_case, tc) { int condition = 1; int this_is_linux = 1; /* Failing condition will fail the test, but the code itself will continue */ ATF_CHECK( 2 > 1 ); /* assert style check. Test will abort if the condition is not met. */ ATF_REQUIRE( 5 > 4 ); ATF_CHECK_EQ(4, 2 + 2); /* Non-fatal test. */ ATF_REQUIRE_EQ(4, 2 + 2); /* Fatal test. */ /* tests can also explicitly report test result */ if (!condition) { atf_tc_fail("Condition not met!"); /* Explicit failure. */ } if (!this_is_linux) { atf_tc_skip("Skipping test. This Linux-only test."); } if (condition && this_is_linux) { /* no extra comments for pass needed. It just passed. */ atf_tc_pass(); } } #ifdef DHCPv6 ATF_TC(parse_byte_order); ATF_TC_HEAD(parse_byte_order, tc) { atf_tc_set_md_var(tc, "descr", "Tests byte-order conversion."); } ATF_TC_BODY(parse_byte_order, tc) { uint32_t ret_value = 0; uint32_t source_value = 0xaabbccdd; /* With order set to 0, function should default to no conversion */ authoring_byte_order = 0; ret_value = parse_byte_order_uint32(&source_value); if (ret_value != source_value) { atf_tc_fail("default/non-conversion failed!"); } /* With matching byte order, function should not do the conversion */ authoring_byte_order = DHCP_BYTE_ORDER; ret_value = parse_byte_order_uint32(&source_value); if (ret_value != source_value) { atf_tc_fail("matching/non-conversion failed!"); } /* With opposite byte order, function should do the conversion */ authoring_byte_order = (DHCP_BYTE_ORDER == LITTLE_ENDIAN ? BIG_ENDIAN : LITTLE_ENDIAN); ret_value = parse_byte_order_uint32(&source_value); if (ret_value != 0xddccbbaa) { atf_tc_fail("conversion failed!"); } /* Converting the converted value should give us the original value */ ret_value = parse_byte_order_uint32(&ret_value); if (ret_value != source_value) { atf_tc_fail("round trip conversion failed!"); } atf_tc_pass(); } #endif /* DHCPv6 */ /* This macro defines main() method that will call specified test cases. tp and simple_test_case names can be whatever you want as long as it is a valid variable identifier. */ ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, simple_test_case); #ifdef DHCPv6 ATF_TP_ADD_TC(tp, parse_byte_order); #endif return (atf_no_error()); } dhcp-4.4.1/relay/dhcrelay.8000644 000765 000024 00000025136 13243301226 015722 0ustar00tmarkstaff000000 000000 .\" dhcrelay.8 .\" .\" Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") .\" Copyright (c) 1997-2003 by Internet Software Consortium .\" .\" 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/. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT .\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" Internet Systems Consortium, Inc. .\" 950 Charter Street .\" Redwood City, CA 94063 .\" .\" https://www.isc.org/ .\" .\" This software has been written for Internet Systems Consortium .\" by Ted Lemon in cooperation with Vixie Enterprises. .\" .\" Support and other services are available for ISC products - see .\" https://www.isc.org for more information or to learn more about ISC. .\" .\" $Id: dhcrelay.8,v 1.20 2012/05/14 23:17:43 sar Exp $ .\" .TH dhcrelay 8 .SH NAME dhcrelay - Dynamic Host Configuration Protocol Relay Agent .SH SYNOPSIS .B dhcrelay [ .B -4 ] [ .B -dqaD ] [ .B -p .I port | .B -rp .I relay-port ] [ .B -c .I count ] [ .B -A .I length ] [ .B -pf .I pid-file ] [ .B --no-pid ] [ .B -m .I append | .I replace | .I forward | .I discard ] [ .B -i .I interface0 [ .B ... .B -i .I interfaceN ] ] [ .B -iu .I interface0 [ .B ... .B -iu .I interfaceN ] ] [ .B -id .I interface0 [ .B ... .B -id .I interfaceN ] ] [ .B -U .I interface ] .I server0 [ .I ...serverN ] .PP .B dhcrelay -6 [ .B -dqI ] [ .B -p .I port | .B -rp .I relay-port ] [ .B -c .I count ] [ .B -pf .I pid-file ] [ .B --no-pid ] [ .B -s .I subscriber-id ] .B -l .I lower0 [ .B ... .B -l .I lowerN ] .B -u .I upper0 [ .B ... .B -u .I upperN ] .SH DESCRIPTION The Internet Systems Consortium DHCP Relay Agent, dhcrelay, provides a means for relaying DHCP and BOOTP requests from a subnet to which no DHCP server is directly connected to one or more DHCP servers on other subnets. It supports both DHCPv4/BOOTP and DHCPv6 protocols. .SH OPERATION .PP The DHCP Relay Agent listens for DHCPv4 or DHCPv6 queries from clients or other relay agents on one or more interfaces, passing them along to ``upstream'' servers or relay agents as specified on the command line. When a reply is received from upstream, it is multicast or unicast back downstream to the source of the original request. .SH COMMAND LINE .PP \fIProtocol selection options:\fR .TP -6 Run dhcrelay as a DHCPv6 relay agent. Incompatible with the \fB-4\fR option. .TP -4 Run dhcrelay as a DHCPv4/BOOTP relay agent. This is the default mode of operation, so the argument is not necessary, but may be specified for clarity. Incompatible with \fB-6\fR. .PP \fISpecifying DHCPv4/BOOTP servers\fR .PP In DHCPv4 mode, a list of one or more server addresses must be specified on the command line, to which DHCP/BOOTP queries should be relayed. .PP \fIOptions available for both DHCPv4 and DHCPv6:\fR .TP -c \fIcount\fR Maximum hop count. When forwarding packets, dhcrelay discards packets which have reached a hop count of COUNT. Default is 10. Maximum is 255. .TP -d Force dhcrelay to run as a foreground process. Useful when running dhcrelay under a debugger, or running out of inittab on System V systems. .TP -p \fIport\fR Listen and transmit on port PORT. This is mostly useful for debugging purposes. Default is port 67 for DHCPv4/BOOTP, or port 547 for DHCPv6. Incompatible with \fB-rp\fR. .TP -rp \fIrelay-port\fR Alternative source port for upstream (i.e toward the server) messages with DHCPv4 RAI relay-port sub-option or DHCPv6 relay-source-port option. Relay port support is only available if the code was compiled with (./configure --enable-relay-port) and requires LPF or BPF link layer access. .TP -q Quiet mode. Prevents dhcrelay6 from printing its network configuration on startup. .TP -pf pid-file Path to alternate pid file. .TP --no-pid Option to disable writing pid files. By default the program will write a pid file. .PP \fIOptions available in DHCPv4 mode only:\fR .TP -a Append an agent option field to each request before forwarding it to the server. Agent option fields in responses sent from servers to clients will be stripped before forwarding such responses back to the client. The agent option field will contain two agent options: the Circuit ID suboption and the Remote ID suboption. Currently, the Circuit ID will be the printable name of the interface on which the client request was received. The client supports inclusion of a Remote ID suboption as well, but this is not used by default. .TP -A \fIlength\fR Specify the maximum packet size to send to a DHCPv4/BOOTP server. This might be done to allow sufficient space for addition of relay agent options while still fitting into the Ethernet MTU size. .TP -D Drop packets from upstream servers if they contain Relay Agent Information options that indicate they were generated in response to a query that came via a different relay agent. If this option is not specified, such packets will be relayed anyway. .TP -i \fIifname\fR Listen for DHCPv4/BOOTP traffic on interface \fIifname\fR. Multiple interfaces may be specified by using more than one \fB-i\fR option. If no interfaces are specified on the command line, dhcrelay will identify all network interfaces, eliminating non-broadcast interfaces if possible, and attempt to listen on all of them. .TP -iu \fIifname\fR Specifies an upstream network interface: an interface from which replies from servers and other relay agents will be accepted. Multiple interfaces may be specified by using more than one \fB-iu\fR option. This argument is intended to be used in conjunction with one or more -i or -id arguments. .TP -id \fIifname\fR Specifies a downstream network interface: an interface from which requests from clients and other relay agents will be accepted. Multiple interfaces may be specified by using more than one \fB-id\fR option. This argument is intended to be used in conjunction with one or more -i or -iu arguments. .TP -m \fIappend\fR|\fIreplace\fR|\fIforward\fR|\fIdiscard\fR Control the handling of incoming DHCPv4 packets which already contain relay agent options. If such a packet does not have \fIgiaddr\fR set in its header, the DHCP standard requires that the packet be discarded. However, if \fIgiaddr\fR is set, the relay agent may handle the situation in four ways: It may \fIappend\fR its own set of relay options to the packet, leaving the supplied option field intact; it may \fIreplace\fR the existing agent option field; it may \fIforward\fR the packet unchanged; or, it may \fIdiscard\fR it. .TP -U \fIifname\fR Enables the addition of a RFC 3527 compliant link selection suboption for clients directly connected to the relay. This RFC allows a relay to specify two different IP addresses: one for the server to use when communicating with the relay (giaddr) the other for choosing the subnet for the client (the suboption). This can be useful if the server is unable to send packets to the relay via the address used for the subnet. When enabled, dhcrelay will add an agent option (as per \fB-a\fR above) that includes the link selection suboption to the forwarded packet. This will only be done to packets received from clients that are directly connected to the relay (i.e. giaddr is zero). The address used in the suboption will be that of the link upon which the inbound packet was received (which would otherwise be used for giaddr). The value of giaddr will be set to that of interface \fIifname\fR. Only one interface should be marked in this fashion. Currently enabling this option on an interface causes the relay to process all DHCP traffic similar to the \fI-i\fR option, in the future we may split the two more completely. This option is off by default. Note that enabling this option automatically enables the \fB-a\fR option. Keep in mind that using options such as \fB-m replace\fR or \fB-m discard\fR on relays upstream from one using \fB-U\fR can pose problems. The upstream relay will wipe out the initial agent option containing the link selection while leaving the re-purposed giaddr value in place, causing packets to go astray. .PP \fIOptions available in DHCPv6 mode only:\fR .TP -I Force use of the DHCPv6 Interface-ID option. This option is automatically sent when there are two or more downstream interfaces in use, to disambiguate between them. The \fB-I\fR option causes dhcrelay to send the option even if there is only one downstream interface. .TP -s subscriber-id Add an option with the specified subscriber-id into the packet. This feature is for testing rather than production as it will put the same subscriber-id into the packet for all clients. .TP -l [\fIaddress%\fR]\fIifname\fR[\fI#index\fR] Specifies the ``lower'' network interface for DHCPv6 relay mode: the interface on which queries will be received from clients or from other relay agents. At least one \fB-l\fR option must be included in the command line when running in DHCPv6 mode. The interface name \fIifname\fR is a mandatory parameter. The link address can be specified by \fIaddress%\fR; if it isn't, dhcrelay will use the first non-link-local address configured on the interface. The optional \fI#index\fR parameter specifies the interface index. .TP -u [\fIaddress%\fR]\fIifname\fR Specifies the ``upper'' network interface for DHCPv6 relay mode: the interface to which queries from clients and other relay agents should be forwarded. At least one \fB-u\fR option must be included in the command line when running in DHCPv6 mode. The interface name \fIifname\fR is a mandatory parameter. The destination unicast or multicast address can be specified by \fIaddress%\fR; if not specified, the relay agent will forward to the DHCPv6 \fIAll_DHCP_Relay_Agents_and_Servers\fR multicast address. .PP It is possible to specify the same interface with different addresses more than once, and even, when the system supports it, to use the same interface as both upper and lower interfaces. .SH SEE ALSO dhclient(8), dhcpd(8), RFC3315, RFC2132, RFC2131. .SH BUGS .PP Using the same interface on both upper and lower sides may cause loops, so when running this way, the maximum hop count should be set to a low value. .PP The loopback interface is not (yet) recognized as a valid interface. .SH AUTHOR .B dhcrelay(8) To learn more about Internet Systems Consortium, see .B https://www.isc.org dhcp-4.4.1/relay/dhcrelay.c000644 000765 000024 00000160220 13243301226 015767 0ustar00tmarkstaff000000 000000 /* dhcrelay.c DHCP/BOOTP Relay Agent. */ /* * Copyright(c) 2004-2018 by Internet Systems Consortium, Inc.("ISC") * Copyright(c) 1997-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #include #include #include #include TIME default_lease_time = 43200; /* 12 hours... */ TIME max_lease_time = 86400; /* 24 hours... */ struct tree_cache *global_options[256]; struct option *requested_opts[2]; /* Needed to prevent linking against conflex.c. */ int lexline; int lexchar; char *token_line; char *tlname; const char *path_dhcrelay_pid = _PATH_DHCRELAY_PID; isc_boolean_t no_dhcrelay_pid = ISC_FALSE; /* False (default) => we write and use a pid file */ isc_boolean_t no_pid_file = ISC_FALSE; int bogus_agent_drops = 0; /* Packets dropped because agent option field was specified and we're not relaying packets that already have an agent option specified. */ int bogus_giaddr_drops = 0; /* Packets sent to us to relay back to a client, but with a bogus giaddr. */ int client_packets_relayed = 0; /* Packets relayed from client to server. */ int server_packet_errors = 0; /* Errors sending packets to servers. */ int server_packets_relayed = 0; /* Packets relayed from server to client. */ int client_packet_errors = 0; /* Errors sending packets to clients. */ int add_agent_options = 0; /* If nonzero, add relay agent options. */ int add_rfc3527_suboption = 0; /* If nonzero, add RFC3527 link selection sub-option. */ int agent_option_errors = 0; /* Number of packets forwarded without agent options because there was no room. */ int drop_agent_mismatches = 0; /* If nonzero, drop server replies that don't have matching circuit-id's. */ int corrupt_agent_options = 0; /* Number of packets dropped because relay agent information option was bad. */ int missing_agent_option = 0; /* Number of packets dropped because no RAI option matching our ID was found. */ int bad_circuit_id = 0; /* Circuit ID option in matching RAI option did not match any known circuit ID. */ int missing_circuit_id = 0; /* Circuit ID option in matching RAI option was missing. */ int max_hop_count = 10; /* Maximum hop count */ int no_daemon = 0; int dfd[2] = { -1, -1 }; #ifdef DHCPv6 /* Force use of DHCPv6 interface-id option. */ isc_boolean_t use_if_id = ISC_FALSE; #endif /* Maximum size of a packet with agent options added. */ int dhcp_max_agent_option_packet_length = DHCP_MTU_MIN; /* What to do about packets we're asked to relay that already have a relay option: */ enum { forward_and_append, /* Forward and append our own relay option. */ forward_and_replace, /* Forward, but replace theirs with ours. */ forward_untouched, /* Forward without changes. */ discard } agent_relay_mode = forward_and_replace; u_int16_t local_port; u_int16_t remote_port; /* Relay agent server list. */ struct server_list { struct server_list *next; struct sockaddr_in to; } *servers; struct interface_info *uplink = NULL; #ifdef DHCPv6 struct stream_list { struct stream_list *next; struct interface_info *ifp; struct sockaddr_in6 link; int id; } *downstreams, *upstreams; static struct stream_list *parse_downstream(char *); static struct stream_list *parse_upstream(char *); static void setup_streams(void); /* * A pointer to a subscriber id to add to the message we forward. * This is primarily for testing purposes as we only have one id * for the entire relay and don't determine one per client which * would be more useful. */ char *dhcrelay_sub_id = NULL; #endif static void do_relay4(struct interface_info *, struct dhcp_packet *, unsigned int, unsigned int, struct iaddr, struct hardware *); static int add_relay_agent_options(struct interface_info *, struct dhcp_packet *, unsigned, struct in_addr); static int find_interface_by_agent_option(struct dhcp_packet *, struct interface_info **, u_int8_t *, int); static int strip_relay_agent_options(struct interface_info *, struct interface_info **, struct dhcp_packet *, unsigned); static void request_v4_interface(const char* name, int flags); static const char copyright[] = "Copyright 2004-2018 Internet Systems Consortium."; static const char arr[] = "All rights reserved."; static const char message[] = "Internet Systems Consortium DHCP Relay Agent"; static const char url[] = "For info, please visit https://www.isc.org/software/dhcp/"; char *progname; #ifdef DHCPv6 #ifdef RELAY_PORT #define DHCRELAY_USAGE \ "Usage: %s [-4] [-d] [-q] [-a] [-D]\n" \ " [-A ] [-c ]\n" \ " [-p | -rp ]\n" \ " [-pf ] [--no-pid]\n"\ " [-m append|replace|forward|discard]\n" \ " [-i interface0 [ ... -i interfaceN]\n" \ " [-iu interface0 [ ... -iu interfaceN]\n" \ " [-id interface0 [ ... -id interfaceN]\n" \ " [-U interface]\n" \ " server0 [ ... serverN]\n\n" \ " %s -6 [-d] [-q] [-I] [-c ]\n" \ " [-p | -rp ]\n" \ " [-pf ] [--no-pid]\n" \ " [-s ]\n" \ " -l lower0 [ ... -l lowerN]\n" \ " -u upper0 [ ... -u upperN]\n" \ " lower (client link): [address%%]interface[#index]\n" \ " upper (server link): [address%%]interface\n\n" \ " %s {--version|--help|-h}" #else #define DHCRELAY_USAGE \ "Usage: %s [-4] [-d] [-q] [-a] [-D]\n" \ " [-A ] [-c ] [-p ]\n" \ " [-pf ] [--no-pid]\n"\ " [-m append|replace|forward|discard]\n" \ " [-i interface0 [ ... -i interfaceN]\n" \ " [-iu interface0 [ ... -iu interfaceN]\n" \ " [-id interface0 [ ... -id interfaceN]\n" \ " [-U interface]\n" \ " server0 [ ... serverN]\n\n" \ " %s -6 [-d] [-q] [-I] [-c ] [-p ]\n" \ " [-pf ] [--no-pid]\n" \ " [-s ]\n" \ " -l lower0 [ ... -l lowerN]\n" \ " -u upper0 [ ... -u upperN]\n" \ " lower (client link): [address%%]interface[#index]\n" \ " upper (server link): [address%%]interface\n\n" \ " %s {--version|--help|-h}" #endif #else /* !DHCPv6 */ #ifdef RELAY_PORT #define DHCRELAY_USAGE \ "Usage: %s [-d] [-q] [-a] [-D] [-A ] [-c ]\n" \ " [-p | -rp ]\n" \ " [-pf ] [--no-pid]\n" \ " [-m append|replace|forward|discard]\n" \ " [-i interface0 [ ... -i interfaceN]\n" \ " [-iu interface0 [ ... -iu interfaceN]\n" \ " [-id interface0 [ ... -id interfaceN]\n" \ " [-U interface]\n" \ " server0 [ ... serverN]\n\n" \ " %s {--version|--help|-h}" #else #define DHCRELAY_USAGE \ "Usage: %s [-d] [-q] [-a] [-D] [-A ] [-c ] [-p ]\n" \ " [-pf ] [--no-pid]\n" \ " [-m append|replace|forward|discard]\n" \ " [-i interface0 [ ... -i interfaceN]\n" \ " [-iu interface0 [ ... -iu interfaceN]\n" \ " [-id interface0 [ ... -id interfaceN]\n" \ " [-U interface]\n" \ " server0 [ ... serverN]\n\n" \ " %s {--version|--help|-h}" #endif #endif /*! * * \brief Print the generic usage message * * If the user has provided an incorrect command line print out * the description of the command line. The arguments provide * a way for the caller to request more specific information about * the error be printed as well. Mostly this will be that some * comamnd doesn't include its argument. * * \param sfmt - The basic string and format for the specific error * \param sarg - Generally the offending argument from the comamnd line. * * \return Nothing */ static const char use_noarg[] = "No argument for command: %s"; #ifdef RELAY_PORT static const char use_port_defined[] = "Port already set, %s inappropriate"; #if !defined (USE_BPF_RECEIVE) && !defined (USE_LPF_RECEIVE) static const char bpf_sock_support[] = "Only LPF and BPF are supported: %s"; #endif #endif #ifdef DHCPv6 static const char use_badproto[] = "Protocol already set, %s inappropriate"; static const char use_v4command[] = "Command not used for DHCPv6: %s"; static const char use_v6command[] = "Command not used for DHCPv4: %s"; #endif static void usage(const char *sfmt, const char *sarg) { log_info("%s %s", message, PACKAGE_VERSION); log_info(copyright); log_info(arr); log_info(url); /* If desired print out the specific error message */ #ifdef PRINT_SPECIFIC_CL_ERRORS if (sfmt != NULL) log_error(sfmt, sarg); #endif log_fatal(DHCRELAY_USAGE, #ifdef DHCPv6 isc_file_basename(progname), #endif isc_file_basename(progname), isc_file_basename(progname)); } int main(int argc, char **argv) { isc_result_t status; struct servent *ent; struct server_list *sp = NULL; char *service_local = NULL, *service_remote = NULL; u_int16_t port_local = 0, port_remote = 0; int quiet = 0; int fd; int i; #ifdef RELAY_PORT int port_defined = 0; #endif #ifdef DHCPv6 struct stream_list *sl = NULL; int local_family_set = 0; #endif #ifdef OLD_LOG_NAME progname = "dhcrelay"; #else progname = argv[0]; #endif /* Make sure that file descriptors 0(stdin), 1,(stdout), and 2(stderr) are open. To do this, we assume that when we open a file the lowest available file descriptor is used. */ fd = open("/dev/null", O_RDWR); if (fd == 0) fd = open("/dev/null", O_RDWR); if (fd == 1) fd = open("/dev/null", O_RDWR); if (fd == 2) log_perror = 0; /* No sense logging to /dev/null. */ else if (fd != -1) close(fd); openlog(isc_file_basename(progname), DHCP_LOG_OPTIONS, LOG_DAEMON); #if !defined(DEBUG) setlogmask(LOG_UPTO(LOG_INFO)); #endif /* Parse arguments changing no_daemon */ for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "-d")) { no_daemon = 1; } else if (!strcmp(argv[i], "--version")) { log_info("isc-dhcrelay-%s", PACKAGE_VERSION); exit(0); } else if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "-h")) { log_info(DHCRELAY_USAGE, #ifdef DHCPv6 isc_file_basename(progname), #endif isc_file_basename(progname), isc_file_basename(progname)); exit(0); } } /* When not forbidden prepare to become a daemon */ if (!no_daemon) { int pid; if (pipe(dfd) == -1) log_fatal("Can't get pipe: %m"); if ((pid = fork ()) < 0) log_fatal("Can't fork daemon: %m"); if (pid != 0) { /* Parent: wait for the child to start */ int n; (void) close(dfd[1]); do { char buf; n = read(dfd[0], &buf, 1); if (n == 1) _exit(0); } while (n == -1 && errno == EINTR); _exit(1); } /* Child */ (void) close(dfd[0]); } /* Set up the isc and dns library managers */ status = dhcp_context_create(DHCP_CONTEXT_PRE_DB, NULL, NULL); if (status != ISC_R_SUCCESS) log_fatal("Can't initialize context: %s", isc_result_totext(status)); /* Set up the OMAPI. */ status = omapi_init(); if (status != ISC_R_SUCCESS) log_fatal("Can't initialize OMAPI: %s", isc_result_totext(status)); /* Set up the OMAPI wrappers for the interface object. */ interface_setup(); for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "-4")) { #ifdef DHCPv6 if (local_family_set && (local_family == AF_INET6)) { usage(use_badproto, "-4"); } local_family_set = 1; local_family = AF_INET; } else if (!strcmp(argv[i], "-6")) { if (local_family_set && (local_family == AF_INET)) { usage(use_badproto, "-6"); } local_family_set = 1; local_family = AF_INET6; #endif } else if (!strcmp(argv[i], "-d")) { /* no_daemon = 1; */ } else if (!strcmp(argv[i], "-q")) { quiet = 1; quiet_interface_discovery = 1; } else if (!strcmp(argv[i], "-p")) { if (++i == argc) usage(use_noarg, argv[i-1]); #ifdef RELAY_PORT if (port_defined) usage(use_port_defined, argv[i-1]); port_defined = 1; #endif local_port = validate_port(argv[i]); log_debug("binding to user-specified port %d", ntohs(local_port)); #ifdef RELAY_PORT } else if (!strcmp(argv[i], "-rp")) { if (++i == argc) usage(use_noarg, argv[i-1]); if (port_defined) usage(use_port_defined, argv[i-1]); port_defined = 1; relay_port = validate_port(argv[i]); log_debug("binding to user-specified relay port %d", ntohs(relay_port)); add_agent_options = 1; #endif } else if (!strcmp(argv[i], "-c")) { int hcount; if (++i == argc) usage(use_noarg, argv[i-1]); hcount = atoi(argv[i]); if (hcount <= 255) max_hop_count= hcount; else usage("Bad hop count to -c: %s", argv[i]); } else if (!strcmp(argv[i], "-i")) { #ifdef DHCPv6 if (local_family_set && (local_family == AF_INET6)) { usage(use_v4command, argv[i]); } local_family_set = 1; local_family = AF_INET; #endif if (++i == argc) { usage(use_noarg, argv[i-1]); } request_v4_interface(argv[i], INTERFACE_STREAMS); } else if (!strcmp(argv[i], "-iu")) { #ifdef DHCPv6 if (local_family_set && (local_family == AF_INET6)) { usage(use_v4command, argv[i]); } local_family_set = 1; local_family = AF_INET; #endif if (++i == argc) { usage(use_noarg, argv[i-1]); } request_v4_interface(argv[i], INTERFACE_UPSTREAM); } else if (!strcmp(argv[i], "-id")) { #ifdef DHCPv6 if (local_family_set && (local_family == AF_INET6)) { usage(use_v4command, argv[i]); } local_family_set = 1; local_family = AF_INET; #endif if (++i == argc) { usage(use_noarg, argv[i-1]); } request_v4_interface(argv[i], INTERFACE_DOWNSTREAM); } else if (!strcmp(argv[i], "-a")) { #ifdef DHCPv6 if (local_family_set && (local_family == AF_INET6)) { usage(use_v4command, argv[i]); } local_family_set = 1; local_family = AF_INET; #endif add_agent_options = 1; } else if (!strcmp(argv[i], "-A")) { #ifdef DHCPv6 if (local_family_set && (local_family == AF_INET6)) { usage(use_v4command, argv[i]); } local_family_set = 1; local_family = AF_INET; #endif if (++i == argc) usage(use_noarg, argv[i-1]); dhcp_max_agent_option_packet_length = atoi(argv[i]); if (dhcp_max_agent_option_packet_length > DHCP_MTU_MAX) log_fatal("%s: packet length exceeds " "longest possible MTU\n", argv[i]); } else if (!strcmp(argv[i], "-m")) { #ifdef DHCPv6 if (local_family_set && (local_family == AF_INET6)) { usage(use_v4command, argv[i]); } local_family_set = 1; local_family = AF_INET; #endif if (++i == argc) usage(use_noarg, argv[i-1]); if (!strcasecmp(argv[i], "append")) { agent_relay_mode = forward_and_append; } else if (!strcasecmp(argv[i], "replace")) { agent_relay_mode = forward_and_replace; } else if (!strcasecmp(argv[i], "forward")) { agent_relay_mode = forward_untouched; } else if (!strcasecmp(argv[i], "discard")) { agent_relay_mode = discard; } else usage("Unknown argument to -m: %s", argv[i]); } else if (!strcmp(argv [i], "-U")) { if (++i == argc) usage(use_noarg, argv[i-1]); if (uplink) { usage("more than one uplink (-U) specified: %s" ,argv[i]); } /* Allocate the uplink interface */ status = interface_allocate(&uplink, MDL); if (status != ISC_R_SUCCESS) { log_fatal("%s: uplink interface_allocate: %s", argv[i], isc_result_totext(status)); } if (strlen(argv[i]) >= sizeof(uplink->name)) { log_fatal("%s: uplink name too long," " it cannot exceed: %ld characters", argv[i], (long)(sizeof(uplink->name) - 1)); } uplink->name[sizeof(uplink->name) - 1] = 0x00; strncpy(uplink->name, argv[i], sizeof(uplink->name) - 1); interface_snorf(uplink, (INTERFACE_REQUESTED | INTERFACE_STREAMS)); /* Turn on -a, in case they don't do so explicitly */ add_agent_options = 1; add_rfc3527_suboption = 1; } else if (!strcmp(argv[i], "-D")) { #ifdef DHCPv6 if (local_family_set && (local_family == AF_INET6)) { usage(use_v4command, argv[i]); } local_family_set = 1; local_family = AF_INET; #endif drop_agent_mismatches = 1; #ifdef DHCPv6 } else if (!strcmp(argv[i], "-I")) { if (local_family_set && (local_family == AF_INET)) { usage(use_v6command, argv[i]); } local_family_set = 1; local_family = AF_INET6; use_if_id = ISC_TRUE; } else if (!strcmp(argv[i], "-l")) { if (local_family_set && (local_family == AF_INET)) { usage(use_v6command, argv[i]); } local_family_set = 1; local_family = AF_INET6; if (downstreams != NULL) use_if_id = ISC_TRUE; if (++i == argc) usage(use_noarg, argv[i-1]); sl = parse_downstream(argv[i]); sl->next = downstreams; downstreams = sl; } else if (!strcmp(argv[i], "-u")) { if (local_family_set && (local_family == AF_INET)) { usage(use_v6command, argv[i]); } local_family_set = 1; local_family = AF_INET6; if (++i == argc) usage(use_noarg, argv[i-1]); sl = parse_upstream(argv[i]); sl->next = upstreams; upstreams = sl; } else if (!strcmp(argv[i], "-s")) { if (local_family_set && (local_family == AF_INET)) { usage(use_v6command, argv[i]); } local_family_set = 1; local_family = AF_INET6; if (++i == argc) usage(use_noarg, argv[i-1]); dhcrelay_sub_id = argv[i]; #endif } else if (!strcmp(argv[i], "-pf")) { if (++i == argc) usage(use_noarg, argv[i-1]); path_dhcrelay_pid = argv[i]; no_dhcrelay_pid = ISC_TRUE; } else if (!strcmp(argv[i], "--no-pid")) { no_pid_file = ISC_TRUE; } else if (argv[i][0] == '-') { usage("Unknown command: %s", argv[i]); } else { struct hostent *he; struct in_addr ia, *iap = NULL; #ifdef DHCPv6 if (local_family_set && (local_family == AF_INET6)) { usage(use_v4command, argv[i]); } local_family_set = 1; local_family = AF_INET; #endif if (inet_aton(argv[i], &ia)) { iap = &ia; } else { he = gethostbyname(argv[i]); if (!he) { log_error("%s: host unknown", argv[i]); } else { iap = ((struct in_addr *) he->h_addr_list[0]); } } if (iap) { sp = ((struct server_list *) dmalloc(sizeof *sp, MDL)); if (!sp) log_fatal("no memory for server.\n"); sp->next = servers; servers = sp; memcpy(&sp->to.sin_addr, iap, sizeof *iap); } } } #if defined(RELAY_PORT) && \ !defined (USE_BPF_RECEIVE) && !defined (USE_LPF_RECEIVE) if (relay_port && (local_family == AF_INET)) usage(bpf_sock_support, "-rp"); #endif /* * If the user didn't specify a pid file directly * find one from environment variables or defaults */ if (no_dhcrelay_pid == ISC_FALSE) { if (local_family == AF_INET) { path_dhcrelay_pid = getenv("PATH_DHCRELAY_PID"); if (path_dhcrelay_pid == NULL) path_dhcrelay_pid = _PATH_DHCRELAY_PID; } #ifdef DHCPv6 else { path_dhcrelay_pid = getenv("PATH_DHCRELAY6_PID"); if (path_dhcrelay_pid == NULL) path_dhcrelay_pid = _PATH_DHCRELAY6_PID; } #endif } if (!quiet) { log_info("%s %s", message, PACKAGE_VERSION); log_info(copyright); log_info(arr); log_info(url); } else log_perror = 0; /* Set default port */ if (local_family == AF_INET) { service_local = "bootps"; service_remote = "bootpc"; port_local = htons(67); port_remote = htons(68); } #ifdef DHCPv6 else { service_local = "dhcpv6-server"; service_remote = "dhcpv6-client"; port_local = htons(547); port_remote = htons(546); } #endif if (!local_port) { ent = getservbyname(service_local, "udp"); if (ent) local_port = ent->s_port; else local_port = port_local; ent = getservbyname(service_remote, "udp"); if (ent) remote_port = ent->s_port; else remote_port = port_remote; endservent(); } if (local_family == AF_INET) { /* We need at least one server */ if (servers == NULL) { log_fatal("No servers specified."); } /* Set up the server sockaddrs. */ for (sp = servers; sp; sp = sp->next) { sp->to.sin_port = local_port; sp->to.sin_family = AF_INET; #ifdef HAVE_SA_LEN sp->to.sin_len = sizeof sp->to; #endif } } #ifdef DHCPv6 else { unsigned code; /* We need at least one upstream and one downstream interface */ if (upstreams == NULL || downstreams == NULL) { log_info("Must specify at least one lower " "and one upper interface.\n"); usage(NULL, NULL); } /* Set up the initial dhcp option universe. */ initialize_common_option_spaces(); /* Check requested options. */ code = D6O_RELAY_MSG; if (!option_code_hash_lookup(&requested_opts[0], dhcpv6_universe.code_hash, &code, 0, MDL)) log_fatal("Unable to find the RELAY_MSG " "option definition."); code = D6O_INTERFACE_ID; if (!option_code_hash_lookup(&requested_opts[1], dhcpv6_universe.code_hash, &code, 0, MDL)) log_fatal("Unable to find the INTERFACE_ID " "option definition."); } #endif /* Get the current time... */ gettimeofday(&cur_tv, NULL); /* Discover all the network interfaces. */ discover_interfaces(DISCOVER_RELAY); #ifdef DHCPv6 if (local_family == AF_INET6) setup_streams(); #endif /* Become a daemon... */ if (!no_daemon) { char buf = 0; FILE *pf; int pfdesc; log_perror = 0; /* Signal parent we started successfully. */ if (dfd[0] != -1 && dfd[1] != -1) { if (write(dfd[1], &buf, 1) != 1) log_fatal("write to parent: %m"); (void) close(dfd[1]); dfd[0] = dfd[1] = -1; } /* Create the pid file. */ if (no_pid_file == ISC_FALSE) { pfdesc = open(path_dhcrelay_pid, O_CREAT | O_TRUNC | O_WRONLY, 0644); if (pfdesc < 0) { log_error("Can't create %s: %m", path_dhcrelay_pid); } else { pf = fdopen(pfdesc, "w"); if (!pf) log_error("Can't fdopen %s: %m", path_dhcrelay_pid); else { fprintf(pf, "%ld\n",(long)getpid()); fclose(pf); } } } (void) close(0); (void) close(1); (void) close(2); (void) setsid(); IGNORE_RET (chdir("/")); } /* Set up the packet handler... */ if (local_family == AF_INET) bootp_packet_handler = do_relay4; #ifdef DHCPv6 else dhcpv6_packet_handler = do_packet6; #endif #if defined(ENABLE_GENTLE_SHUTDOWN) /* no signal handlers until we deal with the side effects */ /* install signal handlers */ signal(SIGINT, dhcp_signal_handler); /* control-c */ signal(SIGTERM, dhcp_signal_handler); /* kill */ #endif /* Start dispatching packets and timeouts... */ dispatch(); /* In fact dispatch() never returns. */ return (0); } static void do_relay4(struct interface_info *ip, struct dhcp_packet *packet, unsigned int length, unsigned int from_port, struct iaddr from, struct hardware *hfrom) { struct server_list *sp; struct sockaddr_in to; struct interface_info *out; struct hardware hto, *htop; if (packet->hlen > sizeof packet->chaddr) { log_info("Discarding packet with invalid hlen, received on " "%s interface.", ip->name); return; } if (ip->address_count < 1 || ip->addresses == NULL) { log_info("Discarding packet received on %s interface that " "has no IPv4 address assigned.", ip->name); return; } /* Find the interface that corresponds to the giaddr in the packet. */ if (packet->giaddr.s_addr) { for (out = interfaces; out; out = out->next) { int i; for (i = 0 ; i < out->address_count ; i++ ) { if (out->addresses[i].s_addr == packet->giaddr.s_addr) { i = -1; break; } } if (i == -1) break; } } else { out = NULL; } /* If it's a bootreply, forward it to the client. */ if (packet->op == BOOTREPLY) { if (!(ip->flags & INTERFACE_UPSTREAM)) { log_debug("Dropping reply received on %s", ip->name); return; } if (!(packet->flags & htons(BOOTP_BROADCAST)) && can_unicast_without_arp(out)) { to.sin_addr = packet->yiaddr; to.sin_port = remote_port; /* and hardware address is not broadcast */ htop = &hto; } else { to.sin_addr.s_addr = htonl(INADDR_BROADCAST); to.sin_port = remote_port; /* hardware address is broadcast */ htop = NULL; } to.sin_family = AF_INET; #ifdef HAVE_SA_LEN to.sin_len = sizeof to; #endif memcpy(&hto.hbuf[1], packet->chaddr, packet->hlen); hto.hbuf[0] = packet->htype; hto.hlen = packet->hlen + 1; /* Wipe out the agent relay options and, if possible, figure out which interface to use based on the contents of the option that we put on the request to which the server is replying. */ if (!(length = strip_relay_agent_options(ip, &out, packet, length))) return; if (!out) { log_error("Packet to bogus giaddr %s.\n", inet_ntoa(packet->giaddr)); ++bogus_giaddr_drops; return; } if (send_packet(out, NULL, packet, length, out->addresses[0], &to, htop) < 0) { ++server_packet_errors; } else { log_debug("Forwarded BOOTREPLY for %s to %s", print_hw_addr(packet->htype, packet->hlen, packet->chaddr), inet_ntoa(to.sin_addr)); ++server_packets_relayed; } return; } /* If giaddr matches one of our addresses, ignore the packet - we just sent it. */ if (out) return; if (!(ip->flags & INTERFACE_DOWNSTREAM)) { log_debug("Dropping request received on %s", ip->name); return; } /* Add relay agent options if indicated. If something goes wrong, * drop the packet. Note this may set packet->giaddr if RFC3527 * is enabled. */ if (!(length = add_relay_agent_options(ip, packet, length, ip->addresses[0]))) return; /* If giaddr is not already set, Set it so the server can figure out what net it's from and so that we can later forward the response to the correct net. If it's already set, the response will be sent directly to the relay agent that set giaddr, so we won't see it. */ if (!packet->giaddr.s_addr) packet->giaddr = ip->addresses[0]; if (packet->hops < max_hop_count) packet->hops = packet->hops + 1; else return; /* Otherwise, it's a BOOTREQUEST, so forward it to all the servers. */ for (sp = servers; sp; sp = sp->next) { if (send_packet((fallback_interface ? fallback_interface : interfaces), NULL, packet, length, ip->addresses[0], &sp->to, NULL) < 0) { ++client_packet_errors; } else { log_debug("Forwarded BOOTREQUEST for %s to %s", print_hw_addr(packet->htype, packet->hlen, packet->chaddr), inet_ntoa(sp->to.sin_addr)); ++client_packets_relayed; } } } /* Strip any Relay Agent Information options from the DHCP packet option buffer. If there is a circuit ID suboption, look up the outgoing interface based upon it. */ static int strip_relay_agent_options(struct interface_info *in, struct interface_info **out, struct dhcp_packet *packet, unsigned length) { int is_dhcp = 0; u_int8_t *op, *nextop, *sp, *max; int good_agent_option = 0; int status; /* If we're not adding agent options to packets, we're not taking them out either. */ if (!add_agent_options) return (length); /* If there's no cookie, it's a bootp packet, so we should just forward it unchanged. */ if (memcmp(packet->options, DHCP_OPTIONS_COOKIE, 4)) return (length); max = ((u_int8_t *)packet) + length; sp = op = &packet->options[4]; while (op < max) { switch(*op) { /* Skip padding... */ case DHO_PAD: if (sp != op) *sp = *op; ++op; ++sp; continue; /* If we see a message type, it's a DHCP packet. */ case DHO_DHCP_MESSAGE_TYPE: is_dhcp = 1; goto skip; break; /* Quit immediately if we hit an End option. */ case DHO_END: if (sp != op) *sp++ = *op++; goto out; case DHO_DHCP_AGENT_OPTIONS: /* We shouldn't see a relay agent option in a packet before we've seen the DHCP packet type, but if we do, we have to leave it alone. */ if (!is_dhcp) goto skip; /* Do not process an agent option if it exceeds the * buffer. Fail this packet. */ nextop = op + op[1] + 2; if (nextop > max) return (0); status = find_interface_by_agent_option(packet, out, op + 2, op[1]); if (status == -1 && drop_agent_mismatches) return (0); if (status) good_agent_option = 1; op = nextop; break; skip: /* Skip over other options. */ default: /* Fail if processing this option will exceed the * buffer(op[1] is malformed). */ nextop = op + op[1] + 2; if (nextop > max) return (0); if (sp != op) { memmove(sp, op, op[1] + 2); sp += op[1] + 2; op = nextop; } else op = sp = nextop; break; } } out: /* If it's not a DHCP packet, we're not supposed to touch it. */ if (!is_dhcp) return (length); /* If none of the agent options we found matched, or if we didn't find any agent options, count this packet as not having any matching agent options, and if we're relying on agent options to determine the outgoing interface, drop the packet. */ if (!good_agent_option) { ++missing_agent_option; if (drop_agent_mismatches) return (0); } /* Adjust the length... */ if (sp != op) { length = sp -((u_int8_t *)packet); /* Make sure the packet isn't short(this is unlikely, but WTH) */ if (length < BOOTP_MIN_LEN) { memset(sp, DHO_PAD, BOOTP_MIN_LEN - length); length = BOOTP_MIN_LEN; } } return (length); } /* Find an interface that matches the circuit ID specified in the Relay Agent Information option. If one is found, store it through the pointer given; otherwise, leave the existing pointer alone. We actually deviate somewhat from the current specification here: if the option buffer is corrupt, we suggest that the caller not respond to this packet. If the circuit ID doesn't match any known interface, we suggest that the caller to drop the packet. Only if we find a circuit ID that matches an existing interface do we tell the caller to go ahead and process the packet. */ static int find_interface_by_agent_option(struct dhcp_packet *packet, struct interface_info **out, u_int8_t *buf, int len) { int i = 0; u_int8_t *circuit_id = 0; unsigned circuit_id_len = 0; struct interface_info *ip; while (i < len) { /* If the next agent option overflows the end of the packet, the agent option buffer is corrupt. */ if (i + 1 == len || i + buf[i + 1] + 2 > len) { ++corrupt_agent_options; return (-1); } switch(buf[i]) { /* Remember where the circuit ID is... */ case RAI_CIRCUIT_ID: circuit_id = &buf[i + 2]; circuit_id_len = buf[i + 1]; i += circuit_id_len + 2; continue; default: i += buf[i + 1] + 2; break; } } /* If there's no circuit ID, it's not really ours, tell the caller it's no good. */ if (!circuit_id) { ++missing_circuit_id; return (-1); } /* Scan the interface list looking for an interface whose name matches the one specified in circuit_id. */ for (ip = interfaces; ip; ip = ip->next) { if (ip->circuit_id && ip->circuit_id_len == circuit_id_len && !memcmp(ip->circuit_id, circuit_id, circuit_id_len)) break; } /* If we got a match, use it. */ if (ip) { *out = ip; return (1); } /* If we didn't get a match, the circuit ID was bogus. */ ++bad_circuit_id; return (-1); } /* * Examine a packet to see if it's a candidate to have a Relay * Agent Information option tacked onto its tail. If it is, tack * the option on. */ static int add_relay_agent_options(struct interface_info *ip, struct dhcp_packet *packet, unsigned length, struct in_addr giaddr) { int is_dhcp = 0, mms; unsigned optlen; u_int8_t *op, *nextop, *sp, *max, *end_pad = NULL; int adding_link_select; /* If we're not adding agent options to packets, we can skip this. */ if (!add_agent_options) return (length); /* If there's no cookie, it's a bootp packet, so we should just forward it unchanged. */ if (memcmp(packet->options, DHCP_OPTIONS_COOKIE, 4)) return (length); max = ((u_int8_t *)packet) + dhcp_max_agent_option_packet_length; /* Add link selection suboption if enabled and we're the first relay */ adding_link_select = (add_rfc3527_suboption && (packet->giaddr.s_addr == 0)); /* Commence processing after the cookie. */ sp = op = &packet->options[4]; while (op < max) { switch(*op) { /* Skip padding... */ case DHO_PAD: /* Remember the first pad byte so we can commandeer * padded space. * * XXX: Is this really a good idea? Sure, we can * seemingly reduce the packet while we're looking, * but if the packet was signed by the client then * this padding is part of the checksum(RFC3118), * and its nonpresence would break authentication. */ if (end_pad == NULL) end_pad = sp; if (sp != op) *sp++ = *op++; else sp = ++op; continue; /* If we see a message type, it's a DHCP packet. */ case DHO_DHCP_MESSAGE_TYPE: is_dhcp = 1; goto skip; /* * If there's a maximum message size option, we * should pay attention to it */ case DHO_DHCP_MAX_MESSAGE_SIZE: mms = ntohs(*(op + 2)); if (mms < dhcp_max_agent_option_packet_length && mms >= DHCP_MTU_MIN) max = ((u_int8_t *)packet) + mms; goto skip; /* Quit immediately if we hit an End option. */ case DHO_END: goto out; case DHO_DHCP_AGENT_OPTIONS: /* We shouldn't see a relay agent option in a packet before we've seen the DHCP packet type, but if we do, we have to leave it alone. */ if (!is_dhcp) goto skip; end_pad = NULL; /* There's already a Relay Agent Information option in this packet. How embarrassing. Decide what to do based on the mode the user specified. */ switch(agent_relay_mode) { case forward_and_append: goto skip; case forward_untouched: return (length); case discard: return (0); case forward_and_replace: default: break; } /* Skip over the agent option and start copying if we aren't copying already. */ op += op[1] + 2; break; skip: /* Skip over other options. */ default: /* Fail if processing this option will exceed the * buffer(op[1] is malformed). */ nextop = op + op[1] + 2; if (nextop > max) return (0); end_pad = NULL; if (sp != op) { memmove(sp, op, op[1] + 2); sp += op[1] + 2; op = nextop; } else op = sp = nextop; break; } } out: /* If it's not a DHCP packet, we're not supposed to touch it. */ if (!is_dhcp) return (length); /* If the packet was padded out, we can store the agent option at the beginning of the padding. */ if (end_pad != NULL) sp = end_pad; #if 0 /* Remember where the end of the packet was after parsing it. */ op = sp; #endif /* Sanity check. Had better not ever happen. */ if ((ip->circuit_id_len > 255) ||(ip->circuit_id_len < 1)) log_fatal("Circuit ID length %d out of range [1-255] on " "%s\n", ip->circuit_id_len, ip->name); optlen = ip->circuit_id_len + 2; /* RAI_CIRCUIT_ID + len */ if (ip->remote_id) { if (ip->remote_id_len > 255 || ip->remote_id_len < 1) log_fatal("Remote ID length %d out of range [1-255] " "on %s\n", ip->remote_id_len, ip->name); optlen += ip->remote_id_len + 2; /* RAI_REMOTE_ID + len */ } if (adding_link_select) { optlen += 6; } #ifdef RELAY_PORT if (relay_port) { optlen += 2; } #endif /* We do not support relay option fragmenting(multiple options to * support an option data exceeding 255 bytes). */ if ((optlen < 3) ||(optlen > 255)) log_fatal("Total agent option length(%u) out of range " "[3 - 255] on %s\n", optlen, ip->name); /* * Is there room for the option, its code+len, and DHO_END? * If not, forward without adding the option. */ if (max - sp >= optlen + 3) { log_debug("Adding %d-byte relay agent option", optlen + 3); /* Okay, cons up *our* Relay Agent Information option. */ *sp++ = DHO_DHCP_AGENT_OPTIONS; *sp++ = optlen; /* Copy in the circuit id... */ *sp++ = RAI_CIRCUIT_ID; *sp++ = ip->circuit_id_len; memcpy(sp, ip->circuit_id, ip->circuit_id_len); sp += ip->circuit_id_len; /* Copy in remote ID... */ if (ip->remote_id) { *sp++ = RAI_REMOTE_ID; *sp++ = ip->remote_id_len; memcpy(sp, ip->remote_id, ip->remote_id_len); sp += ip->remote_id_len; } /* RFC3527: Use the inbound packet's interface address in * the link selection suboption and set the outbound giaddr * to the uplink address. */ if (adding_link_select) { *sp++ = RAI_LINK_SELECT; *sp++ = 4u; memcpy(sp, &giaddr.s_addr, 4); sp += 4; packet->giaddr = uplink->addresses[0]; log_debug ("Adding link selection suboption" " with addr: %s", inet_ntoa(giaddr)); } #ifdef RELAY_PORT /* draft-ietf-dhc-relay-port-10.txt section 5.1 */ if (relay_port) { *sp++ = RAI_RELAY_PORT; *sp++ = 0u; } #endif } else { ++agent_option_errors; log_error("No room in packet (used %d of %d) " "for %d-byte relay agent option: omitted", (int) (sp - ((u_int8_t *) packet)), (int) (max - ((u_int8_t *) packet)), optlen + 3); } /* * Deposit an END option unless the packet is full (shouldn't * be possible). */ if (sp < max) *sp++ = DHO_END; /* Recalculate total packet length. */ length = sp -((u_int8_t *)packet); /* Make sure the packet isn't short(this is unlikely, but WTH) */ if (length < BOOTP_MIN_LEN) { memset(sp, DHO_PAD, BOOTP_MIN_LEN - length); return (BOOTP_MIN_LEN); } return (length); } #ifdef DHCPv6 /* * Parse a downstream argument: [address%]interface[#index]. */ static struct stream_list * parse_downstream(char *arg) { struct stream_list *dp, *up; struct interface_info *ifp = NULL; char *ifname, *addr, *iid; isc_result_t status; if (!supports_multiple_interfaces(ifp) && (downstreams != NULL)) log_fatal("No support for multiple interfaces."); /* Decode the argument. */ ifname = strchr(arg, '%'); if (ifname == NULL) { ifname = arg; addr = NULL; } else { *ifname++ = '\0'; addr = arg; } iid = strchr(ifname, '#'); if (iid != NULL) { *iid++ = '\0'; } if (strlen(ifname) >= sizeof(ifp->name)) { usage("Interface name '%s' too long", ifname); } /* Don't declare twice. */ for (dp = downstreams; dp; dp = dp->next) { if (strcmp(ifname, dp->ifp->name) == 0) log_fatal("Down interface '%s' declared twice.", ifname); } /* Share with up side? */ for (up = upstreams; up; up = up->next) { if (strcmp(ifname, up->ifp->name) == 0) { log_info("parse_downstream: Interface '%s' is " "both down and up.", ifname); ifp = up->ifp; break; } } /* New interface. */ if (ifp == NULL) { status = interface_allocate(&ifp, MDL); if (status != ISC_R_SUCCESS) log_fatal("%s: interface_allocate: %s", arg, isc_result_totext(status)); strcpy(ifp->name, ifname); if (interfaces) { interface_reference(&ifp->next, interfaces, MDL); interface_dereference(&interfaces, MDL); } interface_reference(&interfaces, ifp, MDL); } ifp->flags |= INTERFACE_REQUESTED | INTERFACE_DOWNSTREAM; /* New downstream. */ dp = (struct stream_list *) dmalloc(sizeof(*dp), MDL); if (!dp) log_fatal("No memory for downstream."); dp->ifp = ifp; if (iid != NULL) { dp->id = atoi(iid); } else { dp->id = -1; } /* !addr case handled by setup. */ if (addr && (inet_pton(AF_INET6, addr, &dp->link.sin6_addr) <= 0)) log_fatal("Bad link address '%s'", addr); return dp; } /* * Parse an upstream argument: [address]%interface. */ static struct stream_list * parse_upstream(char *arg) { struct stream_list *up, *dp; struct interface_info *ifp = NULL; char *ifname, *addr; isc_result_t status; /* Decode the argument. */ ifname = strchr(arg, '%'); if (ifname == NULL) { ifname = arg; addr = All_DHCP_Servers; } else { *ifname++ = '\0'; addr = arg; } if (strlen(ifname) >= sizeof(ifp->name)) { log_fatal("Interface name '%s' too long", ifname); } /* Shared up interface? */ for (up = upstreams; up; up = up->next) { if (strcmp(ifname, up->ifp->name) == 0) { ifp = up->ifp; break; } } for (dp = downstreams; dp; dp = dp->next) { if (strcmp(ifname, dp->ifp->name) == 0) { log_info("parse_upstream: Interface '%s' is " "both down and up.", ifname); ifp = dp->ifp; break; } } /* New interface. */ if (ifp == NULL) { status = interface_allocate(&ifp, MDL); if (status != ISC_R_SUCCESS) log_fatal("%s: interface_allocate: %s", arg, isc_result_totext(status)); strcpy(ifp->name, ifname); if (interfaces) { interface_reference(&ifp->next, interfaces, MDL); interface_dereference(&interfaces, MDL); } interface_reference(&interfaces, ifp, MDL); } ifp->flags |= INTERFACE_REQUESTED | INTERFACE_UPSTREAM; /* New upstream. */ up = (struct stream_list *) dmalloc(sizeof(*up), MDL); if (up == NULL) log_fatal("No memory for upstream."); up->ifp = ifp; if (inet_pton(AF_INET6, addr, &up->link.sin6_addr) <= 0) log_fatal("Bad address %s", addr); return up; } /* * Setup downstream interfaces. */ static void setup_streams(void) { struct stream_list *dp, *up; int i; isc_boolean_t link_is_set; for (dp = downstreams; dp; dp = dp->next) { /* Check interface */ if (dp->ifp->v6address_count == 0) log_fatal("Interface '%s' has no IPv6 addresses.", dp->ifp->name); /* Check/set link. */ if (IN6_IS_ADDR_UNSPECIFIED(&dp->link.sin6_addr)) link_is_set = ISC_FALSE; else link_is_set = ISC_TRUE; for (i = 0; i < dp->ifp->v6address_count; i++) { if (IN6_IS_ADDR_LINKLOCAL(&dp->ifp->v6addresses[i])) continue; if (!link_is_set) break; if (!memcmp(&dp->ifp->v6addresses[i], &dp->link.sin6_addr, sizeof(dp->link.sin6_addr))) break; } if (i == dp->ifp->v6address_count) log_fatal("Interface %s does not have global IPv6 " "address assigned.", dp->ifp->name); if (!link_is_set) memcpy(&dp->link.sin6_addr, &dp->ifp->v6addresses[i], sizeof(dp->link.sin6_addr)); /* Set interface-id. */ if (dp->id == -1) dp->id = dp->ifp->index; } for (up = upstreams; up; up = up->next) { up->link.sin6_port = local_port; up->link.sin6_family = AF_INET6; #ifdef HAVE_SA_LEN up->link.sin6_len = sizeof(up->link); #endif if (up->ifp->v6address_count == 0) log_fatal("Interface '%s' has no IPv6 addresses.", up->ifp->name); /* RFC 3315 Sec 20 - "If the relay agent relays messages to * the All_DHCP_Servers address or other multicast addresses, * it sets the Hop Limit field to 32." */ if (IN6_IS_ADDR_MULTICAST(&up->link.sin6_addr)) { set_multicast_hop_limit(up->ifp, HOP_COUNT_LIMIT); } } } /* * Add DHCPv6 agent options here. */ static const int required_forw_opts[] = { D6O_INTERFACE_ID, D6O_SUBSCRIBER_ID, #if defined(RELAY_PORT) D6O_RELAY_SOURCE_PORT, #endif D6O_RELAY_MSG, 0 }; /* * Process a packet upwards, i.e., from client to server. */ static void process_up6(struct packet *packet, struct stream_list *dp) { char forw_data[65535]; unsigned cursor; struct dhcpv6_relay_packet *relay; struct option_state *opts; struct stream_list *up; u_int16_t relay_client_port = 0; /* Check if the message should be relayed to the server. */ switch (packet->dhcpv6_msg_type) { case DHCPV6_SOLICIT: case DHCPV6_REQUEST: case DHCPV6_CONFIRM: case DHCPV6_RENEW: case DHCPV6_REBIND: case DHCPV6_RELEASE: case DHCPV6_DECLINE: case DHCPV6_INFORMATION_REQUEST: case DHCPV6_RELAY_FORW: case DHCPV6_LEASEQUERY: case DHCPV6_DHCPV4_QUERY: log_info("Relaying %s from %s port %d going up.", dhcpv6_type_names[packet->dhcpv6_msg_type], piaddr(packet->client_addr), ntohs(packet->client_port)); break; case DHCPV6_ADVERTISE: case DHCPV6_REPLY: case DHCPV6_RECONFIGURE: case DHCPV6_RELAY_REPL: case DHCPV6_LEASEQUERY_REPLY: case DHCPV6_DHCPV4_RESPONSE: log_info("Discarding %s from %s port %d going up.", dhcpv6_type_names[packet->dhcpv6_msg_type], piaddr(packet->client_addr), ntohs(packet->client_port)); return; default: log_info("Unknown %d type from %s port %d going up.", packet->dhcpv6_msg_type, piaddr(packet->client_addr), ntohs(packet->client_port)); return; } /* Build the relay-forward header. */ relay = (struct dhcpv6_relay_packet *) forw_data; cursor = offsetof(struct dhcpv6_relay_packet, options); relay->msg_type = DHCPV6_RELAY_FORW; if (packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) { if (packet->dhcpv6_hop_count >= max_hop_count) { log_info("Hop count exceeded,"); return; } relay->hop_count = packet->dhcpv6_hop_count + 1; if (dp) { memcpy(&relay->link_address, &dp->link.sin6_addr, 16); } else { /* On smart relay add: && !global. */ if (!use_if_id && downstreams->next) { log_info("Shan't get back the interface."); return; } memset(&relay->link_address, 0, 16); } if (packet->client_port != htons(547)) { relay_client_port = packet->client_port; } } else { relay->hop_count = 0; if (!dp) return; memcpy(&relay->link_address, &dp->link.sin6_addr, 16); } memcpy(&relay->peer_address, packet->client_addr.iabuf, 16); /* Get an option state. */ opts = NULL; if (!option_state_allocate(&opts, MDL)) { log_fatal("No memory for upwards options."); } /* Add an interface-id (if used). */ if (use_if_id) { int if_id; if (dp) { if_id = dp->id; } else if (!downstreams->next) { if_id = downstreams->id; } else { log_info("Don't know the interface."); option_state_dereference(&opts, MDL); return; } if (!save_option_buffer(&dhcpv6_universe, opts, NULL, (unsigned char *) &if_id, sizeof(int), D6O_INTERFACE_ID, 0)) { log_error("Can't save interface-id."); option_state_dereference(&opts, MDL); return; } } /* Add a subscriber-id if desired. */ /* This is for testing rather than general use */ if (dhcrelay_sub_id != NULL) { if (!save_option_buffer(&dhcpv6_universe, opts, NULL, (unsigned char *) dhcrelay_sub_id, strlen(dhcrelay_sub_id), D6O_SUBSCRIBER_ID, 0)) { log_error("Can't save subsriber-id."); option_state_dereference(&opts, MDL); return; } } #if defined(RELAY_PORT) /* * If we use a non-547 UDP source port or if we have received * from a downstream relay agent uses a non-547 port, we need * to include the RELAY-SOURCE-PORT option. The "Downstream * UDP Port" field value in the option allow us to send * relay-reply message back to the downstream relay agent * with the correct UDP source port. */ if (relay_port || relay_client_port) { if (!save_option_buffer(&dhcpv6_universe, opts, NULL, (unsigned char *) &relay_client_port, sizeof(u_int16_t), D6O_RELAY_SOURCE_PORT, 0)) { log_error("Can't save relay-source-port."); option_state_dereference(&opts, MDL); return; } } #else /* Avoid unused but set warning, */ (void)(relay_client_port); #endif /* Add the relay-msg carrying the packet. */ if (!save_option_buffer(&dhcpv6_universe, opts, NULL, (unsigned char *) packet->raw, packet->packet_length, D6O_RELAY_MSG, 0)) { log_error("Can't save relay-msg."); option_state_dereference(&opts, MDL); return; } /* Finish the relay-forward message. */ cursor += store_options6(forw_data + cursor, sizeof(forw_data) - cursor, opts, packet, required_forw_opts, NULL); option_state_dereference(&opts, MDL); /* Send it to all upstreams. */ for (up = upstreams; up; up = up->next) { send_packet6(up->ifp, (unsigned char *) forw_data, (size_t) cursor, &up->link); } } /* * Process a packet downwards, i.e., from server to client. */ static void process_down6(struct packet *packet) { struct stream_list *dp; struct option_cache *oc; struct data_string relay_msg; const struct dhcpv6_packet *msg; struct data_string if_id; #if defined(RELAY_PORT) struct data_string down_port; #endif struct sockaddr_in6 to; struct iaddr peer; /* The packet must be a relay-reply message. */ if (packet->dhcpv6_msg_type != DHCPV6_RELAY_REPL) { if (packet->dhcpv6_msg_type < dhcpv6_type_name_max) log_info("Discarding %s from %s port %d going down.", dhcpv6_type_names[packet->dhcpv6_msg_type], piaddr(packet->client_addr), ntohs(packet->client_port)); else log_info("Unknown %d type from %s port %d going down.", packet->dhcpv6_msg_type, piaddr(packet->client_addr), ntohs(packet->client_port)); return; } /* Inits. */ memset(&relay_msg, 0, sizeof(relay_msg)); memset(&if_id, 0, sizeof(if_id)); #if defined(RELAY_PORT) memset(&down_port, 0, sizeof(down_port)); #endif memset(&to, 0, sizeof(to)); to.sin6_family = AF_INET6; #ifdef HAVE_SA_LEN to.sin6_len = sizeof(to); #endif to.sin6_port = remote_port; peer.len = 16; /* Get the relay-msg option (carrying the message to relay). */ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_RELAY_MSG); if (oc == NULL) { log_info("No relay-msg."); return; } if (!evaluate_option_cache(&relay_msg, packet, NULL, NULL, packet->options, NULL, &global_scope, oc, MDL) || (relay_msg.len < offsetof(struct dhcpv6_packet, options))) { log_error("Can't evaluate relay-msg."); return; } msg = (const struct dhcpv6_packet *) relay_msg.data; /* Get the interface-id (if exists) and the downstream. */ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_INTERFACE_ID); if (oc != NULL) { int if_index; if (!evaluate_option_cache(&if_id, packet, NULL, NULL, packet->options, NULL, &global_scope, oc, MDL) || (if_id.len != sizeof(int))) { log_info("Can't evaluate interface-id."); goto cleanup; } memcpy(&if_index, if_id.data, sizeof(int)); for (dp = downstreams; dp; dp = dp->next) { if (dp->id == if_index) break; } } else { if (use_if_id) { /* Require an interface-id. */ log_info("No interface-id."); goto cleanup; } for (dp = downstreams; dp; dp = dp->next) { /* Get the first matching one. */ if (!memcmp(&dp->link.sin6_addr, &packet->dhcpv6_link_address, sizeof(struct in6_addr))) break; } } /* Why bother when there is no choice. */ if (!dp && downstreams && !downstreams->next) dp = downstreams; if (!dp) { log_info("Can't find the down interface."); goto cleanup; } memcpy(peer.iabuf, &packet->dhcpv6_peer_address, peer.len); to.sin6_addr = packet->dhcpv6_peer_address; /* Check if we should relay the carried message. */ switch (msg->msg_type) { /* Relay-Reply of for another relay, not a client. */ case DHCPV6_RELAY_REPL: to.sin6_port = local_port; #if defined(RELAY_PORT) oc = lookup_option(&dhcpv6_universe, packet->options, D6O_RELAY_SOURCE_PORT); if (oc != NULL) { u_int16_t down_relay_port; memset(&down_port, 0, sizeof(down_port)); if (!evaluate_option_cache(&down_port, packet, NULL, NULL, packet->options, NULL, &global_scope, oc, MDL) || (down_port.len != sizeof(u_int16_t))) { log_info("Can't evaluate down " "relay-source-port."); goto cleanup; } memcpy(&down_relay_port, down_port.data, sizeof(u_int16_t)); /* * If the down_relay_port value is non-zero, * that means our downstream relay agent uses * a non-547 UDP source port sending * relay-forw message to us. We need to use * the same UDP port sending reply back. */ if (down_relay_port) { to.sin6_port = down_relay_port; } } #endif /* Fall into: */ case DHCPV6_ADVERTISE: case DHCPV6_REPLY: case DHCPV6_RECONFIGURE: case DHCPV6_RELAY_FORW: case DHCPV6_LEASEQUERY_REPLY: case DHCPV6_DHCPV4_RESPONSE: log_info("Relaying %s to %s port %d down.", dhcpv6_type_names[msg->msg_type], piaddr(peer), ntohs(to.sin6_port)); break; case DHCPV6_SOLICIT: case DHCPV6_REQUEST: case DHCPV6_CONFIRM: case DHCPV6_RENEW: case DHCPV6_REBIND: case DHCPV6_RELEASE: case DHCPV6_DECLINE: case DHCPV6_INFORMATION_REQUEST: case DHCPV6_LEASEQUERY: case DHCPV6_DHCPV4_QUERY: log_info("Discarding %s to %s port %d down.", dhcpv6_type_names[msg->msg_type], piaddr(peer), ntohs(to.sin6_port)); goto cleanup; default: log_info("Unknown %d type to %s port %d down.", msg->msg_type, piaddr(peer), ntohs(to.sin6_port)); goto cleanup; } /* Send the message to the downstream. */ send_packet6(dp->ifp, (unsigned char *) relay_msg.data, (size_t) relay_msg.len, &to); cleanup: if (relay_msg.data != NULL) data_string_forget(&relay_msg, MDL); if (if_id.data != NULL) data_string_forget(&if_id, MDL); } /* * Called by the dispatch packet handler with a decoded packet. */ void dhcpv6(struct packet *packet) { struct stream_list *dp; /* Try all relay-replies downwards. */ if (packet->dhcpv6_msg_type == DHCPV6_RELAY_REPL) { process_down6(packet); return; } /* Others are candidates to go up if they come from down. */ for (dp = downstreams; dp; dp = dp->next) { if (packet->interface != dp->ifp) continue; process_up6(packet, dp); return; } /* Relay-forward could work from an unknown interface. */ if (packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) { process_up6(packet, NULL); return; } log_info("Can't process packet from interface '%s'.", packet->interface->name); } #endif /* Stub routines needed for linking with DHCP libraries. */ void bootp(struct packet *packet) { return; } void dhcp(struct packet *packet) { return; } #if defined(DHCPv6) && defined(DHCP4o6) isc_result_t dhcpv4o6_handler(omapi_object_t *h) { return ISC_R_NOTIMPLEMENTED; } #endif void classify(struct packet *p, struct class *c) { return; } int check_collection(struct packet *p, struct lease *l, struct collection *c) { return 0; } isc_result_t find_class(struct class **class, const char *c1, const char *c2, int i) { return ISC_R_NOTFOUND; } int parse_allow_deny(struct option_cache **oc, struct parse *p, int i) { return 0; } isc_result_t dhcp_set_control_state(control_object_state_t oldstate, control_object_state_t newstate) { char buf = 0; if (newstate != server_shutdown) return ISC_R_SUCCESS; if (no_pid_file == ISC_FALSE) (void) unlink(path_dhcrelay_pid); if (!no_daemon && dfd[0] != -1 && dfd[1] != -1) { IGNORE_RET(write(dfd[1], &buf, 1)); (void) close(dfd[1]); dfd[0] = dfd[1] = -1; } exit(0); } /*! * * \brief Allocate an interface as requested with a given set of flags * * The requested interface is allocated, its flags field is set to * INTERFACE_REQUESTED OR'd with the given flags, and then added to * the list of interfaces. * * \param name - name of the requested interface * \param flags - additional flags for the interface * * \return Nothing */ void request_v4_interface(const char* name, int flags) { struct interface_info *tmp = NULL; int len = strlen(name); isc_result_t status; if (len >= sizeof(tmp->name)) { log_fatal("%s: interface name too long (is %d)", name, len); } status = interface_allocate(&tmp, MDL); if (status != ISC_R_SUCCESS) { log_fatal("%s: interface_allocate: %s", name, isc_result_totext(status)); } log_debug("Requesting: %s as upstream: %c downstream: %c", name, (flags & INTERFACE_UPSTREAM ? 'Y' : 'N'), (flags & INTERFACE_DOWNSTREAM ? 'Y' : 'N')); strncpy(tmp->name, name, len); interface_snorf(tmp, (INTERFACE_REQUESTED | flags)); interface_dereference(&tmp, MDL); } dhcp-4.4.1/relay/Makefile.am000644 000765 000024 00000000536 13243301226 016067 0ustar00tmarkstaff000000 000000 AM_CPPFLAGS = -DLOCALSTATEDIR='"@localstatedir@"' sbin_PROGRAMS = dhcrelay dhcrelay_SOURCES = dhcrelay.c dhcrelay_LDADD = ../common/libdhcp.@A@ ../omapip/libomapi.@A@ \ @BINDLIBIRSDIR@/libirs.@A@ \ @BINDLIBDNSDIR@/libdns.@A@ \ @BINDLIBISCCFGDIR@/libisccfg.@A@ \ @BINDLIBISCDIR@/libisc.@A@ man_MANS = dhcrelay.8 EXTRA_DIST = $(man_MANS) dhcp-4.4.1/omapip/alloc.c000644 000765 000024 00000070270 13243301226 015444 0ustar00tmarkstaff000000 000000 /* alloc.c Functions supporting memory allocation for the object management protocol... */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1999-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #include #if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) || \ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) struct dmalloc_preamble *dmalloc_list; unsigned long dmalloc_outstanding; unsigned long dmalloc_longterm; unsigned long dmalloc_generation; unsigned long dmalloc_cutoff_generation; #endif #if defined (DEBUG_RC_HISTORY) struct rc_history_entry rc_history [RC_HISTORY_MAX]; int rc_history_index; int rc_history_count; #endif #if defined (DEBUG_RC_HISTORY) static void print_rc_hist_entry (int); #endif static int dmalloc_failures; static char out_of_memory[] = "Run out of memory."; void * dmalloc(size_t size, const char *file, int line) { unsigned char *foo; size_t len; void **bar; #if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) || \ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) int i; struct dmalloc_preamble *dp; #endif len = size + DMDSIZE; if (len < size) return NULL; foo = malloc(len); if (!foo) { dmalloc_failures++; if (dmalloc_failures > 10) { /* In case log_fatal() returns here */ IGNORE_RET(write(STDERR_FILENO, out_of_memory, strlen(out_of_memory))); IGNORE_RET(write(STDERR_FILENO, "\n", 1)); exit(1); } else if (dmalloc_failures >= 10) { /* Something went wrong beyond repair. */ log_fatal("Fatal error: out of memory."); } return NULL; } bar = (void *)(foo + DMDOFFSET); memset (bar, 0, size); #if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) || \ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) dp = (struct dmalloc_preamble *)foo; dp -> prev = dmalloc_list; if (dmalloc_list) dmalloc_list -> next = dp; dmalloc_list = dp; dp -> next = (struct dmalloc_preamble *)0; dp -> size = size; dp -> file = file; dp -> line = line; dp -> generation = dmalloc_generation++; dmalloc_outstanding += size; for (i = 0; i < DMLFSIZE; i++) dp -> low_fence [i] = (((unsigned long) (&dp -> low_fence [i])) % 143) + 113; for (i = DMDOFFSET; i < DMDSIZE; i++) foo [i + size] = (((unsigned long) (&foo [i + size])) % 143) + 113; #if defined (DEBUG_MALLOC_POOL_EXHAUSTIVELY) /* Check _every_ entry in the pool! Very expensive. */ for (dp = dmalloc_list; dp; dp = dp -> prev) { for (i = 0; i < DMLFSIZE; i++) { if (dp -> low_fence [i] != (((unsigned long) (&dp -> low_fence [i])) % 143) + 113) { log_error ("malloc fence modified: %s(%d)", dp -> file, dp -> line); abort (); } } foo = (unsigned char *)dp; for (i = DMDOFFSET; i < DMDSIZE; i++) { if (foo [i + dp -> size] != (((unsigned long) (&foo [i + dp -> size])) % 143) + 113) { log_error ("malloc fence modified: %s(%d)", dp -> file, dp -> line); abort (); } } } #endif #endif #ifdef DEBUG_REFCNT_DMALLOC_FREE rc_register (file, line, 0, foo + DMDOFFSET, 1, 0, RC_MALLOC); #endif return bar; } void dfree(void *ptr, const char *file, int line) { if (!ptr) { log_error ("dfree %s(%d): free on null pointer.", file, line); return; } #if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) || \ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) { unsigned char *bar = ptr; struct dmalloc_preamble *dp, *cur; int i; bar -= DMDOFFSET; cur = (struct dmalloc_preamble *)bar; for (dp = dmalloc_list; dp; dp = dp -> prev) if (dp == cur) break; if (!dp) { log_error ("%s(%d): freeing unknown memory: %lx", file, line, (unsigned long)cur); abort (); } if (dp -> prev) dp -> prev -> next = dp -> next; if (dp -> next) dp -> next -> prev = dp -> prev; if (dp == dmalloc_list) dmalloc_list = dp -> prev; if (dp -> generation >= dmalloc_cutoff_generation) dmalloc_outstanding -= dp -> size; else dmalloc_longterm -= dp -> size; for (i = 0; i < DMLFSIZE; i++) { if (dp -> low_fence [i] != (((unsigned long) (&dp -> low_fence [i])) % 143) + 113) { log_error ("malloc fence modified: %s(%d)", dp -> file, dp -> line); abort (); } } for (i = DMDOFFSET; i < DMDSIZE; i++) { if (bar [i + dp -> size] != (((unsigned long) (&bar [i + dp -> size])) % 143) + 113) { log_error ("malloc fence modified: %s(%d)", dp -> file, dp -> line); abort (); } } ptr = bar; } #endif #ifdef DEBUG_REFCNT_DMALLOC_FREE rc_register (file, line, 0, (unsigned char *)ptr + DMDOFFSET, 0, 1, RC_MALLOC); #endif free (ptr); } #if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) || \ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) /* For allocation functions that keep their own free lists, we want to account for the reuse of the memory. */ void dmalloc_reuse(void *foo, const char *file, int line, int justref) { struct dmalloc_preamble *dp; /* Get the pointer to the dmalloc header. */ dp = foo; dp--; /* If we just allocated this and are now referencing it, this function would almost be a no-op, except that it would increment the generation count needlessly. So just return in this case. */ if (dp -> generation == dmalloc_generation) return; /* If this is longterm data, and we just made reference to it, don't put it on the short-term list or change its name - we don't need to know about this. */ if (dp -> generation < dmalloc_cutoff_generation && justref) return; /* Take it out of the place in the allocated list where it was. */ if (dp -> prev) dp -> prev -> next = dp -> next; if (dp -> next) dp -> next -> prev = dp -> prev; if (dp == dmalloc_list) dmalloc_list = dp -> prev; /* Account for its removal. */ if (dp -> generation >= dmalloc_cutoff_generation) dmalloc_outstanding -= dp -> size; else dmalloc_longterm -= dp -> size; /* Now put it at the head of the list. */ dp -> prev = dmalloc_list; if (dmalloc_list) dmalloc_list -> next = dp; dmalloc_list = dp; dp -> next = (struct dmalloc_preamble *)0; /* Change the reference location information. */ dp -> file = file; dp -> line = line; /* Increment the generation. */ dp -> generation = dmalloc_generation++; /* Account for it. */ dmalloc_outstanding += dp -> size; } void dmalloc_dump_outstanding () { static unsigned long dmalloc_cutoff_point; struct dmalloc_preamble *dp; #if defined(DEBUG_MALLOC_POOL) unsigned char *foo; int i; #endif if (!dmalloc_cutoff_point) dmalloc_cutoff_point = dmalloc_cutoff_generation; for (dp = dmalloc_list; dp; dp = dp -> prev) { if (dp -> generation <= dmalloc_cutoff_point) break; #if defined (DEBUG_MALLOC_POOL) for (i = 0; i < DMLFSIZE; i++) { if (dp -> low_fence [i] != (((unsigned long) (&dp -> low_fence [i])) % 143) + 113) { log_error ("malloc fence modified: %s(%d)", dp -> file, dp -> line); abort (); } } foo = (unsigned char *)dp; for (i = DMDOFFSET; i < DMDSIZE; i++) { if (foo [i + dp -> size] != (((unsigned long) (&foo [i + dp -> size])) % 143) + 113) { log_error ("malloc fence modified: %s(%d)", dp -> file, dp -> line); abort (); } } #endif #if defined (DEBUG_MEMORY_LEAKAGE) || \ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) /* Don't count data that's actually on a free list somewhere. */ if (dp -> file) { #if defined (DEBUG_RC_HISTORY) int i, count, inhistory = 0, noted = 0; /* If we have the info, see if this is actually new garbage. */ if (rc_history_count < RC_HISTORY_MAX) { count = rc_history_count; } else count = RC_HISTORY_MAX; i = rc_history_index - 1; if (i < 0) i += RC_HISTORY_MAX; do { if (rc_history [i].addr == dp + 1) { inhistory = 1; if (!noted) { log_info (" %s(%d): %ld", dp -> file, dp -> line, (long) dp -> size); noted = 1; } print_rc_hist_entry (i); if (!rc_history [i].refcnt) break; } if (--i < 0) i = RC_HISTORY_MAX - 1; } while (count--); if (!inhistory) #endif log_info (" %s(%d): %ld", dp -> file, dp -> line, (long) dp -> size); } #endif } if (dmalloc_list) dmalloc_cutoff_point = dmalloc_list -> generation; } #endif /* DEBUG_MEMORY_LEAKAGE || DEBUG_MALLOC_POOL */ #if defined (DEBUG_RC_HISTORY) static void print_rc_hist_entry (int i) { log_info (" referenced by %s(%d)[%lx]: addr = %lx refcnt = %x", rc_history [i].file, rc_history [i].line, (unsigned long)rc_history [i].reference, (unsigned long)rc_history [i].addr, rc_history [i].refcnt); } void dump_rc_history (void *addr) { int i; i = rc_history_index; if (!rc_history [i].file) i = 0; else if (rc_history_count < RC_HISTORY_MAX) { i -= rc_history_count; if (i < 0) i += RC_HISTORY_MAX; } rc_history_count = 0; while (rc_history [i].file) { if (!addr || addr == rc_history [i].addr) print_rc_hist_entry (i); ++i; if (i == RC_HISTORY_MAX) i = 0; if (i == rc_history_index) break; } } void rc_history_next (int d) { #if defined (RC_HISTORY_COMPRESSION) int i, j = 0, m, n = 0; void *ap, *rp; /* If we are decreasing the reference count, try to find the entry where the reference was made and eliminate it; then we can also eliminate this reference. */ if (d) { m = rc_history_index - 1000; if (m < -1) m = -1; ap = rc_history [rc_history_index].addr; rp = rc_history [rc_history_index].reference; for (i = rc_history_index - 1; i > m; i--) { if (rc_history [i].addr == ap) { if (rc_history [i].reference == rp) { if (n > 10) { for (n = i; n <= rc_history_index; n++) print_rc_hist_entry (n); n = 11; } memmove (&rc_history [i], &rc_history [i + 1], (unsigned)((rc_history_index - i) * sizeof (struct rc_history_entry))); --rc_history_count; --rc_history_index; for (j = i; j < rc_history_count; j++) { if (rc_history [j].addr == ap) --rc_history [j].refcnt; } if (n > 10) { for (n = i; n <= rc_history_index; n++) print_rc_hist_entry (n); n = 11; exit (0); } return; } } } } #endif if (++rc_history_index == RC_HISTORY_MAX) rc_history_index = 0; ++rc_history_count; } #endif /* DEBUG_RC_HISTORY */ #if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) || \ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) struct caller { struct dmalloc_preamble *dp; int count; }; static int dmalloc_find_entry (struct dmalloc_preamble *dp, struct caller *array, int min, int max) { int middle; middle = (min + max) / 2; if (middle == min) return middle; if (array [middle].dp -> file == dp -> file) { if (array [middle].dp -> line == dp -> line) return middle; else if (array [middle].dp -> line < dp -> line) return dmalloc_find_entry (dp, array, middle, max); else return dmalloc_find_entry (dp, array, 0, middle); } else if (array [middle].dp -> file < dp -> file) return dmalloc_find_entry (dp, array, middle, max); else return dmalloc_find_entry (dp, array, 0, middle); } void omapi_print_dmalloc_usage_by_caller () { struct dmalloc_preamble *dp; int ccur, cmax, i; struct caller cp [1024]; cmax = 1024; ccur = 0; memset (cp, 0, sizeof cp); for (dp = dmalloc_list; dp; dp = dp -> prev) { i = dmalloc_find_entry (dp, cp, 0, ccur); if ((i == ccur || cp [i].dp -> file != dp -> file || cp [i].dp -> line != dp -> line) && ccur == cmax) { log_error ("no space for memory usage summary."); return; } if (i == ccur) { cp [ccur++].dp = dp; cp [i].count = 1; } else if (cp [i].dp -> file < dp -> file || (cp [i].dp -> file == dp -> file && cp [i].dp -> line < dp -> line)) { if (i + 1 != ccur) memmove (cp + i + 2, cp + i + 1, (ccur - i) * sizeof *cp); cp [i + 1].dp = dp; cp [i + 1].count = 1; ccur++; } else if (cp [i].dp -> file != dp -> file || cp [i].dp -> line != dp -> line) { memmove (cp + i + 1, cp + i, (ccur - i) * sizeof *cp); cp [i].dp = dp; cp [i].count = 1; ccur++; } else cp [i].count++; #if 0 printf ("%d\t%s:%d\n", i, dp -> file, dp -> line); dump_rc_history (dp + 1); #endif } for (i = 0; i < ccur; i++) { printf ("%d\t%s:%d\t%d\n", i, cp [i].dp -> file, cp [i].dp -> line, cp [i].count); #if defined(DUMP_RC_HISTORY) dump_rc_history (cp [i].dp + 1); #endif } } #endif /* DEBUG_MEMORY_LEAKAGE || DEBUG_MALLOC_POOL */ isc_result_t omapi_object_allocate (omapi_object_t **o, omapi_object_type_t *type, size_t size, const char *file, int line) { size_t tsize; omapi_object_t *foo; isc_result_t status; if (type -> allocator) { foo = (omapi_object_t *)0; status = (*type -> allocator) (&foo, file, line); tsize = type -> size; } else { status = ISC_R_NOMEMORY; tsize = 0; } if (status == ISC_R_NOMEMORY) { if (type -> sizer) tsize = (*type -> sizer) (size); else tsize = type -> size; /* Sanity check. */ if (tsize < sizeof (omapi_object_t)) return DHCP_R_INVALIDARG; foo = dmalloc (tsize, file, line); if (!foo) return ISC_R_NOMEMORY; } status = omapi_object_initialize (foo, type, size, tsize, file, line); if (status != ISC_R_SUCCESS) { if (type -> freer) (*type -> freer) (foo, file, line); else dfree (foo, file, line); return status; } return omapi_object_reference (o, foo, file, line); } isc_result_t omapi_object_initialize (omapi_object_t *o, omapi_object_type_t *type, size_t usize, size_t psize, const char *file, int line) { memset (o, 0, psize); o -> type = type; if (type -> initialize) (*type -> initialize) (o, file, line); return ISC_R_SUCCESS; } isc_result_t omapi_object_reference (omapi_object_t **r, omapi_object_t *h, const char *file, int line) { if (!h || !r) return DHCP_R_INVALIDARG; if (*r) { #if defined (POINTER_DEBUG) log_error ("%s(%d): reference store into non-null pointer!", file, line); abort (); #else return DHCP_R_INVALIDARG; #endif } *r = h; h -> refcnt++; rc_register (file, line, r, h, h -> refcnt, 0, h -> type -> rc_flag); return ISC_R_SUCCESS; } isc_result_t omapi_object_dereference (omapi_object_t **h, const char *file, int line) { int outer_reference = 0; int inner_reference = 0; int handle_reference = 0; int extra_references; omapi_object_t *p, *hp; if (!h) return DHCP_R_INVALIDARG; if (!*h) { #if defined (POINTER_DEBUG) log_error ("%s(%d): dereference of null pointer!", file, line); abort (); #else return DHCP_R_INVALIDARG; #endif } if ((*h) -> refcnt <= 0) { #if defined (POINTER_DEBUG) log_error ("%s(%d): dereference of pointer with refcnt of zero!", file, line); #if defined (DEBUG_RC_HISTORY) dump_rc_history (*h); #endif abort (); #else *h = 0; return DHCP_R_INVALIDARG; #endif } /* See if this object's inner object refers to it, but don't count this as a reference if we're being asked to free the reference from the inner object. */ if ((*h) -> inner && (*h) -> inner -> outer && h != &((*h) -> inner -> outer)) inner_reference = 1; /* Ditto for the outer object. */ if ((*h) -> outer && (*h) -> outer -> inner && h != &((*h) -> outer -> inner)) outer_reference = 1; /* Ditto for the outer object. The code below assumes that the only reason we'd get a dereference from the handle table is if this function does it - otherwise we'd have to traverse the handle table to find the address where the reference is stored and compare against that, and we don't want to do that if we can avoid it. */ if ((*h) -> handle) handle_reference = 1; /* If we are getting rid of the last reference other than references to inner and outer objects, or from the handle table, then we must examine all the objects in either direction to see if they hold any non-inner, non-outer, non-handle-table references. If not, we need to free the entire chain of objects. */ if ((*h) -> refcnt == inner_reference + outer_reference + handle_reference + 1) { if (inner_reference || outer_reference || handle_reference) { /* XXX we could check for a reference from the handle table here. */ extra_references = 0; for (p = (*h) -> inner; p && !extra_references; p = p -> inner) { extra_references += p -> refcnt; if (p -> inner && p -> inner -> outer == p) --extra_references; if (p -> outer) --extra_references; if (p -> handle) --extra_references; } for (p = (*h) -> outer; p && !extra_references; p = p -> outer) { extra_references += p -> refcnt; if (p -> outer && p -> outer -> inner == p) --extra_references; if (p -> inner) --extra_references; if (p -> handle) --extra_references; } } else extra_references = 0; if (!extra_references) { hp = *h; *h = 0; hp -> refcnt--; if (inner_reference) omapi_object_dereference (&hp -> inner, file, line); if (outer_reference) omapi_object_dereference (&hp -> outer, file, line); /* if (!hp -> type -> freer) */ rc_register (file, line, h, hp, 0, 1, hp -> type -> rc_flag); if (handle_reference) { if (omapi_handle_clear(hp->handle) != ISC_R_SUCCESS) { log_debug("Attempt to clear null " "handle pointer"); } } if (hp -> type -> destroy) (*(hp -> type -> destroy)) (hp, file, line); if (hp -> type -> freer) (hp -> type -> freer (hp, file, line)); else dfree (hp, file, line); } else { (*h) -> refcnt--; /* if (!(*h) -> type -> freer) */ rc_register (file, line, h, *h, (*h) -> refcnt, 1, (*h) -> type -> rc_flag); } } else { (*h) -> refcnt--; /* if (!(*h) -> type -> freer) */ rc_register (file, line, h, *h, (*h) -> refcnt, 1, (*h) -> type -> rc_flag); } *h = 0; return ISC_R_SUCCESS; } isc_result_t omapi_buffer_new (omapi_buffer_t **h, const char *file, int line) { omapi_buffer_t *t; isc_result_t status; t = (omapi_buffer_t *)dmalloc (sizeof *t, file, line); if (!t) return ISC_R_NOMEMORY; memset (t, 0, sizeof *t); status = omapi_buffer_reference (h, t, file, line); if (status != ISC_R_SUCCESS) dfree (t, file, line); (*h) -> head = sizeof ((*h) -> buf) - 1; return status; } isc_result_t omapi_buffer_reference (omapi_buffer_t **r, omapi_buffer_t *h, const char *file, int line) { if (!h || !r) return DHCP_R_INVALIDARG; if (*r) { #if defined (POINTER_DEBUG) log_error ("%s(%d): reference store into non-null pointer!", file, line); abort (); #else return DHCP_R_INVALIDARG; #endif } *r = h; h -> refcnt++; rc_register (file, line, r, h, h -> refcnt, 0, RC_MISC); return ISC_R_SUCCESS; } isc_result_t omapi_buffer_dereference (omapi_buffer_t **h, const char *file, int line) { if (!h) return DHCP_R_INVALIDARG; if (!*h) { #if defined (POINTER_DEBUG) log_error ("%s(%d): dereference of null pointer!", file, line); abort (); #else return DHCP_R_INVALIDARG; #endif } if ((*h) -> refcnt <= 0) { #if defined (POINTER_DEBUG) log_error ("%s(%d): dereference of pointer with refcnt of zero!", file, line); #if defined (DEBUG_RC_HISTORY) dump_rc_history (*h); #endif abort (); #else *h = 0; return DHCP_R_INVALIDARG; #endif } --(*h) -> refcnt; rc_register (file, line, h, *h, (*h) -> refcnt, 1, RC_MISC); if ((*h) -> refcnt == 0) dfree (*h, file, line); *h = 0; return ISC_R_SUCCESS; } isc_result_t omapi_typed_data_new (const char *file, int line, omapi_typed_data_t **t, omapi_datatype_t type, ...) { va_list l; omapi_typed_data_t *new; unsigned len; unsigned val = 0; int intval = 0; char *s = NULL; isc_result_t status; omapi_object_t *obj = NULL; va_start (l, type); switch (type) { case omapi_datatype_int: len = OMAPI_TYPED_DATA_INT_LEN; intval = va_arg (l, int); break; case omapi_datatype_string: s = va_arg (l, char *); val = strlen (s); len = OMAPI_TYPED_DATA_NOBUFFER_LEN + val; if (len < val) { va_end(l); return DHCP_R_INVALIDARG; } break; case omapi_datatype_data: val = va_arg (l, unsigned); len = OMAPI_TYPED_DATA_NOBUFFER_LEN + val; if (len < val) { va_end(l); return DHCP_R_INVALIDARG; } break; case omapi_datatype_object: len = OMAPI_TYPED_DATA_OBJECT_LEN; obj = va_arg (l, omapi_object_t *); break; default: va_end (l); return DHCP_R_INVALIDARG; } va_end (l); new = dmalloc (len, file, line); if (!new) return ISC_R_NOMEMORY; memset (new, 0, len); switch (type) { case omapi_datatype_int: new -> u.integer = intval; break; case omapi_datatype_string: memcpy (new -> u.buffer.value, s, val); new -> u.buffer.len = val; break; case omapi_datatype_data: new -> u.buffer.len = val; break; case omapi_datatype_object: status = omapi_object_reference (&new -> u.object, obj, file, line); if (status != ISC_R_SUCCESS) { dfree (new, file, line); return status; } break; } new -> type = type; return omapi_typed_data_reference (t, new, file, line); } isc_result_t omapi_typed_data_reference (omapi_typed_data_t **r, omapi_typed_data_t *h, const char *file, int line) { if (!h || !r) return DHCP_R_INVALIDARG; if (*r) { #if defined (POINTER_DEBUG) log_error ("%s(%d): reference store into non-null pointer!", file, line); abort (); #else return DHCP_R_INVALIDARG; #endif } *r = h; h -> refcnt++; rc_register (file, line, r, h, h -> refcnt, 0, RC_MISC); return ISC_R_SUCCESS; } isc_result_t omapi_typed_data_dereference (omapi_typed_data_t **h, const char *file, int line) { if (!h) return DHCP_R_INVALIDARG; if (!*h) { #if defined (POINTER_DEBUG) log_error ("%s(%d): dereference of null pointer!", file, line); abort (); #else return DHCP_R_INVALIDARG; #endif } if ((*h) -> refcnt <= 0) { #if defined (POINTER_DEBUG) log_error ("%s(%d): dereference of pointer with refcnt of zero!", file, line); #if defined (DEBUG_RC_HISTORY) dump_rc_history (*h); #endif abort (); #else *h = 0; return DHCP_R_INVALIDARG; #endif } --((*h) -> refcnt); rc_register (file, line, h, *h, (*h) -> refcnt, 1, RC_MISC); if ((*h) -> refcnt <= 0 ) { switch ((*h) -> type) { case omapi_datatype_int: case omapi_datatype_string: case omapi_datatype_data: default: break; case omapi_datatype_object: omapi_object_dereference (&(*h) -> u.object, file, line); break; } dfree (*h, file, line); } *h = 0; return ISC_R_SUCCESS; } isc_result_t omapi_data_string_new (omapi_data_string_t **d, unsigned len, const char *file, int line) { omapi_data_string_t *new; unsigned nlen; nlen = OMAPI_DATA_STRING_EMPTY_SIZE + len; if (nlen < len) return DHCP_R_INVALIDARG; new = dmalloc (nlen, file, line); if (!new) return ISC_R_NOMEMORY; memset (new, 0, OMAPI_DATA_STRING_EMPTY_SIZE); new -> len = len; return omapi_data_string_reference (d, new, file, line); } isc_result_t omapi_data_string_reference (omapi_data_string_t **r, omapi_data_string_t *h, const char *file, int line) { if (!h || !r) return DHCP_R_INVALIDARG; if (*r) { #if defined (POINTER_DEBUG) log_error ("%s(%d): reference store into non-null pointer!", file, line); abort (); #else return DHCP_R_INVALIDARG; #endif } *r = h; h -> refcnt++; rc_register (file, line, r, h, h -> refcnt, 0, RC_MISC); return ISC_R_SUCCESS; } isc_result_t omapi_data_string_dereference (omapi_data_string_t **h, const char *file, int line) { if (!h) return DHCP_R_INVALIDARG; if (!*h) { #if defined (POINTER_DEBUG) log_error ("%s(%d): dereference of null pointer!", file, line); abort (); #else return DHCP_R_INVALIDARG; #endif } if ((*h) -> refcnt <= 0) { #if defined (POINTER_DEBUG) log_error ("%s(%d): dereference of pointer with refcnt of zero!", file, line); #if defined (DEBUG_RC_HISTORY) dump_rc_history (*h); #endif abort (); #else *h = 0; return DHCP_R_INVALIDARG; #endif } --((*h) -> refcnt); rc_register (file, line, h, *h, (*h) -> refcnt, 1, RC_MISC); if ((*h) -> refcnt <= 0 ) { dfree (*h, file, line); } *h = 0; return ISC_R_SUCCESS; } isc_result_t omapi_value_new (omapi_value_t **d, const char *file, int line) { omapi_value_t *new; new = dmalloc (sizeof *new, file, line); if (!new) return ISC_R_NOMEMORY; memset (new, 0, sizeof *new); return omapi_value_reference (d, new, file, line); } isc_result_t omapi_value_reference (omapi_value_t **r, omapi_value_t *h, const char *file, int line) { if (!h || !r) return DHCP_R_INVALIDARG; if (*r) { #if defined (POINTER_DEBUG) log_error ("%s(%d): reference store into non-null pointer!", file, line); abort (); #else return DHCP_R_INVALIDARG; #endif } *r = h; h -> refcnt++; rc_register (file, line, r, h, h -> refcnt, 0, RC_MISC); return ISC_R_SUCCESS; } isc_result_t omapi_value_dereference (omapi_value_t **h, const char *file, int line) { if (!h) return DHCP_R_INVALIDARG; if (!*h) { #if defined (POINTER_DEBUG) log_error ("%s(%d): dereference of null pointer!", file, line); abort (); #else return DHCP_R_INVALIDARG; #endif } if ((*h) -> refcnt <= 0) { #if defined (POINTER_DEBUG) log_error ("%s(%d): dereference of pointer with refcnt of zero!", file, line); #if defined (DEBUG_RC_HISTORY) dump_rc_history (*h); #endif abort (); #else *h = 0; return DHCP_R_INVALIDARG; #endif } --((*h) -> refcnt); rc_register (file, line, h, *h, (*h) -> refcnt, 1, RC_MISC); if ((*h) -> refcnt == 0) { if ((*h) -> name) omapi_data_string_dereference (&(*h) -> name, file, line); if ((*h) -> value) omapi_typed_data_dereference (&(*h) -> value, file, line); dfree (*h, file, line); } *h = 0; return ISC_R_SUCCESS; } isc_result_t omapi_addr_list_new (omapi_addr_list_t **d, unsigned count, const char *file, int line) { omapi_addr_list_t *new; new = dmalloc ((count * sizeof (omapi_addr_t)) + sizeof (omapi_addr_list_t), file, line); if (!new) return ISC_R_NOMEMORY; memset (new, 0, ((count * sizeof (omapi_addr_t)) + sizeof (omapi_addr_list_t))); new -> count = count; new -> addresses = (omapi_addr_t *)(new + 1); return omapi_addr_list_reference (d, new, file, line); } isc_result_t omapi_addr_list_reference (omapi_addr_list_t **r, omapi_addr_list_t *h, const char *file, int line) { if (!h || !r) return DHCP_R_INVALIDARG; if (*r) { #if defined (POINTER_DEBUG) log_error ("%s(%d): reference store into non-null pointer!", file, line); abort (); #else return DHCP_R_INVALIDARG; #endif } *r = h; h -> refcnt++; rc_register (file, line, r, h, h -> refcnt, 0, RC_MISC); return ISC_R_SUCCESS; } isc_result_t omapi_addr_list_dereference (omapi_addr_list_t **h, const char *file, int line) { if (!h) return DHCP_R_INVALIDARG; if (!*h) { #if defined (POINTER_DEBUG) log_error ("%s(%d): dereference of null pointer!", file, line); abort (); #else return DHCP_R_INVALIDARG; #endif } if ((*h) -> refcnt <= 0) { #if defined (POINTER_DEBUG) log_error ("%s(%d): dereference of pointer with zero refcnt!", file, line); #if defined (DEBUG_RC_HISTORY) dump_rc_history (*h); #endif abort (); #else *h = 0; return DHCP_R_INVALIDARG; #endif } --((*h) -> refcnt); rc_register (file, line, h, *h, (*h) -> refcnt, 1, RC_MISC); if ((*h) -> refcnt <= 0 ) { dfree (*h, file, line); } *h = 0; return ISC_R_SUCCESS; } dhcp-4.4.1/omapip/array.c000644 000765 000024 00000010665 13243301226 015472 0ustar00tmarkstaff000000 000000 /* listener.c Subroutines that support the omapi extensible array type. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 2001-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #include /* Allocate a new extensible array. */ isc_result_t omapi_array_allocate (omapi_array_t **array, omapi_array_ref_t ref, omapi_array_deref_t deref, const char *file, int line) { omapi_array_t *aptr; if (!array || *array) return DHCP_R_INVALIDARG; aptr = dmalloc (sizeof (omapi_array_t),file, line); if (!aptr) return ISC_R_NOMEMORY; *array = aptr; aptr -> ref = ref; aptr -> deref = deref; return ISC_R_SUCCESS; } isc_result_t omapi_array_free (omapi_array_t **array, const char *file, int line) { omapi_array_t *aptr; int i; if (!array || !*array) return DHCP_R_INVALIDARG; aptr = *array; for (i = 0; i < aptr -> count; i++) if (aptr -> data [i] && aptr -> deref) (*aptr -> deref) (&aptr -> data [i], file, line); dfree (aptr -> data, MDL); dfree (aptr, MDL); *array = (omapi_array_t *)0; return ISC_R_SUCCESS; } /* Extend the size of the array by one entry (we may allocate more than that) and store the specified value in the new array element. */ isc_result_t omapi_array_extend (omapi_array_t *array, char *ptr, int *index, const char *file, int line) { isc_result_t status; int new = array -> count; status = omapi_array_set (array, ptr, new, file, line); if (index && status == ISC_R_SUCCESS) *index = new; return status; } /* Set a value in the specified array, extending it if necessary. */ isc_result_t omapi_array_set (omapi_array_t *array, void *ptr, int index, const char *file, int line) { char **newbuf; int delta; isc_result_t status; if (!array) return DHCP_R_INVALIDARG; if (!ptr) return DHCP_R_INVALIDARG; if (index < 0) return DHCP_R_INVALIDARG; /* If the proposed index is larger than the current available space in the array, make more space in the array. */ if (array -> max <= index) { delta = index - array -> max + 10; newbuf = dmalloc ((array -> max + delta) * sizeof (char *), file, line); if (!newbuf) return ISC_R_NOMEMORY; /* Zero the new elements. */ memset (&newbuf [array -> max], 0, (sizeof (char *)) * delta); array -> max += delta; /* Copy the old array data into the new buffer. */ if (array -> data) { memcpy (newbuf, array -> data, array -> count * sizeof (char *)); dfree (array -> data, file, line); } array -> data = newbuf; } else { /* If there's already data there, and this is an array of references, dereference what's there. */ if (array -> data [index]) { status = ((*array -> deref) (&array -> data [index], file, line)); if (status != ISC_R_SUCCESS) return status; } } /* Store the pointer using the referencer function. We have either just memset this to zero or dereferenced what was there previously, so there is no need to do anything if the pointer we have been asked to store is null. */ if (ptr) { status = (*array -> ref) (&array -> data [index], ptr, file, line); if (status != ISC_R_SUCCESS) return status; } if (index >= array -> count) array -> count = index + 1; return ISC_R_SUCCESS; } isc_result_t omapi_array_lookup (char **ptr, omapi_array_t *array, int index, const char *file, int line) { if (!array || !ptr || *ptr || index < 0 || index >= array -> count) return DHCP_R_INVALIDARG; if (array -> data [index]) return (*array -> ref) (ptr, array -> data [index], file, line); return ISC_R_NOTFOUND; } dhcp-4.4.1/omapip/auth.c000644 000765 000024 00000016610 13243301226 015311 0ustar00tmarkstaff000000 000000 /* auth.c Subroutines having to do with authentication. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1998-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #include OMAPI_OBJECT_ALLOC (omapi_auth_key, omapi_auth_key_t, omapi_type_auth_key) typedef struct hash omapi_auth_hash_t; HASH_FUNCTIONS_DECL (omapi_auth_key, const char *, omapi_auth_key_t, omapi_auth_hash_t) omapi_auth_hash_t *auth_key_hash; HASH_FUNCTIONS (omapi_auth_key, const char *, omapi_auth_key_t, omapi_auth_hash_t, omapi_auth_key_reference, omapi_auth_key_dereference, do_case_hash) isc_result_t omapi_auth_key_new (omapi_auth_key_t **o, const char *file, int line) { return omapi_auth_key_allocate (o, file, line); } isc_result_t omapi_auth_key_destroy (omapi_object_t *h, const char *file, int line) { omapi_auth_key_t *a; if (h->type != omapi_type_auth_key) return DHCP_R_INVALIDARG; a = (omapi_auth_key_t *)h; if (auth_key_hash != NULL) omapi_auth_key_hash_delete(auth_key_hash, a->name, 0, MDL); if (a->name != NULL) dfree(a->name, MDL); if (a->algorithm != NULL) dfree(a->algorithm, MDL); if (a->key != NULL) omapi_data_string_dereference(&a->key, MDL); if (a->tsec_key != NULL) dns_tsec_destroy(&a->tsec_key); return ISC_R_SUCCESS; } isc_result_t omapi_auth_key_enter (omapi_auth_key_t *a) { omapi_auth_key_t *tk; isc_result_t status; dst_key_t *dstkey; if (a -> type != omapi_type_auth_key) return DHCP_R_INVALIDARG; tk = (omapi_auth_key_t *)0; if (auth_key_hash) { omapi_auth_key_hash_lookup (&tk, auth_key_hash, a -> name, 0, MDL); if (tk == a) { omapi_auth_key_dereference (&tk, MDL); return ISC_R_SUCCESS; } if (tk) { omapi_auth_key_hash_delete (auth_key_hash, tk -> name, 0, MDL); omapi_auth_key_dereference (&tk, MDL); } } else { if (!omapi_auth_key_new_hash(&auth_key_hash, KEY_HASH_SIZE, MDL)) return ISC_R_NOMEMORY; } /* * If possible create a tsec structure for this key, * if we can't create the structure we put out a warning * and continue. */ status = isclib_make_dst_key(a->name, a->algorithm, a->key->value, a->key->len, &dstkey); if (status == ISC_R_SUCCESS) { status = dns_tsec_create(dhcp_gbl_ctx.mctx, dns_tsectype_tsig, dstkey, &a->tsec_key); dst_key_free(&dstkey); } if (status != ISC_R_SUCCESS) log_error("Unable to create tsec structure for %s", a->name); omapi_auth_key_hash_add (auth_key_hash, a -> name, 0, a, MDL); return ISC_R_SUCCESS; } isc_result_t omapi_auth_key_lookup_name (omapi_auth_key_t **a, const char *name) { if (!auth_key_hash) return ISC_R_NOTFOUND; if (!omapi_auth_key_hash_lookup (a, auth_key_hash, name, 0, MDL)) return ISC_R_NOTFOUND; return ISC_R_SUCCESS; } isc_result_t omapi_auth_key_lookup (omapi_object_t **h, omapi_object_t *id, omapi_object_t *ref) { isc_result_t status; omapi_value_t *name = (omapi_value_t *)0; omapi_value_t *algorithm = (omapi_value_t *)0; if (!auth_key_hash) return ISC_R_NOTFOUND; if (!ref) return DHCP_R_NOKEYS; status = omapi_get_value_str (ref, id, "name", &name); if (status != ISC_R_SUCCESS) return status; if ((name -> value -> type != omapi_datatype_string) && (name -> value -> type != omapi_datatype_data)) { omapi_value_dereference (&name, MDL); return ISC_R_NOTFOUND; } status = omapi_get_value_str (ref, id, "algorithm", &algorithm); if (status != ISC_R_SUCCESS) { omapi_value_dereference (&name, MDL); return status; } if ((algorithm -> value -> type != omapi_datatype_string) && (algorithm -> value -> type != omapi_datatype_data)) { omapi_value_dereference (&name, MDL); omapi_value_dereference (&algorithm, MDL); return ISC_R_NOTFOUND; } if (!omapi_auth_key_hash_lookup ((omapi_auth_key_t **)h, auth_key_hash, (const char *) name -> value -> u.buffer.value, name -> value -> u.buffer.len, MDL)) { omapi_value_dereference (&name, MDL); omapi_value_dereference (&algorithm, MDL); return ISC_R_NOTFOUND; } if (omapi_td_strcasecmp (algorithm -> value, ((omapi_auth_key_t *)*h) -> algorithm) != 0) { omapi_value_dereference (&name, MDL); omapi_value_dereference (&algorithm, MDL); omapi_object_dereference (h, MDL); return ISC_R_NOTFOUND; } omapi_value_dereference (&name, MDL); omapi_value_dereference (&algorithm, MDL); return ISC_R_SUCCESS; } isc_result_t omapi_auth_key_stuff_values (omapi_object_t *c, omapi_object_t *id, omapi_object_t *h) { omapi_auth_key_t *a; isc_result_t status; if (h -> type != omapi_type_auth_key) return DHCP_R_INVALIDARG; a = (omapi_auth_key_t *)h; /* Write only the name and algorithm -- not the secret! */ if (a -> name) { status = omapi_connection_put_name (c, "name"); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_string (c, a -> name); if (status != ISC_R_SUCCESS) return status; } if (a -> algorithm) { status = omapi_connection_put_name (c, "algorithm"); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_string (c, a -> algorithm); if (status != ISC_R_SUCCESS) return status; } return ISC_R_SUCCESS; } isc_result_t omapi_auth_key_get_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_value_t **value) { omapi_auth_key_t *a; isc_result_t status; if (h -> type != omapi_type_auth_key) return ISC_R_UNEXPECTED; a = (omapi_auth_key_t *)h; if (omapi_ds_strcmp (name, "name") == 0) { if (a -> name) return omapi_make_string_value (value, name, a -> name, MDL); else return ISC_R_NOTFOUND; } else if (omapi_ds_strcmp (name, "key") == 0) { if (a -> key) { status = omapi_value_new (value, MDL); if (status != ISC_R_SUCCESS) return status; status = omapi_data_string_reference (&(*value) -> name, name, MDL); if (status != ISC_R_SUCCESS) { omapi_value_dereference (value, MDL); return status; } status = omapi_typed_data_new (MDL, &(*value) -> value, omapi_datatype_data, a -> key -> len); if (status != ISC_R_SUCCESS) { omapi_value_dereference (value, MDL); return status; } memcpy ((*value) -> value -> u.buffer.value, a -> key -> value, a -> key -> len); return ISC_R_SUCCESS; } else return ISC_R_NOTFOUND; } else if (omapi_ds_strcmp (name, "algorithm") == 0) { if (a -> algorithm) return omapi_make_string_value (value, name, a -> algorithm, MDL); else return ISC_R_NOTFOUND; } return ISC_R_SUCCESS; } dhcp-4.4.1/omapip/buffer.c000644 000765 000024 00000045446 13243301226 015632 0ustar00tmarkstaff000000 000000 /* buffer.c Buffer access functions for the object management protocol... */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1999-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #include #include #if defined (TRACING) static void trace_connection_input_input (trace_type_t *, unsigned, char *); static void trace_connection_input_stop (trace_type_t *); static void trace_connection_output_input (trace_type_t *, unsigned, char *); static void trace_connection_output_stop (trace_type_t *); static trace_type_t *trace_connection_input; static trace_type_t *trace_connection_output; static isc_result_t omapi_connection_reader_trace (omapi_object_t *, unsigned, char *, unsigned *); extern omapi_array_t *omapi_connections; void omapi_buffer_trace_setup () { trace_connection_input = trace_type_register ("connection-input", (void *)0, trace_connection_input_input, trace_connection_input_stop, MDL); trace_connection_output = trace_type_register ("connection-output", (void *)0, trace_connection_output_input, trace_connection_output_stop, MDL); } static void trace_connection_input_input (trace_type_t *ttype, unsigned length, char *buf) { unsigned left, taken, cc = 0; char *s; int32_t connect_index; isc_result_t status; omapi_connection_object_t *c = (omapi_connection_object_t *)0; memcpy (&connect_index, buf, sizeof connect_index); connect_index = ntohl (connect_index); omapi_array_foreach_begin (omapi_connections, omapi_connection_object_t, lp) { if (lp -> index == ntohl (connect_index)) { omapi_connection_reference (&c, lp, MDL); omapi_connection_dereference (&lp, MDL); break; } } omapi_array_foreach_end (omapi_connections, omapi_connection_object_t, lp); if (!c) { log_error ("trace connection input: no connection index %ld", (long int)connect_index); return; } s = buf + sizeof connect_index; left = length - sizeof connect_index; while (left) { taken = 0; status = omapi_connection_reader_trace ((omapi_object_t *)c, left, s, &taken); if (status != ISC_R_SUCCESS) { log_error ("trace connection input: %s", isc_result_totext (status)); break; } if (!taken) { if (cc > 0) { log_error ("trace connection_input: %s", "input is not being consumed."); break; } cc++; } else { cc = 0; left -= taken; } } omapi_connection_dereference (&c, MDL); } static void trace_connection_input_stop (trace_type_t *ttype) { } static void trace_connection_output_input (trace_type_t *ttype, unsigned length, char *buf) { /* We *could* check to see if the output is correct, but for now we aren't going to do that. */ } static void trace_connection_output_stop (trace_type_t *ttype) { } #endif /* Make sure that at least len bytes are in the input buffer, and if not, read enough bytes to make up the difference. */ isc_result_t omapi_connection_reader (omapi_object_t *h) { #if defined (TRACING) return omapi_connection_reader_trace (h, 0, (char *)0, (unsigned *)0); } static isc_result_t omapi_connection_reader_trace (omapi_object_t *h, unsigned stuff_len, char *stuff_buf, unsigned *stuff_taken) { #endif omapi_buffer_t *buffer; isc_result_t status; unsigned read_len; int read_status; omapi_connection_object_t *c; unsigned bytes_to_read; if (!h || h -> type != omapi_type_connection) return DHCP_R_INVALIDARG; c = (omapi_connection_object_t *)h; /* See if there are enough bytes. */ if (c -> in_bytes >= OMAPI_BUF_SIZE - 1 && c -> in_bytes > c -> bytes_needed) return ISC_R_SUCCESS; if (c -> inbufs) { for (buffer = c -> inbufs; buffer -> next; buffer = buffer -> next) ; if (!BUFFER_BYTES_FREE (buffer)) { status = omapi_buffer_new (&buffer -> next, MDL); if (status != ISC_R_SUCCESS) return status; buffer = buffer -> next; } } else { status = omapi_buffer_new (&c -> inbufs, MDL); if (status != ISC_R_SUCCESS) return status; buffer = c -> inbufs; } bytes_to_read = BUFFER_BYTES_FREE (buffer); while (bytes_to_read) { if (buffer -> tail > buffer -> head) read_len = sizeof (buffer -> buf) - buffer -> tail; else read_len = buffer -> head - buffer -> tail; #if defined (TRACING) if (trace_playback()) { if (stuff_len) { if (read_len > stuff_len) read_len = stuff_len; if (stuff_taken) *stuff_taken += read_len; memcpy (&buffer -> buf [buffer -> tail], stuff_buf, read_len); stuff_len -= read_len; stuff_buf += read_len; read_status = read_len; } else { break; } } else #endif { read_status = read (c -> socket, &buffer -> buf [buffer -> tail], read_len); } if (read_status < 0) { if (errno == EWOULDBLOCK) break; else if (errno == EIO) return ISC_R_IOERROR; else if (errno == EINVAL) return DHCP_R_INVALIDARG; else if (errno == ECONNRESET) { omapi_disconnect (h, 1); return ISC_R_SHUTTINGDOWN; } else return ISC_R_UNEXPECTED; } /* If we got a zero-length read, as opposed to EWOULDBLOCK, the remote end closed the connection. */ if (read_status == 0) { omapi_disconnect (h, 0); return ISC_R_SHUTTINGDOWN; } #if defined (TRACING) if (trace_record ()) { trace_iov_t iov [2]; int32_t connect_index; connect_index = htonl (c -> index); iov [0].buf = (char *)&connect_index; iov [0].len = sizeof connect_index; iov [1].buf = &buffer -> buf [buffer -> tail]; iov [1].len = read_status; status = (trace_write_packet_iov (trace_connection_input, 2, iov, MDL)); if (status != ISC_R_SUCCESS) { trace_stop (); log_error ("trace connection input: %s", isc_result_totext (status)); } } #endif buffer -> tail += read_status; c -> in_bytes += read_status; if (buffer -> tail == sizeof buffer -> buf) buffer -> tail = 0; if (read_status < read_len) break; bytes_to_read -= read_status; } if (c -> bytes_needed <= c -> in_bytes) { omapi_signal (h, "ready", c); } return ISC_R_SUCCESS; } /* Put some bytes into the output buffer for a connection. */ isc_result_t omapi_connection_copyin (omapi_object_t *h, const unsigned char *bufp, unsigned len) { omapi_buffer_t *buffer; isc_result_t status; int bytes_copied = 0; unsigned copy_len; int sig_flags = SIG_MODE_UPDATE; omapi_connection_object_t *c; /* no need to verify len as it's unsigned */ if (!h || h -> type != omapi_type_connection) return DHCP_R_INVALIDARG; c = (omapi_connection_object_t *)h; /* If the connection is closed, return an error if the caller tries to copy in. */ if (c -> state == omapi_connection_disconnecting || c -> state == omapi_connection_closed) return ISC_R_NOTCONNECTED; if (c -> outbufs) { for (buffer = c -> outbufs; buffer -> next; buffer = buffer -> next) ; } else { status = omapi_buffer_new (&c -> outbufs, MDL); if (status != ISC_R_SUCCESS) goto leave; buffer = c -> outbufs; } while (bytes_copied < len) { /* If there is no space available in this buffer, allocate a new one. */ if (!BUFFER_BYTES_FREE (buffer)) { status = (omapi_buffer_new (&buffer -> next, MDL)); if (status != ISC_R_SUCCESS) goto leave; buffer = buffer -> next; } if (buffer -> tail > buffer -> head) copy_len = sizeof (buffer -> buf) - buffer -> tail; else copy_len = buffer -> head - buffer -> tail; if (copy_len > (len - bytes_copied)) copy_len = len - bytes_copied; if (c -> out_key) { if (!c -> out_context) sig_flags |= SIG_MODE_INIT; status = omapi_connection_sign_data (sig_flags, c -> out_key, &c -> out_context, &bufp [bytes_copied], copy_len, (omapi_typed_data_t **)0); if (status != ISC_R_SUCCESS) goto leave; } memcpy (&buffer -> buf [buffer -> tail], &bufp [bytes_copied], copy_len); buffer -> tail += copy_len; c -> out_bytes += copy_len; bytes_copied += copy_len; if (buffer -> tail == sizeof buffer -> buf) buffer -> tail = 0; } status = ISC_R_SUCCESS; leave: /* * If we have any bytes to send and we have a proper io object * inform the socket code that we would like to know when we * can send more bytes. */ if (c->out_bytes != 0) { if ((c->outer != NULL) && (c->outer->type == omapi_type_io_object)) { omapi_io_object_t *io = (omapi_io_object_t *)c->outer; isc_socket_fdwatchpoke(io->fd, ISC_SOCKFDWATCH_WRITE); } } return (status); } /* Copy some bytes from the input buffer, and advance the input buffer pointer beyond the bytes copied out. */ isc_result_t omapi_connection_copyout (unsigned char *buf, omapi_object_t *h, unsigned size) { unsigned bytes_remaining; unsigned bytes_this_copy; unsigned first_byte; omapi_buffer_t *buffer; unsigned char *bufp; int sig_flags = SIG_MODE_UPDATE; omapi_connection_object_t *c; isc_result_t status; if (!h || h -> type != omapi_type_connection) return DHCP_R_INVALIDARG; c = (omapi_connection_object_t *)h; if (size > c -> in_bytes) return ISC_R_NOMORE; bufp = buf; bytes_remaining = size; buffer = c -> inbufs; while (bytes_remaining) { if (!buffer) return ISC_R_UNEXPECTED; if (BYTES_IN_BUFFER (buffer)) { if (buffer -> head == (sizeof buffer -> buf) - 1) first_byte = 0; else first_byte = buffer -> head + 1; if (first_byte > buffer -> tail) { bytes_this_copy = (sizeof buffer -> buf - first_byte); } else { bytes_this_copy = buffer -> tail - first_byte; } if (bytes_this_copy > bytes_remaining) bytes_this_copy = bytes_remaining; if (bufp) { if (c -> in_key) { if (!c -> in_context) sig_flags |= SIG_MODE_INIT; status = omapi_connection_sign_data (sig_flags, c -> in_key, &c -> in_context, (unsigned char *) &buffer -> buf [first_byte], bytes_this_copy, (omapi_typed_data_t **)0); if (status != ISC_R_SUCCESS) return status; } memcpy (bufp, &buffer -> buf [first_byte], bytes_this_copy); bufp += bytes_this_copy; } bytes_remaining -= bytes_this_copy; buffer -> head = first_byte + bytes_this_copy - 1; c -> in_bytes -= bytes_this_copy; } if (!BYTES_IN_BUFFER (buffer)) buffer = buffer -> next; } /* Get rid of any input buffers that we emptied. */ buffer = (omapi_buffer_t *)0; while (c -> inbufs && !BYTES_IN_BUFFER (c -> inbufs)) { if (c -> inbufs -> next) { omapi_buffer_reference (&buffer, c -> inbufs -> next, MDL); omapi_buffer_dereference (&c -> inbufs -> next, MDL); } omapi_buffer_dereference (&c -> inbufs, MDL); if (buffer) { omapi_buffer_reference (&c -> inbufs, buffer, MDL); omapi_buffer_dereference (&buffer, MDL); } } return ISC_R_SUCCESS; } isc_result_t omapi_connection_writer (omapi_object_t *h) { unsigned bytes_this_write; int bytes_written; unsigned first_byte; omapi_buffer_t *buffer; omapi_connection_object_t *c; if (!h || h -> type != omapi_type_connection) return DHCP_R_INVALIDARG; c = (omapi_connection_object_t *)h; /* Already flushed... */ if (!c -> out_bytes) return ISC_R_SUCCESS; buffer = c -> outbufs; while (c -> out_bytes) { if (!buffer) return ISC_R_UNEXPECTED; if (BYTES_IN_BUFFER (buffer)) { if (buffer -> head == (sizeof buffer -> buf) - 1) first_byte = 0; else first_byte = buffer -> head + 1; if (first_byte > buffer -> tail) { bytes_this_write = (sizeof buffer -> buf - first_byte); } else { bytes_this_write = buffer -> tail - first_byte; } bytes_written = write (c -> socket, &buffer -> buf [first_byte], bytes_this_write); /* If the write failed with EWOULDBLOCK or we wrote zero bytes, a further write would block, so we have flushed as much as we can for now. Other errors are really errors. */ if (bytes_written < 0) { if (errno == EWOULDBLOCK || errno == EAGAIN) return ISC_R_INPROGRESS; else if (errno == EPIPE) return ISC_R_NOCONN; #ifdef EDQUOT else if (errno == EFBIG || errno == EDQUOT) #else else if (errno == EFBIG) #endif return ISC_R_NORESOURCES; else if (errno == ENOSPC) return ISC_R_NOSPACE; else if (errno == EIO) return ISC_R_IOERROR; else if (errno == EINVAL) return DHCP_R_INVALIDARG; else if (errno == ECONNRESET) return ISC_R_SHUTTINGDOWN; else return ISC_R_UNEXPECTED; } if (bytes_written == 0) return ISC_R_INPROGRESS; #if defined (TRACING) if (trace_record ()) { isc_result_t status; trace_iov_t iov [2]; int32_t connect_index; connect_index = htonl (c -> index); iov [0].buf = (char *)&connect_index; iov [0].len = sizeof connect_index; iov [1].buf = &buffer -> buf [buffer -> tail]; iov [1].len = bytes_written; status = (trace_write_packet_iov (trace_connection_input, 2, iov, MDL)); if (status != ISC_R_SUCCESS) { trace_stop (); log_error ("trace %s output: %s", "connection", isc_result_totext (status)); } } #endif buffer -> head = first_byte + bytes_written - 1; c -> out_bytes -= bytes_written; /* If we didn't finish out the write, we filled the O.S. output buffer and a further write would block, so stop trying to flush now. */ if (bytes_written != bytes_this_write) return ISC_R_INPROGRESS; } if (!BYTES_IN_BUFFER (buffer)) buffer = buffer -> next; } /* Get rid of any output buffers we emptied. */ buffer = (omapi_buffer_t *)0; while (c -> outbufs && !BYTES_IN_BUFFER (c -> outbufs)) { if (c -> outbufs -> next) { omapi_buffer_reference (&buffer, c -> outbufs -> next, MDL); omapi_buffer_dereference (&c -> outbufs -> next, MDL); } omapi_buffer_dereference (&c -> outbufs, MDL); if (buffer) { omapi_buffer_reference (&c -> outbufs, buffer, MDL); omapi_buffer_dereference (&buffer, MDL); } } /* If we had data left to write when we're told to disconnect, * we need recall disconnect, now that we're done writing. * See rt46767. */ if (c->out_bytes == 0 && c->state == omapi_connection_disconnecting) { omapi_disconnect (h, 1); return ISC_R_SHUTTINGDOWN; } return ISC_R_SUCCESS; } isc_result_t omapi_connection_get_uint32 (omapi_object_t *c, u_int32_t *result) { u_int32_t inbuf; isc_result_t status; status = omapi_connection_copyout ((unsigned char *)&inbuf, c, sizeof inbuf); if (status != ISC_R_SUCCESS) return status; *result = ntohl (inbuf); return ISC_R_SUCCESS; } isc_result_t omapi_connection_put_uint32 (omapi_object_t *c, u_int32_t value) { u_int32_t inbuf; inbuf = htonl (value); return omapi_connection_copyin (c, (unsigned char *)&inbuf, sizeof inbuf); } isc_result_t omapi_connection_get_uint16 (omapi_object_t *c, u_int16_t *result) { u_int16_t inbuf; isc_result_t status; status = omapi_connection_copyout ((unsigned char *)&inbuf, c, sizeof inbuf); if (status != ISC_R_SUCCESS) return status; *result = ntohs (inbuf); return ISC_R_SUCCESS; } isc_result_t omapi_connection_put_uint16 (omapi_object_t *c, u_int32_t value) { u_int16_t inbuf; inbuf = htons (value); return omapi_connection_copyin (c, (unsigned char *)&inbuf, sizeof inbuf); } isc_result_t omapi_connection_write_typed_data (omapi_object_t *c, omapi_typed_data_t *data) { isc_result_t status; omapi_handle_t handle; /* Null data is valid. */ if (!data) return omapi_connection_put_uint32 (c, 0); switch (data -> type) { case omapi_datatype_int: status = omapi_connection_put_uint32 (c, sizeof (u_int32_t)); if (status != ISC_R_SUCCESS) return status; return omapi_connection_put_uint32 (c, ((u_int32_t) (data -> u.integer))); case omapi_datatype_string: case omapi_datatype_data: status = omapi_connection_put_uint32 (c, data -> u.buffer.len); if (status != ISC_R_SUCCESS) return status; if (data -> u.buffer.len) return omapi_connection_copyin (c, data -> u.buffer.value, data -> u.buffer.len); return ISC_R_SUCCESS; case omapi_datatype_object: if (data -> u.object) { status = omapi_object_handle (&handle, data -> u.object); if (status != ISC_R_SUCCESS) return status; } else handle = 0; status = omapi_connection_put_uint32 (c, sizeof handle); if (status != ISC_R_SUCCESS) return status; return omapi_connection_put_uint32 (c, handle); } return DHCP_R_INVALIDARG; } isc_result_t omapi_connection_put_name (omapi_object_t *c, const char *name) { isc_result_t status; unsigned len = strlen (name); status = omapi_connection_put_uint16 (c, len); if (status != ISC_R_SUCCESS) return status; return omapi_connection_copyin (c, (const unsigned char *)name, len); } isc_result_t omapi_connection_put_string (omapi_object_t *c, const char *string) { isc_result_t status; unsigned len; if (string) len = strlen (string); else len = 0; status = omapi_connection_put_uint32 (c, len); if (status != ISC_R_SUCCESS) return status; if (len) return omapi_connection_copyin (c, (const unsigned char *)string, len); return ISC_R_SUCCESS; } isc_result_t omapi_connection_put_handle (omapi_object_t *c, omapi_object_t *h) { isc_result_t status; omapi_handle_t handle; if (h) { status = omapi_object_handle (&handle, h); if (status != ISC_R_SUCCESS) return status; } else handle = 0; /* The null handle. */ status = omapi_connection_put_uint32 (c, sizeof handle); if (status != ISC_R_SUCCESS) return status; return omapi_connection_put_uint32 (c, handle); } isc_result_t omapi_connection_put_named_uint32 (omapi_object_t *c, const char *name, u_int32_t value) { isc_result_t status; status = omapi_connection_put_name(c, name); if (status != ISC_R_SUCCESS) return (status); status = omapi_connection_put_uint32(c, sizeof(u_int32_t)); if (status != ISC_R_SUCCESS) return (status); status = omapi_connection_put_uint32(c, value); return (status); } dhcp-4.4.1/omapip/connection.c000644 000765 000024 00000073723 13243301226 016517 0ustar00tmarkstaff000000 000000 /* connection.c Subroutines for dealing with connections. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1999-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #include #include #include #include #include #if defined (TRACING) static void trace_connect_input (trace_type_t *, unsigned, char *); static void trace_connect_stop (trace_type_t *); static void trace_disconnect_input (trace_type_t *, unsigned, char *); static void trace_disconnect_stop (trace_type_t *); trace_type_t *trace_connect; trace_type_t *trace_disconnect; extern omapi_array_t *trace_listeners; #endif static isc_result_t omapi_connection_connect_internal (omapi_object_t *); OMAPI_OBJECT_ALLOC (omapi_connection, omapi_connection_object_t, omapi_type_connection) isc_result_t omapi_connect (omapi_object_t *c, const char *server_name, unsigned port) { struct hostent *he; unsigned i, hix; omapi_addr_list_t *addrs = (omapi_addr_list_t *)0; struct in_addr foo; isc_result_t status; #ifdef DEBUG_PROTOCOL log_debug ("omapi_connect(%s, port=%d)", server_name, port); #endif if (!inet_aton (server_name, &foo)) { /* If we didn't get a numeric address, try for a domain name. It's okay for this call to block. */ he = gethostbyname (server_name); if (!he) return DHCP_R_HOSTUNKNOWN; for (i = 0; he -> h_addr_list [i]; i++) ; if (i == 0) return DHCP_R_HOSTUNKNOWN; hix = i; status = omapi_addr_list_new (&addrs, hix, MDL); if (status != ISC_R_SUCCESS) return status; for (i = 0; i < hix; i++) { addrs -> addresses [i].addrtype = he -> h_addrtype; addrs -> addresses [i].addrlen = he -> h_length; memcpy (addrs -> addresses [i].address, he -> h_addr_list [i], (unsigned)he -> h_length); addrs -> addresses [i].port = port; } } else { status = omapi_addr_list_new (&addrs, 1, MDL); if (status != ISC_R_SUCCESS) return status; addrs -> addresses [0].addrtype = AF_INET; addrs -> addresses [0].addrlen = sizeof foo; memcpy (addrs -> addresses [0].address, &foo, sizeof foo); addrs -> addresses [0].port = port; } status = omapi_connect_list (c, addrs, (omapi_addr_t *)0); omapi_addr_list_dereference (&addrs, MDL); return status; } isc_result_t omapi_connect_list (omapi_object_t *c, omapi_addr_list_t *remote_addrs, omapi_addr_t *local_addr) { isc_result_t status; omapi_connection_object_t *obj; int flag; struct sockaddr_in local_sin; obj = (omapi_connection_object_t *)0; status = omapi_connection_allocate (&obj, MDL); if (status != ISC_R_SUCCESS) return status; status = omapi_object_reference (&c -> outer, (omapi_object_t *)obj, MDL); if (status != ISC_R_SUCCESS) { omapi_connection_dereference (&obj, MDL); return status; } status = omapi_object_reference (&obj -> inner, c, MDL); if (status != ISC_R_SUCCESS) { omapi_connection_dereference (&obj, MDL); return status; } /* Store the address list on the object. */ omapi_addr_list_reference (&obj -> connect_list, remote_addrs, MDL); obj -> cptr = 0; obj -> state = omapi_connection_unconnected; #if defined (TRACING) /* If we're playing back, don't actually try to connect - just leave the object available for a subsequent connect or disconnect. */ if (!trace_playback ()) { #endif /* Create a socket on which to communicate. */ obj -> socket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); if (obj -> socket < 0) { omapi_connection_dereference (&obj, MDL); if (errno == EMFILE || errno == ENFILE || errno == ENOBUFS) return ISC_R_NORESOURCES; return ISC_R_UNEXPECTED; } /* Set up the local address, if any. */ if (local_addr) { /* Only do TCPv4 so far. */ if (local_addr -> addrtype != AF_INET) { close(obj->socket); omapi_connection_dereference (&obj, MDL); return DHCP_R_INVALIDARG; } local_sin.sin_port = htons (local_addr -> port); memcpy (&local_sin.sin_addr, local_addr -> address, local_addr -> addrlen); #if defined (HAVE_SA_LEN) local_sin.sin_len = sizeof local_addr; #endif local_sin.sin_family = AF_INET; memset (&local_sin.sin_zero, 0, sizeof local_sin.sin_zero); if (bind (obj -> socket, (struct sockaddr *)&local_sin, sizeof local_sin) < 0) { omapi_connection_object_t **objp = &obj; omapi_object_t **o = (omapi_object_t **)objp; close(obj->socket); omapi_object_dereference(o, MDL); if (errno == EADDRINUSE) return ISC_R_ADDRINUSE; if (errno == EADDRNOTAVAIL) return ISC_R_ADDRNOTAVAIL; if (errno == EACCES) return ISC_R_NOPERM; return ISC_R_UNEXPECTED; } obj -> local_addr = local_sin; } #if defined(F_SETFD) if (fcntl (obj -> socket, F_SETFD, 1) < 0) { close (obj -> socket); omapi_connection_dereference (&obj, MDL); return ISC_R_UNEXPECTED; } #endif /* Set the SO_REUSEADDR flag (this should not fail). */ flag = 1; if (setsockopt (obj -> socket, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof flag) < 0) { omapi_connection_dereference (&obj, MDL); return ISC_R_UNEXPECTED; } /* Set the file to nonblocking mode. */ if (fcntl (obj -> socket, F_SETFL, O_NONBLOCK) < 0) { omapi_connection_dereference (&obj, MDL); return ISC_R_UNEXPECTED; } #ifdef SO_NOSIGPIPE /* * If available stop the OS from killing our * program on a SIGPIPE failure */ flag = 1; if (setsockopt(obj->socket, SOL_SOCKET, SO_NOSIGPIPE, (char *)&flag, sizeof(flag)) < 0) { omapi_connection_dereference (&obj, MDL); return ISC_R_UNEXPECTED; } #endif status = (omapi_register_io_object ((omapi_object_t *)obj, 0, omapi_connection_writefd, 0, omapi_connection_connect, omapi_connection_reaper)); if (status != ISC_R_SUCCESS) goto out; status = omapi_connection_connect_internal ((omapi_object_t *) obj); /* * inprogress is the same as success but used * to indicate to the dispatch code that we should * mark the socket as requiring more attention. * Routines calling this function should handle * success properly. */ if (status == ISC_R_INPROGRESS) { status = ISC_R_SUCCESS; } #if defined (TRACING) } omapi_connection_register (obj, MDL); #endif out: omapi_connection_dereference (&obj, MDL); return status; } #if defined (TRACING) omapi_array_t *omapi_connections; OMAPI_ARRAY_TYPE(omapi_connection, omapi_connection_object_t) void omapi_connection_trace_setup (void) { trace_connect = trace_type_register ("connect", (void *)0, trace_connect_input, trace_connect_stop, MDL); trace_disconnect = trace_type_register ("disconnect", (void *)0, trace_disconnect_input, trace_disconnect_stop, MDL); } void omapi_connection_register (omapi_connection_object_t *obj, const char *file, int line) { isc_result_t status; trace_iov_t iov [6]; int iov_count = 0; int32_t connect_index, listener_index; static int32_t index; if (!omapi_connections) { status = omapi_connection_array_allocate (&omapi_connections, file, line); if (status != ISC_R_SUCCESS) return; } status = omapi_connection_array_extend (omapi_connections, obj, (int *)0, file, line); if (status != ISC_R_SUCCESS) { obj -> index = -1; return; } #if defined (TRACING) if (trace_record ()) { /* Connection registration packet: int32_t index int32_t listener_index [-1 means no listener] u_int16_t remote_port u_int16_t local_port u_int32_t remote_addr u_int32_t local_addr */ connect_index = htonl (index); index++; if (obj -> listener) listener_index = htonl (obj -> listener -> index); else listener_index = htonl (-1); iov [iov_count].buf = (char *)&connect_index; iov [iov_count++].len = sizeof connect_index; iov [iov_count].buf = (char *)&listener_index; iov [iov_count++].len = sizeof listener_index; iov [iov_count].buf = (char *)&obj -> remote_addr.sin_port; iov [iov_count++].len = sizeof obj -> remote_addr.sin_port; iov [iov_count].buf = (char *)&obj -> local_addr.sin_port; iov [iov_count++].len = sizeof obj -> local_addr.sin_port; iov [iov_count].buf = (char *)&obj -> remote_addr.sin_addr; iov [iov_count++].len = sizeof obj -> remote_addr.sin_addr; iov [iov_count].buf = (char *)&obj -> local_addr.sin_addr; iov [iov_count++].len = sizeof obj -> local_addr.sin_addr; status = trace_write_packet_iov (trace_connect, iov_count, iov, file, line); } #endif } static void trace_connect_input (trace_type_t *ttype, unsigned length, char *buf) { struct sockaddr_in remote, local; int32_t connect_index, listener_index; char *s = buf; omapi_connection_object_t *obj; isc_result_t status; int i; if (length != ((sizeof connect_index) + (sizeof remote.sin_port) + (sizeof remote.sin_addr)) * 2) { log_error ("Trace connect: invalid length %d", length); return; } memset (&remote, 0, sizeof remote); memset (&local, 0, sizeof local); memcpy (&connect_index, s, sizeof connect_index); s += sizeof connect_index; memcpy (&listener_index, s, sizeof listener_index); s += sizeof listener_index; memcpy (&remote.sin_port, s, sizeof remote.sin_port); s += sizeof remote.sin_port; memcpy (&local.sin_port, s, sizeof local.sin_port); s += sizeof local.sin_port; memcpy (&remote.sin_addr, s, sizeof remote.sin_addr); s += sizeof remote.sin_addr; memcpy (&local.sin_addr, s, sizeof local.sin_addr); s += sizeof local.sin_addr; POST(s); connect_index = ntohl (connect_index); listener_index = ntohl (listener_index); /* If this was a connect to a listener, then we just slap together a new connection. */ if (listener_index != -1) { omapi_listener_object_t *listener; listener = (omapi_listener_object_t *)0; omapi_array_foreach_begin (trace_listeners, omapi_listener_object_t, lp) { if (lp -> address.sin_port == local.sin_port) { omapi_listener_reference (&listener, lp, MDL); omapi_listener_dereference (&lp, MDL); break; } } omapi_array_foreach_end (trace_listeners, omapi_listener_object_t, lp); if (!listener) { log_error ("%s%ld, addr %s, port %d", "Spurious traced listener connect - index ", (long int)listener_index, inet_ntoa (local.sin_addr), ntohs (local.sin_port)); return; } obj = (omapi_connection_object_t *)0; status = omapi_listener_connect (&obj, listener, -1, &remote); if (status != ISC_R_SUCCESS) { log_error ("traced listener connect: %s", isc_result_totext (status)); } if (obj) omapi_connection_dereference (&obj, MDL); omapi_listener_dereference (&listener, MDL); return; } /* Find the matching connect object, if there is one. */ omapi_array_foreach_begin (omapi_connections, omapi_connection_object_t, lp) { for (i = 0; (lp->connect_list && i < lp->connect_list->count); i++) { if (!memcmp (&remote.sin_addr, &lp->connect_list->addresses[i].address, sizeof remote.sin_addr) && (ntohs (remote.sin_port) == lp->connect_list->addresses[i].port)) { lp->state = omapi_connection_connected; lp->remote_addr = remote; lp->remote_addr.sin_family = AF_INET; omapi_addr_list_dereference(&lp->connect_list, MDL); lp->index = connect_index; status = omapi_signal_in((omapi_object_t *)lp, "connect"); omapi_connection_dereference (&lp, MDL); return; } } } omapi_array_foreach_end (omapi_connections, omapi_connection_object_t, lp); log_error ("Spurious traced connect - index %ld, addr %s, port %d", (long int)connect_index, inet_ntoa (remote.sin_addr), ntohs (remote.sin_port)); return; } static void trace_connect_stop (trace_type_t *ttype) { } static void trace_disconnect_input (trace_type_t *ttype, unsigned length, char *buf) { int32_t *index; if (length != sizeof *index) { log_error ("trace disconnect: wrong length %d", length); return; } index = (int32_t *)buf; omapi_array_foreach_begin (omapi_connections, omapi_connection_object_t, lp) { if (lp -> index == ntohl (*index)) { omapi_disconnect ((omapi_object_t *)lp, 1); omapi_connection_dereference (&lp, MDL); return; } } omapi_array_foreach_end (omapi_connections, omapi_connection_object_t, lp); log_error ("trace disconnect: no connection matching index %ld", (long int)ntohl (*index)); } static void trace_disconnect_stop (trace_type_t *ttype) { } #endif /* Disconnect a connection object from the remote end. If force is nonzero, close the connection immediately. Otherwise, shut down the receiving end but allow any unsent data to be sent before actually closing the socket. */ isc_result_t omapi_disconnect (omapi_object_t *h, int force) { omapi_connection_object_t *c; #ifdef DEBUG_PROTOCOL log_debug ("omapi_disconnect(%s)", force ? "force" : ""); #endif c = (omapi_connection_object_t *)h; if (c -> type != omapi_type_connection) return DHCP_R_INVALIDARG; #if defined (TRACING) if (trace_record ()) { isc_result_t status; int32_t index; index = htonl (c -> index); status = trace_write_packet (trace_disconnect, sizeof index, (char *)&index, MDL); if (status != ISC_R_SUCCESS) { trace_stop (); log_error ("trace_write_packet: %s", isc_result_totext (status)); } } if (!trace_playback ()) { #endif if (!force) { /* If we're already disconnecting, we don't have to do anything. */ if (c -> state == omapi_connection_disconnecting) return ISC_R_SUCCESS; /* Try to shut down the socket - this sends a FIN to the remote end, so that it won't send us any more data. If the shutdown succeeds, and we still have bytes left to write, defer closing the socket until that's done. */ if (!shutdown (c -> socket, SHUT_RD)) { if (c -> out_bytes > 0) { c -> state = omapi_connection_disconnecting; return ISC_R_SUCCESS; } } } close (c -> socket); #if defined (TRACING) } #endif c -> state = omapi_connection_closed; #if 0 /* * Disconnecting from the I/O object seems incorrect as it doesn't * cause the I/O object to be cleaned and released. Previous to * using the isc socket library this wouldn't have caused a problem * with the socket library we would have a reference to a closed * socket. Instead we now do an unregister to properly free the * I/O object. */ /* Disconnect from I/O object, if any. */ if (h -> outer) { if (h -> outer -> inner) omapi_object_dereference (&h -> outer -> inner, MDL); omapi_object_dereference (&h -> outer, MDL); } #else if (h->outer) { omapi_unregister_io_object(h); } #endif /* If whatever created us registered a signal handler, send it a disconnect signal. */ omapi_signal (h, "disconnect", h); /* Disconnect from protocol object, if any. */ if (h->inner != NULL) { if (h->inner->outer != NULL) { omapi_object_dereference(&h->inner->outer, MDL); } omapi_object_dereference(&h->inner, MDL); } /* XXX: the code to free buffers should be in the dereference function, but there is no special-purpose function to dereference connections, so these just get leaked */ /* Free any buffers */ if (c->inbufs != NULL) { omapi_buffer_dereference(&c->inbufs, MDL); } c->in_bytes = 0; if (c->outbufs != NULL) { omapi_buffer_dereference(&c->outbufs, MDL); } c->out_bytes = 0; return ISC_R_SUCCESS; } isc_result_t omapi_connection_require (omapi_object_t *h, unsigned bytes) { omapi_connection_object_t *c; if (h -> type != omapi_type_connection) return DHCP_R_INVALIDARG; c = (omapi_connection_object_t *)h; c -> bytes_needed = bytes; if (c -> bytes_needed <= c -> in_bytes) { return ISC_R_SUCCESS; } return DHCP_R_NOTYET; } /* Return the socket on which the dispatcher should wait for readiness to read, for a connection object. */ int omapi_connection_readfd (omapi_object_t *h) { omapi_connection_object_t *c; if (h -> type != omapi_type_connection) return -1; c = (omapi_connection_object_t *)h; if (c -> state != omapi_connection_connected) return -1; return c -> socket; } /* * Return the socket on which the dispatcher should wait for readiness * to write, for a connection object. When bytes are buffered we should * also poke the dispatcher to tell it to start or re-start watching the * socket. */ int omapi_connection_writefd (omapi_object_t *h) { omapi_connection_object_t *c; if (h -> type != omapi_type_connection) return -1; c = (omapi_connection_object_t *)h; return c->socket; } isc_result_t omapi_connection_connect (omapi_object_t *h) { isc_result_t status; /* * We use the INPROGRESS status to indicate that * we want more from the socket. In this case we * have now connected and are trying to write to * the socket for the first time. For the signaling * code this is the same as a SUCCESS so we don't * pass it on as a signal. */ status = omapi_connection_connect_internal (h); if (status == ISC_R_INPROGRESS) return ISC_R_INPROGRESS; if (status != ISC_R_SUCCESS) omapi_signal (h, "status", status); return ISC_R_SUCCESS; } static isc_result_t omapi_connection_connect_internal (omapi_object_t *h) { int error = 0; omapi_connection_object_t *c; socklen_t sl; isc_result_t status; if (h -> type != omapi_type_connection) return DHCP_R_INVALIDARG; c = (omapi_connection_object_t *)h; if (c -> state == omapi_connection_connecting) { sl = sizeof error; if (getsockopt (c -> socket, SOL_SOCKET, SO_ERROR, (char *)&error, &sl) < 0) { omapi_disconnect (h, 1); return ISC_R_SUCCESS; } if (!error) c -> state = omapi_connection_connected; } if (c -> state == omapi_connection_connecting || c -> state == omapi_connection_unconnected) { if (c -> cptr >= c -> connect_list -> count) { switch (error) { case ECONNREFUSED: status = ISC_R_CONNREFUSED; break; case ENETUNREACH: status = ISC_R_NETUNREACH; break; default: status = uerr2isc (error); break; } omapi_disconnect (h, 1); return status; } if (c -> connect_list -> addresses [c -> cptr].addrtype != AF_INET) { omapi_disconnect (h, 1); return DHCP_R_INVALIDARG; } memcpy (&c -> remote_addr.sin_addr, &c -> connect_list -> addresses [c -> cptr].address, sizeof c -> remote_addr.sin_addr); c -> remote_addr.sin_family = AF_INET; c -> remote_addr.sin_port = htons (c -> connect_list -> addresses [c -> cptr].port); #if defined (HAVE_SA_LEN) c -> remote_addr.sin_len = sizeof c -> remote_addr; #endif memset (&c -> remote_addr.sin_zero, 0, sizeof c -> remote_addr.sin_zero); ++c -> cptr; error = connect (c -> socket, (struct sockaddr *)&c -> remote_addr, sizeof c -> remote_addr); if (error < 0) { error = errno; if (error != EINPROGRESS) { omapi_disconnect (h, 1); switch (error) { case ECONNREFUSED: status = ISC_R_CONNREFUSED; break; case ENETUNREACH: status = ISC_R_NETUNREACH; break; default: status = uerr2isc (error); break; } return status; } c -> state = omapi_connection_connecting; return DHCP_R_INCOMPLETE; } c -> state = omapi_connection_connected; } /* I don't know why this would fail, so I'm tempted not to test the return value. */ sl = sizeof (c -> local_addr); if (getsockname (c -> socket, (struct sockaddr *)&c -> local_addr, &sl) < 0) { } /* Reregister with the I/O object. If we don't already have an I/O object this turns into a register call, otherwise we simply modify the pointers in the I/O object. */ status = omapi_reregister_io_object (h, omapi_connection_readfd, omapi_connection_writefd, omapi_connection_reader, omapi_connection_writer, omapi_connection_reaper); if (status != ISC_R_SUCCESS) { omapi_disconnect (h, 1); return status; } omapi_signal_in (h, "connect"); omapi_addr_list_dereference (&c -> connect_list, MDL); return ISC_R_INPROGRESS; } /* Reaper function for connection - if the connection is completely closed, reap it. If it's in the disconnecting state, there were bytes left to write when the user closed it, so if there are now no bytes left to write, we can close it. */ isc_result_t omapi_connection_reaper (omapi_object_t *h) { omapi_connection_object_t *c; if (h -> type != omapi_type_connection) return DHCP_R_INVALIDARG; c = (omapi_connection_object_t *)h; if (c -> state == omapi_connection_disconnecting && c -> out_bytes == 0) { #ifdef DEBUG_PROTOCOL log_debug ("omapi_connection_reaper(): disconnect"); #endif omapi_disconnect (h, 1); } if (c -> state == omapi_connection_closed) { #ifdef DEBUG_PROTOCOL log_debug ("omapi_connection_reaper(): closed"); #endif return ISC_R_NOTCONNECTED; } return ISC_R_SUCCESS; } static isc_result_t make_dst_key (dst_key_t **dst_key, omapi_object_t *a) { omapi_value_t *name = (omapi_value_t *)0; omapi_value_t *algorithm = (omapi_value_t *)0; omapi_value_t *key = (omapi_value_t *)0; char *name_str = NULL; isc_result_t status = ISC_R_SUCCESS; if (status == ISC_R_SUCCESS) status = omapi_get_value_str (a, (omapi_object_t *)0, "name", &name); if (status == ISC_R_SUCCESS) status = omapi_get_value_str (a, (omapi_object_t *)0, "algorithm", &algorithm); if (status == ISC_R_SUCCESS) status = omapi_get_value_str (a, (omapi_object_t *)0, "key", &key); if (status == ISC_R_SUCCESS) { if ((algorithm->value->type != omapi_datatype_data && algorithm->value->type != omapi_datatype_string) || strncasecmp((char *)algorithm->value->u.buffer.value, NS_TSIG_ALG_HMAC_MD5 ".", algorithm->value->u.buffer.len) != 0) { status = DHCP_R_INVALIDARG; } } if (status == ISC_R_SUCCESS) { name_str = dmalloc (name -> value -> u.buffer.len + 1, MDL); if (!name_str) status = ISC_R_NOMEMORY; } if (status == ISC_R_SUCCESS) { memcpy (name_str, name -> value -> u.buffer.value, name -> value -> u.buffer.len); name_str [name -> value -> u.buffer.len] = 0; status = isclib_make_dst_key(name_str, DHCP_HMAC_MD5_NAME, key->value->u.buffer.value, key->value->u.buffer.len, dst_key); if (*dst_key == NULL) status = ISC_R_NOMEMORY; } if (name_str) dfree (name_str, MDL); if (key) omapi_value_dereference (&key, MDL); if (algorithm) omapi_value_dereference (&algorithm, MDL); if (name) omapi_value_dereference (&name, MDL); return status; } isc_result_t omapi_connection_sign_data (int mode, dst_key_t *key, void **context, const unsigned char *data, const unsigned len, omapi_typed_data_t **result) { omapi_typed_data_t *td = (omapi_typed_data_t *)0; isc_result_t status; dst_context_t **dctx = (dst_context_t **)context; /* Create the context for the dst module */ if (mode & SIG_MODE_INIT) { status = dst_context_create(key, dhcp_gbl_ctx.mctx, dctx); if (status != ISC_R_SUCCESS) { return status; } } /* If we have any data add it to the context */ if (len != 0) { isc_region_t region; region.base = (unsigned char *)data; region.length = len; dst_context_adddata(*dctx, ®ion); } /* Finish the signature and clean up the context */ if (mode & SIG_MODE_FINAL) { unsigned int sigsize; isc_buffer_t sigbuf; status = dst_key_sigsize(key, &sigsize); if (status != ISC_R_SUCCESS) { goto cleanup; } status = omapi_typed_data_new (MDL, &td, omapi_datatype_data, sigsize); if (status != ISC_R_SUCCESS) { goto cleanup; } isc_buffer_init(&sigbuf, td->u.buffer.value, td->u.buffer.len); status = dst_context_sign(*dctx, &sigbuf); if (status != ISC_R_SUCCESS) { goto cleanup; } if (result) { omapi_typed_data_reference (result, td, MDL); } cleanup: /* We are done with the context and the td. On success * the td is now referenced from result, on failure we * don't need it any more */ if (td) { omapi_typed_data_dereference (&td, MDL); } dst_context_destroy(dctx); return status; } return ISC_R_SUCCESS; } isc_result_t omapi_connection_output_auth_length (omapi_object_t *h, unsigned *l) { omapi_connection_object_t *c; if (h->type != omapi_type_connection) return DHCP_R_INVALIDARG; c = (omapi_connection_object_t *)h; if (c->out_key == NULL) return ISC_R_NOTFOUND; return(dst_key_sigsize(c->out_key, l)); } isc_result_t omapi_connection_set_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_typed_data_t *value) { omapi_connection_object_t *c; isc_result_t status; if (h -> type != omapi_type_connection) return DHCP_R_INVALIDARG; c = (omapi_connection_object_t *)h; if (omapi_ds_strcmp (name, "input-authenticator") == 0) { if (value && value -> type != omapi_datatype_object) return DHCP_R_INVALIDARG; if (c -> in_context) { omapi_connection_sign_data (SIG_MODE_FINAL, c -> in_key, &c -> in_context, 0, 0, (omapi_typed_data_t **) 0); } if (c->in_key != NULL) { dst_key_free(&c->in_key); } if (value) { status = make_dst_key (&c -> in_key, value -> u.object); if (status != ISC_R_SUCCESS) return status; } return ISC_R_SUCCESS; } else if (omapi_ds_strcmp (name, "output-authenticator") == 0) { if (value && value -> type != omapi_datatype_object) return DHCP_R_INVALIDARG; if (c -> out_context) { omapi_connection_sign_data (SIG_MODE_FINAL, c -> out_key, &c -> out_context, 0, 0, (omapi_typed_data_t **) 0); } if (c->out_key != NULL) { dst_key_free(&c->out_key); } if (value) { status = make_dst_key (&c -> out_key, value -> u.object); if (status != ISC_R_SUCCESS) return status; } return ISC_R_SUCCESS; } if (h -> inner && h -> inner -> type -> set_value) return (*(h -> inner -> type -> set_value)) (h -> inner, id, name, value); return ISC_R_NOTFOUND; } isc_result_t omapi_connection_get_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_value_t **value) { omapi_connection_object_t *c; omapi_typed_data_t *td = (omapi_typed_data_t *)0; isc_result_t status; unsigned int sigsize; if (h -> type != omapi_type_connection) return DHCP_R_INVALIDARG; c = (omapi_connection_object_t *)h; if (omapi_ds_strcmp (name, "input-signature") == 0) { if (!c -> in_key || !c -> in_context) return ISC_R_NOTFOUND; status = omapi_connection_sign_data (SIG_MODE_FINAL, c -> in_key, &c -> in_context, 0, 0, &td); if (status != ISC_R_SUCCESS) return status; status = omapi_make_value (value, name, td, MDL); omapi_typed_data_dereference (&td, MDL); return status; } else if (omapi_ds_strcmp (name, "input-signature-size") == 0) { if (c->in_key == NULL) return ISC_R_NOTFOUND; status = dst_key_sigsize(c->in_key, &sigsize); if (status != ISC_R_SUCCESS) { return(status); } return omapi_make_int_value(value, name, sigsize, MDL); } else if (omapi_ds_strcmp (name, "output-signature") == 0) { if (!c -> out_key || !c -> out_context) return ISC_R_NOTFOUND; status = omapi_connection_sign_data (SIG_MODE_FINAL, c -> out_key, &c -> out_context, 0, 0, &td); if (status != ISC_R_SUCCESS) return status; status = omapi_make_value (value, name, td, MDL); omapi_typed_data_dereference (&td, MDL); return status; } else if (omapi_ds_strcmp (name, "output-signature-size") == 0) { if (c->out_key == NULL) return ISC_R_NOTFOUND; status = dst_key_sigsize(c->out_key, &sigsize); if (status != ISC_R_SUCCESS) { return(status); } return omapi_make_int_value(value, name, sigsize, MDL); } if (h -> inner && h -> inner -> type -> get_value) return (*(h -> inner -> type -> get_value)) (h -> inner, id, name, value); return ISC_R_NOTFOUND; } isc_result_t omapi_connection_destroy (omapi_object_t *h, const char *file, int line) { omapi_connection_object_t *c; #ifdef DEBUG_PROTOCOL log_debug ("omapi_connection_destroy()"); #endif if (h -> type != omapi_type_connection) return ISC_R_UNEXPECTED; c = (omapi_connection_object_t *)(h); if (c -> state == omapi_connection_connected) omapi_disconnect (h, 1); if (c -> listener) omapi_listener_dereference (&c -> listener, file, line); if (c -> connect_list) omapi_addr_list_dereference (&c -> connect_list, file, line); return ISC_R_SUCCESS; } isc_result_t omapi_connection_signal_handler (omapi_object_t *h, const char *name, va_list ap) { if (h -> type != omapi_type_connection) return DHCP_R_INVALIDARG; #ifdef DEBUG_PROTOCOL log_debug ("omapi_connection_signal_handler(%s)", name); #endif if (h -> inner && h -> inner -> type -> signal_handler) return (*(h -> inner -> type -> signal_handler)) (h -> inner, name, ap); return ISC_R_NOTFOUND; } /* Write all the published values associated with the object through the specified connection. */ isc_result_t omapi_connection_stuff_values (omapi_object_t *c, omapi_object_t *id, omapi_object_t *m) { if (m -> type != omapi_type_connection) return DHCP_R_INVALIDARG; if (m -> inner && m -> inner -> type -> stuff_values) return (*(m -> inner -> type -> stuff_values)) (c, id, m -> inner); return ISC_R_SUCCESS; } dhcp-4.4.1/omapip/convert.c000644 000765 000024 00000006770 13243301226 016036 0ustar00tmarkstaff000000 000000 /* convert.c Safe copying of option values into and out of the option buffer, which can't be assumed to be aligned. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1996-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #include u_int32_t getULong (buf) const unsigned char *buf; { u_int32_t ibuf; memcpy (&ibuf, buf, sizeof (u_int32_t)); return ntohl (ibuf); } int32_t getLong (buf) const unsigned char *buf; { int32_t ibuf; memcpy (&ibuf, buf, sizeof (int32_t)); return ntohl (ibuf); } u_int32_t getUShort (buf) const unsigned char *buf; { unsigned short ibuf; memcpy (&ibuf, buf, sizeof (u_int16_t)); return ntohs (ibuf); } int32_t getShort (buf) const unsigned char *buf; { short ibuf; memcpy (&ibuf, buf, sizeof (int16_t)); return ntohs (ibuf); } void putULong (obuf, val) unsigned char *obuf; u_int32_t val; { u_int32_t tmp = htonl (val); memcpy (obuf, &tmp, sizeof tmp); } void putLong (obuf, val) unsigned char *obuf; int32_t val; { int32_t tmp = htonl (val); memcpy (obuf, &tmp, sizeof tmp); } void putUShort (obuf, val) unsigned char *obuf; u_int32_t val; { u_int16_t tmp = htons (val); memcpy (obuf, &tmp, sizeof tmp); } void putShort (obuf, val) unsigned char *obuf; int32_t val; { int16_t tmp = htons (val); memcpy (obuf, &tmp, sizeof tmp); } void putUChar (obuf, val) unsigned char *obuf; u_int32_t val; { *obuf = val; } u_int32_t getUChar (obuf) const unsigned char *obuf; { return obuf [0]; } int converted_length (buf, base, width) const unsigned char *buf; unsigned int base; unsigned int width; { u_int32_t number; u_int32_t column; int power = 1; u_int32_t newcolumn = base; if (base > 16) return 0; if (width == 1) number = getUChar (buf); else if (width == 2) number = getUShort (buf); else if (width == 4) number = getULong (buf); else return 0; do { column = newcolumn; if (number < column) return power; power++; newcolumn = column * base; /* If we wrap around, it must be the next power of two up. */ } while (newcolumn > column); return power; } int binary_to_ascii (outbuf, inbuf, base, width) unsigned char *outbuf; const unsigned char *inbuf; unsigned int base; unsigned int width; { u_int32_t number; static char h2a [] = "0123456789abcdef"; int power = converted_length (inbuf, base, width); int i; if (base > 16) return 0; if (width == 1) number = getUChar (inbuf); else if (width == 2) number = getUShort (inbuf); else if (width == 4) number = getULong (inbuf); else return 0; for (i = power - 1 ; i >= 0; i--) { outbuf [i] = h2a [number % base]; number /= base; } return power; } dhcp-4.4.1/omapip/dispatch.c000644 000765 000024 00000060544 13243301226 016154 0ustar00tmarkstaff000000 000000 /* dispatch.c I/O dispatcher. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1999-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #include #include static omapi_io_object_t omapi_io_states; struct timeval cur_tv; struct eventqueue *rw_queue_empty; OMAPI_OBJECT_ALLOC (omapi_io, omapi_io_object_t, omapi_type_io_object) OMAPI_OBJECT_ALLOC (omapi_waiter, omapi_waiter_object_t, omapi_type_waiter) void register_eventhandler(struct eventqueue **queue, void (*handler)(void *)) { struct eventqueue *t, *q; /* traverse to end of list */ t = NULL; for (q = *queue ; q ; q = q->next) { if (q->handler == handler) return; /* handler already registered */ t = q; } q = ((struct eventqueue *)dmalloc(sizeof(struct eventqueue), MDL)); if (!q) log_fatal("register_eventhandler: no memory!"); memset(q, 0, sizeof *q); if (t) t->next = q; else *queue = q; q->handler = handler; return; } void unregister_eventhandler(struct eventqueue **queue, void (*handler)(void *)) { struct eventqueue *t, *q; /* traverse to end of list */ t= NULL; for (q = *queue ; q ; q = q->next) { if (q->handler == handler) { if (t) t->next = q->next; else *queue = q->next; dfree(q, MDL); /* Don't access q after this!*/ break; } t = q; } return; } void trigger_event(struct eventqueue **queue) { struct eventqueue *q; for (q=*queue ; q ; q=q->next) { if (q->handler) (*q->handler)(NULL); } } /* * Callback routine to connect the omapi I/O object and socket with * the isc socket code. The isc socket code will call this routine * which will then call the correct local routine to process the bytes. * * Currently we are always willing to read more data, this should be modified * so that on connections we don't read more if we already have enough. * * If we have more bytes to write we ask the library to call us when * we can write more. If we indicate we don't have more to write we need * to poke the library via isc_socket_fdwatchpoke. */ /* * sockdelete indicates if we are deleting the socket or leaving it in place * 1 is delete, 0 is leave in place */ #define SOCKDELETE 1 int omapi_iscsock_cb(isc_task_t *task, isc_socket_t *socket, void *cbarg, int flags) { omapi_io_object_t *obj; isc_result_t status; /* Get the current time... */ gettimeofday (&cur_tv, (struct timezone *)0); /* isc socket stuff */ #if SOCKDELETE /* * walk through the io states list, if our object is on there * service it. if not ignore it. */ for (obj = omapi_io_states.next; obj != NULL; obj = obj->next) { if (obj == cbarg) break; } if (obj == NULL) { return(0); } #else /* Not much to be done if we have the wrong type of object. */ if (((omapi_object_t *)cbarg) -> type != omapi_type_io_object) { log_fatal ("Incorrect object type, must be of type io_object"); } obj = (omapi_io_object_t *)cbarg; /* * If the object is marked as closed don't try and process * anything just indicate that we don't want any more. * * This should be a temporary fix until we arrange to properly * close the socket. */ if (obj->closed == ISC_TRUE) { return(0); } #endif if ((flags == ISC_SOCKFDWATCH_READ) && (obj->reader != NULL) && (obj->inner != NULL)) { status = obj->reader(obj->inner); /* * If we are shutting down (basically tried to * read and got no bytes) we don't need to try * again. */ if (status == ISC_R_SHUTTINGDOWN) return (0); /* Otherwise We always ask for more when reading */ return (1); } else if ((flags == ISC_SOCKFDWATCH_WRITE) && (obj->writer != NULL) && (obj->inner != NULL)) { status = obj->writer(obj->inner); /* If the writer has more to write they should return * ISC_R_INPROGRESS */ if (status == ISC_R_INPROGRESS) { return (1); } } /* * We get here if we either had an error (inconsistent * structures etc) or no more to write, tell the socket * lib we don't have more to do right now. */ return (0); } /* Register an I/O handle so that we can do asynchronous I/O on it. */ isc_result_t omapi_register_io_object (omapi_object_t *h, int (*readfd) (omapi_object_t *), int (*writefd) (omapi_object_t *), isc_result_t (*reader) (omapi_object_t *), isc_result_t (*writer) (omapi_object_t *), isc_result_t (*reaper) (omapi_object_t *)) { isc_result_t status; omapi_io_object_t *obj, *p; int fd_flags = 0, fd = 0; /* omapi_io_states is a static object. If its reference count is zero, this is the first I/O handle to be registered, so we need to initialize it. Because there is no inner or outer pointer on this object, and we're setting its refcnt to 1, it will never be freed. */ if (!omapi_io_states.refcnt) { omapi_io_states.refcnt = 1; omapi_io_states.type = omapi_type_io_object; } obj = (omapi_io_object_t *)0; status = omapi_io_allocate (&obj, MDL); if (status != ISC_R_SUCCESS) return status; obj->closed = ISC_FALSE; /* mark as open */ status = omapi_object_reference (&obj -> inner, h, MDL); if (status != ISC_R_SUCCESS) { omapi_io_dereference (&obj, MDL); return status; } status = omapi_object_reference (&h -> outer, (omapi_object_t *)obj, MDL); if (status != ISC_R_SUCCESS) { omapi_io_dereference (&obj, MDL); return status; } /* * Attach the I/O object to the isc socket library via the * fdwatch function. This allows the socket library to watch * over a socket that we built. If there are both a read and * a write socket we asssume they are the same socket. */ if (readfd) { fd_flags |= ISC_SOCKFDWATCH_READ; fd = readfd(h); } if (writefd) { fd_flags |= ISC_SOCKFDWATCH_WRITE; fd = writefd(h); } if (fd_flags != 0) { status = isc_socket_fdwatchcreate(dhcp_gbl_ctx.socketmgr, fd, fd_flags, omapi_iscsock_cb, obj, dhcp_gbl_ctx.task, &obj->fd); if (status != ISC_R_SUCCESS) { log_error("Unable to register fd with library %s", isc_result_totext(status)); /*sar*/ /* is this the cleanup we need? */ omapi_object_dereference(&h->outer, MDL); omapi_io_dereference (&obj, MDL); return (status); } } /* Find the last I/O state, if there are any. */ for (p = omapi_io_states.next; p && p -> next; p = p -> next) ; if (p) omapi_io_reference (&p -> next, obj, MDL); else omapi_io_reference (&omapi_io_states.next, obj, MDL); obj -> readfd = readfd; obj -> writefd = writefd; obj -> reader = reader; obj -> writer = writer; obj -> reaper = reaper; omapi_io_dereference(&obj, MDL); return ISC_R_SUCCESS; } /* * ReRegister an I/O handle so that we can do asynchronous I/O on it. * If the handle doesn't exist we call the register routine to build it. * If it does exist we change the functions associated with it, and * repoke the fd code to make it happy. Neither the objects nor the * fd are allowed to have changed. */ isc_result_t omapi_reregister_io_object (omapi_object_t *h, int (*readfd) (omapi_object_t *), int (*writefd) (omapi_object_t *), isc_result_t (*reader) (omapi_object_t *), isc_result_t (*writer) (omapi_object_t *), isc_result_t (*reaper) (omapi_object_t *)) { omapi_io_object_t *obj; int fd_flags = 0; if ((!h -> outer) || (h -> outer -> type != omapi_type_io_object)) { /* * If we don't have an object or if the type isn't what * we expect do the normal registration (which will overwrite * an incorrect type, that's what we did historically, may * want to change that) */ return (omapi_register_io_object (h, readfd, writefd, reader, writer, reaper)); } /* We have an io object of the correct type, try to update it */ /*sar*/ /* Should we validate that the fd matches the previous one? * It's suppossed to, that's a requirement, don't bother yet */ obj = (omapi_io_object_t *)h->outer; obj->readfd = readfd; obj->writefd = writefd; obj->reader = reader; obj->writer = writer; obj->reaper = reaper; if (readfd) { fd_flags |= ISC_SOCKFDWATCH_READ; } if (writefd) { fd_flags |= ISC_SOCKFDWATCH_WRITE; } isc_socket_fdwatchpoke(obj->fd, fd_flags); return (ISC_R_SUCCESS); } isc_result_t omapi_unregister_io_object (omapi_object_t *h) { omapi_io_object_t *obj, *ph; #if SOCKDELETE omapi_io_object_t *p, *last; #endif if (!h -> outer || h -> outer -> type != omapi_type_io_object) return DHCP_R_INVALIDARG; obj = (omapi_io_object_t *)h -> outer; ph = (omapi_io_object_t *)0; omapi_io_reference (&ph, obj, MDL); #if SOCKDELETE /* * For now we leave this out. We can't clean up the isc socket * structure cleanly yet so we need to leave the io object in place. * By leaving it on the io states list we avoid it being freed. * We also mark it as closed to avoid using it. */ /* remove from the list of I/O states */ last = &omapi_io_states; for (p = omapi_io_states.next; p; p = p -> next) { if (p == obj) { omapi_io_dereference (&last -> next, MDL); omapi_io_reference (&last -> next, p -> next, MDL); break; } last = p; } if (obj -> next) omapi_io_dereference (&obj -> next, MDL); #endif if (obj -> outer) { if (obj -> outer -> inner == (omapi_object_t *)obj) omapi_object_dereference (&obj -> outer -> inner, MDL); omapi_object_dereference (&obj -> outer, MDL); } omapi_object_dereference (&obj -> inner, MDL); omapi_object_dereference (&h -> outer, MDL); #if SOCKDELETE /* remove isc socket associations */ if (obj->fd != NULL) { isc_socket_cancel(obj->fd, dhcp_gbl_ctx.task, ISC_SOCKCANCEL_ALL); isc_socket_detach(&obj->fd); } #else obj->closed = ISC_TRUE; #endif omapi_io_dereference (&ph, MDL); return ISC_R_SUCCESS; } isc_result_t omapi_dispatch (struct timeval *t) { return omapi_wait_for_completion ((omapi_object_t *)&omapi_io_states, t); } isc_result_t omapi_wait_for_completion (omapi_object_t *object, struct timeval *t) { isc_result_t status; omapi_waiter_object_t *waiter; omapi_object_t *inner; if (object) { waiter = (omapi_waiter_object_t *)0; status = omapi_waiter_allocate (&waiter, MDL); if (status != ISC_R_SUCCESS) return status; /* Paste the waiter object onto the inner object we're waiting on. */ for (inner = object; inner -> inner; inner = inner -> inner) ; status = omapi_object_reference (&waiter -> outer, inner, MDL); if (status != ISC_R_SUCCESS) { omapi_waiter_dereference (&waiter, MDL); return status; } status = omapi_object_reference (&inner -> inner, (omapi_object_t *)waiter, MDL); if (status != ISC_R_SUCCESS) { omapi_waiter_dereference (&waiter, MDL); return status; } } else waiter = (omapi_waiter_object_t *)0; do { status = omapi_one_dispatch ((omapi_object_t *)waiter, t); if (status != ISC_R_SUCCESS) return status; } while (!waiter || !waiter -> ready); if (waiter -> outer) { if (waiter -> outer -> inner) { omapi_object_dereference (&waiter -> outer -> inner, MDL); if (waiter -> inner) omapi_object_reference (&waiter -> outer -> inner, waiter -> inner, MDL); } omapi_object_dereference (&waiter -> outer, MDL); } if (waiter -> inner) omapi_object_dereference (&waiter -> inner, MDL); status = waiter -> waitstatus; omapi_waiter_dereference (&waiter, MDL); return status; } isc_result_t omapi_one_dispatch (omapi_object_t *wo, struct timeval *t) { fd_set r, w, x, rr, ww, xx; int max = 0; int count; int desc; struct timeval now, to; omapi_io_object_t *io, *prev, *next; omapi_waiter_object_t *waiter; omapi_object_t *tmp = (omapi_object_t *)0; if (!wo || wo -> type != omapi_type_waiter) waiter = (omapi_waiter_object_t *)0; else waiter = (omapi_waiter_object_t *)wo; FD_ZERO (&x); /* First, see if the timeout has expired, and if so return. */ if (t) { gettimeofday (&now, (struct timezone *)0); cur_tv.tv_sec = now.tv_sec; cur_tv.tv_usec = now.tv_usec; if (now.tv_sec > t -> tv_sec || (now.tv_sec == t -> tv_sec && now.tv_usec >= t -> tv_usec)) return ISC_R_TIMEDOUT; /* We didn't time out, so figure out how long until we do. */ to.tv_sec = t -> tv_sec - now.tv_sec; to.tv_usec = t -> tv_usec - now.tv_usec; if (to.tv_usec < 0) { to.tv_usec += 1000000; to.tv_sec--; } /* It is possible for the timeout to get set larger than the largest time select() is willing to accept. Restricting the timeout to a maximum of one day should work around this. -DPN. (Ref: Bug #416) */ if (to.tv_sec > (60 * 60 * 24)) to.tv_sec = 60 * 60 * 24; } /* If the object we're waiting on has reached completion, return now. */ if (waiter && waiter -> ready) return ISC_R_SUCCESS; again: /* If we have no I/O state, we can't proceed. */ if (!(io = omapi_io_states.next)) return ISC_R_NOMORE; /* Set up the read and write masks. */ FD_ZERO (&r); FD_ZERO (&w); for (; io; io = io -> next) { /* Check for a read socket. If we shouldn't be trying to read for this I/O object, either there won't be a readfd function, or it'll return -1. */ if (io -> readfd && io -> inner && (desc = (*(io -> readfd)) (io -> inner)) >= 0) { FD_SET (desc, &r); if (desc > max) max = desc; } /* Same deal for write fdets. */ if (io -> writefd && io -> inner && (desc = (*(io -> writefd)) (io -> inner)) >= 0) { FD_SET (desc, &w); if (desc > max) max = desc; } } /* poll if all reader are dry */ now.tv_sec = 0; now.tv_usec = 0; rr=r; ww=w; xx=x; /* poll once */ count = select(max + 1, &r, &w, &x, &now); if (!count) { /* We are dry now */ trigger_event(&rw_queue_empty); /* Wait for a packet or a timeout... XXX */ r = rr; w = ww; x = xx; count = select(max + 1, &r, &w, &x, t ? &to : NULL); } /* Get the current time... */ gettimeofday (&cur_tv, (struct timezone *)0); /* We probably have a bad file descriptor. Figure out which one. When we find it, call the reaper function on it, which will maybe make it go away, and then try again. */ if (count < 0) { struct timeval t0; omapi_io_object_t *prev = (omapi_io_object_t *)0; io = (omapi_io_object_t *)0; if (omapi_io_states.next) omapi_io_reference (&io, omapi_io_states.next, MDL); while (io) { omapi_object_t *obj; FD_ZERO (&r); FD_ZERO (&w); t0.tv_sec = t0.tv_usec = 0; if (io -> readfd && io -> inner && (desc = (*(io -> readfd)) (io -> inner)) >= 0) { FD_SET (desc, &r); count = select (desc + 1, &r, &w, &x, &t0); bogon: if (count < 0) { log_error ("Bad descriptor %d.", desc); for (obj = (omapi_object_t *)io; obj -> outer; obj = obj -> outer) ; for (; obj; obj = obj -> inner) { omapi_value_t *ov; int len; const char *s; ov = (omapi_value_t *)0; omapi_get_value_str (obj, (omapi_object_t *)0, "name", &ov); if (ov && ov -> value && (ov -> value -> type == omapi_datatype_string)) { s = (char *) ov -> value -> u.buffer.value; len = ov -> value -> u.buffer.len; } else { s = ""; len = 0; } log_error ("Object %lx %s%s%.*s", (unsigned long)obj, obj -> type -> name, len ? " " : "", len, s); if (len) omapi_value_dereference (&ov, MDL); } (*(io -> reaper)) (io -> inner); if (prev) { omapi_io_dereference (&prev -> next, MDL); if (io -> next) omapi_io_reference (&prev -> next, io -> next, MDL); } else { omapi_io_dereference (&omapi_io_states.next, MDL); if (io -> next) omapi_io_reference (&omapi_io_states.next, io -> next, MDL); } omapi_io_dereference (&io, MDL); goto again; } } FD_ZERO (&r); FD_ZERO (&w); t0.tv_sec = t0.tv_usec = 0; /* Same deal for write fdets. */ if (io -> writefd && io -> inner && (desc = (*(io -> writefd)) (io -> inner)) >= 0) { FD_SET (desc, &w); count = select (desc + 1, &r, &w, &x, &t0); if (count < 0) goto bogon; } if (prev) omapi_io_dereference (&prev, MDL); omapi_io_reference (&prev, io, MDL); omapi_io_dereference (&io, MDL); if (prev -> next) omapi_io_reference (&io, prev -> next, MDL); } if (prev) omapi_io_dereference (&prev, MDL); } for (io = omapi_io_states.next; io; io = io -> next) { if (!io -> inner) continue; omapi_object_reference (&tmp, io -> inner, MDL); /* Check for a read descriptor, and if there is one, see if we got input on that socket. */ if (io -> readfd && (desc = (*(io -> readfd)) (tmp)) >= 0) { if (FD_ISSET (desc, &r)) ((*(io -> reader)) (tmp)); } /* Same deal for write descriptors. */ if (io -> writefd && (desc = (*(io -> writefd)) (tmp)) >= 0) { if (FD_ISSET (desc, &w)) ((*(io -> writer)) (tmp)); } omapi_object_dereference (&tmp, MDL); } /* Now check for I/O handles that are no longer valid, and remove them from the list. */ prev = NULL; io = NULL; if (omapi_io_states.next != NULL) { omapi_io_reference(&io, omapi_io_states.next, MDL); } while (io != NULL) { if ((io->inner == NULL) || ((io->reaper != NULL) && ((io->reaper)(io->inner) != ISC_R_SUCCESS))) { omapi_io_object_t *tmp = NULL; /* Save a reference to the next pointer, if there is one. */ if (io->next != NULL) { omapi_io_reference(&tmp, io->next, MDL); omapi_io_dereference(&io->next, MDL); } if (prev != NULL) { omapi_io_dereference(&prev->next, MDL); if (tmp != NULL) omapi_io_reference(&prev->next, tmp, MDL); } else { omapi_io_dereference(&omapi_io_states.next, MDL); if (tmp != NULL) omapi_io_reference (&omapi_io_states.next, tmp, MDL); else omapi_signal_in( (omapi_object_t *) &omapi_io_states, "ready"); } if (tmp != NULL) omapi_io_dereference(&tmp, MDL); } else { if (prev != NULL) { omapi_io_dereference(&prev, MDL); } omapi_io_reference(&prev, io, MDL); } /* * Equivalent to: * io = io->next * But using our reference counting voodoo. */ next = NULL; if (io->next != NULL) { omapi_io_reference(&next, io->next, MDL); } omapi_io_dereference(&io, MDL); if (next != NULL) { omapi_io_reference(&io, next, MDL); omapi_io_dereference(&next, MDL); } } if (prev != NULL) { omapi_io_dereference(&prev, MDL); } return ISC_R_SUCCESS; } isc_result_t omapi_io_set_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_typed_data_t *value) { if (h -> type != omapi_type_io_object) return DHCP_R_INVALIDARG; if (h -> inner && h -> inner -> type -> set_value) return (*(h -> inner -> type -> set_value)) (h -> inner, id, name, value); return ISC_R_NOTFOUND; } isc_result_t omapi_io_get_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_value_t **value) { if (h -> type != omapi_type_io_object) return DHCP_R_INVALIDARG; if (h -> inner && h -> inner -> type -> get_value) return (*(h -> inner -> type -> get_value)) (h -> inner, id, name, value); return ISC_R_NOTFOUND; } /* omapi_io_destroy (object, MDL); * * Find the requested IO [object] and remove it from the list of io * states, causing the cleanup functions to destroy it. Note that we must * hold a reference on the object while moving its ->next reference and * removing the reference in the chain to the target object...otherwise it * may be cleaned up from under us. */ isc_result_t omapi_io_destroy (omapi_object_t *h, const char *file, int line) { omapi_io_object_t *obj = NULL, *p, *last = NULL, **holder; if (h -> type != omapi_type_io_object) return DHCP_R_INVALIDARG; /* remove from the list of I/O states */ for (p = omapi_io_states.next; p; p = p -> next) { if (p == (omapi_io_object_t *)h) { omapi_io_reference (&obj, p, MDL); if (last) holder = &last -> next; else holder = &omapi_io_states.next; omapi_io_dereference (holder, MDL); if (obj -> next) { omapi_io_reference (holder, obj -> next, MDL); omapi_io_dereference (&obj -> next, MDL); } return omapi_io_dereference (&obj, MDL); } last = p; } return ISC_R_NOTFOUND; } isc_result_t omapi_io_signal_handler (omapi_object_t *h, const char *name, va_list ap) { if (h -> type != omapi_type_io_object) return DHCP_R_INVALIDARG; if (h -> inner && h -> inner -> type -> signal_handler) return (*(h -> inner -> type -> signal_handler)) (h -> inner, name, ap); return ISC_R_NOTFOUND; } isc_result_t omapi_io_stuff_values (omapi_object_t *c, omapi_object_t *id, omapi_object_t *i) { if (i -> type != omapi_type_io_object) return DHCP_R_INVALIDARG; if (i -> inner && i -> inner -> type -> stuff_values) return (*(i -> inner -> type -> stuff_values)) (c, id, i -> inner); return ISC_R_SUCCESS; } isc_result_t omapi_waiter_signal_handler (omapi_object_t *h, const char *name, va_list ap) { omapi_waiter_object_t *waiter; if (h -> type != omapi_type_waiter) return DHCP_R_INVALIDARG; if (!strcmp (name, "ready")) { waiter = (omapi_waiter_object_t *)h; waiter -> ready = 1; waiter -> waitstatus = ISC_R_SUCCESS; return ISC_R_SUCCESS; } if (!strcmp(name, "status")) { waiter = (omapi_waiter_object_t *)h; waiter->ready = 1; waiter->waitstatus = va_arg(ap, isc_result_t); return ISC_R_SUCCESS; } if (!strcmp (name, "disconnect")) { waiter = (omapi_waiter_object_t *)h; waiter -> ready = 1; waiter -> waitstatus = DHCP_R_CONNRESET; return ISC_R_SUCCESS; } if (h -> inner && h -> inner -> type -> signal_handler) return (*(h -> inner -> type -> signal_handler)) (h -> inner, name, ap); return ISC_R_NOTFOUND; } /** @brief calls a given function on every object * * @param func function to be called * @param p parameter to be passed to each function instance * * @return result (ISC_R_SUCCESS if successful, error code otherwise) */ isc_result_t omapi_io_state_foreach (isc_result_t (*func) (omapi_object_t *, void *), void *p) { omapi_io_object_t *io = NULL; isc_result_t status; omapi_io_object_t *next = NULL; /* * This just calls func on every inner object on the list. It would * be much simpler in general case, but one of the operations could be * release of the objects. Therefore we need to ref count the io and * io->next pointers. */ if (omapi_io_states.next) { omapi_object_reference((omapi_object_t**)&io, (omapi_object_t*)omapi_io_states.next, MDL); } while(io) { /* If there's a next object, save it */ if (io->next) { omapi_object_reference((omapi_object_t**)&next, (omapi_object_t*)io->next, MDL); } if (io->inner) { status = (*func) (io->inner, p); if (status != ISC_R_SUCCESS) { /* Something went wrong. Let's stop using io & next pointer * and bail out */ omapi_object_dereference((omapi_object_t**)&io, MDL); if (next) { omapi_object_dereference((omapi_object_t**)&next, MDL); } return status; } } /* Update the io pointer and free the next pointer */ omapi_object_dereference((omapi_object_t**)&io, MDL); if (next) { omapi_object_reference((omapi_object_t**)&io, (omapi_object_t*)next, MDL); omapi_object_dereference((omapi_object_t**)&next, MDL); } } /* * The only way to get here is when next is NULL. There's no need * to dereference it. */ return ISC_R_SUCCESS; } dhcp-4.4.1/omapip/errwarn.c000644 000765 000024 00000021562 13243301226 016032 0ustar00tmarkstaff000000 000000 /* errwarn.c Errors and warnings... */ /* * Copyright (c) 1995 RadioMail Corporation. * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1996-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * * This software was written for RadioMail Corporation by Ted Lemon * under a contract with Vixie Enterprises. Further modifications have * been made for Internet Systems Consortium under a contract * with Vixie Laboratories. */ #include "dhcpd.h" #include #include #include #ifdef DEBUG int log_perror = -1; #else int log_perror = 1; #endif void (*log_cleanup) (void); #define CVT_BUF_MAX 1023 static char mbuf [CVT_BUF_MAX + 1]; static char fbuf [CVT_BUF_MAX + 1]; /* Log an error message, then exit... */ void log_fatal (const char * fmt, ... ) { va_list list; do_percentm (fbuf, fmt); /* %Audit% This is log output. %2004.06.17,Safe% * If we truncate we hope the user can get a hint from the log. */ va_start (list, fmt); vsnprintf (mbuf, sizeof mbuf, fbuf, list); va_end (list); #ifndef DEBUG syslog (LOG_ERR, "%s", mbuf); #endif /* Also log it to stderr? */ if (log_perror) { IGNORE_RET (write (STDERR_FILENO, mbuf, strlen (mbuf))); IGNORE_RET (write (STDERR_FILENO, "\n", 1)); } log_error ("%s", ""); log_error ("If you think you have received this message due to a bug rather"); log_error ("than a configuration issue please read the section on submitting"); log_error ("bugs on either our web page at www.isc.org or in the README file"); log_error ("before submitting a bug. These pages explain the proper"); log_error ("process and the information we find helpful for debugging."); log_error ("%s", ""); log_error ("exiting."); if (log_cleanup) (*log_cleanup) (); exit (1); } /* Log an error message... */ int log_error (const char * fmt, ...) { va_list list; do_percentm (fbuf, fmt); /* %Audit% This is log output. %2004.06.17,Safe% * If we truncate we hope the user can get a hint from the log. */ va_start (list, fmt); vsnprintf (mbuf, sizeof mbuf, fbuf, list); va_end (list); #ifndef DEBUG syslog (LOG_ERR, "%s", mbuf); #endif if (log_perror) { IGNORE_RET (write (STDERR_FILENO, mbuf, strlen (mbuf))); IGNORE_RET (write (STDERR_FILENO, "\n", 1)); } return 0; } /* Log a note... */ int log_info (const char *fmt, ...) { va_list list; do_percentm (fbuf, fmt); /* %Audit% This is log output. %2004.06.17,Safe% * If we truncate we hope the user can get a hint from the log. */ va_start (list, fmt); vsnprintf (mbuf, sizeof mbuf, fbuf, list); va_end (list); #ifndef DEBUG syslog (LOG_INFO, "%s", mbuf); #endif if (log_perror) { IGNORE_RET (write (STDERR_FILENO, mbuf, strlen (mbuf))); IGNORE_RET (write (STDERR_FILENO, "\n", 1)); } return 0; } /* Log a debug message... */ int log_debug (const char *fmt, ...) { va_list list; do_percentm (fbuf, fmt); /* %Audit% This is log output. %2004.06.17,Safe% * If we truncate we hope the user can get a hint from the log. */ va_start (list, fmt); vsnprintf (mbuf, sizeof mbuf, fbuf, list); va_end (list); #ifndef DEBUG syslog (LOG_DEBUG, "%s", mbuf); #endif if (log_perror) { IGNORE_RET (write (STDERR_FILENO, mbuf, strlen (mbuf))); IGNORE_RET (write (STDERR_FILENO, "\n", 1)); } return 0; } /* Find %m in the input string and substitute an error message string. */ void do_percentm (obuf, ibuf) char *obuf; const char *ibuf; { const char *s = ibuf; char *p = obuf; int infmt = 0; const char *m; int len = 0; while (*s) { if (infmt) { if (*s == 'm') { #ifndef __CYGWIN32__ m = strerror (errno); #else m = pWSAError (); #endif if (!m) m = ""; len += strlen (m); if (len > CVT_BUF_MAX) goto out; strcpy (p - 1, m); p += strlen (p); ++s; } else { if (++len > CVT_BUF_MAX) goto out; *p++ = *s++; } infmt = 0; } else { if (*s == '%') infmt = 1; if (++len > CVT_BUF_MAX) goto out; *p++ = *s++; } } out: *p = 0; } #ifdef NO_STRERROR char *strerror (err) int err; { extern char *sys_errlist []; extern int sys_nerr; static char errbuf [128]; if (err < 0 || err >= sys_nerr) { sprintf (errbuf, "Error %d", err); return errbuf; } return sys_errlist [err]; } #endif /* NO_STRERROR */ #ifdef _WIN32 char *pWSAError () { int err = WSAGetLastError (); switch (err) { case WSAEACCES: return "Permission denied"; case WSAEADDRINUSE: return "Address already in use"; case WSAEADDRNOTAVAIL: return "Cannot assign requested address"; case WSAEAFNOSUPPORT: return "Address family not supported by protocol family"; case WSAEALREADY: return "Operation already in progress"; case WSAECONNABORTED: return "Software caused connection abort"; case WSAECONNREFUSED: return "Connection refused"; case WSAECONNRESET: return "Connection reset by peer"; case WSAEDESTADDRREQ: return "Destination address required"; case WSAEFAULT: return "Bad address"; case WSAEHOSTDOWN: return "Host is down"; case WSAEHOSTUNREACH: return "No route to host"; case WSAEINPROGRESS: return "Operation now in progress"; case WSAEINTR: return "Interrupted function call"; case WSAEINVAL: return "Invalid argument"; case WSAEISCONN: return "Socket is already connected"; case WSAEMFILE: return "Too many open files"; case WSAEMSGSIZE: return "Message too long"; case WSAENETDOWN: return "Network is down"; case WSAENETRESET: return "Network dropped connection on reset"; case WSAENETUNREACH: return "Network is unreachable"; case WSAENOBUFS: return "No buffer space available"; case WSAENOPROTOOPT: return "Bad protocol option"; case WSAENOTCONN: return "Socket is not connected"; case WSAENOTSOCK: return "Socket operation on non-socket"; case WSAEOPNOTSUPP: return "Operation not supported"; case WSAEPFNOSUPPORT: return "Protocol family not supported"; case WSAEPROCLIM: return "Too many processes"; case WSAEPROTONOSUPPORT: return "Protocol not supported"; case WSAEPROTOTYPE: return "Protocol wrong type for socket"; case WSAESHUTDOWN: return "Cannot send after socket shutdown"; case WSAESOCKTNOSUPPORT: return "Socket type not supported"; case WSAETIMEDOUT: return "Connection timed out"; case WSAEWOULDBLOCK: return "Resource temporarily unavailable"; case WSAHOST_NOT_FOUND: return "Host not found"; #if 0 case WSA_INVALID_HANDLE: return "Specified event object handle is invalid"; case WSA_INVALID_PARAMETER: return "One or more parameters are invalid"; case WSAINVALIDPROCTABLE: return "Invalid procedure table from service provider"; case WSAINVALIDPROVIDER: return "Invalid service provider version number"; case WSA_IO_PENDING: return "Overlapped operations will complete later"; case WSA_IO_INCOMPLETE: return "Overlapped I/O event object not in signaled state"; case WSA_NOT_ENOUGH_MEMORY: return "Insufficient memory available"; #endif case WSANOTINITIALISED: return "Successful WSAStartup not yet performer"; case WSANO_DATA: return "Valid name, no data record of requested type"; case WSANO_RECOVERY: return "This is a non-recoverable error"; #if 0 case WSAPROVIDERFAILEDINIT: return "Unable to initialize a service provider"; case WSASYSCALLFAILURE: return "System call failure"; #endif case WSASYSNOTREADY: return "Network subsystem is unavailable"; case WSATRY_AGAIN: return "Non-authoritative host not found"; case WSAVERNOTSUPPORTED: return "WINSOCK.DLL version out of range"; case WSAEDISCON: return "Graceful shutdown in progress"; #if 0 case WSA_OPERATION_ABORTED: return "Overlapped operation aborted"; #endif } return "Unknown WinSock error"; } #endif /* _WIN32 */ dhcp-4.4.1/omapip/generic.c000644 000765 000024 00000021323 13243301226 015761 0ustar00tmarkstaff000000 000000 /* generic.c Subroutines that support the generic object. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1999-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #include OMAPI_OBJECT_ALLOC (omapi_generic, omapi_generic_object_t, omapi_type_generic) isc_result_t omapi_generic_new (omapi_object_t **gen, const char *file, int line) { /* Backwards compatibility. */ return omapi_generic_allocate ((omapi_generic_object_t **)gen, file, line); } isc_result_t omapi_generic_set_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_typed_data_t *value) { omapi_generic_object_t *g; omapi_value_t *new; omapi_value_t **va; u_int8_t *ca; int vm_new; int i, vfree = -1; isc_result_t status; if (h -> type != omapi_type_generic) return DHCP_R_INVALIDARG; g = (omapi_generic_object_t *)h; /* See if there's already a value with this name attached to the generic object, and if so, replace the current value with the new one. */ for (i = 0; i < g -> nvalues; i++) { if (!g -> values[i]) continue; if (!omapi_data_string_cmp (name, g -> values [i] -> name)) { /* There's an inconsistency here: the standard behaviour of a set_values method when passed a matching name and a null value is to delete the value associated with that name (where possible). In the generic object, we remember the name/null pair, because generic objects are generally used to pass messages around, and this is the way that remote entities delete values from local objects. If the get_value method of a generic object is called for a name that maps to a name/null pair, ISC_R_NOTFOUND is returned. */ new = (omapi_value_t *)0; status = (omapi_value_new (&new, MDL)); if (status != ISC_R_SUCCESS) return status; omapi_data_string_reference (&new -> name, name, MDL); if (value) omapi_typed_data_reference (&new -> value, value, MDL); omapi_value_dereference (&(g -> values [i]), MDL); status = (omapi_value_reference (&(g -> values [i]), new, MDL)); omapi_value_dereference (&new, MDL); g -> changed [i] = 1; return status; } /* Notice a free slot if we pass one. */ else if (vfree == -1 && !g -> values [i]) vfree = i; } /* If the name isn't already attached to this object, see if an inner object has it. */ if (h -> inner && h -> inner -> type -> set_value) { status = ((*(h -> inner -> type -> set_value)) (h -> inner, id, name, value)); if (status != ISC_R_NOTFOUND) return status; } /* Okay, so it's a value that no inner object knows about, and (implicitly, since the outer object set_value method would have called this object's set_value method) it's an object that no outer object knows about, it's this object's responsibility to remember it - that's what generic objects do. */ /* Arrange for there to be space for the pointer to the new name/value pair if necessary: */ if (vfree == -1) { vfree = g -> nvalues; if (vfree == g -> va_max) { if (g -> va_max) vm_new = 2 * g -> va_max; else vm_new = 10; va = dmalloc (vm_new * sizeof *va, MDL); if (!va) return ISC_R_NOMEMORY; ca = dmalloc (vm_new * sizeof *ca, MDL); if (!ca) { dfree (va, MDL); return ISC_R_NOMEMORY; } if (g -> va_max) { memcpy (va, g -> values, g -> va_max * sizeof *va); memcpy (ca, g -> changed, g -> va_max * sizeof *ca); } memset (va + g -> va_max, 0, (vm_new - g -> va_max) * sizeof *va); memset (ca + g -> va_max, 0, (vm_new - g -> va_max) * sizeof *ca); if (g -> values) dfree (g -> values, MDL); if (g -> changed) dfree (g -> changed, MDL); g -> values = va; g -> changed = ca; g -> va_max = vm_new; } } status = omapi_value_new (&g -> values [vfree], MDL); if (status != ISC_R_SUCCESS) return status; omapi_data_string_reference (&g -> values [vfree] -> name, name, MDL); if (value) omapi_typed_data_reference (&g -> values [vfree] -> value, value, MDL); g -> changed [vfree] = 1; if (vfree == g -> nvalues) g -> nvalues++; return ISC_R_SUCCESS; } isc_result_t omapi_generic_get_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_value_t **value) { int i; omapi_generic_object_t *g; if (h -> type != omapi_type_generic) return DHCP_R_INVALIDARG; g = (omapi_generic_object_t *)h; /* Look up the specified name in our list of objects. */ for (i = 0; i < g -> nvalues; i++) { if (!g -> values[i]) continue; if (!omapi_data_string_cmp (name, g -> values [i] -> name)) { /* If this is a name/null value pair, this is the same as if there were no value that matched the specified name, so return ISC_R_NOTFOUND. */ if (!g -> values [i] -> value) return ISC_R_NOTFOUND; /* Otherwise, return the name/value pair. */ return omapi_value_reference (value, g -> values [i], MDL); } } if (h -> inner && h -> inner -> type -> get_value) return (*(h -> inner -> type -> get_value)) (h -> inner, id, name, value); return ISC_R_NOTFOUND; } isc_result_t omapi_generic_destroy (omapi_object_t *h, const char *file, int line) { omapi_generic_object_t *g; int i; if (h -> type != omapi_type_generic) return ISC_R_UNEXPECTED; g = (omapi_generic_object_t *)h; if (g -> values) { for (i = 0; i < g -> nvalues; i++) { if (g -> values [i]) omapi_value_dereference (&g -> values [i], file, line); } dfree (g -> values, file, line); dfree (g -> changed, file, line); g -> values = (omapi_value_t **)0; g -> changed = (u_int8_t *)0; g -> va_max = 0; } return ISC_R_SUCCESS; } isc_result_t omapi_generic_signal_handler (omapi_object_t *h, const char *name, va_list ap) { if (h -> type != omapi_type_generic) return DHCP_R_INVALIDARG; if (h -> inner && h -> inner -> type -> signal_handler) return (*(h -> inner -> type -> signal_handler)) (h -> inner, name, ap); return ISC_R_NOTFOUND; } /* Write all the published values associated with the object through the specified connection. */ isc_result_t omapi_generic_stuff_values (omapi_object_t *c, omapi_object_t *id, omapi_object_t *g) { omapi_generic_object_t *src; int i; isc_result_t status; if (g -> type != omapi_type_generic) return DHCP_R_INVALIDARG; src = (omapi_generic_object_t *)g; for (i = 0; i < src -> nvalues; i++) { if (src -> values [i] && src -> values [i] -> name -> len && src -> changed [i]) { status = (omapi_connection_put_uint16 (c, src -> values [i] -> name -> len)); if (status != ISC_R_SUCCESS) return status; status = (omapi_connection_copyin (c, src -> values [i] -> name -> value, src -> values [i] -> name -> len)); if (status != ISC_R_SUCCESS) return status; status = (omapi_connection_write_typed_data (c, src -> values [i] -> value)); if (status != ISC_R_SUCCESS) return status; } } if (g -> inner && g -> inner -> type -> stuff_values) return (*(g -> inner -> type -> stuff_values)) (c, id, g -> inner); return ISC_R_SUCCESS; } /* Clear the changed flags on the object. This has the effect that if generic_stuff is called, any attributes that still have a cleared changed flag aren't sent to the peer. This also deletes any values that are null, presuming that these have now been properly handled. */ isc_result_t omapi_generic_clear_flags (omapi_object_t *o) { int i; omapi_generic_object_t *g; if (o -> type != omapi_type_generic) return DHCP_R_INVALIDARG; g = (omapi_generic_object_t *)o; for (i = 0; i < g -> nvalues; i++) { g -> changed [i] = 0; if (g -> values [i] && !g -> values [i] -> value) omapi_value_dereference (&g -> values [i], MDL); } return ISC_R_SUCCESS; } dhcp-4.4.1/omapip/handle.c000644 000765 000024 00000023711 13243301226 015603 0ustar00tmarkstaff000000 000000 /* handle.c Functions for maintaining handles on objects. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1999-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #include /* The handle table is a hierarchical tree designed for quick mapping of handle identifiers to objects. Objects contain their own handle identifiers if they have them, so the reverse mapping is also quick. The hierarchy is made up of table objects, each of which has 120 entries, a flag indicating whether the table is a leaf table or an indirect table, the handle of the first object covered by the table and the first object after that that's *not* covered by the table, a count of how many objects of either type are currently stored in the table, and an array of 120 entries pointing either to objects or tables. When we go to add an object to the table, we look to see if the next object handle to be assigned is covered by the outermost table. If it is, we find the place within that table where the next handle should go, and if necessary create additional nodes in the tree to contain the new handle. The pointer to the object is then stored in the correct position. Theoretically, we could have some code here to free up handle tables as they go out of use, but by and large handle tables won't go out of use, so this is being skipped for now. It shouldn't be too hard to implement in the future if there's a different application. */ omapi_handle_table_t *omapi_handle_table; omapi_handle_t omapi_next_handle = 1; /* Next handle to be assigned. */ #define FIND_HAND 0 #define CLEAR_HAND 1 static isc_result_t omapi_handle_lookup_in (omapi_object_t **, omapi_handle_t, omapi_handle_table_t *, int); static isc_result_t omapi_object_handle_in_table (omapi_handle_t, omapi_handle_table_t *, omapi_object_t *); static isc_result_t omapi_handle_table_enclose (omapi_handle_table_t **); isc_result_t omapi_object_handle (omapi_handle_t *h, omapi_object_t *o) { isc_result_t status; if (o -> handle) { *h = o -> handle; return ISC_R_SUCCESS; } if (!omapi_handle_table) { omapi_handle_table = dmalloc (sizeof *omapi_handle_table, MDL); if (!omapi_handle_table) return ISC_R_NOMEMORY; memset (omapi_handle_table, 0, sizeof *omapi_handle_table); omapi_handle_table -> first = 0; omapi_handle_table -> limit = OMAPI_HANDLE_TABLE_SIZE; omapi_handle_table -> leafp = 1; } /* If this handle doesn't fit in the outer table, we need to make a new outer table. This is a while loop in case for some reason we decide to do disjoint handle allocation, where the next level of indirection still isn't big enough to enclose the next handle ID. */ while (omapi_next_handle >= omapi_handle_table -> limit) { omapi_handle_table_t *new; new = dmalloc (sizeof *new, MDL); if (!new) return ISC_R_NOMEMORY; memset (new, 0, sizeof *new); new -> first = 0; new -> limit = (omapi_handle_table -> limit * OMAPI_HANDLE_TABLE_SIZE); new -> leafp = 0; new -> children [0].table = omapi_handle_table; omapi_handle_table = new; } /* Try to cram this handle into the existing table. */ status = omapi_object_handle_in_table (omapi_next_handle, omapi_handle_table, o); /* If it worked, return the next handle and increment it. */ if (status == ISC_R_SUCCESS) { *h = omapi_next_handle; omapi_next_handle++; return ISC_R_SUCCESS; } if (status != ISC_R_NOSPACE) return status; status = omapi_handle_table_enclose (&omapi_handle_table); if (status != ISC_R_SUCCESS) return status; status = omapi_object_handle_in_table (omapi_next_handle, omapi_handle_table, o); if (status != ISC_R_SUCCESS) return status; *h = omapi_next_handle; omapi_next_handle++; return ISC_R_SUCCESS; } static isc_result_t omapi_object_handle_in_table (omapi_handle_t h, omapi_handle_table_t *table, omapi_object_t *o) { omapi_handle_table_t *inner; omapi_handle_t scale, index; isc_result_t status; if (table -> first > h || table -> limit <= h) return ISC_R_NOSPACE; /* If this is a leaf table, just stash the object in the appropriate place. */ if (table -> leafp) { status = (omapi_object_reference (&table -> children [h - table -> first].object, o, MDL)); if (status != ISC_R_SUCCESS) return status; o -> handle = h; return ISC_R_SUCCESS; } /* Scale is the number of handles represented by each child of this table. For a leaf table, scale would be 1. For a first level of indirection, 120. For a second, 120 * 120. Et cetera. */ scale = (table -> limit - table -> first) / OMAPI_HANDLE_TABLE_SIZE; /* So the next most direct table from this one that contains the handle must be the subtable of this table whose index into this table's array of children is the handle divided by the scale. */ index = (h - table -> first) / scale; inner = table -> children [index].table; /* If there is no more direct table than this one in the slot we came up with, make one. */ if (!inner) { inner = dmalloc (sizeof *inner, MDL); if (!inner) return ISC_R_NOMEMORY; memset (inner, 0, sizeof *inner); inner -> first = index * scale + table -> first; inner -> limit = inner -> first + scale; if (scale == OMAPI_HANDLE_TABLE_SIZE) inner -> leafp = 1; table -> children [index].table = inner; } status = omapi_object_handle_in_table (h, inner, o); if (status == ISC_R_NOSPACE) { status = (omapi_handle_table_enclose (&table -> children [index].table)); if (status != ISC_R_SUCCESS) return status; return omapi_object_handle_in_table (h, table -> children [index].table, o); } return status; } static isc_result_t omapi_handle_table_enclose (omapi_handle_table_t **table) { omapi_handle_table_t *inner = *table; omapi_handle_table_t *new; int index, base, scale; /* The scale of the table we're enclosing is going to be the difference between its "first" and "limit" members. So the scale of the table enclosing it is going to be that multiplied by the table size. */ scale = (inner -> first - inner -> limit) * OMAPI_HANDLE_TABLE_SIZE; /* The range that the enclosing table covers is going to be the result of subtracting the remainder of dividing the enclosed table's first entry number by the enclosing table's scale. If handle IDs are being allocated sequentially, the enclosing table's "first" value will be the same as the enclosed table's "first" value. */ base = inner -> first - inner -> first % scale; /* The index into the enclosing table at which the enclosed table will be stored is going to be the difference between the "first" value of the enclosing table and the enclosed table - zero, if we are allocating sequentially. */ index = (base - inner -> first) / OMAPI_HANDLE_TABLE_SIZE; new = dmalloc (sizeof *new, MDL); if (!new) return ISC_R_NOMEMORY; memset (new, 0, sizeof *new); new -> first = base; new -> limit = base + scale; if (scale == OMAPI_HANDLE_TABLE_SIZE) new -> leafp = 0; new -> children [index].table = inner; *table = new; return ISC_R_SUCCESS; } isc_result_t omapi_handle_lookup (omapi_object_t **o, omapi_handle_t h) { return(omapi_handle_lookup_in(o, h, omapi_handle_table, FIND_HAND)); } static isc_result_t omapi_handle_lookup_in (omapi_object_t **o, omapi_handle_t h, omapi_handle_table_t *table, int op) { omapi_handle_t scale, index; if (!table || table->first > h || table->limit <= h) return(ISC_R_NOTFOUND); /* If this is a leaf table, just grab the object. */ if (table->leafp) { /* Not there? */ if (!table->children[h - table->first].object) return(ISC_R_NOTFOUND); if (op == CLEAR_HAND) { table->children[h - table->first].object = NULL; return(ISC_R_SUCCESS); } else { return(omapi_object_reference (o, table->children[h - table->first].object, MDL)); } } /* Scale is the number of handles represented by each child of this table. For a leaf table, scale would be 1. For a first level of indirection, 120. For a second, 120 * 120. Et cetera. */ scale = (table->limit - table->first) / OMAPI_HANDLE_TABLE_SIZE; /* So the next most direct table from this one that contains the handle must be the subtable of this table whose index into this table's array of children is the handle divided by the scale. */ index = (h - table->first) / scale; return(omapi_handle_lookup_in(o, h, table->children[index].table, op)); } /* For looking up objects based on handles that have been sent on the wire. */ isc_result_t omapi_handle_td_lookup (omapi_object_t **obj, omapi_typed_data_t *handle) { omapi_handle_t h; if (handle->type == omapi_datatype_int) h = handle->u.integer; else if (handle->type == omapi_datatype_data && handle->u.buffer.len == sizeof h) { memcpy(&h, handle->u.buffer.value, sizeof h); h = ntohl(h); } else return(DHCP_R_INVALIDARG); return(omapi_handle_lookup(obj, h)); } isc_result_t omapi_handle_clear(omapi_handle_t h) { return(omapi_handle_lookup_in(NULL, h, omapi_handle_table, CLEAR_HAND)); } dhcp-4.4.1/omapip/hash.c000644 000765 000024 00000027552 13243301226 015302 0ustar00tmarkstaff000000 000000 /* hash.c Routines for manipulating hash tables... */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1995-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #include #include #include static unsigned find_length(const void *key, unsigned (*do_hash)(const void *, unsigned, unsigned)) { if (do_hash == do_case_hash || do_hash == do_string_hash) return strlen((const char *)key); if (do_hash == do_number_hash) return sizeof(unsigned); if (do_hash == do_ip4_hash) return 4; log_debug("Unexpected hash function at %s:%d.", MDL); /* * If we get a hash function we don't specifically expect * return a length of 0, this covers the case where a client * id has a length of 0. */ return 0; } int new_hash_table (tp, count, file, line) struct hash_table **tp; unsigned count; const char *file; int line; { struct hash_table *rval; unsigned extra; if (!tp) { log_error ("%s(%d): new_hash_table called with null pointer.", file, line); #if defined (POINTER_DEBUG) abort (); #endif return 0; } if (*tp) { log_error ("%s(%d): non-null target for new_hash_table.", file, line); #if defined (POINTER_DEBUG) abort (); #endif } /* There is one hash bucket in the structure. Allocate extra * memory beyond the end of the structure to fulfill the requested * count ("count - 1"). Do not let there be less than one. */ if (count <= 1) extra = 0; else extra = count - 1; rval = dmalloc(sizeof(struct hash_table) + (extra * sizeof(struct hash_bucket *)), file, line); if (!rval) return 0; rval -> hash_count = count; *tp = rval; return 1; } void free_hash_table (tp, file, line) struct hash_table **tp; const char *file; int line; { struct hash_table *ptr = *tp; #if defined (DEBUG_MEMORY_LEAKAGE) || \ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) int i; struct hash_bucket *hbc, *hbn = (struct hash_bucket *)0; for (i = 0; ptr != NULL && i < ptr -> hash_count; i++) { for (hbc = ptr -> buckets [i]; hbc; hbc = hbn) { hbn = hbc -> next; if (ptr -> dereferencer && hbc -> value) (*ptr -> dereferencer) (&hbc -> value, MDL); } for (hbc = ptr -> buckets [i]; hbc; hbc = hbn) { hbn = hbc -> next; free_hash_bucket (hbc, MDL); } ptr -> buckets [i] = (struct hash_bucket *)0; } #endif dfree((void *)ptr, MDL); *tp = (struct hash_table *)0; } struct hash_bucket *free_hash_buckets; #if defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) struct hash_bucket *hash_bucket_hunks; void relinquish_hash_bucket_hunks () { struct hash_bucket *c, *n, **p; /* Account for all the hash buckets on the free list. */ p = &free_hash_buckets; for (c = free_hash_buckets; c; c = c -> next) { for (n = hash_bucket_hunks; n; n = n -> next) { if (c > n && c < n + 127) { *p = c -> next; n -> len++; break; } } /* If we didn't delete the hash bucket from the free list, advance the pointer. */ if (!n) p = &c -> next; } for (c = hash_bucket_hunks; c; c = n) { n = c -> next; if (c -> len != 126) { log_info ("hashbucket %lx hash_buckets %d free %u", (unsigned long)c, 127, c -> len); } dfree (c, MDL); } } #endif struct hash_bucket *new_hash_bucket (file, line) const char *file; int line; { struct hash_bucket *rval; int i = 0; if (!free_hash_buckets) { rval = dmalloc (127 * sizeof (struct hash_bucket), file, line); if (!rval) return rval; # if defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) rval -> next = hash_bucket_hunks; hash_bucket_hunks = rval; hash_bucket_hunks -> len = 0; i++; rval++; #endif for (; i < 127; i++) { rval -> next = free_hash_buckets; free_hash_buckets = rval; rval++; } } rval = free_hash_buckets; free_hash_buckets = rval -> next; return rval; } void free_hash_bucket (ptr, file, line) struct hash_bucket *ptr; const char *file; int line; { #if defined (DEBUG_MALLOC_POOL) struct hash_bucket *hp; for (hp = free_hash_buckets; hp; hp = hp -> next) { if (hp == ptr) { log_error ("hash bucket freed twice!"); abort (); } } #endif ptr -> next = free_hash_buckets; free_hash_buckets = ptr; } int new_hash(struct hash_table **rp, hash_reference referencer, hash_dereference dereferencer, unsigned hsize, unsigned (*hasher)(const void *, unsigned, unsigned), const char *file, int line) { if (hsize == 0) hsize = DEFAULT_HASH_SIZE; if (!new_hash_table (rp, hsize, file, line)) return 0; memset ((*rp)->buckets, 0, hsize * sizeof(struct hash_bucket *)); (*rp)->referencer = referencer; (*rp)->dereferencer = dereferencer; (*rp)->do_hash = hasher; if (hasher == do_case_hash) (*rp)->cmp = casecmp; else (*rp)->cmp = memcmp; return 1; } unsigned do_case_hash(const void *name, unsigned len, unsigned size) { register unsigned accum = 0; register const unsigned char *s = name; int i = len; register unsigned c; while (i--) { /* Make the hash case-insensitive. */ c = *s++; if (isascii(c)) c = tolower(c); /* Add the character in... */ accum = (accum << 1) + c; /* Add carry back in... */ while (accum > 65535) { accum = (accum & 65535) + (accum >> 16); } } return accum % size; } unsigned do_string_hash(const void *name, unsigned len, unsigned size) { register unsigned accum = 0; register const unsigned char *s = (const unsigned char *)name; int i = len; while (i--) { /* Add the character in... */ accum = (accum << 1) + *s++; /* Add carry back in... */ while (accum > 65535) { accum = (accum & 65535) + (accum >> 16); } } return accum % size; } /* Client identifiers are generally 32-bits of ordinary * non-randomness followed by 24-bits of unordinary randomness. * So, end-align in 24-bit chunks, and xor any preceding data * just to mix it up a little. */ unsigned do_id_hash(const void *name, unsigned len, unsigned size) { register unsigned accum = 0; register const unsigned char *s = (const unsigned char *)name; const unsigned char *end = s + len; if (len == 0) return 0; /* * The switch handles our starting conditions, then we hash the * remaining bytes in groups of 3 */ switch (len % 3) { case 0: break; case 2: accum ^= *s++ << 8; case 1: accum ^= *s++; break; } while (s < end) { accum ^= *s++ << 16; accum ^= *s++ << 8; accum ^= *s++; } return accum % size; } unsigned do_number_hash(const void *key, unsigned len, unsigned size) { register unsigned number = *((const unsigned *)key); return number % size; } unsigned do_ip4_hash(const void *key, unsigned len, unsigned size) { u_int32_t number; memcpy(&number, key, 4); number = ntohl(number); return number % size; } unsigned char * hash_report(struct hash_table *table) { static unsigned char retbuf[sizeof("Contents/Size (%): " "2147483647/2147483647 " "(2147483647%). " "Min/max: 2147483647/2147483647")]; unsigned curlen, pct, contents=0, minlen=UINT_MAX, maxlen=0; unsigned i; struct hash_bucket *bp; if (table == NULL) return (unsigned char *) "No table."; if (table->hash_count == 0) return (unsigned char *) "Invalid hash table."; for (i = 0 ; i < table->hash_count ; i++) { curlen = 0; bp = table->buckets[i]; while (bp != NULL) { curlen++; bp = bp->next; } if (curlen < minlen) minlen = curlen; if (curlen > maxlen) maxlen = curlen; contents += curlen; } if (contents >= (UINT_MAX / 100)) pct = contents / ((table->hash_count / 100) + 1); else pct = (contents * 100) / table->hash_count; if (contents > 2147483647 || table->hash_count > 2147483647 || pct > 2147483647 || minlen > 2147483647 || maxlen > 2147483647) return (unsigned char *) "Report out of range for display."; sprintf((char *)retbuf, "Contents/Size (%%): %u/%u (%u%%). Min/max: %u/%u", contents, table->hash_count, pct, minlen, maxlen); return retbuf; } void add_hash (table, key, len, pointer, file, line) struct hash_table *table; unsigned len; const void *key; hashed_object_t *pointer; const char *file; int line; { int hashno; struct hash_bucket *bp; void *foo; if (!table) return; if (!len) len = find_length(key, table->do_hash); hashno = (*table->do_hash)(key, len, table->hash_count); bp = new_hash_bucket (file, line); if (!bp) { log_error ("Can't add entry to hash table: no memory."); return; } bp -> name = key; if (table -> referencer) { foo = &bp -> value; (*(table -> referencer)) (foo, pointer, file, line); } else bp -> value = pointer; bp -> next = table -> buckets [hashno]; bp -> len = len; table -> buckets [hashno] = bp; } void delete_hash_entry (table, key, len, file, line) struct hash_table *table; unsigned len; const void *key; const char *file; int line; { int hashno; struct hash_bucket *bp, *pbp = (struct hash_bucket *)0; void *foo; if (!table) return; if (!len) len = find_length(key, table->do_hash); hashno = (*table->do_hash)(key, len, table->hash_count); /* Go through the list looking for an entry that matches; if we find it, delete it. */ for (bp = table -> buckets [hashno]; bp; bp = bp -> next) { if ((!bp -> len && !strcmp ((const char *)bp->name, key)) || (bp -> len == len && !(table -> cmp)(bp->name, key, len))) { if (pbp) { pbp -> next = bp -> next; } else { table -> buckets [hashno] = bp -> next; } if (bp -> value && table -> dereferencer) { foo = &bp -> value; (*(table -> dereferencer)) (foo, file, line); } free_hash_bucket (bp, file, line); break; } pbp = bp; /* jwg, 9/6/96 - nice catch! */ } } int hash_lookup (vp, table, key, len, file, line) hashed_object_t **vp; struct hash_table *table; const void *key; unsigned len; const char *file; int line; { int hashno; struct hash_bucket *bp; if (!table) return 0; if (!len) len = find_length(key, table->do_hash); if (*vp != NULL) { log_fatal("Internal inconsistency: storage value has not been " "initialized to zero (from %s:%d).", file, line); } hashno = (*table->do_hash)(key, len, table->hash_count); for (bp = table -> buckets [hashno]; bp; bp = bp -> next) { if (len == bp -> len && !(*table->cmp)(bp->name, key, len)) { if (table -> referencer) (*table -> referencer) (vp, bp -> value, file, line); else *vp = bp -> value; return 1; } } return 0; } int hash_foreach (struct hash_table *table, hash_foreach_func func) { int i; struct hash_bucket *bp, *next; int count = 0; if (!table) return 0; for (i = 0; i < table -> hash_count; i++) { bp = table -> buckets [i]; while (bp) { next = bp -> next; if ((*func)(bp->name, bp->len, bp->value) != ISC_R_SUCCESS) return count; bp = next; count++; } } return count; } int casecmp (const void *v1, const void *v2, size_t len) { size_t i; const unsigned char *s = v1; const unsigned char *t = v2; for (i = 0; i < len; i++) { int c1, c2; if (isascii(s[i])) c1 = tolower(s[i]); else c1 = s[i]; if (isascii(t[i])) c2 = tolower(t[i]); else c2 = t[i]; if (c1 < c2) return -1; if (c1 > c2) return 1; } return 0; } dhcp-4.4.1/omapip/inet_addr.c000644 000765 000024 00000007304 13243301226 016301 0ustar00tmarkstaff000000 000000 /* $NetBSD: inet_addr.c,v 1.6 1996/02/02 15:22:23 mrg Exp $ */ /* * Copyright (c) 1983, 1990, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "dhcpd.h" #include "omapip/omapip_p.h" #ifdef NEED_INET_ATON /* * Check whether "cp" is a valid ascii representation * of an Internet address and convert to a binary address. * Returns 1 if the address is valid, 0 if not. * This replaces inet_addr, the return value from which * cannot distinguish between failure and a local broadcast address. */ int inet_aton(cp, addr) const char *cp; struct in_addr *addr; { register u_long val; register int base, n; register char c; u_int parts[4]; register u_int *pp = parts; for (;;) { /* * Collect number up to ``.''. * Values are specified as for C: * 0x=hex, 0=octal, other=decimal. */ val = 0; base = 10; if (*cp == '0') { if (*++cp == 'x' || *cp == 'X') base = 16, cp++; else base = 8; } while ((c = *cp) != '\0') { if (isascii(c) && isdigit((int)c)) { val = (val * base) + (c - '0'); cp++; continue; } if (base == 16 && isascii(c) && isxdigit((int)c)) { val = (val << 4) + (c + 10 - (islower((int)c) ? 'a' : 'A')); cp++; continue; } break; } if (*cp == '.') { /* * Internet format: * a.b.c.d * a.b.c (with c treated as 16-bits) * a.b (with b treated as 24 bits) */ if (pp >= parts + 3 || val > 0xff) return (0); *pp++ = val, cp++; } else break; } /* * Check for trailing characters. */ if (*cp && (!isascii(*cp) || !isspace((int)*cp))) return (0); /* * Concoct the address according to * the number of parts specified. */ n = pp - parts + 1; switch (n) { case 0: return (0); /* initial nondigit */ case 1: /* a -- 32 bits */ break; case 2: /* a.b -- 8.24 bits */ if (val > 0xffffff) return (0); val |= parts[0] << 24; break; case 3: /* a.b.c -- 8.8.16 bits */ if (val > 0xffff) return (0); val |= (parts[0] << 24) | (parts[1] << 16); break; case 4: /* a.b.c.d -- 8.8.8.8 bits */ if (val > 0xff) return (0); val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); break; } if (addr) addr->s_addr = htonl(val); return (1); } #endif dhcp-4.4.1/omapip/isclib.c000644 000765 000024 00000024565 13243301226 015625 0ustar00tmarkstaff000000 000000 /* * Copyright(c) 2009-2017 by 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * http://www.isc.org/ * */ /*Trying to figure out what we need to define to get things to work. It looks like we want/need the library but need the fdwatchcommand which may be a problem */ #include "dhcpd.h" #include #include dhcp_context_t dhcp_gbl_ctx; int shutdown_signal = 0; #if defined (NSUPDATE) /* This routine will open up the /etc/resolv.conf file and * send any nameservers it finds to the DNS client code. * It may be moved to be part of the dns client code instead * of being in the DHCP code */ isc_result_t dhcp_dns_client_setservers(void) { isc_result_t result; irs_resconf_t *resconf = NULL; isc_sockaddrlist_t *nameservers; isc_sockaddr_t *sa; result = irs_resconf_load(dhcp_gbl_ctx.mctx, _PATH_RESOLV_CONF, &resconf); if (result != ISC_R_SUCCESS && result != ISC_R_FILENOTFOUND) { log_error("irs_resconf_load failed: %d.", result); return (result); } nameservers = irs_resconf_getnameservers(resconf); /* Initialize port numbers */ for (sa = ISC_LIST_HEAD(*nameservers); sa != NULL; sa = ISC_LIST_NEXT(sa, link)) { switch (sa->type.sa.sa_family) { case AF_INET: sa->type.sin.sin_port = htons(NS_DEFAULTPORT); break; case AF_INET6: sa->type.sin6.sin6_port = htons(NS_DEFAULTPORT); break; default: break; } } result = dns_client_setservers(dhcp_gbl_ctx.dnsclient, dns_rdataclass_in, NULL, nameservers); if (result != ISC_R_SUCCESS) { log_error("dns_client_setservers failed: %d.", result); } return (result); } #endif void isclib_cleanup(void) { #if defined (NSUPDATE) if (dhcp_gbl_ctx.dnsclient != NULL) dns_client_destroy((dns_client_t **)&dhcp_gbl_ctx.dnsclient); #endif if (dhcp_gbl_ctx.task != NULL) { isc_task_shutdown(dhcp_gbl_ctx.task); isc_task_detach(&dhcp_gbl_ctx.task); } if (dhcp_gbl_ctx.timermgr != NULL) isc_timermgr_destroy(&dhcp_gbl_ctx.timermgr); if (dhcp_gbl_ctx.socketmgr != NULL) isc_socketmgr_destroy(&dhcp_gbl_ctx.socketmgr); if (dhcp_gbl_ctx.taskmgr != NULL) isc_taskmgr_destroy(&dhcp_gbl_ctx.taskmgr); if (dhcp_gbl_ctx.actx_started != ISC_FALSE) { isc_app_ctxfinish(dhcp_gbl_ctx.actx); dhcp_gbl_ctx.actx_started = ISC_FALSE; } if (dhcp_gbl_ctx.actx != NULL) isc_appctx_destroy(&dhcp_gbl_ctx.actx); if (dhcp_gbl_ctx.mctx != NULL) isc_mem_detach(&dhcp_gbl_ctx.mctx); return; } /* Installs a handler for a signal using sigaction */ static void handle_signal(int sig, void (*handler)(int)) { struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = handler; sigfillset(&sa.sa_mask); if (sigaction(sig, &sa, NULL) != 0) { log_debug("handle_signal() failed for signal %d error: %s", sig, strerror(errno)); } } isc_result_t dhcp_context_create(int flags, struct in_addr *local4, struct in6_addr *local6) { isc_result_t result; if ((flags & DHCP_CONTEXT_PRE_DB) != 0) { /* * Set up the error messages, this isn't the right place * for this call but it is convienent for now. */ result = dhcp_result_register(); if (result != ISC_R_SUCCESS) { log_fatal("register_table() %s: %u", "failed", result); } memset(&dhcp_gbl_ctx, 0, sizeof (dhcp_gbl_ctx)); isc_lib_register(); /* get the current time for use as the random seed */ gettimeofday(&cur_tv, (struct timezone *)0); isc_random_seed(cur_tv.tv_sec); /* we need to create the memory context before * the lib inits in case we aren't doing NSUPDATE * in which case dst needs a memory context */ result = isc_mem_create(0, 0, &dhcp_gbl_ctx.mctx); if (result != ISC_R_SUCCESS) goto cleanup; #if defined (NSUPDATE) result = dns_lib_init(); if (result != ISC_R_SUCCESS) goto cleanup; #else /* The dst library is inited as part of dns_lib_init, we don't * need it if NSUPDATE is enabled */ result = dst_lib_init(dhcp_gbl_ctx.mctx, NULL, 0); if (result != ISC_R_SUCCESS) goto cleanup; #endif result = isc_appctx_create(dhcp_gbl_ctx.mctx, &dhcp_gbl_ctx.actx); if (result != ISC_R_SUCCESS) goto cleanup; result = isc_taskmgr_createinctx(dhcp_gbl_ctx.mctx, dhcp_gbl_ctx.actx, 1, 0, &dhcp_gbl_ctx.taskmgr); if (result != ISC_R_SUCCESS) goto cleanup; result = isc_socketmgr_createinctx(dhcp_gbl_ctx.mctx, dhcp_gbl_ctx.actx, &dhcp_gbl_ctx.socketmgr); if (result != ISC_R_SUCCESS) goto cleanup; result = isc_timermgr_createinctx(dhcp_gbl_ctx.mctx, dhcp_gbl_ctx.actx, &dhcp_gbl_ctx.timermgr); if (result != ISC_R_SUCCESS) goto cleanup; result = isc_task_create(dhcp_gbl_ctx.taskmgr, 0, &dhcp_gbl_ctx.task); if (result != ISC_R_SUCCESS) goto cleanup; result = isc_app_ctxstart(dhcp_gbl_ctx.actx); if (result != ISC_R_SUCCESS) return (result); dhcp_gbl_ctx.actx_started = ISC_TRUE; /* Not all OSs support suppressing SIGPIPE through socket * options, so set the sigal action to be ignore. This allows * broken connections to fail gracefully with EPIPE on writes */ handle_signal(SIGPIPE, SIG_IGN); /* Reset handlers installed by isc_app_ctxstart() * to default for control-c and kill */ handle_signal(SIGINT, SIG_DFL); handle_signal(SIGTERM, SIG_DFL); } #if defined (NSUPDATE) if ((flags & DHCP_CONTEXT_POST_DB) != 0) { /* Setting addresses only. * All real work will be done later on if needed to avoid * listening on ddns port if client/server was compiled with * ddns support but not using it. */ if (local4 != NULL) { dhcp_gbl_ctx.use_local4 = 1; isc_sockaddr_fromin(&dhcp_gbl_ctx.local4_sockaddr, local4, 0); } if (local6 != NULL) { dhcp_gbl_ctx.use_local6 = 1; isc_sockaddr_fromin6(&dhcp_gbl_ctx.local6_sockaddr, local6, 0); } if (!(flags & DHCP_DNS_CLIENT_LAZY_INIT)) { result = dns_client_init(); } } #endif return(ISC_R_SUCCESS); cleanup: /* * Currently we don't try and cleanup, just return an error * expecting that our caller will log the error and exit. */ return(result); } /* * Convert a string name into the proper structure for the isc routines * * Previously we allowed names without a trailing '.' however the current * dns and dst code requires the names to end in a period. If the * name doesn't have a trailing period add one as part of creating * the dns name. */ isc_result_t dhcp_isc_name(unsigned char *namestr, dns_fixedname_t *namefix, dns_name_t **name) { size_t namelen; isc_buffer_t b; isc_result_t result; namelen = strlen((char *)namestr); isc_buffer_init(&b, namestr, namelen); isc_buffer_add(&b, namelen); dns_fixedname_init(namefix); *name = dns_fixedname_name(namefix); result = dns_name_fromtext(*name, &b, dns_rootname, 0, NULL); isc_buffer_invalidate(&b); return(result); } isc_result_t isclib_make_dst_key(char *inname, char *algorithm, unsigned char *secret, int length, dst_key_t **dstkey) { isc_result_t result; dns_name_t *name; dns_fixedname_t name0; isc_buffer_t b; unsigned int algorithm_code; isc_buffer_init(&b, secret, length); isc_buffer_add(&b, length); if (strcasecmp(algorithm, DHCP_HMAC_MD5_NAME) == 0) { algorithm_code = DST_ALG_HMACMD5; } else if (strcasecmp(algorithm, DHCP_HMAC_SHA1_NAME) == 0) { algorithm_code = DST_ALG_HMACSHA1; } else if (strcasecmp(algorithm, DHCP_HMAC_SHA224_NAME) == 0) { algorithm_code = DST_ALG_HMACSHA224; } else if (strcasecmp(algorithm, DHCP_HMAC_SHA256_NAME) == 0) { algorithm_code = DST_ALG_HMACSHA256; } else if (strcasecmp(algorithm, DHCP_HMAC_SHA384_NAME) == 0) { algorithm_code = DST_ALG_HMACSHA384; } else if (strcasecmp(algorithm, DHCP_HMAC_SHA512_NAME) == 0) { algorithm_code = DST_ALG_HMACSHA512; } else { return(DHCP_R_INVALIDARG); } result = dhcp_isc_name((unsigned char *)inname, &name0, &name); if (result != ISC_R_SUCCESS) { return(result); } return(dst_key_frombuffer(name, algorithm_code, DNS_KEYOWNER_ENTITY, DNS_KEYPROTO_DNSSEC, dns_rdataclass_in, &b, dhcp_gbl_ctx.mctx, dstkey)); } /** * signal handler that initiates server shutdown * * @param signal signal code that we received */ void dhcp_signal_handler(int signal) { isc_appctx_t *ctx = dhcp_gbl_ctx.actx; int prev = shutdown_signal; if (prev != 0) { /* Already in shutdown. */ return; } /* Possible race but does it matter? */ shutdown_signal = signal; /* Use reload (aka suspend) for easier dispatch() reenter. */ if (ctx && ctx->methods && ctx->methods->ctxsuspend) { (void) isc_app_ctxsuspend(ctx); } } isc_result_t dns_client_init() { isc_result_t result; if (dhcp_gbl_ctx.dnsclient == NULL) { result = dns_client_createx2(dhcp_gbl_ctx.mctx, dhcp_gbl_ctx.actx, dhcp_gbl_ctx.taskmgr, dhcp_gbl_ctx.socketmgr, dhcp_gbl_ctx.timermgr, 0, &dhcp_gbl_ctx.dnsclient, (dhcp_gbl_ctx.use_local4 ? &dhcp_gbl_ctx.local4_sockaddr : NULL), (dhcp_gbl_ctx.use_local6 ? &dhcp_gbl_ctx.local6_sockaddr : NULL)); if (result != ISC_R_SUCCESS) { log_error("Unable to create DNS client context:" " result: %d", result); return result; } /* If we can't set up the servers we may not be able to * do DDNS but we should continue to try and perform * our basic functions and let the user sort it out. */ result = dhcp_dns_client_setservers(); if (result != ISC_R_SUCCESS) { log_error("Unable to set resolver from resolv.conf; " "startup continuing but DDNS support " "may be affected: result %d", result); } } return ISC_R_SUCCESS; } dhcp-4.4.1/omapip/iscprint.c000644 000765 000024 00000024251 13243301226 016203 0ustar00tmarkstaff000000 000000 /* * Copyright (C) 2004-2017 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2003 Internet Software Consortium. * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* $Id: iscprint.c,v 1.2 2005/03/17 20:30:41 dhankins Exp $ */ #include "dhcpd.h" #ifdef NO_SNPRINTF #ifndef LINT static char copyright[] = "$Id: iscprint.c,v 1.2 2005/03/17 20:30:41 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium, Inc. All rights reserved."; #endif #define INSIST(cond) REQUIRE(cond) #define REQUIRE(cond) if (!(cond)) { return 0; } /* * Return length of string that would have been written if not truncated. */ int isc_print_snprintf(char *str, size_t size, const char *format, ...) { va_list ap; int ret; va_start(ap, format); ret = vsnprintf(str, size, format, ap); va_end(ap); return (ret); } /* * Return length of string that would have been written if not truncated. */ int isc_print_vsnprintf(char *str, size_t size, const char *format, va_list ap) { int h; int l; int q; int alt; int zero; int left; int plus; int space; int neg; isc_int64_t tmpi; isc_uint64_t tmpui; unsigned long width; unsigned long precision; unsigned int length; char buf[1024]; char c; void *v; char *save = str; const char *cp; const char *head; int count = 0; int pad; int zeropad; int dot; double dbl; #ifdef HAVE_LONG_DOUBLE long double ldbl; #endif char fmt[32]; INSIST(str != NULL); INSIST(format != NULL); while (*format != '\0') { if (*format != '%') { if (size > 1) { *str++ = *format; size--; } count++; format++; continue; } format++; /* * Reset flags. */ dot = neg = space = plus = left = zero = alt = h = l = q = 0; width = precision = 0; head = ""; length = pad = zeropad = 0; do { if (*format == '#') { alt = 1; format++; } else if (*format == '-') { left = 1; zero = 0; format++; } else if (*format == ' ') { if (!plus) space = 1; format++; } else if (*format == '+') { plus = 1; space = 0; format++; } else if (*format == '0') { if (!left) zero = 1; format++; } else break; } while (1); /* * Width. */ if (*format == '*') { width = va_arg(ap, int); format++; } else if (isdigit((unsigned char)*format)) { char *e; width = strtoul(format, &e, 10); format = e; } /* * Precision. */ if (*format == '.') { format++; dot = 1; if (*format == '*') { precision = va_arg(ap, int); format++; } else if (isdigit((unsigned char)*format)) { char *e; precision = strtoul(format, &e, 10); format = e; } } switch (*format) { case '\0': continue; case '%': if (size > 1) { *str++ = *format; size--; } count++; break; case 'q': q = 1; format++; goto doint; case 'h': h = 1; format++; goto doint; case 'l': l = 1; format++; if (*format == 'l') { q = 1; format++; } goto doint; case 'n': case 'i': case 'd': case 'o': case 'u': case 'x': case 'X': doint: if (precision != 0) zero = 0; switch (*format) { case 'n': if (h) { short int *p; p = va_arg(ap, short *); REQUIRE(p != NULL); *p = str - save; } else if (l) { long int *p; p = va_arg(ap, long *); REQUIRE(p != NULL); *p = str - save; } else { int *p; p = va_arg(ap, int *); REQUIRE(p != NULL); *p = str - save; } break; case 'i': case 'd': if (q) tmpi = va_arg(ap, isc_int64_t); else if (l) tmpi = va_arg(ap, long int); else tmpi = va_arg(ap, int); if (tmpi < 0) { head = "-"; tmpui = -tmpi; } else { if (plus) head = "+"; else if (space) head = " "; else head = ""; tmpui = tmpi; } sprintf(buf, "%u", tmpui); goto printint; case 'o': if (q) tmpui = va_arg(ap, isc_uint64_t); else if (l) tmpui = va_arg(ap, long int); else tmpui = va_arg(ap, int); sprintf(buf, alt ? "%#o" : "%o", tmpui); goto printint; case 'u': if (q) tmpui = va_arg(ap, isc_uint64_t); else if (l) tmpui = va_arg(ap, unsigned long int); else tmpui = va_arg(ap, unsigned int); sprintf(buf, "%u", tmpui); goto printint; case 'x': if (q) tmpui = va_arg(ap, isc_uint64_t); else if (l) tmpui = va_arg(ap, unsigned long int); else tmpui = va_arg(ap, unsigned int); if (alt) { head = "0x"; if (precision > 2) precision -= 2; } sprintf(buf, "%x", tmpui); goto printint; case 'X': if (q) tmpui = va_arg(ap, isc_uint64_t); else if (l) tmpui = va_arg(ap, unsigned long int); else tmpui = va_arg(ap, unsigned int); if (alt) { head = "0X"; if (precision > 2) precision -= 2; } sprintf(buf, "%X", tmpui); goto printint; printint: if (precision != 0 || width != 0) { length = strlen(buf); if (length < precision) zeropad = precision - length; else if (length < width && zero) zeropad = width - length; if (width != 0) { pad = width - length - zeropad - strlen(head); if (pad < 0) pad = 0; } } count += strlen(head) + strlen(buf) + pad + zeropad; if (!left) { while (pad > 0 && size > 1) { *str++ = ' '; size--; pad--; } } cp = head; while (*cp != '\0' && size > 1) { *str++ = *cp++; size--; } while (zeropad > 0 && size > 1) { *str++ = '0'; size--; zeropad--; } cp = buf; while (*cp != '\0' && size > 1) { *str++ = *cp++; size--; } while (pad > 0 && size > 1) { *str++ = ' '; size--; pad--; } break; default: break; } break; case 's': cp = va_arg(ap, char *); REQUIRE(cp != NULL); if (precision != 0) { /* * cp need not be NULL terminated. */ const char *tp; unsigned long n; n = precision; tp = cp; while (n != 0 && *tp != '\0') n--, tp++; length = precision - n; } else { length = strlen(cp); } if (width != 0) { pad = width - length; if (pad < 0) pad = 0; } count += pad + length; if (!left) while (pad > 0 && size > 1) { *str++ = ' '; size--; pad--; } if (precision != 0) while (precision > 0 && *cp != '\0' && size > 1) { *str++ = *cp++; size--; precision--; } else while (*cp != '\0' && size > 1) { *str++ = *cp++; size--; } while (pad > 0 && size > 1) { *str++ = ' '; size--; pad--; } break; case 'c': c = va_arg(ap, int); if (width > 0) { count += width; width--; if (left) { *str++ = c; size--; } while (width-- > 0 && size > 1) { *str++ = ' '; size--; } if (!left && size > 1) { *str++ = c; size--; } } else { count++; if (size > 1) { *str++ = c; size--; } } break; case 'p': v = va_arg(ap, void *); sprintf(buf, "%p", v); length = strlen(buf); if (precision > length) zeropad = precision - length; if (width > 0) { pad = width - length - zeropad; if (pad < 0) pad = 0; } count += length + pad + zeropad; if (!left) while (pad > 0 && size > 1) { *str++ = ' '; size--; pad--; } cp = buf; if (zeropad > 0 && buf[0] == '0' && (buf[1] == 'x' || buf[1] == 'X')) { if (size > 1) { *str++ = *cp++; size--; } if (size > 1) { *str++ = *cp++; size--; } while (zeropad > 0 && size > 1) { *str++ = '0'; size--; zeropad--; } } while (*cp != '\0' && size > 1) { *str++ = *cp++; size--; } while (pad > 0 && size > 1) { *str++ = ' '; size--; pad--; } break; case 'D': /*deprecated*/ INSIST("use %ld instead of %D" == NULL); case 'O': /*deprecated*/ INSIST("use %lo instead of %O" == NULL); case 'U': /*deprecated*/ INSIST("use %lu instead of %U" == NULL); case 'L': #ifdef HAVE_LONG_DOUBLE l = 1; #else INSIST("long doubles are not supported" == NULL); #endif /*FALLTHROUGH*/ case 'e': case 'E': case 'f': case 'g': case 'G': if (!dot) precision = 6; /* * IEEE floating point. * MIN 2.2250738585072014E-308 * MAX 1.7976931348623157E+308 * VAX floating point has a smaller range than IEEE. * * precisions > 324 don't make much sense. * if we cap the precision at 512 we will not * overflow buf. */ if (precision > 512) precision = 512; sprintf(fmt, "%%%s%s.%lu%s%c", alt ? "#" : "", plus ? "+" : space ? " " : "", precision, l ? "L" : "", *format); switch (*format) { case 'e': case 'E': case 'f': case 'g': case 'G': #ifdef HAVE_LONG_DOUBLE if (l) { ldbl = va_arg(ap, long double); sprintf(buf, fmt, ldbl); } else #endif { dbl = va_arg(ap, double); sprintf(buf, fmt, dbl); } length = strlen(buf); if (width > 0) { pad = width - length; if (pad < 0) pad = 0; } count += length + pad; if (!left) while (pad > 0 && size > 1) { *str++ = ' '; size--; pad--; } cp = buf; while (*cp != ' ' && size > 1) { *str++ = *cp++; size--; } while (pad > 0 && size > 1) { *str++ = ' '; size--; pad--; } break; default: continue; } break; default: continue; } format++; } if (size > 0) *str = '\0'; return (count); } #endif dhcp-4.4.1/omapip/listener.c000644 000765 000024 00000031513 13243301226 016174 0ustar00tmarkstaff000000 000000 /* listener.c Subroutines that support the generic listener object. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1999-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #include #include #if defined (TRACING) omapi_array_t *trace_listeners; static void trace_listener_accept_input (trace_type_t *, unsigned, char *); static void trace_listener_remember (omapi_listener_object_t *, const char *, int); static void trace_listener_accept_stop (trace_type_t *); trace_type_t *trace_listener_accept; #endif OMAPI_OBJECT_ALLOC (omapi_listener, omapi_listener_object_t, omapi_type_listener) isc_result_t omapi_listen (omapi_object_t *h, unsigned port, int max) { omapi_addr_t addr; #ifdef DEBUG_PROTOCOL log_debug ("omapi_listen(port=%d, max=%d)", port, max); #endif addr.addrtype = AF_INET; addr.addrlen = sizeof (struct in_addr); memset (addr.address, 0, sizeof addr.address); /* INADDR_ANY */ addr.port = port; return omapi_listen_addr (h, &addr, max); } isc_result_t omapi_listen_addr (omapi_object_t *h, omapi_addr_t *addr, int max) { isc_result_t status; omapi_listener_object_t *obj; int i; /* Currently only support IPv4 addresses. */ if (addr->addrtype != AF_INET) return DHCP_R_INVALIDARG; /* Get the handle. */ obj = (omapi_listener_object_t *)0; status = omapi_listener_allocate (&obj, MDL); if (status != ISC_R_SUCCESS) /* * we could simply return here but by going to * error_exit we keep the code check tools happy * without removing the NULL check on obj at * the exit, which we could skip curently but * might want in the future. */ goto error_exit; obj->socket = -1; /* Connect this object to the inner object. */ status = omapi_object_reference (&h -> outer, (omapi_object_t *)obj, MDL); if (status != ISC_R_SUCCESS) goto error_exit; status = omapi_object_reference (&obj -> inner, h, MDL); if (status != ISC_R_SUCCESS) goto error_exit; /* Set up the address on which we will listen... */ obj -> address.sin_port = htons (addr -> port); memcpy (&obj -> address.sin_addr, addr -> address, sizeof obj -> address.sin_addr); #if defined (HAVE_SA_LEN) obj -> address.sin_len = sizeof (struct sockaddr_in); #endif obj -> address.sin_family = AF_INET; memset (&(obj -> address.sin_zero), 0, sizeof obj -> address.sin_zero); #if defined (TRACING) /* If we're playing back a trace file, we remember the object on the trace listener queue. */ if (trace_playback ()) { trace_listener_remember (obj, MDL); } else { #endif /* Create a socket on which to listen. */ obj -> socket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); if (obj->socket == -1) { if (errno == EMFILE || errno == ENFILE || errno == ENOBUFS) status = ISC_R_NORESOURCES; else status = ISC_R_UNEXPECTED; goto error_exit; } #if defined (HAVE_SETFD) if (fcntl (obj -> socket, F_SETFD, 1) < 0) { status = ISC_R_UNEXPECTED; goto error_exit; } #endif /* Set the REUSEADDR option so that we don't fail to start if we're being restarted. */ i = 1; if (setsockopt (obj -> socket, SOL_SOCKET, SO_REUSEADDR, (char *)&i, sizeof i) < 0) { status = ISC_R_UNEXPECTED; goto error_exit; } /* Try to bind to the wildcard address using the port number we were given. */ i = bind (obj -> socket, (struct sockaddr *)&obj -> address, sizeof obj -> address); if (i < 0) { if (errno == EADDRINUSE) status = ISC_R_ADDRNOTAVAIL; else if (errno == EPERM) status = ISC_R_NOPERM; else status = ISC_R_UNEXPECTED; goto error_exit; } /* Now tell the kernel to listen for connections. */ if (listen (obj -> socket, max)) { status = ISC_R_UNEXPECTED; goto error_exit; } if (fcntl (obj -> socket, F_SETFL, O_NONBLOCK) < 0) { status = ISC_R_UNEXPECTED; goto error_exit; } status = omapi_register_io_object ((omapi_object_t *)obj, omapi_listener_readfd, 0, omapi_accept, 0, 0); #if defined (TRACING) } #endif omapi_listener_dereference (&obj, MDL); return status; error_exit: if (obj != NULL) { if (h->outer == (omapi_object_t *)obj) { omapi_object_dereference((omapi_object_t **)&h->outer, MDL); } if (obj->inner == h) { omapi_object_dereference((omapi_object_t **)&obj->inner, MDL); } if (obj->socket != -1) { close(obj->socket); } omapi_listener_dereference(&obj, MDL); } return status; } /* Return the socket on which the dispatcher should wait for readiness to read, for a listener object. */ int omapi_listener_readfd (omapi_object_t *h) { omapi_listener_object_t *l; if (h -> type != omapi_type_listener) return -1; l = (omapi_listener_object_t *)h; return l -> socket; } /* Reader callback for a listener object. Accept an incoming connection. */ isc_result_t omapi_accept (omapi_object_t *h) { isc_result_t status; socklen_t len; omapi_connection_object_t *obj; omapi_listener_object_t *listener; struct sockaddr_in addr; int socket; if (h -> type != omapi_type_listener) return DHCP_R_INVALIDARG; listener = (omapi_listener_object_t *)h; /* Accept the connection. */ len = sizeof addr; socket = accept (listener -> socket, ((struct sockaddr *)&(addr)), &len); if (socket < 0) { if (errno == EMFILE || errno == ENFILE || errno == ENOBUFS) return ISC_R_NORESOURCES; return ISC_R_UNEXPECTED; } if ((MAX_FD_VALUE != 0) && (socket > MAX_FD_VALUE)) { close(socket); return (ISC_R_NORESOURCES); } #if defined (TRACING) /* If we're recording a trace, remember the connection. */ if (trace_record ()) { trace_iov_t iov [3]; iov [0].buf = (char *)&addr.sin_port; iov [0].len = sizeof addr.sin_port; iov [1].buf = (char *)&addr.sin_addr; iov [1].len = sizeof addr.sin_addr; iov [2].buf = (char *)&listener -> address.sin_port; iov [2].len = sizeof listener -> address.sin_port; trace_write_packet_iov (trace_listener_accept, 3, iov, MDL); } #endif obj = (omapi_connection_object_t *)0; status = omapi_listener_connect (&obj, listener, socket, &addr); if (status != ISC_R_SUCCESS) { close (socket); return status; } status = omapi_register_io_object ((omapi_object_t *)obj, omapi_connection_readfd, omapi_connection_writefd, omapi_connection_reader, omapi_connection_writer, omapi_connection_reaper); /* Lose our reference to the connection, so it'll be gc'd when it's reaped. */ omapi_connection_dereference (&obj, MDL); if (status != ISC_R_SUCCESS) omapi_disconnect ((omapi_object_t *)(obj), 1); return status; } isc_result_t omapi_listener_connect (omapi_connection_object_t **obj, omapi_listener_object_t *listener, int socket, struct sockaddr_in *remote_addr) { isc_result_t status; omapi_object_t *h = (omapi_object_t *)listener; omapi_addr_t addr; #ifdef DEBUG_PROTOCOL log_debug ("omapi_accept()"); #endif /* Get the handle. */ status = omapi_connection_allocate (obj, MDL); if (status != ISC_R_SUCCESS) return status; (*obj) -> state = omapi_connection_connected; (*obj) -> remote_addr = *remote_addr; (*obj) -> socket = socket; /* Verify that this host is allowed to connect. */ if (listener -> verify_addr) { addr.addrtype = AF_INET; addr.addrlen = sizeof (remote_addr -> sin_addr); memcpy (addr.address, &remote_addr -> sin_addr, sizeof (remote_addr -> sin_addr)); addr.port = ntohs(remote_addr -> sin_port); status = (listener -> verify_addr) (h, &addr); if (status != ISC_R_SUCCESS) { omapi_disconnect ((omapi_object_t *)(*obj), 1); omapi_connection_dereference (obj, MDL); return status; } } omapi_listener_reference (&(*obj) -> listener, listener, MDL); #if defined (TRACING) omapi_connection_register (*obj, MDL); #endif status = omapi_signal (h, "connect", (*obj)); return status; } #if defined (TRACING) OMAPI_ARRAY_TYPE(omapi_listener, omapi_listener_object_t) void omapi_listener_trace_setup (void) { trace_listener_accept = trace_type_register ("listener-accept", (void *)0, trace_listener_accept_input, trace_listener_accept_stop, MDL); } static void trace_listener_remember (omapi_listener_object_t *obj, const char *file, int line) { isc_result_t status; if (!trace_listeners) { status = omapi_listener_array_allocate (&trace_listeners, file, line); if (status != ISC_R_SUCCESS) { foo: log_error ("trace_listener_remember: %s", isc_result_totext (status)); return; } } status = omapi_listener_array_extend (trace_listeners, obj, &obj -> index, MDL); if (status != ISC_R_SUCCESS) goto foo; } static void trace_listener_accept_input (trace_type_t *ttype, unsigned length, char *buf) { struct in_addr *addr; u_int16_t *remote_port; u_int16_t *local_port; omapi_connection_object_t *obj; isc_result_t status; struct sockaddr_in remote_addr; addr = (struct in_addr *)buf; remote_port = (u_int16_t *)(addr + 1); local_port = remote_port + 1; memset (&remote_addr, 0, sizeof remote_addr); remote_addr.sin_addr = *addr; remote_addr.sin_port = *remote_port; omapi_array_foreach_begin (trace_listeners, omapi_listener_object_t, lp) { if (lp -> address.sin_port == *local_port) { obj = (omapi_connection_object_t *)0; status = omapi_listener_connect (&obj, lp, 0, &remote_addr); if (status != ISC_R_SUCCESS) { log_error("%s:%d: OMAPI: Failed to connect " "a listener.", MDL); } omapi_listener_dereference (&lp, MDL); return; } } omapi_array_foreach_end (trace_listeners, omapi_listener_object_t, lp); log_error ("trace_listener_accept: %s from %s/%d to port %d", "unexpected connect", inet_ntoa (*addr), *remote_port, *local_port); } static void trace_listener_accept_stop (trace_type_t *ttype) { } #endif isc_result_t omapi_listener_configure_security (omapi_object_t *h, isc_result_t (*verify_addr) (omapi_object_t *, omapi_addr_t *)) { omapi_listener_object_t *l; if (h -> type != omapi_type_listener) return DHCP_R_INVALIDARG; l = (omapi_listener_object_t *)h; l -> verify_addr = verify_addr; return ISC_R_SUCCESS; } isc_result_t omapi_listener_set_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_typed_data_t *value) { if (h -> type != omapi_type_listener) return DHCP_R_INVALIDARG; if (h -> inner && h -> inner -> type -> set_value) return (*(h -> inner -> type -> set_value)) (h -> inner, id, name, value); return ISC_R_NOTFOUND; } isc_result_t omapi_listener_get_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_value_t **value) { if (h -> type != omapi_type_listener) return DHCP_R_INVALIDARG; if (h -> inner && h -> inner -> type -> get_value) return (*(h -> inner -> type -> get_value)) (h -> inner, id, name, value); return ISC_R_NOTFOUND; } isc_result_t omapi_listener_destroy (omapi_object_t *h, const char *file, int line) { omapi_listener_object_t *l; if (h -> type != omapi_type_listener) return DHCP_R_INVALIDARG; l = (omapi_listener_object_t *)h; #ifdef DEBUG_PROTOCOL log_debug ("omapi_listener_destroy()"); #endif if (l -> socket != -1) { close (l -> socket); l -> socket = -1; } return ISC_R_SUCCESS; } isc_result_t omapi_listener_signal_handler (omapi_object_t *h, const char *name, va_list ap) { if (h -> type != omapi_type_listener) return DHCP_R_INVALIDARG; if (h -> inner && h -> inner -> type -> signal_handler) return (*(h -> inner -> type -> signal_handler)) (h -> inner, name, ap); return ISC_R_NOTFOUND; } /* Write all the published values associated with the object through the specified connection. */ isc_result_t omapi_listener_stuff_values (omapi_object_t *c, omapi_object_t *id, omapi_object_t *l) { if (l -> type != omapi_type_listener) return DHCP_R_INVALIDARG; if (l -> inner && l -> inner -> type -> stuff_values) return (*(l -> inner -> type -> stuff_values)) (c, id, l -> inner); return ISC_R_SUCCESS; } dhcp-4.4.1/omapip/Makefile.am000644 000765 000024 00000001251 13243313027 016235 0ustar00tmarkstaff000000 000000 BINDLIBIRSDIR=@BINDLIBIRSDIR@ BINDLIBDNSDIR=@BINDLIBDNSDIR@ BINDLIBISCCFGDIR=@BINDLIBISCCFGDIR@ BINDLIBISCDIR=@BINDLIBISCDIR@ lib_LIBRARIES = libomapi.a noinst_PROGRAMS = svtest libomapi_a_SOURCES = protocol.c buffer.c alloc.c result.c connection.c \ errwarn.c listener.c dispatch.c generic.c support.c \ handle.c message.c convert.c hash.c auth.c inet_addr.c \ array.c trace.c toisc.c iscprint.c isclib.c man_MANS = omapi.3 EXTRA_DIST = $(man_MANS) svtest_SOURCES = test.c svtest_LDADD = libomapi.a \ $(BINDLIBIRSDIR)/libirs.a \ $(BINDLIBDNSDIR)/libdns.a \ $(BINDLIBISCCFGDIR)/libisccfg.a \ $(BINDLIBISCDIR)/libisc.a dhcp-4.4.1/omapip/Makefile.am.in000644 000765 000024 00000001306 13243301226 016641 0ustar00tmarkstaff000000 000000 BINDLIBIRSDIR=@Q@BINDLIBIRSDIR@Q@ BINDLIBDNSDIR=@Q@BINDLIBDNSDIR@Q@ BINDLIBISCCFGDIR=@Q@BINDLIBISCCFGDIR@Q@ BINDLIBISCDIR=@Q@BINDLIBISCDIR@Q@ lib_@DHLIBS@ = libomapi.@A@ noinst_PROGRAMS = svtest libomapi_@A@_SOURCES = protocol.c buffer.c alloc.c result.c connection.c \ errwarn.c listener.c dispatch.c generic.c support.c \ handle.c message.c convert.c hash.c auth.c inet_addr.c \ array.c trace.c toisc.c iscprint.c isclib.c man_MANS = omapi.3 EXTRA_DIST = $(man_MANS) svtest_SOURCES = test.c svtest_LDADD = libomapi.@A@ \ $(BINDLIBIRSDIR)/libirs.@A@ \ $(BINDLIBDNSDIR)/libdns.@A@ \ $(BINDLIBISCCFGDIR)/libisccfg.@A@ \ $(BINDLIBISCDIR)/libisc.@A@ dhcp-4.4.1/omapip/message.c000644 000765 000024 00000054631 13243301226 016001 0ustar00tmarkstaff000000 000000 /* message.c Subroutines for dealing with message objects. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1999-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #include OMAPI_OBJECT_ALLOC (omapi_message, omapi_message_object_t, omapi_type_message) omapi_message_object_t *omapi_registered_messages; isc_result_t omapi_message_new (omapi_object_t **o, const char *file, int line) { omapi_message_object_t *m; omapi_object_t *g; isc_result_t status; m = (omapi_message_object_t *)0; status = omapi_message_allocate (&m, file, line); if (status != ISC_R_SUCCESS) return status; g = (omapi_object_t *)0; status = omapi_generic_new (&g, file, line); if (status != ISC_R_SUCCESS) { dfree (m, file, line); return status; } status = omapi_object_reference (&m -> inner, g, file, line); if (status != ISC_R_SUCCESS) { omapi_object_dereference ((omapi_object_t **)&m, file, line); omapi_object_dereference (&g, file, line); return status; } status = omapi_object_reference (&g -> outer, (omapi_object_t *)m, file, line); if (status != ISC_R_SUCCESS) { omapi_object_dereference ((omapi_object_t **)&m, file, line); omapi_object_dereference (&g, file, line); return status; } status = omapi_object_reference (o, (omapi_object_t *)m, file, line); omapi_message_dereference (&m, file, line); omapi_object_dereference (&g, file, line); if (status != ISC_R_SUCCESS) return status; return status; } isc_result_t omapi_message_set_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_typed_data_t *value) { omapi_message_object_t *m; isc_result_t status; if (h -> type != omapi_type_message) return DHCP_R_INVALIDARG; m = (omapi_message_object_t *)h; /* Can't set authlen. */ /* Can set authenticator, but the value must be typed data. */ if (!omapi_ds_strcmp (name, "authenticator")) { if (m -> authenticator) omapi_typed_data_dereference (&m -> authenticator, MDL); omapi_typed_data_reference (&m -> authenticator, value, MDL); return ISC_R_SUCCESS; } else if (!omapi_ds_strcmp (name, "object")) { if (value -> type != omapi_datatype_object) return DHCP_R_INVALIDARG; if (m -> object) omapi_object_dereference (&m -> object, MDL); omapi_object_reference (&m -> object, value -> u.object, MDL); return ISC_R_SUCCESS; } else if (!omapi_ds_strcmp (name, "notify-object")) { if (value -> type != omapi_datatype_object) return DHCP_R_INVALIDARG; if (m -> notify_object) omapi_object_dereference (&m -> notify_object, MDL); omapi_object_reference (&m -> notify_object, value -> u.object, MDL); return ISC_R_SUCCESS; /* Can set authid, but it has to be an integer. */ } else if (!omapi_ds_strcmp (name, "authid")) { if (value -> type != omapi_datatype_int) return DHCP_R_INVALIDARG; m -> authid = value -> u.integer; return ISC_R_SUCCESS; /* Can set op, but it has to be an integer. */ } else if (!omapi_ds_strcmp (name, "op")) { if (value -> type != omapi_datatype_int) return DHCP_R_INVALIDARG; m -> op = value -> u.integer; return ISC_R_SUCCESS; /* Handle also has to be an integer. */ } else if (!omapi_ds_strcmp (name, "handle")) { if (value -> type != omapi_datatype_int) return DHCP_R_INVALIDARG; m -> h = value -> u.integer; return ISC_R_SUCCESS; /* Transaction ID has to be an integer. */ } else if (!omapi_ds_strcmp (name, "id")) { if (value -> type != omapi_datatype_int) return DHCP_R_INVALIDARG; m -> id = value -> u.integer; return ISC_R_SUCCESS; /* Remote transaction ID has to be an integer. */ } else if (!omapi_ds_strcmp (name, "rid")) { if (value -> type != omapi_datatype_int) return DHCP_R_INVALIDARG; m -> rid = value -> u.integer; return ISC_R_SUCCESS; } /* Try to find some inner object that can take the value. */ if (h -> inner && h -> inner -> type -> set_value) { status = ((*(h -> inner -> type -> set_value)) (h -> inner, id, name, value)); if (status == ISC_R_SUCCESS) return status; } return ISC_R_NOTFOUND; } isc_result_t omapi_message_get_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_value_t **value) { omapi_message_object_t *m; if (h -> type != omapi_type_message) return DHCP_R_INVALIDARG; m = (omapi_message_object_t *)h; /* Look for values that are in the message data structure. */ if (!omapi_ds_strcmp (name, "authlen")) return omapi_make_int_value (value, name, (int)m -> authlen, MDL); else if (!omapi_ds_strcmp (name, "authenticator")) { if (m -> authenticator) return omapi_make_value (value, name, m -> authenticator, MDL); else return ISC_R_NOTFOUND; } else if (!omapi_ds_strcmp (name, "authid")) { return omapi_make_int_value (value, name, (int)m -> authid, MDL); } else if (!omapi_ds_strcmp (name, "op")) { return omapi_make_int_value (value, name, (int)m -> op, MDL); } else if (!omapi_ds_strcmp (name, "handle")) { return omapi_make_int_value (value, name, (int)m -> h, MDL); } else if (!omapi_ds_strcmp (name, "id")) { return omapi_make_int_value (value, name, (int)m -> id, MDL); } else if (!omapi_ds_strcmp (name, "rid")) { return omapi_make_int_value (value, name, (int)m -> rid, MDL); } /* See if there's an inner object that has the value. */ if (h -> inner && h -> inner -> type -> get_value) return (*(h -> inner -> type -> get_value)) (h -> inner, id, name, value); return ISC_R_NOTFOUND; } isc_result_t omapi_message_destroy (omapi_object_t *h, const char *file, int line) { omapi_message_object_t *m; if (h -> type != omapi_type_message) return DHCP_R_INVALIDARG; m = (omapi_message_object_t *)h; if (m -> authenticator) { omapi_typed_data_dereference (&m -> authenticator, file, line); } if (!m -> prev && omapi_registered_messages != m) omapi_message_unregister (h); if (m -> id_object) omapi_object_dereference (&m -> id_object, file, line); if (m -> object) omapi_object_dereference (&m -> object, file, line); if (m -> notify_object) omapi_object_dereference (&m -> notify_object, file, line); if (m -> protocol_object) omapi_protocol_dereference (&m -> protocol_object, file, line); return ISC_R_SUCCESS; } isc_result_t omapi_message_signal_handler (omapi_object_t *h, const char *name, va_list ap) { omapi_message_object_t *m; if (h -> type != omapi_type_message) return DHCP_R_INVALIDARG; m = (omapi_message_object_t *)h; if (!strcmp (name, "status")) { if (m -> notify_object && m -> notify_object -> type -> signal_handler) return ((m -> notify_object -> type -> signal_handler)) (m -> notify_object, name, ap); else if (m -> object && m -> object -> type -> signal_handler) return ((m -> object -> type -> signal_handler)) (m -> object, name, ap); } if (h -> inner && h -> inner -> type -> signal_handler) return (*(h -> inner -> type -> signal_handler)) (h -> inner, name, ap); return ISC_R_NOTFOUND; } /* Write all the published values associated with the object through the specified connection. */ isc_result_t omapi_message_stuff_values (omapi_object_t *c, omapi_object_t *id, omapi_object_t *m) { if (m -> type != omapi_type_message) return DHCP_R_INVALIDARG; if (m -> inner && m -> inner -> type -> stuff_values) return (*(m -> inner -> type -> stuff_values)) (c, id, m -> inner); return ISC_R_SUCCESS; } isc_result_t omapi_message_register (omapi_object_t *mo) { omapi_message_object_t *m; if (mo -> type != omapi_type_message) return DHCP_R_INVALIDARG; m = (omapi_message_object_t *)mo; /* Already registered? */ if (m -> prev || m -> next || omapi_registered_messages == m) return DHCP_R_INVALIDARG; if (omapi_registered_messages) { omapi_object_reference ((omapi_object_t **)&m -> next, (omapi_object_t *)omapi_registered_messages, MDL); omapi_object_reference ((omapi_object_t **)&omapi_registered_messages -> prev, (omapi_object_t *)m, MDL); omapi_object_dereference ((omapi_object_t **)&omapi_registered_messages, MDL); } omapi_object_reference ((omapi_object_t **)&omapi_registered_messages, (omapi_object_t *)m, MDL); return ISC_R_SUCCESS;; } isc_result_t omapi_message_unregister (omapi_object_t *mo) { omapi_message_object_t *m; omapi_message_object_t *n; if (mo -> type != omapi_type_message) return DHCP_R_INVALIDARG; m = (omapi_message_object_t *)mo; /* Not registered? */ if (!m -> prev && omapi_registered_messages != m) return DHCP_R_INVALIDARG; n = (omapi_message_object_t *)0; if (m -> next) { omapi_object_reference ((omapi_object_t **)&n, (omapi_object_t *)m -> next, MDL); omapi_object_dereference ((omapi_object_t **)&m -> next, MDL); omapi_object_dereference ((omapi_object_t **)&n -> prev, MDL); } if (m -> prev) { omapi_message_object_t *tmp = (omapi_message_object_t *)0; omapi_object_reference ((omapi_object_t **)&tmp, (omapi_object_t *)m -> prev, MDL); omapi_object_dereference ((omapi_object_t **)&m -> prev, MDL); if (tmp -> next) omapi_object_dereference ((omapi_object_t **)&tmp -> next, MDL); if (n) omapi_object_reference ((omapi_object_t **)&tmp -> next, (omapi_object_t *)n, MDL); omapi_object_dereference ((omapi_object_t **)&tmp, MDL); } else { omapi_object_dereference ((omapi_object_t **)&omapi_registered_messages, MDL); if (n) omapi_object_reference ((omapi_object_t **)&omapi_registered_messages, (omapi_object_t *)n, MDL); } if (n) omapi_object_dereference ((omapi_object_t **)&n, MDL); return ISC_R_SUCCESS; } #ifdef DEBUG_PROTOCOL const char *omapi_message_op_name(int op) { switch (op) { case OMAPI_OP_OPEN: return "OMAPI_OP_OPEN"; case OMAPI_OP_REFRESH: return "OMAPI_OP_REFRESH"; case OMAPI_OP_UPDATE: return "OMAPI_OP_UPDATE"; case OMAPI_OP_STATUS: return "OMAPI_OP_STATUS"; case OMAPI_OP_DELETE: return "OMAPI_OP_DELETE"; case OMAPI_OP_NOTIFY: return "OMAPI_OP_NOTIFY"; default: return "(unknown op)"; } } #endif static isc_result_t omapi_message_process_internal (omapi_object_t *, omapi_object_t *); isc_result_t omapi_message_process (omapi_object_t *mo, omapi_object_t *po) { isc_result_t status; #if defined (DEBUG_MEMORY_LEAKAGE) && 0 unsigned long previous_outstanding = dmalloc_outstanding; #endif status = omapi_message_process_internal (mo, po); #if defined (DEBUG_MEMORY_LEAKAGE) && 0 log_info ("generation %ld: %ld new, %ld outstanding, %ld long-term", dmalloc_generation, dmalloc_outstanding - previous_outstanding, dmalloc_outstanding, dmalloc_longterm); #endif #if defined (DEBUG_MEMORY_LEAKAGE) && 0 dmalloc_dump_outstanding (); #endif #if defined (DEBUG_RC_HISTORY_EXHAUSTIVELY) && 0 dump_rc_history (); #endif return status; } static isc_result_t omapi_message_process_internal (omapi_object_t *mo, omapi_object_t *po) { omapi_message_object_t *message, *m; omapi_object_t *object = (omapi_object_t *)0; omapi_value_t *tv = (omapi_value_t *)0; unsigned long create, update, exclusive; unsigned long wsi; isc_result_t status, waitstatus; omapi_object_type_t *type; if (mo -> type != omapi_type_message) return DHCP_R_INVALIDARG; message = (omapi_message_object_t *)mo; #ifdef DEBUG_PROTOCOL log_debug ("omapi_message_process(): " "op=%s handle=%#x id=%#x rid=%#x", omapi_message_op_name (message -> op), message -> h, message -> id, message -> rid); #endif if (message -> rid) { for (m = omapi_registered_messages; m; m = m -> next) if (m -> id == message -> rid) break; /* If we don't have a real message corresponding to the message ID to which this message claims it is a response, something's fishy. */ if (!m) return ISC_R_NOTFOUND; /* The authenticator on responses must match the initial message. */ if (message -> authid != m -> authid) return ISC_R_NOTFOUND; } else { m = (omapi_message_object_t *)0; /* All messages must have an authenticator, with the exception of messages that are opening a new authenticator. */ if (omapi_protocol_authenticated(po) && !message->id_object && message->op != OMAPI_OP_OPEN) { return omapi_protocol_send_status (po, message->id_object, DHCP_R_NOKEYS, message->id, "No authenticator on message"); } } switch (message -> op) { case OMAPI_OP_OPEN: if (m) { return omapi_protocol_send_status (po, message->id_object, DHCP_R_INVALIDARG, message->id, "OPEN can't be a response"); } /* Get the type of the requested object, if one was specified. */ status = omapi_get_value_str (mo, message -> id_object, "type", &tv); if (status == ISC_R_SUCCESS && (tv -> value -> type == omapi_datatype_data || tv -> value -> type == omapi_datatype_string)) { for (type = omapi_object_types; type; type = type -> next) if (!omapi_td_strcmp (tv -> value, type -> name)) break; } else type = (omapi_object_type_t *)0; if (tv) omapi_value_dereference (&tv, MDL); /* If this object had no authenticator, the requested object must be an authenticator object. */ if (omapi_protocol_authenticated(po) && !message->id_object && type != omapi_type_auth_key) { return omapi_protocol_send_status (po, message->id_object, DHCP_R_NOKEYS, message->id, "No authenticator on message"); } /* Get the create flag. */ status = omapi_get_value_str (mo, message -> id_object, "create", &tv); if (status == ISC_R_SUCCESS) { status = omapi_get_int_value (&create, tv -> value); omapi_value_dereference (&tv, MDL); if (status != ISC_R_SUCCESS) { return omapi_protocol_send_status (po, message -> id_object, status, message -> id, "invalid create flag value"); } } else create = 0; /* Get the update flag. */ status = omapi_get_value_str (mo, message -> id_object, "update", &tv); if (status == ISC_R_SUCCESS) { status = omapi_get_int_value (&update, tv -> value); omapi_value_dereference (&tv, MDL); if (status != ISC_R_SUCCESS) { return omapi_protocol_send_status (po, message -> id_object, status, message -> id, "invalid update flag value"); } } else update = 0; /* Get the exclusive flag. */ status = omapi_get_value_str (mo, message -> id_object, "exclusive", &tv); if (status == ISC_R_SUCCESS) { status = omapi_get_int_value (&exclusive, tv -> value); omapi_value_dereference (&tv, MDL); if (status != ISC_R_SUCCESS) { return omapi_protocol_send_status (po, message -> id_object, status, message -> id, "invalid exclusive flag value"); } } else exclusive = 0; /* If we weren't given a type, look the object up with the handle. */ if (!type) { if (create) { return omapi_protocol_send_status (po, message->id_object, DHCP_R_INVALIDARG, message->id, "type required on create"); } goto refresh; } /* If the type doesn't provide a lookup method, we can't look up the object. */ if (!type -> lookup) { return omapi_protocol_send_status (po, message -> id_object, ISC_R_NOTIMPLEMENTED, message -> id, "unsearchable object type"); } status = (*(type -> lookup)) (&object, message -> id_object, message -> object); if (status != ISC_R_SUCCESS && status != ISC_R_NOTFOUND && status != DHCP_R_NOKEYS) { return omapi_protocol_send_status (po, message -> id_object, status, message -> id, "object lookup failed"); } /* If we didn't find the object and we aren't supposed to create it, return an error. */ if (status == ISC_R_NOTFOUND && !create) { return omapi_protocol_send_status (po, message -> id_object, ISC_R_NOTFOUND, message -> id, "no object matches specification"); } /* If we found an object, we're supposed to be creating an object, and we're not supposed to have found an object, return an error. */ if (status == ISC_R_SUCCESS && create && exclusive) { omapi_object_dereference (&object, MDL); return omapi_protocol_send_status (po, message -> id_object, ISC_R_EXISTS, message -> id, "specified object already exists"); } /* If we're creating the object, do it now. */ if (!object) { status = omapi_object_create (&object, message -> id_object, type); if (status != ISC_R_SUCCESS) { return omapi_protocol_send_status (po, message -> id_object, status, message -> id, "can't create new object"); } } /* If we're updating it, do so now. */ if (create || update) { /* This check does not belong here. */ if (object -> type == omapi_type_auth_key) { omapi_object_dereference (&object, MDL); return omapi_protocol_send_status (po, message -> id_object, status, message -> id, "can't update object"); } status = omapi_object_update (object, message -> id_object, message -> object, message -> h); if (status != ISC_R_SUCCESS) { omapi_object_dereference (&object, MDL); return omapi_protocol_send_status (po, message -> id_object, status, message -> id, "can't update object"); } } /* If this is an authenticator object, add it to the active set for the connection. */ if (object -> type == omapi_type_auth_key) { omapi_handle_t handle; status = omapi_object_handle (&handle, object); if (status != ISC_R_SUCCESS) { omapi_object_dereference (&object, MDL); return omapi_protocol_send_status (po, message -> id_object, status, message -> id, "can't select authenticator"); } status = omapi_protocol_add_auth (po, object, handle); if (status != ISC_R_SUCCESS) { omapi_object_dereference (&object, MDL); return omapi_protocol_send_status (po, message -> id_object, status, message -> id, "can't select authenticator"); } } /* Now send the new contents of the object back in response. */ goto send; case OMAPI_OP_REFRESH: refresh: status = omapi_handle_lookup (&object, message -> h); if (status != ISC_R_SUCCESS) { return omapi_protocol_send_status (po, message -> id_object, status, message -> id, "no matching handle"); } send: status = omapi_protocol_send_update (po, message -> id_object, message -> id, object); omapi_object_dereference (&object, MDL); return status; case OMAPI_OP_UPDATE: if (m && m -> object) { status = omapi_object_reference (&object, m -> object, MDL); } else { status = omapi_handle_lookup (&object, message -> h); if (status != ISC_R_SUCCESS) { return omapi_protocol_send_status (po, message -> id_object, status, message -> id, "no matching handle"); } } if (object -> type == omapi_type_auth_key || (object -> inner && object -> inner -> type == omapi_type_auth_key)) { if (!m) { omapi_object_dereference (&object, MDL); return omapi_protocol_send_status (po, message -> id_object, status, message -> id, "cannot update authenticator"); } status = omapi_protocol_add_auth (po, object, message -> h); } else { status = omapi_object_update (object, message -> id_object, message -> object, message -> h); } if (status != ISC_R_SUCCESS) { omapi_object_dereference (&object, MDL); if (!message -> rid) return omapi_protocol_send_status (po, message -> id_object, status, message -> id, "can't update object"); if (m) omapi_signal ((omapi_object_t *)m, "status", status, (omapi_typed_data_t *)0); return ISC_R_SUCCESS; } if (!message -> rid) status = omapi_protocol_send_status (po, message -> id_object, ISC_R_SUCCESS, message -> id, (char *)0); if (m) { omapi_signal ((omapi_object_t *)m, "status", ISC_R_SUCCESS, (omapi_typed_data_t *)0); omapi_message_unregister ((omapi_object_t *)m); } omapi_object_dereference (&object, MDL); return status; case OMAPI_OP_NOTIFY: return omapi_protocol_send_status (po, message -> id_object, ISC_R_NOTIMPLEMENTED, message -> id, "notify not implemented yet"); case OMAPI_OP_STATUS: /* The return status of a request. */ if (!m) return ISC_R_UNEXPECTED; /* Get the wait status. */ status = omapi_get_value_str (mo, message -> id_object, "result", &tv); if (status == ISC_R_SUCCESS) { status = omapi_get_int_value (&wsi, tv -> value); waitstatus = wsi; omapi_value_dereference (&tv, MDL); if (status != ISC_R_SUCCESS) waitstatus = ISC_R_UNEXPECTED; } else waitstatus = ISC_R_UNEXPECTED; status = omapi_get_value_str (mo, message -> id_object, "message", &tv); omapi_signal ((omapi_object_t *)m, "status", waitstatus, tv); if (status == ISC_R_SUCCESS) omapi_value_dereference (&tv, MDL); omapi_message_unregister((omapi_object_t *)m); return ISC_R_SUCCESS; case OMAPI_OP_DELETE: status = omapi_handle_lookup (&object, message -> h); if (status != ISC_R_SUCCESS) { return omapi_protocol_send_status (po, message -> id_object, status, message -> id, "no matching handle"); } if (!object -> type -> remove) return omapi_protocol_send_status (po, message -> id_object, ISC_R_NOTIMPLEMENTED, message -> id, "no remove method for object"); status = (*(object -> type -> remove)) (object, message -> id_object); omapi_object_dereference (&object, MDL); return omapi_protocol_send_status (po, message -> id_object, status, message -> id, (char *)0); } return ISC_R_NOTIMPLEMENTED; } dhcp-4.4.1/omapip/omapi.3000644 000765 000024 00000017734 13243301226 015405 0ustar00tmarkstaff000000 000000 .\" omapi.3 .\" .\" Copyright (c) 2009-2010,2014 by Internet Systems Consortium, Inc. ("ISC") .\" Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") .\" Copyright (c) 2000-2003 by Internet Software Consortium .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT .\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" Internet Systems Consortium, Inc. .\" 950 Charter Street .\" Redwood City, CA 94063 .\" .\" https://www.isc.org/ .\" .\" This software has been written for Internet Systems Consortium .\" by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. .\" .\" Support and other services are available for ISC products - see .\" https://www.isc.org for more information or to learn more about ISC. .\" .TH omapi 3 .SH NAME OMAPI - Object Management Application Programming Interface .SH DESCRIPTION .PP OMAPI is an programming layer designed for controlling remote applications, and for querying them for their state. It is currently used by the ISC DHCP server and this outline addresses the parts of OMAPI appropriate to the clients of DHCP server. It does this by also describing the use of a thin API layered on top of OMAPI called \'dhcpctl\' .PP OMAPI uses TCP/IP as the transport for server communication, and security can be imposed by having the client and server cryptographically sign messages using a shared secret. .PP dhcpctl works by presenting the client with handles to objects that act as surrogates for the real objects in the server. For example a client will create a handle for a lease object, and will request the server to fill the lease handle's state. The client application can then pull details such as the lease expiration time from the lease handle. .PP Modifications can be made to the server state by creating handles to new objects, or by modifying attributes of handles to existing objects, and then instructing the server to update itself according to the changes made. .SH USAGE .PP The client application must always call dhcpctl_initialize() before making calls to any other dhcpctl functions. This initializes various internal data structures. .PP To create the connection to the server the client must use dhcpctl_connect() function. As well as making the physical connection it will also set up the connection data structures to do authentication on each message, if that is required. .PP All the dhcpctl functions return an integer value of type isc_result_t. A successful call will yield a result of ISC_R_SUCCESS. If the call fails for a reason local to the client (e.g. insufficient local memory, or invalid arguments to the call) then the return value of the dhcpctl function will show that. If the call succeeds but the server couldn't process the request the error value from the server is returned through another way, shown below. .PP The easiest way to understand dhcpctl is to see it in action. The following program is fully functional, but almost all error checking has been removed to make is shorter and easier to understand. This program will query the server running on the localhost for the details of the lease for IP address 10.0.0.101. It will then print out the time the lease ends. .PP .nf #include #include #include #include #include #include #include int main (int argc, char **argv) { dhcpctl_data_string ipaddrstring = NULL; dhcpctl_data_string value = NULL; .fi .PP All modifications of handles and all accesses of handle data happen via dhcpctl_data_string objects. .PP .nf dhcpctl_handle connection = NULL; dhcpctl_handle lease = NULL; isc_result_t waitstatus; struct in_addr convaddr; time_t thetime; dhcpctl_initialize (); .fi .PP Required first step. .PP .nf dhcpctl_connect (&connection, "127.0.0.1", 7911, 0); .fi .PP Sets up the connection to the server. The server normally listens on port 7911 unless configured to do otherwise. .PP .nf dhcpctl_new_object (&lease, connection, "lease"); .fi .PP Here we create a handle to a lease. This call just sets up local data structure. The server hasn't yet made any association between the client's data structure and any lease it has. .PP .nf memset (&ipaddrstring, 0, sizeof ipaddrstring); inet_pton(AF_INET, "10.0.0.101", &convaddr); omapi_data_string_new (&ipaddrstring, 4, MDL); .fi .PP Create a new data string to storing in the handle. .PP .nf memcpy(ipaddrstring->value, &convaddr.s_addr, 4); dhcpctl_set_value (lease, ipaddrstring, "ip-address"); .fi .PP We're setting the ip-address attribute of the lease handle to the given address. We've not set any other attributes so when the server makes the association the ip address will be all it uses to look up the lease in its tables. .PP .nf dhcpctl_open_object (lease, connection, 0); .fi .PP Here we prime the connection with the request to look up the lease in the server and fill up the local handle with the attributes the server will send over in its answer. .PP .nf dhcpctl_wait_for_completion (lease, &waitstatus); .fi .PP This call causes the message to get sent to the server (the message to look up the lease and send back the attribute values in the answer). The value in the variable waitstatus when the function returns will be the result from the server. If the message could not be processed properly by the server then the error will be reflected here. .PP .nf if (waitstatus != ISC_R_SUCCESS) { /* server not authoritative */ exit (0); } dhcpctl_data_string_dereference(&ipaddrstring, MDL); .fi .PP Clean-up memory we no longer need. .PP .nf dhcpctl_get_value (&value, lease, "ends"); .fi .PP Get the attribute named ``ends'' from the lease handle. This is a 4-byte integer of the time (in unix epoch seconds) that the lease will expire. .PP .nf memcpy(&thetime, value->value, value->len); dhcpctl_data_string_dereference(&value, MDL); fprintf (stdout, "ending time is %s", ctime(&thetime)); } .fi .SH AUTHENTICATION If the server demands authenticated connections then before opening the connection the user must call dhcpctl_new_authenticator. .PP .nf dhcpctl_handle authenticator = NULL; const char *keyname = "a-key-name"; const char *algorithm = "hmac-md5"; const char *secret = "a-shared-secret"; dhcpctl_new_authenticator (&authenticator, keyname, algorithm, secret, strlen(secret) + 1); .fi .PP The keyname, algorithm and must all match what is specified in the server's dhcpd.conf file, excepting that the secret should appear in \'raw\' form, not in base64 as it would in dhcpd.conf: .PP .nf key "a-key-name" { algorithm hmac-md5; secret "a-shared-secret"; }; # Set the omapi-key value to use # authenticated connections omapi-key a-key-name; .fi .PP The authenticator handle that is created by the call to dhcpctl_new_authenticator must be given as the last (the 4th) argument to the call to dhcpctl_connect(). All messages will then be signed with the given secret string using the specified algorithm. .SH SEE ALSO dhcpctl(3), omshell(1), dhcpd(8), dhclient(8), dhcpd.conf(5), dhclient.conf(5). .SH AUTHOR .B omapi is maintained by ISC. To learn more about Internet Systems Consortium, see .B https://www.isc.org dhcp-4.4.1/omapip/protocol.c000644 000765 000024 00000110233 13243301226 016205 0ustar00tmarkstaff000000 000000 /* protocol.c Functions supporting the object management protocol... */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1999-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #include OMAPI_OBJECT_ALLOC (omapi_protocol, omapi_protocol_object_t, omapi_type_protocol) OMAPI_OBJECT_ALLOC (omapi_protocol_listener, omapi_protocol_listener_object_t, omapi_type_protocol_listener) isc_result_t omapi_protocol_connect (omapi_object_t *h, const char *server_name, unsigned port, omapi_object_t *a) { isc_result_t rstatus, status; omapi_protocol_object_t *obj; #ifdef DEBUG_PROTOCOL log_debug ("omapi_protocol_connect(%s port=%d)", server_name, port); #endif obj = (omapi_protocol_object_t *)0; status = omapi_protocol_allocate (&obj, MDL); if (status != ISC_R_SUCCESS) return status; rstatus = omapi_connect ((omapi_object_t *)obj, server_name, port); if (rstatus != ISC_R_SUCCESS && rstatus != DHCP_R_INCOMPLETE) { omapi_protocol_dereference (&obj, MDL); return rstatus; } status = omapi_object_reference (&h -> outer, (omapi_object_t *)obj, MDL); if (status != ISC_R_SUCCESS) { omapi_protocol_dereference (&obj, MDL); return status; } status = omapi_object_reference (&obj -> inner, h, MDL); if (status != ISC_R_SUCCESS) { omapi_protocol_dereference (&obj, MDL); return status; } /* If we were passed a default authenticator, store it now. We'll open it once we're connected. */ if (a) { obj -> default_auth = dmalloc (sizeof(omapi_remote_auth_t), MDL); if (!obj -> default_auth) { omapi_protocol_dereference (&obj, MDL); return ISC_R_NOMEMORY; } obj -> default_auth -> next = (omapi_remote_auth_t *)0; status = omapi_object_reference (&obj -> default_auth -> a, a, MDL); if (status != ISC_R_SUCCESS) { dfree (obj -> default_auth, MDL); omapi_protocol_dereference (&obj, MDL); return status; } obj -> insecure = 0; rstatus = DHCP_R_INCOMPLETE; } else { obj -> insecure = 1; #if 0 status = ISC_R_SUCCESS; #endif } omapi_protocol_dereference (&obj, MDL); return rstatus; } /* Send the protocol introduction message. */ isc_result_t omapi_protocol_send_intro (omapi_object_t *h, unsigned ver, unsigned hsize) { isc_result_t status; omapi_protocol_object_t *p; #ifdef DEBUG_PROTOCOL log_debug ("omapi_protocol_send_intro()"); #endif if (h -> type != omapi_type_protocol) return DHCP_R_INVALIDARG; p = (omapi_protocol_object_t *)h; if (!h -> outer || h -> outer -> type != omapi_type_connection) return ISC_R_NOTCONNECTED; status = omapi_connection_put_uint32 (h -> outer, ver); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_uint32 (h -> outer, hsize); if (status != ISC_R_SUCCESS) return status; /* Require the other end to send an intro - this kicks off the protocol input state machine. */ p -> state = omapi_protocol_intro_wait; status = omapi_connection_require (h -> outer, 8); if (status != ISC_R_SUCCESS && status != DHCP_R_NOTYET) return status; /* Make up an initial transaction ID for this connection. */ p -> next_xid = random (); return ISC_R_SUCCESS; } #ifdef DEBUG_PROTOCOL extern const char *omapi_message_op_name(int); #endif /* DEBUG_PROTOCOL */ isc_result_t omapi_protocol_send_message (omapi_object_t *po, omapi_object_t *id, omapi_object_t *mo, omapi_object_t *omo) { omapi_protocol_object_t *p; omapi_object_t *c; omapi_message_object_t *m, *om; omapi_remote_auth_t *ra; omapi_value_t *signature; isc_result_t status; unsigned auth_len; if (po -> type != omapi_type_protocol || !po -> outer || po -> outer -> type != omapi_type_connection || mo -> type != omapi_type_message) return DHCP_R_INVALIDARG; if (omo && omo -> type != omapi_type_message) return DHCP_R_INVALIDARG; p = (omapi_protocol_object_t *)po; c = (omapi_object_t *)(po -> outer); m = (omapi_message_object_t *)mo; om = (omapi_message_object_t *)omo; #ifdef DEBUG_PROTOCOL log_debug ("omapi_protocol_send_message(): " "op=%s handle=%#lx id=%#lx rid=%#lx", omapi_message_op_name (m->op), (long)(m -> object ? m -> object -> handle : m -> handle), (long)p -> next_xid, (long)m -> rid); #endif /* Find the authid to use for this message. */ if (id) { for (ra = p -> remote_auth_list; ra; ra = ra -> next) { if (ra -> a == id) { break; } } if (!ra) return DHCP_R_KEY_UNKNOWN; } else if (p -> remote_auth_list) { ra = p -> default_auth; } else { ra = (omapi_remote_auth_t *)0; } if (ra) { m -> authid = ra -> remote_handle; status = omapi_object_reference (&m -> id_object, ra -> a, MDL); if (status != ISC_R_SUCCESS) return status; } /* Write the ID of the authentication key we're using. */ status = omapi_connection_put_uint32 (c, ra ? ra -> remote_handle : 0); if (status != ISC_R_SUCCESS) { omapi_disconnect (c, 1); return status; } /* Activate the authentication key on the connection. */ auth_len = 0; if (ra) { status = omapi_set_object_value (c, (omapi_object_t *)0, "output-authenticator", ra -> a); if (status != ISC_R_SUCCESS) { omapi_disconnect (c, 1); return status; } status = omapi_connection_output_auth_length (c, &auth_len); if (status != ISC_R_SUCCESS) { omapi_disconnect (c, 1); return status; } } /* Write the authenticator length */ status = omapi_connection_put_uint32 (c, auth_len); if (status != ISC_R_SUCCESS) { omapi_disconnect (c, 1); return status; } /* Write the opcode. */ status = omapi_connection_put_uint32 (c, m -> op); if (status != ISC_R_SUCCESS) { omapi_disconnect (c, 1); return status; } /* Write the handle. If we've been given an explicit handle, use that. Otherwise, use the handle of the object we're sending. The caller is responsible for arranging for one of these handles to be set (or not). */ status = omapi_connection_put_uint32 (c, (m -> h ? m -> h : (m -> object ? m -> object -> handle : 0))); if (status != ISC_R_SUCCESS) { omapi_disconnect (c, 1); return status; } /* Set and write the transaction ID. */ m -> id = p -> next_xid++; status = omapi_connection_put_uint32 (c, m -> id); if (status != ISC_R_SUCCESS) { omapi_disconnect (c, 1); return status; } /* Write the transaction ID of the message to which this is a response, if there is such a message. */ status = omapi_connection_put_uint32 (c, om ? om -> id : m -> rid); if (status != ISC_R_SUCCESS) { omapi_disconnect (c, 1); return status; } /* Stuff out the name/value pairs specific to this message. */ status = omapi_stuff_values (c, id, (omapi_object_t *)m); if (status != ISC_R_SUCCESS) { omapi_disconnect (c, 1); return status; } /* Write the zero-length name that terminates the list of name/value pairs specific to the message. */ status = omapi_connection_put_uint16 (c, 0); if (status != ISC_R_SUCCESS) { omapi_disconnect (c, 1); return status; } /* Stuff out all the published name/value pairs in the object that's being sent in the message, if there is one. */ if (m -> object) { status = omapi_stuff_values (c, id, m -> object); if (status != ISC_R_SUCCESS) { omapi_disconnect (c, 1); return status; } } /* Write the zero-length name that terminates the list of name/value pairs for the associated object. */ status = omapi_connection_put_uint16 (c, 0); if (status != ISC_R_SUCCESS) { omapi_disconnect (c, 1); return status; } if (ra) { /* Calculate the message signature. */ signature = (omapi_value_t *)0; status = omapi_get_value_str (c, (omapi_object_t *)0, "output-signature", &signature); if (status != ISC_R_SUCCESS) { omapi_disconnect (c, 1); return status; } /* Write the authenticator... */ status = (omapi_connection_copyin (c, signature -> value -> u.buffer.value, signature -> value -> u.buffer.len)); omapi_value_dereference (&signature, MDL); if (status != ISC_R_SUCCESS) { omapi_disconnect (c, 1); return status; } /* Dectivate the authentication key on the connection. */ status = omapi_set_value_str (c, (omapi_object_t *)0, "output-authenticator", (omapi_typed_data_t *)0); if (status != ISC_R_SUCCESS) { omapi_disconnect (c, 1); return status; } } if (!omo) { omapi_protocol_reference (&m -> protocol_object, p, MDL); } return ISC_R_SUCCESS; } isc_result_t omapi_protocol_signal_handler (omapi_object_t *h, const char *name, va_list ap) { isc_result_t status; omapi_protocol_object_t *p; omapi_object_t *c; omapi_message_object_t *m; omapi_value_t *signature = NULL; u_int16_t nlen; u_int32_t vlen; u_int32_t th; #if defined (DEBUG_MEMORY_LEAKAGE) unsigned long previous_outstanding = 0xDEADBEEF; unsigned long connect_outstanding = 0xDEADBEEF; #endif if (h -> type != omapi_type_protocol) { /* XXX shouldn't happen. Put an assert here? */ return ISC_R_UNEXPECTED; } p = (omapi_protocol_object_t *)h; if (!strcmp (name, "connect")) { #if defined (DEBUG_MEMORY_LEAKAGE) connect_outstanding = dmalloc_outstanding; #endif /* Send the introductory message. */ status = omapi_protocol_send_intro (h, OMAPI_PROTOCOL_VERSION, sizeof (omapi_protocol_header_t)); if (status != ISC_R_SUCCESS) { omapi_disconnect (p -> outer, 1); return status; } return ISC_R_SUCCESS; } /* Should only receive these when opening the initial authenticator. */ if (!strcmp (name, "status")) { status = va_arg (ap, isc_result_t); if (status != ISC_R_SUCCESS) { omapi_signal_in (h -> inner, "status", status, (omapi_object_t *)0); omapi_disconnect (p -> outer, 1); return status; } else { return omapi_signal_in (h -> inner, "ready"); } } /* If we get a disconnect, dump memory usage. */ if (!strcmp (name, "disconnect")) { #if defined (DEBUG_MEMORY_LEAKAGE) if (connect_outstanding != 0xDEADBEEF) { log_info ("generation %ld: %ld new, %ld outstanding, %ld%s", dmalloc_generation, dmalloc_outstanding - previous_outstanding, dmalloc_outstanding, dmalloc_longterm, " long-term"); } #endif #if defined (DEBUG_MEMORY_LEAKAGE) dmalloc_dump_outstanding (); #endif #if defined (DEBUG_RC_HISTORY_EXHAUSTIVELY) dump_rc_history (h); #endif for (m = omapi_registered_messages; m; m = m -> next) { if (m -> protocol_object == p) { if (m -> object) omapi_signal (m -> object, "disconnect"); } } /* XXX */ return ISC_R_SUCCESS; } /* Not a signal we recognize? */ if (strcmp (name, "ready")) { if (p -> inner && p -> inner -> type -> signal_handler) return (*(p -> inner -> type -> signal_handler)) (h, name, ap); return ISC_R_NOTFOUND; } if (!p -> outer || p -> outer -> type != omapi_type_connection) return DHCP_R_INVALIDARG; c = p -> outer; /* We get here because we requested that we be woken up after some number of bytes were read, and that number of bytes has in fact been read. */ switch (p -> state) { case omapi_protocol_intro_wait: /* Get protocol version and header size in network byte order. */ omapi_connection_get_uint32 (c, &p -> protocol_version); omapi_connection_get_uint32 (c, &p -> header_size); /* We currently only support the current protocol version. */ if (p -> protocol_version != OMAPI_PROTOCOL_VERSION) { omapi_disconnect (c, 1); return DHCP_R_VERSIONMISMATCH; } if (p -> header_size < sizeof (omapi_protocol_header_t)) { omapi_disconnect (c, 1); return DHCP_R_PROTOCOLERROR; } if (p -> default_auth) { status = omapi_protocol_send_open (h, (omapi_object_t *)0, "authenticator", p -> default_auth -> a, OMAPI_NOTIFY_PROTOCOL); if (status != ISC_R_SUCCESS) { omapi_disconnect (c, 1); return status; } } else { status = omapi_signal_in (h -> inner, "ready"); } to_header_wait: /* The next thing we're expecting is a message header. */ p -> state = omapi_protocol_header_wait; /* Register a need for the number of bytes in a header, and if we already have that many, process them immediately. */ if ((omapi_connection_require (c, p -> header_size)) != ISC_R_SUCCESS) break; /* If we already have the data, fall through. */ case omapi_protocol_header_wait: #if defined (DEBUG_MEMORY_LEAKAGE) if (previous_outstanding != 0xDEADBEEF) { log_info ("%s %ld: %ld new, %ld outstanding, %ld%s", "generation", dmalloc_generation, dmalloc_outstanding - previous_outstanding, dmalloc_outstanding, dmalloc_longterm, " long-term"); #endif #if (defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL)) dmalloc_dump_outstanding (); #endif #if defined (DEBUG_RC_HISTORY_EXHAUSTIVELY) dump_rc_history (h); #endif #if defined (DEBUG_MEMORY_LEAKAGE) } previous_outstanding = dmalloc_outstanding; #endif status = omapi_message_new ((omapi_object_t **)&p -> message, MDL); if (status != ISC_R_SUCCESS) { omapi_disconnect (c, 1); return status; } p -> verify_result = ISC_R_SUCCESS; /* Swap in the header... */ omapi_connection_get_uint32 (c, &p -> message -> authid); /* Bind the authenticator to the message object. */ if (p -> message -> authid) { status = (omapi_protocol_lookup_auth (&p -> message -> id_object, h, p -> message -> authid)); if (status != ISC_R_SUCCESS) p -> verify_result = status; /* Activate the authentication key. */ status = omapi_set_object_value (c, (omapi_object_t *)0, "input-authenticator", p -> message -> id_object); if (status != ISC_R_SUCCESS) { omapi_disconnect (c, 1); return status; } } omapi_connection_get_uint32 (c, &p -> message -> authlen); omapi_connection_get_uint32 (c, &p -> message -> op); omapi_connection_get_uint32 (c, &th); p -> message -> h = th; omapi_connection_get_uint32 (c, &p -> message -> id); omapi_connection_get_uint32 (c, &p -> message -> rid); /* If there was any extra header data, skip over it. */ if (p -> header_size > sizeof (omapi_protocol_header_t)) { omapi_connection_copyout (0, c, (p -> header_size - sizeof (omapi_protocol_header_t))); } /* XXX must compute partial signature across the XXX preceding bytes. Also, if authenticator specifies encryption as well as signing, we may have to decrypt the data on the way in. */ /* First we read in message-specific values, then object values. */ p -> reading_message_values = 1; need_name_length: /* The next thing we're expecting is length of the first name. */ p -> state = omapi_protocol_name_length_wait; /* Wait for a 16-bit length. */ if ((omapi_connection_require (c, 2)) != ISC_R_SUCCESS) break; /* If it's already here, fall through. */ case omapi_protocol_name_length_wait: omapi_connection_get_uint16 (c, &nlen); /* A zero-length name means that we're done reading name+value pairs. */ if (nlen == 0) { /* If we've already read in the object, we are done reading the message, but if we've just finished reading in the values associated with the message, we need to read the object. */ if (p -> reading_message_values) { p -> reading_message_values = 0; goto need_name_length; } /* If the authenticator length is zero, there's no signature to read in, so go straight to processing the message. */ if (p -> message -> authlen == 0) goto message_done; /* The next thing we're expecting is the message signature. */ p -> state = omapi_protocol_signature_wait; /* Wait for the number of bytes specified for the authenticator. If we already have it, go read it in. */ if (omapi_connection_require (c, p -> message -> authlen) == ISC_R_SUCCESS) goto signature_wait; break; } /* Allocate a buffer for the name. */ status = (omapi_data_string_new (&p -> name, nlen, MDL)); if (status != ISC_R_SUCCESS) { omapi_disconnect (c, 1); return ISC_R_NOMEMORY; } p -> state = omapi_protocol_name_wait; if (omapi_connection_require (c, nlen) != ISC_R_SUCCESS) break; /* If it's already here, fall through. */ case omapi_protocol_name_wait: omapi_connection_copyout (p -> name -> value, c, p -> name -> len); /* Wait for a 32-bit length. */ p -> state = omapi_protocol_value_length_wait; if ((omapi_connection_require (c, 4)) != ISC_R_SUCCESS) break; /* If it's already here, fall through. */ case omapi_protocol_value_length_wait: omapi_connection_get_uint32 (c, &vlen); /* Zero-length values are allowed - if we get one, we don't have to read any data for the value - just get the next one, if there is a next one. */ if (!vlen) goto insert_new_value; status = omapi_typed_data_new (MDL, &p -> value, omapi_datatype_data, vlen); if (status != ISC_R_SUCCESS) { omapi_disconnect (c, 1); return ISC_R_NOMEMORY; } p -> state = omapi_protocol_value_wait; if (omapi_connection_require (c, vlen) != ISC_R_SUCCESS) break; /* If it's already here, fall through. */ case omapi_protocol_value_wait: omapi_connection_copyout (p -> value -> u.buffer.value, c, p -> value -> u.buffer.len); insert_new_value: if (p -> reading_message_values) { status = (omapi_set_value ((omapi_object_t *)p -> message, p -> message -> id_object, p -> name, p -> value)); } else { if (!p -> message -> object) { /* We need a generic object to hang off of the incoming message. */ status = (omapi_generic_new (&p -> message -> object, MDL)); if (status != ISC_R_SUCCESS) { omapi_disconnect (c, 1); return status; } } status = (omapi_set_value ((omapi_object_t *)p -> message -> object, p -> message -> id_object, p -> name, p -> value)); } if (status != ISC_R_SUCCESS) { omapi_disconnect (c, 1); return status; } omapi_data_string_dereference (&p -> name, MDL); if (p -> value) omapi_typed_data_dereference (&p -> value, MDL); goto need_name_length; signature_wait: case omapi_protocol_signature_wait: if (p -> message -> id_object) { /* Compute the signature of the message. */ status = omapi_get_value_str (c, (omapi_object_t *)0, "input-signature", &signature); if (status != ISC_R_SUCCESS) { omapi_disconnect (c, 1); return status; } /* Disable the authentication key on the connection. */ status = omapi_set_value_str (c, (omapi_object_t *)0, "input-authenticator", (omapi_typed_data_t *)0); if (status != ISC_R_SUCCESS) { omapi_value_dereference (&signature, MDL); omapi_disconnect (c, 1); return status; } } /* Read the authenticator. */ status = omapi_typed_data_new (MDL, &p -> message -> authenticator, omapi_datatype_data, p -> message -> authlen); if (status != ISC_R_SUCCESS) { if (signature != NULL) { omapi_value_dereference (&signature, MDL); } omapi_disconnect (c, 1); return ISC_R_NOMEMORY; } omapi_connection_copyout (p -> message -> authenticator -> u.buffer.value, c, p -> message -> authlen); /* Verify the signature. */ if (p -> message -> id_object && ((signature -> value -> u.buffer.len != p -> message -> authlen) || (memcmp (signature -> value -> u.buffer.value, p -> message -> authenticator -> u.buffer.value, p -> message -> authlen) != 0))) { /* Invalid signature. */ p->verify_result = DHCP_R_INVALIDKEY; } if (signature != NULL) { omapi_value_dereference (&signature, MDL); } /* Process the message. */ message_done: if (p -> verify_result != ISC_R_SUCCESS) { status = omapi_protocol_send_status (h, (omapi_object_t *)0, p -> verify_result, p -> message -> id, (char *)0); } else { status = omapi_message_process ((omapi_object_t *)p -> message, h); } if (status != ISC_R_SUCCESS) { omapi_disconnect (c, 1); return ISC_R_NOMEMORY; } omapi_message_dereference (&p -> message, MDL); #if defined (DEBUG_MEMORY_LEAKAGE) log_info ("generation %ld: %ld new, %ld outstanding, %ld%s", dmalloc_generation, dmalloc_outstanding - previous_outstanding, dmalloc_outstanding, dmalloc_longterm, " long-term"); #endif #if (defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL)) dmalloc_dump_outstanding (); #endif #if defined (DEBUG_RC_HISTORY_EXHAUSTIVELY) dump_rc_history (h); #endif #if defined (DEBUG_MEMORY_LEAKAGE) previous_outstanding = 0xDEADBEEF; #endif /* Now wait for the next message. */ goto to_header_wait; default: /* XXX should never get here. Assertion? */ break; } return ISC_R_SUCCESS; } isc_result_t omapi_protocol_add_auth (omapi_object_t *po, omapi_object_t *ao, omapi_handle_t handle) { omapi_protocol_object_t *p; omapi_remote_auth_t *r; isc_result_t status; if (ao -> type != omapi_type_auth_key && (!ao -> inner || ao -> inner -> type != omapi_type_auth_key)) return DHCP_R_INVALIDARG; if (po -> type != omapi_type_protocol) return DHCP_R_INVALIDARG; p = (omapi_protocol_object_t *)po; #ifdef DEBUG_PROTOCOL log_debug ("omapi_protocol_add_auth(name=%s)", ((omapi_auth_key_t *)ao) -> name); #endif if (p -> verify_auth) { status = (p -> verify_auth) (po, (omapi_auth_key_t *)ao); if (status != ISC_R_SUCCESS) return status; } /* If omapi_protocol_connect() was called with a default authenticator, p -> default_auth will already be set, but p -> remote_auth_list will not yet be initialized. */ if (p -> default_auth && !p -> remote_auth_list) { if (p -> default_auth -> a != ao) { /* Something just went horribly wrong. */ omapi_disconnect (p -> outer, 1); return ISC_R_UNEXPECTED; } p -> remote_auth_list = p -> default_auth; p -> default_auth -> remote_handle = handle; return omapi_signal_in (p -> inner, "ready"); } r = dmalloc (sizeof(*r), MDL); if (!r) return ISC_R_NOMEMORY; status = omapi_object_reference (&r -> a, ao, MDL); if (status != ISC_R_SUCCESS) { dfree (r, MDL); return status; } r -> remote_handle = handle; r -> next = p -> remote_auth_list; p -> remote_auth_list = r; return ISC_R_SUCCESS; } isc_result_t omapi_protocol_lookup_auth (omapi_object_t **a, omapi_object_t *po, omapi_handle_t handle) { omapi_protocol_object_t *p; omapi_remote_auth_t *r; if (po -> type != omapi_type_protocol) return DHCP_R_INVALIDARG; p = (omapi_protocol_object_t *)po; for (r = p -> remote_auth_list; r; r = r -> next) if (r -> remote_handle == handle) return omapi_object_reference (a, r -> a, MDL); return DHCP_R_KEY_UNKNOWN; } isc_result_t omapi_protocol_set_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_typed_data_t *value) { omapi_protocol_object_t *p; omapi_remote_auth_t *r; if (h -> type != omapi_type_protocol) return DHCP_R_INVALIDARG; p = (omapi_protocol_object_t *)h; if (omapi_ds_strcmp (name, "default-authenticator") == 0) { if (!value || value -> type != omapi_datatype_object) return DHCP_R_INVALIDARG; if (!value -> u.object) { p -> default_auth = (omapi_remote_auth_t *)0; } else { for (r = p -> remote_auth_list; r; r = r -> next) if (r -> a == value -> u.object) break; if (!r) return DHCP_R_KEY_UNKNOWN; p -> default_auth = r; } return ISC_R_SUCCESS; } if (h -> inner && h -> inner -> type -> set_value) return (*(h -> inner -> type -> set_value)) (h -> inner, id, name, value); return ISC_R_NOTFOUND; } isc_result_t omapi_protocol_get_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_value_t **value) { omapi_protocol_object_t *p; if (h -> type != omapi_type_protocol) return DHCP_R_INVALIDARG; p = (omapi_protocol_object_t *)h; if (omapi_ds_strcmp (name, "default-authenticator") == 0) { if (!p -> default_auth) return ISC_R_NOTFOUND; return omapi_make_object_value (value, name, p -> default_auth -> a, MDL); } if (h -> inner && h -> inner -> type -> get_value) return (*(h -> inner -> type -> get_value)) (h -> inner, id, name, value); return ISC_R_NOTFOUND; } isc_result_t omapi_protocol_destroy (omapi_object_t *h, const char *file, int line) { omapi_protocol_object_t *p; if (h -> type != omapi_type_protocol) return DHCP_R_INVALIDARG; p = (omapi_protocol_object_t *)h; if (p -> message) omapi_message_dereference (&p -> message, file, line); /* This will happen if: 1) A default authenticator is supplied to omapi_protocol_connect(), and 2) something goes wrong before the authenticator can be opened. */ if (p -> default_auth && !p -> remote_auth_list) dfree (p -> default_auth, file, line); while (p -> remote_auth_list) { omapi_remote_auth_t *r = p -> remote_auth_list; p -> remote_auth_list = p -> remote_auth_list -> next; omapi_object_dereference (&r -> a, file, line); dfree (r, file, line); } return ISC_R_SUCCESS; } /* Write all the published values associated with the object through the specified connection. */ isc_result_t omapi_protocol_stuff_values (omapi_object_t *c, omapi_object_t *id, omapi_object_t *p) { if (p -> type != omapi_type_protocol) return DHCP_R_INVALIDARG; if (p -> inner && p -> inner -> type -> stuff_values) return (*(p -> inner -> type -> stuff_values)) (c, id, p -> inner); return ISC_R_SUCCESS; } /* Returns a boolean indicating whether this protocol requires that messages be authenticated or not. */ isc_boolean_t omapi_protocol_authenticated (omapi_object_t *h) { if (h -> type != omapi_type_protocol) return isc_boolean_false; if (((omapi_protocol_object_t *)h) -> insecure) return isc_boolean_false; else return isc_boolean_true; } /* Sets the address and authenticator verification callbacks. The handle is to a listener object, not a protocol object. */ isc_result_t omapi_protocol_configure_security (omapi_object_t *h, isc_result_t (*verify_addr) (omapi_object_t *, omapi_addr_t *), isc_result_t (*verify_auth) (omapi_object_t *, omapi_auth_key_t *)) { omapi_protocol_listener_object_t *l; if (h -> outer && h -> outer -> type == omapi_type_protocol_listener) h = h -> outer; if (h -> type != omapi_type_protocol_listener) return DHCP_R_INVALIDARG; l = (omapi_protocol_listener_object_t *)h; l -> verify_auth = verify_auth; l -> insecure = 0; if (h -> outer != NULL) { return omapi_listener_configure_security (h -> outer, verify_addr); } else { return DHCP_R_INVALIDARG; } } /* Set up a listener for the omapi protocol. The handle stored points to a listener object, not a protocol object. */ isc_result_t omapi_protocol_listen (omapi_object_t *h, unsigned port, int max) { isc_result_t status; omapi_protocol_listener_object_t *obj; obj = (omapi_protocol_listener_object_t *)0; status = omapi_protocol_listener_allocate (&obj, MDL); if (status != ISC_R_SUCCESS) return status; status = omapi_object_reference (&h -> outer, (omapi_object_t *)obj, MDL); if (status != ISC_R_SUCCESS) { omapi_protocol_listener_dereference (&obj, MDL); return status; } status = omapi_object_reference (&obj -> inner, h, MDL); if (status != ISC_R_SUCCESS) { omapi_protocol_listener_dereference (&obj, MDL); return status; } /* What a terrible default. */ obj -> insecure = 1; status = omapi_listen ((omapi_object_t *)obj, port, max); omapi_protocol_listener_dereference (&obj, MDL); return status; } /* Signal handler for protocol listener - if we get a connect signal, create a new protocol connection, otherwise pass the signal down. */ isc_result_t omapi_protocol_listener_signal (omapi_object_t *o, const char *name, va_list ap) { isc_result_t status; omapi_object_t *c; omapi_protocol_object_t *obj; omapi_protocol_listener_object_t *p; if (!o || o -> type != omapi_type_protocol_listener) return DHCP_R_INVALIDARG; p = (omapi_protocol_listener_object_t *)o; /* Not a signal we recognize? */ if (strcmp (name, "connect")) { if (p -> inner && p -> inner -> type -> signal_handler) return (*(p -> inner -> type -> signal_handler)) (p -> inner, name, ap); return ISC_R_NOTFOUND; } c = va_arg (ap, omapi_object_t *); if (!c || c -> type != omapi_type_connection) return DHCP_R_INVALIDARG; obj = (omapi_protocol_object_t *)0; status = omapi_protocol_allocate (&obj, MDL); if (status != ISC_R_SUCCESS) return status; obj -> verify_auth = p -> verify_auth; obj -> insecure = p -> insecure; status = omapi_object_reference (&obj -> outer, c, MDL); if (status != ISC_R_SUCCESS) { lose: omapi_protocol_dereference (&obj, MDL); omapi_disconnect (c, 1); return status; } status = omapi_object_reference (&c -> inner, (omapi_object_t *)obj, MDL); if (status != ISC_R_SUCCESS) goto lose; /* Send the introductory message. */ status = omapi_protocol_send_intro ((omapi_object_t *)obj, OMAPI_PROTOCOL_VERSION, sizeof (omapi_protocol_header_t)); if (status != ISC_R_SUCCESS) goto lose; omapi_protocol_dereference (&obj, MDL); return status; } isc_result_t omapi_protocol_listener_set_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_typed_data_t *value) { if (h -> type != omapi_type_protocol_listener) return DHCP_R_INVALIDARG; if (h -> inner && h -> inner -> type -> set_value) return (*(h -> inner -> type -> set_value)) (h -> inner, id, name, value); return ISC_R_NOTFOUND; } isc_result_t omapi_protocol_listener_get_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_value_t **value) { if (h -> type != omapi_type_protocol_listener) return DHCP_R_INVALIDARG; if (h -> inner && h -> inner -> type -> get_value) return (*(h -> inner -> type -> get_value)) (h -> inner, id, name, value); return ISC_R_NOTFOUND; } isc_result_t omapi_protocol_listener_destroy (omapi_object_t *h, const char *file, int line) { if (h -> type != omapi_type_protocol_listener) return DHCP_R_INVALIDARG; return ISC_R_SUCCESS; } /* Write all the published values associated with the object through the specified connection. */ isc_result_t omapi_protocol_listener_stuff (omapi_object_t *c, omapi_object_t *id, omapi_object_t *p) { if (p -> type != omapi_type_protocol_listener) return DHCP_R_INVALIDARG; if (p -> inner && p -> inner -> type -> stuff_values) return (*(p -> inner -> type -> stuff_values)) (c, id, p -> inner); return ISC_R_SUCCESS; } isc_result_t omapi_protocol_send_status (omapi_object_t *po, omapi_object_t *id, isc_result_t waitstatus, unsigned rid, const char *msg) { isc_result_t status; omapi_message_object_t *message = (omapi_message_object_t *)0; omapi_object_t *mo; if (po -> type != omapi_type_protocol) return DHCP_R_INVALIDARG; status = omapi_message_new ((omapi_object_t **)&message, MDL); if (status != ISC_R_SUCCESS) return status; mo = (omapi_object_t *)message; status = omapi_set_int_value (mo, (omapi_object_t *)0, "op", OMAPI_OP_STATUS); if (status != ISC_R_SUCCESS) { omapi_message_dereference (&message, MDL); return status; } status = omapi_set_int_value (mo, (omapi_object_t *)0, "rid", (int)rid); if (status != ISC_R_SUCCESS) { omapi_message_dereference (&message, MDL); return status; } status = omapi_set_int_value (mo, (omapi_object_t *)0, "result", (int)waitstatus); if (status != ISC_R_SUCCESS) { omapi_message_dereference (&message, MDL); return status; } /* If a message has been provided, send it. */ if (msg) { status = omapi_set_string_value (mo, (omapi_object_t *)0, "message", msg); if (status != ISC_R_SUCCESS) { omapi_message_dereference (&message, MDL); return status; } } status = omapi_protocol_send_message (po, id, mo, (omapi_object_t *)0); omapi_message_dereference (&message, MDL); return status; } /* The OMAPI_NOTIFY_PROTOCOL flag will cause the notify-object for the message to be set to the protocol object. This is used when opening the default authenticator. */ isc_result_t omapi_protocol_send_open (omapi_object_t *po, omapi_object_t *id, const char *type, omapi_object_t *object, unsigned flags) { isc_result_t status; omapi_message_object_t *message = (omapi_message_object_t *)0; omapi_object_t *mo; if (po -> type != omapi_type_protocol) return DHCP_R_INVALIDARG; status = omapi_message_new ((omapi_object_t **)&message, MDL); mo = (omapi_object_t *)message; if (status == ISC_R_SUCCESS) status = omapi_set_int_value (mo, (omapi_object_t *)0, "op", OMAPI_OP_OPEN); if (status == ISC_R_SUCCESS) status = omapi_set_object_value (mo, (omapi_object_t *)0, "object", object); if ((flags & OMAPI_CREATE) && (status == ISC_R_SUCCESS)) status = omapi_set_boolean_value (mo, (omapi_object_t *)0, "create", 1); if ((flags & OMAPI_UPDATE) && (status == ISC_R_SUCCESS)) status = omapi_set_boolean_value (mo, (omapi_object_t *)0, "update", 1); if ((flags & OMAPI_EXCL) && (status == ISC_R_SUCCESS)) status = omapi_set_boolean_value (mo, (omapi_object_t *)0, "exclusive", 1); if ((flags & OMAPI_NOTIFY_PROTOCOL) && (status == ISC_R_SUCCESS)) status = omapi_set_object_value (mo, (omapi_object_t *)0, "notify-object", po); if (type && (status == ISC_R_SUCCESS)) status = omapi_set_string_value (mo, (omapi_object_t *)0, "type", type); if (status == ISC_R_SUCCESS) status = omapi_message_register (mo); if (status == ISC_R_SUCCESS) { status = omapi_protocol_send_message (po, id, mo, (omapi_object_t *)0); if (status != ISC_R_SUCCESS) omapi_message_unregister (mo); } if (message) omapi_message_dereference (&message, MDL); return status; } isc_result_t omapi_protocol_send_update (omapi_object_t *po, omapi_object_t *id, unsigned rid, omapi_object_t *object) { isc_result_t status; omapi_message_object_t *message = (omapi_message_object_t *)0; omapi_object_t *mo; if (po -> type != omapi_type_protocol) return DHCP_R_INVALIDARG; status = omapi_message_new ((omapi_object_t **)&message, MDL); if (status != ISC_R_SUCCESS) return status; mo = (omapi_object_t *)message; status = omapi_set_int_value (mo, (omapi_object_t *)0, "op", OMAPI_OP_UPDATE); if (status != ISC_R_SUCCESS) { omapi_message_dereference (&message, MDL); return status; } if (rid) { omapi_handle_t handle; status = omapi_set_int_value (mo, (omapi_object_t *)0, "rid", (int)rid); if (status != ISC_R_SUCCESS) { omapi_message_dereference (&message, MDL); return status; } status = omapi_object_handle (&handle, object); if (status != ISC_R_SUCCESS) { omapi_message_dereference (&message, MDL); return status; } status = omapi_set_int_value (mo, (omapi_object_t *)0, "handle", (int)handle); if (status != ISC_R_SUCCESS) { omapi_message_dereference (&message, MDL); return status; } } status = omapi_set_object_value (mo, (omapi_object_t *)0, "object", object); if (status != ISC_R_SUCCESS) { omapi_message_dereference (&message, MDL); return status; } status = omapi_protocol_send_message (po, id, mo, (omapi_object_t *)0); omapi_message_dereference (&message, MDL); return status; } dhcp-4.4.1/omapip/result.c000644 000765 000024 00000005237 13243301226 015671 0ustar00tmarkstaff000000 000000 /* result.c */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1999-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" /* * In the previous code the results started at 36 * rather than ISC_RESULTCLASS_DHCP + 0 * ISC_R_NOTCONNECTED was + 4 (40), it has been superseeded by the isc version */ static const char *text[DHCP_R_NRESULTS] = { "host unknown", /* 0 */ "protocol version mismatch", /* 1 */ "protocol error", /* 2 */ "invalid argument", /* 3 */ "data not yet available", /* 4 */ "object unchanged", /* 5 */ "more than one object matches key", /* 6 */ "key conflict", /* 7 */ "parse error(s) occurred", /* 8 */ "no key specified", /* 9 */ "zone TSIG key not known", /* 10 */ "invalid TSIG key", /* 11 */ "operation in progress", /* 12 */ "DNS format error", /* 13 */ "DNS server failed", /* 14 */ "no such domain", /* 15 */ "not implemented", /* 16 */ "refused", /* 17 */ "domain already exists", /* 18 */ "RRset already exists", /* 19 */ "no such RRset", /* 20 */ "not authorized", /* 21 */ "not a zone", /* 22 */ "bad DNS signature", /* 23 */ "bad DNS key", /* 24 */ "clock skew too great", /* 25 */ "no root zone", /* 26 */ "destination address required", /* 27 */ "cross-zone update", /* 28 */ "no TSIG signature", /* 29 */ "not equal", /* 30 */ "connection reset by peer", /* 31 */ "unknown attribute" /* 32 */ }; #define DHCP_RESULT_RESULTSET 2 #define DHCP_RESULT_UNAVAILABLESET 3 // This is a placeholder as we don't allow for external message catalogs yet isc_msgcat_t * dhcp_msgcat = NULL; isc_result_t dhcp_result_register(void) { isc_result_t result; result = isc_result_register(ISC_RESULTCLASS_DHCP, DHCP_R_NRESULTS, text, dhcp_msgcat, DHCP_RESULT_RESULTSET); return(result); } dhcp-4.4.1/omapip/support.c000644 000765 000024 00000054133 13243301226 016066 0ustar00tmarkstaff000000 000000 /* support.c Subroutines providing general support for objects. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1999-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #include omapi_object_type_t *omapi_type_connection; omapi_object_type_t *omapi_type_listener; omapi_object_type_t *omapi_type_io_object; omapi_object_type_t *omapi_type_datagram; omapi_object_type_t *omapi_type_generic; omapi_object_type_t *omapi_type_protocol; omapi_object_type_t *omapi_type_protocol_listener; omapi_object_type_t *omapi_type_waiter; omapi_object_type_t *omapi_type_remote; omapi_object_type_t *omapi_type_message; omapi_object_type_t *omapi_type_auth_key; omapi_object_type_t *omapi_object_types; int omapi_object_type_count; #if defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) void omapi_type_relinquish () { omapi_object_type_t *t, *n; for (t = omapi_object_types; t; t = n) { n = t -> next; dfree (t, MDL); } omapi_object_types = (omapi_object_type_t *)0; } #endif isc_result_t omapi_init (void) { isc_result_t status; /* Register all the standard object types... */ status = omapi_object_type_register (&omapi_type_connection, "connection", omapi_connection_set_value, omapi_connection_get_value, omapi_connection_destroy, omapi_connection_signal_handler, omapi_connection_stuff_values, 0, 0, 0, 0, 0, 0, sizeof (omapi_connection_object_t), 0, RC_MISC); if (status != ISC_R_SUCCESS) return status; status = omapi_object_type_register (&omapi_type_listener, "listener", omapi_listener_set_value, omapi_listener_get_value, omapi_listener_destroy, omapi_listener_signal_handler, omapi_listener_stuff_values, 0, 0, 0, 0, 0, 0, sizeof (omapi_listener_object_t), 0, RC_MISC); if (status != ISC_R_SUCCESS) return status; status = omapi_object_type_register (&omapi_type_io_object, "io", omapi_io_set_value, omapi_io_get_value, omapi_io_destroy, omapi_io_signal_handler, omapi_io_stuff_values, 0, 0, 0, 0, 0, 0, sizeof (omapi_io_object_t), 0, RC_MISC); if (status != ISC_R_SUCCESS) return status; status = omapi_object_type_register (&omapi_type_generic, "generic", omapi_generic_set_value, omapi_generic_get_value, omapi_generic_destroy, omapi_generic_signal_handler, omapi_generic_stuff_values, 0, 0, 0, 0, 0, 0, sizeof (omapi_generic_object_t), 0, RC_MISC); if (status != ISC_R_SUCCESS) return status; status = omapi_object_type_register (&omapi_type_protocol, "protocol", omapi_protocol_set_value, omapi_protocol_get_value, omapi_protocol_destroy, omapi_protocol_signal_handler, omapi_protocol_stuff_values, 0, 0, 0, 0, 0, 0, sizeof (omapi_protocol_object_t), 0, RC_MISC); if (status != ISC_R_SUCCESS) return status; status = (omapi_object_type_register (&omapi_type_protocol_listener, "protocol-listener", omapi_protocol_listener_set_value, omapi_protocol_listener_get_value, omapi_protocol_listener_destroy, omapi_protocol_listener_signal, omapi_protocol_listener_stuff, 0, 0, 0, 0, 0, 0, sizeof (omapi_protocol_listener_object_t), 0, RC_MISC)); if (status != ISC_R_SUCCESS) return status; status = omapi_object_type_register (&omapi_type_message, "message", omapi_message_set_value, omapi_message_get_value, omapi_message_destroy, omapi_message_signal_handler, omapi_message_stuff_values, 0, 0, 0, 0, 0, 0, sizeof (omapi_message_object_t), 0, RC_MISC); if (status != ISC_R_SUCCESS) return status; status = omapi_object_type_register (&omapi_type_waiter, "waiter", 0, 0, 0, omapi_waiter_signal_handler, 0, 0, 0, 0, 0, 0, 0, sizeof (omapi_waiter_object_t), 0, RC_MISC); if (status != ISC_R_SUCCESS) return status; status = omapi_object_type_register (&omapi_type_auth_key, "authenticator", 0, omapi_auth_key_get_value, omapi_auth_key_destroy, 0, omapi_auth_key_stuff_values, omapi_auth_key_lookup, 0, 0, 0, 0, 0, sizeof (omapi_auth_key_t), 0, RC_MISC); if (status != ISC_R_SUCCESS) return status; #if defined (TRACING) omapi_listener_trace_setup (); omapi_connection_trace_setup (); omapi_buffer_trace_setup (); #endif /* This seems silly, but leave it. */ return ISC_R_SUCCESS; } isc_result_t omapi_object_type_register (omapi_object_type_t **type, const char *name, isc_result_t (*set_value) (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_typed_data_t *), isc_result_t (*get_value) (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_value_t **), isc_result_t (*destroy) (omapi_object_t *, const char *, int), isc_result_t (*signal_handler) (omapi_object_t *, const char *, va_list), isc_result_t (*stuff_values) (omapi_object_t *, omapi_object_t *, omapi_object_t *), isc_result_t (*lookup) (omapi_object_t **, omapi_object_t *, omapi_object_t *), isc_result_t (*create) (omapi_object_t **, omapi_object_t *), isc_result_t (*remove) (omapi_object_t *, omapi_object_t *), isc_result_t (*freer) (omapi_object_t *, const char *, int), isc_result_t (*allocator) (omapi_object_t **, const char *, int), isc_result_t (*sizer) (size_t), size_t size, isc_result_t (*initialize) (omapi_object_t *, const char *, int), int rc_flag) { omapi_object_type_t *t; t = dmalloc (sizeof *t, MDL); if (!t) return ISC_R_NOMEMORY; memset (t, 0, sizeof *t); t -> name = name; t -> set_value = set_value; t -> get_value = get_value; t -> destroy = destroy; t -> signal_handler = signal_handler; t -> stuff_values = stuff_values; t -> lookup = lookup; t -> create = create; t -> remove = remove; t -> next = omapi_object_types; t -> sizer = sizer; t -> size = size; t -> freer = freer; t -> allocator = allocator; t -> initialize = initialize; t -> rc_flag = rc_flag; omapi_object_types = t; if (type) *type = t; return ISC_R_SUCCESS; } isc_result_t omapi_signal (omapi_object_t *handle, const char *name, ...) { va_list ap; omapi_object_t *outer; isc_result_t status; va_start (ap, name); for (outer = handle; outer -> outer; outer = outer -> outer) ; if (outer -> type -> signal_handler) status = (*(outer -> type -> signal_handler)) (outer, name, ap); else status = ISC_R_NOTFOUND; va_end (ap); return status; } isc_result_t omapi_signal_in (omapi_object_t *handle, const char *name, ...) { va_list ap; isc_result_t status; if (!handle) return ISC_R_NOTFOUND; va_start (ap, name); if (handle -> type -> signal_handler) status = (*(handle -> type -> signal_handler)) (handle, name, ap); else status = ISC_R_NOTFOUND; va_end (ap); return status; } isc_result_t omapi_set_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_typed_data_t *value) { omapi_object_t *outer; isc_result_t status; #if defined (DEBUG) if (!value) { log_info ("omapi_set_value (%.*s, NULL)", (int)name -> len, name -> value); } else if (value -> type == omapi_datatype_int) { log_info ("omapi_set_value (%.*s, %ld)", (int)name -> len, name -> value, (long)value -> u.integer); } else if (value -> type == omapi_datatype_string) { log_info ("omapi_set_value (%.*s, %.*s)", (int)name -> len, name -> value, (int)value -> u.buffer.len, value -> u.buffer.value); } else if (value -> type == omapi_datatype_data) { log_info ("omapi_set_value (%.*s, %ld %lx)", (int)name -> len, name -> value, (long)value -> u.buffer.len, (unsigned long)value -> u.buffer.value); } else if (value -> type == omapi_datatype_object) { log_info ("omapi_set_value (%.*s, %s)", (int)name -> len, name -> value, value -> u.object ? (value -> u.object -> type ? value -> u.object -> type -> name : "(unknown object)") : "(unknown object)"); } #endif for (outer = h; outer -> outer; outer = outer -> outer) ; if (outer -> type -> set_value) status = (*(outer -> type -> set_value)) (outer, id, name, value); else status = ISC_R_NOTFOUND; #if defined (DEBUG) log_info (" ==> %s", isc_result_totext (status)); #endif return status; } isc_result_t omapi_set_value_str (omapi_object_t *h, omapi_object_t *id, const char *name, omapi_typed_data_t *value) { omapi_data_string_t *nds; isc_result_t status; nds = (omapi_data_string_t *)0; status = omapi_data_string_new (&nds, strlen (name), MDL); if (status != ISC_R_SUCCESS) return status; memcpy (nds -> value, name, strlen (name)); status = omapi_set_value (h, id, nds, value); omapi_data_string_dereference (&nds, MDL); return status; } isc_result_t omapi_set_boolean_value (omapi_object_t *h, omapi_object_t *id, const char *name, int value) { isc_result_t status; omapi_typed_data_t *tv = (omapi_typed_data_t *)0; omapi_data_string_t *n = (omapi_data_string_t *)0; status = omapi_data_string_new (&n, strlen (name), MDL); if (status != ISC_R_SUCCESS) return status; memcpy (n -> value, name, strlen (name)); status = omapi_typed_data_new (MDL, &tv, omapi_datatype_int, value); if (status != ISC_R_SUCCESS) { omapi_data_string_dereference (&n, MDL); return status; } status = omapi_set_value (h, id, n, tv); omapi_data_string_dereference (&n, MDL); omapi_typed_data_dereference (&tv, MDL); return status; } isc_result_t omapi_set_int_value (omapi_object_t *h, omapi_object_t *id, const char *name, int value) { isc_result_t status; omapi_typed_data_t *tv = (omapi_typed_data_t *)0; omapi_data_string_t *n = (omapi_data_string_t *)0; status = omapi_data_string_new (&n, strlen (name), MDL); if (status != ISC_R_SUCCESS) return status; memcpy (n -> value, name, strlen (name)); status = omapi_typed_data_new (MDL, &tv, omapi_datatype_int, value); if (status != ISC_R_SUCCESS) { omapi_data_string_dereference (&n, MDL); return status; } status = omapi_set_value (h, id, n, tv); omapi_data_string_dereference (&n, MDL); omapi_typed_data_dereference (&tv, MDL); return status; } isc_result_t omapi_set_object_value (omapi_object_t *h, omapi_object_t *id, const char *name, omapi_object_t *value) { isc_result_t status; omapi_typed_data_t *tv = (omapi_typed_data_t *)0; omapi_data_string_t *n = (omapi_data_string_t *)0; status = omapi_data_string_new (&n, strlen (name), MDL); if (status != ISC_R_SUCCESS) return status; memcpy (n -> value, name, strlen (name)); status = omapi_typed_data_new (MDL, &tv, omapi_datatype_object, value); if (status != ISC_R_SUCCESS) { omapi_data_string_dereference (&n, MDL); return status; } status = omapi_set_value (h, id, n, tv); omapi_data_string_dereference (&n, MDL); omapi_typed_data_dereference (&tv, MDL); return status; } isc_result_t omapi_set_string_value (omapi_object_t *h, omapi_object_t *id, const char *name, const char *value) { isc_result_t status; omapi_typed_data_t *tv = (omapi_typed_data_t *)0; omapi_data_string_t *n = (omapi_data_string_t *)0; status = omapi_data_string_new (&n, strlen (name), MDL); if (status != ISC_R_SUCCESS) return status; memcpy (n -> value, name, strlen (name)); status = omapi_typed_data_new (MDL, &tv, omapi_datatype_string, value); if (status != ISC_R_SUCCESS) { omapi_data_string_dereference (&n, MDL); return status; } status = omapi_set_value (h, id, n, tv); omapi_data_string_dereference (&n, MDL); omapi_typed_data_dereference (&tv, MDL); return status; } isc_result_t omapi_get_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_value_t **value) { omapi_object_t *outer; for (outer = h; outer -> outer; outer = outer -> outer) ; if (outer -> type -> get_value) return (*(outer -> type -> get_value)) (outer, id, name, value); return ISC_R_NOTFOUND; } isc_result_t omapi_get_value_str (omapi_object_t *h, omapi_object_t *id, const char *name, omapi_value_t **value) { omapi_object_t *outer; omapi_data_string_t *nds; isc_result_t status; nds = (omapi_data_string_t *)0; status = omapi_data_string_new (&nds, strlen (name), MDL); if (status != ISC_R_SUCCESS) return status; memcpy (nds -> value, name, strlen (name)); for (outer = h; outer -> outer; outer = outer -> outer) ; if (outer -> type -> get_value) status = (*(outer -> type -> get_value)) (outer, id, nds, value); else status = ISC_R_NOTFOUND; omapi_data_string_dereference (&nds, MDL); return status; } isc_result_t omapi_stuff_values (omapi_object_t *c, omapi_object_t *id, omapi_object_t *o) { omapi_object_t *outer; for (outer = o; outer -> outer; outer = outer -> outer) ; if (outer -> type -> stuff_values) return (*(outer -> type -> stuff_values)) (c, id, outer); return ISC_R_NOTFOUND; } isc_result_t omapi_object_create (omapi_object_t **obj, omapi_object_t *id, omapi_object_type_t *type) { if (!type -> create) return ISC_R_NOTIMPLEMENTED; return (*(type -> create)) (obj, id); } isc_result_t omapi_object_update (omapi_object_t *obj, omapi_object_t *id, omapi_object_t *src, omapi_handle_t handle) { omapi_generic_object_t *gsrc; isc_result_t status; int i; if (!src) return DHCP_R_INVALIDARG; if (src -> type != omapi_type_generic) return ISC_R_NOTIMPLEMENTED; gsrc = (omapi_generic_object_t *)src; for (i = 0; i < gsrc -> nvalues; i++) { status = omapi_set_value (obj, id, gsrc -> values [i] -> name, gsrc -> values [i] -> value); if (status != ISC_R_SUCCESS && status != DHCP_R_UNCHANGED) return status; } /* * For now ignore the return value. I'm not sure if we want to * generate an error if we can't set the handle value. If we * do add a check we probably should allow unchanged and notfound */ if (handle) (void) omapi_set_int_value (obj, id, "remote-handle", (int)handle); status = omapi_signal (obj, "updated"); if (status != ISC_R_NOTFOUND) return status; return ISC_R_SUCCESS; } int omapi_data_string_cmp (omapi_data_string_t *s1, omapi_data_string_t *s2) { unsigned len; int rv; if (s1 -> len > s2 -> len) len = s2 -> len; else len = s1 -> len; rv = memcmp (s1 -> value, s2 -> value, len); if (rv) return rv; if (s1 -> len > s2 -> len) return 1; else if (s1 -> len < s2 -> len) return -1; return 0; } int omapi_ds_strcmp (omapi_data_string_t *s1, const char *s2) { unsigned len, slen; int rv; slen = strlen (s2); if (slen > s1 -> len) len = s1 -> len; else len = slen; rv = memcmp (s1 -> value, s2, len); if (rv) return rv; if (s1 -> len > slen) return 1; else if (s1 -> len < slen) return -1; return 0; } int omapi_td_strcmp (omapi_typed_data_t *s1, const char *s2) { unsigned len, slen; int rv; /* If the data type is not compatible, never equal. */ if (s1 -> type != omapi_datatype_data && s1 -> type != omapi_datatype_string) return -1; slen = strlen (s2); if (slen > s1 -> u.buffer.len) len = s1 -> u.buffer.len; else len = slen; rv = memcmp (s1 -> u.buffer.value, s2, len); if (rv) return rv; if (s1 -> u.buffer.len > slen) return 1; else if (s1 -> u.buffer.len < slen) return -1; return 0; } int omapi_td_strcasecmp (omapi_typed_data_t *s1, const char *s2) { unsigned len, slen; int rv; /* If the data type is not compatible, never equal. */ if (s1 -> type != omapi_datatype_data && s1 -> type != omapi_datatype_string) return -1; slen = strlen (s2); if (slen > s1 -> u.buffer.len) len = s1 -> u.buffer.len; else len = slen; rv = casecmp (s1 -> u.buffer.value, s2, len); if (rv) return rv; if (s1 -> u.buffer.len > slen) return 1; else if (s1 -> u.buffer.len < slen) return -1; return 0; } isc_result_t omapi_make_value (omapi_value_t **vp, omapi_data_string_t *name, omapi_typed_data_t *value, const char *file, int line) { isc_result_t status; status = omapi_value_new (vp, file, line); if (status != ISC_R_SUCCESS) return status; status = omapi_data_string_reference (&(*vp) -> name, name, file, line); if (status != ISC_R_SUCCESS) { omapi_value_dereference (vp, file, line); return status; } if (value) { status = omapi_typed_data_reference (&(*vp) -> value, value, file, line); if (status != ISC_R_SUCCESS) { omapi_value_dereference (vp, file, line); return status; } } return ISC_R_SUCCESS; } isc_result_t omapi_make_const_value (omapi_value_t **vp, omapi_data_string_t *name, const unsigned char *value, unsigned len, const char *file, int line) { isc_result_t status; status = omapi_value_new (vp, file, line); if (status != ISC_R_SUCCESS) return status; status = omapi_data_string_reference (&(*vp) -> name, name, file, line); if (status != ISC_R_SUCCESS) { omapi_value_dereference (vp, file, line); return status; } if (value) { status = omapi_typed_data_new (file, line, &(*vp) -> value, omapi_datatype_data, len); if (status != ISC_R_SUCCESS) { omapi_value_dereference (vp, file, line); return status; } memcpy ((*vp) -> value -> u.buffer.value, value, len); } return ISC_R_SUCCESS; } isc_result_t omapi_make_int_value (omapi_value_t **vp, omapi_data_string_t *name, int value, const char *file, int line) { isc_result_t status; status = omapi_value_new (vp, file, line); if (status != ISC_R_SUCCESS) return status; status = omapi_data_string_reference (&(*vp) -> name, name, file, line); if (status != ISC_R_SUCCESS) { omapi_value_dereference (vp, file, line); return status; } status = omapi_typed_data_new (file, line, &(*vp) -> value, omapi_datatype_int, value); if (status != ISC_R_SUCCESS) { omapi_value_dereference (vp, file, line); return status; } return ISC_R_SUCCESS; } isc_result_t omapi_make_uint_value (omapi_value_t **vp, omapi_data_string_t *name, unsigned int value, const char *file, int line) { return omapi_make_int_value (vp, name, (int)value, file, line); } isc_result_t omapi_make_object_value (omapi_value_t **vp, omapi_data_string_t *name, omapi_object_t *value, const char *file, int line) { isc_result_t status; status = omapi_value_new (vp, file, line); if (status != ISC_R_SUCCESS) return status; status = omapi_data_string_reference (&(*vp) -> name, name, file, line); if (status != ISC_R_SUCCESS) { omapi_value_dereference (vp, file, line); return status; } if (value) { status = omapi_typed_data_new (file, line, &(*vp) -> value, omapi_datatype_object, value); if (status != ISC_R_SUCCESS) { omapi_value_dereference (vp, file, line); return status; } } return ISC_R_SUCCESS; } isc_result_t omapi_make_handle_value (omapi_value_t **vp, omapi_data_string_t *name, omapi_object_t *value, const char *file, int line) { isc_result_t status; status = omapi_value_new (vp, file, line); if (status != ISC_R_SUCCESS) return status; status = omapi_data_string_reference (&(*vp) -> name, name, file, line); if (status != ISC_R_SUCCESS) { omapi_value_dereference (vp, file, line); return status; } if (value) { status = omapi_typed_data_new (file, line, &(*vp) -> value, omapi_datatype_int); if (status != ISC_R_SUCCESS) { omapi_value_dereference (vp, file, line); return status; } status = (omapi_object_handle ((omapi_handle_t *)&(*vp) -> value -> u.integer, value)); if (status != ISC_R_SUCCESS) { omapi_value_dereference (vp, file, line); return status; } } return ISC_R_SUCCESS; } isc_result_t omapi_make_string_value (omapi_value_t **vp, omapi_data_string_t *name, const char *value, const char *file, int line) { isc_result_t status; status = omapi_value_new (vp, file, line); if (status != ISC_R_SUCCESS) return status; status = omapi_data_string_reference (&(*vp) -> name, name, file, line); if (status != ISC_R_SUCCESS) { omapi_value_dereference (vp, file, line); return status; } if (value) { status = omapi_typed_data_new (file, line, &(*vp) -> value, omapi_datatype_string, value); if (status != ISC_R_SUCCESS) { omapi_value_dereference (vp, file, line); return status; } } return ISC_R_SUCCESS; } isc_result_t omapi_get_int_value (unsigned long *v, omapi_typed_data_t *t) { u_int32_t rv; if (t -> type == omapi_datatype_int) { *v = t -> u.integer; return ISC_R_SUCCESS; } else if (t -> type == omapi_datatype_string || t -> type == omapi_datatype_data) { if (t -> u.buffer.len != sizeof (rv)) return DHCP_R_INVALIDARG; memcpy (&rv, t -> u.buffer.value, sizeof rv); *v = ntohl (rv); return ISC_R_SUCCESS; } return DHCP_R_INVALIDARG; } dhcp-4.4.1/omapip/test.c000644 000765 000024 00000006105 13243301226 015325 0ustar00tmarkstaff000000 000000 /* test.c Test code for omapip... */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1999-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "config.h" #include #include #include #include #include #include #include #include #include int main (int argc, char **argv) { omapi_object_t *listener = (omapi_object_t*)0; omapi_object_t *connection = (omapi_object_t*)0; isc_result_t status; status = dhcp_context_create(DHCP_CONTEXT_PRE_DB | DHCP_CONTEXT_POST_DB, NULL, NULL); if (status != ISC_R_SUCCESS) { fprintf(stderr, "Can't initialize context: %s\n", isc_result_totext(status)); exit(1); } status = omapi_init (); if (status != ISC_R_SUCCESS) { fprintf(stderr, "omapi_init failed: %s\n", isc_result_totext(status)); exit(1); } if (argc > 1 && !strcmp (argv [1], "listen")) { if (argc < 3) { fprintf (stderr, "Usage: test listen port\n"); exit (1); } status = omapi_generic_new (&listener, MDL); if (status != ISC_R_SUCCESS) { fprintf (stderr, "omapi_generic_new: %s\n", isc_result_totext (status)); exit (1); } status = omapi_protocol_listen (listener, (unsigned)atoi (argv [2]), 1); if (status != ISC_R_SUCCESS) { fprintf (stderr, "omapi_listen: %s\n", isc_result_totext (status)); exit (1); } omapi_dispatch (0); } else if (argc > 1 && !strcmp (argv [1], "connect")) { if (argc < 4) { fprintf (stderr, "Usage: test listen address port\n"); exit (1); } status = omapi_generic_new (&connection, MDL); if (status != ISC_R_SUCCESS) { fprintf (stderr, "omapi_generic_new: %s\n", isc_result_totext (status)); exit (1); } status = omapi_protocol_connect (connection, argv [2], (unsigned)atoi (argv [3]), 0); fprintf (stderr, "connect: %s\n", isc_result_totext (status)); if (status != ISC_R_SUCCESS) exit (1); status = omapi_wait_for_completion (connection, 0); fprintf (stderr, "completion: %s\n", isc_result_totext (status)); if (status != ISC_R_SUCCESS) exit (1); /* ... */ } else { fprintf (stderr, "Usage: test [listen | connect] ...\n"); exit (1); } return 0; } dhcp-4.4.1/omapip/toisc.c000644 000765 000024 00000007701 13243301226 015472 0ustar00tmarkstaff000000 000000 /* toisc.c Convert non-ISC result codes to ISC result codes. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 2001-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #include #include "arpa/nameser.h" #include "ns_name.h" #include isc_result_t uerr2isc (int err) { switch (err) { case EPERM: return ISC_R_NOPERM; case ENOENT: return ISC_R_NOTFOUND; case ESRCH: return ISC_R_NOTFOUND; case EIO: return ISC_R_IOERROR; case ENXIO: return ISC_R_NOTFOUND; case E2BIG: return ISC_R_NOSPACE; case ENOEXEC: return DHCP_R_FORMERR; case ECHILD: return ISC_R_NOTFOUND; case ENOMEM: return ISC_R_NOMEMORY; case EACCES: return ISC_R_NOPERM; case EFAULT: return DHCP_R_INVALIDARG; case EEXIST: return ISC_R_EXISTS; case EINVAL: return DHCP_R_INVALIDARG; case ENOTTY: return DHCP_R_INVALIDARG; case EFBIG: return ISC_R_NOSPACE; case ENOSPC: return ISC_R_NOSPACE; case EROFS: return ISC_R_NOPERM; case EMLINK: return ISC_R_NOSPACE; case EPIPE: return ISC_R_NOTCONNECTED; case EINPROGRESS: return ISC_R_ALREADYRUNNING; case EALREADY: return ISC_R_ALREADYRUNNING; case ENOTSOCK: return ISC_R_INVALIDFILE; case EDESTADDRREQ: return DHCP_R_DESTADDRREQ; case EMSGSIZE: return ISC_R_NOSPACE; case EPROTOTYPE: return DHCP_R_INVALIDARG; case ENOPROTOOPT: return ISC_R_NOTIMPLEMENTED; case EPROTONOSUPPORT: return ISC_R_NOTIMPLEMENTED; case ESOCKTNOSUPPORT: return ISC_R_NOTIMPLEMENTED; case EOPNOTSUPP: return ISC_R_NOTIMPLEMENTED; case EPFNOSUPPORT: return ISC_R_NOTIMPLEMENTED; case EAFNOSUPPORT: return ISC_R_NOTIMPLEMENTED; case EADDRINUSE: return ISC_R_ADDRINUSE; case EADDRNOTAVAIL: return ISC_R_ADDRNOTAVAIL; case ENETDOWN: return ISC_R_NETDOWN; case ENETUNREACH: return ISC_R_NETUNREACH; case ECONNABORTED: return ISC_R_TIMEDOUT; case ECONNRESET: return DHCP_R_CONNRESET; case ENOBUFS: return ISC_R_NOSPACE; case EISCONN: return ISC_R_ALREADYRUNNING; case ENOTCONN: return ISC_R_NOTCONNECTED; case ESHUTDOWN: return ISC_R_SHUTTINGDOWN; case ETIMEDOUT: return ISC_R_TIMEDOUT; case ECONNREFUSED: return ISC_R_CONNREFUSED; case EHOSTDOWN: return ISC_R_HOSTDOWN; case EHOSTUNREACH: return ISC_R_HOSTUNREACH; #ifdef EDQUOT case EDQUOT: return ISC_R_QUOTA; #endif #ifdef EBADRPC case EBADRPC: return ISC_R_NOTIMPLEMENTED; #endif #ifdef ERPCMISMATCH case ERPCMISMATCH: return DHCP_R_VERSIONMISMATCH; #endif #ifdef EPROGMISMATCH case EPROGMISMATCH: return DHCP_R_VERSIONMISMATCH; #endif #ifdef EAUTH case EAUTH: return DHCP_R_NOTAUTH; #endif #ifdef ENEEDAUTH case ENEEDAUTH: return DHCP_R_NOTAUTH; #endif #ifdef EOVERFLOW case EOVERFLOW: return ISC_R_NOSPACE; #endif } return ISC_R_UNEXPECTED; } dhcp-4.4.1/omapip/trace.c000644 000765 000024 00000045223 13243301226 015450 0ustar00tmarkstaff000000 000000 /* trace.c Subroutines that support tracing of OMAPI wire transactions and provide a mechanism for programs using OMAPI to trace their own transactions... */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 2001-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #include #include #if defined (TRACING) void (*trace_set_time_hook) (TIME); static int tracing_stopped; static int traceoutfile; static int traceindex; static trace_type_t **trace_types; static int trace_type_count; static int trace_type_max; static trace_type_t *new_trace_types; static FILE *traceinfile; static tracefile_header_t tracefile_header; static int trace_playback_flag; trace_type_t trace_time_marker; #if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) extern omapi_array_t *trace_listeners; extern omapi_array_t *omapi_connections; extern int errno; void trace_free_all () { trace_type_t *tp; int i; tp = new_trace_types; while (tp) { new_trace_types = tp -> next; if (tp -> name) { dfree (tp -> name, MDL); tp -> name = (char *)0; } dfree (tp, MDL); tp = new_trace_types; } for (i = 0; i < trace_type_count; i++) { if (trace_types [i]) { if (trace_types [i] -> name) dfree (trace_types [i] -> name, MDL); dfree (trace_types [i], MDL); } } dfree (trace_types, MDL); trace_types = (trace_type_t **)0; trace_type_count = trace_type_max = 0; omapi_array_free (&trace_listeners, MDL); omapi_array_free (&omapi_connections, MDL); } #endif static isc_result_t trace_type_record (trace_type_t *, unsigned, const char *, int); int trace_playback () { return trace_playback_flag; } int trace_record () { if (traceoutfile && !tracing_stopped) return 1; return 0; } isc_result_t trace_init (void (*set_time) (TIME), const char *file, int line) { trace_type_t *root_type; static int root_setup = 0; if (root_setup) return ISC_R_SUCCESS; trace_set_time_hook = set_time; root_type = trace_type_register ("trace-index-mapping", (void *)0, trace_index_map_input, trace_index_stop_tracing, file, line); if (!root_type) return ISC_R_UNEXPECTED; if (new_trace_types == root_type) new_trace_types = new_trace_types -> next; root_type -> index = 0; trace_type_stash (root_type); root_setup = 1; return ISC_R_SUCCESS; } isc_result_t trace_begin (const char *filename, const char *file, int line) { tracefile_header_t tfh; int status; trace_type_t *tptr, *next; isc_result_t result; if (traceoutfile) { log_error ("%s(%d): trace_begin called twice", file, line); return DHCP_R_INVALIDARG; } traceoutfile = open (filename, O_CREAT | O_WRONLY | O_EXCL, 0600); if (traceoutfile < 0 && errno == EEXIST) { log_error ("WARNING: Overwriting trace file \"%s\"", filename); traceoutfile = open (filename, O_WRONLY | O_EXCL | O_TRUNC, 0600); } if (traceoutfile < 0) { log_error ("%s(%d): trace_begin: %s: %m", file, line, filename); return ISC_R_UNEXPECTED; } #if defined (HAVE_SETFD) if (fcntl (traceoutfile, F_SETFD, 1) < 0) log_error ("Can't set close-on-exec on %s: %m", filename); #endif tfh.magic = htonl (TRACEFILE_MAGIC); tfh.version = htonl (TRACEFILE_VERSION); tfh.hlen = htonl (sizeof (tracefile_header_t)); tfh.phlen = htonl (sizeof (tracepacket_t)); status = write (traceoutfile, &tfh, sizeof tfh); if (status < 0) { log_error ("%s(%d): trace_begin write failed: %m", file, line); return ISC_R_UNEXPECTED; } else if (status != sizeof tfh) { log_error ("%s(%d): trace_begin: short write (%d:%ld)", file, line, status, (long)(sizeof tfh)); trace_stop (); return ISC_R_UNEXPECTED; } /* Stash all the types that have already been set up. */ if (new_trace_types) { next = new_trace_types; new_trace_types = (trace_type_t *)0; for (tptr = next; tptr; tptr = next) { next = tptr -> next; if (tptr -> index != 0) { result = (trace_type_record (tptr, strlen (tptr -> name), file, line)); if (result != ISC_R_SUCCESS) return status; } } } return ISC_R_SUCCESS; } isc_result_t trace_write_packet (trace_type_t *ttype, unsigned length, const char *buf, const char *file, int line) { trace_iov_t iov; iov.buf = buf; iov.len = length; return trace_write_packet_iov (ttype, 1, &iov, file, line); } isc_result_t trace_write_packet_iov (trace_type_t *ttype, int count, trace_iov_t *iov, const char *file, int line) { tracepacket_t tmp; int status; int i; int length; /* Really shouldn't get called here, but it may be hard to turn off tracing midstream if the trace file write fails or something. */ if (tracing_stopped) return 0; if (!ttype) { log_error ("%s(%d): trace_write_packet with null trace type", file ? file : "", line); return DHCP_R_INVALIDARG; } if (!traceoutfile) { log_error ("%s(%d): trace_write_packet with no tracefile.", file ? file : "", line); return DHCP_R_INVALIDARG; } /* Compute the total length of the iov. */ length = 0; for (i = 0; i < count; i++) length += iov [i].len; /* We have to swap out the data, because it may be read back on a machine of different endianness. */ memset(&tmp, 0, sizeof(tmp)); tmp.type_index = htonl (ttype -> index); tmp.when = htonl (time ((time_t *)0)); /* XXX */ tmp.length = htonl (length); status = write (traceoutfile, &tmp, sizeof tmp); if (status < 0) { log_error ("%s(%d): trace_write_packet write failed: %m", file, line); return ISC_R_UNEXPECTED; } else if (status != sizeof tmp) { log_error ("%s(%d): trace_write_packet: short write (%d:%ld)", file, line, status, (long)(sizeof tmp)); trace_stop (); } for (i = 0; i < count; i++) { status = write (traceoutfile, iov [i].buf, iov [i].len); if (status < 0) { log_error ("%s(%d): %s write failed: %m", file, line, "trace_write_packet"); return ISC_R_UNEXPECTED; } else if (status != iov [i].len) { log_error ("%s(%d): %s: short write (%d:%d)", file, line, "trace_write_packet", status, length); trace_stop (); } } /* Write padding on the end of the packet to align the next packet to an 8-byte boundary. This is in case we decide to use mmap in some clever way later on. */ if (length % 8) { static char zero [] = { 0, 0, 0, 0, 0, 0, 0 }; unsigned padl = 8 - (length % 8); status = write (traceoutfile, zero, padl); if (status < 0) { log_error ("%s(%d): trace_write_packet write failed: %m", file, line); return ISC_R_UNEXPECTED; } else if (status != padl) { log_error ("%s(%d): trace_write_packet: short write (%d:%d)", file, line, status, padl); trace_stop (); } } return ISC_R_SUCCESS; } void trace_type_stash (trace_type_t *tptr) { trace_type_t **vec; int delta; if (trace_type_max <= tptr -> index) { delta = tptr -> index - trace_type_max + 10; vec = dmalloc (((trace_type_max + delta) * sizeof (trace_type_t *)), MDL); if (!vec) return; memset (&vec [trace_type_max], 0, (sizeof (trace_type_t *)) * delta); trace_type_max += delta; if (trace_types) { memcpy (vec, trace_types, trace_type_count * sizeof (trace_type_t *)); dfree (trace_types, MDL); } trace_types = vec; } trace_types [tptr -> index] = tptr; if (tptr -> index >= trace_type_count) trace_type_count = tptr -> index + 1; } trace_type_t *trace_type_register (const char *name, void *baggage, void (*have_packet) (trace_type_t *, unsigned, char *), void (*stop_tracing) (trace_type_t *), const char *file, int line) { trace_type_t *ttmp; unsigned slen = strlen (name); isc_result_t status; ttmp = dmalloc (sizeof *ttmp, file, line); if (!ttmp) return ttmp; ttmp -> index = -1; ttmp -> name = dmalloc (slen + 1, file, line); if (!ttmp -> name) { dfree (ttmp, file, line); return (trace_type_t *)0; } strcpy (ttmp -> name, name); ttmp -> have_packet = have_packet; ttmp -> stop_tracing = stop_tracing; if (traceoutfile) { status = trace_type_record (ttmp, slen, file, line); if (status != ISC_R_SUCCESS) { dfree (ttmp -> name, file, line); dfree (ttmp, file, line); return (trace_type_t *)0; } } else { ttmp -> next = new_trace_types; new_trace_types = ttmp; } return ttmp; } static isc_result_t trace_type_record (trace_type_t *ttmp, unsigned slen, const char *file, int line) { trace_index_mapping_t *tim; isc_result_t status; tim = dmalloc (slen + TRACE_INDEX_MAPPING_SIZE, file, line); if (!tim) return ISC_R_NOMEMORY; ttmp -> index = ++traceindex; trace_type_stash (ttmp); tim -> index = htonl (ttmp -> index); memcpy (tim -> name, ttmp -> name, slen); status = trace_write_packet (trace_types [0], slen + TRACE_INDEX_MAPPING_SIZE, (char *)tim, file, line); dfree (tim, file, line); return status; } /* Stop all registered trace types from trying to trace. */ void trace_stop (void) { int i; for (i = 0; i < trace_type_count; i++) if (trace_types [i] -> stop_tracing) (*(trace_types [i] -> stop_tracing)) (trace_types [i]); tracing_stopped = 1; } void trace_index_map_input (trace_type_t *ttype, unsigned length, char *buf) { trace_index_mapping_t *tmap; unsigned len; trace_type_t *tptr, **prev; if (length < TRACE_INDEX_MAPPING_SIZE) { log_error ("short trace index mapping"); return; } tmap = (trace_index_mapping_t *)buf; prev = &new_trace_types; for (tptr = new_trace_types; tptr; tptr = tptr -> next) { len = strlen (tptr -> name); if (len == length - TRACE_INDEX_MAPPING_SIZE && !memcmp (tptr -> name, tmap -> name, len)) { tptr -> index = ntohl (tmap -> index); trace_type_stash (tptr); *prev = tptr -> next; return; } prev = &tptr -> next; } log_error ("No registered trace type for type name %.*s", (int)length - TRACE_INDEX_MAPPING_SIZE, tmap -> name); return; } void trace_index_stop_tracing (trace_type_t *ttype) { } void trace_replay_init (void) { trace_playback_flag = 1; } void trace_file_replay (const char *filename) { tracepacket_t *tpkt = NULL; int status; char *buf = NULL; unsigned buflen; unsigned bufmax = 0; trace_type_t *ttype = NULL; isc_result_t result; int len; traceinfile = fopen (filename, "r"); if (!traceinfile) { log_error("Can't open tracefile %s: %m", filename); return; } #if defined (HAVE_SETFD) if (fcntl (fileno(traceinfile), F_SETFD, 1) < 0) log_error("Can't set close-on-exec on %s: %m", filename); #endif status = fread(&tracefile_header, 1, sizeof tracefile_header, traceinfile); if (status < sizeof tracefile_header) { if (ferror(traceinfile)) log_error("Error reading trace file header: %m"); else log_error("Short read on trace file header: %d %ld.", status, (long)(sizeof tracefile_header)); goto out; } tracefile_header.magic = ntohl(tracefile_header.magic); tracefile_header.version = ntohl(tracefile_header.version); tracefile_header.hlen = ntohl(tracefile_header.hlen); tracefile_header.phlen = ntohl(tracefile_header.phlen); if (tracefile_header.magic != TRACEFILE_MAGIC) { log_error("%s: not a dhcp trace file.", filename); goto out; } if (tracefile_header.version > TRACEFILE_VERSION) { log_error ("tracefile version %ld > current %ld.", (long int)tracefile_header.version, (long int)TRACEFILE_VERSION); goto out; } if (tracefile_header.phlen < sizeof *tpkt) { log_error("tracefile packet size too small - %ld < %ld", (long int)tracefile_header.phlen, (long int)sizeof *tpkt); goto out; } len = (sizeof tracefile_header) - tracefile_header.hlen; if (len < 0) { log_error("tracefile header size too small - %ld < %ld", (long int)tracefile_header.hlen, (long int)sizeof tracefile_header); goto out; } if (len > 0) { status = fseek(traceinfile, (long)len, SEEK_CUR); if (status < 0) { log_error("can't seek past header: %m"); goto out; } } tpkt = dmalloc((unsigned)tracefile_header.phlen, MDL); if (tpkt == NULL) { log_error ("can't allocate trace packet header."); goto out; } while ((result = trace_get_next_packet(&ttype, tpkt, &buf, &buflen, &bufmax)) == ISC_R_SUCCESS) { (*ttype->have_packet)(ttype, tpkt->length, buf); ttype = NULL; } out: fclose(traceinfile); if (buf != NULL) dfree(buf, MDL); if (tpkt != NULL) dfree(tpkt, MDL); } /* Get the next packet from the file. If ttp points to a nonzero pointer to a trace type structure, check the next packet to see if it's of the expected type, and back off if not. */ isc_result_t trace_get_next_packet (trace_type_t **ttp, tracepacket_t *tpkt, char **buf, unsigned *buflen, unsigned *bufmax) { trace_type_t *ttype; unsigned paylen; int status, curposok = 0; fpos_t curpos; while(1) { curposok = 0; status = fgetpos(traceinfile, &curpos); if (status < 0) { log_error("Can't save tracefile position: %m"); } else { curposok = 1; } status = fread(tpkt, 1, (size_t)tracefile_header.phlen, traceinfile); if (status < tracefile_header.phlen) { if (ferror(traceinfile)) log_error("Error reading trace packet header: " "%m"); else if (status == 0) return ISC_R_EOF; else log_error ("Short read on trace packet header:" " %ld %ld.", (long int)status, (long int)tracefile_header.phlen); return DHCP_R_PROTOCOLERROR; } /* Swap the packet. */ tpkt->type_index = ntohl(tpkt -> type_index); tpkt->length = ntohl(tpkt -> length); tpkt->when = ntohl(tpkt -> when); /* See if there's a handler for this packet type. */ if (tpkt->type_index < trace_type_count && trace_types[tpkt->type_index]) ttype = trace_types[tpkt->type_index]; else { log_error ("Trace packet with unknown index %ld", (long int)tpkt->type_index); return DHCP_R_PROTOCOLERROR; } /* * Determine if we should try to expire any timer events. * We do so if: * we aren't looking for a specific type of packet * we have a hook to use to update the timer * the timestamp on the packet doesn't match the current time * When we do so we rewind the file to the beginning of this * packet and then try for a new packet. This allows * any code triggered by a timeout to get the current packet * while we get the next one. */ if ((ttp != NULL) && (*ttp == NULL) && (tpkt->when != cur_tv.tv_sec) && (trace_set_time_hook != NULL)) { if (curposok == 0) { log_error("no curpos for fsetpos in " "tracefile"); return DHCP_R_PROTOCOLERROR; } status = fsetpos(traceinfile, &curpos); if (status < 0) { log_error("fsetpos in tracefile failed: %m"); return DHCP_R_PROTOCOLERROR; } (*trace_set_time_hook) (tpkt->when); continue; } break; } /* If we were supposed to get a particular kind of packet, check to see that we got the right kind. */ if (ttp && *ttp && ttype != *ttp) { log_error ("Read packet type %s when expecting %s", ttype -> name, (*ttp) -> name); status = fsetpos (traceinfile, &curpos); if (status < 0) { log_error ("fsetpos in tracefile failed: %m"); return DHCP_R_PROTOCOLERROR; } return ISC_R_UNEXPECTEDTOKEN; } paylen = tpkt -> length; if (paylen % 8) paylen += 8 - (tpkt -> length % 8); /* allocate a buffer if we need one or current buffer is too small */ if ((*buf == NULL) || (paylen > (*bufmax))) { if ((*buf)) dfree ((*buf), MDL); (*bufmax) = ((paylen + 1023) & ~1023U); (*buf) = dmalloc ((*bufmax), MDL); if (!(*buf)) { log_error ("Can't allocate input buffer sized %d", (*bufmax)); return ISC_R_NOMEMORY; } } status = fread ((*buf), 1, paylen, traceinfile); if (status < paylen) { if (ferror (traceinfile)) log_error ("Error reading trace payload: %m"); else log_error ("Short read on trace payload: %d %d.", status, paylen); return DHCP_R_PROTOCOLERROR; } /* Store the actual length of the payload. */ *buflen = tpkt -> length; if (ttp) *ttp = ttype; return ISC_R_SUCCESS; } isc_result_t trace_get_packet (trace_type_t **ttp, unsigned *buflen, char **buf) { tracepacket_t *tpkt; unsigned bufmax = 0; isc_result_t status; if (!buf || *buf) return DHCP_R_INVALIDARG; tpkt = dmalloc ((unsigned)tracefile_header.phlen, MDL); if (!tpkt) { log_error ("can't allocate trace packet header."); return ISC_R_NOMEMORY; } status = trace_get_next_packet (ttp, tpkt, buf, buflen, &bufmax); dfree (tpkt, MDL); return status; } /* Get a packet from the trace input file that contains a file with the specified name. We don't hunt for the packet - it should be the next packet in the tracefile. If it's not, or something else bad happens, return an error code. */ isc_result_t trace_get_file (trace_type_t *ttype, const char *filename, unsigned *len, char **buf) { fpos_t curpos; unsigned max = 0; tracepacket_t *tpkt; int status; isc_result_t result; /* Disallow some obvious bogosities. */ if (!buf || !len || *buf) return DHCP_R_INVALIDARG; /* Save file position in case of filename mismatch. */ status = fgetpos (traceinfile, &curpos); if (status < 0) log_error ("Can't save tracefile position: %m"); tpkt = dmalloc ((unsigned)tracefile_header.phlen, MDL); if (!tpkt) { log_error ("can't allocate trace packet header."); return ISC_R_NOMEMORY; } result = trace_get_next_packet (&ttype, tpkt, buf, len, &max); /* done with tpkt, free it */ dfree (tpkt, MDL); if (result != ISC_R_SUCCESS) { if (*buf) { dfree (*buf, MDL); *buf = NULL; } return result; } /* Make sure the filename is right. */ if (strcmp (filename, *buf)) { log_error ("Read file %s when expecting %s", *buf, filename); dfree (*buf, MDL); *buf = NULL; status = fsetpos (traceinfile, &curpos); if (status < 0) { log_error ("fsetpos in tracefile failed: %m"); return DHCP_R_PROTOCOLERROR; } return ISC_R_UNEXPECTEDTOKEN; } return ISC_R_SUCCESS; } #endif /* TRACING */ dhcp-4.4.1/m4/README000644 000765 000024 00000000207 13243301226 014112 0ustar00tmarkstaff000000 000000 Currently we have no m4 macros. This directory is here to accommodate some versions of ac_local() which treat its absence as an error. dhcp-4.4.1/includes/arpa/000755 000765 000024 00000000000 13243313033 015443 5ustar00tmarkstaff000000 000000 dhcp-4.4.1/includes/cdefs.h000644 000765 000024 00000005161 13243301226 015761 0ustar00tmarkstaff000000 000000 /* cdefs.h Standard C definitions... */ /* * Copyright (c) 1995 RadioMail Corporation. All rights reserved. * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1996-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * * This software was written for RadioMail Corporation by Ted Lemon * under a contract with Vixie Enterprises. Further modifications have * been made for Internet Systems Consortium under a contract * with Vixie Laboratories. */ #if !defined (__ISC_DHCP_CDEFS_H__) #define __ISC_DHCP_CDEFS_H__ /* Delete attributes if not gcc or not the right version of gcc. */ #if !defined(__GNUC__) || __GNUC__ < 2 || \ (__GNUC__ == 2 && __GNUC_MINOR__ < 5) || defined (darwin) #define __attribute__(x) #endif /* The following macro handles the case of unwanted return values. In * GCC one can specify an attribute for a function to generate a warning * if the return value of the function is ignored and one can't dispose of * the warning by the use of void. In conjunction with the use of -Werror * these warnings prohibit the compilation of the package. This macro * allows us to assign the return value to a variable and then ignore it. * * __attribute__((unused)) is added for avoiding another warning about set, * but unused variable. This is produced by unused-but-set-variable switch * that is enabled by default in gcc 4.6. */ #if !defined(__GNUC__) || (__GNUC__ < 4) #define IGNORE_RET(x) (void) x #else #define IGNORE_RET(x) \ do { \ int __attribute__((unused)) ignore_return ;\ ignore_return = x; \ } while (0) #endif /* This macro is defined to avoid unused-but-set-variable warning * that is enabled in gcc 4.6 */ #define IGNORE_UNUSED(x) { x = x; } #endif /* __ISC_DHCP_CDEFS_H__ */ dhcp-4.4.1/includes/config.h.in000644 000765 000024 00000022076 13243301226 016553 0ustar00tmarkstaff000000 000000 /* includes/config.h.in. Generated from configure.ac by autoheader. */ /* Define if building universal (internal helper macro) */ #undef AC_APPLE_UNIVERSAL_BUILD /* Define to support binary insertion of leases into queues. */ #undef BINARY_LEASES /* Define to compile debug-only DHCP software. */ #undef DEBUG /* Define to queue multiple DHCPACK replies per fsync. */ #undef DELAYED_ACK /* Define to 1 to include DHCPv4 over DHCPv6 support. */ #undef DHCP4o6 /* Define to BIG_ENDIAN for MSB (Motorola or SPARC CPUs) or LITTLE_ENDIAN for LSB (Intel CPUs). */ #undef DHCP_BYTE_ORDER /* Define to 1 to include DHCPv6 support. */ #undef DHCPv6 /* Define to any value to chroot() prior to loading config. */ #undef EARLY_CHROOT /* Define to include execute() config language support. */ #undef ENABLE_EXECUTE /* Define to include Failover Protocol support. */ #undef FAILOVER_PROTOCOL /* Define to nothing if C supports flexible array members, and to 1 if it does not. That way, with a declaration like `struct s { int n; double d[FLEXIBLE_ARRAY_MEMBER]; };', the struct hack can be used with pre-C99 compilers. When computing the size of such an object, don't use 'sizeof (struct s)' as it overestimates the size. Use 'offsetof (struct s, d)' instead. Don't use 'offsetof (struct s, d[0])', as this doesn't work with MSVC and with C++ compilers. */ #undef FLEXIBLE_ARRAY_MEMBER /* ATF framework specified? */ #undef HAVE_ATF /* Define to 1 to use the Berkeley Packet Filter interface code. */ #undef HAVE_BPF /* Define to 1 if you have the /dev/random or other configured file. */ #undef HAVE_DEV_RANDOM /* Define to 1 to use DLPI interface code. */ #undef HAVE_DLPI /* Define to 1 if you have the header file. */ #undef HAVE_IFADDRS_H /* Define to 1 if you have the `inet_ntop' function. */ #undef HAVE_INET_NTOP /* Define to 1 if you have the `inet_pton' function. */ #undef HAVE_INET_PTON /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_LDAP_H /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_TYPES_H /* Define to 1 to use the Linux Packet Filter interface code. */ #undef HAVE_LPF /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H /* Define to 1 if you have the header file. */ #undef HAVE_MICASA_MGMD_H /* Define to 1 if you have the header file. */ #undef HAVE_NET_IF6_H /* Define to 1 if you have the header file. */ #undef HAVE_NET_IF_DL_H /* Define to 1 if you have the header file. */ #undef HAVE_REGEX_H /* Define to 1 if the sockaddr structure has a length field. */ #undef HAVE_SA_LEN /* 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 you have the header file. */ #undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H /* Define to 1 if you have the `strlcat' function. */ #undef HAVE_STRLCAT /* Define to 1 if you have the header file. */ #undef HAVE_SYS_SOCKET_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 you have the header file. */ #undef HAVE_UNISTD_H /* Define to the string for a noreturn attribute. */ #undef ISC_DHCP_NORETURN /* Define to 1 if the system has 'struct if_laddrconf'. */ #undef ISC_PLATFORM_HAVEIF_LADDRCONF /* Define to 1 if the system has 'struct if_laddrreq'. */ #undef ISC_PLATFORM_HAVEIF_LADDRREQ /* Define to 1 if the system has 'struct lifnum'. */ #undef ISC_PLATFORM_HAVELIFNUM /* Define to 1 if the inet_aton() function is missing. */ #undef NEED_INET_ATON /* 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 any value to include Ari's PARANOIA patch. */ #undef PARANOIA /* Define to 1 to include relay port support. */ #undef RELAY_PORT /* The size of `struct iaddr *', as computed by sizeof. */ #undef SIZEOF_STRUCT_IADDR_P /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS /* Define to include server activity tracing support. */ #undef TRACING /* Define to 1 if ethernet devices are in /dev/net */ #undef USE_DEV_NET /* Define to include PIDs in syslog messages. */ #undef USE_LOG_PID /* Define to 1 to use the standard BSD socket API. */ #undef USE_SOCKETS /* Enable extensions on AIX 3, Interix. */ #ifndef _ALL_SOURCE # undef _ALL_SOURCE #endif /* Enable GNU extensions on systems that have them. */ #ifndef _GNU_SOURCE # undef _GNU_SOURCE #endif /* Enable threading extensions on Solaris. */ #ifndef _POSIX_PTHREAD_SEMANTICS # undef _POSIX_PTHREAD_SEMANTICS #endif /* Enable extensions on HP NonStop. */ #ifndef _TANDEM_SOURCE # undef _TANDEM_SOURCE #endif /* Enable general extensions on Solaris. */ #ifndef __EXTENSIONS__ # undef __EXTENSIONS__ #endif /* Define to 1 to enable IPv4 packet info support. */ #undef USE_V4_PKTINFO /* Version number of package */ #undef VERSION /* tpacket_auxdata.tp_vlan_tci present */ #undef VLAN_TCI_PRESENT /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel). */ #if defined AC_APPLE_UNIVERSAL_BUILD # if defined __BIG_ENDIAN__ # define WORDS_BIGENDIAN 1 # endif #else # ifndef WORDS_BIGENDIAN # undef WORDS_BIGENDIAN # endif #endif /* Define to 1 if on MINIX. */ #undef _MINIX /* File for dhclient6 leases. */ #undef _PATH_DHCLIENT6_DB /* File for dhclient6 process information. */ #undef _PATH_DHCLIENT6_PID /* File for dhclient leases. */ #undef _PATH_DHCLIENT_DB /* File for dhclient process information. */ #undef _PATH_DHCLIENT_PID /* File for dhcpd6 leases. */ #undef _PATH_DHCPD6_DB /* File for dhcpd6 process information. */ #undef _PATH_DHCPD6_PID /* Default file containing dhcpd configuration. */ #undef _PATH_DHCPD_CONF /* File for dhcpd leases. */ #undef _PATH_DHCPD_DB /* File for dhcpd process information. */ #undef _PATH_DHCPD_PID /* File for dhcrelay6 process information. */ #undef _PATH_DHCRELAY6_PID /* File for dhcrelay process information. */ #undef _PATH_DHCRELAY_PID /* Define to 2 if the system does not provide POSIX.1 features except with this defined. */ #undef _POSIX_1_SOURCE /* Define to 1 if you need to in order for `stat' and other things to work. */ #undef _POSIX_SOURCE /* Define for Solaris 2.5.1 so the uint32_t typedef from , , or is not used. If the typedef were allowed, the #define below would cause a syntax error. */ #undef _UINT32_T /* Define for Solaris 2.5.1 so the uint64_t typedef from , , or is not used. If the typedef were allowed, the #define below would cause a syntax error. */ #undef _UINT64_T /* Define for Solaris 2.5.1 so the uint8_t typedef from , , or is not used. If the typedef were allowed, the #define below would cause a syntax error. */ #undef _UINT8_T /* Define to the type of a signed integer type of width exactly 16 bits if such a type exists and the standard includes do not define it. */ #undef int16_t /* Define to the type of a signed integer type of width exactly 32 bits if such a type exists and the standard includes do not define it. */ #undef int32_t /* Define to the type of a signed integer type of width exactly 64 bits if such a type exists and the standard includes do not define it. */ #undef int64_t /* Define to the type of a signed integer type of width exactly 8 bits if such a type exists and the standard includes do not define it. */ #undef int8_t /* Define a type for 16-bit unsigned integers. */ #undef u_int16_t /* Define a type for 32-bit unsigned integers. */ #undef u_int32_t /* Define a type for 64-bit unsigned integers. */ #undef u_int64_t /* Define a type for 8-bit unsigned integers. */ #undef u_int8_t /* Define to the type of an unsigned integer type of width exactly 16 bits if such a type exists and the standard includes do not define it. */ #undef uint16_t /* Define to the type of an unsigned integer type of width exactly 32 bits if such a type exists and the standard includes do not define it. */ #undef uint32_t /* Define to the type of an unsigned integer type of width exactly 64 bits if such a type exists and the standard includes do not define it. */ #undef uint64_t /* Define to the type of an unsigned integer type of width exactly 8 bits if such a type exists and the standard includes do not define it. */ #undef uint8_t dhcp-4.4.1/includes/ctrace.h000644 000765 000024 00000005013 13243301226 016132 0ustar00tmarkstaff000000 000000 /* trace.h Definitions for dhcp tracing facility... */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 2001-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ typedef struct { struct in_addr primary_address; u_int32_t index; struct hardware hw_address; char name [IFNAMSIZ]; } trace_interface_packet_t; typedef struct { u_int32_t index; struct iaddr from; u_int16_t from_port; struct hardware hfrom; u_int8_t havehfrom; } trace_inpacket_t; typedef struct { u_int32_t index; struct iaddr from; struct iaddr to; u_int16_t to_port; struct hardware hto; u_int8_t havehto; } trace_outpacket_t; void trace_interface_register (trace_type_t *, struct interface_info *); void trace_interface_input (trace_type_t *, unsigned, char *); void trace_interface_stop (trace_type_t *); void trace_inpacket_stash (struct interface_info *, struct dhcp_packet *, unsigned, unsigned int, struct iaddr, struct hardware *); void trace_inpacket_input (trace_type_t *, unsigned, char *); void trace_inpacket_stop (trace_type_t *); void trace_outpacket_input (trace_type_t *, unsigned, char *); void trace_outpacket_stop (trace_type_t *); ssize_t trace_packet_send (struct interface_info *, struct packet *, struct dhcp_packet *, size_t, struct in_addr, struct sockaddr_in *, struct hardware *); void trace_icmp_input_input (trace_type_t *, unsigned, char *); void trace_icmp_input_stop (trace_type_t *); void trace_icmp_output_input (trace_type_t *, unsigned, char *); void trace_icmp_output_stop (trace_type_t *); void trace_seed_stash (trace_type_t *, unsigned); void trace_seed_input (trace_type_t *, unsigned, char *); void trace_seed_stop (trace_type_t *); dhcp-4.4.1/includes/dhcp.h000644 000765 000024 00000015063 13243301226 015615 0ustar00tmarkstaff000000 000000 /* dhcp.h Protocol structures... */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1995-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #ifndef DHCP_H #define DHCP_H #define DHCP_UDP_OVERHEAD (20 + /* IP header */ \ 8) /* UDP header */ #define DHCP_SNAME_LEN 64 #define DHCP_FILE_LEN 128 #define DHCP_FIXED_NON_UDP 236 #define DHCP_FIXED_LEN (DHCP_FIXED_NON_UDP + DHCP_UDP_OVERHEAD) /* Everything but options. */ #define BOOTP_MIN_LEN 300 #define DHCP_MTU_MAX 1500 #define DHCP_MTU_MIN 576 #define DHCP_MAX_OPTION_LEN (DHCP_MTU_MAX - DHCP_FIXED_LEN) #define DHCP_MIN_OPTION_LEN (DHCP_MTU_MIN - DHCP_FIXED_LEN) struct dhcp_packet { u_int8_t op; /* 0: Message opcode/type */ u_int8_t htype; /* 1: Hardware addr type (net/if_types.h) */ u_int8_t hlen; /* 2: Hardware addr length */ u_int8_t hops; /* 3: Number of relay agent hops from client */ u_int32_t xid; /* 4: Transaction ID */ u_int16_t secs; /* 8: Seconds since client started looking */ u_int16_t flags; /* 10: Flag bits */ struct in_addr ciaddr; /* 12: Client IP address (if already in use) */ struct in_addr yiaddr; /* 16: Client IP address */ struct in_addr siaddr; /* 18: IP address of next server to talk to */ struct in_addr giaddr; /* 20: DHCP relay agent IP address */ unsigned char chaddr [16]; /* 24: Client hardware address */ char sname [DHCP_SNAME_LEN]; /* 40: Server name */ char file [DHCP_FILE_LEN]; /* 104: Boot filename */ unsigned char options [DHCP_MAX_OPTION_LEN]; /* 212: Optional parameters (actual length dependent on MTU). */ }; /* BOOTP (rfc951) message types */ #define BOOTREQUEST 1 #define BOOTREPLY 2 /* Possible values for flags field... */ #define BOOTP_BROADCAST 32768L /* Possible values for hardware type (htype) field... */ #define HTYPE_ETHER 1 /* Ethernet 10Mbps */ #define HTYPE_IEEE802 6 /* IEEE 802.2 Token Ring... */ #define HTYPE_FDDI 8 /* FDDI... */ #define HTYPE_INFINIBAND 32 /* IP over Infiniband */ #define HTYPE_IPMP 255 /* IPMP - random hw address - there * is no standard for this so we * just steal a type */ /* Magic cookie validating dhcp options field (and bootp vendor extensions field). */ #define DHCP_OPTIONS_COOKIE "\143\202\123\143" /* DHCP Option codes: */ #define DHO_PAD 0 #define DHO_SUBNET_MASK 1 #define DHO_TIME_OFFSET 2 #define DHO_ROUTERS 3 #define DHO_TIME_SERVERS 4 #define DHO_NAME_SERVERS 5 #define DHO_DOMAIN_NAME_SERVERS 6 #define DHO_LOG_SERVERS 7 #define DHO_COOKIE_SERVERS 8 #define DHO_LPR_SERVERS 9 #define DHO_IMPRESS_SERVERS 10 #define DHO_RESOURCE_LOCATION_SERVERS 11 #define DHO_HOST_NAME 12 #define DHO_BOOT_SIZE 13 #define DHO_MERIT_DUMP 14 #define DHO_DOMAIN_NAME 15 #define DHO_SWAP_SERVER 16 #define DHO_ROOT_PATH 17 #define DHO_EXTENSIONS_PATH 18 #define DHO_IP_FORWARDING 19 #define DHO_NON_LOCAL_SOURCE_ROUTING 20 #define DHO_POLICY_FILTER 21 #define DHO_MAX_DGRAM_REASSEMBLY 22 #define DHO_DEFAULT_IP_TTL 23 #define DHO_PATH_MTU_AGING_TIMEOUT 24 #define DHO_PATH_MTU_PLATEAU_TABLE 25 #define DHO_INTERFACE_MTU 26 #define DHO_ALL_SUBNETS_LOCAL 27 #define DHO_BROADCAST_ADDRESS 28 #define DHO_PERFORM_MASK_DISCOVERY 29 #define DHO_MASK_SUPPLIER 30 #define DHO_ROUTER_DISCOVERY 31 #define DHO_ROUTER_SOLICITATION_ADDRESS 32 #define DHO_STATIC_ROUTES 33 #define DHO_TRAILER_ENCAPSULATION 34 #define DHO_ARP_CACHE_TIMEOUT 35 #define DHO_IEEE802_3_ENCAPSULATION 36 #define DHO_DEFAULT_TCP_TTL 37 #define DHO_TCP_KEEPALIVE_INTERVAL 38 #define DHO_TCP_KEEPALIVE_GARBAGE 39 #define DHO_NIS_DOMAIN 40 #define DHO_NIS_SERVERS 41 #define DHO_NTP_SERVERS 42 #define DHO_VENDOR_ENCAPSULATED_OPTIONS 43 #define DHO_NETBIOS_NAME_SERVERS 44 #define DHO_NETBIOS_DD_SERVER 45 #define DHO_NETBIOS_NODE_TYPE 46 #define DHO_NETBIOS_SCOPE 47 #define DHO_FONT_SERVERS 48 #define DHO_X_DISPLAY_MANAGER 49 #define DHO_DHCP_REQUESTED_ADDRESS 50 #define DHO_DHCP_LEASE_TIME 51 #define DHO_DHCP_OPTION_OVERLOAD 52 #define DHO_DHCP_MESSAGE_TYPE 53 #define DHO_DHCP_SERVER_IDENTIFIER 54 #define DHO_DHCP_PARAMETER_REQUEST_LIST 55 #define DHO_DHCP_MESSAGE 56 #define DHO_DHCP_MAX_MESSAGE_SIZE 57 #define DHO_DHCP_RENEWAL_TIME 58 #define DHO_DHCP_REBINDING_TIME 59 #define DHO_VENDOR_CLASS_IDENTIFIER 60 #define DHO_DHCP_CLIENT_IDENTIFIER 61 #define DHO_NWIP_DOMAIN_NAME 62 #define DHO_NWIP_SUBOPTIONS 63 #define DHO_USER_CLASS 77 #define DHO_FQDN 81 #define DHO_DHCP_AGENT_OPTIONS 82 #define DHO_AUTHENTICATE 90 /* RFC3118, was 210 */ #define DHO_CLIENT_LAST_TRANSACTION_TIME 91 #define DHO_ASSOCIATED_IP 92 #define DHO_SUBNET_SELECTION 118 /* RFC3011! */ #define DHO_DOMAIN_SEARCH 119 /* RFC3397 */ #define DHO_VIVCO_SUBOPTIONS 124 #define DHO_VIVSO_SUBOPTIONS 125 #define DHO_END 255 /* DHCP message types. */ #define DHCPDISCOVER 1 #define DHCPOFFER 2 #define DHCPREQUEST 3 #define DHCPDECLINE 4 #define DHCPACK 5 #define DHCPNAK 6 #define DHCPRELEASE 7 #define DHCPINFORM 8 #define DHCPLEASEQUERY 10 #define DHCPLEASEUNASSIGNED 11 #define DHCPLEASEUNKNOWN 12 #define DHCPLEASEACTIVE 13 /* Relay Agent Information option subtypes: */ #define RAI_CIRCUIT_ID 1 #define RAI_REMOTE_ID 2 #define RAI_AGENT_ID 3 #define RAI_LINK_SELECT 5 /* not yet assigned but next free value */ #define RAI_RELAY_PORT 19 /* FQDN suboptions: */ #define FQDN_NO_CLIENT_UPDATE 1 #define FQDN_SERVER_UPDATE 2 #define FQDN_ENCODED 3 #define FQDN_RCODE1 4 #define FQDN_RCODE2 5 #define FQDN_HOSTNAME 6 #define FQDN_DOMAINNAME 7 #define FQDN_FQDN 8 #define FQDN_SUBOPTION_COUNT 8 /* Enterprise Suboptions: */ #define VENDOR_ISC_SUBOPTIONS 2495 #endif /* DHCP_H */ dhcp-4.4.1/includes/dhcp6.h000644 000765 000024 00000022611 13243301226 015700 0ustar00tmarkstaff000000 000000 /* dhcp6.h DHCPv6 Protocol structures... */ /* * Copyright (c) 2006-2017 by 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ */ /* DHCPv6 Option codes: */ #define D6O_CLIENTID 1 /* RFC3315 */ #define D6O_SERVERID 2 #define D6O_IA_NA 3 #define D6O_IA_TA 4 #define D6O_IAADDR 5 #define D6O_ORO 6 #define D6O_PREFERENCE 7 #define D6O_ELAPSED_TIME 8 #define D6O_RELAY_MSG 9 /* Option code 10 unassigned. */ #define D6O_AUTH 11 #define D6O_UNICAST 12 #define D6O_STATUS_CODE 13 #define D6O_RAPID_COMMIT 14 #define D6O_USER_CLASS 15 #define D6O_VENDOR_CLASS 16 #define D6O_VENDOR_OPTS 17 #define D6O_INTERFACE_ID 18 #define D6O_RECONF_MSG 19 #define D6O_RECONF_ACCEPT 20 #define D6O_SIP_SERVERS_DNS 21 /* RFC3319 */ #define D6O_SIP_SERVERS_ADDR 22 /* RFC3319 */ #define D6O_NAME_SERVERS 23 /* RFC3646 */ #define D6O_DOMAIN_SEARCH 24 /* RFC3646 */ #define D6O_IA_PD 25 /* RFC3633 */ #define D6O_IAPREFIX 26 /* RFC3633 */ #define D6O_NIS_SERVERS 27 /* RFC3898 */ #define D6O_NISP_SERVERS 28 /* RFC3898 */ #define D6O_NIS_DOMAIN_NAME 29 /* RFC3898 */ #define D6O_NISP_DOMAIN_NAME 30 /* RFC3898 */ #define D6O_SNTP_SERVERS 31 /* RFC4075 */ #define D6O_INFORMATION_REFRESH_TIME 32 /* RFC4242 */ #define D6O_BCMCS_SERVER_D 33 /* RFC4280 */ #define D6O_BCMCS_SERVER_A 34 /* RFC4280 */ /* 35 is unassigned */ #define D6O_GEOCONF_CIVIC 36 /* RFC4776 */ #define D6O_REMOTE_ID 37 /* RFC4649 */ #define D6O_SUBSCRIBER_ID 38 /* RFC4580 */ #define D6O_CLIENT_FQDN 39 /* RFC4704 */ #define D6O_PANA_AGENT 40 /* paa-option */ #define D6O_NEW_POSIX_TIMEZONE 41 /* RFC4833 */ #define D6O_NEW_TZDB_TIMEZONE 42 /* RFC4833 */ #define D6O_ERO 43 /* RFC4994 */ #define D6O_LQ_QUERY 44 /* RFC5007 */ #define D6O_CLIENT_DATA 45 /* RFC5007 */ #define D6O_CLT_TIME 46 /* RFC5007 */ #define D6O_LQ_RELAY_DATA 47 /* RFC5007 */ #define D6O_LQ_CLIENT_LINK 48 /* RFC5007 */ #define D6O_MIP6_HNIDF 49 /* RFC6610 */ #define D6O_MIP6_VDINF 50 /* RFC6610 */ #define D6O_V6_LOST 51 /* RFC5223 */ #define D6O_CAPWAP_AC_V6 52 /* RFC5417 */ #define D6O_RELAY_ID 53 /* RFC5460 */ #define D6O_IPV6_ADDRESS_MOS 54 /* RFC5678 */ #define D6O_IPV6_FQDN_MOS 55 /* RFC5678 */ #define D6O_NTP_SERVER 56 /* RFC5908 */ #define D6O_V6_ACCESS_DOMAIN 57 /* RFC5986 */ #define D6O_SIP_UA_CS_LIST 58 /* RFC6011 */ #define D6O_BOOTFILE_URL 59 /* RFC5970 */ #define D6O_BOOTFILE_PARAM 60 /* RFC5970 */ #define D6O_CLIENT_ARCH_TYPE 61 /* RFC5970 */ #define D6O_NII 62 /* RFC5970 */ #define D6O_GEOLOCATION 63 /* RFC6225 */ #define D6O_AFTR_NAME 64 /* RFC6334 */ #define D6O_ERP_LOCAL_DOMAIN_NAME 65 /* RFC6440 */ #define D6O_RSOO 66 /* RFC6422 */ #define D6O_PD_EXCLUDE 67 /* RFC6603 */ #define D6O_VSS 68 /* RFC6607 */ #define D6O_MIP6_IDINF 69 /* RFC6610 */ #define D6O_MIP6_UDINF 70 /* RFC6610 */ #define D6O_MIP6_HNP 71 /* RFC6610 */ #define D6O_MIP6_HAA 72 /* RFC6610 */ #define D6O_MIP6_HAF 73 /* RFC6610 */ #define D6O_RDNSS_SELECTION 74 /* RFC6731 */ #define D6O_KRB_PRINCIPAL_NAME 75 /* RFC6784 */ #define D6O_KRB_REALM_NAME 76 /* RFC6784 */ #define D6O_KRB_DEFAULT_REALM_NAME 77 /* RFC6784 */ #define D6O_KRB_KDC 78 /* RFC6784 */ #define D6O_CLIENT_LINKLAYER_ADDR 79 /* RFC6939 */ #define D6O_LINK_ADDRESS 80 /* RFC6977 */ #define D6O_RADIUS 81 /* RFC7037 */ #define D6O_SOL_MAX_RT 82 /* RFC7083 */ #define D6O_INF_MAX_RT 83 /* RFC7083 */ #define D6O_ADDRSEL 84 /* RFC7078 */ #define D6O_ADDRSEL_TABLE 85 /* RFC7078 */ #define D6O_V6_PCP_SERVER 86 /* RFC7291 */ #define D6O_DHCPV4_MSG 87 /* RFC7341 */ #define D6O_DHCP4_O_DHCP6_SERVER 88 /* RFC7341 */ /* not yet assigned but next free value */ #define D6O_RELAY_SOURCE_PORT 135 /* I-D */ /* * Status Codes, from RFC 3315 section 24.4, and RFC 3633, 5007, 5460. */ #define STATUS_Success 0 #define STATUS_UnspecFail 1 #define STATUS_NoAddrsAvail 2 #define STATUS_NoBinding 3 #define STATUS_NotOnLink 4 #define STATUS_UseMulticast 5 #define STATUS_NoPrefixAvail 6 #define STATUS_UnknownQueryType 7 #define STATUS_MalformedQuery 8 #define STATUS_NotConfigured 9 #define STATUS_NotAllowed 10 #define STATUS_QueryTerminated 11 /* * DHCPv6 message types, defined in section 5.3 of RFC 3315 */ #define DHCPV6_SOLICIT 1 #define DHCPV6_ADVERTISE 2 #define DHCPV6_REQUEST 3 #define DHCPV6_CONFIRM 4 #define DHCPV6_RENEW 5 #define DHCPV6_REBIND 6 #define DHCPV6_REPLY 7 #define DHCPV6_RELEASE 8 #define DHCPV6_DECLINE 9 #define DHCPV6_RECONFIGURE 10 #define DHCPV6_INFORMATION_REQUEST 11 #define DHCPV6_RELAY_FORW 12 #define DHCPV6_RELAY_REPL 13 #define DHCPV6_LEASEQUERY 14 /* RFC5007 */ #define DHCPV6_LEASEQUERY_REPLY 15 /* RFC5007 */ #define DHCPV6_LEASEQUERY_DONE 16 /* RFC5460 */ #define DHCPV6_LEASEQUERY_DATA 17 /* RFC5460 */ #define DHCPV6_RECONFIGURE_REQUEST 18 /* RFC6977 */ #define DHCPV6_RECONFIGURE_REPLY 19 /* RFC6977 */ #define DHCPV6_DHCPV4_QUERY 20 /* RFC7341 */ #define DHCPV6_DHCPV4_RESPONSE 21 /* RFC7341 */ extern const char *dhcpv6_type_names[]; extern const int dhcpv6_type_name_max; /* DUID type definitions (RFC3315 section 9). */ #define DUID_LLT 1 #define DUID_EN 2 #define DUID_LL 3 #define DUID_UUID 4 /* RFC6355 */ /* Offsets into IA_*'s where Option spaces commence. */ #define IA_NA_OFFSET 12 /* IAID, T1, T2, all 4 octets each */ #define IA_TA_OFFSET 4 /* IAID only, 4 octets */ #define IA_PD_OFFSET 12 /* IAID, T1, T2, all 4 octets each */ /* Offset into IAADDR's where Option spaces commence. */ #define IAADDR_OFFSET 24 /* Offset into IAPREFIX's where Option spaces commence. */ #define IAPREFIX_OFFSET 25 /* Offset into LQ_QUERY's where Option spaces commence. */ #define LQ_QUERY_OFFSET 17 /* * DHCPv6 well-known multicast addressess, from section 5.1 of RFC 3315 */ #define All_DHCP_Relay_Agents_and_Servers "FF02::1:2" #define All_DHCP_Servers "FF05::1:3" /* * DHCPv6 Retransmission Constants (RFC3315 section 5.5, RFC 5007) */ #define SOL_MAX_DELAY 1 #define SOL_TIMEOUT 1 #define SOL_MAX_RT 120 #define REQ_TIMEOUT 1 #define REQ_MAX_RT 30 #define REQ_MAX_RC 10 #define CNF_MAX_DELAY 1 #define CNF_TIMEOUT 1 #define CNF_MAX_RT 4 #define CNF_MAX_RD 10 #define REN_TIMEOUT 10 #define REN_MAX_RT 600 #define REB_TIMEOUT 10 #define REB_MAX_RT 600 #define INF_MAX_DELAY 1 #define INF_TIMEOUT 1 #define INF_MAX_RT 120 #define REL_TIMEOUT 1 #define REL_MAX_RC 5 #define DEC_TIMEOUT 1 #define DEC_MAX_RC 5 #define REC_TIMEOUT 2 #define REC_MAX_RC 8 #define HOP_COUNT_LIMIT 32 #define LQ6_TIMEOUT 1 #define LQ6_MAX_RT 10 #define LQ6_MAX_RC 5 /* * Normal packet format, defined in section 6 of RFC 3315 */ struct dhcpv6_packet { unsigned char msg_type; unsigned char transaction_id[3]; unsigned char options[FLEXIBLE_ARRAY_MEMBER]; }; /* Offset into DHCPV6 Reply packets where Options spaces commence. */ #define REPLY_OPTIONS_INDEX 4 /* * Relay packet format, defined in section 7 of RFC 3315 */ struct dhcpv6_relay_packet { unsigned char msg_type; unsigned char hop_count; unsigned char link_address[16]; unsigned char peer_address[16]; unsigned char options[FLEXIBLE_ARRAY_MEMBER]; }; #define MAX_V6RELAY_HOPS 32 /* * DHCPv4-over-DHCPv6 packet format, defined in RFC 4731 */ struct dhcpv4_over_dhcpv6_packet { unsigned char msg_type; unsigned char flags[3]; unsigned char options[FLEXIBLE_ARRAY_MEMBER]; }; #define DHCP4O6_QUERY_UNICAST 128 /* DHCPv4-over-DHCPv6 ISC vendor suboptions */ #define D4O6_INTERFACE 60000 #define D4O6_SRC_ADDRESS 60001 /* Leasequery query-types (RFC 5007, 5460) */ #define LQ6QT_BY_ADDRESS 1 #define LQ6QT_BY_CLIENTID 2 #define LQ6QT_BY_RELAY_ID 3 #define LQ6QT_BY_LINK_ADDRESS 4 #define LQ6QT_BY_REMOTE_ID 5 /* * DUID time starts 2000-01-01. * This constant is the number of seconds since 1970-01-01, * when the Unix epoch began. */ #define DUID_TIME_EPOCH 946684800 /* Information-Request Time option (RFC 4242) */ #define IRT_DEFAULT 86400 #define IRT_MINIMUM 600 #define EUI_64_ID_LEN 12 /* 2 for duid-type, 2 for hardware type, 8 for ID */ #define IAID_LEN 4 /* Offsets with iasubopt wire data of data values for IA_NA and TA */ #define IASUBOPT_NA_ADDR_OFFSET 0 #define IASUBOPT_NA_PREF_OFFSET 16 #define IASUBOPT_NA_VALID_OFFSET 20 #define IASUBOPT_NA_LEN 24 /* Offsets with iasubopt wire data of data values for PD */ #define IASUBOPT_PD_PREF_OFFSET 0 #define IASUBOPT_PD_VALID_OFFSET 4 #define IASUBOPT_PD_PREFLEN_OFFSET 8 #define IASUBOPT_PD_PREFIX_OFFSET 9 #define IASUBOPT_PD_LEN 25 dhcp-4.4.1/includes/dhcpd.h000644 000765 000024 00000410404 13243301226 015757 0ustar00tmarkstaff000000 000000 /* dhcpd.h Definitions for dhcpd... */ /* * Copyright (c) 2004-2018 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1996-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ /*! \file includes/dhcpd.h */ #include "config.h" #ifndef __CYGWIN32__ #include #include #include #include #include #include #include #else #define fd_set cygwin_fd_set #include #endif #include #include #include #include #include #include #include #include #include #include #include #undef FDDI #include #include #if HAVE_NET_IF_DL_H # include #endif #include #include "cdefs.h" #include "osdep.h" #include "arpa/nameser.h" #include "ns_name.h" struct hash_table; typedef struct hash_table group_hash_t; typedef struct hash_table universe_hash_t; typedef struct hash_table option_name_hash_t; typedef struct hash_table option_code_hash_t; typedef struct hash_table dns_zone_hash_t; typedef struct hash_table lease_ip_hash_t; typedef struct hash_table lease_id_hash_t; typedef struct hash_table host_hash_t; typedef struct hash_table class_hash_t; typedef time_t TIME; #ifndef EOL #define EOL '\n' #endif #include #include #include "dhcp.h" #include "dhcp6.h" #include "statement.h" #include "tree.h" #include "inet.h" #include "dhctoken.h" #include #if defined(LDAP_CONFIGURATION) # include # include /* for uname() */ #endif #if !defined (BYTE_NAME_HASH_SIZE) # define BYTE_NAME_HASH_SIZE 401 /* Default would be ridiculous. */ #endif #if !defined (BYTE_CODE_HASH_SIZE) # define BYTE_CODE_HASH_SIZE 254 /* Default would be ridiculous. */ #endif /* Although it is highly improbable that a 16-bit option space might * actually use 2^16 actual defined options, it is the worst case * scenario we must prepare for. Having 4 options per bucket in this * case is pretty reasonable. */ #if !defined (WORD_NAME_HASH_SIZE) # define WORD_NAME_HASH_SIZE 20479 #endif #if !defined (WORD_CODE_HASH_SIZE) # define WORD_CODE_HASH_SIZE 16384 #endif /* Not only is it improbable that the 32-bit spaces might actually use 2^32 * defined options, it is infeasible. It would be best for this kind of * space to be dynamically sized. Instead we size it at the word hash's * level. */ #if !defined (QUAD_NAME_HASH_SIZE) # define QUAD_NAME_HASH_SIZE WORD_NAME_HASH_SIZE #endif #if !defined (QUAD_CODE_HASH_SIZE) # define QUAD_CODE_HASH_SIZE WORD_CODE_HASH_SIZE #endif #if !defined (DNS_HASH_SIZE) # define DNS_HASH_SIZE 0 /* Default. */ #endif /* Default size to use for name/code hashes on user-defined option spaces. */ #if !defined (DEFAULT_SPACE_HASH_SIZE) # define DEFAULT_SPACE_HASH_SIZE 11 #endif #if !defined (NWIP_HASH_SIZE) # define NWIP_HASH_SIZE 17 /* A really small table. */ #endif #if !defined (FQDN_HASH_SIZE) # define FQDN_HASH_SIZE 13 /* A ridiculously small table. */ #endif /* I really doubt a given installation is going to have more than a few * hundred vendors involved. */ #if !defined (VIVCO_HASH_SIZE) # define VIVCO_HASH_SIZE 127 #endif #if !defined (VIVSO_HASH_SIZE) # define VIVSO_HASH_SIZE VIVCO_HASH_SIZE #endif #if !defined (VSIO_HASH_SIZE) # define VSIO_HASH_SIZE VIVCO_HASH_SIZE #endif #if !defined (VIV_ISC_HASH_SIZE) # define VIV_ISC_HASH_SIZE 3 /* An incredulously small table. */ #endif #if !defined (UNIVERSE_HASH_SIZE) # define UNIVERSE_HASH_SIZE 13 /* A really small table. */ #endif #if !defined (GROUP_HASH_SIZE) # define GROUP_HASH_SIZE 0 /* Default. */ #endif /* At least one person has indicated they use ~20k host records. */ #if !defined (HOST_HASH_SIZE) # define HOST_HASH_SIZE 22501 #endif /* We have user reports of use of ISC DHCP numbering leases in the 200k's. * * We also have reports of folks using 10.0/8 as a dynamic range. The * following is something of a compromise between the two. At the ~2-3 * hundred thousand leases, there's ~2-3 leases to search in each bucket. */ #if !defined (LEASE_HASH_SIZE) # define LEASE_HASH_SIZE 100003 #endif /* It is not known what the worst case subclass hash size is. We estimate * high, I think. */ #if !defined (SCLASS_HASH_SIZE) # define SCLASS_HASH_SIZE 12007 #endif #if !defined (AGENT_HASH_SIZE) # define AGENT_HASH_SIZE 11 /* A really small table. */ #endif /* The server hash size is used for both names and codes. There aren't * many (roughly 50 at the moment), so we use a smaller table. If we * use a 1:1 table size, then we get name collisions due to poor name * hashing. So we use double the space we need, which drastically * reduces collisions. */ #if !defined (SERVER_HASH_SIZE) # define SERVER_HASH_SIZE (2*(sizeof(server_options) / sizeof(struct option))) #endif /* How many options are likely to appear in a single packet? */ #if !defined (OPTION_HASH_SIZE) # define OPTION_HASH_SIZE 17 # define OPTION_HASH_PTWO 32 /* Next power of two above option hash. */ # define OPTION_HASH_EXP 5 /* The exponent for that power of two. */ #endif #define compute_option_hash(x) \ (((x) & (OPTION_HASH_PTWO - 1)) + \ (((x) >> OPTION_HASH_EXP) & \ (OPTION_HASH_PTWO - 1))) % OPTION_HASH_SIZE; /* Lease queue information. We have two ways of storing leases. * The original is a linear linked list which is slower but uses * less memory while the other adds a binary array on top of that * list to make insertions faster. We define several macros * based on which is in use to allow the code to be cleaner by * avoiding #ifdefs. * * POOL_DESTROYP is used for cleanup */ #if !defined (BINARY_LEASES) #define LEASE_STRUCT struct lease * #define LEASE_STRUCT_PTR struct lease ** #define LEASE_GET_FIRST(LQ) LQ #define LEASE_GET_FIRSTP(LQ) *(LQ) #define LEASE_GET_NEXT(LQ, LEASE) LEASE->next #define LEASE_GET_NEXTP(LQ, LEASE) LEASE->next #define LEASE_INSERTP(LQ, LEASE) lease_insert(LQ, LEASE) #define LEASE_REMOVEP(LQ, LEASE) lease_remove(LQ, LEASE) #define LEASE_NOT_EMPTY(LQ) LQ #define LEASE_NOT_EMPTYP(LQ) *LQ #define POOL_DESTROYP(LQ) lease_remove_all(LQ) #else #define LEASE_STRUCT struct leasechain #define LEASE_STRUCT_PTR struct leasechain * #define LEASE_GET_FIRST(LQ) lc_get_first_lease(&LQ) #define LEASE_GET_FIRSTP(LQ) lc_get_first_lease(LQ) #define LEASE_GET_NEXT(LQ, LEASE) lc_get_next(&LQ, LEASE) #define LEASE_GET_NEXTP(LQ, LEASE) lc_get_next(LQ, LEASE) #define LEASE_INSERTP(LQ, LEASE) lc_add_sorted_lease(LQ, LEASE) #define LEASE_REMOVEP(LQ, LEASE) lc_unlink_lease(LQ, LEASE) #define LEASE_NOT_EMPTY(LQ) lc_not_empty(&LQ) #define LEASE_NOT_EMPTYP(LQ) lc_not_empty(LQ) #define POOL_DESTROYP(LQ) lc_delete_all(LQ) #endif enum dhcp_shutdown_state { shutdown_listeners, shutdown_omapi_connections, shutdown_drop_omapi_connections, shutdown_dhcp, shutdown_done }; /* Client FQDN option, failover FQDN option, etc. */ typedef struct { u_int8_t codes [2]; unsigned length; u_int8_t *data; } ddns_fqdn_t; #include "failover.h" /* A parsing context. */ struct parse { int lexline; int lexchar; char *token_line; char *prev_line; char *cur_line; const char *tlname; int eol_token; /* * In order to give nice output when we have a parsing error * in our file, we keep track of where we are in the line so * that we can show the user. * * We need to keep track of two lines, because we can look * ahead, via the "peek" function, to the next line sometimes. * * The "line1" and "line2" variables act as buffers for this * information. The "lpos" variable tells us where we are in the * line. * * When we "put back" a character from the parsing context, we * do not want to have the character appear twice in the error * output. So, we set a flag, the "ugflag", which the * get_char() function uses to check for this condition. */ char line1 [81]; char line2 [81]; int lpos; int line; int tlpos; int tline; enum dhcp_token token; int ugflag; char *tval; int tlen; char tokbuf [1500]; int warnings_occurred; int file; char *inbuf; size_t bufix, buflen; size_t bufsiz; struct parse *saved_state; #if defined(LDAP_CONFIGURATION) /* * LDAP configuration uses a call-back to iteratively read config * off of the LDAP repository. * XXX: The token stream can not be rewound reliably, so this must * be addressed for DHCPv6 support. */ int (*read_function)(struct parse *); #endif }; /* Variable-length array of data. */ struct string_list { struct string_list *next; char string [1]; }; /* A name server, from /etc/resolv.conf. */ struct name_server { struct name_server *next; struct sockaddr_in addr; TIME rcdate; }; /* A domain search list element. */ struct domain_search_list { struct domain_search_list *next; char *domain; TIME rcdate; }; /* Option tag structures are used to build chains of option tags, for when we're sure we're not going to have enough of them to justify maintaining an array. */ struct option_tag { struct option_tag *next; u_int8_t data [1]; }; /* An agent option structure. We need a special structure for the Relay Agent Information option because if more than one appears in a message, we have to keep them separate. */ struct agent_options { struct agent_options *next; int length; struct option_tag *first; }; struct option_cache { int refcnt; struct option_cache *next; struct expression *expression; struct option *option; struct data_string data; #define OPTION_HAD_NULLS 0x00000001 u_int32_t flags; }; struct option_state { int refcnt; int universe_count; int site_universe; int site_code_min; void *universes [1]; }; /* A dhcp packet and the pointers to its option values. */ struct packet { struct dhcp_packet *raw; int refcnt; unsigned packet_length; int packet_type; unsigned char dhcpv6_msg_type; /* DHCPv6 message type */ /* DHCPv6 transaction ID */ unsigned char dhcpv6_transaction_id[3]; /* DHCPv6 relay information */ unsigned char dhcpv6_hop_count; struct in6_addr dhcpv6_link_address; struct in6_addr dhcpv6_peer_address; /* DHCPv6 packet containing this one, or NULL if none */ struct packet *dhcpv6_container_packet; /* DHCPv4-over-DHCPv6 flags */ unsigned char dhcp4o6_flags[3]; /* DHCPv4-over-DHCPv6 response, or NULL */ struct data_string *dhcp4o6_response; int options_valid; int client_port; struct iaddr client_addr; struct interface_info *interface; /* Interface on which packet was received. */ struct hardware *haddr; /* Physical link address of local sender (maybe gateway). */ /* Information for relay agent options (see draft-ietf-dhc-agent-options-xx.txt). */ u_int8_t *circuit_id; /* Circuit ID of client connection. */ int circuit_id_len; u_int8_t *remote_id; /* Remote ID of client. */ int remote_id_len; int got_requested_address; /* True if client sent the dhcp-requested-address option. */ struct shared_network *shared_network; struct option_state *options; #if !defined (PACKET_MAX_CLASSES) # define PACKET_MAX_CLASSES 5 #endif int class_count; struct class *classes [PACKET_MAX_CLASSES]; int known; int authenticated; /* If we stash agent options onto the packet option state, to pretend * options we got in a previous exchange were still there, we need * to signal this in a reliable way. */ isc_boolean_t agent_options_stashed; /* * ISC_TRUE if packet received unicast (as opposed to multicast). * Only used in DHCPv6. */ isc_boolean_t unicast; /* Propagates server value SV_ECHO_CLIENT_ID so it is available * in cons_options() */ int sv_echo_client_id; /* Relay port check */ isc_boolean_t relay_source_port; }; /* * A network interface's MAC address. * 20 bytes for the hardware address * and 1 byte for the type tag */ #define HARDWARE_ADDR_LEN 20 struct hardware { u_int8_t hlen; u_int8_t hbuf[HARDWARE_ADDR_LEN + 1]; }; #if defined(LDAP_CONFIGURATION) # define LDAP_BUFFER_SIZE 8192 # define LDAP_METHOD_STATIC 0 # define LDAP_METHOD_DYNAMIC 1 #if defined (LDAP_USE_SSL) # define LDAP_SSL_OFF 0 # define LDAP_SSL_ON 1 # define LDAP_SSL_TLS 2 # define LDAP_SSL_LDAPS 3 #endif /* This is a tree of the current configuration we are building from LDAP */ struct ldap_config_stack { LDAPMessage * res; /* Pointer returned from ldap_search */ LDAPMessage * ldent; /* Current item in LDAP that we're processing. in res */ int close_brace; /* Put a closing } after we're through with this item */ int processed; /* We set this flag if this base item has been processed. After this base item is processed, we can start processing the children */ struct ldap_config_stack *children; struct ldap_config_stack *next; }; #endif typedef enum { server_startup = 0, server_running = 1, server_shutdown = 2, server_hibernate = 3, server_awaken = 4 } control_object_state_t; typedef struct { OMAPI_OBJECT_PREAMBLE; control_object_state_t state; } dhcp_control_object_t; /* Lease states: */ #define FTS_FREE 1 #define FTS_ACTIVE 2 #define FTS_EXPIRED 3 #define FTS_RELEASED 4 #define FTS_ABANDONED 5 #define FTS_RESET 6 #define FTS_BACKUP 7 typedef u_int8_t binding_state_t; /* FTS_LAST is the highest value that is valid for a lease binding state. */ #define FTS_LAST FTS_BACKUP /* * A block for the on statements so we can share the structure * between v4 and v6 */ struct on_star { struct executable_statement *on_expiry; struct executable_statement *on_commit; struct executable_statement *on_release; }; /* A dhcp lease declaration structure. */ struct lease { OMAPI_OBJECT_PREAMBLE; struct lease *next; #if defined (BINARY_LEASES) struct lease *prev; struct leasechain *lc; #endif struct lease *n_uid, *n_hw; struct iaddr ip_addr; TIME starts, ends, sort_time; #if defined (BINARY_LEASES) long int sort_tiebreaker; #endif char *client_hostname; struct binding_scope *scope; struct host_decl *host; struct subnet *subnet; struct pool *pool; struct class *billing_class; struct option_chain_head *agent_options; /* insert the structure directly */ struct on_star on_star; unsigned char *uid; unsigned short uid_len; unsigned short uid_max; unsigned char uid_buf [7]; struct hardware hardware_addr; u_int8_t flags; # define STATIC_LEASE 1 # define BOOTP_LEASE 2 # define RESERVED_LEASE 4 # define MS_NULL_TERMINATION 8 # define ON_UPDATE_QUEUE 16 # define ON_ACK_QUEUE 32 # define ON_QUEUE (ON_UPDATE_QUEUE | ON_ACK_QUEUE) # define UNICAST_BROADCAST_HACK 64 # define ON_DEFERRED_QUEUE 128 /* Persistent flags are to be preserved on a given lease structure. */ # define PERSISTENT_FLAGS (ON_ACK_QUEUE | ON_UPDATE_QUEUE) /* Ephemeral flags are to be preserved on a given lease (copied etc). */ # define EPHEMERAL_FLAGS (MS_NULL_TERMINATION | \ UNICAST_BROADCAST_HACK | \ RESERVED_LEASE | \ BOOTP_LEASE) /* * The lease's binding state is its current state. The next binding * state is the next state this lease will move into by expiration, * or timers in general. The desired binding state is used on lease * updates; the caller is attempting to move the lease to the desired * binding state (and this may either succeed or fail, so the binding * state must be preserved). * * The 'rewind' binding state is used in failover processing. It * is used for an optimization when out of communications; it allows * the server to "rewind" a lease to the previous state acknowledged * by the peer, and progress forward from that point. */ binding_state_t binding_state; binding_state_t next_binding_state; binding_state_t desired_binding_state; binding_state_t rewind_binding_state; struct lease_state *state; /* * 'tsfp' is more of an 'effective' tsfp. It may be calculated from * stos+mclt for example if it's an expired lease and the server is * in partner-down state. 'atsfp' is zeroed whenever a lease is * updated - and only set when the peer acknowledges it. This * ensures every state change is transmitted. */ TIME tstp; /* Time sent to partner. */ TIME tsfp; /* Time sent from partner. */ TIME atsfp; /* Actual time sent from partner. */ TIME cltt; /* Client last transaction time. */ u_int32_t last_xid; /* XID we sent in this lease's BNDUPD */ struct lease *next_pending; /* * A pointer to the state of the ddns update for this lease. * It should be set while the update is in progress and cleared * when the update finishes. It can be used to cancel the * update if we want to do a different update. */ struct dhcp_ddns_cb *ddns_cb; /* Set when a lease has been disqualified for cache-threshold reuse */ unsigned short cannot_reuse; }; struct lease_state { struct lease_state *next; struct interface_info *ip; struct packet *packet; /* The incoming packet. */ TIME offered_expiry; struct option_state *options; struct data_string parameter_request_list; int max_message_size; unsigned char expiry[4], renewal[4], rebind[4]; struct data_string filename, server_name; int got_requested_address; int got_server_identifier; struct shared_network *shared_network; /* Shared network of interface on which request arrived. */ u_int32_t xid; u_int16_t secs; u_int16_t bootp_flags; struct in_addr ciaddr; struct in_addr siaddr; struct in_addr giaddr; u_int8_t hops; u_int8_t offer; struct iaddr from; }; #define ROOT_GROUP 0 #define HOST_DECL 1 #define SHARED_NET_DECL 2 #define SUBNET_DECL 3 #define CLASS_DECL 4 #define GROUP_DECL 5 #define POOL_DECL 6 /* Possible modes in which discover_interfaces can run. */ #define DISCOVER_RUNNING 0 #define DISCOVER_SERVER 1 #define DISCOVER_UNCONFIGURED 2 #define DISCOVER_RELAY 3 #define DISCOVER_SERVER46 4 #define DISCOVER_REQUESTED 5 /* DDNS_UPDATE_STYLE enumerations. */ #define DDNS_UPDATE_STYLE_NONE 0 #define DDNS_UPDATE_STYLE_AD_HOC 1 #define DDNS_UPDATE_STYLE_INTERIM 2 #define DDNS_UPDATE_STYLE_STANDARD 3 /* Server option names. */ #define SV_DEFAULT_LEASE_TIME 1 #define SV_MAX_LEASE_TIME 2 #define SV_MIN_LEASE_TIME 3 #define SV_BOOTP_LEASE_CUTOFF 4 #define SV_BOOTP_LEASE_LENGTH 5 #define SV_BOOT_UNKNOWN_CLIENTS 6 #define SV_DYNAMIC_BOOTP 7 #define SV_ALLOW_BOOTP 8 #define SV_ALLOW_BOOTING 9 #define SV_ONE_LEASE_PER_CLIENT 10 #define SV_GET_LEASE_HOSTNAMES 11 #define SV_USE_HOST_DECL_NAMES 12 #define SV_USE_LEASE_ADDR_FOR_DEFAULT_ROUTE 13 #define SV_MIN_SECS 14 #define SV_FILENAME 15 #define SV_SERVER_NAME 16 #define SV_NEXT_SERVER 17 #define SV_AUTHORITATIVE 18 #define SV_VENDOR_OPTION_SPACE 19 #define SV_ALWAYS_REPLY_RFC1048 20 #define SV_SITE_OPTION_SPACE 21 #define SV_ALWAYS_BROADCAST 22 #define SV_DDNS_DOMAIN_NAME 23 #define SV_DDNS_HOST_NAME 24 #define SV_DDNS_REV_DOMAIN_NAME 25 #define SV_LEASE_FILE_NAME 26 #define SV_PID_FILE_NAME 27 #define SV_DUPLICATES 28 #define SV_DECLINES 29 #define SV_DDNS_UPDATES 30 #define SV_OMAPI_PORT 31 #define SV_LOCAL_PORT 32 #define SV_LIMITED_BROADCAST_ADDRESS 33 #define SV_REMOTE_PORT 34 #define SV_LOCAL_ADDRESS 35 #define SV_OMAPI_KEY 36 #define SV_STASH_AGENT_OPTIONS 37 #define SV_DDNS_TTL 38 #define SV_DDNS_UPDATE_STYLE 39 #define SV_CLIENT_UPDATES 40 #define SV_UPDATE_OPTIMIZATION 41 #define SV_PING_CHECKS 42 #define SV_UPDATE_STATIC_LEASES 43 #define SV_LOG_FACILITY 44 #define SV_DO_FORWARD_UPDATES 45 #define SV_PING_TIMEOUT 46 #define SV_RESERVE_INFINITE 47 #define SV_DDNS_CONFLICT_DETECT 48 #define SV_LEASEQUERY 49 #define SV_ADAPTIVE_LEASE_TIME_THRESHOLD 50 #define SV_DO_REVERSE_UPDATES 51 #define SV_FQDN_REPLY 52 #define SV_PREFER_LIFETIME 53 #define SV_DHCPV6_LEASE_FILE_NAME 54 #define SV_DHCPV6_PID_FILE_NAME 55 #define SV_LIMIT_ADDRS_PER_IA 56 #define SV_LIMIT_PREFS_PER_IA 57 #define SV_DELAYED_ACK 58 #define SV_MAX_ACK_DELAY 59 #if defined(LDAP_CONFIGURATION) # define SV_LDAP_SERVER 60 # define SV_LDAP_PORT 61 # define SV_LDAP_USERNAME 62 # define SV_LDAP_PASSWORD 63 # define SV_LDAP_BASE_DN 64 # define SV_LDAP_METHOD 65 # define SV_LDAP_DEBUG_FILE 66 # define SV_LDAP_DHCP_SERVER_CN 67 # define SV_LDAP_REFERRALS 68 #if defined (LDAP_USE_SSL) # define SV_LDAP_SSL 69 # define SV_LDAP_TLS_REQCERT 70 # define SV_LDAP_TLS_CA_FILE 71 # define SV_LDAP_TLS_CA_DIR 72 # define SV_LDAP_TLS_CERT 73 # define SV_LDAP_TLS_KEY 74 # define SV_LDAP_TLS_CRLCHECK 75 # define SV_LDAP_TLS_CIPHERS 76 # define SV_LDAP_TLS_RANDFILE 77 #endif # define SV_LDAP_INIT_RETRY 178 #if defined (LDAP_USE_GSSAPI) # define SV_LDAP_GSSAPI_KEYTAB 179 # define SV_LDAP_GSSAPI_PRINCIPAL 180 #endif #endif #define SV_CACHE_THRESHOLD 78 #define SV_DONT_USE_FSYNC 79 #define SV_DDNS_LOCAL_ADDRESS4 80 #define SV_DDNS_LOCAL_ADDRESS6 81 #define SV_IGNORE_CLIENT_UIDS 82 #define SV_LOG_THRESHOLD_LOW 83 #define SV_LOG_THRESHOLD_HIGH 84 #define SV_ECHO_CLIENT_ID 85 #define SV_SERVER_ID_CHECK 86 #define SV_PREFIX_LEN_MODE 87 #define SV_DHCPV6_SET_TEE_TIMES 88 #define SV_ABANDON_LEASE_TIME 89 #ifdef EUI_64 #define SV_USE_EUI_64 90 #define SV_PERSIST_EUI_64_LEASES 91 #endif #if defined (FAILOVER_PROTOCOL) #define SV_CHECK_SECS_BYTE_ORDER 91 #endif #define SV_DDNS_DUAL_STACK_MIXED_MODE 92 #define SV_DDNS_GUARD_ID_MUST_MATCH 93 #define SV_DDNS_OTHER_GUARD_IS_DYNAMIC 94 #define SV_RELEASE_ON_ROAM 95 #define SV_LOCAL_ADDRESS6 96 #define SV_BIND_LOCAL_ADDRESS6 97 #if !defined (DEFAULT_PING_TIMEOUT) # define DEFAULT_PING_TIMEOUT 1 #endif #if !defined (DEFAULT_DELAYED_ACK) # define DEFAULT_DELAYED_ACK 0 /* default 0 disables delayed acking */ #endif #if !defined (DEFAULT_ACK_DELAY_SECS) # define DEFAULT_ACK_DELAY_SECS 0 #endif #if !defined (DEFAULT_ACK_DELAY_USECS) # define DEFAULT_ACK_DELAY_USECS 250000 /* 1/4 of a second */ #endif #if !defined (DEFAULT_MIN_ACK_DELAY_USECS) # define DEFAULT_MIN_ACK_DELAY_USECS 10000 /* 1/100 second */ #endif #if !defined (DEFAULT_CACHE_THRESHOLD) # define DEFAULT_CACHE_THRESHOLD 25 #endif #if !defined (DEFAULT_DEFAULT_LEASE_TIME) # define DEFAULT_DEFAULT_LEASE_TIME 43200 #endif #if !defined (DEFAULT_MIN_LEASE_TIME) # define DEFAULT_MIN_LEASE_TIME 300 #endif #if !defined (DEFAULT_MAX_LEASE_TIME) # define DEFAULT_MAX_LEASE_TIME 86400 #endif #if !defined (DEFAULT_DDNS_TTL) # define DEFAULT_DDNS_TTL 3600 #endif #if !defined (MAX_DEFAULT_DDNS_TTL) # define MAX_DEFAULT_DDNS_TTL 3600 #endif #if !defined (MIN_LEASE_WRITE) # define MIN_LEASE_WRITE 15 #endif #if !defined (DEFAULT_ABANDON_LEASE_TIME) # define DEFAULT_ABANDON_LEASE_TIME 86400 #endif #define PLM_IGNORE 0 #define PLM_PREFER 1 #define PLM_EXACT 2 #define PLM_MINIMUM 3 #define PLM_MAXIMUM 4 /* Client option names */ #define CL_TIMEOUT 1 #define CL_SELECT_INTERVAL 2 #define CL_REBOOT_TIMEOUT 3 #define CL_RETRY_INTERVAL 4 #define CL_BACKOFF_CUTOFF 5 #define CL_INITIAL_INTERVAL 6 #define CL_BOOTP_POLICY 7 #define CL_SCRIPT_NAME 8 #define CL_REQUESTED_OPTIONS 9 #define CL_REQUESTED_LEASE_TIME 10 #define CL_SEND_OPTIONS 11 #define CL_MEDIA 12 #define CL_REJECT_LIST 13 #ifndef CL_DEFAULT_TIMEOUT # define CL_DEFAULT_TIMEOUT 60 #endif #ifndef CL_DEFAULT_SELECT_INTERVAL # define CL_DEFAULT_SELECT_INTERVAL 0 #endif #ifndef CL_DEFAULT_REBOOT_TIMEOUT # define CL_DEFAULT_REBOOT_TIMEOUT 10 #endif #ifndef CL_DEFAULT_RETRY_INTERVAL # define CL_DEFAULT_RETRY_INTERVAL 300 #endif #ifndef CL_DEFAULT_BACKOFF_CUTOFF # define CL_DEFAULT_BACKOFF_CUTOFF 120 #endif #ifndef CL_DEFAULT_INITIAL_INTERVAL # define CL_DEFAULT_INITIAL_INTERVAL 10 #endif #ifndef CL_DEFAULT_BOOTP_POLICY # define CL_DEFAULT_BOOTP_POLICY P_ACCEPT #endif #ifndef CL_DEFAULT_REQUESTED_OPTIONS # define CL_DEFAULT_REQUESTED_OPTIONS \ { DHO_SUBNET_MASK, \ DHO_BROADCAST_ADDRESS, \ DHO_TIME_OFFSET, \ DHO_ROUTERS, \ DHO_DOMAIN_NAME, \ DHO_DOMAIN_NAME_SERVERS, \ DHO_HOST_NAME } #endif struct group_object { OMAPI_OBJECT_PREAMBLE; struct group_object *n_dynamic; struct group *group; char *name; int flags; #define GROUP_OBJECT_DELETED 1 #define GROUP_OBJECT_DYNAMIC 2 #define GROUP_OBJECT_STATIC 4 }; /* Group of declarations that share common parameters. */ struct group { struct group *next; int refcnt; struct group_object *object; struct subnet *subnet; struct shared_network *shared_network; int authoritative; struct executable_statement *statements; }; /* A dhcp host declaration structure. */ struct host_decl { OMAPI_OBJECT_PREAMBLE; struct host_decl *n_ipaddr; struct host_decl *n_dynamic; char *name; struct hardware interface; struct data_string client_identifier; struct option *host_id_option; struct data_string host_id; /* XXXSK: fixed_addr should be an array of iaddr values, not an option_cache, but it's referenced in a lot of places, so we'll leave it for now. */ struct option_cache *fixed_addr; struct iaddrcidrnetlist *fixed_prefix; struct group *group; struct group_object *named_group; struct data_string auth_key_id; int flags; #define HOST_DECL_DELETED 1 #define HOST_DECL_DYNAMIC 2 #define HOST_DECL_STATIC 4 /* For v6 the host-identifer option can specify which relay to use when trying to look up an option. We store the value here. */ int relays; }; struct permit { struct permit *next; enum { permit_unknown_clients, permit_known_clients, permit_authenticated_clients, permit_unauthenticated_clients, permit_all_clients, permit_dynamic_bootp_clients, permit_class, permit_after } type; struct class *class; TIME after; /* date after which this clause applies */ }; #if defined (BINARY_LEASES) struct leasechain { struct lease **list; /* lease list */ size_t total; /* max number of elements in this list, * including free pointers at the end if any */ size_t nelem; /* the number of elements, also the next index to use */ size_t growth; /* the growth factor to use when increase an array * this is set after parsing the pools and before * creatin an array. */ }; #endif struct pool { OMAPI_OBJECT_PREAMBLE; struct pool *next; struct group *group; struct shared_network *shared_network; struct permit *permit_list; struct permit *prohibit_list; LEASE_STRUCT active; LEASE_STRUCT expired; LEASE_STRUCT free; LEASE_STRUCT backup; LEASE_STRUCT abandoned; LEASE_STRUCT reserved; TIME next_event_time; int lease_count; int free_leases; int backup_leases; int index; TIME valid_from; /* deny pool use before this date */ TIME valid_until; /* deny pool use after this date */ #if defined (FAILOVER_PROTOCOL) dhcp_failover_state_t *failover_peer; #endif int logged; /* already logged a message */ int low_threshold; /* low threshold to restart logging */ }; struct shared_network { OMAPI_OBJECT_PREAMBLE; struct shared_network *next; char *name; #define SHARED_IMPLICIT 1 /* This network was synthesized. */ int flags; struct subnet *subnets; struct interface_info *interface; struct pool *pools; struct ipv6_pond *ipv6_pond; struct group *group; #if defined (FAILOVER_PROTOCOL) dhcp_failover_state_t *failover_peer; #endif }; struct subnet { OMAPI_OBJECT_PREAMBLE; struct subnet *next_subnet; struct subnet *next_sibling; struct shared_network *shared_network; struct interface_info *interface; struct iaddr interface_address; struct iaddr net; struct iaddr netmask; int prefix_len; /* XXX: currently for IPv6 only */ struct group *group; }; struct collection { struct collection *next; const char *name; struct class *classes; }; /* Used as an argument to parse_clasS_decl() */ #define CLASS_TYPE_VENDOR 0 #define CLASS_TYPE_USER 1 #define CLASS_TYPE_CLASS 2 #define CLASS_TYPE_SUBCLASS 3 /* XXX classes must be reference-counted. */ struct class { OMAPI_OBJECT_PREAMBLE; struct class *nic; /* Next in collection. */ struct class *superclass; /* Set for spawned classes only. */ char *name; /* Not set for spawned classes. */ /* A class may be configured to permit a limited number of leases. */ int lease_limit; int leases_consumed; struct lease **billed_leases; /* If nonzero, class has not been saved since it was last modified. */ int dirty; /* Hash table containing subclasses. */ class_hash_t *hash; struct data_string hash_string; /* Expression used to match class. */ struct expression *expr; /* Expression used to compute subclass identifiers for spawning and to do subclass matching. */ struct expression *submatch; int spawning; struct group *group; /* Statements to execute if class matches. */ struct executable_statement *statements; #define CLASS_DECL_DELETED 1 #define CLASS_DECL_DYNAMIC 2 #define CLASS_DECL_STATIC 4 #define CLASS_DECL_SUBCLASS 8 int flags; }; /* DHCP client lease structure... */ struct client_lease { struct client_lease *next; /* Next lease in list. */ TIME expiry, renewal, rebind; /* Lease timeouts. */ struct iaddr address; /* Address being leased. */ char *server_name; /* Name of boot server. */ char *filename; /* Name of file we're supposed to boot. */ struct string_list *medium; /* Network medium. */ struct auth_key *key; /* Key used in basic DHCP authentication. */ unsigned int is_static : 1; /* If set, lease is from config file. */ unsigned int is_bootp: 1; /* If set, lease was acquired with BOOTP. */ struct option_state *options; /* Options supplied with lease. */ struct iaddr next_srv_addr; /* Address of the next server to use */ }; /* DHCPv6 lease structures */ struct dhc6_addr { struct dhc6_addr *next; struct iaddr address; u_int8_t plen; /* Address state flags. */ #define DHC6_ADDR_DEPREFFED 0x01 #define DHC6_ADDR_EXPIRED 0x02 #define DHC6_ADDR_DECLINED 0x04 u_int8_t flags; TIME starts; u_int32_t preferred_life; u_int32_t max_life; struct option_state *options; }; struct dhc6_ia { struct dhc6_ia *next; unsigned char iaid[4]; u_int16_t ia_type; TIME starts; u_int32_t renew; u_int32_t rebind; struct dhc6_addr *addrs; struct option_state *options; }; struct dhc6_lease { struct dhc6_lease *next; struct data_string server_id; isc_boolean_t released; int score; u_int8_t pref; unsigned char dhcpv6_transaction_id[3]; struct dhc6_ia *bindings; struct option_state *options; }; /* Possible states in which the client can be. */ enum dhcp_state { S_REBOOTING = 1, S_INIT = 2, S_SELECTING = 3, S_REQUESTING = 4, S_BOUND = 5, S_RENEWING = 6, S_REBINDING = 7, S_DECLINING = 8, S_STOPPED = 9 }; /* Possible pending client operations. */ enum dhcp_pending { P_NONE = 0, P_REBOOT = 1, P_RELEASE = 2 }; /* Authentication and BOOTP policy possibilities (not all values work for each). */ enum policy { P_IGNORE, P_ACCEPT, P_PREFER, P_REQUIRE, P_DONT }; /* Configuration information from the config file... */ struct client_config { /* * When a message has been received, run these statements * over it. */ struct group *on_receipt; /* * When a message is sent, run these statements. */ struct group *on_transmission; struct option **required_options; /* Options that MUST be present. */ struct option **requested_options; /* Options to request (ORO/PRL). */ TIME timeout; /* Start to panic if we don't get a lease in this time period when SELECTING. */ TIME initial_delay; /* Set initial delay before first transmission. */ TIME initial_interval; /* All exponential backoff intervals start here. */ TIME retry_interval; /* If the protocol failed to produce an address before the timeout, try the protocol again after this many seconds. */ TIME select_interval; /* Wait this many seconds from the first DHCPDISCOVER before picking an offered lease. */ TIME reboot_timeout; /* When in INIT-REBOOT, wait this long before giving up and going to INIT. */ TIME backoff_cutoff; /* When doing exponential backoff, never back off to an interval longer than this amount. */ u_int32_t requested_lease; /* Requested lease time, if user doesn't configure one. */ struct string_list *media; /* Possible network media values. */ char *script_name; /* Name of config script. */ char *vendor_space_name; /* Name of config script. */ enum policy bootp_policy; /* Ignore, accept or prefer BOOTP responses. */ enum policy auth_policy; /* Require authentication, prefer authentication, or don't try to authenticate. */ struct string_list *medium; /* Current network medium. */ struct iaddrmatchlist *reject_list; /* Servers to reject. */ int omapi_port; /* port on which to accept OMAPI connections, or -1 for no listener. */ int do_forward_update; /* If nonzero, and if we have the information we need, update the A record for the address we get. */ int lease_id_format; /* format for IDs in lease file, TOKEN_OCTAL or TOKEN_HEX */ }; /* Per-interface state used in the dhcp client... */ /* XXX: consider union {}'ing this for v4/v6. */ struct client_state { struct client_state *next; struct interface_info *interface; char *name; /* Common values. */ struct client_config *config; /* Client configuration. */ struct string_list *env; /* Client script environment. */ int envc; /* Number of entries in environment. */ struct option_state *sent_options; /* Options we sent. */ enum dhcp_state state; /* Current state for this interface. */ TIME last_write; /* Last time this state was written. */ enum dhcp_pending pending; /* Current pending operation. */ /* DHCPv4 values. */ struct client_lease *active; /* Currently active lease. */ struct client_lease *new; /* New lease. */ struct client_lease *offered_leases; /* Leases offered to us. */ struct client_lease *leases; /* Leases we currently hold. */ struct client_lease *alias; /* Alias lease. */ struct iaddr destination; /* Where to send packet. */ u_int32_t xid; /* Transaction ID. */ u_int16_t secs; /* secs value from DHCPDISCOVER. */ TIME first_sending; /* When was first copy sent? */ TIME interval; /* What's the current resend interval? */ struct string_list *medium; /* Last media type tried. */ struct dhcp_packet packet; /* Outgoing DHCP packet. */ unsigned packet_length; /* Actual length of generated packet. */ struct iaddr requested_address; /* Address we would like to get. */ /* DHCPv6 values. */ unsigned char dhcpv6_transaction_id[3]; u_int8_t refresh_type; struct dhc6_lease *active_lease; struct dhc6_lease *old_lease; struct dhc6_lease *advertised_leases; struct dhc6_lease *selected_lease; struct dhc6_lease *held_leases; struct timeval start_time; u_int16_t elapsed; int txcount; /* See RFC3315 section 14. */ TIME RT; /* In hundredths of seconds. */ TIME IRT; /* In hundredths of seconds. */ TIME MRC; /* Count. */ TIME MRT; /* In hundredths of seconds. */ TIME MRD; /* In seconds, relative. */ TIME next_MRD; /* In seconds, absolute. */ /* Rather than a state, we use a function that shifts around * depending what stage of life the v6 state machine is in. * This is where incoming packets are dispatched to (sometimes * a no-op). */ void (*v6_handler)(struct packet *, struct client_state *); /* * A pointer to the state of the ddns update for this lease. * It should be set while the update is in progress and cleared * when the update finishes. It can be used to cancel the * update if we want to do a different update. */ struct dhcp_ddns_cb *ddns_cb; }; struct envadd_state { struct client_state *client; const char *prefix; }; struct dns_update_state { struct client_state *client; struct iaddr address; int dns_update_timeout; }; /* Information about each network interface. */ struct interface_info { OMAPI_OBJECT_PREAMBLE; struct interface_info *next; /* Next interface in list... */ struct shared_network *shared_network; /* Networks connected to this interface. */ struct hardware hw_address; /* Its physical address. */ struct in_addr *addresses; /* Addresses associated with this * interface. */ int address_count; /* Number of addresses stored. */ int address_max; /* Size of addresses buffer. */ struct in6_addr *v6addresses; /* IPv6 addresses associated with this interface. */ int v6address_count; /* Number of IPv6 addresses associated with this interface. */ int v6address_max; /* Maximum number of IPv6 addresses we can store in current buffer. */ u_int8_t *circuit_id; /* Circuit ID associated with this interface. */ unsigned circuit_id_len; /* Length of Circuit ID, if there is one. */ u_int8_t *remote_id; /* Remote ID associated with this interface (if any). */ unsigned remote_id_len; /* Length of Remote ID. */ char name [IFNAMSIZ]; /* Its name... */ int index; /* Its if_nametoindex(). */ int rfdesc; /* Its read file descriptor. */ int wfdesc; /* Its write file descriptor, if different. */ unsigned char *rbuf; /* Read buffer, if required. */ unsigned int rbuf_max; /* Size of read buffer. */ size_t rbuf_offset; /* Current offset into buffer. */ size_t rbuf_len; /* Length of data in buffer. */ struct ifreq *ifp; /* Pointer to ifreq struct. */ int configured; /* If set to 1, interface has at least * one valid IP address. */ u_int32_t flags; /* Control flags... */ #define INTERFACE_REQUESTED 1 #define INTERFACE_AUTOMATIC 2 #define INTERFACE_RUNNING 4 #define INTERFACE_DOWNSTREAM 8 #define INTERFACE_UPSTREAM 16 #define INTERFACE_STREAMS (INTERFACE_DOWNSTREAM | INTERFACE_UPSTREAM) /* Only used by DHCP client code. */ struct client_state *client; # if defined(USE_DLPI_SEND) || defined(USE_DLPI_RECEIVE) || \ defined(USE_DLPI_HWADDR) int dlpi_sap_length; struct hardware dlpi_broadcast_addr; # endif /* DLPI_SEND || DLPI_RECEIVE */ struct hardware anycast_mac_addr; }; struct hardware_link { struct hardware_link *next; char name [IFNAMSIZ]; struct hardware address; }; struct leasequeue { struct leasequeue *prev; struct leasequeue *next; struct lease *lease; }; typedef void (*tvref_t)(void *, void *, const char *, int); typedef void (*tvunref_t)(void *, const char *, int); struct timeout { struct timeout *next; struct timeval when; void (*func) (void *); void *what; tvref_t ref; tvunref_t unref; isc_timer_t *isc_timeout; }; struct eventqueue { struct eventqueue *next; void (*handler)(void *); }; struct protocol { struct protocol *next; int fd; void (*handler) (struct protocol *); void *local; }; struct dns_query; /* forward */ struct dns_wakeup { struct dns_wakeup *next; /* Next wakeup in chain. */ void (*func) (struct dns_query *); }; struct dns_question { u_int16_t type; /* Type of query. */ u_int16_t class; /* Class of query. */ unsigned char data [1]; /* Query data. */ }; struct dns_answer { u_int16_t type; /* Type of answer. */ u_int16_t class; /* Class of answer. */ int count; /* Number of answers. */ unsigned char *answers[1]; /* Pointers to answers. */ }; struct dns_query { struct dns_query *next; /* Next query in hash bucket. */ u_int32_t hash; /* Hash bucket index. */ TIME expiry; /* Query expiry time (zero if not yet answered. */ u_int16_t id; /* Query ID (also hash table index) */ caddr_t waiters; /* Pointer to list of things waiting on this query. */ struct dns_question *question; /* Question, internal format. */ struct dns_answer *answer; /* Answer, internal format. */ unsigned char *query; /* Query formatted for DNS server. */ unsigned len; /* Length of entire query. */ int sent; /* The query has been sent. */ struct dns_wakeup *wakeups; /* Wakeups to call if this query is answered. */ struct name_server *next_server; /* Next server to try. */ int backoff; /* Current backoff, in seconds. */ }; #define DNS_ZONE_ACTIVE 0 #define DNS_ZONE_INACTIVE 1 struct dns_zone { int refcnt; TIME timeout; char *name; struct option_cache *primary; struct option_cache *secondary; struct option_cache *primary6; struct option_cache *secondary6; struct auth_key *key; u_int16_t flags; }; struct icmp_state { OMAPI_OBJECT_PREAMBLE; int socket; void (*icmp_handler) (struct iaddr, u_int8_t *, int); }; #include "ctrace.h" /* Bitmask of dhcp option codes. */ typedef unsigned char option_mask [16]; /* DHCP Option mask manipulation macros... */ #define OPTION_ZERO(mask) (memset (mask, 0, 16)) #define OPTION_SET(mask, bit) (mask [bit >> 8] |= (1 << (bit & 7))) #define OPTION_CLR(mask, bit) (mask [bit >> 8] &= ~(1 << (bit & 7))) #define OPTION_ISSET(mask, bit) (mask [bit >> 8] & (1 << (bit & 7))) #define OPTION_ISCLR(mask, bit) (!OPTION_ISSET (mask, bit)) /* An option occupies its length plus two header bytes (code and length) for every 255 bytes that must be stored. */ #define OPTION_SPACE(x) ((x) + 2 * ((x) / 255 + 1)) /* Default path to dhcpd config file. */ #ifdef DEBUG #undef _PATH_DHCPD_CONF #define _PATH_DHCPD_CONF "dhcpd.conf" #undef _PATH_DHCPD_DB #define _PATH_DHCPD_DB "dhcpd.leases" #undef _PATH_DHCPD6_DB #define _PATH_DHCPD6_DB "dhcpd6.leases" #undef _PATH_DHCPD_PID #define _PATH_DHCPD_PID "dhcpd.pid" #undef _PATH_DHCPD6_PID #define _PATH_DHCPD6_PID "dhcpd6.pid" #else /* !DEBUG */ #ifndef _PATH_DHCPD_CONF #define _PATH_DHCPD_CONF "/etc/dhcpd.conf" #endif /* DEBUG */ #ifndef _PATH_DHCPD_DB #define _PATH_DHCPD_DB LOCALSTATEDIR"/db/dhcpd.leases" #endif #ifndef _PATH_DHCPD6_DB #define _PATH_DHCPD6_DB LOCALSTATEDIR"/db/dhcpd6.leases" #endif #ifndef _PATH_DHCPD_PID #define _PATH_DHCPD_PID LOCALSTATEDIR"/run/dhcpd.pid" #endif #ifndef _PATH_DHCPD6_PID #define _PATH_DHCPD6_PID LOCALSTATEDIR"/run/dhcpd6.pid" #endif #endif /* DEBUG */ #ifndef _PATH_DHCLIENT_CONF #define _PATH_DHCLIENT_CONF "/etc/dhclient.conf" #endif #ifndef _PATH_DHCLIENT_SCRIPT #define _PATH_DHCLIENT_SCRIPT "/sbin/dhclient-script" #endif #ifndef _PATH_DHCLIENT_PID #define _PATH_DHCLIENT_PID LOCALSTATEDIR"/run/dhclient.pid" #endif #ifndef _PATH_DHCLIENT6_PID #define _PATH_DHCLIENT6_PID LOCALSTATEDIR"/run/dhclient6.pid" #endif #ifndef _PATH_DHCLIENT_DB #define _PATH_DHCLIENT_DB LOCALSTATEDIR"/db/dhclient.leases" #endif #ifndef _PATH_DHCLIENT6_DB #define _PATH_DHCLIENT6_DB LOCALSTATEDIR"/db/dhclient6.leases" #endif #ifndef _PATH_RESOLV_CONF #define _PATH_RESOLV_CONF "/etc/resolv.conf" #endif #ifndef _PATH_DHCRELAY_PID #define _PATH_DHCRELAY_PID LOCALSTATEDIR"/run/dhcrelay.pid" #endif #ifndef _PATH_DHCRELAY6_PID #define _PATH_DHCRELAY6_PID LOCALSTATEDIR"/run/dhcrelay6.pid" #endif #ifndef DHCPD_LOG_FACILITY #define DHCPD_LOG_FACILITY LOG_DAEMON #endif #define INFINITE_TIME 0xffffffff #define MAX_TIME 0x7fffffff #define MIN_TIME 0 #ifdef USE_LOG_PID /* include the pid in the syslog messages */ #define DHCP_LOG_OPTIONS LOG_NDELAY | LOG_PID #else #define DHCP_LOG_OPTIONS LOG_NDELAY #endif /* these are referenced */ typedef struct hash_table ia_hash_t; typedef struct hash_table iasubopt_hash_t; /* IAADDR/IAPREFIX lease */ struct iasubopt { int refcnt; /* reference count */ struct in6_addr addr; /* IPv6 address/prefix */ u_int8_t plen; /* iaprefix prefix length */ binding_state_t state; /* state */ struct binding_scope *scope; /* "set var = value;" */ time_t hard_lifetime_end_time; /* time address expires */ time_t soft_lifetime_end_time; /* time ephemeral expires */ u_int32_t prefer; /* cached preferred lifetime */ u_int32_t valid; /* cached valid lifetime */ struct ia_xx *ia; /* IA for this lease */ struct ipv6_pool *ipv6_pool; /* pool for this lease */ /* * For now, just pick an arbitrary time to keep old hard leases * around (value in seconds). */ #define EXPIRED_IPV6_CLEANUP_TIME (60*60) /* index into heaps, or -1 (internal use only) */ int active_index; int inactive_index; /* * A pointer to the state of the ddns update for this lease. * It should be set while the update is in progress and cleared * when the update finishes. It can be used to cancel the * update if we want to do a different update. */ struct dhcp_ddns_cb *ddns_cb; /* space for the on * executable statements */ struct on_star on_star; int static_lease; }; struct ia_xx { int refcnt; /* reference count */ struct data_string iaid_duid; /* from the client */ u_int16_t ia_type; /* IA_XX */ int num_iasubopt; /* number of IAADDR/PREFIX */ int max_iasubopt; /* space available for IAADDR/PREFIX */ time_t cltt; /* client last transaction time */ struct iasubopt **iasubopt; /* pointers to the IAADDR/IAPREFIXs */ }; extern ia_hash_t *ia_na_active; extern ia_hash_t *ia_ta_active; extern ia_hash_t *ia_pd_active; /*! * * \brief ipv6_pool structure * * This structure is part of a range of addresses or prefixes. * A range6 or prefix6 statement will map to one or more of these * with each pool being a simple block of the form xxxx/yyy and * all the pools adding up to comprise the entire range. When * choosing an address or prefix the code will walk through the * pools until it finds one that is available. * * The naming for this structure is unfortunate as there is also * a v4 pool structure and the two are not equivalent. The v4 * pool matches the ipv6_pond structure. I considered changing the * name of this structure but concluded that doing so would be worse * than leaving it as is. Changing it adds some risk and makes for * larger differences between the 4.1 & 4.2 code and the 4.3 code. * */ struct ipv6_pool { int refcnt; /* reference count */ u_int16_t pool_type; /* IA_xx */ struct in6_addr start_addr; /* first IPv6 address */ int bits; /* number of bits, CIDR style */ int units; /* allocation unit in bits */ iasubopt_hash_t *leases; /* non-free leases */ isc_uint64_t num_active; /* count of active leases */ isc_uint64_t num_abandoned; /* count of abandoned leases */ isc_heap_t *active_timeouts; /* timeouts for active leases */ int num_inactive; /* count of inactive leases */ isc_heap_t *inactive_timeouts; /* timeouts for expired or released leases */ struct shared_network *shared_network; /* shared_network for this pool */ struct subnet *subnet; /* subnet for this pool */ struct ipv6_pond *ipv6_pond; /* pond for this pool */ }; /*! * * \brief ipv6_pond structure * * This structure is the ipv6 version of the v4 pool structure. * It contains the address and prefix information via the pointers * to the ipv6_pools and the allowability of this pool for a given * client via the permit lists and the valid TIMEs. * */ struct ipv6_pond { int refcnt; struct ipv6_pond *next; struct group *group; struct shared_network *shared_network; /* backpointer to the enclosing shared network */ struct permit *permit_list; /* allow clients from this list */ struct permit *prohibit_list; /* deny clients from this list */ TIME valid_from; /* deny pool use before this date */ TIME valid_until; /* deny pool use after this date */ struct ipv6_pool **ipv6_pools; /* NULL-terminated array */ int last_ipv6_pool; /* offset of last IPv6 pool used to issue a lease */ isc_uint64_t num_total; /* Total number of elements in the pond */ isc_uint64_t num_active; /* Number of elements in the pond in use */ isc_uint64_t num_abandoned; /* count of abandoned leases */ int logged; /* already logged a message */ isc_uint64_t low_threshold; /* low threshold to restart logging */ int jumbo_range; #ifdef EUI_64 int use_eui_64; /* use EUI-64 address assignment when true */ #endif }; /* * Max addresses in a pond that can be supported by log threshold * Currently based on max value supported by isc_uint64_t. */ #define POND_TRACK_MAX ISC_UINT64_MAX /* Flags for dhcp_ddns_cb_t */ #define DDNS_UPDATE_ADDR 0x0001 #define DDNS_UPDATE_PTR 0x0002 #define DDNS_INCLUDE_RRSET 0x0004 #define DDNS_CONFLICT_DETECTION 0x0008 #define DDNS_CLIENT_DID_UPDATE 0x0010 #define DDNS_EXECUTE_NEXT 0x0020 #define DDNS_ABORT 0x0040 #define DDNS_STATIC_LEASE 0x0080 #define DDNS_ACTIVE_LEASE 0x0100 #define DDNS_DUAL_STACK_MIXED_MODE 0x0200 #define DDNS_GUARD_ID_MUST_MATCH 0x0400 #define DDNS_OTHER_GUARD_IS_DYNAMIC 0x0800 #define CONFLICT_BITS (DDNS_CONFLICT_DETECTION|\ DDNS_DUAL_STACK_MIXED_MODE|\ DDNS_GUARD_ID_MUST_MATCH|\ DDNS_OTHER_GUARD_IS_DYNAMIC) /* States for dhcp_ddns_cb_t */ #define DDNS_STATE_CLEANUP 0 /* startup or the previous step failed, cleanup */ #define DDNS_STATE_ADD_FW_NXDOMAIN 1 #define DDNS_STATE_ADD_FW_YXDHCID 2 #define DDNS_STATE_ADD_PTR 3 #define DDNS_STATE_DSMM_FW_ADD3 4 #define DDNS_STATE_REM_FW_YXDHCID 17 #define DDNS_STATE_REM_FW_NXRR 18 #define DDNS_STATE_REM_PTR 19 #define DDNS_STATE_REM_FW_DSMM_OTHER 20 /* * Flags for the dns print function */ #define DDNS_PRINT_INBOUND 1 #define DDNS_PRINT_OUTBOUND 0 struct dhcp_ddns_cb; typedef void (*ddns_action_t)(struct dhcp_ddns_cb *ddns_cb, isc_result_t result); typedef struct dhcp_ddns_cb { struct data_string fwd_name; struct data_string rev_name; struct data_string dhcid; struct iaddr address; int address_type; unsigned long ttl; unsigned char zone_name[DHCP_MAXDNS_WIRE]; isc_sockaddrlist_t zone_server_list; isc_sockaddr_t zone_addrs[DHCP_MAXNS]; int zone_addr_count; struct dns_zone *zone; u_int16_t flags; TIME timeout; int state; ddns_action_t cur_func; struct dhcp_ddns_cb * next_op; /* Lease or client state that triggered the ddns operation */ void *lease; struct binding_scope **scope; void *transaction; void *dataspace; dns_rdataclass_t dhcid_class; dns_rdataclass_t other_dhcid_class; char *lease_tag; struct ia_xx *fixed6_ia; } dhcp_ddns_cb_t; extern struct ipv6_pool **pools; /* External definitions... */ HASH_FUNCTIONS_DECL (group, const char *, struct group_object, group_hash_t) HASH_FUNCTIONS_DECL (universe, const char *, struct universe, universe_hash_t) HASH_FUNCTIONS_DECL (option_name, const char *, struct option, option_name_hash_t) HASH_FUNCTIONS_DECL (option_code, const unsigned *, struct option, option_code_hash_t) HASH_FUNCTIONS_DECL (dns_zone, const char *, struct dns_zone, dns_zone_hash_t) HASH_FUNCTIONS_DECL(lease_ip, const unsigned char *, struct lease, lease_ip_hash_t) HASH_FUNCTIONS_DECL(lease_id, const unsigned char *, struct lease, lease_id_hash_t) HASH_FUNCTIONS_DECL (host, const unsigned char *, struct host_decl, host_hash_t) HASH_FUNCTIONS_DECL (class, const char *, struct class, class_hash_t) /* options.c */ extern struct option *vendor_cfg_option; int parse_options (struct packet *); int parse_option_buffer (struct option_state *, const unsigned char *, unsigned, struct universe *); struct universe *find_option_universe (struct option *, const char *); int parse_encapsulated_suboptions (struct option_state *, struct option *, const unsigned char *, unsigned, struct universe *, const char *); int cons_options (struct packet *, struct dhcp_packet *, struct lease *, struct client_state *, int, struct option_state *, struct option_state *, struct binding_scope **, int, int, int, struct data_string *, const char *); int fqdn_universe_decode (struct option_state *, const unsigned char *, unsigned, struct universe *); struct option_cache * lookup_fqdn6_option(struct universe *universe, struct option_state *options, unsigned code); void save_fqdn6_option(struct universe *universe, struct option_state *options, struct option_cache *oc, isc_boolean_t appendp); void delete_fqdn6_option(struct universe *universe, struct option_state *options, int code); void fqdn6_option_space_foreach(struct packet *packet, struct lease *lease, struct client_state *client_state, struct option_state *in_options, struct option_state *cfg_options, struct binding_scope **scope, struct universe *u, void *stuff, void (*func)(struct option_cache *, struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct binding_scope **, struct universe *, void *)); int fqdn6_option_space_encapsulate(struct data_string *result, struct packet *packet, struct lease *lease, struct client_state *client_state, struct option_state *in_options, struct option_state *cfg_options, struct binding_scope **scope, struct universe *universe); int fqdn6_universe_decode(struct option_state *options, const unsigned char *buffer, unsigned length, struct universe *u); int append_option(struct data_string *dst, struct universe *universe, struct option *option, struct data_string *src); int store_options(int *ocount, unsigned char *buffer, unsigned buflen, unsigned index, struct packet *packet, struct lease *lease, struct client_state *client_state, struct option_state *in_options, struct option_state *cfg_options, struct binding_scope **scope, unsigned *priority_list, int priority_len, unsigned first_cutoff, int second_cutoff, int terminate, const char *vuname); int store_options6(char *, int, struct option_state *, struct packet *, const int *, struct data_string *); int format_has_text(const char *); int format_min_length(const char *, struct option_cache *); const char *pretty_print_option (struct option *, const unsigned char *, unsigned, int, int); int pretty_escape(char **, char *, const unsigned char **, const unsigned char *); int get_option (struct data_string *, struct universe *, struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct option_state *, struct binding_scope **, unsigned, const char *, int); int get_option_int (int *, struct universe *, struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct option_state *, struct binding_scope **, unsigned, const char *, int); void set_option (struct universe *, struct option_state *, struct option_cache *, enum statement_op); struct option_cache *lookup_option (struct universe *, struct option_state *, unsigned); struct option_cache *lookup_hashed_option (struct universe *, struct option_state *, unsigned); struct option_cache *next_hashed_option(struct universe *, struct option_state *, struct option_cache *); int save_option_buffer (struct universe *, struct option_state *, struct buffer *, unsigned char *, unsigned, unsigned, int); int append_option_buffer(struct universe *, struct option_state *, struct buffer *, unsigned char *, unsigned, unsigned, int); void build_server_oro(struct data_string *, struct option_state *, const char *, int); void save_option(struct universe *, struct option_state *, struct option_cache *); void also_save_option(struct universe *, struct option_state *, struct option_cache *); void save_hashed_option(struct universe *, struct option_state *, struct option_cache *, isc_boolean_t appendp); void delete_option (struct universe *, struct option_state *, int); void delete_hashed_option (struct universe *, struct option_state *, int); int option_cache_dereference (struct option_cache **, const char *, int); int hashed_option_state_dereference (struct universe *, struct option_state *, const char *, int); int store_option (struct data_string *, struct universe *, struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct binding_scope **, struct option_cache *); int option_space_encapsulate (struct data_string *, struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct binding_scope **, struct data_string *); int hashed_option_space_encapsulate (struct data_string *, struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct binding_scope **, struct universe *); int nwip_option_space_encapsulate (struct data_string *, struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct binding_scope **, struct universe *); int fqdn_option_space_encapsulate (struct data_string *, struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct binding_scope **, struct universe *); void suboption_foreach (struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct binding_scope **, struct universe *, void *, void (*) (struct option_cache *, struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct binding_scope **, struct universe *, void *), struct option_cache *, const char *); void option_space_foreach (struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct binding_scope **, struct universe *, void *, void (*) (struct option_cache *, struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct binding_scope **, struct universe *, void *)); void hashed_option_space_foreach (struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct binding_scope **, struct universe *, void *, void (*) (struct option_cache *, struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct binding_scope **, struct universe *, void *)); int linked_option_get (struct data_string *, struct universe *, struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct option_state *, struct binding_scope **, unsigned); int linked_option_state_dereference (struct universe *, struct option_state *, const char *, int); void save_linked_option(struct universe *, struct option_state *, struct option_cache *, isc_boolean_t appendp); void linked_option_space_foreach (struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct binding_scope **, struct universe *, void *, void (*) (struct option_cache *, struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct binding_scope **, struct universe *, void *)); int linked_option_space_encapsulate (struct data_string *, struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct binding_scope **, struct universe *); void delete_linked_option (struct universe *, struct option_state *, int); struct option_cache *lookup_linked_option (struct universe *, struct option_state *, unsigned); void do_packet (struct interface_info *, struct dhcp_packet *, unsigned, unsigned int, struct iaddr, struct hardware *); void do_packet6(struct interface_info *, const char *, int, int, const struct iaddr *, isc_boolean_t); int packet6_len_okay(const char *, int); int validate_packet(struct packet *); int add_option(struct option_state *options, unsigned int option_num, void *data, unsigned int data_len); void parse_vendor_option(struct packet *packet, struct lease *lease, struct client_state *client_state, struct option_state *in_options, struct option_state *out_options, struct binding_scope **scope); /* dhcp4o6.c */ #if defined(DHCP4o6) extern int dhcp4o6_fd; extern omapi_object_t *dhcp4o6_object; extern omapi_object_type_t *dhcp4o6_type; extern void dhcp4o6_setup(u_int16_t); /* dependency */ extern isc_result_t dhcpv4o6_handler(omapi_object_t *); #endif /* dhcpd.c */ extern struct timeval cur_tv; #define cur_time cur_tv.tv_sec extern int ddns_update_style; #if defined (NSUPDATE) extern u_int16_t ddns_conflict_mask; #endif extern int dont_use_fsync; extern int server_id_check; #ifdef EUI_64 extern int persist_eui64; #endif #ifdef DHCPv6 extern int prefix_length_mode; extern int do_release_on_roam; #endif extern int authoring_byte_order; extern int lease_id_format; extern u_int32_t abandon_lease_time; extern const char *path_dhcpd_conf; extern const char *path_dhcpd_db; extern const char *path_dhcpd_pid; extern int dhcp_max_agent_option_packet_length; extern struct eventqueue *rw_queue_empty; #if defined (PARANOIA) extern uid_t set_uid; extern gid_t set_gid; #endif int main(int, char **); void postconf_initialization(int); void postdb_startup(void); void cleanup (void); void lease_pinged (struct iaddr, u_int8_t *, int); void lease_ping_timeout (void *); int dhcpd_interface_setup_hook (struct interface_info *ip, struct iaddr *ia); extern enum dhcp_shutdown_state shutdown_state; isc_result_t dhcp_io_shutdown (omapi_object_t *, void *); isc_result_t dhcp_set_control_state (control_object_state_t oldstate, control_object_state_t newstate); #if defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) void relinquish_ackqueue(void); #endif /* conflex.c */ isc_result_t new_parse (struct parse **, int, char *, unsigned, const char *, int); isc_result_t end_parse (struct parse **); isc_result_t save_parse_state(struct parse *cfile); isc_result_t restore_parse_state(struct parse *cfile); enum dhcp_token next_token (const char **, unsigned *, struct parse *); enum dhcp_token peek_token (const char **, unsigned *, struct parse *); enum dhcp_token next_raw_token(const char **rval, unsigned *rlen, struct parse *cfile); enum dhcp_token peek_raw_token(const char **rval, unsigned *rlen, struct parse *cfile); /* * Use skip_token when we are skipping a token we have previously * used peek_token on as we know what the result will be in this case. */ #define skip_token(a,b,c) ((void) next_token((a),(b),(c))) /* confpars.c */ void parse_trace_setup (void); isc_result_t readconf (void); isc_result_t read_conf_file (const char *, struct group *, int, int); #if defined (TRACING) void trace_conf_input (trace_type_t *, unsigned, char *); void trace_conf_stop (trace_type_t *ttype); #endif isc_result_t conf_file_subparse (struct parse *, struct group *, int); isc_result_t lease_file_subparse (struct parse *); int parse_statement (struct parse *, struct group *, int, struct host_decl *, int); #if defined (FAILOVER_PROTOCOL) void parse_failover_peer (struct parse *, struct group *, int); void parse_failover_state_declaration (struct parse *, dhcp_failover_state_t *); void parse_failover_state (struct parse *, enum failover_state *, TIME *); #endif int permit_list_match (struct permit *, struct permit *); void parse_pool_statement (struct parse *, struct group *, int); int parse_lbrace (struct parse *); void parse_host_declaration (struct parse *, struct group *); int parse_class_declaration (struct class **, struct parse *, struct group *, int); void parse_shared_net_declaration (struct parse *, struct group *); void parse_subnet_declaration (struct parse *, struct shared_network *); void parse_subnet6_declaration (struct parse *, struct shared_network *); void parse_group_declaration (struct parse *, struct group *); int parse_fixed_addr_param (struct option_cache **, struct parse *, enum dhcp_token); int parse_lease_declaration (struct lease **, struct parse *); int parse_ip6_addr(struct parse *, struct iaddr *); int parse_ip6_addr_expr(struct expression **, struct parse *); int parse_ip6_prefix(struct parse *, struct iaddr *, u_int8_t *); void parse_address_range (struct parse *, struct group *, int, struct pool *, struct lease **); void parse_address_range6(struct parse *cfile, struct group *group, struct ipv6_pond *); void parse_prefix6(struct parse *cfile, struct group *group, struct ipv6_pond *); void parse_fixed_prefix6(struct parse *cfile, struct host_decl *host_decl); void parse_ia_na_declaration(struct parse *); void parse_ia_ta_declaration(struct parse *); void parse_ia_pd_declaration(struct parse *); void parse_server_duid(struct parse *cfile); void parse_server_duid_conf(struct parse *cfile); void parse_pool6_statement (struct parse *, struct group *, int); uint32_t parse_byte_order_uint32(const void *source); /* ddns.c */ int ddns_updates(struct packet *, struct lease *, struct lease *, struct iasubopt *, struct iasubopt *, struct option_state *); isc_result_t ddns_removals(struct lease *, struct iasubopt *, struct dhcp_ddns_cb *, isc_boolean_t); u_int16_t get_conflict_mask(struct option_state *input_options); #if defined (TRACING) void trace_ddns_init(void); #endif /* parse.c */ void add_enumeration (struct enumeration *); struct enumeration *find_enumeration (const char *, int); struct enumeration_value *find_enumeration_value (const char *, int, unsigned *, const char *); void skip_to_semi (struct parse *); void skip_to_rbrace (struct parse *, int); int parse_semi (struct parse *); int parse_string (struct parse *, char **, unsigned *); char *parse_host_name (struct parse *); int parse_ip_addr_or_hostname (struct expression **, struct parse *, int); void parse_hardware_param (struct parse *, struct hardware *); void parse_lease_time (struct parse *, TIME *); unsigned char *parse_numeric_aggregate (struct parse *, unsigned char *, unsigned *, int, int, unsigned); void convert_num (struct parse *, unsigned char *, const char *, int, unsigned); TIME parse_date (struct parse *); TIME parse_date_core(struct parse *); isc_result_t parse_option_name (struct parse *, int, int *, struct option **); void parse_option_space_decl (struct parse *); int parse_option_code_definition (struct parse *, struct option *); int parse_base64 (struct data_string *, struct parse *); int parse_cshl (struct data_string *, struct parse *); int parse_executable_statement (struct executable_statement **, struct parse *, int *, enum expression_context); int parse_executable_statements (struct executable_statement **, struct parse *, int *, enum expression_context); int parse_zone (struct dns_zone *, struct parse *); int parse_key (struct parse *); int parse_on_statement (struct executable_statement **, struct parse *, int *); int parse_switch_statement (struct executable_statement **, struct parse *, int *); int parse_case_statement (struct executable_statement **, struct parse *, int *, enum expression_context); int parse_if_statement (struct executable_statement **, struct parse *, int *); int parse_boolean_expression (struct expression **, struct parse *, int *); int parse_boolean (struct parse *); int parse_data_expression (struct expression **, struct parse *, int *); int parse_numeric_expression (struct expression **, struct parse *, int *); int parse_dns_expression (struct expression **, struct parse *, int *); int parse_non_binary (struct expression **, struct parse *, int *, enum expression_context); int parse_expression (struct expression **, struct parse *, int *, enum expression_context, struct expression **, enum expr_op); int parse_option_data(struct expression **expr, struct parse *cfile, int lookups, struct option *option); int parse_option_statement (struct executable_statement **, struct parse *, int, struct option *, enum statement_op); int parse_option_token (struct expression **, struct parse *, const char **, struct expression *, int, int); int parse_allow_deny (struct option_cache **, struct parse *, int); int parse_auth_key (struct data_string *, struct parse *); int parse_warn (struct parse *, const char *, ...) __attribute__((__format__(__printf__,2,3))); struct expression *parse_domain_list(struct parse *cfile, int); /* tree.c */ extern struct binding_scope *global_scope; pair cons (caddr_t, pair); int make_const_option_cache (struct option_cache **, struct buffer **, u_int8_t *, unsigned, struct option *, const char *, int); int make_host_lookup (struct expression **, const char *); int enter_dns_host (struct dns_host_entry **, const char *); int make_const_data (struct expression **, const unsigned char *, unsigned, int, int, const char *, int); int make_const_int (struct expression **, unsigned long); int make_concat (struct expression **, struct expression *, struct expression *); int make_encapsulation (struct expression **, struct data_string *); int make_substring (struct expression **, struct expression *, struct expression *, struct expression *); int make_limit (struct expression **, struct expression *, int); int make_let (struct executable_statement **, const char *); int option_cache (struct option_cache **, struct data_string *, struct expression *, struct option *, const char *, int); int evaluate_expression (struct binding_value **, struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct binding_scope **, struct expression *, const char *, int); int binding_value_dereference (struct binding_value **, const char *, int); int evaluate_boolean_expression (int *, struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct binding_scope **, struct expression *); int evaluate_data_expression (struct data_string *, struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct binding_scope **, struct expression *, const char *, int); int evaluate_numeric_expression (unsigned long *, struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct binding_scope **, struct expression *); int evaluate_option_cache (struct data_string *, struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct binding_scope **, struct option_cache *, const char *, int); int evaluate_boolean_option_cache (int *, struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct binding_scope **, struct option_cache *, const char *, int); int evaluate_boolean_expression_result (int *, struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct binding_scope **, struct expression *); void expression_dereference (struct expression **, const char *, int); int is_dns_expression (struct expression *); int is_boolean_expression (struct expression *); int is_data_expression (struct expression *); int is_numeric_expression (struct expression *); int is_compound_expression (struct expression *); int op_precedence (enum expr_op, enum expr_op); enum expression_context expression_context (struct expression *); enum expression_context op_context (enum expr_op); int write_expression (FILE *, struct expression *, int, int, int); struct binding *find_binding (struct binding_scope *, const char *); int free_bindings (struct binding_scope *, const char *, int); int binding_scope_dereference (struct binding_scope **, const char *, int); int fundef_dereference (struct fundef **, const char *, int); int data_subexpression_length (int *, struct expression *); int expr_valid_for_context (struct expression *, enum expression_context); struct binding *create_binding (struct binding_scope **, const char *); int bind_ds_value (struct binding_scope **, const char *, struct data_string *); int find_bound_string (struct data_string *, struct binding_scope *, const char *); int unset (struct binding_scope *, const char *); int data_string_sprintfa(struct data_string *ds, const char *fmt, ...); int concat_dclists (struct data_string *, struct data_string *, struct data_string *); /* dhcp.c */ extern int outstanding_pings; extern int max_outstanding_acks; extern int max_ack_delay_secs; extern int max_ack_delay_usecs; void dhcp (struct packet *); void dhcpdiscover (struct packet *, int); void dhcprequest (struct packet *, int, struct lease *); void dhcprelease (struct packet *, int); void dhcpdecline (struct packet *, int); void dhcpinform (struct packet *, int); void nak_lease (struct packet *, struct iaddr *cip, struct group*); void ack_lease (struct packet *, struct lease *, unsigned int, TIME, char *, int, struct host_decl *); void echo_client_id(struct packet*, struct lease*, struct option_state*, struct option_state*); void dhcp_reply (struct lease *); int find_lease (struct lease **, struct packet *, struct shared_network *, int *, int *, struct lease *, const char *, int); int mockup_lease (struct lease **, struct packet *, struct shared_network *, struct host_decl *); void static_lease_dereference (struct lease *, const char *, int); int allocate_lease (struct lease **, struct packet *, struct pool *, int *); int permitted (struct packet *, struct permit *); int locate_network (struct packet *); int parse_agent_information_option (struct packet *, int, u_int8_t *); unsigned cons_agent_information_options (struct option_state *, struct dhcp_packet *, unsigned, unsigned); void get_server_source_address(struct in_addr *from, struct option_state *options, struct option_state *out_options, struct packet *packet); void eval_network_statements(struct option_state **options, struct packet *packet, struct group *network_group); u_int16_t dhcp_check_relayport(struct packet *packet); /* dhcpleasequery.c */ void dhcpleasequery (struct packet *, int); void dhcpv6_leasequery (struct data_string *, struct packet *); /* dhcpv6.c */ isc_boolean_t server_duid_isset(void); void copy_server_duid(struct data_string *ds, const char *file, int line); void set_server_duid(struct data_string *new_duid); isc_result_t set_server_duid_from_option(void); void set_server_duid_type(int type); isc_result_t generate_new_server_duid(void); isc_result_t get_client_id(struct packet *, struct data_string *); void dhcpv6(struct packet *); /* bootp.c */ void bootp(struct packet *); void use_host_decl_name(struct packet *, struct lease* , struct option_state *); /* memory.c */ extern int (*group_write_hook) (struct group_object *); extern struct group *root_group; extern group_hash_t *group_name_hash; isc_result_t delete_group (struct group_object *, int); isc_result_t supersede_group (struct group_object *, int); int clone_group (struct group **, struct group *, const char *, int); int write_group (struct group_object *); /* salloc.c */ void relinquish_lease_hunks (void); struct lease *new_leases (unsigned, const char *, int); #if defined (DEBUG_MEMORY_LEAKAGE) || \ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) void relinquish_free_lease_states (void); #endif OMAPI_OBJECT_ALLOC_DECL (lease, struct lease, dhcp_type_lease) OMAPI_OBJECT_ALLOC_DECL (class, struct class, dhcp_type_class) OMAPI_OBJECT_ALLOC_DECL (subclass, struct class, dhcp_type_subclass) OMAPI_OBJECT_ALLOC_DECL (pool, struct pool, dhcp_type_pool) OMAPI_OBJECT_ALLOC_DECL (host, struct host_decl, dhcp_type_host) /* alloc.c */ OMAPI_OBJECT_ALLOC_DECL (subnet, struct subnet, dhcp_type_subnet) OMAPI_OBJECT_ALLOC_DECL (shared_network, struct shared_network, dhcp_type_shared_network) OMAPI_OBJECT_ALLOC_DECL (group_object, struct group_object, dhcp_type_group) OMAPI_OBJECT_ALLOC_DECL (dhcp_control, dhcp_control_object_t, dhcp_type_control) #if defined (DEBUG_MEMORY_LEAKAGE) || \ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) void relinquish_free_pairs (void); void relinquish_free_expressions (void); void relinquish_free_binding_values (void); void relinquish_free_option_caches (void); void relinquish_free_packets (void); #endif int option_chain_head_allocate (struct option_chain_head **, const char *, int); int option_chain_head_reference (struct option_chain_head **, struct option_chain_head *, const char *, int); int option_chain_head_dereference (struct option_chain_head **, const char *, int); int group_allocate (struct group **, const char *, int); int group_reference (struct group **, struct group *, const char *, int); int group_dereference (struct group **, const char *, int); struct dhcp_packet *new_dhcp_packet (const char *, int); struct protocol *new_protocol (const char *, int); struct lease_state *new_lease_state (const char *, int); struct domain_search_list *new_domain_search_list (const char *, int); struct name_server *new_name_server (const char *, int); void free_name_server (struct name_server *, const char *, int); struct option *new_option (const char *, const char *, int); int option_reference(struct option **dest, struct option *src, const char * file, int line); int option_dereference(struct option **dest, const char *file, int line); struct universe *new_universe (const char *, int); void free_universe (struct universe *, const char *, int); void free_domain_search_list (struct domain_search_list *, const char *, int); void free_lease_state (struct lease_state *, const char *, int); void free_protocol (struct protocol *, const char *, int); void free_dhcp_packet (struct dhcp_packet *, const char *, int); struct client_lease *new_client_lease (const char *, int); void free_client_lease (struct client_lease *, const char *, int); struct permit *new_permit (const char *, int); void free_permit (struct permit *, const char *, int); pair new_pair (const char *, int); void free_pair (pair, const char *, int); int expression_allocate (struct expression **, const char *, int); int expression_reference (struct expression **, struct expression *, const char *, int); void free_expression (struct expression *, const char *, int); int binding_value_allocate (struct binding_value **, const char *, int); int binding_value_reference (struct binding_value **, struct binding_value *, const char *, int); void free_binding_value (struct binding_value *, const char *, int); int fundef_allocate (struct fundef **, const char *, int); int fundef_reference (struct fundef **, struct fundef *, const char *, int); int option_cache_allocate (struct option_cache **, const char *, int); int option_cache_reference (struct option_cache **, struct option_cache *, const char *, int); int buffer_allocate (struct buffer **, unsigned, const char *, int); int buffer_reference (struct buffer **, struct buffer *, const char *, int); int buffer_dereference (struct buffer **, const char *, int); int dns_host_entry_allocate (struct dns_host_entry **, const char *, const char *, int); int dns_host_entry_reference (struct dns_host_entry **, struct dns_host_entry *, const char *, int); int dns_host_entry_dereference (struct dns_host_entry **, const char *, int); int option_state_allocate (struct option_state **, const char *, int); int option_state_reference (struct option_state **, struct option_state *, const char *, int); int option_state_dereference (struct option_state **, const char *, int); int data_string_new(struct data_string *, const char *, unsigned int, const char *, int); void data_string_copy(struct data_string *, const struct data_string *, const char *, int); void data_string_forget (struct data_string *, const char *, int); void data_string_truncate (struct data_string *, int); int data_string_terminate (struct data_string *, const char *, int); int executable_statement_allocate (struct executable_statement **, const char *, int); int executable_statement_reference (struct executable_statement **, struct executable_statement *, const char *, int); int packet_allocate (struct packet **, const char *, int); int packet_reference (struct packet **, struct packet *, const char *, int); int packet_dereference (struct packet **, const char *, int); int binding_scope_allocate (struct binding_scope **, const char *, int); int binding_scope_reference (struct binding_scope **, struct binding_scope *, const char *, int); int dns_zone_allocate (struct dns_zone **, const char *, int); int dns_zone_reference (struct dns_zone **, struct dns_zone *, const char *, int); /* print.c */ #define DEFAULT_TIME_FORMAT 0 #define LOCAL_TIME_FORMAT 1 extern int db_time_format; char *quotify_string (const char *, const char *, int); char *quotify_buf (const unsigned char *, unsigned, const char, const char *, int); char *print_base64 (const unsigned char *, unsigned, const char *, int); char *print_hw_addr (const int, const int, const unsigned char *); void print_lease (struct lease *); void dump_raw (const unsigned char *, unsigned); void dump_packet_option (struct option_cache *, struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct binding_scope **, struct universe *, void *); void dump_packet (struct packet *); void hash_dump (struct hash_table *); char *print_hex (unsigned, const u_int8_t *, unsigned, unsigned); void print_hex_only (unsigned, const u_int8_t *, unsigned, char *); void print_hex_or_string (unsigned, const u_int8_t *, unsigned, char *); #define print_hex_1(len, data, limit) print_hex(len, data, limit, 0) #define print_hex_2(len, data, limit) print_hex(len, data, limit, 1) #define print_hex_3(len, data, limit) print_hex(len, data, limit, 2) char *print_dotted_quads (unsigned, const u_int8_t *); char *print_dec_1 (unsigned long); char *print_dec_2 (unsigned long); void print_expression (const char *, struct expression *); int token_print_indent_concat (FILE *, int, int, const char *, const char *, ...); int token_indent_data_string (FILE *, int, int, const char *, const char *, struct data_string *); int token_print_indent (FILE *, int, int, const char *, const char *, const char *); void indent_spaces (FILE *, int); #if defined (NSUPDATE) void print_dns_status (int, struct dhcp_ddns_cb *, isc_result_t); #endif const char *print_time(TIME); void get_hw_addr(const char *name, struct hardware *hw); char *buf_to_hex (const unsigned char *s, unsigned len, const char *file, int line); char *format_lease_id(const unsigned char *s, unsigned len, int format, const char *file, int line); char *absolute_path(const char *orgpath); /* socket.c */ #if defined (USE_SOCKET_SEND) || defined (USE_SOCKET_RECEIVE) \ || defined (USE_SOCKET_FALLBACK) int if_register_socket(struct interface_info *, int, int *, struct in6_addr *); void set_multicast_hop_limit(struct interface_info* info, int hop_limit); #endif #if defined (USE_SOCKET_FALLBACK) && !defined (USE_SOCKET_SEND) void if_reinitialize_fallback (struct interface_info *); void if_register_fallback (struct interface_info *); ssize_t send_fallback (struct interface_info *, struct packet *, struct dhcp_packet *, size_t, struct in_addr, struct sockaddr_in *, struct hardware *); ssize_t send_fallback6(struct interface_info *, struct packet *, struct dhcp_packet *, size_t, struct in6_addr *, struct sockaddr_in6 *, struct hardware *); #endif #ifdef USE_SOCKET_SEND void if_reinitialize_send (struct interface_info *); void if_register_send (struct interface_info *); void if_deregister_send (struct interface_info *); ssize_t send_packet (struct interface_info *, struct packet *, struct dhcp_packet *, size_t, struct in_addr, struct sockaddr_in *, struct hardware *); #endif ssize_t send_packet6(struct interface_info *, const unsigned char *, size_t, struct sockaddr_in6 *); #ifdef USE_SOCKET_RECEIVE void if_reinitialize_receive (struct interface_info *); void if_register_receive (struct interface_info *); void if_deregister_receive (struct interface_info *); ssize_t receive_packet (struct interface_info *, unsigned char *, size_t, struct sockaddr_in *, struct hardware *); #endif #if defined (USE_SOCKET_FALLBACK) isc_result_t fallback_discard (omapi_object_t *); #endif #if defined (USE_SOCKET_SEND) int can_unicast_without_arp (struct interface_info *); int can_receive_unicast_unconfigured (struct interface_info *); int supports_multiple_interfaces (struct interface_info *); void maybe_setup_fallback (void); #endif void if_register6(struct interface_info *info, int do_multicast); void if_register_linklocal6(struct interface_info *info); ssize_t receive_packet6(struct interface_info *interface, unsigned char *buf, size_t len, struct sockaddr_in6 *from, struct in6_addr *to_addr, unsigned int *if_index); void if_deregister6(struct interface_info *info); /* bpf.c */ #if defined (USE_BPF_SEND) || defined (USE_BPF_RECEIVE) int if_register_bpf (struct interface_info *); #endif #ifdef USE_BPF_SEND void if_reinitialize_send (struct interface_info *); void if_register_send (struct interface_info *); void if_deregister_send (struct interface_info *); ssize_t send_packet (struct interface_info *, struct packet *, struct dhcp_packet *, size_t, struct in_addr, struct sockaddr_in *, struct hardware *); #endif #ifdef USE_BPF_RECEIVE void if_reinitialize_receive (struct interface_info *); void if_register_receive (struct interface_info *); void if_deregister_receive (struct interface_info *); ssize_t receive_packet (struct interface_info *, unsigned char *, size_t, struct sockaddr_in *, struct hardware *); #endif #if defined (USE_BPF_SEND) int can_unicast_without_arp (struct interface_info *); int can_receive_unicast_unconfigured (struct interface_info *); int supports_multiple_interfaces (struct interface_info *); void maybe_setup_fallback (void); #endif /* lpf.c */ #if defined (USE_LPF_SEND) || defined (USE_LPF_RECEIVE) int if_register_lpf (struct interface_info *); #endif #ifdef USE_LPF_SEND void if_reinitialize_send (struct interface_info *); void if_register_send (struct interface_info *); void if_deregister_send (struct interface_info *); ssize_t send_packet (struct interface_info *, struct packet *, struct dhcp_packet *, size_t, struct in_addr, struct sockaddr_in *, struct hardware *); #endif #ifdef USE_LPF_RECEIVE void if_reinitialize_receive (struct interface_info *); void if_register_receive (struct interface_info *); void if_deregister_receive (struct interface_info *); ssize_t receive_packet (struct interface_info *, unsigned char *, size_t, struct sockaddr_in *, struct hardware *); #endif #if defined (USE_LPF_SEND) int can_unicast_without_arp (struct interface_info *); int can_receive_unicast_unconfigured (struct interface_info *); int supports_multiple_interfaces (struct interface_info *); void maybe_setup_fallback (void); #endif /* nit.c */ #if defined (USE_NIT_SEND) || defined (USE_NIT_RECEIVE) int if_register_nit (struct interface_info *); #endif #ifdef USE_NIT_SEND void if_reinitialize_send (struct interface_info *); void if_register_send (struct interface_info *); void if_deregister_send (struct interface_info *); ssize_t send_packet (struct interface_info *, struct packet *, struct dhcp_packet *, size_t, struct in_addr, struct sockaddr_in *, struct hardware *); #endif #ifdef USE_NIT_RECEIVE void if_reinitialize_receive (struct interface_info *); void if_register_receive (struct interface_info *); void if_deregister_receive (struct interface_info *); ssize_t receive_packet (struct interface_info *, unsigned char *, size_t, struct sockaddr_in *, struct hardware *); #endif #if defined (USE_NIT_SEND) int can_unicast_without_arp (struct interface_info *); int can_receive_unicast_unconfigured (struct interface_info *); int supports_multiple_interfaces (struct interface_info *); void maybe_setup_fallback (void); #endif /* dlpi.c */ #if defined (USE_DLPI_SEND) || defined (USE_DLPI_RECEIVE) int if_register_dlpi (struct interface_info *); #endif #ifdef USE_DLPI_SEND int can_unicast_without_arp (struct interface_info *); int can_receive_unicast_unconfigured (struct interface_info *); void if_reinitialize_send (struct interface_info *); void if_register_send (struct interface_info *); void if_deregister_send (struct interface_info *); ssize_t send_packet (struct interface_info *, struct packet *, struct dhcp_packet *, size_t, struct in_addr, struct sockaddr_in *, struct hardware *); int supports_multiple_interfaces (struct interface_info *); void maybe_setup_fallback (void); #endif #ifdef USE_DLPI_RECEIVE void if_reinitialize_receive (struct interface_info *); void if_register_receive (struct interface_info *); void if_deregister_receive (struct interface_info *); ssize_t receive_packet (struct interface_info *, unsigned char *, size_t, struct sockaddr_in *, struct hardware *); #endif /* raw.c */ #ifdef USE_RAW_SEND void if_reinitialize_send (struct interface_info *); void if_register_send (struct interface_info *); void if_deregister_send (struct interface_info *); ssize_t send_packet (struct interface_info *, struct packet *, struct dhcp_packet *, size_t, struct in_addr, struct sockaddr_in *, struct hardware *); int can_unicast_without_arp (struct interface_info *); int can_receive_unicast_unconfigured (struct interface_info *); int supports_multiple_interfaces (struct interface_info *); void maybe_setup_fallback (void); #endif /* discover.c */ extern struct interface_info *interfaces, *dummy_interfaces, *fallback_interface; extern struct protocol *protocols; extern int quiet_interface_discovery; isc_result_t interface_setup (void); void interface_trace_setup (void); extern struct in_addr limited_broadcast; extern int local_family; extern struct in_addr local_address; extern struct in6_addr local_address6; extern int bind_local_address6; extern u_int16_t local_port; extern u_int16_t remote_port; extern u_int16_t relay_port; extern int dhcpv4_over_dhcpv6; extern int (*dhcp_interface_setup_hook) (struct interface_info *, struct iaddr *); extern int (*dhcp_interface_discovery_hook) (struct interface_info *); extern isc_result_t (*dhcp_interface_startup_hook) (struct interface_info *); extern void (*bootp_packet_handler) (struct interface_info *, struct dhcp_packet *, unsigned, unsigned int, struct iaddr, struct hardware *); extern void (*dhcpv6_packet_handler)(struct interface_info *, const char *, int, int, const struct iaddr *, isc_boolean_t); extern struct timeout *timeouts; extern omapi_object_type_t *dhcp_type_interface; #if defined (TRACING) extern trace_type_t *interface_trace; extern trace_type_t *inpacket_trace; extern trace_type_t *outpacket_trace; #endif extern struct interface_info **interface_vector; extern int interface_count; extern int interface_max; isc_result_t interface_initialize(omapi_object_t *, const char *, int); void discover_interfaces(int); int setup_fallback (struct interface_info **, const char *, int); int if_readsocket (omapi_object_t *); void reinitialize_interfaces (void); /* dispatch.c */ void set_time(TIME); struct timeval *process_outstanding_timeouts (struct timeval *); void dispatch (void); isc_result_t got_one(omapi_object_t *); isc_result_t got_one_v6(omapi_object_t *); isc_result_t interface_set_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_typed_data_t *); isc_result_t interface_get_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_value_t **); isc_result_t interface_destroy (omapi_object_t *, const char *, int); isc_result_t interface_signal_handler (omapi_object_t *, const char *, va_list); isc_result_t interface_stuff_values (omapi_object_t *, omapi_object_t *, omapi_object_t *); void add_timeout (struct timeval *, void (*) (void *), void *, tvref_t, tvunref_t); void cancel_timeout (void (*) (void *), void *); void cancel_all_timeouts (void); void relinquish_timeouts (void); OMAPI_OBJECT_ALLOC_DECL (interface, struct interface_info, dhcp_type_interface) /* tables.c */ extern char *default_option_format; extern struct universe dhcp_universe; extern struct universe dhcpv6_universe; extern struct universe nwip_universe; extern struct universe fqdn_universe; extern struct universe vsio_universe; extern int dhcp_option_default_priority_list []; extern int dhcp_option_default_priority_list_count; extern const char *hardware_types [256]; extern int universe_count, universe_max; extern struct universe **universes; extern universe_hash_t *universe_hash; void initialize_common_option_spaces (void); extern struct universe *config_universe; /* stables.c */ #if defined (FAILOVER_PROTOCOL) extern failover_option_t null_failover_option; extern failover_option_t skip_failover_option; extern struct failover_option_info ft_options []; extern u_int32_t fto_allowed []; extern int ft_sizes []; extern const char *dhcp_flink_state_names []; #endif extern const char *binding_state_names []; extern struct universe agent_universe; extern struct universe server_universe; extern struct enumeration ddns_styles; extern struct enumeration syslog_enum; void initialize_server_option_spaces (void); extern struct enumeration prefix_length_modes; /* inet.c */ struct iaddr subnet_number (struct iaddr, struct iaddr); struct iaddr ip_addr (struct iaddr, struct iaddr, u_int32_t); struct iaddr broadcast_addr (struct iaddr, struct iaddr); u_int32_t host_addr (struct iaddr, struct iaddr); int addr_eq (struct iaddr, struct iaddr); int addr_match(struct iaddr *, struct iaddrmatch *); int addr_cmp(const struct iaddr *a1, const struct iaddr *a2); int addr_or(struct iaddr *result, const struct iaddr *a1, const struct iaddr *a2); int addr_and(struct iaddr *result, const struct iaddr *a1, const struct iaddr *a2); isc_boolean_t is_cidr_mask_valid(const struct iaddr *addr, int bits); isc_result_t range2cidr(struct iaddrcidrnetlist **result, const struct iaddr *lo, const struct iaddr *hi); isc_result_t free_iaddrcidrnetlist(struct iaddrcidrnetlist **result); const char *piaddr (struct iaddr); char *piaddrmask(struct iaddr *, struct iaddr *); char *piaddrcidr(const struct iaddr *, unsigned int); u_int16_t validate_port(char *); u_int16_t validate_port_pair(char *); #if defined(DHCPv6) const char *pin6_addr (const struct in6_addr*); #endif /* dhclient.c */ extern int nowait; extern int wanted_ia_na; extern int wanted_ia_ta; extern int wanted_ia_pd; extern int require_all_ias; extern const char *path_dhclient_conf; extern const char *path_dhclient_db; extern const char *path_dhclient_pid; extern char *path_dhclient_script; extern int interfaces_requested; extern struct data_string default_duid; extern int duid_type; extern const char *path_dhclient_duid; extern struct client_config top_level_config; void dhcpoffer (struct packet *); void dhcpack (struct packet *); void dhcpnak (struct packet *); void send_discover (void *); void send_request (void *); void send_release (void *); void send_decline (void *); void state_reboot (void *); void state_init (void *); void state_selecting (void *); void state_requesting (void *); void state_bound (void *); void state_stop (void *); void state_panic (void *); void bind_lease (struct client_state *); void make_client_options (struct client_state *, struct client_lease *, u_int8_t *, struct option_cache *, struct iaddr *, struct option **, struct option_state **); void make_discover (struct client_state *, struct client_lease *); void make_request (struct client_state *, struct client_lease *); void make_decline (struct client_state *, struct client_lease *); void make_release (struct client_state *, struct client_lease *); void destroy_client_lease (struct client_lease *); void rewrite_client_leases (void); void write_lease_option (struct option_cache *, struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct binding_scope **, struct universe *, void *); int write_client_lease (struct client_state *, struct client_lease *, int, int); isc_result_t write_client6_lease(struct client_state *client, struct dhc6_lease *lease, int rewrite, int sync); int dhcp_option_ev_name (char *, size_t, struct option *); void script_init (struct client_state *, const char *, struct string_list *); void client_option_envadd (struct option_cache *, struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct binding_scope **, struct universe *, void *); void script_write_params (struct client_state *, const char *, struct client_lease *); void script_write_requested (struct client_state *); int script_go (struct client_state *); void client_envadd (struct client_state *, const char *, const char *, const char *, ...) __attribute__((__format__(__printf__,4,5))); struct client_lease *packet_to_lease (struct packet *, struct client_state *); void finish (char); void detach (void); void write_client_pid_file (void); void client_location_changed (void); void do_release (struct client_state *); int dhclient_interface_shutdown_hook (struct interface_info *); int dhclient_interface_discovery_hook (struct interface_info *); isc_result_t dhclient_interface_startup_hook (struct interface_info *); void dhclient_schedule_updates(struct client_state *client, struct iaddr *addr, int offset); void client_dns_update_timeout (void *cp); isc_result_t client_dns_update(struct client_state *client, dhcp_ddns_cb_t *ddns_cb); void client_dns_remove(struct client_state *client, struct iaddr *addr); void dhcpv4_client_assignments(void); void dhcpv6_client_assignments(void); void form_duid(struct data_string *duid, const char *file, int line); void dhcp4o6_start(void); /* dhc6.c */ void dhc6_lease_destroy(struct dhc6_lease **src, const char *file, int line); void start_init6(struct client_state *client); void start_info_request6(struct client_state *client); void start_confirm6(struct client_state *client); void start_release6(struct client_state *client); void start_selecting6(struct client_state *client); void unconfigure6(struct client_state *client, const char *reason); /* db.c */ int write_lease (struct lease *); int write_host (struct host_decl *); int write_server_duid(void); #if defined (FAILOVER_PROTOCOL) int write_failover_state (dhcp_failover_state_t *); #endif int db_printable (const unsigned char *); int db_printable_len (const unsigned char *, unsigned); isc_result_t write_named_billing_class(const void *, unsigned, void *); void write_billing_classes (void); int write_billing_class (struct class *); void commit_leases_timeout (void *); int commit_leases (void); int commit_leases_timed (void); void db_startup (int); int new_lease_file (int test_mode); int group_writer (struct group_object *); int write_ia(const struct ia_xx *); /* packet.c */ u_int32_t checksum (unsigned char *, unsigned, u_int32_t); u_int32_t wrapsum (u_int32_t); void assemble_hw_header (struct interface_info *, unsigned char *, unsigned *, struct hardware *); void assemble_udp_ip_header (struct interface_info *, unsigned char *, unsigned *, u_int32_t, u_int32_t, u_int32_t, unsigned char *, unsigned); ssize_t decode_hw_header (struct interface_info *, unsigned char *, unsigned, struct hardware *); ssize_t decode_udp_ip_header (struct interface_info *, unsigned char *, unsigned, struct sockaddr_in *, unsigned, unsigned *, int); /* ethernet.c */ void assemble_ethernet_header (struct interface_info *, unsigned char *, unsigned *, struct hardware *); ssize_t decode_ethernet_header (struct interface_info *, unsigned char *, unsigned, struct hardware *); /* tr.c */ void assemble_tr_header (struct interface_info *, unsigned char *, unsigned *, struct hardware *); ssize_t decode_tr_header (struct interface_info *, unsigned char *, unsigned, struct hardware *); /* dhxpxlt.c */ void convert_statement (struct parse *); void convert_host_statement (struct parse *, jrefproto); void convert_host_name (struct parse *, jrefproto); void convert_class_statement (struct parse *, jrefproto, int); void convert_class_decl (struct parse *, jrefproto); void convert_lease_time (struct parse *, jrefproto, char *); void convert_shared_net_statement (struct parse *, jrefproto); void convert_subnet_statement (struct parse *, jrefproto); void convert_subnet_decl (struct parse *, jrefproto); void convert_host_decl (struct parse *, jrefproto); void convert_hardware_decl (struct parse *, jrefproto); void convert_hardware_addr (struct parse *, jrefproto); void convert_filename_decl (struct parse *, jrefproto); void convert_servername_decl (struct parse *, jrefproto); void convert_ip_addr_or_hostname (struct parse *, jrefproto, int); void convert_fixed_addr_decl (struct parse *, jrefproto); void convert_option_decl (struct parse *, jrefproto); void convert_lease_statement (struct parse *, jrefproto); void convert_address_range (struct parse *, jrefproto); void convert_date (struct parse *, jrefproto, char *); void convert_numeric_aggregate (struct parse *, jrefproto, int, int, int, int); void indent (int); /* route.c */ void add_route_direct (struct interface_info *, struct in_addr); void add_route_net (struct interface_info *, struct in_addr, struct in_addr); void add_route_default_gateway (struct interface_info *, struct in_addr); void remove_routes (struct in_addr); void remove_if_route (struct interface_info *, struct in_addr); void remove_all_if_routes (struct interface_info *); void set_netmask (struct interface_info *, struct in_addr); void set_broadcast_addr (struct interface_info *, struct in_addr); void set_ip_address (struct interface_info *, struct in_addr); /* clparse.c */ isc_result_t read_client_conf (void); int read_client_conf_file (const char *, struct interface_info *, struct client_config *); void read_client_leases (void); void read_client_duid (void); void parse_client_statement (struct parse *, struct interface_info *, struct client_config *); int parse_X (struct parse *, u_int8_t *, unsigned); int parse_option_list (struct parse *, struct option ***); void parse_interface_declaration (struct parse *, struct client_config *, char *); int interface_or_dummy (struct interface_info **, const char *); void make_client_state (struct client_state **); void make_client_config (struct client_state *, struct client_config *); void parse_client_lease_statement (struct parse *, int); void parse_client_lease_declaration (struct parse *, struct client_lease *, struct interface_info **, struct client_state **); int parse_option_decl (struct option_cache **, struct parse *); void parse_string_list (struct parse *, struct string_list **, int); int parse_ip_addr (struct parse *, struct iaddr *); int parse_ip_addr_with_subnet(struct parse *, struct iaddrmatch *); void parse_reject_statement (struct parse *, struct client_config *); /* icmp.c */ OMAPI_OBJECT_ALLOC_DECL (icmp_state, struct icmp_state, dhcp_type_icmp) extern struct icmp_state *icmp_state; void icmp_startup (int, void (*) (struct iaddr, u_int8_t *, int)); int icmp_readsocket (omapi_object_t *); int icmp_echorequest (struct iaddr *); isc_result_t icmp_echoreply (omapi_object_t *); /* dns.c */ isc_result_t enter_dns_zone (struct dns_zone *); isc_result_t dns_zone_lookup (struct dns_zone **, const char *); int dns_zone_dereference (struct dns_zone **, const char *, int); #if defined (NSUPDATE) #define FIND_FORWARD 0 #define FIND_REVERSE 1 isc_result_t find_cached_zone (dhcp_ddns_cb_t *, int); void forget_zone (struct dns_zone **); void repudiate_zone (struct dns_zone **); int get_dhcid (dhcp_ddns_cb_t *, int, const u_int8_t *, unsigned); void dhcid_tolease (struct data_string *, struct data_string *); isc_result_t dhcid_fromlease (struct data_string *, struct data_string *); isc_result_t ddns_update_fwd(struct data_string *, struct iaddr, struct data_string *, unsigned long, unsigned, unsigned); isc_result_t ddns_remove_fwd(struct data_string *, struct iaddr, struct data_string *); char *ddns_state_name(int state); #endif /* NSUPDATE */ dhcp_ddns_cb_t *ddns_cb_alloc(const char *file, int line); void ddns_cb_free (dhcp_ddns_cb_t *ddns_cb, const char *file, int line); void ddns_cb_forget_zone (dhcp_ddns_cb_t *ddns_cb); isc_result_t ddns_modify_fwd(dhcp_ddns_cb_t *ddns_cb, const char *file, int line); isc_result_t ddns_modify_ptr(dhcp_ddns_cb_t *ddns_cb, const char *file, int line); void ddns_cancel(dhcp_ddns_cb_t *ddns_cb, const char *file, int line); /* resolv.c */ extern char path_resolv_conf []; extern struct name_server *name_servers; extern struct domain_search_list *domains; void read_resolv_conf (TIME); struct name_server *first_name_server (void); /* inet_addr.c */ #ifdef NEED_INET_ATON int inet_aton (const char *, struct in_addr *); #endif /* class.c */ extern int have_billing_classes; extern struct class unknown_class; extern struct class known_class; extern struct collection default_collection; extern struct collection *collections; extern struct executable_statement *default_classification_rules; void classification_setup (void); void classify_client (struct packet *); int check_collection (struct packet *, struct lease *, struct collection *); void classify (struct packet *, struct class *); isc_result_t unlink_class (struct class **class); isc_result_t find_class (struct class **, const char *, const char *, int); void unbill_class (struct lease *); int bill_class (struct lease *, struct class *); /* execute.c */ int execute_statements (struct binding_value **result, struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct binding_scope **, struct executable_statement *, struct on_star *); void execute_statements_in_scope (struct binding_value **result, struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct binding_scope **, struct group *, struct group *, struct on_star *); int executable_statement_dereference (struct executable_statement **, const char *, int); void write_statements (FILE *, struct executable_statement *, int); int find_matching_case (struct executable_statement **, struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct binding_scope **, struct expression *, struct executable_statement *); int executable_statement_foreach (struct executable_statement *, int (*) (struct executable_statement *, void *, int), void *, int); /* comapi.c */ extern omapi_object_type_t *dhcp_type_group; extern omapi_object_type_t *dhcp_type_shared_network; extern omapi_object_type_t *dhcp_type_subnet; extern omapi_object_type_t *dhcp_type_control; extern dhcp_control_object_t *dhcp_control_object; void dhcp_common_objects_setup (void); isc_result_t dhcp_group_set_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_typed_data_t *); isc_result_t dhcp_group_get_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_value_t **); isc_result_t dhcp_group_destroy (omapi_object_t *, const char *, int); isc_result_t dhcp_group_signal_handler (omapi_object_t *, const char *, va_list); isc_result_t dhcp_group_stuff_values (omapi_object_t *, omapi_object_t *, omapi_object_t *); isc_result_t dhcp_group_lookup (omapi_object_t **, omapi_object_t *, omapi_object_t *); isc_result_t dhcp_group_create (omapi_object_t **, omapi_object_t *); isc_result_t dhcp_group_remove (omapi_object_t *, omapi_object_t *); isc_result_t dhcp_control_set_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_typed_data_t *); isc_result_t dhcp_control_get_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_value_t **); isc_result_t dhcp_control_destroy (omapi_object_t *, const char *, int); isc_result_t dhcp_control_signal_handler (omapi_object_t *, const char *, va_list); isc_result_t dhcp_control_stuff_values (omapi_object_t *, omapi_object_t *, omapi_object_t *); isc_result_t dhcp_control_lookup (omapi_object_t **, omapi_object_t *, omapi_object_t *); isc_result_t dhcp_control_create (omapi_object_t **, omapi_object_t *); isc_result_t dhcp_control_remove (omapi_object_t *, omapi_object_t *); isc_result_t dhcp_subnet_set_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_typed_data_t *); isc_result_t dhcp_subnet_get_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_value_t **); isc_result_t dhcp_subnet_destroy (omapi_object_t *, const char *, int); isc_result_t dhcp_subnet_signal_handler (omapi_object_t *, const char *, va_list); isc_result_t dhcp_subnet_stuff_values (omapi_object_t *, omapi_object_t *, omapi_object_t *); isc_result_t dhcp_subnet_lookup (omapi_object_t **, omapi_object_t *, omapi_object_t *); isc_result_t dhcp_subnet_create (omapi_object_t **, omapi_object_t *); isc_result_t dhcp_subnet_remove (omapi_object_t *, omapi_object_t *); isc_result_t dhcp_shared_network_set_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_typed_data_t *); isc_result_t dhcp_shared_network_get_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_value_t **); isc_result_t dhcp_shared_network_destroy (omapi_object_t *, const char *, int); isc_result_t dhcp_shared_network_signal_handler (omapi_object_t *, const char *, va_list); isc_result_t dhcp_shared_network_stuff_values (omapi_object_t *, omapi_object_t *, omapi_object_t *); isc_result_t dhcp_shared_network_lookup (omapi_object_t **, omapi_object_t *, omapi_object_t *); isc_result_t dhcp_shared_network_create (omapi_object_t **, omapi_object_t *); isc_result_t dhcp_shared_network_remove (omapi_object_t *, omapi_object_t *); /* omapi.c */ extern int (*dhcp_interface_shutdown_hook) (struct interface_info *); extern omapi_object_type_t *dhcp_type_lease; extern omapi_object_type_t *dhcp_type_pool; extern omapi_object_type_t *dhcp_type_class; extern omapi_object_type_t *dhcp_type_subclass; #if defined (FAILOVER_PROTOCOL) extern omapi_object_type_t *dhcp_type_failover_state; extern omapi_object_type_t *dhcp_type_failover_link; extern omapi_object_type_t *dhcp_type_failover_listener; #endif void dhcp_db_objects_setup (void); isc_result_t dhcp_lease_set_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_typed_data_t *); isc_result_t dhcp_lease_get_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_value_t **); isc_result_t dhcp_lease_destroy (omapi_object_t *, const char *, int); isc_result_t dhcp_lease_signal_handler (omapi_object_t *, const char *, va_list); isc_result_t dhcp_lease_stuff_values (omapi_object_t *, omapi_object_t *, omapi_object_t *); isc_result_t dhcp_lease_lookup (omapi_object_t **, omapi_object_t *, omapi_object_t *); isc_result_t dhcp_lease_create (omapi_object_t **, omapi_object_t *); isc_result_t dhcp_lease_remove (omapi_object_t *, omapi_object_t *); isc_result_t dhcp_host_set_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_typed_data_t *); isc_result_t dhcp_host_get_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_value_t **); isc_result_t dhcp_host_destroy (omapi_object_t *, const char *, int); isc_result_t dhcp_host_signal_handler (omapi_object_t *, const char *, va_list); isc_result_t dhcp_host_stuff_values (omapi_object_t *, omapi_object_t *, omapi_object_t *); isc_result_t dhcp_host_lookup (omapi_object_t **, omapi_object_t *, omapi_object_t *); isc_result_t dhcp_host_create (omapi_object_t **, omapi_object_t *); isc_result_t dhcp_host_remove (omapi_object_t *, omapi_object_t *); isc_result_t dhcp_pool_set_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_typed_data_t *); isc_result_t dhcp_pool_get_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_value_t **); isc_result_t dhcp_pool_destroy (omapi_object_t *, const char *, int); isc_result_t dhcp_pool_signal_handler (omapi_object_t *, const char *, va_list); isc_result_t dhcp_pool_stuff_values (omapi_object_t *, omapi_object_t *, omapi_object_t *); isc_result_t dhcp_pool_lookup (omapi_object_t **, omapi_object_t *, omapi_object_t *); isc_result_t dhcp_pool_create (omapi_object_t **, omapi_object_t *); isc_result_t dhcp_pool_remove (omapi_object_t *, omapi_object_t *); isc_result_t dhcp_class_set_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_typed_data_t *); isc_result_t dhcp_class_get_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_value_t **); isc_result_t dhcp_class_destroy (omapi_object_t *, const char *, int); isc_result_t dhcp_class_signal_handler (omapi_object_t *, const char *, va_list); isc_result_t dhcp_class_stuff_values (omapi_object_t *, omapi_object_t *, omapi_object_t *); isc_result_t dhcp_class_lookup (omapi_object_t **, omapi_object_t *, omapi_object_t *); isc_result_t dhcp_class_create (omapi_object_t **, omapi_object_t *); isc_result_t dhcp_class_remove (omapi_object_t *, omapi_object_t *); isc_result_t dhcp_subclass_set_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_typed_data_t *); isc_result_t dhcp_subclass_get_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_value_t **); isc_result_t dhcp_subclass_destroy (omapi_object_t *, const char *, int); isc_result_t dhcp_subclass_signal_handler (omapi_object_t *, const char *, va_list); isc_result_t dhcp_subclass_stuff_values (omapi_object_t *, omapi_object_t *, omapi_object_t *); isc_result_t dhcp_subclass_lookup (omapi_object_t **, omapi_object_t *, omapi_object_t *); isc_result_t dhcp_subclass_create (omapi_object_t **, omapi_object_t *); isc_result_t dhcp_subclass_remove (omapi_object_t *, omapi_object_t *); isc_result_t dhcp_interface_set_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_typed_data_t *); isc_result_t dhcp_interface_get_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_value_t **); isc_result_t dhcp_interface_destroy (omapi_object_t *, const char *, int); isc_result_t dhcp_interface_signal_handler (omapi_object_t *, const char *, va_list ap); isc_result_t dhcp_interface_stuff_values (omapi_object_t *, omapi_object_t *, omapi_object_t *); isc_result_t dhcp_interface_lookup (omapi_object_t **, omapi_object_t *, omapi_object_t *); isc_result_t dhcp_interface_create (omapi_object_t **, omapi_object_t *); isc_result_t dhcp_interface_remove (omapi_object_t *, omapi_object_t *); void interface_stash (struct interface_info *); void interface_snorf (struct interface_info *, int); isc_result_t binding_scope_set_value (struct binding_scope *, int, omapi_data_string_t *, omapi_typed_data_t *); isc_result_t binding_scope_get_value (omapi_value_t **, struct binding_scope *, omapi_data_string_t *); isc_result_t binding_scope_stuff_values (omapi_object_t *, struct binding_scope *); void register_eventhandler(struct eventqueue **, void (*handler)(void *)); void unregister_eventhandler(struct eventqueue **, void (*handler)(void *)); void trigger_event(struct eventqueue **); /* mdb.c */ extern struct subnet *subnets; extern struct shared_network *shared_networks; extern host_hash_t *host_hw_addr_hash; extern host_hash_t *host_uid_hash; extern host_hash_t *host_name_hash; extern lease_id_hash_t *lease_uid_hash; extern lease_ip_hash_t *lease_ip_addr_hash; extern lease_id_hash_t *lease_hw_addr_hash; extern omapi_object_type_t *dhcp_type_host; extern int numclasseswritten; isc_result_t enter_class (struct class *, int, int); isc_result_t delete_class (struct class *, int); isc_result_t enter_host (struct host_decl *, int, int); isc_result_t delete_host (struct host_decl *, int); void change_host_uid(struct host_decl *host, const char *data, int len); int find_hosts_by_haddr (struct host_decl **, int, const unsigned char *, unsigned, const char *, int); int find_hosts_by_uid (struct host_decl **, const unsigned char *, unsigned, const char *, int); int find_hosts_by_option(struct host_decl **, struct packet *, struct option_state *, const char *, int); int find_host_for_network (struct subnet **, struct host_decl **, struct iaddr *, struct shared_network *); void new_address_range (struct parse *, struct iaddr, struct iaddr, struct subnet *, struct pool *, struct lease **); isc_result_t dhcp_lease_free (omapi_object_t *, const char *, int); isc_result_t dhcp_lease_get (omapi_object_t **, const char *, int); int find_grouped_subnet (struct subnet **, struct shared_network *, struct iaddr, const char *, int); int find_subnet(struct subnet **, struct iaddr, const char *, int); void enter_shared_network (struct shared_network *); void new_shared_network_interface (struct parse *, struct shared_network *, const char *); int subnet_inner_than(const struct subnet *, const struct subnet *, int); void enter_subnet (struct subnet *); void enter_lease (struct lease *); int supersede_lease (struct lease *, struct lease *, int, int, int, int); void make_binding_state_transition (struct lease *); int lease_copy (struct lease **, struct lease *, const char *, int); void release_lease (struct lease *, struct packet *); void abandon_lease (struct lease *, const char *); #if 0 /* this appears to be unused and I plan to remove it SAR */ void dissociate_lease (struct lease *); #endif void pool_timer (void *); int find_lease_by_uid (struct lease **, const unsigned char *, unsigned, const char *, int); int find_lease_by_hw_addr (struct lease **, const unsigned char *, unsigned, const char *, int); int find_lease_by_ip_addr (struct lease **, struct iaddr, const char *, int); void uid_hash_add (struct lease *); void uid_hash_delete (struct lease *); void hw_hash_add (struct lease *); void hw_hash_delete (struct lease *); int write_leases (void); int write_leases6(void); #if !defined(BINARY_LEASES) void lease_insert(struct lease **, struct lease *); void lease_remove(struct lease **, struct lease *); void lease_remove_all(struct lease **); #endif int lease_enqueue (struct lease *); isc_result_t lease_instantiate(const void *, unsigned, void *); void expire_all_pools (void); void dump_subnets (void); #if defined (DEBUG_MEMORY_LEAKAGE) || \ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) void free_everything (void); #endif /* failover.c */ #if defined (FAILOVER_PROTOCOL) extern dhcp_failover_state_t *failover_states; void dhcp_failover_sanity_check (void); void dhcp_failover_startup (void); int dhcp_failover_write_all_states (void); isc_result_t enter_failover_peer (dhcp_failover_state_t *); isc_result_t find_failover_peer (dhcp_failover_state_t **, const char *, const char *, int); isc_result_t dhcp_failover_link_initiate (omapi_object_t *); isc_result_t dhcp_failover_link_signal (omapi_object_t *, const char *, va_list); isc_result_t dhcp_failover_link_set_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_typed_data_t *); isc_result_t dhcp_failover_link_get_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_value_t **); isc_result_t dhcp_failover_link_destroy (omapi_object_t *, const char *, int); isc_result_t dhcp_failover_link_stuff_values (omapi_object_t *, omapi_object_t *, omapi_object_t *); isc_result_t dhcp_failover_listen (omapi_object_t *); isc_result_t dhcp_failover_listener_signal (omapi_object_t *, const char *, va_list); isc_result_t dhcp_failover_listener_set_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_typed_data_t *); isc_result_t dhcp_failover_listener_get_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_value_t **); isc_result_t dhcp_failover_listener_destroy (omapi_object_t *, const char *, int); isc_result_t dhcp_failover_listener_stuff (omapi_object_t *, omapi_object_t *, omapi_object_t *); isc_result_t dhcp_failover_register (omapi_object_t *); isc_result_t dhcp_failover_state_signal (omapi_object_t *, const char *, va_list); isc_result_t dhcp_failover_state_transition (dhcp_failover_state_t *, const char *); isc_result_t dhcp_failover_set_service_state (dhcp_failover_state_t *state); void dhcp_failover_rescind_updates (dhcp_failover_state_t *); isc_result_t dhcp_failover_set_state (dhcp_failover_state_t *, enum failover_state); isc_result_t dhcp_failover_peer_state_changed (dhcp_failover_state_t *, failover_message_t *); void dhcp_failover_pool_rebalance (void *); void dhcp_failover_pool_check (struct pool *); int dhcp_failover_state_pool_check (dhcp_failover_state_t *); void dhcp_failover_timeout (void *); void dhcp_failover_send_contact (void *); isc_result_t dhcp_failover_send_state (dhcp_failover_state_t *); isc_result_t dhcp_failover_send_updates (dhcp_failover_state_t *); int dhcp_failover_queue_update (struct lease *, int); int dhcp_failover_send_acks (dhcp_failover_state_t *); void dhcp_failover_toack_queue_timeout (void *); int dhcp_failover_queue_ack (dhcp_failover_state_t *, failover_message_t *msg); void dhcp_failover_ack_queue_remove (dhcp_failover_state_t *, struct lease *); isc_result_t dhcp_failover_state_set_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_typed_data_t *); void dhcp_failover_keepalive (void *); void dhcp_failover_reconnect (void *); void dhcp_failover_startup_timeout (void *); void dhcp_failover_link_startup_timeout (void *); void dhcp_failover_listener_restart (void *); void dhcp_failover_auto_partner_down(void *vs); isc_result_t dhcp_failover_state_get_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_value_t **); isc_result_t dhcp_failover_state_destroy (omapi_object_t *, const char *, int); isc_result_t dhcp_failover_state_stuff (omapi_object_t *, omapi_object_t *, omapi_object_t *); isc_result_t dhcp_failover_state_lookup (omapi_object_t **, omapi_object_t *, omapi_object_t *); isc_result_t dhcp_failover_state_create (omapi_object_t **, omapi_object_t *); isc_result_t dhcp_failover_state_remove (omapi_object_t *, omapi_object_t *); int dhcp_failover_state_match (dhcp_failover_state_t *, u_int8_t *, unsigned); int dhcp_failover_state_match_by_name(dhcp_failover_state_t *, failover_option_t *); const char *dhcp_failover_reject_reason_print (int); const char *dhcp_failover_state_name_print (enum failover_state); const char *dhcp_failover_message_name (unsigned); const char *dhcp_failover_option_name (unsigned); failover_option_t *dhcp_failover_option_printf (unsigned, char *, unsigned *, unsigned, const char *, ...) __attribute__((__format__(__printf__,5,6))); failover_option_t *dhcp_failover_make_option (unsigned, char *, unsigned *, unsigned, ...); isc_result_t dhcp_failover_put_message (dhcp_failover_link_t *, omapi_object_t *, int, u_int32_t, ...); isc_result_t dhcp_failover_send_connect (omapi_object_t *); isc_result_t dhcp_failover_send_connectack (omapi_object_t *, dhcp_failover_state_t *, int, const char *); isc_result_t dhcp_failover_send_disconnect (omapi_object_t *, int, const char *); isc_result_t dhcp_failover_send_bind_update (dhcp_failover_state_t *, struct lease *); isc_result_t dhcp_failover_send_bind_ack (dhcp_failover_state_t *, failover_message_t *, int, const char *); isc_result_t dhcp_failover_send_poolreq (dhcp_failover_state_t *); isc_result_t dhcp_failover_send_poolresp (dhcp_failover_state_t *, int); isc_result_t dhcp_failover_send_update_request (dhcp_failover_state_t *); isc_result_t dhcp_failover_send_update_request_all (dhcp_failover_state_t *); isc_result_t dhcp_failover_send_update_done (dhcp_failover_state_t *); isc_result_t dhcp_failover_process_bind_update (dhcp_failover_state_t *, failover_message_t *); isc_result_t dhcp_failover_process_bind_ack (dhcp_failover_state_t *, failover_message_t *); isc_result_t dhcp_failover_generate_update_queue (dhcp_failover_state_t *, int); isc_result_t dhcp_failover_process_update_request (dhcp_failover_state_t *, failover_message_t *); isc_result_t dhcp_failover_process_update_request_all (dhcp_failover_state_t *, failover_message_t *); isc_result_t dhcp_failover_process_update_done (dhcp_failover_state_t *, failover_message_t *); void ia_remove_all_lease(struct ia_xx *ia, const char *file, int line); void dhcp_failover_recover_done (void *); void failover_print (char *, unsigned *, unsigned, const char *); void update_partner (struct lease *); int load_balance_mine (struct packet *, dhcp_failover_state_t *); int peer_wants_lease (struct lease *); binding_state_t normal_binding_state_transition_check (struct lease *, dhcp_failover_state_t *, binding_state_t, u_int32_t); binding_state_t conflict_binding_state_transition_check (struct lease *, dhcp_failover_state_t *, binding_state_t, u_int32_t); int lease_mine_to_reallocate (struct lease *); OMAPI_OBJECT_ALLOC_DECL (dhcp_failover_state, dhcp_failover_state_t, dhcp_type_failover_state) OMAPI_OBJECT_ALLOC_DECL (dhcp_failover_listener, dhcp_failover_listener_t, dhcp_type_failover_listener) OMAPI_OBJECT_ALLOC_DECL (dhcp_failover_link, dhcp_failover_link_t, dhcp_type_failover_link) #endif /* FAILOVER_PROTOCOL */ const char *binding_state_print (enum failover_state); /* ldap.c */ #if defined(LDAP_CONFIGURATION) extern struct enumeration ldap_methods; #if defined (LDAP_USE_SSL) extern struct enumeration ldap_ssl_usage_enum; extern struct enumeration ldap_tls_reqcert_enum; extern struct enumeration ldap_tls_crlcheck_enum; #endif isc_result_t ldap_read_config (void); int find_haddr_in_ldap (struct host_decl **, int, unsigned, const unsigned char *, const char *, int); int find_subclass_in_ldap (struct class *, struct class **, struct data_string *); int find_client_in_ldap (struct host_decl **, struct packet*, struct option_state *, const char *, int); #endif /* mdb6.c */ HASH_FUNCTIONS_DECL(ia, unsigned char *, struct ia_xx, ia_hash_t) HASH_FUNCTIONS_DECL(iasubopt, struct in6_addr *, struct iasubopt, iasubopt_hash_t) isc_result_t iasubopt_allocate(struct iasubopt **iasubopt, const char *file, int line); isc_result_t iasubopt_reference(struct iasubopt **iasubopt, struct iasubopt *src, const char *file, int line); isc_result_t iasubopt_dereference(struct iasubopt **iasubopt, const char *file, int line); isc_result_t ia_make_key(struct data_string *key, u_int32_t iaid, const char *duid, unsigned int duid_len, const char *file, int line); isc_result_t ia_allocate(struct ia_xx **ia, u_int32_t iaid, const char *duid, unsigned int duid_len, const char *file, int line); isc_result_t ia_reference(struct ia_xx **ia, struct ia_xx *src, const char *file, int line); isc_result_t ia_dereference(struct ia_xx **ia, const char *file, int line); isc_result_t ia_add_iasubopt(struct ia_xx *ia, struct iasubopt *iasubopt, const char *file, int line); void ia_remove_iasubopt(struct ia_xx *ia, struct iasubopt *iasubopt, const char *file, int line); isc_boolean_t ia_equal(const struct ia_xx *a, const struct ia_xx *b); isc_result_t ipv6_pool_allocate(struct ipv6_pool **pool, u_int16_t type, const struct in6_addr *start_addr, int bits, int units, const char *file, int line); isc_result_t ipv6_pool_reference(struct ipv6_pool **pool, struct ipv6_pool *src, const char *file, int line); isc_result_t ipv6_pool_dereference(struct ipv6_pool **pool, const char *file, int line); isc_result_t create_lease6(struct ipv6_pool *pool, struct iasubopt **addr, unsigned int *attempts, const struct data_string *uid, time_t soft_lifetime_end_time); #ifdef EUI_64 int valid_eui_64_duid(const struct data_string* uid, int duid_beg); int valid_for_eui_64_pool(struct ipv6_pool*, struct data_string* uid, int duid_beg, struct in6_addr* ia_addr); isc_result_t create_lease6_eui_64(struct ipv6_pool *pool, struct iasubopt **addr, const struct data_string *iaid_uid, time_t soft_lifetime_end_time); #endif isc_result_t add_lease6(struct ipv6_pool *pool, struct iasubopt *lease, time_t valid_lifetime_end_time); isc_result_t renew_lease6(struct ipv6_pool *pool, struct iasubopt *lease); isc_result_t expire_lease6(struct iasubopt **leasep, struct ipv6_pool *pool, time_t now); isc_result_t release_lease6(struct ipv6_pool *pool, struct iasubopt *lease); isc_result_t decline_lease6(struct ipv6_pool *pool, struct iasubopt *lease); isc_boolean_t lease6_exists(const struct ipv6_pool *pool, const struct in6_addr *addr); isc_boolean_t lease6_usable(struct iasubopt *lease); isc_result_t cleanup_lease6(ia_hash_t *ia_table, struct ipv6_pool *pool, struct iasubopt *lease, struct ia_xx *ia); isc_result_t mark_lease_unavailble(struct ipv6_pool *pool, const struct in6_addr *addr); isc_result_t create_prefix6(struct ipv6_pool *pool, struct iasubopt **pref, unsigned int *attempts, const struct data_string *uid, time_t soft_lifetime_end_time); isc_boolean_t prefix6_exists(const struct ipv6_pool *pool, const struct in6_addr *pref, u_int8_t plen); isc_result_t add_ipv6_pool(struct ipv6_pool *pool); isc_result_t find_ipv6_pool(struct ipv6_pool **pool, u_int16_t type, const struct in6_addr *addr); isc_boolean_t ipv6_in_pool(const struct in6_addr *addr, const struct ipv6_pool *pool); isc_result_t ipv6_pond_allocate(struct ipv6_pond **pond, const char *file, int line); isc_result_t ipv6_pond_reference(struct ipv6_pond **pond, struct ipv6_pond *src, const char *file, int line); isc_result_t ipv6_pond_dereference(struct ipv6_pond **pond, const char *file, int line); isc_result_t renew_leases(struct ia_xx *ia); isc_result_t release_leases(struct ia_xx *ia); isc_result_t decline_leases(struct ia_xx *ia); void schedule_lease_timeout(struct ipv6_pool *pool); void schedule_all_ipv6_lease_timeouts(); void mark_hosts_unavailable(void); void mark_phosts_unavailable(void); void mark_interfaces_unavailable(void); void report_jumbo_ranges(); #if defined(DHCPv6) int find_hosts6(struct host_decl** host, struct packet* packet, const struct data_string* client_id, char* file, int line); #endif #if defined (BINARY_LEASES) /* leasechain.c */ int lc_not_empty(struct leasechain *lc); void lc_add_sorted_lease(struct leasechain *lc, struct lease *lp); void lc_unlink_lease(struct leasechain *lc, struct lease *lp); struct lease *lc_get_first_lease(struct leasechain *lc); struct lease *lc_get_next(struct leasechain *lc, struct lease *lp); void lc_init_growth(struct leasechain *lc, size_t growth); void lc_delete_all(struct leasechain *lc); #endif /* BINARY_LEASES */ #define MAX_ADDRESS_STRING_LEN \ (sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")) /* Find the percentage of count. We need to try two different * ways to avoid rounding mistakes. */ #define FIND_PERCENT(count, percent) \ ((count) > (INT_MAX / 100) ? \ ((count) / 100) * (percent) : ((count) * (percent)) / 100) #define FIND_POND6_PERCENT(count, percent) \ ((count) > (POND_TRACK_MAX / 100) ? \ ((count) / 100) * (percent) : ((count) * (percent)) / 100) dhcp-4.4.1/includes/dhctoken.h000644 000765 000024 00000016464 13243301226 016504 0ustar00tmarkstaff000000 000000 /* dhctoken.h Tokens for config file lexer and parser. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1996-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ /* * The following tokens have been deprecated and aren't in use anymore. * They have been left in place to avoid disturbing the code. * DNS_UPDATE, DNS_DELETE, NS_UPDATE, UPDATED_DNS_RR */ enum dhcp_token { SEMI = ';', DOT = '.', COLON = ':', COMMA = ',', SLASH = '/', LBRACE = '{', RBRACE = '}', LBRACKET = '[', RBRACKET = ']', LPAREN = '(', RPAREN = ')', EQUAL = '=', TILDE = '~', BANG = '!', PERCENT = '%', PLUS = '+', MINUS = '-', ASTERISK = '*', AMPERSAND = '&', PIPE = '|', CARET = '^', ENDOFLINE = '\n', QUESTIONMARK = '?', HOST = 256, FIRST_TOKEN = HOST, HARDWARE = 257, FILENAME = 258, FIXED_ADDR = 259, OPTION = 260, ETHERNET = 261, STRING = 262, NUMBER = 263, NUMBER_OR_NAME = 264, NAME = 265, TIMESTAMP = 266, STARTS = 267, ENDS = 268, UID = 269, CLASS = 270, LEASE = 271, RANGE = 272, PACKET = 273, CIADDR = 274, YIADDR = 275, SIADDR = 276, GIADDR = 277, SUBNET = 278, NETMASK = 279, DEFAULT_LEASE_TIME = 280, MAX_LEASE_TIME = 281, VENDOR_CLASS = 282, USER_CLASS = 283, SHARED_NETWORK = 284, SERVER_NAME = 285, DYNAMIC_BOOTP = 286, SERVER_IDENTIFIER = 287, DYNAMIC_BOOTP_LEASE_CUTOFF = 288, DYNAMIC_BOOTP_LEASE_LENGTH = 289, BOOT_UNKNOWN_CLIENTS = 290, NEXT_SERVER = 291, TOKEN_RING = 292, GROUP = 293, ONE_LEASE_PER_CLIENT = 294, GET_LEASE_HOSTNAMES = 295, USE_HOST_DECL_NAMES = 296, SEND = 297, CLIENT_IDENTIFIER = 298, REQUEST = 299, REQUIRE = 300, TIMEOUT = 301, RETRY = 302, SELECT_TIMEOUT = 303, SCRIPT = 304, INTERFACE = 305, RENEW = 306, REBIND = 307, EXPIRE = 308, UNKNOWN_CLIENTS = 309, ALLOW = 310, DENY = 312, BOOTING = 313, DEFAULT = 314, MEDIA = 315, MEDIUM = 316, ALIAS = 317, REBOOT = 318, TOKEN_ABANDONED = 319, BACKOFF_CUTOFF = 320, INITIAL_INTERVAL = 321, NAMESERVER = 322, DOMAIN = 323, SEARCH = 324, SUPERSEDE = 325, APPEND = 326, PREPEND = 327, HOSTNAME = 328, CLIENT_HOSTNAME = 329, REJECT = 330, USE_LEASE_ADDR_FOR_DEFAULT_ROUTE = 331, MIN_LEASE_TIME = 332, MIN_SECS = 333, AND = 334, OR = 335, SUBSTRING = 337, SUFFIX = 338, CHECK = 339, EXTRACT_INT = 340, IF = 341, TOKEN_ADD = 342, BREAK = 343, ELSE = 344, ELSIF = 345, SUBCLASS = 346, MATCH = 347, SPAWN = 348, WITH = 349, EXISTS = 350, POOL = 351, UNKNOWN = 352, CLIENTS = 353, KNOWN = 354, AUTHENTICATED = 355, UNAUTHENTICATED = 356, ALL = 357, DYNAMIC = 358, MEMBERS = 359, OF = 360, PSEUDO = 361, LIMIT = 362, BILLING = 363, PEER = 364, FAILOVER = 365, MY = 366, PARTNER = 367, PRIMARY = 368, SECONDARY = 369, IDENTIFIER = 370, PORT = 371, MAX_TRANSMIT_IDLE = 372, MAX_RESPONSE_DELAY = 373, PARTNER_DOWN = 374, NORMAL = 375, COMMUNICATIONS_INTERRUPTED = 376, POTENTIAL_CONFLICT = 377, RECOVER = 378, TOKEN_FDDI = 379, AUTHORITATIVE = 380, TOKEN_NOT = 381, AUTHENTICATION = 383, IGNORE = 384, ACCEPT = 385, PREFER = 386, DONT = 387, CODE = 388, ARRAY = 389, BOOLEAN = 390, INTEGER = 391, SIGNED = 392, UNSIGNED = 393, IP_ADDRESS = 394, TEXT = 395, STRING_TOKEN = 396, SPACE = 397, CONCAT = 398, ENCODE_INT = 399, REVERSE = 402, LEASED_ADDRESS = 403, BINARY_TO_ASCII = 404, PICK = 405, CONFIG_OPTION = 406, HOST_DECL_NAME = 407, ON = 408, EXPIRY = 409, RELEASE = 410, COMMIT = 411, DNS_UPDATE = 412, LEASE_TIME = 413, STATIC = 414, NEVER = 415, INFINITE = 416, TOKEN_DELETED = 417, UPDATED_DNS_RR = 418, DNS_DELETE = 419, DUPLICATES = 420, DECLINES = 421, TSTP = 422, TSFP = 423, OWNER = 424, IS = 425, HBA = 426, MAX_UNACKED_UPDATES = 427, MCLT = 428, SPLIT = 429, AT = 430, TOKEN_NO = 431, TOKEN_DELETE = 432, NS_UPDATE = 433, UPDATE = 434, SWITCH = 435, CASE = 436, NS_FORMERR = 437, NS_NOERROR = 438, NS_NOTAUTH = 439, NS_NOTIMP = 440, NS_NOTZONE = 441, NS_NXDOMAIN = 442, NS_NXRRSET = 443, NS_REFUSED = 444, NS_SERVFAIL = 445, NS_YXDOMAIN = 446, NS_YXRRSET = 447, TOKEN_NULL = 448, TOKEN_SET = 449, DEFINED = 450, UNSET = 451, EVAL = 452, LET = 453, FUNCTION = 454, DEFINE = 455, ZONE = 456, KEY = 457, SECRET = 458, ALGORITHM = 459, LOAD = 460, BALANCE = 461, TOKEN_MAX = 462, SECONDS = 463, ADDRESS = 464, RESOLUTION_INTERRUPTED = 465, STATE = 466, UNKNOWN_STATE = 567, CLTT = 568, INCLUDE = 569, BINDING = 570, TOKEN_FREE = 571, TOKEN_ACTIVE = 572, TOKEN_EXPIRED = 573, TOKEN_RELEASED = 574, TOKEN_RESET = 575, TOKEN_BACKUP = 576, TOKEN_RESERVED = 577, TOKEN_BOOTP = 578, TOKEN_NEXT = 579, OMAPI = 580, LOG = 581, FATAL = 582, ERROR = 583, TOKEN_DEBUG = 584, INFO = 585, RETURN = 586, PAUSED = 587, RECOVER_DONE = 588, SHUTDOWN = 589, STARTUP = 590, ENCAPSULATE = 591, VENDOR = 592, CLIENT_STATE = 593, INIT_REBOOT = 594, TOKEN_INIT = 595, SELECT = 596, BOUND = 597, RENEWING = 598, REBINDING = 599, RECONTACT_INTERVAL = 600, CLIENT_UPDATES = 601, TOKEN_NEW = 601, TRANSMISSION = 602, TOKEN_CLOSE = 603, TOKEN_CREATE = 604, TOKEN_OPEN = 605, TOKEN_HELP = 606, END_OF_FILE = 607, RECOVER_WAIT = 608, TOKEN_SERVER = 609, CONNECT = 610, REMOVE = 611, REFRESH = 612, DOMAIN_NAME = 613, DO_FORWARD_UPDATE = 614, KNOWN_CLIENTS = 615, ATSFP = 616, LCASE = 617, UCASE = 618, WIDTH = 619, LENGTH = 620, HASH = 621, SIZE = 622, EPOCH = 623, DB_TIME_FORMAT = 624, LOCAL = 625, MAX_LEASE_MISBALANCE = 626, MAX_LEASE_OWNERSHIP = 627, MAX_BALANCE = 628, MIN_BALANCE = 629, DOMAIN_LIST = 630, LEASEQUERY = 631, EXECUTE = 632, IP6_ADDRESS = 633, FIXED_ADDR6 = 634, COMPRESSED = 635, SUBNET6 = 636, HOST_IDENTIFIER = 637, IA_NA = 638, IA_TA = 639, IA_PD = 640, IAADDR = 641, IAPREFIX = 642, LEASE6 = 643, PREFERRED_LIFE = 644, MAX_LIFE = 645, DEFAULT_DUID = 646, SERVER_DUID = 647, LLT = 648, EN = 649, LL = 650, RANGE6 = 651, WHITESPACE = 652, TOKEN_ALSO = 653, AFTER = 654, ZEROLEN = 655, TEMPORARY = 656, PREFIX6 = 657, FIXED_PREFIX6 = 658, ANYCAST_MAC = 659, CONFLICT_DONE = 660, AUTO_PARTNER_DOWN = 661, GETHOSTNAME = 662, REWIND = 663, INITIAL_DELAY = 664, GETHOSTBYNAME = 665, PRIMARY6 = 666, SECONDARY6 = 667, TOKEN_INFINIBAND = 668, POOL6 = 669, V6RELAY = 670, V6RELOPT = 671, PARSE_VENDOR_OPT = 672, AUTHORING_BYTE_ORDER = 673, TOKEN_LITTLE_ENDIAN = 674, TOKEN_BIG_ENDIAN = 675, LEASE_ID_FORMAT = 676, TOKEN_HEX = 677, TOKEN_OCTAL = 678, KEY_ALGORITHM = 679 }; #define is_identifier(x) ((x) >= FIRST_TOKEN && \ (x) != STRING && \ (x) != NUMBER && \ (x) != END_OF_FILE) dhcp-4.4.1/includes/failover.h000644 000765 000024 00000026717 13243301226 016516 0ustar00tmarkstaff000000 000000 /* failover.h Definitions for address trees... */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 2000-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #if defined (FAILOVER_PROTOCOL) struct failover_option_info { int code; const char *name; enum { FT_UINT8, FT_IPADDR, FT_UINT32, FT_BYTES, FT_TEXT_OR_BYTES, FT_DDNS, FT_DDNS1, FT_UINT16, FT_TEXT, FT_UNDEF, FT_DIGEST } type; int num_present; int offset; u_int32_t bit; }; typedef struct { unsigned count; u_int8_t *data; } failover_option_t; /* Failover configuration defaults. */ #ifndef DEFAULT_MAX_BALANCE_TIME # define DEFAULT_MAX_BALANCE_TIME 3600 #endif #ifndef DEFAULT_MIN_BALANCE_TIME # define DEFAULT_MIN_BALANCE_TIME 60 #endif #ifndef DEFAULT_MAX_LEASE_MISBALANCE # define DEFAULT_MAX_LEASE_MISBALANCE 15 #endif #ifndef DEFAULT_MAX_LEASE_OWNERSHIP # define DEFAULT_MAX_LEASE_OWNERSHIP 10 #endif #ifndef DEFAULT_MAX_FLYING_UPDATES # define DEFAULT_MAX_FLYING_UPDATES 100 #endif #ifndef DEFAULT_MAX_RESPONSE_DELAY # define DEFAULT_MAX_RESPONSE_DELAY 20 #endif /* * IANA has assigned ports 647 ("dhcp-failover") and 847 ("dhcp-failover2"). * Of these, only port 647 is mentioned in the -12 draft revision. We're not * sure if they are supposed to indicate primary and secondary? No matter, * we'll stick to the -12 draft revision level. */ #ifndef DEFAULT_FAILOVER_PORT # define DEFAULT_FAILOVER_PORT 647 #endif #define FM_OFFSET(x) (long)(&(((failover_message_t *)0) -> x)) /* All of the below definitions are mandated by draft-ietf-dhc-failover-12. * The Sections referenced are Sections within that document of that * version, and may be different in other documents of other versions. */ /* Failover message options from Section 12: */ #define FTO_ADDRESSES_TRANSFERRED 1 #define FTB_ADDRESSES_TRANSFERRED 0x00000002 #define FTO_ASSIGNED_IP_ADDRESS 2 #define FTB_ASSIGNED_IP_ADDRESS 0x00000004 #define FTO_BINDING_STATUS 3 #define FTB_BINDING_STATUS 0x00000008 #define FTO_CLIENT_IDENTIFIER 4 #define FTB_CLIENT_IDENTIFIER 0x00000010 #define FTO_CHADDR 5 #define FTB_CHADDR 0x00000020 #define FTO_CLTT 6 #define FTB_CLTT 0x00000040 #define FTO_REPLY_OPTIONS 7 #define FTB_REPLY_OPTIONS 0x00000080 #define FTO_REQUEST_OPTIONS 8 #define FTB_REQUEST_OPTIONS 0x00000100 #define FTO_DDNS 9 #define FTB_DDNS 0x00000200 #define FTO_DELAYED_SERVICE 10 #define FTB_DELAYED_SERVICE 0x00000400 #define FTO_HBA 11 #define FTB_HBA 0x00000800 #define FTO_IP_FLAGS 12 #define FTB_IP_FLAGS 0x00001000 #define FTO_LEASE_EXPIRY 13 #define FTB_LEASE_EXPIRY 0x00002000 #define FTO_MAX_UNACKED 14 #define FTB_MAX_UNACKED 0x00004000 #define FTO_MCLT 15 #define FTB_MCLT 0x00008000 #define FTO_MESSAGE 16 #define FTB_MESSAGE 0x00010000 #define FTO_MESSAGE_DIGEST 17 #define FTB_MESSAGE_DIGEST 0x00020000 #define FTO_POTENTIAL_EXPIRY 18 #define FTB_POTENTIAL_EXPIRY 0x00040000 #define FTO_RECEIVE_TIMER 19 #define FTB_RECEIVE_TIMER 0x00080000 #define FTO_PROTOCOL_VERSION 20 #define FTB_PROTOCOL_VERSION 0x00100000 #define FTO_REJECT_REASON 21 #define FTB_REJECT_REASON 0x00200000 #define FTO_RELATIONSHIP_NAME 22 #define FTB_RELATIONSHIP_NAME 0x00400000 #define FTO_SERVER_FLAGS 23 #define FTB_SERVER_FLAGS 0x00800000 #define FTO_SERVER_STATE 24 #define FTB_SERVER_STATE 0x01000000 #define FTO_STOS 25 #define FTB_STOS 0x02000000 #define FTO_TLS_REPLY 26 #define FTB_TLS_REPLY 0x04000000 #define FTO_TLS_REQUEST 27 #define FTB_TLS_REQUEST 0x08000000 #define FTO_VENDOR_CLASS 28 #define FTB_VENDOR_CLASS 0x10000000 #define FTO_VENDOR_OPTIONS 29 #define FTB_VENDOR_OPTIONS 0x20000000 #define FTO_MAX FTO_VENDOR_OPTIONS /* Failover protocol message types from Section 6.1: */ #define FTM_POOLREQ 1 #define FTM_POOLRESP 2 #define FTM_BNDUPD 3 #define FTM_BNDACK 4 #define FTM_CONNECT 5 #define FTM_CONNECTACK 6 #define FTM_UPDREQALL 7 #define FTM_UPDDONE 8 #define FTM_UPDREQ 9 #define FTM_STATE 10 #define FTM_CONTACT 11 #define FTM_DISCONNECT 12 #define FTM_MAX FTM_DISCONNECT /* Reject reasons from Section 12.21: */ #define FTR_ILLEGAL_IP_ADDR 1 #define FTR_FATAL_CONFLICT 2 #define FTR_MISSING_BINDINFO 3 #define FTR_TIMEMISMATCH 4 #define FTR_INVALID_MCLT 5 #define FTR_MISC_REJECT 6 #define FTR_DUP_CONNECTION 7 #define FTR_INVALID_PARTNER 8 #define FTR_TLS_UNSUPPORTED 9 #define FTR_TLS_UNCONFIGURED 10 #define FTR_TLS_REQUIRED 11 #define FTR_DIGEST_UNSUPPORTED 12 #define FTR_DIGEST_UNCONFIGURED 13 #define FTR_VERSION_MISMATCH 14 #define FTR_OUTDATED_BIND_INFO 15 #define FTR_LESS_CRIT_BIND_INFO 16 #define FTR_NO_TRAFFIC 17 #define FTR_HBA_CONFLICT 18 #define FTR_IP_NOT_RESERVED 19 #define FTR_IP_DIGEST_FAILURE 20 #define FTR_IP_MISSING_DIGEST 21 #define FTR_UNKNOWN 254 /* Message size limitations defined in Section 6.1: */ #define DHCP_FAILOVER_MIN_MESSAGE_SIZE 12 #define DHCP_FAILOVER_MAX_MESSAGE_SIZE 2048 /* Failover server flags from Section 12.23: */ #define FTF_SERVER_STARTUP 1 /* DDNS flags from Section 12.9. These are really their names. */ #define FTF_DDNS_C 0x0001 #define FTF_DDNS_A 0x0002 #define FTF_DDNS_D 0x0004 #define FTF_DDNS_P 0x0008 /* FTO_IP_FLAGS contents from Section 12.12: */ #define FTF_IP_FLAG_RESERVE 0x0001 #define FTF_IP_FLAG_BOOTP 0x0002 /* FTO_MESSAGE_DIGEST Type Codes from Section 12.17: */ #define FTT_MESSAGE_DIGEST_HMAC_MD5 0x01 typedef struct failover_message { int refcnt; struct failover_message *next; int options_present; u_int32_t time; u_int32_t xid; u_int8_t type; /* One-byte options. */ u_int8_t binding_status; u_int8_t delayed_service; u_int8_t protocol_version; u_int8_t reject_reason; u_int8_t server_flags; u_int8_t server_state; u_int8_t tls_reply; u_int8_t tls_request; /* Two-byte options. */ u_int16_t ip_flags; /* Four-byte options. */ u_int32_t addresses_transferred; u_int32_t assigned_addr; u_int32_t cltt; u_int32_t expiry; u_int32_t max_unacked; u_int32_t mclt; u_int32_t potential_expiry; u_int32_t receive_timer; u_int32_t stos; /* Arbitrary field options. */ failover_option_t chaddr; failover_option_t client_identifier; failover_option_t hba; failover_option_t message; failover_option_t message_digest; failover_option_t relationship_name; failover_option_t reply_options; failover_option_t request_options; failover_option_t vendor_class; failover_option_t vendor_options; /* Special contents options. */ ddns_fqdn_t ddns; } failover_message_t; typedef struct { OMAPI_OBJECT_PREAMBLE; struct option_cache *peer_address; unsigned peer_port; int options_present; enum dhcp_flink_state { dhcp_flink_start, dhcp_flink_message_length_wait, dhcp_flink_message_wait, dhcp_flink_disconnected, dhcp_flink_state_max } state; failover_message_t *imsg; struct _dhcp_failover_state *state_object; u_int16_t imsg_len; unsigned imsg_count; u_int8_t imsg_payoff; /* Pay*load* offset. :') */ u_int32_t xid; } dhcp_failover_link_t; typedef struct _dhcp_failover_listener { OMAPI_OBJECT_PREAMBLE; struct _dhcp_failover_listener *next; omapi_addr_t address; } dhcp_failover_listener_t; #endif /* FAILOVER_PROTOCOL */ /* A failover peer's running state. */ enum failover_state { unknown_state = 0, /* XXX: Not a standard state. */ startup = 1, normal = 2, communications_interrupted = 3, partner_down = 4, potential_conflict = 5, recover = 6, paused = 7, shut_down = 8, recover_done = 9, resolution_interrupted = 10, conflict_done = 11, /* Draft revision 12 of the failover protocol documents a RECOVER-WAIT * state, but does not enumerate its value in the section 12.24 * table. ISC DHCP 3.0.x used value 254 even though the state was * not documented at all. For the time being, we will continue to use * this value. */ recover_wait = 254 }; /* Service states are simplifications of failover states, particularly useful because the startup state isn't actually implementable as a separate failover state without maintaining a state stack. */ enum service_state { unknown_service_state, cooperating, not_cooperating, service_partner_down, not_responding, service_startup }; #if defined (FAILOVER_PROTOCOL) typedef struct _dhcp_failover_config { struct option_cache *address; int port; u_int32_t max_flying_updates; enum failover_state state; TIME stos; u_int32_t max_response_delay; } dhcp_failover_config_t; typedef struct _dhcp_failover_state { OMAPI_OBJECT_PREAMBLE; struct _dhcp_failover_state *next; char *name; /* Name of this failover instance. */ dhcp_failover_config_t me; /* My configuration. */ dhcp_failover_config_t partner; /* Partner's configuration. */ enum failover_state saved_state; /* Saved state during startup. */ struct data_string server_identifier; /* Server identifier (IP addr) */ u_int32_t mclt; u_int8_t *hba; /* Hash bucket array for load balancing. */ int load_balance_max_secs; u_int32_t max_lease_misbalance, max_lease_ownership; u_int32_t max_balance, min_balance; TIME last_balance, sched_balance; u_int32_t auto_partner_down; enum service_state service_state; const char *nrr; /* Printable reason why we're in the not_responding service state (empty string if we are responding. */ dhcp_failover_link_t *link_to_peer; /* Currently-established link to peer. */ enum { primary, secondary } i_am; /* We are primary or secondary in this relationship. */ TIME last_packet_sent; /* Timestamp on last packet we sent. */ TIME last_timestamp_received; /* The last timestamp we sent that has been returned by our partner. */ TIME skew; /* The skew between our clock and our partner's. */ struct lease *update_queue_head; /* List of leases we haven't sent to peer. */ struct lease *update_queue_tail; struct lease *ack_queue_head; /* List of lease updates the peer hasn't yet acked. */ struct lease *ack_queue_tail; struct lease *send_update_done; /* When we get a BNDACK for this lease, send an UPDDONE message. */ int cur_unacked_updates; /* Number of updates we've sent that have not yet been acked. */ /* List of messages which we haven't acked yet. */ failover_message_t *toack_queue_head; failover_message_t *toack_queue_tail; int pending_acks; /* Number of messages in the toack queue. */ int pool_count; /* Number of pools referencing this failover state object. */ int curUPD; /* If an UPDREQ* message is in motion, this value indicates which one. */ u_int32_t updxid; /* XID of UPDREQ* message in action. */ } dhcp_failover_state_t; extern int check_secs_byte_order; /* check byte order of secs field when true */ #define DHCP_FAILOVER_VERSION 1 #endif /* FAILOVER_PROTOCOL */ dhcp-4.4.1/includes/heap.h000644 000765 000024 00000013000 13243301226 015601 0ustar00tmarkstaff000000 000000 /* * Copyright (C) 2004-2017 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1997-2003 Internet Software Consortium. * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* $Id: heap.h,v 1.3 2007/05/19 19:16:25 dhankins Exp $ */ #ifndef ISC_HEAP_H #define ISC_HEAP_H 1 /*! \file isc/heap.h */ /*% * The comparision function returns ISC_TRUE if the first argument has * higher priority than the second argument, and ISC_FALSE otherwise. */ typedef isc_boolean_t (*isc_heapcompare_t)(void *, void *); /*% * The index function allows the client of the heap to receive a callback * when an item's index number changes. This allows it to maintain * sync with its external state, but still delete itself, since deletions * from the heap require the index be provided. */ typedef void (*isc_heapindex_t)(void *, unsigned int); /*% * The heapaction function is used when iterating over the heap. * * NOTE: The heap structure CANNOT BE MODIFIED during the call to * isc_heap_foreach(). */ typedef void (*isc_heapaction_t)(void *, void *); typedef struct isc_heap isc_heap_t; isc_result_t isc_heap_create(isc_heapcompare_t compare, isc_heapindex_t index, unsigned int size_increment, isc_heap_t **heapp); /*!< * \brief Create a new heap. The heap is implemented using a space-efficient * storage method. When the heap elements are deleted space is not freed * but will be reused when new elements are inserted. * * Requires: *\li "mctx" is valid. *\li "compare" is a function which takes two void * arguments and * returns ISC_TRUE if the first argument has a higher priority than * the second, and ISC_FALSE otherwise. *\li "index" is a function which takes a void *, and an unsigned int * argument. This function will be called whenever an element's * index value changes, so it may continue to delete itself from the * heap. This option may be NULL if this functionality is unneeded. *\li "size_increment" is a hint about how large the heap should grow * when resizing is needed. If this is 0, a default size will be * used, which is currently 1024, allowing space for an additional 1024 * heap elements to be inserted before adding more space. *\li "heapp" is not NULL, and "*heap" is NULL. * * Returns: *\li ISC_R_SUCCESS - success *\li ISC_R_NOMEMORY - insufficient memory */ void isc_heap_destroy(isc_heap_t **heapp); /*!< * \brief Destroys a heap. * * Requires: *\li "heapp" is not NULL and "*heap" points to a valid isc_heap_t. */ isc_result_t isc_heap_insert(isc_heap_t *heap, void *elt); /*!< * \brief Inserts a new element into a heap. * * Requires: *\li "heapp" is not NULL and "*heap" points to a valid isc_heap_t. */ void isc_heap_delete(isc_heap_t *heap, unsigned int index); /*!< * \brief Deletes an element from a heap, by element index. * * Requires: *\li "heapp" is not NULL and "*heap" points to a valid isc_heap_t. *\li "index" is a valid element index, as provided by the "index" callback * provided during heap creation. */ void isc_heap_increased(isc_heap_t *heap, unsigned int index); /*!< * \brief Indicates to the heap that an element's priority has increased. * This function MUST be called whenever an element has increased in priority. * * Requires: *\li "heapp" is not NULL and "*heap" points to a valid isc_heap_t. *\li "index" is a valid element index, as provided by the "index" callback * provided during heap creation. */ void isc_heap_decreased(isc_heap_t *heap, unsigned int index); /*!< * \brief Indicates to the heap that an element's priority has decreased. * This function MUST be called whenever an element has decreased in priority. * * Requires: *\li "heapp" is not NULL and "*heap" points to a valid isc_heap_t. *\li "index" is a valid element index, as provided by the "index" callback * provided during heap creation. */ void * isc_heap_element(isc_heap_t *heap, unsigned int index); /*!< * \brief Returns the element for a specific element index. * * Requires: *\li "heapp" is not NULL and "*heap" points to a valid isc_heap_t. *\li "index" is a valid element index, as provided by the "index" callback * provided during heap creation. * * Returns: *\li A pointer to the element for the element index. */ void isc_heap_foreach(isc_heap_t *heap, isc_heapaction_t action, void *uap); /*!< * \brief Iterate over the heap, calling an action for each element. The * order of iteration is not sorted. * * Requires: *\li "heapp" is not NULL and "*heap" points to a valid isc_heap_t. *\li "action" is not NULL, and is a function which takes two arguments. * The first is a void *, representing the element, and the second is * "uap" as provided to isc_heap_foreach. *\li "uap" is a caller-provided argument, and may be NULL. * * Note: *\li The heap structure CANNOT be modified during this iteration. The only * safe function to call while iterating the heap is isc_heap_element(). */ #endif /* ISC_HEAP_H */ dhcp-4.4.1/includes/inet.h000644 000765 000024 00000004100 13243301226 015624 0ustar00tmarkstaff000000 000000 /* inet.h Portable definitions for internet addresses */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1996-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ /* An internet address of up to 128 bits. */ struct iaddr { unsigned len; unsigned char iabuf [16]; }; struct iaddrlist { struct iaddrlist *next; struct iaddr addr; }; /* struct iaddrmatch - used to compare a host IP against a subnet spec * * There is a space/speed tradeoff here implied by the use of a second * struct iaddr to hold the mask; while using an unsigned (byte!) to * represent the subnet prefix length would be more memory efficient, * it makes run-time mask comparisons more expensive. Since such * entries are used currently only in restricted circumstances * (wanting to reject a subnet), the decision is in favour of run-time * efficiency. */ struct iaddrmatch { struct iaddr addr; struct iaddr mask; }; /* its list ... */ struct iaddrmatchlist { struct iaddrmatchlist *next; struct iaddrmatch match; }; /* * Structure to store information about a CIDR network. */ struct iaddrcidrnet { struct iaddr lo_addr; int bits; }; struct iaddrcidrnetlist { struct iaddrcidrnetlist *next; struct iaddrcidrnet cidrnet; }; dhcp-4.4.1/includes/ldap_casa.h000644 000765 000024 00000007712 13243301226 016610 0ustar00tmarkstaff000000 000000 /* ldap_casa.h Definition for CASA modules... */ /* Copyright (c) 2006 Novell, Inc. * All rights reserved. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * 1.Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2.Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3.Neither the name of ISC, ISC DHCP, nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * THIS SOFTWARE IS PROVIDED BY INTERNET SYSTEMS CONSORTIUM AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ISC OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * This file was written by S Kalyanasundaram */ /* * Copyright (c) 2004-2010 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1995-2003 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ */ #if defined(LDAP_CASA_AUTH) #ifndef __LDAP_CASA_H__ #define __LDAP_CASA_H__ #include #define MICASA_LIB "libmicasa.so.1" SSCS_TYPEDEF_LIBCALL(int, CASA_GetCredential_T) ( uint32_t ssFlags, SSCS_SECRET_ID_T *appSecretID, SSCS_SECRET_ID_T *sharedSecretID, uint32_t *credentialType, void *credential, SSCS_EXT_T *ext ); SSCS_TYPEDEF_LIBCALL(int, CASA_SetCredential_T) ( uint32_t ssFlags, SSCS_SECRET_ID_T *appSecretID, SSCS_SECRET_ID_T *sharedSecretID, uint32_t credentialType, void *credential, SSCS_EXT_T *ext ); SSCS_TYPEDEF_LIBCALL(int, CASA_RemoveCredential_T) ( uint32_t ssFlags, SSCS_SECRET_ID_T *appSecretID, SSCS_SECRET_ID_T *sharedSecretID, SSCS_EXT_T *ext ); static CASA_GetCredential_T p_miCASAGetCredential = NULL; static CASA_SetCredential_T p_miCASASetCredential = NULL; static CASA_RemoveCredential_T p_miCASARemoveCredential = NULL; static void *casaIDK = NULL; int load_casa(void); static void release_casa(void); int load_uname_pwd_from_miCASA(char **, char **); #endif /* __LDAP_CASA_H__ */ #endif /* LDAP_CASA_AUTH */ dhcp-4.4.1/includes/ldap_krb_helper.h000644 000765 000024 00000004136 13243301226 020013 0ustar00tmarkstaff000000 000000 /* ldap_krb_helper.h Helper routings for allowing LDAP to read configuration with GSSAPI/krb auth */ /* * Copyright (c) 2015 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 2014 William B. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of The Internet Software Consortium nor the names * of its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * This helper was written by William Brown , * inspired by krb5_helper.c from bind-dyndb-ldap by Simo Sorce (Redhat) */ #ifndef LDAP_KRB5_HELPER #define LDAP_KRB5_HELPER #if defined(LDAP_USE_GSSAPI) #include extern isc_result_t krb5_get_tgt(const char *, const char *); #endif #endif /* LDAP_KRB5_HELPER */ dhcp-4.4.1/includes/Makefile.am000644 000765 000024 00000000756 13243301226 016565 0ustar00tmarkstaff000000 000000 nobase_include_HEADERS = omapip/alloc.h omapip/buffer.h omapip/convert.h \ omapip/hash.h omapip/isclib.h omapip/omapip.h \ omapip/omapip_p.h omapip/result.h omapip/trace.h EXTRA_DIST = cdefs.h ctrace.h dhcp.h dhcp6.h dhcpd.h dhctoken.h failover.h \ heap.h inet.h ns_name.h osdep.h site.h statement.h tree.h \ t_api.h \ ldap_casa.h ldap_krb_helper.h \ arpa/nameser.h arpa/nameser_compat.h \ netinet/if_ether.h netinet/ip.h netinet/ip_icmp.h netinet/udp.h dhcp-4.4.1/includes/netinet/000755 000765 000024 00000000000 13243313033 016166 5ustar00tmarkstaff000000 000000 dhcp-4.4.1/includes/ns_name.h000644 000765 000024 00000003431 13243301226 016313 0ustar00tmarkstaff000000 000000 /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 2001-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * http://www.isc.org/ */ #ifndef NS_NAME_H #define NS_NAME_H #include "cdefs.h" #include "osdep.h" /* * Based on the Dynamic DNS reference implementation by Viraj Bais * */ int MRns_name_compress(const char *, u_char *, size_t, const unsigned char **, const unsigned char **); int MRns_name_unpack(const unsigned char *, const unsigned char *, const unsigned char *, unsigned char *, size_t); int MRns_name_pack (const unsigned char *, unsigned char *, unsigned, const unsigned char **, const unsigned char **); int MRns_name_ntop(const unsigned char *, char *, size_t); int MRns_name_pton(const char *, u_char *, size_t); int MRns_name_uncompress_list(const unsigned char*, int buflen, char*, size_t); int MRns_name_compress_list(const char*, int buflen, unsigned char*, size_t); #endif /* NS_NAME_H */ dhcp-4.4.1/includes/omapip/000755 000765 000024 00000000000 13243313032 016004 5ustar00tmarkstaff000000 000000 dhcp-4.4.1/includes/osdep.h000644 000765 000024 00000016517 13243301226 016016 0ustar00tmarkstaff000000 000000 /* osdep.h Operating system dependencies... */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium,Inc. ("ISC") * Copyright (c) 1996-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #if !defined (__ISC_DHCP_OSDEP_H__) #define __ISC_DHCP_OSDEP_H__ #include "site.h" #include "config.h" #include #ifndef LITTLE_ENDIAN #define LITTLE_ENDIAN 1234 #endif /* LITTLE_ENDIAN */ #ifndef BIG_ENDIAN #define BIG_ENDIAN 4321 #endif /* BIG_ENDIAN */ #ifndef BYTE_ORDER #define BYTE_ORDER DHCP_BYTE_ORDER #endif /* BYTE_ORDER */ /* Porting:: If you add a new network API, you must add a check for it below: */ #if !defined (USE_SOCKETS) && \ !defined (USE_SOCKET_SEND) && \ !defined (USE_SOCKET_RECEIVE) && \ !defined (USE_RAW_SOCKETS) && \ !defined (USE_RAW_SEND) && \ !defined (USE_SOCKET_RECEIVE) && \ !defined (USE_BPF) && \ !defined (USE_BPF_SEND) && \ !defined (USE_BPF_RECEIVE) && \ !defined (USE_LPF) && \ !defined (USE_LPF_SEND) && \ !defined (USE_LPF_RECEIVE) && \ !defined (USE_NIT) && \ !defined (USE_NIT_SEND) && \ !defined (USE_NIT_RECEIVE) && \ !defined (USE_DLPI_SEND) && \ !defined (USE_DLPI_RECEIVE) /* Determine default socket API to USE. */ # if defined(HAVE_BPF) # define USE_BPF 1 # elif defined(HAVE_LPF) # define USE_LPF 1 # elif defined(HAVE_DLPI) # define USE_DLPI 1 # endif #endif #if !defined (TIME_MAX) # define TIME_MAX 2147483647 #endif /* snprintf/vsnprintf hacks. for systems with no libc versions only. */ #ifdef NO_SNPRINTF extern int isc_print_snprintf(char *, size_t, const char *, ...); extern int isc_print_vsnprintf(char *, size_t, const char *, va_list ap); # define snprintf isc_print_snprintf # define vsnprintf isc_print_vsnprintf #endif /* Porting:: If you add a new network API, and have it set up so that it can be used for sending or receiving, but doesn't have to be used for both, then set up an ifdef like the ones below: */ #ifdef USE_SOCKETS # define USE_SOCKET_SEND # define USE_SOCKET_RECEIVE # if defined(HAVE_DLPI) && !defined(sun) && !defined(USE_V4_PKTINFO) # define USE_DLPI_HWADDR # elif defined(HAVE_LPF) # define USE_LPF_HWADDR # elif defined(HAVE_BPF) # define USE_BPF_HWADDR # endif #endif #ifdef USE_RAW_SOCKETS # define USE_RAW_SEND # define USE_SOCKET_RECEIVE #endif #ifdef USE_BPF # define USE_BPF_SEND # define USE_BPF_RECEIVE #endif #ifdef USE_LPF # define USE_LPF_SEND # define USE_LPF_RECEIVE #endif #ifdef USE_NIT # define USE_NIT_SEND # define USE_NIT_RECEIVE #endif #ifdef USE_DLPI # define USE_DLPI_SEND # define USE_DLPI_RECEIVE #endif #ifdef USE_UPF # define USE_UPF_SEND # define USE_UPF_RECEIVE #endif /* Porting:: If you add support for sending packets directly out an interface, and your support does not do ARP or routing, you must use a fallback mechanism to deal with packets that need to be sent to routers. Currently, all low-level packet interfaces use BSD sockets as a fallback. */ #if defined (USE_BPF_SEND) || defined (USE_NIT_SEND) || \ defined (USE_DLPI_SEND) || defined (USE_UPF_SEND) || \ defined (USE_LPF_SEND) || \ (defined (USE_SOCKET_SEND) && defined (HAVE_SO_BINDTODEVICE)) # define USE_SOCKET_FALLBACK # define USE_FALLBACK #endif /* Porting:: If you add support for sending packets directly out an interface and need to be able to assemble packets, add the USE_XXX_SEND definition for your interface to the list tested below. */ #if defined (USE_RAW_SEND) || defined (USE_BPF_SEND) || \ defined (USE_NIT_SEND) || defined (USE_UPF_SEND) || \ defined (USE_DLPI_SEND) || defined (USE_LPF_SEND) # define PACKET_ASSEMBLY #endif /* Porting:: If you add support for receiving packets directly from an interface and need to be able to decode raw packets, add the USE_XXX_RECEIVE definition for your interface to the list tested below. */ #if defined (USE_RAW_RECEIVE) || defined (USE_BPF_SEND) || \ defined (USE_NIT_RECEIVE) || defined (USE_UPF_RECEIVE) || \ defined (USE_DLPI_RECEIVE) || defined (USE_LPF_RECEIVE) # define PACKET_DECODING #endif /* If we don't have a DLPI packet filter, we have to filter in userland. Probably not worth doing, actually. */ #if defined (USE_DLPI_RECEIVE) && !defined (USE_DLPI_PFMOD) # define USERLAND_FILTER #endif /* jmp_buf is assumed to be a struct unless otherwise defined in the system header. */ #ifndef jbp_decl # define jbp_decl(x) jmp_buf *x #endif #ifndef jref # define jref(x) (&(x)) #endif #ifndef jdref # define jdref(x) (*(x)) #endif #ifndef jrefproto # define jrefproto jmp_buf * #endif #ifndef BPF_FORMAT # define BPF_FORMAT "/dev/bpf%d" #endif #if defined (F_SETFD) && !defined (HAVE_SETFD) # define HAVE_SETFD #endif #if defined (IFF_POINTOPOINT) && !defined (HAVE_IFF_POINTOPOINT) # define HAVE_IFF_POINTOPOINT #endif #if defined (AF_LINK) && !defined (HAVE_AF_LINK) # define HAVE_AF_LINK #endif #if defined (ARPHRD_TUNNEL) && !defined (HAVE_ARPHRD_TUNNEL) # define HAVE_ARPHRD_TUNNEL #endif #if defined (ARPHRD_LOOPBACK) && !defined (HAVE_ARPHRD_LOOPBACK) # define HAVE_ARPHRD_LOOPBACK #endif #if defined (ARPHRD_ROSE) && !defined (HAVE_ARPHRD_ROSE) # define HAVE_ARPHRD_ROSE #endif #if defined (ARPHRD_IRDA) && !defined (HAVE_ARPHRD_IRDA) # define HAVE_ARPHRD_IRDA #endif #if defined (ARPHRD_SIT) && !defined (HAVE_ARPHRD_SIT) # define HAVE_ARPHRD_SIT #endif #if defined (ARPHRD_IEEE1394) & !defined (HAVE_ARPHRD_IEEE1394) # define HAVE_ARPHRD_IEEE1394 #endif #if defined (ARPHRD_IEEE802) && !defined (HAVE_ARPHRD_IEEE802) # define HAVE_ARPHRD_IEEE802 #endif #if defined (ARPHRD_IEEE802_TR) && !defined (HAVE_ARPHRD_IEEE802_TR) # define HAVE_ARPHRD_IEEE802_TR #endif #if defined (ARPHRD_FDDI) && !defined (HAVE_ARPHRD_FDDI) # define HAVE_ARPHRD_FDDI #endif #if defined (ARPHRD_AX25) && !defined (HAVE_ARPHRD_AX25) # define HAVE_ARPHRD_AX25 #endif #if defined (ARPHRD_NETROM) && !defined (HAVE_ARPHRD_NETROM) # define HAVE_ARPHRD_NETROM #endif #if defined (ARPHRD_METRICOM) && !defined (HAVE_ARPHRD_METRICOM) # define HAVE_ARPHRD_METRICOM #endif #if defined (SO_BINDTODEVICE) && !defined (HAVE_SO_BINDTODEVICE) # define HAVE_SO_BINDTODEVICE #endif #if defined (AF_LINK) && !defined (HAVE_AF_LINK) # define HAVE_AF_LINK #endif /* Linux needs to define SHUT_* in /usr/include/sys/socket.h someday... */ #if !defined (SHUT_RD) # define SHUT_RD 0 #endif #if !defined (SOCKLEN_T) # define SOCKLEN_T socklen_t #elif defined(_AIX) #undef SOCKLEN_T #define SOCKLEN_T socklen_t #endif #if !defined (STDERR_FILENO) # define STDERR_FILENO 2 #endif #endif /* __ISC_DHCP_OSDEP_H__ */ dhcp-4.4.1/includes/site.h000644 000765 000024 00000033166 13243301226 015647 0ustar00tmarkstaff000000 000000 /* Site-specific definitions. For supported systems, you shouldn't need to make any changes here. However, you may want to, in order to deal with site-specific differences. */ /* Add any site-specific definitions and inclusions here... */ /* #include */ /* #define SITE_FOOBAR */ /* Define this if you don't want dhcpd to run as a daemon and do want to see all its output printed to stdout instead of being logged via syslog(). This also makes dhcpd use the dhcpd.conf in its working directory and write the dhcpd.leases file there. */ /* #define DEBUG */ /* Define this to see what the parser is parsing. You probably don't want to see this. */ /* #define DEBUG_TOKENS */ /* Define this to see dumps of incoming and outgoing packets. This slows things down quite a bit... */ /* #define DEBUG_PACKET */ /* Define this if you want to see dumps of expression evaluation. */ /* #define DEBUG_EXPRESSIONS */ /* Define this if you want to see dumps of find_lease() in action. */ /* #define DEBUG_FIND_LEASE */ /* Define this if you want to see dumps of parsed expressions. */ /* #define DEBUG_EXPRESSION_PARSE */ /* Define this if you want to watch the class matching process. */ /* #define DEBUG_CLASS_MATCHING */ /* Define this if you want to track memory usage for the purpose of noticing memory leaks quickly. */ /* #define DEBUG_MEMORY_LEAKAGE */ /* #define DEBUG_MEMORY_LEAKAGE_ON_EXIT */ /* Define this if you want exhaustive (and very slow) checking of the malloc pool for corruption. */ /* #define DEBUG_MALLOC_POOL */ /* Define this if you want to see a message every time a lease's state changes. */ /* #define DEBUG_LEASE_STATE_TRANSITIONS */ /* Define this if you want to maintain a history of the last N operations that changed reference counts on objects. This can be used to debug cases where an object is dereferenced too often, or not often enough. */ /* #define DEBUG_RC_HISTORY */ /* Define this if you want to see the history every cycle. */ /* #define DEBUG_RC_HISTORY_EXHAUSTIVELY */ /* This is the number of history entries to maintain - by default, 256. */ /* #define RC_HISTORY_MAX 10240 */ /* Define this if you want dhcpd to dump core when a non-fatal memory allocation error is detected (i.e., something that would cause a memory leak rather than a memory smash). */ /* #define POINTER_DEBUG */ /* Define this if you want debugging output for DHCP failover protocol messages. */ /* #define DEBUG_FAILOVER_MESSAGES */ /* Define this to include contact messages in failover message debugging. The contact messages are sent once per second, so this can generate a lot of log entries. */ /* #define DEBUG_FAILOVER_CONTACT_MESSAGES */ /* Define this if you want debugging output for DHCP failover protocol event timeout timing. */ /* #define DEBUG_FAILOVER_TIMING */ /* Define this if you want to include contact message timing, which is performed once per second and can generate a lot of log entries. */ /* #define DEBUG_FAILOVER_CONTACT_TIMING */ /* Define this if you want all leases written to the lease file, even if they are free leases that have never been used. */ /* #define DEBUG_DUMP_ALL_LEASES */ /* Define this if you want to see the requests and replies between the DHCP code and the DNS library code. */ /* #define DEBUG_DNS_UPDATES */ /* Define this if you want to debug the host part of the inform processing */ /* #define DEBUG_INFORM_HOST */ /* Define this if you want to debug the binary leases (lease_chain) code */ /* #define DEBUG_BINARY_LEASES */ /* Define this if you want to debug checksum calculations */ /* #define DEBUG_CHECKSUM */ /* Define this if you want to verbosely debug checksum calculations */ /* #define DEBUG_CHECKSUM_VERBOSE */ /* Define this if you want DHCP failover protocol support in the DHCP server. */ /* #define FAILOVER_PROTOCOL */ /* Define this if you want DNS update functionality to be available. */ #define NSUPDATE /* Define this if you want to enable the DHCP server attempting to find a nameserver to use for DDNS updates. */ #define DNS_ZONE_LOOKUP /* Define this if you want the dhcpd.pid file to go somewhere other than the default (which varies from system to system, but is usually either /etc or /var/run. */ /* #define _PATH_DHCPD_PID "/var/run/dhcpd.pid" */ /* Define this if you want the dhcpd.leases file (the dynamic lease database) to go somewhere other than the default location, which is normally /etc/dhcpd.leases. */ /* #define _PATH_DHCPD_DB "/etc/dhcpd.leases" */ /* Define this if you want the dhcpd.conf file to go somewhere other than the default location. By default, it goes in /etc/dhcpd.conf. */ /* #define _PATH_DHCPD_CONF "/etc/dhcpd.conf" */ /* Network API definitions. You do not need to choose one of these - if you don't choose, one will be chosen for you in your system's config header. DON'T MESS WITH THIS UNLESS YOU KNOW WHAT YOU'RE DOING!!! */ /* Define USE_SOCKETS to use the standard BSD socket API. On many systems, the BSD socket API does not provide the ability to send packets to the 255.255.255.255 broadcast address, which can prevent some clients (e.g., Win95) from seeing replies. This is not a problem on Solaris. In addition, the BSD socket API will not work when more than one network interface is configured on the server. However, the BSD socket API is about as efficient as you can get, so if the aforementioned problems do not matter to you, or if no other API is supported for your system, you may want to go with it. */ /* #define USE_SOCKETS */ /* Define this to use the Sun Streams NIT API. The Sun Streams NIT API is only supported on SunOS 4.x releases. */ /* #define USE_NIT */ /* Define this to use the Berkeley Packet Filter API. The BPF API is available on all 4.4-BSD derivatives, including NetBSD, FreeBSD and BSDI's BSD/OS. It's also available on DEC Alpha OSF/1 in a compatibility mode supported by the Alpha OSF/1 packetfilter interface. */ /* #define USE_BPF */ /* Define this to use the raw socket API. The raw socket API is provided on many BSD derivatives, and provides a way to send out raw IP packets. It is only supported for sending packets - packets must be received with the regular socket API. This code is experimental - I've never gotten it to actually transmit a packet to the 255.255.255.255 broadcast address - so use it at your own risk. */ /* #define USE_RAW_SOCKETS */ /* Define this to keep the old program name (e.g., "dhcpd" for the DHCP server) in place of the (base) name the program was invoked with. */ /* #define OLD_LOG_NAME */ /* Define this to change the logging facility used by dhcpd. */ /* #define DHCPD_LOG_FACILITY LOG_DAEMON */ /* Define this if you want to be able to execute external commands during conditional evaluation. */ /* #define ENABLE_EXECUTE */ /* Define this if you aren't debugging and you want to save memory (potentially a _lot_ of memory) by allocating leases in chunks rather than one at a time. */ #define COMPACT_LEASES /* Define this if you want to be able to save and playback server operational traces. */ /* #define TRACING */ /* Define this if you want the server to use the previous behavior when determining the DDNS TTL. If the user has specified a ddns-ttl option that is used to detemine the ttl. (If the user specifies an option that references the lease structure it is only usable for v4. In that case v6 will use the default.) Otherwise when defined the defaults are: v4 - 1/2 the lease time, v6 - DEFAULT_DDNS_TTL. When undefined the defaults are 1/2 the (preferred) lease time for both but with a cap on the maximum. */ /* #define USE_OLD_DDNS_TTL */ /* Define this if you want a DHCPv6 server to send replies to the source port of the message it received. This is useful for testing but is only included for backwards compatibility. */ /* #define REPLY_TO_SOURCE_PORT */ /* Define this if you want to enable strict checks in DNS Updates mechanism. Do not enable this unless are DHCP developer. */ /* #define DNS_UPDATES_MEMORY_CHECKS */ /* Define this if you want to allow domain list in domain-name option. RFC2132 does not allow that behavior, but it is somewhat used due to historic reasons. Note that it may be removed some time in the future. */ #define ACCEPT_LIST_IN_DOMAIN_NAME /* In previous versions of the code when the server generates a NAK it doesn't attempt to determine if the configuration included a server ID for that client. Defining this option causes the server to make a modest effort to determine the server id when building a NAK as a response. This effort will only check the first subnet and pool associated with a shared subnet and will not check for host declarations. With some configurations the server id computed for a NAK may not match that computed for an ACK. */ #define SERVER_ID_FOR_NAK /* NOTE: SERVER_ID_CHECK switch has been removed. Enabling server id * checking is now done via the server-id-check statement. Please refer * to the dhcpd manpage (server/dhcpd.conf.5) */ /* Include code to do a slow transition of DDNS records from the interim to the standard version, or backwards. The normal code will handle removing an old style record when the name on a lease is being changed. This adds code to handle the case where the name isn't being changed but the old record should be removed to allow a new record to be added. This is the slow transition as leases are only updated as a client touches them. A fast transition would entail updating all the records at once, probably at start up. */ #define DDNS_UPDATE_SLOW_TRANSITION /* Define the default prefix length passed from the client to the script when modifying an IPv6 IA_NA or IA_TA address. The two most useful values are 128 which is what the current specifications call for or 64 which is what has been used in the past. For most OSes 128 will indicate that the address is a host address and doesn't include any on-link information. 64 indicates that the first 64 bits are the subnet or on-link prefix. */ #define DHCLIENT_DEFAULT_PREFIX_LEN 128 /* Enable the gentle shutdown signal handling. Currently this means that on SIGINT or SIGTERM a client will release its address and a server in a failover pair will go through partner down. Both of which can be undesireable in some situations. We plan to revisit this feature and may make non-backwards compatible changes including the removal of this define. Use at your own risk. */ /* #define ENABLE_GENTLE_SHUTDOWN */ /* Include old error codes. This is provided in case you are building an external program similar to omshell for which you need the ISC_R_* error codes. You should switch to DHCP_R_* error codes for those that have been defined (see includes/omapip/result.h). The extra defines and this option will be removed at some time. */ /* #define INCLUDE_OLD_DHCP_ISC_ERROR_CODES */ /* Use the older factors for scoring a lease in the v6 client code. The new factors cause the client to choose more bindings (IAs) over more addresse within a binding. Most uses will get a single address in a single binding and only get an adverstise from a single server and there won't be a difference. */ /* #define USE_ORIGINAL_CLIENT_LEASE_WEIGHTS */ /* Print out specific error messages for dhclient, dhcpd or dhcrelay when processing an incorrect command line. This is included for those that might require the exact error messages, as we don't expect that is necessary it is on by default. */ #define PRINT_SPECIFIC_CL_ERRORS /* Limit the value of a file descriptor the serve will use when accepting a connecting request. This can be used to limit the number of TCP connections that the server will allow at one time. A value of 0 means there is no limit.*/ #define MAX_FD_VALUE 200 /* Enable EUI-64 Address assignment policy. Instructs the server * to use EUI-64 addressing instead of dynamic address allocation * for IA_NA pools, if the parameter use-eui-64 is true for the * pool. Can be at all scopes down to the pool level. Not * supported by the configure script. */ /* #define EUI_64 */ /* Enable enforcement of the require option statement as documented * in man page. Instructs the dhclient, when in -6 mode, to discard * offered leases that do not contain all options specified as required * in the client's configuration file. The client already enforces this * in -4 mode. */ #define ENFORCE_DHCPV6_CLIENT_REQUIRE /* Enable the invocation of the client script with a FAIL state code * by dhclient when running in one-try mode (-T) and the attempt to * obtain the desired lease(s) fails. Applies to IPv4 mode only. */ /* #define CALL_SCRIPT_ON_ONETRY_FAIL */ /* Include definitions for various options. In general these should be left as is, but if you have already defined one of these and prefer your definition you can comment the RFC define out to avoid conflicts */ #define RFC2563_OPTIONS #define RFC2937_OPTIONS #define RFC4776_OPTIONS #define RFC4578_OPTIONS #define RFC4833_OPTIONS #define RFC4994_OPTIONS #define RFC5071_OPTIONS #define RFC5192_OPTIONS #define RFC5223_OPTIONS #define RFC5417_OPTIONS #define RFC5460_OPTIONS #define RFC5859_OPTIONS #define RFC5969_OPTIONS #define RFC5970_OPTIONS #define RFC5986_OPTIONS #define RFC6011_OPTIONS #define RFC6011_OPTIONS #define RFC6153_OPTIONS #define RFC6334_OPTIONS #define RFC6440_OPTIONS #define RFC6731_OPTIONS #define RFC6939_OPTIONS #define RFC6977_OPTIONS #define RFC7083_OPTIONS #define RFC7341_OPTIONS #define RFC7618_OPTIONS #define RFC7710_OPTIONS dhcp-4.4.1/includes/statement.h000644 000765 000024 00000005166 13243301226 016706 0ustar00tmarkstaff000000 000000 /* statement.h Definitions for executable statements... */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1996-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ struct executable_statement { int refcnt; struct executable_statement *next; enum statement_op { null_statement, if_statement, add_statement, eval_statement, break_statement, default_option_statement, supersede_option_statement, append_option_statement, prepend_option_statement, send_option_statement, statements_statement, on_statement, switch_statement, case_statement, default_statement, set_statement, unset_statement, let_statement, define_statement, log_statement, return_statement, execute_statement, vendor_opt_statement } op; union { struct { struct executable_statement *tc, *fc; struct expression *expr; } ie; struct expression *eval; struct expression *retval; struct class *add; struct option_cache *option; struct option_cache *supersede; struct option_cache *prepend; struct option_cache *append; struct executable_statement *statements; struct { int evtypes; # define ON_COMMIT 1 # define ON_EXPIRY 2 # define ON_RELEASE 4 # define ON_TRANSMISSION 8 struct executable_statement *statements; } on; struct { struct expression *expr; struct executable_statement *statements; } s_switch; struct expression *c_case; struct { char *name; struct expression *expr; struct executable_statement *statements; } set, let; char *unset; struct { enum { log_priority_fatal, log_priority_error, log_priority_debug, log_priority_info } priority; struct expression *expr; } log; struct { char *command; struct expression *arglist; int argc; } execute; } data; }; dhcp-4.4.1/includes/t_api.h000644 000765 000024 00000004054 13243301226 015771 0ustar00tmarkstaff000000 000000 /* * Copyright (C) 2004-2017 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2003 Internet Software Consortium. * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* $Id: t_api.h,v 1.4 2009/11/24 02:06:56 sar Exp $ */ #ifndef TESTS_T_API_H #define TESTS_T_API_H 1 /*! \file tests/t_api.h */ #include #include #include #include /* * * Result codes. * */ #define T_PASS 0x1 #define T_FAIL 0x2 #define T_UNRESOLVED 0x3 #define T_UNSUPPORTED 0x4 #define T_UNTESTED 0x5 #define T_THREADONLY 0x6 /* * * Assertion class codes. * */ #define T_OPTIONAL 0x0 #define T_REQUIRED 0x1 /* * Misc */ #define T_MAXTOKS 16 #define T_ARG(n) (*(av + (n))) typedef void (*PFV)(void); typedef struct { PFV pfv; const char *func_name; } testspec_t; extern int T_debug; extern testspec_t T_testlist[]; ISC_LANG_BEGINDECLS void t_assert(const char *component, int anum, int class, const char *what, ...) ISC_FORMAT_PRINTF(4, 5); void t_info(const char *format, ...) ISC_FORMAT_PRINTF(1, 2); void t_result(int result); char * t_getenv(const char *name); char * t_fgetbs(FILE *fp); isc_result_t t_dns_result_fromtext(char *result); unsigned int t_dc_method_fromtext(char *dc_method); int t_bustline(char *line, char **toks); int t_eval(const char *filename, int (*func)(char **), int nargs); ISC_LANG_ENDDECLS #endif /* TESTS_T_API_H */ dhcp-4.4.1/includes/tree.h000644 000765 000024 00000020356 13243301226 015637 0ustar00tmarkstaff000000 000000 /* tree.h Definitions for address trees... */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1996-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ /* A pair of pointers, suitable for making a linked list. */ typedef struct _pair { caddr_t car; struct _pair *cdr; } *pair; struct option_chain_head { int refcnt; pair first; }; struct enumeration_value { const char *name; u_int8_t value; }; struct enumeration { struct enumeration *next; const char *name; unsigned width; struct enumeration_value *values; }; /* Tree node types... */ #define TREE_CONCAT 1 #define TREE_HOST_LOOKUP 2 #define TREE_CONST 3 #define TREE_LIMIT 4 #define TREE_DATA_EXPR 5 /* A data buffer with a reference count. */ struct buffer { int refcnt; unsigned char data [1]; }; /* XXX The mechanism by which data strings are returned is currently XXX broken: rather than returning an ephemeral pointer, we create XXX a reference to the data in the caller's space, which the caller XXX then has to dereference - instead, the reference should be XXX ephemeral by default and be made a persistent reference explicitly. */ /* XXX on the other hand, it seems to work pretty nicely, so maybe the XXX above comment is meshuggenah. */ /* XXX I think the above comment tries to say this: XXX http://tinyurl.com/2tjqre */ /* A string of data bytes, possibly accompanied by a larger buffer. */ struct data_string { struct buffer *buffer; const unsigned char *data; unsigned len; /* Does not include NUL terminator, if any. */ int terminated; }; enum expression_context { context_any, /* indefinite */ context_boolean, context_data, context_numeric, context_dns, context_data_or_numeric, /* indefinite */ context_function }; struct fundef { int refcnt; struct string_list *args; struct executable_statement *statements; }; struct binding_value { int refcnt; enum { binding_boolean, binding_data, binding_numeric, binding_dns, binding_function } type; union value { struct data_string data; unsigned long intval; int boolean; struct fundef *fundef; struct binding_value *bv; } value; }; struct binding { struct binding *next; char *name; struct binding_value *value; }; struct binding_scope { int refcnt; struct binding_scope *outer; struct binding *bindings; }; /* Expression tree structure. */ enum expr_op { expr_none, expr_match, expr_check, expr_equal, expr_substring, expr_suffix, expr_concat, expr_host_lookup, expr_and, expr_or, expr_not, expr_option, expr_hardware, expr_packet, expr_const_data, expr_extract_int8, expr_extract_int16, expr_extract_int32, expr_encode_int8, expr_encode_int16, expr_encode_int32, expr_const_int, expr_exists, expr_encapsulate, expr_known, expr_reverse, expr_leased_address, expr_binary_to_ascii, expr_config_option, expr_host_decl_name, expr_pick_first_value, expr_lease_time, expr_dns_transaction, expr_static, expr_ns_add, expr_ns_delete, expr_ns_exists, expr_ns_not_exists, expr_not_equal, expr_null, expr_variable_exists, expr_variable_reference, expr_filename, expr_sname, expr_arg, expr_funcall, expr_function, expr_add, expr_subtract, expr_multiply, expr_divide, expr_remainder, expr_binary_and, expr_binary_or, expr_binary_xor, expr_client_state, expr_ucase, expr_lcase, expr_regex_match, expr_iregex_match, expr_gethostname, expr_v6relay, expr_concat_dclist }; struct expression { int refcnt; enum expr_op op; union expr_union { struct { struct expression *expr; struct expression *offset; struct expression *len; } substring; struct expression *equal [2]; struct expression *and [2]; struct expression *or [2]; struct expression *not; struct expression *add; struct expression *subtract; struct expression *multiply; struct expression *divide; struct expression *remainder; struct collection *check; struct { struct expression *expr; struct expression *len; } suffix; struct expression *lcase; struct expression *ucase; struct option *option; struct option *config_option; struct { struct expression *offset; struct expression *len; } packet; struct data_string const_data; struct expression *extract_int; struct expression *encode_int; unsigned long const_int; struct expression *concat [2]; struct dns_host_entry *host_lookup; struct option *exists; struct data_string encapsulate; struct { struct expression *base; struct expression *width; struct expression *separator; struct expression *buffer; } b2a; struct { struct expression *width; struct expression *buffer; } reverse; struct { struct expression *car; struct expression *cdr; } pick_first_value; struct { struct expression *car; struct expression *cdr; } dns_transaction; struct { unsigned rrclass; unsigned rrtype; struct expression *rrname; struct expression *rrdata; struct expression *ttl; } ns_add; struct { unsigned rrclass; unsigned rrtype; struct expression *rrname; struct expression *rrdata; } ns_delete, ns_exists, ns_not_exists; char *variable; struct { struct expression *val; struct expression *next; } arg; struct { char *name; struct expression *arglist; } funcall; struct fundef *func; struct { struct expression *relay; struct expression *roption; } v6relay; } data; int flags; # define EXPR_EPHEMERAL 1 }; /* DNS host entry structure... */ struct dns_host_entry { int refcnt; TIME timeout; struct data_string data; char hostname [1]; }; struct option_cache; /* forward */ struct packet; /* forward */ struct option_state; /* forward */ struct decoded_option_state; /* forward */ struct lease; /* forward */ struct client_state; /* forward */ struct universe { const char *name; struct option_cache *(*lookup_func) (struct universe *, struct option_state *, unsigned); void (*save_func) (struct universe *, struct option_state *, struct option_cache *, isc_boolean_t); void (*foreach) (struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct binding_scope **, struct universe *, void *, void (*) (struct option_cache *, struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct binding_scope **, struct universe *, void *)); void (*delete_func) (struct universe *universe, struct option_state *, int); int (*option_state_dereference) (struct universe *, struct option_state *, const char *, int); int (*decode) (struct option_state *, const unsigned char *, unsigned, struct universe *); int (*encapsulate) (struct data_string *, struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct binding_scope **, struct universe *); u_int32_t (*get_tag) (const unsigned char *); void (*store_tag) (unsigned char *, u_int32_t); u_int32_t (*get_length) (const unsigned char *); void (*store_length) (unsigned char *, u_int32_t); int tag_size, length_size; unsigned site_code_min, end; option_name_hash_t *name_hash; option_code_hash_t *code_hash; struct option *enc_opt; int index; /* Flags should probably become condensed. */ int concat_duplicates; }; struct option { const char *name; const char *format; struct universe *universe; unsigned code; int refcnt; }; dhcp-4.4.1/includes/omapip/alloc.h000644 000765 000024 00000006260 13243301226 017255 0ustar00tmarkstaff000000 000000 /* alloc.h Definitions for the object management API protocol memory allocation... */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1996-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ isc_result_t omapi_buffer_new (omapi_buffer_t **, const char *, int); isc_result_t omapi_buffer_reference (omapi_buffer_t **, omapi_buffer_t *, const char *, int); isc_result_t omapi_buffer_dereference (omapi_buffer_t **, const char *, int); #if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) || \ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) #define DMDOFFSET (sizeof (struct dmalloc_preamble)) #define DMLFSIZE 16 #define DMUFSIZE 16 #define DMDSIZE (DMDOFFSET + DMLFSIZE + DMUFSIZE) struct dmalloc_preamble { struct dmalloc_preamble *prev, *next; const char *file; int line; size_t size; unsigned long generation; unsigned char low_fence [DMLFSIZE]; }; #else #define DMDOFFSET 0 #define DMDSIZE 0 #endif /* rc_history flags... */ #define RC_LEASE 1 #define RC_MISC 2 #if defined (DEBUG_RC_HISTORY) #if !defined (RC_HISTORY_MAX) # define RC_HISTORY_MAX 256 #endif #if !defined (RC_HISTORY_FLAGS) # define RC_HISTORY_FLAGS (RC_LEASE | RC_MISC) #endif struct rc_history_entry { const char *file; int line; void *reference; void *addr; int refcnt; }; #define rc_register(x, l, r, y, z, d, f) do { \ if (RC_HISTORY_FLAGS & ~(f)) { \ rc_history [rc_history_index].file = (x); \ rc_history [rc_history_index].line = (l); \ rc_history [rc_history_index].reference = (r); \ rc_history [rc_history_index].addr = (y); \ rc_history [rc_history_index].refcnt = (z); \ rc_history_next (d); \ } \ } while (0) #define rc_register_mdl(r, y, z, d, f) \ rc_register (__FILE__, __LINE__, r, y, z, d, f) #else #define rc_register(file, line, reference, addr, refcnt, d, f) #define rc_register_mdl(reference, addr, refcnt, d, f) #endif #if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) || \ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) extern struct dmalloc_preamble *dmalloc_list; extern unsigned long dmalloc_outstanding; extern unsigned long dmalloc_longterm; extern unsigned long dmalloc_generation; extern unsigned long dmalloc_cutoff_generation; #endif #if defined (DEBUG_RC_HISTORY) extern struct rc_history_entry rc_history [RC_HISTORY_MAX]; extern int rc_history_index; extern int rc_history_count; #endif dhcp-4.4.1/includes/omapip/buffer.h000644 000765 000024 00000006254 13243301226 017437 0ustar00tmarkstaff000000 000000 /* buffer.h Definitions for the object management API protocol buffering... */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1996-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ /* OMAPI buffers are ring buffers, which means that the beginning of the buffer and the end of the buffer chase each other around. As long as the tail never catches up to the head, there's room in the buffer for data. - If the tail and the head are equal, the buffer is empty. - If the tail is less than the head, the contents of the buffer are the bytes from the head to the end of buffer, and in addition, the bytes between the beginning of the buffer and the tail, not including the byte addressed by the tail. - If the tail is greater than the head, then the buffer contains valid bytes starting with the byte addressed by the head, and ending with the byte before the byte addressed by the tail. There will always be at least one byte of waste, because the tail can't increase so that it's equal to the head (that would represent an empty buffer. */ #define OMAPI_BUF_SIZE 4048 typedef struct _omapi_buffer { struct _omapi_buffer *next; /* Buffers can be chained. */ u_int32_t refcnt; /* Buffers are reference counted. */ u_int16_t head, tail; /* Buffers are organized in a ring. */ char buf [OMAPI_BUF_SIZE]; /* The actual buffer is included in the buffer data structure. */ } omapi_buffer_t; #define BUFFER_BYTES_FREE(x) \ ((x) -> tail > (x) -> head \ ? sizeof ((x) -> buf) - ((x) -> tail - (x) -> head) \ : (x) -> head - (x) -> tail) #define BYTES_IN_BUFFER(x) \ ((x) -> tail > (x) -> head \ ? (x) -> tail - (x) -> head - 1 \ : sizeof ((x) -> buf) - ((x) -> head - (x) -> tail) - 1) isc_result_t omapi_connection_require (omapi_object_t *, unsigned); isc_result_t omapi_connection_copyout (unsigned char *, omapi_object_t *, unsigned); isc_result_t omapi_connection_copyin (omapi_object_t *, const unsigned char *, unsigned); isc_result_t omapi_connection_flush (omapi_object_t *); isc_result_t omapi_connection_get_uint32 (omapi_object_t *, u_int32_t *); isc_result_t omapi_connection_put_uint32 (omapi_object_t *, u_int32_t); isc_result_t omapi_connection_get_uint16 (omapi_object_t *, u_int16_t *); isc_result_t omapi_connection_put_uint16 (omapi_object_t *, u_int32_t); dhcp-4.4.1/includes/omapip/convert.h000644 000765 000024 00000003335 13243301226 017643 0ustar00tmarkstaff000000 000000 /* convert.h Safe copying of integers into and out of a non-aligned memory buffer. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1996-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #ifndef OMAPI_CONVERT_H #define OMAPI_CONVERT_H u_int32_t getULong (const unsigned char *); int32_t getLong (const unsigned char *); u_int32_t getUShort (const unsigned char *); int32_t getShort (const unsigned char *); u_int32_t getUChar (const unsigned char *); void putULong (unsigned char *, u_int32_t); void putLong (unsigned char *, int32_t); void putUShort (unsigned char *, u_int32_t); void putShort (unsigned char *, int32_t); void putUChar (unsigned char *, u_int32_t); int converted_length (const unsigned char *, unsigned int, unsigned int); int binary_to_ascii (unsigned char *, const unsigned char *, unsigned int, unsigned int); #endif /* OMAPI_CONVERT_H */ dhcp-4.4.1/includes/omapip/hash.h000644 000765 000024 00000013361 13243301226 017106 0ustar00tmarkstaff000000 000000 /* hash.h Definitions for hashing... */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1995-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #ifndef OMAPI_HASH_H #define OMAPI_HASH_H #if !defined (DEFAULT_HASH_SIZE) # define DEFAULT_HASH_SIZE 9973 #endif #if !defined (KEY_HASH_SIZE) # define KEY_HASH_SIZE 1009 #endif /* The purpose of the hashed_object_t struct is to not match anything else. */ typedef struct { int foo; } hashed_object_t; typedef isc_result_t (*hash_foreach_func)(const void *, unsigned, void *); typedef int (*hash_reference) (hashed_object_t **, hashed_object_t *, const char *, int); typedef int (*hash_dereference) (hashed_object_t **, const char *, int); struct hash_bucket { struct hash_bucket *next; const unsigned char *name; unsigned len; hashed_object_t *value; }; typedef int (*hash_comparator_t)(const void *, const void *, size_t); struct hash_table { unsigned hash_count; hash_reference referencer; hash_dereference dereferencer; hash_comparator_t cmp; unsigned (*do_hash)(const void *, unsigned, unsigned); /* This must remain the last entry in this table. */ struct hash_bucket *buckets [1]; }; struct named_hash { struct named_hash *next; const char *name; struct hash_table *hash; }; #define HASH_FUNCTIONS_DECL(name, bufarg, type, hashtype) \ void name##_hash_add (hashtype *, bufarg, unsigned, type *, \ const char *, int); \ void name##_hash_delete (hashtype *, bufarg, unsigned, \ const char *, int); \ int name##_hash_lookup (type **, hashtype *, bufarg, unsigned, \ const char *, int); \ unsigned char * name##_hash_report(hashtype *); \ int name##_hash_foreach (hashtype *, hash_foreach_func); \ int name##_new_hash (hashtype **, unsigned, const char *, int); \ void name##_free_hash_table (hashtype **, const char *, int); #define HASH_FUNCTIONS(name, bufarg, type, hashtype, ref, deref, hasher) \ void name##_hash_add (hashtype *table, \ bufarg buf, unsigned len, type *ptr, \ const char *file, int line) \ { \ add_hash ((struct hash_table *)table, buf, \ len, (hashed_object_t *)ptr, file, line); \ } \ \ void name##_hash_delete (hashtype *table, bufarg buf, unsigned len, \ const char *file, int line) \ { \ delete_hash_entry ((struct hash_table *)table, buf, len, \ file, line); \ } \ \ int name##_hash_lookup (type **ptr, hashtype *table, \ bufarg buf, unsigned len, const char *file, int line) \ { \ return hash_lookup ((hashed_object_t **)ptr, \ (struct hash_table *)table, \ buf, len, file, line); \ } \ \ unsigned char * name##_hash_report(hashtype *table) \ { \ return hash_report((struct hash_table *)table); \ } \ \ int name##_hash_foreach (hashtype *table, hash_foreach_func func) \ { \ return hash_foreach ((struct hash_table *)table, \ func); \ } \ \ int name##_new_hash (hashtype **tp, unsigned c, const char *file, int line) \ { \ return new_hash ((struct hash_table **)tp, \ (hash_reference)ref, (hash_dereference)deref, c, \ hasher, file, line); \ } \ \ void name##_free_hash_table (hashtype **table, const char *file, int line) \ { \ free_hash_table ((struct hash_table **)table, file, line); \ } void relinquish_hash_bucket_hunks (void); int new_hash_table (struct hash_table **, unsigned, const char *, int); void free_hash_table (struct hash_table **, const char *, int); struct hash_bucket *new_hash_bucket (const char *, int); void free_hash_bucket (struct hash_bucket *, const char *, int); int new_hash(struct hash_table **, hash_reference, hash_dereference, unsigned, unsigned (*do_hash)(const void *, unsigned, unsigned), const char *, int); unsigned do_string_hash(const void *, unsigned, unsigned); unsigned do_case_hash(const void *, unsigned, unsigned); unsigned do_id_hash(const void *, unsigned, unsigned); unsigned do_number_hash(const void *, unsigned, unsigned); unsigned do_ip4_hash(const void *, unsigned, unsigned); unsigned char *hash_report(struct hash_table *); void add_hash (struct hash_table *, const void *, unsigned, hashed_object_t *, const char *, int); void delete_hash_entry (struct hash_table *, const void *, unsigned, const char *, int); int hash_lookup (hashed_object_t **, struct hash_table *, const void *, unsigned, const char *, int); int hash_foreach (struct hash_table *, hash_foreach_func); int casecmp (const void *s, const void *t, size_t len); #endif /* OMAPI_HASH_H */ dhcp-4.4.1/includes/omapip/isclib.h000644 000765 000024 00000006774 13243301226 017442 0ustar00tmarkstaff000000 000000 /* isclib.h connections to the isc and dns libraries */ /* * Copyright (c) 2009-2017 by 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * http://www.isc.org/ * */ #ifndef ISCLIB_H #define ISCLIB_H #include "config.h" #include #define MAXWIRE 256 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "result.h" /* * DHCP context structure * This holds the libisc information for a dhcp entity */ typedef struct dhcp_context { isc_mem_t *mctx; isc_appctx_t *actx; int actx_started; isc_taskmgr_t *taskmgr; isc_task_t *task; isc_socketmgr_t *socketmgr; isc_timermgr_t *timermgr; #if defined (NSUPDATE) dns_client_t *dnsclient; int use_local4; isc_sockaddr_t local4_sockaddr; int use_local6; isc_sockaddr_t local6_sockaddr; #endif } dhcp_context_t; extern dhcp_context_t dhcp_gbl_ctx; #define DHCP_MAXDNS_WIRE 256 #define DHCP_MAXNS 3 #define DHCP_HMAC_MD5_NAME "HMAC-MD5.SIG-ALG.REG.INT." #define DHCP_HMAC_SHA1_NAME "HMAC-SHA1.SIG-ALG.REG.INT." #define DHCP_HMAC_SHA224_NAME "HMAC-SHA224.SIG-ALG.REG.INT." #define DHCP_HMAC_SHA256_NAME "HMAC-SHA256.SIG-ALG.REG.INT." #define DHCP_HMAC_SHA384_NAME "HMAC-SHA384.SIG-ALG.REG.INT." #define DHCP_HMAC_SHA512_NAME "HMAC-SHA512.SIG-ALG.REG.INT." isc_result_t dhcp_isc_name(unsigned char *namestr, dns_fixedname_t *namefix, dns_name_t **name); isc_result_t isclib_make_dst_key(char *inname, char *algorithm, unsigned char *secret, int length, dst_key_t **dstkey); #define DHCP_CONTEXT_PRE_DB 1 #define DHCP_CONTEXT_POST_DB 2 #define DHCP_DNS_CLIENT_LAZY_INIT 4 isc_result_t dhcp_context_create(int flags, struct in_addr *local4, struct in6_addr *local6); void isclib_cleanup(void); void dhcp_signal_handler(int signal); extern int shutdown_signal; isc_result_t dns_client_init(); #endif /* ISCLIB_H */ dhcp-4.4.1/includes/omapip/omapip.h000644 000765 000024 00000057444 13243301226 017462 0ustar00tmarkstaff000000 000000 /* omapip.h Definitions for the object management API and protocol... */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1996-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #ifndef _OMAPIP_H_ #define _OMAPIP_H_ #include "result.h" #include #include typedef unsigned int omapi_handle_t; struct __omapi_object; typedef struct __omapi_object omapi_object_t; typedef enum { omapi_datatype_int, omapi_datatype_string, omapi_datatype_data, omapi_datatype_object } omapi_datatype_t; typedef struct { int refcnt; omapi_datatype_t type; union { struct { unsigned len; #define OMAPI_TYPED_DATA_NOBUFFER_LEN (sizeof (int) + \ sizeof (omapi_datatype_t) + \ sizeof (int)) unsigned char value [1]; } buffer; #define OMAPI_TYPED_DATA_OBJECT_LEN (sizeof (int) + \ sizeof (omapi_datatype_t) + \ sizeof (omapi_object_t *)) omapi_object_t *object; #define OMAPI_TYPED_DATA_REF_LEN (sizeof (int) + \ sizeof (omapi_datatype_t) + \ 3 * sizeof (void *)) struct { void *ptr; isc_result_t (*reference) (void *, void *, const char *, int); isc_result_t (*dereference) (void *, const char *, int); } ref; #define OMAPI_TYPED_DATA_INT_LEN (sizeof (int) + \ sizeof (omapi_datatype_t) + \ sizeof (int)) int integer; } u; } omapi_typed_data_t; typedef struct { int refcnt; unsigned len; #define OMAPI_DATA_STRING_EMPTY_SIZE (2 * sizeof (int)) unsigned char value [1]; } omapi_data_string_t; typedef struct { int refcnt; omapi_data_string_t *name; omapi_typed_data_t *value; } omapi_value_t; typedef struct __omapi_object_type_t { const char *name; struct __omapi_object_type_t *next; isc_result_t (*set_value) (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_typed_data_t *); isc_result_t (*get_value) (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_value_t **); isc_result_t (*destroy) (omapi_object_t *, const char *, int); isc_result_t (*signal_handler) (omapi_object_t *, const char *, va_list); isc_result_t (*stuff_values) (omapi_object_t *, omapi_object_t *, omapi_object_t *); isc_result_t (*lookup) (omapi_object_t **, omapi_object_t *, omapi_object_t *); isc_result_t (*create) (omapi_object_t **, omapi_object_t *); isc_result_t (*remove) (omapi_object_t *, omapi_object_t *); isc_result_t (*freer) (omapi_object_t *, const char *, int); isc_result_t (*allocator) (omapi_object_t **, const char *, int); isc_result_t (*sizer) (size_t); size_t size; int rc_flag; isc_result_t (*initialize) (omapi_object_t *, const char *, int); } omapi_object_type_t; #define OMAPI_OBJECT_PREAMBLE \ omapi_object_type_t *type; \ int refcnt; \ omapi_handle_t handle; \ omapi_object_t *outer, *inner /* The omapi handle structure. */ struct __omapi_object { OMAPI_OBJECT_PREAMBLE; }; /* The port on which applications should listen for OMAPI connections. */ #define OMAPI_PROTOCOL_PORT 7911 typedef struct { unsigned addrtype; unsigned addrlen; unsigned char address [16]; unsigned port; } omapi_addr_t; typedef struct { int refcnt; unsigned count; omapi_addr_t *addresses; } omapi_addr_list_t; typedef struct auth_key { OMAPI_OBJECT_PREAMBLE; char *name; char *algorithm; omapi_data_string_t *key; dns_tsec_t *tsec_key; } omapi_auth_key_t; #define OMAPI_CREATE 1 #define OMAPI_UPDATE 2 #define OMAPI_EXCL 4 #define OMAPI_NOTIFY_PROTOCOL 8 #define OMAPI_OBJECT_ALLOC(name, stype, type) \ isc_result_t name##_allocate (stype **p, const char *file, int line) \ { \ return omapi_object_allocate ((omapi_object_t **)p, \ type, 0, file, line); \ } \ \ isc_result_t name##_reference (stype **pptr, stype *ptr, \ const char *file, int line) \ { \ return omapi_object_reference ((omapi_object_t **)pptr, \ (omapi_object_t *)ptr, file, line); \ } \ \ isc_result_t name##_dereference (stype **ptr, const char *file, int line) \ { \ return omapi_object_dereference ((omapi_object_t **)ptr, file, line); \ } #define OMAPI_OBJECT_ALLOC_DECL(name, stype, type) \ isc_result_t name##_allocate (stype **p, const char *file, int line); \ isc_result_t name##_reference (stype **pptr, stype *ptr, \ const char *file, int line); \ isc_result_t name##_dereference (stype **ptr, const char *file, int line); typedef isc_result_t (*omapi_array_ref_t) (char **, char *, const char *, int); typedef isc_result_t (*omapi_array_deref_t) (char **, const char *, int); /* An extensible array type. */ typedef struct { char **data; omapi_array_ref_t ref; omapi_array_deref_t deref; int count; int max; } omapi_array_t; #define OMAPI_ARRAY_TYPE(name, stype) \ isc_result_t name##_array_allocate (omapi_array_t **p, \ const char *file, int line) \ { \ return (omapi_array_allocate \ (p, \ (omapi_array_ref_t)name##_reference, \ (omapi_array_deref_t)name##_dereference, \ file, line)); \ } \ \ isc_result_t name##_array_free (omapi_array_t **p, \ const char *file, int line) \ { \ return omapi_array_free (p, file, line); \ } \ \ isc_result_t name##_array_extend (omapi_array_t *pptr, stype *ptr, int *index,\ const char *file, int line) \ { \ return omapi_array_extend (pptr, (char *)ptr, index, file, line); \ } \ \ isc_result_t name##_array_set (omapi_array_t *pptr, stype *ptr, int index, \ const char *file, int line) \ { \ return omapi_array_set (pptr, (char *)ptr, index, file, line); \ } \ \ isc_result_t name##_array_lookup (stype **ptr, omapi_array_t *pptr, \ int index, const char *file, int line) \ { \ return omapi_array_lookup ((char **)ptr, pptr, index, file, line); \ } #define OMAPI_ARRAY_TYPE_DECL(name, stype) \ isc_result_t name##_array_allocate (omapi_array_t **, const char *, int); \ isc_result_t name##_array_free (omapi_array_t **, const char *, int); \ isc_result_t name##_array_extend (omapi_array_t *, stype *, int *, \ const char *, int); \ isc_result_t name##_array_set (omapi_array_t *, \ stype *, int, const char *, int); \ isc_result_t name##_array_lookup (stype **, \ omapi_array_t *, int, const char *, int) #define omapi_array_foreach_begin(array, stype, var) \ { \ int omapi_array_foreach_index; \ stype *var = (stype *)0; \ for (omapi_array_foreach_index = 0; \ array && \ omapi_array_foreach_index < (array) -> count; \ omapi_array_foreach_index++) { \ if ((array) -> data [omapi_array_foreach_index]) { \ ((*(array) -> ref) \ ((char **)&var, \ (array) -> data [omapi_array_foreach_index],\ MDL)); #define omapi_array_foreach_end(array, stype, var) \ (*(array) -> deref) ((char **)&var, MDL); \ } \ } \ } isc_result_t omapi_protocol_connect (omapi_object_t *, const char *, unsigned, omapi_object_t *); isc_result_t omapi_connect_list (omapi_object_t *, omapi_addr_list_t *, omapi_addr_t *); isc_result_t omapi_protocol_listen (omapi_object_t *, unsigned, int); isc_boolean_t omapi_protocol_authenticated (omapi_object_t *); isc_result_t omapi_protocol_configure_security (omapi_object_t *, isc_result_t (*) (omapi_object_t *, omapi_addr_t *), isc_result_t (*) (omapi_object_t *, omapi_auth_key_t *)); isc_result_t omapi_protocol_accept (omapi_object_t *); isc_result_t omapi_protocol_send_intro (omapi_object_t *, unsigned, unsigned); isc_result_t omapi_protocol_ready (omapi_object_t *); isc_result_t omapi_protocol_add_auth (omapi_object_t *, omapi_object_t *, omapi_handle_t); isc_result_t omapi_protocol_lookup_auth (omapi_object_t **, omapi_object_t *, omapi_handle_t); isc_result_t omapi_protocol_set_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_typed_data_t *); isc_result_t omapi_protocol_get_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_value_t **); isc_result_t omapi_protocol_stuff_values (omapi_object_t *, omapi_object_t *, omapi_object_t *); isc_result_t omapi_protocol_destroy (omapi_object_t *, const char *, int); isc_result_t omapi_protocol_send_message (omapi_object_t *, omapi_object_t *, omapi_object_t *, omapi_object_t *); isc_result_t omapi_protocol_signal_handler (omapi_object_t *, const char *, va_list); isc_result_t omapi_protocol_listener_set_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_typed_data_t *); isc_result_t omapi_protocol_listener_get_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_value_t **); isc_result_t omapi_protocol_listener_destroy (omapi_object_t *, const char *, int); isc_result_t omapi_protocol_listener_signal (omapi_object_t *, const char *, va_list); isc_result_t omapi_protocol_listener_stuff (omapi_object_t *, omapi_object_t *, omapi_object_t *); isc_result_t omapi_protocol_send_status (omapi_object_t *, omapi_object_t *, isc_result_t, unsigned, const char *); isc_result_t omapi_protocol_send_open (omapi_object_t *, omapi_object_t *, const char *, omapi_object_t *, unsigned); isc_result_t omapi_protocol_send_update (omapi_object_t *, omapi_object_t *, unsigned, omapi_object_t *); isc_result_t omapi_connect (omapi_object_t *, const char *, unsigned); isc_result_t omapi_disconnect (omapi_object_t *, int); int omapi_connection_readfd (omapi_object_t *); int omapi_connection_writefd (omapi_object_t *); isc_result_t omapi_connection_connect (omapi_object_t *); isc_result_t omapi_connection_reader (omapi_object_t *); isc_result_t omapi_connection_writer (omapi_object_t *); isc_result_t omapi_connection_reaper (omapi_object_t *); isc_result_t omapi_connection_output_auth_length (omapi_object_t *, unsigned *); isc_result_t omapi_connection_set_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_typed_data_t *); isc_result_t omapi_connection_get_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_value_t **); isc_result_t omapi_connection_destroy (omapi_object_t *, const char *, int); isc_result_t omapi_connection_signal_handler (omapi_object_t *, const char *, va_list); isc_result_t omapi_connection_stuff_values (omapi_object_t *, omapi_object_t *, omapi_object_t *); isc_result_t omapi_connection_write_typed_data (omapi_object_t *, omapi_typed_data_t *); isc_result_t omapi_connection_put_name (omapi_object_t *, const char *); isc_result_t omapi_connection_put_string (omapi_object_t *, const char *); isc_result_t omapi_connection_put_handle (omapi_object_t *c, omapi_object_t *h); isc_result_t omapi_connection_put_named_uint32 (omapi_object_t *, const char *, u_int32_t); isc_result_t omapi_listen (omapi_object_t *, unsigned, int); isc_result_t omapi_listen_addr (omapi_object_t *, omapi_addr_t *, int); isc_result_t omapi_listener_accept (omapi_object_t *); int omapi_listener_readfd (omapi_object_t *); isc_result_t omapi_accept (omapi_object_t *); isc_result_t omapi_listener_configure_security (omapi_object_t *, isc_result_t (*) (omapi_object_t *, omapi_addr_t *)); isc_result_t omapi_listener_set_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_typed_data_t *); isc_result_t omapi_listener_get_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_value_t **); isc_result_t omapi_listener_destroy (omapi_object_t *, const char *, int); isc_result_t omapi_listener_signal_handler (omapi_object_t *, const char *, va_list); isc_result_t omapi_listener_stuff_values (omapi_object_t *, omapi_object_t *, omapi_object_t *); isc_result_t omapi_register_io_object (omapi_object_t *, int (*)(omapi_object_t *), int (*)(omapi_object_t *), isc_result_t (*)(omapi_object_t *), isc_result_t (*)(omapi_object_t *), isc_result_t (*)(omapi_object_t *)); isc_result_t omapi_reregister_io_object (omapi_object_t *, int (*)(omapi_object_t *), int (*)(omapi_object_t *), isc_result_t (*)(omapi_object_t *), isc_result_t (*)(omapi_object_t *), isc_result_t (*)(omapi_object_t *)); isc_result_t omapi_unregister_io_object (omapi_object_t *); isc_result_t omapi_dispatch (struct timeval *); isc_result_t omapi_wait_for_completion (omapi_object_t *, struct timeval *); isc_result_t omapi_one_dispatch (omapi_object_t *, struct timeval *); isc_result_t omapi_io_set_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_typed_data_t *); isc_result_t omapi_io_get_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_value_t **); isc_result_t omapi_io_destroy (omapi_object_t *, const char *, int); isc_result_t omapi_io_signal_handler (omapi_object_t *, const char *, va_list); isc_result_t omapi_io_stuff_values (omapi_object_t *, omapi_object_t *, omapi_object_t *); isc_result_t omapi_waiter_signal_handler (omapi_object_t *, const char *, va_list); isc_result_t omapi_io_state_foreach (isc_result_t (*func) (omapi_object_t *, void *), void *p); isc_result_t omapi_generic_new (omapi_object_t **, const char *, int); isc_result_t omapi_generic_set_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_typed_data_t *); isc_result_t omapi_generic_get_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_value_t **); isc_result_t omapi_generic_destroy (omapi_object_t *, const char *, int); isc_result_t omapi_generic_signal_handler (omapi_object_t *, const char *, va_list); isc_result_t omapi_generic_stuff_values (omapi_object_t *, omapi_object_t *, omapi_object_t *); isc_result_t omapi_generic_clear_flags (omapi_object_t *); isc_result_t omapi_message_new (omapi_object_t **, const char *, int); isc_result_t omapi_message_set_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_typed_data_t *); isc_result_t omapi_message_get_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_value_t **); isc_result_t omapi_message_destroy (omapi_object_t *, const char *, int); isc_result_t omapi_message_signal_handler (omapi_object_t *, const char *, va_list); isc_result_t omapi_message_stuff_values (omapi_object_t *, omapi_object_t *, omapi_object_t *); isc_result_t omapi_message_register (omapi_object_t *); isc_result_t omapi_message_unregister (omapi_object_t *); isc_result_t omapi_message_process (omapi_object_t *, omapi_object_t *); OMAPI_OBJECT_ALLOC_DECL (omapi_auth_key, omapi_auth_key_t, omapi_type_auth_key) isc_result_t omapi_auth_key_new (omapi_auth_key_t **, const char *, int); isc_result_t omapi_auth_key_destroy (omapi_object_t *, const char *, int); isc_result_t omapi_auth_key_enter (omapi_auth_key_t *); isc_result_t omapi_auth_key_lookup_name (omapi_auth_key_t **, const char *); isc_result_t omapi_auth_key_lookup (omapi_object_t **, omapi_object_t *, omapi_object_t *); isc_result_t omapi_auth_key_get_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_value_t **); isc_result_t omapi_auth_key_stuff_values (omapi_object_t *, omapi_object_t *, omapi_object_t *); extern omapi_object_type_t *omapi_type_connection; extern omapi_object_type_t *omapi_type_listener; extern omapi_object_type_t *omapi_type_io_object; extern omapi_object_type_t *omapi_type_generic; extern omapi_object_type_t *omapi_type_protocol; extern omapi_object_type_t *omapi_type_protocol_listener; extern omapi_object_type_t *omapi_type_waiter; extern omapi_object_type_t *omapi_type_remote; extern omapi_object_type_t *omapi_type_message; extern omapi_object_type_t *omapi_type_auth_key; extern omapi_object_type_t *omapi_object_types; void omapi_type_relinquish (void); isc_result_t omapi_init (void); isc_result_t omapi_object_type_register (omapi_object_type_t **, const char *, isc_result_t (*) (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_typed_data_t *), isc_result_t (*) (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_value_t **), isc_result_t (*) (omapi_object_t *, const char *, int), isc_result_t (*) (omapi_object_t *, const char *, va_list), isc_result_t (*) (omapi_object_t *, omapi_object_t *, omapi_object_t *), isc_result_t (*) (omapi_object_t **, omapi_object_t *, omapi_object_t *), isc_result_t (*) (omapi_object_t **, omapi_object_t *), isc_result_t (*) (omapi_object_t *, omapi_object_t *), isc_result_t (*) (omapi_object_t *, const char *, int), isc_result_t (*) (omapi_object_t **, const char *, int), isc_result_t (*) (size_t), size_t, isc_result_t (*) (omapi_object_t *, const char *, int), int); isc_result_t omapi_signal (omapi_object_t *, const char *, ...); isc_result_t omapi_signal_in (omapi_object_t *, const char *, ...); isc_result_t omapi_set_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_typed_data_t *); isc_result_t omapi_set_value_str (omapi_object_t *, omapi_object_t *, const char *, omapi_typed_data_t *); isc_result_t omapi_set_boolean_value (omapi_object_t *, omapi_object_t *, const char *, int); isc_result_t omapi_set_int_value (omapi_object_t *, omapi_object_t *, const char *, int); isc_result_t omapi_set_object_value (omapi_object_t *, omapi_object_t *, const char *, omapi_object_t *); isc_result_t omapi_set_string_value (omapi_object_t *, omapi_object_t *, const char *, const char *); isc_result_t omapi_get_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_value_t **); isc_result_t omapi_get_value_str (omapi_object_t *, omapi_object_t *, const char *, omapi_value_t **); isc_result_t omapi_stuff_values (omapi_object_t *, omapi_object_t *, omapi_object_t *); isc_result_t omapi_object_create (omapi_object_t **, omapi_object_t *, omapi_object_type_t *); isc_result_t omapi_object_update (omapi_object_t *, omapi_object_t *, omapi_object_t *, omapi_handle_t); int omapi_data_string_cmp (omapi_data_string_t *, omapi_data_string_t *); int omapi_ds_strcmp (omapi_data_string_t *, const char *); int omapi_td_strcmp (omapi_typed_data_t *, const char *); int omapi_td_strcasecmp (omapi_typed_data_t *, const char *); isc_result_t omapi_make_value (omapi_value_t **, omapi_data_string_t *, omapi_typed_data_t *, const char *, int); isc_result_t omapi_make_const_value (omapi_value_t **, omapi_data_string_t *, const unsigned char *, unsigned, const char *, int); isc_result_t omapi_make_int_value (omapi_value_t **, omapi_data_string_t *, int, const char *, int); isc_result_t omapi_make_uint_value (omapi_value_t **, omapi_data_string_t *, unsigned int, const char *, int); isc_result_t omapi_make_object_value (omapi_value_t **, omapi_data_string_t *, omapi_object_t *, const char *, int); isc_result_t omapi_make_handle_value (omapi_value_t **, omapi_data_string_t *, omapi_object_t *, const char *, int); isc_result_t omapi_make_string_value (omapi_value_t **, omapi_data_string_t *, const char *, const char *, int); isc_result_t omapi_get_int_value (unsigned long *, omapi_typed_data_t *); isc_result_t omapi_object_handle (omapi_handle_t *, omapi_object_t *); isc_result_t omapi_handle_lookup (omapi_object_t **, omapi_handle_t); isc_result_t omapi_handle_td_lookup (omapi_object_t **, omapi_typed_data_t *); void * dmalloc (size_t, const char *, int); void dfree (void *, const char *, int); #if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) || \ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) void dmalloc_reuse (void *, const char *, int, int); void dmalloc_dump_outstanding (void); #else #define dmalloc_reuse(x,y,l,z) #endif #define MDL __FILE__, __LINE__ #if defined (DEBUG_RC_HISTORY) void dump_rc_history (void *); void rc_history_next (int); #endif void omapi_print_dmalloc_usage_by_caller (void); isc_result_t omapi_object_allocate (omapi_object_t **, omapi_object_type_t *, size_t, const char *, int); isc_result_t omapi_object_initialize (omapi_object_t *, omapi_object_type_t *, size_t, size_t, const char *, int); isc_result_t omapi_object_reference (omapi_object_t **, omapi_object_t *, const char *, int); isc_result_t omapi_object_dereference (omapi_object_t **, const char *, int); isc_result_t omapi_typed_data_new (const char *, int, omapi_typed_data_t **, omapi_datatype_t, ...); isc_result_t omapi_typed_data_reference (omapi_typed_data_t **, omapi_typed_data_t *, const char *, int); isc_result_t omapi_typed_data_dereference (omapi_typed_data_t **, const char *, int); isc_result_t omapi_data_string_new (omapi_data_string_t **, unsigned, const char *, int); isc_result_t omapi_data_string_reference (omapi_data_string_t **, omapi_data_string_t *, const char *, int); isc_result_t omapi_data_string_dereference (omapi_data_string_t **, const char *, int); isc_result_t omapi_value_new (omapi_value_t **, const char *, int); isc_result_t omapi_value_reference (omapi_value_t **, omapi_value_t *, const char *, int); isc_result_t omapi_value_dereference (omapi_value_t **, const char *, int); isc_result_t omapi_addr_list_new (omapi_addr_list_t **, unsigned, const char *, int); isc_result_t omapi_addr_list_reference (omapi_addr_list_t **, omapi_addr_list_t *, const char *, int); isc_result_t omapi_addr_list_dereference (omapi_addr_list_t **, const char *, int); isc_result_t omapi_array_allocate (omapi_array_t **, omapi_array_ref_t, omapi_array_deref_t, const char *, int); isc_result_t omapi_array_free (omapi_array_t **, const char *, int); isc_result_t omapi_array_extend (omapi_array_t *, char *, int *, const char *, int); isc_result_t omapi_array_set (omapi_array_t *, void *, int, const char *, int); isc_result_t omapi_array_lookup (char **, omapi_array_t *, int, const char *, int); OMAPI_ARRAY_TYPE_DECL(omapi_object, omapi_object_t); #endif /* _OMAPIP_H_ */ dhcp-4.4.1/includes/omapip/omapip_p.h000644 000765 000024 00000021610 13243301226 017763 0ustar00tmarkstaff000000 000000 /* omapip_p.h Private master include file for the OMAPI library. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1996-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #ifndef __OMAPIP_OMAPIP_P_H__ #define __OMAPIP_OMAPIP_P_H__ #ifndef __CYGWIN32__ #include #include #include #include #include #include #else #define fd_set cygwin_fd_set #include #endif #include #include #include #include #include #include #include #include #include /* * XXX: I'm not sure why these were here. #include "cdefs.h" #include "osdep.h" */ #include #include "result.h" #include #include #include #include /* DST_API control flags */ /* These are used in functions dst_sign_data and dst_verify_data */ #define SIG_MODE_INIT 1 /* initalize digest */ #define SIG_MODE_UPDATE 2 /* add data to digest */ #define SIG_MODE_FINAL 4 /* generate/verify signature */ #define SIG_MODE_ALL (SIG_MODE_INIT|SIG_MODE_UPDATE|SIG_MODE_FINAL) /* OMAPI protocol header, version 1.00 */ typedef struct { u_int32_t authlen; /* Length of authenticator. */ u_int32_t authid; /* Authenticator object ID. */ u_int32_t op; /* Opcode. */ omapi_handle_t handle; /* Handle of object being operated on, or zero. */ u_int32_t id; /* Transaction ID. */ u_int32_t rid; /* ID of transaction to which this is a response. */ } omapi_protocol_header_t; #define OMAPI_PROTOCOL_VERSION 100 #define OMAPI_OP_OPEN 1 #define OMAPI_OP_REFRESH 2 #define OMAPI_OP_UPDATE 3 #define OMAPI_OP_NOTIFY 4 #define OMAPI_OP_STATUS 5 #define OMAPI_OP_DELETE 6 typedef enum { omapi_connection_unconnected, omapi_connection_connecting, omapi_connection_connected, omapi_connection_disconnecting, omapi_connection_closed } omapi_connection_state_t; typedef enum { omapi_protocol_intro_wait, omapi_protocol_header_wait, omapi_protocol_signature_wait, omapi_protocol_name_wait, omapi_protocol_name_length_wait, omapi_protocol_value_wait, omapi_protocol_value_length_wait } omapi_protocol_state_t; typedef struct __omapi_message_object { OMAPI_OBJECT_PREAMBLE; struct __omapi_message_object *next, *prev; omapi_object_t *object; omapi_object_t *notify_object; struct __omapi_protocol_object *protocol_object; u_int32_t authlen; omapi_typed_data_t *authenticator; u_int32_t authid; omapi_object_t *id_object; u_int32_t op; u_int32_t h; u_int32_t id; u_int32_t rid; } omapi_message_object_t; typedef struct __omapi_remote_auth { struct __omapi_remote_auth *next; omapi_handle_t remote_handle; omapi_object_t *a; } omapi_remote_auth_t; typedef struct __omapi_protocol_object { OMAPI_OBJECT_PREAMBLE; u_int32_t header_size; u_int32_t protocol_version; u_int32_t next_xid; omapi_protocol_state_t state; /* Input state. */ int reading_message_values; /* True if reading message-specific values. */ omapi_message_object_t *message; /* Incoming message. */ omapi_data_string_t *name; /* Incoming name. */ omapi_typed_data_t *value; /* Incoming value. */ isc_result_t verify_result; omapi_remote_auth_t *default_auth; /* Default authinfo to use. */ omapi_remote_auth_t *remote_auth_list; /* Authenticators active on this connection. */ isc_boolean_t insecure; /* Set to allow unauthenticated messages. */ isc_result_t (*verify_auth) (omapi_object_t *, omapi_auth_key_t *); } omapi_protocol_object_t; typedef struct { OMAPI_OBJECT_PREAMBLE; isc_boolean_t insecure; /* Set to allow unauthenticated messages. */ isc_result_t (*verify_auth) (omapi_object_t *, omapi_auth_key_t *); } omapi_protocol_listener_object_t; #include typedef struct __omapi_listener_object { OMAPI_OBJECT_PREAMBLE; int socket; /* Connection socket. */ int index; struct sockaddr_in address; isc_result_t (*verify_addr) (omapi_object_t *, omapi_addr_t *); } omapi_listener_object_t; typedef struct __omapi_connection_object { OMAPI_OBJECT_PREAMBLE; int socket; /* Connection socket. */ int32_t index; omapi_connection_state_t state; struct sockaddr_in remote_addr; struct sockaddr_in local_addr; omapi_addr_list_t *connect_list; /* List of addresses to which to connect. */ int cptr; /* Current element we are connecting to. */ u_int32_t bytes_needed; /* Bytes of input needed before wakeup. */ u_int32_t in_bytes; /* Bytes of input already buffered. */ omapi_buffer_t *inbufs; u_int32_t out_bytes; /* Bytes of output in buffers. */ omapi_buffer_t *outbufs; omapi_listener_object_t *listener; /* Listener that accepted this connection, if any. */ dst_key_t *in_key; /* Authenticator signing incoming data. */ void *in_context; /* Input hash context. */ dst_key_t *out_key; /* Authenticator signing outgoing data. */ void *out_context; /* Output hash context. */ } omapi_connection_object_t; typedef struct __omapi_io_object { OMAPI_OBJECT_PREAMBLE; struct __omapi_io_object *next; int (*readfd) (omapi_object_t *); int (*writefd) (omapi_object_t *); isc_result_t (*reader) (omapi_object_t *); isc_result_t (*writer) (omapi_object_t *); isc_result_t (*reaper) (omapi_object_t *); isc_socket_t *fd; isc_boolean_t closed; /* ISC_TRUE = closed, do not use */ } omapi_io_object_t; typedef struct __omapi_generic_object { OMAPI_OBJECT_PREAMBLE; omapi_value_t **values; u_int8_t *changed; int nvalues, va_max; } omapi_generic_object_t; typedef struct __omapi_waiter_object { OMAPI_OBJECT_PREAMBLE; int ready; isc_result_t waitstatus; struct __omapi_waiter_object *next; } omapi_waiter_object_t; #define OMAPI_HANDLE_TABLE_SIZE 120 typedef struct __omapi_handle_table { omapi_handle_t first, limit; omapi_handle_t next; int leafp; union { omapi_object_t *object; struct __omapi_handle_table *table; } children [OMAPI_HANDLE_TABLE_SIZE]; } omapi_handle_table_t; #include OMAPI_OBJECT_ALLOC_DECL (omapi_protocol, omapi_protocol_object_t, omapi_type_protocol) OMAPI_OBJECT_ALLOC_DECL (omapi_protocol_listener, omapi_protocol_listener_object_t, omapi_type_protocol_listener) OMAPI_OBJECT_ALLOC_DECL (omapi_connection, omapi_connection_object_t, omapi_type_connection) OMAPI_OBJECT_ALLOC_DECL (omapi_listener, omapi_listener_object_t, omapi_type_listener) OMAPI_OBJECT_ALLOC_DECL (omapi_io, omapi_io_object_t, omapi_type_io_object) OMAPI_OBJECT_ALLOC_DECL (omapi_waiter, omapi_waiter_object_t, omapi_type_waiter) OMAPI_OBJECT_ALLOC_DECL (omapi_generic, omapi_generic_object_t, omapi_type_generic) OMAPI_OBJECT_ALLOC_DECL (omapi_message, omapi_message_object_t, omapi_type_message) isc_result_t omapi_connection_sign_data (int mode, dst_key_t *key, void **context, const unsigned char *data, const unsigned len, omapi_typed_data_t **result); isc_result_t omapi_listener_connect (omapi_connection_object_t **obj, omapi_listener_object_t *listener, int socket, struct sockaddr_in *remote_addr); void omapi_listener_trace_setup (void); void omapi_connection_trace_setup (void); void omapi_buffer_trace_setup (void); void omapi_connection_register (omapi_connection_object_t *, const char *, int); OMAPI_ARRAY_TYPE_DECL(omapi_listener, omapi_listener_object_t); OMAPI_ARRAY_TYPE_DECL(omapi_connection, omapi_connection_object_t); isc_result_t omapi_handle_clear(omapi_handle_t); extern int log_perror; extern void (*log_cleanup) (void); void log_fatal (const char *, ...) __attribute__((__format__(__printf__,1,2))) ISC_DHCP_NORETURN; int log_error (const char *, ...) __attribute__((__format__(__printf__,1,2))); int log_info (const char *, ...) __attribute__((__format__(__printf__,1,2))); int log_debug (const char *, ...) __attribute__((__format__(__printf__,1,2))); void do_percentm (char *obuf, const char *ibuf); isc_result_t uerr2isc (int); isc_result_t ns_rcode_to_isc (int); extern omapi_message_object_t *omapi_registered_messages; #endif /* __OMAPIP_OMAPIP_P_H__ */ dhcp-4.4.1/includes/omapip/result.h000644 000765 000024 00000011100 13243301226 017466 0ustar00tmarkstaff000000 000000 /* result.h */ /* * Copyright (C) 2009-2017 Internet Systems Consortium, Inc. ("ISC") * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ */ #ifndef DHCP_RESULT_H #define DHCP_RESULT_H 1 #include #include #include #include /* * DHCP result codes */ /* * In the previous code the results started at 36 * rather than ISC_RESULTCLASS_DHCP + 0 * ISC_R_NOTCONNECTED was + 4 (40), it has been superseded by the isc version */ #define DHCP_R_HOSTUNKNOWN (ISC_RESULTCLASS_DHCP + 0) #define DHCP_R_VERSIONMISMATCH (ISC_RESULTCLASS_DHCP + 1) #define DHCP_R_PROTOCOLERROR (ISC_RESULTCLASS_DHCP + 2) #define DHCP_R_INVALIDARG (ISC_RESULTCLASS_DHCP + 3) #define DHCP_R_NOTYET (ISC_RESULTCLASS_DHCP + 4) #define DHCP_R_UNCHANGED (ISC_RESULTCLASS_DHCP + 5) #define DHCP_R_MULTIPLE (ISC_RESULTCLASS_DHCP + 6) #define DHCP_R_KEYCONFLICT (ISC_RESULTCLASS_DHCP + 7) #define DHCP_R_BADPARSE (ISC_RESULTCLASS_DHCP + 8) #define DHCP_R_NOKEYS (ISC_RESULTCLASS_DHCP + 9) #define DHCP_R_KEY_UNKNOWN (ISC_RESULTCLASS_DHCP + 10) #define DHCP_R_INVALIDKEY (ISC_RESULTCLASS_DHCP + 11) #define DHCP_R_INCOMPLETE (ISC_RESULTCLASS_DHCP + 12) #define DHCP_R_FORMERR (ISC_RESULTCLASS_DHCP + 13) #define DHCP_R_SERVFAIL (ISC_RESULTCLASS_DHCP + 14) #define DHCP_R_NXDOMAIN (ISC_RESULTCLASS_DHCP + 15) #define DHCP_R_NOTIMPL (ISC_RESULTCLASS_DHCP + 16) #define DHCP_R_REFUSED (ISC_RESULTCLASS_DHCP + 17) #define DHCP_R_YXDOMAIN (ISC_RESULTCLASS_DHCP + 18) #define DHCP_R_YXRRSET (ISC_RESULTCLASS_DHCP + 19) #define DHCP_R_NXRRSET (ISC_RESULTCLASS_DHCP + 20) #define DHCP_R_NOTAUTH (ISC_RESULTCLASS_DHCP + 21) #define DHCP_R_NOTZONE (ISC_RESULTCLASS_DHCP + 22) #define DHCP_R_BADSIG (ISC_RESULTCLASS_DHCP + 23) #define DHCP_R_BADKEY (ISC_RESULTCLASS_DHCP + 24) #define DHCP_R_BADTIME (ISC_RESULTCLASS_DHCP + 25) #define DHCP_R_NOROOTZONE (ISC_RESULTCLASS_DHCP + 26) #define DHCP_R_DESTADDRREQ (ISC_RESULTCLASS_DHCP + 27) #define DHCP_R_CROSSZONE (ISC_RESULTCLASS_DHCP + 28) #define DHCP_R_NO_TSIG (ISC_RESULTCLASS_DHCP + 29) #define DHCP_R_NOT_EQUAL (ISC_RESULTCLASS_DHCP + 30) #define DHCP_R_CONNRESET (ISC_RESULTCLASS_DHCP + 31) #define DHCP_R_UNKNOWNATTRIBUTE (ISC_RESULTCLASS_DHCP + 32) #define DHCP_R_NRESULTS 33 /*%< Number of results */ // Included for historical reasons, these should be removed as // soon as reasonable #ifdef INCLUDE_OLD_DHCP_ISC_ERROR_CODES #define ISC_R_HOSTUNKNOWN DHCP_R_HOSTUNKNOWN #define ISC_R_VERSIONMISMATCH DHCP_R_VERSIONMISMATCH #define ISC_R_PROTOCOLERROR DHCP_R_PROTOCOLERROR #define ISC_R_INVALIDARG DHCP_R_INVALIDARG #define ISC_R_NOTYET DHCP_R_NOTYET #define ISC_R_UNCHANGED DHCP_R_UNCHANGED #define ISC_R_KEYCONFLICT DHCP_R_KEYCONFLICT #define ISC_R_BADPARSE DHCP_R_BADPARSE #define ISC_R_NOKEYS DHCP_R_NOKEYS #define ISC_R_KEY_UNKNOWN DHCP_R_KEY_UNKNOWN #define ISC_R_INVALIDKEY DHCP_R_INVALIDKEY #define ISC_R_INCOMPLETE DHCP_R_INCOMPLETE #define ISC_R_FORMERR DHCP_R_FORMERR #define ISC_R_SERVFAIL DHCP_R_SERVFAIL #define ISC_R_NXDOMAIN DHCP_R_NXDOMAIN #define ISC_R_NOTIMPL DHCP_R_NOTIMPL #define ISC_R_REFUSED DHCP_R_REFUSED #define ISC_R_YXDOMAIN DHCP_R_YXDOMAIN #define ISC_R_YXRRSET DHCP_R_YXRRSET #define ISC_R_NXRRSET DHCP_R_NXRRSET #define ISC_R_NOTAUTH DHCP_R_NOTAUTH #define ISC_R_NOTZONE DHCP_R_NOTZONE #define ISC_R_BADSIG DHCP_R_BADSIG #define ISC_R_BADKEY DHCP_R_BADKEY #define ISC_R_BADTIME DHCP_R_BADTIME #define ISC_R_NOROOTZONE DHCP_R_NOROOTZONE #define ISC_R_DESTADDRREQ DHCP_R_DESTADDRREQ #define ISC_R_CROSSZONE DHCP_R_CROSSZONE #define ISC_R_NO_TSIG DHCP_R_NO_TSIG #define ISC_R_NOT_EQUAL DHCP_R_NOT_EQUAL #define ISC_R_CONNRESET DHCP_R_CONNRESET #define ISC_R_UNKNOWNATTRIBUTE DHCP_R_UNKNOWNATTRIBUTE #endif isc_result_t dhcp_result_register(void); #endif /* DHCP_RESULT_H */ dhcp-4.4.1/includes/omapip/trace.h000644 000765 000024 00000007561 13243301226 017266 0ustar00tmarkstaff000000 000000 /* trace.h Definitions for omapi tracing facility... */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 2001-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #define TRACEFILE_MAGIC 0x64484370UL /* dHCp */ #define TRACEFILE_VERSION 1 /* The first thing in a trace file is the header, which basically just defines the version of the file. */ typedef struct { u_int32_t magic; /* Magic number for trace file. */ u_int32_t version; /* Version of file. */ int32_t hlen; /* Length of this header. */ int32_t phlen; /* Length of packet headers. */ } tracefile_header_t; /* The trace file is composed of a bunch of trace packets. Each such packet has a type, followed by a length, followed by a timestamp, followed by the actual contents of the packet. The type indexes are not fixed - they are allocated either on readback or when writing a trace file. One index type is reserved - type zero means that this record is a type name to index mapping. */ typedef struct { u_int32_t type_index; /* Index to the type of handler that this packet needs. */ u_int32_t length; /* Length of the packet. This includes everything except the fixed header. */ u_int32_t when; /* When the packet was written. */ u_int32_t pad; /* Round this out to a quad boundary. */ } tracepacket_t; #define TRACE_INDEX_MAPPING_SIZE 4 /* trace_index_mapping_t less name. */ typedef struct { u_int32_t index; char name [1]; } trace_index_mapping_t; struct trace_type; /* forward */ typedef struct trace_type trace_type_t; struct trace_type { trace_type_t *next; int index; char *name; void *baggage; void (*have_packet) (trace_type_t *, unsigned, char *); void (*stop_tracing) (trace_type_t *); }; typedef struct trace_iov { const char *buf; unsigned len; } trace_iov_t; typedef struct { u_int16_t addrtype; u_int16_t addrlen; u_int8_t address [16]; u_int16_t port; } trace_addr_t; void trace_free_all (void); int trace_playback (void); int trace_record (void); isc_result_t trace_init(void (*set_time)(time_t), const char *, int); isc_result_t trace_begin (const char *, const char *, int); isc_result_t trace_write_packet (trace_type_t *, unsigned, const char *, const char *, int); isc_result_t trace_write_packet_iov (trace_type_t *, int, trace_iov_t *, const char *, int); void trace_type_stash (trace_type_t *); trace_type_t *trace_type_register (const char *, void *, void (*) (trace_type_t *, unsigned, char *), void (*) (trace_type_t *), const char *, int); void trace_stop (void); void trace_index_map_input (trace_type_t *, unsigned, char *); void trace_index_stop_tracing (trace_type_t *); void trace_replay_init (void); void trace_file_replay (const char *); isc_result_t trace_get_next_packet (trace_type_t **, tracepacket_t *, char **, unsigned *, unsigned *); isc_result_t trace_get_file (trace_type_t *, const char *, unsigned *, char **); isc_result_t trace_get_packet (trace_type_t **, unsigned *, char **); time_t trace_snoop_time (trace_type_t **); dhcp-4.4.1/includes/netinet/if_ether.h000644 000765 000024 00000004544 13243301226 020134 0ustar00tmarkstaff000000 000000 /* $NetBSD: if_ether.h,v 1.20 1995/06/12 00:47:27 mycroft Exp $ */ /* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)if_ether.h 8.1 (Berkeley) 6/10/93 */ #ifndef netinet_if_ether_h #define netinet_if_ether_h /* * Ethernet address - 6 octets * this is only used by the ethers(3) functions. */ struct ether_addr { u_int8_t ether_addr_octet[6]; }; /* * Structure of a 10Mb/s Ethernet header. */ #define ETHER_ADDR_LEN 6 struct isc_ether_header { u_int8_t ether_dhost[ETHER_ADDR_LEN]; u_int8_t ether_shost[ETHER_ADDR_LEN]; u_int16_t ether_type; }; #define ETHERTYPE_PUP 0x0200 /* PUP protocol */ #define ETHERTYPE_IP 0x0800 /* IP protocol */ #define ETHERTYPE_ARP 0x0806 /* address resolution protocol */ #define ETHER_HEADER_SIZE (ETHER_ADDR_LEN * 2 + sizeof (u_int16_t)) #endif dhcp-4.4.1/includes/netinet/ip.h000644 000765 000024 00000012735 13243301226 016760 0ustar00tmarkstaff000000 000000 /* $NetBSD: ip.h,v 1.9 1995/05/15 01:22:44 cgd Exp $ */ /* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)ip.h 8.1 (Berkeley) 6/10/93 */ /* * Definitions for internet protocol version 4. * Per RFC 791, September 1981. */ #define IPVERSION 4 /* * Structure of an internet header, naked of options. * * We declare ip_len and ip_off to be short, rather than u_short * pragmatically since otherwise unsigned comparisons can result * against negative integers quite easily, and fail in subtle ways. */ struct ip { u_int8_t ip_fvhl; /* header length, version */ u_int8_t ip_tos; /* type of service */ int16_t ip_len; /* total length */ u_int16_t ip_id; /* identification */ int16_t ip_off; /* fragment offset field */ #define IP_DF 0x4000 /* dont fragment flag */ #define IP_MF 0x2000 /* more fragments flag */ #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ u_int8_t ip_ttl; /* time to live */ u_int8_t ip_p; /* protocol */ u_int16_t ip_sum; /* checksum */ struct in_addr ip_src, ip_dst; /* source and dest address */ }; #define IP_V(iph) ((iph)->ip_fvhl >> 4) #define IP_HL(iph) (((iph)->ip_fvhl & 0x0F) << 2) #define IP_V_SET(iph,x) ((iph)->ip_fvhl = ((iph)->ip_fvhl & 0x0F) | ((x) << 4)) #define IP_HL_SET(iph,x) ((iph)->ip_fvhl = \ ((iph)->ip_fvhl & 0xF0) | (((x) >> 2) & 0x0F)) #define IP_MAXPACKET 65535 /* maximum packet size */ /* * Definitions for IP type of service (ip_tos) */ #define IPTOS_LOWDELAY 0x10 #define IPTOS_THROUGHPUT 0x08 #define IPTOS_RELIABILITY 0x04 /* IPTOS_LOWCOST 0x02 XXX */ /* * Definitions for IP precedence (also in ip_tos) (hopefully unused) */ #define IPTOS_PREC_NETCONTROL 0xe0 #define IPTOS_PREC_INTERNETCONTROL 0xc0 #define IPTOS_PREC_CRITIC_ECP 0xa0 #define IPTOS_PREC_FLASHOVERRIDE 0x80 #define IPTOS_PREC_FLASH 0x60 #define IPTOS_PREC_IMMEDIATE 0x40 #define IPTOS_PREC_PRIORITY 0x20 #define IPTOS_PREC_ROUTINE 0x00 /* * Definitions for options. */ #define IPOPT_COPIED(o) ((o)&0x80) #define IPOPT_CLASS(o) ((o)&0x60) #define IPOPT_NUMBER(o) ((o)&0x1f) #define IPOPT_CONTROL 0x00 #define IPOPT_RESERVED1 0x20 #define IPOPT_DEBMEAS 0x40 #define IPOPT_RESERVED2 0x60 #define IPOPT_EOL 0 /* end of option list */ #define IPOPT_NOP 1 /* no operation */ #define IPOPT_RR 7 /* record packet route */ #define IPOPT_TS 68 /* timestamp */ #define IPOPT_SECURITY 130 /* provide s,c,h,tcc */ #define IPOPT_LSRR 131 /* loose source route */ #define IPOPT_SATID 136 /* satnet id */ #define IPOPT_SSRR 137 /* strict source route */ /* * Offsets to fields in options other than EOL and NOP. */ #define IPOPT_OPTVAL 0 /* option ID */ #define IPOPT_OLEN 1 /* option length */ #define IPOPT_OFFSET 2 /* offset within option */ #define IPOPT_MINOFF 4 /* min value of above */ /* * Time stamp option structure. */ struct ip_timestamp { u_int8_t ipt_code; /* IPOPT_TS */ u_int8_t ipt_len; /* size of structure (variable) */ u_int8_t ipt_ptr; /* index of current entry */ u_int8_t ipt_flg_oflw; /* flags, see below, overflow counter */ union ipt_timestamp { u_int32_t ipt_time[1]; struct ipt_ta { struct in_addr ipt_addr; u_int32_t ipt_time; } ipt_ta[1]; } ipt_timestamp; }; /* flag bits for ipt_flg */ #define IPOPT_TS_TSONLY 0 /* timestamps only */ #define IPOPT_TS_TSANDADDR 1 /* timestamps and addresses */ #define IPOPT_TS_PRESPEC 3 /* specified modules only */ /* bits for security (not byte swapped) */ #define IPOPT_SECUR_UNCLASS 0x0000 #define IPOPT_SECUR_CONFID 0xf135 #define IPOPT_SECUR_EFTO 0x789a #define IPOPT_SECUR_MMMM 0xbc4d #define IPOPT_SECUR_RESTR 0xaf13 #define IPOPT_SECUR_SECRET 0xd788 #define IPOPT_SECUR_TOPSECRET 0x6bc5 /* * Internet implementation parameters. */ #define MAXTTL 255 /* maximum time to live (seconds) */ #define IPDEFTTL 64 /* default ttl, from RFC 1340 */ #define IPFRAGTTL 60 /* time to live for frags, slowhz */ #define IPTTLDEC 1 /* subtracted when forwarding */ #define IP_MSS 576 /* default maximum segment size */ dhcp-4.4.1/includes/netinet/ip_icmp.h000644 000765 000024 00000015316 13243301226 017766 0ustar00tmarkstaff000000 000000 /* $NetBSD: ip_icmp.h,v 1.11 1996/08/03 15:48:18 neil Exp $ */ /* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)ip_icmp.h 8.1 (Berkeley) 6/10/93 */ /* * Interface Control Message Protocol Definitions. * Per RFC 792, September 1981. */ /* * Internal of an ICMP Router Advertisement */ struct icmp_ra_addr { u_int32_t ira_addr; u_int32_t ira_preference; }; /* * Structure of an icmp header. */ struct icmp { u_int8_t icmp_type; /* type of message, see below */ u_int8_t icmp_code; /* type sub code */ u_int16_t icmp_cksum; /* ones complement cksum of struct */ union { u_int8_t ih_pptr; /* ICMP_PARAMPROB */ struct in_addr ih_gwaddr; /* ICMP_REDIRECT */ struct ih_idseq { int16_t icd_id; int16_t icd_seq; } ih_idseq; int32_t ih_void; /* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */ struct ih_pmtu { int16_t ipm_void; int16_t ipm_nextmtu; } ih_pmtu; struct ih_rtradv { u_int8_t irt_num_addrs; u_int8_t irt_wpa; u_int16_t irt_lifetime; } ih_rtradv; } icmp_hun; #define icmp_pptr icmp_hun.ih_pptr #define icmp_gwaddr icmp_hun.ih_gwaddr #define icmp_id icmp_hun.ih_idseq.icd_id #define icmp_seq icmp_hun.ih_idseq.icd_seq #define icmp_void icmp_hun.ih_void #define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void #define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu #define icmp_num_addrs icmp_hun.ih_rtradv.irt_num_addrs #define icmp_wpa icmp_hun.ih_rtradv.irt_wpa #define icmp_lifetime icmp_hun.ih_rtradv.irt_lifetime union { struct id_ts { u_int32_t its_otime; u_int32_t its_rtime; u_int32_t its_ttime; } id_ts; struct id_ip { struct ip idi_ip; /* options and then 64 bits of data */ } id_ip; struct icmp_ra_addr id_radv; u_int32_t id_mask; int8_t id_data[1]; } icmp_dun; #define icmp_otime icmp_dun.id_ts.its_otime #define icmp_rtime icmp_dun.id_ts.its_rtime #define icmp_ttime icmp_dun.id_ts.its_ttime #define icmp_ip icmp_dun.id_ip.idi_ip #define icmp_radv icmp_dun.id_mask #define icmp_mask icmp_dun.id_mask #define icmp_data icmp_dun.id_data }; /* * Lower bounds on packet lengths for various types. * For the error advice packets must first insure that the * packet is large enought to contain the returned ip header. * Only then can we do the check to see if 64 bits of packet * data have been returned, since we need to check the returned * ip header length. */ #define ICMP_MINLEN 8 /* abs minimum */ #define ICMP_TSLEN (8 + 3 * sizeof (u_int32_t)) /* timestamp */ #define ICMP_MASKLEN 12 /* address mask */ #define ICMP_ADVLENMIN (8 + sizeof (struct ip) + 8) /* min */ #define ICMP_ADVLEN(p) (8 + ((p)->icmp_ip.ip_hl << 2) + 8) /* N.B.: must separately check that ip_hl >= 5 */ /* * Definition of type and code field values. */ #define ICMP_ECHOREPLY 0 /* echo reply */ #define ICMP_UNREACH 3 /* dest unreachable, codes: */ #define ICMP_UNREACH_NET 0 /* bad net */ #define ICMP_UNREACH_HOST 1 /* bad host */ #define ICMP_UNREACH_PROTOCOL 2 /* bad protocol */ #define ICMP_UNREACH_PORT 3 /* bad port */ #define ICMP_UNREACH_NEEDFRAG 4 /* IP_DF caused drop */ #define ICMP_UNREACH_SRCFAIL 5 /* src route failed */ #define ICMP_UNREACH_NET_UNKNOWN 6 /* unknown net */ #define ICMP_UNREACH_HOST_UNKNOWN 7 /* unknown host */ #define ICMP_UNREACH_ISOLATED 8 /* src host isolated */ #define ICMP_UNREACH_NET_PROHIB 9 /* prohibited access */ #define ICMP_UNREACH_HOST_PROHIB 10 /* ditto */ #define ICMP_UNREACH_TOSNET 11 /* bad tos for net */ #define ICMP_UNREACH_TOSHOST 12 /* bad tos for host */ #define ICMP_SOURCEQUENCH 4 /* packet lost, slow down */ #define ICMP_REDIRECT 5 /* shorter route, codes: */ #define ICMP_REDIRECT_NET 0 /* for network */ #define ICMP_REDIRECT_HOST 1 /* for host */ #define ICMP_REDIRECT_TOSNET 2 /* for tos and net */ #define ICMP_REDIRECT_TOSHOST 3 /* for tos and host */ #define ICMP_ECHO 8 /* echo service */ #define ICMP_ROUTERADVERT 9 /* router advertisement */ #define ICMP_ROUTERSOLICIT 10 /* router solicitation */ #define ICMP_TIMXCEED 11 /* time exceeded, code: */ #define ICMP_TIMXCEED_INTRANS 0 /* ttl==0 in transit */ #define ICMP_TIMXCEED_REASS 1 /* ttl==0 in reass */ #define ICMP_PARAMPROB 12 /* ip header bad */ #define ICMP_PARAMPROB_OPTABSENT 1 /* req. opt. absent */ #define ICMP_TSTAMP 13 /* timestamp request */ #define ICMP_TSTAMPREPLY 14 /* timestamp reply */ #define ICMP_IREQ 15 /* information request */ #define ICMP_IREQREPLY 16 /* information reply */ #define ICMP_MASKREQ 17 /* address mask request */ #define ICMP_MASKREPLY 18 /* address mask reply */ #define ICMP_MAXTYPE 18 #define ICMP_INFOTYPE(type) \ ((type) == ICMP_ECHOREPLY || (type) == ICMP_ECHO || \ (type) == ICMP_ROUTERADVERT || (type) == ICMP_ROUTERSOLICIT || \ (type) == ICMP_TSTAMP || (type) == ICMP_TSTAMPREPLY || \ (type) == ICMP_IREQ || (type) == ICMP_IREQREPLY || \ (type) == ICMP_MASKREQ || (type) == ICMP_MASKREPLY) #ifdef _KERNEL void icmp_error __P((struct mbuf *, int, int, n_long, struct ifnet *)); void icmp_input __P((struct mbuf *, ...)); void icmp_reflect __P((struct mbuf *)); void icmp_send __P((struct mbuf *, struct mbuf *)); int icmp_sysctl __P((int *, u_int, void *, size_t *, void *, size_t)); #endif dhcp-4.4.1/includes/netinet/udp.h000644 000765 000024 00000005614 13243301226 017136 0ustar00tmarkstaff000000 000000 /* $NetBSD: udp.h,v 1.6 1995/04/13 06:37:10 cgd Exp $ */ /* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)udp.h 8.1 (Berkeley) 6/10/93 */ /* * Portions Copyright (c) 2004,2009 by Internet Systems Consortium, Inc. ("ISC") * Portions Copyright (c) 2000-2003 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ */ /* * Udp protocol header. * Per RFC 768, September, 1981. */ struct udphdr { u_int16_t uh_sport; /* source port */ u_int16_t uh_dport; /* destination port */ u_int16_t uh_ulen; /* udp length */ u_int16_t uh_sum; /* udp checksum */ }; dhcp-4.4.1/includes/arpa/nameser.h000644 000765 000024 00000040205 13243301226 017250 0ustar00tmarkstaff000000 000000 /* * Copyright (c) 1983, 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Copyright (c) 2004,2009 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1996-2003 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ */ /* * $Id: nameser.h,v 1.7 2009/11/24 02:06:56 sar Exp $ */ #ifndef _ARPA_NAMESER_H_ #define _ARPA_NAMESER_H_ /* * Revision information. This is the release date in YYYYMMDD format. * It can change every day so the right thing to do with it is use it * in preprocessor commands such as "#if (__NAMESER > 19931104)". Do not * compare for equality; rather, use it to determine whether your libbind.a * contains a new enough lib/nameser/ to support the feature you need. */ #define __NAMESER 19991006 /* New interface version stamp. */ /* * Define constants based on RFC 883, RFC 1034, RFC 1035 */ #define NS_PACKETSZ 512 /* maximum packet size */ #define NS_MAXDNAME 1025 /* maximum domain name */ #define NS_MAXCDNAME 255 /* maximum compressed domain name */ #define NS_MAXLABEL 63 /* maximum length of domain label */ #define NS_HFIXEDSZ 12 /* #/bytes of fixed data in header */ #define NS_QFIXEDSZ 4 /* #/bytes of fixed data in query */ #define NS_RRFIXEDSZ 10 /* #/bytes of fixed data in r record */ #define NS_INT32SZ 4 /* #/bytes of data in a u_int32_t */ #define NS_INT16SZ 2 /* #/bytes of data in a u_int16_t */ #define NS_INT8SZ 1 /* #/bytes of data in a u_int8_t */ #define NS_INADDRSZ 4 /* IPv4 T_A */ #define NS_IN6ADDRSZ 16 /* IPv6 T_AAAA */ #define NS_CMPRSFLGS 0xc0 /* Flag bits indicating name compression. */ #define NS_DEFAULTPORT 53 /* For both TCP and UDP. */ /* * These can be expanded with synonyms, just keep ns_parse.c:ns_parserecord() * in synch with it. */ typedef enum __ns_sect { ns_s_qd = 0, /* Query: Question. */ ns_s_zn = 0, /* Update: Zone. */ ns_s_an = 1, /* Query: Answer. */ ns_s_pr = 1, /* Update: Prerequisites. */ ns_s_ns = 2, /* Query: Name servers. */ ns_s_ud = 2, /* Update: Update. */ ns_s_ar = 3, /* Query|Update: Additional records. */ ns_s_max = 4 } ns_sect; /* * This is a message handle. It is caller allocated and has no dynamic data. * This structure is intended to be opaque to all but ns_parse.c, thus the * leading _'s on the member names. Use the accessor functions, not the _'s. */ typedef struct __ns_msg { const u_int8_t *_msg, *_eom; u_int16_t _id, _flags, _counts[ns_s_max]; const u_int8_t *_sections[ns_s_max]; ns_sect _sect; int _rrnum; const u_int8_t *_ptr; } ns_msg; /* Private data structure - do not use from outside library. */ struct _ns_flagdata { int mask, shift; }; extern struct _ns_flagdata _ns_flagdata[]; /* Accessor macros - this is part of the public interface. */ #define ns_msg_getflag(handle, flag) ( \ ((handle)._flags & _ns_flagdata[flag].mask) \ >> _ns_flagdata[flag].shift \ ) #define ns_msg_id(handle) ((handle)._id + 0) #define ns_msg_base(handle) ((handle)._msg + 0) #define ns_msg_end(handle) ((handle)._eom + 0) #define ns_msg_size(handle) ((handle)._eom - (handle)._msg) #define ns_msg_count(handle, section) ((handle)._counts[section] + 0) /* * This is a parsed record. It is caller allocated and has no dynamic data. */ typedef struct __ns_rr { char name[NS_MAXDNAME]; u_int16_t type; u_int16_t rr_class; u_int32_t ttl; u_int16_t rdlength; const u_int8_t *rdata; } ns_rr; /* Accessor macros - this is part of the public interface. */ #define ns_rr_name(rr) (((rr).name[0] != '\0') ? (rr).name : ".") #define ns_rr_type(rr) ((ns_type)((rr).type + 0)) #define ns_rr_class(rr) ((ns_class)((rr).rr_class + 0)) #define ns_rr_ttl(rr) ((rr).ttl + 0) #define ns_rr_rdlen(rr) ((rr).rdlength + 0) #define ns_rr_rdata(rr) ((rr).rdata + 0) /* * These don't have to be in the same order as in the packet flags word, * and they can even overlap in some cases, but they will need to be kept * in synch with ns_parse.c:ns_flagdata[]. */ typedef enum __ns_flag { ns_f_qr, /* Question/Response. */ ns_f_opcode, /* Operation code. */ ns_f_aa, /* Authoritative Answer. */ ns_f_tc, /* Truncation occurred. */ ns_f_rd, /* Recursion Desired. */ ns_f_ra, /* Recursion Available. */ ns_f_z, /* MBZ. */ ns_f_ad, /* Authentic Data (DNSSEC). */ ns_f_cd, /* Checking Disabled (DNSSEC). */ ns_f_rcode, /* Response code. */ ns_f_max } ns_flag; /* * Currently defined opcodes. */ typedef enum __ns_opcode { ns_o_query = 0, /* Standard query. */ ns_o_iquery = 1, /* Inverse query (deprecated/unsupported). */ ns_o_status = 2, /* Name server status query (unsupported). */ /* Opcode 3 is undefined/reserved. */ ns_o_notify = 4, /* Zone change notification. */ ns_o_update = 5, /* Zone update message. */ ns_o_max = 6 } ns_opcode; /* * Currently defined response codes. */ typedef enum __ns_rcode { ns_r_noerror = 0, /* No error occurred. */ ns_r_formerr = 1, /* Format error. */ ns_r_servfail = 2, /* Server failure. */ ns_r_nxdomain = 3, /* Name error. */ ns_r_notimpl = 4, /* Unimplemented. */ ns_r_refused = 5, /* Operation refused. */ /* these are for BIND_UPDATE */ ns_r_yxdomain = 6, /* Name exists */ ns_r_yxrrset = 7, /* RRset exists */ ns_r_nxrrset = 8, /* RRset does not exist */ ns_r_notauth = 9, /* Not authoritative for zone */ ns_r_notzone = 10, /* Zone of record different from zone section */ ns_r_max = 11, /* The following are TSIG extended errors */ ns_r_badsig = 16, ns_r_badkey = 17, ns_r_badtime = 18 } ns_rcode; /* BIND_UPDATE */ typedef enum __ns_update_operation { ns_uop_delete = 0, ns_uop_add = 1, ns_uop_max = 2 } ns_update_operation; #define NS_TSIG_ALG_HMAC_MD5 "HMAC-MD5.SIG-ALG.REG.INT" /* * Currently defined type values for resources and queries. */ typedef enum __ns_type { ns_t_invalid = 0, /* Cookie. */ ns_t_a = 1, /* Host address. */ ns_t_ns = 2, /* Authoritative server. */ ns_t_md = 3, /* Mail destination. */ ns_t_mf = 4, /* Mail forwarder. */ ns_t_cname = 5, /* Canonical name. */ ns_t_soa = 6, /* Start of authority zone. */ ns_t_mb = 7, /* Mailbox domain name. */ ns_t_mg = 8, /* Mail group member. */ ns_t_mr = 9, /* Mail rename name. */ ns_t_null = 10, /* Null resource record. */ ns_t_wks = 11, /* Well known service. */ ns_t_ptr = 12, /* Domain name pointer. */ ns_t_hinfo = 13, /* Host information. */ ns_t_minfo = 14, /* Mailbox information. */ ns_t_mx = 15, /* Mail routing information. */ ns_t_txt = 16, /* Text strings. */ ns_t_rp = 17, /* Responsible person. */ ns_t_afsdb = 18, /* AFS cell database. */ ns_t_x25 = 19, /* X_25 calling address. */ ns_t_isdn = 20, /* ISDN calling address. */ ns_t_rt = 21, /* Router. */ ns_t_nsap = 22, /* NSAP address. */ ns_t_nsap_ptr = 23, /* Reverse NSAP lookup (deprecated). */ ns_t_sig = 24, /* Security signature. */ ns_t_key = 25, /* Security key. */ ns_t_px = 26, /* X.400 mail mapping. */ ns_t_gpos = 27, /* Geographical position (withdrawn). */ ns_t_aaaa = 28, /* Ip6 Address. */ ns_t_loc = 29, /* Location Information. */ ns_t_nxt = 30, /* Next domain (security). */ ns_t_eid = 31, /* Endpoint identifier. */ ns_t_nimloc = 32, /* Nimrod Locator. */ ns_t_srv = 33, /* Server Selection. */ ns_t_atma = 34, /* ATM Address */ ns_t_naptr = 35, /* Naming Authority PoinTeR */ ns_t_kx = 36, /* Key Exchange */ ns_t_cert = 37, /* Certification record */ ns_t_a6 = 38, /* IPv6 address (deprecates AAAA) */ ns_t_dname = 39, /* Non-terminal DNAME (for IPv6) */ ns_t_sink = 40, /* Kitchen sink (experimentatl) */ ns_t_opt = 41, /* EDNS0 option (meta-RR) */ ns_t_tsig = 250, /* Transaction signature. */ ns_t_ixfr = 251, /* Incremental zone transfer. */ ns_t_axfr = 252, /* Transfer zone of authority. */ ns_t_mailb = 253, /* Transfer mailbox records. */ ns_t_maila = 254, /* Transfer mail agent records. */ ns_t_any = 255, /* Wildcard match. */ ns_t_zxfr = 256, /* BIND-specific, nonstandard. */ ns_t_max = 65536 } ns_type; /* Exclusively a QTYPE? (not also an RTYPE) */ #define ns_t_qt_p(t) (ns_t_xfr_p(t) || (t) == ns_t_any || \ (t) == ns_t_mailb || (t) == ns_t_maila) /* Some kind of meta-RR? (not a QTYPE, but also not an RTYPE) */ #define ns_t_mrr_p(t) ((t) == ns_t_tsig || (t) == ns_t_opt) /* Exclusively an RTYPE? (not also a QTYPE or a meta-RR) */ #define ns_t_rr_p(t) (!ns_t_qt_p(t) && !ns_t_mrr_p(t)) #define ns_t_udp_p(t) ((t) != ns_t_axfr && (t) != ns_t_zxfr) #define ns_t_xfr_p(t) ((t) == ns_t_axfr || (t) == ns_t_ixfr || \ (t) == ns_t_zxfr) /* * Values for class field */ typedef enum __ns_class { ns_c_invalid = 0, /* Cookie. */ ns_c_in = 1, /* Internet. */ ns_c_2 = 2, /* unallocated/unsupported. */ ns_c_chaos = 3, /* MIT Chaos-net. */ ns_c_hs = 4, /* MIT Hesiod. */ /* Query class values which do not appear in resource records */ ns_c_none = 254, /* for prereq. sections in update requests */ ns_c_any = 255, /* Wildcard match. */ ns_c_max = 65536 } ns_class; /* DNSSEC constants. */ typedef enum __ns_key_types { ns_kt_rsa = 1, /* key type RSA/MD5 */ ns_kt_dh = 2, /* Diffie Hellman */ ns_kt_dsa = 3, /* Digital Signature Standard (MANDATORY) */ ns_kt_private = 254 /* Private key type starts with OID */ } ns_key_types; typedef enum __ns_cert_types { cert_t_pkix = 1, /* PKIX (X.509v3) */ cert_t_spki = 2, /* SPKI */ cert_t_pgp = 3, /* PGP */ cert_t_url = 253, /* URL private type */ cert_t_oid = 254 /* OID private type */ } ns_cert_types; /* Flags field of the KEY RR rdata. */ #define NS_KEY_TYPEMASK 0xC000 /* Mask for "type" bits */ #define NS_KEY_TYPE_AUTH_CONF 0x0000 /* Key usable for both */ #define NS_KEY_TYPE_CONF_ONLY 0x8000 /* Key usable for confidentiality */ #define NS_KEY_TYPE_AUTH_ONLY 0x4000 /* Key usable for authentication */ #define NS_KEY_TYPE_NO_KEY 0xC000 /* No key usable for either; no key */ /* The type bits can also be interpreted independently, as single bits: */ #define NS_KEY_NO_AUTH 0x8000 /* Key unusable for authentication */ #define NS_KEY_NO_CONF 0x4000 /* Key unusable for confidentiality */ #define NS_KEY_RESERVED2 0x2000 /* Security is *mandatory* if bit=0 */ #define NS_KEY_EXTENDED_FLAGS 0x1000 /* reserved - must be zero */ #define NS_KEY_RESERVED4 0x0800 /* reserved - must be zero */ #define NS_KEY_RESERVED5 0x0400 /* reserved - must be zero */ #define NS_KEY_NAME_TYPE 0x0300 /* these bits determine the type */ #define NS_KEY_NAME_USER 0x0000 /* key is assoc. with user */ #define NS_KEY_NAME_ENTITY 0x0200 /* key is assoc. with entity eg host */ #define NS_KEY_NAME_ZONE 0x0100 /* key is zone key */ #define NS_KEY_NAME_RESERVED 0x0300 /* reserved meaning */ #define NS_KEY_RESERVED8 0x0080 /* reserved - must be zero */ #define NS_KEY_RESERVED9 0x0040 /* reserved - must be zero */ #define NS_KEY_RESERVED10 0x0020 /* reserved - must be zero */ #define NS_KEY_RESERVED11 0x0010 /* reserved - must be zero */ #define NS_KEY_SIGNATORYMASK 0x000F /* key can sign RR's of same name */ #define NS_KEY_RESERVED_BITMASK ( NS_KEY_RESERVED2 | \ NS_KEY_RESERVED4 | \ NS_KEY_RESERVED5 | \ NS_KEY_RESERVED8 | \ NS_KEY_RESERVED9 | \ NS_KEY_RESERVED10 | \ NS_KEY_RESERVED11 ) #define NS_KEY_RESERVED_BITMASK2 0xFFFF /* no bits defined here */ /* The Algorithm field of the KEY and SIG RR's is an integer, {1..254} */ #define NS_ALG_MD5RSA 1 /* MD5 with RSA */ #define NS_ALG_DH 2 /* Diffie Hellman KEY */ #define NS_ALG_DSA 3 /* DSA KEY */ #define NS_ALG_DSS NS_ALG_DSA #define NS_ALG_EXPIRE_ONLY 253 /* No alg, no security */ #define NS_ALG_PRIVATE_OID 254 /* Key begins with OID giving alg */ /* Protocol values */ /* value 0 is reserved */ #define NS_KEY_PROT_TLS 1 #define NS_KEY_PROT_EMAIL 2 #define NS_KEY_PROT_DNSSEC 3 #define NS_KEY_PROT_IPSEC 4 #define NS_KEY_PROT_ANY 255 /* Signatures */ #define NS_MD5RSA_MIN_BITS 512 /* Size of a mod or exp in bits */ #define NS_MD5RSA_MAX_BITS 2552 /* Total of binary mod and exp */ #define NS_MD5RSA_MAX_BYTES ((NS_MD5RSA_MAX_BITS+7/8)*2+3) /* Max length of text sig block */ #define NS_MD5RSA_MAX_BASE64 (((NS_MD5RSA_MAX_BYTES+2)/3)*4) #define NS_MD5RSA_MIN_SIZE ((NS_MD5RSA_MIN_BITS+7)/8) #define NS_MD5RSA_MAX_SIZE ((NS_MD5RSA_MAX_BITS+7)/8) #define NS_DSA_SIG_SIZE 41 #define NS_DSA_MIN_SIZE 213 #define NS_DSA_MAX_BYTES 405 /* Offsets into SIG record rdata to find various values */ #define NS_SIG_TYPE 0 /* Type flags */ #define NS_SIG_ALG 2 /* Algorithm */ #define NS_SIG_LABELS 3 /* How many labels in name */ #define NS_SIG_OTTL 4 /* Original TTL */ #define NS_SIG_EXPIR 8 /* Expiration time */ #define NS_SIG_SIGNED 12 /* Signature time */ #define NS_SIG_FOOT 16 /* Key footprint */ #define NS_SIG_SIGNER 18 /* Domain name of who signed it */ /* How RR types are represented as bit-flags in NXT records */ #define NS_NXT_BITS 8 #define NS_NXT_BIT_SET( n,p) (p[(n)/NS_NXT_BITS] |= (0x80>>((n)%NS_NXT_BITS))) #define NS_NXT_BIT_CLEAR(n,p) (p[(n)/NS_NXT_BITS] &= ~(0x80>>((n)%NS_NXT_BITS))) #define NS_NXT_BIT_ISSET(n,p) (p[(n)/NS_NXT_BITS] & (0x80>>((n)%NS_NXT_BITS))) #define NS_NXT_MAX 127 /* * Inline versions of get/put short/long. Pointer is advanced. */ #define NS_GET16(s, cp) do { \ register u_int8_t *t_cp = (u_int8_t *)(cp); \ (s) = ((u_int16_t)t_cp[0] << 8) \ | ((u_int16_t)t_cp[1]) \ ; \ (cp) += NS_INT16SZ; \ } while (0) #define NS_GET32(l, cp) do { \ register u_int8_t *t_cp = (u_int8_t *)(cp); \ (l) = ((u_int32_t)t_cp[0] << 24) \ | ((u_int32_t)t_cp[1] << 16) \ | ((u_int32_t)t_cp[2] << 8) \ | ((u_int32_t)t_cp[3]) \ ; \ (cp) += NS_INT32SZ; \ } while (0) #define NS_PUT16(s, cp) do { \ register u_int16_t t_s = (u_int16_t)(s); \ register u_int8_t *t_cp = (u_int8_t *)(cp); \ *t_cp++ = t_s >> 8; \ *t_cp = t_s; \ (cp) += NS_INT16SZ; \ } while (0) #define NS_PUT32(l, cp) do { \ register u_int32_t t_l = (u_int32_t)(l); \ register u_int8_t *t_cp = (u_int8_t *)(cp); \ *t_cp++ = t_l >> 24; \ *t_cp++ = t_l >> 16; \ *t_cp++ = t_l >> 8; \ *t_cp = t_l; \ (cp) += NS_INT32SZ; \ } while (0) #include #endif /* !_ARPA_NAMESER_H_ */ dhcp-4.4.1/includes/arpa/nameser_compat.h000644 000765 000024 00000013527 13243301226 020622 0ustar00tmarkstaff000000 000000 /* Copyright (c) 1983, 1989 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * from nameser.h 8.1 (Berkeley) 6/2/93 * $Id: nameser_compat.h,v 1.2 2000/01/27 23:28:08 mellon Exp $ */ #ifndef _ARPA_NAMESER_COMPAT_ #define _ARPA_NAMESER_COMPAT_ /* * Structure for query header. The order of the fields is machine- and * compiler-dependent, depending on the byte/bit order and the layout * of bit fields. We use bit fields only in int variables, as this * is all ANSI requires. This requires a somewhat confusing rearrangement. */ typedef struct { unsigned id :16; /* query identification number */ #if BYTE_ORDER == BIG_ENDIAN /* fields in third byte */ unsigned qr: 1; /* response flag */ unsigned opcode: 4; /* purpose of message */ unsigned aa: 1; /* authoritive answer */ unsigned tc: 1; /* truncated message */ unsigned rd: 1; /* recursion desired */ /* fields in fourth byte */ unsigned ra: 1; /* recursion available */ unsigned unused :1; /* unused bits (MBZ as of 4.9.3a3) */ unsigned ad: 1; /* authentic data from named */ unsigned cd: 1; /* checking disabled by resolver */ unsigned rcode :4; /* response code */ #endif #if BYTE_ORDER == LITTLE_ENDIAN || BYTE_ORDER == PDP_ENDIAN /* fields in third byte */ unsigned rd :1; /* recursion desired */ unsigned tc :1; /* truncated message */ unsigned aa :1; /* authoritive answer */ unsigned opcode :4; /* purpose of message */ unsigned qr :1; /* response flag */ /* fields in fourth byte */ unsigned rcode :4; /* response code */ unsigned cd: 1; /* checking disabled by resolver */ unsigned ad: 1; /* authentic data from named */ unsigned unused :1; /* unused bits (MBZ as of 4.9.3a3) */ unsigned ra :1; /* recursion available */ #endif /* remaining bytes */ unsigned qdcount :16; /* number of question entries */ unsigned ancount :16; /* number of answer entries */ unsigned nscount :16; /* number of authority entries */ unsigned arcount :16; /* number of resource entries */ } HEADER; #define PACKETSZ NS_PACKETSZ #define MAXDNAME NS_MAXDNAME #define MAXCDNAME NS_MAXCDNAME #define MAXLABEL NS_MAXLABEL #define HFIXEDSZ NS_HFIXEDSZ #define QFIXEDSZ NS_QFIXEDSZ #define RRFIXEDSZ NS_RRFIXEDSZ #define INT32SZ NS_INT32SZ #define INT16SZ NS_INT16SZ #define INADDRSZ NS_INADDRSZ #define IN6ADDRSZ NS_IN6ADDRSZ #define INDIR_MASK NS_CMPRSFLGS #define NAMESERVER_PORT NS_DEFAULTPORT #define S_ZONE ns_s_zn #define S_PREREQ ns_s_pr #define S_UPDATE ns_s_ud #define S_ADDT ns_s_ar #define QUERY ns_o_query #define IQUERY ns_o_iquery #define STATUS ns_o_status #define NS_NOTIFY_OP ns_o_notify #define NS_UPDATE_OP ns_o_update #define NOERROR ns_r_noerror #define FORMERR ns_r_formerr #define SERVFAIL ns_r_servfail #define NXDOMAIN ns_r_nxdomain #define NOTIMP ns_r_notimpl #define REFUSED ns_r_refused #define YXDOMAIN ns_r_yxdomain #define YXRRSET ns_r_yxrrset #define NXRRSET ns_r_nxrrset #define NOTAUTH ns_r_notauth #define NOTZONE ns_r_notzone /*#define BADSIG ns_r_badsig*/ /*#define BADKEY ns_r_badkey*/ /*#define BADTIME ns_r_badtime*/ #define DELETE ns_uop_delete #define ADD ns_uop_add #define T_A ns_t_a #define T_NS ns_t_ns #define T_MD ns_t_md #define T_MF ns_t_mf #define T_CNAME ns_t_cname #define T_SOA ns_t_soa #define T_MB ns_t_mb #define T_MG ns_t_mg #define T_MR ns_t_mr #define T_NULL ns_t_null #define T_WKS ns_t_wks #define T_PTR ns_t_ptr #define T_HINFO ns_t_hinfo #define T_MINFO ns_t_minfo #define T_MX ns_t_mx #define T_TXT ns_t_txt #define T_RP ns_t_rp #define T_AFSDB ns_t_afsdb #define T_X25 ns_t_x25 #define T_ISDN ns_t_isdn #define T_RT ns_t_rt #define T_NSAP ns_t_nsap #define T_NSAP_PTR ns_t_nsap_ptr #define T_SIG ns_t_sig #define T_KEY ns_t_key #define T_PX ns_t_px #define T_GPOS ns_t_gpos #define T_AAAA ns_t_aaaa #define T_LOC ns_t_loc #define T_NXT ns_t_nxt #define T_EID ns_t_eid #define T_NIMLOC ns_t_nimloc #define T_SRV ns_t_srv #define T_ATMA ns_t_atma #define T_NAPTR ns_t_naptr #define T_TSIG ns_t_tsig #define T_IXFR ns_t_ixfr #define T_AXFR ns_t_axfr #define T_MAILB ns_t_mailb #define T_MAILA ns_t_maila #define T_ANY ns_t_any #define C_IN ns_c_in #define C_CHAOS ns_c_chaos #define C_HS ns_c_hs /* BIND_UPDATE */ #define C_NONE ns_c_none #define C_ANY ns_c_any #define GETSHORT NS_GET16 #define GETLONG NS_GET32 #define PUTSHORT NS_PUT16 #define PUTLONG NS_PUT32 #endif /* _ARPA_NAMESER_COMPAT_ */ dhcp-4.4.1/doc/api+protocol000644 000765 000024 00000044105 13243301226 016015 0ustar00tmarkstaff000000 000000 This file documents the protocol that the ISC DHCP server and ISC Object Management clients (clients that use the ISC Object Management API) speak between one another. Protocol: All multi-byte numbers are represented in network byte order. On startup, each side sends a status message indicating what version of the protocol they are speaking. The status message looks like this: +---------+---------+ | version | hlength | +---------+---------+ version - a 32-bit fixed-point number with the decimal point between the third and second decimal digits from the left, representing the version of the protocol. The current protocol version is 1.00. If the field were considered as a 32-bit integer, this would correspond to a value of 100 decimal, or 0x64. hlength - a 32-bit integer representing the length of the fixed-length header in subsequent messages. This is normally 56, but can be changed to a value larger than 56 by either side without upgrading the revision number. The startup message is not authenticated. Either side may reject the other side's startup message as invalid by simply closing the connection. The only fixed part of the startup message is the version number - future versions may delete hlength, or add further startup information. Following the startup message, all messages have the same format. Currently, the format includes a fixed-length header (the length in hlength, above) +--------+----+--------+----+-----+---------+------------+------------+-----+ | authid | op | handle | id | rid | authlen | msg values | obj values | sig | +--------+----+--------+----+-----+---------+------------+------------+-----+ The fixed-length header consists of: authid = a 32-bit authenticator handle. For an original message (one not in response to some other message), this will be chosen by the originator. For a message in response to another message, the authenticator for that message is used, except if the response is an error message indicating that the authenticator used was unknown, in which case the null authenticator is used. Messages that are generated as the result of a notify registration use the authenticator used in the original notify registration. The authenticator itself is generated by having one side of the connection send an object of type "authenticator" to the other side with values that indicate what kind of authentication mechanism to use and what key to use. The two most likely things here are a Kerberos V principal name or the name of a shared secret that can be used to calculate an MD5 hash. The mechanism for doing this has yet to be finalized. If authid is zero, the message is not authenticated. op = 32-bit opcode, one of: open = 1 refresh = 2 update = 3 notify = 4 error = 5 delete = 6 handle = 32-bit object handle A handle on the object being opened, created, refreshed or updated. If no handle is yet available (e.g., with open and new), then the value zero is sent. id = 32-bit transaction id of the message - a monotonically increasing number that starts with some randomly chosen number at the beginning of the life of the connection. The value should never be zero. rid = 32-bit transaction ID of the message to which this message is a response, or zero if this message is not in response to a message from the other side. authlen = a 32-bit number representing the length of the authenticator msg values = a series of name+value pairs, specific to this message. Each name+value pair starts with a 16-bit name length, followed by that many bytes of name, followed by a 32-bit value length, followed by that many bytes of value. If the length is zero, this is a value of the blank string. If the length is all ones (2^32-1), then there is no value - for an update, this means the value for this name and the name itself should be deleted from the object, which may or may not be possible. The list of name/value pairs ends with a zero-length name, which is not followed by a value length/value pair. obj values = a series of name+value pairs, as above, specific to the object being created, updated or refreshed. signature = authlen bytes of data signing the message. The signature algorithm is a property of the authenticator handle. Message types: 1: open relevant input values: object-type = the name of the type of object open:create = boolean - create the object if it doesn't yet exist open:exclusive = boolean - don't open the object if it does exist open:update = boolean - update the object with included values if it matches. the handle should always be the null handle The input value must also contain key information for the type of object being searched that uniquely identifies an object, or search information that matches only one object. Each object has a key specification (a key is something that uniquely identifies an object), so see the key specification for that object to see what to send here. An open message with the create flag set must specify a key, and not merely matching criteria. Some objects may allow more than one key, and it may be that the union of those keys is required to uniquely identify the object, or it may be that any one such key will uniquely identify the object. The documentation for the type of object will specify this. An open message will result in an immediate response message whose opcode will either be "error" or "update". The error message may include an error:reason value containing a text string explaining the error, and will always include an error:code value which will be the numeric error code for what went wrong. Possible error codes are: not found - no such object exists already exists - object already exists, and exclusive flag was set. not unique - more than one object matching the specification exists. permission denied - the authenticator ID specified does not have authorization to access this object, or if the update flag was specified, to update the object. If the response is an update message, the update message will include the object handle and all of the name/value pairs associated with that object. 2: refresh no input values except the handle need be specified. The null handle may not be specified. If the handle is valid, and the authenticator ID specified has permission to examine the object, then an update message will be sent for that object. Otherwise, one of the following errors will be sent: invalid handle - the handle does not refer to a known object permisson denied - the handle refers to an object that the requestor does not have permission to examine. 3: update Requests that the contents of the specified object be updated with the values included. Values that are not specified are not updated. The response will be either an error message or an update-ok message. If rid is nonzero, no response will be generated, even if there was an error. Possible errors include: invalid handle - no such object was found permission denied - the handle refers to an object that the requestor does not have permission to modify. not confirmed - the update could not be committed due to some kind of resource problem, for example insufficient memory or a disk failure. 4: notify Requests that whenever the object with the specified handle is modified, an update be sent. If there is something wrong with the request, an error message will be returned immediately. Otherwise, whenever a change is made to the object, an update message will be sent containing whatever changes were made (or possibly all the values associated with the object, depending on the implementation). Possible errors: invalid handle permission denied - the handle refers to an object that the requestor does not have permission to examine. not supported - the object implementation does not support notifications 5: status Sends a status code in response to a message. Always sent in response to a message sent by the other side. There should never be a response to this message. 6: delete Deletes the specified object. Response will be either request-ok, or error. Possible errors include: invalid handle - no such object was found permission denied - the handle refers to an object that the requestor does not have permission to modify. not confirmed - the deletion could not be committed due to some kind of resource problem, for example insufficient memory or a disk failure. 7: notify-cancel Like notify, but requests that an existing notification be cancelled. 8: notify-cancelled Indicates that because of a local change, a notification that had been registered can no longer be performed. This could be as a result of the permissions on a object changing, or an object being deleted. There should never be a response to this message. internals: Both client and server use same protocol and infrastructure. There are many object types, each of which is stored in a registry. Objects whose type is not recognized can either be handled by the generic object type, which is registered with the type "*". If no generic object type is registered, then objects with unknown types are simply not supported. On the client, there are probably no special object handlers (although this is by no means forbidden). On the server, probably everything is a special object. Each object type has the following methods: dhcpctl_status dhcpctl_connect (dhcpctl_handle *connection, char *server_name, int port, dhcpctl_handle *authinfo) synchronous returns nonzero status code if it didn't connect, zero otherwise stores connection handle through connection, which can be used for subsequent access to the specified server. server_name is the name of the server, and port is the TCP port on which it is listening. authinfo is the handle to an object containing authentication information. dhcpctl_status dhcpctl_open_object (dhcpctl_handle h, dhcpctl_handle connection, int flags) asynchronous - just queues the request returns nonzero status code if open couldn't be queued returns zero if open was queued h is a handle to an object created by dhcpctl_new_object connection is a connection to a DHCP server flags include: DHCPCTL_CREATE - if the object doesn't exist, create it DHCPCTL_UPDATE - update the object on the server using the attached parameters DHCPCTL_EXCL - error if the object exists and DHCPCTL_CREATE was also specified dhcpctl_status dhcpctl_new_object (dhcpctl_handle *h, dhcpctl_handle connection, char *object_type) synchronous - creates a local handle for a host entry. returns nonzero status code if the local host entry couldn't be created stores handle to host through h if successful, and returns zero. object_type is a pointer to a NUL-terminated string containing the ascii name of the type of object being accessed - e.g., "host" dhcpctl_status dhcpctl_set_callback (dhcpctl_handle h, void *data, void (*callback) (dhcpctl_handle, dhcpctl_status, void *)) synchronous, with asynchronous aftereffect handle is some object upon which some kind of process has been started - e.g., an open, an update or a refresh. data is an anonymous pointer containing some information that the callback will use to figure out what event completed. return value of 0 means callback was successfully set, a nonzero status code is returned otherwise. Upon completion of whatever task is in process, the callback will be passed the handle to the object, a status code indicating what happened, and the anonymous pointer passed to dhcpctl_status dhcpctl_wait_for_completion (dhcpctl_handle h, dhcpctl_status *s) synchronous returns zero if the callback completes, a nonzero status if there was some problem relating to the wait operation. The status of the queued request will be stored through s, and will also be either zero for success or nonzero for some kind of failure. Never returns until completion or until the connection to the server is lost. This performs the same function as dhcpctl_set_callback and the subsequent callback, for programs that want to do inline execution instead of using callbacks. dhcpctl_status dhcpctl_get_value (data_string *result, dhcpctl_handle h, char *value_name) synchronous returns zero if the call succeeded, a nonzero status code if it didn't. result is the address of an empty data string (initialized with bzero or cleared with data_string_forget). On successful completion, the addressed data string will contain the value that was fetched. dhcpctl_handle refers to some dhcpctl item value_name refers to some value related to that item - e.g., for a handle associated with a completed host lookup, value could be one of "hardware-address", "dhcp-client-identifier", "known" or "client-hostname". dhcpctl_status dhcpctl_get_boolean (int *result, dhcpctl_handle h, char *value_name) like dhcpctl_get_value, but more convenient for boolean values, since no data_string needs to be dealt with. dhcpctl_status dhcpctl_set_value (dhcpctl_handle h, data_string value, char *value_name) Sets a value on an object referred to by a dhcpctl_handle. The opposite of dhcpctl_get_value. Does not update the server - just sets the value on the handle. dhcpctl_status dhcpctl_set_string_value (dhcpctl_handle h, char *value, char *value_name) Sets a NUL-terminated ASCII value on an object referred to by a dhcpctl_handle. like dhcpctl_set_value, but saves the trouble of creating a data_string for a NUL-terminated string. Does not update the server - just sets the value on the handle. dhcpctl_status dhcpctl_set_boolean (dhcpctl_handle h, int value, char *value_name) Sets a boolean value on an object - like dhcpctl_set_value, only more convenient for booleans. dhcpctl_status dhcpctl_object_update (dhcpctl_handle h) Queues an update on the object referenced by the handle (there can't be any other work in progress on the handle). An update means local parameters will be sent to the server. dhcpctl_status dhcpctl_object_refresh (dhcpctl_handle h) Queues an update on the object referenced by the handle (there can't be any other work in progress on the handle). An update means local parameters will be sent to the server. dhcpctl_status dhcpctl_object_delete (dhcpctl_handle h) Queues a delete of the object referenced by the handle (there can't be any other work in progress on the handle). A delete means that the object will be permanently deleted on the remote end, assuming the remote end supports object persistence. So a sample program that would update a host declaration would look something like this: /* Create a local object into which to store authentication information. */ if ((status = dhcpctl_new_object (&auth, dhcpctl_null_handle, "authentication-information"))) dhcpctl_error ("Can't create authentication information: %m"); /* Set up the authenticator with an algorithm type, user name and password. */ if ((status = dhcpctl_set_string_value (&auth, "mellon", "username"))) dhcpctl_error ("Can't set username: %m", status); if ((status = dhcpctl_set_string_value (&auth, "three blind mice", "password"))) dhcpctl_error ("Can't set password: %m", status); if ((status = dhcpctl_set_string_value (&auth, "md5-hash", "algorithm"))) dhcpctl_error ("Can't set authentication algorithm: %m.", status); /* Connect to the server. */ if ((status = dhcpctl_connect (&c, "dhcp.server.com", 612, &auth))) dhcpctl_error ("Can't connect to dhcp.server.com: %m", status); /* Create a host object. */ if ((status = dhcpctl_new_object (&hp, c, "host"))) dhcpctl_error ("Host create failed: %m", status); /* Create a data_string to contain the host's client identifier, and set it. */ if ((status = data_string_create_from_hex (&client_id, "1:08:00:2b:34:1a:c3"))) dhcpctl_error ("Can't create client identifier: %m"); if ((status = dhcpctl_set_value (hp, client_id, "dhcp-client-identifier"))) dhcpctl_error ("Host client identifier set failed."); /* Set the known flag to 1. */ if ((status = dhcpctl_set_boolean (hp, 1, "known"))) dhcpctl_error ("Host known set failed."); /* Open an existing host object that matches the client identifier, and update it from the local context, or if no host entry yet exists matching the identifier, create one and initialize it. */ if ((status = dhcpctl_open_object (&hp, c, DHCPCTL_CREATE | DHCPCTL_UPDATE))) dhcpctl_error ("Can't open host: %m", status); /* Wait for the process to complete, check status. */ if ((status = dhcpctl_wait_for_completion (hp, &wait_status))) dhcpctl_error ("Host create/lookup wait failed: %m", status); if (waitstatus) dhcpctl_error ("Host create/lookup failed: %m", status); The API is a bit complicated, for a couple of reasons. I want to make it general, so that there aren't a bazillion functions to call, one for each data type. I want it to be thread-safe, which is why each function returns a status and the error printer requires a status code for input. I want it to be possible to make it asynchronous, so that it can work in tandem with, for example, an X toolkit. If you're just writing a simple update cgi program, you probably won't want to bother to use the asynchronous callbacks, and indeed the above example doesn't. I glossed over data strings above - basically, they're objects with a pointer to a reference-counted buffer structure, an offset into that buffer, and a length. These are used within the DHCP server, so you can get an idea of how they work - basically, they're a convenient and efficient way to store a string with a length such that substrings can easily be taken and such that more than one user at a time can have a pointer to the string. I will also probably add locking primitives, so that you can get the value of something and be sure that some other updator process won't modify it while you have the lock. dhcp-4.4.1/doc/BIND-libraries000644 000765 000024 00000014171 13243301226 016075 0ustar00tmarkstaff000000 000000 Current ISC DHCP uses the BIND 9 libraries for some services. These libraries can be embedded in the bind directory or external using the configuration parameter --with-libbind=PATH with PATh pointing to the directory where can be found includes and libraries. Using standard embedded libraries is safe but you can configure them with extra parameters (cf --with-bind-extra-config) or simply use external libraries with a different configuration. Note if you want to share libraries between bind 9 and ISC DHCP BIND 9 must be built and installed before ISC DHCP build is configured. Three cases are possible: - everything builds and works fine - building requires extra parameters, for instance --enable-threads can require a link parameter to find POSIX threads. In this case you should look at the config.status file of the bind libraries, take CC, CPPFLAGS, CFLAGS, LIBS or LDFLAGS related parameters, and add them to the call to make. - even when the previous step succeeds ISC DHCP still does not work properly. We are trying to collect such cases (cf the list bellow) but unfortunately you have to forget the particular feature. Below you have 3 kinds of don't use: - please don't use means it should likely break ISC DHCP - don't use means unless you don't have a good reason to use it - by default don't use it means you should not use it for embedded libraries but it doesn't lead to problems (other than possible new ISC DHCP build flags and bigger binaries) when used for external BIND 9 libraries Possible bind9 configuration flags (list built from bind 9.11 and bind 9.12 various branches even ISC DHCP uses only the git v9_11 branch): -h, --help, -V, --version make configure to exit immediately so don't use -q, --quiet, --silent, --cache-file, -C, --config-cache, --with-make-clean you have no reason to use them but they should not have bad effect -n, --no-create make configure to exit too early so don't use --prefix, --exec-prefix not used for embedded libraries but used by BIND 9 --bindir, --sbindir, --libexecdir, --sysconfdir, --sharedstatedir, --localstatedir, --oldincludedir, --datarootdir, --datadir, --infodir, --localedir, --mandir, --docdir, --htmldir, --dvidir, --pdfdir, --psdir not used for embedded nor BIND 9 for its includes and libraries --libdir, --includedir used for embedded libraries for the make install (cf the --enable-bind-install ISC DHCP configuration parameter) --build, --host used for cross compiling and handled by ISC DHCP configure --disable-option-checking this disables a sanity check so please don't use --enable-shared, --enable-static, --enable-fast-install ignored by embedded or BIND 9 libraries configure --disable-libtool-lock parallel builds don't work for embedded or BIND 9 libraries build so it should not have any effect --enable-libbind this was deprecated so please don't use --enable-warn-shadow for developers, should have no effect if compilers don't raise errors (vs warnings) on this (and a shadowing was left) --enable-warn-error, --with-sysroot this likely breaks the build so please don't use --enable-developer for developers, should have no direct effect but still recommend to not use it --enable-afl for developers, useless for ISC DHCP so don't use --enable-seccomp this should not have any effect for ISC DHCP? --enable-kqueue, --enable-epoll, --enable-devpoll please use them at the ISC DHCP configure level, they should work but not yet fully tested so use at your own risk --enable-threads this should work but not yet fully tested so use at your own risk. Note on many systems it requires an extra flag for ISC DHCP building and some experiments showed a large slow down when this is enabled --enable-native-pkcs11 You should not have a reason to use this and it likely won't work so please don't use --enable-openssl-version-check You should never use an antic version of OpenSSL with known security bugs so please don't use --enable-openssl-hash Comes with --with-openssl so if OpenSSL raises some problems with hash or HMAC you can try to disable this --enable-crypto-rand Comes with --with-openssl so if OpenSSL raises some problems with random number generation you can try to disable this --enable-largefile Perhaps there is no systems today where this is required so by default don't use --enable-backtrace, --enable-symtable this should have no effect for ISC DHCP? --enable-ipv6, --disable-tcp-fastopen, --enable-getifaddrs, --enable-atomic please leave embedded or BIND 9 libraries configure to do its job so please don't use --disable-isc-spnego, --with-gssapi this should have no effect for ISC DHCP? --enable-full-report this should have no effect for ISC DHCP but is still useful to get more information about bind configuration so use it if you believe it can be useful --disable-chroot, --disable-linux-caps, --enable-fixed-rrset, --disable-rpz-nsip, --disable-rpz-nsdname, --enable-filter-aaaa, --enable-querytrace, --with-python, --with-python-install-dir, --with-locktype, --with-pkcs11, --with-ecdsa, --with-gost, --with-eddsa, --with-aes, --with-cc-alg, --with-purify, --with-gperftools-profiler, --with-kame, --with-docbook-xsl, --with-dlopen this should have no effect for ISC DHCP --enable-dnsrps-dl, --enable-dnsrps, --enable-dnstap, --with-geoip, --with-lmdb, --with-libxml2, --with-libjson, --with-zlib, --with-readline, --with-dnsrps-libname, --with-dnsrps-dir, --with-protobuf-c, --with-libfstrm, --with-idn, --with-libiconv, --with-iconv, --with-idnlib, --with-dlz-* useless for ISC DHCP so by default don't use --with-pic this should have no effect for ISC DHCP? --with-gnu-ld this should have no effect for ISC DHCP? --with-randomdev, --with-atf please use them at the ISC DHCP configure level --with-libtool please leave the ISC DHCP configure manage this --with-openssl useless for ISC DHCP by default don't use but remember it is required for DNSSEC --with-tuning by default don't use dhcp-4.4.1/doc/devel/000755 000765 000024 00000000000 13243313032 014555 5ustar00tmarkstaff000000 000000 dhcp-4.4.1/doc/DHCPv4-over-DHCPv6000644 000765 000024 00000020733 13243301226 016341 0ustar00tmarkstaff000000 000000 Short notice about DHCPv4 over DHCPv6 aka RFC 7341 -------------------------------------------------- Note well: this code is still somewhat experimental and any user should take care when trying to use it. GENERAL The purpose of DHCPv4 over DHCPv6 (RFC7341) is to encapsulate a DHCPv4 request within a DHCPv6 request in order to transmit it across a v6 only network. This feature may become useful when, during the transition from a v4 to a v6 network, there are still v4 clients at the edges and v4 servers in the center but the links between them are v6 only. In order to support this functionality we have chosen to use two processes each for the client and server. In both pairs one process handles the DHCPv4 processing and the other handles the DHCPv6 processing. The topology is thus something like this: Client processes network Server processes DHCPv4 <-> DHCPv6 <--ipv6--> DHCPv6 <-> DHCPv4 The v6 client and server processes can continue to process DHCPv6 packets as normal but will also allow a DHCPv4 process to connect to them via a socket. The DHCPv4 client will pass a request to the DHCPv6 client which will encapsulate it within a DHCPv6 request which is sent to the DHCPv6 server (possibly via DHCPv6 relays). When the DHCPv6 server receives the packet it will get the DHCPv4 query and pass it to the DHCPv4 server. The response will by handled in a similar fashion. When starting up the paired processes one should take care that they use different files for configuration, leases and process IDs. LOCALIZATION Normally the DHCPv4 server choose a subnet based on a number of options: - follow the Relay Agent Link Selection option if exists - follow the Subnet Selection option if exists - use the relay address if relayed - use the receiving interface With the exception of the last case the address must match a subnet address. Unfortunately when using DHCPv4 over DHCPv6 this information is not available in the packet, to quote RFC 7341: Since the DHCPv4 message is encapsulated in the DHCPv6 message, it lacks the information that is typically used by the DHCPv4 server, implementing [RFC2131], to make address- allocation decisions, e.g., giaddr for relayed messages and IPv4 address of the interface that the server is using to communicate with a directly connected client. In DHCPv4 over DHCPv6, there are a mixture of IPv6 and IPv4 addresses. The DHCPv4 over DHCPv6 server externally uses only IPv6 addresses, even on the DHCPv4 side, so shared networks associated with directly attached interfaces are identified by subnet6 declarations. For this reason, the DHCPv4 side shouldn't request an interface vai the command line or configuration file: all usable interfaces will be requested (i.e., standard behavior when no interface is specified in the command line or configuration file) and it is not an error to have an interface with an address and no matching subnet6 declaration, nor an error to have no usable interfaces (i.e., fully relayed or routed topologies are accepted). Note also there is no involved DHCPv4 relays (DHCPv4 messages are directly encapsulated into DHCPv6 DHCPv4-query/DHCPv4-response messages by clients and servers as there is no cross DHCP version relays specified by RFC 7341) so to get a Relay Agent option or a relay address are very unlikely cases. So the procedure is: - follow the Relay Agent Link Selection option if exists - follow the DHCPv4 Subnet Selection option if exists - use the DHCPv4 relay address if DHCPv4 relayed - when DHCPv6 relayed, use the first relay with an usable (i.e., not unspecified or link-local) address - use the receiving interface The basic network configuration is something like this: ---- shared-network "link1" { subnet6 2001:db8:1:1::/64 { } subnet 192.168.1.0 netmask 255.255.255.0 { range 192.168.1.100 192.168.1.199; } } ---- This groups the 2001:db8:1:1::/64 subnet with the 192.168.1.0 subnet. When the a DHCPv4 over DHCPv6 client uses the 2001:db8:1:1::10 IPv6 address it will get an address from 192.168.1.1xy assigned. There is one remaining question: on which interface should a DHCPv4 over DHCPv6 client apply the assigned IPv4 address? RFC 7341 does not really help: Before applying for an IPv4 address via a DHCPv4-query message, the client must identify a suitable network interface for the address. Once the request is acknowledged by the server, the client can configure the address and other relevant parameters on this interface. The mechanism for determining a suitable interface is out of the scope of the document. The ISC DHCP answer is the IPv4 address is (in fact is required to be) specified in the command line of the DHCPv4 side of the DHCPv4 over DHCPv6 client. BTW in the usual case where the upstream interface is IPv6 only, the IPv4 interface will be a different one. HOW TO USE ---------- CONFIGURATION By default the DHCPv4 over DHCPv6 code is disabled and in order to use it you will need to configure it. Note that this code requires that the dhcpv6 code be enabled (it is enabled by default.) ./configure --enable-dhcpv4o6 CLIENT SETUP The client runs both a DHCPv6 client and a DHCPv4 client on the second Ethernet eth1. The following could be used to launch them from the client directory. ./dhclient -d -v -6 -4o6 6767 -lf leases6 -pf pid6 eth1 and ./dhclient -d -v -4 -4o6 6767 -lf leases4 -pf pid4 eth1 In this case we are using the port pair 6767 and 6768 for communication and one can start or stop either client as necessary (though if the v6 client is stopped the v4 client won't be able to contact a server). The lease files are leases4 and leases6 and the process id files are pid4 and pid6. You would probably put the files elsewhere. For testing purposes it is best to run the two clients in the foreground and in separate windows. SERVER SETUP As with any DHCP servers you will need to ensure there is a path from the clients to the servers - any firewalls must allow DHCPv6 traffic through. You should also verify no other DHCP servers are running and will conflict with the DHCPv4 over DHCPv6 pair. The server VM must have both IPv4 and IPv6 addresses. On a system running Fedora with the second interface named eno33554984, the commands are: ip addr add 10.10.10.1/24 dev eno33554984 and ip -6 addr add 2001:db8:1:1::1/64 dev eno33554984 Note that in theory the IPv4 address is not required but: - there are some DHCPv4 clients which refused responses with no or an invalid server-id - this avoids messages about being unable to find a subnet to configure or something similar Both ISC DHCP and Kea use 2 processes to manage DHCPv4-over-DHCPv6, one in charge of DHCPv6, the other in charge of DHCPv4. They communicate via UDP. ISC DHCP DHCPv6 SERVER The dhcpd.conf6 example configuration file is: ---- # DHCPv6 conf authoritative; default-lease-time 3600; max-lease-time 7200; option dhcp6.dhcp4-o-dhcp6-server 2001:db8:1:1::1; subnet6 2001:db8:1:1::/64 { range6 2001:db8:1:1::1:0/112; } ---- The server is launched from the server directory by: ./dhcpd -f -d -6 -4o6 6767 -cf ./dhcpd.conf6 -lf ./leases6 -pf ./pid6 eno33554984 As with the client above the servers are using the port pair 6767 and 6768 to communicate. The leases file (leases6) must be created before attempting to start the server. ISC DHCP DHCPv4 SERVER The dhcpd.conf4 example configuration file is: ---- # DHCPv4o6 conf authoritative; default-lease-time 3600; max-lease-time 7200; shared-network "eno33554984" { subnet6 2001:db8:1:1::/64 { } subnet 10.10.10.0 netmask 255.255.255.0 { range 10.10.10.100 10.10.10.199; } } ---- The server is launched from the server directory by: ./dhcpd -f -d -4 -4o6 6767 -cf ./dhcpd.conf4 -lf ./leases4 -pf ./pid4 Note that the port specification must be the same as used with the v6 server and that the configuration, lease and process id files should have different names. Again the The leases file (leases4) must be created before attempting to start the server. Finally note in the configuration file the use of the shared-network to connect the DHCPv4 and DHCPv6 subnets. USE WITH DHCPv6 RELAY(s) If the DHCPv6 infrastructure uses one (or more) relay because the client and the server are not on the same link the best choice is to put the first (closest to client) relay address in the dhcp4-o-dhcp6-server option so the same path between the DHCPv6 client part and server part will be used for DHCPv6 and DHCPv4-over-DHCPv6 traffic. dhcp-4.4.1/doc/examples/000755 000765 000024 00000000000 13243313032 015274 5ustar00tmarkstaff000000 000000 dhcp-4.4.1/doc/IANA-arp-parameters000644 000765 000024 00000012727 13243301226 017045 0ustar00tmarkstaff000000 000000 ADDRESS RESOLUTION PROTOCOL PARAMETERS The Address Resolution Protocol (ARP) specified in [RFC826] has several parameters. The assigned values for these parameters are listed here. REVERSE ADDRESS RESOLUTION PROTOCOL OPERATION CODES The Reverse Address Resolution Protocol (RARP) specified in [RFC903] uses the "Reverse" codes below. DYNAMIC REVERSE ARP The Dynamic Reverse Address Resolution Protocol (DRARP) uses the "DRARP" codes below. For further information, contact: David Brownell (suneast!helium!db@Sun.COM). INVERSE ADDRESS RESOULUTION PROTOCOL The Inverse Address Resolution Protocol (IARP) specified in [RFC1293] uses the "InARP" codes below. Assignments: Number Operation Code (op) References ------ -------------------------- ---------- 1 REQUEST [RFC826] 2 REPLY [RFC826] 3 request Reverse [RFC903] 4 reply Reverse [RFC903] 5 DRARP-Request [David Brownell] 6 DRARP-Reply [David Brownell] 7 DRARP-Error [David Brownell] 8 InARP-Request [RFC1293] 9 InARP-Reply [RFC1293] 10 ARP-NAK [RFC1577] 11 MARS-Request [Armitage] 12 MARS-Multi [Armitage] 13 MARS-MServ [Armitage] 14 MARS-Join [Armitage] 15 MARS-Leave [Armitage] 16 MARS-NAK [Armitage] 17 MARS-Unserv [Armitage] 18 MARS-SJoin [Armitage] 19 MARS-SLeave [Armitage] 20 MARS-Grouplist-Request [Armitage] 21 MARS-Grouplist-Reply [Armitage] 22 MARS-Redirect-Map [Armitage] 23 MAPOS-UNARP [Maruyama] Number Hardware Type (hrd) References ------ ----------------------------------- ---------- 1 Ethernet (10Mb) [JBP] 2 Experimental Ethernet (3Mb) [JBP] 3 Amateur Radio AX.25 [PXK] 4 Proteon ProNET Token Ring [Doria] 5 Chaos [GXP] 6 IEEE 802 Networks [JBP] 7 ARCNET [JBP] 8 Hyperchannel [JBP] 9 Lanstar [TU] 10 Autonet Short Address [MXB1] 11 LocalTalk [JKR1] 12 LocalNet (IBM PCNet or SYTEK LocalNET) [JXM] 13 Ultra link [RXD2] 14 SMDS [GXC1] 15 Frame Relay [AGM] 16 Asynchronous Transmission Mode (ATM) [JXB2] 17 HDLC [JBP] 18 Fibre Channel [Yakov Rekhter] 19 Asynchronous Transmission Mode (ATM) [RFC1577] 20 Serial Line [JBP] 21 Asynchronous Transmission Mode (ATM) [MXB1] 22 MIL-STD-188-220 [Jensen] 23 Metricom [Stone] 24 IEEE 1394.1995 [Hattig] 25 MAPOS [Maruyama] Protocol Type (pro) Use the same codes as listed in the section called "Ethernet Numbers of Interest" (all hardware types use this code set for the protocol type). REFERENCES [RFC826] Plummer, D., "An Ethernet Address Resolution Protocol or Converting Network Protocol Addresses to 48-bit Ethernet Addresses for Transmission on Ethernet Hardware", STD 37, RFC 826, MIT-LCS, November 1982. [RFC903] Finlayson, R., Mann, T., Mogul, J., and M. Theimer, "A Reverse Address Resolution Protocol", STD 38, RFC 903, Stanford University, June 1984. [RFC1293] Bradley, T., and C. Brown, "Inverse Address Resolution Protocol", RFC 1293, Wellfleet Communications, Inc., January 1992. PEOPLE [Armitage] Grenville Armitage, , April 1995. [AGM] Andy Malis [GXC1] George Clapp [Doria] Avri Doria December 1994. [GXP] Gill Pratt [Jensen] Herb Jensen, , February 1995. [JBP] Jon Postel [JKR1] Joyce K. Reynolds [JXM] Joseph Murdock <---none---> [Hattig] Myron Hattig, , February 1997. [Maruyama] Mitsuru Maruyama, , March 1997. [MXB1] Mike Burrows [PXK] Philip Koch [RXD2] Rajiv Dhingra [Stone] Jonathan Stone, , May 1996. [TU] Tom Unger [David Brownell] [Mark Laubach] [Yakov Rekhter] [] dhcp-4.4.1/doc/ja_JP.eucJP/000755 000765 000024 00000000000 13243313032 015446 5ustar00tmarkstaff000000 000000 dhcp-4.4.1/doc/Makefile000644 000765 000024 00000003011 13243301226 015113 0ustar00tmarkstaff000000 000000 # Copyright (c) 2004-2006,2009 by Internet Systems Consortium, Inc. ("ISC") # Copyright (c) 1995-2003 by Internet Software Consortium # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # Internet Systems Consortium, Inc. # 950 Charter Street # Redwood City, CA 94063 # # https://www.isc.org/ all: References.txt References.html References.txt: References.xml xml2txt References.xml References.html: References.xml xml2html References.xml devel: mkdir -p html doxygen devel/doxyfile > html/doxygen.log 2>html/doxygen-warnings.log cppcheck: mkdir -p html cd .. && cppcheck --enable=all --inline-suppr \ -f -v -j 2 -i tests/ -i dhcp-*/ \ . 1> doc/html/cppcheck.log 2> doc/html/cppcheck-error.log # cppcheck can be extended with list of suppressions. # --suppressions-list=doc/cppcheck-skip.txt \ .PHONY: devel cppcheckdhcp-4.4.1/doc/References.xml000644 000765 000024 00000106230 13243301226 016265 0ustar00tmarkstaff000000 000000 ]> ISC DHCP References Collection Internet Systems Consortium, Inc.
950 Charter Street Redwood City CA 94063
Internet Systems Consortium, Inc.
950 Charter Street Redwood City CA 94063 +1 650 423 1345 Tomasz_Mrugalski@isc.org
ISC DHCP Reference Implementation This document describes a collection of reference material to which ISC DHCP has been implemented as well as a more complete listing of references for DHCP and DHCPv6 protocols. Copyright (c) 2006-2017 by 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/. THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
As a little historical anecdote, ISC DHCP once packaged all the relevant RFCs and standards documents along with the software package. Until one day when a voice was heard from one of the many fine institutions that build and distribute this software... they took issue with the IETF's copyright on the RFC's. It seems the IETF's copyrights don't allow modification of RFC's (except for translation purposes). Our main purpose in providing the RFCs is to aid in documentation, but since RFCs are now available widely from many points of distribution on the Internet, there is no real need to provide the documents themselves. So, this document has been created in their stead, to list the various IETF RFCs one might want to read, and to comment on how well (or poorly) we have managed to implement them.
ISC DHCP, much like its other cousins in ISC software, is self-described as a 'Reference Implementation.' There has been a great deal of confusion about this term. Some people seem to think that this term applies to any software that once passed a piece of reference material on its way to market (but may do quite a lot of things that aren't described in any reference, or may choose to ignore the reference it saw entirely). Other folks get confused by the word 'reference' and understand that to mean that there is some special status applied to the software - that the software itself is the reference by which all other software is measured. Something along the lines of being "The DHCP Protocol's Reference Clock," it is supposed. The truth is actually quite a lot simpler. Reference implementations are software packages which were written to behave precisely as appears in reference material. They are written "to match reference." If the software has a behaviour that manifests itself externally (whether it be something as simple as the 'wire format' or something higher level, such as a complicated behaviour that arises from multiple message exchanges), that behaviour must be found in a reference document. Anything else is a bug, the only question is whether the bug is in reference or software (failing to implement the reference). This means: To produce new externally-visible behaviour, one must first provide a reference. Before changing externally visible behaviour to work around simple incompatibilities in any other implementation, one must first provide a reference. That is the lofty goal, at any rate. It's well understood that, especially because the ISC DHCP Software package has not always been held to this standard (but not entirely due to it), there are many non-referenced behaviours within ISC DHCP. The primary goal of reference implementation is to prove the reference material. If the reference material is good, then you should be able to sit down and write a program that implements the reference, to the word, and come to an implementation that is distinguishable from others in the details, but not in the facts of operating the protocol. This means that there is no need for 'special knowledge' to work around arcane problems that were left undocumented. No secret handshakes need to be learned to be imparted with the necessary "real documentation". Also, by accepting only reference as the guidebook for ISC DHCP's software implementation, anyone who can make an impact on the color texture or form of that reference has a (somewhat indirect) voice in ISC DHCP's software design. As the IETF RFC's have been selected as the source of reference, that means everyone on the Internet with the will to participate has a say.
It may surprise you to realize that ISC DHCP implements 802.1 'Ethernet' framing, Token Ring, and FDDI. In order to bridge the gap there between these physical and DHCP layers, it must also implement IP and UDP framing. The reason for this stems from Unix systems' handling of BSD sockets (the general way one might engage in transmission of UDP packets) on unconfigured interfaces, or even the handling of broadcast addressing on configured interfaces. There are a few things that DHCP servers, relays, and clients all need to do in order to speak the DHCP protocol in strict compliance with . Transmit a UDP packet from IP:0.0.0.0 Ethernet:Self, destined to IP:255.255.255.255 LinkLayer:Broadcast on an unconfigured (no IP address yet) interface. Receive a UDP packet from IP:remote-system LinkLayer:remote-system, destined to IP:255.255.255.255 LinkLayer:Broadcast, again on an unconfigured interface. Transmit a UDP packet from IP:Self, Ethernet:Self, destined to IP:remote-system LinkLayer:remote-system, without transmitting a single ARP. And of course the simple case, a regular IP unicast that is routed via the usual means (so it may be direct to a local system, with ARP providing the glue, or it may be to a remote system via one or more routers as normal). In this case, the interfaces are always configured. The above isn't as simple as it sounds on a regular BSD socket. Many unix implementations will transmit broadcasts not to 255.255.255.255, but to x.y.z.255 (where x.y.z is the system's local subnet). Such packets are not received by several known DHCP client implementations - and it's not their fault, very explicitly demands that these packets' IP destination addresses be set to 255.255.255.255. Receiving packets sent to 255.255.255.255 isn't a problem on most modern unixes...so long as the interface is configured. When there is no IPv4 address on the interface, things become much more murky. So, for this convoluted and unfortunate state of affairs in the unix systems of the day ISC DHCP was manufactured, in order to do what it needs not only to implement the reference but to interoperate with other implementations, the software must create some form of raw socket to operate on. What it actually does is create, for each interface detected on the system, a Berkeley Packet Filter socket (or equivalent), and program it with a filter that brings in only DHCP packets. A "fallback" UDP Berkeley socket is generally also created, a single one no matter how many interfaces. Should the software need to transmit a contrived packet to the local network the packet is formed piece by piece and transmitted via the BPF socket. Hence the need to implement many forms of Link Layer framing and above. The software gets away with not having to implement IP routing tables as well by simply utilizing the aforementioned 'fallback' UDP socket when unicasting between two configured systems is needed. Modern unixes have opened up some facilities that diminish how much of this sort of nefarious kludgery is necessary, but have not found the state of affairs absolutely resolved. In particular, one might now unicast without ARP by inserting an entry into the ARP cache prior to transmitting. Unconfigured interfaces remain the sticking point, however...on virtually no modern unixes is it possible to receive broadcast packets unless a local IPv4 address has been configured, unless it is done with raw sockets.
ISC DHCP Implements Ethernet Version 2 ("DIX"), which is a variant of IEEE 802.2. No good reference of this framing is known to exist at this time, but it is vaguely described in see the section titled "Packet format"), and the following URL is also thought to be useful. http://en.wikipedia.org/wiki/DIX_Ethernet
IEEE 802.5 defines the Token Ring framing format used by ISC DHCP.
is the most helpful reference ISC DHCP has used to form FDDI packets.
RFC760 fundamentally defines the bare IPv4 protocol which ISC DHCP implements.
RFC768 defines the User Datagram Protocol that ultimately carries the DHCP or BOOTP protocol. The destination DHCP server port is 67, the client port is 68. Source ports are irrelevant.
The DHCP Protocol is strange among protocols in that it is grafted over the top of another protocol - BOOTP (but we don't call it "DHCP over BOOTP" like we do, say "TCP over IP"). BOOTP and DHCP share UDP packet formats - DHCP is merely a conventional use of both BOOTP header fields and the trailing 'options' space. The ISC DHCP server supports BOOTP clients conforming to RFC951 and RFC1542.
"The DHCP[v4] Protocol" is not defined in a single document. The following collection of references of what ISC DHCP terms "The DHCPv4 Protocol".
RFC2131 defines the protocol format and procedures. ISC DHCP is not known to diverge from this document in any way. There are, however, a few points on which different implementations have arisen out of vagueries in the document. DHCP Clients exist which, at one time, present themselves as using a Client Identifier Option which is equal to the client's hardware address. Later, the client transmits DHCP packets with no Client Identifier Option present - essentially identifying themselves using the hardware address. Some DHCP Servers have been developed which identify this client as a single client. ISC has interpreted RFC2131 to indicate that these clients must be treated as two separate entities (and hence two, separate addresses). Client behaviour (Embedded Windows products) has developed that relies on the former implementation, and hence is incompatible with the latter. Also, RFC2131 demands explicitly that some header fields be zeroed upon certain message types. The ISC DHCP Server instead copies many of these fields from the packet received from the client or relay, which may not be zero. It is not known if there is a good reason for this that has not been documented. RFC2132 defines the initial set of DHCP Options and provides a great deal of guidance on how to go about formatting and processing options. The document unfortunately waffles to a great extent about the NULL termination of DHCP Options, and some DHCP Clients (Windows 95) have been implemented that rely upon DHCP Options containing text strings to be NULL-terminated (or else they crash). So, ISC DHCP detects if clients null-terminate the host-name option and, if so, null terminates any text options it transmits to the client. It also removes NULL termination from any known text option it receives prior to any other processing.
RFC2241 defines options for Novell Directory Services. RFC2242 defines an encapsulated option space for NWIP configuration. RFC2485 defines the Open Group's UAP option. RFC2610 defines options for the Service Location Protocol (SLP). RFC2937 defines the Name Service Search Option (not to be confused with the domain-search option). The Name Service Search Option allows eg nsswitch.conf to be reconfigured via dhcp. The ISC DHCP server implements this option, and the ISC DHCP client is compatible...but does not by default install this option's value. One would need to make their relevant dhclient-script process this option in a way that is suitable for the system. RFC3004 defines the User-Class option. Note carefully that ISC DHCP currently does not implement to this reference, but has (inexplicably) selected an incompatible format: a plain text string. RFC3011 defines the Subnet-Selection plain DHCPv4 option. Do not confuse this option with the relay agent "link selection" sub-option, although their behaviour is similar. RFC3396 documents both how long options may be encoded in DHCPv4 packets, and also how multiple instances of the same option code within a DHCPv4 packet will be decoded by receivers. RFC3397 documents the Domain-Search Option, which allows the configuration of the /etc/resolv.conf 'search' parameter in a way that is RFC1035 wire format compatible (in fact, it uses the RFC1035 wire format). ISC DHCP has both client and server support, and supports RFC1035 name compression. RFC3679 documents a number of options that were documented earlier in history, but were not made use of. RFC3925 documents a pair of Enterprise-ID delimited option spaces for vendors to use in order to inform servers of their "vendor class" (sort of like 'uname' or 'who and what am I'), and a means to deliver vendor-specific and vendor-documented option codes and values. RFC3942 redefined the 'site local' option space. defines two BCMS server options for each protocol family. RFC4388 defined the DHCPv4 LEASEQUERY message type and a number of suitable response messages, for the purpose of sharing information about DHCP served addresses and clients.
RFC3046 defines the Relay Agent Information Option and provides a number of sub-option definitions. RFC3256 defines the DOCSIS Device Class sub-option. RFC3527 defines the Link Selection sub-option.
The collection of documents that describe the standards-based method to update dns names of DHCP clients starts most easily with RFC4703 to define the overall architecture, travels through RFCs 4702 and 4704 to describe the DHCPv4 and DHCPv6 FQDN options (to carry the client name), and ends up at RFC4701 which describes the DHCID RR used in DNS to perform a kind of atomic locking. ISC DHCP adopted early versions of these documents, and has not yet synchronized with the final standards versions. For RFCs 4702 and 4704, the 'N' bit is not yet supported. The result is that it is always set zero, and is ignored if set. For RFC4701, which is used to match client identities with names in the DNS as part of name conflict resolution. Note that ISC DHCP's implementation of DHCIDs vary wildly from this specification. First, ISC DHCP uses a TXT record in which the contents are stored in hexadecimal. Second, there is a flaw in the selection of the 'Identifier Type', which results in a completely different value being selected than was defined in an older revision of this document...also this field is one byte prior to hexadecimal encoding rather than two. Third, ISC DHCP does not use a digest type code. Rather, all values for such TXT records are reached via an MD5 sum. In short, nothing is compatible, but the principle of the TXT record is the same as the standard DHCID record. However, for DHCPv6 FQDN, we do use DHCID type code '2', as no other value really makes sense in our context.
The Failover Protocol defines means by which two DHCP Servers can share all the relevant information about leases granted to DHCP clients on given networks, so that one of the two servers may fail and be survived by a server that can act responsibly. Unfortunately it has been quite some years (2003) since the last time this document was edited, and the authors no longer show any interest in fielding comments or improving the document. The status of this protocol is very unsure, but ISC's implementation of it has proven stable and suitable for use in sizable production environments. draft-ietf-dhc-failover-12.txt describes the Failover Protocol. In addition to what is described in this document, ISC DHCP has elected to make some experimental changes that may be revoked in a future version of ISC DHCP (if the draft authors do not adopt the new behaviour). Specifically, ISC DHCP's POOLREQ behaviour differs substantially from what is documented in the draft, and the server also implements a form of 'MAC Address Affinity' which is not described in the failover document. The full nature of these changes have been described on the IETF DHC WG mailing list (which has archives), and also in ISC DHCP's manual pages. Also note that although this document references a RECOVER-WAIT state, it does not document a protocol number assignment for this state. As a consequence, ISC DHCP has elected to use the value 254. An optimization described in the failover protocol draft is included since 4.2.0a1. It permits a DHCP server operating in communications-interrupted state to 'rewind' a lease to the state most recently transmitted to its peer, greatly increasing a server's endurance in communications-interrupted. This is supported using a new 'rewind state' record on the dhcpd.leases entry for each lease. describes the Load Balancing Algorithm (LBA) that ISC DHCP uses in concert with the Failover protocol. Note that versions 3.0.* are known to misimplement the hash algorithm (it will only use the low 4 bits of every byte of the hash bucket array).
explains how to go about obtaining a new DHCP Option code assignment.
For now there is only one document that specifies the base of the DHCPv6 protocol (there have been no updates yet), . Support for DHCPv6 was first added in version 4.0.0. The server and client support only IA_NA. While the server does support multiple IA_NAs within one packet from the client, our client only supports sending one. There is no relay support. DHCPv6 introduces some new and uncomfortable ideas to the common software library. Options sometimes may appear multiple times. The common library used to treat all appearance of multiple options as specified in RFC2131 - to be concatenated. DHCPv6 options may sometimes appear multiple times (such as with IA_NA or IAADDR), but often must not. As of 4.2.1-P1, multiple IA_NA, IA_PD or IA_TA are not supported. The same option space appears in DHCPv6 packets multiple times. If the packet was got via a relay, then the client's packet is stored to an option within the relay's packet...if there were two relays, this recurses. At each of these steps, the root "DHCPv6 option space" is used. Further, a client packet may contain an IA_NA, which may contain an IAADDR - but really, in an abstract sense, this is again re-encapsulation of the DHCPv6 option space beneath options it also contains. Precisely how to correctly support the above conundrums has not quite yet been settled, so support is incomplete. creates a registry at IANA to reserve interface identifiers and specifies a starting set. These IIDs should not be used when constructing addresses to avoid possible conflicts.
defines the SIP server options for DHCPv6. documents the DHCPv6 name-servers and domain-search options. documents the Identity Association Prefix Delegation for DHCPv6, which is included here for protocol wire reference, but which is not supported by ISC DHCP. documents four NIS options for delivering NIS servers and domain information in DHCPv6. defines the DHCPv6 SNTP Servers option. defines the Information Refresh Time option, which advises DHCPv6 Information-Request clients to return for updated information. defines two BCMS server options for each protocol family. defines a DHCPv6 subscriber-id option, which is similar in principle to the DHCPv4 relay agent option of the same name. defines a DHCPv6 remote-id option, which is similar in principle to the DHCPv4 relay agent remote-id.
&rfc760; &rfc768; &rfc894; &rfc951; &rfc1035; &rfc1188; &rfc1542; &rfc2131; &rfc2132; &rfc2241; &rfc2242; &rfc2485; &rfc2610; &rfc2937; &rfc2939; &rfc3004; &rfc3011; &rfc3046; &rfc3074; &rfc3256; &rfc3396; &rfc3397; &rfc3527; &rfc3679; &rfc3925; &rfc3942; &rfc4361; &rfc4388; DHCP Failover Protocol Cisco Systems &rfc3315; &rfc3319; &rfc3633; &rfc3646; &rfc3898; &rfc4075; &rfc4242; &rfc4580; &rfc4649; Address Parameters Option for DHCPv6 Gdansk University of Technology
dhcp-4.4.1/doc/ja_JP.eucJP/dhclient-script.8000644 000765 000024 00000023744 13243301226 020647 0ustar00tmarkstaff000000 000000 .\" $Id: dhclient-script.8,v 1.4 2009/11/24 02:06:56 sar Exp $ .\" .\" Copyright (c) 2004,2009,2016 by Internet Systems Consortium, Inc. ("ISC") .\" Copyright (c) 1996-2003 by Internet Software Consortium .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT .\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" Internet Systems Consortium, Inc. .\" 950 Charter Street .\" Redwood City, CA 94063 .\" .\" https://www.isc.org/ .\" .\" %FreeBSD: src/contrib/isc-dhcp/client/dhclient-script.8,v 1.5.2.4 2002/04/11 10:16:45 murray Exp % .\" .\" $FreeBSD: doc/ja_JP.eucJP/man/man8/dhclient-script.8,v 1.13 2002/05/08 03:27:27 horikawa Exp $ .TH dhclient-script 8 .SH ̾¾Î dhclient-script - DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Î¥Í¥Ã¥È¥ï¡¼¥¯ÀßÄꥹ¥¯¥ê¥×¥È .SH ²òÀâ DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Î¥Í¥Ã¥È¥ï¡¼¥¯ÀßÄꥹ¥¯¥ê¥×¥È¤Ï¡¢ »þ¤¢¤ë¤´¤È¤Ë \fBdhclient(8)\fR ¤¬¸Æ¤Ó½Ð¤·¤Þ¤¹¡£ DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢ËÜ¥¹¥¯¥ê¥×¥È¤ò»ÈÍѤ¹¤ë¤³¤È¤Ë¤è¤ê¡¢ ¥¢¥É¥ì¥¹Í×µá¤ËÀèΩ¤Ä³Æ¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤Î½é´üÀßÄê¤È¡¢ ÉÕÍ¿¤µ¤ì¤¿¥¢¥É¥ì¥¹¤Î¸¡ºº¤È¡¢ ¥ê¡¼¥¹³ÍÆÀ»þ¤Î¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤ÎºÇ½ªÀßÄê¤ò¹Ô¤¤¤Þ¤¹¡£ ¥ê¡¼¥¹¤¬³ÍÆÀ¤µ¤ì¤Ê¤«¤Ã¤¿¾ì¹ç¡¢ ÄêµÁºÑ¤ß¤Î¥ê¡¼¥¹¤¬Â¸ºß¤¹¤ë¤Ê¤é¤Ð¤³¤ì¤ò¸¡ºº¤¹¤ë¤¿¤á¤ËËÜ¥¹¥¯¥ê¥×¥È¤Ï»ÈÍѤµ¤ì¡¢ Í­¸ú¤Ê¥ê¡¼¥¹¤¬È½ÌÀ¤·¤Ê¤«¤Ã¤¿¾ì¹ç¤Ë¤â¤â¤¦ 1 ²ó¤³¤Î¥¹¥¯¥ê¥×¥È¤¬¸Æ¤Ð¤ì¤Þ¤¹¡£ .PP ËÜ¥¹¥¯¥ê¥×¥È¤Ï¡¢¥¨¥ó¥É¥æ¡¼¥¶¤Ë¥«¥¹¥¿¥Þ¥¤¥º¤µ¤ì¤ë¤³¤È¤ò°Õ¿Þ¤·¤Æ¤¤¤Þ¤»¤ó¡£ ¥í¡¼¥«¥ë¤Ê¥«¥¹¥¿¥Þ¥¤¥º¤¬É¬Íפʾì¹ç¡¢ ¤³¤ì¤ÏÆþ (enter) ¤È½Ð (exit) ¤È¤¤¤¦¥Õ¥Ã¥¯¤ò»ÈÍѤ¹¤ë¤³¤È¤Ç²Äǽ¤È¤Ê¤ê¤Þ¤¹ (¾ÜºÙ¤Ï¥Õ¥Ã¥¯»²¾È)¡£ ¤³¤ì¤é¤Î¥Õ¥Ã¥¯¤Ï¡¢ .B /etc/resolv.conf ºîÀ®»þ¤Ë¡¢ ¥¯¥é¥¤¥¢¥ó¥È¤Î¥Ç¥Õ¥©¥ë¥Èưºî¤ò¥æ¡¼¥¶¤¬¥ª¡¼¥Ð¥é¥¤¥É¤Ç¤­¤ë¤è¤¦¤Ë¤·¤Þ¤¹¡£ .PP ÆÃÄê¤Î¥ª¥Ú¥ì¡¼¥Æ¥£¥ó¥°¥·¥¹¥Æ¥à¤Ç¤Ï¡¢ ¥¯¥é¥¤¥¢¥ó¥È¤Î¼ÂÂÎ¤ÏÆ°ºî¤¹¤ë¤È¤·¤Æ¤â¡¢ ɸ½à¤Î¥¹¥¯¥ê¥×¥È¤¬Æ°ºî¤·¤Ê¤¤¤«¤â¤·¤ì¤Þ¤»¤ó¡£ Àè¶îŪ¤Ê¥æ¡¼¥¶¤¬¿·µ¬¥¹¥¯¥ê¥×¥È¤òºîÀ®¤·¤¿¤ê´û¸¤Î¤â¤Î¤ò½¤Àµ¤·¤¿¤ê¤¹¤ëɬÍפ¬¤¢¤ë ¤³¤È¤Ï¤â¤Ã¤È¤â¤Ê¤³¤È¤Ç¤¹¡£ °ìÈÌŪ¤Ë¤Ï¡¢¤½¤ì¤¾¤ì¤Î¥³¥ó¥Ô¥å¡¼¥¿¤Ë¸ÇÍ­¤Î¥«¥¹¥¿¥Þ¥¤¥º¤Ï .B ETCDIR/dhclient.conf ¥¹¥¯¥ê¥×¥È¤Ç¹Ô¤¦¤Ù¤­¤Ç¤¹¡£ .B ETCDIR/dhclient.conf ¤Î¥«¥¹¥¿¥Þ¥¤¥ºÌµ¤·¤Ë¤Ç¤­¤Ê¤¤¥«¥¹¥¿¥Þ¥¤¥º¤ä¡¢ Æþ¤È½Ð¤Î¥Õ¥Ã¥¯¤Î»ÈÍѤǤϤǤ­¤Ê¤¤¥«¥¹¥¿¥Þ¥¤¥º¤Ëµ¤¤Å¤¤¤¿¾ì¹ç¤Ë¤Ï¡¢ ¥Ð¥°¥ì¥Ý¡¼¥È¤òÁ÷¤Ã¤Æ¤¯¤À¤µ¤¤¡£ .SH ¥Õ¥Ã¥¯ ³«»Ï»þ¤Ë¡¢¥¯¥é¥¤¥¢¥ó¥È¥¹¥¯¥ê¥×¥È¤Ï¤Þ¤º¥·¥§¥ë´Ø¿ô¤òÄêµÁ¤·¤Þ¤¹¡£¤½¤Î´Ø¿ô¤Ï .B make_resolv_conf ¤Ç¤¢¤ê¡¢¸å¤Ë .B /etc/resolv.conf ¥Õ¥¡¥¤¥ë¤òºîÀ®¤¹¤ë¤¿¤á¤Ë»ÈÍѤµ¤ì¤Þ¤¹¡£ ¥Ç¥Õ¥©¥ë¥Èưºî¤ò¥ª¡¼¥Ð¥é¥¤¥É¤¹¤ë¤Ë¤Ï¡¢ ¤³¤Î´Ø¿ô¤òÆþ¤Î¥Õ¥Ã¥¯¥¹¥¯¥ê¥×¥È¤ÇºÆÄêµÁ¤·¤Æ¤¯¤À¤µ¤¤¡£ .PP make_resolv_conf ´Ø¿ô¤ÎÄêµÁ¤Î¸å¡¢¥¯¥é¥¤¥¢¥ó¥È¥¹¥¯¥ê¥×¥È¤Ï ¼Â¹Ô²Äǽ¤Ê .B ETCDIR/dhclient-enter-hooks ¥¹¥¯¥ê¥×¥È¤Î¸ºß¤ò¸¡ºº¤·¡¢ ¸ºß¤¹¤ë¾ì¹ç¤Ë¤Ï Bourne ¥·¥§¥ë¤Î '.' ¥³¥Þ¥ó¥É¤ò»ÈÍѤ·¤Æ ËÜ¥¹¥¯¥ê¥×¥È¤ò¥¤¥ó¥é¥¤¥ó¤Çµ¯Æ°¤·¤Þ¤¹¡£ Áàºî¤Çµ­½Ò¤µ¤ì¤Æ¤¤¤ë¤¹¤Ù¤Æ¤Î´Ä¶­¤¬ËÜ¥¹¥¯¥ê¥×¥È¤Ç»ÈÍѲÄǽ¤Ç¤¢¤ê¡¢ ¥¹¥¯¥ê¥×¥È¤Îưºî¤ÎÊѹ¹¤¬É¬Íפʾì¹ç¤Ë¤Ï´Ä¶­¤Î½¤Àµ¤¬µö¤µ¤ì¤Æ¤¤¤Þ¤¹¡£ ¥¹¥¯¥ê¥×¥È¼Â¹ÔÃæ¤Ë¥¨¥é¡¼¤¬È¯À¸¤·¤¿¾ì¹ç¡¢ exit_status ÊÑ¿ô¤òÈó 0 ÃͤËÀßÄꤹ¤ë¤³¤È¤¬²Äǽ¤Ç¤¢¤ê¡¢ ¥¯¥é¥¤¥¢¥ó¥È¥¹¥¯¥ê¥×¥È½ªÎ»Ä¾¸å¤Ë .B CLIENTBINDIR/dhclient-script ¤Ï¤½¤Î¥¨¥é¡¼¥³¡¼¥É¤Ç½ªÎ»¤·¤Þ¤¹¡£ .PP ¤¹¤Ù¤Æ¤Î½èÍý¤Î´°Î»¸å¤Ë¡¢ .B CLIENTBINDIR/dhclient-script ¤Ï¼Â¹Ô²Äǽ¤Ê .B ETCDIR/dhclient-exit-hooks ¥¹¥¯¥ê¥×¥È¤Î¸ºß¤ò¸¡ºº¤·¡¢Â¸ºß¤¹¤ë¾ì¹ç¤Ë¤Ï '.' ¥³¥Þ¥ó¥É¤Ç¤³¤ì¤òµ¯Æ°¤·¤Þ¤¹¡£ dhclient-script ¤Î ½ªÎ»¾õÂÖ¤Ï dhclient-exit-hooks ¤Î exit_status ¥·¥§¥ëÊÑ¿ô¤ËÅϤµ¤ì¡¢ µ¯Æ°¤µ¤ì¤¿»Å»ö¤Ë¥¹¥¯¥ê¥×¥È¤¬À®¸ù¤·¤¿¾ì¹ç¤Ë¤ÏÃͤϾï¤Ë 0 ¤Ë¤Ê¤ê¤Þ¤¹¡£ dhclient-enter-hooks ¤Î¹à¤ÇÁ°½Ò¤·¤¿¤½¤Î¾¤Î´Ä¶­¤â°ú¤­·Ñ¤¬¤ì¤Þ¤¹¡£ .B ETCDIR/dhclient-exit-hooks ¤Ï exit_status ¤Ë¼ê¤ò²Ã¤¨¤Æ dhclient-script ¤ÎÌá¤êÃͤòÊѹ¹¤Ç¤­¤Þ¤¹¡£ .SH Áàºî dhclient ¤¬¥¯¥é¥¤¥¢¥ó¥ÈÀßÄꥹ¥¯¥ê¥×¥È¤òµ¯Æ°¤¹¤ëɬÍפ¬¤¢¤ë¤È¤­¡¢ ÍÍ¡¹¤ÊÊÑ¿ô¤ò´Ä¶­¤ËÄêµÁ¤·¤Æ¤«¤é .B CLIENTBINDIR/dhclient-script ¤òµ¯Æ°¤·¤Þ¤¹¡£ ¤¹¤Ù¤Æ¤Î¾ì¹ç¤Ë¤ª¤¤¤Æ¡¢$reason ¤Ë¤Ï¥¹¥¯¥ê¥×¥È¤¬µ¯Æ°¤µ¤ì¤ëÍýͳ̾¤¬ÀßÄꤵ¤ì¤Þ¤¹¡£ ¼¡¤ÎÍýͳ¤¬¸½ºßÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤¹: MEDIUM, PREINIT, BOUND, RENEW, REBIND, REBOOT, EXPIRE, FAIL, TIMEOUT¡£ .PP .SH MEDIUM DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤Î¥á¥Ç¥£¥¢¥¿¥¤¥×¤ÎÀßÄê¤òµá¤á¤Æ¤¤¤Þ¤¹¡£ ¥¤¥ó¥¿¥Õ¥§¡¼¥¹Ì¾¤Ï $interface ¤ÇÅϤµ¤ì¡¢¥á¥Ç¥£¥¢¥¿¥¤¥×¤Ï $medium ¤ÇÅϤµ¤ì¤Þ¤¹¡£ .SH PREINIT DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢ ¼ÂºÝ¤Î¥¢¥É¥ì¥¹¤ò¼õ¤±¼è¤ëÁ°¤Ë¥Ñ¥±¥Ã¥È¤òÁ÷¿®¤¹¤ëÌÜŪ¤Ç¡¢ Í×µáÄ̤ê¤Ë¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤¬ÀßÄꤵ¤ì¤ë¤³¤È¤òµá¤á¤Æ¤¤¤Þ¤¹¡£ BSD ¤Î¥½¥±¥Ã¥È¥é¥¤¥Ö¥é¥ê¤ò»ÈÍѤ¹¤ë¥¯¥é¥¤¥¢¥ó¥È¤Ç¤Ï¡¢ IP ¥¢¥É¥ì¥¹ 0.0.0.0 ¤«¤Ä¥Ö¥í¡¼¥É¥­¥ã¥¹¥È¥¢¥É¥ì¥¹ 255.255.255.255 ¤Ç¡¢ ¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤òÀßÄꤹ¤ë¤³¤È¤ò°ÕÌ£¤·¤Þ¤¹¡£ ¾¤Î¥¯¥é¥¤¥¢¥ó¥È¤Ç¤Ï¡¢ ¼ÂºÝ¤Ë IP ¥¢¥É¥ì¥¹¤òÍ¿¤¨¤ë¤³¤È¤Ê¤¯Ã±¤Ë¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤òÀßÄꤹ¤ë¤³¤È¤Ç ¼Â¸½¤µ¤ì¤ë¤Ç¤·¤ç¤¦¡£ ¥¤¥ó¥¿¥Õ¥§¡¼¥¹Ì¾¤Ï $interface ¤ÇÅϤµ¤ì¡¢¥á¥Ç¥£¥¢¥¿¥¤¥×¤Ï $medium ¤ÇÅϤµ¤ì¤Þ¤¹¡£ .PP IP ¥¨¥¤¥ê¥¢¥¹¤¬ dhclient.conf ¤ÇÀë¸À¤µ¤ì¤Æ¤¤¤ë¾ì¹ç¡¢ ¤³¤Î¥¢¥É¥ì¥¹¤¬ $alias_ip_address ¤ÇÅϤµ¤ì¤Þ¤¹¡£ ËÜ IP ¥¢¥É¥ì¥¹¤Ø¤Î·ÐÏ©¤È¤È¤â¤Ë¡¢ ËÜ IP ¥¢¥É¥ì¥¹¤òÂоݥ¤¥ó¥¿¥Õ¥§¡¼¥¹¤«¤éºï½ü¤¹¤ëɬÍפ¬¤¢¤ê¤Þ¤¹¡£ .SH BOUND DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢¿·¥¢¥É¥ì¥¹¤Ø¤Î½é´ü¤Î·ë¹ç¤ò´°Î»¤·¤Þ¤·¤¿¡£ ¿·¤·¤¤ IP ¥¢¥É¥ì¥¹¤Ï $new_ip_address ¤ÇÅϤµ¤ì¡¢ ¥¤¥ó¥¿¥Õ¥§¡¼¥¹Ì¾¤Ï $interface ¤ÇÅϤµ¤ì¤Þ¤¹¡£ ¥á¥Ç¥£¥¢¥¿¥¤¥×¤Ï $medium ¤ÇÅϤµ¤ì¤Þ¤¹¡£ ¥µ¡¼¥Ð¤«¤é³ÍÆÀ¤·¤¿¥ª¥×¥·¥ç¥ó¤Ï¡¢\fBdhcp-options\fR ¤ÇÀë¸À¤µ¤ì¤Æ¤¤¤ë ¥ª¥×¥·¥ç¥ó̾¤ÇÅϤµ¤ì¤Þ¤¹¡£ Îã³°¤È¤·¤Æ¡¢ Í­¸ú¤Ê¥·¥§¥ëÊÑ¿ô¤È¤¹¤ë¤¿¤á¤Ë ¥À¥Ã¥·¥å ('-') ¤Ï¥¢¥ó¥À¥¹¥³¥¢('_')¤ÇÃÖ¤­´¹¤¨¤é¤ì¡¢ ÊÑ¿ô̾¤Ï new_ ¤Ç³«»Ï¤·¤Þ¤¹¡£ Î㤨¤Ð¡¢¿·¤·¤¤¥µ¥Ö¥Í¥Ã¥È¥Þ¥¹¥¯¤Ï $new_subnet_mask ¤ÇÅϤµ¤ì¤Þ¤¹¡£ .PP ¥¢¥É¥ì¥¹¤ò¼ÂºÝ¤ËÀßÄꤹ¤ëÁ°¤Ë¡¢dhclient-script ¤Ï²¿¤é¤«¤ÎÊýË¡¤Ç ¤½¤Î¥¢¥É¥ì¥¹¤ËÂФ·¤Æ ARP ¤ò¹Ô¤¤¡¢ÊÖ»ö¤ò¼õ¤±¼è¤Ã¤¿¾ì¹ç¤Ë¤ÏÈó 0 ¤ÎÃÍ¤Ç ½ªÎ»¤¹¤ë¤Ù¤­¤Ç¤¹¡£¤³¤Î¾ì¹ç¥¯¥é¥¤¥¢¥ó¥È¤Ï DHCPDECLINE ¥á¥Ã¥»¡¼¥¸¤ò¥µ¡¼¥Ð ¤ËÁ÷¿®¤·¡¢°ã¤¦¥¢¥É¥ì¥¹¤ò¼èÆÀ¤·¤Þ¤¹¡£ ¤³¤Îºî¶È¤Ï RENEW, REBIND, REBOOT ¾õÂÖ¤Ç¤âÆ±Íͤ˹Ԥ¤¤Þ¤¹¤¬¡¢ ɬ¤º¤·¤âɬÍפǤϤʤ¯¡¢¼ÂºÝ¹¥¤Þ¤·¤¯¤Ê¤¤¤Ç¤·¤ç¤¦¡£ .PP ·ë¹ç¤¬´°Î»¤¹¤ë¤È¡¢ ¥Í¥Ã¥È¥ï¡¼¥¯¤Ë´Ø¤¹¤ë¿¤¯¤Î¥Ñ¥é¥á¡¼¥¿¤òÀßÄꤹ¤ëɬÍפ¬¤¢¤ë¤Ç¤·¤ç¤¦¡£ $new_domain_name ¤ª¤è¤Ó $new_domain_name_servers (¤³¤ì¤Ë¤ÏÊ£¿ô¤Î¥µ¡¼¥Ð¤ò¶õÇò¤Ç¶èÀڤäÆÎóµó¤·¤Æ¤¢¤ë¤«¤â¤·¤ì¤Þ¤»¤ó) ¤ò»ÈÍѤ·¤Æ¡¢ ¿·¤·¤¤ /etc/resolv.conf ¤òºîÀ®¤¹¤ëɬÍפ¬¤¢¤ê¤Þ¤¹¡£ ¥Ç¥Õ¥©¥ë¥È·ÐÏ©¤Ï¡¢$new_routers ¤ò»ÈÍѤ·¤ÆÀßÄꤹ¤ëɬÍפ¬¤¢¤ê¤Þ¤¹¡£ ÀÅŪ·ÐÏ©¤Ï¡¢$new_static_routes ¤ò»ÈÍѤ·¤ÆÀßÄꤹ¤ëɬÍפ¬¤¢¤ë¤«¤â¤·¤ì¤Þ¤»¤ó¡£ .PP IP ¥¨¥¤¥ê¥¢¥¹¤¬Àë¸À¤µ¤ì¤Æ¤¤¤ë¾ì¹ç¡¢¤³¤³¤ÇÀßÄꤹ¤ëɬÍפ¬¤¢¤ê¤Þ¤¹¡£ ¥¨¥¤¥ê¥¢¥¹¤Î IP ¥¢¥É¥ì¥¹¤Ï $alias_ip_address ¤È¤·¤Æµ­½Ò¤µ¤ì¡¢ ¥¨¥¤¥ê¥¢¥¹ÍѤËÀßÄꤵ¤ì¤ë¾¤Î DHCP ¥ª¥×¥·¥ç¥ó (Î㤨¤Ð¥µ¥Ö¥Í¥Ã¥È¥Þ¥¹¥¯) ¤Ï Á°½Ò¤Î¤è¤¦¤ËÊÑ¿ô¤ÇÅϤµ¤ì¤Þ¤¹¤¬¡¢ $new_ ¤Ç³«»Ï¤¹¤ë¤Î¤Ç¤Ï¤Ê¤¯ $alias_ ¤Ç³«»Ï¤·¤Þ¤¹¡£ ¥¨¥¤¥ê¥¢¥¹¤Î IP ¥¢¥É¥ì¥¹¤¬·ë¹ç¤µ¤ì¤¿ IP ¥¢¥É¥ì¥¹ ($new_ip_address) ¤È Ʊ¤¸¾ì¹ç¡¢¤³¤ì¤ò»ÈÍѤ·¤Æ¤Ï¤Ê¤é¤Ê¤¤¤³¤È¤ËÃí°Õ¤·¤Æ¤¯¤À¤µ¤¤¡£ ¤Ê¤¼¤Ê¤é¡¢¤³¤Î¾ì¹ç¤Ë¤Ï¾¤Î¥¨¥¤¥ê¥¢¥¹¤Î¥Ñ¥é¥á¡¼¥¿¤¬Àµ¤·¤¯¤Ê¤¤²ÄǽÀ­¤¬¤¢¤ë ¤«¤é¤Ç¤¹¡£ .SH RENEW ·ë¹ç¤¬¹¹¿·¤µ¤ì¤ë¤È¡¢¥¹¥¯¥ê¥×¥È¤Ï BOUND ¤ÈƱÍͤ˸ƤФì¤Þ¤¹¤¬¡¢ $new_ ¤Ç³«»Ï¤¹¤ëÁ´ÊÑ¿ô¤Ë²Ã¤¨¤Æ $old ¤Ç³«»Ï¤¹¤ëÊ̤ÎÊÑ¿ô¤ÎÁȤ¬¤¢¤ë¤È¤¤¤¦ Îã³°¤¬¤¢¤ê¤Þ¤¹¡£ Êѹ¹¤µ¤ì¤¿²ÄǽÀ­¤¬¤¢¤ë±Ê³Ū¤ÊÀßÄê¤Ï¡¢ºï½ü¤¹¤ëɬÍפ¬¤¢¤ê¤Þ¤¹¡£ Î㤨¤Ð¡¢·ë¹ç¤µ¤ì¤¿¥¢¥É¥ì¥¹¤ËÂФ¹¤ë¥í¡¼¥«¥ë·ÐÏ©¤¬ÀßÄꤵ¤ì¤¿¾ì¹ç¡¢ ¸Å¤¤¥í¡¼¥«¥ë·ÐÏ©¤òºï½ü¤¹¤ëɬÍפ¬¤¢¤ê¤Þ¤¹¡£ ¥Ç¥Õ¥©¥ë¥È·ÐÏ©¤¬Êѹ¹¤µ¤ì¤¿¾ì¹ç¡¢¸Å¤¤¥Ç¥Õ¥©¥ë¥È·ÐÏ©¤òºï½ü¤¹¤ëɬÍפ¬¤¢¤ê¤Þ¤¹¡£ ÀÅŪ·ÐÏ©¤¬Êѹ¹¤µ¤ì¤¿¾ì¹ç¡¢¸Å¤¤¤â¤Î¤òºï½ü¤¹¤ëɬÍפ¬¤¢¤ê¤Þ¤¹¡£ ¤½¤Î¾¤Ë¤Ä¤¤¤Æ¤Ï¡¢BOUND ¤ÈƱÍͤ˽èÍý²Äǽ¤Ç¤¹¡£ .SH REBIND DHCP ¥¯¥é¥¤¥¢¥ó¥È¤¬¡¢¿·µ¬ DHCP ¥µ¡¼¥Ð¤ËºÆ·ë¹ç¤µ¤ì¤Þ¤·¤¿¡£ ¤³¤ì¤Ï RENEW ¤ÈƱÍͤ˰·¤¨¤Þ¤¹¤¬¡¢IP ¥¢¥É¥ì¥¹¤¬ÊѤï¤Ã¤¿¾ì¹ç¤Ë¤Ï¡¢ ARP ɽ¤ò¥¯¥ê¥¢¤¹¤ëɬÍפ¬¤¢¤ê¤Þ¤¹¡£ .SH REBOOT DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢¥ê¥Ö¡¼¥È¸å¤Ë¸µ¤Î¥¢¥É¥ì¥¹¤òºÆ³ÍÆÀ¤¹¤ë¤³¤È¤ËÀ®¸ù¤·¤Þ¤·¤¿¡£ ¤³¤ì¤Ï BOUND ¤ÈƱÍͤ˽èÍý²Äǽ¤Ç¤¹¡£ .SH EXPIRE DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¥ê¡¼¥¹¹¹¿·¤È¿·µ¬¥ê¡¼¥¹³ÍÆÀ¤Ë¼ºÇÔ¤·¡¢ ¥ê¡¼¥¹¤Î´ü¸Â¤¬ÀÚ¤ì¤Þ¤·¤¿¡£ ÂÐ¾Ý IP ¥¢¥É¥ì¥¹¤ò²òÊü¤¹¤ëɬÍפ¬¤¢¤ê¡¢ RENEW ¤ª¤è¤Ó REBIND ¤ÈƱÍͤˡ¢´ØÏ¢¤¹¤ë¥Ñ¥é¥á¡¼¥¿¤òºï½ü¤¹¤ëɬÍפ¬¤¢¤ê¤Þ¤¹¡£ .SH FAIL DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï DHCP ¥µ¡¼¥Ð¤ËÀܳ¤Ç¤­¤º¡¢ ¤Þ¤¿¸¡ºº¤·¤¿ IP ¥¢¥É¥ì¥¹¤Ë¤ÏÍ­¸ú¤Ê¤â¤Î¤Ï¤¢¤ê¤Þ¤»¤ó¤Ç¤·¤¿¡£ ºÇ¸å¤Ë¸¡ºº¤·¤¿¥ê¡¼¥¹¤Î¥Ñ¥é¥á¡¼¥¿¤Ï¡¢ÀßÄê²ò½ü¤¹¤ëɬÍפ¬¤¢¤ê¤Þ¤¹¡£ ¤³¤ì¤Ï¡¢EXPIRE ¤ÈƱÍͤ˰·¤¨¤Þ¤¹¡£ .SH TIMEOUT DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¤É¤Î DHCP ¥µ¡¼¥Ð¤Ë¤âÀܳ¤Ç¤­¤Þ¤»¤ó¤Ç¤·¤¿¡£ ¤·¤«¤·¤Ê¤¬¤é¡¢¸Å¤¤¥ê¡¼¥¹¤¬¼±Ê̤µ¤ì¡¢ BOUND ¤ÈƱÍͤˡ¢¤³¤Î¸Å¤¤¥ê¡¼¥¹¤Î¥Ñ¥é¥á¡¼¥¿¤¬ÅϤµ¤ì¤Þ¤·¤¿¡£ ¥¯¥é¥¤¥¢¥ó¥È¤ÎÀßÄꥹ¥¯¥ê¥×¥È¤Ï¡¢¤³¤Î¥Ñ¥é¥á¡¼¥¿¤ò¸¡ºº¤·¡¢ ¤³¤ì¤¬Í­¸ú¤Ç¤¢¤ë¤È¿®¤¸¤ëÍýͳ¤¬¤¢¤ë¤Ê¤é¤Ð¡¢ÃÍ 0 ¤Ç½ªÎ»¤¹¤Ù¤­¤Ç¤¹¡£ ¤½¤¦¤Ç¤Ê¤¤¤Ê¤é¤Ð¡¢Èó 0 ¤ÎÃͤǽªÎ»¤¹¤Ù¤­¤Ç¤¹¡£ .PP ¥ê¡¼¥¹¤ò¸¡ºº¤¹¤ëÄ̾ï¤ÎÊýË¡¤Ï¡¢REBIND ¤ÈƱÍͤ˥ͥåȥ¥¯¤òÀßÄꤷ¤Æ (Ê£¿ô¤Î¥ê¡¼¥¹¤ò¸¡ºº¤¹¤ë¤¿¤á¤Ë¸Æ¤Ð¤ì¤ë¤³¤È¤¬¤¢¤ë¤«¤é¤Ç¤¹)¡¢ $routers ¤ÇÄêµÁ¤µ¤ì¤ëºÇ½é¤Î¥ë¡¼¥¿¤Ë ping ¤¹¤ë¤³¤È¤Ç¤¹¡£ ±þÅú¤ò¼õ¿®¤·¤¿¾ì¹ç¡¢ ¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤¬¸½ºßÀܳ¤µ¤ì¤Æ¤¤¤ë¥Í¥Ã¥È¥ï¡¼¥¯¤ËÂФ·¤Æ¡¢¥ê¡¼¥¹¤¬Í­¸ú¤Ç¤¹¡£ $new_static_routers ¤Ë²Ã¤¨¤Æ $new_routers ¤ËÎóµó¤µ¤ì¤Æ¤¤¤ëÁ´¥ë¡¼¥¿¤Ë ping ¤ò»î¤¹¤è¤¦¤Ë¤Ê¤ì¤Ð¡¢ ´°Á´À­¤¬Áý¤¹¤Ç¤·¤ç¤¦¡£¤·¤«¤·¡¢¸½ºß¤Î¥¹¥¯¥ê¥×¥È¤Ï¤½¤¦¤Ê¤Ã¤Æ¤¤¤Þ¤»¤ó¡£ .SH ´ØÏ¢¥Õ¥¡¥¤¥ë Îà»÷¤·¤¿¥ª¥Ú¥ì¡¼¥Æ¥£¥ó¥°¥·¥¹¥Æ¥à¤ËÂФ¹¤ë¥¹¥¯¥ê¥×¥È¥Õ¥¡¥¤¥ë¤Ï »÷¤Æ¤¤¤¿¤êÁ´¤¯Æ±¤¸¤«¤â¤·¤ì¤Þ¤»¤ó¤¬¡¢°ìÈ̤ˤϡ¢ ³Æ¥ª¥Ú¥ì¡¼¥Æ¥£¥ó¥°¥·¥¹¥Æ¥àÍѤ˳ơ¹¤Î¥¹¥¯¥ê¥×¥È¥Õ¥¡¥¤¥ë¤¬¤¢¤ë¤Ù¤­¤Ç¤¹¡£ Internet Systems Consortium ¤Î DHCP ÇÛÉۤ˴ޤޤì¤ë¥¹¥¯¥ê¥×¥È¥Õ¥¡¥¤¥ë¤Ï¡¢ client/scripts °Ê²¼¤ÎÇÛÉۥĥ꡼¤Ë¤¢¤ê¡¢ ưºîÂоݥª¥Ú¥ì¡¼¥Æ¥£¥ó¥°¥·¥¹¥Æ¥à̾¤Ë¤Ê¤Ã¤Æ¤¤¤Þ¤¹¡£ .SH ¥Ð¥° Ê£¿ô¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤ò»ÈÍѤ¹¤ë¾ì¹ç¡¢ ¥µ¡¼¥Ð¤¬Ä󶡤¹¤ëÀßÄê¥Ñ¥é¥á¡¼¥¿Æ±»Î¤¬ ¾×ÆÍ¤·¤Ê¤¤¤è¤¦¤Ë¤¹¤ëÌÀ³Î¤ÊÊýË¡¤Ï¤¢¤ê¤Þ¤»¤ó¡£ Î㤨¤Ð¡¢ ɸ½à¤Î dhclient-script ¤Ï /etc/resolv.conf ¤òºÆÅÙ½ñ¤­´¹¤¨¤Æ¤·¤Þ¤¤¤Þ¤¹¡£ ¤¹¤Ê¤ï¤Á¡¢Ê£¿ô¤Î¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤¬ÀßÄꤵ¤ì¤Æ¤¤¤ë¾ì¹ç¡¢ ¤¢¤ë¥µ¡¼¥Ð¤«¤éÄ󶡤µ¤ì¤ëÃÍ¤Ë /etc/resolv.conf ¤¬½é´ü²½¤µ¤ì¤¿¸å¤Ë¡¢ Ê̤Υµ¡¼¥Ð¤«¤éÄ󶡤µ¤ì¤ëÃͤ˽é´ü²½¤µ¤ì¤ë¤È¤¤¤¦Æ°ºî¤ò·«¤êÊÖ¤·¤Þ¤¹¡£ ¤É¤Á¤é¤Î¥µ¡¼¥Ð¤«¤éÄ󶡤µ¤ì¤ë¾ðÊó¤âÍ­¸ú¤Ç¤¢¤ë¾ì¹ç¤Ë¤Ï¡¢ ¼ÂºÝ¾åÌäÂê¤È¤Ï¤Ê¤é¤Ê¤¤¤â¤Î¤Î¡¢º®Íð¤Î¤â¤È¤Ë¤Ê¤ê¤¨¤Þ¤¹¡£ .SH ´ØÏ¢¹àÌÜ dhclient.conf(5), dhclient.leases(5), dhclient(8) .SH ºî¼Ô .B dhclient-script(8) ¤Ï Ted Lemon ¤¬ Vixie Enterprises ¤È¶¨ÎϤ·¤Æ Internet Systems Consortium ¤Î¤¿¤á¤Ë ½ñ¤­¤Þ¤·¤¿¡£ Internet Systems Consortium ¤Ë¤Ä¤¤¤Æ¤è¤ê¾Ü¤·¤¯¤Ï¡¢ .B https://www.isc.org ¤ò¤´Í÷¤¯¤À¤µ¤¤¡£ Vixie Enterprises ¤Ë¤Ä¤¤¤Æ¤è¤ê¾Ü¤·¤¯¤Ï¡¢ .B http://www.vix.com ¤ò¤´Í÷¤¯¤À¤µ¤¤¡£ dhcp-4.4.1/doc/ja_JP.eucJP/dhclient.8000644 000765 000024 00000030277 13243301226 017344 0ustar00tmarkstaff000000 000000 .\" $Id: dhclient.8,v 1.4 2009/11/24 02:06:56 sar Exp $ .\" .\" Copyright (c) 2004,2009 by Internet Systems Consortium, Inc. ("ISC") .\" Copyright (c) 1996-2003 by Internet Software Consortium .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT .\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" Internet Systems Consortium, Inc. .\" 950 Charter Street .\" Redwood City, CA 94063 .\" .\" https://www.isc.org/ .\" .\" Portions copyright (c) 2000 David E. O'Brien. .\" All rights reserved. .\" %FreeBSD: src/contrib/isc-dhcp/client/dhclient.8,v 1.8.2.3 2002/04/11 10:16:45 murray Exp % .\" .\" $FreeBSD: doc/ja_JP.eucJP/man/man8/dhclient.8,v 1.8 2002/05/21 03:46:48 horikawa Exp $ .\" WORD: Dynamic Host Configuration Protocol (DHCP) ưŪ¥Û¥¹¥ÈÀßÄê¥×¥í¥È¥³¥ë .\" WORD: lease ¥ê¡¼¥¹ [dhclient.8] .\" WORD: mobile host °Üư¥Û¥¹¥È .\" WORD: limited broadcast address ¥ê¥ß¥Æ¥Ã¥É¥Ö¥í¡¼¥É¥­¥ã¥¹¥È¥¢¥É¥ì¥¹ .\" WORD: networking framework ¥Í¥Ã¥È¥ï¡¼¥­¥ó¥°¥Õ¥ì¡¼¥à¥ï¡¼¥¯ .\" WORD: housekeeping chores »¨»ö .TH dhclient 8 .SH ̾¾Î dhclient - ưŪ¥Û¥¹¥ÈÀßÄê¥×¥í¥È¥³¥ë¤Î¥¯¥é¥¤¥¢¥ó¥È .SH ½ñ¼° .B dhclient [ .B -p .I port ] [ .B -D ] [ .B -d ] [ .B -q ] [ .B -1 ] [ .B -r ] [ .B -lf .B lease-file ] [ .B -pf .I pid-file ] [ .B -cf .I config-file ] [ .B -sf .I script-file ] [ .B -s server ] [ .B -g relay ] [ .B -n ] [ .B -nw ] [ .B -w ] [ .I if0 [ .I ...ifN ] ] .SH ²òÀâ Internet Systems Consortium ¤Î DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ç¤¢¤ë dhclient ¤ÏưŪ¥Û¥¹¥ÈÀßÄê¥×¥í¥È¥³¥ë (DHCP: Dynamic Host Configuration Protocol) ¤Þ¤¿¤Ï BOOTP ¥×¥í¥È¥³¥ë¤òÍѤ¤¤Æ¡¢¤¢¤ë¤¤¤Ï ¤³¤ì¤é¤Î¥×¥í¥È¥³¥ë¤¬¼ºÇÔ¤·¤¿¾ì¹ç¤Ë¤Ï¥¢¥É¥ì¥¹¤òÀÅŪ¤Ë³ä¤êÅö¤Æ¤Æ¡¢ 1 ¤Ä°Ê¾å¤Î¥Í¥Ã¥È¥ï¡¼¥¯¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤òÀßÄꤹ¤ëÊýË¡¤òÄ󶡤·¤Þ¤¹¡£ .SH Áàºî .PP DHCP ¥×¥í¥È¥³¥ë¤Ç¤Ï¡¢1 ¤Ä°Ê¾å¤Î¥µ¥Ö¥Í¥Ã¥È¤Ë³ä¤êÅö¤Æ¤ë¤³¤È¤Î¤Ç¤­¤ë IP ¥¢¥É¥ì¥¹¤Î¥ê¥¹¥È¤ò´ÉÍý¤¹¤ëÃæ±û¥µ¡¼¥Ð¤Ë¡¢¥Û¥¹¥È¤¬¥¢¥¯¥»¥¹¤Ç¤­¤Þ¤¹¡£ DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¤³¤Î¥ê¥¹¥È¤«¤é¥¢¥É¥ì¥¹¤òÍ׵ᤷ¤Æ¡¢ ¤½¤ì¤ò¥Í¥Ã¥È¥ï¡¼¥¯ÄÌ¿®¤Î°ì»þŪ¤ÊÅÚÂæ¤ËÍѤ¤¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£ ¤Þ¤¿ DHCP ¥×¥í¥È¥³¥ë¤Ï¡¢¥Ç¥Õ¥©¥ë¥È¥ë¡¼¥¿¤Î¾ì½ê¤ä¥Í¡¼¥à¥µ¡¼¥Ð¤Î¾ì½ê¤Ê¤É¡¢ ¥¯¥é¥¤¥¢¥ó¥È¤¬Àܳ¤·¤Æ¤¤¤ë¥Í¥Ã¥È¥ï¡¼¥¯¤Ë´Ø¤¹¤ë½ÅÍפʾðÊó¤ò ¥¯¥é¥¤¥¢¥ó¥È¤Ë¾ÜºÙ¤ËÃΤ餻¤ëµ¡¹½¤âÄ󶡤·¤Þ¤¹¡£ .PP µ¯Æ°»þ¤Ë dhclient ¤Ï .IR dhclient.conf ¤«¤éÀßÄê»Ø¼¨¤òÆÉ¤ß¼è¤ê¤Þ¤¹¡£ ¤½¤ì¤«¤é¸½ºß¤Î¥·¥¹¥Æ¥à¤ËÁȤ߹þ¤Þ¤ì¤Æ¤¤¤ë ¤¹¤Ù¤Æ¤Î¥Í¥Ã¥È¥ï¡¼¥¯¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤Î¥ê¥¹¥È¤ò¼èÆÀ¤·¤Þ¤¹¡£ ³Æ¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤ËÂФ· dhclient ¤Ï DHCP ¥×¥í¥È¥³¥ë¤òÍѤ¤¤ÆÀßÄê¤ò»î¤ß¤Þ¤¹¡£ .PP ¥·¥¹¥Æ¥à¥ê¥Ö¡¼¥È¤ä¥µ¡¼¥ÐºÆµ¯Æ°¤ÎºÝ¤Ë¥ê¡¼¥¹¤ò¼º¤ï¤Ê¤¤¤è¤¦¤Ë¡¢ dhclient ¤Ï³ä¤êÅö¤Æ¤é¤ì¤¿¥ê¡¼¥¹¤Î¥ê¥¹¥È¤ò dhclient.leases(5) ¥Õ¥¡¥¤¥ë¤ËÊݸ¤·¤Þ¤¹¡£ µ¯Æ°»þ¡¢dhclient.conf ¥Õ¥¡¥¤¥ë¤òÆÉ¤ß¼è¤Ã¤¿¸å¡¢ dhclient ¤Ï dhclient.leases ¥Õ¥¡¥¤¥ë¤òÆÉ¤ß¹þ¤ó¤Ç¡¢ ³ä¤êÅö¤Æ¤é¤ì¤¿¥ê¡¼¥¹¤Ë´Ø¤¹¤ë¥á¥â¥ê¤ò¹¹¿·¤·¤Þ¤¹¡£ .PP ¿·¤·¤¤¥ê¡¼¥¹¤ò¼èÆÀ¤¹¤ë¤È¡¢dhclient.leases ¥Õ¥¡¥¤¥ë¤ÎËöÈø¤ËÉÕ¤±²Ã¤¨¤é¤ì¤Þ¤¹¡£ ¥Õ¥¡¥¤¥ë¤¬¶Ëü¤ËÂ礭¤¯¤Ê¤ë¤Î¤òËɤ°¤¿¤á¤Ë¡¢ dhclient ¤Ï»þ¤ª¤ê¥³¥¢ÆâÉô¤Î¥ê¡¼¥¹¥Ç¡¼¥¿¥Ù¡¼¥¹¤«¤é ¿·µ¬¤Ë dhclient.leases ¥Õ¥¡¥¤¥ë¤òºîÀ®¤·¤Þ¤¹¡£ ¸Å¤¤ dhclient.leases ¥Õ¥¡¥¤¥ë¤Ï¡¢ dhclient ¤¬¼¡¤Ë¥Ç¡¼¥¿¥Ù¡¼¥¹¤òºî¤êÂØ¤¨¤ë¤Þ¤Ç¡¢ .IR dhclient.leases~ ¤È¤¤¤¦Ì¾Á°¤ÇÊݸ¤µ¤ì¤Þ¤¹¡£ .PP dhclient ¤¬ºÇ½é¤Ëµ¯Æ°¤µ¤ì¤¿¤È¤­ (°ìÈÌŪ¤Ë¤Ï¥·¥¹¥Æ¥à¥Ö¡¼¥È½é´ü²áÄø¤Î´Ö) ¤Ë DHCP ¥µ¡¼¥Ð¤¬ÍøÍѤǤ­¤Ê¤±¤ì¤Ð¡¢ ¸Å¤¤¥ê¡¼¥¹¤Ï»Ä¤µ¤ì¤Þ¤¹¡£ ¤½¤Î¾ì¹ç¡¢dhclient.leases ¥Õ¥¡¥¤¥ë¤«¤é ¤Þ¤À´ü¸Â¤ÎÀÚ¤ì¤Æ¤¤¤Ê¤¤¸Å¤¤¥ê¡¼¥¹¤ò¸¡ºº¤·¡¢ Í­¸ú¤Ç¤¢¤ë¤ÈȽÃǤµ¤ì¤ì¤Ð¡¢¤½¤ì¤é¤Î´ü¸Â¤¬ÀÚ¤ì¤ë¤« ¤Þ¤¿¤Ï DHCP ¥µ¡¼¥Ð¤¬ÍøÍѤǤ­¤ë¤è¤¦¤Ë¤Ê¤ë¤Þ¤Ç¡¢¤½¤Î¥ê¡¼¥¹¤ò»È¤¤¤Þ¤¹¡£ .PP DHCP ¥µ¡¼¥Ð¤¬Â¸ºß¤·¤Ê¤¤¥Í¥Ã¥È¥ï¡¼¥¯¤Ë»þ¤ª¤ê¥¢¥¯¥»¥¹¤¹¤ëɬÍפ¬ ¤¢¤ë¤è¤¦¤Ê°Üư¥Û¥¹¥È¤Ï¡¢¤½¤Î¥Í¥Ã¥È¥ï¡¼¥¯¾å¤Î¸ÇÄꥢ¥É¥ì¥¹¤Î¥ê¡¼¥¹¤ò ¤¢¤é¤«¤¸¤áÆÉ¤ß¹þ¤ó¤Ç¤ª¤¯¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£ DHCP ¥µ¡¼¥Ð¤Ø¤Î¥¢¥¯¥»¥¹¤¬¤É¤ì¤âÀ®¸ù¤·¤Ê¤«¤Ã¤¿¾ì¹ç¡¢ dhclient ¤Ï¤½¤ÎÀÅŪ¤Ê¥ê¡¼¥¹¤¬Í­¸ú¤Ç¤¢¤ë¤«¸¡¾Ú¤·¡¢ Í­¸ú¤Ç¤¢¤ì¤Ð¼¡¤ËºÆµ¯Æ°¤µ¤ì¤ë¤Þ¤Ç¤½¤Î¥ê¡¼¥¹¤ò»È¤¤¤Þ¤¹¡£ .PP ¤Þ¤¿°Üư¥Û¥¹¥È¤Ï¡¢DHCP ¤ÏÍøÍѤǤ­¤Ê¤¤¤¬ BOOTP ¤Ê¤éÍøÍѤǤ­¤ë¤è¤¦¤Ê ¥Í¥Ã¥È¥ï¡¼¥¯¤Ø°Üư¤¹¤ë¤³¤È¤â¤¢¤ë¤Ç¤·¤ç¤¦¡£ ¤½¤Î¤è¤¦¤Ê¾ì¹ç¤Ï¡¢¸Å¤¤¥ê¡¼¥¹¤ò½ç¼¡»î¤¹¤è¤ê¤â¡¢ ¤½¤Î¥Í¥Ã¥È¥ï¡¼¥¯¤Î´ÉÍý¼Ô¤ÈÁêÃ̤·¤Æ BOOTP ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ë¥¨¥ó¥È¥ê¤òºîÀ®¤·¤Æ¤â¤é¤¤¡¢ ¤½¤Î¥Í¥Ã¥È¥ï¡¼¥¯¾å¤ÇÁÇÁ᤯¥Ö¡¼¥È¤Ç¤­¤ë¤è¤¦¤Ë¤¹¤ë¤È¤è¤¤¤Ç¤·¤ç¤¦¡£ .SH ¥³¥Þ¥ó¥É¥é¥¤¥ó .PP dhclient ¤¬ÀßÄꤷ¤è¤¦¤È¤¹¤ë¥Í¥Ã¥È¥ï¡¼¥¯¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤Î̾Á°¤ò ¥³¥Þ¥ó¥É¥é¥¤¥ó¤Ç»ØÄê¤Ç¤­¤Þ¤¹¡£ ¥³¥Þ¥ó¥É¥é¥¤¥ó¤Ç¥¤¥ó¥¿¥Õ¥§¡¼¥¹Ì¾¤¬»ØÄꤵ¤ì¤Ê¤±¤ì¤Ð¡¢ dhclient ¤Ï¤¹¤Ù¤Æ¤Î¥Í¥Ã¥È¥ï¡¼¥¯¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤ò¼±Ê̤·¡¢ ²Äǽ¤Ê¤éÈó¥Ö¥í¡¼¥É¥­¥ã¥¹¥È¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤Ï½ü¤¤¤Æ¡¢ ¤½¤ì¤¾¤ì¤Î¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤òÀßÄꤷ¤è¤¦¤È¤·¤Þ¤¹¡£ .PP .B dhclient.conf(5) ¥Õ¥¡¥¤¥ëÃæ¤Î̾Á°¤Ç¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤ò»ØÄꤹ¤ë¤³¤È¤â²Äǽ¤Ç¤¹¡£ ¤³¤ÎÊýË¡¤Ç¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤ò»ØÄꤷ¤¿¾ì¹ç¡¢¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢ ÀßÄê¥Õ¥¡¥¤¥ëÃæ¤Ç»ØÄꤷ¤¿¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤â¤·¤¯¤Ï¥³¥Þ¥ó¥É¹Ô¤Ç »ØÄꤷ¤¿¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤Î¤É¤Á¤é¤«¤À¤±¤òÀßÄꤹ¤ë¤Ç¤·¤ç¤¦¡£ .PP .B -D ¥Õ¥é¥°¤ò»ØÄꤹ¤ë¤È¡¢ .B dhclient ¤¬ .B dhclient-script ¤ÈÁȤ߹ç¤ï¤»¤Æ»ÈÍѤ¹¤ë¤¿¤á¤ËºîÀ®¤·¤¿¥¹¥¯¥ê¥×¥È¤ò¡¢ .IR /tmp ¤ËÊݸ¤µ¤»¤Þ¤¹¡£ .PP DHCP ¥¯¥é¥¤¥¢¥ó¥È¤¬É¸½à¥Ý¡¼¥È (¥Ý¡¼¥ÈÈÖ¹æ 68) °Ê³°¤Î¥Ý¡¼¥È¤Ç ÂÔµ¡¤ª¤è¤ÓÁ÷¿®¤¹¤ëɬÍפ¬¤¢¤ë¾ì¹ç¤Ë¤Ï .B -p ¥Õ¥é¥°¤¬»È¤¨¤Þ¤¹¡£ ¤³¤Î¥Õ¥é¥°¤Ë³¤±¤Æ¡¢dhclient ¤¬»È¤¦ udp ¥Ý¡¼¥ÈÈÖ¹æ¤ò»ØÄꤷ¤Þ¤¹¡£ ¤³¤ì¤Ï¼ç¤È¤·¤Æ¥Ç¥Ð¥Ã¥°ÌÜŪ¤Ç¤ÏÍ­ÍѤǤ¹¡£ ¥¯¥é¥¤¥¢¥ó¥È¤¬ÂÔµ¡¤ª¤è¤ÓÁ÷¿®¤¹¤ë¤¿¤á¤Ë»ÈÍѤ¹¤ë¥Ý¡¼¥È¤Ë ¥Ç¥Õ¥©¥ë¥È¤È¤Ï°ã¤¦¥Ý¡¼¥È¤ò»ØÄꤹ¤ë¾ì¹ç¡¢¥¯¥é¥¤¥¢¥ó¥È¤Ï ¤â¤¦ 1 ¤ÄÊ̤ÎÁ÷¿®Àè¥Ý¡¼¥È¤â»ÈÍѤ·¤Þ¤¹¡£¤½¤ÎÁ÷¿®Àè¥Ý¡¼¥È¤Ï¡¢ »ØÄꤷ¤¿Á÷¿®Àè¥Ý¡¼¥È¤è¤ê¤âÂ礭¤ÊÈÖ¹æ¤ò»ý¤Ã¤¿¤â¤Î¤Ç¤¹¡£ .PP DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢Ä̾ï IP ¥¢¥É¥ì¥¹¤ò³ÍÆÀ¤·¤Æ¤¤¤Ê¤¤´Ö Ǥ°Õ¤Î¥×¥í¥È¥³¥ë¥á¥Ã¥»¡¼¥¸¤ò¥ê¥ß¥Æ¥Ã¥É¥Ö¥í¡¼¥É¥­¥ã¥¹¥È ¥¢¥É¥ì¥¹¤Ç¤¢¤ë 255.255.255.255 ¤Ø¤ÈÁ÷¿®¤·¤Þ¤¹¡£ ¥Ç¥Ð¥Ã¥°ÌÜŪ¤Ç¡¢¥µ¡¼¥Ð¤¬¤³¤ì¤é¤Î¥á¥Ã¥»¡¼¥¸¤ò¤É¤³¤«Ê̤Υ¢¥É¥ì¥¹¤Ø Á÷¿®¤·¤¿Êý¤¬ÊØÍø¤Ê¤³¤È¤¬¤¢¤ê¤Þ¤¹¡£ .B -s ¥Õ¥é¥°¤Î¸å¤ËÁ÷¿®Àè¤Î IP ¥¢¥É¥ì¥¹¤â¤·¤¯¤Ï¥É¥á¥¤¥ó̾¤ò¤Ä¤±¤Æ»ØÄê ¤Ç¤­¤Þ¤¹¡£ ¥Æ¥¹¥ÈÌÜŪ¤Ç¡¢DHCP ¥¯¥é¥¤¥¢¥ó¥È¤¬Á÷¿®¤¹¤ëÁ´¤Æ¤Î¥Ñ¥±¥Ã¥È¤Î giaddr ¥Õ¥£¡¼¥ë¥É¤ò .B -g ¥Õ¥é¥°¤ËÁ÷¿®Àè¤Î IP ¥¢¥É¥ì¥¹¤ò³¤±¤¿·Á¤ò»ÈÍѤ¹¤ë¤³¤È¤ÇÀßÄꤹ¤ë ¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£¤³¤ì¤Ï¥Æ¥¹¥ÈÌÜŪ¤Î»þ¤Î¤ßÍ­ÍѤʤâ¤Î¤Ç¤¢¤ê¡¢ ·ø¼Â¤µ¤ä»È¤¤¤ä¤¹¤µ¤òµá¤á¤ë¾õ¶·¤Çưºî¤¹¤ë¤³¤È¤òÁÛÄꤷ¤Æ¤Ï ¤¤¤±¤Þ¤»¤ó¡£ .PP DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢Ä̾磻¥ó¥¿¥Õ¥§¡¼¥¹¤òÀßÄꤹ¤ë¤Þ¤Ç¤Ï ¥Õ¥©¥¢¥°¥é¥¦¥ó¥É¤Çưºî¤·¡¢¤½¤Î¸å¥Ð¥Ã¥¯¥°¥é¥¦¥ó¥É¤Çưºî ¤¹¤ë¤è¤¦¤Ë¤Ê¤ê¤Þ¤¹¡£dhclient ¤ò¾ï¤Ë¥Õ¥©¥¢¥°¥é¥¦¥ó¥É¤Î ¥×¥í¥»¥¹¤È¤·¤ÆÆ°ºî¤µ¤»¤ë¤¿¤á¤Ë¤Ï¡¢ .B -d ¥Õ¥é¥°¤ò»ØÄꤹ¤ëɬÍפ¬¤¢¤ê¤Þ¤¹¡£¤³¤ì¤Ï¡¢DHCP ¥¯¥é¥¤¥¢¥ó¥È¤¬ ¥Ç¥Ð¥Ã¥¬¤Î¤â¤È¤Çưºî¤·¤Æ¤¤¤ë¾ì¹ç¤ä¡¢System V ¥·¥¹¥Æ¥à¤Î inittab ¤Î³°Â¦¤Çưºî¤·¤Æ¤¤¤ë¾ì¹ç¤Ë¤ÏÍ­¸ú¤Ê¤â¤Î¤Ç¤¹¡£ .PP ¤³¤Î¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢Ä̾ï¤Ïµ¯Æ°¥á¥Ã¥»¡¼¥¸¤òɽ¼¨¤·¡¢¥¢¥É¥ì¥¹¤ò ³ÍÆÀ¤¹¤ë¤Þ¤Çɸ½à¥¨¥é¡¼½ÐÎϤ˥ץí¥È¥³¥ë¥·¡¼¥±¥ó¥¹¤ò ½ñ¤­½Ð¤·¤Þ¤¹¡£¥¢¥É¥ì¥¹¤ò³ÍÆÀ¤·¤¿¸å¤Ï .B syslog (3) ¥Õ¥¡¥·¥ê¥Æ¥£¤ò»ÈÍѤ·¤Æ¥á¥Ã¥»¡¼¥¸¤Î¥í¥°¤ò¼è¤ë¤À¤±¤Ë¤Ê¤ê¤Þ¤¹¡£ .B -q ¥Õ¥é¥°¤ò»ÈÍѤ¹¤ë¤È¡¢¥¨¥é¡¼°Ê³°¤Î¥á¥Ã¥»¡¼¥¸¤òɸ½à¥¨¥é¡¼½ÐÎÏ¤Ë ½ñ¤­½Ð¤µ¤Ê¤¤¤è¤¦¤Ë¤Ê¤ê¤Þ¤¹¡£ .PP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢DHCP ¥×¥í¥È¥³¥ë¤ÇµÁ̳¤Å¤±¤é¤ì¤Æ¤¤¤Ê¤¤¤¿¤á¡¢ Ä̾ï¤Ï¸½ºß¼èÆÀ¤·¤Æ¤¤¤ë¥ê¡¼¥¹¤ò³«Êü¤¹¤ë¤³¤È¤Ï¤¢¤ê¤Þ¤»¤ó¡£ ¤¿¤À¡¢¥±¡¼¥Ö¥ë ISP ¤Î¤Ê¤«¤Ë¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ ³ä¤êÅö¤Æ¤é¤ì¤¿IP ¥¢¥É¥ì¥¹¤ò³«Êü¤·¤¿¤¤¾ì¹ç¤Ë¤Ï¡¢¥µ¡¼¥Ð¤Ë ÄÌÃΤ¹¤ë¤è¤¦¤ËµÁ̳¤Å¤±¤Æ¤¤¤ë¤È¤³¤í¤â¤¢¤ê¤Þ¤¹¡£ .B -r ¥Õ¥é¥°¤òÍѤ¤¤ë¤È¡¢ÌÀ¼¨Åª¤Ë¸½ºß¤Î¥ê¡¼¥¹¤ò³«Êü¤·¡¢¤¤¤Ã¤¿¤ó ¥ê¡¼¥¹¤ò³«Êü¤¹¤ë¤È¥¯¥é¥¤¥¢¥ó¥È¤Ï½ªÎ»¤·¤Þ¤¹¡£ .PP .B -1 ¥Õ¥é¥°¤ò»ØÄꤹ¤ë¤È¡¢ dhclient ¤Ï¤Ò¤È¤Ä¤Î¥ê¡¼¥¹¤ËÂФ· 1 ÅÙ¤À¤±¤·¤«¼èÆÀ¤ò»î¤ß¤Þ¤»¤ó¡£ ¤â¤·¼èÆÀ¤Ë¼ºÇÔ¤¹¤ì¤Ð dhclient ¤Ï½ªÎ»¥³¡¼¥É 2 ¤Ç½ªÎ»¤·¤Þ¤¹¡£ .PP DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢Ä̾ï¤ÏÀßÄê¾ðÊó¤ò .B ETCDIR/dhclient.conf ¤«¤é¡¢¥ê¡¼¥¹¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò .B DBDIR/dhclient.leases ¤«¤é¼èÆÀ¤·¡¢¼«Ê¬¤Î¥×¥í¥»¥¹ ID ¤ò .B RUNDIR/dhclient.pid ¤È¤¤¤¦Ì¾Á°¤Î¥Õ¥¡¥¤¥ë¤ËÊݸ¤·¡¢ ¤½¤·¤Æ¥Í¥Ã¥È¥ï¡¼¥¯¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤ò .B CLIENTBINDIR/dhclient-script ¤ò»ÈÍѤ·¤ÆÀßÄꤷ¤Þ¤¹¡£ ¤³¤ì¤é¤Î¥Õ¥¡¥¤¥ë¤ËÊ̤Î̾Á°¤ò»ØÄꤷ¤¿¤ê¡¢Ê̤ξì½ê¤ò »ØÄꤷ¤¿¤ê¤¹¤ë¤Ë¤Ï¡¢¤½¤ì¤¾¤ì .B -cf, .B -lf, .B -pf ¤ª¤è¤Ó .B -sf ¥Õ¥é¥°¤ò¡¢¸å¤í¤Ë¥Õ¥¡¥¤¥ë̾¤ò³¤±¤ë·Á¤Ç»ÈÍѤ·¤Æ¤¯¤À¤µ¤¤¡£ ¤³¤ÎÊýË¡¤Ï¡¢Î㤨¤Ð DHCP ¥¯¥é¥¤¥¢¥ó¥È¤¬µ¯Æ°¤·¤¿¤È¤­¤Ë .B DBDIR ¤â¤·¤¯¤Ï .B RUNDIR ¤¬¤Þ¤À¥Þ¥¦¥ó¥È¤µ¤ì¤Æ¤¤¤Ê¤¤¾ì¹ç¤Ë¤ÏÆÃ¤ËÍ­ÍѤʤâ¤Î¤Ë ¤Ê¤êÆÀ¤Þ¤¹¡£ .PP DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢ÀßÄꤹ¤Ù¤­¥Í¥Ã¥È¥ï¡¼¥¯ ¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤òƱÄê¤Ç¤­¤Ê¤¤¾ì¹ç¡¢Ä̾ï¤Ï½ªÎ»¤·¤Þ¤¹¡£ ¥é¥Ã¥×¥È¥Ã¥×¥³¥ó¥Ô¥å¡¼¥¿¤ä¥Û¥Ã¥È¥¹¥ï¥Ã¥×²Äǽ¤Ê I/O ¥Ð¥¹¤ò »ý¤Ã¤¿¥³¥ó¥Ô¥å¡¼¥¿¤Ç¤Ï¡¢¥Ö¥í¡¼¥É¥­¥ã¥¹¥È¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤¬ ¥·¥¹¥Æ¥àµ¯Æ°¸å¤ËÄɲ䵤ì¤ë¤³¤È¤¬¤¢¤êÆÀ¤Þ¤¹¡£ .B -w ¥Õ¥é¥°¤òÍѤ¤¤ë¤È¡¢¤½¤Î¤è¤¦¤Ê¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤¬ 1 ¤Ä¤â ¸«¤Ä¤«¤é¤Ê¤¤¤È¤­¤Ë¤â¥¯¥é¥¤¥¢¥ó¥È¤¬½ªÎ»¤·¤Ê¤¤¤è¤¦¤Ë¤Ç¤­¤Þ¤¹¡£ ¸å¤Ç .B omshell (8) ¥×¥í¥°¥é¥à¤ò»ÈÍѤ·¤Æ¡¢¥Í¥Ã¥È¥ï¡¼¥¯¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤¬Äɲ䵤줿¤ê ºï½ü¤µ¤ì¤¿¤ê¤·¤¿¤³¤È¤ò¥¯¥é¥¤¥¢¥ó¥È¤ËÄÌÃΤ¹¤ë¤³¤È¤¬¤Ç¤­¡¢ ¤³¤ì¤Ë¤è¤Ã¤Æ¥¯¥é¥¤¥¢¥ó¥È¤¬¤³¤Î¥¤¥ó¥¿¥Õ¥§¡¼¥¹¾å¤Î IP ¥¢¥É¥ì¥¹¤òÀßÄꤹ¤ë¤è¤¦»î¤ß¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£ .PP .B -n ¥Õ¥é¥°¤òÍѤ¤¤ë¤³¤È¤Ç¡¢¤É¤Î¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤âÀßÄꤷ¤è¤¦¤È ¤·¤Ê¤¤¤è¤¦¤Ë DHCP ¥¯¥é¥¤¥¢¥ó¥È¤ò»Ø¼¨¤¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£ ¤³¤Î¥Õ¥é¥°¤Ï¡¢¤­¤Ã¤È .B -w ¥Õ¥é¥°¤È¶¦¤Ë»ÈÍѤ¹¤ë¤ÈÍ­ÍѤǤ·¤ç¤¦¡£ .PP IP ¥¢¥É¥ì¥¹¤ò³ÍÆÀ¤¹¤ë¤Þ¤ÇÂԤĤΤǤϤʤ¯¡¢Â¨ºÂ¤Ë¥Ç¡¼¥â¥ó¤È ¤Ê¤ë¤è¤¦¤Ë¥¯¥é¥¤¥¢¥ó¥È¤ò»Ø¼¨¤¹¤ë¤³¤È¤â¤Ç¤­¤Þ¤¹¡£ .B -nw ¥Õ¥é¥°¤òÍ¿¤¨¤ë¤È²Äǽ¤Ç¤¹¡£ .SH ÀßÄê dhclient.conf(5) ¥Õ¥¡¥¤¥ë¤Î½ñ¼°¤ÏÊ̤˲òÀ⤵¤ì¤Æ¤¤¤Þ¤¹¡£ .SH OMAPI ¤³¤Î DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢Æ°ºîÃæ¤Ë¤½¤Îưºî¤òÄä»ß¤µ¤»¤ë ¤³¤È¤Ê¤¯¼«Ê¬¼«¿È¤òÀ©¸æ¤Ç¤­¤ë¤è¤¦¤Ë¤¹¤ë¤¿¤á¤Îµ¡Ç½¤òÄ󶡤·¤Æ¤¤¤Þ¤¹¡£ ¤³¤Îµ¡Ç½¤Ï¡¢¥ê¥â¡¼¥È¥ª¥Ö¥¸¥§¥¯¥ÈÁàºî API ¤Ç¤¢¤ë OMAPI ¤ò ÍѤ¤¤ÆÄ󶡤µ¤ì¤Æ¤¤¤Þ¤¹¡£OMAPI ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢TCP/IP ¤ò »ÈÍѤ·¤Æ¤³¤Î DHCP ¥¯¥é¥¤¥¢¥ó¥È¤ËÀܳ¤·¤Þ¤¹¡£¤½¤·¤Æ¡¢ DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Î¸½ºß¤Î¾õÂÖ¤ò¸¡ºº¤Ç¤­¡¢¤½¤Î¾õÂÖ¤òÊѹ¹¤¹¤ë¤³¤È¤¬ ¤Ç¤­¤Þ¤¹¡£ .PP ¥æ¡¼¥¶¥×¥í¥°¥é¥à¤Ç¤Ï¡¢´ðÁäˤ¢¤ë OMAPI ¥×¥í¥È¥³¥ë¤òľÀܼÂÁõ¤¹¤ë ¤Î¤Ç¤Ï¤Ê¤¯¡¢dhcpctl API ¤â¤·¤¯¤Ï OMAPI ¤½¤Î¤â¤Î¤ò»ÈÍѤ¹¤Ù¤­¤Ç¤¹¡£ dhcpctl ¤Ï¡¢OMAPI ¤¬¼«Æ°¤Ç¹Ô¤Ã¤Æ¤Ï¤¯¤ì¤Ê¤¤»¨»ö¤Î¤¤¤¯¤Ä¤«¤ò°·¤¦ ¥é¥Ã¥Ñ¤Ç¤¹¡£dhcpctl ¤ª¤è¤Ó OMAPI ¤Ë¤Ä¤¤¤Æ¤Ï \fBdhcpctl(3)\fR ¤ª¤è¤Ó \fBomapi(3)\fR ¤Ëµ­½Ò¤µ¤ì¤Æ¤¤¤Þ¤¹¡£ ¥¯¥é¥¤¥¢¥ó¥È¤òÍѤ¤¤Æ¤ä¤ê¤¿¤¤¤³¤È¤Î¤Û¤È¤ó¤É¤Ï¡¢ÆÃÊÌ¤Ê¥×¥í¥°¥é¥à¤ò ½ñ¤«¤Ê¤¯¤È¤â \fBomshell(1)\fR ¥³¥Þ¥ó¥É¤ò»ÈÍѤ·¤ÆÄ¾Àܼ¸½¤Ç¤­¤ë ¤â¤Î¤Ç¤¹¡£ .SH À©¸æ¥ª¥Ö¥¸¥§¥¯¥È À©¸æ¥ª¥Ö¥¸¥§¥¯¥È¤ò»ÈÍѤ¹¤ë¤È¡¢DHCP ¥¯¥é¥¤¥¢¥ó¥È¤ò½ªÎ»¤µ¤»¡¢ ÊÝ»ý¤·¤Æ¤¤¤ë¥ê¡¼¥¹¤ò¤¹¤Ù¤Æ³«Êü¤·¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬Äɲä·¤¿ DNS ¥ì¥³¡¼¥É¤ò¤¹¤Ù¤Æ¾Ãµî¤¹¤ë¤³¤È¤¬¤Ç¤­¤ë¤è¤¦¤Ë¤Ê¤ê¤Þ¤¹¡£ ¤Þ¤¿¡¢¥¯¥é¥¤¥¢¥ó¥È¤ò°ì»þÄä»ß¤µ¤»¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬»ÈÍѤ·¤Æ¤¤¤ë ¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤ÎÀßÄê¤ò½ü¤¯¤³¤È¤¬¤Ç¤­¤ë¤è¤¦¤Ë¤â¤Ê¤ê¤Þ¤¹¡£ ¤½¤Î¸å¤Ç¡¢DHCP ¥¯¥é¥¤¥¢¥ó¥È¤òºÆµ¯Æ°¤µ¤»¤ë¤³¤È¤¬¤Ç¤­¡¢ ¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤òºÆÀßÄꤹ¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£Ä̾¥Ï¥¤¥Ð¥Í¡¼¥·¥ç¥ó¤Ë Æþ¤ëÁ°¤ä¥é¥Ã¥×¥È¥Ã¥×¥³¥ó¥Ô¥å¡¼¥¿¤Ç¤Ï¥¹¥ê¡¼¥×¤¹¤ëÁ°¤Ë DHCP ¥¯¥é¥¤¥¢¥ó¥È¤ò°ì»þÄä»ß¤µ¤»¤ë¤Ç¤·¤ç¤¦¡£ ¤½¤·¤Æ¡¢ÅŸ»¤¬Ìá¤Ã¤Æ¤­¤¿¸å¤Ç DHCP ¥¯¥é¥¤¥¢¥ó¥È¤ò²óÉü¤µ¤»¤ë ¤Ç¤·¤ç¤¦¡£¤³¤¦¤¹¤ë¤³¤È¤Ç¡¢¥³¥ó¥Ô¥å¡¼¥¿¤¬¥Ï¥¤¥Ð¥Í¡¼¥·¥ç¥ó¤ä ¥¹¥ê¡¼¥×Ãæ¤Ë¤Ï PC ¥«¡¼¥É¤òÄä»ß¤µ¤»¤Æ¤ª¤­¡¢¥³¥ó¥Ô¥å¡¼¥¿¤¬ ¥Ï¥¤¥Ð¥Í¡¼¥·¥ç¥ó¤ä¥¹¥ê¡¼¥×¤«¤éÉüµ¢¤·¤¿¤é°ÊÁ°¤Î¾õÂÖ¤Ë ºÆÅÙ½é´ü²½¤¹¤ë¤³¤È¤¬¤Ç¤­¤ë¤è¤¦¤Ë¤Ê¤ë¤Î¤Ç¤¹¡£ .PP À©¸æ¥ª¥Ö¥¸¥§¥¯¥È¤Ë¤Ï°À­¤¬ 1 ¤Ä¤¢¤ê¤Þ¤¹¡£¤½¤ì¤Ï¾õÂÖ°À­¤Ç¤¹¡£ ¥¯¥é¥¤¥¢¥ó¥È¤ò½ªÎ»¤µ¤»¤ë¤Ë¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤Î¾õÂÖ°À­¤ò 2 ¤Ë ÀßÄꤷ¤Þ¤¹¡£¥¯¥é¥¤¥¢¥ó¥È¤Ï¼«Æ°Åª¤Ë DHCPRELEASE ¤ò¹Ô¤¦¤Ç¤·¤ç¤¦¡£ ¥¯¥é¥¤¥¢¥ó¥È¤ò°ì»þÄä»ß¤µ¤»¤ë¤Ë¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤Î¾õÂÖ°À­¤ò 3 ¤ËÀßÄꤷ¤Þ¤¹¡£¥¯¥é¥¤¥¢¥ó¥È¤òÉüµ¢¤µ¤»¤ë¤Ë¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤Î ¾õÂÖ°À­¤ò 4 ¤ËÀßÄꤷ¤Þ¤¹¡£ .SH ´ØÏ¢¥Õ¥¡¥¤¥ë .B CLIENTBINDIR/dhclient-script, .B ETCDIR/dhclient.conf, DBDIR/dhclient.leases, RUNDIR/dhclient.pid, .B DBDIR/dhclient.leases~ .SH ´ØÏ¢¹àÌÜ dhclient.conf(5), dhclient.leases(5), dhclient-script(8) .SH ºî¼Ô .B dhclient(8) ¤Ï Ted Lemon ¤¬ Vixie Enterprises ¤È¶¨ÎϤ·¤Æ Internet Systems Consortium ¤Î¤¿¤á¤Ë ½ñ¤­¤Þ¤·¤¿¡£ Internet Systems Consortium ¤Ë¤Ä¤¤¤Æ¤è¤ê¾Ü¤·¤¯¤Ï¡¢ .B https://www.isc.org ¤ò¤´Í÷¤¯¤À¤µ¤¤¡£ Vixie Enterprises ¤Ë¤Ä¤¤¤Æ¤è¤ê¾Ü¤·¤¯¤Ï¡¢ .B http://www.vix.com ¤ò¤´Í÷¤¯¤À¤µ¤¤¡£ .PP ËÜ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢Elliot Poger ¤¬ Stanford Âç³Ø¤Î MosquitoNet ¥×¥í¥¸¥§¥¯¥È¤Ë»²²Ã¤·¤Æ¤¤¤ë´Ö¤Ë¡¢ Linux ¤Ç¤ÎÍøÍѤ˺ݤ·ÂçÉý¤Ë½¤Àµ¡¢²þÎɤò¹Ô¤¤¤Þ¤·¤¿¡£ .PP ¸½ºß¤Î¥Ð¡¼¥¸¥ç¥ó¤Ï¡¢Elliot ¤Ë¤è¤ë Linux ¤Ç¤Î²þÎɤËÉ餦¤È¤³¤í¤¬Â礭¤¤¤Ç¤¹¤¬¡¢ Internet Systems Consortium ¤Î DHCP ¥µ¡¼¥Ð¤¬»È¤¦¤â¤Î¤ÈƱ¤¸ ¥Í¥Ã¥È¥ï¡¼¥­¥ó¥°¥Õ¥ì¡¼¥à¥ï¡¼¥¯¤òÍѤ¤¤ë¤è¤¦¤Ë¡¢Ted Lemon ¤¬ ÂçÉý¤ÊºÆÊÔÀ®¤äÉôʬŪ¤Ê½ñ¤­´¹¤¨¤ò¹Ô¤¤¤Þ¤·¤¿¡£ ¥·¥¹¥Æ¥àÆÃÍ­¤ÎÀßÄꥳ¡¼¥É¤ÎÂçÉôʬ¤Ï¥·¥§¥ë¥¹¥¯¥ê¥×¥È¤Ë°Ü¤µ¤ì¤¿¤Î¤Ç¡¢ ¤è¤ê¿¤¯¤Î¥ª¥Ú¥ì¡¼¥Æ¥£¥ó¥°¥·¥¹¥Æ¥à¤Î¥µ¥Ý¡¼¥È¤¬²Ã¤¨¤é¤ì¤ë¤Ë¤Ä¤ì¡¢ ¥·¥¹¥Æ¥àÆÃÍ­¤ÎÀßÄꥳ¡¼¥É¤ò¤½¤Î¥ª¥Ú¥ì¡¼¥Æ¥£¥ó¥°¥·¥¹¥Æ¥à¤Ë °Ü¿¢¤·¤¿¤ê´ÉÍý¤·¤¿¤ê¤¹¤ëɬÍפϤʤ¯¤Ê¤ë¤Ç¤·¤ç¤¦¡£ Âå¤ï¤ê¤Ë¡¢¥·¥§¥ë¥¹¥¯¥ê¥×¥È¤¬´Ä¶­¤Ë¹ç¤Ã¤¿¥Ä¡¼¥ë¤ò¸Æ¤Ó½Ð¤·¤Æ ¤½¤ÎÌÜŪ¤ò²Ì¤¿¤·¤Æ¤¯¤ì¤Þ¤¹¡£ .PP dhcp-4.4.1/doc/ja_JP.eucJP/dhclient.conf.5000644 000765 000024 00000055006 13243301226 020262 0ustar00tmarkstaff000000 000000 .\" $Id: dhclient.conf.5,v 1.4 2009/11/24 02:06:56 sar Exp $ .\" .\" Copyright (c) 2004,2009,2016 by Internet Systems Consortium, Inc. ("ISC") .\" Copyright (c) 1996-2003 by Internet Software Consortium .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT .\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" Internet Systems Consortium, Inc. .\" 950 Charter Street .\" Redwood City, CA 94063 .\" .\" https://www.isc.org/ .\" .\" %FreeBSD: src/contrib/isc-dhcp/client/dhclient.conf.5,v 1.7.2.1 2002/04/11 10:16:46 murray Exp % .\" $FreeBSD: doc/ja_JP.eucJP/man/man5/dhclient.conf.5,v 1.6 2002/05/03 03:23:30 horikawa Exp $ .\" WORD: lease ¥ê¡¼¥¹(¥¢¥É¥ì¥¹¤ÎÂßÍ¿)[dhclient.conf.5] .\" WORD: lease discovery request ¥ê¡¼¥¹È¯¸«Í×µá[dhclient.conf.5] .\" WORD: offer (¥ê¡¼¥¹Ä󶡤Î)¿½¤·½Ð¡¢Äó¶¡¿½¤·½Ð[dhclient.conf.5] .TH dhclient.conf 5 .SH ̾¾Î dhclient.conf - DHCP ¥¯¥é¥¤¥¢¥ó¥ÈÀßÄê¥Õ¥¡¥¤¥ë .SH ²òÀâ dhclient.conf ¥Õ¥¡¥¤¥ë¤Ë¤Ï Internet Systems Consortium ¤Î DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ç¤¢¤ë .IR dhclient ¤ÎÀßÄê¾ðÊ󤬴ޤޤì¤Þ¤¹¡£ .PP dhclient.conf ¤Ï¼«Í³·Á¼°¤Î ASCII ¥Æ¥­¥¹¥È¥Õ¥¡¥¤¥ë¤Ç¤¹¡£ ¤³¤Î¥Õ¥¡¥¤¥ë¤Ï dhclient ¤ËÁȤ߹þ¤Þ¤ì¤¿ºÆµ¢²¼¹ß¥Ñ¡¼¥¶¤Ë²òÀϤµ¤ì¤Þ¤¹¡£ ¥Õ¥¡¥¤¥ë¤Ë¤Ï¡¢À°·Á¤ÎÌÜŪ¤Ç¥¿¥Ö¤ä²þ¹Ô¤ò;ʬ¤Ë´Þ¤á¤ë¤³¤È¤â¤Ç¤­¤Þ¤¹¡£ ¥Õ¥¡¥¤¥ëÃæ¤Î¥­¡¼¥ï¡¼¥É¤Ç¤ÏÂçʸ»ú¾®Ê¸»ú¤ò¶èÊ̤·¤Þ¤»¤ó¡£ (¥¯¥©¡¼¥ÈÆâ¤Ï½ü¤¤¤Æ) ¥Õ¥¡¥¤¥ëÃæ¤Î¤É¤³¤Ç¤â¥³¥á¥ó¥È¤òÃÖ¤¯¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£ ¥³¥á¥ó¥È¤Ïʸ»ú # ¤Ç»Ï¤Þ¤ê¡¢¹ÔËö¤Ç½ª¤ï¤ê¤Þ¤¹¡£ .PP dhclient.conf ¥Õ¥¡¥¤¥ë¤Ç¡¢¥¯¥é¥¤¥¢¥ó¥È¤Î¤µ¤Þ¤¶¤Þ¤Êưºî¤òÀßÄê¤Ç¤­¤Þ¤¹¡£ ¤½¤ì¤é¤Ë¤Ï¡¢¥×¥í¥È¥³¥ë¤Î¥¿¥¤¥ß¥ó¥°¡¢¥µ¡¼¥Ð¤ËÂФ·¤ÆÍ׵᤹¤ë¾ðÊó¡¢ ¥µ¡¼¥Ð¤ËÂФ·¤ÆÉ¬¿Ü¤È¤µ¤ì¤ë¾ðÊó¡¢ ¥µ¡¼¥Ð¤¬¾ðÊó¤òÄ󶡤·¤Ê¤«¤Ã¤¿¾ì¹ç¤ËÍѤ¤¤ë¥Ç¥Õ¥©¥ë¥È¡¢ ¥µ¡¼¥Ð¤«¤éÄ󶡤µ¤ì¤¿¾ðÊó¤ò¾å½ñ¤­¤¹¤ëÃÍ¡¢ ¥µ¡¼¥Ð¤«¤éÄ󶡤µ¤ì¤¿¾ðÊó¤ËÁ°ÃÖ¤ä¸åÃÖ¤¹¤ëÃͤʤɤ¬¤¢¤ê¤Þ¤¹¡£ ¤Þ¤¿¡¢DHCP ¥µ¡¼¥Ð¤ò»ý¤¿¤Ê¤¤¥Í¥Ã¥È¥ï¡¼¥¯¤Ç»È¤¦¥¢¥É¥ì¥¹¤Ç¤¢¤Ã¤Æ¤â¡¢ ¤¢¤é¤«¤¸¤áÀßÄê¥Õ¥¡¥¤¥ë¤Ç½é´ü²½¤¹¤ë¤³¤È¤â¤Ç¤­¤Þ¤¹¡£ .SH ¥×¥í¥È¥³¥ë¤Î¥¿¥¤¥ß¥ó¥° ¥¯¥é¥¤¥¢¥ó¥È¤Î¥¿¥¤¥ß¥ó¥°Æ°ºî¤Ï¡¢¥æ¡¼¥¶¤¬ÀßÄꤹ¤ëɬÍפϤ¢¤ê¤Þ¤»¤ó¡£ ¥æ¡¼¥¶¤¬¥¿¥¤¥ß¥ó¥°ÀßÄê¤ò¹Ô¤ï¤Ê¤±¤ì¤Ð¡¢ ¥µ¡¼¥Ð¤Ë̵Ãá½ø¤ËÉé²Ù¤òÍ¿¤¨¤¿¤ê¤»¤ºÅ¬»þ¹¹¿·¤ò¹Ô¤¦¤è¤¦¤Ê¡¢ ½¼Ê¬¤ËŬÀڤʥ¿¥¤¥ß¥ó¥°Æ°ºî¤¬¥Ç¥Õ¥©¥ë¥È¤ÇÍѤ¤¤é¤ì¤Þ¤¹¡£ .PP ¤·¤«¤·¡¢É¬Íפ˱þ¤¸¤Æ¡¢ ¼¡¤Îʸ¤ò»ØÄꤷ¤Æ DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Î¥¿¥¤¥ß¥ó¥°Æ°ºî¤òÄ´Àá¤Ç¤­¤Þ¤¹: .PP .B timeout .I ʸ .PP .B timeout .I time .B ; .PP .I timeout ʸ¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬¥¢¥É¥ì¥¹¤ò·è¤á¤ë»î¤ß¤ò³«»Ï¤·¤Æ¤«¤é¡¢ ¥µ¡¼¥Ð¤Ë¥¢¥¯¥»¥¹¤¹¤ë¤³¤È¤¬ ¤Ç¤­¤Ê¤¤¤ÈȽÃǤ¹¤ë¤Þ¤Ç¤Ë·Ð²á¤¹¤Ù¤­»þ´Ö¤ò·è¤á¤Þ¤¹¡£ ¥Ç¥Õ¥©¥ë¥È¤Ç¤Ï¤³¤Î¥¿¥¤¥à¥¢¥¦¥ÈÃÍ¤Ï 60 ÉäǤ¹¡£ ¤³¤Î¥¿¥¤¥à¥¢¥¦¥ÈÃͤ¬²á¤®¤¿¸å¤Ï¡¢ ¤â¤·ÀÅŪ¤Ê¥ê¡¼¥¹¤¬ÀßÄê¥Õ¥¡¥¤¥ë¤ËÄêµÁ¤µ¤ì¤Æ¤¤¤ë¤«¡¢ ¥ê¡¼¥¹¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ë¤Þ¤À´ü¸ÂÀÚ¤ì¤Ë¤Ê¤Ã¤Æ¤¤¤Ê¤¤¥ê¡¼¥¹¤¬»Ä¤Ã¤Æ¤¤¤ì¤Ð¡¢ ¥¯¥é¥¤¥¢¥ó¥È¤Ï¤½¤ì¤é¤Î¥ê¡¼¥¹¤ò¤Ò¤È¤Ä¤º¤Ä¸¡¾Ú¤·¤Æ¤ß¤Æ¡¢ ¤¤¤º¤ì¤«¤¬Í­¸ú¤Ê¤è¤¦¤Ç¤¢¤ì¤Ð¤½¤Î¥ê¡¼¥¹¤Î¥¢¥É¥ì¥¹¤ò»È¤¤¤Þ¤¹¡£ ¤â¤·ÀÅŪ¤Ê¥ê¡¼¥¹¤â¡¢¥ê¡¼¥¹¥Ç¡¼¥¿¥Ù¡¼¥¹Æâ¤Î´ü¸Â¤ÎÀÚ¤ì¤Æ¤¤¤Ê¤¤¥ê¡¼¥¹¤Ç Í­¸ú¤Ê¤â¤Î¤â¸ºß¤·¤Ê¤±¤ì¤Ð¡¢ ¥¯¥é¥¤¥¢¥ó¥È¤ÏÄêµÁ¤µ¤ì¤¿ retry ´Ö³Ö¤Î¸å¤Ç¥×¥í¥È¥³¥ë¤òºÆ³«¤µ¤»¤Þ¤¹¡£ .PP .B retry .I ʸ .PP \fBretry \fItime\fR\fB;\fR .PP .I retry ʸ¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ DHCP ¥µ¡¼¥Ð¤¬Â¸ºß¤·¤Ê¤¤¤ÈȽÃǤ·¤Æ¤«¤é ºÆ¤Ó DHCP ¥µ¡¼¥Ð¤Ë¥¢¥¯¥»¥¹¤ò»î¤ß¤ë¤Þ¤Ç¤Î´Ö¤Ë¡¢·Ð²á¤¹¤ë¤Ù¤­»þ´Ö¤ò·è¤á¤Þ¤¹¡£ ¥Ç¥Õ¥©¥ë¥È¤Ç¤Ï¡¢¤³¤ì¤Ï 5 ʬ¤Ç¤¹¡£ .PP .B select-timeout .I ʸ .PP \fBselect-timeout \fItime\fR\fB;\fR .PP ¤¢¤ë¥Í¥Ã¥È¥ï¡¼¥¯¾å¤Ç¡¢Ê£¿ô¤Î DHCP ¥µ¡¼¥Ð¤¬¥µ¡¼¥Ó¥¹¤òÄ󶡤¹¤ë¤³¤È¤â¤Ç¤­¤Þ¤¹ (¤½¤ÎÊý¤¬Ë¾¤Þ¤·¤¤¤È¤¤¤¦°Õ¸«¤â¤¢¤ê¤Þ¤¹)¡£ ¤½¤Î¾ì¹ç¡¢ºÇ½é¤Î¥ê¡¼¥¹È¯¸«¥á¥Ã¥»¡¼¥¸ (lease discovery message) ¤Ø¤Î±þÅú¤È¤·¤Æ¡¢ ¥¯¥é¥¤¥¢¥ó¥È¤¬Ê£¿ô¤Î¥ê¡¼¥¹Ä󶡤、·½Ð¤ò¼õ¤±¤ë¤³¤È¤â¤¢¤êÆÀ¤Þ¤¹¡£ ¤½¤ì¤é¤Î¤¦¤Á¡¢¤¢¤ëÄ󶡤¬Â¾¤ÎÄ󶡤è¤ê¤â¹¥¤Þ¤·¤¤¤«¤â¤·¤ì¤Þ¤»¤ó (Î㤨¤Ð¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬°ÊÁ°»ÈÍѤ·¤Æ¤¤¤¿¥¢¥É¥ì¥¹¤¬¤¢¤ëÄó¶¡¤Ë´Þ¤Þ¤ì¤Æ¤¤¤ë¤¬¡¢ ¾¤ÎÄ󶡤ˤϴޤޤì¤Ê¤¤¤Ê¤É)¡£ .PP .I select-timeout ¤Ï¥¯¥é¥¤¥¢¥ó¥È¤¬ºÇ½é¤Î¥ê¡¼¥¹È¯¸«Í×µá ¤òÁ÷¿®¤·¤Æ¡¢ ¾¯¤Ê¤¯¤È¤â 1 ¤Ä¤ÎÄó¶¡¿½¤·½Ð¤ò¼õ¤±¤¿¾ì¹ç¡¢ ¥µ¡¼¥Ð¤«¤é¤ÎÄó¶¡¿½¤·½ÐÂÔ¤Á¤ò¤ä¤á¤ë¤Þ¤Ç¤Î»þ´Ö¤Ç¤¹¡£ ¤â¤· .I select-timeout ¤¬ÀÚ¤ì¤ë¤Þ¤Ç¤Ë¤É¤³¤«¤é¤âÄó¶¡¿½¤·½Ð¤ò¼õ¤±¼è¤ì¤Ê¤±¤ì¤Ð¡¢ ¥¯¥é¥¤¥¢¥ó¥È¤Ï¤½¤Î¤¢¤ÈºÇ½é¤ËÅþÃ夹¤ëÄó¶¡¿½¤·½Ð¤ò¼õ¤±Æþ¤ì¤Þ¤¹¡£ .PP ¥Ç¥Õ¥©¥ë¥È¤Ç¤Ï¡¢select-timeout ÃÍ¤Ï 0 ÉäǤ¹¡£ ¤Ä¤Þ¤ê¥¯¥é¥¤¥¢¥ó¥È¤ÏºÇ½é¤Ë¼õ¤±¼è¤ëÄó¶¡¿½¤·½Ð¤ò¼õ¤±Æþ¤ì¤Þ¤¹¡£ .PP .B reboot .I ʸ .PP \fBreboot \fItime\fR\fB;\fR .PP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢ºÆµ¯Æ°¤¹¤ë¤È¡¢ ºÇ¸å¤ËÊÝ»ý¤·¤Æ¤¤¤¿¥¢¥É¥ì¥¹¤ò¤Þ¤º¼èÆÀ¤·Ä¾¤½¤¦¤È¤·¤Þ¤¹¡£ ¤³¤ì¤ò INIT-REBOOT (½é´ü¥ê¥Ö¡¼¥È) ¾õÂ֤ȸƤӤޤ¹¡£ ºÇ¸å¤Ëưºî¤·¤Æ¤¤¤¿¤È¤­¤ÈƱ¤¸¥Í¥Ã¥È¥ï¡¼¥¯¤Ë ¥¯¥é¥¤¥¢¥ó¥È¤¬¤Þ¤ÀÀܳ¤·¤Æ¤¤¤ì¤Ð¡¢¤³¤ì¤¬ºÇ¤âÁÇÁᤤµ¯Æ°Ë¡¤È¤Ê¤ê¤Þ¤¹¡£ .I reboot ʸ¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ºÇ½é¤Ë¸Å¤¤¥¢¥É¥ì¥¹¤ÎºÆ¼èÆÀ¤ò»î¤ß¤Æ¤«¤é¡¢ ¤¢¤­¤é¤á¤Æ¿·¤·¤¤¥¢¥É¥ì¥¹¤òȯ¸«¤·¤è¤¦¤È¤¹¤ë¤Þ¤Ç¤Ë¡¢ ·Ð²á¤¹¤Ù¤­»þ´Ö¤òÀßÄꤷ¤Þ¤¹¡£ ¥Ç¥Õ¥©¥ë¥È¤Ç¤Ï¡¢reboot ¥¿¥¤¥à¥¢¥¦¥ÈÃÍ¤Ï 10 ÉäǤ¹¡£ .PP .B backoff-cutoff .I ʸ .PP \fBbackoff-cutoff \fItime\fR\fB;\fR .PP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢»Ø¿ôŪ¤Ê°ì»þÂàÈò (backoff) ¥¢¥ë¥´¥ê¥º¥à¤ò¡¢¤¢¤ëÄøÅ٤ΠÍð¿ôÉÕ¤­¤Ç»ÈÍѤ·¤Þ¤¹¡£¤³¤ì¤Ï¡¢Â¿¤¯¤Î¥¯¥é¥¤¥¢¥ó¥È¤¬Æ±»þ¤Ë¼«Ê¬¤òÀßÄꤷ¤è¤¦ ¤È¤·¤¿¤È¤­¤Ç¤â¡¢¥ê¥¯¥¨¥¹¥È¤¬¥í¥Ã¥¯¤·¤Æ¤·¤Þ¤¦¤³¤È¤¬¤Ê¤¤¤è¤¦¤Ë¤¹¤ë¤¿¤á¤Ç¤¹¡£ .I backoff-cutoff ʸ¤Ï¡¢°ì»þÂàÈò¤Ëµö¤µ¤ì¤¿ºÇÂç»þ´Ö¤ò·èÄꤷ¤Þ¤¹¡£¥Ç¥Õ¥©¥ë¥ÈÃÍ¤Ï 2 ʬ¤Ç¤¹¡£ .PP .B initial-interval .I ʸ .PP \fBinitial-interval \fItime\fR\fB;\fR .PP .I initial-interval ʸ¤Ï¡¢¥µ¡¼¥Ð¤Ø¤ÎºÇ½é¤Î¥¢¥¯¥»¥¹¤Î»î¤ß¤«¤é¼¡¤Î»î¤ß¤Þ¤Ç¤Î´Ö¤Î»þ´Ö¤ò ÀßÄꤷ¤Þ¤¹¡£¥á¥Ã¥»¡¼¥¸¤Î´Ö³Ö¤Ï¡¢¥á¥Ã¥»¡¼¥¸¤ò 1 ²óÁ÷¿®¤¹¤ë¤¿¤Ó¤Ë¡¢ ¸½ºß¤Î´Ö³Ö¤Ë 0 ¤«¤é 1 ¤Î´Ö¤ÎÍð¿ôÃͤò¾è¤¸¤¿¤â¤Î¤Î 2 Çܤò¡¢¸½ºß¤Î´Ö³Ö¤Ë ²Ã¤¨¤¿¤â¤Î¤Ë¤Ê¤ê¤Þ¤¹¡£ ¤³¤ÎÃͤ¬ backoff-cutoff Ãͤè¤êÂ礭¤¯¤Ê¤ë¤È¡¢¤³¤Î»þ´Ö¤¬ÀßÄꤵ¤ì¤Þ¤¹¡£ ¥Ç¥Õ¥©¥ë¥ÈÃÍ¤Ï 10 ÉäǤ¹¡£ .SH ¥ê¡¼¥¹Í×µá¤È¥ê¥¯¥¨¥¹¥È DHCP ¥×¥í¥È¥³¥ë¤Ç¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤«¤é¥µ¡¼¥Ð¤ËÂФ·¡¢ÆÃÄê¤Î¾ðÊó¤òÁ÷¤ë¤è¤¦ Í׵ᤷ¤¿¤ê¡¢¼õ¤±Æþ¤ì½àÈ÷¤Î¤Ç¤­¤Æ¤¤¤Ê¤¤Â¾¤Î¾ðÊó¤ÏÁ÷¤é¤Ê¤¤¤è¤¦¤ËÍ׵ᤷ¤¿¤ê ¤Ç¤­¤Þ¤¹¡£ ¤Þ¤¿¡¢¥µ¡¼¥Ð¤«¤é¤ÎÄó¶¡¿½¤·½Ð¤Ë¥¯¥é¥¤¥¢¥ó¥È¤ÎɬÍפȤ¹¤ë¾ðÊ󤬴ޤޤì¤Ê¤¤ ¾ì¹ç¤ä¡¢Ä󶡤µ¤ì¤¿¾ðÊ󤬽¼Ê¬¤Ç¤Ê¤¤¾ì¹ç¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬Äó¶¡¿½¤·½Ð¤ò µñÈݤ¹¤ë¤³¤È¤â¤Ç¤­¤Þ¤¹¡£ .PP DHCP ¥µ¡¼¥Ð¤¬ DHCP ¥¯¥é¥¤¥¢¥ó¥È¤ËÁ÷¤ëÄó¶¡¿½¤·½Ð¤Ë´Þ¤Þ¤ì¤ë¥Ç¡¼¥¿¤Ë¤Ï¡¢ ¤µ¤Þ¤¶¤Þ¤Ê¤â¤Î¤¬¤¢¤ê¤Þ¤¹¡£ ÆÃ¤ËÍ×µá¤Ç¤­¤ë¥Ç¡¼¥¿¤Ï \fIDHCP ¥ª¥×¥·¥ç¥ó\fR ¤È¸Æ¤Ð¤ì¤ë¤â¤Î¤Ç¤¹¡£ DHCP ¥ª¥×¥·¥ç¥ó¤Ï \fBdhcp-options(5)\fR ¤ËÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤¹¡£ .PP .B request .I ʸ .PP \fBrequest [ \fIoption\fR ] [\fB,\fI ... \fIoption\fR ]\fB;\fR .PP request ʸ¤ò»ØÄꤹ¤ë¤³¤È¤Ç¡¢¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢¥µ¡¼¥Ð¤ËÂФ·¡¢¤½¤Î ¥¯¥é¥¤¥¢¥ó¥È¤Ë±þÅú¤¹¤ë¤Ê¤é¤Ð¡¢»ØÄꤷ¤¿¥ª¥×¥·¥ç¥ó¤ÎÃͤòÁ÷¤ë¤è¤¦ Í׵᤹¤ë¤è¤¦¤Ë¤Ê¤ê¤Þ¤¹¡£ request ʸ¤Ë¤Ï¥ª¥×¥·¥ç¥ó̾¤À¤±¤ò»ØÄꤷ¡¢¥ª¥×¥·¥ç¥ó¥Ñ¥é¥á¡¼¥¿¤Ï»ØÄꤷ¤Þ¤»¤ó¡£ ¥Ç¥Õ¥©¥ë¥È¤Ç¤Ï DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï subnet-mask, broadcast-address, time-offset, routers, domain-name, domain-name-servers, host-name ¥ª¥×¥·¥ç¥ó¤òÍ׵ᤷ¤Þ¤¹¡£ .PP ¾ì¹ç¤Ë¤è¤Ã¤Æ¤ÏÍ×µá¥ê¥¹¥È¤òÁ´¤¯Á÷¤é¤Ê¤¤¤³¤È¤¬Ë¾¤Þ¤·¤¤¤³¤È¤â¤¢¤ê¤Þ¤¹¡£ ¤½¤¦¤¹¤ë¤¿¤á¤Ë¤Ï¡¢Ã±½ã¤Ë¥Ñ¥é¥á¡¼¥¿¤ò»ØÄꤷ¤Ê¤¤ request ʸ¤ò½ñ¤¤¤Æ²¼¤µ¤¤: .PP .nf request; .fi .PP .B require .I ʸ .PP \fBrequire [ \fIoption\fR ] [\fB,\fI ... \fIoption ]\fB;\fR .PP require ʸ¤Ë¤Ï¡¢¤¢¤ëÄó¶¡¿½¤·½Ð¤ò¥¯¥é¥¤¥¢¥ó¥È¤¬¼õ¤±Æþ¤ì¤ë¤¿¤á¤Ë ¥µ¡¼¥Ð¤¬Á÷¤ë¤Ù¤­¥ª¥×¥·¥ç¥ó¤òÎóµó¤·¤Þ¤¹¡£ Îóµó¤µ¤ì¤¿¥ª¥×¥·¥ç¥ó¤¹¤Ù¤Æ¤ò´Þ¤Þ¤Ê¤¤Äó¶¡¿½¤·½Ð¤Ï̵»ë¤µ¤ì¤Þ¤¹¡£ .PP .B send .I ʸ .PP \fBsend { [ \fIoption declaration\fR ] [\fB,\fI ... \fIoption declaration\fR ]\fB}\fR .PP send ʸ¤ò»ØÄꤹ¤ë¤³¤È¤Ç¡¢¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢ »ØÄꤷ¤¿¥ª¥×¥·¥ç¥ó¤ò»ØÄꤷ¤¿Ãͤǥµ¡¼¥Ð¤ËÁ÷¿®¤¹¤ë¤è¤¦¤Ë¤Ê¤ê¤Þ¤¹¡£ ¤³¤³¤Ç»ØÄê¤Ç¤­¤ë¥ª¥×¥·¥ç¥ó¤Ï¡¢ \fBdhcp-options(5)\fR ¤ÇÀâÌÀ¤µ¤ì¤Æ¤¤¤ë¥ª¥×¥·¥ç¥óÀë¸À¤¹¤Ù¤Æ¤Ç¤¹¡£ DHCP ¥×¥í¥È¥³¥ë¤Ç¾ï¤ËÁ÷¤é¤ì¤ë¥ª¥×¥·¥ç¥ó¤Ï ¤³¤³¤Ë»ØÄꤹ¤ë¤Ù¤­¤Ç¤Ï¤¢¤ê¤Þ¤»¤ó¡£Ã¢¤·¡¢ \fBrequested-lease-time\fR ¥ª¥×¥·¥ç¥ó¤ò¥Ç¥Õ¥©¥ë¥È¤Î¥ê¡¼¥¹»þ´Ö (2 »þ´Ö) °Ê³°¤ÎÃͤǻØÄꤹ¤ë¤³¤È¤Ï¤Ç¤­¤Þ¤¹¡£¤³¤Îʸ¤ò»È¤¦Â¾¤Î¾ì¹ç¤È¤·¤ÆÌÀ¤é¤«¤Ê ¤â¤Î¤Ï¡¢¼«Ê¬¤ÈÊ̤μïÎà¤Î¥¯¥é¥¤¥¢¥ó¥È¤È¤ò¶èÊ̤Ǥ­¤ë¤è¤¦¤Ê ¾ðÊó¤ò¡¢¥µ¡¼¥Ð¤ËÂФ·Á÷¿®¤¹¤ë¾ì¹ç¤Ç¤¹¡£ .SH ưŪ DNS ¸½ºß¡¢¥ê¡¼¥¹¤¬³ÍÆÀ¤µ¤ì¤¿ºÝ¤Ë DNS ¤Î¹¹¿·¤ò¹Ô¤¦¤¿¤á¤Î¡¢ Èó¾ï¤Ë¸ÂÄêŪ¤Ê¥µ¥Ý¡¼¥È¤¬¥¯¥é¥¤¥¢¥ó¥È¤Ë¤¢¤ê¤Þ¤¹¡£ ¤³¤ì¤Ï¥×¥í¥È¥¿¥¤¥×Ū¤Ê¤â¤Î¤Ç¤¢¤ê¡¢ ¤ª¤½¤é¤¯¤¢¤Ê¤¿¤¬»×¤Ã¤Æ¤¤¤ë¤è¤¦¤Ë¤Ïư¤­¤Þ¤»¤ó¡£ ¤â¤·¡¢¤¢¤Ê¤¿¤¬¶öÁ³¤Ë¤â¼«Ê¬¤Î¤È¤³¤í¤Î DNS ¥µ¡¼¥Ð¤Î´ÉÍý¼Ô¤Ç¤¢¤ë¤È¤¤¤¦¤Ê¤é¡¢ ¤½¤Î¾ì¹ç¤Ë¸Â¤Ã¤Æ¤Ïư¤­¤Þ¤¹¡£¤È¤Æ¤â¤¢¤ê¤½¤¦¤Ë¤Ê¤¤¤³¤È¤Ç¤¹¤¬¡£ .PP ¤³¤ì¤òưºî¤µ¤»¤ë¤¿¤á¤Ë¤Ï¡¢DHCP ¥µ¡¼¥Ð¤ÎÃæ¤Ç ¸°¤È¥¾¡¼¥ó¤òÀë¸À¤¹¤ëɬÍפ¬¤¢¤ê¤Þ¤¹ (¾ÜºÙ¤Ï \fBdhcpd.conf\fR(5) ¤ò»²¾È)¡£ ¤Þ¤¿¡¢¼¡¤Î¤è¤¦¤Ë¥¯¥é¥¤¥¢¥ó¥È¤Ç fqdn ¥ª¥×¥·¥ç¥ó¤òÀßÄꤹ¤ëɬÍפ¬¤¢¤ê¤Þ¤¹: .PP .nf send fqdn.fqdn "grosse.example.com."; send fqdn.encoded on; send fqdn.server-update off; .fi .PP \fIfqdn.fqdn\fR ¥ª¥×¥·¥ç¥ó¤Ï \fBɬ¤º\fR ´°Á´¤Ê¥É¥á¥¤¥ó̾¤Ç¤Ê¤±¤ì¤Ð¤Ê¤ê¤Þ¤»¤ó¡£ ¹¹¿·¤¹¤ë¥¾¡¼¥ó¤ËÂФ¹¤ë¥¾¡¼¥óʸ¤ò \fBɬ¤º\fR ÄêµÁ¤·¤Ê¤±¤ì¤Ð¤Ê¤ê¤Þ¤»¤ó¡£ \fIfqdn.encoded\fR ¥ª¥×¥·¥ç¥ó¤Ï¡¢»ÈÍѤ·¤Æ¤¤¤ë DHCP ¥µ¡¼¥Ð¤Ë¤è¤Ã¤Æ¤Ï¡¢ \fIon\fR ¤« \fIoff\fR ¤ËÀßÄꤹ¤ëɬÍפ¬¤¢¤ë¤«¤â¤·¤ì¤Þ¤»¤ó¡£ .PP .B no-client-updates .I ʸ .PP \fBno-client-updates [ \fIflag\fR ] \fB;\fR .PP DHCP ¥¯¥é¥¤¥¢¥ó¥È¤¬Ä¾ÀÜ DNS ¤Î¹¹¿·¤ò¹Ô¤¦¤è¤ê¤â¡¢ DHCP ¥¯¥é¥¤¥¢¥ó¥È¥¹¥¯¥ê¥×¥È (\fBdhclient-script(8)\fR »²¾È) ¤ÎÃæ¤Ç DNS ¤Î¹¹¿·¤ò¹Ô¤¤¤¿¤¤¾ì¹ç (Î㤨¤Ð¡¢DHCP ¥¯¥é¥¤¥¢¥ó¥È¤¬Ä¾ÀÜ¥µ¥Ý¡¼¥È¤·¤Æ¤¤¤Ê¤¤ SIG(0) ǧ¾Ú¤ò»ÈÍѤ·¤¿¤¤¾ì¹ç) ¤Ë¤Ï¡¢\fBno-client-updates\fR ʸ¤ò»È¤Ã¤Æ¡¢¹¹¿·¤ò¹Ô¤ï¤Ê¤¤¤è¤¦¤Ë ¥¯¥é¥¤¥¢¥ó¥È¤Ë¶µ¤¨¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£ DHCP ¥¯¥é¥¤¥¢¥ó¥È¤¬¹¹¿·¤¹¤ë¤³¤È¤ò˾¤Þ¤Ê¤¤¾ì¹ç¤Ï \fIflag\fR ¤ò \fBtrue\fR ¤Ë¤·¡¢ ¹¹¿·¤¹¤ë¤³¤È¤ò˾¤à¾ì¹ç¤Ï \fIflag\fR ¤ò \fBfalse\fR ¤Ë¤¹¤ë¤³¤È¤Ë¤Ê¤ê¤Þ¤¹¡£ ¥Ç¥Õ¥©¥ë¥È¤Ç¤Ï DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï DNS ¤Î¹¹¿·¤ò¹Ô¤¤¤Þ¤¹¡£ .PP .SH ¥ª¥×¥·¥ç¥ó½¤¾þ»Ò ¤½¤Î¥¯¥é¥¤¥¢¥ó¥È¤Ë¤È¤Ã¤Æ¼ÂºÝ¤Ë¤ÏŬÀڤǤʤ¤ ¥ª¥×¥·¥ç¥ó¥Ç¡¼¥¿¤ò¼õ¤±¼è¤Ã¤¿¤ê¡¢É¬ÍפʾðÊó¤ò¼õ¤±¼è¤é¤Ê¤«¤Ã¤¿¤ê ¤¹¤ë¾ì¹ç¤Ç¡¢¤«¤Ä¡¢¤½¤ì¤é¤Î¾ðÊó¤ËÍøÍѲÄǽ¤Ê¥Ç¥Õ¥©¥ë¥È¤ÎÃͤ¬ ¥¯¥é¥¤¥¢¥ó¥È¦¤Ë¸ºß¤¹¤ë¾ì¹ç¤¬¤¢¤ê¤Þ¤¹¡£ ¤Þ¤¿¡¢ÍøÍѲÄǽ¤Ç¤Ï¤¢¤ë¤¬¥í¡¼¥«¥ë¤Î¾ðÊó¤ÇÊ䤦ɬÍפΤ¢¤ë¾ðÊó¤ò ¥¯¥é¥¤¥¢¥ó¥È¤¬¼õ¤±¤È¤ë¾ì¹ç¤â¤¢¤ê¤Þ¤¹¡£ ¤³¤¦¤¤¤¦¾ì¹ç¤ò°·¤¦¤¿¤á¤Ë¡¢ ¤¤¤¯¤Ä¤«¤Î¥ª¥×¥·¥ç¥ó½¤¾þ»Ò¤¬ÍøÍѤǤ­¤Þ¤¹¡£ .PP .B default .I ʸ .PP \fBdefault [ \fIoption declaration\fR ] \fB;\fR .PP ¤¢¤ë¥ª¥×¥·¥ç¥ó¤Ë¤Ä¤¤¤Æ¡¢ ¥µ¡¼¥Ð¤«¤éÄ󶡤µ¤ì¤ëÃͤò¥¯¥é¥¤¥¢¥ó¥È¤¬»È¤ï¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¤¬¡¢ ¤â¤·¥µ¡¼¥Ð¤«¤éÃͤ¬Ä󶡤µ¤ì¤Ê¤±¤ì¤Ð ²¿¤é¤«¤Î¥Ç¥Õ¥©¥ë¥ÈÃͤò»È¤¦É¬Íפ¬¤¢¤ë¾ì¹ç¡¢ ¤½¤ì¤é¤ÎÃͤò .B default ʸ¤ÇÄêµÁ¤¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£ .PP .B supersede .I ʸ .PP \fBsupersede [ \fIoption declaration\fR ] \fB;\fR .PP ¤¢¤ë¥ª¥×¥·¥ç¥ó¤Ë¤Ä¤¤¤Æ¡¢ ¤É¤Î¤è¤¦¤ÊÃͤ¬¥µ¡¼¥Ð¤«¤éÄ󶡤µ¤ì¤Æ¤â¡¢ ¾ï¤Ë¥í¡¼¥«¥ë¤ÇÀßÄꤵ¤ì¤¿Ãͤò»È¤ï¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¾ì¹ç¡¢ ¤½¤ì¤é¤ÎÃͤò .B supersede ʸ¤ÇÄêµÁ¤¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£ .PP .B prepend .I ʸ .PP \fBprepend [ \fIoption declaration\fR ] \fB;\fR .PP ¤¢¤ë¥ª¥×¥·¥ç¥ó¤Î½¸¹ç¤Ë¤Ä¤¤¤Æ¡¢¤Þ¤º¥æ¡¼¥¶¤¬Ä󶡤¹¤ëÃͤò»È¤¤¡¢ ¤½¤Î¼¡¤Ë¥µ¡¼¥Ð¤«¤éÄ󶡤µ¤ì¤¿Ãͤ¬¤¢¤ì¤Ð¤½¤ì¤ò»È¤¦¾ì¹ç¡¢ ¤½¤ì¤é¤ÎÃͤò .B prepend ʸ¤ÇÄêµÁ¤¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£ .B prepend ʸ¤ÏÊ£¿ô¤ÎÃͤò¼è¤ë¤³¤È¤Î¤Ç¤­¤ë¥ª¥×¥·¥ç¥ó¤Ë¤Î¤ßÍѤ¤¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£ ¤³¤ÎÀ©Ìó¤Ï¶¯À©¤µ¤ì¤ë¤â¤Î¤Ç¤Ï¤¢¤ê¤Þ¤»¤ó¤¬¡¢ ¤³¤ì¤ò̵»ë¤·¤¿¾ì¹ç¡¢¤É¤Î¤è¤¦¤Êµóư¤Ë¤Ê¤ë¤«¤ÏͽÁۤǤ­¤Þ¤»¤ó¡£ .PP .B append .I ʸ .PP \fBappend [ \fIoption declaration\fR ] \fB;\fR .PP ¤¢¤ë¥ª¥×¥·¥ç¥ó¤Î½¸¹ç¤Ë¤Ä¤¤¤Æ¡¢¤Þ¤º¥µ¡¼¥Ð¤«¤éÄ󶡤µ¤ì¤¿Ãͤò»È¤¤¡¢ ¤½¤Î¼¡¤Ë¥æ¡¼¥¶¤¬Ä󶡤¹¤ëÃͤ¬¤¢¤ì¤Ð¤½¤ì¤â»È¤¦¾ì¹ç¡¢ ¤½¤ì¤é¤ÎÃͤò .B append ʸ¤ÇÄêµÁ¤¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£ .B append ʸ¤ÏÊ£¿ô¤ÎÃͤò¼è¤ë¤³¤È¤Î¤Ç¤­¤ë¥ª¥×¥·¥ç¥ó¤Ë¤Î¤ßÍѤ¤¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£ ¤³¤ÎÀ©Ìó¤Ï¶¯À©¤µ¤ì¤ë¤â¤Î¤Ç¤Ï¤¢¤ê¤Þ¤»¤ó¤¬¡¢ ¤â¤·°ãÈ¿¤¹¤ë¤Èͽ´ü¤Ç¤­¤Ê¤¤·ë²Ì¤È¤Ê¤ê¤Þ¤¹¡£ .SH ¥ê¡¼¥¹Àë¸À .PP .B lease .I Àë¸À .PP \fBlease {\fR \fIlease-declaration\fR [ ... \fIlease-declaration ] \fB}\fR .PP ¤¢¤ë»þ´Ö (\fB¥×¥í¥È¥³¥ë¤Î¥¿¥¤¥ß¥ó¥°\fR »²¾È) ¤Î¸å¡¢DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï ¥µ¡¼¥Ð¤Ø¤Î¥¢¥¯¥»¥¹¤ËÀ®¸ù¤·¤½¤¦¤Ë¤Ê¤¤¤ÈȽÃǤ¹¤ë¾ì¹ç¤¬¤¢¤ê¤Þ¤¹¡£ ¤½¤Î»þÅÀ¤Ç¡¢¥¯¥é¥¤¥¢¥ó¥È¤Ï¼«Ê¬¤¬»ý¤Ã¤Æ¤¤¤ë¡¢¸Å¤¤¥ê¡¼¥¹¤Î¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò ¸«¤Æ¡¢»þ´ÖÀÚ¤ì¤Ë¤Ê¤Ã¤Æ¤¤¤Ê¤¤¥ê¡¼¥¹¤ò½ç¤ËÄ´¤Ù¡¢¤½¤³¤Ëµó¤¬¤Ã¤Æ¤¤¤ë ¥ë¡¼¥¿¤Ë ping ¤ò¹Ô¤Ã¤Æ¡¢¤½¤ì¤¬ÍøÍѲÄǽ¤Ê¥ê¡¼¥¹¤«¤É¤¦¤«¤òÄ´¤Ù¤Þ¤¹¡£ DHCP ¥µ¡¼¥Ó¥¹¤ä BOOTP ¥µ¡¼¥Ó¥¹¤¬Â¸ºß¤·¤Ê¤¤¥Í¥Ã¥È¥ï¡¼¥¯¤Î¤¿¤á¤Ë¡¢ 1 ¤Ä°Ê¾å¤Î \fI¸ÇÄê\fR ¥ê¡¼¥¹¤ò¥¯¥é¥¤¥¢¥ó¥ÈÀßÄê¥Õ¥¡¥¤¥ë¤ËÄêµÁ¤·¤Æ¤ª¤¤¤Æ¡¢ ¥¯¥é¥¤¥¢¥ó¥È¤¬¥¢¥É¥ì¥¹¤ò¼«Æ°Åª¤ËÀßÄê¤Ç¤­¤ë¤è¤¦¤Ë¤¹¤ë¤³¤È¤â¤Ç¤­¤Þ¤¹¡£ ¤³¤ì¤Ï .B lease ʸ¤Ç¹Ô¤¤¤Þ¤¹¡£ .PP Ãí°Õ: lease ʸ¤Ï¡¢DHCP ¥µ¡¼¥Ð¤«¤é¼õ¤±¼è¤Ã¤¿¥ê¡¼¥¹¤òµ­Ï¿¤¹¤ë¤¿¤á¤Ë¡¢ dhclient.leases ¥Õ¥¡¥¤¥ë¤Ç¤â»È¤ï¤ì¤Þ¤¹¡£ °Ê²¼¤ËÀâÌÀ¤¹¤ë¥ê¡¼¥¹ÍѤΥ·¥ó¥¿¥Ã¥¯¥¹¤Ë¤Ï dhclient.leases ¥Õ¥¡¥¤¥ë¤Ç¤Î¤ßɬÍפʤâ¤Î¤â¤¢¤ê¤Þ¤¹¡£ ÀâÌÀ¤ò´°Á´¤Ê¤â¤Î¤Ë¤¹¤ë¤¿¤á¡¢¤½¤Î¤è¤¦¤Ê¥·¥ó¥¿¥Ã¥¯¥¹¤â¤³¤³¤Çµ­½Ò¤·¤Þ¤¹¡£ .PP lease ʸ¤Ï¡¢¥ê¡¼¥¹¥­¡¼¥ï¡¼¥É¡¢º¸Ãæ³ç¸Ì¡¢1 ¤Ä°Ê¾å¤Î¥ê¡¼¥¹Àë¸Àʸ¡¢ ±¦Ãæ³ç¸Ì¤¬Â³¤¤¤¿¤â¤Î¤Ç¹½À®¤µ¤ì¤Þ¤¹¡£ ¥ê¡¼¥¹Àë¸À¤È¤·¤Æ¡¢¼¡¤Î¤â¤Î¤¬²Äǽ¤Ç¤¹: .PP \fBbootp;\fR .PP .B bootp ʸ¤Ï¡¢¥ê¡¼¥¹¤¬ DHCP ¥×¥í¥È¥³¥ë¤Ç¤Ï¤Ê¤¯¡¢ BOOTP ¥×¥í¥È¥³¥ë¤òÍѤ¤¤Æ¼èÆÀ¤µ¤ì¤¿¤³¤È¤ò¼¨¤·¤Þ¤¹¡£ ¤³¤Îʸ¤ò¥¯¥é¥¤¥¢¥ó¥ÈÀßÄê¥Õ¥¡¥¤¥ë¤Ë»ØÄꤹ¤ëɬÍפÏÁ´¤¯¤¢¤ê¤Þ¤»¤ó¡£ ¥¯¥é¥¤¥¢¥ó¥È¤Ï¤³¤Î¹½Ê¸¤ò¥ê¡¼¥¹¥Ç¡¼¥¿¥Ù¡¼¥¹¥Õ¥¡¥¤¥ëÆâ¤Ç»È¤¤¤Þ¤¹¡£ .PP \fBinterface\fR \fB"\fR\fIstring\fR\fB";\fR .PP .B interface ¥ê¡¼¥¹Ê¸¤Ï¡¢¤½¤Î¥ê¡¼¥¹¤òÍ­¸ú¤È¤¹¤ë¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤ò¼¨¤·¤Þ¤¹¡£ ¤³¤ì¤¬ÀßÄꤵ¤ì¤Æ¤¤¤ë¾ì¹ç¡¢¤³¤Î¥ê¡¼¥¹¤Ï¡¢»ØÄꤵ¤ì¤¿¥¤¥ó¥¿¥Õ¥§¡¼¥¹ ¾å¤Ç¤Î¤ß»ÈÍѤµ¤ì¤Þ¤¹¡£ ¥µ¡¼¥Ð¤«¤é¥ê¡¼¥¹¤ò¼õ¤±¼è¤Ã¤¿¤È¤­¡¢ ¥¯¥é¥¤¥¢¥ó¥È¤Ï¾ï¤Ë¤½¤Î¥ê¡¼¥¹¤ò¼õ¤±¼è¤Ã¤¿¥¤¥ó¥¿¥Õ¥§¡¼¥¹ÈÖ¹æ¤òµ­Ï¿¤·¤Þ¤¹¡£ dhclient.conf ¥Õ¥¡¥¤¥ë¤Ç»öÁ°¤Ë¥ê¡¼¥¹¤òÄêµÁ¤·¤Æ¤¤¤ë¾ì¹ç¡¢Í׵ᤵ¤ì¤Æ¤Ê¤¤ ¤Î¤Ç¤¹¤¬¡¢¤½¤Î¥ê¡¼¥¹¤Ç¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤â¤¢¤ï¤»¤Æ»ØÄꤷ¤Ê¤±¤ì¤Ð ¤Ê¤ê¤Þ¤»¤ó¡£ .PP \fBfixed-address\fR \fIip-address\fR\fB;\fR .PP .B fixed-address ʸ¤ÏÆÃÄê¤Î¥ê¡¼¥¹¤Î IP ¥¢¥É¥ì¥¹¤ò»ØÄꤹ¤ëºÝ¤Ë»È¤¤¤Þ¤¹¡£ ¤³¤ì¤Ï¤¹¤Ù¤Æ¤Î lease ʸ¤ËɬÍפǤ¹¡£ IP ¥¢¥É¥ì¥¹¤Ï (12.34.56.78 ¤Î¤è¤¦¤Ë) ¥É¥Ã¥ÈÉÕ¤­ 4 ¤ÄÁÈ·Á¼°¤Ç »ØÄꤷ¤Ê¤±¤ì¤Ð¤Ê¤ê¤Þ¤»¤ó¡£ .PP \fBfilename "\fR\fIstring\fR\fB";\fR .PP .B filename ʸ¤Ï»ÈÍѤ¹¤ë¥Ö¡¼¥È¥Õ¥¡¥¤¥ë̾¤ò»ØÄꤷ¤Þ¤¹¡£ ¤³¤ì¤Ïɸ½àŪ¤Ê¥¯¥é¥¤¥¢¥ó¥ÈÀßÄꥹ¥¯¥ê¥×¥È¤Ç¤Ï»È¤ï¤ì¤Þ¤»¤ó¤¬¡¢ ÀâÌÀ¤Î´°Á´¤ò´ü¤¹¤¿¤á¤Ë¤³¤³¤Ë´Þ¤á¤Æ¤¢¤ê¤Þ¤¹¡£ .PP \fBserver-name "\fR\fIstring\fR\fB";\fR .PP .B server-name ʸ¤Ï»ÈÍѤ¹¤ë¥Ö¡¼¥È¥µ¡¼¥Ð̾¤ò»ØÄꤷ¤Þ¤¹¡£ ¤³¤ì¤âɸ½àŪ¤Ê¥¯¥é¥¤¥¢¥ó¥ÈÀßÄꥹ¥¯¥ê¥×¥È¤Ç¤Ï»È¤ï¤ì¤Þ¤»¤ó¡£ .PP \fBoption\fR \fIoption-declaration\fR\fB;\fR .PP .B option ʸ¤Ï¡¢¥µ¡¼¥Ð¤«¤éÄ󶡤µ¤ì¤ë¥ª¥×¥·¥ç¥ó¤ÎÃͤò»ØÄꤹ¤ë¤Î¤Ë»È¤¤¤Þ¤¹¡£ ¤¢¤ë¤¤¤Ï¡¢dhclient.conf ¤Ç»öÁ°ÄêµÁ¥ê¡¼¥¹¤¬Àë¸À¤µ¤ì¤Æ¤¤¤ë¾ì¹ç¤Ë¤Ï¡¢ ¤½¤Î»öÁ°ÄêµÁ¥ê¡¼¥¹¤¬»È¤ï¤ì¤ëºÝ¤Ë¥¯¥é¥¤¥¢¥ó¥ÈÀßÄꥹ¥¯¥ê¥×¥È¤Ç»ÈÍѤ·¤Æ Íߤ·¤¤Ãͤò»ØÄꤷ¤Þ¤¹¡£ .PP \fBscript "\fIscript-name\fB";\fR .PP .B script ʸ¤Ï dhcp ¥¯¥é¥¤¥¢¥ó¥ÈÀßÄꥹ¥¯¥ê¥×¥È¤Î¥Ñ¥¹Ì¾¤ò»ØÄꤹ¤ë¤Î¤Ë»È¤¤¤Þ¤¹¡£ ¤³¤Î¥¹¥¯¥ê¥×¥È¤Ï¡¢¥¢¥É¥ì¥¹¤òÍ׵ᤷ¤¿¤ê¡¢°ÊÁ°¤ËÄ󶡤µ¤ì¤¿¥¢¥É¥ì¥¹¤ò »î¤·¤¿¤ê¡¢ ¥ê¡¼¥¹¤ò¼èÆÀ¤·¤Æ¤«¤é¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤ÎºÇ½ªÀßÄê¤ò¹Ô¤Ã¤¿¤ê¤¹¤ëÁ°¤Ë¡¢ dhcp ¥¯¥é¥¤¥¢¥ó¥È¤¬³Æ¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤Î½é´üÀßÄê¤ò¹Ô¤¦¤Î¤Ë»È¤¤¤Þ¤¹¡£ ¥ê¡¼¥¹¤¬¼èÆÀ¤Ç¤­¤Ê¤«¤Ã¤¿¾ì¹ç¤Ë¤Ï¡¢ »öÁ°ÄêµÁ¥ê¡¼¥¹¤¬Â¸ºß¤¹¤ë¾ì¹ç¡¢¤½¤ì¤é¤ò»î¤¹¤¿¤á¤Ë¤³¤Î¥¹¥¯¥ê¥×¥È¤¬»È¤ï¤ì¤Þ¤¹¡£ ¤Þ¤¿¡¢Í­¸ú¤Ê¥ê¡¼¥¹¤¬¤Ò¤È¤Ä¤âÆÀ¤é¤ì¤Ê¤«¤Ã¤¿¾ì¹ç¤Ç¤â¡¢¤³¤Î¥¹¥¯¥ê¥×¥È¤Ï¡¢ 1 ²ó¤Ï¸Æ¤Ó½Ð¤µ¤ì¤Þ¤¹¡£ ¤è¤ê¾Ü¤·¤¯¤Ï¡¢ .B dhclient-script(8) ¤ò»²¾È¤·¤Æ¤¯¤À¤µ¤¤¡£ .PP \fBvendor option space "\fIname\fB";\fR .PP .B vendor option space ʸ¤Ï¡¢vendor-encapsulate-options ¥ª¥×¥·¥ç¥ó¤ò¼õ¿®¤·¤¿¾ì¹ç¡¢ Éü¹æ²½¤Ë¤É¤Î¥ª¥×¥·¥ç¥ó¶õ´Ö¤ò»ÈÍѤ¹¤ë¤Ù¤­¤«¤ò»ØÄꤹ¤ë¤¿¤á¤Ë»ÈÍѤµ¤ì¤Þ¤¹¡£ ¥µ¡¼¥Ð¤«¤é¤Î¥Ù¥ó¥À¥ª¥×¥·¥ç¥ó¤ÎÆÃÄê¤Î¥¯¥é¥¹¤òÍ׵᤹¤ë¤¿¤á¤Ë¡¢ \fIdhcp-vendor-identifier\fR ¤ò»ÈÍѤ¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£ ¾ÜºÙ¤Ï .B dhcp-options(5) ¤ò»²¾È¤·¤Æ¤¯¤À¤µ¤¤¡£ .PP \fBmedium "\fImedia setup\fB";\fR .PP .B medium ʸ¤Ï¡¢Àܳ¤µ¤ì¤Æ¤¤¤ë¥Í¥Ã¥È¥ï¡¼¥¯¤Î¥¿¥¤¥×¤ò¥Í¥Ã¥È¥ï¡¼¥¯¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤¬ ¼«Æ°Åª¤ËȽÃǤǤ­¤Ê¤¤¤è¤¦¤Ê¥·¥¹¥Æ¥à¤Ç»È¤¦¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£ ʸ»úÎó media setup ¤Ï¥·¥¹¥Æ¥à°Í¸¤Î¥Ñ¥é¥á¡¼¥¿¤Ç¡¢ ¥¤¥ó¥¿¥Õ¥§¡¼¥¹½é´ü²½¤ÎºÝ¤Ë dhcp ¥¯¥é¥¤¥¢¥ó¥ÈÀßÄꥹ¥¯¥ê¥×¥È¤ËÅϤµ¤ì¤Þ¤¹¡£ Unix ¤ª¤è¤Ó Unix É÷¤Î¥·¥¹¥Æ¥à¤Ç¤Ï¡¢ ¤³¤Î°ú¿ô¤Ï¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤òÀßÄꤹ¤ë¤È¤­¤Ë ifconfig ¥³¥Þ¥ó¥É¥é¥¤¥ó¤Ë ÅϤµ¤ì¤Þ¤¹¡£ .PP ¥ê¡¼¥¹¤òÆÀ¤ë¤¿¤á¤Ë¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤òÀßÄꤹ¤ë ºÝ¤Ë¡¢dhcp ¥¯¥é¥¤¥¢¥ó¥È¤¬¥á¥Ç¥£¥¢¥¿¥¤¥× ( .B media ʸ¤ò»²¾È) ¤ò»ÈÍѤ¹¤ë¾ì¹ç¡¢dhcp ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢¤³¤Î¥Ñ¥é¥á¡¼¥¿¤ò ¼«Æ°Åª¤ËÀë¸À¤·¤Þ¤¹¡£¥Í¥Ã¥È¥ï¡¼¥¯¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤¬¥á¥Ç¥£¥¢¥¿¥¤¥×¤Î ÀßÄê¤òɬÍפȤ¹¤ë¾ì¹ç¤Ï (¤¹¤ë¾ì¹ç¤Ë¸Â¤ê)¡¢¤³¤Îʸ¤ò»öÁ°ÄêµÁ¥ê¡¼¥¹¤Ç »ÈÍѤ·¤Ê¤±¤ì¤Ð¤Ê¤ê¤Þ¤»¤ó¡£ .PP \fBrenew\fR \fIdate\fB;\fR .PP \fBrebind\fR \fIdate\fB;\fR .PP \fBexpire\fR \fIdate\fB;\fR .PP \fBrenew\fR ʸ¤Ï¡¢¸½ºß»ÈÍÑÃæ¤Î¥ê¡¼¥¹¤ò¹¹¿· (renew) ¤¹¤ë¤¿¤á¤Ë¡¢ dhcp ¥¯¥é¥¤¥¢¥ó¥È¤¬»ÈÍÑÃæ¤Î¥ê¡¼¥¹¤òÄ󶡤·¤Æ¤¯¤ì¤¿¥µ¡¼¥Ð¤Ø¤Î¥¢¥¯¥»¥¹¤Î »î¤ß¤ò³«»Ï¤·¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤Æü»þ¤òÄêµÁ¤·¤Þ¤¹¡£\fBrebind\fR ʸ¤Ï¡¢ ¥ê¡¼¥¹¤ò¹¹¿·¤¹¤ë¤¿¤á¤Ë¡¢dhcp ¥¯¥é¥¤¥¢¥ó¥È¤¬ \fI¤¤¤º¤ì¤«¤Î\fR dhcp ¥µ¡¼¥Ð¤Ø¤Î¥¢¥¯¥»¥¹¤Î»î¤ß¤ò³«»Ï¤·¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤Æü»þ¤òÄêµÁ¤·¤Þ¤¹¡£ \fBexpire\fR ʸ¤Ï¡¢¥ê¡¼¥¹¤Î¹¹¿·¤Î¤¿¤á¤Ë¥µ¡¼¥Ð¤Ë¥¢¥¯¥»¥¹¤Ç¤­¤Ê¤«¤Ã¤¿¾ì¹ç¡¢ dhcp ¥¯¥é¥¤¥¢¥ó¥È¤¬¤½¤Î¥ê¡¼¥¹¤Î»ÈÍѤòÄä»ß¤·¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤Æü»þ¤ò ÄêµÁ¤·¤Þ¤¹¡£ .PP ¤³¤ì¤é¤ÎÀë¸À¤Ï¡¢DHCP ¥¯¥é¥¤¥¢¥ó¥È¤¬ÆÀ¤¿¥ê¡¼¥¹Ãæ¤Ç¤Ï¼«Æ°Åª¤ËÀßÄꤵ¤ì¤Þ¤¹¡£ »öÁ°ÄêµÁ¥ê¡¼¥¹¤Î¤¦¤Á¡¢DHCP ¥¯¥é¥¤¥¢¥ó¥È¤ËÍ­¸ú´ü¸Â¤¬²á¤®¤¿¤â¤Î¤ò»ÈÍѤ·¤Æ Íߤ·¤¯¤Ê¤¤¤â¤Î¤ÎÃæ¤Ç¤Ï¡¢¤³¤ì¤é¤ÎÀë¸À¤òÀßÄꤷ¤Æ¤ª¤¯É¬Íפ¬¤¢¤ê¤Þ¤¹¡£ .PP date ¤Ï°Ê²¼¤Î¤è¤¦¤Ë»ØÄꤷ¤Þ¤¹¡£ .PP \fI \fB/\fI\fB/\fI \fB:\fI\fB:\fI\fR .PP weekday ¤Ï¡¢¿Í´Ö¤¬¸«¤Æ¥ê¡¼¥¹´ü¸Â¤ò¤ï¤«¤ê¤ä¤¹¤¯¤¹¤ë¤¿¤á¤Ë¸ºß¤·¤Þ¤¹¡£ ¤³¤ì¤Ï¡¢0 ¤«¤é 6 ¤Þ¤Ç¤Î¿ô»ú¤Ç»ØÄꤷ¤Þ¤¹¡£0 ¤ÏÆüÍËÆü¤Ç¤¹¡£year ¤ÏÀ¤µª ¹þ¤ß¤Ç»ØÄꤷ¤Þ¤¹¡£¤Ç¤¹¤«¤é¡¢ËÜÅö¤ËŤ¤¥ê¡¼¥¹¤òÊ̤ˤ¹¤ë¤È¡¢É¬¤º 4 ·å¤Ë ¤Ê¤ë¤Ï¤º¤Ç¤¹¡£month ¤Ï 1 (1 ·î¤òɽ¤·¤Þ¤¹) ¤«¤é»Ï¤Þ¤ë¿ô»ú¤Ç»ØÄꤷ¤Þ¤¹¡£ day ¤ÏƱÍÍ¤Ë 1 ¤«¤é»Ï¤Þ¤ë (·î¤Ë¤ª¤±¤ë) Æü¤È¤·¤Æ»ØÄꤷ¤Þ¤¹¡£hour ¤Ï¡¢ 0 ¤«¤é 23 ¤Î´Ö¤Î¿ô»ú¤Ç¤¹¡£minute ¤È second ¤Ï¤È¤â¤Ë 0 ¤«¤é 59 ¤Î´Ö¤Î ¿ô»ú¤ò»ØÄꤷ¤Þ¤¹¡£ .SH ¥¨¥¤¥ê¥¢¥¹Àë¸À \fBalias { \fI declarations ... \fB}\fR .PP DHCP ¥¯¥é¥¤¥¢¥ó¥È¤¬ TCP/IP ¥í¡¼¥ß¥ó¥° (roaming) ¥×¥í¥È¥³¥ë¤ò¼Â¹Ô¤·¤Æ ¤¤¤ë¾ì¹ç¡¢DHCP ¤òÍѤ¤¤ÆÆÀ¤é¤ì¤ë¥ê¡¼¥¹¤À¤±¤Ç¤Ê¤¯¡¢»öÁ°¤ËÄêµÁ¤µ¤ì¤¿ IP ¥¨¥¤¥ê¥¢¥¹¤â¡¢¼«Ê¬¤¬»ÈÍѤ¹¤ë¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤ËÀßÄꤹ¤ëɬÍפ¬¤¢¤ë ¾ì¹ç¤¬¤¢¤ê¤Þ¤¹¡£Internet Systems Consortium ÈÇ DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢ ¸ÇÄꥢ¥É¥ì¥¹Ä¾ÀÜ»ØÄê¤Î¥í¡¼¥ß¥ó¥°¤ò¥µ¥Ý¡¼¥È¤·¤Æ¤¤¤Þ¤»¤ó¤¬¡¢¤½¤Î¼ï¤Î¼Â¸³ ¤¬¤Ç¤­¤ë¤è¤¦¤Ë¡¢¤³¤Î dhcp ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢ .B alias Àë¸À¤ò»È¤Ã¤Æ IP ¥¨¥¤¥ê¥¢¥¹¤òÀßÄꤹ¤ë½àÈ÷¤Ï¤Ç¤­¤Æ¤¤¤Þ¤¹¡£ .PP alias Àë¸À¤Ï lease Àë¸À¤Ë»÷¤Æ¤¤¤Þ¤¹¡£Ã¢¤·¡¢É¸½à¤Î ¥¯¥é¥¤¥¢¥ó¥ÈÀßÄꥹ¥¯¥ê¥×¥È¤Ç¤Ï¡¢subnet-mask ¥ª¥×¥·¥ç¥ó°Ê³°¤Î ¥ª¥×¥·¥ç¥ó¤È¡¢³Æ¼ïÍ­¸ú´ü¸Â (expiry times) ¤¬Ìµ»ë¤µ¤ì¤ëÅÀ¤¬°Û¤Ê¤ê¤Þ¤¹¡£ ÉáÄ̤Πalias Àë¸À¤Ç¤Ï¡¢ interface Àë¸À¡¢IP ¥¨¥¤¥ê¥¢¥¹¤Î¤¿¤á¤Î ¸ÇÄꥢ¥É¥ì¥¹Àë¸À¡¢subnet-mask ¥ª¥×¥·¥ç¥ó¤ò´Þ¤ß¤Þ¤¹¡£alias Àë¸À¤Ë¤Ï medium ʸ¤Ï·è¤·¤Æ´Þ¤Þ¤ì¤Æ¤Ï¤Ê¤ê¤Þ¤»¤ó¡£ .SH ¤½¤Î¾¤ÎÀë¸À \fBreject \fIip-address\fB;\fR .PP .B reject ʸ¤Ë¤è¤ê¡¢DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï»ØÄꤷ¤¿¥¢¥É¥ì¥¹¤ò¥µ¡¼¥Ð¼±Ê̻ҤȤ·¤Æ»ÈÍѤ¹¤ë ¥µ¡¼¥Ð¤«¤é¤ÎÄó¶¡¿½¤·½Ð¤òµñÈݤ¹¤ë¤è¤¦¤Ë¤Ê¤ê¤Þ¤¹¡£É¸½à¤Ë½àµò¤·¤Ê¤¤ dhcp ¥µ¡¼¥Ð¤äÀßÄê¤ò´Ö°ã¤¨¤Æ¤¤¤ë dhcp ¥µ¡¼¥Ð¤Ë¤è¤Ã¤Æ¥¯¥é¥¤¥¢¥ó¥È¤¬ÀßÄꤵ¤ì¤Ê¤¤ ¤è¤¦¤Ë¤¹¤ë¤¿¤á¤Ë¡¢¤³¤Îʸ¤ò»ÈÍѤ¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£¤·¤«¤·¤Ê¤¬¤é¡¢¤³¤ì¤Ï ºÇ¸å¤ÎÉð´ï¤È¤¹¤ë¤Ù¤­¤Ç¤¹¡£¤³¤ì¤ËÀèΩ¤Á¡¢Éå¤Ã¤¿ DHCP ¥µ¡¼¥Ð¤òÄɤ¤¤«¤±¤Æ ¤½¤ì¤òľ¤¹Êý¤¬¤è¤¤¤Ç¤¹¡£ .PP \fBinterface "\fIname\fB" { \fIdeclarations ... \fB } .PP Ê£¿ô¤Î¥Í¥Ã¥È¥ï¡¼¥¯¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤ò»ý¤Ä¥¯¥é¥¤¥¢¥ó¥È¤Î¾ì¹ç¡¢DHCP ¤Ç ÀßÄꤵ¤ì¤ë¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤Ë¤è¤Ã¤Æ°Û¤Ê¤ëưºî¤ò¤µ¤»¤ëɬÍפ¬¤¢¤ë¾ì¹ç¤¬ ¤¢¤ê¤Þ¤¹¡£lease Àë¸À¤È alias Àë¸À¤ò½ü¤¯¤¹¤Ù¤Æ¤Î¥¿¥¤¥ß¥ó¥°¥Ñ¥é¥á¡¼¥¿ ¤ÈÀë¸À¤ò¡¢interface Àë¸À¤Ç°Ï¤à¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£¤½¤Î¾ì¹ç¡¢°Ï¤Þ¤ì¤¿ ¥Ñ¥é¥á¡¼¥¿¤Ï»ØÄꤷ¤¿Ì¾Á°¤Ë¹çÃפ¹¤ë¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤Ë¤Î¤ßŬÍѤµ¤ì¤Þ¤¹¡£ interface Àë¸À¤ò»ý¤¿¤Ê¤¤¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤Ï¡¢¤¹¤Ù¤Æ¤Î interface Àë¸À¤Î ³°Â¦¤ÇÀë¸À¤µ¤ì¤¿¥Ñ¥é¥á¡¼¥¿¡¢¤â¤·¤¯¤Ï¥Ç¥Õ¥©¥ë¥È¤ÎÀßÄ꤬ŬÍѤµ¤ì¤Þ¤¹¡£ .PP \fBpseudo "\fIname\fR" "\fIreal-name\fB" { \fIdeclarations ... \fB } .PP ¾õ¶·¤Ë¤è¤Ã¤Æ¤Ï²¾ÁÛ¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤òÀë¸À¤·¡¢ DHCP ¥¯¥é¥¤¥¢¥ó¥È¤¬¤³¤Î¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤Î¤¿¤á¤ÎÀßÄê¤ò¼èÆÀ¤¹¤ë¤è¤¦¤Ë¤¹¤ë¤È ÊØÍø¤Ë¤Ê¤êÆÀ¤Þ¤¹¡£ Ä̾ï DHCP ¥¯¥é¥¤¥¢¥ó¥È¤¬¥µ¥Ý¡¼¥È¤·¤Æ¤¤¤ë³Æ¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤Ï¡¢ ¤½¤Î¥ê¡¼¥¹¤ò³ÍÆÀ¤·´ÉÍý¤¹¤ë¤¿¤á¤Ë¡¢ DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Î¾õÂÖµ¡³£¤ò¼Â¹Ô¤·¤Æ¤¤¤Þ¤¹¡£ ²¾ÁÛ¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤Ï¡¢\fIreal-name\fR ¤È̾ÉÕ¤±¤é¤ì¤¿¥¤¥ó¥¿¥Õ¥§¡¼¥¹¾å¤Ç ²ÔƯ¤·¤Æ¤¤¤ë¡¢¤Þ¤µ¤·¤¯¤â¤¦°ì¤Ä¤Î¾õÂÖµ¡³£¤Ç¤¹¡£ ¤³¤Îµ¡Ç½¤ò»ÈÍѤ¹¤ë¾ì¹ç¡¢ ²¾ÁÛ¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤È¼ÂºÝ¤Î¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤ÎξÊý¤ËÂФ·¤Æ ¥¯¥é¥¤¥¢¥ó¥È¼±Ê̻ҤòÄ󶡤·¤Ê¤±¤ì¤Ð¤Ê¤ê¤Þ¤»¤ó¡£ ¤Þ¤¿¡¢»ÈÍѤ·¤¿¤¤ IP ¥¢¥É¥ì¥¹¤ËÂФ¹¤ë²¾ÁÛ¥¤¥ó¥¿¥Õ¥§¡¼¥¹ÍÑ¤Ë Ê¬Î¥¤µ¤ì¤¿¥¯¥é¥¤¥¢¥ó¥È¥¹¥¯¥ê¥×¥È¤òÄ󶡤·¤Ê¤±¤ì¤Ð¤Ê¤ê¤Þ¤»¤ó¡£ Î㤨¤Ð¼¡¤Î¤è¤¦¤Ë¤Ê¤ê¤Þ¤¹: .PP .nf interface "ep0" { send dhcp-client-identifier "my-client-ep0"; } pseudo "secondary" "ep0" { send dhcp-client-identifier "my-client-ep0-secondary"; script "/etc/dhclient-secondary"; } .fi .PP ²¾ÁÛ¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤Î¤¿¤á¤Î¥¯¥é¥¤¥¢¥ó¥È¥¹¥¯¥ê¥×¥È¤Ï ¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤òÍ­¸ú¤Ë¤·¤¿¤ê̵¸ú¤Ë¤·¤¿¤ê¤¹¤ëÀßÄê¤ò¤¹¤ë¤Ù¤­¤Ç¤Ï¤¢¤ê¤Þ¤»¤ó¡£ ÆÃ¤Ë¡¢¥ê¡¼¥¹¤Î³ÍÆÀ¤ä¹¹¿·¤Î¾õÂÖ¡¢¤½¤·¤Æ¥ê¡¼¥¹¤Î´ü¸ÂÀÚ¤ì¤Î¾õÂÖ¤ò ¼è¤ê°·¤¦¤¿¤á¤Ë¤Ï¡¢¤½¤Î¤³¤È¤¬É¬ÍפǤ¹¡£ ¾ÜºÙ¤Ï \fBdhclient-script(8)\fR ¤ò»²¾È¤·¤Æ²¼¤µ¤¤¡£ .PP \fBmedia "\fImedia setup\fB"\fI [ \fB, "\fImedia setup\fB", \fI... ]\fB;\fR .PP .B media ʸ¤Ï¡¢IP ¥¢¥É¥ì¥¹¼èÆÀÃæ¤Ë»ÈÍѤ¬»î¤ß¤é¤ì¤ë¡¢¥á¥Ç¥£¥¢ÀßÄê¥Ñ¥é¥á¡¼¥¿¤ò 1 ¤Ä °Ê¾åÄêµÁ¤·¤Þ¤¹¡£dhcp ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢¥ê¥¹¥ÈÃæ¤Î³Æ media setup ʸ»úÎó¤ò ½ç¼¡»ÈÍѤ·¡¢¤¢¤ë¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤ò¤½¤ì¤ÇÀßÄꤷ¡¢¥Ö¡¼¥È¤ò»î¤ß¤Þ¤¹¡£ ÂÌÌܤʤé¤Ð¼¡¤Î media setup ʸ»úÎó¤ò»ÈÍѤ·¤Þ¤¹¡£¤³¤Îʸ¤Ï¡¢ ¥á¥Ç¥£¥¢¥¿¥¤¥×¤ò¸¡½Ð¤¹¤ëǽÎϤò»ý¤¿¤Ê¤¤¥Í¥Ã¥È¥ï¡¼¥¯¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤Ë ÂФ·¤ÆÍøÍѤǤ­¤Þ¤¹¡£¥µ¡¼¥Ð¤Ø¤Î¥ê¥¯¥¨¥¹¥È¤¬¤Ç¤­±þÅú¤¬ÆÀ¤é¤ì¤ë¤â¤Î ¤Ê¤é¤Ð¡¢¤É¤Î¤è¤¦¤Ê¥á¥Ç¥£¥¢¥¿¥¤¥×¤Ç¤â¤¿¤Ö¤óÀµÅö¤Ç¤¹ (ÊݾڤϤ·¤Þ¤»¤ó¤¬)¡£ .PP media setup ¤Ï¥¢¥É¥ì¥¹¼èÆÀ¤Î½é´ü¥Õ¥§¡¼¥º (DHCPDISCOVER ¥Ñ¥±¥Ã¥È¤È DHCPOFFER ¥Ñ¥±¥Ã¥È)¤Ç¤Î¤ß»ÈÍѤµ¤ì¤Þ¤¹¡£¤Ò¤È¤¿¤Ó¥¢¥É¥ì¥¹¤¬¼èÆÀ¤µ¤ì¤ë¤È¡¢ dhcp ¥¯¥é¥¤¥¢¥ó¥È¤Ï¤½¤Î¥¢¥É¥ì¥¹¤ò¥ê¡¼¥¹¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ëµ­Ï¿¤·¡¢ ¤½¤Î¥¢¥É¥ì¥¹¤òÆÀ¤ëºÝ¤ËÍѤ¤¤¿¥á¥Ç¥£¥¢¥¿¥¤¥×¤òµ­Ï¿¤·¤Þ¤¹¡£¥¯¥é¥¤¥¢¥ó¥È¤¬ ¥ê¡¼¥¹¤ò¹¹¿·¤·¤è¤¦¤È¤¹¤ëºÝ¤Ë¤Ï¾ï¤Ë¡¢¤½¤ì¤ÈƱ¤¸¥á¥Ç¥£¥¢¥¿¥¤¥×¤ò»ÈÍѤ·¤Þ¤¹¡£ ¥ê¡¼¥¹¤ò´ü¸ÂÀÚ¤ì¤Ë¤·¤Æ¤Ï¤¸¤á¤Æ¡¢¥¯¥é¥¤¥¢¥ó¥È¤Ï¥á¥Ç¥£¥¢¥¿¥¤¥×¤ò½ç¤Ë»î¤¹ ¾õÂÖ¤ËÌá¤ê¤Þ¤¹¡£ .\"X .SH SAMPLE ... man-jp ɸ½à¤Ï¤Ê¤ó¤À¤Ã¤¿¤Ã¤± .SH »ÈÍÑÎ㠰ʲ¼¤ÎÀßÄê¥Õ¥¡¥¤¥ë¤Ï¡¢NetBSD 1.3 ¤ò¼Â¹Ô¤¹¤ë¤¢¤ë¥é¥Ã¥×¥È¥Ã¥×¥Þ¥·¥ó¤Ç »ÈÍѤµ¤ì¤Æ¤¤¤ë¤â¤Î¤Ç¤¹¡£¤³¤Î¥Þ¥·¥ó¤Ï¡¢IP ¥¨¥¤¥ê¥¢¥¹¤È¤·¤Æ 192.5.5.213¡¢ ¥¤¥ó¥¿¥Õ¥§¡¼¥¹ ep0 (3Com 3C589C) ¤ò¤Ò¤È¤Ä»ý¤Ã¤Æ¤¤¤Þ¤¹¡£¤³¤Î¥¯¥é¥¤¥¢¥ó¥È ¤Ï¡¢DHCP ³èư¤¬¤Û¤È¤ó¤É¤Ê¤¤¥Í¥Ã¥È¥ï¡¼¥¯¤Ç»þ´Ö¤ÎÂçÉôʬ¤ò¾ÃÈñ¤¹¤ë¤³¤È¤¬ ¤ï¤«¤Ã¤Æ¤¤¤ë¤Î¤Ç¡¢¥Ö¡¼¥È´Ö³Ö¤Ï¥Ç¥Õ¥©¥ë¥ÈÃͤ«¤é¤¤¤¯¤Ö¤ó¾®¤µ¤¯¤·¤Æ ¤¢¤ê¤Þ¤¹¡£¤³¤Î¥Þ¥·¥ó¤ÏÊ£¿ô¥Í¥Ã¥È¥ï¡¼¥¯´Ö¤Ç¥í¡¼¥ß¥ó¥° (°Üư) ¤·¤Þ¤¹¡£ .nf timeout 60; retry 60; reboot 10; select-timeout 5; initial-interval 2; reject 192.33.137.209; interface "ep0" { send host-name "andare.example.com"; send dhcp-client-identifier 1:0:a0:24:ab:fb:9c; send dhcp-lease-time 3600; supersede domain-name "example.com rc.isc.org home.isc.org"; prepend domain-name-servers 127.0.0.1; request subnet-mask, broadcast-address, time-offset, routers, domain-name, domain-name-servers, host-name; require subnet-mask, domain-name-servers; script "CLIENTBINDIR/dhclient-script"; media "media 10baseT/UTP", "media 10base2/BNC"; } alias { interface "ep0"; fixed-address 192.5.5.213; option subnet-mask 255.255.255.255; } .fi ¤³¤ì¤Ï dhclient.conf ¥Õ¥¡¥¤¥ë¤È¤·¤Æ¤ÏÈó¾ï¤ËÊ£»¨¤Ê¤â¤Î¤Ç¤¹¡£°ìÈ̤ˡ¢ ³§¤µ¤ó¤¬»ÈÍѤ¹¤ë¤â¤Î¤Ï¤Ï¤ë¤«¤Ë´Êñ¤Ê¤Ï¤º¤Ç¤¹¡£Â¿¤¯¤Î¾ì¹ç¡¢dhclient.conf ¥Õ¥¡¥¤¥ë¤È¤·¤Æ¶õ¤Î¥Õ¥¡¥¤¥ë¤òÀ¸À®¤¹¤ë¤À¤±¤Ç½½Ê¬¤Ê¤Ï¤º¤Ç¤¹¡£ ¤Ä¤Þ¤ê¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤǤ褤¤Î¤¬ÉáÄ̤Ǥ¹¡£ .SH ´ØÏ¢¹àÌÜ dhcp-options(5), dhclient.leases(5), dhclient(8), RFC2132, RFC2131 .SH ºî¼Ô .B dhclient(8) ¤Ï Vixie Labs ¤È¤Î·ÀÌó¤Î¤â¤È¤Ç Ted Lemon ¤¬½ñ¤­¤Þ¤·¤¿¡£ ËÜ¥×¥í¥¸¥§¥¯¥È¤Î´ð¶â¤Ï Internet Systems Consortium ¤¬Ä󶡤·¤Þ¤·¤¿¡£ Internet Systems Consortium ¤Ë´Ø¤¹¤ë¾ðÊó¤Ï¡¢ .B https://www.isc.org ¤Ë¤¢¤ê¤Þ¤¹¡£ dhcp-4.4.1/doc/ja_JP.eucJP/dhclient.leases.5000644 000765 000024 00000004260 13243301226 020605 0ustar00tmarkstaff000000 000000 .\" $Id: dhclient.leases.5,v 1.4 2009/11/24 02:06:56 sar Exp $ .\" .\" Copyright (c) 2004,2009,2016 by Internet Systems Consortium, Inc. ("ISC") .\" Copyright (c) 1997-2003 by Internet Software Consortium .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT .\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" Internet Systems Consortium, Inc. .\" 950 Charter Street .\" Redwood City, CA 94063 .\" .\" https://www.isc.org/ .\" .\" %FreeBSD: src/contrib/isc-dhcp/client/dhclient.leases.5,v 1.2.4.1 2002/04/11 10:16:46 murray Exp % .\" .\" $FreeBSD: doc/ja_JP.eucJP/man/man5/dhclient.leases.5,v 1.6 2002/05/05 20:40:23 horikawa Exp $ .TH dhclient.leases 5 .SH ̾¾Î dhclient.leases - DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Î¥ê¡¼¥¹¥Ç¡¼¥¿¥Ù¡¼¥¹ .SH ²òÀâ Internet Systems Consortium ¤Î DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢ ³ÍÆÀ¤·¤¿¥ê¡¼¥¹¤Î¤¦¤Á¤Þ¤ÀÍ­¸ú¤Ç¤¢¤ë¤â¤Î¤ò´ÉÍý¤¹¤ë¤¿¤á¤Î¡¢ ±Ê³Ū¤Ê¥Ç¡¼¥¿¥Ù¡¼¥¹¤òÊÝ»ý¤·¤Þ¤¹¡£ ¤³¤Î¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ï¡¢¼«Í³·Á¼°¤Î ASCII ¥Õ¥¡¥¤¥ë¤Ç¤¢¤ê¡¢ ¥ê¡¼¥¹ 1 ¤Ä¤Ë¤Ä¤­Í­¸ú¤ÊÀë¸À¤ò 1 ¤Ä´Þ¤ß¤Þ¤¹¡£ ¤¢¤ë¥ê¡¼¥¹¤ËÂФ·¤ÆÊ£¿ô¤ÎÀë¸À¤¬Åо줹¤ë¾ì¹ç¡¢ ¥Õ¥¡¥¤¥ëÃæ¤ÎºÇ¸å¤Î¤â¤Î¤¬»ÈÍѤµ¤ì¤Þ¤¹¡£ ¤³¤Î¥Õ¥¡¥¤¥ë¤Ï¥í¥°¤È¤·¤Æ½ñ¤­¹þ¤Þ¤ì¤Þ¤¹¤Î¤Ç¡¢ ¤³¤Î¤è¤¦¤Ê¾õÂ֤ˤʤ뤳¤È¤Ï°Û¾ï¤Ç¤Ï¤¢¤ê¤Þ¤»¤ó¡£ .PP ¥ê¡¼¥¹Àë¸À¤Î½ñ¼°¤Ï¡¢ .B dhclient.conf(5) ¤Ëµ­½Ò¤µ¤ì¤Æ¤¤¤Þ¤¹¡£ .SH ´ØÏ¢¥Õ¥¡¥¤¥ë .B DBDIR/dhclient.leases .SH ´ØÏ¢¹àÌÜ dhclient(8), dhcp-options(5), dhclient.conf(5), RFC2132, RFC2131 .SH ºî¼Ô .B dhclient(8) ¤Ï¡¢Vixie Labs ¤È¤Î·ÀÌó¤Î¤â¤È¤Ç¡¢Ted Lemon ¤¬µ­½Ò¤·¤Þ¤·¤¿¡£ ËÜ¥×¥í¥¸¥§¥¯¥È¤Î»ñ¶â¤Ï¡¢Internet Systems Consortium ¤¬Ä󶡤·¤Þ¤·¤¿¡£ Internet Systems Consortium ¤Ë´Ø¤¹¤ë¾ðÊó¤Ï¡¢ .B https://www.isc.org ¤Ë¤¢¤ê¤Þ¤¹¡£ dhcp-4.4.1/doc/ja_JP.eucJP/dhcp-eval.5000644 000765 000024 00000037575 13243301226 017422 0ustar00tmarkstaff000000 000000 .\" $Id: dhcp-eval.5,v 1.5 2009/11/24 02:06:56 sar Exp $ .\" .\" Copyright (c) 2004,2009,2016 by Internet Systems Consortium, Inc. ("ISC") .\" Copyright (c) 1996-2003 by Internet Software Consortium .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT .\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" Internet Systems Consortium, Inc. .\" 950 Charter Street .\" Redwood City, CA 94063 .\" .\" https://www.isc.org/ .\" .\" $FreeBSD: doc/ja_JP.eucJP/man/man5/dhcp-eval.5,v 1.2 2002/05/23 04:17:13 horikawa Exp $ .TH dhcp-eval 5 .SH ̾¾Î dhcp-eval - ISC DHCP ¤Ë¤ª¤±¤ë¾ò·ïÉÕ¤­É¾²Á .SH ²òÀâ Internet Systems Consortium ¤Î DHCP ¥¯¥é¥¤¥¢¥ó¥È¤È¥µ¡¼¥Ð¤Ï¡¢¤É¤Á¤é¤â ¼õ¿®¤¹¤ë¥Ñ¥±¥Ã¥È¤Ë°Í¸¤·¤¿¾ò·ïÉÕ¤­Æ°ºî¤ò¹Ô¤¦Ç½ÎϤò»ý¤Á¤Þ¤¹¡£ ¾ò·ïÉÕ¤­Æ°ºî¤Îʸˡ¤ò¤³¤³¤Ë¼¨¤·¤Þ¤¹¡£ .SH »²¾È: ¾ò·ïÉÕ¤­Æ°ºî ¾ò·ïÉÕ¤­Æ°ºî¤Ï¡¢if, else, elsif ʸ¤ò»ÈÍѤ·¤Æ»ØÄꤷ¤Þ¤¹¡£ ¾ò·ïʸ¤Ï¡¢Ä̾ïʸ (option ʸ) ¤¬Åоì²Äǽ¤Ê¾ì½ê¤Ï¤É¤³¤Ë¤Ç¤âÅоì²Äǽ¤Ç¤¢¤ê¡¢ ¤Þ¤¿¤³¤Î¤è¤¦¤Êʸ¤ò³ç¤ë¤³¤È¤â²Äǽ¤Ç¤¹¡£ ¥µ¡¼¥Ð¤Ë¤ª¤±¤ë¾ò·ïʸ¤Ï¼¡¤Î¤è¤¦¤Ë¤Ê¤ë¤³¤È¤¬Â¿¤¤¤Ç¤·¤ç¤¦: .PP .nf if option dhcp-user-class = "accounting" { max-lease-time 17600; option domain-name "accounting.example.org"; option domain-name-servers ns1.accounting.example.org, ns2.accounting.example.org; } elsif option dhcp-user-class = "sales" { max-lease-time 17600; option domain-name "sales.example.org"; option domain-name-servers ns1.sales.example.org, ns2.sales.example.org; } elsif option dhcp-user-class = "engineering" { max-lease-time 17600; option domain-name "engineering.example.org"; option domain-name-servers ns1.engineering.example.org, ns2.engineering.example.org; } else { max-lease-time 600; option domain-name "misc.example.org"; option domain-name-servers ns1.misc.example.org, ns2.misc.example.org; } .fi .PP ¥¯¥é¥¤¥¢¥ó¥È¦¤Ç¤Ï¡¢¾ò·ïÉÕ¤­É¾²Á¤ÎÎã¤Ï¼¡¤Î¤è¤¦¤Ë¤Ê¤ë¤Ç¤·¤ç¤¦: .PP .nf # example.org ¤Ï¥Õ¥¡¥¤¥ä¥¦¥©¡¼¥ë¤Ç DNS ¤ò¥Õ¥£¥ë¥¿¤¹¤ë¤Î¤Ç¡¢ # example.org ¥Í¥Ã¥È¥ï¡¼¥¯¤Ë·Ò¤¬¤ë¤È¤­¤Î¤ß¡¢¤½¤Î DNS ¥µ¡¼¥Ð¤ò»ÈÍѤ·¤Þ¤¹¡£ # example.org ¤Ë·Ò¤¬¤ë¤Î¤Ç¤Ï¤Ê¤¤¾ì¹ç¡¢¼«¸Ê¤Î DNS ¥µ¡¼¥Ð¤òÍ¥Àè»ÈÍѤ·¤Þ¤¹¡£ if not option domain-name = "example.org" { prepend domain-name-servers 127.0.0.1; } .fi .PP .B if ʸ¤È .B elsif ·Ñ³ʸ¤Ï¡¢°ú¿ô¤È¤·¤Æ¥Ö¡¼¥ë¼°¤ò¼è¤ê¤Þ¤¹¡£ ¤Ä¤Þ¤ê¡¢¤³¤ì¤é¤Îʸ¤Ï¡¢É¾²Á¤µ¤ì¤ë¤È¥Ö¡¼¥ëÃͤηë²Ì¤òÀ¸À®¤¹¤ë¼°¤ò¼è¤ê¤Þ¤¹¡£ ¼°¤Îɾ²Á·ë²Ì¤¬¿¿¤Ë¤Ê¤ë¤È¡¢ .B if ʸ¤Îľ¸å¤Î¥Ö¥ì¡¼¥¹¤Ç³ç¤é¤ì¤¿Ê¸¤¬¼Â¹Ô¤µ¤ì¡¢¸å³¤¹¤ë .B elsif ¤È .B else ¤ÎÀá¤Ï¥¹¥­¥Ã¥×¤µ¤ì¤Þ¤¹¡£ ¤½¤¦¤Ç¤Ê¤¤¾ì¹ç¡¢É¾²Á·ë²Ì¤¬¿¿¤Ë¤Ê¤ë elsif Àá¤Ë½Ð²ñ¤¦¤Þ¤Ç¡¢¸å³¤¹¤ë³Æ .B elsif Àá¤Î¼°¤¬¥Á¥§¥Ã¥¯¤µ¤ì¤Þ¤¹¡£ ¤½¤Î¤è¤¦¤ÊÀ᤬¸«ÉÕ¤«¤ë¤È¡¢Ä¾¸å¤Î¥Ö¥ì¡¼¥¹Ãæ¤Îʸ¤¬¼Â¹Ô¤µ¤ì¡¢¸å³¤¹¤ë .B elsif ¤È .B else ¤ÎÀá¤Ï¥¹¥­¥Ã¥×¤µ¤ì¤Þ¤¹¡£ ¤¹¤Ù¤Æ¤Î .B if ¤ª¤è¤Ó .B elsif ¤ÎÀ᤬¥Á¥§¥Ã¥¯¤µ¤ì¤¿¤â¤Î¤Î¤É¤Î¼°¤â¿¿¤Ë¤Ê¤é¤Ê¤¤¾ì¹ç¤Ç¡¢ .B else À᤬¸ºß¤¹¤ë¾ì¹ç¡¢ .B else ¤Îľ¸å¤Î¥Ö¥ì¡¼¥¹Ãæ¤Îʸ¤¬É¾²Á¤µ¤ì¤Þ¤¹¡£ ¾ò·ï¤Ë¤ª¤¤¤Æ¤Ï¡¢É¾²Á·ë²Ì¤¬¶õ¤Ë¤Ê¤ë¥Ö¡¼¥ë¼°¤Ïµ¶¤È¤·¤Æ°·¤ï¤ì¤Þ¤¹¡£ .SH ¥Ö¡¼¥ë¼° °Ê²¼¤Ï¡¢DHCP ÇÛÉÛʪ¤Ç¸½ºß¥µ¥Ý¡¼¥È¤µ¤ì¤Æ¤¤¤ë¥Ö¡¼¥ë¼°¤Î°ìÍ÷¤Ç¤¹¡£ .PP .I data-expression-1 \fB=\fI data-expression-2\fR .RS 0.25i .PP \fB=\fR ¥ª¥Ú¥ì¡¼¥¿¤Ï¡¢2 ¸Ä¤Î¥Ç¡¼¥¿¼°¤òÈæ³Ó¤·¡¢Î¾¼Ô¤¬Æ±¤¸¾ì¹ç¤Ï¿¿¤òÊÖ¤·¡¢ Ʊ°ì¤Ç¤Ê¤¤¾ì¹ç¤Ïµ¶¤òÊÖ¤·¤Þ¤¹¡£ º¸Êդ⤷¤¯¤Ï±¦ÊդΤ¤¤º¤ì¤«¤¬¶õ¤Î¾ì¹ç¡¢·ë²Ì¤Ï¶õ¤Ë¤Ê¤ê¤Þ¤¹¡£ .RE .PP .I boolean-expression-1 \fBand\fI boolean-expression-2\fR .PP .RS 0.25i \fBand\fR ¥ª¥Ú¥ì¡¼¥¿¤Ï¡¢º¸ÊդΥ֡¼¥ë¼°¤È±¦ÊդΥ֡¼¥ë¼°¤ÎξÊý¤Îɾ²Á·ë²Ì¤¬ ¿¿¤Î¾ì¹ç¡¢¿¿¤Èɾ²Á¤µ¤ì¤Þ¤¹¡£ ¤½¤¦¤Ç¤Ê¤¤¾ì¹ç¡¢µ¶¤Èɾ²Á¤µ¤ì¤Þ¤¹¡£ º¸Êդ⤷¤¯¤Ï±¦ÊդΤ¤¤º¤ì¤«¤¬¶õ¤Î¾ì¹ç¡¢·ë²Ì¤Ï¶õ¤Ë¤Ê¤ê¤Þ¤¹¡£ .RE .PP .I boolean-expression-1 \fBor\fI boolean-expression-2\fR .PP .RS 0.25i \fBor\fR ¥ª¥Ú¥ì¡¼¥¿¤Ï¡¢º¸ÊդΥ֡¼¥ë¼°¤È±¦ÊդΥ֡¼¥ë¼°¤Î¤¤¤º¤ì¤«¤Îɾ²Á·ë²Ì¤¬ ¿¿¤Î¾ì¹ç¡¢¿¿¤Èɾ²Á¤µ¤ì¤Þ¤¹¡£ ¤½¤¦¤Ç¤Ê¤¤¾ì¹ç¡¢µ¶¤Èɾ²Á¤µ¤ì¤Þ¤¹¡£ º¸Êդ⤷¤¯¤Ï±¦ÊդΤ¤¤º¤ì¤«¤¬¶õ¤Î¾ì¹ç¡¢·ë²Ì¤Ï¶õ¤Ë¤Ê¤ê¤Þ¤¹¡£ .RE .PP .B not \fIboolean-expression .PP .RS 0.25i \fBnot\fR ¥ª¥Ú¥ì¡¼¥¿¤Ï¡¢\fIboolean-expression\fR ¤Îɾ²Á·ë²Ì¤¬µ¶¤Î¾ì¹ç¡¢ ¿¿¤Èɾ²Á¤µ¤ì¤Þ¤¹¡£ ¤Þ¤¿¡¢\fIboolean-expression\fR ¤Îɾ²Á·ë²Ì¤¬¿¿¤Î¾ì¹ç¡¢µ¶¤Èɾ²Á¤µ¤ì¤Þ¤¹¡£ \fIboolean-expression\fR ¤Îɾ²Á·ë²Ì¤¬¶õ¤Î¾ì¹ç¡¢·ë²Ì¤â¤Þ¤¿¶õ¤Ë¤Ê¤ê¤Þ¤¹¡£ .RE .PP .B exists \fIoption-name\fR .PP .RS 0.25i \fBexists\fR ¼°¤Ï¡¢½èÍýÂÐ¾Ý¤ÎÆþÎÏ DCHP ¥Ñ¥±¥Ã¥ÈÃæ¤Ë¡¢ »ØÄꤵ¤ì¤¿¥ª¥×¥·¥ç¥ó¤¬Â¸ºß¤¹¤ë¾ì¹ç¡¢¿¿¤òÊÖ¤·¤Þ¤¹¡£ .RE .B known .PP .RS 0.25i \fBknown\fR ¼°¤Ï¡¢Í×µáÂбþÃæ¤Î¥¯¥é¥¤¥¢¥ó¥È¤¬´ûÃΤξì¹ç¡¢ ¤¹¤Ê¤ï¤Á¥Û¥¹¥ÈÀë¸À¤¬¤¢¤ë¾ì¹ç¡¢¿¿¤òÊÖ¤·¤Þ¤¹¡£ .RE .B static .PP .RS 0.25i \fBstatic\fR ¼°¤Ï¡¢Í×µáÂбþÃæ¤Î¥¯¥é¥¤¥¢¥ó¥È¤Ø¤Î¥ê¡¼¥¹³ä¤êÅö¤Æ¤¬¡¢ ÀÅŪ¥¢¥É¥ì¥¹³ä¤êÅö¤Æ¤Ë¤è¤ë¤â¤Î¤Ç¤¢¤Ã¤¿¾ì¹ç¡¢¿¿¤òÊÖ¤·¤Þ¤¹¡£ .RE .SH ¥Ç¡¼¥¿¼° Á°½Ò¤Î¥Ö¡¼¥ë¼°¤Ï¡¢¥Ç¡¼¥¿¼°¤Îɾ²Á·ë²Ì¤Ë°Í¸¤·¤Þ¤¹¡£ ¥Ç¡¼¥¿¼°¤ò¤³¤³¤Ë¼¨¤·¤Þ¤¹¡£ .PP .B substring (\fIdata-expr\fB, \fIoffset\fB, \fIlength\fB)\fR .PP .RS 0.25i \fBsubstring\fR ¥ª¥Ú¥ì¡¼¥¿¤Ï¡¢¥Ç¡¼¥¿¼°¤òɾ²Á¤·¡¢ ɾ²Á·ë²ÌÃæ¤Î \fIoffset\fR ¥Ð¥¤¥È¤«¤é³«»Ï¤·¤Æ \fIlength\fR ¥Ð¥¤¥È·Ñ³¤¹¤ë ¥µ¥Ö¥¹¥È¥ê¥ó¥°¤òÊÖ¤·¤Þ¤¹¡£ \fIoffset\fR ¤È \fIlength\fR ¤Ï¶¦¤Ë¿ôÃͼ°¤Ç¤¹¡£ \fIdata-expr\fR, \fIoffset\fR, \fIlength\fR ¤Î¤¤¤º¤ì¤«¤¬¶õ¤Èɾ²Á¤µ¤ì¤ë¾ì¹ç¡¢ ·ë²Ì¤â¤Þ¤¿¶õ¤Ë¤Ê¤ê¤Þ¤¹¡£ \fIoffset\fR ¤¬¡¢É¾²Á¤µ¤ì¤¿¥Ç¡¼¥¿¤ÎŤµ°Ê¾å¤Ç¤¢¤ë¾ì¹ç¡¢ Ťµ 0 ¤Î¥Ç¡¼¥¿Ê¸»úÎó¤¬ÊÖ¤µ¤ì¤Þ¤¹¡£ \fIlength\fI ¤¬¡¢É¾²Á¤µ¤ì¤¿¥Ç¡¼¥¿¤Î \fIoffset\fR ¤è¤ê¸å¤ÎŤµ¤è¤êÂ礭¤¤¾ì¹ç¡¢ ɾ²Á¤µ¤ì¤¿¥Ç¡¼¥¿¤Î \fIoffset\fR ¤«¤é½ªÃ¼¤Þ¤Ç¤ÎÁ´¥Ç¡¼¥¿¤ò´Þ¤à ¥Ç¡¼¥¿Ê¸»úÎó¤¬ÊÖ¤µ¤ì¤Þ¤¹¡£ .RE .PP .B suffix (\fIdata-expr\fB, \fIlength\fB)\fR .PP .RS 0.25i \fBsuffix\fR ¥ª¥Ú¥ì¡¼¥¿¤Ï¡¢\fIdata-expr\fR ¤òɾ²Á¤·¡¢ ɾ²Á·ë²Ì¤ÎºÇ¸å¤Î \fIlength\fR ¥Ð¥¤¥È¤òÊÖ¤·¤Þ¤¹¡£ \fIlength\fR ¤Ï¿ôÃͼ°¤Ç¤¹¡£ \fIdata-expr\fR ¤Þ¤¿¤Ï \fIlength\fR ¤Îɾ²Á·ë²Ì¤¬¶õ¤Î¾ì¹ç¡¢ ·ë²Ì¤â¤Þ¤¿¶õ¤Ë¤Ê¤ê¤Þ¤¹¡£ \fIsuffix\fR (ÌõÃí: \fIlength\fR ¤¬Àµ¤·¤¤¤È»×¤ï¤ì¤Þ¤¹) ¤Îɾ²Á·ë²Ì¤¬É¾²Á¤µ¤ì¤¿¥Ç¡¼¥¿¤ÎŤµ¤è¤êÂ礭¤¤¾ì¹ç¡¢ ɾ²Á¤µ¤ì¤¿¥Ç¡¼¥¿¤¬ÊÖ¤µ¤ì¤Þ¤¹¡£ .\" horikawa@jp.FreeBSD.org 2002/04/29 .RE .PP .B option \fIoption-name\fR .PP .RS 0.25i \fBoption\fR ¥ª¥Ú¥ì¡¼¥¿¤Ï¡¢¥µ¡¼¥Ð¤¬±þÅú½èÍýÃæ¤Î¥Ñ¥±¥Ã¥È¤ÎÃæ¤Î¡¢ »ØÄꤷ¤¿¥ª¥×¥·¥ç¥ó¤ÎÆâÍÆ¤òÊÖ¤·¤Þ¤¹¡£ .RE .PP .B config-option \fIoption-name\fR .PP .RS 0.25i \fBconfig-option\fR ¥ª¥Ú¥ì¡¼¥¿¤Ï¡¢»ØÄꤷ¤¿¥ª¥×¥·¥ç¥ó¤ËÂФ·¡¢ DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Þ¤¿¤Ï¥µ¡¼¥Ð¤¬Á÷½Ð¤¹¤ë¤è¤¦ÀßÄꤵ¤ì¤¿ÃͤòÊÖ¤·¤Þ¤¹¡£ .RE .PP .B hardware .PP .RS 0.25i \fBhardware\fR ¥ª¥Ú¥ì¡¼¥¿¤Ï¡¢¥Ç¡¼¥¿¥¹¥È¥ê¥ó¥°¤òÊÖ¤·¤Þ¤¹¡£ ¥Ç¡¼¥¿¥¹¥È¥ê¥ó¥°¤ÎºÇ½é¤ÎÍ×ÁǤϡ¢ Âоݥѥ±¥Ã¥È¤¬¼¨¤¹¥Í¥Ã¥È¥ï¡¼¥¯¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤Î¥¿¥¤¥×¤Ç¤¢¤ê¡¢ ¸å³¤¹¤ëÍ×ÁǤϡ¢¥¯¥é¥¤¥¢¥ó¥È¤Î¥ê¥ó¥¯ÁØ¥¢¥É¥ì¥¹¤Ç¤¹¡£ ¥Ñ¥±¥Ã¥È¤¬Â¸ºß¤·¤Ê¤¤¾ì¹ç¤â¤·¤¯¤Ï RFC2131 \fIhlen\fR ¥Õ¥£¡¼¥ë¥É¤¬Ìµ¸ú¤Ê¾ì¹ç¡¢ ·ë²Ì¤Ï¶õ¤Ë¤Ê¤ê¤Þ¤¹¡£ ¥Ï¡¼¥É¥¦¥§¥¢¥¿¥¤¥×¤Ë¤Ï¡¢¥¤¡¼¥µ¥Í¥Ã¥È (1)¡¢¥È¡¼¥¯¥ó¥ê¥ó¥° (6)¡¢ FDDI (8) ¤¬´Þ¤Þ¤ì¤Þ¤¹¡£ ¥Ï¡¼¥É¥¦¥§¥¢¥¿¥¤¥×¤Ï IETF ¤Ë¤è¤Ã¤Æµ¬Äꤵ¤ì¡¢ ¤É¤Î¤è¤¦¤Ë¥¿¥¤¥×¤Î¿ôÃͤ¬ÄêµÁ¤µ¤ì¤ë¤«¤Î¾ÜºÙ¤Ï RFC2131 (ISC DHCP ÇÛÉÛʪ¤Ç¤Ï¡¢doc/ ¥µ¥Ö¥Ç¥£¥ì¥¯¥È¥ê¤Ë¤¢¤ê¤Þ¤¹) ¤ò»²¾È¤·¤Æ¤¯¤À¤µ¤¤¡£ .RE .PP .B packet (\fIoffset\fB, \fIlength\fB)\fR .PP .RS 0.25i \fBpacket\fR ¥ª¥Ú¥ì¡¼¥¿¤Ï¡¢Âоݥѥ±¥Ã¥È¤Î»ØÄêÉôʬ¤òÊÖ¤¹¤«¡¢ Âоݥѥ±¥Ã¥È¤¬Ìµ¤¤Ê¸Ì®¤Ç¤Ï¶õ¤òÊÖ¤·¤Þ¤¹¡£ \fIoffset\fR ¤È \fIlength\fR ¤Ï¡¢ \fBsubstring\fR ¥ª¥Ú¥ì¡¼¥¿¤ÈƱÍͤˡ¢¥Ñ¥±¥Ã¥È¤ÎÆâÍÆ¤ËŬÍѤµ¤ì¤Þ¤¹¡£ .RE .PP .I string .PP .RS 0.25i ¥¯¥©¡¼¥È¤Ç³ç¤é¤ì¤¿¥¹¥È¥ê¥ó¥°¤Ï¥Ç¡¼¥¿¼°¤È¤·¤Æ»ØÄê²Äǽ¤Ç¤¢¤ê¡¢ ¥¯¥©¡¼¥È¤Î´Ö¤ò ASCII ¥¨¥ó¥³¡¼¥É¤·¤¿¤Î¥Æ¥­¥¹¥È¤òÊÖ¤·¤Þ¤¹¡£ ¥Ð¥Ã¥¯¥¹¥é¥Ã¥·¥å ('\\') ʸ»ú¤Ï C ¥×¥í¥°¥é¥à¤Î¤è¤¦¤ËÆÃḚ̂·¤¤¤µ¤ì¤Þ¤¹: ¤¹¤Ê¤ï¤Á '\\t' ¤Ï¥¿¥Ö¤ò¡¢'\\r' ¤ÏÉü²þ¤ò¡¢'\\n' ¤Ï²þ¹Ô¤ò¡¢'\\b' ¤Ï¥Ù¥ë¤ò °ÕÌ£¤·¤Þ¤¹¡£ 8 ¿Ê¿ôÃÍ¤Ï '\\nnn' ¤Ç»ØÄê²Äǽ¤Ç¤¢¤ê¡¢nnn ¤Ï 0 °Ê¾å 0377 °Ê²¼¤Î 8 ¿Ê¿ôÃͤǤ¹¡£ 16 ¿Ê¿ôÃÍ¤Ï '\\xnn' ¤Ç»ØÄê²Äǽ¤Ç¤¢¤ê¡¢nn ¤Ï 0 °Ê¾å 0xff °Ê²¼¤Î 16 ¿Ê¿ôÃͤǤ¹¡£ .\" ÃͤÎÈϰϤθí¤ê¤Ë¤Ä¤¤¤Æ¤Ï¡¢Murray ·Ðͳ¤Ç¥ì¥Ý¡¼¥ÈºÑ .\" horikawa@jp.FreeBSD.org 2002/05/01 .RE .PP .I colon-separated hexadecimal list .PP .RS 0.25i ¥³¥í¥ó¤Ç¶èÀÚ¤é¤ì¤¿ 16 ¿Ê¿ô¤Î¥ª¥¯¥Æ¥Ã¥ÈÃͤΥꥹ¥È¤ò¡¢ ¥Ç¡¼¥¿¼°¤È¤·¤Æ»ØÄê²Äǽ¤Ç¤¹¡£ .RE .PP .B concat (\fIdata-expr1\fB, ..., \fIdata-exprN\fB)\fR .RS 0.25i ¼°¤¬É¾²Á¤µ¤ì¡¢³ÆÉ¾²Á·ë²Ì¤¬¥µ¥Ö¼°¤Î½çÈÖ¤ËÏ¢·ë¤µ¤ì¤Þ¤¹¡£ ¥µ¥Ö¼°¤Î¤¤¤º¤ì¤«¤Îɾ²Á·ë²Ì¤¬¶õ¤Ë¤Ê¤ë¾ì¹ç¡¢Ï¢·ë¤Î·ë²Ì¤Ï¶õ¤Ë¤Ê¤ê¤Þ¤¹¡£ .RE .PP .B reverse (\fInumeric-expr1\fB, \fIdata-expr2\fB)\fR .RS 0.25i 2 ¸Ä¤Î¼°¤¬É¾²Á¤µ¤ì¡¢¥Ç¡¼¥¿¼°¤Îɾ²Á·ë²Ì¤¬¤½¤Î¾ì¤Çȿž¤µ¤ì¤Þ¤¹¡£ ȿž¤Ï¡¢¿ôÃͼ°¤Ç»ØÄꤵ¤ì¤ëÂ礭¤µ¤Îñ°Ì¤Ç¹Ô¤ï¤ì¤Þ¤¹¡£ Î㤨¤Ð¡¢¿ôÃͼ°¤Îɾ²Á·ë²Ì¤¬ 4 ¤Î¾ì¹ç¤Ç¡¢ ¥Ç¡¼¥¿¼°¤Îɾ²Á·ë²Ì¤¬ 12 ¥Ð¥¤¥È¤Ë¤Ê¤ë¾ì¹ç¡¢ reverse ¼°¤Îɾ²Á·ë²Ì¤Ï¡¢¼¡¤Î¤è¤¦¤Ê 12 ¥Ð¥¤¥È¤Î¥Ç¡¼¥¿¤Ë¤Ê¤ê¤Þ¤¹¡£ ¤¹¤Ê¤ï¤Á¡¢ÆþÎϤκǸå¤Î 4 ¥Ð¥¤¥È¡¢¿¿Ãæ¤Î 4¥Ð¥¤¥È¡¢ºÇ½é¤Î 4 ¥Ð¥¤¥È¤Î ½ç¤Ë¤Ê¤ê¤Þ¤¹¡£ .RE .PP .B leased-address .RS 0.25i ¤¤¤«¤Ê¤ëʸ̮¤Ë¤ª¤¤¤Æ¤â¡¢ Í×µá½èÍýÂоݤȤʤäƤ¤¤ë¥¯¥é¥¤¥¢¥ó¥È¤Ë IP ¥¢¥É¥ì¥¹¤¬³ä¤êÅö¤ÆºÑ¤Î¾ì¹ç¡¢ ¤½¤Î IP ¥¢¥É¥ì¥¹¤¬ÊÖ¤µ¤ì¤Þ¤¹¡£ .RE .PP .B binary-to-ascii (\fInumeric-expr1\fB, \fInumeric-expr2\fB, .B \fIdata-expr1\fB,\fR \fIdata-expr2\fB)\fR .RS 0.25i data-expr2 ¤Îɾ²Á·ë²Ì¤ò¥Æ¥­¥¹¥È¥¹¥È¥ê¥ó¥°¤ËÊÑ´¹¤·¤Þ¤¹¡£ ¤³¤Î¥Æ¥­¥¹¥È¥¹¥È¥ê¥ó¥°Ãæ¤Ç¤Ï¡¢ data-expr2 ¤Îɾ²Á·ë²Ì¤Î³ÆÍ×ÁǤ¬¡¢1 ¸Ä¤Î¿ôÃͤˤʤê¤Þ¤¹¡£ ³Æ¿ôÃͤϡ¢¤½¤ì¤¾¤ì¡¢data-expr1 ¤Îɾ²Á·ë²Ì¤Ë¤è¤Ã¤Æ¶èÀÚ¤é¤ì¤Þ¤¹¡£ numeric-expr1 ¤Îɾ²Á·ë²Ì¤Ï¡¢´ð¿ô (2 ¤«¤é 16) ¤Ç¤¢¤ê¡¢ ¤³¤Î´ð¿ô¤Ë¿ôÃͤ¬ÊÑ´¹¤µ¤ì¤Þ¤¹¡£ numeric-expr2 ¤Îɾ²Á·ë²Ì¤Ï¡¢³Æ¿ôÃͤΥӥåÈÉý¤Ç¤¢¤ê¡¢ 8, 16, 32 ¤Î¤¤¤º¤ì¤«¤Ç¤¹¡£ .PP ºÇ½é¤Î 3 ¸Ä¤Î¥¿¥¤¥×¤Î¼°¤ÎÎã¤È¤·¤Æ¡¢ ¥¯¥é¥¤¥¢¥ó¥È¤Ë³ä¤êÅö¤Æ¤é¤ì¤¿ IP ¥¢¥É¥ì¥¹ÍѤΠPTR ¥ì¥³¡¼¥É¤Î̾Á°¤òÀ¸À®¤¹¤ë¤¿¤á¤Ë»ÈÍѲÄǽ¤Ê¼°¤ò¼¨¤·¤Þ¤¹ .RE .PP .nf concat (binary-to-ascii (10, 8, ".", reverse (1, leased-address)), ".in-addr.arpa."); .fi .PP .B encode-int (\fInumeric-expr\fB, \fIwidth\fB)\fR .RS 0.25i ¿ôÃͼ°¤¬É¾²Á¤µ¤ì¡¢»ØÄꤵ¤ì¤¿Éý¤Î¥Ç¡¼¥¿¥¹¥È¥ê¥ó¥°¤Ë ¥Í¥Ã¥È¥ï¡¼¥¯¥Ð¥¤¥È½ç (ºÇ¾å°Ì¥Ð¥¤¥È¤¬ºÇ½é) ¤Ç¥¨¥ó¥³¡¼¥É¤µ¤ì¤Þ¤¹¡£ ¿ôÃͼ°¤Îɾ²Á·ë²Ì¤¬¶õ¤ÎÃͤˤʤë¾ì¹ç¡¢·ë²Ì¤â¤Þ¤¿¶õ¤Ç¤¹¡£ .RE .\" ¤³¤Î ".RE" ¤¬Ìµ¤¤¤È¡¢¥¤¥ó¥Ç¥ó¥È¤¬Àµ¤·¤¯¤Ê¤¤¤Ç¤¹ .\" horikawa@jp.FreeBSD.org 2002/04/29 .PP .B pick-first-value (\fIdata-expr1\fR [ ... \fIexpr\fRn ] \fB)\fR .RS 0.25i pick-first-value ´Ø¿ô¤Ï¡¢Ç¤°Õ¸Ä¤Î¥Ç¡¼¥¿¼°¤ò¼è¤êÆÀ¤Þ¤¹¡£ ¥ê¥¹¥È¤ÎÀèÆ¬¤«¤é³Æ¼°¤¬É¾²Á¤µ¤ì¡¢ ɾ²Á·ë²Ì¤¬¶õ¤Ç¤Ï¤Ê¤¤¼°¤¬¸«ÉÕ¤«¤ë¤Þ¤Ç¤³¤ì¤¬Â³¤­¤Þ¤¹¡£ ¤³¤Î¼°¤¬ÊÖ¤µ¤ì¡¢¤³¤Î¼°¤Ë¸å³¤¹¤ë¼°¤Ïɾ²Á¤µ¤ì¤Þ¤»¤ó¡£ ¤¹¤Ù¤Æ¤Î¼°¤Îɾ²Á·ë²Ì¤¬¶õ¤Î¾ì¹ç¡¢¶õ¤ÎÃͤ¬ÊÖ¤µ¤ì¤Þ¤¹¡£ .RE .PP .B host-decl-name .RS 0.25i host-decl-name ´Ø¿ô¤Ï¡¢¸½ºßÍ×µá½èÍýÂоݤȤʤäƤ¤¤ë¥¯¥é¥¤¥¢¥ó¥È¤Ë¥Þ¥Ã¥Á¤¹¤ë¡¢ ¥Û¥¹¥ÈÀë¸À¤Î̾Á°¤òÊÖ¤·¤Þ¤¹¡£ ¤É¤Î¥Û¥¹¥ÈÀë¸À¤â¥Þ¥Ã¥Á¤·¤Ê¤¤¾ì¹ç¡¢·ë²Ì¤Ï¶õ¤Ë¤Ê¤ê¤Þ¤¹¡£ .RE .SH ¿ôÃͼ° ¿ôÃͼ°¤Ï¡¢É¾²Á·ë²Ì¤¬À°¿ô¤Ë¤Ê¤ë¼°¤Ç¤¹¡£ °ìÈ̤ˡ¢À°¿ô¤ÎºÇÂ祵¥¤¥º¤¬ 32 ¥Ó¥Ã¥È̤Ëþ¤Ç¤¢¤ë¤È²¾Äꤹ¤Ù¤­¤Ç¤Ï¤¢¤ê¤Þ¤»¤ó¤¬¡¢ À°¿ô¤ÎÀºÅÙ¤¬ 32 ¥Ó¥Ã¥È¤ò±Û¤¨¤ë¤³¤È¤Ï¤¢¤êÆÀ¤Þ¤¹¡£ .PP .B extract-int (\fIdata-expr\fB, \fIwidth\fB)\fR .PP .RS 0.25i \fBextract-int\fR ¥ª¥Ú¥ì¡¼¥¿¤Ï¡¢¥Í¥Ã¥È¥ï¡¼¥¯¥Ð¥¤¥È½ç¤ÎÀ°¿ô¤ò¡¢ »ØÄꤷ¤¿¥Ç¡¼¥¿¼°¤Îɾ²Á·ë²Ì¤«¤é¼è¤ê½Ð¤·¤Þ¤¹¡£ Éý¤Ï¡¢¼è¤ê½Ð¤¹À°¿ô¤Î¥Ó¥Ã¥ÈÉý¤Ç¤¹¡£ ¸½ºß¡¢¥µ¥Ý¡¼¥È¤µ¤ì¤Æ¤¤¤ëÉý¤Ï 8, 16, 32 ¤Î¤¤¤º¤ì¤«¤Ç¤¹¡£ ¥Ç¡¼¥¿¼°¤Îɾ²Á·ë²Ì¤¬¡¢»ØÄꤷ¤¿Â礭¤µ¤ÎÀ°¿ô¤È¼è¤ê½Ð¤¹¤Î¤Ë ½½Ê¬¤Ê¥Ó¥Ã¥È¤òÄ󶡤·¤Ê¤¤¾ì¹ç¡¢¶õ¤ÎÃͤ¬ÊÖ¤µ¤ì¤Þ¤¹¡£ .RE .PP .B lease-time .PP .RS 0.25i ¸½ºß¤Î¥ê¡¼¥¹¤Î´ü´Ö¤Ç¤¹¡£ ¤¹¤Ê¤ï¤Á¡¢¸½ºß¤Î»þ¹ï¤È¥ê¡¼¥¹¤Î´ü¸Â¤¬ÀÚ¤ì¤ë»þ¹ï¤È¤Îº¹¤Ç¤¹¡£ .RE .PP .I number .PP .RS 0.25i 0 ¤«¤éɽ¸½²Äǽ¤ÊºÇÂ祵¥¤¥º¤ÎÈϰϤÎǤ°Õ¤Î¿ôÃͤò¡¢¿ôÃͼ°¤È¤·¤Æ»ØÄê²Äǽ¤Ç¤¹¡£ .RE .PP .B client-state .PP .RS 0.25i ½èÍýÂоݤΥ¯¥é¥¤¥¢¥ó¥È¤Î¸½ºß¤Î¾õÂ֤Ǥ¹¡£ DHCP ¥¯¥é¥¤¥¢¥ó¥ÈÀßÄê¥Õ¥¡¥¤¥ë¤Ë¤ª¤¤¤Æ¤Î¤ßÍ­ÍѤǤ¹¡£ ¼è¤êÆÀ¤ëÃͤϼ¡¤ÎÄ̤ê¤Ç¤¹: .TP 2 .I \(bu Booting - DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï INIT ¾õÂ֤Ǥ¢¤ê¡¢ IP ¥¢¥É¥ì¥¹¤ò¤Þ¤À»ý¤Á¤Þ¤»¤ó¡£ ¼¡¤ËÁ÷¿®¤µ¤ì¤ë¥á¥Ã¥»¡¼¥¸¤Ï DHCPDISCOVER ¤Ç¤¢¤ê¡¢ ¤³¤ì¤Ï¥Ö¥í¡¼¥É¥­¥ã¥¹¥È¤µ¤ì¤Þ¤¹¡£ .TP .I \(bu Reboot - DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï INIT-REBOOT ¾õÂ֤Ǥ¹¡£ IP ¥¢¥É¥ì¥¹¤ò»ý¤Á¤Þ¤¹¤¬¤Þ¤À»ÈÍѤ·¤Æ¤¤¤Þ¤»¤ó¡£ ¼¡¤ËÁ÷¿®¤µ¤ì¤ë¥á¥Ã¥»¡¼¥¸¤Ï DHCPREQUEST ¤Ç¤¢¤ê¡¢ ¤³¤ì¤Ï¥Ö¥í¡¼¥É¥­¥ã¥¹¥È¤µ¤ì¤Þ¤¹¡£ ±þÅú¤¬²¿¤âʹ¤³¤¨¤Ê¤¤¤È¡¢¥¯¥é¥¤¥¢¥ó¥È¤Ï¤³¤Î¥¢¥É¥ì¥¹¤Ë¥Ð¥¤¥ó¥É¤·¡¢ BOUND ¾õÂÖ¤ËÁ«°Ü¤·¤Þ¤¹¡£ .TP .I \(bu Select - DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï SELECTING ¾õÂ֤Ǥ¹¡£ ¾¯¤Ê¤¯¤È¤â 1 ¸Ä¤Î DHCPOFFER ¥á¥Ã¥»¡¼¥¸¤Ï¼õ¿®¤·¤Þ¤·¤¿¤¬¡¢ ¾¤Î DHCPOFFER ¥á¥Ã¥»¡¼¥¸¤ò¾¤Î¥µ¡¼¥Ð¤«¤é¼õ¤±¼è¤ë¤«¤É¤¦¤«ÂԤäƤ¤¤Þ¤¹¡£ SELECTING ¾õÂ֤Ǥϥá¥Ã¥»¡¼¥¸¤ÏÁ÷¿®¤µ¤ì¤Þ¤»¤ó¡£ .TP .I \(bu Request - DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï REQUESTING ¾õÂ֤Ǥ¹¡£ ¾¯¤Ê¤¯¤È¤â 1 ¸Ä¤Î DHCPOFFER ¥á¥Ã¥»¡¼¥¸¤ò¼õ¿®¤·¡¢ ¤½¤Î¤¦¤Á¤Î¤É¤ì¤òÍ׵᤹¤ë¤«ÁªÂò¤·¤Þ¤·¤¿¡£ ¼¡¤ËÁ÷¿®¤µ¤ì¤ë¥á¥Ã¥»¡¼¥¸¤Ï DHCPREQUEST ¥á¥Ã¥»¡¼¥¸¤Ç¤¢¤ê¡¢ ¤³¤ì¤Ï¥Ö¥í¡¼¥É¥­¥ã¥¹¥È¤µ¤ì¤Þ¤¹¡£ .TP .I \(bu Bound - DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï BOUND ¾õÂ֤Ǥ¹¡£ IP ¥¢¥É¥ì¥¹¤ò½êÍ­¤·¤Æ¤¤¤Þ¤¹¡£ ¤³¤Î¾õÂ֤Ǥϥá¥Ã¥»¡¼¥¸¤ÏÁ÷¿®¤µ¤ì¤Þ¤»¤ó¡£ .TP .I \(bu Renew - DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï RENEWING ¾õÂ֤Ǥ¹¡£ IP ¥¢¥É¥ì¥¹¤ò½êÍ­¤·¤Æ¤ª¤ê¡¢¤³¤ì¤ò¹¹¿·¤¹¤ë¤¿¤á¤Ë¥µ¡¼¥Ð¤ËÀܳ¤ò»î¤ß¤Æ¤¤¤Þ¤¹¡£ ¼¡¤ËÁ÷¿®¤µ¤ì¤ë¥á¥Ã¥»¡¼¥¸¤Ï DHCPREQUEST ¥á¥Ã¥»¡¼¥¸¤Ç¤¢¤ê¡¢ ¤³¤ì¤Ï¥µ¡¼¥Ð¤ËľÀÜ¥æ¥Ë¥­¥ã¥¹¥È¤µ¤ì¤Þ¤¹¡£ .TP .I \(bu Rebind - DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï REBINDING ¾õÂ֤Ǥ¹¡£ IP ¥¢¥É¥ì¥¹¤ò½êÍ­¤·¤Æ¤ª¤ê¡¢ ¤³¤ì¤ò¹¹¿·¤¹¤ë¤¿¤á¤ËǤ°Õ¤Î¥µ¡¼¥Ð¤ËÀܳ¤ò»î¤ß¤Æ¤¤¤Þ¤¹¡£ ¼¡¤ËÁ÷¿®¤µ¤ì¤ë¥á¥Ã¥»¡¼¥¸¤Ï DHCPREQUEST ¥á¥Ã¥»¡¼¥¸¤Ç¤¢¤ê¡¢ ¤³¤ì¤Ï¥Ö¥í¡¼¥É¥­¥ã¥¹¥È¤µ¤ì¤Þ¤¹¡£ .RE .SH »²¾È: ¥í¥° ¥í¥°Ê¸¤ò»ÈÍѤ·¤Æ¡¢É¸½à¥í¥°¥Á¥ã¥Í¥ë¤Ë¾ðÊó¤òÁ÷¿®²Äǽ¤Ç¤¹¡£ ¥í¥°Ê¸¤Ï¡¢¾Êά²Äǽ¤Ê priority (\fBfatal\fR, \fBerror\fR, \fBinfo\fR, \fBdebug\fR ¤Î¤¤¤º¤ì¤«) ¤È¡¢ ¥Ç¡¼¥¿¼°¤ò¼è¤ê¤Þ¤¹¡£ .PP .B log (\fIpriority\fB, \fIdata-expr\fB)\fR .\" "\FB" ¤Ï "\fB" ¤¬Àµ¤·¤¤ .\" horikawa@jp.FreeBSD.org 2002/04/29 .PP ¥í¥°Ê¸¤Ï¡¢Ã±°ì¤Î¥Ç¡¼¥¿¼°°ú¿ô¤Î¤ß¼è¤ê¤Þ¤¹¡£ Ê£¿ô¤Î¥Ç¡¼¥¿Ãͤò½ÐÎϤ·¤¿¤¤¾ì¹ç¡¢ \fBconcat\fR ¥ª¥Ú¥ì¡¼¥¿¤ò»ÈÍѤ·¤Æ¤½¤ì¤é¤òÏ¢·ë¤¹¤ëɬÍפ¬¤¢¤ê¤Þ¤¹¡£ .RE .SH »²¾È: ưŪ¤Ê DNS ¹¹¿· .PP DHCP ¥¯¥é¥¤¥¢¥ó¥È¤È¥µ¡¼¥Ð¤Ï¡¢ ưŪ¤Ë¥É¥á¥¤¥ó¥Í¡¼¥à¥·¥¹¥Æ¥à¤ò¹¹¿·¤¹¤ëǽÎϤ¬¤¢¤ê¤Þ¤¹¡£ ÀßÄê¥Õ¥¡¥¤¥ëÃæ¤Ë¡¢¤É¤Î¤è¤¦¤Ë¥É¥á¥¤¥ó¥Í¡¼¥à¥·¥¹¥Æ¥à¤ò¹¹¿·¤·¤ÆÍߤ·¤¤¤«¡¢ ÄêµÁ²Äǽ¤Ç¤¹¡£ ¹¹¿·¤Ï RFC 2136 ¤Ë½¾¤Ã¤Æ¤¤¤ë¤¿¤á¡¢ RFC 2136 ¤ò¥µ¥Ý¡¼¥È¤¹¤ë DNS ¥µ¡¼¥Ð¤Ï¡¢ DHCP ¥µ¡¼¥Ð¤«¤é¤Î¹¹¿·¤ò¼õ¤±ÉÕ¤±²Äǽ¤È»×¤ï¤ì¤Þ¤¹¡£ .SH ¥»¥­¥å¥ê¥Æ¥£ TSIG ¤ª¤è¤Ó DNSSEC ¤Ï¤Þ¤À¥µ¥Ý¡¼¥È¤µ¤ì¤Æ¤¤¤Þ¤»¤ó¡£ DHCP ¥µ¡¼¥Ð¤Þ¤¿¤Ï¥¯¥é¥¤¥¢¥ó¥È¤«¤é¤Î¹¹¿·¤ò¼õ¤±ÉÕ¤±¤ë¤è¤¦¤Ë DNS ¥µ¡¼¥Ð¤òÀßÄꤹ¤ë¾ì¹ç¡¢¸¢¸Â¤Î̵¤¤¹¹¿·¤ËÂФ·¤Æ DNS ¥µ¡¼¥Ð¤ò»¯¤¹¤³¤È¤Ë¤Ê¤ë¤«¤â¤·¤ì¤Þ¤»¤ó¡£ ¤³¤ì¤òÈò¤±¤ë¤¿¤á¤Ëº£¤¹¤°¤Ç¤­¤ëºÇÎɤÎÊýË¡¤Ï¡¢ IP ¥¢¥É¥ì¥¹¥Ù¡¼¥¹¤Î¥Ñ¥±¥Ã¥È¥Õ¥£¥ë¥¿¤ò»ÈÍѤ·¤Æ¡¢ ¸¢¸Â¤Î̵¤¤¥Û¥¹¥È¤«¤é¤Î¹¹¿·Í×µáȯ¹Ô¤òÍ޻ߤ¹¤ë¤³¤È¤Ç¤¹¡£ ÌÀ¤é¤«¤Ë¡¢¸½¾õ¤Ç¤Ï¥¯¥é¥¤¥¢¥ó¥È¤Î¹¹¿·¤ËÂФ¹¤ë¥»¥­¥å¥ê¥Æ¥£¤òÄ󶡤¹¤ëÊýË¡¤Ï ¤¢¤ê¤Þ¤»¤ó¡£ ¤³¤Î¤¿¤á¤Ë¤Ï TSIG ¤« DNSSEC ¤¬É¬ÍפǤ¹¤¬¡¢ ¤³¤Î DHCP ÇÛÉÛʪ¤Ë¤Ï¤Þ¤À´Þ¤Þ¤ì¤Æ¤¤¤Þ¤»¤ó¡£ .PP ưŪ DNS (DDNS) ¹¹¿·¤Ï¡¢\fBdns-update\fR ¼°¤ò»ÈÍѤ¹¤ë¤³¤È¤Ç¼Â¹Ô¤µ¤ì¤Þ¤¹¡£ \fBdns-update\fR ¼°¤Ï¡¢¥Ö¡¼¥ë¼°¤Ç¤¢¤ê¡¢4 ¸Ä¤Î¥Ñ¥é¥á¡¼¥¿¤ò¼è¤ê¤Þ¤¹¡£ ¹¹¿·¤ËÀ®¸ù¤¹¤ë¤È¡¢·ë²Ì¤Ï¿¿¤Ë¤Ê¤ê¤Þ¤¹¡£ ¼ºÇÔ¤¹¤ë¤È¡¢·ë²Ì¤Ïµ¶¤Ë¤Ê¤ê¤Þ¤¹¡£ 4 ¸Ä¤Î¥Ñ¥é¥á¡¼¥¿¤Ï¡¢¥ê¥½¡¼¥¹¥ì¥³¡¼¥É¥¿¥¤¥× (RR)¡¢ RR ¤Îº¸ÊÕ¡¢RR ¤Î±¦ÊÕ¡¢¥ì¥³¡¼¥É¤ËŬÍѤµ¤ì¤ë¤Ù¤­ ttl ¤Ç¤¹¡£ ¤³¤Î´Ø¿ô¤ÎºÇ¤â´Êñ¤Ê»ÈÍÑÎã¤Ï¡¢dhcpd.conf ¥Õ¥¡¥¤¥ë¤Î»²¾ÈÀá¤Ë¤¢¤ê¡¢ ¤Ê¤Ë¤¬µ¯¤­¤ë¤«µ­½Ò¤µ¤ì¤Æ¤¤¤Þ¤¹¡£ ¤³¤ÎÎã¤Ç¤Ï¡¢Ê£¿ô¤Î¼°¤¬»ÈÍѤµ¤ì¤Æ¡¢ \fBdns-update\fR ÍѤΰú¿ô¤¬ºîÀ®¤µ¤ì¤Æ¤¤¤Þ¤¹¡£ .PP Îã¤ÎÃæ¤Ç¤Ï¡¢ºÇ½é¤Î \fBdns-update\fR ¼°¤Ø¤Î 1 ÈÖÌܤΰú¿ô¤Ï¡¢ A RR ¥¿¥¤¥×¤Ëɾ²Á¤µ¤ì¤ë¥Ç¡¼¥¿¼°¤Ç¤¹¡£ 2 ÈÖÌܤΰú¿ô¤Ï¡¢DHCP host-name ¥ª¥×¥·¥ç¥ó¤È ¥í¡¼¥«¥ë¥É¥á¥¤¥ó¡¢¤³¤Î¾ì¹ç "ssd.example.net"¡¢ ¤ò´Þ¤à¥Æ¥­¥¹¥È¥¹¥È¥ê¥ó¥°¤òÏ¢·ë¤¹¤ë¤³¤È¤Ç¡¢¹½ÃÛ¤µ¤ì¤Þ¤¹¡£ 3 ÈÖÌܤΰú¿ô¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤Ë³ä¤êÅö¤Æ¤é¤ì¤¿¥¢¥É¥ì¥¹¤ò¡¢ 32 ¥Ó¥Ã¥È¤Î¿ôÃͤ«¤é³Æ¥Ð¥¤¥È¤ò "." ¤Ç¶èÀڤä¿ ASCII ʸ»úÎó¤ËÊÑ´¹¤¹¤ë¤³¤È¤Ç¡¢ ¹½ÃÛ¤µ¤ì¤Þ¤¹¡£ 4 ÈÖÌܤΰú¿ô TTL ¤Ï¡¢¥ê¡¼¥¹¤Î»Ä¤ê»þ´Ö¤Ç¤¹ (¤³¤ì¤ÏËÜÅö¤ÏÀµ¤·¤¯¤¢¤ê¤Þ¤»¤ó¡£ ¤Ê¤¼¤Ê¤é DNS ¥µ¡¼¥Ð¤Ï¡¢Í×µá¤ËÂФ·¤Æ¤¤¤Ä¤â¤³¤Î TTL Ãͤò½ÐÎϤ·¤Æ¤·¤Þ¤¦¤«¤é¤Ç¤¹¡£ ¤³¤ì¤Ï¡¢¥ê¡¼¥¹´ü¸ÂÀÚ¤ì¤Î¿ôÉÃÁ°¤Ç¤¢¤Ã¤Æ¤â¤Ç¤¹)¡£ .PP ºÇ½é¤Î \fBdns-update\fR ʸ¤¬À®¸ù¤¹¤ë¤È¡¢ °ú¤­Â³¤¤¤Æ 2 ÈÖÌܤι¹¿·¤Ë¤è¤ê PTR RR ¤¬¥¤¥ó¥¹¥È¡¼¥ë¤µ¤ì¤Þ¤¹¡£ PTR ¥ì¥³¡¼¥É¤Î¥¤¥ó¥¹¥È¡¼¥ë¤Ï¡¢A RR ¤Î¥¤¥ó¥¹¥È¡¼¥ë¤ÈƱÍͤǤ¹¤¬¡¢ ¥ì¥³¡¼¥É¤Îº¸Êդϥ꡼¥¹¤µ¤ì¤¿¥¢¥É¥ì¥¹¤òµÕ¤Ë¤·¤Æ ".in-addr.arpa" ¤È ·ë¹ç¤µ¤ì¤¿¤â¤Î¤Ç¤¹¡£ ±¦Êդϡ¢¥¢¥É¥ì¥¹¤Î¥ê¡¼¥¹Äó¶¡À襯¥é¥¤¥¢¥ó¥È¤Î¡¢´°Á´¤Ê·Á¤Ç¤Î¥É¥á¥¤¥ó̾¤Ç¤¹¡£ .SH ´ØÏ¢¹àÌÜ dhcpd.conf(5), dhcpd.leases(5), dhclient.conf(5), dhcp-eval(5), dhcpd(8), dhclient(8), RFC2132, RFC2131 .SH ºî¼Ô Internet Systems Consortium DHCP Distribution ¤Ï¡¢Vixie Labs ¤È¤Î·ÀÌó¤Î¤â¤È¤Ç¡¢Ted Lemon ¤¬µ­½Ò¤·¤Þ¤·¤¿¡£ ËÜ¥×¥í¥¸¥§¥¯¥È¤Î»ñ¶â¤Ï¡¢Internet Systems Consortium ¤¬Ä󶡤·¤Þ¤·¤¿¡£ Internet Systems Consortium ¤Ë´Ø¤¹¤ë¾ðÊó¤Ï¡¢ .B https://www.isc.org ¤Ë¤¢¤ê¤Þ¤¹¡£ dhcp-4.4.1/doc/ja_JP.eucJP/dhcp-options.5000644 000765 000024 00000142576 13243301226 020164 0ustar00tmarkstaff000000 000000 .\" $Id: dhcp-options.5,v 1.5 2010/07/20 21:09:14 dhankins Exp $ .\" .\" Copyright (c) 2004,2009,2016 by Internet Systems Consortium, Inc. ("ISC") .\" Copyright (c) 1996-2003 by Internet Software Consortium .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT .\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" Internet Systems Consortium, Inc. .\" 950 Charter Street .\" Redwood City, CA 94063 .\" .\" https://www.isc.org/ .\" .\" %FreeBSD: src/contrib/isc-dhcp/common/dhcp-options.5,v 1.2.2.1 2002/04/11 10:16:46 murray Exp % .\" $FreeBSD: doc/ja_JP.eucJP/man/man5/dhcp-options.5,v 1.11 2002/05/21 03:51:52 horikawa Exp $ .\" WORD: Dynamic Host Configuration Protocol ưŪ¥Û¥¹¥È¹½À®¥×¥í¥È¥³¥ë .\" WORD: Path MTU Discovery ¥Ñ¥¹ MTU õº÷ .\" WORD: Router Discovery ¥ë¡¼¥¿Ãµº÷ .\" WORD: Router Solicitation ¥ë¡¼¥¿Í×ÀÁ .\" WORD: Mask Discovery ¥Þ¥¹¥¯Ãµº÷ .\" .TH dhcp-options 5 .SH ̾¾Î dhcp-options - ưŪ¥Û¥¹¥È¹½À®¥×¥í¥È¥³¥ë¤Î¥ª¥×¥·¥ç¥ó .SH ²òÀâ Æ°Åª¥Û¥¹¥È¹½À®¥×¥í¥È¥³¥ë (DHCP: Dynamic Host Configuration Protocol) ¤ò »ÈÍѤ¹¤ë¤³¤È¤Ë¤è¤ê¡¢¥¯¥é¥¤¥¢¥ó¥È¤Ï DHCP ¥µ¡¼¥Ð¤«¤é¡¢¥Í¥Ã¥È¥ï¡¼¥¯ÀßÄê¤ä ¥Í¥Ã¥È¥ï¡¼¥¯¾å¤ÇÍøÍѲÄǽ¤ÊÍÍ¡¹¤Ê¥µ¡¼¥Ó¥¹¤Ë¤Ä¤¤¤Æµ­½Ò¤·¤Æ¤¤¤ë .B ¥ª¥×¥·¥ç¥ó ¤ò¼õ¤±¼è¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£ .B dhcpd(8) ¤ä .B dhclient(8) ¤òÀßÄꤹ¤ë¤È¤­¤Ë¡¢¤·¤Ð¤·¤Ð¥ª¥×¥·¥ç¥ó¤òÀë¸À¤¹¤ëɬÍפ¬¤¢¤ë¤Ç¤·¤ç¤¦¡£ ¤³¤³¤Ç¤Ï¡¢¥ª¥×¥·¥ç¥ó¤òÀë¸À¤¹¤ëʸˡ¡¢ ¤½¤·¤ÆÀë¸À²Äǽ¤Ê¥ª¥×¥·¥ç¥ó¤Î̾Á°¤È½ñ¼°¤òʸ½ñ²½¤·¤Æ¤¤¤Þ¤¹¡£ .SH ¥ê¥Õ¥¡¥ì¥ó¥¹: ¥ª¥×¥·¥ç¥óʸ .PP DHCP \fIoption\fR ʸ¤Ï¡¢¾ï¤Ë¥­¡¼¥ï¡¼¥É \fIoption\fR ¤Ç³«»Ï¤·¡¢ ñ°ì¤Î¥ª¥×¥·¥ç¥ó̾¤¬Â³¤­¡¢¥ª¥×¥·¥ç¥ó¥Ç¡¼¥¿¤¬Â³¤­¤Þ¤¹¡£ ¥ª¥×¥·¥ç¥ó¤Î̾Á°¤È¥Ç¡¼¥¿¤Î½ñ¼°¤Ï¸å½Ò¤·¤Þ¤¹¡£ ¤¹¤Ù¤Æ¤Î DHCP ¥ª¥×¥·¥ç¥ó¤òÌÖÍåŪ¤Ë»ØÄꤹ¤ëɬÍפϤʤ¯¡¢ ¥¯¥é¥¤¥¢¥ó¥È¤ËɬÍפʥª¥×¥·¥ç¥ó¤Î¤ß¤ò»ØÄꤷ¤Þ¤¹¡£ .PP ¥ª¥×¥·¥ç¥ó¥Ç¡¼¥¿¤Ë¤Ï¡¢¼¡¤Î¤è¤¦¤ËÍÍ¡¹¤Ê½ñ¼°¤¬¤¢¤ê¤Þ¤¹: .PP .B ip-address ¥Ç¡¼¥¿¥¿¥¤¥×¤Ï¡¢ÌÀ¼¨Åª¤Ê IP ¥¢¥É¥ì¥¹ (Î㤨¤Ð 239.254.197.10) ¤Þ¤¿¤Ï ¥É¥á¥¤¥ó̾ (Î㤨¤Ð haagen.isc.org) ¤Î¤É¤Á¤é¤Ç¤â»ØÄê²Äǽ¤Ç¤¹¡£ ¥É¥á¥¤¥ó̾¤Ç»ØÄꤹ¤ë¾ì¹ç¡¢ ¤½¤Î¥É¥á¥¤¥ó̾¤ò²ò·è¤¹¤ë¤Èñ°ì¤Î IP ¥¢¥É¥ì¥¹¤Ë¤Ê¤ë¤è¤¦¤Ë¤·¤Æ¤¯¤À¤µ¤¤¡£ .PP .B int32 ¥Ç¡¼¥¿¥¿¥¤¥×¤ÏÉ乿ÉÕ¤­ 32 ¥Ó¥Ã¥ÈÀ°¿ô¤ò»ØÄꤷ¤Þ¤¹¡£ .B uint32 ¥Ç¡¼¥¿¥¿¥¤¥×¤ÏÉä¹æÌµ¤· 32 ¥Ó¥Ã¥ÈÀ°¿ô¤ò»ØÄꤷ¤Þ¤¹¡£ .B int16 ¤ª¤è¤Ó .B uint16 ¤Î¥Ç¡¼¥¿¥¿¥¤¥×¤Ï¡¢É乿ÉÕ¤­¤ª¤è¤ÓÉä¹æÌµ¤·¤Î 16 ¥Ó¥Ã¥ÈÀ°¿ô¤ò»ØÄꤷ¤Þ¤¹¡£ .B int8 ¤ª¤è¤Ó .B uint8 ¤Î¥Ç¡¼¥¿¥¿¥¤¥×¤Ï¡¢É乿ÉÕ¤­¤ª¤è¤ÓÉä¹æÌµ¤·¤Î 8 ¥Ó¥Ã¥ÈÀ°¿ô¤ò»ØÄꤷ¤Þ¤¹¡£ Éä¹æÌµ¤· 8 ¥Ó¥Ã¥ÈÀ°¿ô¤Ï¡¢¥ª¥¯¥Æ¥Ã¥È¤È¸Æ¤Ð¤ì¤ë¤³¤È¤â¤¢¤ê¤Þ¤¹¡£ .PP .B text ¥Ç¡¼¥¿¥¿¥¤¥×¤Ï NVT ASCII ʸ»úÎó¤ò»ØÄꤷ¤Þ¤¹¡£ ʸ»úÎó¤Ï¥À¥Ö¥ë¥¯¥©¡¼¥È¤Ç³ç¤ëɬÍפ¬¤¢¤ê¤Þ¤¹¡£ Î㤨¤Ð root-path ¥ª¥×¥·¥ç¥ó¤ò»ØÄꤹ¤ëʸˡ¤Ï¡¢¼¡¤Î¤è¤¦¤Ë¤Ê¤ê¤Þ¤¹¡£ .nf .sp 1 option root-path "10.0.1.4:/var/tmp/rootfs"; .fi .PP .B domain-name ¥Ç¡¼¥¿¥¿¥¤¥×¤Ï¥É¥á¥¤¥ó̾¤ò»ØÄꤷ¤Þ¤¹¡£ ʸ»úÎó¤ò¥À¥Ö¥ë¥¯¥©¡¼¥È¤Ç³ç¤Ã¤Æ¤¤¤±¤Þ¤»¤ó¡£ ¤³¤Î¥Ç¡¼¥¿¥¿¥¤¥×¤Ï¡¢Â¾¤Î´û¸¤Î DHCP ¥ª¥×¥·¥ç¥ó¤Ë¤Ï»È¤ï¤ì¤Þ¤»¤ó¡£ ¥É¥á¥¤¥ó̾¤Ï¡¢text ¥ª¥×¥·¥ç¥ó¤Ç¤¢¤ë¤«¤Î¤è¤¦¤ËÊÝ»ý¤µ¤ì¤Þ¤¹¡£ .\" text ¥Ç¡¼¥¿¥¿¥¤¥×¤Ç¤¢¤ë¤«¤Î¤è¤¦¤Ë? .\" metal .PP .B flag ¥Ç¡¼¥¿¥¿¥¤¥×¤Ï¥Ö¡¼¥ëÃͤò»ØÄꤷ¤Þ¤¹¡£ ¥Ö¡¼¥ëÃÍ¤Ï true ¤Þ¤¿¤Ï false ¤Î¤¤¤º¤ì¤«¤Ç¤¹ (¤â¤·¤¯¤Ï¡¢on ¤Þ¤¿¤Ï off ¤ÎÊý¤¬Ê¬¤«¤ê¤ä¤¹¤±¤ì¤Ð¡¢¤³¤Á¤é¤Ç¤â¤«¤Þ¤¤¤Þ¤»¤ó)¡£ .PP .B string ¥Ç¡¼¥¿¥¿¥¤¥×¤Ï¡¢¥À¥Ö¥ë¥¯¥©¡¼¥È¤Ç³ç¤é¤ì¤ë NVT ASCII ʸ»úÎ󤫡¢ ¥³¥í¥ó¶èÀÚ¤ê¤Î 16 ¿Ê¿ô¤Ç»ØÄꤵ¤ì¤ë¥ª¥¯¥Æ¥Ã¥È¤ÎϢ³¤Î¤¤¤º¤ì¤«¤ò»ØÄꤷ¤Þ¤¹¡£ Î㤨¤Ð¼¡¤Î¤è¤¦¤Ë¤Ê¤ê¤Þ¤¹: .nf .sp 1 option dhcp-client-identifier "CLIENT-FOO"; ¤â¤·¤¯¤Ï option dhcp-client-identifier 43:4c:49:45:54:2d:46:4f:4f; .fi .SH ¼°¤òÍѤ¤¤¿¥ª¥×¥·¥ç¥óÃͤÎÀßÄê .\" metal ¥¯¥é¥¤¥¢¥ó¥È¤¬Á÷½Ð¤¹¤ë¤¤¤¯¤Ä¤«¤ÎÃͤò¡¢DHCP ¥ª¥×¥·¥ç¥ó¤ÎÃͤòÀßÄꤹ¤ë¤Î¤Ë »È¤¨¤ë¤ÈÊØÍø¤Ê¤³¤È¤¬¤¢¤ê¤Þ¤¹¡£ ¤³¤ì¤ò¤¹¤ë¤Ë¤Ï¼°¤Îɾ²Á¤¬ÍøÍѤǤ­¤Þ¤¹¡£ .B dhcp-eval(5) ¥Þ¥Ë¥å¥¢¥ë¥Ú¡¼¥¸¤Ë¼°¤Î½ñ¤­Êý¤¬½Ò¤Ù¤é¤ì¤Æ¤¤¤Þ¤¹¡£ ɾ²Á¤Î·ë²Ì¤ò¥ª¥×¥·¥ç¥ó¤ËÂåÆþ¤¹¤ë¤Ë¤Ï¡¢¥ª¥×¥·¥ç¥ó¤ò¼¡¤Î¤è¤¦¤ËÄêµÁ¤·¤Þ¤¹: .nf .sp 1 \fBoption \fImy-option \fB= \fIexpression \fB;\fR .fi .PP Î㤨¤Ð¼¡¤Î¤è¤¦¤Ë¤·¤Þ¤¹: .nf .sp 1 option hostname = binary-to-ascii (16, 8, "-", substring (hardware, 1, 6)); .fi .SH ɸ½à DHCP ¥ª¥×¥·¥ç¥ó ¼¡¤Ë¼¨¤¹ÍÍ¡¹¤Ê¥ª¥×¥·¥ç¥ó¤Ë´Ø¤¹¤ëµ­½Ò¤Ï¡¢ DHCP ¥ª¥×¥·¥ç¥ó¤Ë´Ø¤¹¤ëºÇ¿·¤Î IETF ¥É¥é¥Õ¥Èʸ½ñ¤«¤é¤Î¤â¤Î¤Ç¤¹¡£ ̾Á°¤¬·ÇºÜ¤µ¤ì¤Æ¤¤¤Ê¤¤¥ª¥×¥·¥ç¥ó¤Ï¡¢¤Þ¤À¼ÂÁõ¤µ¤ì¤Æ¤¤¤Ê¤¤¤«¤â¤·¤ì¤Þ¤»¤ó¤¬¡¢ ÀßÄê¥Õ¥¡¥¤¥ë¤ËÄêµÁ¤¹¤ë¤³¤È¤Ç¡¢¤½¤Î¤è¤¦¤Ê¥ª¥×¥·¥ç¥ó¤ò»È¤¨¤ë¤«¤â¤·¤ì¤Þ¤»¤ó¡£ ¾Ü¤·¤¯¤Ï¡¢¤³¤ÎÀè¤Î¡Ö¿·µ¬¥ª¥×¥·¥ç¥ó¤ÎÄêµÁ¡×¤«¤é³¤¯µ­½Ò¤ò»²¾È¤·¤Æ¤¯¤À¤µ¤¤¡£ .PP ¤³¤³¤Ëµ­½Ò¤µ¤ì¤Æ¤¤¤ë¥ª¥×¥·¥ç¥ó¤Î¤¦¤Á¤Î¤¤¤¯¤Ä¤«¤Ï¡¢DHCP ¥µ¡¼¥Ð¤â¤·¤¯¤Ï ¥¯¥é¥¤¥¢¥ó¥È¤Ë¤è¤Ã¤Æ¼«Æ°Åª¤ËÀ¸À®¤µ¤ì¤ë¤â¤Î¤Ç¡¢¥æ¡¼¥¶¤Ë¤ÏÀßÄê¤Ç¤­¤Þ¤»¤ó¡£ ¤½¤Î¤è¤¦¤Ê¥ª¥×¥·¥ç¥ó¤ÎÃͤϡ¢¼õ¿®Â¦¤Î DHCP ¥×¥í¥È¥³¥ë¥¨¡¼¥¸¥§¥ó¥È (¥µ¡¼¥Ð¤â¤·¤¯¤Ï¥¯¥é¥¤¥¢¥ó¥È) ¤ÎÀßÄê¥Õ¥¡¥¤¥ëÃæ¤Î¡¢Î㤨¤Ð¾ò·ï¼°¤Ê¤É¤Ç »È¤ï¤ì¤Þ¤¹¡£ ¤·¤«¤·¤³¤Î¥ª¥×¥·¥ç¥ó¤ÎÃͤϡ¢Á÷¿®Â¦¤Î¥¨¡¼¥¸¥§¥ó¥È¤ÎÀßÄê¥Õ¥¡¥¤¥ëÃæ¤Ç¤Ï »È¤ï¤ì¤ë¤³¤È¤Ï¤¢¤ê¤Þ¤»¤ó¡£ ¤È¤¤¤¦¤Î¤â¡¢¤½¤ÎÃͤϡ¢ÀßÄê¥Õ¥¡¥¤¥ë¤¬½èÍý¤µ¤ì¤¿¸å¤Ë·èÄꤵ¤ì¤ë¤«¤é¤Ç¤¹¡£ °Ê¹ß¤Îµ­½Ò¤Ë¤ª¤¤¤Æ¡¢¤½¤Î¤è¤¦¤Ê¥ª¥×¥·¥ç¥ó¤Ë¤Ï ¡Ö¥æ¡¼¥¶¤¬ÀßÄꤹ¤ë¤³¤È¤Ï¤Ç¤­¤Þ¤»¤ó¡×¤Èµ­¤µ¤ì¤Þ¤¹¡£ .PP ɸ½à¥ª¥×¥·¥ç¥ó¤ò¼¨¤·¤Þ¤¹: .PP .B option \fBall-subnets-local\fR \fIflag\fR\fB;\fR .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬Àܳ¤µ¤ì¤Æ¤¤¤ë IP ¥Í¥Ã¥È¥ï¡¼¥¯¤ÎÁ´¥µ¥Ö¥Í¥Ã¥È¤¬ »ÈÍѤ¹¤ë MTU ¤¬¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬Ä¾ÀÜÀܳ¤µ¤ì¤Æ¤¤¤ë¥µ¥Ö¥Í¥Ã¥È¤Î MTU ¤È Ʊ¤¸¤Ç¤¢¤ë¤È¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬²¾Äꤷ¤Æ¤è¤¤¤«¤ò»ØÄꤷ¤Þ¤¹¡£ ÃÍ true ¤Ï¡¢Á´¥µ¥Ö¥Í¥Ã¥È¤ÏƱ°ì¤Î MTU ¤Ç¤¢¤ë¤³¤È¤ò°ÕÌ£¤·¤Þ¤¹¡£ ÃÍ false ¤Ï¡¢Ä¾ÀÜÀܳ¤µ¤ì¤Æ¤¤¤ë¥Í¥Ã¥È¥ï¡¼¥¯¤Î¥µ¥Ö¥Í¥Ã¥È¤Ë¤Ï¡¢¤è¤ê¾®¤µ¤Ê MTU ¤ò »ý¤Ä¤â¤Î¤¬¤¢¤ë¤È¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬²¾Äꤹ¤Ù¤­¤Ç¤¢¤ë¤³¤È¤ò°ÕÌ£¤·¤Þ¤¹¡£ .RE .PP .B option \fBarp-cache-timeout\fR \fIuint32\fR\fB;\fR .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢ARP ¥­¥ã¥Ã¥·¥å¥¨¥ó¥È¥ê¤Î¥¿¥¤¥à¥¢¥¦¥È¤òÉÿô¤Ç»ØÄꤷ¤Þ¤¹¡£ .RE .PP .B option \fBbootfile-name\fR \fItext\fR\fB;\fR .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢µ¯Æ°¥Õ¥¡¥¤¥ë¤ò»ØÄꤹ¤ë¤¿¤á¤Ë»ÈÍѤ·¤Þ¤¹¡£ ¥¯¥é¥¤¥¢¥ó¥È¤Ë¤è¤Ã¤Æ¥µ¥Ý¡¼¥È¤µ¤ì¤Æ¤¤¤ë¾ì¹ç¡¢ ¤³¤ì¤Ï \fBfilename\fR Àë¸À¤ÈƱ¤¸¸ú²Ì¤ò»ý¤Á¤Þ¤¹¡£ BOOTP ¥¯¥é¥¤¥¢¥ó¥È¤Ç¡¢¤³¤Î¥ª¥×¥·¥ç¥ó¤ò¥µ¥Ý¡¼¥È¤·¤Æ¤¤¤ë¤â¤Î¤Ï¾¯¤Ê¤¤¤Ç¤·¤ç¤¦¡£ DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ë¤è¤Ã¤Æ¤Ï¥µ¥Ý¡¼¥È¤¹¤ë¤â¤Î¤¬¤¢¤ê¡¢ ¼ÂºÝɬ¿Ü¤È¤·¤Æ¤¤¤ë¤â¤Î¤¬¤¢¤ê¤Þ¤¹¡£ .RE .PP .B option \fBboot-size\fR \fIuint16\fR\fB;\fR .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥ÈÍѤΥǥե©¥ë¥È¤Î¥Ö¡¼¥È¥¤¥á¡¼¥¸¤ÎŤµ¤ò¡¢ 512 ¥ª¥¯¥Æ¥Ã¥È¥Ö¥í¥Ã¥¯¿ô¤Ç»ØÄꤷ¤Þ¤¹¡£ .RE .PP .B option \fBbroadcast-address\fR \fIip-address\fR\fB;\fR .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤Î¥µ¥Ö¥Í¥Ã¥È¤Ç»ÈÍѤµ¤ì¤Æ¤¤¤ë ¥Ö¥í¡¼¥É¥­¥ã¥¹¥È¥¢¥É¥ì¥¹¤ò»ØÄꤷ¤Þ¤¹¡£ ÀµÅö¤Ê¥Ö¥í¡¼¥É¥­¥ã¥¹¥È¥¢¥É¥ì¥¹¤ÎÃͤϡ¢STD 3 (RFC1122) ¤Î 3.2.1.3 Àá¤Ë µ¬Äꤵ¤ì¤Æ¤¤¤Þ¤¹¡£ .RE .PP .B option \fBcookie-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP ¥¯¥Ã¥­¡¼¥µ¡¼¥Ð¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê RFC 865 ¥¯¥Ã¥­¡¼¥µ¡¼¥Ð¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£ ¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£ .RE .PP .B option \fBdefault-ip-ttl\fR \fIuint8;\fR .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬¥Ç¡¼¥¿¥°¥é¥à¤òÁ÷½Ð¤¹¤ë¤È¤­¤Ë»ÈÍѤ¹¤Ù¤­¡¢ ¥Ç¥Õ¥©¥ë¥È¤ÎÀ¸Â¸»þ´Ö (TTL) ¤ò»ØÄꤷ¤Þ¤¹¡£ .RE .PP .B option \fBdefault-tcp-ttl\fR \fIuint8\fR\fB;\fR .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ TCP ¥»¥°¥á¥ó¥È¤òÁ÷½Ð¤¹¤ë¤È¤­¤Ë»ÈÍѤ¹¤Ù¤­¡¢ ¥Ç¥Õ¥©¥ë¥È¤Î TTL ¤ò»ØÄꤷ¤Þ¤¹¡£ ºÇ¾®ÃÍ¤Ï 1 ¤Ç¤¹¡£ .RE .PP .B option \fBdhcp-client-identifier\fR \fIstring\fR\fB;\fR .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤ò»È¤Ã¤Æ¡¢¥Û¥¹¥ÈÀë¸ÀÃæ¤Ç DHCP ¥¯¥é¥¤¥¢¥ó¥È¼±Ê̻Ҥò »ØÄꤹ¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£ ¤³¤Î¥¯¥é¥¤¥¢¥ó¥È¼±Ê̻ҤǾȹç¤ò¹Ô¤¦¤³¤È¤Ç¡¢ dhcpd ¤Ï¤½¤Î¥Û¥¹¥È¤Î¥ì¥³¡¼¥É¤òȯ¸«¤¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£ .PP .\" metal DHCP ¥¯¥é¥¤¥¢¥ó¥È¤ÎÃæ¤Ë¤Ï¡¢ASCII ¥Æ¥­¥¹¥È¤Ë¤è¤Ã¤Æ¥¯¥é¥¤¥¢¥ó¥È¼±Ê̻Ҥ¬ ÀßÄꤵ¤ì¤¿¾ì¹ç¡¢¤½¤Î ASCII ¥Æ¥­¥¹¥È¤ÎÀèÆ¬¤Ë 0 ¤ò¤Ä¤±¤ë¤â¤Î¤¬¤¢¤ë¤³¤È¤Ë Ãí°Õ¤·¤Æ¤¯¤À¤µ¤¤¡£ ¤½¤Î¾ì¹ç¡¢ .nf option dhcp-client-identifier "foo"; ¤Ç¤Ï¤Ê¤¯¡¢°Ê²¼¤Î¤è¤¦¤Ëµ­½Ò¤¹¤ëɬÍפ¬¤¢¤ë¤Ç¤·¤ç¤¦¡£ option dhcp-client-identifier "\\0foo"; .fi .RE .PP .B option \fBdhcp-lease-time\fR \fIuint32\fR\fB;\fR .RS 0.25i .PP .\" metal ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥ÈÍ×µá (DHCPDISCOVER ¤Þ¤¿¤Ï DHCPREQUEST) ¤ÎÃæ¤Ç¡¢ ¥¯¥é¥¤¥¢¥ó¥È¤¬ IP ¥¢¥É¥ì¥¹¤Î¥ê¡¼¥¹»þ´Ö¤òÍ׵᤹¤ë¤¿¤á¤Ë»ÈÍѤµ¤ì¤Þ¤¹¡£ ¤Þ¤¿¥µ¡¼¥Ð±þÅú (DHCPOFFER) ¤ÎÃæ¤Ç¡¢DHCP ¥µ¡¼¥Ð¤¬Ä󼨤·¤¿¤¤¥ê¡¼¥¹»þ´Ö¤ò »ØÄꤹ¤ë¤Î¤Ë¤â¡¢¤³¤Î¥ª¥×¥·¥ç¥ó¤Ï»È¤ï¤ì¤Þ¤¹¡£ .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥µ¡¼¥Ð¤Ç¤Ï¥æ¡¼¥¶¤¬Ä¾ÀÜÀßÄꤹ¤ë¤³¤È¤Ï¤Ç¤­¤Þ¤»¤ó¡£ .B dhcpd.conf(5) ¤Î \fImax-lease-time\fR ¤È \fidefault-lease-time\fR ¥µ¡¼¥Ð¥ª¥×¥·¥ç¥ó¤ò »²¾È¤·¤Æ¤¯¤À¤µ¤¤¡£ .RE .PP .B option \fBdhcp-max-message-size\fR \fIuint16\fR\fB;\fR .RS 0.25i .PP .\" metal ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤«¤éÁ÷½Ð¤µ¤ì¤¿¾ì¹ç¡¢¥µ¡¼¥Ð¤¬¥¯¥é¥¤¥¢¥ó¥È¤Ë Á÷½Ð¤¹¤ë¤¹¤Ù¤Æ¤Î±þÅú¤ÎºÇÂ祵¥¤¥º¤ò»ØÄꤷ¤Þ¤¹¡£ ¥µ¡¼¥Ð¤ÇÀßÄꤵ¤ì¤¿¾ì¹ç¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ dhcp-max-message-size ¥ª¥×¥·¥ç¥ó¤ò Á÷¿®¤·¤Æ¤³¤Ê¤«¤Ã¤¿ºÝ¤Ë¡¢¤³¤Î¥µ¡¼¥Ð¤ÇÀßÄꤵ¤ì¤¿Ãͤ¬»ÈÍѤµ¤ì¤Þ¤¹¡£ ¤³¤ì¤Ï¡¢BOOTP ±þÅú¤Ç¤â DHCP ±þÅú¤ÈƱÍÍ¤ËÆ°ºî¤·¤Þ¤¹¡£ .RE .PP .B option \fBdhcp-message\fR \fItext\fR\fB;\fR .RS 0.25i .PP .\" metal ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¾ã³²¤¬µ¯¤­¤¿»þ¤Ë¡¢DHCP ¥µ¡¼¥Ð¤¬ DHCPNAK ¥á¥Ã¥»¡¼¥¸Ãæ¤Ç DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ø¥¨¥é¡¼¥á¥Ã¥»¡¼¥¸¤òÄ󶡤¹¤ë¤Î¤Ë»ÈÍѤ·¤Þ¤¹¡£ ¤Þ¤¿¥¯¥é¥¤¥¢¥ó¥È¤¬¡¢Ä󼨤µ¤ì¤¿¥Ñ¥é¥á¡¼¥¿¤òµñÈݤ·¤¿Íýͳ¤ò¼¨¤¹¤¿¤á¤Ë¡¢ DHCPDECLINE ¥á¥Ã¥»¡¼¥¸Ãæ¤ÇËÜ¥ª¥×¥·¥ç¥ó¤ò»È¤¦¤³¤È¤â¤¢¤ê¤Þ¤¹¡£ .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥æ¡¼¥¶¤¬ÀßÄꤹ¤ë¤³¤È¤Ï¤Ç¤­¤Þ¤»¤ó¡£ .RE .PP .B option \fBdhcp-message-type\fR \fIuint8\fR\fB;\fR .RS 0.25i .PP .\" metal ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤È¥µ¡¼¥Ð¤Îξ¼Ô¤«¤éÁ÷½Ð¤µ¤ì¡¢ DHCP ¥Ñ¥±¥Ã¥È¤¬´Þ¤ó¤Ç¤¤¤ë DHCP ¥á¥Ã¥»¡¼¥¸¤Î¥¿¥¤¥×¤ò»ØÄꤷ¤Þ¤¹¡£ ËÜ¥ª¥×¥·¥ç¥ó¤¬¼è¤êÆÀ¤ëÃͤϡ¢°Ê²¼¤Î¤È¤ª¤ê¤Ç¤¹ (RFC2132 ¤è¤ê¤½¤Î¤Þ¤ÞÈ´¿è)¡£ .PP .nf 1 DHCPDISCOVER 2 DHCPOFFER 3 DHCPREQUEST 4 DHCPDECLINE 5 DHCPACK 6 DHCPNAK 7 DHCPRELEASE 8 DHCPINFORM .fi .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥æ¡¼¥¶¤¬ÀßÄꤹ¤ë¤³¤È¤Ï¤Ç¤­¤Þ¤»¤ó¡£ .PP .RE .B option \fBdhcp-option-overload\fR \fIuint8\fR\fB;\fR .RS 0.25i .PP .\" metal ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢DHCP 'sname' ¤â¤·¤¯¤Ï 'file' ¥Õ¥£¡¼¥ë¥É¤¬¡¢ DHCP ¥ª¥×¥·¥ç¥ó¤òÊÝ»ý¤¹¤ë¤¿¤á¤ËµÍ¤á¹þ¤ß²á¤®¤Ë¤Ê¤Ã¤Æ¤¤¤ë¤³¤È¤ò ¼¨¤¹¤Î¤Ë»È¤ï¤ì¤Þ¤¹¡£ DHCP ¥µ¡¼¥Ð¤Ï¡¢ÊֵѤµ¤ì¤¿¥Ñ¥é¥á¡¼¥¿¤¬¡¢¥ª¥×¥·¥ç¥ó¤ËÄ̾ï³ä¤êÅö¤Æ¤é¤ì¤¿ ¶õ´Ö¤òĶ²á¤·¤¿¾ì¹ç¡¢ËÜ¥ª¥×¥·¥ç¥ó¤òÁÞÆþ¤·¤Þ¤¹¡£ .PP ËÜ¥ª¥×¥·¥ç¥ó¤¬Â¸ºß¤·¤¿¾ì¹ç¡¢¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢É¸½à¤Î¥ª¥×¥·¥ç¥ó¥Õ¥£¡¼¥ë¥É¤Î ²ò¼á¤¬½ªÎ»¤·¤¿¸å¡¢»ØÄꤵ¤ì¤¿Éղåե£¡¼¥ë¥É¤Î²ò¼á¤ò¹Ô¤¤¤Þ¤¹¡£ .PP ËÜ¥ª¥×¥·¥ç¥ó¤ÎÀµÅö¤ÊÃͤϡ¢°Ê²¼¤ÎÄ̤ê¤Ç¤¹: .PP .nf 1 'file' ¥Õ¥£¡¼¥ë¥É¤¬¡¢¥ª¥×¥·¥ç¥óÊÝ»ý¤Ë»ÈÍѤµ¤ì¤Æ¤Þ¤¹ 2 'sname' ¥Õ¥£¡¼¥ë¥É¤¬¡¢¥ª¥×¥·¥ç¥óÊÝ»ý¤Ë»ÈÍѤµ¤ì¤Æ¤Þ¤¹ 3 ξÊý¤Î¥Õ¥£¡¼¥ë¥É¤¬¡¢¥ª¥×¥·¥ç¥óÊÝ»ý¤Ë»ÈÍѤµ¤ì¤Æ¤Þ¤¹ .fi .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥æ¡¼¥¶¤¬ÀßÄꤹ¤ë¤³¤È¤Ï¤Ç¤­¤Þ¤»¤ó¡£ .PP .RE .PP .B option \fBdhcp-parameter-request-list\fR \fIuint16\fR\fB;\fR .RS 0.25i .PP .\" metal ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤«¤éÁ÷½Ð¤µ¤ì¤¿¾ì¹ç¡¢ ¥µ¡¼¥Ð¤ËÊÖÅú¤ò´õ˾¤¹¤ë¥ª¥×¥·¥ç¥ó¤ò¥¯¥é¥¤¥¢¥ó¥È¤¬»ØÄꤷ¤Þ¤¹¡£ Ä̾ï ISC DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ç¤Ï¡¢\fIrequest\fR ʸ¤òÍѤ¤¤Æ¹Ô¤ï¤ì¤Þ¤¹¡£ ËÜ¥ª¥×¥·¥ç¥ó¤¬¥¯¥é¥¤¥¢¥ó¥È¤«¤é»ØÄꤵ¤ì¤Ê¤«¤Ã¤¿¾ì¹ç¡¢Ä̾ï DHCP ¥µ¡¼¥Ð¤Ï¡¢ ¥¹¥³¡¼¥×Æâ¤ÇÍ­¸ú¤«¤Ä±þÅú¤Ë¼ý¤Þ¤ë¤¹¤Ù¤Æ¤Î¥ª¥×¥·¥ç¥ó¤òÊÖ¤·¤Þ¤¹¡£ ËÜ¥ª¥×¥·¥ç¥ó¤¬¥µ¡¼¥Ð¾å¤Ç»ØÄꤵ¤ì¤¿¾ì¹ç¡¢¥µ¡¼¥Ð¤Ï¤½¤Î»ØÄꤵ¤ì¤¿¥ª¥×¥·¥ç¥ó¤ò ÊÖ¤·¤Þ¤¹¡£ ¤³¤ì¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬Í׵ᤷ¤Ê¤«¤Ã¤¿¥ª¥×¥·¥ç¥ó¤ò¡¢¥¯¥é¥¤¥¢¥ó¥È¤Ë ¶¯À©¤¹¤ë¤Î¤Ë»ÈÍѤµ¤ì¤Þ¤¹¡£ ¤Þ¤¿¡¢Ä̾掠¡¼¥Ð¤¬ÊÖ¤¹¥ª¥×¥·¥ç¥ó¤Î¥»¥Ã¥È¤ò¤µ¤é¤ËÀ©¸Â¤¹¤ëɬÍפΤ¢¤ë ¥¯¥é¥¤¥¢¥ó¥È¤ËÂФ·¤Æ¡¢DHCP ¥µ¡¼¥Ð¤Î±þÅú¤òÄ´À°¤¹¤ë¤Î¤Ë¤â»ÈÍѤµ¤ì¤Þ¤¹¡£ .RE .PP .B option \fBdhcp-rebinding-time\fR \fIuint32\fR\fB;\fR .RS 0.25i .PP .\" metal ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬¥¢¥É¥ì¥¹¤ò¼èÆÀ¤·¤Æ¤«¤é REBINDING ¾õÂÖ¤Ë °Ü¹Ô¤¹¤ë¤Þ¤Ç¤Î»þ´Ö¤òÉÿô¤Ç»ØÄꤷ¤Þ¤¹¡£ .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥æ¡¼¥¶¤¬ÀßÄꤹ¤ë¤³¤È¤Ï¤Ç¤­¤Þ¤»¤ó¡£ .PP .RE .PP .B option \fBdhcp-renewal-time\fR \fIuint32\fR\fB;\fR .RS 0.25i .PP .\" metal ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬¥¢¥É¥ì¥¹¤ò¼èÆÀ¤·¤Æ¤«¤é RENEWING ¾õÂÖ¤Ë °Ü¹Ô¤¹¤ë¤Þ¤Ç¤Î»þ´Ö¤òÉÿô¤Ç»ØÄꤷ¤Þ¤¹¡£ .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥æ¡¼¥¶¤¬ÀßÄꤹ¤ë¤³¤È¤Ï¤Ç¤­¤Þ¤»¤ó¡£ .PP .RE .PP .B option \fBdhcp-requested-address\fR \fIip-address\fR\fB;\fR .RS 0.25i .PP .\" metal ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬¡¢DHCPDISCOVER Æâ¤ÇÆÃÄê¤Î IP ¥¢¥É¥ì¥¹¤¬ ³ä¤êÅö¤Æ¤é¤ì¤ë¤³¤È¤òÍ׵᤹¤ë¤Î¤Ë»ÈÍѤµ¤ì¤Þ¤¹¡£ .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥æ¡¼¥¶¤¬ÀßÄꤹ¤ë¤³¤È¤Ï¤Ç¤­¤Þ¤»¤ó¡£ .PP .RE .PP .B option \fBdhcp-server-identifier\fR \fIip-address\fR\fB;\fR .RS 0.25i .PP .\" metal ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢DHCPOFFER ¤È DHCPREQUEST ¥á¥Ã¥»¡¼¥¸Ãæ¤Ç»ÈÍѤµ¤ì¡¢ ¤Þ¤¿ DHCPACK ¤È DHCPNAK ¥á¥Ã¥»¡¼¥¸Ãæ¤Ë¤â´Þ¤Þ¤ì¤ë¤³¤È¤¬¤¢¤ê¤Þ¤¹¡£ DHCP ¥µ¡¼¥Ð¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ (ÌõÃí: Ê£¿ô¥µ¡¼¥Ð¤«¤é¤Î) ¥ê¡¼¥¹¤ÎÄ󼨤ò ¶èÊ̤Ǥ­¤ë¤è¤¦¡¢DHCPOFFER ¤ËËÜ¥ª¥×¥·¥ç¥ó¤ò´Þ¤á¤Þ¤¹¡£ DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢DHCP ¥µ¡¼¥Ð¤Ø¥æ¥Ë¥­¥ã¥¹¥È¤¹¤ë¤¹¤Ù¤Æ¤Î DHCP ¥á¥Ã¥»¡¼¥¸¤Î °¸À襢¥É¥ì¥¹¤È¤·¤Æ 'server identifier' ¥Õ¥£¡¼¥ë¥É¤ÎÆâÍÆ¤ò»ÈÍѤ·¤Þ¤¹¡£ ¤Þ¤¿ DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢DHCPREQUEST ¥á¥Ã¥»¡¼¥¸Ãæ¤ËËÜ¥ª¥×¥·¥ç¥ó¤ò´Þ¤á¡¢ Ê£¿ô¤Î¥ê¡¼¥¹¤ÎÄ󼨤Τɤì¤ò¼õ¤±Æþ¤ì¤¿¤«¤ò¼¨¤·¤Þ¤¹¡£ .PP ËÜ¥ª¥×¥·¥ç¥ó¤ÎÃͤϡ¢¥µ¡¼¥Ð¤Î IP ¥¢¥É¥ì¥¹¤Ç¤¹¡£ .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥æ¡¼¥¶¤¬Ä¾ÀÜÀßÄꤹ¤ë¤³¤È¤Ï¤Ç¤­¤Þ¤»¤ó¡£ .B \fIdhcpd.conf(5) ¤Î \fIserver-identifier\fR ¥µ¡¼¥Ð¥ª¥×¥·¥ç¥ó¤ò»²¾È¤·¤Æ¤¯¤À¤µ¤¤¡£ .PP .RE .PP .B option \fBdomain-name\fR \fItext\fR\fB;\fR .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥É¥á¥¤¥ó¥Í¡¼¥à¥·¥¹¥Æ¥à¤ò»ÈÍѤ·¤Æ¥Û¥¹¥È̾¤ò²ò·è¤¹¤ë¤È¤­¤Ë ¥¯¥é¥¤¥¢¥ó¥È¤¬»ÈÍѤ¹¤Ù¤­¥É¥á¥¤¥ó̾¤ò»ØÄꤷ¤Þ¤¹¡£ .RE .PP .B option \fBdomain-name-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP domain-name-servers ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê ¥É¥á¥¤¥ó¥Í¡¼¥à¥·¥¹¥Æ¥à (STD 13, RFC 1035) ¤Î¥Í¡¼¥à¥µ¡¼¥Ð¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£ ¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£ .RE .PP .B option \fBextensions-path\fR \fItext\fR\fB;\fR .RS 0.25i .PP .\" metal ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢Äɲ媥ץ·¥ç¥ó¤ò´Þ¤à¥Õ¥¡¥¤¥ë¤Î¥Õ¥¡¥¤¥ë̾¤ò»ØÄꤷ¤Þ¤¹¡£ ¤³¤ÎÄɲ媥ץ·¥ç¥ó¤Ï¡¢RFC2132 ¤Çµ¬Äꤵ¤ì¤Æ¤¤¤ë DHCP ¥ª¥×¥·¥ç¥ó¤Î½ñ¼°¤Ë±è¤Ã¤Æ ²ò¼á¤µ¤ì¤Þ¤¹¡£ .RE .PP .B option \fBfinger-server\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP Finger ¥µ¡¼¥Ð¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê Finger ¤Î¥ê¥¹¥È¤ò »ØÄꤷ¤Þ¤¹¡£ ¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£ .RE .PP .B option \fBfont-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê X Window System ¥Õ¥©¥ó¥È¥µ¡¼¥Ð¤ò »ØÄꤷ¤Þ¤¹¡£ ¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£ .RE .PP .B option \fBhost-name\fR \fIstring\fR\fB;\fR .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤Î̾Á°¤ò»ØÄꤷ¤Þ¤¹¡£ ¤³¤Î̾Á°¤Ï¡¢¥í¡¼¥«¥ë¥É¥á¥¤¥ó̾¤Ë½¤¾þ¤µ¤ì¤Æ¤¤¤Æ¤â¡¢¤¤¤Ê¤¯¤Æ¤â¤«¤Þ¤¤¤»¤ó (¥É¥á¥¤¥ó̾¤ò»ØÄꤹ¤ë¤Ë¤Ï¡¢domain-name ¥ª¥×¥·¥ç¥ó¤Î»ÈÍѤò¤ª´«¤á¤·¤Þ¤¹)¡£ ʸ»ú½¸¹ç¤ÎÀ©Ìó¤Ë¤Ä¤¤¤Æ¤Ï RFC 1035 ¤ò»²¾È¤·¤Æ¤¯¤À¤µ¤¤¡£ ¥¯¥é¥¤¥¢¥ó¥È¥Þ¥·¥ó¤Î¥Û¥¹¥È̾¤¬ÀßÄꤵ¤ì¤Æ¤¤¤Ê¤¤¾ì¹ç (¤¹¤Ê¤ï¤Á .B rc.conf(5) ¤Ç¶õʸ»úÎó¤ËÀßÄꤵ¤ì¤Æ¤¤¤ë¾ì¹ç) ¤Î¤ß¡¢ .B dhclient-script(8) ¤¬ËÜ¥ª¥×¥·¥ç¥ó¤òº½Å¤·¤Þ¤¹¡£ .RE .PP .B option \fBieee802-3-encapsulation\fR \fIflag\fR\fB;\fR .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤¬¥¤¡¼¥µ¥Í¥Ã¥È¤Ç¤¢¤ë¾ì¹ç¤Ë¡¢ ¥¯¥é¥¤¥¢¥ó¥È¤¬¥¤¡¼¥µ¥Í¥Ã¥È¥Ð¡¼¥¸¥ç¥ó 2 (RFC 894) ¤È IEEE 802.3 (RFC 1042) ¤Î¤É¤Á¤é¤Î¥«¥×¥»¥ë²½¤ò»ÈÍѤ¹¤Ù¤­¤«¤ò»ØÄꤷ¤Þ¤¹¡£ ÃÍ false ¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ RFC 894 ¤Î¥«¥×¥»¥ë²½¤ò»ÈÍѤ¹¤Ù¤­¤Ç¤¢¤ë¤³¤È¤ò °ÕÌ£¤·¤Þ¤¹¡£ ÃÍ true ¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ RFC 1042 ¤Î¥«¥×¥»¥ë²½¤ò»ÈÍѤ¹¤Ù¤­¤Ç¤¢¤ë¤³¤È¤ò °ÕÌ£¤·¤Þ¤¹¡£ .RE .PP .B option \fBien116-name-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]; .RS 0.25i .PP ien116-name-servers ¥ª¥×¥·¥ç¥ó¤Ï¡¢ ¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê IEN 116 ¥Í¡¼¥à¥µ¡¼¥Ð¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£ ¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£ .RE .PP .B option \fBimpress-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP impress-server ¥ª¥×¥·¥ç¥ó¤Ï¡¢ ¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê Imagen Impress ¥µ¡¼¥Ð¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£ ¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£ .RE .PP .B option \fBinterface-mtu\fR \fIuint16\fR\fB;\fR .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¤³¤Î¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤ËÂФ·¤Æ»ÈÍѤ¹¤ë MTU ¤ò»ØÄꤷ¤Þ¤¹¡£ MTU ¤ËÂФ¹¤ëºÇ¾®¤ÎÀµÅöÃÍ¤Ï 68 ¤Ç¤¹¡£ .RE .PP .B option \fBip-forwarding\fR \fIflag\fR\fB;\fR .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬¡¢¥Ñ¥±¥Ã¥È¤òžÁ÷¤¹¤ë¤è¤¦¤Ë ¼«Ê¬¤Î IP ÁؤòÀßÄꤹ¤Ù¤­¤«¤ò»ØÄꤷ¤Þ¤¹¡£ ÃÍ false ¤Ï IP žÁ÷¤ò̵¸ú¤Ë¤¹¤ë¤³¤È¤ò°ÕÌ£¤·¡¢ ÃÍ true ¤Ï IP žÁ÷¤òÍ­¸ú¤Ë¤¹¤ë¤³¤È¤ò°ÕÌ£¤·¤Þ¤¹¡£ .RE .PP .B option \fBirc-server\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP IRC ¥µ¡¼¥Ð¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê IRC ¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£ ¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£ .RE .PP .B option \fBlog-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP log-server ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê MIT-LCS UDP ¥í¥°¥µ¡¼¥Ð¤Î ¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£ ¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£ .RE .PP .B option \fBlpr-servers\fR \fIip-address \fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP LPR ¥µ¡¼¥Ð¥ª¥×¥·¥ç¥ó¤Ï¡¢ ¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê RFC 1179 ¥é¥¤¥ó¥×¥ê¥ó¥¿¥µ¡¼¥Ð¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£ ¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£ .RE .PP .B option \fBmask-supplier\fR \fIflag\fR\fB;\fR .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢ICMP ¤ò»ÈÍѤ·¤¿¥µ¥Ö¥Í¥Ã¥È¥Þ¥¹¥¯Í×µá¤ËÂФ·¤Æ¡¢ ¥¯¥é¥¤¥¢¥ó¥È¤¬±þÅú¤¹¤Ù¤­¤«¤ò»ØÄꤷ¤Þ¤¹¡£ ÃÍ false ¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬±þÅú¤¹¤Ù¤­¤Ç¤Ê¤¤¤³¤È¤ò°ÕÌ£¤·¤Þ¤¹¡£ ÃÍ true ¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬±þÅú¤¹¤Ù¤­¤Ç¤¢¤ë¤³¤È¤ò°ÕÌ£¤·¤Þ¤¹¡£ .RE .PP .B option \fBmax-dgram-reassembly\fR \fIuint16\fR\fB;\fR .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ºÆÁȤßΩ¤Æ¤Î½àÈ÷¤ò¤¹¤Ù¤­ ºÇÂç¥Ç¡¼¥¿¥°¥é¥à¥µ¥¤¥º¤ò»ØÄꤷ¤Þ¤¹¡£ ºÇ¾®¤ÎÀµÅöÃÍ¤Ï 576 ¤Ç¤¹¡£ .\" The minimum value legal value is 576. .\" The minimum legal value is 576. ¤«¤Ê (horikawa@jp.freebsd.org 19990404) .RE .PP .B option \fBmerit-dump\fR \fItext\fR\fB;\fR .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬¥¯¥é¥Ã¥·¥å¤¹¤ë¤È¤­¤Î ¥¯¥é¥¤¥¢¥ó¥È¤Î¥³¥¢¥¤¥á¡¼¥¸¤¬¥À¥ó¥×¤µ¤ì¤ë¥Õ¥¡¥¤¥ë¤Î¥Ñ¥¹Ì¾¤ò»ØÄꤷ¤Þ¤¹¡£ ¥Ñ¥¹¤Î½ñ¼°¤Ï¡¢NVT ASCII ʸ»ú½¸¹ç¤Îʸ»ú¤«¤é¤Ê¤ëʸ»úÎó¤Ç¤¹¡£ .RE .PP .B option \fBmobile-ip-home-agent\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê¥â¥Ð¥¤¥ë IP ¥Û¡¼¥à¥¨¡¼¥¸¥§¥ó¥È¤Î IP ¥¢¥É¥ì¥¹¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£ ¥¨¡¼¥¸¥§¥ó¥È¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£ ¤¿¤À¤·¡¢Ä̾泌¡¼¥¸¥§¥ó¥È¤Ï 1 ¤Ä¤Ç¤·¤ç¤¦¡£ .RE .PP .B option \fBnds-context\fR \fIstring\fR\fB;\fR .RS 0.25i .PP .\" metal nds-context ¥ª¥×¥·¥ç¥ó¤Ï¡¢NDS ¥¯¥é¥¤¥¢¥ó¥È¤Î¤¿¤á¤ÎºÇ½é¤Î NetWare ¥Ç¥£¥ì¥¯¥È¥ê¥µ¡¼¥Ó¥¹¤Î̾Á°¤ò»ØÄꤷ¤Þ¤¹¡£ .RE .PP .B option \fBnds-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP .\" metal nds-servers ¥ª¥×¥·¥ç¥ó¤Ï¡¢NDS ¥µ¡¼¥Ð¤Î IP ¥¢¥É¥ì¥¹¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£ .RE .PP .B option \fBnds-tree-name\fR \fIstring\fR\fB;\fR .RS 0.25i .PP .\" metal nds-tree-name ¥ª¥×¥·¥ç¥ó¤Ï¡¢NDS ¥¯¥é¥¤¥¢¥ó¥È¤¬»ÈÍѤ¹¤Ù¤­ NDS ¥Ä¥ê¡¼¤Î ̾Á°¤ò»ØÄꤷ¤Þ¤¹¡£ .RE .PP .B option \fBnetbios-dd-server\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP NetBIOS ¥Ç¡¼¥¿¥°¥é¥àÇÛÉÛ¥µ¡¼¥Ð (NBDD) ¥ª¥×¥·¥ç¥ó¤Ï¡¢ RFC 1001/1002 ¤Î NBDD ¥µ¡¼¥Ð¤Î¥ê¥¹¥È¤ò¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë»ØÄꤷ¤Þ¤¹¡£ .RE .PP .B option \fBnetbios-name-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...]\fB;\fR .RS 0.25i .PP NetBIOS ¥Í¡¼¥à¥µ¡¼¥Ð (NBNS) ¥ª¥×¥·¥ç¥ó¤Ï¡¢ RFC 1001/1002 ¤Î NBNS ¥Í¡¼¥à¥µ¡¼¥Ð¤Î¥ê¥¹¥È¤ò¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë»ØÄꤷ¤Þ¤¹¡£ ¸½ºß¤Ç¤Ï¡¢NetBIOS ¥Í¡¼¥à¥µ¡¼¥Ó¥¹¤Ï WINS ¤È¸Æ¤Ð¤ì¤ë¤³¤È¤ÎÊý¤¬Â¿¤¤¤Ç¤¹¡£ netbios-name-servers ¥ª¥×¥·¥ç¥ó¤ò»ÈÍѤ·¤Æ¡¢WINS ¥µ¡¼¥Ð¤ò»ØÄê²Äǽ¤Ç¤¹¡£ .RE .PP .B option \fBnetbios-node-type\fR \fIuint8\fR\fB;\fR .RS 0.25i .PP NetBIOS ¥Î¡¼¥É¥¿¥¤¥×¥ª¥×¥·¥ç¥ó¤Ï¡¢ ÀßÄê²Äǽ¤Ê NetBIOS over TCP/IP ¥¯¥é¥¤¥¢¥ó¥È¤ò¡¢ RFC 1001/1002 ¤Ëµ­½Ò¤µ¤ì¤Æ¤¤¤ë¤è¤¦¤ËÀßÄꤷ¤Þ¤¹¡£ ÃͤÏñ°ì¤Î¥ª¥¯¥Æ¥Ã¥È¤È¤·¤Æ»ØÄꤵ¤ì¡¢¥¯¥é¥¤¥¢¥ó¥È¥¿¥¤¥×¤ò°ÕÌ£¤·¤Þ¤¹¡£ .PP »ÈÍѲÄǽ¤Ê¥Î¡¼¥É¥¿¥¤¥×¤Ï¼¡¤ÎÄ̤ê¤Ç¤¹: .PP .TP 5 .I 1 B ¥Î¡¼¥É: ¥Ö¥í¡¼¥É¥­¥ã¥¹¥È - WINS ̵¤· .TP .I 2 P ¥Î¡¼¥É: ¥Ô¥¢ - WINS ¤Î¤ß .TP .I 4 M ¥Î¡¼¥É: ¥ß¥Ã¥¯¥¹ - ¥Ö¥í¡¼¥É¥­¥ã¥¹¥È¸å¤Ë WINS .TP .I 8 H ¥Î¡¼¥É: ¥Ï¥¤¥Ö¥ê¥Ã¥É - WINS ¸å¤Ë¥Ö¥í¡¼¥É¥­¥ã¥¹¥È .RE .PP .B option \fBnetbios-scope\fR \fIstring\fR\fB;\fR .RS 0.25i .PP NetBIOS ¥¹¥³¡¼¥×¥ª¥×¥·¥ç¥ó¤Ï¡¢RFC 1001/1002 ¤Ëµ¬Äꤵ¤ì¤Æ¤¤¤ë¤è¤¦¤Ë¡¢ ¥¯¥é¥¤¥¢¥ó¥È¤Î NetBIOS over TCP/IP ¥¹¥³¡¼¥×¥Ñ¥é¥á¡¼¥¿¤ò»ØÄꤷ¤Þ¤¹¡£ ʸ»ú½¸¹ç¤ÎÀ©Ìó¤Ë¤Ä¤¤¤Æ¤Ï RFC1001, RFC1002, RFC1035 ¤ò»²¾È¤·¤Æ¤¯¤À¤µ¤¤¡£ .RE .PP .B option \fBnis-domain\fR \fItext\fR\fB;\fR .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤Î NIS (Sun Network Information Services) ¥É¥á¥¤¥ó¤ò»ØÄꤷ¤Þ¤¹¡£ ¥É¥á¥¤¥ó¤Î½ñ¼°¤Ï¡¢NVT ASCII ʸ»ú½¸¹ç¤Îʸ»ú¤«¤é¤Ê¤ëʸ»úÎó¤Ç¤¹¡£ .RE .PP .B option \fBnis-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê NIS ¥µ¡¼¥Ð¤ò¼¨¤¹ IP ¥¢¥É¥ì¥¹¤Î ¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£ ¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£ .RE .PP .B option \fBnisplus-domain\fR \fItext\fR\fB;\fR .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤Î NIS+ ¥É¥á¥¤¥ó¤Î̾Á°¤ò»ØÄꤷ¤Þ¤¹¡£ ¥É¥á¥¤¥ó¤Î½ñ¼°¤Ï¡¢NVT ASCII ʸ»ú½¸¹ç¤Îʸ»ú¤«¤é¤Ê¤ëʸ»úÎó¤Ç¤¹¡£ .RE .PP .B option \fBnisplus-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê NIS+ ¥µ¡¼¥Ð¤ò¼¨¤¹ IP ¥¢¥É¥ì¥¹¤Î ¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£ ¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£ .RE .PP .B option \fBnntp-server\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP NNTP ¥µ¡¼¥Ð¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê NNTP ¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£ ¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£ .RE .PP .B option \fBnon-local-source-routing\fR \fIflag\fR\fB;\fR .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢Èó¥í¡¼¥«¥ë¤Ê»ØÄê·ÐÏ© (non-local source route) ¤ò»ý¤Ä ¥Ç¡¼¥¿¥°¥é¥à¤òžÁ÷¤¹¤ë¤è¤¦¤Ë¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬¼«Ê¬¤Î IP ÁؤòÀßÄꤹ¤Ù¤­¤«¤ò »ØÄꤷ¤Þ¤¹ (ËܹàÌܤˤĤ¤¤Æ¤Ï [4] ¤Î 3.3.5 Àá¤ò»²¾È¤·¤Æ¤¯¤À¤µ¤¤)¡£ ÃÍ false ¤Ï¤½¤Î¤è¤¦¤Ê¥Ç¡¼¥¿¥°¥é¥à¤ÎžÁ÷¤òµö²Ä¤·¤Ê¤¤¤³¤È¤ò°ÕÌ£¤·¡¢ ÃÍ true ¤ÏžÁ÷µö²Ä¤ò°ÕÌ£¤·¤Þ¤¹¡£ .RE .PP .B option \fBntp-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê NTP (RFC 1035) ¥µ¡¼¥Ð¤ò¼¨¤¹ IP ¥¢¥É¥ì¥¹¤ò»ØÄꤷ¤Þ¤¹¡£ ¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£ .RE .PP .B option \fBnwip-domain\fR \fIstring\fR\fB;\fR .RS 0.25i .PP .\" metal NetWare/IP ¥¯¥é¥¤¥¢¥ó¥È¤¬»ÈÍѤ¹¤Ù¤­ NetWare/IP ¥É¥á¥¤¥ó¤Î̾Á°¤Ç¤¹¡£ .RE .PP .B option \fBnwip-suboptions\fR \fIstring\fR\fB;\fR .RS 0.25i .PP .\" metal NetWare/IP ¥¯¥é¥¤¥¢¥ó¥ÈÍѤΥµ¥Ö¥ª¥×¥·¥ç¥ó¤Î¥·¡¼¥±¥ó¥¹¤Ç¤¹¡£ ¾Ü¤·¤¯¤Ï RFC2242 ¤ò»²¾È¤·¤Æ¤¯¤À¤µ¤¤¡£ Ä̾ËÜ¥ª¥×¥·¥ç¥ó¤ÏÆÃÄê¤Î NetWare/IP ¥µ¥Ö¥ª¥×¥·¥ç¥ó¤ò»ØÄꤹ¤ë¤³¤È¤Ç ÀßÄꤵ¤ì¤Þ¤¹¡£ ¤µ¤é¤Ê¤ë¾ðÊó¤Ï¡ÖNetWare/IP ¥µ¥Ö¥ª¥×¥·¥ç¥ó¡×¤Î¾Ï¤ò»²¾È¤·¤Æ¤¯¤À¤µ¤¤¡£ .RE .PP .B option \fBpath-mtu-aging-timeout\fR \fIuint32\fR\fB;\fR .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢RFC 1191 ¤ÇÄêµÁ¤µ¤ì¤ëµ¡¹½¤Çȯ¸«¤µ¤ì¤¿¥Ñ¥¹ MTU ÃͤΠ¥¨¡¼¥¸¥ó¥°¤Ë»ÈÍѤ¹¤ë¥¿¥¤¥à¥¢¥¦¥È (ÉÃñ°Ì) ¤ò»ØÄꤷ¤Þ¤¹¡£ .RE .PP .B option \fBpath-mtu-plateau-table\fR \fIuint16\fR [\fB,\fR \fIuint16\fR... ]\fB;\fR .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢RFC 1191 ¤ÇÄêµÁ¤µ¤ì¤ë¥Ñ¥¹ MTU õº÷ (Path MTU Discovery) ¼Â»Ü»þ¤Ë»ÈÍѤµ¤ì¤ë MTU ¤Î¥µ¥¤¥º¤Îɽ¤ò»ØÄꤷ¤Þ¤¹¡£ ɽ¤Î½ñ¼°¤Ï¡¢ºÇ¾®¤«¤é½ç¤ËºÇÂç¤Þ¤Ç¤Î¡¢16 ¥Ó¥Ã¥ÈÉä¹æÌµ¤·À°¿ô¤Î¥ê¥¹¥È¤Ç¤¹¡£ ºÇ¾® MTU ¤Ï 68 ¤è¤ê¾®¤µ¤¯¤Æ¤Ï¤Ê¤ê¤Þ¤»¤ó¡£ .RE .PP .B option \fBperform-mask-discovery\fR \fIflag\fR\fB;\fR .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ ICMP ¤ò»ÈÍѤ·¤Æ¥µ¥Ö¥Í¥Ã¥È¥Þ¥¹¥¯Ãµº÷¤ò ¼Â»Ü¤¹¤Ù¤­¤«¤ò»ØÄꤷ¤Þ¤¹¡£ ÃÍ false ¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬¥Þ¥¹¥¯Ãµº÷¤ò¼Â»Ü¤¹¤Ù¤­¤Ç¤Ê¤¤¤³¤È¤ò°ÕÌ£¤·¤Þ¤¹¡£ ÃÍ true ¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬¥Þ¥¹¥¯Ãµº÷¤ò¼Â»Ü¤¹¤Ù¤­¤Ç¤¢¤ë¤³¤È¤ò°ÕÌ£¤·¤Þ¤¹¡£ .RE .PP .nf .B option \fBpolicy-filter\fR \fIip-address ip-address\fR [\fB,\fR \fIip-address ip-address\fR...]\fB;\fR .RE .fi .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢Èó¥í¡¼¥«¥ë¤Ê»ØÄê·ÐÏ©À©¸æ¤ËÂФ¹¤ë¥Ý¥ê¥·¥Õ¥£¥ë¥¿¤ò»ØÄꤷ¤Þ¤¹¡£ ¥Õ¥£¥ë¥¿¤Ï¡¢IP ¥¢¥É¥ì¥¹¤È¥Þ¥¹¥¯¤ÎÁȤΥꥹ¥È¤«¤é¤Ê¤ê¡¢ ÅþÃ夹¤ë»ØÄê·ÐÏ©À©¸æ¤µ¤ì¤¿¥Ç¡¼¥¿¥°¥é¥àÍѤΥե£¥ë¥¿¤È¤Ê¤ë °¸Àè/¥Þ¥¹¥¯¤ÎÁȤò»ØÄꤷ¤Þ¤¹¡£ .PP ¼¡¥Û¥Ã¥×¥¢¥É¥ì¥¹¤¬¥Õ¥£¥ë¥¿¤Î¤¤¤º¤ì¤Ë¤âŬ¹ç¤·¤Ê¤¤»ØÄê·ÐÏ©À©¸æ¤µ¤ì¤¿ ¥Ç¡¼¥¿¥°¥é¥à¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÇË´þ¤¹¤Ù¤­¤Ç¤¹¡£ .PP ¤µ¤é¤Ê¤ë¾ðÊó¤Ï STD 3 (RFC1122) ¤ò»²¾È¤·¤Æ¤¯¤À¤µ¤¤¡£ .RE .PP .B option \fBpop-server\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP POP3 ¥µ¡¼¥Ð¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê POP3 ¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£ ¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£ .RE .PP .B option \fBresource-location-servers\fR \fIip-address\fR [\fB, \fR\fIip-address\fR...]\fB;\fR .fi .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê RFC 887 ¥ê¥½¡¼¥¹¥í¥±¡¼¥·¥ç¥ó¥µ¡¼¥Ð¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£ ¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£ .RE .PP .B option \fBroot-path\fR \fItext\fB;\fR\fR .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤Î¥ë¡¼¥È¥Ç¥£¥¹¥¯¤¬´Þ¤Þ¤ì¤ë¥Ñ¥¹Ì¾¤ò»ØÄꤷ¤Þ¤¹¡£ ¥Ñ¥¹¤Î½ñ¼°¤Ï¡¢NVT ASCII ʸ»ú½¸¹ç¤Îʸ»ú¤«¤é¤Ê¤ëʸ»úÎó¤Ç¤¹¡£ .RE .PP .B option \fBrouter-discovery\fR \fIflag\fR\fB;\fR .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢RFC 1256 ¤ÇÄêµÁ¤µ¤ì¤ë¥ë¡¼¥¿Ãµº÷ (Router Discovery) µ¡¹½¤ò »ÈÍѤ·¤Æ¡¢¥ë¡¼¥¿¤òÍ×ÀÁ¤¹¤Ù¤­¤«¤ò»ØÄꤷ¤Þ¤¹¡£ ÃÍ false ¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬¥ë¡¼¥¿Ãµº÷¤ò¼Â»Ü¤¹¤Ù¤­¤Ç¤Ê¤¤¤³¤È¤ò°ÕÌ£¤·¤Þ¤¹¡£ ÃÍ true ¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤Ï¥ë¡¼¥¿Ãµº÷¤ò¼Â»Ü¤¹¤Ù¤­¤Ç¤¢¤ë¤³¤È¤ò°ÕÌ£¤·¤Þ¤¹¡£ .RE .PP .B option \fBrouter-solicitation-address\fR \fIip-address\fR\fB;\fR .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤Î¥ë¡¼¥¿Í×ÀÁ¤ÎÁ÷½ÐÀ襢¥É¥ì¥¹¤ò»ØÄꤷ¤Þ¤¹¡£ .RE .PP .B option routers \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP routers ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤Î¥µ¥Ö¥Í¥Ã¥È¾å¤Ë¤¢¤ë¥ë¡¼¥¿¤Î IP ¥¢¥É¥ì¥¹¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£ ¥ë¡¼¥¿¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£ .RE .PP .B option slp-directory-agent \fIboolean ip-address [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP .\" metal ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢2 ¤Ä¤Î¹àÌܤò»ØÄꤷ¤Þ¤¹: 1 ¤Ä°Ê¾å¤Î¥µ¡¼¥Ó¥¹¥í¥±¡¼¥·¥ç¥ó¥×¥í¥È¥³¥ë¥Ç¥£¥ì¥¯¥È¥ê¥¨¡¼¥¸¥§¥ó¥È (Service Location Protocol Directory Agent) ¤Î IP ¥¢¥É¥ì¥¹¤È¡¢ ¤³¤ì¤é¤Î¥¢¥É¥ì¥¹¤Î»ÈÍѤ¬¶¯À©Åª¤«¤É¤¦¤«¤Ç¤¹¡£ ºÇ½é¤Î¥Ö¡¼¥ëÃͤ¬ true ¤Ç¤¢¤ì¤Ð¡¢SLP ¥¨¡¼¥¸¥§¥ó¥È¤Ï¡¢¤¿¤ÀÍ¿¤¨¤é¤ì¤¿ IP ¥¢¥É¥ì¥¹¤Î¤ß¤ò»ÈÍѤ¹¤Ù¤­¤Ç¤¹¡£ Ãͤ¬ false ¤Ç¤¢¤ì¤Ð¡¢SLP ¥¨¡¼¥¸¥§¥ó¥È¤Ï¡¢SLP ¥¨¡¼¥¸¥§¥ó¥È¤Î ǽưŪ¤â¤·¤¯¤Ï¼õưŪ¤Ê¥Þ¥ë¥Á¥­¥ã¥¹¥Èõº÷¤òÄɲäǹԤäƤ⹽¤¤¤Þ¤»¤ó (¾Ü¤·¤¯¤Ï RFC2165 ¤ò»²¾È¤·¤Æ¤¯¤À¤µ¤¤)¡£ .PP ËÜ¥ª¥×¥·¥ç¥ó¤È slp-service-scope ¥ª¥×¥·¥ç¥ó¤Ë¤ª¤¤¤Æ¡¢ ¡ÖSLP ¥¨¡¼¥¸¥§¥ó¥È¡×¤È¤Ï¡¢DHCP ¥×¥í¥È¥³¥ë¤òÍѤ¤¤ÆÀßÄꤵ¤ì¤¿¥Þ¥·¥ó¾å¤Ç ưºî¤·¤Æ¤¤¤ë¥µ¡¼¥Ó¥¹¥í¥±¡¼¥·¥ç¥ó¥×¥í¥È¥³¥ë¥¨¡¼¥¸¥§¥ó¥È¤ò»Ø¤·¤Æ¤¤¤ë¤³¤È¤Ë Ãí°Õ¤·¤Æ¤¯¤À¤µ¤¤¡£ .PP ¤Þ¤¿¡¢¤¤¤¯¤Ä¤«¤Î´ë¶È¤Ï SLP ¤ò NDS ¤È¸Æ¤ó¤Ç¤¤¤ë¤³¤È¤âµ¤¤òÉÕ¤±¤Æ¤¯¤À¤µ¤¤¡£ ¤â¤· NDS ¥Ç¥£¥ì¥¯¥È¥ê¥¨¡¼¥¸¥§¥ó¥È¤¬¤¢¤ê¡¢¤½¤Î¥¢¥É¥ì¥¹¤òÀßÄꤹ¤ëɬÍפ¬ ¤¢¤ë¾ì¹ç¤Ï¡¢ slp-directory-agent ¥ª¥×¥·¥ç¥ó¤¬ÍøÍѤǤ­¤ë¤Ï¤º¤Ç¤¹¡£ .RE .PP .B option slp-service-scope \fIboolean text\fR\fB;\fR .RS 0.25i .PP .\" metal ¥µ¡¼¥Ó¥¹¥í¥±¡¼¥·¥ç¥ó¥×¥í¥È¥³¥ë¤Î¥µ¡¼¥Ó¥¹¥¹¥³¡¼¥×¥ª¥×¥·¥ç¥ó¤Ï¡¢ 2 ¤Ä¤Î¹àÌܤò»ØÄꤷ¤Þ¤¹: SLP ÍѤΥµ¡¼¥Ó¥¹¥¹¥³¡¼¥×¤Î¥ê¥¹¥È¤È¡¢¤³¤Î¥ê¥¹¥È¤Î»ÈÍѤ¬¶¯À©Åª¤«¤É¤¦¤«¤Ç¤¹¡£ ºÇ½é¤Î¥Ö¡¼¥ëÃͤ¬ true ¤Ç¤¢¤ì¤Ð¡¢SLP ¥¨¡¼¥¸¥§¥ó¥È¤Ï¡¢ËÜ¥ª¥×¥·¥ç¥ó¤Ë¤è¤ê Ä󶡤µ¤ì¤ë¥¹¥³¡¼¥×¤Î¥ê¥¹¥È¤Î¤ß¤ò»ÈÍѤ¹¤Ù¤­¤Ç¤¹¡£ ¤½¤¦¤Ç¤Ê¤±¤ì¤Ð¡¢¤³¤Î¥ª¥×¥·¥ç¥ó¤ÇÄ󶡤µ¤ì¤ë¥ê¥¹¥È¤ËÍ¥À褷¤Æ¡¢ ¤½¤ì¤¾¤ì¤Î¸ÇͭŪ¤ÎÀßÄê¤ò»È¤Ã¤Æ¤â¹½¤¤¤Þ¤»¤ó¡£ .PP text ʸ»úÎó¤Ï¡¢SLP ¥¨¡¼¥¸¥§¥ó¥È¤¬»ÈÍѤ¹¤Ù¤­¥¹¥³¡¼¥×¤Î¡¢¥³¥ó¥Þ¶èÀÚ¤ê¤Î ¥ê¥¹¥È¤È¤·¤Æ¤¯¤À¤µ¤¤¡£ ¤³¤ì¤Ï¾Êά²Äǽ¤Ç¡¢¤½¤Î¾ì¹ç SLP ¥¨¡¼¥¸¥§¥ó¥È¤Ï¡¢¼«Ê¬¤¬ÃΤäƤ¤¤ë ¤¹¤Ù¤Æ¤Î¥Ç¥£¥ì¥¯¥È¥ê¥¨¡¼¥¸¥§¥ó¥È¤Î¥¹¥³¡¼¥×¤Î°ì³ç¥ê¥¹¥È¤ò»È¤¤¤Þ¤¹¡£ .RE .PP .B option \fBsmtp-server\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP SMTP ¥µ¡¼¥Ð¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê SMTP ¥µ¡¼¥Ð¤Î¥ê¥¹¥È¤ò »ØÄꤷ¤Þ¤¹¡£ ¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£ .RE .PP .nf .B option \fBstatic-routes\fR \fIip-address ip-address\fR [\fB,\fR \fIip-address ip-address\fR...]\fB;\fR .fi .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬·ÐÏ©¥­¥ã¥Ã¥·¥å¤ËÁȤ߹þ¤à¤Ù¤­ ÀÅŪ·ÐÏ©¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£ Ʊ¤¸°¸Àè¤ËÂФ·¤ÆÊ£¿ô¤Î·ÐÏ©¤¬»ØÄꤵ¤ì¤Æ¤¤¤ë¾ì¹ç¤Ï¡¢ Í¥ÀèÅÙ¤¬Ä㤯¤Ê¤ë½ç½ø¤Ç¥ê¥¹¥È¤µ¤ì¤Þ¤¹¡£ .PP ·ÐÏ©¤Ï IP ¥¢¥É¥ì¥¹¤ÎÁȤΥꥹ¥È¤«¤é¤Ê¤ê¤Þ¤¹¡£ ºÇ½é¤Î¥¢¥É¥ì¥¹¤Ï°¸À襢¥É¥ì¥¹¤Ç¤¢¤ê¡¢ 2 ÈÖÌܤΥ¢¥É¥ì¥¹¤Ï¤½¤Î°¸Àè¤ËÂФ¹¤ë¥ë¡¼¥¿¤Î¥¢¥É¥ì¥¹¤Ç¤¹¡£ .PP ¥Ç¥Õ¥©¥ë¥È·ÐÏ© (0.0.0.0) ¤Ï¡¢ÀÅŪ·ÐÏ©¤ËÂФ·¤Æ¤ÏÉÔÀµ¤Ê°¸Àè¤Ç¤¹¡£ ¥Ç¥Õ¥©¥ë¥È·ÐÏ©¤ò»ØÄꤹ¤ë¤Ë¤Ï¡¢ .B routers ¥ª¥×¥·¥ç¥ó¤ò»ÈÍѤ·¤Æ¤¯¤À¤µ¤¤¡£ ¤Þ¤¿¡¢ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¹¥ì¥¹¤Ê IP ·ÐÏ©À©¸æ¤ò°Õ¿Þ¤·¤¿¤â¤Î¤Ç¤Ï ¤Ê¤¤¤³¤È¤ËÃí°Õ¤·¤Æ²¼¤µ¤¤¡£ ¤³¤ì¤Ï¥µ¥Ö¥Í¥Ã¥È¥Þ¥¹¥¯¤ò´Þ¤ó¤Ç¤¤¤Þ¤»¤ó¡£ ¸½ºß¡¢¥¯¥é¥¹¥ì¥¹¤Ê IP ·ÐÏ©À©¸æ¤Ï¡¢¤â¤Ã¤È¤â¹­¤¯Å¸³«¤µ¤ì¤Æ¤¤¤ë ·ÐÏ©À©¸æÉ¸½à¤Ê¤Î¤Ç¡¢ËÜ¥ª¥×¥·¥ç¥ó¤Ï¼Â¼ÁŪ¤Ë̵°ÕÌ£¤Ç¤¹¡£ ¤½¤·¤Æ¡¢¥Þ¥¤¥¯¥í¥½¥Õ¥È DHCP ¥¯¥é¥¤¥¢¥ó¥È¤ò¤Ï¤¸¤á¤È¤¹¤ë¤è¤¯ÃΤé¤ì¤¿ DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ë¤Ï¼ÂÁõ¤µ¤ì¤Æ¤¤¤Þ¤»¤ó¡£ .RE .PP .nf .B option \fBstreettalk-directory-assistance-server\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...]\fB;\fR .fi .RS 0.25i .PP StreetTalk Directory Assistance (STDA) ¥µ¡¼¥Ð¥ª¥×¥·¥ç¥ó¤Ï¡¢ ¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê STDA ¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£ ¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£ .RE .PP .B option \fBstreettalk-server\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP StreetTalk ¥µ¡¼¥Ð¥ª¥×¥·¥ç¥ó¤Ï¡¢ ¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê StreetTalk ¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£ ¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£ .RE .PP .B option subnet-mask \fIip-address\fR\fB;\fR .RS 0.25i .PP ¥µ¥Ö¥Í¥Ã¥È¥Þ¥¹¥¯¥ª¥×¥·¥ç¥ó¤Ï¡¢RFC 950 ¤Ë½¾¤Ã¤Æ¡¢ ¥¯¥é¥¤¥¢¥ó¥È¤Î¥µ¥Ö¥Í¥Ã¥È¥Þ¥¹¥¯¤ò»ØÄꤷ¤Þ¤¹¡£ ¥¹¥³¡¼¥×Ãæ¤Î¤É¤³¤Ë¤â¥µ¥Ö¥Í¥Ã¥È¥Þ¥¹¥¯¥ª¥×¥·¥ç¥ó¤¬»ØÄꤵ¤ì¤Æ¤¤¤Ê¤¤¾ì¹ç¡¢ ºÇ½ª¼êÃʤȤ·¤Æ¡¢dhcpd ¤Ï¡¢¥¢¥É¥ì¥¹¤ò³ä¤êÅö¤Æ¤è¤¦¤È¤·¤Æ¤¤¤ë ¥Í¥Ã¥È¥ï¡¼¥¯¤Î¥µ¥Ö¥Í¥Ã¥ÈÀë¸À¤«¤é¡¢¥µ¥Ö¥Í¥Ã¥È¥Þ¥¹¥¯¤ò»ÈÍѤ·¤Þ¤¹¡£ ¤·¤«¤·¡¢¥¢¥É¥ì¥¹¤ò³ä¤êÅö¤Æ¤è¤¦¤È¤·¤Æ¤¤¤ë¥Í¥Ã¥È¥ï¡¼¥¯¤Î¥¹¥³¡¼¥×Ãæ¤Î .I ¤É¤Î¤è¤¦¤Ê ¥µ¥Ö¥Í¥Ã¥È¥Þ¥¹¥¯¥ª¥×¥·¥ç¥óÀë¸À¤Ç¤¢¤Ã¤Æ¤â¡¢ ¥µ¥Ö¥Í¥Ã¥ÈÀë¸À¤Ç»ØÄꤵ¤ì¤¿¥µ¥Ö¥Í¥Ã¥È¥Þ¥¹¥¯¤ËÍ¥À褷¤Þ¤¹¡£ .RE .PP .B option \fBsubnet-selection\fR \fIstring\fR\fB;\fR .RS 0.25i .PP .\" metal (Í׵᤬½Ð¤µ¤ì¤¿¥µ¥Ö¥Í¥Ã¥È¤Ë¤Ä¤Ê¤¬¤ì¤¿¥ê¥ì¡¼¥µ¡¼¥Ð¤Î¥¢¥É¥ì¥¹¤Ë´ð¤Å¤¤¤Æ) Ä̾ïÁªÂò¤µ¤ì¤ë¤Ç¤¢¤í¤¦¤â¤Î¤Ç¤Ï¤Ê¤¤¥µ¥Ö¥Í¥Ã¥È¤Î¥¢¥É¥ì¥¹¤¬É¬Íפʾì¹ç¡¢ ¥¯¥é¥¤¥¢¥ó¥È¤¬Á÷½Ð¤·¤Þ¤¹¡£ RFC3011 ¤ò»²¾È¤·¤Æ¤¯¤À¤µ¤¤¡£ ¤³¤Î¥µ¡¼¥Ð¤Ë¤ª¤¤¤Æ»È¤ï¤ì¤ë¥ª¥×¥·¥ç¥ó¥Ê¥ó¥Ð¤Ï 118 ¤Ç¤¹¡£ ¤³¤Î¥Ê¥ó¥Ð¤Ï°ÊÁ°¤«¤é¤º¤Ã¤ÈÄêµÁ¤µ¤ì¤Æ¤¤¤¿¥Ê¥ó¥Ð¤Ç¤Ï¤Ê¤¯¡¢ °ã¤¦Ãͤò»ÈÍѤ¹¤ë¥¯¥é¥¤¥¢¥ó¥È¤â¸ºß¤¹¤ë¤³¤È¤ËÃí°Õ¤·¤Æ¤¯¤À¤µ¤¤¡£ ¤³¤Î¥ª¥×¥·¥ç¥ó¤Î»ÈÍѤϾ¯¡¹¼Â¸³Åª¤Ç¤¢¤ë¤È¹Í¤¨¤ë¤Ù¤­¤Ç¤·¤ç¤¦! .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥µ¡¼¥Ð¤Ç¤Ï¥æ¡¼¥¶¤¬ÀßÄꤹ¤ë¤³¤È¤Ï¤Ç¤­¤Þ¤»¤ó¡£ .PP .RE .PP .B option \fBswap-server\fR \fIip-address\fR\fB;\fR .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤Î¥¹¥ï¥Ã¥×¥µ¡¼¥Ð¤Î IP ¥¢¥É¥ì¥¹¤ò»ØÄꤷ¤Þ¤¹¡£ .RE .PP .B option \fBtcp-keepalive-garbage\fR \fIflag\fR\fB;\fR .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¸Å¤¤¼ÂÁõ¤È¤Î¸ß´¹À­¤Î¤¿¤á¤Ë¡¢¥´¥ß¤Î¥ª¥¯¥Æ¥Ã¥È¤È°ì½ï¤Ë¡¢ TCP ¥­¡¼¥×¥¢¥é¥¤¥Ö¥á¥Ã¥»¡¼¥¸¤ò¥¯¥é¥¤¥¢¥ó¥È¤¬Á÷¤ë¤Ù¤­¤«¤ò»ØÄꤷ¤Þ¤¹¡£ ÃÍ false ¤Ï¡¢¥´¥ß¤Î¥ª¥¯¥Æ¥Ã¥È¤òÁ÷¤ë¤Ù¤­¤Ç¤Ê¤¤¤³¤È¤ò°ÕÌ£¤·¤Þ¤¹¡£ ÃÍ true ¤Ï¡¢¥´¥ß¤Î¥ª¥¯¥Æ¥Ã¥È¤òÁ÷¤ë¤Ù¤­¤Ç¤¢¤ë¤³¤È¤ò°ÕÌ£¤·¤Þ¤¹¡£ .RE .PP .B option \fBtcp-keepalive-interval\fR \fIuint32\fR\fB;\fR .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤Î TCP ¤¬¥­¡¼¥×¥¢¥é¥¤¥Ö (keepalive) ¥á¥Ã¥»¡¼¥¸¤ò TCP Àܳ¾å¤ËÁ÷¿®¤¹¤ëÁ°¤ËÂԤĤ٤­´Ö³Ö (ÉÃñ°Ì) ¤ò»ØÄꤷ¤Þ¤¹¡£ »þ´Ö¤Ï 32 ¥Ó¥Ã¥ÈÉä¹æÌµ¤·À°¿ô¤Ç»ØÄꤷ¤Þ¤¹¡£ ÃÍ 0 ¤Ï¡¢¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¤¬ÌÀ¼¨Åª¤ËÍ׵ᤷ¤Ê¤¤¸Â¤ê¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ Àܳ¾å¤Ë¥­¡¼¥×¥¢¥é¥¤¥Ö¥á¥Ã¥»¡¼¥¸¤òÀ¸À®¤¹¤Ù¤­¤Ç¤Ê¤¤¤³¤È¤ò°ÕÌ£¤·¤Þ¤¹¡£ .RE .PP .B option \fBtftp-server-name\fR \fItext\fR\fB;\fR .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï TFTP ¥µ¡¼¥Ð¤ò»ØÄꤹ¤ë¤Î¤Ë»ÈÍѤµ¤ì¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ ¥µ¥Ý¡¼¥È¤·¤Æ¤¤¤ë¾ì¹ç¤Ë¤Ï \fBserver-name\fR Àë¸À¤ÈƱ¤¸¸ú²Ì¤ò»ý¤Á¤Þ¤¹¡£ BOOTP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢ËÜ¥ª¥×¥·¥ç¥ó¤ò¥µ¥Ý¡¼¥È¤·¤Ê¤¤¤Ç¤·¤ç¤¦¡£ DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ë¤è¤Ã¤Æ¤Ï¥µ¥Ý¡¼¥È¤·¤Æ¤¤¤ë¤â¤Î¤¬¤¢¤ê¡¢ ¼ÂºÝɬ¿Ü¤È¤·¤Æ¤¤¤ë¤â¤Î¤¬¤¢¤ê¤Þ¤¹¡£ .RE .PP .B option time-offset \fIint32\fR\fB;\fR .RS 0.25i .PP time-offset ¥ª¥×¥·¥ç¥ó¤Ï¡¢¶¨ÄêÀ¤³¦»þ (UTC) ¤ò´ðÅÀ¤È¤·¤Æ¡¢ ¥¯¥é¥¤¥¢¥ó¥È¤Î¥µ¥Ö¥Í¥Ã¥È¤Î¥ª¥Õ¥»¥Ã¥È¤òÉäǻØÄꤷ¤Þ¤¹¡£ .RE .PP .B option time-servers \fIip-address\fR [, \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP time-server ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê RFC 868 »þ¹ï¥µ¡¼¥Ð¤Î ¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£ ¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£ .RE .PP .B option \fBtrailer-encapsulation\fR \fIflag\fR\fB;\fR .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢ARP ¥×¥í¥È¥³¥ë»ÈÍÑ»þ¤Ë¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬¥È¥ì¥¤¥é»ÈÍѤΠ¥Í¥´¥·¥¨¡¼¥·¥ç¥ó (RFC 893 [14]) ¤ò¤¹¤Ù¤­¤«¤ò»ØÄꤷ¤Þ¤¹¡£ ÃÍ false ¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬¥È¥ì¥¤¥é»ÈÍѤò»î¤ß¤ë¤Ù¤­¤Ç¤Ê¤¤¤È°ÕÌ£¤·¤Þ¤¹¡£ ÃÍ true ¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬¥È¥ì¥¤¥é»ÈÍѤò»î¤ß¤ë¤Ù¤­¤Ç¤¢¤ë¤È°ÕÌ£¤·¤Þ¤¹¡£ .RE .PP .B option \fBuap-servers\fR \fItext\fR\fB;\fR .RS 0.25i .PP .\" metal ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥æ¡¼¥¶Ç§¾Ú¥×¥í¥È¥³¥ë (UAP) ¤ËÊñ¤Þ¤ì¤¿Ç§¾ÚÍ×µá¤ò ½èÍý¤¹¤ëǽÎϤΤ¢¤ë¥æ¡¼¥¶Ç§¾Ú¥µ¡¼¥Ó¥¹¤ò¤½¤ì¤¾¤ì»Ø¤·¤Æ¤¤¤ë URL ¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£ UAP ¥µ¡¼¥Ð¤Ï HTTP 1.1 Àܳ¤â SSLv3 Àܳ¤â¼õ¤±¼è¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£ ¥ê¥¹¥È¤Ë´Þ¤Þ¤ì¤¿ URL ¤Ë¥Ý¡¼¥ÈÉôʬ¤¬´Þ¤Þ¤ì¤Æ¤Ê¤¤¾ì¹ç¤Ï¡¢ Ä̾ï¤Î¥Ç¥Õ¥©¥ë¥È¥Ý¡¼¥È¤¬²¾Äꤵ¤ì¤Þ¤¹ (¤Ä¤Þ¤ê http ¤Ë¤Ï 80 ÈÖ¡¢https ¤Ë¤Ï 443 ÈÖ)¡£ ¥ê¥¹¥È¤Ë´Þ¤Þ¤ì¤¿ URL ¤Ë¥Ñ¥¹¤ÎÉôʬ¤¬´Þ¤Þ¤ì¤Æ¤Ê¤¤¾ì¹ç¤Ï¡¢ ¥Ñ¥¹¤Ï /uap ¤È²¾Äꤵ¤ì¤Þ¤¹¡£ 2 ¤Ä°Ê¾å¤Î URL ¤¬¤³¤Î¥ê¥¹¥È¤Ë»ØÄꤵ¤ì¤¿¾ì¹ç¡¢URL ¤Ï¶õÇò¤Ç¶èÀÚ¤é¤ì¤Þ¤¹¡£ .RE .PP .B option \fBuser-class\fR \fIstring\fR\fB;\fR .RS 0.25i .PP .\" metal ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥æ¡¼¥¶¤¬¼±Ê̾ðÊó¤ò¥¯¥é¥¤¥¢¥ó¥È¤Ë»ØÄꤹ¤ë¼êÃʤȤ·¤Æ¡¢ ¤¤¤¯¤Ä¤«¤Î DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ç»È¤ï¤ì¤Þ¤¹¡£ ¤³¤ì¤Ï vendor-class-identifier ¥ª¥×¥·¥ç¥ó¤ÈƱÍͤ˻Ȥï¤ì¤Þ¤¹¤¬¡¢ ¤½¤ÎÃͤϡ¢¥Ù¥ó¥À¤Ç¤Ï¤Ê¤¯¡¢¥æ¡¼¥¶¤Ë¤è¤Ã¤Æ»ØÄꤵ¤ì¤Þ¤¹¡£ ºÇ¶á¤Î¤Û¤È¤ó¤É¤Î DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢¤³¤Î¼±Ê̻ҤËÃͤò»ØÄꤹ¤ë¤¿¤á¤Î ¥æ¡¼¥¶¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤òÈ÷¤¨¤Æ¤¤¤Þ¤¹¡£ ¤³¤Î¼±Ê̻Ҥϡ¢ÄÌ¾ï¥Æ¥­¥¹¥Èʸ»úÎó¤Ç¤¹¡£ .RE .PP .B option \fBvendor-class-identifier\fR \fIstring\fR\fB;\fR .RS 0.25i .PP .\" metal ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥Ù¥ó¥À¥¿¥¤¥×¤ä¡¢²Äǽ¤Ç¤¢¤ì¤Ð DHCP ¥¯¥é¥¤¥¢¥ó¥È¤ÎÀßÄê¤ò ¼±Ê̤¹¤ë¤¿¤á¤Ë¡¢¤¤¤¯¤Ä¤«¤Î DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ç»È¤ï¤ì¤Þ¤¹¡£ ¤³¤Î¾ðÊó¤ÎÆâÍÆ¤Ï¡¢¥Ù¥ó¥À¸ÇÍ­¤Î¥Ð¥¤¥Èʸ»úÎó¤Ç¡¢É¸½à¤Ç¤Ïµ¬Äꤵ¤ì¤Æ¤¤¤Þ¤»¤ó¡£ ¥¯¥é¥¤¥¢¥ó¥È¤¬Á÷½Ð¤¹¤ë¥Ù¥ó¥À¥¯¥é¥¹¼±Ê̻Ҥò³Îǧ¤¹¤ë¤Ë¤Ï¡¢ °Ê²¼¤ÎÀßÄê¤ò DHCP ¥µ¡¼¥ÐÀßÄê¥Õ¥¡¥¤¥ë¤Ë²Ã¤¨¤Æ¤¯¤À¤µ¤¤: .nf .PP set vendor-class option vendor-class-identifier; .fi .PP ¤³¤ÎÀßÄê¤Ï¡¢DHCP ¥µ¡¼¥Ð¤Î¥ê¡¼¥¹¥Ç¡¼¥¿¥Ù¡¼¥¹¥Õ¥¡¥¤¥ëÃæ¤Î¡¢ °Ê²¼¤Î¤è¤¦¤Ê set ʸ¤ò»ý¤Ä vendor-class-identifier ¥ª¥×¥·¥ç¥ó¤ò Á÷¤Ã¤Æ¤¯¤ë¥¯¥é¥¤¥¢¥ó¥È¤¹¤Ù¤Æ¤Î¥¨¥ó¥È¥ê¤ËºîÍѤ·¤Þ¤¹¡£ .nf .PP set vendor-class "SUNW.Ultra-5_10"; .fi .PP vendor-class-identifier ¥ª¥×¥·¥ç¥ó¤Ï¡¢Ä̾ï DHCP Server ¤Ë¤è¤Ã¤Æ¡¢ .B vendor-encapsulated-options ¥ª¥×¥·¥ç¥óÃæ¤ÇÊÖ¤µ¤ì¤ë¥ª¥×¥·¥ç¥ó¤ò·èÄꤹ¤ë¤Î¤Ë»È¤ï¤ì¤Þ¤¹¡£ ¤µ¤é¤Ê¤ë¾ðÊó¤Ï¡¢dhcpd.conf ¥Þ¥Ë¥å¥¢¥ë¥Ú¡¼¥¸¤Î VENDOR ENCAPSULATED OPTIONS ¤Î ¾Ï¤ò»²¾È¤·¤Æ¤¯¤À¤µ¤¤¡£ .RE .PP .B option \fBvendor-encapsulated-options\fR \fIstring\fR\fB;\fR .RS 0.25i .PP .\" metal \fBvendor-encapsulated-options\fR ¥ª¥×¥·¥ç¥ó¤Ï¡¢1 ¤Ä¤Î¥Ù¥ó¥À¸ÇÍ­ÃÍ¡¢ ¤â¤·¤¯¤Ï 1 ¤Ä¤Þ¤¿¤Ï¤½¤ì°Ê¾å¤Î¥Ù¥ó¥À¸ÇÍ­¥µ¥Ö¥ª¥×¥·¥ç¥ó¤ò´Þ¤ß¤Þ¤¹¡£ ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢Ä̾ï DHCP ¥µ¡¼¥Ð¤ÎÀßÄê¥Õ¥¡¥¤¥ë¤ÇÀßÄꤵ¤ì¤ë¤â¤Î¤Ç¤Ï ¤¢¤ê¤Þ¤»¤ó¡£ Ä̾ï¤Ï¡¢¥Ù¥ó¥À¥¯¥é¥¹¤¬¥Ù¥ó¥ÀËè¤ËÄêµÁ¤µ¤ì¡¢ ¥Ù¥ó¥À¥¯¥é¥¹¥µ¥Ö¥ª¥×¥·¥ç¥ó¤¬ÄêµÁ¤µ¤ì¡¢¤½¤Î¥µ¥Ö¥ª¥×¥·¥ç¥ó¤ÎÃͤ¬ ÄêµÁ¤µ¤ì¡¢DHCP ¥µ¡¼¥Ð¤Ï¤½¤ì¤é¤ò¤â¤È¤Ë±þÅú¤òÁȤ߾夲¤Þ¤¹¡£ .PP ¤è¤¯ÃΤé¤ì¤¿ DHCP ¥¯¥é¥¤¥¢¥ó¥È¥Ù¥ó¥À (º£¤Î¤È¤³¤í Microsoft Windows 2000 DHCP ¥¯¥é¥¤¥¢¥ó¥È) ¸þ¤±¤Î¤¤¤¯¤Ä¤«¤Î¥Ç¥Õ¥©¥ë¥È¤Îưºî¤Ç¤Ï¡¢ ¤³¤Î¥ª¥×¥·¥ç¥ó¤Ï¼«Æ°Åª¤ËÀßÄꤵ¤ì¤Þ¤¹¤¬¡¢¤½¤Î¾¤Î¤â¤Î¤Ë´Ø¤·¤Æ¤Ï¡¢ ¼êư¤ÇÀßÄꤷ¤Ê¤±¤ì¤Ð¤Ê¤ê¤Þ¤»¤ó¡£ ¾ÜºÙ¤Ï \fIdhcpd.conf\fI ¤Î VENDOR ENCAPSULATED OPTIONS ¤Î¾Ï¤ò »²¾È¤·¤Æ¤¯¤À¤µ¤¤¡£ .RE .PP .B option \fBwww-server\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP WWW ¥µ¡¼¥Ð¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê WWW ¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£ ¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£ .RE .PP .B option \fBx-display-manager\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê X Window System ¥Ç¥£¥¹¥×¥ì¥¤¥Þ¥Í¡¼¥¸¥ã¤ò¼Â¹Ô¤·¤Æ¤¤¤ë¥·¥¹¥Æ¥à¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£ ¥¢¥É¥ì¥¹¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£ .RE .SH ¥ê¥ì¡¼¥¨¡¼¥¸¥§¥ó¥È¾ðÊ󥪥ץ·¥ç¥ó .\" metal IETF ¥É¥é¥Õ¥È draft-ietf-dhc-agent-options-11.txt ¤Ë¤Ï¡¢ DHCP ¥ê¥ì¡¼¥¨¡¼¥¸¥§¥ó¥È¤¬ DHCP ¥Ñ¥±¥Ã¥È¤ò DHCP ¥µ¡¼¥Ð¤ËžÁ÷¤¹¤ëºÝ¡¢ DHCP ¥Ñ¥±¥Ã¥È¤ËÉղ乤뤳¤È¤Î¤Ç¤­¤ë°ìÏ¢¤Î¥«¥×¥»¥ë²½¤µ¤ì¤¿¥ª¥×¥·¥ç¥ó¤¬ ÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤¹¡£ ¥µ¡¼¥Ð¤Ï¡¢¤³¤ì¤é¤Î¥ª¥×¥·¥ç¥ó¤Ë´ð¤Å¤­¡¢¥¢¥É¥ì¥¹³äÅö¤Î·èÄê (¤ä¡¢¤½¤Î¾¤ÎȽÃÇ) ¤ò¹Ô¤¦¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£ ¤Þ¤¿¥µ¡¼¥Ð¤Ï¡¢¥ê¥ì¡¼¥¨¡¼¥¸¥§¥ó¥È¤òÄ̤·¤ÆÊÖ¤µ¤ì¤ë¤É¤Î¥Ñ¥±¥Ã¥È¤Ë¤â ¤³¤ì¤é¤Î¥ª¥×¥·¥ç¥ó¤òÆþ¤ì¤ÆÊÖ¤·¤Þ¤¹¡£ ¤³¤ì¤Ë¤è¤Ã¤Æ¥ê¥ì¡¼¥¨¡¼¥¸¥§¥ó¥È¤Ï¡¢ÇÛÁ÷¤ä¥¢¥«¥¦¥ó¥Æ¥£¥ó¥°¤Ê¤É¤ò ¹Ô¤¦¤¿¤á¤Ë¡¢¤³¤ì¤é¤Î¥ª¥×¥·¥ç¥ó¤Ë´Þ¤Þ¤ì¤ë¾ðÊó¤òÍøÍѤǤ­¤Þ¤¹¡£ .PP ¸½ºß¤Î¥É¥é¥Õ¥È¤Ë¤Ï 2 ¤Ä¤Î¥ª¥×¥·¥ç¥ó¤¬ÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤¹¡£ DHCP ¥µ¡¼¥Ð¤Ç¤³¤ì¤é¤Î¥ª¥×¥·¥ç¥ó¤ò»²¾È¤¹¤ë¤Ë¤Ï¡¢¥ª¥×¥·¥ç¥ó¶õ´Ö̾ "agent" ¤Î¤¢¤È¤Ë¥Ô¥ê¥ª¥É¤ò¤Ä¤±¡¢¤½¤Î¸å¤Ë¥ª¥×¥·¥ç¥ó̾¤ò³¤±¤Æ¤¯¤À¤µ¤¤¡£ ¥µ¡¼¥Ð¤Ç¤³¤ì¤é¤Î¥ª¥×¥·¥ç¥ó¤ÎÃͤòÄêµÁ¤¹¤ë¤³¤È¤Ï¡¢ Ä̾濫¤Þ¤êÍ­¸ú¤Ç¤Ï¤¢¤ê¤Þ¤»¤ó¤¬¡¢µöÍÆ¤µ¤ì¤Æ¤¤¤Þ¤¹¡£ ¤³¤ì¤é¤Î¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤Ç¤Ï¥µ¥Ý¡¼¥È¤µ¤ì¤Æ¤¤¤Þ¤»¤ó¡£ .PP .B option \fBagent.circuit-id\fR \fIstring\fR\fB;\fR .RS 0.25i .PP circuit-id ¥µ¥Ö¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤«¤é¥µ¡¼¥Ð¤Ø¤Î DHCP ¥Ñ¥±¥Ã¥È¤ò ¼õ¤±¼è¤Ã¤¿¥µ¡¼¥­¥Ã¥È¤ò¼¨¤¹¡¢¥¨¡¼¥¸¥§¥ó¥È¥í¡¼¥«¥ë¤Ê¥µ¡¼¥­¥Ã¥È¼±Ê̻Ҥò ¥¨¥ó¥³¡¼¥É¤·¤Æ¤¤¤Þ¤¹¡£ ¤³¤ì¤Ï¡¢DHCP ±þÅú¤òŬÀڤʥµ¡¼¥­¥Ã¥È¤Ø¤ÈÁ÷¤êÊÖ¤»¤ë¤è¤¦¡¢ ¥¨¡¼¥¸¥§¥ó¥È¤Ë¤è¤Ã¤Æ»È¤ï¤ì¤ë¤³¤È¤ò°Õ¿Þ¤·¤Æ¤¤¤Þ¤¹¡£ ¸½ºß¡¢¤³¤Î¥ª¥×¥·¥ç¥ó¤Î½ñ¼°¤ÎÄêµÁ¤Ï¥Ù¥ó¥À°Í¸¤È¤Ê¤Ã¤Æ¤ª¤ê¡¢ ¿ʬ¤³¤Î¤Þ¤Þ»Ä¤µ¤ì¤ë¤Ç¤·¤ç¤¦¡£ ¤·¤«¤·¾­Í褳¤Î½ñ¼°¤¬É¸½à²½¤µ¤ì¤ë²ÄǽÀ­¤â¡¢¸½ºß¤Î¥É¥é¥Õ¥È¤Ë¤Ï»Ä¤µ¤ì¤Æ¤¤¤Þ¤¹¡£ .RE .PP .B option \fBagent.remote-id\fR \fIstring\fR\fB;\fR .RS 0.25i .PP remote-id ¥µ¥Ö¥ª¥×¥·¥ç¥ó¤Ï¡¢¥µ¡¼¥­¥Ã¥È¤Î½ªÃ¼¤Î¥ê¥â¡¼¥È¥Û¥¹¥È¤Î ¾ðÊó¤ò¥¨¥ó¥³¡¼¥É¤·¤Æ¤¤¤Þ¤¹¡£ ¤³¤ì¤Ë´Þ¤Þ¤ì¤ë¤Ç¤¢¤í¤¦¾ðÊó¤Ï¡¢¼¡¤Î¤è¤¦¤Ê¤â¤Î¤Ç¤¹¡£ ¸Æ½Ð¸µ ID ¾ðÊ󡢥桼¥¶Ì¾¾ðÊó¡¢¥ê¥â¡¼¥È ATM ¥¢¥É¥ì¥¹¡¢¥±¡¼¥Ö¥ë¥â¥Ç¥à ID¡¢ ¤½¤Î¾¤ÎƱÍͤʾðÊó¡£ ¸¶Â§Åª¤Ë¤Ï¡¢¤³¤Î¥ª¥×¥·¥ç¥ó¤Î°ÕÌ£¤Ï¤Á¤ã¤ó¤ÈÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤»¤ó¡£ ¤·¤«¤·Ä̾¥µ¡¼¥­¥Ã¥È¤ÎÆÃÄê¤Î¥ê¥â¡¼¥È¥¨¥ó¥É¤ËÂФ·¤Æ°ì°Õ¤Ç¤¢¤ë¤è¤¦ ´ÉÍý¾åÊݾڤµ¤ì¤¿¡¢¤Ê¤ó¤é¤«¤Î¥ª¥Ö¥¸¥§¥¯¥È¤È¹Í¤¨¤ë¤Ù¤­¤â¤Î¤Ç¤¹¡£ .RE .SH ¥¯¥é¥¤¥¢¥ó¥È FQDN ¥µ¥Ö¥ª¥×¥·¥ç¥ó .\" metal ¸½ºß¡¢¥¤¥ó¥¿¡¼¥Í¥Ã¥È¥É¥é¥Õ¥È draft-ietf-dhc-fqdn-option-00.txt ¤Ç ÄêµÁ¤µ¤ì¤Æ¤¤¤ë¥¯¥é¥¤¥¢¥ó¥È FQDN ¥ª¥×¥·¥ç¥ó¤Ï¡¢¤Þ¤Àɸ½à¤È¤Ê¤Ã¤Æ¤Ï¤¤¤Þ¤»¤ó¡£ ¤·¤«¤·¤¹¤Ç¤Ë½½Ê¬¹­¤¯ÍøÍѤµ¤ì¤Æ¤ª¤ê¡¢²æ¡¹¤â¤³¤ì¤ò¼ÂÁõ¤·¤Æ¤¤¤Þ¤¹¡£ ¥ª¥×¥·¥ç¥ó¤Î½ñ¼°¤¬Ê£»¨¤Ê¤¿¤á¡¢¤³¤³¤Ç¤Ï¡¢Ã±ÆÈ¤Î¥ª¥×¥·¥ç¥ó¤Ç¤Ï¤Ê¤¯¡¢ ¥µ¥Ö¥ª¥×¥·¥ç¥ó¶õ´Ö¤Ë¼ÂÁõ¤·¤Æ¤¤¤Þ¤¹¡£ °ìÈÌŪ¤Ë¤Ï¡¢ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥æ¡¼¥¶¤Ë¤è¤Ã¤ÆÀßÄꤵ¤ì¤ë¤â¤Î¤Ç¤Ï¤Ê¤¯¡¢ ¼«Æ° DNS ¹¹¿·¥·¥¹¥Æ¥à¤Î°ìÉô¤È¤·¤Æ»È¤ï¤ì¤ë¤Ù¤­¤â¤Î¤Ç¤¹¡£ .PP .B option fqdn.no-client-update \fIflag\fB; .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤¬¥¯¥é¥¤¥¢¥ó¥È¤«¤éÁ÷½Ð¤µ¤ì¤¿¾ì¹ç¡¢¤³¤ì¤¬ true ¤Ç¤¢¤ì¤Ð¡¢ ¥¯¥é¥¤¥¢¥ó¥È¤Ï¼«Ê¬¤Î A ¥ì¥³¡¼¥É¤ò¹¹¿·¤·¤Ê¤¤¤³¤È¤ò°ÕÌ£¤·¤Þ¤¹¡£ ¥µ¡¼¥Ð¤«¤é¥¯¥é¥¤¥¢¥ó¥È¤ËÁ÷½Ð¤µ¤ì¤¿¾ì¹ç¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤Ï ¼«Ê¬¤Î A ¥ì¥³¡¼¥É¤ò¹¹¿·¤¹¤ë \fI¤Ù¤­¤Ç¤Ï¤Ê¤¤\fR ¤³¤È¤ò°ÕÌ£¤·¤Þ¤¹¡£ .RE .PP .B option fqdn.server-update \fIflag\fB; .RS 0.25i .PP ËÜ¥ª¥×¥·¥ç¥ó¤¬¥¯¥é¥¤¥¢¥ó¥È¤«¤é¥µ¡¼¥Ð¤ËÁ÷½Ð¤µ¤ì¤¿¾ì¹ç¡¢ ¥µ¡¼¥Ð¤Ë¥¯¥é¥¤¥¢¥ó¥È¤Î A ¥ì¥³¡¼¥É¤Î¹¹¿·¤òÍ׵ᤷ¤Æ¤¤¤Þ¤¹¡£ ¥µ¡¼¥Ð¤«¤éÁ÷½Ð¤µ¤ì¤¿¾ì¹ç¡¢¥µ¡¼¥Ð¤¬¥¯¥é¥¤¥¢¥ó¥È¤Î A ¥ì¥³¡¼¥É¤ò ¹¹¿·¤·¤¿ (¤â¤·¤¯¤Ï¤â¤¦¤¹¤°¹¹¿·¤¹¤ë¤È¤³¤í) ¤Ç¤¢¤ë¤³¤È¤ò°ÕÌ£¤·¤Þ¤¹¡£ .RE .PP .B option fqdn.encoded \fIflag\fB; .RS 0.25i .PP true ¤Ç¤¢¤Ã¤¿»þ¡¢¥ª¥×¥·¥ç¥ó¤Ë´Þ¤Þ¤ì¤ë¥É¥á¥¤¥ó̾¤¬¡¢ ¤¿¤À¤Î ASCII ¥Æ¥­¥¹¥È¤Ç¤Ï¤Ê¤¯¡¢DNS ¥ï¥¤¥ä¥Õ¥©¡¼¥Þ¥Ã¥È¤Ç ¥¨¥ó¥³¡¼¥É¤µ¤ì¤Æ¤¤¤ë¤³¤È¤ò¼¨¤·¤Æ¤Þ¤¹¡£ ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢¼«Ê¬¤¬ FQDN ¥ª¥×¥·¥ç¥ó¤Î DNS ¥ï¥¤¥ä¥Õ¥©¡¼¥Þ¥Ã¥È¤ò ¥µ¥Ý¡¼¥È¤·¤Æ¤Ê¤¤¾ì¹ç¡¢Ä̾盧¤Î¥µ¥Ö¥ª¥×¥·¥ç¥ó¤ò false ¤ËÀßÄꤷ¤Þ¤¹¡£ ¥µ¡¼¥Ð¤Ï¾ï¤Ë¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÀßÄꤷ¤¿¤Î¤ÈƱ¤¸ÃͤòÀßÄꤷ¤ÆÊÖ¤¹¤Ù¤­¤Ç¤¹¡£ ¤³¤ÎÃͤ¬ÀßÄê¥Õ¥¡¥¤¥ë¤ËÀßÄꤵ¤ì¤Æ¤¤¤¿»þ¤Ï¡¢\fIfqdn.fqdn\fR ¥µ¥Ö¥ª¥×¥·¥ç¥ó¤ò ¥¨¥ó¥³¡¼¥É¤¹¤ë¥Õ¥©¡¼¥Þ¥Ã¥È¤òÀ©¸æ¤·¤Þ¤¹¡£ .RE .PP .B option fqdn.rcode1 \fIflag\fB; .PP .B option fqdn.rcode2 \fIflag\fB; .RS 0.25i .PP ¤³¤ì¤é¤Î¥ª¥×¥·¥ç¥ó¤Ï¤½¤ì¤¾¤ì¡¢A ¥ì¥³¡¼¥É¤È PTR ¥ì¥³¡¼¥É¤Î¹¹¿··ë²Ì¤ò¼¨¤·¤Þ¤¹¡£ ¤³¤ì¤é¤Ï¡¢DHCP ¥µ¡¼¥Ð¤«¤é DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ø¤Î¤ßÁ÷¤é¤ì¤Þ¤¹¡£ ¤³¤ì¤é¤Î¥Õ¥£¡¼¥ë¥É¤ÎÃͤϡ¢DNS ¥×¥í¥È¥³¥ëµ¬³Ê¤Ë¤è¤êÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤¹¡£ .RE .PP .B option fqdn.fqdn \fItext\fB; .RS 0.25i .PP ¥¯¥é¥¤¥¢¥ó¥È¤¬»ÈÍѤò˾¤à¥É¥á¥¤¥ó̾¤ò»ØÄꤷ¤Þ¤¹¡£ ¤³¤ì¤Ï´°Á´½¤¾þ¤µ¤ì¤¿¥É¥á¥¤¥ó̾¤Ç¤â¡¢Ã±°ì¤Î¥é¥Ù¥ë¤Ç¤â¹½¤¤¤Þ¤»¤ó¡£ ¤â¤·Ì¾Á°¤Ë '.' ʸ»ú¤¬´Þ¤Þ¤ì¤Ê¤±¤ì¤Ð¡¢¤½¤Î̾Á°¤Ï´°Á´½¤¾þ¤µ¤ì¤Æ¤ª¤é¤º¡¢ ¥µ¡¼¥Ð¤ÏÄ̾¥í¡¼¥«¥ë¤ËÄêµÁ¤µ¤ì¤¿¥É¥á¥¤¥óÃæ¤Î¤½¤Î̾Á°¤ò¹¹¿·¤·¤Þ¤¹¡£ .RE .PP ¤â¤·¤³¤ì¤é¤Î¥µ¥Ö¥ª¥×¥·¥ç¥ó¤ò»ÈÍѤ·¤è¤¦¤È»×¤Ã¤Æ¤¤¤ë¤Î¤Ç¤¢¤ì¤Ð¡¢ ¥¯¥é¥¤¥¢¥ó¥È FQDN ¥ª¥×¥·¥ç¥ó¤Î¥É¥é¥Õ¥È (¤â¤·¤¯¤Ï¡¢É¸½à¤Ë¤Ê¤Ã¤¿»þ¤Ï¤½¤Îɸ½à) ¤ò»²¾È¤¹¤ë¤³¤È¤ò¶¯¤¯¿ä¾©¤·¤Þ¤¹¡£ ¤³¤Îʸ½ñ¤Ï¡¢¤½¤Î¥É¥é¥Õ¥È¤ËÈæ¤Ù¤ÆÂ绨ÇĤÇÉÔ´°Á´¤Ç¤¢¤ê¡¢ ¥¯¥é¥¤¥¢¥ó¥È FQDN ¥ª¥×¥·¥ç¥óµ¬³Ê¤ò¤¹¤Ç¤ËÍý²ò¤·¤Æ¤¤¤ë¿Í¤Ë»²¾È¤µ¤ì¤ë¤³¤È¤ò ñ¤Ë°Õ¿Þ¤·¤Æ¤¤¤ë¤â¤Î¤Ç¤¹¡£ .SH NetWare/IP ¥µ¥Ö¥ª¥×¥·¥ç¥ó .\" metal RFC2242 ¤Ï¡¢Novell ¤Î NetWare/IP ¥¯¥é¥¤¥¢¥ó¥ÈÍѤΥ«¥×¥»¥ë²½¤µ¤ì¤¿ ¥ª¥×¥·¥ç¥ó¤ÎÁȤòÄêµÁ¤·¤Æ¤¤¤Þ¤¹¡£ DHCP ¥µ¡¼¥Ð¤Ë¤ª¤¤¤Æ¤³¤ì¤é¤Î¥ª¥×¥·¥ç¥ó¤ò»ÈÍѤ¹¤ë¤Ë¤Ï¡¢¥ª¥×¥·¥ç¥ó¶õ´Ö̾ "nwip" ¤Î¸å¤Ë¥Ô¥ê¥ª¥É¤ò¤Ä¤±¡¢¤½¤Î¸å¤Ë¥ª¥×¥·¥ç¥ó̾¤ò³¤±¤Æ¤¯¤À¤µ¤¤¡£ °Ê²¼¤Î¥ª¥×¥·¥ç¥ó¤¬»ØÄê¤Ç¤­¤Þ¤¹: .PP .B option \fBnwip.nsq-broadcast\fR \fIflag\fR\fB;\fR .RS 0.25i .PP true ¤Ç¤¢¤Ã¤¿¾ì¹ç¡¢¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢NetWare/IP ¥µ¡¼¥Ð¤Î°ÌÃÖ¤ò õ¤¹¤Î¤Ë NetWare Nearest Server Query ¤ò»È¤¦¤Ù¤­¤Ç¤¹¡£ ËÜ¥µ¥Ö¥ª¥×¥·¥ç¥ó¤¬ false ¤Ç¤¢¤Ã¤¿¾ì¹ç¡¢¤â¤·¤¯¤Ï»ØÄꤵ¤ì¤Ê¤«¤Ã¤¿¾ì¹ç¤Î Novell ¥¯¥é¥¤¥¢¥ó¥È¤Îưºî¤Ïµ¬Äꤵ¤ì¤Æ¤¤¤Þ¤»¤ó¡£ .PP .RE .B option \fBnwip.preferred-dss\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fR\fB;\fR .RS 0.25i .PP ËÜ¥µ¥Ö¥ª¥×¥·¥ç¥ó¤Ë¤Ï¡¢5 ¤Ä¤Þ¤Ç¤Î IP ¥¢¥É¥ì¥¹¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£ ¤½¤ì¤¾¤ì¤Î¥¢¥É¥ì¥¹¤Ï¡¢NetWare ¥É¥á¥¤¥ó SAP/RIP ¥µ¡¼¥Ð (DSS) ¤Î IP ¥¢¥É¥ì¥¹¤Ç¤¹¡£ .RE .PP .B option \fBnwip.nearest-nwip-server\fR \fI\fIip-address\fR [\fB,\fR \fIip-address\fR...]\fR\fB;\fR .RS 0.25i .PP ËÜ¥µ¥Ö¥ª¥×¥·¥ç¥ó¤Ë¤Ï¡¢5 ¤Ä¤Þ¤Ç¤Î IP ¥¢¥É¥ì¥¹¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£ ¤½¤ì¤¾¤ì¤Î¥¢¥É¥ì¥¹¤Ï¡¢¶áÀܤΠNetWare IP ¥µ¡¼¥Ð (Nearest NetWare IP Server) ¤Î IP ¥¢¥É¥ì¥¹¤Ç¤¹¡£ .RE .PP .B option \fBnwip.autoretries\fR \fIuint8\fR\fB;\fR .RS 0.25i .PP µ¯Æ°»þ¤Ë¡¢NetWare/IP ¥¯¥é¥¤¥¢¥ó¥È¤¬¡¢Í¿¤¨¤é¤ì¤¿ DSS ¥µ¡¼¥Ð¤È ²¿²óÄÌ¿®¤ò»î¤ß¤ë¤Ù¤­¤«¤ò»ØÄꤷ¤Þ¤¹¡£ .RE .PP .B option \fBnwip.autoretry-secs\fR \fIuint8\fR\fB;\fR .RS 0.25i .PP µ¯Æ°»þ¤Ë¡¢NetWare/IP ¥¯¥é¥¤¥¢¥ó¥È¤¬¡¢Í¿¤¨¤é¤ì¤¿ DSS ¥µ¡¼¥Ð¤È ÄÌ¿®¤ò³ÎΩ¤¹¤ë»þ¤Ë¡¢¥ê¥È¥é¥¤¤Î´Ö²¿ÉÃÂԤĤ٤­¤«¤ò»ØÄꤷ¤Þ¤¹¡£ .RE .PP .B option \fBnwip.nwip-1-1\fR \fIuint8\fR\fB;\fR .RS 0.25i .PP true ¤Ç¤¢¤Ã¤¿¾ì¹ç¡¢NetWare/IP ¥¯¥é¥¤¥¢¥ó¥È¤Ï NetWare/IP ¥Ð¡¼¥¸¥ç¥ó 1.1 ¤ò¥µ¥Ý¡¼¥È¤·¤Æ¤¤¤ë¤Ù¤­¤Ç¤¹¡£ ¤³¤ì¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ NetWare/IP ¥Ð¡¼¥¸¥ç¥ó 1.1 ¤Î¥µ¡¼¥Ð¤È ÄÌ¿®¤¹¤ë»þ¤Î¤ßɬÍפǤ¹¡£ .RE .PP .B option \fBnwip.primary-dss\fR \fIip-address\fR\fB;\fR .RS 0.25i .PP NetWare/IP ¥É¥á¥¤¥ó¤Î¥×¥é¥¤¥Þ¥ê¥É¥á¥¤¥ó SAP/RIP ¥µ¡¼¥Ó¥¹¥µ¡¼¥Ð (DSS) ¤Î IP ¥¢¥É¥ì¥¹¤ò»ØÄꤷ¤Þ¤¹¡£ ¥»¥«¥ó¥À¥ê DSS ¥µ¡¼¥Ð¤ÎÀßÄê»þ¤Ë¡¢NetWare/IP ´ÉÍý¥æ¡¼¥Æ¥£¥ê¥Æ¥£¤Ï¡¢ ¤³¤ÎÃͤò¥×¥é¥¤¥Þ¥ê DSS ¥µ¡¼¥Ð¤È¤·¤Æ»ÈÍѤ·¤Þ¤¹¡£ .RE .SH ¿·µ¬¥ª¥×¥·¥ç¥ó¤ÎÄêµÁ .\" metal Internet Systems Consortium DHCP ¥¯¥é¥¤¥¢¥ó¥È¤È¥µ¡¼¥Ð¤Ï¡¢ ¿·µ¬¥ª¥×¥·¥ç¥ó¤òÄêµÁ¤¹¤ëµ¡¹½¤âÄ󶡤·¤Æ¤¤¤Þ¤¹¡£ ¤½¤ì¤¾¤ì¤Î DHCP ¥ª¥×¥·¥ç¥ó¤Ï¡¢Ì¾Á°¤È¥³¡¼¥É¡¢¹½Â¤¤ò»ý¤Ã¤Æ¤¤¤Þ¤¹¡£ ̾Á°¤Ï¡¢»ÈÍѼԤ¬¥ª¥×¥·¥ç¥ó¤ò»²¾È¤¹¤ë¤Î¤Ë»ÈÍѤµ¤ì¤Þ¤¹¡£ ¥³¡¼¥É¤Ï¡¢DHCP ¥µ¡¼¥Ð¤È¥¯¥é¥¤¥¢¥ó¥È¤¬¥ª¥×¥·¥ç¥ó¤ò»²¾È¤¹¤ë¤Î¤Ë »ÈÍѤ¹¤ëÈÖ¹æ¤Ç¤¹¡£ ¹½Â¤¤Ï¡¢¥ª¥×¥·¥ç¥ó¤ÎÆâÍÆ¤¬¤É¤Î¤è¤¦¤Ê¤â¤Î¤«¤òµ­½Ò¤·¤Æ¤¤¤Þ¤¹¡£ .PP ¿·µ¬¥ª¥×¥·¥ç¥ó¤òÄêµÁ¤¹¤ë¤Ë¤Ï¡¢Â¾¤Î¥ª¥×¥·¥ç¥ó¤Ç¤Ï»È¤ï¤ì¤Æ¤¤¤Ê¤¤Ì¾Á°¤ò Áª¤ÖɬÍפ¬¤¢¤ê¤Þ¤¹¡£ Î㤨¤Ð¡¢"host-name" ¤È¸À¤¦Ì¾Á°¤Ï»ÈÍѤǤ­¤Þ¤»¤ó¡£ ¤È¤¤¤¦¤Î¤â¡¢¤³¤Î¥Þ¥Ë¥å¥¢¥ë¥Ú¡¼¥¸¤Ë½Ð¤Æ¤­¤¿¤è¤¦¤Ë¡¢ DHCP ¥×¥í¥È¥³¥ë¤¬´û¤Ë host-name ¥ª¥×¥·¥ç¥ó¤òÄêµÁ¤·¤Æ¤¤¤ë¤«¤é¤Ç¤¹¡£ ¤³¤Î¥Þ¥Ë¥å¥¢¥ë¥Ú¡¼¥¸¤Ë½Ð¤Æ¤­¤Æ¤¤¤Ê¤¤¥ª¥×¥·¥ç¥ó̾¤Ê¤é¤Ð »È¤Ã¤Æ¤â¹½¤¤¤Þ¤»¤ó¤¬¡¢¾­Íè½Ð¤Æ¤¯¤ë¥ª¥×¥·¥ç¥ó¤È½Å¤Ê¤é¤Ê¤¤¤è¤¦¤Ë¡¢ ¥ª¥×¥·¥ç¥ó̾¤ÎºÇ½é¤ËÆÈ¼«¤Îʸ»úÎó¤ò¤Ä¤±¤ë¤³¤È¤Ï¡¢Â¿Ê¬¤¤¤¤¹Í¤¨¤Ç¤·¤ç¤¦¡£ Î㤨¤Ð¡¢¸ø¼°¤Î DHCP ¥ª¥×¥·¥ç¥ó¤Ë¤Ï "local" ¤Ç»Ï¤Þ¤ë¤â¤Î¤¬¤Ê¤¤¤Î¤Ç¡¢ "local-host-name" ¤È¸À¤¦Ì¾Á°¤Ï¡¢¤¤¤¯¤é¤«°Â¿´¤·¤ÆÄêµÁ¤Ç¤­¤ë¤Ç¤·¤ç¤¦¡£ .PP ̾Á°¤òÁªÂò¤·¤¿¤é¡¢¼¡¤Ï¥³¡¼¥É¤òÁª¤Ð¤Í¤Ð¤Ê¤ê¤Þ¤»¤ó¡£ DHCP ¥ª¥×¥·¥ç¥ó¤Î 128 ¤«¤é 256 ¤Þ¤Ç¤Î¥³¡¼¥É¤Ï¡¢ ¥µ¥¤¥È¥í¡¼¥«¥ë¥ª¥×¥·¥ç¥óÍѤȤ·¤ÆÍ½Ìó¤µ¤ì¤Æ¤¤¤ë¤Î¤Ç¡¢ ¤³¤ÎÃæ¤Î¥³¡¼¥É¤Ê¤é¤É¤ì¤Ç¤âÁª¤Ö¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£ ¼ÂºÝ¤Ë¤Ï¡¢¥×¥í¥È¥³¥ë¤ò¾¯¡¹¤¢¤¤¤Þ¤¤¤Ë²ò¼á¤·¤Æ¤¤¤ë¥Ù¥ó¥À¤¬¤¢¤ê¡¢ 128 ¤è¤êÂ礭¤¤ÃͤΥª¥×¥·¥ç¥ó¥³¡¼¥É¤ò»ÈÍѤ·¤Æ¤¤¤Þ¤¹¡£ ¤³¤ÎÌäÂê¤òËÜÅö¤Ë²óÈò¤¹¤ëÊýË¡¤Ï¤¢¤ê¤Þ¤»¤ó¤¬¡¢ ¼ÂºÝ¤Ë¤Ï¤½¤¦Â礭¤ÊÌäÂê¤ò°ú¤­µ¯¤³¤¹¤â¤Î¤Ç¤Ï¤Ê¤¤¤Ç¤·¤ç¤¦¡£ .PP ¥ª¥×¥·¥ç¥ó¤Î¹½Â¤¤È¤Ï¡¢Ã±¤Ë¥ª¥×¥·¥ç¥ó¤Î¥Ç¡¼¥¿¤¬É½¸½¤µ¤ì¤Æ¤¤¤ë·Á¼°¤Ç¤¹¡£ ¸½ºß ISC DHCP ¥µ¡¼¥Ð¤Ï¡¢À°¿ô¡¢¥Ö¡¼¥ëÃÍ¡¢Ê¸»úÎ󤽤·¤Æ IP ¥¢¥É¥ì¥¹¤È¤¤¤Ã¤¿¡¢ ¤¤¤¯¤Ä¤«¤Îñ½ã¤Ê¥Ç¡¼¥¿·¿¤ò¥µ¥Ý¡¼¥È¤·¤Æ¤ª¤ê¡¢ ¤Þ¤¿Ã±°ì¥Ç¡¼¥¿·¿¤ÎÇÛÎó¤ä¸ÇÄê½ç¤Î¥Ç¡¼¥¿·¿Îó¤ÎÇÛÎó¤òÄêµÁ¤¹¤ë¤³¤È¤â¤Ç¤­¤Þ¤¹¡£ .PP ¿·µ¬¥ª¥×¥·¥ç¥ó¤Ï¡¢°Ê²¼¤Î¤è¤¦¤ËÀë¸À¤µ¤ì¤Þ¤¹: .PP .B option .I new-name .B code .I new-code .B = .I definition .B ; .PP .I new-name ¤È .I new-code ¤ÎÃͤϡ¢¿·µ¬¥ª¥×¥·¥ç¥óÍѤˤ¢¤Ê¤¿¤¬Áª¤ó¤À¤â¤Î¤Ç¤¹¡£ .I definition ¤Ï¡¢¥ª¥×¥·¥ç¥ó¤Î¹½Â¤¤ÎÄêµÁ¤Ç¤¹¡£ .PP °Ê²¼¤Îñ½ã¤Ê¥ª¥×¥·¥ç¥ó¤Î·¿ÄêµÁ¤¬¥µ¥Ý¡¼¥È¤µ¤ì¤Æ¤¤¤Þ¤¹: .PP .B ¥Ö¡¼¥ëÃÍ .PP .B option .I new-name .B code .I new-code .B = .B boolean .B ; .PP ¥Ö¡¼¥ë·¿¤Î¥ª¥×¥·¥ç¥ó¤Ï¡¢on ¤Þ¤¿¤Ï off (¤â¤·¤¯¤Ï true ¤« false) ¤ÎÃͤò »ý¤Ä¥Õ¥é¥°¤Ç¤¹¡£ ¥Ö¡¼¥ë·¿¤Î»ÈÍÑÎã¤Ï¡¢°Ê²¼¤Î¤è¤¦¤Ë¤Ê¤ê¤Þ¤¹: .nf option use-zephyr code 180 = boolean; option use-zephyr on; .fi .B À°¿ô .PP .B option .I new-name .B code .I new-code .B = .I sign .B integer .I width .B ; .PP \fIsign\fR ¥È¡¼¥¯¥ó¤Ï¡¢¶õÇò¡¢\fIunsigned\fR¡¢\fIsigned\fR ¤Î¤¤¤º¤ì¤«¤Ç¤¹¡£ width ¤Ï 8, 16, 32 ¤Î¤¤¤º¤ì¤«¤Ç¡¢À°¿ô¤Î bit ¿ô¤ò¼¨¤·¤Þ¤¹¡£ Î㤨¤Ð¡¢°Ê²¼¤Î 2 ¹Ô¤Ï¡¢sql-connection-max ¥ª¥×¥·¥ç¥ó¤ÎÄêµÁ¤È »ÈÍÑË¡¤ò¼¨¤·¤Þ¤¹: .nf option sql-connection-max code 192 = unsigned integer 16; option sql-connection-max 1536; .fi .B IP ¥¢¥É¥ì¥¹ .PP .B option .I new-name .B code .I new-code .B = .B ip-address .B ; .PP IP ¥¢¥É¥ì¥¹·¿¤Î¹½Â¤¤ò»ý¤Ä¥ª¥×¥·¥ç¥ó¤Ï¡¢¥É¥á¥¤¥ó̾¤â¤·¤¯¤Ï ¥É¥Ã¥È¶èÀÚ¤ê¤Î 4 À°¿ô¤Çɽ¸½¤µ¤ì¤Þ¤¹¡£ °Ê²¼¤Ï¡¢IP ¥¢¥É¥ì¥¹·¿¤Î»ÈÍÑÎã¤Ç¤¹: .nf option sql-server-address code 193 = ip-address; option sql-server-address sql.example.com; .fi .PP .B ¥Æ¥­¥¹¥È .PP .B option .I new-name .B code .I new-code .B = .B text .B ; .PP ¥Æ¥­¥¹¥È·¿¤Î¥ª¥×¥·¥ç¥ó¤Ï¡¢ASCII ¥Æ¥­¥¹¥Èʸ»úÎó¤ò¥¨¥ó¥³¡¼¥É¤·¤Þ¤¹¡£ Î㤨¤Ð: .nf option sql-default-connection-name code 194 = text; option sql-default-connection-name "PRODZA"; .fi .PP .B ¥Ç¡¼¥¿Ê¸»úÎó .PP .B option .I new-name .B code .I new-code .B = .B string .B ; .PP ¥Ç¡¼¥¿Ê¸»úÎ󷿤Υª¥×¥·¥ç¥ó¤Ï¡¢ËܼÁŪ¤Ë¤Ïñ¤Ê¤ë¥Ð¥¤¥È¤Î½¸¹çÂΤǤ¹¡£ ¥Æ¥­¥¹¥È·¿¤Î¤è¤¦¤Ë¥¯¥ª¡¼¥È¤µ¤ì¤¿¥Æ¥­¥¹¥È¤Ç»ØÄꤵ¤ì¤ë¤«¡¢ ¤â¤·¤¯¤Ï¥³¥í¥ó¶èÀÚ¤ê¤Î 16 ¿Ê¿ô¤Î¥ê¥¹¥È¤Ç»ØÄꤵ¤ì¤Þ¤¹¡£ ¤³¤Î»þ¥³¥í¥ó¤Ç¶èÀÚ¤é¤ì¤¿Ãæ¿È¤Ï¡¢0 ¤«¤é FF ¤Î´Ö¤ÎÃͤǤʤ±¤ì¤Ð¤Ê¤ê¤Þ¤»¤ó¡£ Î㤨¤Ð: .nf option sql-identification-token code 195 = string; option sql-identification-token 17:23:19:a6:42:ea:99:7c:22; .fi .PP .B ¥«¥×¥»¥ë²½ .PP .B option .I new-name .B code .I new-code .B = .B encapsulate .I identifier .B ; .PP ¥«¥×¥»¥ë²½·¿¤Î¥ª¥×¥·¥ç¥ó¤Ï¡¢\fIidentifier\fR ¤Ç»ØÄꤵ¤ì¤¿ ¥ª¥×¥·¥ç¥ó¶õ´Ö¤ÎÃæ¿È¤ò¥«¥×¥»¥ë²½¤·¤Þ¤¹¡£ ¸½ºß DHCP ¥×¥í¥È¥³¥ë¤Ë¸ºß¤¹¤ë¥«¥×¥»¥ë²½¥ª¥×¥·¥ç¥ó¤ÎÎã¤Ï¡¢ vendor-encapsulated-options ¥ª¥×¥·¥ç¥ó¡¢netware-suboptions ¥ª¥×¥·¥ç¥ó¡¢ relay-agent-information ¥ª¥×¥·¥ç¥ó¤Ê¤É¤Ç¤¹¡£ .nf option space local; option local.demo code 1 = text; option local-encapsulation code 197 = encapsulate local; option local.demo "demo"; .fi .PP .B ÇÛÎó .PP ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥Æ¥­¥¹¥È·¿¤È¥Ç¡¼¥¿Ê¸»úÎ󷿰ʳ°¤Î¾å½Ò¤Î¤¤¤«¤Ê¤ë¥Ç¡¼¥¿·¿¤Î ÇÛÎó¤â´Þ¤à¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£ ¥Æ¥­¥¹¥È·¿¤È¥Ç¡¼¥¿Ê¸»úÎ󷿤ϡ¢¸½ºßÇÛÎó¤Ç¤Ï¥µ¥Ý¡¼¥È¤µ¤ì¤Æ¤¤¤Þ¤»¤ó¡£ ÇÛÎóÄêµÁ¤ÎÎã¤Ï°Ê²¼¤ÎÄ̤ê¤Ç¤¹: .nf option kerberos-servers code 200 = array of ip-address; option kerberos-servers 10.20.10.1, 10.20.11.1; .fi .B ¥ì¥³¡¼¥É .PP ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥Ç¡¼¥¿·¿¤ÎÎó¤Ç¹½À®¤µ¤ì¤ë¥Ç¡¼¥¿¹½Â¤¤ò´Þ¤à¤³¤È¤â¤Ç¤­¤Þ¤¹¡£ ¤³¤ì¤Ï¤·¤Ð¤·¤Ð¥ì¥³¡¼¥É·¿¤È¸Æ¤Ð¤ì¤Þ¤¹¡£ Î㤨¤Ð: .nf option contrived-001 code 201 = { boolean, integer 32, text }; option contrived-001 on 1772 "contrivance"; .fi ¤Þ¤¿¥ì¥³¡¼¥É¤ÎÇÛÎó¤Î¥ª¥×¥·¥ç¥ó¤ò»ý¤Ä¤³¤È¤â¤Ç¤­¤Þ¤¹¡£ Î㤨¤Ð: .nf option new-static-routes code 201 = array of { ip-address, ip-address, ip-address, integer 8 }; option static-routes 10.0.0.0 255.255.255.0 net-0-rtr.example.com 1, 10.0.1.0 255.255.255.0 net-1-rtr.example.com 1, 10.2.0.0 255.255.224.0 net-2-0-rtr.example.com 3; .fi .SH ¥Ù¥ó¥À¥«¥×¥»¥ë²½¥ª¥×¥·¥ç¥ó .\" metal DHCP ¥×¥í¥È¥³¥ë¤Ë¤Ï¡¢\fB vendor-encapsulated-options\fR ¥ª¥×¥·¥ç¥ó¤¬ ÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤¹¡£ ¥Ù¥ó¥À¤Ï¡¢¤³¤Î¥ª¥×¥·¥ç¥ó¤Ë¤è¤Ã¤Æ¡¢¥Ù¥ó¥À¸ÇÍ­¤Î¥ª¥×¥·¥ç¥ó¤ò ɸ½à DHCP ¥ª¥×¥·¥ç¥ó¤Ë´Þ¤á¤ÆÁ÷½Ð¤¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£ .B vendor-encapsulated-options ¥ª¥×¥·¥ç¥ó¤Î½ñ¼°¤Ï¡¢½ñ¼°¤¬µ¬Äꤵ¤ì¤Æ¤¤¤Ê¤¤°ìÏ¢¤Î¥Ð¥¤¥ÈÎó¡¢ ¤â¤·¤¯¤Ï°ìÏ¢¤Î¥ª¥×¥·¥ç¥óÎó¤Ç¤¹¡£ ¥ª¥×¥·¥ç¥óÎóÃæ¤Î¤½¤ì¤¾¤ì¤Î¥ª¥×¥·¥ç¥ó¤Ï¡¢1 ¥Ð¥¤¥È¤Î¥Ù¥ó¥À¸ÇÍ­¤Î ¥ª¥×¥·¥ç¥ó¥³¡¼¥É¤Î¸å¤Ë 1 ¥Ð¥¤¥È¤Î¥Ç¡¼¥¿Ä¹¡¢ ¤½¤·¤Æ¤½¤Î¥Ç¡¼¥¿Ä¹¤Ç»ØÄꤵ¤ì¤¿Â礭¤µ¤Î¥Ç¡¼¥¿¤¬Â³¤¤¤¿¤â¤Î¤Ç¹½À®¤µ¤ì¤Þ¤¹ (¥Ç¡¼¥¿Ä¹¤Ë¤Ï¡¢¥Ç¡¼¥¿Ä¹¼«¿È¤ä¥ª¥×¥·¥ç¥ó¥³¡¼¥É¤Ï´Þ¤Þ¤ì¤Þ¤»¤ó)¡£ .PP ËÜ¥ª¥×¥·¥ç¥ó¤ÎÃͤϡ¢2 ¤Ä¤ÎÊýË¡¤Î¤¤¤º¤ì¤«¤ÇÀßÄꤵ¤ì¤Þ¤¹¡£ 1 ÈÖÌܤÎÊýË¡¤Ï¡¢Ã±¤Ë¥Ç¡¼¥¿¤òľÀÜ»ØÄꤹ¤ë¤â¤Î¤Ç¤¹¡£ ¥Ç¡¼¥¿¤Î»ØÄê¤Ë¤Ï¡¢¥Æ¥­¥¹¥Èʸ»úÎ󤫥³¥í¥ó¤Ç¶èÀÚ¤é¤ì¤¿ 16 ¿Ê¿ôÃͤòÍѤ¤¤Þ¤¹¡£ Î㤨¤Ð: .PP .nf option vendor-encapsulated-options 2:4:AC:11:41:1: 3:12:73:75:6e:64:68:63:70:2d:73:65:72:76:65:72:31:37:2d:31: 4:12:2f:65:78:70:6f:72:74:2f:72:6f:6f:74:2f:69:38:36:70:63; .fi .PP ËÜ¥ª¥×¥·¥ç¥ó¤òÀßÄꤹ¤ë 2 ÈÖÌܤÎÊýË¡¤Ï¡¢DHCP ¥µ¡¼¥Ð¤Ë ¥Ù¥ó¥À¸ÇÍ­¥ª¥×¥·¥ç¥ó¥Ð¥Ã¥Õ¥¡¤òºîÀ®¤µ¤»¤ë¤È¤¤¤¦¤â¤Î¤Ç¤¹¡£ ¤³¤ì¤ò¤¹¤ë¤Ë¤Ï¡¢°Ê²¼¤Î 4 ¤Ä¤Î¤³¤È¤ò¤¹¤ëɬÍפ¬¤¢¤ê¤Þ¤¹: ¥ª¥×¥·¥ç¥ó¶õ´Ö¤òÄêµÁ¤·¡¢¤½¤Î¥ª¥×¥·¥ç¥ó¶õ´ÖÆâ¤Ë¥ª¥×¥·¥ç¥ó¤òÄêµÁ¤·¡¢ ¤½¤ì¤é¤ØÃͤò³ä¤ê¿¶¤ê¡¢ºÇ¸å¤Ë¤½¤Î¥ª¥×¥·¥ç¥ó¶õ´Ö¤¬ .B vendor-encapsulated-options ¥ª¥×¥·¥ç¥ó¤ÎÀ¸À®¤Ë»ÈÍѤµ¤ì¤ë¤³¤È¤ò»ØÄꤷ¤Þ¤¹¡£ .PP ¥Ù¥ó¥À¥ª¥×¥·¥ç¥ó¤¬³ÊǼ¤µ¤ì¤ë¥ª¥×¥·¥ç¥ó¶õ´Ö¤ò¿·µ¬¤ËÄêµÁ¤¹¤ë¤Ë¤Ï¡¢ \fRoption space\fP ʸ¤ò»ÈÍѤ·¤Þ¤¹: .PP .B option .B space .I name .B ; .PP ¤³¤Îʸ½ñ¤Ë¤³¤ì¤Þ¤Ç½ñ¤«¤ì¤Æ¤¤¤ë¤è¤¦¤Ë¡¢ ¤³¤Î name ¤Ï¡¢¥ª¥×¥·¥ç¥óÄêµÁ¤Ç»ÈÍѤ¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£ Î㤨¤Ð: .nf option space SUNW; option SUNW.server-address code 2 = ip-address; option SUNW.server-name code 3 = text; option SUNW.root-path code 4 = text; .fi °ìÅÙ¡¢¥ª¥×¥·¥ç¥ó¶õ´Ö¤È¥ª¥×¥·¥ç¥ó¤Î½ñ¼°¤òÄêµÁ¤·¤¿¤é¡¢ ¤½¤ì¤é¤Î¥ª¥×¥·¥ç¥ó¤ÎÃͤòÄêµÁ¤¹¤ë¥¹¥³¡¼¥×¤òÀßÄê¤Ç¤­¡¢ ¤½¤ì¤é¤Î¥ª¥×¥·¥ç¥ó¤ò¤¤¤Ä»È¤¦¤«¤ò»ØÄꤹ¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£ Î㤨¤Ð¡¢2 ¤Ä¤Î°Û¤Ê¤ë¥¯¥é¥¹¤Î¥¯¥é¥¤¥¢¥ó¥È¤ò°·¤¤¤¿¤¤¤È¤·¤Þ¤·¤ç¤¦¡£ Á°½Ò¤ÎÎã¤Ç¼¨¤·¤¿¥ª¥×¥·¥ç¥ó¶õ´Ö¤ÎÄêµÁ¤ò»È¤Ã¤Æ¡¢°Ê²¼¤Î¤è¤¦¤Ë¡¢ ¥¯¥é¥¤¥¢¥ó¥È¤«¤éÁ÷¤é¤ì¤Æ¤­¤¿ vendor-class-identifier ¥ª¥×¥·¥ç¥ó¤Ë´ð¤Å¤¤¤Æ¡¢ °Û¤Ê¤ë¥ª¥×¥·¥ç¥ó¤ÎÃͤò°Û¤Ê¤ë¥¯¥é¥¤¥¢¥ó¥È¤ËÁ÷½Ð¤¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£ .PP .nf class "vendor-classes" { match option vendor-class-identifier; } option SUNW.server-address 172.17.65.1; option SUNW.server-name "sundhcp-server17-1"; subclass "vendor-classes" "SUNW.Ultra-5_10" { vendor-option-space SUNW; option SUNW.root-path "/export/root/sparc"; } subclass "vendor-classes" "SUNW.i86pc" { vendor-option-space SUNW; option SUNW.root-path "/export/root/i86pc"; } .fi .PP Àè¤ÎÎã¤Ç¸«¤¿¤è¤¦¤Ë¡¢Ä̾ï¤Î¥¹¥³¡¼¥×¥ë¡¼¥ë¤òŬÍѤ¹¤ë¤³¤È¤Ç¡¢ ¥°¥í¡¼¥Ð¥ë¤ÊÃͤò¥°¥í¡¼¥Ð¥ë¥¹¥³¡¼¥×Ãæ¤ËÄêµÁ¤Ç¤­¡¢ ÆÃÄê¤Î¥¯¥é¥¹¤Ë¸ÇÍ­¤ÎÃͤÀ¤±¤ò¥í¡¼¥«¥ë¥¹¥³¡¼¥×¤ËÄêµÁ¤Ç¤­¤Þ¤¹¡£ \fBvendor-option-space\fR Àë¸À¤ò»È¤¦¤³¤È¤Ç¡¢ .B vendor-encapsulated-options ¥ª¥×¥·¥ç¥ó¤ò¹½À®¤¹¤ë¤Î¤Ë¡¢SUNW ¥ª¥×¥·¥ç¥ó¶õ´ÖÆâ¤Î¥ª¥×¥·¥ç¥ó¤ò»È¤¦¤è¤¦ DHCP ¥µ¡¼¥Ð¤Ë»Ø¼¨¤¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£ .SH ´ØÏ¢¹àÌÜ dhclient.conf(5), dhcp-eval(5), dhclient(8), RFC2132, RFC2131 .SH ºî¼Ô Internet Systems Consortium DHCP Distribution ¤Ï¡¢Vixie Labs ¤È¤Î·ÀÌó¤Î¤â¤È¤Ç¡¢Ted Lemon ¤¬µ­½Ò¤·¤Þ¤·¤¿¡£ ËÜ¥×¥í¥¸¥§¥¯¥È¤Î»ñ¶â¤Ï¡¢Internet Systems Consortium ¤¬Ä󶡤·¤Þ¤·¤¿¡£ Internet Systems Consortium ¤Ë´Ø¤¹¤ë¾ðÊó¤Ï¡¢ .B https://www.isc.org ¤Ë¤¢¤ê¤Þ¤¹¡£ dhcp-4.4.1/doc/examples/dhclient-dhcpv6.conf000644 000765 000024 00000000576 13243301226 021137 0ustar00tmarkstaff000000 000000 # Client configuration file example for DHCPv6 # The client side command to enable rapid-commit (2 packet exchange) ##send dhcp6.rapid-commit; # name-servers and domain-search are requested by default. # here is the way to request sip-servers-addresses too also request dhcp6.sip-servers-addresses; # Likely to be useful: the script path script "/usr/local/etc/dhclient-script"; dhcp-4.4.1/doc/examples/dhcpd-dhcpv6.conf000644 000765 000024 00000006452 13243301226 020426 0ustar00tmarkstaff000000 000000 # Server configuration file example for DHCPv6 # From the file used for TAHI tests - addresses chosen # to match TAHI rather than example block. # IPv6 address valid lifetime # (at the end the address is no longer usable by the client) # (set to 30 days, the usual IPv6 default) default-lease-time 2592000; # IPv6 address preferred lifetime # (at the end the address is deprecated, i.e., the client should use # other addresses for new connections) # (set to 7 days, the usual IPv6 default) preferred-lifetime 604800; # T1, the delay before Renew # (default is 1/2 preferred lifetime) # (set to 1 hour) option dhcp-renewal-time 3600; # T2, the delay before Rebind (if Renews failed) # (default is 3/4 preferred lifetime) # (set to 2 hours) option dhcp-rebinding-time 7200; # Enable RFC 5007 support (same than for DHCPv4) allow leasequery; # Global definitions for name server address(es) and domain search list option dhcp6.name-servers 3ffe:501:ffff:100:200:ff:fe00:3f3e; option dhcp6.domain-search "test.example.com","example.com"; # Set preference to 255 (maximum) in order to avoid waiting for # additional servers when there is only one ##option dhcp6.preference 255; # Server side command to enable rapid-commit (2 packet exchange) ##option dhcp6.rapid-commit; # The delay before information-request refresh # (minimum is 10 minutes, maximum one day, default is to not refresh) # (set to 6 hours) option dhcp6.info-refresh-time 21600; # The path of the lease file dhcpv6-lease-file-name "/usr/local/var/db/dhcpd6.leases"; # Static definition (must be global) host myclient { # The entry is looked up by this host-identifier option dhcp6.client-id 00:01:00:01:00:04:93:e0:00:00:00:00:a2:a2; # A fixed address fixed-address6 3ffe:501:ffff:100::1234; # A fixed prefix fixed-prefix6 3ffe:501:ffff:101::/64; # Override of the global definitions, # works only when a resource (address or prefix) is assigned option dhcp6.name-servers 3ffe:501:ffff:100:200:ff:fe00:4f4e; # For debug (to see when the entry statements are executed) # (log "sol" when a matching Solicitation is received) ##if packet(0,1) = 1 { log(debug,"sol"); } } host otherclient { # This host entry is hopefully matched if the client supplies a DUID-LL # or DUID-LLT containing this MAC address. hardware ethernet 01:00:80:a2:55:67; fixed-address6 3ffe:501:ffff:100::4321; } # The subnet where the server is attached # (i.e., the server has an address in this subnet) subnet6 3ffe:501:ffff:100::/64 { # Two addresses available to clients # (the third client should get NoAddrsAvail) range6 3ffe:501:ffff:100::10 3ffe:501:ffff:100::11; # Use the whole /64 prefix for temporary addresses # (i.e., direct application of RFC 4941) range6 3ffe:501:ffff:100:: temporary; # Some /64 prefixes available for Prefix Delegation (RFC 3633) prefix6 3ffe:501:ffff:100:: 3ffe:501:ffff:111:: /64; } # A second subnet behind a relay agent subnet6 3ffe:501:ffff:101::/64 { range6 3ffe:501:ffff:101::10 3ffe:501:ffff:101::11; # Override of the global definitions, # works only when a resource (address or prefix) is assigned option dhcp6.name-servers 3ffe:501:ffff:101:200:ff:fe00:3f3e; } # A third subnet behind a relay agent chain subnet6 3ffe:501:ffff:102::/64 { range6 3ffe:501:ffff:102::10 3ffe:501:ffff:102::11; } dhcp-4.4.1/doc/devel/arch.dox000644 000765 000024 00000000271 13243301226 016210 0ustar00tmarkstaff000000 000000 /** @page archSrv Server Architecture @todo: Describe high level server architecture here. @page archCli Client Architecture @todo: Describe high level client architecture here. */dhcp-4.4.1/doc/devel/atf.dox000644 000765 000024 00000021073 13243301226 016050 0ustar00tmarkstaff000000 000000 /** @page tests Testing @section testsOverview Testing Overview In DHCP, a unit test exercises a particular piece of code in isolation. There is a separate unit test per module or API. Each unit test lives in a directory beneath the code it is designed to exercise. So, we (will eventually) have: @verbatim server/tests/ client/tests/ common/tests/ dhcpctl/tests/ ... @endverbatim And so on. Ideally each function would be invoked with every possible type of input, and each branch of every function would be checked. In practice we try to be a bit more pragmatic, and target the most basic operations, as well tricky code, and areas we have seen bugs in the past. We are using ATF (Automated Test Framework) as a framework to run our unittests. @section testsAtf ATF unit-tests ATF stands for Automated Test Framework, and is the framework used for unit tests in ISC DHCP and BIND9. ATF sources can be downloaded from https://github.com/jmmv/kyua . ATF itself must be configured, compiled and then installed to be available during the DHCP configure procedure. There are three options for installing ATF. 1) Get the ATF sources and follow INSTALL file supplied with ATF sources (it's essentially the typical ./configure && make && make install procedure). 2) install Kyua with the ATF compatibility package, or 3) use the ATF version included in bind sources. To configure and build the ATF from BIND you should set the "--with-atf" option to "bind". @verbatim ./configure --with-atf=bind @endverbatim Beginning with ATF version 0.16, it is necessary to include the following options --enable-tools and --disable-shared when configuring ATF: @verbatim configure --prefix= --enable-tools --disable-shared @endverbatim ISC DHCP unittests will run with ATF releases upto 0.19. Beginning with ATF 0.20, the tools, atf-run and atf-report required by ISC DHCP, were deprecated and are no longer included with ATF. The ATF successor, called Kyua, is being developed. As of August 2012, the latest available release of Kyua is 0.5. It claims to offer feature parity with ATF. Migration to Kyua may be planned some time in the future, but DHCP uses ATF for now. Such an upgrade should be done in coordination with BIND. To build and run the unit-tests, use the following: @verbatim $ ./configure --with-atf $ make $ make check @endverbatim This will traverse the source tree running the unit tests in each unit test subdirectory. Note that if one or more tests in a unit test subdirectory fail the make process will stop. To run all of the tests regardless of outcome, use "make -k check" The following syntax is supported as well: @verbatim $ ./configure --with-atf=/path/to/your/atf/install @endverbatim but it seems to have troubles sometimes detecting ATF installation, at least with ATF 0.14 and Mac OS X 10.6.8. Each code directory (e.g. server/) that has unit-tests has a sub-directory named tests (e.g. server/tests). You can execute "make check" in that directory to run specific subset of tests. Unit-tests are grouped into suites, each suite being a separate executable. The typical way to run tests is: @verbatim $ atf-run | atf-report (This assumes atf-run and atf-report are in your path) or $ sh ../../tests/unittests.sh @endverbatim atf-run will read the Atffile in the current directory and execute all the tests specified in it. Using atf-run - rather than calling the test binary directly - has several major benefits. The main one is that atf-run is able to recover from test segfault and continue execution from the next case onwards. Another is that it is possible to specify a timeout for a test. atf-run will kill the test in case of any infinite loops and will continue running next tests. It is possible to run atf-run without passing its output to atf-report, but its output is somewhat convoluted. That is useful in some situations, e.g. when one wants to see test output. It is possible to run test binary directly. The only required parameter is the test case name. The binary will print out a warning that direct binary execution is not recommended as it won't be able to recover from crash. However, such an approach is convenient for running the test under the debugger. @section testsAtfAdding Adding new unit-tests There are a small number of unit-tests that are not ATF based. They will be converted to ATF soon. Please do not use any other frameworks. Sadly, the DHCP code was not written with unit-testing in mind: often a non-standard approach is required for writing unit-tests. The existing code often has many dependencies that make testing a single piece of code awkward to unit test. For example, to test hash tables, one needs to also include the OMAPI code. Rather than significantly refactoring the code (a huge task that could take months), we decided to link whatever is needed in the tests. If developing new test suite, it is recommended that you take a look at existing tests and just copy them as a starting point. In particular, the following things should be done for adding new tests: 1. Tests directory. For each code component (server, client, common, etc.) there should be a tests subdirectory. If it isn't there yet, then it must be created. This can be done by: a). Creating the directory: @verbatim $ mkdir $subdir/tests $ cvs add tests @endverbatim b). Adding the subdirectory to the build system: Add to $subdir/Makefile.am: @verbatim SUBDIRS = tests @endverbatim Add to the AC_OUTPUT macro in configure.ac: @verbatim subdir/tests/Makefile @endverbatim c. Create a Makefile.am in the new directory, something similar to this: @verbatim AM_CPPFLAGS = -I../.. check_PROGRAMS = test_foo TESTS = test_foo test_foo_SOURCES = test_foo.c test_foo_LDADD = ../../tests/libt_api.a # plus others... @endverbatim See existing Makefile.am for examples, and the Automake documentation: http://www.gnu.org/software/automake/manual/html_node/Tests.html 2. Implement the test. That typically means that you create a new file that will hold test code. It is recommended you name it (tested_feature_name)_unittest.c and put the file in specified tests directory. For example tests related to hash tables used on the server side should be named server/tests/hash_unittest.c. If in doubt, it is convenient to name the test code after the file that holds tested code, e.g. server/mdb6.c is tested in server/tests/mdb6_unittest.c. The file server/tests/simple_unittest.c holds a template explaining the basic layout of the ATF tests. There may be many test cases in a single *_unittest.c file. Make sure that you register all your test cases using ATF_TP_ADD_TC() macro, and try to minimize modifications to the tested code if possible. Keep in mind that we are using modernized \ref codingGuidelines for test development. You are advised to also look at atf-c-api(3) man page. To add a new test, such as when a new module is added or when you want to start testing existing code, you can copy the server/tests/simple_unittest.c as a new new file, add the new file as a target in Makefile.am, and begin adding tests. Reviewing that file is a good idea, even if you decide to write your test from scratch, as it give you quick overview of the essential capabilities of the ATF framework (how to write test, how to make checks, pass or fail test etc.). Do not forget to add your new file to git via "git add yourtest_unittest.c". 3. Extend Makefile.am to build your test. In particular, add your binary name to ATF_TESTS. The tests directory will be built only in case where ATF is enabled, using --with-atf during configure phase. 4. Modify Atffile to include your new test, if needed. Tests in the specified directory must be registered in Atffile. See server/tests/Atffile for an example. Currently every executable with name of the form *_unittest will be executed automatically. If you followed naming convention proposed in a previous step, your test will be included and will be included automatically. 5. Enjoy your improved confidence in the code, as you can run the tests after any change you may want to do: @verbatim $ make check @endverbatim to run all tests for all components. See \ref atfTests section for more details on running tests. @section testsAtfCoding ATF Coding Guidelines As the unit-test code creates an evironment that works under a different regime than the production code, there are slight differences to standard coding guidelines. In particular: - The code is written using C99. Double slash comments are allowed. - Please do not use tabs. Use 4 spaces for each indent level. */ dhcp-4.4.1/doc/devel/contrib.dox000644 000765 000024 00000000367 13243301226 016741 0ustar00tmarkstaff000000 000000 /** @page contrib Contributing to DHCP @section contribDir 3rd party contributions in contrib/ directory @todo: Describe contrib/ dir @section codingGuidelines Coding Guidelines @todo: (... if people want to contribute significant code) */ dhcp-4.4.1/doc/devel/debug.dox000644 000765 000024 00000002723 13243301226 016365 0ustar00tmarkstaff000000 000000 /** @page debug Debugging This page enumerates various techniques useful for debugging ISC DHCP software. @section debugTips Debugging Tips & Tricks ISC DHCP code is somewhat convoluted. Due to extensive macros use, it is often difficult to even find whole function, much less to understand what they actually do. One way to find such a macro-defined function is to compile the code with debugging symbols (-g), load the binary into gdb and set a breakpoint for such a function. gdb will print out exact place in the code where the function is defined. Presumably one will find a macro at that specific location. For example to find where \ref lease_reference function is defined do: @verbatim gdb file dhcpd b lease_reference @endverbatim DEBUG_MEMORY_LEAKAGE may be defined in includes/site.h to enable some debugging code to help with debugging memory issues. This code keeps a running total of the outstanding memory that has been allocated and a list of the outstanding allocations. Both are updated whent he memory is freed. Status information is printed when do_packet() and do_packet6() complete processing. The outstanding value is expected to grow when new structures are used - for example when a new IPv6 lease is created. It is not expected to grow when a structure is reused for example when an IPv6 lease is renewed. DEBUG_RC_HISTORY and DEBUG_RC_HISTORY_EXHAUSTIVELY can also be defined to provide more verbose information about reference counts on objects. */ dhcp-4.4.1/doc/devel/doxyfile.in000644 000765 000024 00000225275 13243301226 016747 0ustar00tmarkstaff000000 000000 # Doxyfile 1.8.1.1 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" "). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or sequence of words) that should # identify the project. Note that if you do not use Doxywizard you need # to put quotes around the project name if it contains spaces. PROJECT_NAME = "ISC DHCP" # 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 = @PACKAGE_VERSION@ # 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 = "A reference DHCPv4 and DHCPv6 implementation" # With the PROJECT_LOGO tag one can specify an logo or 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 = devel/isc-logo.jpg # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = YES # 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. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) 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. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) 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. 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" "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. 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. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then 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. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then 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. STRIP_FROM_PATH = @abs_top_srcdir@ # 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 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 if your file system # doesn't support long names like on DOS, Mac, or CD-ROM. 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 # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = 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 comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) 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 behaviour. # 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 behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. 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. 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. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding # "class=itcl::class" will allow you to use the command class in the # itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. 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. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this # tag. The format is ext=language, where ext is a file extension, and language # is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, # C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. 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 that for custom extensions # you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = # If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all # comments according to the Markdown format, which allows for more readable # documentation. See http://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you # can mix doxygen, HTML, and XML commands with Markdown formatting. # Disable only in case of backward compatibilities issues. MARKDOWN_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); v.s. # func(std::string) {}). This also makes the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip 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. 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 (the default) # will make doxygen 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. 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. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) 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. 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). INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and # unions with only public data 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 (the default), # structs, classes, and unions are shown on a separate page (for HTML and Man # pages) or section (for LaTeX and RTF). INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT 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. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penalty. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will roughly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols. SYMBOL_CACHE_SIZE = 0 # Similar to the SYMBOL_CACHE_SIZE 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 appear 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. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # 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 and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_PACKAGE tag is set to YES all members with package or internal scope will be included in the documentation. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. 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. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When 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 (the default) only methods in the interface are included. 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 namespaces are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) 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. 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 (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. 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 (the default) these blocks will be appended to the # function's detailed documentation block. 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 (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # 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. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) 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. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # 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 default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # 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 default), 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. 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. 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. 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. 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. 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. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or macro consists of 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 initializer of individual variables and macros in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. 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. 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 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 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 , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. 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. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files # containing the references data. This must be a list of .bib files. The # .bib extension is automatically appended if omitted. Using this command # requires the bibtex tool to be installed. See also # http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style # of the bibliography can be controlled using LATEX_BIB_STYLE. To use this # feature you need bibtex and perl available in the search path. 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 # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED 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. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR 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. WARN_IF_DOC_ERROR = YES # The 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 (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = YES # 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) 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 stderr. WARN_LOGFILE = doxygen-warnings.log #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be 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. INPUT = .. # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. 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 pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh # *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py # *.f90 *.f *.for *.vhd *.vhdl FILE_PATTERNS = *.c *.h *.dox # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. 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 = */tests/* */bind/* # 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 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. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , 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. 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 # info on how filters are used. If FILTER_PATTERNS is empty or if # non of the patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). 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 option only has effect when # FILTER_SOURCE_FILES is enabled. FILTER_SOURCE_PATTERNS = #--------------------------------------------------------------------------- # 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 also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C, C++ and Fortran comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # 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. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) 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. 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. ALPHABETICAL_INDEX = YES # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. 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. If left blank `html' will be used as the default path. 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). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. Note that when using a custom header you are responsible # for the proper inclusion of any scripts and style sheets that doxygen # needs, which is dependent on the configuration options used. # It is advised to generate a default header using "doxygen -w html # header.html footer.html stylesheet.css YourConfigFile" and then modify # that header. Note that the header is subject to change so you typically # have to redo this when upgrading to a newer version of doxygen or when # changing the value of configuration settings such as GENERATE_TREEVIEW! HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. 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 the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # style sheet in the HTML output directory as well, or it will be erased! HTML_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. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the style sheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. 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. 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. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. HTML_DYNAMIC_SECTIONS = NO # 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. 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, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # 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. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, 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. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_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. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, 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. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, 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. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, 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. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. 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. 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. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. 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. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, 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. 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. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) # at top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. Since the tabs have the same information as the # navigation tree you can set this option to NO if you already set # GENERATE_TREEVIEW to YES. DISABLE_INDEX = YES # 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. # Since the tree basically has the same information as the tab index you # could consider to set DISABLE_INDEX to NO when enabling this option. GENERATE_TREEVIEW = YES # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values # (range [0,1..20]) 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. 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. TREEVIEW_WIDTH = 250 # When 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. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # 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. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax # (see http://www.mathjax.org) which uses client side Javascript for the # rendering instead of using prerendered 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. USE_MATHJAX = NO # When MathJax is enabled you need to specify the location relative to the # HTML output directory using the MATHJAX_RELPATH option. The destination # directory should contain the MathJax.js script. For instance, if the mathjax # directory is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to # the MathJax Content Delivery Network so you can quickly see the result without # installing MathJax. # However, it is strongly recommended to install a local # copy of MathJax from http://www.mathjax.org before deployment. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension # names that should be enabled during MathJax rendering. MATHJAX_EXTENSIONS = # 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. SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a PHP enabled web server instead of at the web client # using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server # based approach is that it scales better to large projects and allows # full text search. The disadvantages are that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4 # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for # the generated latex document. The footer should contain everything after # the last chapter. If it is left blank doxygen will generate a # standard footer. Notice: only use this tag if you know what you are doing! LATEX_FOOTER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See # http://en.wikipedia.org/wiki/BibTeX for more info. LATEX_BIB_STYLE = plain #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load style sheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # pointed to by INCLUDE_PATH will be searched when a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition that # overrules the definition found in the source code. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all references to function-like macros # that are alone on a line, have an all uppercase name, and do not end with a # semicolon, because these will confuse the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. For each # tag file the location of the external documentation should be added. The # format of a tag file without this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths # or URLs. Note that each tag file must have a unique name (where the name does # NOT include the path). If a tag file is not located in the directory in which # doxygen is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option also works with HAVE_DOT disabled, but it is recommended to # install and use dot, since it yields more powerful graphs. CLASS_DIAGRAMS = NO # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will use the Helvetica font for all dot files that # doxygen generates. When you want a differently looking font you can specify # the font name using DOT_FONTNAME. You need to make sure dot is able to find # the font, which can be done by putting it in a standard location or by setting # the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the # directory containing the font. DOT_FONTNAME = Helvetica # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the Helvetica font. # If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to # set the path where dot can find it. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If the UML_LOOK tag is enabled, the fields and methods are shown inside # the class node. If there are many fields or methods and many nodes the # graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS # threshold limits the number of items for each type to make the size more # managable. Set this to 0 for no limit. Note that the threshold may be # exceeded by 50% before the limit is enforced. UML_LIMIT_NUM_FIELDS = 10 # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will generate a graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are svg, png, jpg, or gif. # If left blank png will be used. If you choose svg you need to set # HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible in IE 9+ (other browsers do not have this requirement). DOT_IMAGE_FORMAT = png # If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to # enable generation of interactive SVG images that allow zooming and panning. # Note that this requires a modern browser other than Internet Explorer. # Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you # need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible. Older versions of IE do not have SVG support. INTERACTIVE_SVG = NO # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the # \mscfile command). MSCFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES dhcp-4.4.1/doc/devel/isc-logo.jpg000644 000765 000024 00000010544 13243301226 017001 0ustar00tmarkstaff000000 000000 ÿØÿàJFIFHHÿÛC ÿÛC     ÿÀ8|ÿÄ  ÿÄK!"1QAa $2Vq#&3¡Õ7BD„‘áð4CFRSUtv”•´µÁÑÿÄÿÄA !1A"Qa2q²ðBR‘&5Sbs¡±#$4Ccr’ÂÒÓÿÚ ?ïæGžˆz"L=.Gžˆ&Gžˆ—#ÏDFFˆŒ"2<ôDdyèˆÈóÑ‘ç¢#D\pùBIùç¶3Á6\3Œ{½£Q÷þÜ TDxóÔÂ¥(IY H*RŽ‘É$ù !HSq´­M³e¹;—uûÍÔ¥È[]õGD  Kk®Io¾Ñ ‚#2{R0Vã#‚…2´õëÜi,ª "¼å—Dª·­TŠ40œd%i‰Ð·±áÔòœ_¿¨D$­u#w72Šf%éT“Jz&Ñêš…>@?ÉzÎÙ‡G˜Zߤ$§)6æéÇuVµ)«Sqc0·ž´#(šum-$)j¦¥G’Ô}[*K€ÏJº[R¨`‚“ƒ”NRr=ÿ L((Æ©[›vƒSºkTÚ!°õBªïdÂT †Ò0T·\Y !  ­D€”‚I¤) @¯ÚV;¶Ý^§b×ju¹ökñÑs½9†˜.4…–êzK²‡ú[)w¾C« ï¡R‰N2qŸöêaDÊ\xü8:ˆ•Né8þ¡“÷h’—ÿoÿt…1+ôK²]f6ˆ«ŸÐª ãÙÌyóýz •\.cü¡_ÇE±ÿÂÿÈÔµPT•D¼¾<çáç©PŽ~) ç9ý¼h‹'qç\uÅ©Ç]Q[®(åKR»ÊRÇ ò}Ú"ÇãîóûôDxg>ïïøè‹ÑL˜r£Ì†úâËŠê]‹%¥­·[!IRTžAIär4E-_ñX¼-ú^íS"7ú¤Ÿen$”6ÅqS¨˜”£(¨4æ1ŽÕ/Ý É88>\ÿï:"˜èiþí]rëPì뻋%ë^ÜqC f— »YŒçõÅlÆ Ç)SéñΙvµÈ(Q.úy¦®¨›Â„ªb[Q—k.ã¡Î²‡"$„ŒsŒá¨07P¥:6ËE¢Fb­»µÇ­ä´‡eÀm/Ü2R´‚’ë+)nP2hGƒJë/¦è—7æi6ÕÇÙòïòZ§q®¢+»š¯êÙ—yÎâ]ôNÆ.ËZÞJ±vÚ…Fìþ­j¶Ò. šÔ:”åA*ŒÙÇú˜èòç[µ¯Ú°Mb^ü2Ƶ_KZÁ"Ô6‹}Üïó$þ Þ½‡y7( ¡7:ÐÞ0DhÈl %”¤€|5•?cÖ“~~{Ö²î<×dÝ?Ë” µòkv­ê£ q­èH\¢R›Þƒ Š}R"×Çhë1PÔy‰ÊK­õ¨g¥Ôaµ ö—[ŽG „È'Çxò1à¶Þô«{oT6þ*°œ¸×áË÷Džó²ì~×Ó‘FÛ=»¤ÇšÕYŠe³HˆÍR?PfJX„Óa満WK=C#8<ë›Ô¦ên-x‚ x_BÛ×eÅ&Õ¤Ac€p=àä0es?ӦܮÝ{ùgÐíÊTšÕV]• µ +eÅt¦¡Q*Z°p”$ ©G„Œ’xÔîªá´¶ÖÎȾî·îŠãg.Ú6rÙu†Ôr?›4øçë‚1©EˆÝ:$–­-£´im#êL¬0õÁ5~JuU¨y¢:>í ßmÀHi«Q–PpÌvìûl! Ïu)›ÀHà{üôE‡ÎÚ&á&ÚØ÷JáÒÍ!4gPPvŠä÷’>â8Ñ>¯³×Hè‡&­µµe“К‚nŠ£Î žŽÛscŽGù)ã¢&¥×a\vx‰*§™TZ‘Åç§<™´¹ sÒĶz¢*AÂÑà´¤ñ¢'~ÍÈj«Z«m¼÷’Ý3t š"âºPÕ]$=G‘Î9LÄ!²r;Ž/ÏDQ"¢ÊD•CS."b\ì `“Öêé éñÎxÇá¢)ŸxbÉEåBÚê#+œ­¼¦Ãµbˆžµ?WWçê] ¶ R×P}ÔBR=Ú8OúU:“³,Á1+Û¬¤h\@âÛÇø°>²–“òCGºÎUÔæ·}†…@+\`†õ>'ÃÃùlx¯zG6åÖZ{»`úƒ¡û¬ï=î Ü1åJ‘1÷¥Ëä™RV§dÉyen8µ¥©jVI$òI:ßÀk@€¸=ZªKžI$ä™$žòNäþ+ÎxÆxφ½WŠ\/?Ù㤄HAÁ8ãPH@»—´¤|Õm—?æþÅq-_7•¿Ö³8WôE§î)| \Ýôü¼®*vàÑí tïgÑjö¤I5”Fm ?7éÓ†d¾—e²Ž¤4¥t+G:² 6W:‰Ï xœŸÇöùjU(Ñ¢##DFŠSÎÒ¾ë–r¦1 LTh5b”Ü6µEúlô!ŒŽðUĸ-D Ñ^·)˧·¹;b䦨d´ªÕ Ç{j…·4¯-%n¤$»ÅpÃø#¡Ü8YT¥Æè”¹þ‘Ô[µÈÈM«X†ÖéMa#¥¤Ænœksš<¥"C.°Gžž¡AðZ«uRí*d­Å«+´ÜÝÈõ™´7Ï ¥S¥¸®Þ 2ICóI)hä”5Ô¼åÆÊvÞÑEÓý}@}[NÒï>ƒà¹O¤¾1úºÐ­Ýýsĸ°Ãÿ'dáÍû*ÆúàvÀÀ+ÕèÆÔï¸åÌgæ+:Ò¸¬ÿk·?>Ð]‹Ñè›ïŸ°T °ÛÓVÚYJ¢È®ÙU‚…V"4’Vˤ¤¼Â•Ü'£…!DžðÀ:ÏkÚ35´órÔo³ýñúô+Kà®/­ ¹í{ KwžÐƒ·0èprà !M×öÈí®åÙu=ÑÙ9h„ä&ž“P·Û DwÊ{W˜ «½ä¤õ% +Ž”€BµƒÓõËËíµ½lÌ?´ÞIyš¼‰m<_j\·Ô¤¦ïF±¹·¬æU˜å. KKsÌÐw=Óf¶Ræ³öòñÛkñú\ú=̹*nU÷ÖòS6:c¾“ë›JJCiRêï‘ƨÖ5šW7Tîh60áŒèã¼v¯„¸BçOÓn4û×1ÔêóÁq =¡Ž™­æS‚ÖÙ$íŽß^v̓5u*ÝÐÛÝŒºë½ !ÇZìQÔc0¥¶’N:IQã)bÞóZuýÍ:·ØöwÞNå_é¼#õ6Ÿ^ÞÉüÕ*ìjG(žFŸdvÉÄ€q¤Û-•¸é[Qpm6ãȦͤTwÙ²è²[· ‡HWo¬)·‡Zxá@`fãRÖiÔ½eݸpp‰çÛ³ŒA?g LJxBæßH«¥ß–8žWS&@9ûMþÐ9b#TÝŸnlµkkvâk.L®©b]Z¼êÐ:$)>°¯£°ç% JBp2I™-ÆTµ4§bFm•©²¤¡E%I8% ãݬMÝvV¯R ç8xemú-­K+õ º60pKZHÀÆ;—ÿÙdhcp-4.4.1/doc/devel/libtool.dox000644 000765 000024 00000006567 13243301226 016755 0ustar00tmarkstaff000000 000000 /** @page libtool Build with libtool @section libtoolBuild 4.4.0 introduces the dynamic library build option. It uses Gnu autotools and in particular libtool but is fully backward compatible. The Gnu autoreconf which is a driver for other autotools: it runs autoconf (building configure from configure.ac), autoheader (creating the AC_CONFIG_HEADERS file, includes/config.h for ISC DHC), aclocal (building aclocal.m4 script), automake (creating .in files from .am files), libtoolize (adding libtool support when enabled in configure.ac) and autopoint (for gettext which is not used by ISC DHCP). Static libraries are built using the same setup as in previous versions. This enforces strict backward compatibility and is the default option so users who do not want dynamic libraries have nothing different to do. (In other words changes for the support of dynamic libraries is invisible for legacy users). Dynamic libraries are handled by libtool, and therefore require a few extra steps before invoking configure. For users not reading the documentation but still wanting dynamic libraries "--enable-libtool" is accepted even without these extra steps (cp configure.ac+lt configure.ac; autoreconf -i) in a recovery procedure (invoking config+lt script) but not in a very robust way. Note libtool allows you to build static libraries: there is nothing to make this impossible but as the recommend way to build static libraries does not use libtool it is neither recommended nor supported. The autoreconf input file is configure.ac. There are four versions of this: - configure.ac-base which has code for legacy and libtool options - configure.ac-lt which has only the legacy option with recovery when --with-libtool is given. Note it is not included in the distribution as its configure.ac copy is - configure.ac+lt which has the libool code and defaults to --with-libtool - configure.ac which is either configure.ac-lt in the distribution and configure.ac+lt when copied by the user at the first step for dynamic libraries or by the config+lt recovery script Three scripts manage legacy and libtool options: - util/lt.pl which extracts configure.ac-lt and configure.ac+lt from configure.ac-base - util/regen.sh which invokes util/lt.pl and copies configure.ac-lt to configure.ac and run autoconf. The correct way for developers to update the configure system is to update configure.ac-base and to call util/regen.sh - config+lt which is the recovery script. As it can be called in place of configure it must be executable (i.e. -rwxr-xr-x rights) in the distribution. To perform the reverse recovery, i.e. from libtool to legacy options, the simplest (and most reliable) way is to simply clean the build directory and to restart from the initial step, i.e., to extract the distribution. Automake uses different names for library related variables if libtool is used or not for some Makefile's the procedure is a bit complex. Usually the source Makefile is Makefile.am. Automake is run before the distribution build to generate Makefile.in and at configuration time the config.status script is invoked at the end of ./configure on AC_CONFIG_FILES to substitute @xxx@ variables into Makefile. ISC DHCP uses an extra step with a postconfig phase at the end of ./configure which invokes automake and reruns config.status giving a chain with Makefile.am.in, Makefile.am, Makefile.in and Makefile */ dhcp-4.4.1/doc/devel/mainpage.dox000644 000765 000024 00000002370 13243301226 017056 0ustar00tmarkstaff000000 000000 /** @mainpage This is an ISC DHCP Developer's Guide. This documentation is intended for developers, contributors and other programmers that are interested in internal operation of the code. To download the latest version of the software, please go to the http://www.isc.org/software/dhcp website. @section toc Table Of Contents - Build - @subpage libtool - @subpage libtoolBuild - Server - @subpage ipv6structures - @subpage tests - @subpage testsOverview - @subpage testsAtf - @subpage testsAtfAdding - @subpage testsAtfCoding - @subpage qa - @subpage qaTests - @subpage cppcheck - @subpage doxygen - @subpage valgrind - @subpage debug Pages planned to be written: - @subpage archSrv - @subpage archCli - @subpage omapi - @subpage omapiIntro - @subpage omapiC - @subpage dhcpctl - @subpage contrib - @subpage contribDir - @subpage codingGuidelines Note: some of the links below may not work if corresponding logs are not available.
Doxygen: [generation log] [errors and warnings]
cppcheck: [generation log] [errors and warnings]
*/dhcp-4.4.1/doc/devel/omapi.dox000644 000765 000024 00000000374 13243301226 016404 0ustar00tmarkstaff000000 000000 /** @page omapi OMAPI Interface @section omapiIntro Introduction @todo: Description of OMAPI and dhcpctl @section omapiC Object Management API @todo: Essentially omapi.3 @section dhcpctl dhcpctl Reference Guide @todo: Essentially dhcpctl.3 */ dhcp-4.4.1/doc/devel/qa.dox000644 000765 000024 00000007124 13243301226 015700 0ustar00tmarkstaff000000 000000 /** @page qa Quality Assurance There is a wide scale effort in progress to improve the quality of the ISC DHCP implementation. The following section describes the major aspects of quality assurance that are being implemented. As this is a work in progress, expect radical changes in this area. @section qaTests ATF Unit-tests See @ref tests Section for details description of ATF-based unit-tests. @section cppcheck cppcheck tool cppcheck is a static analysis tool for C/C++ code. Unlike C/C++ compilers and many other analysis tools it does not detect syntax errors in the code. Cppcheck primarily detects the types of bugs that the compilers normally do not detect. To generate cppcheck report, you must have cppcheck installed in your system. Generation is simple: @verbatim cd doc/ make cppcheck @endverbatim The log files will be stored in doc/html/cppcheck.log and doc/html/cppcheck-error.log. While the former is useful for verifying that all sources were checked, the latter is much more useful. It contains a list of problems that were detected by cppcheck. The goal is to correct all problems and make this an empty file. In the unlikely event of cppcheck finding false positives it is possible to add special comments formatted to instruct cppcheck to not report what it thinks is an issue. make cppcheck target is configured to make cppcheck print out a specific issue type reported. For example to disable the following error report: @verbatim bind/bind-9.8.1/bin/dnssec/dnssec-keygen.c:522: check_fail: Memory leak: algname (error,memleak) @endverbatim the following line could be added before line 522 in dnssec-keygen.c: @verbatim // cppcheck-suppress memleak @endverbatim Please consult cppcheck manual for details. It is section 6.2 "Inline suppressions" in cppcheck 1.54 manual. Section number may change in later versions. @section doxygen Doxygen checks ISC DHCP Developer's Guide (the documentation you are reading now) is generated with doxygen. Doxygen is an open source tool for generating source code documentation. It is available from www.doxygen.org website. Once Doxygen is installed, ISC DHCP documentation can be generated with: @verbatim cd doc make devel @endverbatim Note that cppcheck (see @ref cppcheck Section) reports are linked from Developer's Guide. It is useful to generate both. @section systemTests System level tests ISC is developing a comprehensive set of system level tests. They are described by a separate document called DHCP Test Plan. @section perfdhcp Performance tests using perfdhcp ISC is also developing a performance measurement tool, called perfdhcp. Its main purpose is to measure performance of DHCPv4 and DHCPv6 servers. It is being developed as part of the BIND10 project. See tests/tools/perfdhcp directory in BIND10 source code. @section tahiTests Conformance tests using TAHI TAHI project developed an extensive suite of DHCPv6 conformance tests. ISC plans to deploy and run them periodically in the near future. @section valgrind Memory correctness using valgrind Valgrind is a powerful tool for dynamic code analysis. It allows running existing code (often even without recompiling) in a special environment that tracks memory operations. In particular, it is able to detect: memory leaks, buffer overflows, usage of uninitialized memory, double frees and similar errors. We currently do not use valgrind in ISC DHCP testing, but there are plans for starting to use it. */ dhcp-4.4.1/dhcpctl/callback.c000644 000765 000024 00000011536 13243301226 016242 0ustar00tmarkstaff000000 000000 /* callback.c The dhcpctl callback object. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1999-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #include #include "dhcpctl.h" /* dhcpctl_set_callback synchronous, with asynchronous aftereffect handle is some object upon which some kind of process has been started - e.g., an open, an update or a refresh. data is an anonymous pointer containing some information that the callback will use to figure out what event completed. return value of 0 means callback was successfully set, a nonzero status code is returned otherwise. Upon completion of whatever task is in process, the callback will be passed the handle to the object, a status code indicating what happened, and the anonymous pointer passed to */ dhcpctl_status dhcpctl_set_callback (dhcpctl_handle h, void *data, void (*func) (dhcpctl_handle, dhcpctl_status, void *)) { dhcpctl_callback_object_t *callback; omapi_object_t *inner; callback = dmalloc (sizeof *callback, MDL); if (!callback) return ISC_R_NOMEMORY; /* Tie the callback object to the innermost object in the chain. */ for (inner = h; inner -> inner; inner = inner -> inner) ; omapi_object_reference (&inner -> inner, (omapi_object_t *)callback, MDL); omapi_object_reference ((omapi_object_t **)&callback -> outer, inner, MDL); /* Save the actual handle pointer we were passed for the callback. */ omapi_object_reference (&callback -> object, h, MDL); callback -> data = data; callback -> callback = func; return ISC_R_SUCCESS; } /* Callback methods (not meant to be called directly) */ isc_result_t dhcpctl_callback_set_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_typed_data_t *value) { if (h -> type != dhcpctl_callback_type) return DHCP_R_INVALIDARG; if (h -> inner && h -> inner -> type -> set_value) return (*(h -> inner -> type -> set_value)) (h -> inner, id, name, value); return ISC_R_NOTFOUND; } isc_result_t dhcpctl_callback_get_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_value_t **value) { if (h -> type != dhcpctl_callback_type) return DHCP_R_INVALIDARG; if (h -> inner && h -> inner -> type -> get_value) return (*(h -> inner -> type -> get_value)) (h -> inner, id, name, value); return ISC_R_NOTFOUND; } isc_result_t dhcpctl_callback_signal_handler (omapi_object_t *o, const char *name, va_list ap) { dhcpctl_callback_object_t *p; isc_result_t waitstatus; if (o -> type != dhcpctl_callback_type) return DHCP_R_INVALIDARG; p = (dhcpctl_callback_object_t *)o; /* Not a signal we recognize? */ if (strcmp (name, "ready")) { if (p -> inner && p -> inner -> type -> signal_handler) return (*(p -> inner -> type -> signal_handler)) (p -> inner, name, ap); return ISC_R_NOTFOUND; } if (p -> object -> type == dhcpctl_remote_type) { waitstatus = (((dhcpctl_remote_object_t *) (p -> object)) -> waitstatus); } else waitstatus = ISC_R_SUCCESS; /* Do the callback. */ if (p -> callback) (*(p -> callback)) (p -> object, waitstatus, p -> data); return ISC_R_SUCCESS; } isc_result_t dhcpctl_callback_destroy (omapi_object_t *h, const char *file, int line) { dhcpctl_callback_object_t *p; if (h -> type != dhcpctl_callback_type) return DHCP_R_INVALIDARG; p = (dhcpctl_callback_object_t *)h; if (p -> handle) omapi_object_dereference ((omapi_object_t **)&p -> handle, file, line); return ISC_R_SUCCESS; } /* Write all the published values associated with the object through the specified connection. */ isc_result_t dhcpctl_callback_stuff_values (omapi_object_t *c, omapi_object_t *id, omapi_object_t *p) { if (p -> type != dhcpctl_callback_type) return DHCP_R_INVALIDARG; if (p -> inner && p -> inner -> type -> stuff_values) return (*(p -> inner -> type -> stuff_values)) (c, id, p -> inner); return ISC_R_SUCCESS; } dhcp-4.4.1/dhcpctl/cltest.c000644 000765 000024 00000013106 13243301226 015777 0ustar00tmarkstaff000000 000000 /* cltest.c Example program that uses the dhcpctl library. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 2000-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * * This software was contributed to Internet Systems Consortium * by Brian Murrell. */ #include "config.h" #include #include #include #include #include #include #include "omapip/result.h" #include "dhcpctl.h" #include "dhcpd.h" /* Fixups */ isc_result_t find_class (struct class **c, const char *n, const char *f, int l) { return 0; } int parse_allow_deny (struct option_cache **oc, struct parse *cfile, int flag) { return 0; } void dhcp (struct packet *packet) { } void bootp (struct packet *packet) { } #ifdef DHCPv6 /* XXX: should we warn or something here? */ void dhcpv6(struct packet *packet) { } #ifdef DHCP4o6 isc_result_t dhcpv4o6_handler(omapi_object_t *h) { return ISC_R_NOTIMPLEMENTED; } #endif /* DHCP4o6 */ #endif /* DHCPv6 */ int check_collection (struct packet *p, struct lease *l, struct collection *c) { return 0; } void classify (struct packet *packet, struct class *class) { } isc_result_t dhcp_set_control_state (control_object_state_t oldstate, control_object_state_t newstate) { return ISC_R_SUCCESS; } int main (int, char **); enum modes { up, down, undefined }; static void usage (char *s) { fprintf (stderr, "Usage: %s [-n ] [-p ] [-a ]" "(-u | -d) \n", s); exit (1); } int main (argc, argv) int argc; char **argv; { isc_result_t status, waitstatus; dhcpctl_handle authenticator; dhcpctl_handle connection; dhcpctl_handle interface_handle; dhcpctl_data_string result; int i; int mode = undefined; const char *interface = 0; const char *action; for (i = 1; i < argc; i++) { if (!strcmp (argv[i], "-u")) { mode = up; } else if (!strcmp (argv [i], "-d")) { mode = down; } else if (argv[i][0] == '-') { usage(argv[0]); } else { interface = argv[i]; } } if (!interface) usage(argv[0]); if (mode == undefined) usage(argv[0]); status = dhcpctl_initialize (); if (status != ISC_R_SUCCESS) { fprintf (stderr, "dhcpctl_initialize: %s\n", isc_result_totext (status)); exit (1); } authenticator = dhcpctl_null_handle; connection = dhcpctl_null_handle; status = dhcpctl_connect (&connection, "127.0.0.1", 7911, authenticator); if (status != ISC_R_SUCCESS) { fprintf (stderr, "dhcpctl_connect: %s\n", isc_result_totext (status)); exit (1); } interface_handle = dhcpctl_null_handle; status = dhcpctl_new_object (&interface_handle, connection, "interface"); if (status != ISC_R_SUCCESS) { fprintf (stderr, "dhcpctl_new_object: %s\n", isc_result_totext (status)); exit (1); } status = dhcpctl_set_string_value (interface_handle, interface, "name"); if (status != ISC_R_SUCCESS) { fprintf (stderr, "dhcpctl_set_value: %s\n", isc_result_totext (status)); exit (1); } if (mode == up) { /* "up" the interface */ printf ("upping interface %s\n", interface); action = "create"; status = dhcpctl_open_object (interface_handle, connection, DHCPCTL_CREATE | DHCPCTL_EXCL); if (status != ISC_R_SUCCESS) { fprintf (stderr, "dhcpctl_open_object: %s\n", isc_result_totext (status)); exit (1); } } else { /* down the interface */ printf ("downing interface %s\n", interface); action = "remove"; status = dhcpctl_open_object (interface_handle, connection, 0); if (status != ISC_R_SUCCESS) { fprintf (stderr, "dhcpctl_open_object: %s\n", isc_result_totext (status)); exit (1); } status = dhcpctl_wait_for_completion (interface_handle, &waitstatus); if (status != ISC_R_SUCCESS) { fprintf (stderr, "dhcpctl_wait_for_completion: %s\n", isc_result_totext (status)); exit (1); } if (waitstatus != ISC_R_SUCCESS) { fprintf (stderr, "dhcpctl_wait_for_completion: %s\n", isc_result_totext (waitstatus)); exit (1); } status = dhcpctl_object_remove (connection, interface_handle); if (status != ISC_R_SUCCESS) { fprintf (stderr, "dhcpctl_open_object: %s\n", isc_result_totext (status)); exit (1); } } status = dhcpctl_wait_for_completion (interface_handle, &waitstatus); if (status != ISC_R_SUCCESS) { fprintf (stderr, "dhcpctl_wait_for_completion: %s\n", isc_result_totext (status)); exit (1); } if (waitstatus != ISC_R_SUCCESS) { fprintf (stderr, "interface object %s: %s\n", action, isc_result_totext (waitstatus)); exit (1); } memset (&result, 0, sizeof result); status = dhcpctl_get_value (&result, interface_handle, "state"); if (status != ISC_R_SUCCESS) { fprintf (stderr, "dhcpctl_get_value: %s\n", isc_result_totext (status)); exit (1); } exit (0); } dhcp-4.4.1/dhcpctl/dhcpctl.3000644 000765 000024 00000026416 13243301226 016052 0ustar00tmarkstaff000000 000000 .\" -*- nroff -*- .\" .\" Project: DHCP .\" File: dhcpctl.3 .\" RCSId: $Id: dhcpctl.3,v 1.9 2011/04/25 23:43:16 sar Exp $ .\" .\" Copyright (c) 2011,2014 by Internet Systems Consortium, Inc. ("ISC") .\" Copyright (c) 2004,2009 by Internet Systems Consortium, Inc. ("ISC") .\" Copyright (c) 2000-2003 by Internet Software Consortium .\" Copyright (c) 2000 Nominum, Inc. .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT .\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" Internet Systems Consortium, Inc. .\" 950 Charter Street .\" Redwood City, CA 94063 .\" .\" https://www.isc.org/ .\" .\" Description: dhcpctl man page. .\" .\" .Dd Nov 15, 2000 .Dt DHCPCTL 3 .Os DHCP 3 .ds vT DHCP Programmer's Manual .\" .\" .\" .Sh NAME .Nm dhcpctl_initialize .Nd dhcpctl library initialization. .\" .\" .\" .Sh SYNOPSIS .Fd #include .Ft dhcpctl_status .Fo dhcpctl_initialize .Fa void .Fc .\" .Ft dhcpctl_status .Fo dhcpctl_connect .Fa "dhcpctl_handle *cxn" .Fa "const char *host" .Fa "int port" .Fa "dhcpctl_handle auth" .Fc .\" .\" .\" .Ft dhcpctl_status .Fo dhcpctl_wait_for_completion .Fa "dhcpctl_handle object" .Fa "dhcpctl_status *status" .Fc .\" .\" .\" .Ft dhcpctl_status .Fo dhcpctl_get_value .Fa "dhcpctl_data_string *value" .Fa "dhcpctl_handle object" .Fa "const char *name" .Fc .\" .\" .\" .Ft dhcpctl_status .Fo dhcpctl_get_boolean .Fa "int *value" .Fa "dhcpctl_handle object" .Fa "const char *name" .Fc .\" .\" .\" .Ft dhcpctl_status .Fo dhcpctl_set_value .Fa "dhcpctl_handle object" .Fa "dhcpctl_data_string value" .Fa "const char *name" .Fc .\" .\" .\" .Ft dhcpctl_status .Fo dhcpctl_set_string_value .Fa "dhcpctl_handle object" .Fa "const char *value" .Fa "const char *name" .Fc .\" .\" .\" .Ft dhcpctl_status .Fo dhcpctl_set_boolean_value .Fa "dhcpctl_handle object" .Fa "int value" .Fa "const char *name" .Fc .\" .\" .\" .Ft dhcpctl_status .Fo dhcpctl_set_int_value .Fa "dhcpctl_handle object" .Fa "int value" .Fa "const char *name" .Fc .\" .\" .\" .Ft dhcpctl_status .Fo dhcpctl_object_update .Fa "dhcpctl_handle connection" .Fa "dhcpctl_handle object" .Fc .\" .\" .\" .Ft dhcpctl_status .Fo dhcpctl_object_refresh .Fa "dhcpctl_handle connection" .Fa "dhcpctl_handle object" .Fc .\" .\" .\" .Ft dhcpctl_status .Fo dhcpctl_object_remove .Fa "dhcpctl_handle connection" .Fa "dhcpctl_handle object" .Fc .\" .\" .\" .Ft dhcpctl_status .Fo dhcpctl_set_callback .Fa "dhcpctl_handle object" .Fa "void *data" .Fa "void (*function) (dhcpctl_handle, dhcpctl_status, void *)" .Fc .\" .\" .\" .Ft dhcpctl_status .Fo dhcpctl_new_authenticator .Fa "dhcpctl_handle *object" .Fa "const char *name" .Fa "const char *algorithm" .Fa "const char *secret" .Fa "unsigned secret_len" .Fc .\" .\" .\" .Ft dhcpctl_status .Fo dhcpctl_new_object .Fa "dhcpctl_handle *object" .Fa "dhcpctl_handle connection" .Fa "const char *object_type" .Fc .\" .\" .\" .Ft dhcpctl_status .Fo dhcpctl_open_object .Fa "dhcpctl_handle object" .Fa "dhcpctl_handle connection" .Fa "int flags" .Fc .\" .\" .\" .Ft isc_result_t .Fo omapi_data_string_new .Fa dhcpctl_data_string *data .Fa unsigned int length .Fa const char *filename, .Fa int lineno .Fc .\" .\" .\" .Ft isc_result_t .Fo dhcpctl_data_string_dereference .Fa "dhcpctl_data_string *" .Fa "const char *" .Fa "int" .Fc .Sh DESCRIPTION The dhcpctl set of functions provide an API that can be used to communicate with and manipulate a running ISC DHCP server. All functions return a value of .Dv isc_result_t . The return values reflects the result of operations to local data structures. If an operation fails on the server for any reason, then the error result will be returned through the second parameter of the .Fn dhcpctl_wait_for_completion call. .\" .\" .\" .Pp .Fn dhcpctl_initialize sets up the data structures the library needs to do its work. This function must be called once before any other. .Pp .Fn dhcpctl_connect opens a connection to the DHCP server at the given host and port. If an authenticator has been created for the connection, then it is given as the 4th argument. On a successful return the address pointed at by the first argument will have a new connection object assigned to it. .Pp For example: .Bd -literal -offset indent s = dhcpctl_connect(&cxn, "127.0.0.1", 7911, NULL); .Ed .Pp connects to the DHCP server on the localhost via port 7911 (the standard OMAPI port). No authentication is used for the connection. .\" .\" .\" .Pp .Fn dhcpctl_wait_for_completion flushes a pending message to the server and waits for the response. The result of the request as processed on the server is returned via the second parameter. .Bd -literal -offset indent s = dhcpctl_wait_for_completion(cxn, &wv); if (s != ISC_R_SUCCESS) local_failure(s); else if (wv != ISC_R_SUCCESS) server_failure(wc); .Ed .Pp The call to .Fn dhcpctl_wait_for_completion won't return until the remote message processing completes or the connection to the server is lost. .\" .\" .\" .Pp .Fn dhcpctl_get_value extracts a value of an attribute from the handle. The value can be of any length and is treated as a sequence of bytes. The handle must have been created first with .Fn dhcpctl_new_object and opened with .Fn dhcpctl_open_object . The value is returned via the parameter named .Dq value . The last parameter is the name of attribute to retrieve. .Bd -literal -offset indent dhcpctl_data_string value = NULL; dhcpctl_handle lease; time_t thetime; s = dhcpctl_get_value (&value, lease, "ends"); assert(s == ISC_R_SUCCESS && value->len == sizeof(thetime)); memcpy(&thetime, value->value, value->len); .Ed .\" .\" .\" .Pp .Fn dhcpctl_get_boolean extracts a boolean valued attribute from the object handle. .\" .\" .\" .Pp The .Fn dhcpctl_set_value , .Fn dhcpctl_set_string_value , .Fn dhcpctl_set_boolean_value , and .Fn dhcpctl_set_int_value functions all set a value on the object handle. .\" .\" .\" .Pp .Fn dhcpctl_object_update function queues a request for all the changes made to the object handle be sent to the remote for processing. The changes made to the attributes on the handle will be applied to remote object if permitted. .\" .\" .\" .Pp .Fn dhcpctl_object_refresh queues up a request for a fresh copy of all the attribute values to be sent from the remote to refresh the values in the local object handle. .\" .\" .\" .Pp .Fn dhcpctl_object_remove queues a request for the removal on the server of the object referenced by the handle. .\" .\" .\" .Pp The .Fn dhcpctl_set_callback function sets up a user-defined function to be called when an event completes on the given object handle. This is needed for asynchronous handling of events, versus the synchronous handling given by .Fn dhcpctl_wait_for_completion . When the function is called the first parameter is the object the event arrived for, the second is the status of the message that was processed, the third is the same value as the second parameter given to .Fn dhcpctl_set_callback . .\" .\" .\" .Pp The .Fn dhcpctl_new_authenticator creates a new authenticator object to be used for signing the messages that cross over the network. The .Dq name , .Dq algorithm , and .Dq secret values must all match what the server uses and are defined in its configuration file. The created object is returned through the first parameter and must be used as the 4th parameter to .Fn dhcpctl_connect . Note that the 'secret' value must not be base64 encoded, which is different from how the value appears in the dhcpd.conf file. .\" .\" .\" .Pp .Fn dhcpctl_new_object creates a local handle for an object on the server. The .Dq object_type parameter is the ascii name of the type of object being accessed. e.g. .Qq lease . This function only sets up local data structures, it does not queue any messages to be sent to the remote side, .Fn dhcpctl_open_object does that. .\" .\" .\" .Pp .Fn dhcpctl_open_object builds and queues the request to the remote side. This function is used with handle created via .Fn dhcpctl_new_object . The flags argument is a bit mask with the following values available for setting: .Bl -tag -offset indent -width 20 .It DHCPCTL_CREATE if the object does not exist then the remote will create it .It DHCPCTL_UPDATE update the object on the remote side using the attributes already set in the handle. .It DHCPCTL_EXCL return and error if the object exists and DHCPCTL_CREATE was also specified .El .\" .\" .\" .Pp The .Fn omapi_data_string_new function allocates a new .Ft dhcpctl_data_string object. The data string will be large enough to hold .Dq length bytes of data. The .Dq file and .Dq lineno arguments are the source file location the call is made from, typically by using the .Dv __FILE__ and .Dv __LINE__ macros or the .Dv MDL macro defined in . .\" .\" .\" .Pp .Fn dhcpctl_data_string_dereference deallocates a data string created by .Fn omapi_data_string_new . The memory for the object won't be freed until the last reference is released. .Sh EXAMPLES .Pp The following program will connect to the DHCP server running on the local host and will get the details of the existing lease for IP address 10.0.0.101. It will then print out the time the lease is due to expire. Note that most error checking has been omitted for brevity. .Bd -literal -offset indent #include #include #include #include #include #include #include #include #include "omapip/result.h" #include "dhcpctl.h" int main (int argc, char **argv) { dhcpctl_data_string ipaddrstring = NULL; dhcpctl_data_string value = NULL; dhcpctl_handle connection = NULL; dhcpctl_handle lease = NULL; isc_result_t waitstatus; struct in_addr convaddr; time_t thetime; dhcpctl_initialize (); dhcpctl_connect (&connection, "127.0.0.1", 7911, 0); dhcpctl_new_object (&lease, connection, "lease"); memset (&ipaddrstring, 0, sizeof ipaddrstring); inet_pton(AF_INET, "10.0.0.101", &convaddr); omapi_data_string_new (&ipaddrstring, 4, MDL); memcpy(ipaddrstring->value, &convaddr.s_addr, 4); dhcpctl_set_value (lease, ipaddrstring, "ip-address"); dhcpctl_open_object (lease, connection, 0); dhcpctl_wait_for_completion (lease, &waitstatus); if (waitstatus != ISC_R_SUCCESS) { /* server not authoritative */ exit (0); } dhcpctl_data_string_dereference(&ipaddrstring, MDL); dhcpctl_get_value (&value, lease, "ends"); memcpy(&thetime, value->value, value->len); dhcpctl_data_string_dereference(&value, MDL); fprintf (stdout, "ending time is %s", ctime(&thetime)); } .Ed .Sh SEE ALSO omapi(3), omshell(1), dhcpd(8), dhclient(8), dhcpd.conf(5), dhclient.conf(5). .Sh AUTHOR .Em dhcpctl is maintained by ISC. To learn more about Internet Systems Consortium, see .B https://www.isc.org dhcp-4.4.1/dhcpctl/dhcpctl.c000644 000765 000024 00000042051 13243301226 016123 0ustar00tmarkstaff000000 000000 /* dhcpctl.c Subroutines providing general support for objects. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1999-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #include #include "dhcpctl.h" omapi_object_type_t *dhcpctl_callback_type; omapi_object_type_t *dhcpctl_remote_type; /* dhcpctl_initialize () Must be called before any other dhcpctl function. */ dhcpctl_status dhcpctl_initialize () { isc_result_t status; /* Set up the isc and dns library managers */ status = dhcp_context_create(DHCP_CONTEXT_PRE_DB | DHCP_CONTEXT_POST_DB, NULL, NULL); if (status != ISC_R_SUCCESS) return status; status = omapi_init(); if (status != ISC_R_SUCCESS) return status; status = omapi_object_type_register (&dhcpctl_callback_type, "dhcpctl-callback", dhcpctl_callback_set_value, dhcpctl_callback_get_value, dhcpctl_callback_destroy, dhcpctl_callback_signal_handler, dhcpctl_callback_stuff_values, 0, 0, 0, 0, 0, 0, sizeof (dhcpctl_callback_object_t), 0, RC_MISC); if (status != ISC_R_SUCCESS) return status; status = omapi_object_type_register (&dhcpctl_remote_type, "dhcpctl-remote", dhcpctl_remote_set_value, dhcpctl_remote_get_value, dhcpctl_remote_destroy, dhcpctl_remote_signal_handler, dhcpctl_remote_stuff_values, 0, 0, 0, 0, 0, 0, sizeof (dhcpctl_remote_object_t), 0, RC_MISC); if (status != ISC_R_SUCCESS) return status; return ISC_R_SUCCESS; } /* dhcpctl_connect synchronous returns nonzero status code if it didn't connect, zero otherwise stores connection handle through connection, which can be used for subsequent access to the specified server. server_name is the name of the server, and port is the TCP port on which it is listening. authinfo is the handle to an object containing authentication information. */ dhcpctl_status dhcpctl_connect (dhcpctl_handle *connection, const char *server_name, int port, dhcpctl_handle authinfo) { isc_result_t status; status = omapi_generic_new (connection, MDL); if (status != ISC_R_SUCCESS) { return status; } status = omapi_protocol_connect (*connection, server_name, (unsigned)port, authinfo); if (status == ISC_R_SUCCESS) return status; if (status != DHCP_R_INCOMPLETE) { omapi_object_dereference (connection, MDL); return status; } status = omapi_wait_for_completion (*connection, 0); if (status != ISC_R_SUCCESS) { omapi_object_dereference (connection, MDL); return status; } return status; } /* dhcpctl_wait_for_completion synchronous returns zero if the callback completes, a nonzero status if there was some problem relating to the wait operation. The status of the queued request will be stored through s, and will also be either zero for success or nonzero for some kind of failure. Never returns until completion or until the connection to the server is lost. This performs the same function as dhcpctl_set_callback and the subsequent callback, for programs that want to do inline execution instead of using callbacks. */ dhcpctl_status dhcpctl_wait_for_completion (dhcpctl_handle h, dhcpctl_status *s) { isc_result_t status; status = omapi_wait_for_completion (h, 0); if (status != ISC_R_SUCCESS) return status; if (h -> type == dhcpctl_remote_type) *s = ((dhcpctl_remote_object_t *)h) -> waitstatus; return ISC_R_SUCCESS; } /* dhcpctl_get_value synchronous returns zero if the call succeeded, a nonzero status code if it didn't. result is the address of an empty data string (initialized with bzero or cleared with data_string_forget). On successful completion, the addressed data string will contain the value that was fetched. dhcpctl_handle refers to some dhcpctl item value_name refers to some value related to that item - e.g., for a handle associated with a completed host lookup, value could be one of "hardware-address", "dhcp-client-identifier", "known" or "client-hostname". */ dhcpctl_status dhcpctl_get_value (dhcpctl_data_string *result, dhcpctl_handle h, const char *value_name) { isc_result_t status; omapi_value_t *tv = (omapi_value_t *)0; unsigned len; int ip; status = omapi_get_value_str (h, (omapi_object_t *)0, value_name, &tv); if (status != ISC_R_SUCCESS) return status; switch (tv -> value -> type) { case omapi_datatype_int: len = sizeof (int); break; case omapi_datatype_string: case omapi_datatype_data: len = tv -> value -> u.buffer.len; break; case omapi_datatype_object: len = sizeof (omapi_handle_t); break; default: omapi_typed_data_dereference (&tv -> value, MDL); return ISC_R_UNEXPECTED; } status = omapi_data_string_new (result, len, MDL); if (status != ISC_R_SUCCESS) { omapi_typed_data_dereference (&tv -> value, MDL); return status; } switch (tv -> value -> type) { case omapi_datatype_int: ip = htonl (tv -> value -> u.integer); memcpy ((*result) -> value, &ip, sizeof ip); break; case omapi_datatype_string: case omapi_datatype_data: memcpy ((*result) -> value, tv -> value -> u.buffer.value, tv -> value -> u.buffer.len); break; case omapi_datatype_object: ip = htonl (tv -> value -> u.object -> handle); memcpy ((*result) -> value, &ip, sizeof ip); break; } omapi_value_dereference (&tv, MDL); return ISC_R_SUCCESS; } /* dhcpctl_get_boolean like dhcpctl_get_value, but more convenient for boolean values, since no data_string needs to be dealt with. */ dhcpctl_status dhcpctl_get_boolean (int *result, dhcpctl_handle h, const char *value_name) { isc_result_t status; dhcpctl_data_string data = (dhcpctl_data_string)0; int rv; status = dhcpctl_get_value (&data, h, value_name); if (status != ISC_R_SUCCESS) return status; if (data -> len != sizeof rv) { omapi_data_string_dereference (&data, MDL); return ISC_R_UNEXPECTED; } memcpy (&rv, data -> value, sizeof rv); *result = ntohl (rv); omapi_data_string_dereference (&data, MDL); return ISC_R_SUCCESS; } /* dhcpctl_set_value Sets a value on an object referred to by a dhcpctl_handle. The opposite of dhcpctl_get_value. Does not update the server - just sets the value on the handle. */ dhcpctl_status dhcpctl_set_value (dhcpctl_handle h, dhcpctl_data_string value, const char *value_name) { isc_result_t status; omapi_typed_data_t *tv = (omapi_typed_data_t *)0; omapi_data_string_t *name = (omapi_data_string_t *)0; status = omapi_data_string_new (&name, strlen (value_name), MDL); if (status != ISC_R_SUCCESS) return status; memcpy (name -> value, value_name, strlen (value_name)); status = omapi_typed_data_new (MDL, &tv, omapi_datatype_data, value -> len); if (status != ISC_R_SUCCESS) { omapi_data_string_dereference (&name, MDL); return status; } memcpy (tv -> u.buffer.value, value -> value, value -> len); status = omapi_set_value (h, (omapi_object_t *)0, name, tv); omapi_data_string_dereference (&name, MDL); omapi_typed_data_dereference (&tv, MDL); return status; } /* dhcpctl_set_string_value Sets a NUL-terminated ASCII value on an object referred to by a dhcpctl_handle. like dhcpctl_set_value, but saves the trouble of creating a data_string for a NUL-terminated string. Does not update the server - just sets the value on the handle. */ dhcpctl_status dhcpctl_set_string_value (dhcpctl_handle h, const char *value, const char *value_name) { isc_result_t status; omapi_typed_data_t *tv = (omapi_typed_data_t *)0; omapi_data_string_t *name = (omapi_data_string_t *)0; status = omapi_data_string_new (&name, strlen (value_name), MDL); if (status != ISC_R_SUCCESS) return status; memcpy (name -> value, value_name, strlen (value_name)); status = omapi_typed_data_new (MDL, &tv, omapi_datatype_string, value); if (status != ISC_R_SUCCESS) { omapi_data_string_dereference (&name, MDL); return status; } status = omapi_set_value (h, (omapi_object_t *)0, name, tv); omapi_data_string_dereference (&name, MDL); omapi_typed_data_dereference (&tv, MDL); return status; } /* dhcpctl_set_buffer_value Sets a value on an object referred to by a dhcpctl_handle. like dhcpctl_set_value, but saves the trouble of creating a data_string for string for which we have a buffer and length. Does not update the server - just sets the value on the handle. */ dhcpctl_status dhcpctl_set_data_value (dhcpctl_handle h, const char *value, unsigned len, const char *value_name) { isc_result_t status; omapi_typed_data_t *tv = (omapi_typed_data_t *)0; omapi_data_string_t *name = (omapi_data_string_t *)0; unsigned ll; ll = strlen (value_name); status = omapi_data_string_new (&name, ll, MDL); if (status != ISC_R_SUCCESS) return status; memcpy (name -> value, value_name, ll); status = omapi_typed_data_new (MDL, &tv, omapi_datatype_data, len, value); if (status != ISC_R_SUCCESS) { omapi_data_string_dereference (&name, MDL); return status; } memcpy (tv -> u.buffer.value, value, len); status = omapi_set_value (h, (omapi_object_t *)0, name, tv); omapi_data_string_dereference (&name, MDL); omapi_typed_data_dereference (&tv, MDL); return status; } /* dhcpctl_set_null_value Sets a null value on an object referred to by a dhcpctl_handle. */ dhcpctl_status dhcpctl_set_null_value (dhcpctl_handle h, const char *value_name) { isc_result_t status; omapi_data_string_t *name = (omapi_data_string_t *)0; unsigned ll; ll = strlen (value_name); status = omapi_data_string_new (&name, ll, MDL); if (status != ISC_R_SUCCESS) return status; memcpy (name -> value, value_name, ll); status = omapi_set_value (h, (omapi_object_t *)0, name, (omapi_typed_data_t *)0); omapi_data_string_dereference (&name, MDL); return status; } /* dhcpctl_set_boolean_value Sets a boolean value on an object - like dhcpctl_set_value, only more convenient for booleans. */ dhcpctl_status dhcpctl_set_boolean_value (dhcpctl_handle h, int value, const char *value_name) { isc_result_t status; omapi_typed_data_t *tv = (omapi_typed_data_t *)0; omapi_data_string_t *name = (omapi_data_string_t *)0; status = omapi_data_string_new (&name, strlen (value_name), MDL); if (status != ISC_R_SUCCESS) return status; memcpy (name -> value, value_name, strlen (value_name)); status = omapi_typed_data_new (MDL, &tv, omapi_datatype_int, value); if (status != ISC_R_SUCCESS) { omapi_data_string_dereference (&name, MDL); return status; } status = omapi_set_value (h, (omapi_object_t *)0, name, tv); omapi_data_string_dereference (&name, MDL); omapi_typed_data_dereference (&tv, MDL); return status; } /* dhcpctl_set_int_value Sets a boolean value on an object - like dhcpctl_set_value, only more convenient for booleans. */ dhcpctl_status dhcpctl_set_int_value (dhcpctl_handle h, int value, const char *value_name) { isc_result_t status; omapi_typed_data_t *tv = (omapi_typed_data_t *)0; omapi_data_string_t *name = (omapi_data_string_t *)0; status = omapi_data_string_new (&name, strlen (value_name), MDL); if (status != ISC_R_SUCCESS) return status; memcpy (name -> value, value_name, strlen (value_name)); status = omapi_typed_data_new (MDL, &tv, omapi_datatype_int, value); if (status != ISC_R_SUCCESS) { omapi_data_string_dereference (&name, MDL); return status; } status = omapi_set_value (h, (omapi_object_t *)0, name, tv); omapi_data_string_dereference (&name, MDL); omapi_typed_data_dereference (&tv, MDL); return status; } /* dhcpctl_object_update Queues an update on the object referenced by the handle (there can't be any other work in progress on the handle). An update means local parameters will be sent to the server. */ dhcpctl_status dhcpctl_object_update (dhcpctl_handle connection, dhcpctl_handle h) { isc_result_t status; omapi_object_t *message = (omapi_object_t *)0; dhcpctl_remote_object_t *ro; if (h -> type != dhcpctl_remote_type) return DHCP_R_INVALIDARG; ro = (dhcpctl_remote_object_t *)h; status = omapi_message_new (&message, MDL); if (status != ISC_R_SUCCESS) { omapi_object_dereference (&message, MDL); return status; } status = omapi_set_int_value (message, (omapi_object_t *)0, "op", OMAPI_OP_UPDATE); if (status != ISC_R_SUCCESS) { omapi_object_dereference (&message, MDL); return status; } status = omapi_set_object_value (message, (omapi_object_t *)0, "object", h); if (status != ISC_R_SUCCESS) { omapi_object_dereference (&message, MDL); return status; } status = omapi_set_int_value (message, (omapi_object_t *)0, "handle", (int)(ro -> remote_handle)); if (status != ISC_R_SUCCESS) { omapi_object_dereference (&message, MDL); return status; } omapi_message_register (message); status = omapi_protocol_send_message (connection -> outer, (omapi_object_t *)0, message, (omapi_object_t *)0); omapi_object_dereference (&message, MDL); return status; } /* Requests a refresh on the object referenced by the handle (there can't be any other work in progress on the handle). A refresh means local parameters are updated from the server. */ dhcpctl_status dhcpctl_object_refresh (dhcpctl_handle connection, dhcpctl_handle h) { isc_result_t status; omapi_object_t *message = (omapi_object_t *)0; dhcpctl_remote_object_t *ro; if (h -> type != dhcpctl_remote_type) return DHCP_R_INVALIDARG; ro = (dhcpctl_remote_object_t *)h; status = omapi_message_new (&message, MDL); if (status != ISC_R_SUCCESS) { omapi_object_dereference (&message, MDL); return status; } status = omapi_set_int_value (message, (omapi_object_t *)0, "op", OMAPI_OP_REFRESH); if (status != ISC_R_SUCCESS) { omapi_object_dereference (&message, MDL); return status; } status = omapi_set_int_value (message, (omapi_object_t *)0, "handle", (int)(ro -> remote_handle)); if (status != ISC_R_SUCCESS) { omapi_object_dereference (&message, MDL); return status; } omapi_message_register (message); status = omapi_protocol_send_message (connection -> outer, (omapi_object_t *)0, message, (omapi_object_t *)0); /* We don't want to send the contents of the object down the wire, but we do need to reference it so that we know what to do with the update. */ status = omapi_set_object_value (message, (omapi_object_t *)0, "object", h); if (status != ISC_R_SUCCESS) { omapi_object_dereference (&message, MDL); return status; } omapi_object_dereference (&message, MDL); return status; } /* Requests the removal of the object referenced by the handle (there can't be any other work in progress on the handle). A removal means that all searchable references to the object on the server are deleted. */ dhcpctl_status dhcpctl_object_remove (dhcpctl_handle connection, dhcpctl_handle h) { isc_result_t status; omapi_object_t *message = (omapi_object_t *)0; dhcpctl_remote_object_t *ro; if (h -> type != dhcpctl_remote_type) return DHCP_R_INVALIDARG; ro = (dhcpctl_remote_object_t *)h; status = omapi_message_new (&message, MDL); if (status != ISC_R_SUCCESS) { omapi_object_dereference (&message, MDL); return status; } status = omapi_set_int_value (message, (omapi_object_t *)0, "op", OMAPI_OP_DELETE); if (status != ISC_R_SUCCESS) { omapi_object_dereference (&message, MDL); return status; } status = omapi_set_int_value (message, (omapi_object_t *)0, "handle", (int)(ro -> remote_handle)); if (status != ISC_R_SUCCESS) { omapi_object_dereference (&message, MDL); return status; } status = omapi_set_object_value (message, (omapi_object_t *)0, "notify-object", h); if (status != ISC_R_SUCCESS) { omapi_object_dereference (&message, MDL); return status; } omapi_message_register (message); status = omapi_protocol_send_message (connection -> outer, (omapi_object_t *)0, message, (omapi_object_t *)0); omapi_object_dereference (&message, MDL); return status; } isc_result_t dhcpctl_data_string_dereference (dhcpctl_data_string *vp, const char *file, int line) { return omapi_data_string_dereference (vp, file, line); } dhcp-4.4.1/dhcpctl/dhcpctl.h000644 000765 000024 00000011145 13243301226 016130 0ustar00tmarkstaff000000 000000 /* $Id: dhcpctl.h,v 1.18 2009/11/24 02:06:56 sar Exp $ Subroutines providing general support for objects. */ /* * Copyright (c) 2004,2009,2014 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1999-2003 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #ifndef _DHCPCTL_H_ #define _DHCPCTL_H_ #include typedef isc_result_t dhcpctl_status; typedef omapi_object_t *dhcpctl_handle; typedef omapi_data_string_t *dhcpctl_data_string; #define dhcpctl_null_handle ((dhcpctl_handle) 0) #define DHCPCTL_CREATE OMAPI_CREATE #define DHCPCTL_UPDATE OMAPI_UPDATE #define DHCPCTL_EXCL OMAPI_EXCL typedef struct { OMAPI_OBJECT_PREAMBLE; omapi_object_t *object; void *data; void (*callback) (dhcpctl_handle, dhcpctl_status, void *); } dhcpctl_callback_object_t; typedef struct { OMAPI_OBJECT_PREAMBLE; omapi_typed_data_t *rtype; isc_result_t waitstatus; omapi_typed_data_t *message; omapi_handle_t remote_handle; } dhcpctl_remote_object_t; extern omapi_object_type_t *dhcpctl_callback_type; extern omapi_object_type_t *dhcpctl_remote_type; dhcpctl_status dhcpctl_initialize (void); dhcpctl_status dhcpctl_connect (dhcpctl_handle *, const char *, int, dhcpctl_handle); dhcpctl_status dhcpctl_wait_for_completion (dhcpctl_handle, dhcpctl_status *); dhcpctl_status dhcpctl_get_value (dhcpctl_data_string *, dhcpctl_handle, const char *); dhcpctl_status dhcpctl_get_boolean (int *, dhcpctl_handle, const char *); dhcpctl_status dhcpctl_set_value (dhcpctl_handle, dhcpctl_data_string, const char *); dhcpctl_status dhcpctl_set_string_value (dhcpctl_handle, const char *, const char *); dhcpctl_status dhcpctl_set_data_value (dhcpctl_handle, const char *, unsigned, const char *); dhcpctl_status dhcpctl_set_null_value (dhcpctl_handle, const char *); dhcpctl_status dhcpctl_set_boolean_value (dhcpctl_handle, int, const char *); dhcpctl_status dhcpctl_set_int_value (dhcpctl_handle, int, const char *); dhcpctl_status dhcpctl_object_update (dhcpctl_handle, dhcpctl_handle); dhcpctl_status dhcpctl_object_refresh (dhcpctl_handle, dhcpctl_handle); dhcpctl_status dhcpctl_object_remove (dhcpctl_handle, dhcpctl_handle); dhcpctl_status dhcpctl_set_callback (dhcpctl_handle, void *, void (*) (dhcpctl_handle, dhcpctl_status, void *)); isc_result_t dhcpctl_callback_set_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_typed_data_t *); isc_result_t dhcpctl_callback_get_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_value_t **); isc_result_t dhcpctl_callback_destroy (omapi_object_t *, const char *, int); isc_result_t dhcpctl_callback_signal_handler (omapi_object_t *, const char *, va_list); isc_result_t dhcpctl_callback_stuff_values (omapi_object_t *, omapi_object_t *, omapi_object_t *); dhcpctl_status dhcpctl_new_authenticator (dhcpctl_handle *, const char *, const char *, const unsigned char *, unsigned); dhcpctl_status dhcpctl_open_object (dhcpctl_handle, dhcpctl_handle, int); dhcpctl_status dhcpctl_new_object (dhcpctl_handle *, dhcpctl_handle, const char *); isc_result_t dhcpctl_remote_set_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_typed_data_t *); isc_result_t dhcpctl_remote_get_value (omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_value_t **); isc_result_t dhcpctl_remote_destroy (omapi_object_t *, const char *, int); isc_result_t dhcpctl_remote_signal_handler (omapi_object_t *, const char *, va_list); isc_result_t dhcpctl_remote_stuff_values (omapi_object_t *, omapi_object_t *, omapi_object_t *); isc_result_t dhcpctl_data_string_dereference (dhcpctl_data_string *, const char *, int); #endif /* _DHCPCTL_H_ */ dhcp-4.4.1/dhcpctl/Makefile.am000644 000765 000024 00000001430 13243313027 016370 0ustar00tmarkstaff000000 000000 BINDLIBIRSDIR=@BINDLIBIRSDIR@ BINDLIBDNSDIR=@BINDLIBDNSDIR@ BINDLIBISCCFGDIR=@BINDLIBISCCFGDIR@ BINDLIBISCDIR=@BINDLIBISCDIR@ bin_PROGRAMS = omshell lib_LIBRARIES = libdhcpctl.a noinst_PROGRAMS = cltest man_MANS = omshell.1 dhcpctl.3 EXTRA_DIST = $(man_MANS) omshell_SOURCES = omshell.c omshell_LDADD = libdhcpctl.a ../common/libdhcp.a ../omapip/libomapi.a \ $(BINDLIBIRSDIR)/libirs.a \ $(BINDLIBDNSDIR)/libdns.a \ $(BINDLIBISCCFGDIR)/libisccfg.a \ $(BINDLIBISCDIR)/libisc.a libdhcpctl_a_SOURCES = dhcpctl.c callback.c remote.c cltest_SOURCES = cltest.c cltest_LDADD = libdhcpctl.a ../common/libdhcp.a ../omapip/libomapi.a \ $(BINDLIBIRSDIR)/libirs.a \ $(BINDLIBDNSDIR)/libdns.a \ $(BINDLIBISCCFGDIR)/libisccfg.a \ $(BINDLIBISCDIR)/libisc.a dhcp-4.4.1/dhcpctl/Makefile.am.in000644 000765 000024 00000001507 13243301226 017000 0ustar00tmarkstaff000000 000000 BINDLIBIRSDIR=@Q@BINDLIBIRSDIR@Q@ BINDLIBDNSDIR=@Q@BINDLIBDNSDIR@Q@ BINDLIBISCCFGDIR=@Q@BINDLIBISCCFGDIR@Q@ BINDLIBISCDIR=@Q@BINDLIBISCDIR@Q@ bin_PROGRAMS = omshell lib_@DHLIBS@ = libdhcpctl.@A@ noinst_PROGRAMS = cltest man_MANS = omshell.1 dhcpctl.3 EXTRA_DIST = $(man_MANS) omshell_SOURCES = omshell.c omshell_LDADD = libdhcpctl.@A@ ../common/libdhcp.@A@ ../omapip/libomapi.@A@ \ $(BINDLIBIRSDIR)/libirs.@A@ \ $(BINDLIBDNSDIR)/libdns.@A@ \ $(BINDLIBISCCFGDIR)/libisccfg.@A@ \ $(BINDLIBISCDIR)/libisc.@A@ libdhcpctl_@A@_SOURCES = dhcpctl.c callback.c remote.c cltest_SOURCES = cltest.c cltest_LDADD = libdhcpctl.@A@ ../common/libdhcp.@A@ ../omapip/libomapi.@A@ \ $(BINDLIBIRSDIR)/libirs.@A@ \ $(BINDLIBDNSDIR)/libdns.@A@ \ $(BINDLIBISCCFGDIR)/libisccfg.@A@ \ $(BINDLIBISCDIR)/libisc.@A@ dhcp-4.4.1/dhcpctl/omshell.1000644 000765 000024 00000025756 13243301226 016100 0ustar00tmarkstaff000000 000000 .\" $Id: omshell.1,v 1.6 2009/11/24 02:06:56 sar Exp $ .\" .\" Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") .\" Copyright (c) 2001-2003 by Internet Software Consortium .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT .\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" Internet Systems Consortium, Inc. .\" 950 Charter Street .\" Redwood City, CA 94063 .\" .\" https://www.isc.org/ .\" .TH omshell 1 .SH NAME omshell - OMAPI Command Shell .SH SYNOPSIS .B omshell .SH DESCRIPTION The OMAPI Command Shell, omshell, provides an interactive way to connect to, query, and possibly change, the ISC DHCP Server's state via OMAPI, the Object Management API. By using OMAPI and omshell, you do not have to stop, make changes, and then restart the DHCP server, but can make the changes while the server is running. Omshell provides a way of accessing OMAPI. .PP OMAPI is simply a communications mechanism that allows you to manipulate objects. In order to actually \fIuse\fR omshell, you .I must understand what objects are available and how to use them. Documentation for OMAPI objects can be found in the documentation for the server that provides them - for example, in the \fBdhcpd(1)\fR manual page and the \fBdhclient(1)\fR manual page. .SH CONTRIBUTIONS .PP This software is free software. At various times its development has been underwritten by various organizations, including the ISC and Vixie Enterprises. The development of 3.0 has been funded almost entirely by Nominum, Inc. .PP At this point development is hosted by the ISC, but the future of this project depends on you. If you have features you want, please consider implementing them. .SH LOCAL AND REMOTE OBJECTS .PP Throughout this document, there are references to local and remote objects. Local objects are ones created in omshell with the \fBnew\fR command. Remote objects are ones on the server: leases, hosts, and groups that the DHCP server knows about. Local and remote objects are associated together to enable viewing and modification of object attributes. Also, new remote objects can be created to match local objects. .SH OPENING A CONNECTION .PP omshell is started from the command line. Once omshell is started, there are several commands that can be issued: .PP .B server \fIaddress\fR .RS 0.5i where address is the IP address of the DHCP server to connect to. If this is not specified, the default server is 127.0.0.1 (localhost). .RE .PP .B port \fInumber\fR .RS 0.5i where number is the port that OMAPI listens on. By default, this is 7911. .RE .PP .B key \fIname secret\fR .RS 0.5i This specifies the TSIG key to use to authenticate the OMAPI transactions. \fIname\fR is the name of a key defined in \fIdhcpd.conf\fR with the \fBomapi-key\fR statement. The \fIsecret\fR is the secret key generated from \fBdnssec-keygen\fR or another key generation program. The key algorithm is assumed to be HMAC-MD5 key. If a different algorithm was specified in dhcpd.conf file for the key, then it must be specified via the \fIkey-algorithm\fR statement. .RE .PP .B key-algorithm \fIalgorithm\fR .RS 0.5i This specifies the cryptographic algorithm for the key used when authenticating OMAPI transactions. Supported values for \fIalgorithm\fR are: .nf HMAC-MD5 HMAC-SHA1 HMAC-SHA224 HMAC-SHA256 HMAC-SHA384 HMAC-SHA512 fi The default is HMAC-MD5. (Value is not case sensitive). .RE .PP .B connect .RS 0.5i This starts the OMAPI connection to the server as specified by the \fIserver\fR statement. .SH CREATING LOCAL OBJECTS .PP Any object defined in OMAPI can be created, queried, and/or modified. The object types available to OMAPI are defined in \fBdhcpd(8)\fR and \fBdhclient(8)\fR. When using omshell, objects are first defined locally, manipulated as desired, and then associated with an object on the server. Only one object can be manipulated at a time. To create a local object, use .PP .B new \fIobject-type\fR .RS 0.5i \fIobject-type\fR is one of group, host, or lease. .RE .PP At this point, you now have an object that you can set properties on. For example, if a new lease object was created with \fInew lease\fR, any of a lease's attributes can be set as follows: .PP .B set \fIattribute-name = value\fR .RS 0.5i \fBAttribute\fR names are defined in \fBdhcpd(8)\fR and \fBdhclient(8)\fR. Values should be quoted if they are strings. So, to set a lease's IP address, you would do the following: \fB set ip-address = 192.168.4.50\fR .SH ASSOCIATING LOCAL AND REMOTE OBJECTS .PP At this point, you can query the server for information about this lease, by .PP .B open .PP Now, the local lease object you created and set the IP address for is associated with the corresponding lease object on the DHCP server. All of the lease attributes from the DHCP server are now also the attributes on the local object, and will be shown in omshell. .SH VIEWING A REMOTE OBJECT .PP To query a lease of address 192.168.4.50, and find out its attributes, after connecting to the server, take the following steps: .PP .B new "lease" .PP This creates a new local lease object. .PP .B set ip-address = 192.168.4.50 .PP This sets the \fIlocal\fR object's IP address to be 192.168.4.50 .PP .B open .PP Now, if a lease with that IP address exists, you will see all the information the DHCP server has about that particular lease. Any data that isn't readily printable text will show up in colon-separated hexadecimal values. In this example, output back from the server for the entire transaction might look like this: .nf .sp 1 > new "lease" obj: lease > set ip-address = 192.168.4.50 obj: lease ip-address = c0:a8:04:32 > open obj: lease ip-address = c0:a8:04:32 state = 00:00:00:02 dhcp-client-identifier = 01:00:10:a4:b2:36:2c client-hostname = "wendelina" subnet = 00:00:00:06 pool = 00:00:00:07 hardware-address = 00:10:a4:b2:36:2c hardware-type = 00:00:00:01 ends = dc:d9:0d:3b starts = 5c:9f:04:3b tstp = 00:00:00:00 tsfp = 00:00:00:00 cltt = 00:00:00:00 .fi .PP As you can see here, the IP address is represented in hexadecimal, as are the starting and ending times of the lease. .SH MODIFYING A REMOTE OBJECT .PP Attributes of remote objects are updated by using the \fBset\fR command as before, and then issuing an \fBupdate\fR command. The \fBset\fR command sets the attributes on the current local object, and the \fBupdate\fR command pushes those changes out to the server. .PP Continuing with the previous example, if a \fBset client-hostname = "something-else"\fR was issued, followed by an \fBupdate\fR command, the output would look about like this: .nf .sp 1 > set client-hostname = "something-else" obj: lease ip-address = c0:a8:04:32 state = 00:00:00:02 dhcp-client-identifier = 01:00:10:a4:b2:36:2c client-hostname = "something-else" subnet = 00:00:00:06 pool = 00:00:00:07 hardware-address = 00:10:a4:b2:36:2c hardware-type = 00:00:00:01 ends = dc:d9:0d:3b starts = 5c:9f:04:3b tstp = 00:00:00:00 tsfp = 00:00:00:00 cltt = 00:00:00:00 > update obj: lease ip-address = c0:a8:04:32 state = 00:00:00:02 dhcp-client-identifier = 01:00:10:a4:b2:36:2c client-hostname = "something-else" subnet = 00:00:00:06 pool = 00:00:00:07 hardware-address = 00:10:a4:b2:36:2c hardware-type = 00:00:00:01 ends = dc:d9:0d:3b starts = 5c:9f:04:3b tstp = 00:00:00:00 tsfp = 00:00:00:00 cltt = 00:00:00:00 .fi .SH NEW REMOTE OBJECTS .PP New remote objects are created much in the same way that existing server objects are modified. Create a local object using \fBnew\fR, set the attributes as you'd wish them to be, and then create the remote object with the same properties by using .PP .B create .PP Now a new object exists on the DHCP server which matches the properties that you gave your local object. Objects created via OMAPI are saved into the dhcpd.leases file. .PP For example, if a new host with the IP address of 192.168.4.40 needs to be created it would be done as follows: .nf .sp 1 > new host obj: host > set name = "some-host" obj: host name = "some-host" > set hardware-address = 00:80:c7:84:b1:94 obj: host name = "some-host" hardware-address = 00:80:c7:84:b1:94 > set hardware-type = 1 obj: host name = "some-host" hardware-address = 00:80:c7:84:b1:94 hardware-type = 1 > set ip-address = 192.168.4.40 obj: host name = "some-host" hardware-address = 00:80:c7:84:b1:94 hardware-type = 1 ip-address = c0:a8:04:28 > create obj: host name = "some-host" hardware-address = 00:80:c7:84:b1:94 hardware-type = 00:00:00:01 ip-address = c0:a8:04:28 > .fi .PP Your dhcpd.leases file would then have an entry like this in it: .nf .sp 1 host some-host { dynamic; hardware ethernet 00:80:c7:84:b1:94; fixed-address 192.168.4.40; } .fi .PP The \fIdynamic;\fR line is to denote that this host entry did not come from dhcpd.conf, but was created dynamically via OMAPI. .SH RESETTING ATTRIBUTES .PP If you want to remove an attribute from an object, you can do this with the \fBunset\fR command. Once you have unset an attribute, you must use the \fBupdate\fR command to update the remote object. So, if the host "some-host" from the previous example will not have a static IP address anymore, the commands in omshell would look like this: .nf .sp 1 obj: host name = "some-host" hardware-address = 00:80:c7:84:b1:94 hardware-type = 00:00:00:01 ip-address = c0:a8:04:28 > unset ip-address obj: host name = "some-host" hardware-address = 00:80:c7:84:b1:94 hardware-type = 00:00:00:01 ip-address = > .fi .SH REFRESHING OBJECTS .PP A local object may be refreshed with the current remote object properties using the \fBrefresh\fR command. This is useful for object that change periodically, like leases, to see if they have been updated. This isn't particularly useful for hosts. .SH DELETING OBJECTS .PP Any remote object that can be created can also be destroyed. This is done by creating a new local object, setting attributes, associating the local and remote object using \fBopen\fR, and then using the \fBremove\fR command. If the host "some-host" from before was created in error, this could be corrected as follows: .nf .sp 1 obj: host name = "some-host" hardware-address = 00:80:c7:84:b1:94 hardware-type = 00:00:00:01 ip-address = c0:a8:04:28 > remove obj: > .fi .SH HELP .PP The \fBhelp\fR command will print out all of the commands available in omshell, with some syntax pointers. .SH SEE ALSO dhcpctl(3), omapi(3), dhcpd(8), dhclient(8), dhcpd.conf(5), dhclient.conf(5). .SH AUTHOR .B omshell is maintained by ISC. To learn more about Internet Systems Consortium, see .B https://www.isc.org dhcp-4.4.1/dhcpctl/omshell.c000644 000765 000024 00000045666 13243301226 016164 0ustar00tmarkstaff000000 000000 /* omshell.c Examine and modify omapi objects. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 2001-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "config.h" #include #include #include #include #include #include //#include "result.h" #include #include "dhcpctl.h" #include "dhcpd.h" #include /* Fixups */ isc_result_t find_class (struct class **c, const char *n, const char *f, int l) { return 0; } int parse_allow_deny (struct option_cache **oc, struct parse *cfile, int flag) { return 0; } void dhcp (struct packet *packet) { } void bootp (struct packet *packet) { } #ifdef DHCPv6 /* XXX: should we warn or something here? */ void dhcpv6(struct packet *packet) { } #ifdef DHCP4o6 isc_result_t dhcpv4o6_handler(omapi_object_t *h) { return ISC_R_NOTIMPLEMENTED; } #endif /* DHCP4o6 */ #endif /* DHCPv6 */ int check_collection (struct packet *p, struct lease *l, struct collection *c) { return 0; } void classify (struct packet *packet, struct class *class) { } static void usage (const char *s) { fprintf (stderr, "Usage: %s\n", s); exit (1); } static void check (isc_result_t status, const char *func) { if (status != ISC_R_SUCCESS) { fprintf (stderr, "%s: %s\n", func, isc_result_totext (status)); exit (1); } } int main(int argc, char **argv) { isc_result_t status, waitstatus; dhcpctl_handle connection; dhcpctl_handle authenticator; dhcpctl_handle oh; struct data_string secret; const char *name = 0, *algorithm = "hmac-md5"; int i; int port = 7911; const char *server = "127.0.0.1"; struct parse *cfile; enum dhcp_token token; const char *val; char *s; char buf[1024]; char s1[1024]; int connected = 0; char hex_buf[1025]; char *progname; #ifdef OLD_LOG_NAME progname = "omshell"; #else progname = argv[0]; #endif for (i = 1; i < argc; i++) { usage(isc_file_basename(progname)); } /* Initially, log errors to stderr as well as to syslogd. */ openlog (isc_file_basename(progname), DHCP_LOG_OPTIONS, DHCPD_LOG_FACILITY); status = dhcpctl_initialize (); if (status != ISC_R_SUCCESS) { fprintf (stderr, "dhcpctl_initialize: %s\n", isc_result_totext (status)); exit (1); } memset (&oh, 0, sizeof oh); do { if (!connected) { } else if (oh == NULL) { printf ("obj: \n"); } else { dhcpctl_remote_object_t *r = (dhcpctl_remote_object_t *)oh; omapi_generic_object_t *g = (omapi_generic_object_t *)(r -> inner); printf ("obj: "); if (r -> rtype -> type != omapi_datatype_string) { printf ("?\n"); } else { printf ("%.*s\n", (int)(r -> rtype -> u . buffer . len), r -> rtype -> u . buffer . value); } for (i = 0; i < g -> nvalues; i++) { omapi_value_t *v = g -> values [i]; if (!g -> values [i]) continue; printf ("%.*s = ", (int)v -> name -> len, v -> name -> value); if (!v -> value) { printf ("\n"); continue; } switch (v -> value -> type) { case omapi_datatype_int: printf ("%d\n", v -> value -> u . integer); break; case omapi_datatype_string: printf ("\"%.*s\"\n", (int) v -> value -> u.buffer.len, v -> value -> u.buffer.value); break; case omapi_datatype_data: print_hex_or_string(v->value->u.buffer.len, v->value->u.buffer.value, sizeof(hex_buf), hex_buf); printf("%s\n", hex_buf); break; case omapi_datatype_object: printf ("\n"); break; } } } fputs ("> ", stdout); fflush (stdout); if (fgets (buf, sizeof(buf), stdin) == NULL) break; status = new_parse (&cfile, -1, buf, strlen(buf), "", 1); check(status, "new_parse()"); token = next_token (&val, (unsigned *)0, cfile); switch (token) { default: parse_warn (cfile, "unknown token: %s", val); skip_to_semi (cfile); break; case END_OF_FILE: case ENDOFLINE: /* EOL: */ break; case TOKEN_HELP: case QUESTIONMARK: /* '?': */ printf ("Commands:\n"); printf (" port \n"); printf (" server \n"); printf (" key \n"); printf (" connect\n"); printf (" new \n"); printf (" set = \n"); printf (" create\n"); printf (" open\n"); printf (" update\n"); printf (" unset \n"); printf (" refresh\n"); printf (" remove\n"); skip_to_semi (cfile); break; case PORT: token = next_token (&val, (unsigned *)0, cfile); if (is_identifier (token)) { struct servent *se; se = getservbyname (val, "tcp"); if (se) port = ntohs (se -> s_port); else { printf ("unknown service name: %s\n", val); break; } } else if (token == NUMBER) { port = atoi (val); } else { skip_to_semi (cfile); printf ("usage: port \n"); break; } token = next_token (&val, (unsigned *)0, cfile); if (token != END_OF_FILE && token != EOL) { printf ("usage: port \n"); skip_to_semi (cfile); break; } break; case TOKEN_SERVER: token = next_token (&val, (unsigned *)0, cfile); if (token == NUMBER) { int alen = (sizeof buf) - 1; int len; s = &buf [0]; len = strlen (val); if (len + 1 > alen) { baddq: printf ("usage: server \n"); skip_to_semi (cfile); break; } strcpy (buf, val); s += len; token = next_token (&val, (unsigned *)0, cfile); if (token != DOT) goto baddq; *s++ = '.'; token = next_token (&val, (unsigned *)0, cfile); if (token != NUMBER) goto baddq; len = strlen (val); if (len + 1 > alen) goto baddq; strcpy (s, val); s += len; token = next_token (&val, (unsigned *)0, cfile); if (token != DOT) goto baddq; *s++ = '.'; token = next_token (&val, (unsigned *)0, cfile); if (token != NUMBER) goto baddq; len = strlen (val); if (len + 1 > alen) goto baddq; strcpy (s, val); s += len; token = next_token (&val, (unsigned *)0, cfile); if (token != DOT) goto baddq; *s++ = '.'; token = next_token (&val, (unsigned *)0, cfile); if (token != NUMBER) goto baddq; len = strlen (val); if (len + 1 > alen) goto baddq; strcpy (s, val); val = &buf [0]; } else if (is_identifier (token)) { /* Use val directly. */ } else { printf ("usage: server \n"); skip_to_semi (cfile); break; } s = dmalloc (strlen (val) + 1, MDL); if (!server) { printf ("no memory to store server name.\n"); skip_to_semi (cfile); break; } strcpy (s, val); server = s; token = next_token (&val, (unsigned *)0, cfile); if (token != END_OF_FILE && token != EOL) { printf ("usage: server \n"); skip_to_semi (cfile); break; } break; case KEY_ALGORITHM: /* Algorithm is optional */ token = next_token (&val, (unsigned *)0, cfile); if (token != NAME || !is_identifier(token)) { printf ("missing or invalid algorithm name\n"); printf ("usage: key-algoritm \n"); skip_to_semi (cfile); break; } s = dmalloc (strlen (val) + 1, MDL); if (!s) { printf ("no memory for algorithm name.\n"); skip_to_semi (cfile); break; } strcpy (s, val); algorithm = s; token = next_token (&val, (unsigned *)0, cfile); if (token != END_OF_FILE && token != EOL) { printf ("extra information after %s\n", algorithm); printf ("usage: key-algorithm \n"); skip_to_semi (cfile); break; } break; case KEY: token = peek_token(&val, (unsigned *)0, cfile); if (token == STRING) { token = next_token (&val, (unsigned *)0, cfile); if (!is_identifier (token)) { printf ("usage: key \n"); skip_to_semi (cfile); break; } s = dmalloc (strlen (val) + 1, MDL); if (!s) { printf ("no memory for key name.\n"); skip_to_semi (cfile); break; } strcpy (s, val); } else { s = parse_host_name(cfile); if (s == NULL) { printf ("usage: key \n"); skip_to_semi(cfile); break; } } name = s; memset (&secret, 0, sizeof secret); if (!parse_base64 (&secret, cfile)) { skip_to_semi (cfile); break; } token = next_token (&val, (unsigned *)0, cfile); if (token != END_OF_FILE && token != EOL) { printf ("usage: key \n"); skip_to_semi (cfile); break; } break; case CONNECT: token = next_token (&val, (unsigned *)0, cfile); if (token != END_OF_FILE && token != EOL) { printf ("usage: connect\n"); skip_to_semi (cfile); break; } authenticator = dhcpctl_null_handle; if (name) { status = dhcpctl_new_authenticator (&authenticator, name, algorithm, secret.data, secret.len); if (status != ISC_R_SUCCESS) { fprintf (stderr, "Cannot create authenticator: %s\n", isc_result_totext (status)); break; } } memset (&connection, 0, sizeof connection); status = dhcpctl_connect (&connection, server, port, authenticator); if (status != ISC_R_SUCCESS) { fprintf (stderr, "dhcpctl_connect: %s\n", isc_result_totext (status)); break; } connected = 1; break; case TOKEN_NEW: token = next_token (&val, (unsigned *)0, cfile); if ((!is_identifier (token) && token != STRING)) { printf ("usage: new \n"); break; } if (oh) { printf ("an object is already open.\n"); skip_to_semi (cfile); break; } if (!connected) { printf ("not connected.\n"); skip_to_semi (cfile); break; } status = dhcpctl_new_object (&oh, connection, val); if (status != ISC_R_SUCCESS) { printf ("can't create object: %s\n", isc_result_totext (status)); break; } token = next_token (&val, (unsigned *)0, cfile); if (token != END_OF_FILE && token != EOL) { printf ("usage: new \n"); skip_to_semi (cfile); break; } break; case TOKEN_CLOSE: token = next_token (&val, (unsigned *)0, cfile); if (token != END_OF_FILE && token != EOL) { printf ("usage: close\n"); skip_to_semi (cfile); break; } if (!connected) { printf ("not connected.\n"); skip_to_semi (cfile); break; } if (!oh) { printf ("not open.\n"); skip_to_semi (cfile); break; } omapi_object_dereference (&oh, MDL); break; case TOKEN_SET: token = next_token (&val, (unsigned *)0, cfile); if ((!is_identifier (token) && token != STRING)) { set_usage: printf ("usage: set = \n"); skip_to_semi (cfile); break; } if (oh == NULL) { printf ("no open object.\n"); skip_to_semi (cfile); break; } if (!connected) { printf ("not connected.\n"); skip_to_semi (cfile); break; } #ifdef HAVE_STRLCPY strlcpy (s1, val, sizeof(s1)); #else s1[0] = 0; strncat (s1, val, sizeof(s1)-strlen(s1)-1); #endif token = next_token (&val, (unsigned *)0, cfile); if (token != EQUAL) goto set_usage; token = next_token (&val, (unsigned *)0, cfile); switch (token) { case STRING: dhcpctl_set_string_value (oh, val, s1); token = next_token (&val, (unsigned *)0, cfile); break; case NUMBER: strcpy (buf, val); token = peek_token (&val, (unsigned *)0, cfile); /* Colon-separated hex list? */ if (token == COLON) goto cshl; else if (token == DOT) { s = buf; val = buf; do { int intval = atoi (val); if (intval > 255) { parse_warn (cfile, "dotted octet > 255: %s", val); skip_to_semi (cfile); goto badnum; } *s++ = intval; token = next_token (&val, (unsigned *)0, cfile); if (token != DOT) break; /* DOT is zero. */ while ((token = next_token (&val, (unsigned *)0, cfile)) == DOT) *s++ = 0; } while (token == NUMBER); dhcpctl_set_data_value (oh, buf, (unsigned)(s - buf), s1); break; } dhcpctl_set_int_value (oh, atoi (buf), s1); token = next_token (&val, (unsigned *)0, cfile); badnum: break; case NUMBER_OR_NAME: strcpy (buf, val); cshl: s = buf; val = buf; do { convert_num (cfile, (unsigned char *)s, val, 16, 8); ++s; token = next_token (&val, (unsigned *)0, cfile); if (token != COLON) break; token = next_token (&val, (unsigned *)0, cfile); } while (token == NUMBER || token == NUMBER_OR_NAME); dhcpctl_set_data_value (oh, buf, (unsigned)(s - buf), s1); break; default: printf ("invalid value.\n"); skip_to_semi (cfile); } if (token != END_OF_FILE && token != EOL) goto set_usage; break; case UNSET: token = next_token (&val, (unsigned *)0, cfile); if ((!is_identifier (token) && token != STRING)) { unset_usage: printf ("usage: unset \n"); skip_to_semi (cfile); break; } if (!oh) { printf ("no open object.\n"); skip_to_semi (cfile); break; } if (!connected) { printf ("not connected.\n"); skip_to_semi (cfile); break; } #if HAVE_STRLCPY strlcpy (s1, val, sizeof(s1)); #else s1[0] = 0; strncat (s1, val, sizeof(s1)-strlen(s1)-1); #endif token = next_token (&val, (unsigned *)0, cfile); if (token != END_OF_FILE && token != EOL) goto unset_usage; dhcpctl_set_null_value (oh, s1); break; case TOKEN_CREATE: case TOKEN_OPEN: i = token; token = next_token (&val, (unsigned *)0, cfile); if (token != END_OF_FILE && token != EOL) { printf ("usage: %s\n", val); skip_to_semi (cfile); break; } if (!connected) { printf ("not connected.\n"); skip_to_semi (cfile); break; } if (!oh) { printf ("you must make a new object first!\n"); skip_to_semi (cfile); break; } if (i == TOKEN_CREATE) i = DHCPCTL_CREATE | DHCPCTL_EXCL; else i = 0; status = dhcpctl_open_object (oh, connection, i); if (status == ISC_R_SUCCESS) status = dhcpctl_wait_for_completion (oh, &waitstatus); if (status == ISC_R_SUCCESS) status = waitstatus; if (status != ISC_R_SUCCESS) { printf ("can't open object: %s\n", isc_result_totext (status)); break; } break; case UPDATE: token = next_token (&val, (unsigned *)0, cfile); if (token != END_OF_FILE && token != EOL) { printf ("usage: %s\n", val); skip_to_semi (cfile); break; } if (!connected) { printf ("not connected.\n"); skip_to_semi (cfile); break; } if (!oh) { printf ("you haven't opened an object yet!\n"); skip_to_semi (cfile); break; } status = dhcpctl_object_update(connection, oh); if (status == ISC_R_SUCCESS) status = dhcpctl_wait_for_completion (oh, &waitstatus); if (status == ISC_R_SUCCESS) status = waitstatus; if (status != ISC_R_SUCCESS) { printf ("can't update object: %s\n", isc_result_totext (status)); break; } break; case REMOVE: token = next_token (&val, (unsigned *)0, cfile); if (token != END_OF_FILE && token != EOL) { printf ("usage: remove\n"); skip_to_semi (cfile); break; } if (!connected) { printf ("not connected.\n"); break; } if (!oh) { printf ("no object.\n"); break; } status = dhcpctl_object_remove(connection, oh); if (status == ISC_R_SUCCESS) status = dhcpctl_wait_for_completion (oh, &waitstatus); if (status == ISC_R_SUCCESS) status = waitstatus; if (status != ISC_R_SUCCESS) { printf ("can't destroy object: %s\n", isc_result_totext (status)); break; } omapi_object_dereference (&oh, MDL); break; case REFRESH: token = next_token (&val, (unsigned *)0, cfile); if (token != END_OF_FILE && token != EOL) { printf ("usage: refresh\n"); skip_to_semi (cfile); break; } if (!connected) { printf ("not connected.\n"); break; } if (!oh) { printf ("no object.\n"); break; } status = dhcpctl_object_refresh(connection, oh); if (status == ISC_R_SUCCESS) status = dhcpctl_wait_for_completion (oh, &waitstatus); if (status == ISC_R_SUCCESS) status = waitstatus; if (status != ISC_R_SUCCESS) { printf ("can't refresh object: %s\n", isc_result_totext (status)); break; } break; } end_parse (&cfile); } while (1); exit (0); } /* Sigh */ isc_result_t dhcp_set_control_state (control_object_state_t oldstate, control_object_state_t newstate) { if (newstate != server_shutdown) return ISC_R_SUCCESS; exit (0); } dhcp-4.4.1/dhcpctl/remote.c000644 000765 000024 00000024214 13243301226 015776 0ustar00tmarkstaff000000 000000 /* remote.c The dhcpctl remote object. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1999-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #include #include "dhcpctl.h" /* dhcpctl_new_authenticator synchronous - creates an authenticator object. returns nonzero status code if the object couldn't be created stores handle to authenticator through h if successful, and returns zero. name is the authenticator name (NUL-terminated string). algorithm is the NUL-terminated string name of the algorithm to use (currently, only "hmac-md5" is supported). secret and secret_len is the key secret. */ dhcpctl_status dhcpctl_new_authenticator (dhcpctl_handle *h, const char *name, const char *algorithm, const unsigned char *secret, unsigned secret_len) { struct auth_key *key = (struct auth_key *)0; isc_result_t status; status = omapi_auth_key_new (&key, MDL); if (status != ISC_R_SUCCESS) return status; key -> name = dmalloc (strlen (name) + 1, MDL); if (!key -> name) { omapi_auth_key_dereference (&key, MDL); return ISC_R_NOMEMORY; } strcpy (key -> name, name); /* If the algorithm name isn't an FQDN, tack on the .SIG-ALG.REG.NET. domain. */ if (strchr (algorithm, '.') == 0) { static char add[] = ".SIG-ALG.REG.INT."; key -> algorithm = dmalloc (strlen (algorithm) + sizeof (add), MDL); if (!key -> algorithm) { omapi_auth_key_dereference (&key, MDL); return ISC_R_NOMEMORY; } strcpy (key -> algorithm, algorithm); strcat (key -> algorithm, add); } else { key -> algorithm = dmalloc (strlen (algorithm) + 1, MDL); if (!key -> algorithm) { omapi_auth_key_dereference (&key, MDL); return ISC_R_NOMEMORY; } strcpy (key -> algorithm, algorithm); } status = omapi_data_string_new (&key -> key, secret_len, MDL); if (status != ISC_R_SUCCESS) { omapi_auth_key_dereference (&key, MDL); return status; } memcpy (key -> key -> value, secret, secret_len); key -> key -> len = secret_len; *h = (dhcpctl_handle) key; return ISC_R_SUCCESS; } /* dhcpctl_new_object synchronous - creates a local handle for a host entry. returns nonzero status code if the local host entry couldn't be created stores handle to host through h if successful, and returns zero. object_type is a pointer to a NUL-terminated string containing the ascii name of the type of object being accessed - e.g., "host" */ dhcpctl_status dhcpctl_new_object (dhcpctl_handle *h, dhcpctl_handle connection, const char *object_type) { dhcpctl_remote_object_t *m; omapi_object_t *g; isc_result_t status; m = (dhcpctl_remote_object_t *)0; status = omapi_object_allocate((omapi_object_t **)&m, dhcpctl_remote_type, 0, MDL); if (status != ISC_R_SUCCESS) return status; g = (omapi_object_t *)0; status = omapi_generic_new (&g, MDL); if (status != ISC_R_SUCCESS) { dfree (m, MDL); return status; } status = omapi_object_reference (&m -> inner, g, MDL); if (status != ISC_R_SUCCESS) { omapi_object_dereference ((omapi_object_t **)&m, MDL); omapi_object_dereference (&g, MDL); return status; } status = omapi_object_reference (&g -> outer, (omapi_object_t *)m, MDL); if (status != ISC_R_SUCCESS) { omapi_object_dereference ((omapi_object_t **)&m, MDL); omapi_object_dereference (&g, MDL); return status; } status = omapi_typed_data_new (MDL, &m -> rtype, omapi_datatype_string, object_type); if (status != ISC_R_SUCCESS) { omapi_object_dereference ((omapi_object_t **)&m, MDL); omapi_object_dereference (&g, MDL); return status; } status = omapi_object_reference (h, (omapi_object_t *)m, MDL); omapi_object_dereference ((omapi_object_t **)&m, MDL); omapi_object_dereference (&g, MDL); if (status != ISC_R_SUCCESS) return status; return status; } /* asynchronous - just queues the request returns nonzero status code if open couldn't be queued returns zero if open was queued h is a handle to an object created by dhcpctl_new_object connection is a connection to a DHCP server flags include: DHCPCTL_CREATE - if the object doesn't exist, create it DHCPCTL_UPDATE - update the object on the server using the attached parameters DHCPCTL_EXCL - error if the object exists and DHCPCTL_CREATE was also specified */ dhcpctl_status dhcpctl_open_object (dhcpctl_handle h, dhcpctl_handle connection, int flags) { isc_result_t status; omapi_object_t *message = (omapi_object_t *)0; dhcpctl_remote_object_t *remote; if (h -> type != dhcpctl_remote_type) return DHCP_R_INVALIDARG; remote = (dhcpctl_remote_object_t *)h; status = omapi_message_new (&message, MDL); if (status != ISC_R_SUCCESS) return status; status = omapi_set_int_value (message, (omapi_object_t *)0, "op", OMAPI_OP_OPEN); if (status != ISC_R_SUCCESS) { omapi_object_dereference (&message, MDL); return status; } status = omapi_set_object_value (message, (omapi_object_t *)0, "object", h); if (status != ISC_R_SUCCESS) { omapi_object_dereference (&message, MDL); return status; } if (flags & DHCPCTL_CREATE) { status = omapi_set_boolean_value (message, (omapi_object_t *)0, "create", 1); if (status != ISC_R_SUCCESS) { omapi_object_dereference (&message, MDL); return status; } } if (flags & DHCPCTL_UPDATE) { status = omapi_set_boolean_value (message, (omapi_object_t *)0, "update", 1); if (status != ISC_R_SUCCESS) { omapi_object_dereference (&message, MDL); return status; } } if (flags & DHCPCTL_EXCL) { status = omapi_set_boolean_value (message, (omapi_object_t *)0, "exclusive", 1); if (status != ISC_R_SUCCESS) { omapi_object_dereference (&message, MDL); return status; } } if (remote -> rtype) { status = omapi_set_value_str (message, (omapi_object_t *)0, "type", remote -> rtype); if (status != ISC_R_SUCCESS) { omapi_object_dereference (&message, MDL); return status; } } status = omapi_message_register (message); if (status != ISC_R_SUCCESS) { omapi_object_dereference (&message, MDL); return status; } status = omapi_protocol_send_message (connection -> outer, (omapi_object_t *)0, message, (omapi_object_t *)0); if (status != ISC_R_SUCCESS) omapi_message_unregister (message); omapi_object_dereference (&message, MDL); return status; } /* Callback methods (not meant to be called directly) */ isc_result_t dhcpctl_remote_set_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_typed_data_t *value) { dhcpctl_remote_object_t *ro; unsigned long rh; isc_result_t status; if (h -> type != dhcpctl_remote_type) return DHCP_R_INVALIDARG; ro = (dhcpctl_remote_object_t *)h; if (!omapi_ds_strcmp (name, "remote-handle")) { status = omapi_get_int_value (&rh, value); if (status == ISC_R_SUCCESS) ro -> remote_handle = rh; return status; } if (h -> inner && h -> inner -> type -> set_value) return (*(h -> inner -> type -> set_value)) (h -> inner, id, name, value); return ISC_R_NOTFOUND; } isc_result_t dhcpctl_remote_get_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_value_t **value) { if (h -> type != dhcpctl_remote_type) return DHCP_R_INVALIDARG; if (h -> inner && h -> inner -> type -> get_value) return (*(h -> inner -> type -> get_value)) (h -> inner, id, name, value); return ISC_R_NOTFOUND; } isc_result_t dhcpctl_remote_signal_handler (omapi_object_t *o, const char *name, va_list ap) { dhcpctl_remote_object_t *p; omapi_typed_data_t *tv; if (o -> type != dhcpctl_remote_type) return DHCP_R_INVALIDARG; p = (dhcpctl_remote_object_t *)o; if (!strcmp (name, "updated")) { p -> waitstatus = ISC_R_SUCCESS; if (o -> inner -> type == omapi_type_generic) omapi_generic_clear_flags (o -> inner); return omapi_signal_in (o -> inner, "ready"); } if (!strcmp (name, "status")) { p -> waitstatus = va_arg (ap, isc_result_t); if (p -> message) omapi_typed_data_dereference (&p -> message, MDL); tv = va_arg (ap, omapi_typed_data_t *); if (tv) omapi_typed_data_reference (&p -> message, tv, MDL); return omapi_signal_in (o -> inner, "ready"); } if (p -> inner && p -> inner -> type -> signal_handler) return (*(p -> inner -> type -> signal_handler)) (p -> inner, name, ap); return ISC_R_SUCCESS; } isc_result_t dhcpctl_remote_destroy (omapi_object_t *h, const char *file, int line) { dhcpctl_remote_object_t *p; if (h -> type != dhcpctl_remote_type) return DHCP_R_INVALIDARG; p = (dhcpctl_remote_object_t *)h; if (p -> handle) omapi_object_dereference ((omapi_object_t **)&p -> handle, file, line); if (p -> rtype) omapi_typed_data_dereference ((omapi_typed_data_t **)&p->rtype, file, line); return ISC_R_SUCCESS; } /* Write all the published values associated with the object through the specified connection. */ isc_result_t dhcpctl_remote_stuff_values (omapi_object_t *c, omapi_object_t *id, omapi_object_t *p) { if (p -> type != dhcpctl_remote_type) return DHCP_R_INVALIDARG; if (p -> inner && p -> inner -> type -> stuff_values) return (*(p -> inner -> type -> stuff_values)) (c, id, p -> inner); return ISC_R_SUCCESS; } dhcp-4.4.1/contrib/3.0b1-lease-convert000755 000765 000024 00000005267 13243301226 017603 0ustar00tmarkstaff000000 000000 #!/usr/bin/perl # # Start Date: Mon, 26 Mar 2001 14:24:09 +0200 # Time-stamp: # File: leaseconvertor.pl # RCSId: $Id: 3.0b1-lease-convert,v 1.1 2001/04/18 19:17:34 mellon Exp $ # # Description: Convert 3.0b1 to 3.0b2/final lease file format # require 5.004; my $rcsID =<<'EOM'; $Id: 3.0b1-lease-convert,v 1.1 2001/04/18 19:17:34 mellon Exp $ EOM use strict; my $revstatement =<<'EOS'; switch (ns-update (delete (1, 12, ddns-rev-name, null))) { case 0: unset ddns-rev-name; break; } EOS my $fwdstatement =<<'EOS'; switch (ns-update (delete (1, 1, ddns-fwd-name, leased-address))) { case 0: unset ddns-fwd-name; break; } EOS if (@ARGV && $ARGV[0] =~ m!^-!) { usage(); } # read stdin and write stdout. while (<>) { if (! /^lease\s/) { print; } else { my $lease = $_; while (<>) { $lease .= $_; # in a b1 file we should only see a left curly brace on a lease # lines. Seening it anywhere else means the user is probably # running a b2 or later file through this. # Ditto for a 'set' statement. if (m!\{! || m!^\s*set\s!) { warn "this doesn't look like a 3.0b1 file. Ignoring rest.\n"; print $lease; dumpRestAndExit(); } last if m!^\}\s*$!; } # $lease contains all the lines for the lease entry. $lease = makeNewLease($lease); print $lease; } } sub usage { my $prog = $0; $prog =~ s!.*/!!; print STDERR <) { print; } exit (0); } dhcp-4.4.1/contrib/dhclient-tz-exithook.sh000644 000765 000024 00000011735 13243301226 020773 0ustar00tmarkstaff000000 000000 #!/bin/bash # # dhclient-tz-exithook.sh # Version 1.01 elear # # Copyright (c) 2007, Cisco Systems, Inc. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # - Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # - Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # - Neither the name of Cisco Systems, Inc. nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # the following script is used to set the timezone based on the new # dhcp timezone option defined currently in the IETF document # draft-ietf-dhc-timezone-option-04.txt. # this code is intended for use with ISC's dhclient. it is to be called # either as, or by, dhclient-exit-hooks # # As this is test code, in order for it to be called two changes # must be made to /etc/dhclient.conf. First, dhclient.conf must be # aware of the tzName option. The IANA has assigned tzName option # code 101. You may need to add this to your configuration file. # # option tzName code 101 = text; # # Next, add tzName to the list of options in the "request" statement. # For example: # # request subnet-mask, broadcast-address, time-offset, routers, # domain-name, domain-name-servers, host-name, tzName; # # # And of course make sure that your dhcp server is transmitting timezone # information for option 101. For IOS this can be done as follows: # # option 101 ascii "Europe/Berlin" # timefile=/etc/localtime oldfile=$timefile.old tmpfile=$timefile.$$ # function to clean up just in case we are interrupted or something # bad happens. restore_file () { if [ ! -f $timefile ]; then $DEBUG mv $tmpfile $timefile fi $DEBUG rm $tmpfile exit } #set DEBUG to "echo" to see what would happen. if [ x$DEBUG = x ]; then DEBUG= fi # if something has already gone wrong we're not doing a thing. if [ x$exit_status != x0 ]; then exit $exit_status fi # if we don't have a new timezone, then we have nothing to change, so # goodbye. if [ x$new_tzName = x ]; then exit 0 fi # if the timezone doesn't exist, goodbye. if [ ! -e $timefile ]; then exit 0 fi # find zoneinfo. use the first one. ftz=0 for a in /usr/share/zoneinfo /usr/lib/zoneinfo /var/share/zoneinfo /var/zoneinfo; do if [ -d $a -a $ftz = 0 ]; then zoneinfo=$a ftz=1 fi done # no zoneinfo found. goodbye. if [ x$zoneinfo = x ]; then exit 0 fi # timezone not found. goodbye. if [ ! -f $zoneinfo/$new_tzName ]; then exit 0 fi # if we're here we can actually do something useful. # first, link a copy of the existing timefile. $DEBUG ln $timefile $tmpfile if [ $? != 0 ]; then echo "unable to create temporary file" exit -1 fi # in case of interrupt, cleanup. trap restore_file SIGINT SIGSEGV SIGQUIT SIGTERM # we destroy old backup files in this process. if we cannot and the # file exists then something went wrong. if [ -e $oldfile ]; then $DEBUG rm $oldfile if [ $? != 0 ]; then echo "$0: failed to remove $oldfile" rm -f $tmpfile exit -1 fi fi # sensitive part happens here: # $DEBUG mv $timefile $oldfile if [ $? != 0 ]; then echo "$0: failed to move old $timefile file out of the way" rm $tmpfile exit -1 fi $DEBUG ln $zoneinfo/$new_tzName $timefile # we don't complain just yet- a hard link could fail because # we're on two different file systems. Go for a soft link. # if [ $? != 0 ]; then $DEBUG ln -s $zoneinfo/$new_tzName $timefile fi if [ $? != 0 ]; then # failed to softlink. now we're getting nervous. echo "$0: unable to establish new timezone. Attempting to revert." $DEBUG ln $tmpfile $timefile fi if [ $? != 0 ]; then # we're absolutely hosed echo "$0: unable to link or softlink timezone file, and unable to restore old file - giving up!" exit -1 fi $DEBUG rm $tmpfile exit $? dhcp-4.4.1/contrib/dhcp-lease-list.pl000644 000765 000024 00000013253 13243301226 017672 0ustar00tmarkstaff000000 000000 #!/usr/bin/perl # # Shows current leases. # # THIS SCRIPT IS PUBLIC DOMAIN, NO RIGHTS RESERVED! # # I've removed the email addresses of Christian and vom to avoid # putting them on spam lists. If either of you would like to have # your email in here please send mail to the DHCP bugs list at ISC. # # 2008-07-13, Christian Hammers # # 2009-06-?? - added loading progress counter, pulls hostname, adjusted formatting # vom # # 2013-04-22 - added option to choose lease file, made manufacture information # optional, sar # # 2016-01-19 - updated to better trim the manu string and output the hostnames, sar # # 2016-01-18 - Mainly cosmetics. Eliminated spurious output in "parsable" mode. # Provided for the various conventional lease file locations. (cbp) use strict; use warnings; use POSIX qw(strftime); my @LEASES = ('/var/db/dhcpd.leases', '/var/lib/dhcp/dhcpd.leases', '/var/lib/dhcp3/dhcpd.leases'); my @all_leases; my @leases; my @OUIS = ('/usr/share/misc/oui.txt', '/usr/local/etc/oui.txt'); my $OUI_URL = 'http://standards.ieee.org/regauth/oui/oui.txt'; my $oui; my %data; my $opt_format = 'human'; my $opt_keep = 'active'; our $total_leases = 0; ## Return manufactorer name for specified MAC address (aa:bb:cc:dd:ee:ff). sub get_manufactorer_for_mac($) { my $manu = "-NA-"; if (defined $oui) { $manu = join('-', ($_[0] =~ /^(..):(..):(..):/)); $manu = `grep -i '$manu' $oui | cut -f3`; $manu =~ s/^\s+|\s+$//g; } return $manu; } ## Read oui.txt or print warning. sub check_oui_file() { for my $oui_cand (@OUIS) { if ( -r $oui_cand) { $oui = $oui_cand; last; } } if (not defined $oui) { print(STDERR "To get manufacturer names please download $OUI_URL "); print(STDERR "to /usr/local/etc/oui.txt\n"); } } ## Read current leases file into array. sub read_dhcpd_leases() { my $db; for my $db_cand (@LEASES) { if ( -r $db_cand) { $db = $db_cand; last; } } die("Cannot find leases db") unless defined $db; open(F, $db) or die("Cannot open $db: $!"); print("Reading leases from $db\n") if $opt_format eq 'human'; my $content = join('', ); close(F); @all_leases = split(/lease/, $content); foreach my $lease (@all_leases) { if ($lease =~ /^\s+([\.\d]+)\s+{.*starts \d+ ([\/\d\ \:]+);.*ends \d+ ([\/\d\ \:]+);.*ethernet ([a-f0-9:]+);/s) { ++$total_leases; } } } ## Add manufactor name and sort out obsolet assignements. sub process_leases() { my $gm_now = strftime("%Y/%m/%d %H:%M:%S", gmtime()); my %tmp_leases; # for sorting and filtering my $counter = $opt_format eq 'human' ? 1 : 0; # parse entries foreach my $lease (@all_leases) { # skip invalid lines next if not ($lease =~ /^\s+([\.\d]+)\s+{.*starts \d+ ([\/\d\ \:]+);.*ends \d+ ([\/\d\ \:]+);.*ethernet ([a-f0-9:]+);(.*client-hostname \"(\S+)\";)*/s); # skip outdated lines next if ($opt_keep eq 'active' and $3 lt $gm_now); if ($counter) { my $percent = (($counter / $total_leases)*100); printf "Processing: %2d%% complete\r", $percent; ++$counter; } my $hostname = "-NA-"; if ($6) { $hostname = $6; } my $mac = $4; my $date_end = $3; my %entry = ( 'ip' => $1, 'date_begin' => $2, 'date_end' => $date_end, 'mac' => $mac, 'hostname' => $hostname, 'manu' => get_manufactorer_for_mac($mac), ); $entry{'date_begin'} =~ s#\/#-#g; # long live ISO 8601 $entry{'date_end'} =~ s#\/#-#g; if ($opt_keep eq 'all') { push(@leases, \%entry); } elsif (not defined $tmp_leases{$mac} or $tmp_leases{$mac}{'date_end'} gt $date_end) { $tmp_leases{$mac} = \%entry; } } # In case we used the hash to filtered if (%tmp_leases) { foreach (sort keys %tmp_leases) { my $h = $tmp_leases{$_}; push(@leases, $h); } } # print "\n"; } # Output all valid leases. sub output_leases() { if ($opt_format eq 'human') { printf "%-19s%-16s%-15s%-20s%-20s\n","MAC","IP","hostname","valid until","manufacturer"; print("===============================================================================================\n"); } foreach (@leases) { if ($opt_format eq 'human') { printf("%-19s%-16s%-14.14s %-20s%-20s\n", $_->{'mac'}, # MAC $_->{'ip'}, # IP address $_->{'hostname'}, # hostname $_->{'date_end'}, # Date $_->{'manu'}); # manufactor name } else { printf("MAC %s IP %s HOSTNAME %s BEGIN %s END %s MANUFACTURER %s\n", $_->{'mac'}, $_->{'ip'}, $_->{'hostname'}, $_->{'date_begin'}, $_->{'date_end'}, $_->{'manu'}); } } } # Commandline Processing. sub cli_processing() { while (my $arg = shift(@ARGV)) { if ($arg eq '--help') { print( "Prints active DHCP leases.\n\n". "Usage: $0 [options]\n". " --help shows this help\n". " --parsable machine readable output with full dates\n". " --last prints the last (even if end vars . ./vars cat << EOF >> includes/site.h #define _PATH_DHCPD_PID "$VARRUN/dhcpd.pid" #define _PATH_DHCPD_DB "$ETC/dhcpd.leases" #define _PATH_DHCPD_CONF "$ETC/dhcpd.conf" EOF ./configure --with-nsupdate %build make %install rm -rf $RPM_BUILD_ROOT mkdir -p $RPM_BUILD_ROOT/usr/local/sbin make DESTDIR="$RPM_BUILD_ROOT" install %ifos linux mkdir -p ${RPM_BUILD_ROOT}/etc/rc.d/{init,rc0,rc1,rc2,rc3,rc4,rc5,rc6}.d install -m 755 linux.init ${RPM_BUILD_ROOT}/etc/rc.d/init.d/dhcpd %else %ifos solaris mkdir -p ${RPM_BUILD_ROOT}/etc/init.d sed -e s'|@PREFIX@|%{_prefix}|g' < contrib/solaris.init > ${RPM_BUILD_ROOT}/etc/init.d/dhcpd chmod 755 ${RPM_BUILD_ROOT}/etc/init.d/dhcpd %endif %endif # strip binaries and libraries strip $RPM_BUILD_ROOT%{_prefix}/sbin/* || : for i in `find $RPM_BUILD_ROOT/ -type 'f' -perm '+a=x' ! -name 'lib*so*'`; do file $i |grep -q "not stripped" && strip $i done %post %ifos linux /sbin/chkconfig --add dhcpd /etc/rc.d/init.d/dhcpd start %else %ifos solaris ln /etc/init.d/dhcpd /etc/rc2.d/S90dhcpd ln /etc/init.d/dhcpd /etc/rc0.d/K30dhcpd /etc/init.d/dhcpd start %else echo "Unknown O/S. You will need to manually configure your\nsystem" echo "to start the DHCP server on system startup." %endif %endif %preun if [ $1 = 0 ]; then %ifos linux /etc/rc.d/init.d/dhcpd stop /sbin/chkconfig --del dhcpd %else %ifos solaris /etc/init.d/dhcpd stop rm /etc/rc2.d/S90dhcpd rm /etc/rc0.d/K30dhcpd %else echo "Unknown O/S. You will need to manually clean up the DHCP" echo "server startup\n in your system startup environment." %endif %endif fi %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root) %doc COPYRIGHT DOCUMENTATION ISC-LICENSE CHANGES README RELNOTES doc/* %{_prefix}/sbin/dhcpd %{_prefix}/man/cat1m/dhcpd.1m %{_prefix}/man/cat4/dhcpd.conf.4 %{_prefix}/man/cat4/dhcpd.leases.4 %{_prefix}/man/cat4/dhcp-options.4 %{_prefix}/man/cat4/dhcp-eval.4 %{_prefix}/man/cat4/dhcp-contrib.4 %ifos linux %config /etc/rc.d/init.d/dhcpd %else %ifos solaris %config /etc/init.d/dhcpd %endif %endif %files devel %{_prefix}/man/cat3 %{_prefix}/lib %{_prefix}/include %files client %{_prefix}/etc/dhclient-script %{_prefix}/sbin/dhclient %{_prefix}/man/cat1m/dhclient.1m %{_prefix}/man/cat1m/dhclient-script.1m %{_prefix}/man/cat4/dhclient.conf.4 %{_prefix}/man/cat4/dhclient.leases.4 %files relay %{_prefix}/sbin/dhcrelay %{_prefix}/man/cat1m/dhcrelay.1m %changelog * Fri Oct 1 1999 Brian J. Murrell - write a spec file for dhcpd dhcp-4.4.1/contrib/ldap/000755 000765 000024 00000000000 13243313032 015271 5ustar00tmarkstaff000000 000000 dhcp-4.4.1/contrib/ms2isc/000755 000765 000024 00000000000 13243313032 015551 5ustar00tmarkstaff000000 000000 dhcp-4.4.1/contrib/sethostname.sh000644 000765 000024 00000001603 13243301226 017241 0ustar00tmarkstaff000000 000000 #!/bin/sh # This script can be installed in /etc/dhclient-enter-hooks to set the client's # hostname based either on the hostname that the DHCP server supplied or the # hostname in whatever ptr record exists for the assigned IP address. if [ x$new_host_name = x ]; then ptrname=`echo $new_ip_address \ |sed -e \ 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\4.\3.\2.\1.in-addr.arpa/'` (echo "set type=ptr"; echo "$ptrname") |nslookup >/tmp/nslookup.$$ set `sed -n -e "s/$ptrname[ ]*\(canonical \)*name *= *\(.*\)/\2 \1/p" \ < /tmp/nslookup.$$` _ if [ x$1 = x_ ]; then new_host_name="" else if [ $# -gt 1 ] && [ x$2 = xcanonical ]; then new_host_name=`sed -n -e "s/$1[ ]*name *= *\(.*\)/\1/p" \ -o [-p [-k ]] [-f ] Server IP or name for NT4 DHCP server to fetch the configuration from. Output filename for the configuration file. Primary DNS server name for sending the dynamic DNS update to. Key name for use in updating the dynamic DNS zone. Failover peer name shared with the DHCP partner. Essentially the needs to be an NT4 (3.x should work but not tested) which you should have registry read access to. You must run this script from a Windows machine because of the requirement to access the registry. The is optional parameter for desginating the dynamic DNS update if missing then the "zone" section of the declaration will be skipped. The is needed if you've configured your DNS zone with a key, in addition, you'll need to define that key in this DHCP configuration file elsewhere manually, read the DHCP Handbook to figure out what you need to define. The specifies the fail-over peer name in the pool section, you'll need to define additional detail elsewhere manually, again read the DHCP handbook. NOTE: the program only knows of the following global and subnet options: 3, 6, 15, 28, 44, and 46 If it runs into options other than the known ones, it will quit. You may fix this by modifying the following procedures: GetGlobalOptions GetScopes PrintSubnetConfig In addition, the resulting subnets configuration will have the "deny dynamic bootp clients" you should take them out if that's not what you want :). Finally, as the parameter structures implied, it is assumed that you want the same zone primary and update key for all zones and that the same failover is to be applied to all the pools. Furthermore the subnet zones are all assumed to be class C delineated, but if you happend to be delegated at the class B level, this will work fine too. Author: Shu-Min Chang Copyright: Please read the top of the source code Acknowledgement: Brian L. King for coding help, Douglas A. Darrah for testing, and James E. Pressley for being the DHCP reference book :). Usage: $ARGV -s -o [-p [-k ]] [-f ] Version: 1.0.1 ENDOFHELP ###################### Begin Main Program #################################### my (%opts, %GlobalOptions, %SuperScopes, %Scopes); ### Get parameters and make sure that they meet the require/optoinal criteria getopts('s:o:p:k:f:', \%opts) or die $usage; ($opts{s} and $opts{o}) or die $usage; if ($opts{k}) { $opts{p} or die $usage; } ### Read all the registry stuff into the memory %GlobalOptions = GetGlobalOptions($opts{s}); %SuperScopes = GetSuperScope($opts{s}); %Scopes = GetScopes ($opts{s}); ### Process and print out to the output file my ($outfile, $i, $j, @Domains); $outfile = new FileHandle "> $opts{o}"; if (!defined $outfile) { die "Can't open file: $opts{o}: $!"; } for $i (keys %SuperScopes) { print $outfile "\n##############################################################\n"; my ($Scopename) = $i; $Scopename =~ s/ //g; print $outfile "shared-network $Scopename {\n"; foreach $j (@{$SuperScopes{$i}}) { PrintSubnetConfig($outfile, \%GlobalOptions, \%{$Scopes{$j}}, $j, "\t", $opts{f}); InsertIfUnique (\@Domains, $Scopes{$j}{domain}) if exists $Scopes{$j}{domain}; delete $Scopes{$j}; } print $outfile "}\n"; if ($opts{p} or $opts{k}) { foreach $j (@{$SuperScopes{$i}}) { PrintSubnetUpdate($outfile, $j, $opts{p}, $opts{k}); } } } for $i (keys %Scopes) { print $outfile "\n##############################################################\n"; PrintSubnetConfig($outfile, \%GlobalOptions, \%{$Scopes{$i}}, $i, "", $opts{f}); if ($opts{p} or $opts{k}) { PrintSubnetUpdate($outfile, $i, $opts{p}, $opts{k}); } InsertIfUnique (\@Domains, $Scopes{$i}{domain}) if exists $Scopes{$i}{domain}; } if ($opts{p} or $opts{k}) { InsertIfUnique (\@Domains, $GlobalOptions{domain}) if exists $GlobalOptions{domain}; for $i (@Domains) { PrintDomainUpdate($outfile, $i, $opts{p}, $opts{k}); } } undef ($outfile); print "Done.\n"; exit(); ################################## End Main Program ########################### ###################################################################### sub InsertIfUnique ($$) { my ($Array, $data) = @_; # purpose: insert $data into array @{$Array} iff the data is not in there yet # input: # $data: scalar data to be added to the @{$Array} if unique # $Array: reference of the Array to compare the uniqueness of the $data # output: # $Array: reference of the array with the resulting array. # return: none my ($i); for ($i=0; $i<=$#{$Array} && ${$Array}[$i] ne $data; $i++) { } if ($i > $#{$Array}) { ${$Array}[$i] = $data; } } ###################################################################### sub PrintDomainUpdate ($$$$) { my ($outfile, $Domain, $DDNSServer, $key) = @_; # purpose: print out the foward domain zone update declaration # input: # $outfile: filehandle of the file to write the output to # $Domain: a string representing the forward domain # $DDNSServer: a string of the DNS server accepting the DDNS update # $key: a string representing the key used to update the zone # output: none # return: none # print $outfile "zone $Domain {\n"; print $outfile "\tprimary $DDNSServer;\n"; !$key or print $outfile "\tkey $key;\n"; print $outfile "}\n"; } ###################################################################### sub PrintSubnetUpdate ($$$$) { my ($outfile, $Subnet, $DDNSServer, $key) = @_; # purpose: print out the reverse domain zone update declaration # input: # $outfile: filehandle of the file to write the output to # $Subnet: a string representing the subnet in the form 1.2.3.4 # $DDNSServer: a string of the DNS server accepting the DDNS update # $key: a string representing the key used to update the zone # output: none # return: none # my ($Reverse); $_ = join (".", reverse(split(/\./, $Subnet))); m/\d*\.(.*)/; $Reverse = $1; print $outfile "zone $Reverse.in-addr.arpa. {\n"; print $outfile "\tprimary $DDNSServer;\n"; !$key or print $outfile "\tkey $key;\n"; print $outfile "}\n"; } ###################################################################### sub PrintSubnetConfig ($$$$$$) { my ($outfile, $GlobalOptions, $Scope, $Subnet, $prefix, $failover) = @_; # purpose: print out the effective scope configuration for one subnet as # derived from the global and scope options. # input: # $outfile: filehandle of the file to write the output to # $GlobalOptions: refernce to the hashed variable from GetGlobalOptions # $Scopes: reference to the hashed variable of the subnet in interest # $Subnet: string variable of the subnet being processed # $prefix: string to be printed before each line (designed for tab) # $failover: string to be used for the "failover peer" line # output: none # return: none # my ($pound) = ( ${$Scope}{disable}? "#".$prefix : $prefix); print $outfile $pound, "subnet $Subnet netmask ${$Scope}{mask} {\n"; print $outfile "$prefix# Name: ${$Scope}{name}\n"; print $outfile "$prefix# Comment: ${$Scope}{comment}\n"; if (exists ${$Scope}{routers}) { print $outfile $pound, "\toption routers @{${$Scope}{routers}};\n"; } elsif (exists ${$GlobalOptions}{routers}) { print $outfile $pound, "\toption routers @{${$GlobalOptions}{routers}};\t# NOTE: obtained from global option, bad practice detected\n"; } else { print $outfile "### WARNING: No router was found for this subnet!!! ##########\n"; } if (exists ${$Scope}{dnses}) { print $outfile $pound, "\toption domain-name-servers ", join(",", @{${$Scope}{dnses}}), ";\n"; } elsif (exists ${$GlobalOptions}{dnses}) { print $outfile $pound, "\toption domain-name-servers ", join(",", @{${$GlobalOptions}{dnses}}), ";\n"; } if (exists ${$Scope}{domain}) { print $outfile $pound, "\toption domain-name \"${$Scope}{domain}\";\n"; } elsif (exists ${$GlobalOptions}{domain}) { print $outfile $pound, "\toption domain-name \"${$GlobalOptions}{domain}\";\n"; } if (exists ${$Scope}{broadcast}) { print $outfile $pound, "\toption broadcast-address ${$Scope}{broadcast};\n"; } elsif (exists ${$GlobalOptions}{broadcast}) { print $outfile $pound, "\toption broadcast-address ${$GlobalOptions}{broadcast};\n"; } if (exists ${$Scope}{winses}) { print $outfile $pound, "\toption netbios-name-servers ", join(",", @{${$Scope}{winses}}), ";\n"; } elsif (exists ${$GlobalOptions}{winses}) { print $outfile $pound, "\toption netbios-name-servers ", join(",", @{${$GlobalOptions}{winses}}), ";\n"; } if (exists ${$Scope}{winstype}) { print $outfile $pound, "\toption netbios-node-type ${$Scope}{winstype};\n"; } elsif (exists ${$GlobalOptions}{winstype}) { print $outfile $pound, "\toption netbios-node-type ${$GlobalOptions}{winstype};\n" } print $outfile $pound, "\tdefault-lease-time ${$Scope}{leaseduration};\n"; print $outfile $pound, "\tpool {\n"; for (my $r=0; $r<=$#{${$Scope}{ranges}}; $r+=2) { print $outfile $pound, "\t\trange ${$Scope}{ranges}[$r] ${$Scope}{ranges}[$r+1];\n"; } !$failover or print $outfile $pound, "\t\tfailover peer \"$failover\";\n"; print $outfile $pound, "\t\tdeny dynamic bootp clients;\n"; print $outfile $pound, "\t}\n"; print $outfile $pound, "}\n"; } ###################################################################### sub GetScopes ($) { my ($Server) = @_; my (%Scopes); # purpose: to return NT4 server's scope configuration # input: # $Server: string of the valid IP or name of the NT4 server # output: none # return: # %Scope: hash of hash of hash of various data types to be returned of the # following data structure # $Scope{}{disable} => boolean # $Scope{}{mask} => string (e.g. "1.2.3.255") # $Scope{}{name} => string (e.g "Office Subnet #1") # $Scope{}{comment} => string (e.g. "This is a funny subnet") # $Scope{}{ranges} => array of paired inclusion IP addresses # (e.g. "1.2.3.1 1.2.3.10 1.2.3.100 10.2.3.200 # says that we have 2 inclusion ranges of # 1-10 and 100-200) # $Scopes{}{routers} => array of IP address strings # $Scopes{}{dnses} => array of IP address/name string # $Scopes{}{domain} > string # $Scopes{}{broadcast} => string # $Scopes{}{winses} => array of IP addresses/name string # $Scopes{}{winstype} => integer # $Scopes{}{leaseduration} => integer my ($RegVal, @Subnets, @Router, $SubnetName, $SubnetComment, @SubnetOptions, @SRouter, @SDNSServers, @SDomainname, @SWINSservers, @SNetBIOS, @SLeaseDuration, @SSubnetState, @SExclusionRanges, @SSubnetAddress, @SSubnetMask, @SFirstAddress, $SStartAddress, $SEndAddress, @InclusionRanges, @SBroadcastAddress); print "Getting list of subnets\n"; if (Registry::GetRegSubkeyList ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets", \@Subnets)) { die "Unable to obtain a list of subnets from the server!\n"; } for (my $i=0; $i<=$#Subnets; $i++) { print "\t Fetching Subnet $Subnets[$i] (",$i+1, "/", $#Subnets+1, "): "; print "."; if (!Registry::GetRegSubkeyList ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\IpRanges", \@SFirstAddress)) { # Don't know why MS has a tree for this, but as far # as I can tell, only one subtree will ever come out of # this, so I'm skipping the 'for' loop print "."; if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\IpRanges\\$SFirstAddress[0]\\StartAddress", \$RegVal)) { $SStartAddress = $RegVal; } print "."; if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\IpRanges\\$SFirstAddress[0]\\EndAddress", \$RegVal)) { $SEndAddress = $RegVal; } # print "\n\tInclusion Range: ", Registry::ExtractIp($SStartAddress), " - ", Registry::ExtractIp($SEndAddress),"\n"; } else { die "\n\n# Error Getting Inclusion Range FirstAddress!!!\n\n"; } if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\ExcludedIpRanges", \$RegVal)) { @SExclusionRanges = Registry::ExtractExclusionRanges($RegVal); # for (my $j=2; $j<=$#SExclusionRanges; $j+=2) { # if (unpack("L",$SExclusionRanges[$j]) < unpack("L",$SExclusionRanges[$j-2])) { # print ("\n******** Subnet exclusion ranges out of order ********\n"); # } # } @SExclusionRanges = sort(@SExclusionRanges); # print "\n\tExclusion Ranges: "; # for (my $j=0; $j<=$#SExclusionRanges; $j+=2) { # print "\n\t\t",Registry::ExtractIp($SExclusionRanges[$j])," - ",Registry::ExtractIp($SExclusionRanges[$j+1]); # } } @InclusionRanges = FindInclusionRanges ($SStartAddress, $SEndAddress, @SExclusionRanges); print "."; if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\SubnetName", \$RegVal)) { $SubnetName = $RegVal; # print "\n\tSubnetName: $SubnetName"; } print "."; if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\SubnetComment", \$RegVal)) { $SubnetComment = $RegVal; # print "\n\tSubnetComment: $SubnetComment"; } print "."; if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\SubnetAddress", \$RegVal)) { @SSubnetAddress = Registry::ExtractIp($RegVal); # print "\n\tSubnetAddress: $SSubnetAddress[0]"; } print "."; if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\SubnetMask", \$RegVal)) { @SSubnetMask = Registry::ExtractIp($RegVal); # print "\n\tSubnetMask: $SSubnetMask[0]"; } print "."; if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\SubnetState", \$RegVal)) { @SSubnetState = Registry::ExtractHex ($RegVal); # print "\n\tSubnetState = $SSubnetState[0]"; } $Scopes{$Subnets[$i]}{disable} = hex($SSubnetState[0]) ? 1 : 0; $Scopes{$Subnets[$i]}{mask} = $SSubnetMask[0]; $Scopes{$Subnets[$i]}{name} = $SubnetName; $Scopes{$Subnets[$i]}{comment} = $SubnetComment; for (my $r=0; $r<=$#InclusionRanges; $r++) { $Scopes{$Subnets[$i]}{ranges}[$r] = Registry::ExtractIp($InclusionRanges[$r]); } ################## Get scope options my (@SubnetOptionsList); print "\n\t\tOptions:"; if (Registry::GetRegSubkeyList ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\SubnetOptions", \@SubnetOptionsList)) { die "Unable to get subnet options list for $Subnets[$i]!\n"; } for (my $j=0; $j<=$#SubnetOptionsList; $j++) { print "."; if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\SubnetOptions\\$SubnetOptionsList[$j]\\OptionValue", \$RegVal)) { for ($SubnetOptionsList[$j]) { /003/ and do { # @SRouter = Registry::ExtractOptionIps($RegVal); $Scopes{$Subnets[$i]}{routers} = [Registry::ExtractOptionIps($RegVal)]; last; }; /006/ and do { @SDNSServers = Registry::ExtractOptionIps($RegVal); for (my $d=0; $d<=$#SDNSServers; $d++) { my ($ipname, $rest) = gethostbyaddr(pack("C4", split(/\./, $SDNSServers[$d])), &AF_INET); $Scopes{$Subnets[$i]}{dnses}[$d] = $ipname ? $ipname : $SDNSServers[$d]; } last; }; /015/ and do { @SDomainname = Registry::ExtractOptionStrings($RegVal); $Scopes{$Subnets[$i]}{domain} = $SDomainname[0]; last; }; /028/ and do { @SBroadcastAddress = Registry::ExtractOptionIps($RegVal); $Scopes{$Subnets[$i]}{broadcast} = $SBroadcastAddress[0]; last; }; /044/ and do { @SWINSservers = Registry::ExtractOptionIps($RegVal); for (my $w=0; $w<=$#SWINSservers; $w++) { my ($ipname, $rest) = gethostbyaddr(pack("C4", split(/\./, $SWINSservers[$w])), &AF_INET); $Scopes{$Subnets[$i]}{winses}[$w] = $ipname ? $ipname : $SWINSservers[$w]; } last; }; /046/ and do { @SNetBIOS = Registry::ExtractOptionHex($RegVal); $Scopes{$Subnets[$i]}{winstype} = hex($SNetBIOS[0]); last; }; /051/ and do { @SLeaseDuration = Registry::ExtractOptionHex($RegVal); $Scopes{$Subnets[$i]}{leaseduration} = hex($SLeaseDuration[0]); last; }; die "This program does not recognize subnet option \#$SubnetOptionsList[$j] yet!\n" } } else { die "Unable to obtain option SubnetOptionsList[$j] from $Subnets[$i], most likely a registry problem!\n" } } print "\n"; } return %Scopes; } ###################################################################### sub FindInclusionRanges ($$@) { my ($StartAddress, $EndAddress, @ExclusionRanges) = @_; # Purpose: to calculate and return the DHCP inclusion ranges out of # data provided by the NT4 DHCP server # input: $StartAddress: # $EndAddress: # @ExclusionRanges # output: none # return: An arry of IP address pair representing the inclusion ranges # in the native registry format. # my ($SA, $EA, @ER); $SA = unpack("L", $StartAddress); $EA = unpack("L", $EndAddress); @ER = @ExclusionRanges; for (my $i=0; $i<=$#ER; $i++) { $ER[$i] = unpack ("L", $ER[$i]); } my @InclusionRanges; $InclusionRanges[0] = $SA; $InclusionRanges[1] = $EA; for (my $i=0; $i<=$#ER; $i+=2) { if ($ER[$i] == $InclusionRanges[$#InclusionRanges-1]) { $InclusionRanges[$#InclusionRanges-1] = $ER[$i+1] + 1; } if ($ER[$i] > $InclusionRanges[$#InclusionRanges-1]) { $InclusionRanges[$#InclusionRanges] = $ER[$i]-1; } if (($ER[$i+1] > $InclusionRanges[$#InclusionRanges]) && ($ER[$i+1] != $EA)) { $InclusionRanges[$#InclusionRanges+1] = $ER[$i+1] + 1; $InclusionRanges[$#InclusionRanges+1] = $EA; } if ($InclusionRanges[$#InclusionRanges] < $InclusionRanges[$#InclusionRanges-1]) { $#InclusionRanges -= 2; } } for (my $i=0; $i<=$#InclusionRanges; $i++) { $InclusionRanges[$i] = pack("L", $InclusionRanges[$i]); # print "Inclusion: ", Registry::ExtractIp($InclusionRanges[$i]), "\n"; } return @InclusionRanges; } #################################################################### sub GetSuperScope ($) { my ($Server) = @_; my (%SuperScopes); # # purpose: gets the Superscope list from the given server # input: # $Server: string of the valid IP address or name of the NT4 server # ouput: none # return: # %SuperScopes: hash of array subnets with the following data structure # $SuperScopes{} => array of sunbets # my (@SuperScopeNames, @SCSubnetList); print "Getting Superscope list: "; if (!Registry::GetRegSubkeyList ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\SuperScope", \@SuperScopeNames)) { for (my $i=0; $i<=$#SuperScopeNames; $i++) { print "."; if (!Registry::GetRegSubkeyList ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\SuperScope\\$SuperScopeNames[$i]", \@SCSubnetList)) { $SuperScopes{$SuperScopeNames[$i]} = [@SCSubnetList]; } } print "\n"; } return %SuperScopes; } #################################################################### sub GetGlobalOptions($) { my ($Server) = @_; my (%GlobalOptions); # purpose: to return NT4 server's global scope configuration # input: # $Server: string of the valid IP or name of the NT4 server # output: none # return: # %GlobalOptions: hash of hash of various data types to be returned of the # following data structure # $GlobalOptions{routers} => array of IP address strings # $GlobalOptions{dnses} => array of IP address/name string # $GlobalOptions{domain} > string # $GlobalOptions{broadcast} => string # $GlobalOptions{winses} => array of IP addresses/name string # $GlobalOptions{winstype} => integer my ($RegVal, @temp, @GlobalOptionValues); print "Getting Global Options: "; if (Registry::GetRegSubkeyList ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\GlobalOptionValues", \@GlobalOptionValues)) { die "Unable to obtain GlobalOptionValues"; } for (my $i=0; $i<=$#GlobalOptionValues; $i++) { print "."; if (Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\configuration\\globaloptionvalues\\$GlobalOptionValues[$i]\\optionvalue", \$RegVal)) { die "Unable to retrive global option $GlobalOptionValues[$i]\n"; } for ($GlobalOptionValues[$i]) { /003/ and do { @temp=Registry::ExtractOptionIps($RegVal); $GlobalOptions{routers} = [@temp]; last; }; /006/ and do { # DNS Servers @temp = Registry::ExtractOptionIps($RegVal); for (my $d=0; $d<=$#temp; $d++) { my ($ipname, $rest) = gethostbyaddr(pack("C4", split(/\./, $temp[$d])), &AF_INET); $GlobalOptions{dnses}[$d] = $ipname ? $ipname : $temp[$d]; } last; }; /015/ and do { # Domain Name @temp = Registry::ExtractOptionStrings($RegVal); $GlobalOptions{domain} = $temp[0]; last; }; /028/ and do { # broadcast address @temp = Registry::ExtractOptionIps($RegVal); $GlobalOptions{broadcast} = $temp[0]; last; }; /044/ and do { # WINS Servers @temp = Registry::ExtractOptionIps ($RegVal); $GlobalOptions{winses} = [@temp]; for (my $w=0; $w<=$#temp; $w++) { my ($ipname, $rest) = gethostbyaddr(pack("C4", split(/\./, $temp[$w])), &AF_INET); $GlobalOptions{winses}[$w] = $ipname ? $ipname : $temp[$w]; } last; }; /046/ and do { # NETBIOS node type @temp = Registry::ExtractOptionHex($RegVal); $GlobalOptions{winstype} = hex($temp[0]); last; }; die "This program does not recgonize global option \#$GlobalOptionValues[$i] yet!\n" } } print "\n"; return %GlobalOptions; } dhcp-4.4.1/contrib/ms2isc/readme.txt000644 000765 000024 00000001015 13243301226 017546 0ustar00tmarkstaff000000 000000 Copyright: please read the top of the source code. Usage: Objective: please read the help screen by executing the program without any parameter. Revision: SMC: Shu-Min Chang Who When What --- ------ -------------------------------------------------------------------- SMC 021107 Initial release Version 1.0 to ISC DHCP repository SMC 030129 Fixed inclusion range calculation by sorting exclusion before passing to FindInclusionRanges SMC 030228 release 1.0.1 to ISC DHCP repository dhcp-4.4.1/contrib/ms2isc/Registry.pm000644 000765 000024 00000030040 13243301226 017716 0ustar00tmarkstaff000000 000000 # Registry.pm # A perl module provided easy Windows Registry access # # Author: Shu-Min Chang # # Copyright(c) 2002 Intel Corporation. All rights reserved # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright notice # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution # 3. Neither the name of Intel Corporation nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE INTEL CORPORATION AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL CORPORATION OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO PROCUREMENT OF SUBSTITUE # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT # OF THE USE OF THIS SOFTWARE, EVEN IF ADVICED OF THE POSSIBILITY OF SUCH # DAMAGE. package Registry; use strict; use Win32API::Registry 0.21 qw( :ALL ); ############################################################################### #----------------------------------------- sub GetRegKeyVal($*) { my ($FullRegPath, $value) = @_; #----------------------------------------- # Purpose: uses Win32API to get registry information from a given server # # WARNING: this procedure is VERY Win32 specific, you'll need a Win32 manual # to figure out why something is done. # input: $FullRegPath: a MS specific way of fully qualifying a registry path # \\Server\RootKey\Path\ValueName # output: *value: the value of the registry key of $FullRegPath # my ($RemoteMachine, $RootKey, $RegPath, $KeyName, $i); #print "in sub:GetRegKeyVal:Parameters:", @_, "\n"; # Check the for valid fully qualified registry path return -1 if (! ($FullRegPath =~ /\\.+\\.+/)) && (!($FullRegPath =~ /\\\\.+\\.+\\.+/)); $RemoteMachine = (index($FullRegPath, "\\\\") == $[ ? substr($FullRegPath, $[+2, index($FullRegPath, "\\", $[+2)-2):0); #print "RemoteMachine = $RemoteMachine\n"; $i = $RemoteMachine ? $[+3+length($RemoteMachine) : $[+1; $RootKey = substr ($FullRegPath, $i, index($FullRegPath, "\\", $i)-$i); $KeyName = $FullRegPath; $KeyName =~ s/.*\\(.+)/$1/; #print "KeyName = $KeyName\n"; $i = index($FullRegPath, $RootKey, $[+length($RemoteMachine)) + $[ + length($RootKey)+1; $RegPath = substr ($FullRegPath, $i, length($FullRegPath) - length($KeyName) -$i - 1); #print "RegPath = $RegPath\n"; my ($RootKeyHandle, $handle, $key, $type); if ($RemoteMachine) { $RootKeyHandle = regConstant($RootKey); if (!RegConnectRegistry ($RemoteMachine, $RootKeyHandle, $handle)) { $$value = regLastError(); return -2; } } else { # not valid actually because I can't find the mapping table of default # local handle mapping. Should always pass in the Machine name to use for now $handle = $RootKey; } if (!RegOpenKeyEx ($handle, $RegPath, 0, KEY_READ, $key)) { $$value = regLastError(); #print "regLastError = $$value\n"; return -3; } if (!RegQueryValueEx( $key, $KeyName, [], $type, $$value, [] )) { $$value = regLastError(); #print "regLastError = $$value\n"; return -4; } #print "RegType=$type\n"; # Perl doesn't fetch type, at this in this # ActiveState 5.6.0 that I'm using #print "RegValue=$$value\n"; RegCloseKey ($key); RegCloseKey ($handle); return 0; } ############################################################################### #----------------------------------------- sub GetRegSubkeyList($*) { my ($FullKeyRegPath, $Subkeys) = @_; #----------------------------------------- # Purpose: uses Win32API to get registry subkey list from a given server # # WARNING: this procedure is VERY Win32 specific, you'll need a Win32 manual # to figure out why something is done. # input: $FullKeyRegPath: a MS specific way of fully qualifying a registry path # \\Server\RootKey\Path\KeyName # output: *Subkeys: the list of subkeys in array of the registry key of # $FullKeyRegPath # my ($RemoteMachine, $RootKey, $RegPath, $KeyName, $i); #print "in sub:GetRegSubkeyList:Parameters:", @_, "\n"; # Check the for valid registry key path return -1 if (! ($FullKeyRegPath =~ /\\.+\\.+/)) && (!($FullKeyRegPath =~ /\\\\.+\\.+\\.+/)); $RemoteMachine = (index($FullKeyRegPath, "\\\\") == $[ ? substr($FullKeyRegPath, $[+2, index($FullKeyRegPath, "\\", $[+2)-2):0); #print "RemoteMachine = $RemoteMachine\n"; $i = $RemoteMachine ? $[+3+length($RemoteMachine) : $[+1; $RootKey = substr ($FullKeyRegPath, $i, index($FullKeyRegPath, "\\", $i)-$i); $i = index($FullKeyRegPath, $RootKey, $[+length($RemoteMachine)) + $[ + length($RootKey)+1; $RegPath = substr ($FullKeyRegPath, $i); #print "RegPath = $RegPath\n"; my ($RootKeyHandle, $handle, $key, $type); if ($RemoteMachine) { $RootKeyHandle = regConstant($RootKey); if (!RegConnectRegistry ($RemoteMachine, $RootKeyHandle, $handle)) { @$Subkeys[0]= regLastError(); return -2; } } else { # not valid actually because I can't find the mapping table of default # local handle mapping. Should always pass in the Machine name to use for now $handle = $RootKey; } if (!RegOpenKeyEx ($handle, $RegPath, 0, KEY_READ, $key)) { @$Subkeys[0] = regLastError(); #print "regLastError = @$Subkeys[0]\n"; return -3; } my $tmp; # For some reason, the regLastError() stays at ERROR_NO_MORE_ITEMS # in occasional call sequence, so I'm resetting the error code # before entering the loop regLastError(0); for ($i=0; regLastError()==regConstant("ERROR_NO_MORE_ITEMS"); $i++) { #print "\nERROR: error enumumerating reg\n"; if (RegEnumKeyEx ($key, $i, $tmp, [], [], [], [], [])) { @$Subkeys[$i] = $tmp; } } #print "RegType=$type\n"; #print "RegValue=@$Subkeys\n"; RegCloseKey ($key); RegCloseKey ($handle); return 0; } ##################################################### sub ExtractOptionIps ($) { my ($MSDHCPOption6Value) = @_; my @ip; # purpose: DHCP registry specific; to return the extracted IP addresses from # the input variable # input: # $MSDHCPOption6Value: Option 6 was used to develop, but it works for any # other options of the same datatype. # output: none # return: # @ip: an arry of IP addresses in human readable format. # First extract the size of the option my ($byte, $size, $ind1, $ind2, @octet) = unpack("VVVV", $MSDHCPOption6Value); # print "byte = $byte\nsize=$size\nind1=$ind1\nind2=$ind2\n"; # Calculate total number of bytes that IP addresses occupy my $number = $size * $ind1; ($byte, $size, $ind1, $ind2, @octet) = unpack("VVVVC$number", $MSDHCPOption6Value); for (my $i=0; $i<$#octet; $i=$i+4) { $ip[$i/4] = "$octet[$i+3]\.$octet[$i+2]\.$octet[$i+1]\.$octet[$i]"; } return @ip; } ##################################################### sub ExtractOptionStrings ($) { my ($MSDHCPOption15Value) = @_; my @string; # purpose: DHCP registry specific; to return the extracted string from # the input variable # input: # $MSDHCPOption15Value: Option 15 was used to develop, but it works for any # other options of the same datatype. # output: none # return: # @string: an arry of strings in human readable format. # First extract the size of the option my ($byte, $start, $ind1, $ind2, $size, @data) = unpack("VVVVV", $MSDHCPOption15Value); # print "byte = $byte\nstart=$start\nind1=$ind1\nind2=$ind2\nsize=$size\n"; # Calculate total number of bytes that IP addresses occupy my $number = $size * $ind1; ($byte, $start, $ind1, $ind2, $size, @data) = unpack("VVVVVC$number", $MSDHCPOption15Value); for (my $i=0; $i<$ind1; $i++) { # actually this is only programmed to do one string, until I see # example of how the multiple strings are represented, I don't have a # guess to how to program them properly. for (my $j=0; $j<$#data & $data[$j]!=0; $j+=2) { $string[$i] = $string[$i].chr($data[$j]); } } return @string; } ##################################################### sub ExtractOptionHex ($) { my ($MSDHCPOption46Value) = @_; my @Hex; # purpose: DHCP registry specific; to return the extracted hex from the input # variable # input: # $MSDHCPOption46Value: Option 46 was used to develop, but it works for any # other options of the same datatype. # output: none # return: # @Hex: an arry of hex strings in human readable format. my $Temp; # First extract the size of the option my ($byte, $unknown, $ind1, $ind2, @data) = unpack("VVVV", $MSDHCPOption46Value); # print "byte=$byte\nunknown=$unknown\nind1=$ind1\nind2=$ind2\n"; # Calculate total number of bytes that IP addresses occupy my $number = $byte - 15; ($byte, $unknown, $ind1, $ind2, @data) = unpack("VVVVC$number", $MSDHCPOption46Value); # printf "data=%4x\n", $data[0]; for (my $i=0; $i<$ind1; $i++) { # actually this is only programmed to do one Hex, until I see # example of how the multiple Hexes are represented, I don't have a # guess to how to program them properly. for (my $j=3; $j>=0; $j--) { $Hex[$i] = $Hex[$i].sprintf ("%x", $data[$j+$i*4]); } } return @Hex; } ##################################################### sub ExtractExclusionRanges ($) { my ($MSDHCPExclusionRanges) = @_; my @RangeList; # purpose: DHCP registry specific; to return the extracted exclusion ranges # from the input variable # input: # $MSDHCPExclusionRanges: Exclusion range as DHCP server returns them # output: none # return: # @RangeList: an arry of paird IP addresses strings in human readable format. # First extract the size of the option my ($paircount, @data) = unpack("V", $MSDHCPExclusionRanges); # print "paircount = $paircount\n"; # Calculate total number of bytes that IP addresses occupy # my $number = $paircount * 4*2; # ($paircount, @data) = unpack("VC$number", $MSDHCPExclusionRanges); # # for (my $i=0; $i<$#data; $i=$i+4) { # $ip[$i/4] = "$data[$i+3]\.$data[$i+2]\.$data[$i+1]\.$data[$i]"; # } # my $number = $paircount * 2; ($paircount, @data) = unpack("VL$number", $MSDHCPExclusionRanges); for (my $i=0; $i<=$#data; $i++) { $RangeList[$i] = pack ("L", $data[$i]); # print "extracted", ExtractIp ($RangeList[$i]), "\n"; } return @RangeList; } ##################################################### sub ExtractIp ($) { my ($octet) = @_; # purpose: to return the registry saved IP address in a readable form # input: # $octet: a 4 byte data storing the IP address as the registry save it as # output: none # return: anonymous variable of a string of IP address my (@data) = unpack ("C4", $octet); return "$data[3]\.$data[2]\.$data[1]\.$data[0]"; } ##################################################### sub ExtractHex ($) { my ($HexVal) = @_; my @Hex; # purpose: to return the registry saved hex number in a readable form # input: # $octet: a 4 byte data storing the hex number as the registry save it as # output: none # return: # $Hex: string of hex digit # First extract the size of the option my (@data) = unpack("C4", $HexVal); for (my $i=3; $i>=0; $i--) { $Hex[0] = $Hex[0] . sprintf ("%x", $data[$i]); } return @Hex; } 1; dhcp-4.4.1/contrib/ldap/dhcp.schema000644 000765 000024 00000054525 13243301226 017406 0ustar00tmarkstaff000000 000000 attributetype ( 2.16.840.1.113719.1.203.4.1 NAME 'dhcpPrimaryDN' EQUALITY distinguishedNameMatch DESC 'The DN of the dhcpServer which is the primary server for the configuration.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE ) attributetype ( 2.16.840.1.113719.1.203.4.2 NAME 'dhcpSecondaryDN' EQUALITY distinguishedNameMatch DESC 'The DN of dhcpServer(s) which provide backup service for the configuration.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 ) attributetype ( 2.16.840.1.113719.1.203.4.3 NAME 'dhcpStatements' EQUALITY caseIgnoreIA5Match DESC 'Flexible storage for specific data depending on what object this exists in. Like conditional statements, server parameters, etc. This allows the standard to evolve without needing to adjust the schema.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) attributetype ( 2.16.840.1.113719.1.203.4.4 NAME 'dhcpRange' EQUALITY caseIgnoreIA5Match DESC 'The starting & ending IP Addresses in the range (inclusive), separated by a hyphen; if the range only contains one address, then just the address can be specified with no hyphen. Each range is defined as a separate value.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) attributetype ( 2.16.840.1.113719.1.203.4.5 NAME 'dhcpPermitList' EQUALITY caseIgnoreIA5Match DESC 'This attribute contains the permit lists associated with a pool. Each permit list is defined as a separate value.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) attributetype ( 2.16.840.1.113719.1.203.4.6 NAME 'dhcpNetMask' EQUALITY integerMatch DESC 'The subnet mask length for the subnet. The mask can be easily computed from this length.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) attributetype ( 2.16.840.1.113719.1.203.4.7 NAME 'dhcpOption' EQUALITY caseIgnoreIA5Match DESC 'Encoded option values to be sent to clients. Each value represents a single option and contains (OptionTag, Length, OptionValue) encoded in the format used by DHCP.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) attributetype ( 2.16.840.1.113719.1.203.4.8 NAME 'dhcpClassData' EQUALITY caseIgnoreIA5Match DESC 'Encoded text string or list of bytes expressed in hexadecimal, separated by colons. Clients match subclasses based on matching the class data with the results of match or spawn with statements in the class name declarations.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) attributetype ( 2.16.840.1.113719.1.203.4.9 NAME 'dhcpOptionsDN' EQUALITY distinguishedNameMatch DESC 'The distinguished name(s) of the dhcpOption objects containing the configuration options provided by the server.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 ) attributetype ( 2.16.840.1.113719.1.203.4.10 NAME 'dhcpHostDN' EQUALITY distinguishedNameMatch DESC 'the distinguished name(s) of the dhcpHost objects.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 ) attributetype ( 2.16.840.1.113719.1.203.4.11 NAME 'dhcpPoolDN' EQUALITY distinguishedNameMatch DESC 'The distinguished name(s) of pools.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 ) attributetype ( 2.16.840.1.113719.1.203.4.12 NAME 'dhcpGroupDN' EQUALITY distinguishedNameMatch DESC 'The distinguished name(s) of the groups.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 ) attributetype ( 2.16.840.1.113719.1.203.4.13 NAME 'dhcpSubnetDN' EQUALITY distinguishedNameMatch DESC 'The distinguished name(s) of the subnets.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 ) attributetype ( 2.16.840.1.113719.1.203.4.14 NAME 'dhcpLeaseDN' EQUALITY distinguishedNameMatch DESC 'The distinguished name of a client address.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE) attributetype ( 2.16.840.1.113719.1.203.4.15 NAME 'dhcpLeasesDN' DESC 'The distinguished name(s) client addresses.' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 ) attributetype ( 2.16.840.1.113719.1.203.4.16 NAME 'dhcpClassesDN' EQUALITY distinguishedNameMatch DESC 'The distinguished name(s) of a class(es) in a subclass.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 ) attributetype ( 2.16.840.1.113719.1.203.4.17 NAME 'dhcpSubclassesDN' EQUALITY distinguishedNameMatch DESC 'The distinguished name(s) of subclass(es).' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 ) attributetype ( 2.16.840.1.113719.1.203.4.18 NAME 'dhcpSharedNetworkDN' EQUALITY distinguishedNameMatch DESC 'The distinguished name(s) of sharedNetworks.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 ) attributetype ( 2.16.840.1.113719.1.203.4.19 NAME 'dhcpServiceDN' EQUALITY distinguishedNameMatch DESC 'The DN of dhcpService object(s)which contain the configuration information. Each dhcpServer object has this attribute identifying the DHCP configuration(s) that the server is associated with.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 ) attributetype ( 2.16.840.1.113719.1.203.4.20 NAME 'dhcpVersion' DESC 'The version attribute of this object.' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) attributetype ( 2.16.840.1.113719.1.203.4.21 NAME 'dhcpImplementation' EQUALITY caseIgnoreIA5Match DESC 'Description of the DHCP Server implementation e.g. DHCP Servers vendor.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) attributetype ( 2.16.840.1.113719.1.203.4.22 NAME 'dhcpAddressState' EQUALITY caseIgnoreIA5Match DESC 'This stores information about the current binding-status of an address. For dynamic addresses managed by DHCP, the values should be restricted to the following: "FREE", "ACTIVE", "EXPIRED", "RELEASED", "RESET", "ABANDONED", "BACKUP". For other addresses, it SHOULD be one of the following: "UNKNOWN", "RESERVED" (an address that is managed by DHCP that is reserved for a specific client), "RESERVED-ACTIVE" (same as reserved, but address is currently in use), "ASSIGNED" (assigned manually or by some other mechanism), "UNASSIGNED", "NOTASSIGNABLE".' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) attributetype ( 2.16.840.1.113719.1.203.4.23 NAME 'dhcpExpirationTime' EQUALITY generalizedTimeMatch DESC 'This is the time the current lease for an address expires.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE ) attributetype ( 2.16.840.1.113719.1.203.4.24 NAME 'dhcpStartTimeOfState' EQUALITY generalizedTimeMatch DESC 'This is the time of the last state change for a leased address.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE ) attributetype ( 2.16.840.1.113719.1.203.4.25 NAME 'dhcpLastTransactionTime' EQUALITY generalizedTimeMatch DESC 'This is the last time a valid DHCP packet was received from the client.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE ) attributetype ( 2.16.840.1.113719.1.203.4.26 NAME 'dhcpBootpFlag' EQUALITY booleanMatch DESC 'This indicates whether the address was assigned via BOOTP.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE ) attributetype ( 2.16.840.1.113719.1.203.4.27 NAME 'dhcpDomainName' EQUALITY caseIgnoreIA5Match DESC 'This is the name of the domain sent to the client by the server. It is essentially the same as the value for DHCP option 15 sent to the client, and represents only the domain - not the full FQDN. To obtain the full FQDN assigned to the client you must prepend the "dhcpAssignedHostName" to this value with a ".".' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) attributetype ( 2.16.840.1.113719.1.203.4.28 NAME 'dhcpDnsStatus' EQUALITY integerMatch DESC 'This indicates the status of updating DNS resource records on behalf of the client by the DHCP server for this address. The value is a 16-bit bitmask.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) attributetype ( 2.16.840.1.113719.1.203.4.29 NAME 'dhcpRequestedHostName' EQUALITY caseIgnoreIA5Match DESC 'This is the hostname that was requested by the client.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) attributetype ( 2.16.840.1.113719.1.203.4.30 NAME 'dhcpAssignedHostName' EQUALITY caseIgnoreIA5Match DESC 'This is the actual hostname that was assigned to a client. It may not be the name that was requested by the client. The fully qualified domain name can be determined by appending the value of "dhcpDomainName" (with a dot separator) to this name.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) attributetype ( 2.16.840.1.113719.1.203.4.31 NAME 'dhcpReservedForClient' EQUALITY distinguishedNameMatch DESC 'The distinguished name of a "dhcpClient" that an address is reserved for. This may not be the same as the "dhcpAssignedToClient" attribute if the address is being reassigned but the current lease has not yet expired.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE ) attributetype ( 2.16.840.1.113719.1.203.4.32 NAME 'dhcpAssignedToClient' EQUALITY distinguishedNameMatch DESC 'This is the distinguished name of a "dhcpClient" that an address is currently assigned to. This attribute is only present in the class when the address is leased.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE ) attributetype ( 2.16.840.1.113719.1.203.4.33 NAME 'dhcpRelayAgentInfo' EQUALITY octetStringMatch DESC 'If the client request was received via a relay agent, this contains information about the relay agent that was available from the DHCP request. This is a hex-encoded option value.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 SINGLE-VALUE ) attributetype ( 2.16.840.1.113719.1.203.4.34 NAME 'dhcpHWAddress' EQUALITY caseIgnoreIA5Match DESC 'The clients hardware address that requested this IP address.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) attributetype ( 2.16.840.1.113719.1.203.4.35 NAME 'dhcpHashBucketAssignment' EQUALITY octetStringMatch DESC 'HashBucketAssignment bit map for the DHCP Server, as defined in DHC Load Balancing Algorithm [RFC 3074].' SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 SINGLE-VALUE ) attributetype ( 2.16.840.1.113719.1.203.4.36 NAME 'dhcpDelayedServiceParameter' EQUALITY integerMatch DESC 'Delay in seconds corresponding to Delayed Service Parameter configuration, as defined in DHC Load Balancing Algorithm [RFC 3074]. ' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) attributetype ( 2.16.840.1.113719.1.203.4.37 NAME 'dhcpMaxClientLeadTime' EQUALITY integerMatch DESC 'Maximum Client Lead Time configuration in seconds, as defined in DHCP Failover Protocol [FAILOVR]' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) attributetype ( 2.16.840.1.113719.1.203.4.38 NAME 'dhcpFailOverEndpointState' EQUALITY caseIgnoreIA5Match DESC 'Server (Failover Endpoint) state, as defined in DHCP Failover Protocol [FAILOVR]' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) attributetype ( 2.16.840.1.113719.1.203.4.39 NAME 'dhcpErrorLog' EQUALITY caseIgnoreIA5Match DESC 'Generic error log attribute that allows logging error conditions within a dhcpService or a dhcpSubnet, like no IP addresses available for lease.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) attributetype ( 2.16.840.1.113719.1.203.4.40 NAME 'dhcpLocatorDN' EQUALITY distinguishedNameMatch DESC 'The DN of dhcpLocator object which contain the DNs of all DHCP configuration objects. There will be a single dhcpLocator object in the tree with links to all the DHCP objects in the tree' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 ) attributetype ( 2.16.840.1.113719.1.203.4.41 NAME 'dhcpKeyAlgorithm' EQUALITY caseIgnoreIA5Match DESC 'Algorithm to generate TSIG Key' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) attributetype ( 2.16.840.1.113719.1.203.4.42 NAME 'dhcpKeySecret' EQUALITY octetStringMatch DESC 'Secret to generate TSIG Key' SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 SINGLE-VALUE ) attributetype ( 2.16.840.1.113719.1.203.4.43 NAME 'dhcpDnsZoneServer' EQUALITY caseIgnoreIA5Match DESC 'Master server of the DNS Zone' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) attributetype ( 2.16.840.1.113719.1.203.4.44 NAME 'dhcpKeyDN' EQUALITY distinguishedNameMatch DESC 'The DNs of TSIG Key to use in secure dynamic updates. In case of locator object, this will be list of TSIG keys. In case of DHCP Service, Shared Network, Subnet and DNS Zone, it will be a single key.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12) attributetype ( 2.16.840.1.113719.1.203.4.45 NAME 'dhcpZoneDN' EQUALITY distinguishedNameMatch DESC 'The DNs of DNS Zone. In case of locator object, this will be list of DNS Zones in the tree. In case of DHCP Service, Shared Network and Subnet, it will be a single DNS Zone.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12) attributetype ( 2.16.840.1.113719.1.203.4.46 NAME 'dhcpFailOverPrimaryServer' EQUALITY caseIgnoreIA5Match DESC 'IP address or DNS name of the server playing primary role in DHC Load Balancing and Fail over.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) attributetype ( 2.16.840.1.113719.1.203.4.47 NAME 'dhcpFailOverSecondaryServer' EQUALITY caseIgnoreIA5Match DESC 'IP address or DNS name of the server playing secondary role in DHC Load Balancing and Fail over.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) attributetype ( 2.16.840.1.113719.1.203.4.48 NAME 'dhcpFailOverPrimaryPort' EQUALITY integerMatch DESC 'Port on which primary server listens for connections from its fail over peer (secondary server)' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 ) attributetype ( 2.16.840.1.113719.1.203.4.49 NAME 'dhcpFailOverSecondaryPort' EQUALITY integerMatch DESC 'Port on which secondary server listens for connections from its fail over peer (primary server)' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 ) attributetype ( 2.16.840.1.113719.1.203.4.50 NAME 'dhcpFailOverResponseDelay' EQUALITY integerMatch DESC 'Maximum response time in seconds, before Server assumes that connection to fail over peer has failed' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 ) attributetype ( 2.16.840.1.113719.1.203.4.51 NAME 'dhcpFailOverUnackedUpdates' EQUALITY integerMatch DESC 'Number of BNDUPD messages that server can send before it receives BNDACK from its fail over peer' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 ) attributetype ( 2.16.840.1.113719.1.203.4.52 NAME 'dhcpFailOverSplit' EQUALITY integerMatch DESC 'Split between the primary and secondary servers for fail over purpose' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 ) attributetype ( 2.16.840.1.113719.1.203.4.53 NAME 'dhcpFailOverLoadBalanceTime' EQUALITY integerMatch DESC 'Cutoff time in seconds, after which load balance is disabled' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 ) attributetype ( 2.16.840.1.113719.1.203.4.54 NAME 'dhcpFailOverPeerDN' EQUALITY distinguishedNameMatch DESC 'The DNs of Fail over peers. In case of locator object, this will be list of fail over peers in the tree. In case of Subnet and pool, it will be a single Fail Over Peer' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 ) #List of all servers in the tree attributetype ( 2.16.840.1.113719.1.203.4.55 NAME 'dhcpServerDN' EQUALITY distinguishedNameMatch DESC 'List of all DHCP Servers in the tree. Used by dhcpLocatorObject' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 ) attributetype ( 2.16.840.1.113719.1.203.4.56 NAME 'dhcpComments' EQUALITY caseIgnoreIA5Match DESC 'Generic attribute that allows coments within any DHCP object' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) attributetype ( 2.16.840.1.113719.1.203.4.57 NAME 'dhcpClientId' EQUALITY caseIgnoreIA5Match DESC 'client Identifier.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) attributetype ( 2.16.840.1.113719.1.203.4.58 NAME 'dhcpRange6' EQUALITY caseIgnoreIA5Match DESC 'The starting & ending IP Addresses in the range (inclusive), separated by a hyphen; if the range only contains one address, then just the address can be specified with no hyphen. Each range is defined as a separate value.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) # Classes objectclass ( 2.16.840.1.113719.1.203.6.1 NAME 'dhcpService' DESC 'Service object that represents the actual DHCP Service configuration. This is a container object.' SUP top MUST (cn) MAY ( dhcpPrimaryDN $ dhcpSecondaryDN $ dhcpServerDN $ dhcpSharedNetworkDN $ dhcpSubnetDN $ dhcpGroupDN $ dhcpHostDN $ dhcpClassesDN $ dhcpOptionsDN $ dhcpZoneDN $ dhcpKeyDN $ dhcpFailOverPeerDN $ dhcpStatements $dhcpComments $ dhcpOption) ) objectclass ( 2.16.840.1.113719.1.203.6.2 NAME 'dhcpSharedNetwork' DESC 'This stores configuration information for a shared network.' SUP top MUST cn MAY ( dhcpSubnetDN $ dhcpPoolDN $ dhcpOptionsDN $ dhcpZoneDN $ dhcpStatements $dhcpComments $ dhcpOption) X-NDS_CONTAINMENT ('dhcpService' ) ) objectclass ( 2.16.840.1.113719.1.203.6.3 NAME 'dhcpSubnet' DESC 'This class defines a subnet. This is a container object.' SUP top MUST ( cn $ dhcpNetMask ) MAY ( dhcpRange $ dhcpPoolDN $ dhcpGroupDN $ dhcpHostDN $ dhcpClassesDN $ dhcpLeasesDN $ dhcpOptionsDN $ dhcpZoneDN $ dhcpKeyDN $ dhcpFailOverPeerDN $ dhcpStatements $ dhcpComments $ dhcpOption ) X-NDS_CONTAINMENT ('dhcpService' 'dhcpSharedNetwork') ) objectclass ( 2.16.840.1.113719.1.203.6.4 NAME 'dhcpPool' DESC 'This stores configuration information about a pool.' SUP top MUST ( cn $ dhcpRange ) MAY ( dhcpClassesDN $ dhcpPermitList $ dhcpLeasesDN $ dhcpOptionsDN $ dhcpZoneDN $dhcpKeyDN $ dhcpStatements $ dhcpComments $ dhcpOption ) X-NDS_CONTAINMENT ('dhcpSubnet' 'dhcpSharedNetwork') ) objectclass ( 2.16.840.1.113719.1.203.6.5 NAME 'dhcpGroup' DESC 'Group object that lists host DNs and parameters. This is a container object.' SUP top MUST cn MAY ( dhcpHostDN $ dhcpOptionsDN $ dhcpStatements $ dhcpComments $ dhcpOption ) X-NDS_CONTAINMENT ('dhcpSubnet' 'dhcpService' ) ) objectclass ( 2.16.840.1.113719.1.203.6.6 NAME 'dhcpHost' DESC 'This represents information about a particular client' SUP top MUST cn MAY (dhcpLeaseDN $ dhcpHWAddress $ dhcpOptionsDN $ dhcpStatements $ dhcpComments $ dhcpOption $ dhcpClientId) X-NDS_CONTAINMENT ('dhcpService' 'dhcpSubnet' 'dhcpGroup') ) objectclass ( 2.16.840.1.113719.1.203.6.7 NAME 'dhcpClass' DESC 'Represents information about a collection of related clients.' SUP top MUST cn MAY (dhcpSubClassesDN $ dhcpOptionsDN $ dhcpStatements $ dhcpComments $ dhcpOption) X-NDS_CONTAINMENT ('dhcpService' 'dhcpSubnet' ) ) objectclass ( 2.16.840.1.113719.1.203.6.8 NAME 'dhcpSubClass' DESC 'Represents information about a collection of related classes.' SUP top MUST cn MAY (dhcpClassData $ dhcpOptionsDN $ dhcpStatements $ dhcpComments $ dhcpOption) X-NDS_CONTAINMENT 'dhcpClass' ) objectclass ( 2.16.840.1.113719.1.203.6.9 NAME 'dhcpOptions' DESC 'Represents information about a collection of options defined.' SUP top AUXILIARY MUST cn MAY ( dhcpOption $ dhcpComments ) X-NDS_CONTAINMENT ('dhcpService' 'dhcpSharedNetwork' 'dhcpSubnet' 'dhcpPool' 'dhcpGroup' 'dhcpHost' 'dhcpClass' ) ) objectclass ( 2.16.840.1.113719.1.203.6.10 NAME 'dhcpLeases' DESC 'This class represents an IP Address, which may or may not have been leased.' SUP top MUST ( cn $ dhcpAddressState ) MAY ( dhcpExpirationTime $ dhcpStartTimeOfState $ dhcpLastTransactionTime $ dhcpBootpFlag $ dhcpDomainName $ dhcpDnsStatus $ dhcpRequestedHostName $ dhcpAssignedHostName $ dhcpReservedForClient $ dhcpAssignedToClient $ dhcpRelayAgentInfo $ dhcpHWAddress ) X-NDS_CONTAINMENT ( 'dhcpService' 'dhcpSubnet' 'dhcpPool') ) objectclass ( 2.16.840.1.113719.1.203.6.11 NAME 'dhcpLog' DESC 'This is the object that holds past information about the IP address. The cn is the time/date stamp when the address was assigned or released, the address state at the time, if the address was assigned or released.' SUP top MUST ( cn ) MAY ( dhcpAddressState $ dhcpExpirationTime $ dhcpStartTimeOfState $ dhcpLastTransactionTime $ dhcpBootpFlag $ dhcpDomainName $ dhcpDnsStatus $ dhcpRequestedHostName $ dhcpAssignedHostName $ dhcpReservedForClient $ dhcpAssignedToClient $ dhcpRelayAgentInfo $ dhcpHWAddress $ dhcpErrorLog) X-NDS_CONTAINMENT ('dhcpLeases' 'dhcpPool' 'dhcpSubnet' 'dhcpSharedNetwork' 'dhcpService' ) ) objectclass ( 2.16.840.1.113719.1.203.6.12 NAME 'dhcpServer' DESC 'DHCP Server Object' SUP top MUST ( cn ) MAY (dhcpServiceDN $ dhcpLocatorDN $ dhcpVersion $ dhcpImplementation $ dhcpHashBucketAssignment $ dhcpDelayedServiceParameter $ dhcpMaxClientLeadTime $ dhcpFailOverEndpointState $ dhcpStatements $ dhcpComments $ dhcpOption) X-NDS_CONTAINMENT ('organization' 'organizationalunit' 'domain') ) objectclass ( 2.16.840.1.113719.1.203.6.13 NAME 'dhcpTSigKey' DESC 'TSIG key for secure dynamic updates' SUP top MUST (cn $ dhcpKeyAlgorithm $ dhcpKeySecret ) MAY ( dhcpComments ) X-NDS_CONTAINMENT ('dhcpService' 'dhcpSharedNetwork' 'dhcpSubnet') ) objectclass ( 2.16.840.1.113719.1.203.6.14 NAME 'dhcpDnsZone' DESC 'DNS Zone for updating leases' SUP top MUST (cn $ dhcpDnsZoneServer ) MAY (dhcpKeyDN $ dhcpComments) X-NDS_CONTAINMENT ('dhcpService' 'dhcpSharedNetwork' 'dhcpSubnet') ) objectclass ( 2.16.840.1.113719.1.203.6.15 NAME 'dhcpFailOverPeer' DESC 'This class defines the Fail over peer' SUP top MUST ( cn $ dhcpFailOverPrimaryServer $ dhcpFailOverSecondaryServer $ dhcpFailoverPrimaryPort $ dhcpFailOverSecondaryPort) MAY (dhcpFailOverResponseDelay $ dhcpFailOverUnackedUpdates $ dhcpMaxClientLeadTime $ dhcpFailOverSplit $ dhcpHashBucketAssignment $ dhcpFailOverLoadBalanceTime $ dhcpComments ) X-NDS_CONTAINMENT ('dhcpService' 'dhcpSharedNetwork' 'dhcpSubnet') ) objectclass ( 2.16.840.1.113719.1.203.6.16 NAME 'dhcpLocator' DESC 'Locator object for DHCP configuration in the tree. There will be a single dhcpLocator object in the tree with links to all the DHCP objects in the tree' SUP top MUST ( cn ) MAY ( dhcpServiceDN $dhcpServerDN $ dhcpSharedNetworkDN $ dhcpSubnetDN $ dhcpPoolDN $ dhcpGroupDN $ dhcpHostDN $ dhcpClassesDN $ dhcpKeyDN $ dhcpZoneDN $ dhcpFailOverPeerDN $ dhcpOption $ dhcpComments) X-NDS_CONTAINMENT ('organization' 'organizationalunit' 'domain') ) objectclass ( 2.16.840.1.113719.1.203.6.17 NAME 'dhcpSubnet6' DESC 'This class defines an IPv6 subnet. This is a container object.' SUP top MUST ( cn ) MAY ( dhcpRange6 $ dhcpPoolDN $ dhcpGroupDN $ dhcpHostDN $ dhcpClassesDN $ dhcpLeasesDN $ dhcpOptionsDN $ dhcpZoneDN $ dhcpKeyDN $ dhcpFailOverPeerDN $ dhcpStatements $ dhcpComments $ dhcpOption $ dhcpPermitList ) X-NDS_CONTAINMENT ('dhcpService' 'dhcpSharedNetwork') ) objectclass ( 2.16.840.1.113719.1.203.6.18 NAME 'dhcpPool6' DESC 'This stores configuration information about an IPv6 pool.' SUP top MUST ( cn $ dhcpRange6 ) MAY ( dhcpClassesDN $ dhcpPermitList $ dhcpLeasesDN $ dhcpOptionsDN $ dhcpZoneDN $dhcpKeyDN $ dhcpStatements $ dhcpComments $ dhcpOption ) X-NDS_CONTAINMENT ('dhcpSubnet6' 'dhcpSharedNetwork') ) dhcp-4.4.1/contrib/ldap/dhcpd-conf-to-ldap000644 000765 000024 00000043224 13243301226 020566 0ustar00tmarkstaff000000 000000 #!/usr/bin/perl -w # Brian Masney # To use this script, set your base DN below. Then run # ./dhcpd-conf-to-ldap.pl < /path-to-dhcpd-conf/dhcpd.conf > output-file # The output of this script will generate entries in LDIF format. You can use # the slapadd command to add these entries into your LDAP server. You will # definately want to double check that your LDAP entries are correct before # you load them into LDAP. # This script does not do much error checking. Make sure before you run this # that the DHCP server doesn't give any errors about your config file # FailOver notes: # Failover is disabled by default, since it may need manually intervention. # You can try the '--use=failover' option to see what happens :-) # # If enabled, the failover pool references will be written to LDIF output. # The failover configs itself will be added to the dhcpServer statements # and not to the dhcpService object (since this script uses only one and # it may be usefull to have multiple service containers in failover mode). # Further, this script does not check if primary or secondary makes sense, # it simply converts what it gets... use Net::Domain qw(hostname hostfqdn hostdomain); use Getopt::Long; my $domain = hostdomain(); # your.domain my $basedn = "dc=".$domain; $basedn =~ s/\./,dc=/g; # dc=your,dc=domain my $server = hostname(); # hostname (nodename) my $dhcpcn = 'DHCP Config'; # CN of DHCP config tree my $dhcpdn = "cn=$dhcpcn, $basedn"; # DHCP config tree DN my $second = ''; # secondary server DN / hostname my $i_conf = ''; # dhcp.conf file to read or stdin my $o_ldif = ''; # output ldif file name or stdout my @use = (); # extended flags (failover) sub usage($;$) { my $rc = shift; my $err= shift; print STDERR "Error: $err\n\n" if(defined $err); print STDERR <<__EOF_USAGE__; usage: $0 [options] < dhcpd.conf > dhcpd.ldif options: --basedn "dc=your,dc=domain" ("$basedn") --dhcpdn "dhcp config DN" ("$dhcpdn") --server "dhcp server name" ("$server") --second "secondary server or DN" ("$second") --conf "/path/to/dhcpd.conf" (default is stdin) --ldif "/path/to/output.ldif" (default is stdout) --use "extended features" (see source comments) __EOF_USAGE__ exit($rc); } sub next_token { local ($lowercase) = @_; local ($token, $newline); do { if (!defined ($line) || length ($line) == 0) { $line = <>; return undef if !defined ($line); chop $line; $line_number++; $token_number = 0; } $line =~ s/#.*//; $line =~ s/^\s+//; $line =~ s/\s+$//; } while (length ($line) == 0); if (($token, $newline) = $line =~ /^(.*?)\s+(.*)/) { if ($token =~ /^"/) { #handle quoted token if ($token !~ /"\s*$/) { ($tok, $newline) = $newline =~ /([^"]+")(.*)/; $token .= " $tok"; } } $line = $newline; } else { $token = $line; $line = ''; } $token_number++; $token =~ y/[A-Z]/[a-z]/ if $lowercase; return ($token); } sub remaining_line { local ($block) = shift || 0; local ($tmp, $str); $str = ""; while (defined($tmp = next_token (0))) { $str .= ' ' if !($str eq ""); $str .= $tmp; last if $tmp =~ /;\s*$/; last if($block and $tmp =~ /\s*[}{]\s*$/); } $str =~ s/;$//; return ($str); } sub add_dn_to_stack { local ($dn) = @_; $current_dn = "$dn, $current_dn"; $curentry{'current_dn'} = $current_dn; } sub remove_dn_from_stack { $current_dn =~ s/^.*?,\s*//; } sub parse_error { print "Parse error on line number $line_number at token number $token_number\n"; exit (1); } sub new_entry { if (%curentry) { $curentry{'current_dn'} = $current_dn; push(@entrystack, {%curentry}); undef(%curentry); } } sub pop_entry { if (%curentry) { push(@outputlist, {%curentry}); } $rentry = pop(@entrystack); %curentry = %$rentry if $rentry; } sub print_entry { return if (scalar keys %curentry == 0); if (!defined ($curentry{'type'})) { $hostdn = "cn=$server, $basedn"; print "dn: $hostdn\n"; print "cn: $server\n"; print "objectClass: top\n"; print "objectClass: dhcpServer\n"; print "dhcpServiceDN: $curentry{'current_dn'}\n"; if(grep(/FaIlOvEr/i, @use)) { foreach my $fo_peer (keys %failover) { next if(scalar(@{$failover{$fo_peer}}) <= 1); print "dhcpStatements: failover peer $fo_peer { ", join('; ', @{$failover{$fo_peer}}), "; }\n"; } } print "\n"; print "dn: $curentry{'current_dn'}\n"; print "cn: $dhcpcn\n"; print "objectClass: top\n"; print "objectClass: dhcpService\n"; if (defined ($curentry{'options'})) { print "objectClass: dhcpOptions\n"; } print "dhcpPrimaryDN: $hostdn\n"; if(grep(/FaIlOvEr/i, @use) and ($second ne '')) { print "dhcpSecondaryDN: $second\n"; } } elsif ($curentry{'type'} eq 'subnet') { print "dn: $curentry{'current_dn'}\n"; print "cn: " . $curentry{'ip'} . "\n"; print "objectClass: top\n"; print "objectClass: dhcpSubnet\n"; if (defined ($curentry{'options'})) { print "objectClass: dhcpOptions\n"; } print "dhcpNetMask: " . $curentry{'netmask'} . "\n"; if (defined ($curentry{'ranges'})) { foreach $statement (@{$curentry{'ranges'}}) { print "dhcpRange: $statement\n"; } } } elsif ($curentry{'type'} eq 'shared-network') { print "dn: $curentry{'current_dn'}\n"; print "cn: " . $curentry{'descr'} . "\n"; print "objectClass: top\n"; print "objectClass: dhcpSharedNetwork\n"; if (defined ($curentry{'options'})) { print "objectClass: dhcpOptions\n"; } } elsif ($curentry{'type'} eq 'group') { print "dn: $curentry{'current_dn'}\n"; print "cn: group", $curentry{'idx'}, "\n"; print "objectClass: top\n"; print "objectClass: dhcpGroup\n"; if (defined ($curentry{'options'})) { print "objectClass: dhcpOptions\n"; } } elsif ($curentry{'type'} eq 'host') { print "dn: $curentry{'current_dn'}\n"; print "cn: " . $curentry{'host'} . "\n"; print "objectClass: top\n"; print "objectClass: dhcpHost\n"; if (defined ($curentry{'options'})) { print "objectClass: dhcpOptions\n"; } if (defined ($curentry{'hwaddress'})) { $curentry{'hwaddress'} =~ y/[A-Z]/[a-z]/; print "dhcpHWAddress: " . $curentry{'hwaddress'} . "\n"; } } elsif ($curentry{'type'} eq 'pool') { print "dn: $curentry{'current_dn'}\n"; print "cn: pool", $curentry{'idx'}, "\n"; print "objectClass: top\n"; print "objectClass: dhcpPool\n"; if (defined ($curentry{'options'})) { print "objectClass: dhcpOptions\n"; } if (defined ($curentry{'ranges'})) { foreach $statement (@{$curentry{'ranges'}}) { print "dhcpRange: $statement\n"; } } } elsif ($curentry{'type'} eq 'class') { print "dn: $curentry{'current_dn'}\n"; print "cn: " . $curentry{'class'} . "\n"; print "objectClass: top\n"; print "objectClass: dhcpClass\n"; if (defined ($curentry{'options'})) { print "objectClass: dhcpOptions\n"; } } elsif ($curentry{'type'} eq 'subclass') { print "dn: $curentry{'current_dn'}\n"; print "cn: " . $curentry{'subclass'} . "\n"; print "objectClass: top\n"; print "objectClass: dhcpSubClass\n"; if (defined ($curentry{'options'})) { print "objectClass: dhcpOptions\n"; } print "dhcpClassData: " . $curentry{'class'} . "\n"; } if (defined ($curentry{'statements'})) { foreach $statement (@{$curentry{'statements'}}) { print "dhcpStatements: $statement\n"; } } if (defined ($curentry{'options'})) { foreach $statement (@{$curentry{'options'}}) { print "dhcpOption: $statement\n"; } } print "\n"; undef (%curentry); } sub parse_netmask { local ($netmask) = @_; local ($i); if ((($a, $b, $c, $d) = $netmask =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) != 4) { parse_error (); } $num = (($a & 0xff) << 24) | (($b & 0xff) << 16) | (($c & 0xff) << 8) | ($d & 0xff); for ($i=1; $i<=32 && $num & (1 << (32 - $i)); $i++) { } $i--; return ($i); } sub parse_subnet { local ($ip, $tmp, $netmask); new_entry (); $ip = next_token (0); parse_error () if !defined ($ip); $tmp = next_token (1); parse_error () if !defined ($tmp); parse_error () if !($tmp eq 'netmask'); $tmp = next_token (0); parse_error () if !defined ($tmp); $netmask = parse_netmask ($tmp); $tmp = next_token (0); parse_error () if !defined ($tmp); parse_error () if !($tmp eq '{'); add_dn_to_stack ("cn=$ip"); $curentry{'type'} = 'subnet'; $curentry{'ip'} = $ip; $curentry{'netmask'} = $netmask; $cursubnet = $ip; $curcounter{$ip} = { pool => 0, group => 0 }; } sub parse_shared_network { local ($descr, $tmp); new_entry (); $descr = next_token (0); parse_error () if !defined ($descr); $tmp = next_token (0); parse_error () if !defined ($tmp); parse_error () if !($tmp eq '{'); add_dn_to_stack ("cn=$descr"); $curentry{'type'} = 'shared-network'; $curentry{'descr'} = $descr; } sub parse_host { local ($descr, $tmp); new_entry (); $host = next_token (0); parse_error () if !defined ($host); $tmp = next_token (0); parse_error () if !defined ($tmp); parse_error () if !($tmp eq '{'); add_dn_to_stack ("cn=$host"); $curentry{'type'} = 'host'; $curentry{'host'} = $host; } sub parse_group { local ($descr, $tmp); new_entry (); $tmp = next_token (0); parse_error () if !defined ($tmp); parse_error () if !($tmp eq '{'); my $idx; if(exists($curcounter{$cursubnet})) { $idx = ++$curcounter{$cursubnet}->{'group'}; } else { $idx = ++$curcounter{''}->{'group'}; } add_dn_to_stack ("cn=group".$idx); $curentry{'type'} = 'group'; $curentry{'idx'} = $idx; } sub parse_pool { local ($descr, $tmp); new_entry (); $tmp = next_token (0); parse_error () if !defined ($tmp); parse_error () if !($tmp eq '{'); my $idx; if(exists($curcounter{$cursubnet})) { $idx = ++$curcounter{$cursubnet}->{'pool'}; } else { $idx = ++$curcounter{''}->{'pool'}; } add_dn_to_stack ("cn=pool".$idx); $curentry{'type'} = 'pool'; $curentry{'idx'} = $idx; } sub parse_class { local ($descr, $tmp); new_entry (); $class = next_token (0); parse_error () if !defined ($class); $tmp = next_token (0); parse_error () if !defined ($tmp); parse_error () if !($tmp eq '{'); $class =~ s/\"//g; add_dn_to_stack ("cn=$class"); $curentry{'type'} = 'class'; $curentry{'class'} = $class; } sub parse_subclass { local ($descr, $tmp); new_entry (); $class = next_token (0); parse_error () if !defined ($class); $subclass = next_token (0); parse_error () if !defined ($subclass); if (substr($subclass,-1) eq ';') { $tmp = ";"; $subclass = substr($subclass,0,-1); } else { $tmp = next_token (0); parse_error () if !defined ($tmp); } parse_error () if !($tmp eq '{' or $tmp eq ';'); add_dn_to_stack ("cn=$subclass"); $curentry{'type'} = 'subclass'; $curentry{'class'} = $class; $curentry{'subclass'} = $subclass; if ($tmp eq ';') { pop_entry (); remove_dn_from_stack (); } } sub parse_hwaddress { local ($type, $hw, $tmp); $type = next_token (1); parse_error () if !defined ($type); $hw = next_token (1); parse_error () if !defined ($hw); $hw =~ s/;$//; $curentry{'hwaddress'} = "$type $hw"; } sub parse_range { local ($tmp, $str); $str = remaining_line (); if (!($str eq '')) { $str =~ s/;$//; push (@{$curentry{'ranges'}}, $str); } } sub parse_statement { local ($token) = shift; local ($str); if ($token eq 'option') { $str = remaining_line (); push (@{$curentry{'options'}}, $str); } elsif($token eq 'failover') { $str = remaining_line (1); # take care on block if($str =~ /[{]/) { my ($peername, @statements); parse_error() if($str !~ /^\s*peer\s+(.+?)\s+[{]\s*$/); parse_error() if(($peername = $1) !~ /^\"?[^\"]+\"?$/); # # failover config block found: # e.g. 'failover peer "some-name" {' # if(not grep(/FaIlOvEr/i, @use)) { print STDERR "Warning: Failover config 'peer $peername' found!\n"; print STDERR " Skipping it, since failover disabled!\n"; print STDERR " You may try out --use=failover option.\n"; } until($str =~ /[}]/ or $str eq "") { $str = remaining_line (1); # collect all statements, except ending '}' push(@statements, $str) if($str !~ /[}]/); } $failover{$peername} = [@statements]; } else { # # pool reference to failover config is fine # e.g. 'failover peer "some-name";' # if(not grep(/FaIlOvEr/i, @use)) { print STDERR "Warning: Failover reference '$str' found!\n"; print STDERR " Skipping it, since failover disabled!\n"; print STDERR " You may try out --use=failover option.\n"; } else { push (@{$curentry{'statements'}}, $token. " " . $str); } } } elsif($token eq 'zone') { $str = $token; while($str !~ /}$/) { $str .= ' ' . next_token (0); } push (@{$curentry{'statements'}}, $str); } elsif($token =~ /^(authoritative)[;]*$/) { push (@{$curentry{'statements'}}, $1); } else { $str = $token . " " . remaining_line (); push (@{$curentry{'statements'}}, $str); } } my $ok = GetOptions( 'basedn=s' => \$basedn, 'dhcpdn=s' => \$dhcpdn, 'server=s' => \$server, 'second=s' => \$second, 'conf=s' => \$i_conf, 'ldif=s' => \$o_ldif, 'use=s' => \@use, 'h|help|usage' => sub { usage(0); }, ); unless($server =~ /^\w+/) { usage(1, "invalid server name '$server'"); } unless($basedn =~ /^\w+=[^,]+/) { usage(1, "invalid base dn '$basedn'"); } if($dhcpdn =~ /^cn=([^,]+)/i) { $dhcpcn = "$1"; } $second = '' if not defined $second; unless($second eq '' or $second =~ /^cn=[^,]+\s*,\s*\w+=[^,]+/i) { if($second =~ /^cn=[^,]+$/i) { # relative DN 'cn=name' $second = "$second, $basedn"; } elsif($second =~ /^\w+/) { # assume hostname only $second = "cn=$second, $basedn"; } else { usage(1, "invalid secondary '$second'") } } usage(1) unless($ok); if($i_conf ne "" and -f $i_conf) { if(not open(STDIN, '<', $i_conf)) { print STDERR "Error: can't open conf file '$i_conf': $!\n"; exit(1); } } if($o_ldif ne "") { if(-e $o_ldif) { print STDERR "Error: output ldif name '$o_ldif' already exists!\n"; exit(1); } if(not open(STDOUT, '>', $o_ldif)) { print STDERR "Error: can't open ldif file '$o_ldif': $!\n"; exit(1); } } print STDERR "Creating LDAP Configuration with the following options:\n"; print STDERR "\tBase DN: $basedn\n"; print STDERR "\tDHCP DN: $dhcpdn\n"; print STDERR "\tServer DN: cn=$server, $basedn\n"; print STDERR "\tSecondary DN: $second\n" if(grep(/FaIlOvEr/i, @use) and $second ne ''); print STDERR "\n"; my $token; my $token_number = 0; my $line_number = 0; my $cursubnet = ''; my %curcounter = ( '' => { pool => 0, group => 0 } ); $current_dn = "$dhcpdn"; $curentry{'current_dn'} = $current_dn; $curentry{'descr'} = $dhcpcn; $line = ''; %failover = (); while (($token = next_token (1))) { if ($token eq '}') { pop_entry (); if($current_dn =~ /.+?,\s*${dhcpdn}$/) { # don't go below dhcpdn ... remove_dn_from_stack (); } } elsif ($token eq 'subnet') { parse_subnet (); next; } elsif ($token eq 'shared-network') { parse_shared_network (); next; } elsif ($token eq 'class') { parse_class (); next; } elsif ($token eq 'subclass') { parse_subclass (); next; } elsif ($token eq 'pool') { parse_pool (); next; } elsif ($token eq 'group') { parse_group (); next; } elsif ($token eq 'host') { parse_host (); next; } elsif ($token eq 'hardware') { parse_hwaddress (); next; } elsif ($token eq 'range') { parse_range (); next; } else { parse_statement ($token); next; } } pop_entry (); while ($#outputlist >= 0) { $rentry = pop(@outputlist); if ($rentry) { %curentry = %$rentry; print_entry (); } } close(STDIN) if($i_conf); close(STDOUT) if($o_ldif); print STDERR "Done.\n"; dhcp-4.4.1/contrib/ldap/README.ldap000644 000765 000024 00000022654 13243301226 017103 0ustar00tmarkstaff000000 000000 LDAP Support in DHCP Original Author: Brian Masney Current Maintainer: David Cantrell Last updated 07-Jul-2009 This document describes setting up the DHCP server to read it's configuration from LDAP. This work is based on the IETF document draft-ietf-dhc-ldap-schema-01.txt included in the doc directory. For the latest version of this document, please see http://dcantrel.fedorapeople.org/dhcp/ldap-patch/ First question on most people's mind is "Why do I want to store my configuration in LDAP?" If you run a small DHCP server, and the configuration on it rarely changes, then you won't need to store your configuration in LDAP. But, if you have several DHCP servers, and you want an easy way to manage your configuration, this can be a solution. The first step will be to setup your LDAP server. I am using OpenLDAP from www.openldap.org. Building and installing OpenLDAP is beyond the scope of this document. There is plenty of documentation out there about this. Once you have OpenLDAP installed, you will have to edit your slapd.conf file. I added the following 2 lines to my configuration file: include /etc/ldap/schema/dhcp.schema index dhcpHWAddress eq index dhcpClassData eq The first line tells it to include the dhcp schema file. You will find this file under the contrib directory in this distribution. You will need to copy this file to where your other schema files are (maybe /etc/openldap/schema/). The second line sets up an index for the dhcpHWAddress parameter. The third parameter is for reading subclasses from LDAP every time a DHCP request comes in. Make sure you run the slapindex command and restart slapd to have these changes to into effect. Now that you have LDAP setup, you should be able to use gq (http://biot.com/gq/) to verify that the dhcp schema file is loaded into LDAP. Pull up gq, and click on the Schema tab. Go under objectClasses, and you should see at least the following object classes listed: dhcpClass, dhcpGroup, dhcpHost, dhcpOptions, dhcpPool, dhcpServer, dhcpService, dhcpSharedNetwork, dhcpSubClass, and dhcpSubnet. If you do not see these, you need to check over your LDAP configuration before you go any further. You should now be ready to build DHCP. If you would like to enable LDAP in dhcpd, you will need to perform the following steps: * Apply the patch here to the unpacked ISC dhcp source tree. * Regenerate the configure script (requires GNU autoconf and automake): aclocal libtoolize --copy --force autoconf autoheader automake --foreign --add-missing --copy * Run ./configure with the '--with-ldap' argument to enable OpenLDAP. If you want LDAP over SSL, also use the '--with-ldapcrypto' argument. * Run 'make' to build ISC dhcp. Once you have DHCP installed, you will need to setup your initial plaintext config file. In my /etc/dhcpd.conf file, I have: ldap-server "localhost"; ldap-port 389; ldap-username "cn=DHCP User, dc=ntelos, dc=net"; ldap-password "blah"; ldap-base-dn "dc=ntelos, dc=net"; ldap-method dynamic; ldap-debug-file "/var/log/dhcp-ldap-startup.log"; If SSL has been enabled at compile time, the dhcp server trys to use TLS if possible, but continues without TLS if not. You can modify this behaviour using following option in /etc/dhcp/dhcpd.conf: ldap-ssl off: disables TLS/LDAPS. ldaps: enables LDAPS -- don't forget to set ldap-port to 636. start_tls: enables TLS using START_TLS command on: enables LDAPS if ldap-port is set to 636 or TLS in other cases. See also "man 5 ldap.conf" for description the following TLS related options: ldap-tls-reqcert, ldap-tls-ca-file, ldap-tls-ca-dir, ldap-tls-cert ldap-tls-key, ldap-tls-crlcheck, ldap-tls-ciphers, ldap-tls-randfile The ldap-init-retry enables an optional ldap connect retry loop with the specified number of retries with a one second sleep between each try during the initial startup of the dhcp server. It allows to catch the condition, that the (remote) ldap server is not yet started at the start time of the dhcp server. All of these parameters should be self explanatory except for the ldap-method. You can set this to static or dynamic. If you set it to static, the configuration is read once on startup, and LDAP isn't used anymore. But, if you set this to dynamic, the configuration is read once on startup, and the hosts that are stored in LDAP are looked up every time a DHCP request comes in. When the optional statement ldap-debug-file is specified, on startup the DHCP server will write out the configuration that it generated from LDAP. If you are getting errors about your LDAP configuration, this is a good place to start looking. The next step is to set up your LDAP tree. Here is an example config that will give a 10.100.0.x address to machines that have a host entry in LDAP. Otherwise, it will give a 10.200.0.x address to them. (NOTE: replace dc=ntelos, dc=net with your base dn). If you would like to convert your existing dhcpd.conf file to LDIF format, there is a script dhcpd-conf-to-ldap that will convert it for you. Type dhcpd-conf-to-ldap --help to see the usage information for this script. # You must specify the server's host name in LDAP that you are going to run # DHCP on and point it to which config tree you want to use. Whenever DHCP # first starts up, it will do a search for this entry to find out which # config to use dn: cn=brian.ntelos.net, dc=ntelos, dc=net objectClass: top objectClass: dhcpServer cn: brian.ntelos.net dhcpServiceDN: cn=DHCP Service Config, dc=ntelos, dc=net # Here is the config tree that brian.ntelos.net points to. dn: cn=DHCP Service Config, dc=ntelos, dc=net cn: DHCP Service Config objectClass: top objectClass: dhcpService dhcpPrimaryDN: dc=ntelos, dc=net dhcpStatements: ddns-update-style none dhcpStatements: default-lease-time 600 dhcpStatements: max-lease-time 7200 # Set up a shared network segment dn: cn=WV Test, cn=DHCP Service Config, dc=ntelos, dc=net cn: WV objectClass: top objectClass: dhcpSharedNetwork # Set up a subnet declaration with a pool statement. Also note that we have # a dhcpOptions object with this entry dn: cn=10.100.0.0, cn=WV Test, cn=DHCP Service Config, dc=ntelos, dc=net cn: 10.100.0.0 objectClass: top objectClass: dhcpSubnet objectClass: dhcpOptions dhcpOption: domain-name-servers 10.100.0.2 dhcpOption: routers 10.100.0.1 dhcpOption: subnet-mask 255.255.255.0 dhcpOption: broadcast-address 10.100.0.255 dhcpNetMask: 24 # Set up a pool for this subnet. Only known hosts will get these IPs dn: cn=Known Pool, cn=10.100.0.0, cn=WV Test, cn=DHCP Service Config, dc=ntelos, dc=net cn: Known Pool objectClass: top objectClass: dhcpPool dhcpRange: 10.100.0.3 10.100.0.254 dhcpPermitList: deny unknown-clients # Set up another subnet declaration with a pool statement dn: cn=10.200.0.0, cn=WV Test, cn=DHCP Service Config, dc=ntelos, dc=net cn: 10.200.0.0 objectClass: top objectClass: dhcpSubnet objectClass: dhcpOptions dhcpOption: domain-name-servers 10.200.0.2 dhcpOption: routers 10.200.0.1 dhcpOption: subnet-mask 255.255.255.0 dhcpOption: broadcast-address 10.200.0.255 dhcpNetMask: 24 # Set up a pool for this subnet. Only unknown hosts will get these IPs dn: cn=Known Pool, cn=10.200.0.0, cn=WV Test, cn=DHCP Service Config, dc=ntelos, dc=net cn: Known Pool objectClass: top objectClass: dhcpPool dhcpRange: 10.200.0.3 10.200.0.254 dhcpPermitList: deny known clients # Set aside a group for all of our known MAC addresses dn: cn=Customers, cn=DHCP Service Config, dc=ntelos, dc=net objectClass: top objectClass: dhcpGroup cn: Customers # Host entry for my laptop dn: cn=brianlaptop, cn=Customers, cn=DHCP Service Config, dc=ntelos, dc=net objectClass: top objectClass: dhcpHost cn: brianlaptop dhcpHWAddress: ethernet 00:00:00:00:00:00 You can use the command ldapadd to load all of these entries into your LDAP server. After you load this, you should be able to start up DHCP. If you run into problems reading the configuration, try running dhcpd with the -d flag. If you still have problems, edit the site.conf file in the DHCP source and add the line: COPTS= -DDEBUG_LDAP and recompile DHCP. (make sure you run make clean and rerun configure before you rebuild). DHCPv6 requires a separate instance of the dhcpd server from the DHCPv4 server. It is convenient to use distinct LDAP login DNs for the two servers, and setup LDAP access restrictions in the LDAP server, so that each DHCP server only has access to its own data. You will need to create a separate configuration file, call it /etc/dhcpd6.conf. For example: ldap-server "localhost"; ldap-port 389; ldap-username "cn=DHCPv6 User, dc=ntelos, dc=net"; ldap-password "blahblah"; ldap-base-dn "dc=ntelos, dc=net"; ldap-method dynamic; ldap-debug-file "/var/log/dhcp-ldap-startup.log"; And use these command line arguments to dhcpd: dhcpd eth... -6 -cf /etc/dhcpd6.conf -pf /var/run/dhcpd6.pid -lf /var/lib/dhcpd6/dhcpd.leases For DHCPv6, the client configuration is the same, but substitute the Client ID for the Ethernet hardware address. Here is an example of a host definition for a DHCPv6 client: dn: cn=examplehost,cn=XXXX:XXXX:XXXX:XXXX::/64,cn=Network-eth1,cn=DHCPv6,dc=example,dc=com objectClass: top objectClass: dhcpHost cn: examplehost dhcpClientId: XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX dhcpStatements: fixed-address6 XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX option host-name "examplehost.ipv6.example.com" option domain-name "ipv6.example.com" dhcp-4.4.1/common/alloc.c000644 000765 000024 00000071752 13243301226 015455 0ustar00tmarkstaff000000 000000 /* alloc.c Memory allocation... */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1996-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #include struct dhcp_packet *dhcp_free_list; struct packet *packet_free_list; int option_chain_head_allocate (ptr, file, line) struct option_chain_head **ptr; const char *file; int line; { struct option_chain_head *h; if (!ptr) { log_error ("%s(%d): null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } if (*ptr) { log_error ("%s(%d): non-null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else *ptr = (struct option_chain_head *)0; #endif } h = dmalloc (sizeof *h, file, line); if (h) { memset (h, 0, sizeof *h); return option_chain_head_reference (ptr, h, file, line); } return 0; } int option_chain_head_reference (ptr, bp, file, line) struct option_chain_head **ptr; struct option_chain_head *bp; const char *file; int line; { if (!ptr) { log_error ("%s(%d): null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } if (*ptr) { log_error ("%s(%d): non-null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else *ptr = (struct option_chain_head *)0; #endif } *ptr = bp; bp -> refcnt++; rc_register (file, line, ptr, bp, bp -> refcnt, 0, RC_MISC); return 1; } int option_chain_head_dereference (ptr, file, line) struct option_chain_head **ptr; const char *file; int line; { struct option_chain_head *option_chain_head; pair car, cdr; if (!ptr || !*ptr) { log_error ("%s(%d): null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } option_chain_head = *ptr; *ptr = (struct option_chain_head *)0; --option_chain_head -> refcnt; rc_register (file, line, ptr, option_chain_head, option_chain_head -> refcnt, 1, RC_MISC); if (option_chain_head -> refcnt > 0) return 1; if (option_chain_head -> refcnt < 0) { log_error ("%s(%d): negative refcnt!", file, line); #if defined (DEBUG_RC_HISTORY) dump_rc_history (option_chain_head); #endif #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } /* If there are any options on this head, free them. */ for (car = option_chain_head -> first; car; car = cdr) { cdr = car -> cdr; if (car -> car) option_cache_dereference ((struct option_cache **) (&car -> car), MDL); dfree (car, MDL); } dfree (option_chain_head, file, line); return 1; } int group_allocate (ptr, file, line) struct group **ptr; const char *file; int line; { struct group *g; if (!ptr) { log_error ("%s(%d): null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } if (*ptr) { log_error ("%s(%d): non-null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else *ptr = (struct group *)0; #endif } g = dmalloc (sizeof *g, file, line); if (g) { memset (g, 0, sizeof *g); return group_reference (ptr, g, file, line); } return 0; } int group_reference (ptr, bp, file, line) struct group **ptr; struct group *bp; const char *file; int line; { if (!ptr) { log_error ("%s(%d): null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } if (*ptr) { log_error ("%s(%d): non-null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else *ptr = (struct group *)0; #endif } *ptr = bp; bp -> refcnt++; rc_register (file, line, ptr, bp, bp -> refcnt, 0, RC_MISC); return 1; } int group_dereference (ptr, file, line) struct group **ptr; const char *file; int line; { struct group *group; if (!ptr || !*ptr) { log_error ("%s(%d): null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } group = *ptr; *ptr = (struct group *)0; --group -> refcnt; rc_register (file, line, ptr, group, group -> refcnt, 1, RC_MISC); if (group -> refcnt > 0) return 1; if (group -> refcnt < 0) { log_error ("%s(%d): negative refcnt!", file, line); #if defined (DEBUG_RC_HISTORY) dump_rc_history (group); #endif #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } if (group -> object) group_object_dereference (&group -> object, file, line); if (group -> subnet) subnet_dereference (&group -> subnet, file, line); if (group -> shared_network) shared_network_dereference (&group -> shared_network, file, line); if (group -> statements) executable_statement_dereference (&group -> statements, file, line); if (group -> next) group_dereference (&group -> next, file, line); dfree (group, file, line); return 1; } struct dhcp_packet *new_dhcp_packet (file, line) const char *file; int line; { struct dhcp_packet *rval; rval = (struct dhcp_packet *)dmalloc (sizeof (struct dhcp_packet), file, line); return rval; } struct protocol *new_protocol (file, line) const char *file; int line; { struct protocol *rval = dmalloc (sizeof (struct protocol), file, line); return rval; } struct domain_search_list *new_domain_search_list (file, line) const char *file; int line; { struct domain_search_list *rval = dmalloc (sizeof (struct domain_search_list), file, line); return rval; } struct name_server *new_name_server (file, line) const char *file; int line; { struct name_server *rval = dmalloc (sizeof (struct name_server), file, line); return rval; } void free_name_server (ptr, file, line) struct name_server *ptr; const char *file; int line; { dfree ((void *)ptr, file, line); } struct option *new_option (name, file, line) const char *name; const char *file; int line; { struct option *rval; int len; len = strlen(name); rval = dmalloc(sizeof(struct option) + len + 1, file, line); if(rval) { memcpy(rval + 1, name, len); rval->name = (char *)(rval + 1); } return rval; } struct universe *new_universe (file, line) const char *file; int line; { struct universe *rval = dmalloc (sizeof (struct universe), file, line); return rval; } void free_universe (ptr, file, line) struct universe *ptr; const char *file; int line; { dfree ((void *)ptr, file, line); } void free_domain_search_list (ptr, file, line) struct domain_search_list *ptr; const char *file; int line; { dfree ((void *)ptr, file, line); } void free_protocol (ptr, file, line) struct protocol *ptr; const char *file; int line; { dfree ((void *)ptr, file, line); } void free_dhcp_packet (ptr, file, line) struct dhcp_packet *ptr; const char *file; int line; { dfree ((void *)ptr, file, line); } struct client_lease *new_client_lease (file, line) const char *file; int line; { return (struct client_lease *)dmalloc (sizeof (struct client_lease), file, line); } void free_client_lease (lease, file, line) struct client_lease *lease; const char *file; int line; { dfree (lease, file, line); } pair free_pairs; pair new_pair (file, line) const char *file; int line; { pair foo; if (free_pairs) { foo = free_pairs; free_pairs = foo -> cdr; memset (foo, 0, sizeof *foo); dmalloc_reuse (foo, file, line, 0); return foo; } foo = dmalloc (sizeof *foo, file, line); if (!foo) return foo; memset (foo, 0, sizeof *foo); return foo; } void free_pair (foo, file, line) pair foo; const char *file; int line; { foo -> cdr = free_pairs; free_pairs = foo; dmalloc_reuse (free_pairs, __FILE__, __LINE__, 0); } #if defined (DEBUG_MEMORY_LEAKAGE) || \ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) void relinquish_free_pairs () { pair pf, pc; for (pf = free_pairs; pf; pf = pc) { pc = pf -> cdr; dfree (pf, MDL); } free_pairs = (pair)0; } #endif struct expression *free_expressions; int expression_allocate (cptr, file, line) struct expression **cptr; const char *file; int line; { struct expression *rval; if (free_expressions) { rval = free_expressions; free_expressions = rval -> data.not; dmalloc_reuse (rval, file, line, 1); } else { rval = dmalloc (sizeof (struct expression), file, line); if (!rval) return 0; } memset (rval, 0, sizeof *rval); return expression_reference (cptr, rval, file, line); } int expression_reference (ptr, src, file, line) struct expression **ptr; struct expression *src; const char *file; int line; { if (!ptr) { log_error ("%s(%d): null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } if (*ptr) { log_error ("%s(%d): non-null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else *ptr = (struct expression *)0; #endif } *ptr = src; src -> refcnt++; rc_register (file, line, ptr, src, src -> refcnt, 0, RC_MISC); return 1; } void free_expression (expr, file, line) struct expression *expr; const char *file; int line; { expr -> data.not = free_expressions; free_expressions = expr; dmalloc_reuse (free_expressions, __FILE__, __LINE__, 0); } #if defined (DEBUG_MEMORY_LEAKAGE) || \ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) void relinquish_free_expressions () { struct expression *e, *n; for (e = free_expressions; e; e = n) { n = e -> data.not; dfree (e, MDL); } free_expressions = (struct expression *)0; } #endif struct binding_value *free_binding_values; int binding_value_allocate (cptr, file, line) struct binding_value **cptr; const char *file; int line; { struct binding_value *rval; if (free_binding_values) { rval = free_binding_values; free_binding_values = rval -> value.bv; dmalloc_reuse (rval, file, line, 1); } else { rval = dmalloc (sizeof (struct binding_value), file, line); if (!rval) return 0; } memset (rval, 0, sizeof *rval); return binding_value_reference (cptr, rval, file, line); } int binding_value_reference (ptr, src, file, line) struct binding_value **ptr; struct binding_value *src; const char *file; int line; { if (!ptr) { log_error ("%s(%d): null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } if (*ptr) { log_error ("%s(%d): non-null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else *ptr = (struct binding_value *)0; #endif } *ptr = src; src -> refcnt++; rc_register (file, line, ptr, src, src -> refcnt, 0, RC_MISC); return 1; } void free_binding_value (bv, file, line) struct binding_value *bv; const char *file; int line; { bv -> value.bv = free_binding_values; free_binding_values = bv; dmalloc_reuse (free_binding_values, (char *)0, 0, 0); } #if defined (DEBUG_MEMORY_LEAKAGE) || \ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) void relinquish_free_binding_values () { struct binding_value *b, *n; for (b = free_binding_values; b; b = n) { n = b -> value.bv; dfree (b, MDL); } free_binding_values = (struct binding_value *)0; } #endif int fundef_allocate (cptr, file, line) struct fundef **cptr; const char *file; int line; { struct fundef *rval; rval = dmalloc (sizeof (struct fundef), file, line); if (!rval) return 0; memset (rval, 0, sizeof *rval); return fundef_reference (cptr, rval, file, line); } int fundef_reference (ptr, src, file, line) struct fundef **ptr; struct fundef *src; const char *file; int line; { if (!ptr) { log_error ("%s(%d): null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } if (*ptr) { log_error ("%s(%d): non-null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else *ptr = (struct fundef *)0; #endif } *ptr = src; src -> refcnt++; rc_register (file, line, ptr, src, src -> refcnt, 0, RC_MISC); return 1; } struct option_cache *free_option_caches; #if defined (DEBUG_MEMORY_LEAKAGE) || \ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) void relinquish_free_option_caches () { struct option_cache *o, *n; for (o = free_option_caches; o; o = n) { n = (struct option_cache *)(o -> expression); dfree (o, MDL); } free_option_caches = (struct option_cache *)0; } #endif int option_cache_allocate (cptr, file, line) struct option_cache **cptr; const char *file; int line; { struct option_cache *rval; if (free_option_caches) { rval = free_option_caches; free_option_caches = (struct option_cache *)(rval -> expression); dmalloc_reuse (rval, file, line, 0); } else { rval = dmalloc (sizeof (struct option_cache), file, line); if (!rval) return 0; } memset (rval, 0, sizeof *rval); return option_cache_reference (cptr, rval, file, line); } int option_cache_reference (ptr, src, file, line) struct option_cache **ptr; struct option_cache *src; const char *file; int line; { if (!ptr) { log_error ("%s(%d): null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } if (*ptr) { log_error ("%s(%d): non-null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else *ptr = (struct option_cache *)0; #endif } *ptr = src; src -> refcnt++; rc_register (file, line, ptr, src, src -> refcnt, 0, RC_MISC); return 1; } int buffer_allocate (ptr, len, file, line) struct buffer **ptr; unsigned len; const char *file; int line; { struct buffer *bp; /* XXXSK: should check for bad ptr values, otherwise we leak memory if they are wrong */ bp = dmalloc (len + sizeof *bp, file, line); if (!bp) return 0; /* XXXSK: both of these initializations are unnecessary */ memset (bp, 0, sizeof *bp); bp -> refcnt = 0; return buffer_reference (ptr, bp, file, line); } int buffer_reference (ptr, bp, file, line) struct buffer **ptr; struct buffer *bp; const char *file; int line; { if (!ptr) { log_error ("%s(%d): null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } if (*ptr) { log_error ("%s(%d): non-null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else *ptr = (struct buffer *)0; #endif } *ptr = bp; bp -> refcnt++; rc_register (file, line, ptr, bp, bp -> refcnt, 0, RC_MISC); return 1; } int buffer_dereference (ptr, file, line) struct buffer **ptr; const char *file; int line; { if (!ptr) { log_error ("%s(%d): null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } if (!*ptr) { log_error ("%s(%d): null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } (*ptr) -> refcnt--; rc_register (file, line, ptr, *ptr, (*ptr) -> refcnt, 1, RC_MISC); if (!(*ptr) -> refcnt) { dfree ((*ptr), file, line); } else if ((*ptr) -> refcnt < 0) { log_error ("%s(%d): negative refcnt!", file, line); #if defined (DEBUG_RC_HISTORY) dump_rc_history (*ptr); #endif #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } *ptr = (struct buffer *)0; return 1; } int dns_host_entry_allocate (ptr, hostname, file, line) struct dns_host_entry **ptr; const char *hostname; const char *file; int line; { struct dns_host_entry *bp; bp = dmalloc (strlen (hostname) + sizeof *bp, file, line); if (!bp) return 0; memset (bp, 0, sizeof *bp); bp -> refcnt = 0; strcpy (bp -> hostname, hostname); return dns_host_entry_reference (ptr, bp, file, line); } int dns_host_entry_reference (ptr, bp, file, line) struct dns_host_entry **ptr; struct dns_host_entry *bp; const char *file; int line; { if (!ptr) { log_error ("%s(%d): null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } if (*ptr) { log_error ("%s(%d): non-null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else *ptr = (struct dns_host_entry *)0; #endif } *ptr = bp; bp -> refcnt++; rc_register (file, line, ptr, bp, bp -> refcnt, 0, RC_MISC); return 1; } int dns_host_entry_dereference (ptr, file, line) struct dns_host_entry **ptr; const char *file; int line; { if (!ptr || !*ptr) { log_error ("%s(%d): null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } (*ptr)->refcnt--; rc_register (file, line, ptr, *ptr, (*ptr)->refcnt, 1, RC_MISC); if ((*ptr)->refcnt == 0) { dfree ((*ptr), file, line); } else if ((*ptr)->refcnt < 0) { log_error ("%s(%d): negative refcnt!", file, line); #if defined (DEBUG_RC_HISTORY) dump_rc_history (*ptr); #endif #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } *ptr = (struct dns_host_entry *)0; return 1; } int option_state_allocate (ptr, file, line) struct option_state **ptr; const char *file; int line; { unsigned size; if (!ptr) { log_error ("%s(%d): null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } if (*ptr) { log_error ("%s(%d): non-null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else *ptr = (struct option_state *)0; #endif } size = sizeof **ptr + (universe_count - 1) * sizeof (void *); *ptr = dmalloc (size, file, line); if (*ptr) { memset (*ptr, 0, size); (*ptr) -> universe_count = universe_count; (*ptr) -> refcnt = 1; rc_register (file, line, ptr, *ptr, (*ptr) -> refcnt, 0, RC_MISC); return 1; } return 0; } int option_state_reference (ptr, bp, file, line) struct option_state **ptr; struct option_state *bp; const char *file; int line; { if (!ptr) { log_error ("%s(%d): null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } if (*ptr) { log_error ("%s(%d): non-null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else *ptr = (struct option_state *)0; #endif } *ptr = bp; bp -> refcnt++; rc_register (file, line, ptr, bp, bp -> refcnt, 0, RC_MISC); return 1; } int option_state_dereference (ptr, file, line) struct option_state **ptr; const char *file; int line; { int i; struct option_state *options; if (!ptr || !*ptr) { log_error ("%s(%d): null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } options = *ptr; *ptr = (struct option_state *)0; --options -> refcnt; rc_register (file, line, ptr, options, options -> refcnt, 1, RC_MISC); if (options -> refcnt > 0) return 1; if (options -> refcnt < 0) { log_error ("%s(%d): negative refcnt!", file, line); #if defined (DEBUG_RC_HISTORY) dump_rc_history (options); #endif #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } /* Loop through the per-universe state. */ for (i = 0; i < options -> universe_count; i++) if (options -> universes [i] && universes [i] -> option_state_dereference) ((*(universes [i] -> option_state_dereference)) (universes [i], options, file, line)); dfree (options, file, line); return 1; } int executable_statement_allocate (ptr, file, line) struct executable_statement **ptr; const char *file; int line; { struct executable_statement *bp; bp = dmalloc (sizeof *bp, file, line); if (!bp) return 0; memset (bp, 0, sizeof *bp); return executable_statement_reference (ptr, bp, file, line); } int executable_statement_reference (ptr, bp, file, line) struct executable_statement **ptr; struct executable_statement *bp; const char *file; int line; { if (!ptr) { log_error ("%s(%d): null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } if (*ptr) { log_error ("%s(%d): non-null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else *ptr = (struct executable_statement *)0; #endif } *ptr = bp; bp -> refcnt++; rc_register (file, line, ptr, bp, bp -> refcnt, 0, RC_MISC); return 1; } static struct packet *free_packets; #if defined (DEBUG_MEMORY_LEAKAGE) || \ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) void relinquish_free_packets () { struct packet *p, *n; for (p = free_packets; p; p = n) { n = (struct packet *)(p -> raw); dfree (p, MDL); } free_packets = (struct packet *)0; } #endif int packet_allocate (ptr, file, line) struct packet **ptr; const char *file; int line; { struct packet *p; if (!ptr) { log_error ("%s(%d): null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } if (*ptr) { log_error ("%s(%d): non-null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else *ptr = (struct packet *)0; #endif } if (free_packets) { p = free_packets; free_packets = (struct packet *)(p -> raw); dmalloc_reuse (p, file, line, 1); } else { p = dmalloc (sizeof *p, file, line); } if (p) { memset (p, 0, sizeof *p); return packet_reference (ptr, p, file, line); } return 0; } int packet_reference (ptr, bp, file, line) struct packet **ptr; struct packet *bp; const char *file; int line; { if (!ptr) { log_error ("%s(%d): null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } if (*ptr) { log_error ("%s(%d): non-null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else *ptr = (struct packet *)0; #endif } *ptr = bp; bp -> refcnt++; rc_register (file, line, ptr, bp, bp -> refcnt, 0, RC_MISC); return 1; } int packet_dereference (ptr, file, line) struct packet **ptr; const char *file; int line; { int i; struct packet *packet; if (!ptr || !*ptr) { log_error ("%s(%d): null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } packet = *ptr; *ptr = (struct packet *)0; --packet -> refcnt; rc_register (file, line, ptr, packet, packet -> refcnt, 1, RC_MISC); if (packet -> refcnt > 0) return 1; if (packet -> refcnt < 0) { log_error ("%s(%d): negative refcnt!", file, line); #if defined (DEBUG_RC_HISTORY) dump_rc_history (packet); #endif #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } if (packet -> options) option_state_dereference (&packet -> options, file, line); if (packet -> interface) interface_dereference (&packet -> interface, MDL); if (packet -> shared_network) shared_network_dereference (&packet -> shared_network, MDL); for (i = 0; i < packet -> class_count && i < PACKET_MAX_CLASSES; i++) { if (packet -> classes [i]) omapi_object_dereference ((omapi_object_t **) &packet -> classes [i], MDL); } packet -> raw = (struct dhcp_packet *)free_packets; free_packets = packet; dmalloc_reuse (free_packets, __FILE__, __LINE__, 0); return 1; } int dns_zone_allocate (ptr, file, line) struct dns_zone **ptr; const char *file; int line; { struct dns_zone *d; if (!ptr) { log_error ("%s(%d): null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } if (*ptr) { log_error ("%s(%d): non-null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else *ptr = (struct dns_zone *)0; #endif } d = dmalloc (sizeof *d, file, line); if (d) { memset (d, 0, sizeof *d); return dns_zone_reference (ptr, d, file, line); } return 0; } int dns_zone_reference (ptr, bp, file, line) struct dns_zone **ptr; struct dns_zone *bp; const char *file; int line; { if (!ptr) { log_error ("%s(%d): null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } if (*ptr) { log_error ("%s(%d): non-null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else *ptr = (struct dns_zone *)0; #endif } *ptr = bp; bp -> refcnt++; rc_register (file, line, ptr, bp, bp -> refcnt, 0, RC_MISC); return 1; } int binding_scope_allocate (ptr, file, line) struct binding_scope **ptr; const char *file; int line; { struct binding_scope *bp; if (!ptr) { log_error ("%s(%d): null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } if (*ptr) { log_error ("%s(%d): non-null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } bp = dmalloc (sizeof *bp, file, line); if (!bp) return 0; memset (bp, 0, sizeof *bp); binding_scope_reference (ptr, bp, file, line); return 1; } int binding_scope_reference (ptr, bp, file, line) struct binding_scope **ptr; struct binding_scope *bp; const char *file; int line; { if (!ptr) { log_error ("%s(%d): null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } if (*ptr) { log_error ("%s(%d): non-null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else *ptr = (struct binding_scope *)0; #endif } *ptr = bp; bp -> refcnt++; rc_register (file, line, ptr, bp, bp -> refcnt, 0, RC_MISC); return 1; } /*! * \brief Constructs a null-terminated data_string from a char* and length. * * Allocates a data_string and copies into it the given length of bytes * from the given source, adding a terminating null if not present in the source * at length-1. * * \param new_string pointer to the data_string to construct. Cannot be * NULL. Note that its contents will be overwritten. Passing in the address * of an allocated data_string will result in memory leaks. * \param src data to be copied. Cannot be NULL. * \param len length of the data to copied * * \return 1 - if the data_string is constructed successfully, 0 if * target data_struct is NULL or the buffer allocation fails. */ int data_string_new(struct data_string *new_string, const char *src, unsigned int len, const char *file, int line) { unsigned int copy_len = 0; if (new_string == NULL) { log_error("data_string_new: new_string cannot be NULL %s(%d)", file, line); return (0); } if (src == NULL) { log_error("data_string_new: src cannot be NULL %s(%d)", file, line); return (0); } memset(new_string, 0, sizeof (struct data_string)); /* If we already have a NULL back off length by one. This lets * us always just add a NULL at the end. */ copy_len = (len > 0 && src[len - 1] == 0) ? len - 1 : len; /* Allocate the buffer, accounting for terminating null */ if (!buffer_allocate(&(new_string->buffer), copy_len + 1, MDL)) { log_error("data_string_new: No memory %s(%d)", file, line); return (0); } /* Only copy if there's something to copy */ if (copy_len > 0) { memcpy(new_string->buffer->data, src, copy_len); } /* Always tack on the null */ new_string->buffer->data[copy_len] = 0; /* Update data_string accessor values. Note len does NOT include * the NULL. */ new_string->data = new_string->buffer->data; new_string->len = copy_len; new_string->terminated = 1; return (1); } /* Make a copy of the data in data_string, upping the buffer reference count if there's a buffer. */ void data_string_copy(struct data_string *dest, const struct data_string *src, const char *file, int line) { if (src -> buffer) { buffer_reference (&dest -> buffer, src -> buffer, file, line); } else { dest->buffer = NULL; } dest -> data = src -> data; dest -> terminated = src -> terminated; dest -> len = src -> len; } /* Release the reference count to a data string's buffer (if any) and zero out the other information, yielding the null data string. */ void data_string_forget (data, file, line) struct data_string *data; const char *file; int line; { if (data -> buffer) buffer_dereference (&data -> buffer, file, line); memset (data, 0, sizeof *data); } /* If the data_string is larger than the specified length, reduce the data_string to the specified size. */ void data_string_truncate (dp, len) struct data_string *dp; int len; { /* XXX: do we need to consider the "terminated" flag in the check? */ if (len < dp -> len) { dp -> terminated = 0; dp -> len = len; } } /* \brief Converts a data_string to a null-terminated data string * * If the given string isn't null-terminated, replace it with a * null-terminated version and free the current string decrementing * the referecne count. If the string is null-terminated it is left * as is. * * Currently this routine doesn't check if the string is 0 length * that must be checked by the caller. * * \param [in/out] str the data_string to convert * \param file the file this routine was called from * \param line the line this routine was called from * * \return 1 if the string was converted successfully (or already terminated), * 0 if the conversion failed. Failure is only possible if memory for the new * string could not be allocated. If the conversion fails, the original * string's content is lost. */ int data_string_terminate(str, file, line) struct data_string* str; const char *file; int line; { int ret_val = 1; if (str->terminated == 0) { struct data_string temp; memset(&temp, 0, sizeof(temp)); data_string_copy(&temp, str, file, line); data_string_forget(str, file, line); if (data_string_new(str, (const char*)temp.data, temp.len, file, line) == 0) { /* couldn't create a copy, probably a memory issue, * an error message has already been logged. */ ret_val = 0; } /* get rid of temp string */ data_string_forget(&temp, file, line); } return (ret_val); } dhcp-4.4.1/common/bpf.c000644 000765 000024 00000045636 13243301226 015134 0ustar00tmarkstaff000000 000000 /* bpf.c BPF socket interface code, originally contributed by Archie Cobbs. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1996-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * * This software was contributed to Internet Systems Consortium * by Archie Cobbs. * * Patches for FDDI support on Digital Unix were written by Bill * Stapleton, and maintained for a while by Mike Meredith before he * managed to get me to integrate them. */ #include "dhcpd.h" #if defined (USE_BPF_SEND) || defined (USE_BPF_RECEIVE) \ || defined (USE_LPF_RECEIVE) # if defined (USE_LPF_RECEIVE) # include # include # define bpf_insn sock_filter /* Linux: dare to be gratuitously different. */ # else # include # include # include # if defined (NEED_OSF_PFILT_HACKS) # include # endif # endif #include #include "includes/netinet/ip.h" #include "includes/netinet/udp.h" #include "includes/netinet/if_ether.h" #endif #if defined(USE_BPF_SEND) || defined(USE_BPF_RECEIVE) || defined(USE_BPF_HWADDR) #include #include #endif #include /* Reinitializes the specified interface after an address change. This is not required for packet-filter APIs. */ #ifdef USE_BPF_SEND void if_reinitialize_send (info) struct interface_info *info; { } #endif #ifdef USE_BPF_RECEIVE void if_reinitialize_receive (info) struct interface_info *info; { } #endif /* Called by get_interface_list for each interface that's discovered. Opens a packet filter for each interface and adds it to the select mask. */ #if defined (USE_BPF_SEND) || defined (USE_BPF_RECEIVE) int if_register_bpf (info) struct interface_info *info; { int sock; char filename[50]; int b; /* Open a BPF device */ for (b = 0; 1; b++) { /* %Audit% 31 bytes max. %2004.06.17,Safe% */ sprintf(filename, BPF_FORMAT, b); sock = open (filename, O_RDWR, 0); if (sock < 0) { if (errno == EBUSY) { continue; } else { if (!b) log_fatal ("No bpf devices.%s%s%s", " Please read the README", " section for your operating", " system."); log_fatal ("Can't find free bpf: %m"); } } else { break; } } /* Set the BPF device to point at this interface. */ if (ioctl (sock, BIOCSETIF, info -> ifp) < 0) log_fatal ("Can't attach interface %s to bpf device %s: %m", info -> name, filename); get_hw_addr(info->name, &info->hw_address); return sock; } #endif /* USE_BPF_SEND || USE_BPF_RECEIVE */ #ifdef USE_BPF_SEND void if_register_send (info) struct interface_info *info; { /* If we're using the bpf API for sending and receiving, we don't need to register this interface twice. */ #ifndef USE_BPF_RECEIVE info -> wfdesc = if_register_bpf (info, interface); #else info -> wfdesc = info -> rfdesc; #endif if (!quiet_interface_discovery) log_info ("Sending on BPF/%s/%s%s%s", info -> name, print_hw_addr (info -> hw_address.hbuf [0], info -> hw_address.hlen - 1, &info -> hw_address.hbuf [1]), (info -> shared_network ? "/" : ""), (info -> shared_network ? info -> shared_network -> name : "")); } void if_deregister_send (info) struct interface_info *info; { /* If we're using the bpf API for sending and receiving, we don't need to register this interface twice. */ #ifndef USE_BPF_RECEIVE close (info -> wfdesc); #endif info -> wfdesc = -1; if (!quiet_interface_discovery) log_info ("Disabling output on BPF/%s/%s%s%s", info -> name, print_hw_addr (info -> hw_address.hbuf [0], info -> hw_address.hlen - 1, &info -> hw_address.hbuf [1]), (info -> shared_network ? "/" : ""), (info -> shared_network ? info -> shared_network -> name : "")); } #endif /* USE_BPF_SEND */ #if defined (USE_BPF_RECEIVE) || defined (USE_LPF_RECEIVE) /* Packet filter program... XXX Changes to the filter program may require changes to the constant offsets used in if_register_send to patch the BPF program! XXX */ struct bpf_insn dhcp_bpf_filter [] = { /* Make sure this is an IP packet... */ BPF_STMT (BPF_LD + BPF_H + BPF_ABS, 12), BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 8), /* Make sure it's a UDP packet... */ BPF_STMT (BPF_LD + BPF_B + BPF_ABS, 23), BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 6), /* Make sure this isn't a fragment... */ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20), BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 4, 0), /* Get the IP header length... */ BPF_STMT (BPF_LDX + BPF_B + BPF_MSH, 14), /* Make sure it's to the right port... */ BPF_STMT (BPF_LD + BPF_H + BPF_IND, 16), BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 67, 0, 1), /* patch */ /* If we passed all the tests, ask for the whole packet. */ BPF_STMT (BPF_RET + BPF_K, (u_int)-1), /* Otherwise, drop it. */ BPF_STMT (BPF_RET + BPF_K, 0), }; #if defined(RELAY_PORT) /* * For relay port extension */ struct bpf_insn dhcp_bpf_relay_filter [] = { /* Make sure this is an IP packet... */ BPF_STMT (BPF_LD + BPF_H + BPF_ABS, 12), BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 10), /* Make sure it's a UDP packet... */ BPF_STMT (BPF_LD + BPF_B + BPF_ABS, 23), BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 8), /* Make sure this isn't a fragment... */ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20), BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 6, 0), /* Get the IP header length... */ BPF_STMT (BPF_LDX + BPF_B + BPF_MSH, 14), /* Make sure it's to the right port... */ BPF_STMT (BPF_LD + BPF_H + BPF_IND, 16), BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 67, 2, 0), /* patch */ /* relay can have an alternative port... */ BPF_STMT (BPF_LD + BPF_H + BPF_IND, 16), BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 67, 0, 1), /* patch */ /* If we passed all the tests, ask for the whole packet. */ BPF_STMT (BPF_RET + BPF_K, (u_int)-1), /* Otherwise, drop it. */ BPF_STMT (BPF_RET + BPF_K, 0), }; int dhcp_bpf_relay_filter_len = sizeof dhcp_bpf_relay_filter / sizeof (struct bpf_insn); #endif #if defined (DEC_FDDI) struct bpf_insn *bpf_fddi_filter = NULL; #endif int dhcp_bpf_filter_len = sizeof dhcp_bpf_filter / sizeof (struct bpf_insn); #if defined (HAVE_TR_SUPPORT) struct bpf_insn dhcp_bpf_tr_filter [] = { /* accept all token ring packets due to variable length header */ /* if we want to get clever, insert the program here */ /* If we passed all the tests, ask for the whole packet. */ BPF_STMT(BPF_RET+BPF_K, (u_int)-1), /* Otherwise, drop it. */ BPF_STMT(BPF_RET+BPF_K, 0), }; int dhcp_bpf_tr_filter_len = (sizeof dhcp_bpf_tr_filter / sizeof (struct bpf_insn)); #endif /* HAVE_TR_SUPPORT */ #endif /* USE_LPF_RECEIVE || USE_BPF_RECEIVE */ #if defined (USE_BPF_RECEIVE) void if_register_receive (info) struct interface_info *info; { int flag = 1; struct bpf_version v; struct bpf_program p; #ifdef NEED_OSF_PFILT_HACKS u_int32_t bits; #endif #ifdef DEC_FDDI int link_layer; #endif /* DEC_FDDI */ /* Open a BPF device and hang it on this interface... */ info -> rfdesc = if_register_bpf (info); /* Make sure the BPF version is in range... */ if (ioctl (info -> rfdesc, BIOCVERSION, &v) < 0) log_fatal ("Can't get BPF version: %m"); if (v.bv_major != BPF_MAJOR_VERSION || v.bv_minor < BPF_MINOR_VERSION) log_fatal ("BPF version mismatch - recompile DHCP!"); /* Set immediate mode so that reads return as soon as a packet comes in, rather than waiting for the input buffer to fill with packets. */ if (ioctl (info -> rfdesc, BIOCIMMEDIATE, &flag) < 0) log_fatal ("Can't set immediate mode on bpf device: %m"); #ifdef NEED_OSF_PFILT_HACKS /* Allow the copyall flag to be set... */ if (ioctl(info -> rfdesc, EIOCALLOWCOPYALL, &flag) < 0) log_fatal ("Can't set ALLOWCOPYALL: %m"); /* Clear all the packet filter mode bits first... */ bits = 0; if (ioctl (info -> rfdesc, EIOCMBIS, &bits) < 0) log_fatal ("Can't clear pfilt bits: %m"); /* Set the ENBATCH, ENCOPYALL, ENBPFHDR bits... */ bits = ENBATCH | ENCOPYALL | ENBPFHDR; if (ioctl (info -> rfdesc, EIOCMBIS, &bits) < 0) log_fatal ("Can't set ENBATCH|ENCOPYALL|ENBPFHDR: %m"); #endif /* Get the required BPF buffer length from the kernel. */ if (ioctl (info -> rfdesc, BIOCGBLEN, &info -> rbuf_max) < 0) log_fatal ("Can't get bpf buffer length: %m"); info -> rbuf = dmalloc (info -> rbuf_max, MDL); if (!info -> rbuf) log_fatal ("Can't allocate %ld bytes for bpf input buffer.", (long)(info -> rbuf_max)); info -> rbuf_offset = 0; info -> rbuf_len = 0; /* Set up the bpf filter program structure. */ p.bf_len = dhcp_bpf_filter_len; #ifdef DEC_FDDI /* See if this is an FDDI interface, flag it for later. */ if (ioctl(info -> rfdesc, BIOCGDLT, &link_layer) >= 0 && link_layer == DLT_FDDI) { if (!bpf_fddi_filter) { bpf_fddi_filter = dmalloc (sizeof dhcp_bpf_filter, MDL); if (!bpf_fddi_filter) log_fatal ("No memory for FDDI filter."); memcpy (bpf_fddi_filter, dhcp_bpf_filter, sizeof dhcp_bpf_filter); /* Patch the BPF program to account for the difference in length between ethernet headers (14), FDDI and 802.2 headers (16 +8=24, +10). XXX changes to filter program may require changes to XXX the insn number(s) used below! */ bpf_fddi_filter[0].k += 10; bpf_fddi_filter[2].k += 10; bpf_fddi_filter[4].k += 10; bpf_fddi_filter[6].k += 10; bpf_fddi_filter[7].k += 10; } p.bf_insns = bpf_fddi_filter; } else #endif /* DEC_FDDI */ p.bf_insns = dhcp_bpf_filter; /* Patch the server port into the BPF program... XXX changes to filter program may require changes to the insn number(s) used below! XXX */ #if defined(RELAY_PORT) if (relay_port) { /* * If user defined relay UDP port, we need to filter * also on the user UDP port. */ p.bf_len = dhcp_bpf_relay_filter_len; p.bf_insns = dhcp_bpf_relay_filter; dhcp_bpf_relay_filter [10].k = ntohs (relay_port); } #endif p.bf_insns [8].k = ntohs (local_port); if (ioctl (info -> rfdesc, BIOCSETF, &p) < 0) log_fatal ("Can't install packet filter program: %m"); if (!quiet_interface_discovery) log_info ("Listening on BPF/%s/%s%s%s", info -> name, print_hw_addr (info -> hw_address.hbuf [0], info -> hw_address.hlen - 1, &info -> hw_address.hbuf [1]), (info -> shared_network ? "/" : ""), (info -> shared_network ? info -> shared_network -> name : "")); } void if_deregister_receive (info) struct interface_info *info; { close (info -> rfdesc); info -> rfdesc = -1; if (!quiet_interface_discovery) log_info ("Disabling input on BPF/%s/%s%s%s", info -> name, print_hw_addr (info -> hw_address.hbuf [0], info -> hw_address.hlen - 1, &info -> hw_address.hbuf [1]), (info -> shared_network ? "/" : ""), (info -> shared_network ? info -> shared_network -> name : "")); } #endif /* USE_BPF_RECEIVE */ #ifdef USE_BPF_SEND ssize_t send_packet (interface, packet, raw, len, from, to, hto) struct interface_info *interface; struct packet *packet; struct dhcp_packet *raw; size_t len; struct in_addr from; struct sockaddr_in *to; struct hardware *hto; { unsigned hbufp = 0, ibufp = 0; double hw [4]; double ip [32]; struct iovec iov [3]; int result; if (!strcmp (interface -> name, "fallback")) return send_fallback (interface, packet, raw, len, from, to, hto); if (hto == NULL && interface->anycast_mac_addr.hlen) hto = &interface->anycast_mac_addr; /* Assemble the headers... */ assemble_hw_header (interface, (unsigned char *)hw, &hbufp, hto); assemble_udp_ip_header (interface, (unsigned char *)ip, &ibufp, from.s_addr, to -> sin_addr.s_addr, to -> sin_port, (unsigned char *)raw, len); /* Fire it off */ iov [0].iov_base = ((char *)hw); iov [0].iov_len = hbufp; iov [1].iov_base = ((char *)ip); iov [1].iov_len = ibufp; iov [2].iov_base = (char *)raw; iov [2].iov_len = len; result = writev(interface -> wfdesc, iov, 3); if (result < 0) log_error ("send_packet: %m"); return result; } #endif /* USE_BPF_SEND */ #ifdef USE_BPF_RECEIVE ssize_t receive_packet (interface, buf, len, from, hfrom) struct interface_info *interface; unsigned char *buf; size_t len; struct sockaddr_in *from; struct hardware *hfrom; { int length = 0; int offset = 0; struct bpf_hdr hdr; unsigned paylen; /* All this complexity is because BPF doesn't guarantee that only one packet will be returned at a time. We're getting what we deserve, though - this is a terrible abuse of the BPF interface. Sigh. */ /* Process packets until we get one we can return or until we've done a read and gotten nothing we can return... */ /* If the buffer is empty, fill it. */ if (interface->rbuf_offset >= interface->rbuf_len) { length = read(interface->rfdesc, interface->rbuf, (size_t)interface->rbuf_max); if (length <= 0) { #ifdef __FreeBSD__ if (errno == ENXIO) { #else if (errno == EIO) { #endif dhcp_interface_remove ((omapi_object_t *)interface, NULL); } return (length); } interface->rbuf_offset = 0; interface->rbuf_len = BPF_WORDALIGN(length); } do { /* If there isn't room for a whole bpf header, something went wrong, but we'll ignore it and hope it goes away... XXX */ if (interface->rbuf_len - interface->rbuf_offset < sizeof hdr) { interface->rbuf_offset = interface->rbuf_len; continue; } /* Copy out a bpf header... */ memcpy(&hdr, &interface->rbuf[interface->rbuf_offset], sizeof hdr); /* If the bpf header plus data doesn't fit in what's left of the buffer, stick head in sand yet again... */ if (interface->rbuf_offset + hdr.bh_hdrlen + hdr.bh_caplen > interface->rbuf_len) { interface->rbuf_offset = interface->rbuf_len; continue; } /* If the captured data wasn't the whole packet, or if the packet won't fit in the input buffer, all we can do is drop it. */ if (hdr.bh_caplen != hdr.bh_datalen) { interface->rbuf_offset = BPF_WORDALIGN(interface->rbuf_offset + hdr.bh_hdrlen + hdr.bh_caplen); continue; } /* Skip over the BPF header... */ interface->rbuf_offset += hdr.bh_hdrlen; /* Decode the physical header... */ offset = decode_hw_header(interface, interface->rbuf, interface->rbuf_offset, hfrom); /* If a physical layer checksum failed (dunno of any physical layer that supports this, but WTH), skip this packet. */ if (offset < 0) { interface->rbuf_offset = BPF_WORDALIGN(interface->rbuf_offset + hdr.bh_caplen); continue; } interface->rbuf_offset += offset; hdr.bh_caplen -= offset; /* Decode the IP and UDP headers... */ offset = decode_udp_ip_header(interface, interface->rbuf, interface->rbuf_offset, from, hdr.bh_caplen, &paylen, 1); /* If the IP or UDP checksum was bad, skip the packet... */ if (offset < 0) { interface->rbuf_offset = BPF_WORDALIGN(interface->rbuf_offset + hdr.bh_caplen); continue; } interface->rbuf_offset = interface->rbuf_offset + offset; hdr.bh_caplen -= offset; /* If there's not enough room to stash the packet data, we have to skip it (this shouldn't happen in real life, though). */ if (hdr.bh_caplen > len) { interface->rbuf_offset = BPF_WORDALIGN(interface->rbuf_offset + hdr.bh_caplen); continue; } /* Copy out the data in the packet... */ memcpy(buf, interface->rbuf + interface->rbuf_offset, paylen); interface->rbuf_offset = BPF_WORDALIGN(interface->rbuf_offset + hdr.bh_caplen); return paylen; } while (interface->rbuf_offset < interface->rbuf_len); return (0); } int can_unicast_without_arp (ip) struct interface_info *ip; { return 1; } int can_receive_unicast_unconfigured (ip) struct interface_info *ip; { return 1; } int supports_multiple_interfaces (ip) struct interface_info *ip; { return 1; } void maybe_setup_fallback () { isc_result_t status; struct interface_info *fbi = (struct interface_info *)0; if (setup_fallback (&fbi, MDL)) { if_register_fallback (fbi); status = omapi_register_io_object ((omapi_object_t *)fbi, if_readsocket, 0, fallback_discard, 0, 0); if (status != ISC_R_SUCCESS) log_fatal ("Can't register I/O handle for %s: %s", fbi -> name, isc_result_totext (status)); interface_dereference (&fbi, MDL); } } #endif #if defined(USE_BPF_RECEIVE) || defined(USE_BPF_HWADDR) void get_hw_addr(const char *name, struct hardware *hw) { struct ifaddrs *ifa; struct ifaddrs *p; struct sockaddr_dl *sa; if (getifaddrs(&ifa) != 0) { log_fatal("Error getting interface information; %m"); } /* * Loop through our interfaces finding a match. */ sa = NULL; for (p=ifa; (p != NULL) && (sa == NULL); p = p->ifa_next) { if ((p->ifa_addr->sa_family == AF_LINK) && !strcmp(p->ifa_name, name)) { sa = (struct sockaddr_dl *)p->ifa_addr; } } if (sa == NULL) { log_fatal("No interface called '%s'", name); } /* * Pull out the appropriate information. */ switch (sa->sdl_type) { case IFT_ETHER: #if defined (IFT_L2VLAN) case IFT_L2VLAN: #endif hw->hlen = sa->sdl_alen + 1; hw->hbuf[0] = HTYPE_ETHER; memcpy(&hw->hbuf[1], LLADDR(sa), sa->sdl_alen); break; case IFT_ISO88023: case IFT_ISO88024: /* "token ring" */ case IFT_ISO88025: case IFT_ISO88026: hw->hlen = sa->sdl_alen + 1; hw->hbuf[0] = HTYPE_IEEE802; memcpy(&hw->hbuf[1], LLADDR(sa), sa->sdl_alen); break; #ifdef IFT_FDDI case IFT_FDDI: hw->hlen = sa->sdl_alen + 1; hw->hbuf[0] = HTYPE_FDDI; memcpy(&hw->hbuf[1], LLADDR(sa), sa->sdl_alen); break; #endif /* IFT_FDDI */ default: log_fatal("Unsupported device type %d for \"%s\"", sa->sdl_type, name); } freeifaddrs(ifa); } #endif dhcp-4.4.1/common/comapi.c000644 000765 000024 00000060533 13243301226 015626 0ustar00tmarkstaff000000 000000 /* omapi.c OMAPI object interfaces for the DHCP server. */ /* * Copyright (c) 2004-2017 Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1999-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ /* Many, many thanks to Brian Murrell and BCtel for this code - BCtel provided the funding that resulted in this code and the entire OMAPI support library being written, and Brian helped brainstorm and refine the requirements. To the extent that this code is useful, you have Brian and BCtel to thank. Any limitations in the code are a result of mistakes on my part. -- Ted Lemon */ #include "dhcpd.h" #include OMAPI_OBJECT_ALLOC (subnet, struct subnet, dhcp_type_subnet) OMAPI_OBJECT_ALLOC (shared_network, struct shared_network, dhcp_type_shared_network) OMAPI_OBJECT_ALLOC (group_object, struct group_object, dhcp_type_group) OMAPI_OBJECT_ALLOC (dhcp_control, dhcp_control_object_t, dhcp_type_control) omapi_object_type_t *dhcp_type_group; omapi_object_type_t *dhcp_type_shared_network; omapi_object_type_t *dhcp_type_subnet; omapi_object_type_t *dhcp_type_control; dhcp_control_object_t *dhcp_control_object; void dhcp_common_objects_setup () { isc_result_t status; status = omapi_object_type_register (&dhcp_type_control, "control", dhcp_control_set_value, dhcp_control_get_value, dhcp_control_destroy, dhcp_control_signal_handler, dhcp_control_stuff_values, dhcp_control_lookup, dhcp_control_create, dhcp_control_remove, 0, 0, 0, sizeof (dhcp_control_object_t), 0, RC_MISC); if (status != ISC_R_SUCCESS) log_fatal ("Can't register control object type: %s", isc_result_totext (status)); status = dhcp_control_allocate (&dhcp_control_object, MDL); if (status != ISC_R_SUCCESS) log_fatal ("Can't make initial control object: %s", isc_result_totext (status)); dhcp_control_object -> state = server_startup; status = omapi_object_type_register (&dhcp_type_group, "group", dhcp_group_set_value, dhcp_group_get_value, dhcp_group_destroy, dhcp_group_signal_handler, dhcp_group_stuff_values, dhcp_group_lookup, dhcp_group_create, dhcp_group_remove, 0, 0, 0, sizeof (struct group_object), 0, RC_MISC); if (status != ISC_R_SUCCESS) log_fatal ("Can't register group object type: %s", isc_result_totext (status)); status = omapi_object_type_register (&dhcp_type_subnet, "subnet", dhcp_subnet_set_value, dhcp_subnet_get_value, dhcp_subnet_destroy, dhcp_subnet_signal_handler, dhcp_subnet_stuff_values, dhcp_subnet_lookup, dhcp_subnet_create, dhcp_subnet_remove, 0, 0, 0, sizeof (struct subnet), 0, RC_MISC); if (status != ISC_R_SUCCESS) log_fatal ("Can't register subnet object type: %s", isc_result_totext (status)); status = omapi_object_type_register (&dhcp_type_shared_network, "shared-network", dhcp_shared_network_set_value, dhcp_shared_network_get_value, dhcp_shared_network_destroy, dhcp_shared_network_signal_handler, dhcp_shared_network_stuff_values, dhcp_shared_network_lookup, dhcp_shared_network_create, dhcp_shared_network_remove, 0, 0, 0, sizeof (struct shared_network), 0, RC_MISC); if (status != ISC_R_SUCCESS) log_fatal ("Can't register shared network object type: %s", isc_result_totext (status)); interface_setup (); } isc_result_t dhcp_group_set_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_typed_data_t *value) { struct group_object *group; isc_result_t status; if (h -> type != dhcp_type_group) return DHCP_R_INVALIDARG; group = (struct group_object *)h; /* XXX For now, we can only set these values on new group objects. XXX Soon, we need to be able to update group objects. */ if (!omapi_ds_strcmp (name, "name")) { if (group -> name) return ISC_R_EXISTS; if (value -> type == omapi_datatype_data || value -> type == omapi_datatype_string) { group -> name = dmalloc (value -> u.buffer.len + 1, MDL); if (!group -> name) return ISC_R_NOMEMORY; memcpy (group -> name, value -> u.buffer.value, value -> u.buffer.len); group -> name [value -> u.buffer.len] = 0; } else return DHCP_R_INVALIDARG; return ISC_R_SUCCESS; } if (!omapi_ds_strcmp (name, "statements")) { if (group -> group && group -> group -> statements) return ISC_R_EXISTS; if (!group -> group) { if (!clone_group (&group -> group, root_group, MDL)) return ISC_R_NOMEMORY; } if (value -> type == omapi_datatype_data || value -> type == omapi_datatype_string) { struct parse *parse; int lose = 0; parse = NULL; status = new_parse(&parse, -1, (char *) value->u.buffer.value, value->u.buffer.len, "network client", 0); if (status != ISC_R_SUCCESS || parse == NULL) return status; if (!(parse_executable_statements (&group -> group -> statements, parse, &lose, context_any))) { end_parse (&parse); return DHCP_R_BADPARSE; } end_parse (&parse); return ISC_R_SUCCESS; } else return DHCP_R_INVALIDARG; } /* Try to find some inner object that can take the value. */ if (h -> inner && h -> inner -> type -> set_value) { status = ((*(h -> inner -> type -> set_value)) (h -> inner, id, name, value)); if (status == ISC_R_SUCCESS || status == DHCP_R_UNCHANGED) return status; } return ISC_R_NOTFOUND; } isc_result_t dhcp_group_get_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_value_t **value) { struct group_object *group; isc_result_t status; if (h -> type != dhcp_type_group) return DHCP_R_INVALIDARG; group = (struct group_object *)h; if (!omapi_ds_strcmp (name, "name")) return omapi_make_string_value (value, name, group -> name, MDL); /* Try to find some inner object that can take the value. */ if (h -> inner && h -> inner -> type -> get_value) { status = ((*(h -> inner -> type -> get_value)) (h -> inner, id, name, value)); if (status == ISC_R_SUCCESS) return status; } return ISC_R_NOTFOUND; } isc_result_t dhcp_group_destroy (omapi_object_t *h, const char *file, int line) { struct group_object *group, *t; if (h -> type != dhcp_type_group) return DHCP_R_INVALIDARG; group = (struct group_object *)h; if (group -> name) { if (group_name_hash) { t = (struct group_object *)0; if (group_hash_lookup (&t, group_name_hash, group -> name, strlen (group -> name), MDL)) { group_hash_delete (group_name_hash, group -> name, strlen (group -> name), MDL); group_object_dereference (&t, MDL); } } dfree (group -> name, file, line); group -> name = (char *)0; } if (group -> group) group_dereference (&group -> group, MDL); return ISC_R_SUCCESS; } isc_result_t dhcp_group_signal_handler (omapi_object_t *h, const char *name, va_list ap) { struct group_object *group; isc_result_t status; int updatep = 0; if (h -> type != dhcp_type_group) return DHCP_R_INVALIDARG; group = (struct group_object *)h; if (!strcmp (name, "updated")) { /* A group object isn't valid if a subgroup hasn't yet been associated with it. */ if (!group -> group) return DHCP_R_INVALIDARG; /* Group objects always have to have names. */ if (!group -> name) { char hnbuf [64]; sprintf (hnbuf, "ng%08lx%08lx", (unsigned long)cur_time, (unsigned long)group); group -> name = dmalloc (strlen (hnbuf) + 1, MDL); if (!group -> name) return ISC_R_NOMEMORY; strcpy (group -> name, hnbuf); } supersede_group (group, 1); updatep = 1; } /* Try to find some inner object that can take the value. */ if (h -> inner && h -> inner -> type -> get_value) { status = ((*(h -> inner -> type -> signal_handler)) (h -> inner, name, ap)); if (status == ISC_R_SUCCESS) return status; } if (updatep) return ISC_R_SUCCESS; return ISC_R_NOTFOUND; } isc_result_t dhcp_group_stuff_values (omapi_object_t *c, omapi_object_t *id, omapi_object_t *h) { struct group_object *group; isc_result_t status; if (h -> type != dhcp_type_group) return DHCP_R_INVALIDARG; group = (struct group_object *)h; /* Write out all the values. */ if (group -> name) { status = omapi_connection_put_name (c, "name"); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_string (c, group -> name); if (status != ISC_R_SUCCESS) return status; } /* Write out the inner object, if any. */ if (h -> inner && h -> inner -> type -> stuff_values) { status = ((*(h -> inner -> type -> stuff_values)) (c, id, h -> inner)); if (status == ISC_R_SUCCESS) return status; } return ISC_R_SUCCESS; } isc_result_t dhcp_group_lookup (omapi_object_t **lp, omapi_object_t *id, omapi_object_t *ref) { omapi_value_t *tv = (omapi_value_t *)0; isc_result_t status; struct group_object *group; if (!ref) return DHCP_R_NOKEYS; /* First see if we were sent a handle. */ status = omapi_get_value_str (ref, id, "handle", &tv); if (status == ISC_R_SUCCESS) { status = omapi_handle_td_lookup (lp, tv -> value); omapi_value_dereference (&tv, MDL); if (status != ISC_R_SUCCESS) return status; /* Don't return the object if the type is wrong. */ if ((*lp) -> type != dhcp_type_group) { omapi_object_dereference (lp, MDL); return DHCP_R_INVALIDARG; } } /* Now look for a name. */ status = omapi_get_value_str (ref, id, "name", &tv); if (status == ISC_R_SUCCESS) { group = (struct group_object *)0; if (group_name_hash && group_hash_lookup (&group, group_name_hash, (const char *) tv -> value -> u.buffer.value, tv -> value -> u.buffer.len, MDL)) { omapi_value_dereference (&tv, MDL); if (*lp && *lp != (omapi_object_t *)group) { group_object_dereference (&group, MDL); omapi_object_dereference (lp, MDL); return DHCP_R_KEYCONFLICT; } else if (!*lp) { /* XXX fix so that hash lookup itself creates XXX the reference. */ omapi_object_reference (lp, (omapi_object_t *)group, MDL); group_object_dereference (&group, MDL); } } else if (!*lp) return ISC_R_NOTFOUND; } /* If we get to here without finding a group, no valid key was specified. */ if (!*lp) return DHCP_R_NOKEYS; if (((struct group_object *)(*lp)) -> flags & GROUP_OBJECT_DELETED) { omapi_object_dereference (lp, MDL); return ISC_R_NOTFOUND; } return ISC_R_SUCCESS; } isc_result_t dhcp_group_create (omapi_object_t **lp, omapi_object_t *id) { struct group_object *group; isc_result_t status; group = (struct group_object *)0; status = group_object_allocate (&group, MDL); if (status != ISC_R_SUCCESS) return status; group -> flags = GROUP_OBJECT_DYNAMIC; status = omapi_object_reference (lp, (omapi_object_t *)group, MDL); group_object_dereference (&group, MDL); return status; } isc_result_t dhcp_group_remove (omapi_object_t *lp, omapi_object_t *id) { struct group_object *group; isc_result_t status; if (lp -> type != dhcp_type_group) return DHCP_R_INVALIDARG; group = (struct group_object *)lp; group -> flags |= GROUP_OBJECT_DELETED; if (group_write_hook) { if (!(*group_write_hook) (group)) return ISC_R_IOERROR; } status = dhcp_group_destroy ((omapi_object_t *)group, MDL); return status; } isc_result_t dhcp_control_set_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_typed_data_t *value) { dhcp_control_object_t *control; isc_result_t status; unsigned long newstate; if (h -> type != dhcp_type_control) return DHCP_R_INVALIDARG; control = (dhcp_control_object_t *)h; if (!omapi_ds_strcmp (name, "state")) { status = omapi_get_int_value (&newstate, value); if (status != ISC_R_SUCCESS) return status; status = dhcp_set_control_state (control -> state, newstate); if (status == ISC_R_SUCCESS) control -> state = value -> u.integer; return status; } /* Try to find some inner object that can take the value. */ if (h -> inner && h -> inner -> type -> set_value) { status = ((*(h -> inner -> type -> set_value)) (h -> inner, id, name, value)); if (status == ISC_R_SUCCESS || status == DHCP_R_UNCHANGED) return status; } return ISC_R_NOTFOUND; } isc_result_t dhcp_control_get_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_value_t **value) { dhcp_control_object_t *control; isc_result_t status; if (h -> type != dhcp_type_control) return DHCP_R_INVALIDARG; control = (dhcp_control_object_t *)h; if (!omapi_ds_strcmp (name, "state")) return omapi_make_int_value (value, name, (int)control -> state, MDL); /* Try to find some inner object that can take the value. */ if (h -> inner && h -> inner -> type -> get_value) { status = ((*(h -> inner -> type -> get_value)) (h -> inner, id, name, value)); if (status == ISC_R_SUCCESS) return status; } return ISC_R_NOTFOUND; } isc_result_t dhcp_control_destroy (omapi_object_t *h, const char *file, int line) { if (h -> type != dhcp_type_control) return DHCP_R_INVALIDARG; /* Can't destroy the control object. */ return ISC_R_NOPERM; } isc_result_t dhcp_control_signal_handler (omapi_object_t *h, const char *name, va_list ap) { /* In this function h should be a (dhcp_control_object_t *) */ isc_result_t status; if (h -> type != dhcp_type_control) return DHCP_R_INVALIDARG; /* Try to find some inner object that can take the value. */ if (h -> inner && h -> inner -> type -> get_value) { status = ((*(h -> inner -> type -> signal_handler)) (h -> inner, name, ap)); if (status == ISC_R_SUCCESS) return status; } return ISC_R_NOTFOUND; } isc_result_t dhcp_control_stuff_values (omapi_object_t *c, omapi_object_t *id, omapi_object_t *h) { dhcp_control_object_t *control; isc_result_t status; if (h -> type != dhcp_type_control) return DHCP_R_INVALIDARG; control = (dhcp_control_object_t *)h; /* Write out all the values. */ status = omapi_connection_put_name (c, "state"); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_uint32 (c, sizeof (u_int32_t)); if (status != ISC_R_SUCCESS) return status; status = omapi_connection_put_uint32 (c, control -> state); if (status != ISC_R_SUCCESS) return status; /* Write out the inner object, if any. */ if (h -> inner && h -> inner -> type -> stuff_values) { status = ((*(h -> inner -> type -> stuff_values)) (c, id, h -> inner)); if (status == ISC_R_SUCCESS) return status; } return ISC_R_SUCCESS; } isc_result_t dhcp_control_lookup (omapi_object_t **lp, omapi_object_t *id, omapi_object_t *ref) { omapi_value_t *tv = (omapi_value_t *)0; isc_result_t status; /* First see if we were sent a handle. */ if (ref) { status = omapi_get_value_str (ref, id, "handle", &tv); if (status == ISC_R_SUCCESS) { status = omapi_handle_td_lookup (lp, tv -> value); omapi_value_dereference (&tv, MDL); if (status != ISC_R_SUCCESS) return status; /* Don't return the object if the type is wrong. */ if ((*lp) -> type != dhcp_type_control) { omapi_object_dereference (lp, MDL); return DHCP_R_INVALIDARG; } } } /* Otherwise, stop playing coy - there's only one control object, so we can just return it. */ dhcp_control_reference ((dhcp_control_object_t **)lp, dhcp_control_object, MDL); return ISC_R_SUCCESS; } isc_result_t dhcp_control_create (omapi_object_t **lp, omapi_object_t *id) { /* Can't create a control object - there can be only one. */ return ISC_R_NOPERM; } isc_result_t dhcp_control_remove (omapi_object_t *lp, omapi_object_t *id) { /* Form is emptiness; emptiness form. The control object cannot go out of existance. */ return ISC_R_NOPERM; } isc_result_t dhcp_subnet_set_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_typed_data_t *value) { /* In this function h should be a (struct subnet *) */ isc_result_t status; if (h -> type != dhcp_type_subnet) return DHCP_R_INVALIDARG; /* No values to set yet. */ /* Try to find some inner object that can take the value. */ if (h -> inner && h -> inner -> type -> set_value) { status = ((*(h -> inner -> type -> set_value)) (h -> inner, id, name, value)); if (status == ISC_R_SUCCESS || status == DHCP_R_UNCHANGED) return status; } return ISC_R_NOTFOUND; } isc_result_t dhcp_subnet_get_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_value_t **value) { /* In this function h should be a (struct subnet *) */ isc_result_t status; if (h -> type != dhcp_type_subnet) return DHCP_R_INVALIDARG; /* No values to get yet. */ /* Try to find some inner object that can provide the value. */ if (h -> inner && h -> inner -> type -> get_value) { status = ((*(h -> inner -> type -> get_value)) (h -> inner, id, name, value)); if (status == ISC_R_SUCCESS) return status; } return ISC_R_NOTFOUND; } isc_result_t dhcp_subnet_destroy (omapi_object_t *h, const char *file, int line) { struct subnet *subnet; if (h -> type != dhcp_type_subnet) return DHCP_R_INVALIDARG; subnet = (struct subnet *)h; if (subnet -> next_subnet) subnet_dereference (&subnet -> next_subnet, file, line); if (subnet -> next_sibling) subnet_dereference (&subnet -> next_sibling, file, line); if (subnet -> shared_network) shared_network_dereference (&subnet -> shared_network, file, line); if (subnet -> interface) interface_dereference (&subnet -> interface, file, line); if (subnet -> group) group_dereference (&subnet -> group, file, line); return ISC_R_SUCCESS; } isc_result_t dhcp_subnet_signal_handler (omapi_object_t *h, const char *name, va_list ap) { /* In this function h should be a (struct subnet *) */ isc_result_t status; if (h -> type != dhcp_type_subnet) return DHCP_R_INVALIDARG; /* Can't write subnets yet. */ /* Try to find some inner object that can take the value. */ if (h -> inner && h -> inner -> type -> get_value) { status = ((*(h -> inner -> type -> signal_handler)) (h -> inner, name, ap)); if (status == ISC_R_SUCCESS) return status; } return ISC_R_NOTFOUND; } isc_result_t dhcp_subnet_stuff_values (omapi_object_t *c, omapi_object_t *id, omapi_object_t *h) { /* In this function h should be a (struct subnet *) */ isc_result_t status; if (h -> type != dhcp_type_subnet) return DHCP_R_INVALIDARG; /* Can't stuff subnet values yet. */ /* Write out the inner object, if any. */ if (h -> inner && h -> inner -> type -> stuff_values) { status = ((*(h -> inner -> type -> stuff_values)) (c, id, h -> inner)); if (status == ISC_R_SUCCESS) return status; } return ISC_R_SUCCESS; } isc_result_t dhcp_subnet_lookup (omapi_object_t **lp, omapi_object_t *id, omapi_object_t *ref) { /* Can't look up subnets yet. */ /* If we get to here without finding a subnet, no valid key was specified. */ if (!*lp) return DHCP_R_NOKEYS; return ISC_R_SUCCESS; } isc_result_t dhcp_subnet_create (omapi_object_t **lp, omapi_object_t *id) { return ISC_R_NOTIMPLEMENTED; } isc_result_t dhcp_subnet_remove (omapi_object_t *lp, omapi_object_t *id) { return ISC_R_NOTIMPLEMENTED; } isc_result_t dhcp_shared_network_set_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_typed_data_t *value) { /* In this function h should be a (struct shared_network *) */ isc_result_t status; if (h -> type != dhcp_type_shared_network) return DHCP_R_INVALIDARG; /* No values to set yet. */ /* Try to find some inner object that can take the value. */ if (h -> inner && h -> inner -> type -> set_value) { status = ((*(h -> inner -> type -> set_value)) (h -> inner, id, name, value)); if (status == ISC_R_SUCCESS || status == DHCP_R_UNCHANGED) return status; } return ISC_R_NOTFOUND; } isc_result_t dhcp_shared_network_get_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_value_t **value) { /* In this function h should be a (struct shared_network *) */ isc_result_t status; if (h -> type != dhcp_type_shared_network) return DHCP_R_INVALIDARG; /* No values to get yet. */ /* Try to find some inner object that can provide the value. */ if (h -> inner && h -> inner -> type -> get_value) { status = ((*(h -> inner -> type -> get_value)) (h -> inner, id, name, value)); if (status == ISC_R_SUCCESS) return status; } return ISC_R_NOTFOUND; } isc_result_t dhcp_shared_network_destroy (omapi_object_t *h, const char *file, int line) { /* In this function h should be a (struct shared_network *) */ struct shared_network *shared_network; if (h -> type != dhcp_type_shared_network) return DHCP_R_INVALIDARG; shared_network = (struct shared_network *)h; if (shared_network -> next) shared_network_dereference (&shared_network -> next, file, line); if (shared_network -> name) { dfree (shared_network -> name, file, line); shared_network -> name = 0; } if (shared_network -> subnets) subnet_dereference (&shared_network -> subnets, file, line); if (shared_network -> interface) interface_dereference (&shared_network -> interface, file, line); if (shared_network -> pools) omapi_object_dereference ((omapi_object_t **) &shared_network -> pools, file, line); if (shared_network -> group) group_dereference (&shared_network -> group, file, line); #if defined (FAILOVER_PROTOCOL) if (shared_network -> failover_peer) omapi_object_dereference ((omapi_object_t **) &shared_network -> failover_peer, file, line); #endif return ISC_R_SUCCESS; } isc_result_t dhcp_shared_network_signal_handler (omapi_object_t *h, const char *name, va_list ap) { /* In this function h should be a (struct shared_network *) */ isc_result_t status; if (h -> type != dhcp_type_shared_network) return DHCP_R_INVALIDARG; /* Can't write shared_networks yet. */ /* Try to find some inner object that can take the value. */ if (h -> inner && h -> inner -> type -> get_value) { status = ((*(h -> inner -> type -> signal_handler)) (h -> inner, name, ap)); if (status == ISC_R_SUCCESS) return status; } return ISC_R_NOTFOUND; } isc_result_t dhcp_shared_network_stuff_values (omapi_object_t *c, omapi_object_t *id, omapi_object_t *h) { /* In this function h should be a (struct shared_network *) */ isc_result_t status; if (h -> type != dhcp_type_shared_network) return DHCP_R_INVALIDARG; /* Can't stuff shared_network values yet. */ /* Write out the inner object, if any. */ if (h -> inner && h -> inner -> type -> stuff_values) { status = ((*(h -> inner -> type -> stuff_values)) (c, id, h -> inner)); if (status == ISC_R_SUCCESS) return status; } return ISC_R_SUCCESS; } isc_result_t dhcp_shared_network_lookup (omapi_object_t **lp, omapi_object_t *id, omapi_object_t *ref) { /* Can't look up shared_networks yet. */ /* If we get to here without finding a shared_network, no valid key was specified. */ if (!*lp) return DHCP_R_NOKEYS; return ISC_R_SUCCESS; } isc_result_t dhcp_shared_network_create (omapi_object_t **lp, omapi_object_t *id) { return ISC_R_NOTIMPLEMENTED; } isc_result_t dhcp_shared_network_remove (omapi_object_t *lp, omapi_object_t *id) { return ISC_R_NOTIMPLEMENTED; } dhcp-4.4.1/common/conflex.c000644 000765 000024 00000120740 13243301226 016011 0ustar00tmarkstaff000000 000000 /* conflex.c Lexical scanner for dhcpd config file... */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1995-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #include static int get_char (struct parse *); static void unget_char(struct parse *, int); static void skip_to_eol (struct parse *); static enum dhcp_token read_whitespace(int c, struct parse *cfile); static enum dhcp_token read_string (struct parse *); static enum dhcp_token read_number (int, struct parse *); static enum dhcp_token read_num_or_name (int, struct parse *); static enum dhcp_token intern (char *, enum dhcp_token); isc_result_t new_parse (cfile, file, inbuf, buflen, name, eolp) struct parse **cfile; int file; char *inbuf; unsigned buflen; const char *name; int eolp; { isc_result_t status = ISC_R_SUCCESS; struct parse *tmp; tmp = dmalloc(sizeof(struct parse), MDL); if (tmp == NULL) { return (ISC_R_NOMEMORY); } /* * We don't need to initialize things to zero here, since * dmalloc() returns memory that is set to zero. */ tmp->tlname = name; tmp->lpos = tmp -> line = 1; tmp->cur_line = tmp->line1; tmp->prev_line = tmp->line2; tmp->token_line = tmp->cur_line; tmp->cur_line[0] = tmp->prev_line[0] = 0; tmp->file = file; tmp->eol_token = eolp; if (inbuf != NULL) { tmp->inbuf = inbuf; tmp->buflen = buflen; tmp->bufsiz = 0; } else { struct stat sb; if (fstat(file, &sb) < 0) { status = ISC_R_IOERROR; goto cleanup; } if (sb.st_size == 0) goto cleanup; tmp->bufsiz = tmp->buflen = (size_t) sb.st_size; tmp->inbuf = mmap(NULL, tmp->bufsiz, PROT_READ, MAP_SHARED, file, 0); if (tmp->inbuf == MAP_FAILED) { status = ISC_R_IOERROR; goto cleanup; } } *cfile = tmp; return (ISC_R_SUCCESS); cleanup: dfree(tmp, MDL); return (status); } isc_result_t end_parse (cfile) struct parse **cfile; { /* "Memory" config files have no file. */ if ((*cfile)->file != -1) { munmap((*cfile)->inbuf, (*cfile)->bufsiz); close((*cfile)->file); } if ((*cfile)->saved_state != NULL) { dfree((*cfile)->saved_state, MDL); } dfree(*cfile, MDL); *cfile = NULL; return ISC_R_SUCCESS; } /* * Save the current state of the parser. * * Only one state may be saved. Any previous saved state is * lost. */ isc_result_t save_parse_state(struct parse *cfile) { /* * Free any previous saved state. */ if (cfile->saved_state != NULL) { dfree(cfile->saved_state, MDL); } /* * Save our current state. */ cfile->saved_state = dmalloc(sizeof(struct parse), MDL); if (cfile->saved_state == NULL) { return ISC_R_NOMEMORY; } memcpy(cfile->saved_state, cfile, sizeof(*cfile)); return ISC_R_SUCCESS; } /* * Return the parser to the previous saved state. * * You must call save_parse_state() every time before calling * restore_parse_state(). * * Note: When the read function callback is in use in ldap mode, * a call to get_char() may reallocate the buffer and will append * config data to the buffer until a state restore. * Do not restore to the (freed) pointer and size, but use new one. */ isc_result_t restore_parse_state(struct parse *cfile) { struct parse *saved_state; #if defined(LDAP_CONFIGURATION) char *inbuf = cfile->inbuf; size_t size = cfile->bufsiz; #endif if (cfile->saved_state == NULL) { return DHCP_R_NOTYET; } saved_state = cfile->saved_state; memcpy(cfile, saved_state, sizeof(*cfile)); dfree(saved_state, MDL); cfile->saved_state = NULL; #if defined(LDAP_CONFIGURATION) cfile->inbuf = inbuf; cfile->bufsiz = size; #endif return ISC_R_SUCCESS; } static int get_char (cfile) struct parse *cfile; { /* My kingdom for WITH... */ int c; if (cfile->bufix == cfile->buflen) { #if !defined(LDAP_CONFIGURATION) c = EOF; #else /* defined(LDAP_CONFIGURATION) */ if (cfile->read_function != NULL) c = cfile->read_function(cfile); else c = EOF; #endif } else { c = cfile->inbuf [cfile->bufix]; cfile->bufix++; } if (!cfile->ugflag) { if (c == EOL) { if (cfile->cur_line == cfile->line1) { cfile->cur_line = cfile->line2; cfile->prev_line = cfile->line1; } else { cfile->cur_line = cfile->line1; cfile->prev_line = cfile->line2; } cfile->line++; cfile->lpos = 1; cfile->cur_line [0] = 0; } else if (c != EOF) { if (cfile->lpos <= 80) { cfile->cur_line [cfile->lpos - 1] = c; cfile->cur_line [cfile->lpos] = 0; } cfile->lpos++; } } else cfile->ugflag = 0; return c; } /* * Return a character to our input buffer. */ static void unget_char(struct parse *cfile, int c) { if (c != EOF) { cfile->bufix--; cfile->ugflag = 1; /* do not put characters into our error buffer on the next call to get_char() */ } } /* * GENERAL NOTE ABOUT TOKENS * * We normally only want non-whitespace tokens. There are some * circumstances where we *do* want to see whitespace (for example * when parsing IPv6 addresses). * * Generally we use the next_token() function to read tokens. This * in turn calls get_next_token, which does *not* return tokens for * whitespace. Rather, it skips these. * * When we need to see whitespace, we us next_raw_token(), which also * returns the WHITESPACE token. * * The peek_token() and peek_raw_token() functions work as expected. * * Warning: if you invoke peek_token(), then if there is a whitespace * token, it will be lost, and subsequent use of next_raw_token() or * peek_raw_token() will NOT see it. */ static enum dhcp_token get_raw_token(struct parse *cfile) { int c; enum dhcp_token ttok; static char tb [2]; int l, p; do { l = cfile -> line; p = cfile -> lpos; c = get_char (cfile); if (!((c == '\n') && cfile->eol_token) && isascii(c) && isspace(c)) { ttok = read_whitespace(c, cfile); break; } if (c == '#') { skip_to_eol (cfile); continue; } if (c == '"') { cfile -> lexline = l; cfile -> lexchar = p; ttok = read_string (cfile); break; } if ((isascii (c) && isdigit (c)) || c == '-') { cfile -> lexline = l; cfile -> lexchar = p; ttok = read_number (c, cfile); break; } else if (isascii (c) && isalpha (c)) { cfile -> lexline = l; cfile -> lexchar = p; ttok = read_num_or_name (c, cfile); break; } else if (c == EOF) { ttok = END_OF_FILE; cfile -> tlen = 0; break; } else { cfile -> lexline = l; cfile -> lexchar = p; tb [0] = c; tb [1] = 0; cfile -> tval = tb; cfile -> tlen = 1; ttok = c; break; } } while (1); return ttok; } /* * The get_next_token() function consumes the next token and * returns it to the caller. * * Since the code is almost the same for "normal" and "raw" * input, we pass a flag to alter the way it works. */ static enum dhcp_token get_next_token(const char **rval, unsigned *rlen, struct parse *cfile, isc_boolean_t raw) { int rv; if (cfile -> token) { if (cfile -> lexline != cfile -> tline) cfile -> token_line = cfile -> cur_line; cfile -> lexchar = cfile -> tlpos; cfile -> lexline = cfile -> tline; rv = cfile -> token; cfile -> token = 0; } else { rv = get_raw_token(cfile); cfile -> token_line = cfile -> cur_line; } if (!raw) { while (rv == WHITESPACE) { rv = get_raw_token(cfile); cfile->token_line = cfile->cur_line; } } if (rval) *rval = cfile -> tval; if (rlen) *rlen = cfile -> tlen; #ifdef DEBUG_TOKENS fprintf (stderr, "%s:%d ", cfile -> tval, rv); #endif return rv; } /* * Get the next token from cfile and return it. * * If rval is non-NULL, set the pointer it contains to * the contents of the token. * * If rlen is non-NULL, set the integer it contains to * the length of the token. */ enum dhcp_token next_token(const char **rval, unsigned *rlen, struct parse *cfile) { return get_next_token(rval, rlen, cfile, ISC_FALSE); } /* * The same as the next_token() function above, but will return space * as the WHITESPACE token. */ enum dhcp_token next_raw_token(const char **rval, unsigned *rlen, struct parse *cfile) { return get_next_token(rval, rlen, cfile, ISC_TRUE); } /* * The do_peek_token() function checks the next token without * consuming it, and returns it to the caller. * * Since the code is almost the same for "normal" and "raw" * input, we pass a flag to alter the way it works. (See the * warning in the GENERAL NOTES ABOUT TOKENS above though.) */ enum dhcp_token do_peek_token(const char **rval, unsigned int *rlen, struct parse *cfile, isc_boolean_t raw) { int x; if (!cfile->token || (!raw && (cfile->token == WHITESPACE))) { cfile -> tlpos = cfile -> lexchar; cfile -> tline = cfile -> lexline; do { cfile->token = get_raw_token(cfile); } while (!raw && (cfile->token == WHITESPACE)); if (cfile -> lexline != cfile -> tline) cfile -> token_line = cfile -> prev_line; x = cfile -> lexchar; cfile -> lexchar = cfile -> tlpos; cfile -> tlpos = x; x = cfile -> lexline; cfile -> lexline = cfile -> tline; cfile -> tline = x; } if (rval) *rval = cfile -> tval; if (rlen) *rlen = cfile -> tlen; #ifdef DEBUG_TOKENS fprintf (stderr, "(%s:%d) ", cfile -> tval, cfile -> token); #endif return cfile -> token; } /* * Get the next token from cfile and return it, leaving it for a * subsequent call to next_token(). * * Note that it WILL consume whitespace tokens. * * If rval is non-NULL, set the pointer it contains to * the contents of the token. * * If rlen is non-NULL, set the integer it contains to * the length of the token. */ enum dhcp_token peek_token(const char **rval, unsigned *rlen, struct parse *cfile) { return do_peek_token(rval, rlen, cfile, ISC_FALSE); } /* * The same as the peek_token() function above, but will return space * as the WHITESPACE token. */ enum dhcp_token peek_raw_token(const char **rval, unsigned *rlen, struct parse *cfile) { return do_peek_token(rval, rlen, cfile, ISC_TRUE); } static void skip_to_eol (cfile) struct parse *cfile; { int c; do { c = get_char (cfile); if (c == EOF) return; if (c == EOL) { return; } } while (1); } static enum dhcp_token read_whitespace(int c, struct parse *cfile) { int ofs; /* * Read as much whitespace as we have available. */ ofs = 0; do { if (ofs >= (sizeof(cfile->tokbuf) - 1)) { /* * As the file includes a huge amount of whitespace, * it's probably broken. * Print out a warning and bail out. */ parse_warn(cfile, "whitespace too long, buffer overflow."); log_fatal("Exiting"); } cfile->tokbuf[ofs++] = c; c = get_char(cfile); if (c == EOF) return END_OF_FILE; } while (!((c == '\n') && cfile->eol_token) && isascii(c) && isspace(c)); /* * Put the last (non-whitespace) character back. */ unget_char(cfile, c); /* * Return our token. */ cfile->tokbuf[ofs] = '\0'; cfile->tlen = ofs; cfile->tval = cfile->tokbuf; return WHITESPACE; } static enum dhcp_token read_string (cfile) struct parse *cfile; { int i; int bs = 0; int c; int value = 0; int hex = 0; for (i = 0; i < sizeof cfile -> tokbuf; i++) { again: c = get_char (cfile); if (c == EOF) { parse_warn (cfile, "eof in string constant"); break; } if (bs == 1) { switch (c) { case 't': cfile -> tokbuf [i] = '\t'; break; case 'r': cfile -> tokbuf [i] = '\r'; break; case 'n': cfile -> tokbuf [i] = '\n'; break; case 'b': cfile -> tokbuf [i] = '\b'; break; case '0': case '1': case '2': case '3': hex = 0; value = c - '0'; ++bs; goto again; case 'x': hex = 1; value = 0; ++bs; goto again; default: cfile -> tokbuf [i] = c; break; } bs = 0; } else if (bs > 1) { if (hex) { if (c >= '0' && c <= '9') { value = value * 16 + (c - '0'); } else if (c >= 'a' && c <= 'f') { value = value * 16 + (c - 'a' + 10); } else if (c >= 'A' && c <= 'F') { value = value * 16 + (c - 'A' + 10); } else { parse_warn (cfile, "invalid hex digit: %x", c); bs = 0; continue; } if (++bs == 4) { cfile -> tokbuf [i] = value; bs = 0; } else goto again; } else { if (c >= '0' && c <= '7') { value = value * 8 + (c - '0'); } else { if (value != 0) { parse_warn (cfile, "invalid octal digit %x", c); continue; } else cfile -> tokbuf [i] = 0; bs = 0; } if (++bs == 4) { cfile -> tokbuf [i] = value; bs = 0; } else goto again; } } else if (c == '\\') { bs = 1; goto again; } else if (c == '"') break; else cfile -> tokbuf [i] = c; } /* Normally, I'd feel guilty about this, but we're talking about strings that'll fit in a DHCP packet here... */ if (i == sizeof cfile -> tokbuf) { parse_warn (cfile, "string constant larger than internal buffer"); --i; } cfile -> tokbuf [i] = 0; cfile -> tlen = i; cfile -> tval = cfile -> tokbuf; return STRING; } static enum dhcp_token read_number (c, cfile) int c; struct parse *cfile; { int i = 0; int token = NUMBER; cfile -> tokbuf [i++] = c; for (; i < sizeof cfile -> tokbuf; i++) { c = get_char (cfile); /* Promote NUMBER -> NUMBER_OR_NAME -> NAME, never demote. * Except in the case of '0x' syntax hex, which gets called * a NAME at '0x', and returned to NUMBER_OR_NAME once it's * verified to be at least 0xf or less. */ switch(isascii(c) ? token : BREAK) { case NUMBER: if(isdigit(c)) break; /* FALLTHROUGH */ case NUMBER_OR_NAME: if(isxdigit(c)) { token = NUMBER_OR_NAME; break; } /* FALLTHROUGH */ case NAME: if((i == 2) && isxdigit(c) && (cfile->tokbuf[0] == '0') && ((cfile->tokbuf[1] == 'x') || (cfile->tokbuf[1] == 'X'))) { token = NUMBER_OR_NAME; break; } else if(((c == '-') || (c == '_') || isalnum(c))) { token = NAME; break; } /* FALLTHROUGH */ case BREAK: /* At this point c is either EOF or part of the next * token. If not EOF, rewind the file one byte so * the next token is read from there. */ unget_char(cfile, c); goto end_read; default: log_fatal("read_number():%s:%d: impossible case", MDL); } cfile -> tokbuf [i] = c; } if (i == sizeof cfile -> tokbuf) { parse_warn (cfile, "numeric token larger than internal buffer"); --i; } end_read: cfile -> tokbuf [i] = 0; cfile -> tlen = i; cfile -> tval = cfile -> tokbuf; /* * If this entire token from start to finish was "-", such as * the middle parameter in "42 - 7", return just the MINUS token. */ if ((i == 1) && (cfile->tokbuf[i] == '-')) return MINUS; else return token; } static enum dhcp_token read_num_or_name (c, cfile) int c; struct parse *cfile; { int i = 0; enum dhcp_token rv = NUMBER_OR_NAME; cfile -> tokbuf [i++] = c; for (; i < sizeof cfile -> tokbuf; i++) { c = get_char (cfile); if (!isascii (c) || (c != '-' && c != '_' && !isalnum (c))) { unget_char(cfile, c); break; } if (!isxdigit (c)) rv = NAME; cfile -> tokbuf [i] = c; } if (i == sizeof cfile -> tokbuf) { parse_warn (cfile, "token larger than internal buffer"); --i; } cfile -> tokbuf [i] = 0; cfile -> tlen = i; cfile -> tval = cfile -> tokbuf; return intern(cfile->tval, rv); } static enum dhcp_token intern(char *atom, enum dhcp_token dfv) { if (!isascii(atom[0])) return dfv; switch (tolower((unsigned char)atom[0])) { case '-': if (atom [1] == 0) return MINUS; break; case 'a': if (!strcasecmp(atom + 1, "bandoned")) return TOKEN_ABANDONED; if (!strcasecmp(atom + 1, "ctive")) return TOKEN_ACTIVE; if (!strncasecmp(atom + 1, "dd", 2)) { if (atom[3] == '\0') return TOKEN_ADD; else if (!strcasecmp(atom + 3, "ress")) return ADDRESS; break; } if (!strcasecmp(atom + 1, "fter")) return AFTER; if (isascii(atom[1]) && (tolower((unsigned char)atom[1]) == 'l')) { if (!strcasecmp(atom + 2, "gorithm")) return ALGORITHM; if (!strcasecmp(atom + 2, "ias")) return ALIAS; if (isascii(atom[2]) && (tolower((unsigned char)atom[2]) == 'l')) { if (atom[3] == '\0') return ALL; else if (!strcasecmp(atom + 3, "ow")) return ALLOW; break; } if (!strcasecmp(atom + 2, "so")) return TOKEN_ALSO; break; } if (isascii(atom[1]) && (tolower((unsigned char)atom[1]) == 'n')) { if (!strcasecmp(atom + 2, "d")) return AND; if (!strcasecmp(atom + 2, "ycast-mac")) return ANYCAST_MAC; break; } if (!strcasecmp(atom + 1, "ppend")) return APPEND; if (!strcasecmp(atom + 1, "rray")) return ARRAY; if (isascii(atom[1]) && (tolower((unsigned char)atom[1]) == 't')) { if (atom[2] == '\0') return AT; if (!strcasecmp(atom + 2, "sfp")) return ATSFP; break; } if (!strcasecmp(atom + 1, "uthoring-byte-order")) return AUTHORING_BYTE_ORDER; if (!strncasecmp(atom + 1, "ut", 2)) { if (isascii(atom[3]) && (tolower((unsigned char)atom[3]) == 'h')) { if (!strncasecmp(atom + 4, "enticat", 7)) { if (!strcasecmp(atom + 11, "ed")) return AUTHENTICATED; if (!strcasecmp(atom + 11, "ion")) return AUTHENTICATION; break; } if (!strcasecmp(atom + 4, "oritative")) return AUTHORITATIVE; break; } if (!strcasecmp(atom + 3, "o-partner-down")) return AUTO_PARTNER_DOWN; break; } break; case 'b': if (!strcasecmp (atom + 1, "ackup")) return TOKEN_BACKUP; if (!strcasecmp (atom + 1, "ootp")) return TOKEN_BOOTP; if (!strcasecmp (atom + 1, "inding")) return BINDING; if (!strcasecmp (atom + 1, "inary-to-ascii")) return BINARY_TO_ASCII; if (!strcasecmp (atom + 1, "ackoff-cutoff")) return BACKOFF_CUTOFF; if (!strcasecmp (atom + 1, "ooting")) return BOOTING; if (!strcasecmp (atom + 1, "oot-unknown-clients")) return BOOT_UNKNOWN_CLIENTS; if (!strcasecmp (atom + 1, "reak")) return BREAK; if (!strcasecmp (atom + 1, "illing")) return BILLING; if (!strcasecmp (atom + 1, "oolean")) return BOOLEAN; if (!strcasecmp (atom + 1, "alance")) return BALANCE; if (!strcasecmp (atom + 1, "ound")) return BOUND; if (!strcasecmp(atom+1, "ig-endian")) { return TOKEN_BIG_ENDIAN; } break; case 'c': if (!strcasecmp(atom + 1, "ase")) return CASE; if (!strcasecmp(atom + 1, "heck")) return CHECK; if (!strcasecmp(atom + 1, "iaddr")) return CIADDR; if (isascii(atom[1]) && tolower((unsigned char)atom[1]) == 'l') { if (!strcasecmp(atom + 2, "ass")) return CLASS; if (!strncasecmp(atom + 2, "ient", 4)) { if (!strcasecmp(atom + 6, "s")) return CLIENTS; if (atom[6] == '-') { if (!strcasecmp(atom + 7, "hostname")) return CLIENT_HOSTNAME; if (!strcasecmp(atom + 7, "identifier")) return CLIENT_IDENTIFIER; if (!strcasecmp(atom + 7, "state")) return CLIENT_STATE; if (!strcasecmp(atom + 7, "updates")) return CLIENT_UPDATES; break; } break; } if (!strcasecmp(atom + 2, "ose")) return TOKEN_CLOSE; if (!strcasecmp(atom + 2, "tt")) return CLTT; break; } if (isascii(atom[1]) && tolower((unsigned char)atom[1]) == 'o') { if (!strcasecmp(atom + 2, "de")) return CODE; if (isascii(atom[2]) && tolower((unsigned char)atom[2]) == 'm') { if (!strcasecmp(atom + 3, "mit")) return COMMIT; if (!strcasecmp(atom + 3, "munications-interrupted")) return COMMUNICATIONS_INTERRUPTED; if (!strcasecmp(atom + 3, "pressed")) return COMPRESSED; break; } if (isascii(atom[2]) && tolower((unsigned char)atom[2]) == 'n') { if (!strcasecmp(atom + 3, "cat")) return CONCAT; if (!strcasecmp(atom + 3, "fig-option")) return CONFIG_OPTION; if (!strcasecmp(atom + 3, "flict-done")) return CONFLICT_DONE; if (!strcasecmp(atom + 3, "nect")) return CONNECT; break; } break; } if (!strcasecmp(atom + 1, "reate")) return TOKEN_CREATE; break; case 'd': if (!strcasecmp(atom + 1, "b-time-format")) return DB_TIME_FORMAT; if (!strcasecmp (atom + 1, "omain")) return DOMAIN; if (!strncasecmp (atom + 1, "omain-", 6)) { if (!strcasecmp(atom + 7, "name")) return DOMAIN_NAME; if (!strcasecmp(atom + 7, "list")) return DOMAIN_LIST; } if (!strcasecmp (atom + 1, "o-forward-updates")) return DO_FORWARD_UPDATE; /* do-forward-update is included for historical reasons */ if (!strcasecmp (atom + 1, "o-forward-update")) return DO_FORWARD_UPDATE; if (!strcasecmp (atom + 1, "ebug")) return TOKEN_DEBUG; if (!strcasecmp (atom + 1, "eny")) return DENY; if (!strcasecmp (atom + 1, "eleted")) return TOKEN_DELETED; if (!strcasecmp (atom + 1, "elete")) return TOKEN_DELETE; if (!strncasecmp (atom + 1, "efault", 6)) { if (!atom [7]) return DEFAULT; if (!strcasecmp(atom + 7, "-duid")) return DEFAULT_DUID; if (!strcasecmp (atom + 7, "-lease-time")) return DEFAULT_LEASE_TIME; break; } if (!strncasecmp (atom + 1, "ynamic", 6)) { if (!atom [7]) return DYNAMIC; if (!strncasecmp (atom + 7, "-bootp", 6)) { if (!atom [13]) return DYNAMIC_BOOTP; if (!strcasecmp (atom + 13, "-lease-cutoff")) return DYNAMIC_BOOTP_LEASE_CUTOFF; if (!strcasecmp (atom + 13, "-lease-length")) return DYNAMIC_BOOTP_LEASE_LENGTH; break; } } if (!strcasecmp (atom + 1, "uplicates")) return DUPLICATES; if (!strcasecmp (atom + 1, "eclines")) return DECLINES; if (!strncasecmp (atom + 1, "efine", 5)) { if (!strcasecmp (atom + 6, "d")) return DEFINED; if (!atom [6]) return DEFINE; } break; case 'e': if (isascii (atom [1]) && tolower((unsigned char)atom[1]) == 'x') { if (!strcasecmp (atom + 2, "tract-int")) return EXTRACT_INT; if (!strcasecmp (atom + 2, "ists")) return EXISTS; if (!strcasecmp (atom + 2, "piry")) return EXPIRY; if (!strcasecmp (atom + 2, "pire")) return EXPIRE; if (!strcasecmp (atom + 2, "pired")) return TOKEN_EXPIRED; } if (!strcasecmp (atom + 1, "ncode-int")) return ENCODE_INT; if (!strcasecmp(atom + 1, "poch")) return EPOCH; if (!strcasecmp (atom + 1, "thernet")) return ETHERNET; if (!strcasecmp (atom + 1, "nds")) return ENDS; if (!strncasecmp (atom + 1, "ls", 2)) { if (!strcasecmp (atom + 3, "e")) return ELSE; if (!strcasecmp (atom + 3, "if")) return ELSIF; break; } if (!strcasecmp (atom + 1, "rror")) return ERROR; if (!strcasecmp (atom + 1, "val")) return EVAL; if (!strcasecmp (atom + 1, "ncapsulate")) return ENCAPSULATE; if (!strcasecmp(atom + 1, "xecute")) return EXECUTE; if (!strcasecmp(atom+1, "n")) { return EN; } break; case 'f': if (!strcasecmp (atom + 1, "atal")) return FATAL; if (!strcasecmp (atom + 1, "ilename")) return FILENAME; if (!strcasecmp (atom + 1, "ixed-address")) return FIXED_ADDR; if (!strcasecmp (atom + 1, "ixed-address6")) return FIXED_ADDR6; if (!strcasecmp (atom + 1, "ixed-prefix6")) return FIXED_PREFIX6; if (!strcasecmp (atom + 1, "ddi")) return TOKEN_FDDI; if (!strcasecmp (atom + 1, "ormerr")) return NS_FORMERR; if (!strcasecmp (atom + 1, "unction")) return FUNCTION; if (!strcasecmp (atom + 1, "ailover")) return FAILOVER; if (!strcasecmp (atom + 1, "ree")) return TOKEN_FREE; break; case 'g': if (!strncasecmp(atom + 1, "et", 2)) { if (!strcasecmp(atom + 3, "-lease-hostnames")) return GET_LEASE_HOSTNAMES; if (!strcasecmp(atom + 3, "hostbyname")) return GETHOSTBYNAME; if (!strcasecmp(atom + 3, "hostname")) return GETHOSTNAME; break; } if (!strcasecmp (atom + 1, "iaddr")) return GIADDR; if (!strcasecmp (atom + 1, "roup")) return GROUP; break; case 'h': if (!strcasecmp(atom + 1, "ash")) return HASH; if (!strcasecmp (atom + 1, "ba")) return HBA; if (!strcasecmp (atom + 1, "ost")) return HOST; if (!strcasecmp (atom + 1, "ost-decl-name")) return HOST_DECL_NAME; if (!strcasecmp(atom + 1, "ost-identifier")) return HOST_IDENTIFIER; if (!strcasecmp (atom + 1, "ardware")) return HARDWARE; if (!strcasecmp (atom + 1, "ostname")) return HOSTNAME; if (!strcasecmp (atom + 1, "elp")) return TOKEN_HELP; if (!strcasecmp (atom + 1, "ex")) { return TOKEN_HEX; } break; case 'i': if (!strcasecmp(atom+1, "a-na")) return IA_NA; if (!strcasecmp(atom+1, "a-ta")) return IA_TA; if (!strcasecmp(atom+1, "a-pd")) return IA_PD; if (!strcasecmp(atom+1, "aaddr")) return IAADDR; if (!strcasecmp(atom+1, "aprefix")) return IAPREFIX; if (!strcasecmp (atom + 1, "nclude")) return INCLUDE; if (!strcasecmp (atom + 1, "nteger")) return INTEGER; if (!strcasecmp (atom + 1, "nfiniband")) return TOKEN_INFINIBAND; if (!strcasecmp (atom + 1, "nfinite")) return INFINITE; if (!strcasecmp (atom + 1, "nfo")) return INFO; if (!strcasecmp (atom + 1, "p-address")) return IP_ADDRESS; if (!strcasecmp (atom + 1, "p6-address")) return IP6_ADDRESS; if (!strcasecmp (atom + 1, "nitial-interval")) return INITIAL_INTERVAL; if (!strcasecmp (atom + 1, "nitial-delay")) return INITIAL_DELAY; if (!strcasecmp (atom + 1, "nterface")) return INTERFACE; if (!strcasecmp (atom + 1, "dentifier")) return IDENTIFIER; if (!strcasecmp (atom + 1, "f")) return IF; if (!strcasecmp (atom + 1, "s")) return IS; if (!strcasecmp (atom + 1, "gnore")) return IGNORE; break; case 'k': if (!strncasecmp (atom + 1, "nown", 4)) { if (!strcasecmp (atom + 5, "-clients")) return KNOWN_CLIENTS; if (!atom[5]) return KNOWN; break; } if (!strcasecmp (atom + 1, "ey")) return KEY; if (!strcasecmp (atom + 1, "ey-algorithm")) return KEY_ALGORITHM; break; case 'l': if (!strcasecmp (atom + 1, "case")) return LCASE; if (!strcasecmp (atom + 1, "ease")) return LEASE; if (!strcasecmp(atom + 1, "ease6")) return LEASE6; if (!strcasecmp (atom + 1, "eased-address")) return LEASED_ADDRESS; if (!strcasecmp (atom + 1, "ease-time")) return LEASE_TIME; if (!strcasecmp(atom + 1, "easequery")) return LEASEQUERY; if (!strcasecmp(atom + 1, "ength")) return LENGTH; if (!strcasecmp (atom + 1, "imit")) return LIMIT; if (!strcasecmp (atom + 1, "et")) return LET; if (!strcasecmp (atom + 1, "oad")) return LOAD; if (!strcasecmp(atom + 1, "ocal")) return LOCAL; if (!strcasecmp (atom + 1, "og")) return LOG; if (!strcasecmp(atom+1, "lt")) { return LLT; } if (!strcasecmp(atom+1, "l")) { return LL; } if (!strcasecmp(atom+1, "ittle-endian")) { return TOKEN_LITTLE_ENDIAN; } if (!strcasecmp (atom + 1, "ease-id-format")) { return LEASE_ID_FORMAT; } break; case 'm': if (!strncasecmp (atom + 1, "ax", 2)) { if (!atom [3]) return TOKEN_MAX; if (!strcasecmp (atom + 3, "-balance")) return MAX_BALANCE; if (!strncasecmp (atom + 3, "-lease-", 7)) { if (!strcasecmp(atom + 10, "misbalance")) return MAX_LEASE_MISBALANCE; if (!strcasecmp(atom + 10, "ownership")) return MAX_LEASE_OWNERSHIP; if (!strcasecmp(atom + 10, "time")) return MAX_LEASE_TIME; } if (!strcasecmp(atom + 3, "-life")) return MAX_LIFE; if (!strcasecmp (atom + 3, "-transmit-idle")) return MAX_TRANSMIT_IDLE; if (!strcasecmp (atom + 3, "-response-delay")) return MAX_RESPONSE_DELAY; if (!strcasecmp (atom + 3, "-unacked-updates")) return MAX_UNACKED_UPDATES; } if (!strncasecmp (atom + 1, "in-", 3)) { if (!strcasecmp (atom + 4, "balance")) return MIN_BALANCE; if (!strcasecmp (atom + 4, "lease-time")) return MIN_LEASE_TIME; if (!strcasecmp (atom + 4, "secs")) return MIN_SECS; break; } if (!strncasecmp (atom + 1, "edi", 3)) { if (!strcasecmp (atom + 4, "a")) return MEDIA; if (!strcasecmp (atom + 4, "um")) return MEDIUM; break; } if (!strcasecmp (atom + 1, "atch")) return MATCH; if (!strcasecmp (atom + 1, "embers")) return MEMBERS; if (!strcasecmp (atom + 1, "y")) return MY; if (!strcasecmp (atom + 1, "clt")) return MCLT; break; case 'n': if (!strcasecmp (atom + 1, "ormal")) return NORMAL; if (!strcasecmp (atom + 1, "ameserver")) return NAMESERVER; if (!strcasecmp (atom + 1, "etmask")) return NETMASK; if (!strcasecmp (atom + 1, "ever")) return NEVER; if (!strcasecmp (atom + 1, "ext-server")) return NEXT_SERVER; if (!strcasecmp (atom + 1, "ot")) return TOKEN_NOT; if (!strcasecmp (atom + 1, "o")) return TOKEN_NO; if (!strcasecmp (atom + 1, "oerror")) return NS_NOERROR; if (!strcasecmp (atom + 1, "otauth")) return NS_NOTAUTH; if (!strcasecmp (atom + 1, "otimp")) return NS_NOTIMP; if (!strcasecmp (atom + 1, "otzone")) return NS_NOTZONE; if (!strcasecmp (atom + 1, "xdomain")) return NS_NXDOMAIN; if (!strcasecmp (atom + 1, "xrrset")) return NS_NXRRSET; if (!strcasecmp (atom + 1, "ull")) return TOKEN_NULL; if (!strcasecmp (atom + 1, "ext")) return TOKEN_NEXT; if (!strcasecmp (atom + 1, "ew")) return TOKEN_NEW; break; case 'o': if (!strcasecmp (atom + 1, "mapi")) return OMAPI; if (!strcasecmp (atom + 1, "r")) return OR; if (!strcasecmp (atom + 1, "n")) return ON; if (!strcasecmp (atom + 1, "pen")) return TOKEN_OPEN; if (!strcasecmp (atom + 1, "ption")) return OPTION; if (!strcasecmp (atom + 1, "ne-lease-per-client")) return ONE_LEASE_PER_CLIENT; if (!strcasecmp (atom + 1, "f")) return OF; if (!strcasecmp (atom + 1, "wner")) return OWNER; if (!strcasecmp (atom + 1, "ctal")) { return TOKEN_OCTAL; } break; case 'p': if (!strcasecmp (atom + 1, "arse-vendor-option")) return PARSE_VENDOR_OPT; if (!strcasecmp (atom + 1, "repend")) return PREPEND; if (!strcasecmp(atom + 1, "referred-life")) return PREFERRED_LIFE; if (!strcasecmp (atom + 1, "acket")) return PACKET; if (!strcasecmp (atom + 1, "ool")) return POOL; if (!strcasecmp (atom + 1, "ool6")) return POOL6; if (!strcasecmp (atom + 1, "refix6")) return PREFIX6; if (!strcasecmp (atom + 1, "seudo")) return PSEUDO; if (!strcasecmp (atom + 1, "eer")) return PEER; if (!strcasecmp (atom + 1, "rimary")) return PRIMARY; if (!strcasecmp (atom + 1, "rimary6")) return PRIMARY6; if (!strncasecmp (atom + 1, "artner", 6)) { if (!atom [7]) return PARTNER; if (!strcasecmp (atom + 7, "-down")) return PARTNER_DOWN; } if (!strcasecmp (atom + 1, "ort")) return PORT; if (!strcasecmp (atom + 1, "otential-conflict")) return POTENTIAL_CONFLICT; if (!strcasecmp (atom + 1, "ick-first-value") || !strcasecmp (atom + 1, "ick")) return PICK; if (!strcasecmp (atom + 1, "aused")) return PAUSED; break; case 'r': if (!strcasecmp(atom + 1, "ange")) return RANGE; if (!strcasecmp(atom + 1, "ange6")) return RANGE6; if (isascii(atom[1]) && (tolower((unsigned char)atom[1]) == 'e')) { if (!strcasecmp(atom + 2, "bind")) return REBIND; if (!strcasecmp(atom + 2, "boot")) return REBOOT; if (!strcasecmp(atom + 2, "contact-interval")) return RECONTACT_INTERVAL; if (!strncasecmp(atom + 2, "cover", 5)) { if (atom[7] == '\0') return RECOVER; if (!strcasecmp(atom + 7, "-done")) return RECOVER_DONE; if (!strcasecmp(atom + 7, "-wait")) return RECOVER_WAIT; break; } if (!strcasecmp(atom + 2, "fresh")) return REFRESH; if (!strcasecmp(atom + 2, "fused")) return NS_REFUSED; if (!strcasecmp(atom + 2, "ject")) return REJECT; if (!strcasecmp(atom + 2, "lease")) return RELEASE; if (!strcasecmp(atom + 2, "leased")) return TOKEN_RELEASED; if (!strcasecmp(atom + 2, "move")) return REMOVE; if (!strcasecmp(atom + 2, "new")) return RENEW; if (!strcasecmp(atom + 2, "quest")) return REQUEST; if (!strcasecmp(atom + 2, "quire")) return REQUIRE; if (isascii(atom[2]) && (tolower((unsigned char)atom[2]) == 's')) { if (!strcasecmp(atom + 3, "erved")) return TOKEN_RESERVED; if (!strcasecmp(atom + 3, "et")) return TOKEN_RESET; if (!strcasecmp(atom + 3, "olution-interrupted")) return RESOLUTION_INTERRUPTED; break; } if (!strcasecmp(atom + 2, "try")) return RETRY; if (!strcasecmp(atom + 2, "turn")) return RETURN; if (!strcasecmp(atom + 2, "verse")) return REVERSE; if (!strcasecmp(atom + 2, "wind")) return REWIND; break; } break; case 's': if (!strcasecmp(atom + 1, "cript")) return SCRIPT; if (isascii(atom[1]) && tolower((unsigned char)atom[1]) == 'e') { if (!strcasecmp(atom + 2, "arch")) return SEARCH; if (isascii(atom[2]) && tolower((unsigned char)atom[2]) == 'c') { if (!strncasecmp(atom + 3, "ond", 3)) { if (!strcasecmp(atom + 6, "ary")) return SECONDARY; if (!strcasecmp(atom + 6, "ary6")) return SECONDARY6; if (!strcasecmp(atom + 6, "s")) return SECONDS; break; } if (!strcasecmp(atom + 3, "ret")) return SECRET; break; } if (!strncasecmp(atom + 2, "lect", 4)) { if (atom[6] == '\0') return SELECT; if (!strcasecmp(atom + 6, "-timeout")) return SELECT_TIMEOUT; break; } if (!strcasecmp(atom + 2, "nd")) return SEND; if (!strncasecmp(atom + 2, "rv", 2)) { if (!strncasecmp(atom + 4, "er", 2)) { if (atom[6] == '\0') return TOKEN_SERVER; if (atom[6] == '-') { if (!strcasecmp(atom + 7, "duid")) return SERVER_DUID; if (!strcasecmp(atom + 7, "name")) return SERVER_NAME; if (!strcasecmp(atom + 7, "identifier")) return SERVER_IDENTIFIER; break; } break; } if (!strcasecmp(atom + 4, "fail")) return NS_SERVFAIL; break; } if (!strcasecmp(atom + 2, "t")) return TOKEN_SET; break; } if (isascii(atom[1]) && tolower((unsigned char)atom[1]) == 'h') { if (!strcasecmp(atom + 2, "ared-network")) return SHARED_NETWORK; if (!strcasecmp(atom + 2, "utdown")) return SHUTDOWN; break; } if (isascii(atom[1]) && tolower((unsigned char)atom[1]) == 'i') { if (!strcasecmp(atom + 2, "addr")) return SIADDR; if (!strcasecmp(atom + 2, "gned")) return SIGNED; if (!strcasecmp(atom + 2, "ze")) return SIZE; break; } if (isascii(atom[1]) && tolower((unsigned char)atom[1]) == 'p') { if (isascii(atom[2]) && tolower((unsigned char)atom[2]) == 'a') { if (!strcasecmp(atom + 3, "ce")) return SPACE; if (!strcasecmp(atom + 3, "wn")) return SPAWN; break; } if (!strcasecmp(atom + 2, "lit")) return SPLIT; break; } if (isascii(atom[1]) && tolower((unsigned char)atom[1]) == 't') { if (isascii(atom[2]) && tolower((unsigned char)atom[2]) == 'a') { if(!strncasecmp(atom + 3, "rt", 2)) { if (!strcasecmp(atom + 5, "s")) return STARTS; if (!strcasecmp(atom + 5, "up")) return STARTUP; break; } if (isascii(atom[3]) && tolower((unsigned char)atom[3]) == 't') { if (!strcasecmp(atom + 4, "e")) return STATE; if (!strcasecmp(atom + 4, "ic")) return STATIC; break; } } if (!strcasecmp(atom + 2, "ring")) return STRING_TOKEN; break; } if (!strncasecmp(atom + 1, "ub", 2)) { if (!strcasecmp(atom + 3, "class")) return SUBCLASS; if (!strcasecmp(atom + 3, "net")) return SUBNET; if (!strcasecmp(atom + 3, "net6")) return SUBNET6; if (!strcasecmp(atom + 3, "string")) return SUBSTRING; break; } if (isascii(atom[1]) && tolower((unsigned char)atom[1]) == 'u') { if (!strcasecmp(atom + 2, "ffix")) return SUFFIX; if (!strcasecmp(atom + 2, "persede")) return SUPERSEDE; } if (!strcasecmp(atom + 1, "witch")) return SWITCH; break; case 't': if (!strcasecmp (atom + 1, "imestamp")) return TIMESTAMP; if (!strcasecmp (atom + 1, "imeout")) return TIMEOUT; if (!strcasecmp (atom + 1, "oken-ring")) return TOKEN_RING; if (!strcasecmp (atom + 1, "ext")) return TEXT; if (!strcasecmp (atom + 1, "stp")) return TSTP; if (!strcasecmp (atom + 1, "sfp")) return TSFP; if (!strcasecmp (atom + 1, "ransmission")) return TRANSMISSION; if (!strcasecmp(atom + 1, "emporary")) return TEMPORARY; break; case 'u': if (!strcasecmp (atom + 1, "case")) return UCASE; if (!strcasecmp (atom + 1, "nset")) return UNSET; if (!strcasecmp (atom + 1, "nsigned")) return UNSIGNED; if (!strcasecmp (atom + 1, "id")) return UID; if (!strncasecmp (atom + 1, "se", 2)) { if (!strcasecmp (atom + 3, "r-class")) return USER_CLASS; if (!strcasecmp (atom + 3, "-host-decl-names")) return USE_HOST_DECL_NAMES; if (!strcasecmp (atom + 3, "-lease-addr-for-default-route")) return USE_LEASE_ADDR_FOR_DEFAULT_ROUTE; break; } if (!strncasecmp (atom + 1, "nknown", 6)) { if (!strcasecmp (atom + 7, "-clients")) return UNKNOWN_CLIENTS; if (!strcasecmp (atom + 7, "-state")) return UNKNOWN_STATE; if (!atom [7]) return UNKNOWN; break; } if (!strcasecmp (atom + 1, "nauthenticated")) return UNAUTHENTICATED; if (!strcasecmp (atom + 1, "pdate")) return UPDATE; break; case 'v': if (!strcasecmp (atom + 1, "6relay")) return V6RELAY; if (!strcasecmp (atom + 1, "6relopt")) return V6RELOPT; if (!strcasecmp (atom + 1, "endor-class")) return VENDOR_CLASS; if (!strcasecmp (atom + 1, "endor")) return VENDOR; break; case 'w': if (!strcasecmp (atom + 1, "ith")) return WITH; if (!strcasecmp(atom + 1, "idth")) return WIDTH; break; case 'y': if (!strcasecmp (atom + 1, "iaddr")) return YIADDR; if (!strcasecmp (atom + 1, "xdomain")) return NS_YXDOMAIN; if (!strcasecmp (atom + 1, "xrrset")) return NS_YXRRSET; break; case 'z': if (!strcasecmp (atom + 1, "erolen")) return ZEROLEN; if (!strcasecmp (atom + 1, "one")) return ZONE; break; } return dfv; } dhcp-4.4.1/common/ctrace.c000644 000765 000024 00000017224 13243301226 015616 0ustar00tmarkstaff000000 000000 /* trace.c Subroutines that support dhcp tracing... */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 2001-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #if defined (TRACING) void trace_interface_register (trace_type_t *ttype, struct interface_info *ip) { trace_interface_packet_t tipkt; if (trace_record ()) { memset (&tipkt, 0, sizeof tipkt); memcpy (&tipkt.hw_address, &ip -> hw_address, sizeof ip -> hw_address); if (ip->address_count) memcpy(&tipkt.primary_address, ip->addresses, sizeof(*ip->addresses)); memcpy (tipkt.name, ip -> name, sizeof ip -> name); tipkt.index = htonl (ip -> index); trace_write_packet (ttype, sizeof tipkt, (char *)&tipkt, MDL); } } void trace_interface_input (trace_type_t *ttype, unsigned len, char *buf) { trace_interface_packet_t *tipkt; struct interface_info *ip; struct sockaddr_in *sin; struct iaddr addr; isc_result_t status; if (len != sizeof *tipkt) { log_error ("trace interface packet size mismatch: %ld != %d", (long)(sizeof *tipkt), len); return; } tipkt = (trace_interface_packet_t *)buf; ip = (struct interface_info *)0; status = interface_allocate (&ip, MDL); if (status != ISC_R_SUCCESS) { foo: log_error ("trace_interface_input: %s.", isc_result_totext (status)); return; } ip -> ifp = dmalloc (sizeof *(ip -> ifp), MDL); if (!ip -> ifp) { interface_dereference (&ip, MDL); status = ISC_R_NOMEMORY; goto foo; } memcpy (&ip -> hw_address, &tipkt -> hw_address, sizeof ip -> hw_address); /* XXX: Without the full addresses state it's not quite a full * trace. */ ip->address_count = ip->address_max = 1; ip->addresses = dmalloc(sizeof(*ip->addresses), MDL); if (!ip->addresses) { dfree(ip->ifp, MDL); ip->ifp = NULL; interface_dereference (&ip, MDL); status = ISC_R_NOMEMORY; goto foo; } memcpy(ip->addresses, &tipkt->primary_address, sizeof(*ip->addresses)); memcpy (ip -> name, tipkt -> name, sizeof ip -> name); ip -> index = ntohl (tipkt -> index); interface_snorf (ip, 0); if (dhcp_interface_discovery_hook) (*dhcp_interface_discovery_hook) (ip); /* Fake up an ifp. */ memcpy (ip -> ifp -> ifr_name, ip -> name, sizeof ip -> name); #ifdef HAVE_SA_LEN ip -> ifp -> ifr_addr.sa_len = sizeof (struct sockaddr_in); #endif sin = (struct sockaddr_in *)&ip -> ifp -> ifr_addr; sin->sin_addr = ip->addresses[0]; addr.len = 4; memcpy (addr.iabuf, &sin -> sin_addr.s_addr, addr.len); if (dhcp_interface_setup_hook) (*dhcp_interface_setup_hook) (ip, &addr); interface_stash (ip); if (!quiet_interface_discovery) { log_info ("Listening on Trace/%s/%s%s%s", ip -> name, print_hw_addr (ip -> hw_address.hbuf [0], ip -> hw_address.hlen - 1, &ip -> hw_address.hbuf [1]), (ip -> shared_network ? "/" : ""), (ip -> shared_network ? ip -> shared_network -> name : "")); if (strcmp (ip -> name, "fallback")) { log_info ("Sending on Trace/%s/%s%s%s", ip -> name, print_hw_addr (ip -> hw_address.hbuf [0], ip -> hw_address.hlen - 1, &ip -> hw_address.hbuf [1]), (ip -> shared_network ? "/" : ""), (ip -> shared_network ? ip -> shared_network -> name : "")); } } interface_dereference (&ip, MDL); } void trace_interface_stop (trace_type_t *ttype) { /* XXX */ } void trace_inpacket_stash (struct interface_info *interface, struct dhcp_packet *packet, unsigned len, unsigned int from_port, struct iaddr from, struct hardware *hfrom) { trace_inpacket_t tip; trace_iov_t iov [2]; if (!trace_record ()) return; tip.from_port = from_port; tip.from = from; tip.from.len = htonl (tip.from.len); if (hfrom) { tip.hfrom = *hfrom; tip.havehfrom = 1; } else { memset (&tip.hfrom, 0, sizeof tip.hfrom); tip.havehfrom = 0; } tip.index = htonl (interface -> index); iov [0].buf = (char *)&tip; iov [0].len = sizeof tip; iov [1].buf = (char *)packet; iov [1].len = len; trace_write_packet_iov (inpacket_trace, 2, iov, MDL); } void trace_inpacket_input (trace_type_t *ttype, unsigned len, char *buf) { trace_inpacket_t *tip; int index; if (len < sizeof *tip) { log_error ("trace_input_packet: too short - %d", len); return; } tip = (trace_inpacket_t *)buf; index = ntohl (tip -> index); tip -> from.len = ntohl (tip -> from.len); if (index > interface_count || index < 0 || !interface_vector [index]) { log_error ("trace_input_packet: unknown interface index %d", index); return; } if (!bootp_packet_handler) { log_error ("trace_input_packet: no bootp packet handler."); return; } (*bootp_packet_handler) (interface_vector [index], (struct dhcp_packet *)(tip + 1), len - sizeof *tip, tip -> from_port, tip -> from, (tip -> havehfrom ? &tip -> hfrom : (struct hardware *)0)); } void trace_inpacket_stop (trace_type_t *ttype) { } ssize_t trace_packet_send (struct interface_info *interface, struct packet *packet, struct dhcp_packet *raw, size_t len, struct in_addr from, struct sockaddr_in *to, struct hardware *hto) { trace_outpacket_t tip; trace_iov_t iov [2]; if (trace_record ()) { if (hto) { tip.hto = *hto; tip.havehto = 1; } else { memset (&tip.hto, 0, sizeof tip.hto); tip.havehto = 0; } tip.from.len = 4; memcpy (tip.from.iabuf, &from, 4); tip.to.len = 4; memcpy (tip.to.iabuf, &to -> sin_addr, 4); tip.to_port = to -> sin_port; tip.index = htonl (interface -> index); iov [0].buf = (char *)&tip; iov [0].len = sizeof tip; iov [1].buf = (char *)raw; iov [1].len = len; trace_write_packet_iov (outpacket_trace, 2, iov, MDL); } if (!trace_playback ()) { return send_packet (interface, packet, raw, len, from, to, hto); } return len; } void trace_outpacket_input (trace_type_t *ttype, unsigned len, char *buf) { trace_outpacket_t *tip; int index; if (len < sizeof *tip) { log_error ("trace_input_packet: too short - %d", len); return; } tip = (trace_outpacket_t *)buf; index = ntohl (tip -> index); if (index > interface_count || index < 0 || !interface_vector [index]) { log_error ("trace_input_packet: unknown interface index %d", index); return; } /* XXX would be nice to somehow take notice of these. */ } void trace_outpacket_stop (trace_type_t *ttype) { } void trace_seed_stash (trace_type_t *ttype, unsigned seed) { u_int32_t outseed; if (!trace_record ()) return; outseed = htonl (seed); trace_write_packet (ttype, sizeof outseed, (char *)&outseed, MDL); return; } void trace_seed_input (trace_type_t *ttype, unsigned length, char *buf) { u_int32_t *seed; if (length != sizeof seed) { log_error ("trace_seed_input: wrong size (%d)", length); } seed = (u_int32_t *)buf; srandom (ntohl (*seed)); } void trace_seed_stop (trace_type_t *ttype) { } #endif /* TRACING */ dhcp-4.4.1/common/dhcp-eval.5000644 000765 000024 00000052003 13243301226 016134 0ustar00tmarkstaff000000 000000 .\" $Id: dhcp-eval.5,v 1.33 2012/05/17 15:50:14 sar Exp $ .\" .\" Copyright (c) 2009-2012,2014-2015 by Internet Systems Consortium, Inc. ("ISC") .\" Copyright (c) 2004,2007 by Internet Systems Consortium, Inc. ("ISC") .\" Copyright (c) 1996-2003 by Internet Software Consortium .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT .\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" Internet Systems Consortium, Inc. .\" 950 Charter Street .\" Redwood City, CA 94063 .\" .\" https://www.isc.org/ .\" .\" Support and other services are available for ISC products - see .\" https://www.isc.org for more information or to learn more about ISC. .\" .TH dhcp-eval 5 .SH NAME dhcp-eval - ISC DHCP conditional evaluation .SH DESCRIPTION The Internet Systems Consortium DHCP client and server both provide the ability to perform conditional behavior depending on the contents of packets they receive. The syntax for specifying this conditional behaviour is documented here. .SH REFERENCE: CONDITIONAL BEHAVIOUR Conditional behaviour may be specified using the if statement and the else or elsif statements or the switch and case statements. A conditional statement can appear anywhere that a regular statement (e.g., an option statement) can appear, and can enclose one or more such statements. .PP .B CONDITIONAL BEHAVIOUR: IF .PP A typical conditional if statement in a server might be: .PP .nf if option dhcp-user-class = "accounting" { max-lease-time 17600; option domain-name "accounting.example.org"; option domain-name-servers ns1.accounting.example.org, ns2.accounting.example.org; } elsif option dhcp-user-class = "sales" { max-lease-time 17600; option domain-name "sales.example.org"; option domain-name-servers ns1.sales.example.org, ns2.sales.example.org; } elsif option dhcp-user-class = "engineering" { max-lease-time 17600; option domain-name "engineering.example.org"; option domain-name-servers ns1.engineering.example.org, ns2.engineering.example.org; } else { max-lease-time 600; option domain-name "misc.example.org"; option domain-name-servers ns1.misc.example.org, ns2.misc.example.org; } .fi .PP On the client side, an example of conditional evaluation might be: .PP .nf # example.org filters DNS at its firewall, so we have to use their DNS # servers when we connect to their network. If we are not at # example.org, prefer our own DNS server. if not option domain-name = "example.org" { prepend domain-name-servers 127.0.0.1; } .fi .PP The .B if statement and the .B elsif continuation statement both take boolean expressions as their arguments. That is, they take expressions that, when evaluated, produce a boolean result. If the expression evaluates to true, then the statements enclosed in braces following the .B if statement are executed, and all subsequent .B elsif and .B else clauses are skipped. Otherwise, each subsequent .B elsif clause's expression is checked, until an elsif clause is encountered whose test evaluates to true. If such a clause is found, the statements in braces following it are executed, and then any subsequent .B elsif and .B else clauses are skipped. If all the .B if and .B elsif clauses are checked but none of their expressions evaluate true, then if there is an .B else clause, the statements enclosed in braces following the .B else are evaluated. Boolean expressions that evaluate to null are treated as false in conditionals. .PP .B CONDITIONAL BEHAVIOUR: SWITCH .PP The above example can be rewritten using a switch construct as well. .PP .nf switch (option dhcp-user-class) { case "accounting": max-lease-time 17600; option domain-name "accounting.example.org"; option domain-name-servers ns1.accounting.example.org, ns2.accounting.example.org; case "sales": max-lease-time 17600; option domain-name "sales.example.org"; option domain-name-servers ns1.sales.example.org, ns2.sales.example.org; break; case "engineering": max-lease-time 17600; option domain-name "engineering.example.org"; option domain-name-servers ns1.engineering.example.org, ns2.engineering.example.org; break; default: max-lease-time 600; option domain-name "misc.example.org"; option domain-name-servers ns1.misc.example.org, ns2.misc.example.org; break; } .fi .PP The .B switch statement and the .B case statements can both be data expressions or numeric expressions. Within a switch statement they all must be the same type. The server evaluates the expression from the switch statement and then it evaluates the expressions from the case statements until it finds a match. .PP If it finds a match it starts executing statements from that case until the next break statement. If it doesn't find a match it starts from the default statement and again proceeds to the next break statement. If there is no match and no default it does nothing. .PP .SH BOOLEAN EXPRESSIONS The following is the current list of boolean expressions that are supported by the DHCP distribution. .PP .I data-expression-1 \fB=\fR \fIdata-expression-2\fR .RS 0.25i .PP The \fB=\fR operator compares the values of two data expressions, returning true if they are the same, false if they are not. If either the left-hand side or the right-hand side are null, the result is also null. .RE .PP .I data-expression-1 \fB~=\fR \fIdata-expression-2\fR .I data-expression-1 \fB~~\fR \fIdata-expression-2\fR .RS 0.25i .PP The \fB~=\fR and \fB~~\fR operators (not available on all systems) perform extended regex(7) matching of the values of two data expressions, returning true if \fIdata-expression-1\fR matches against the regular expression evaluated by \fIdata-expression-2\fR, or false if it does not match or encounters some error. If either the left-hand side or the right-hand side are null or empty strings, the result is also false. The \fB~~\fR operator differs from the \fB~=\fR operator in that it is case-insensitive. .RE .PP .I boolean-expression-1 \fBand\fR \fIboolean-expression-2\fR .PP .RS 0.25i The \fBand\fR operator evaluates to true if the boolean expression on the left-hand side and the boolean expression on the right-hand side both evaluate to true. Otherwise, it evaluates to false. If either the expression on the left-hand side or the expression on the right-hand side are null, the result is null. .RE .PP .I boolean-expression-1 \fBor\fR \fIboolean-expression-2\fR .PP .RS 0.25i The \fBor\fR operator evaluates to true if either the boolean expression on the left-hand side or the boolean expression on the right-hand side evaluate to true. Otherwise, it evaluates to false. If either the expression on the left-hand side or the expression on the right-hand side are null, the result is null. .RE .PP .B not \fIboolean-expression .PP .RS 0.25i The \fBnot\fR operator evaluates to true if \fIboolean-expression\fR evaluates to false, and returns false if \fIboolean-expression\fR evaluates to true. If \fIboolean-expression\fR evaluates to null, the result is also null. .RE .PP .B exists \fIoption-name\fR .PP .RS 0.25i The \fBexists\fR expression returns true if the specified option exists in the incoming DHCP packet being processed. .RE .B known .PP .RS 0.25i The \fBknown\fR expression returns true if the client whose request is currently being processed is known - that is, if there's a host declaration for it. .RE .B static .PP .RS 0.25i The \fBstatic\fR expression returns true if the lease assigned to the client whose request is currently being processed is derived from a static address assignment. .RE .SH DATA EXPRESSIONS Several of the boolean expressions above depend on the results of evaluating data expressions. A list of these expressions is provided here. .PP .B substring (\fIdata-expr\fB, \fIoffset\fB, \fIlength\fB)\fR .PP .RS 0.25i The \fBsubstring\fR operator evaluates the data expression and returns the substring of the result of that evaluation that starts \fIoffset\fR bytes from the beginning, continuing for \fIlength\fR bytes. \fIOffset\fR and \fIlength\fR are both numeric expressions. If \fIdata-expr\fR, \fIoffset\fR or \fIlength\fR evaluate to null, then the result is also null. If \fIoffset\fR is greater than or equal to the length of the evaluated data, then a zero-length data string is returned. If \fIlength\fI is greater then the remaining length of the evaluated data after \fIoffset\fR, then a data string containing all data from \fIoffset\fR to the end of the evaluated data is returned. .RE .PP .B suffix (\fIdata-expr\fB, \fIlength\fB)\fR .PP .RS 0.25i The \fBsuffix\fR operator evaluates \fIdata-expr\fR and returns the last \fIlength\fR bytes of the result of that evaluation. \fILength\fR is a numeric expression. If \fIdata-expr\fR or \fIlength\fR evaluate to null, then the result is also null. If \fIsuffix\fR evaluates to a number greater than the length of the evaluated data, then the evaluated data is returned. .RE .PP .B lcase (\fIdata-expr\fB)\fR .PP .RS 0.25i The \fBlcase\fR function returns the result of evaluating \fIdata-expr\fR converted to lower case. If \fIdata-expr\fR evaluates to null, then the result is also null. .RE .PP .B ucase (\fIdata-expr\fB)\fR .PP .RS 0.25i The \fBucase\fR function returns the result of evaluating \fIdata-expr\fR converted to upper case. If \fIdata-expr\fR evaluates to null, then the result is also null. .RE .PP .B option \fIoption-name\fR .PP .RS 0.25i The \fBoption\fR operator returns the contents of the specified option in the packet to which the server is responding. .RE .PP .B config-option \fIoption-name\fR .PP .RS 0.25i The \fBconfig-option\fR operator returns the value for the specified option that the DHCP client or server has been configured to send. .RE .PP .B gethostname() .PP .RS 0.25i The \fBgethostname()\fR function returns a data string whose contents are a character string, the results of calling gethostname() on the local system with a size limit of 255 bytes (not including NULL terminator). This can be used for example to configure dhclient to send the local hostname without knowing the local hostname at the time dhclient.conf is written. .RE .PP .B hardware .PP .RS 0.25i The \fBhardware\fR operator returns a data string whose first element is the type of network interface indicated in packet being considered, and whose subsequent elements are client's link-layer address. If there is no packet, or if the RFC2131 \fIhlen\fR field is invalid, then the result is null. Hardware types include ethernet (1), token-ring (6), and fddi (8). Hardware types are specified by the IETF, and details on how the type numbers are defined can be found in RFC2131 (in the ISC DHCP distribution, this is included in the doc/ subdirectory). .RE .PP .B packet (\fIoffset\fB, \fIlength\fB)\fR .PP .RS 0.25i The \fBpacket\fR operator returns the specified portion of the packet being considered, or null in contexts where no packet is being considered. \fIOffset\fR and \fIlength\fR are applied to the contents packet as in the \fBsubstring\fR operator. .RE .PP .I string .PP .RS 0.25i A string, enclosed in quotes, may be specified as a data expression, and returns the text between the quotes, encoded in ASCII. The backslash ('\\') character is treated specially, as in C programming: '\\t' means TAB, '\\r' means carriage return, '\\n' means newline, and '\\b' means bell. Any octal value can be specified with '\\nnn', where nnn is any positive octal number less than 0400. Any hexadecimal value can be specified with '\\xnn', where nn is any positive hexadecimal number less than or equal to 0xff. .RE .PP .I colon-separated hexadecimal list .PP .RS 0.25i A list of hexadecimal octet values, separated by colons, may be specified as a data expression. .RE .PP .B concat (\fIdata-expr1\fB, ..., \fIdata-exprN\fB)\fR .RS 0.25i The expressions are evaluated, and the results of each evaluation are concatenated in the sequence that the subexpressions are listed. If any subexpression evaluates to null, the result of the concatenation is null. .RE .PP .B reverse (\fInumeric-expr1\fB, \fIdata-expr2\fB)\fR .RS 0.25i The two expressions are evaluated, and then the result of evaluating the data expression is reversed in place, using hunks of the size specified in the numeric expression. For example, if the numeric expression evaluates to four, and the data expression evaluates to twelve bytes of data, then the reverse expression will evaluate to twelve bytes of data, consisting of the last four bytes of the input data, followed by the middle four bytes, followed by the first four bytes. .RE .PP .B leased-address .RS 0.25i In any context where the client whose request is being processed has been assigned an IP address, this data expression returns that IP address. In any context where the client whose request is being processed has not been assigned an ip address, if this data expression is found in executable statements executed on that client's behalf, a log message indicating "there is no lease associated with this client" is syslogged to the debug level (this is considered dhcpd.conf debugging information). .RE .PP .B binary-to-ascii (\fInumeric-expr1\fB, \fInumeric-expr2\fB, .B \fIdata-expr1\fB,\fR \fIdata-expr2\fB)\fR .RS 0.25i Converts the result of evaluating data-expr2 into a text string containing one number for each element of the result of evaluating data-expr2. Each number is separated from the other by the result of evaluating data-expr1. The result of evaluating numeric-expr1 specifies the base (2 through 16) into which the numbers should be converted. The result of evaluating numeric-expr2 specifies the width in bits of each number, which may be either 8, 16 or 32. .PP As an example of the preceding three types of expressions, to produce the name of a PTR record for the IP address being assigned to a client, one could write the following expression: .RE .PP .nf concat (binary-to-ascii (10, 8, ".", reverse (1, leased-address)), ".in-addr.arpa."); .fi .RE .PP .B encode-int (\fInumeric-expr\fB, \fIwidth\fB)\fR .RS 0.25i Numeric-expr is evaluated and encoded as a data string of the specified width, in network byte order (most significant byte first). If the numeric expression evaluates to the null value, the result is also null. .RE .PP .B pick-first-value (\fIdata-expr1\fR [ ... \fIexpr\fRn ] \fB)\fR .RS 0.25i The pick-first-value function takes any number of data expressions as its arguments. Each expression is evaluated, starting with the first in the list, until an expression is found that does not evaluate to a null value. That expression is returned, and none of the subsequent expressions are evaluated. If all expressions evaluate to a null value, the null value is returned. .RE .PP .B host-decl-name .RS 0.25i The host-decl-name function returns the name of the host declaration that matched the client whose request is currently being processed, if any. If no host declaration matched, the result is the null value. .RE .SH NUMERIC EXPRESSIONS Numeric expressions are expressions that evaluate to an integer. In general, the maximum size of such an integer should not be assumed to be representable in fewer than 32 bits, but the precision of such integers may be more than 32 bits. .PP In addition to the following operators several standard math functions are available. They are: .nf operation symbol add \fB+\fR subtract \fB-\fR divide \fB/\fR multiply \fB*\fR modulus \fB%\fR bitwise and \fB&\fR bitwise or \fB|\fR bitwise xor \fB^\fR .fi .PP .B extract-int (\fIdata-expr\fB, \fIwidth\fB)\fR .PP .RS 0.25i The \fBextract-int\fR operator extracts an integer value in network byte order from the result of evaluating the specified data expression. Width is the width in bits of the integer to extract. Currently, the only supported widths are 8, 16 and 32. If the evaluation of the data expression doesn't provide sufficient bits to extract an integer of the specified size, the null value is returned. .RE .PP .B lease-time .PP .RS 0.25i The duration of the current lease - that is, the difference between the current time and the time that the lease expires. .RE .PP .I number .PP .RS 0.25i Any number between zero and the maximum representable size may be specified as a numeric expression. .RE .PP .B client-state .PP .RS 0.25i The current state of the client instance being processed. This is only useful in DHCP client configuration files. Possible values are: .TP 2 .I \(bu Booting - DHCP client is in the INIT state, and does not yet have an IP address. The next message transmitted will be a DHCPDISCOVER, which will be broadcast. .TP .I \(bu Reboot - DHCP client is in the INIT-REBOOT state. It has an IP address, but is not yet using it. The next message to be transmitted will be a DHCPREQUEST, which will be broadcast. If no response is heard, the client will bind to its address and move to the BOUND state. .TP .I \(bu Select - DHCP client is in the SELECTING state - it has received at least one DHCPOFFER message, but is waiting to see if it may receive other DHCPOFFER messages from other servers. No messages are sent in the SELECTING state. .TP .I \(bu Request - DHCP client is in the REQUESTING state - it has received at least one DHCPOFFER message, and has chosen which one it will request. The next message to be sent will be a DHCPREQUEST message, which will be broadcast. .TP .I \(bu Bound - DHCP client is in the BOUND state - it has an IP address. No messages are transmitted in this state. .TP .I \(bu Renew - DHCP client is in the RENEWING state - it has an IP address, and is trying to contact the server to renew it. The next message to be sent will be a DHCPREQUEST message, which will be unicast directly to the server. .TP .I \(bu Rebind - DHCP client is in the REBINDING state - it has an IP address, and is trying to contact any server to renew it. The next message to be sent will be a DHCPREQUEST, which will be broadcast. .RE .SH REFERENCE: ACTION EXPRESSIONS .PP .B log (\fIpriority\fB, \fIdata-expr\fB)\fR .RS 0.25i .PP Logging statements may be used to send information to the standard logging channels. A logging statement includes an optional priority (\fBfatal\fR, \fBerror\fR, \fBinfo\fR, or \fBdebug\fR), and a data expression. .PP Logging statements take only a single data expression argument, so if you want to output multiple data values, you will need to use the \fBconcat\fR operator to concatenate them. .RE .PP .B execute (\fIcommand-path\fB [, \fIdata-expr1\fB, ... \fIdata-exprN\fB]);\fR .RS 0.25i .PP The \fBexecute\fR statement runs an external command. The first argument is a string literal containing the name or path of the command to run. The other arguments, if present, are either string literals or data- expressions which evaluate to text strings, to be passed as command-line arguments to the command. .PP \fBexecute\fR is synchronous; the program will block until the external command being run has finished. Please note that lengthy program execution (for example, in an "on commit" in dhcpd.conf) may result in bad performance and timeouts. Only external applications with very short execution times are suitable for use. .PP Passing user-supplied data to an external application might be dangerous. Make sure the external application checks input buffers for validity. Non-printable ASCII characters will be converted into dhcpd.conf language octal escapes ("\\nnn"), make sure your external command handles them as such. .PP It is possible to use the execute statement in any context, not only on events. If you put it in a regular scope in the configuration file you will execute that command every time a scope is evaluated. .RE .PP .B parse-vendor-option;\fR .RS 0.25i .PP The \fBparse-vendor-option\fR statement attempts to parse a vendor option (code 43). It is only useful while processing a packet on the server and requires that the administrator has already used the \fBvendor-option-space\fR statement to select a valid vendor space. .PP This functionality may be used if the server needs to take different actions depending on the values the client placed in the vendor option and the sub-options are not at fixed locations. It is handled as an action to allow an administrator to examine the incoming options and choose the correct vendor space. .RE .SH REFERENCE: DYNAMIC DNS UPDATES .PP See the dhcpd.conf and dhclient.conf man pages for more information about DDNS. .SH SEE ALSO dhcpd.conf(5), dhcpd.leases(5), dhclient.conf(5), dhcp-options(5), dhcpd(8), dhclient(8), RFC2132, RFC2131. .SH AUTHOR Information about Internet Systems Consortium can be found at .B https://www.isc.org. dhcp-4.4.1/common/dhcp-options.5000644 000765 000024 00000241614 13243301226 016710 0ustar00tmarkstaff000000 000000 .\" $Id: dhcp-options.5,v 1.50 2011/05/20 13:48:32 tomasz Exp $ .\" .\" Copyright (c) 2004-2018 by Internet Systems Consortium, Inc. ("ISC") .\" Copyright (c) 1996-2003 by Internet Software Consortium .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT .\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" Internet Systems Consortium, Inc. .\" 950 Charter Street .\" Redwood City, CA 94063 .\" .\" https://www.isc.org/ .\" .\" Support and other services are available for ISC products - see .\" https://www.isc.org for more information or to learn more about ISC. .\" .TH dhcp-options 5 .SH NAME dhcp-options - Dynamic Host Configuration Protocol options .SH DESCRIPTION The Dynamic Host Configuration protocol allows the client to receive .B options from the DHCP server describing the network configuration and various services that are available on the network. When configuring .B dhcpd(8) or .B dhclient(8) , options must often be declared. The syntax for declaring options, and the names and formats of the options that can be declared, are documented here. .SH REFERENCE: OPTION STATEMENTS .PP DHCP \fIoption\fR statements always start with the \fIoption\fR keyword, followed by an option name, followed by option data. The option names and data formats are described below. It is not necessary to exhaustively specify all DHCP options - only those options which are needed by clients must be specified. .PP Option data comes in a variety of formats, as defined below: .PP The .B ip-address data type can be entered either as an explicit IP address (e.g., 239.254.197.10) or as a domain name (e.g., haagen.isc.org). When entering a domain name, be sure that that domain name resolves to a single IP address. .PP The .B ip6-address data specifies an IPv6 address, like ::1 or 3ffe:bbbb:aaaa:aaaa::1. .PP The .B int32 data type specifies a signed 32-bit integer. The .B uint32 data type specifies an unsigned 32-bit integer. The .B int16 and .B uint16 data types specify signed and unsigned 16-bit integers. The .B int8 and .B uint8 data types specify signed and unsigned 8-bit integers. Unsigned 8-bit integers are also sometimes referred to as octets. .PP The .B text data type specifies an NVT ASCII string, which must be enclosed in double quotes - for example, to specify a root-path option, the syntax would be .nf .sp 1 option root-path "10.0.1.4:/var/tmp/rootfs"; .fi .PP The .B domain-name data type specifies a domain name, which must not be enclosed in double quotes. The domain name is stored just as if it were a text option. .PP The .B domain-list data type specifies a list of domain names, enclosed in double quotes and separated by commas ("example.com", "foo.example.com"). .PP The .B flag data type specifies a boolean value. Booleans can be either true or false (or on or off, if that makes more sense to you). .PP The .B string data type specifies either an NVT ASCII string enclosed in double quotes, or a series of octets specified in hexadecimal, separated by colons. For example: .nf .sp 1 option dhcp-client-identifier "CLIENT-FOO"; or option dhcp-client-identifier 43:4c:49:45:54:2d:46:4f:4f; .fi .SH SETTING OPTION VALUES USING EXPRESSIONS Sometimes it's helpful to be able to set the value of a DHCP option based on some value that the client has sent. To do this, you can use expression evaluation. The .B dhcp-eval(5) manual page describes how to write expressions. To assign the result of an evaluation to an option, define the option as follows: .nf .sp 1 \fBoption \fImy-option \fB= \fIexpression \fB;\fR .fi .PP For example: .nf .sp 1 option hostname = binary-to-ascii (16, 8, "-", substring (hardware, 1, 6)); .fi .SH INCLUDING OPTION DEFINITIONS Starting with 4.3.0 when ISC adds new option definitions those definitions will be included in the code based on the definition of an argument for the RFC that defines the option in includes/site.h. This provides you with a method for over-riding the ISC definitions if necessary - for example if you have previously defined the option with a different format using the mechanism from DEFINING NEW OPTIONS below. .PP By default all of the options are enabled. In order to disable an option you would edit the includes/site.h file and comment out the definition for the proper RFC. .SH STANDARD DHCPV4 OPTIONS The documentation for the various options mentioned below is taken from the latest IETF draft document on DHCP options. Options not listed below may not yet be implemented, but it is possible to use such options by defining them in the configuration file. Please see the DEFINING NEW OPTIONS heading later in this document for more information. .PP Some of the options documented here are automatically generated by the DHCP server or by clients, and cannot be configured by the user. The value of such an option can be used in the configuration file of the receiving DHCP protocol agent (server or client), for example in conditional expressions. However, the value of the option cannot be used in the configuration file of the sending agent, because the value is determined only \fIafter\fR the configuration file has been processed. In the following documentation, such options will be shown as "not user configurable" .PP The standard options are: .PP .B option \fBall-subnets-local\fR \fIflag\fR\fB;\fR .RS 0.25i .PP This option specifies whether or not the client may assume that all subnets of the IP network to which the client is connected use the same MTU as the subnet of that network to which the client is directly connected. A value of true indicates that all subnets share the same MTU. A value of false means that the client should assume that some subnets of the directly connected network may have smaller MTUs. .RE .PP .B option \fBarp-cache-timeout\fR \fIuint32\fR\fB;\fR .RS 0.25i .PP This option specifies the timeout in seconds for ARP cache entries. .RE .PP .B option \fBassociated-ip\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP This option is part of lease query. It is used to return all of the IP addresses associated with a given DHCP client. .PP This option is not user configurable. .RE .PP .B option \fBbcms-controller-address\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP This option configures a list of IPv4 addresses for use as Broadcast and Multicast Controller Servers ("BCMS"). .RE .PP .B option \fBbcms-controller-names\fR \fIdomain-list\fR\fB;\fR .RS 0.25i .PP This option contains the domain names of local Broadcast and Multicast Controller Servers ("BCMS") controllers which the client may use. .RE .PP .B option \fBbootfile-name\fR \fItext\fR\fB;\fR .RS 0.25i .PP This option is used to identify a bootstrap file. If supported by the client, it should have the same effect as the \fBfilename\fR declaration. BOOTP clients are unlikely to support this option. Some DHCP clients will support it, and others actually require it. .RE .PP .B option \fBboot-size\fR \fIuint16\fR\fB;\fR .RS 0.25i .PP This option specifies the length in 512-octet blocks of the default boot image for the client. .RE .PP .B option \fBbroadcast-address\fR \fIip-address\fR\fB;\fR .RS 0.25i .PP This option specifies the broadcast address in use on the client's subnet. Legal values for broadcast addresses are specified in section 3.2.1.3 of STD 3 (RFC1122). .RE .PP .B option .B capwap-ac-v4 .I ip-address \fR[\fB, .I ip-address \fR... ] .B ; .RS 0.25i .PP A list of IPv4 addresses of CAPWAP ACs that the WTP may use. The addresses are listed in preference order. .PP This option is included based on RFC 5417. .RE .PP .B option \fBclient-last-transaction-time\fR \fIuint32\fR\fB;\fR .RS 0.25i .PP This option is part of lease query. It allows the receiver to determine the time of the most recent access by the client. The value is a duration in seconds from when the client last communicated with the DHCP server. .PP This option is not user configurable. .RE .PP .B option \fBcookie-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP The cookie server option specifies a list of RFC 865 cookie servers available to the client. Servers should be listed in order of preference. .RE .PP .B option \fBdefault-ip-ttl\fR \fIuint8;\fR .RS 0.25i .PP This option specifies the default time-to-live that the client should use on outgoing datagrams. .RE .PP .B option \fBdefault-tcp-ttl\fR \fIuint8\fR\fB;\fR .RS 0.25i .PP This option specifies the default TTL that the client should use when sending TCP segments. The minimum value is 1. .RE .PP .B option \fBdefault-url\fR \fIstring\fR\fB;\fR .RS 0.25i .PP The format and meaning of this option is not described in any standards document, but is claimed to be in use by Apple Computer. It is not known what clients may reasonably do if supplied with this option. Use at your own risk. .RE .PP .B option \fBdhcp-client-identifier\fR \fIstring\fR\fB;\fR .RS 0.25i .PP This option can be used to specify a DHCP client identifier in a host declaration, so that dhcpd can find the host record by matching against the client identifier. .PP Please be aware that some DHCP clients, when configured with client identifiers that are ASCII text, will prepend a zero to the ASCII text. So you may need to write: .nf option dhcp-client-identifier "\\0foo"; rather than: option dhcp-client-identifier "foo"; .fi .RE .PP .B option \fBdhcp-lease-time\fR \fIuint32\fR\fB;\fR .RS 0.25i .PP This option is used in a client request (DHCPDISCOVER or DHCPREQUEST) to allow the client to request a lease time for the IP address. In a server reply (DHCPOFFER), a DHCP server uses this option to specify the lease time it is willing to offer. .PP This option is not directly user configurable in the server; refer to the \fImax-lease-time\fR and \fIdefault-lease-time\fR server options in .B dhcpd.conf(5). .RE .PP .B option \fBdhcp-max-message-size\fR \fIuint16\fR\fB;\fR .RS 0.25i .PP This option, when sent by the client, specifies the maximum size of any response that the server sends to the client. When specified on the server, if the client did not send a dhcp-max-message-size option, the size specified on the server is used. This works for BOOTP as well as DHCP responses. .RE .PP .B option \fBdhcp-message\fR \fItext\fR\fB;\fR .RS 0.25i .PP This option is used by a DHCP server to provide an error message to a DHCP client in a DHCPNAK message in the event of a failure. A client may use this option in a DHCPDECLINE message to indicate why the client declined the offered parameters. .PP This option is not user configurable. .RE .PP .B option \fBdhcp-message-type\fR \fIuint8\fR\fB;\fR .RS 0.25i .PP This option, sent by both client and server, specifies the type of DHCP message contained in the DHCP packet. Possible values (taken directly from RFC2132) are: .PP .nf 1 DHCPDISCOVER 2 DHCPOFFER 3 DHCPREQUEST 4 DHCPDECLINE 5 DHCPACK 6 DHCPNAK 7 DHCPRELEASE 8 DHCPINFORM .fi .PP This option is not user configurable. .PP .RE .B option \fBdhcp-option-overload\fR \fIuint8\fR\fB;\fR .RS 0.25i .PP This option is used to indicate that the DHCP \'sname\' or \'file\' fields are being overloaded by using them to carry DHCP options. A DHCP server inserts this option if the returned parameters will exceed the usual space allotted for options. .PP If this option is present, the client interprets the specified additional fields after it concludes interpretation of the standard option fields. .PP Legal values for this option are: .PP .nf 1 the \'file\' field is used to hold options 2 the \'sname\' field is used to hold options 3 both fields are used to hold options .fi .PP This option is not user configurable. .PP .RE .PP .B option \fBdhcp-parameter-request-list\fR \fIuint8\fR [\fB,\fR \fIuint8\fR... ]\fB;\fR .RS 0.25i .PP This option, when sent by the client, specifies which options the client wishes the server to return. Normally, in the ISC DHCP client, this is done using the \fIrequest\fR statement. If this option is not specified by the client, the DHCP server will normally return every option that is valid in scope and that fits into the reply. When this option is specified on the server, the server returns the specified options. This can be used to force a client to take options that it hasn't requested, and it can also be used to tailor the response of the DHCP server for clients that may need a more limited set of options than those the server would normally return. .RE .PP .B option \fBdhcp-rebinding-time\fR \fIuint32\fR\fB;\fR .RS 0.25i .PP This option specifies the number of seconds from the time a client gets an address until the client transitions to the REBINDING state. .PP This option is user configurable, but it will be ignored if the value is greater than or equal to the lease time. .PP To make DHCPv4+DHCPv6 migration easier in the future, any value configured in this option is also used as a DHCPv6 "T1" (renew) time. .PP .RE .PP .B option \fBdhcp-renewal-time\fR \fIuint32\fR\fB;\fR .RS 0.25i .PP This option specifies the number of seconds from the time a client gets an address until the client transitions to the RENEWING state. .PP This option is user configurable, but it will be ignored if the value is greater than or equal to the rebinding time, or lease time. .PP To make DHCPv4+DHCPv6 migration easier in the future, any value configured in this option is also used as a DHCPv6 "T2" (rebind) time. .PP .RE .PP .B option \fBdhcp-requested-address\fR \fIip-address\fR\fB;\fR .RS 0.25i .PP This option is used by the client in a DHCPDISCOVER to request that a particular IP address be assigned. .PP This option is not user configurable. .PP .RE .PP .B option \fBdhcp-server-identifier\fR \fIip-address\fR\fB;\fR .RS 0.25i .PP This option is used in DHCPOFFER and DHCPREQUEST messages, and may optionally be included in the DHCPACK and DHCPNAK messages. DHCP servers include this option in the DHCPOFFER in order to allow the client to distinguish between lease offers. DHCP clients use the contents of the \'server identifier\' field as the destination address for any DHCP messages unicast to the DHCP server. DHCP clients also indicate which of several lease offers is being accepted by including this option in a DHCPREQUEST message. .PP The value of this option is the IP address of the server. .PP This option is not directly user configurable. See the \fIserver-identifier\fR server option in .B \fIdhcpd.conf(5). .PP .RE .PP .B option \fBdomain-name\fR \fItext\fR\fB;\fR .RS 0.25i .PP This option specifies the domain name that client should use when resolving hostnames via the Domain Name System. .RE .PP .B option \fBdomain-name-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP The domain-name-servers option specifies a list of Domain Name System (STD 13, RFC 1035) name servers available to the client. Servers should be listed in order of preference. .RE .PP .B option \fBdomain-search\fR \fIdomain-list\fR\fB;\fR .RS 0.25i .PP The domain-search option specifies a \'search list\' of Domain Names to be used by the client to locate not-fully-qualified domain names. The difference between this option and historic use of the domain-name option for the same ends is that this option is encoded in RFC1035 compressed labels on the wire. For example: .nf .sp 1 option domain-search "example.com", "sales.example.com", "eng.example.com"; .fi .RE .PP .B option \fBextensions-path\fR \fItext\fR\fB;\fR .RS 0.25i .PP This option specifies the name of a file containing additional options to be interpreted according to the DHCP option format as specified in RFC2132. .RE .PP .B option \fBfinger-server\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP The Finger server option specifies a list of Finger servers available to the client. Servers should be listed in order of preference. .RE .PP .B option \fBfont-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP This option specifies a list of X Window System Font servers available to the client. Servers should be listed in order of preference. .RE .PP .B option \fBgeoconf-civic\fR \fIstring\fR\fB;\fR .RS 0.25i .PP A string to hold the geoconf civic structure. .PP This option is included based on RFC 4776. .RE .PP .B option \fBhost-name\fR \fIstring\fR\fB;\fR .RS 0.25i .PP This option specifies the name of the client. The name may or may not be qualified with the local domain name (it is preferable to use the domain-name option to specify the domain name). See RFC 1035 for character set restrictions. This option is only honored by .B dhclient-script(8) if the hostname for the client machine is not set. .RE .PP .B option \fBieee802-3-encapsulation\fR \fIflag\fR\fB;\fR .RS 0.25i .PP This option specifies whether or not the client should use Ethernet Version 2 (RFC 894) or IEEE 802.3 (RFC 1042) encapsulation if the interface is an Ethernet. A value of false indicates that the client should use RFC 894 encapsulation. A value of true means that the client should use RFC 1042 encapsulation. .RE .PP .B option \fBien116-name-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]; .RS 0.25i .PP The ien116-name-servers option specifies a list of IEN 116 name servers available to the client. Servers should be listed in order of preference. .RE .PP .B option \fBimpress-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP The impress-server option specifies a list of Imagen Impress servers available to the client. Servers should be listed in order of preference. .RE .PP .B option \fBinterface-mtu\fR \fIuint16\fR\fB;\fR .RS 0.25i .PP This option specifies the MTU to use on this interface. The minimum legal value for the MTU is 68. .RE .PP .B option \fBip-forwarding\fR \fIflag\fR\fB;\fR .RS 0.25i .PP This option specifies whether the client should configure its IP layer for packet forwarding. A value of false means disable IP forwarding, and a value of true means enable IP forwarding. .RE .PP .B option \fBirc-server\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP The IRC server option specifies a list of IRC servers available to the client. Servers should be listed in order of preference. .RE .PP .B option \fBloader-configfile\fR \fItext\fR .RS 0.25i .PP This option is used to specify a boot loading configuration file a PXE client should use. .PP This option is included based on RFC 5071. .RE .PP .B option \fBloader-pathprefix\fR \fItext\fR .RS 0.25i .PP This option is used to specify a path prefix a PXE client should use in conjunction with the boot load configuration file. .PP This option is included based on RFC 5071. .RE .PP .B option \fBloader-reboottime\fR \fIuint32\fR .RS 0.25i .PP This option is used to dictate the maximum amount of time a PXE client should allow itself to achieve configured network resources before rebooting. .PP This option is included based on RFC 5071. .RE .PP .B option \fBlog-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP The log-server option specifies a list of MIT-LCS UDP log servers available to the client. Servers should be listed in order of preference. .RE .PP .B option \fBlpr-servers\fR \fIip-address \fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP The LPR server option specifies a list of RFC 1179 line printer servers available to the client. Servers should be listed in order of preference. .RE .PP .B option \fBmask-supplier\fR \fIflag\fR\fB;\fR .RS 0.25i .PP This option specifies whether or not the client should respond to subnet mask requests using ICMP. A value of false indicates that the client should not respond. A value of true means that the client should respond. .RE .PP .B option \fBmax-dgram-reassembly\fR \fIuint16\fR\fB;\fR .RS 0.25i .PP This option specifies the maximum size datagram that the client should be prepared to reassemble. The minimum legal value is 576. .RE .PP .B option \fBmerit-dump\fR \fItext\fR\fB;\fR .RS 0.25i .PP This option specifies the path-name of a file to which the client's core image should be dumped in the event the client crashes. The path is formatted as a character string consisting of characters from the NVT ASCII character set. .RE .PP .B option \fBmobile-ip-home-agent\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP This option specifies a list of IP addresses indicating mobile IP home agents available to the client. Agents should be listed in order of preference, although normally there will be only one such agent. .RE .PP .B option \fBname-service-search\fR \fIuint16\fR [\fB,\fR \fIuint6\fR... ]\fB;\fR .RS 0.25i .PP This option specifies a list of name services in the order the client should attempt to use them. .PP This option is included based on RFC 2937. .RE .PP .B option \fBnds-context\fR \fIstring\fR\fB;\fR .RS 0.25i .PP The nds-context option specifies the name of the initial Netware Directory Service for an NDS client. .RE .PP .B option \fBnds-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP The nds-servers option specifies a list of IP addresses of NDS servers. .RE .PP .B option \fBnds-tree-name\fR \fIstring\fR\fB;\fR .RS 0.25i .PP The nds-tree-name option specifies NDS tree name that the NDS client should use. .RE .PP .B option \fBnetbios-dd-server\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP The NetBIOS datagram distribution server (NBDD) option specifies a list of RFC 1001/1002 NBDD servers listed in order of preference. .RE .PP .B option \fBnetbios-name-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...]\fB;\fR .RS 0.25i .PP The NetBIOS name server (NBNS) option specifies a list of RFC 1001/1002 NBNS name servers listed in order of preference. NetBIOS Name Service is currently more commonly referred to as WINS. WINS servers can be specified using the netbios-name-servers option. .RE .PP .B option \fBnetbios-node-type\fR \fIuint8\fR\fB;\fR .RS 0.25i .PP The NetBIOS node type option allows NetBIOS over TCP/IP clients which are configurable to be configured as described in RFC 1001/1002. The value is specified as a single octet which identifies the client type. .PP Possible node types are: .PP .TP 5 .I 1 B-node: Broadcast - no WINS .TP .I 2 P-node: Peer - WINS only .TP .I 4 M-node: Mixed - broadcast, then WINS .TP .I 8 H-node: Hybrid - WINS, then broadcast .RE .PP .B option \fBnetbios-scope\fR \fIstring\fR\fB;\fR .RS 0.25i .PP The NetBIOS scope option specifies the NetBIOS over TCP/IP scope parameter for the client as specified in RFC 1001/1002. See RFC1001, RFC1002, and RFC1035 for character-set restrictions. .RE .PP .B option \fBnetinfo-server-address\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP The \fBnetinfo-server-address\fR option has not been described in any RFC, but has been allocated (and is claimed to be in use) by Apple Computers. It's hard to say if the above is the correct format, or what clients might be expected to do if values were configured. Use at your own risk. .RE .PP .B option \fBnetinfo-server-tag\fR \fItext\fR\fB;\fR .RS 0.25i .PP The \fBnetinfo-server-tag\fR option has not been described in any RFC, but has been allocated (and is claimed to be in use) by Apple Computers. It's hard to say if the above is the correct format, or what clients might be expected to do if values were configured. Use at your own risk. .RE .PP .B option \fBnis-domain\fR \fItext\fR\fB;\fR .RS 0.25i .PP This option specifies the name of the client's NIS (Sun Network Information Services) domain. The domain is formatted as a character string consisting of characters from the NVT ASCII character set. .RE .PP .B option \fBnis-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP This option specifies a list of IP addresses indicating NIS servers available to the client. Servers should be listed in order of preference. .RE .PP .B option \fBnisplus-domain\fR \fItext\fR\fB;\fR .RS 0.25i .PP This option specifies the name of the client's NIS+ domain. The domain is formatted as a character string consisting of characters from the NVT ASCII character set. .RE .PP .B option \fBnisplus-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP This option specifies a list of IP addresses indicating NIS+ servers available to the client. Servers should be listed in order of preference. .RE .PP .B option \fBnntp-server\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP The NNTP server option specifies a list of NNTP servers available to the client. Servers should be listed in order of preference. .RE .PP .B option \fBnon-local-source-routing\fR \fIflag\fR\fB;\fR .RS 0.25i .PP This option specifies whether the client should configure its IP layer to allow forwarding of datagrams with non-local source routes (see Section 3.3.5 of [4] for a discussion of this topic). A value of false means disallow forwarding of such datagrams, and a value of true means allow forwarding. .RE .PP .B option \fBntp-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP This option specifies a list of IP addresses indicating NTP (RFC 5905) servers available to the client. Servers should be listed in order of preference. .RE .PP .B option \fBnwip-domain\fR \fIstring\fR\fB;\fR .RS 0.25i .PP The name of the NetWare/IP domain that a NetWare/IP client should use. .RE .PP .B option \fBnwip-suboptions\fR \fIstring\fR\fB;\fR .RS 0.25i .PP A sequence of suboptions for NetWare/IP clients - see RFC2242 for details. Normally this option is set by specifying specific NetWare/IP suboptions - see the NETWARE/IP SUBOPTIONS section for more information. .RE .PP .B option \fBpxe-system-type\fR \fIuint16\fR [\fB, \fIuint16\fR ... ]\fB;\fR .RS 0.25i .PP A list of one ore more 16-bit integers which allows a client to specify its pre-boot architecture type(s). .PP This option is included based on RFC 4578. .RE .PP .B option \fBpxe-interface-id\fR \fIuint8\fR \fIuint8\fR \fIuint8\fR .RS 0.25i .PP A three octet value which allows a client to specify its network interface type. .PP This option is included based on RFC 4578. .RE .PP .B option \fBpxe-client-id\fR \fIuint8\fR \fIstring\fR .RS 0.25i .PP A single octet indicating type, followed by a string that allows a client to specify its PXE client identity. .PP This option is included based on RFC 4578. .RE .PP .B option \fBoption-6rd\fR \fIuint8 uint8 ip6-address ip-address\fR [\fB,\fR .I ip-address \fR...]\fB;\fR .RS 0.25i .PP This option contains information about the rapid deployment option. It is 8 bits of ipv4 mask length, 8 bits of 6rd prefix length, an ipv6 prefix as an ipv6 address and a list of one or more ipv4 addresses. .PP This option is included based on RFC 5969. .RE .PP .B option .B pana-agent .I ip-address \fR[\fB, .I ip-address \fR... ] .B ; .RS 0.25i .PP A set of IPv4 addresses of a PAA for the client to use. The addresses are listed in preferred order. .PP This option is included based on RFC 5192. .RE .PP .B option \fBpath-mtu-aging-timeout\fR \fIuint32\fR\fB;\fR .RS 0.25i .PP This option specifies the timeout (in seconds) to use when aging Path MTU values discovered by the mechanism defined in RFC 1191. .RE .PP .B option \fBpath-mtu-plateau-table\fR \fIuint16\fR [\fB,\fR \fIuint16\fR... ]\fB;\fR .RS 0.25i .PP This option specifies a table of MTU sizes to use when performing Path MTU Discovery as defined in RFC 1191. The table is formatted as a list of 16-bit unsigned integers, ordered from smallest to largest. The minimum MTU value cannot be smaller than 68. .RE .PP .B option \fBpcode\fR \fItext\fR\fB;\fR .RS 0.25i .PP This option specifies a string suitable for the TZ variable. .PP This option is included based on RFC 4833. .RE .PP .B option \fBperform-mask-discovery\fR \fIflag\fR\fB;\fR .RS 0.25i .PP This option specifies whether or not the client should perform subnet mask discovery using ICMP. A value of false indicates that the client should not perform mask discovery. A value of true means that the client should perform mask discovery. .RE .PP .nf .B option \fBpolicy-filter\fR \fIip-address ip-address\fR [\fB,\fR \fIip-address ip-address\fR...]\fB;\fR .RE .fi .RS 0.25i .PP This option specifies policy filters for non-local source routing. The filters consist of a list of IP addresses and masks which specify destination/mask pairs with which to filter incoming source routes. .PP Any source routed datagram whose next-hop address does not match one of the filters should be discarded by the client. .PP See STD 3 (RFC1122) for further information. .RE .PP .B option \fBpop-server\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP The POP3 server option specifies a list of POP3 servers available to the client. Servers should be listed in order of preference. .RE .PP .B option \fBrdnss-selection\fR \fIuint8 ip-address ip-address domain-name\fR\fB;\fR .RS 0.25i .PP The rdnss-selection option specifies an 8 bit flags field, a primary and secondary ip address for the name server and a domainlist of domains for which the RDNSS has special knowledge. .PP This option is included based on RFC 6731. .RE .PP .B option \fBresource-location-servers\fR \fIip-address\fR [\fB, \fR\fIip-address\fR...]\fB;\fR .fi .RS 0.25i .PP This option specifies a list of RFC 887 Resource Location servers available to the client. Servers should be listed in order of preference. .RE .PP .B option \fBroot-path\fR \fItext\fB;\fR\fR .RS 0.25i .PP This option specifies the path-name that contains the client's root disk. The path is formatted as a character string consisting of characters from the NVT ASCII character set. .RE .PP .B option \fBrouter-discovery\fR \fIflag\fR\fB;\fR .RS 0.25i .PP This option specifies whether or not the client should solicit routers using the Router Discovery mechanism defined in RFC 1256. A value of false indicates that the client should not perform router discovery. A value of true means that the client should perform router discovery. .RE .PP .B option \fBrouter-solicitation-address\fR \fIip-address\fR\fB;\fR .RS 0.25i .PP This option specifies the address to which the client should transmit router solicitation requests. .RE .PP .B option routers \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP The routers option specifies a list of IP addresses for routers on the client's subnet. Routers should be listed in order of preference. .RE .PP .B option slp-directory-agent \fIboolean ip-address [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP This option specifies two things: the IP addresses of one or more Service Location Protocol Directory Agents, and whether the use of these addresses is mandatory. If the initial boolean value is true, the SLP agent should just use the IP addresses given. If the value is false, the SLP agent may additionally do active or passive multicast discovery of SLP agents (see RFC2165 for details). .PP Please note that in this option and the slp-service-scope option, the term "SLP Agent" is being used to refer to a Service Location Protocol agent running on a machine that is being configured using the DHCP protocol. .PP Also, please be aware that some companies may refer to SLP as NDS. If you have an NDS directory agent whose address you need to configure, the slp-directory-agent option should work. .RE .PP .B option \fBslp-service-scope\fR \fIboolean text\fR\fB;\fR .RS 0.25i .PP The Service Location Protocol Service Scope Option specifies two things: a list of service scopes for SLP, and whether the use of this list is mandatory. If the initial boolean value is true, the SLP agent should only use the list of scopes provided in this option; otherwise, it may use its own static configuration in preference to the list provided in this option. .PP The text string should be a comma-separated list of scopes that the SLP agent should use. It may be omitted, in which case the SLP Agent will use the aggregated list of scopes of all directory agents known to the SLP agent. .RE .PP .B option \fBsmtp-server\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP The SMTP server option specifies a list of SMTP servers available to the client. Servers should be listed in order of preference. .RE .PP .nf .B option \fBstatic-routes\fR \fIip-address ip-address\fR [\fB,\fR \fIip-address ip-address\fR...]\fB;\fR .fi .RS 0.25i .PP This option specifies a list of static routes that the client should install in its routing cache. If multiple routes to the same destination are specified, they are listed in descending order of priority. .PP The routes consist of a list of IP address pairs. The first address is the destination address, and the second address is the router for the destination. .PP The default route (0.0.0.0) is an illegal destination for a static route. To specify the default route, use the .B routers option. Also, please note that this option is not intended for classless IP routing - it does not include a subnet mask. Since classless IP routing is now the most widely deployed routing standard, this option is virtually useless, and is not implemented by any of the popular DHCP clients, for example the Microsoft DHCP client. .RE .PP .nf .B option \fBstreettalk-directory-assistance-server\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...]\fB;\fR .fi .RS 0.25i .PP The StreetTalk Directory Assistance (STDA) server option specifies a list of STDA servers available to the client. Servers should be listed in order of preference. .RE .PP .B option \fBstreettalk-server\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP The StreetTalk server option specifies a list of StreetTalk servers available to the client. Servers should be listed in order of preference. .RE .PP .B option subnet-mask \fIip-address\fR\fB;\fR .RS 0.25i .PP The subnet mask option specifies the client's subnet mask as per RFC 950. If no subnet mask option is provided anywhere in scope, as a last resort dhcpd will use the subnet mask from the subnet declaration for the network on which an address is being assigned. However, .I any subnet-mask option declaration that is in scope for the address being assigned will override the subnet mask specified in the subnet declaration. .RE .PP .B option \fBsubnet-selection\fR \fIip-address\fR\fB;\fR .RS 0.25i .PP Sent by the client if an address is required in a subnet other than the one that would normally be selected (based on the relaying address of the connected subnet the request is obtained from). See RFC3011. Note that the option number used by this server is 118; this has not always been the defined number, and some clients may use a different value. Use of this option should be regarded as slightly experimental! .RE .PP This option is not user configurable in the server. .PP .PP .B option \fBswap-server\fR \fIip-address\fR\fB;\fR .RS 0.25i .PP This specifies the IP address of the client's swap server. .RE .PP .B option \fBtftp-server-address\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP This option configures a list of one or more IPv4 addresses of tftp servers a client may use. .PP This option is included based on RFC 5859 .RE .PP .B option \fBtcp-keepalive-garbage\fR \fIflag\fR\fB;\fR .RS 0.25i .PP This option specifies whether or not the client should send TCP keepalive messages with an octet of garbage for compatibility with older implementations. A value of false indicates that a garbage octet should not be sent. A value of true indicates that a garbage octet should be sent. .RE .PP .B option \fBtcp-keepalive-interval\fR \fIuint32\fR\fB;\fR .RS 0.25i .PP This option specifies the interval (in seconds) that the client TCP should wait before sending a keepalive message on a TCP connection. The time is specified as a 32-bit unsigned integer. A value of zero indicates that the client should not generate keepalive messages on connections unless specifically requested by an application. .RE .PP .B option \fBtcode\fR \fItext\fR\fB;\fR .RS 0.25i .PP This option specifies a name of a zone entry in the TZ database. .PP This option is included based on RFC 4833. .RE .PP .B option \fBtftp-server-name\fR \fItext\fR\fB;\fR .RS 0.25i .PP This option is used to identify a TFTP server and, if supported by the client, should have the same effect as the \fBserver-name\fR declaration. BOOTP clients are unlikely to support this option. Some DHCP clients will support it, and others actually require it. .RE .PP .B option time-offset \fIint32\fR\fB;\fR .RS 0.25i .PP The time-offset option specifies the offset of the client's subnet in seconds from Coordinated Universal Time (UTC). .RE .PP .B option time-servers \fIip-address\fR [, \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP The time-server option specifies a list of RFC 868 time servers available to the client. Servers should be listed in order of preference. .RE .PP .B option \fBtrailer-encapsulation\fR \fIflag\fR\fB;\fR .RS 0.25i .PP This option specifies whether or not the client should negotiate the use of trailers (RFC 893 [14]) when using the ARP protocol. A value of false indicates that the client should not attempt to use trailers. A value of true means that the client should attempt to use trailers. .RE .PP .B option \fBuap-servers\fR \fItext\fR\fB;\fR .RS 0.25i .PP This option specifies a list of URLs, each pointing to a user authentication service that is capable of processing authentication requests encapsulated in the User Authentication Protocol (UAP). UAP servers can accept either HTTP 1.1 or SSLv3 connections. If the list includes a URL that does not contain a port component, the normal default port is assumed (i.e., port 80 for http and port 443 for https). If the list includes a URL that does not contain a path component, the path /uap is assumed. If more than one URL is specified in this list, the URLs are separated by spaces. .RE .PP .B option \fBuser-class\fR \fIstring\fR\fB;\fR .RS 0.25i .PP This option is used by some DHCP clients as a way for users to specify identifying information to the client. This can be used in a similar way to the vendor-class-identifier option, but the value of the option is specified by the user, not the vendor. Most recent DHCP clients have a way in the user interface to specify the value for this identifier, usually as a text string. .RE .PP .B option \fBv4-access-domain\fR \fIdomain-name\fR\fB;\fR .RS 0.25i .PP The domain name associated with the access network for use with LIS Discovery. .PP This option is included based on RFC 5986. .RE .PP .B option \fBv4-lost\fR \fIdomain-name\fR\fB;\fR .RS 0.25i .PP The domain name of the LoST server for the client to use. .PP This option is included based on RFC 5223. .RE .PP .B option \fBvendor-class-identifier\fR \fIstring\fR\fB;\fR .RS 0.25i .PP This option is used by some DHCP clients to identify the vendor type and possibly the configuration of a DHCP client. The information is a string of bytes whose contents are specific to the vendor and are not specified in a standard. To see what vendor class identifier clients are sending, you can write the following in your DHCP server configuration file: .nf .PP set vendor-string = option vendor-class-identifier; .fi .PP This will result in all entries in the DHCP server lease database file for clients that sent vendor-class-identifier options having a set statement that looks something like this: .nf .PP set vendor-string = "SUNW.Ultra-5_10"; .fi .PP The vendor-class-identifier option is normally used by the DHCP server to determine the options that are returned in the .B vendor-encapsulated-options option. Please see the VENDOR ENCAPSULATED OPTIONS section later in this manual page for further information. .RE .PP .B option \fBvendor-encapsulated-options\fR \fIstring\fR\fB;\fR .RS 0.25i .PP The \fBvendor-encapsulated-options\fR option can contain either a single vendor-specific value or one or more vendor-specific suboptions. This option is not normally specified in the DHCP server configuration file - instead, a vendor class is defined for each vendor, vendor class suboptions are defined, values for those suboptions are defined, and the DHCP server makes up a response on that basis. .PP Some default behaviours for well-known DHCP client vendors (currently, the Microsoft Windows 2000 DHCP client) are configured automatically, but otherwise this must be configured manually - see the VENDOR ENCAPSULATED OPTIONS section later in this manual page for details. .RE .PP .B option \fBvivso\fR \fIstring\fR\fB;\fR .RS 0.25i .PP The \fBvivso\fR option can contain multiple separate options, one for each 32-bit Enterprise ID. Each Enterprise-ID discriminated option then contains additional options whose format is defined by the vendor who holds that ID. This option is usually not configured manually, but rather is configured via intervening option definitions. Please also see the VENDOR ENCAPSULATED OPTIONS section later in this manual page for details. .RE .PP .B option \fBwww-server\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP The WWW server option specifies a list of WWW servers available to the client. Servers should be listed in order of preference. .RE .PP .B option \fBx-display-manager\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR .RS 0.25i .PP This option specifies a list of systems that are running the X Window System Display Manager and are available to the client. Addresses should be listed in order of preference. .RE .SH RELAY AGENT INFORMATION OPTION An IETF draft, draft-ietf-dhc-agent-options-11.txt, defines a series of encapsulated options that a relay agent can add to a DHCP packet when relaying it to the DHCP server. The server can then make address allocation decisions (or whatever other decisions it wants) based on these options. The server also returns these options in any replies it sends through the relay agent, so that the relay agent can use the information in these options for delivery or accounting purposes. .PP The current draft defines two options. To reference these options in the dhcp server, specify the option space name, "agent", followed by a period, followed by the option name. It is not normally useful to define values for these options in the server, although it is permissible. These options are not supported in the client. .PP .B option \fBagent.circuit-id\fR \fIstring\fR\fB;\fR .RS 0.25i .PP The circuit-id suboption encodes an agent-local identifier of the circuit from which a DHCP client-to-server packet was received. It is intended for use by agents in relaying DHCP responses back to the proper circuit. The format of this option is currently defined to be vendor-dependent, and will probably remain that way, although the current draft allows for the possibility of standardizing the format in the future. .RE .PP .B option \fBagent.remote-id\fR \fIstring\fR\fB;\fR .RS 0.25i .PP The remote-id suboption encodes information about the remote host end of a circuit. Examples of what it might contain include caller ID information, username information, remote ATM address, cable modem ID, and similar things. In principal, the meaning is not well-specified, and it should generally be assumed to be an opaque object that is administratively guaranteed to be unique to a particular remote end of a circuit. .RE .PP .B option \fBagent.DOCSIS-device-class\fR \fIuint32\fR\fB;\fR .RS 0.25i .PP The DOCSIS-device-class suboption is intended to convey information about the host endpoint, hardware, and software, that either the host operating system or the DHCP server may not otherwise be aware of (but the relay is able to distinguish). This is implemented as a 32-bit field (4 octets), each bit representing a flag describing the host in one of these ways. So far, only bit zero (being the least significant bit) is defined in RFC3256. If this bit is set to one, the host is considered a CPE Controlled Cable Modem (CCCM). All other bits are reserved. .RE .PP .B option \fBagent.link-selection\fR \fIip-address\fR\fB;\fR .RS 0.25i .PP The link-selection suboption is provided by relay agents to inform servers what subnet the client is actually attached to. This is useful in those cases where the giaddr (where responses must be sent to the relay agent) is not on the same subnet as the client. When this option is present in a packet from a relay agent, the DHCP server will use its contents to find a subnet declared in configuration, and from here take one step further backwards to any shared-network the subnet may be defined within; the client may be given any address within that shared network, as normally appropriate. .RE .SH THE CLIENT FQDN SUBOPTIONS The Client FQDN option, currently defined in the Internet Draft draft-ietf-dhc-fqdn-option-00.txt is not a standard yet, but is in sufficiently wide use already that we have implemented it. Due to the complexity of the option format, we have implemented it as a suboption space rather than a single option. In general this option should not be configured by the user - instead it should be used as part of an automatic DNS update system. .PP .B option fqdn.no-client-update \fIflag\fB; .RS 0.25i .PP When the client sends this, if it is true, it means the client will not attempt to update its A record. When sent by the server to the client, it means that the client \fIshould not\fR update its own A record. .RE .PP .B option fqdn.server-update \fIflag\fB; .RS 0.25i .PP When the client sends this to the server, it is requesting that the server update its A record. When sent by the server, it means that the server has updated (or is about to update) the client's A record. .RE .PP .B option fqdn.encoded \fIflag\fB; .RS 0.25i .PP If true, this indicates that the domain name included in the option is encoded in DNS wire format, rather than as plain ASCII text. The client normally sets this to false if it doesn't support DNS wire format in the FQDN option. The server should always send back the same value that the client sent. When this value is set on the configuration side, it controls the format in which the \fIfqdn.fqdn\fR suboption is encoded. .RE .PP .B option fqdn.rcode1 \fIflag\fB; .PP .B option fqdn.rcode2 \fIflag\fB; .RS 0.25i .PP These options specify the result of the updates of the A and PTR records, respectively, and are only sent by the DHCP server to the DHCP client. The values of these fields are those defined in the DNS protocol specification. .RE .PP .B option fqdn.fqdn \fItext\fB; .RS 0.25i .PP Specifies the domain name that the client wishes to use. This can be a fully-qualified domain name, or a single label. If there is no trailing \'.\' character in the name, it is not fully-qualified, and the server will generally update that name in some locally-defined domain. .RE .PP .B option fqdn.hostname \fI--never set--\fB; .RS 0.25i .PP This option should never be set, but it can be read back using the \fBoption\fR and \fBconfig-option\fR operators in an expression, in which case it returns the first label in the \fBfqdn.fqdn\fR suboption - for example, if the value of \fBfqdn.fqdn\fR is "foo.example.com.", then \fBfqdn.hostname\fR will be "foo". .RE .PP .B option fqdn.domainname \fI--never set--\fB; .RS 0.25i .PP This option should never be set, but it can be read back using the \fBoption\fR and \fBconfig-option\fR operators in an expression, in which case it returns all labels after the first label in the \fBfqdn.fqdn\fR suboption - for example, if the value of \fBfqdn.fqdn\fR is "foo.example.com.", then \fBfqdn.domainname\fR will be "example.com.". If this suboption value is not set, it means that an unqualified name was sent in the \fBfqdn\fR option, or that no \fBfqdn\fR option was sent at all. .RE .PP If you wish to use any of these suboptions, we strongly recommend that you refer to the Client FQDN option draft (or standard, when it becomes a standard) - the documentation here is sketchy and incomplete in comparison, and is just intended for reference by people who already understand the Client FQDN option specification. .SH THE NETWARE/IP SUBOPTIONS RFC2242 defines a set of encapsulated options for Novell NetWare/IP clients. To use these options in the dhcp server, specify the option space name, "nwip", followed by a period, followed by the option name. The following options can be specified: .PP .B option \fBnwip.nsq-broadcast\fR \fIflag\fR\fB;\fR .RS 0.25i .PP If true, the client should use the NetWare Nearest Server Query to locate a NetWare/IP server. The behaviour of the Novell client if this suboption is false, or is not present, is not specified. .PP .RE .B option \fBnwip.preferred-dss\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fR\fB;\fR .RS 0.25i .PP This suboption specifies a list of up to five IP addresses, each of which should be the IP address of a NetWare Domain SAP/RIP server (DSS). .RE .PP .B option \fBnwip.nearest-nwip-server\fR \fI\fIip-address\fR [\fB,\fR \fIip-address\fR...]\fR\fB;\fR .RS 0.25i .PP This suboption specifies a list of up to five IP addresses, each of which should be the IP address of a Nearest NetWare IP server. .RE .PP .B option \fBnwip.autoretries\fR \fIuint8\fR\fB;\fR .RS 0.25i .PP Specifies the number of times that a NetWare/IP client should attempt to communicate with a given DSS server at startup. .RE .PP .B option \fBnwip.autoretry-secs\fR \fIuint8\fR\fB;\fR .RS 0.25i .PP Specifies the number of seconds that a Netware/IP client should wait between retries when attempting to establish communications with a DSS server at startup. .RE .PP .B option \fBnwip.nwip-1-1\fR \fIuint8\fR\fB;\fR .RS 0.25i .PP If true, the NetWare/IP client should support NetWare/IP version 1.1 compatibility. This is only needed if the client will be contacting Netware/IP version 1.1 servers. .RE .PP .B option \fBnwip.primary-dss\fR \fIip-address\fR\fB;\fR .RS 0.25i .PP Specifies the IP address of the Primary Domain SAP/RIP Service server (DSS) for this NetWare/IP domain. The NetWare/IP administration utility uses this value as Primary DSS server when configuring a secondary DSS server. .RE .SH STANDARD DHCPV6 OPTIONS DHCPv6 options differ from DHCPv4 options partially due to using 16-bit code and length tags, but semantically zero-length options are legal in DHCPv6, and multiple options are treated differently. Whereas in DHCPv4 multiple options would be concatenated to form one option, in DHCPv6 they are expected to be individual instantiations. Understandably, many options are not "allowed" to have multiple instances in a packet - normally these are options which are digested by the DHCP protocol software, and not by users or applications. .PP .B option \fBdhcp6.client-id\fR \fIstring\fR\fB;\fR .RS 0.25i .PP This option specifies the client's DUID identifier. DUIDs are similar but different from DHCPv4 client identifiers - there are documented duid types: .PP .I duid-llt .PP .I duid-en .PP .I duid-ll .PP This value should not be configured, but rather is provided by clients and treated as an opaque identifier key blob by servers. .RE .PP .B option \fBdhcp6.server-id\fR \fIstring\fR\fB;\fR .RS 0.25i .PP This option specifies the server's DUID identifier. One may use this option to configure an opaque binary blob for your server's identifier. .RE .PP .B option \fBdhcp6.ia-na\fR \fIstring\fR\fB;\fR .RS 0.25i .PP The Identity Association for Non-temporary Addresses (ia-na) carries assigned addresses that are not temporary addresses for use by the DHCPv6 client. This option is produced by the DHCPv6 server software, and should not be configured. .RE .PP .B option \fBdhcp6.ia-ta\fR \fIstring\fR\fB;\fR .RS 0.25i .PP The Identity Association for Temporary Addresses (ia-ta) carries temporary addresses, which may change upon every renewal. There is no support for this in the current DHCPv6 software. .RE .PP .B option \fBdhcp6.ia-addr\fR \fIstring\fR\fB;\fR .RS 0.25i .PP The Identity Association Address option is encapsulated inside ia-na or ia-ta options in order to represent addresses associated with those IA's. These options are manufactured by the software, so should not be configured. .RE .PP .B option \fBdhcp6.oro\fR \fIuint16\fR [ \fB,\fR \fIuint16\fR\fB,\fR ... ]\fB;\fR .RS 0.25i .PP The Option Request Option ("ORO") is the DHCPv6 equivalent of the parameter-request-list. Clients supply this option to ask servers to reply with options relevant to their needs and use. This option must not be directly configured, the request syntax in dhclient.conf (5) should be used instead. .RE .PP .B option \fBdhcp6.preference\fR \fIuint8\fR\fB;\fR .RS 0.25i .PP The \fBpreference\fR option informs a DHCPv6 client which server is \'preferred\' for use on a given subnet. This preference is only applied during the initial stages of configuration - once a client is bound to an IA, it will remain bound to that IA until it is no longer valid or has expired. This value may be configured on the server, and is digested by the client software. .RE .PP .B option \fBdhcp6.elapsed-time\fR \fIuint16\fR\fB;\fR .RS 0.25i .PP The \fBelapsed-time\fR option is constructed by the DHCPv6 client software, and is potentially consumed by intermediaries. This option should not be configured. .RE .PP .B option \fBdhcp6.relay-msg\fR \fIstring\fR\fB;\fR .RS 0.25i .PP The \fBrelay-msg\fR option is constructed by intervening DHCPv6 relay agent software. This option is entirely used by protocol software, and is not meant for user configuration. .RE .PP .B option \fBdhcp6.unicast\fR \fIip6-address\fR\fB;\fR .RS 0.25i .PP The \fBunicast\fR option is provided by DHCPv6 servers which are willing (or prefer) to receive Request, Renew, Decline, and Release packets from their clients via unicast. Normally, DHCPv6 clients will multicast these messages. Per RFC 3315, the server will reject a unicast message received from a client unless it previously sent (or would have sent) the unicast option to that client. This option may be configured on the server at the global and shared network level. When a unicast message is received, the server will check for an applicable definition of the unicast option. If such an option is found the message will be accepted, if not it will be rejected. .RE .PP .B option \fBdhcp6.status-code\fR \fIstatus-code\fR [ \fIstring\fR ] \fB;\fR .RS 0.25i .PP The \fBstatus-code\fR option is provided by DHCPv6 servers to inform clients of error conditions during protocol communication. This option is manufactured and digested by protocol software, and should not be configured. .RE .PP .B option \fBdhcp6.rapid-commit\fR \fB;\fR .RS 0.25i .PP The \fBrapid-commit\fR option is a zero-length option that clients use to indicate their desire to enter into rapid-commit with the server. .RE .PP .B option \fBdhcp6.vendor-opts\fR \fIstring\fR\fB;\fR .RS 0.25i .PP The \fBvendor-opts\fR option is actually an encapsulated sub-option space, in which each Vendor-specific Information Option (VSIO) is identified by a 32-bit Enterprise-ID number. The encapsulated option spaces within these options are defined by the vendors. .PP To make use of this option, the best way is to examine the section titled VENDOR ENCAPSULATED OPTIONS below, in particular the bits about the "vsio" option space. .RE .PP .B option \fBdhcp6.interface-id\fR \fIstring\fR\fB;\fR .RS 0.25i .PP The \fBinterface-id\fR option is manufactured by relay agents, and may be used to guide configuration differentiating clients by the interface they are remotely attached to. It does not make sense to configure a value for this option, but it may make sense to inspect its contents. .RE .PP .B option \fBdhcp6.reconf-msg\fR \fIdhcpv6-message\fR\fB;\fR .RS 0.25i .PP The \fBreconf-msg\fR option is manufactured by servers, and sent to clients in Reconfigure messages to inform them of what message the client should Reconfigure using. There is no support for DHCPv6 Reconfigure extensions, and this option is documented informationally only. .RE .PP .B option \fBdhcp6.reconf-accept ;\fR .RS 0.25i .PP The \fBreconf-accept\fR option is included by DHCPv6 clients that support the Reconfigure extensions, advertising that they will respond if the server were to ask them to Reconfigure. There is no support for DHCPv6 Reconfigure extensions, and this option is documented informationally only. .RE .PP .B option \fBdhcp6.sip-servers-names\fR \fIdomain-list\fR\fB;\fR .RS 0.25i .PP The \fBsip-servers-names\fR option allows SIP clients to locate a local SIP server that is to be used for all outbound SIP requests, a so-called"outbound proxy server." If you wish to use manually entered IPv6 addresses instead, please see the \fBsip-servers-addresses\fR option below. .RE .PP .B option .B dhcp6.sip-servers-addresses .I ip6-address \fR[\fB,\fR .I ip6-address \fR... ] .B ; .RS 0.25i .PP The \fBsip-servers-addresses\fR option allows SIP clients to locate a local SIP server that is to be used for all outbound SIP requests, a so-called "outbound proxy servers." If you wish to use domain names rather than IPv6 addresses, please see the \fBsip-servers-names\fR option above. .RE .PP .B option .B dhcp6.name-servers .I ip6-address \fR[\fB,\fR .I ip6-address \fR... ] .B ; .RS 0.25i .PP The \fBname-servers\fR option instructs clients about locally available recursive DNS servers. It is easiest to describe this as the "nameserver" line in /etc/resolv.conf. .RE .PP .B option \fBdhcp6.domain-search\fR \fIdomain-list\fR\fB;\fR .RS 0.25i .PP The \fBdomain-search\fR option specifies the client's domain search path to be applied to recursive DNS queries. It is easiest to describe this as the "search" line in /etc/resolv.conf. .RE .PP .B option \fBdhcp6.ia-pd\fR \fIstring\fR\fB;\fR .RS 0.25i .PP The \fBia-pd\fR option is manufactured by clients and servers to create a Prefix Delegation binding - to delegate an IPv6 prefix to the client. It is not directly edited in dhcpd.conf(5) or dhclient.conf(5), but rather is manufactured and consumed by the software. .RE .PP .B option \fBdhcp6.ia-prefix\fR \fIstring\fR\fB;\fR .RS 0.25i .PP The \fBia-prefix\fR option is placed inside \fBia-pd\fR options in order to identify the prefix(es) allocated to the client. It is not directly edited in dhcpd.conf(5) or dhclient.conf(5), but rather is manufactured and consumed by the software. .RE .PP .B option .B dhcp6.nis-servers .I ip6-address \fR[\fB, .I ip6-address \fR... ] .B ; .RS 0.25i .PP The \fBnis-servers\fR option identifies, in order, NIS servers available to the client. .RE .PP .B option .B dhcp6.nisp-servers .I ip6-address \fR[\fB, .I ip6-address \fR... ] .B ; .RS 0.25i .PP The \fBnisp-servers\fR option identifies, in order, NIS+ servers available to the client. .RE .PP .B option \fBnis-domain-name\fR \fIdomain-list\fR\fB;\fR .RS 0.25i .PP The \fBnis-domain-name\fR option specifies the NIS domain name the client is expected to use, and is related to the \fBnis-servers\fR option. .RE .PP .B option \fBdhcp6.nis-domain-name\fR \fIdomain-name\fR\fB;\fR .RS 0.25i .PP The \fBdhcp6.nis-domain-name\fR option specifies NIS domain name the client is expected to use, and is related to \fBdhcp6.nis-servers\fR option. .RE .PP .B option \fBnisp-domain-name\fR \fIdomain-list\fR\fB;\fR .RS 0.25i .PP The \fBnisp-domain-name\fR option specifies the NIS+ domain name the client is expected to use, and is related to the \fBnisp-servers\fR option. .RE .PP .B option \fBdhcp6.nisp-domain-name\fR \fIdomain-name\fR\fB;\fR .RS 0.25i .PP The \fBdhcp6.nis-domain-name\fR option specifies NIS+ domain name the client is expected to use, and is related to \fBdhcp6.nisp-servers\fR option. .RE .PP .B option .B dhcp6.sntp-servers .I ip6-address \fR[\fB, .I ip6-address \fR... ] .B ; .RS 0.25i .PP The \fBsntp-servers\fR option specifies a list of local SNTP servers available for the client to synchronize their clocks. .RE .PP .B option \fBdhcp6.info-refresh-time\fR \fIuint32\fR\fB;\fR .RS 0.25i .PP The \fBinfo-refresh-time\fR option gives DHCPv6 clients using Information-request messages a hint as to how long they should between refreshing the information they were given. Note that this option will only be delivered to the client, and be likely to affect the client's behaviour, if the client requested the option. .RE .PP .B option \fBdhcp6.bcms-server-d\fR \fIdomain-list\fR\fB;\fR .RS 0.25i .PP The \fBbcms-server-d\fR option contains the domain names of local BCMS (Broadcast and Multicast Control Services) controllers which the client may use. .RE .PP .B option .B dhcp6.bcms-server-a .I ip6-address \fR[\fB, .I ip6-address \fR... ] .B ; .RS 0.25i .PP The \fBbcms-server-a\fR option contains the IPv6 addresses of local BCMS (Broadcast and Multicast Control Services) controllers which the client may use. .RE .PP .B option \fBdhcp6.geoconf-civic\fR \fIstring\fR\fB;\fR .RS 0.25i .PP A string to hold the geoconf civic structure. .PP This option is included based on RFC 4776. .RE .PP .B option \fBdhcp6.remote-id\fR \fIstring\fR\fB;\fR .RS 0.25i .PP The \fBremote-id\fR option is constructed by relay agents, to inform the server of details pertaining to what the relay knows about the client (such as what port it is attached to, and so forth). The contents of this option have some vendor-specific structure (similar to VSIO), but we have chosen to treat this option as an opaque field. .RE .PP .B option \fBdhcp6.subscriber-id\fR \fIstring\fR\fB;\fR .RS 0.25i .PP The \fBsubscriber-id\fR option is an opaque field provided by the relay agent, which provides additional information about the subscriber in question. The exact contents of this option depend upon the vendor and/or the operator's configuration of the remote device, and as such is an opaque field. .RE .PP .B option \fBdhcp6.fqdn\fR \fIstring\fR\fB;\fR .RS 0.25i .PP The \fBfqdn\fR option is normally constructed by the client or server, and negotiates the client's Fully Qualified Domain Name, as well as which party is responsible for Dynamic DNS Updates. See the section on the Client FQDN SubOptions for full details (the DHCPv4 and DHCPv6 FQDN options use the same "fqdn." encapsulated space, so are in all ways identical). .RE .PP .B option \fBdhcp6.pana-agent\fR .I ip6-address \fR[\fB, .I ip6-address \fR... ] .B ; .RS 0.25i .PP A set of IPv6 addresses of a PAA for the client to use. The addresses are listed in preferred order. .PP This option is included based on RFC 5192. .RE .PP .B option \fBdhcp6.new-posix-timezone\fR \fItext\fR\fB;\fR .RS 0.25i .PP This option specifies a string suitable for the TZ variable. .PP This option is included based on RFC 4833. .RE .PP .B option \fBdhcp6.new-tzdb-timezone\fR \fItext\fR\fB;\fR .RS 0.25i .PP This option specifies a name of a zone entry in the TZ database. .PP This option is included based on RFC 4833. .RE .PP .B option \fBdhcp6.ero\fR .I uint16 \fR[\fB, .I uint16 \fR... ] .B ; .RS 0.25i .PP A list of the options requested by the relay agent. .PP This option is included based on RFC 4994. .RE .PP .B option \fBdhcp6.lq-query\fR \fIstring\fR\fB;\fR .RS 0.25i .PP The \fBlq-query\fR option is used internally for lease query. .RE .PP .B option \fBdhcp6.client-data\fR \fIstring\fR\fB;\fR .RS 0.25i .PP The \fBclient-data\fR option is used internally for lease query. .RE .PP .B option \fBdhcp6.clt-time\fR \fIuint32\fR\fB;\fR .RS 0.25i .PP The \fBclt-time\fR option is used internally for lease query. .RE .PP .B option \fBdhcp6.lq-relay-data\fR \fIip6-address string\fR\fB;\fR .RS 0.25i .PP The \fBlq-relay-data\fR option is used internally for lease query. .RE .PP .B option .B dhcp6.lq-client-link .I ip6-address \fR[\fB,\fR .I ip6-address \fR... ] .B ; .RS 0.25i .PP The \fBlq-client-link\fR option is used internally for lease query. .RE .PP .B option \fBdhcp6.v6-lost\fR \fIdomain-name\fR\fB;\fR .RS 0.25i .PP The domain name of the LoST server for the client to use. .PP This option is included based on RFC 5223. .RE .PP .B option \fBdhcp6.capwap-ac-v6\fR .I ip6-address \fR[\fB, .I ip6-address \fR... ] .B ; .RS 0.25i .PP A list of IPv6 addresses of CAPWAP ACs that the WTP may use. The addresses are listed in preference order. .PP This option is included based on RFC 5417. .RE .PP .B option \fBdhcp6.relay-id\fR \fIstring\fR\fB;\fR .RS 0.25i .PP The DUID for the relay agent. .PP This option is included based on RFC 5460. .RE .PP .B option \fBdhcp6.v6-access-domain\fR \fIdomain-name\fR\fB;\fR .RS 0.25i .PP The domain name associated with the access network for use with LIS Discovery. .PP This option is included based on RFC5986. .RE .PP .B option \fBdhcp6.sip-ua-cs-list\fR \fIdomain-list\fR\fB;\fR .RS 0.25i .PP The list of domain names in the SIP User Agent Configuration Service Domains. .PP This option is included based on RFC 6011. .RE .PP .B option \fBdhcp6.bootfile-url\fR \fItext\fR\fB;\fR .RS 0.25i .PP The URL for a boot file. .PP This option is included based on RFC 5970. .RE .PP .B option \fBdhcp6.bootfile-param\fR \fIstring\fR\fB;\fR .RS 0.25i .PP A string for the parameters to the bootfile. See RFC 5970 for more description of the layout of the parameters within the string. .PP This option is included based on RFC 5970. .RE .PP .B option \fBdhcp6.client-arch-type\fR .I uint16 \fR[\fB, .I uint16 \fR... ] .B ; .RS 0.25i .PP A list of one or more architecture types described as 16 bit values. .PP This option is included based on RFC 5970. .RE .PP .B option \fBdhcp6.nii\fR \fIuint8 uint8 uint8\fR\fB;\fR .RS 0.25i .PP The client network interface identitier option supplies information about a client's level of UNDI support. The values are, in order, the type, the major value and the minor value. .PP This option is included based on RFC5970. .RE .PP .B option \fBdhcp6.aftr-name\fR \fIdomain-name\fR\fB;\fR .RS 0.25i .PP A domain name of the AFTR tunnel endpoint. .PP This option is included based on RFC 6334. .RE .PP .B option \fBdhcp6.erp-local-domain-name\fR \fIdomain-name\fR\fB;\fR .RS 0.25i .PP A domain name for the ERP domain. .PP This option is included based on RFC 6440. .RE .PP .B option \fBdhcp6.rdnss-selection\fR \fIip6-address uint8 domain-name\fR\fB;\fR .RS 0.25i .PP RDNSS information consists of an IPv6 address of RDNSS, an 8 bit flags field and a domain-list of domains for which the RDNSS has special knowledge. .PP This option is included based on RFC 6731. .RE .PP .B option \fBdhcp6.client-linklayer-addr\fR \fIstring\fR\fB;\fR .RS 0.25i .PP A client link-layer address. The first two bytes must be the type of the link-layer followed by the address itself. .PP This option is included based on RFC 6939. .RE .PP .B option \fBdhcp6.link-address\fR \fIip6-address\fR\fB;\fR .RS 0.25i .PP An IPv6 address used by a relay agent to indicate to the server the link on which the client is located. .PP This option is included based on RFC 6977. .RE .PP .B option \fBdhcp6.solmax-rt\fR \fIuint32\fR\fB;\fR .RS 0.25i .PP A value to override the default for SOL_MAX_RT. This is a 32 bit value. .PP This option is included based on RFC 7083. .RE .PP .B option \fBdhcp6.inf-max-rt\fR \fIuint32\fR\fB;\fR .RS 0.25i .PP A value to override the default for INF_MAX_RT. This is a 32 bit value. .PP This option is included based on RFC 7083. .RE .SH ACCESSING DHCPV6 RELAY OPTIONS .PP .B v6relay (\fBrelay-number\fR, \fBoption\fR) This option allows access to an option that has been added to a packet by a relay agent. Relay-number value selects the relay to examine and option is the option to find. In DHCPv6 each relay encapsulates the entire previous message into an option, adds its own options (if any) and sends the result onwards. The RFC specifies a limit of 32 hops. A relay-number of 0 is a no-op and means don't look at the relays. 1 is the relay that is closest to the client, 2 would be the next in from the client and so on. Any value greater than the max number of hops is which is closest to the server independent of number. To use this option in a class statement you would have something like this: .PP match if v6relay(1, option dhcp6.subscriber-id) = "client_1"; .RE .PP .RE .SH DEFINING NEW OPTIONS The Internet Systems Consortium DHCP client and server provide the capability to define new options. Each DHCP option has a name, a code, and a structure. The name is used by you to refer to the option. The code is a number, used by the DHCP server and client to refer to an option. The structure describes what the contents of an option looks like. .PP To define a new option, you need to choose a name for it that is not in use for some other option - for example, you can't use "host-name" because the DHCP protocol already defines a host-name option, which is documented earlier in this manual page. If an option name doesn't appear in this manual page, you can use it, but it's probably a good idea to put some kind of unique string at the beginning so you can be sure that future options don't take your name. For example, you might define an option, "local-host-name", feeling some confidence that no official DHCP option name will ever start with "local". .PP Once you have chosen a name, you must choose a code. All codes between 224 and 254 are reserved as \'site-local\' DHCP options, so you can pick any one of these for your site (not for your product/application). In RFC3942, site-local space was moved from starting at 128 to starting at 224. In practice, some vendors have interpreted the protocol rather loosely and have used option code values greater than 128 themselves. There's no real way to avoid this problem, and it was thought to be unlikely to cause too much trouble in practice. If you come across a vendor-documented option code in either the new or old site-local spaces, please contact your vendor and inform them about rfc3942. .PP The structure of an option is simply the format in which the option data appears. The ISC DHCP server currently supports a few simple types, like integers, booleans, strings and IP addresses, and it also supports the ability to define arrays of single types or arrays of fixed sequences of types. .PP New options are declared as follows: .PP .B option .I new-name .B code .I new-code .B = .I definition .B ; .PP The values of .I new-name and .I new-code should be the name you have chosen for the new option and the code you have chosen. The .I definition should be the definition of the structure of the option. .PP The following simple option type definitions are supported: .PP .B BOOLEAN .PP .B option .I new-name .B code .I new-code .B = .B boolean .B ; .PP An option of type boolean is a flag with a value of either on or off (or true or false). So an example use of the boolean type would be: .nf option use-zephyr code 180 = boolean; option use-zephyr on; .fi .B INTEGER .PP .B option .I new-name .B code .I new-code .B = .I sign .B integer .I width .B ; .PP The \fIsign\fR token should either be blank, \fIunsigned\fR or \fIsigned\fR. The width can be either 8, 16 or 32, and refers to the number of bits in the integer. So for example, the following two lines show a definition of the sql-connection-max option and its use: .nf option sql-connection-max code 192 = unsigned integer 16; option sql-connection-max 1536; .fi .B IP-ADDRESS .PP .B option .I new-name .B code .I new-code .B = .B ip-address .B ; .PP An option whose structure is an IP address can be expressed either as a domain name or as a dotted quad. So the following is an example use of the ip-address type: .nf option sql-server-address code 193 = ip-address; option sql-server-address sql.example.com; .fi .B IP6-ADDRESS .PP .B option .I new-name .B code .I new-code .B = .B ip6-address .B ; .PP An option whose structure is an IPv6 address must be expressed as a valid IPv6 address. The following is an example use of the ip6-address type: .nf option dhcp6.some-server code 1234 = array of ip6-address; option dhcp6.some-server 3ffe:bbbb:aaaa:aaaa::1, 3ffe:bbbb:aaaa:aaaa::2; .fi .PP .B TEXT .PP .B option .I new-name .B code .I new-code .B = .B text .B ; .PP An option whose type is text will encode an ASCII text string. For example: .nf option sql-default-connection-name code 194 = text; option sql-default-connection-name "PRODZA"; .fi .PP .B DATA STRING .PP .B option .I new-name .B code .I new-code .B = .B string .B ; .PP An option whose type is a data string is essentially just a collection of bytes, and can be specified either as quoted text, like the text type, or as a list of hexadecimal contents separated by colons whose values must be between 0 and FF. For example: .nf option sql-identification-token code 195 = string; option sql-identification-token 17:23:19:a6:42:ea:99:7c:22; .fi .PP .B DOMAIN-LIST .PP .B option .I new-name .B code .I new-code .B = .B domain-list .B [compressed] .B ; .PP An option whose type is \fBdomain-list\fR is an RFC1035 formatted (on the wire, "DNS Format") list of domain names, separated by root labels. The optional \fBcompressed\fR keyword indicates if the option should be compressed relative to the start of the option contents (not the packet contents). .PP When in doubt, omit the \fBcompressed\fR keyword. When the software receives an option that is compressed and the \fBcompressed\fR keyword is omitted, it will still decompress the option (relative to the option contents field). The keyword only controls whether or not transmitted packets are compressed. .PP Note that when .B domain-list formatted options are output as environment variables to .B dhclient-script(8), the standard DNS \-escape mechanism is used: they are decimal. This is appropriate for direct use in eg /etc/resolv.conf. .nf .fi .PP .B ENCAPSULATION .PP .B option .I new-name .B code .I new-code .B = .B encapsulate .I identifier .B ; .PP An option whose type is \fBencapsulate\fR will encapsulate the contents of the option space specified in \fIidentifier\fR. Examples of encapsulated options in the DHCP protocol as it currently exists include the vendor-encapsulated-options option, the netware-suboptions option and the relay-agent-information option. .nf option space local; option local.demo code 1 = text; option local-encapsulation code 197 = encapsulate local; option local.demo "demo"; .fi .PP .B ARRAYS .PP Options can contain arrays of any of the above types except for the text and data string types, which aren't currently supported in arrays. An example of an array definition is as follows: .nf option kerberos-servers code 200 = array of ip-address; option kerberos-servers 10.20.10.1, 10.20.11.1; .fi .B RECORDS .PP Options can also contain data structures consisting of a sequence of data types, which is sometimes called a record type. For example: .nf option contrived-001 code 201 = { boolean, integer 32, text }; option contrived-001 on 1772 "contrivance"; .fi It's also possible to have options that are arrays of records, for example: .nf option new-static-routes code 201 = array of { ip-address, ip-address, ip-address, integer 8 }; option static-routes 10.0.0.0 255.255.255.0 net-0-rtr.example.com 1, 10.0.1.0 255.255.255.0 net-1-rtr.example.com 1, 10.2.0.0 255.255.224.0 net-2-0-rtr.example.com 3; .fi .SH VENDOR ENCAPSULATED OPTIONS The DHCP protocol defines the \fBvendor-encapsulated-options\fR option, which allows vendors to define their own options that will be sent encapsulated in a standard DHCP option. It also defines the \fBVendor Identified Vendor Sub Options\fR option ("VIVSO"), and the DHCPv6 protocol defines the \fBVendor-specific Information Option\fR ("VSIO"). The format of all of these options is usually internally a string of options, similarly to other normal DHCP options. The VIVSO and VSIO options differ in that they contain options that correspond to vendor Enterprise-ID numbers (assigned by IANA), which then contain options according to each Vendor's specifications. You will need to refer to your vendor's documentation in order to form options to their specification. .PP The value of these options can be set in one of two ways. The first way is to simply specify the data directly, using a text string or a colon-separated list of hexadecimal values. For help in forming these strings, please refer to \fBRFC2132\fR for the DHCPv4 \fBVendor Specific Information Option\fR, \fBRFC3925\fR for the DHCPv4 \fBVendor Identified Vendor Sub Options\fR, or \fBRFC3315\fR for the DHCPv6 \fBVendor-specific Information Option\fR. For example: .PP .nf option vendor-encapsulated-options 2:4: AC:11:41:1: 3:12: 73:75:6e:64:68:63:70:2d:73:65:72:76:65:72:31:37:2d:31: 4:12: 2f:65:78:70:6f:72:74:2f:72:6f:6f:74:2f:69:38:36:70:63; option vivso 00:00:09:bf:0E: 01:0c: 48:65:6c:6c:6f:20:77:6f:72:6c:64:21; option dhcp6.vendor-opts 00:00:09:bf: 00:01:00:0c: 48:65:6c:6c:6f:20:77:6f:72:6c:64:21; .fi .PP The second way of setting the value of these options is to have the DHCP server generate a vendor-specific option buffer. To do this, you must do four things: define an option space, define some options in that option space, provide values for them, and specify that that option space should be used to generate the relevant option. .PP To define a new option space in which vendor options can be stored, use the \fRoption space\fP statement: .PP .B option .B space .I name .B [ [ code width .I number .B ] [ length width .I number .B ] [ hash size .I number .B ] ] ; .PP Where the numbers following \fBcode width\fR, \fBlength width\fR, and \fBhash size\fR respectively identify the number of bytes used to describe option codes, option lengths, and the size in buckets of the hash tables to hold options in this space (most DHCPv4 option spaces use 1 byte codes and lengths, which is the default, whereas most DHCPv6 option spaces use 2 byte codes and lengths). .PP The code and length widths are used in DHCP protocol - you must configure these numbers to match the applicable option space you are configuring. They each default to 1. Valid values for code widths are 1, 2 or 4. Valid values for length widths are 0, 1 or 2. Most DHCPv4 option spaces use 1 byte codes and lengths, which is the default, whereas most DHCPv6 option spaces use 2 byte codes and lengths. A zero-byte length produces options similar to the DHCPv6 Vendor-specific Information Option - but not their contents! .PP The hash size defaults depend upon the \fBcode width\fR selected, and may be 254 or 1009. Valid values range between 1 and 65535. Note that the higher you configure this value, the more memory will be used. It is considered good practice to configure a value that is slightly larger than the estimated number of options you plan to configure within the space. Previous versions of ISC DHCP (up to and including DHCP 3.0.*), this value was fixed at 9973. .PP The name can then be used in option definitions, as described earlier in this document. For example: .nf option space SUNW code width 1 length width 1 hash size 3; option SUNW.server-address code 2 = ip-address; option SUNW.server-name code 3 = text; option SUNW.root-path code 4 = text; option space ISC code width 1 length width 1 hash size 3; option ISC.sample code 1 = text; option vendor.ISC code 2495 = encapsulate vivso-sample; option vendor-class.ISC code 2495 = text; option ISC.sample "configuration text here"; option vendor-class.ISC "vendor class here"; option space docsis code width 2 length width 2 hash size 17; option docsis.tftp-servers code 32 = array of ip6-address; option docsis.cablelabs-configuration-file code 33 = text; option docsis.cablelabs-syslog-servers code 34 = array of ip6-address; option docsis.device-id code 36 = string; option docsis.time-servers code 37 = array of ip6-address; option docsis.time-offset code 38 = signed integer 32; option vsio.docsis code 4491 = encapsulate docsis; .fi Once you have defined an option space and the format of some options, you can set up scopes that define values for those options, and you can say when to use them. For example, suppose you want to handle two different classes of clients. Using the option space definition shown in the previous example, you can send different option values to different clients based on the vendor-class-identifier option that the clients send, as follows: .PP .nf class "vendor-classes" { match option vendor-class-identifier; } subclass "vendor-classes" "SUNW.Ultra-5_10" { vendor-option-space SUNW; option SUNW.root-path "/export/root/sparc"; } subclass "vendor-classes" "SUNW.i86pc" { vendor-option-space SUNW; option SUNW.root-path "/export/root/i86pc"; } option SUNW.server-address 172.17.65.1; option SUNW.server-name "sundhcp-server17-1"; option vivso-sample.sample "Hello world!"; option docsis.tftp-servers ::1; .fi .PP As you can see in the preceding example, regular scoping rules apply, so you can define values that are global in the global scope, and only define values that are specific to a particular class in the local scope. The \fBvendor-option-space\fR declaration tells the DHCP server to use options in the SUNW option space to construct the DHCPv4 .B vendor-encapsulated-options option. This is a limitation of that option - the DHCPv4 VIVSO and the DHCPv6 VSIO options can have multiple vendor definitions all at once (even transmitted to the same client), so it is not necessary to configure this. .SH SEE ALSO dhcpd.conf(5), dhcpd.leases(5), dhclient.conf(5), dhcp-eval(5), dhcpd(8), dhclient(8), RFC2132, RFC2131, RFC3046, RFC3315. .SH AUTHOR Information about Internet Systems Consortium can be found at .B https://www.isc.org. dhcp-4.4.1/common/dhcp4o6.c000644 000765 000024 00000007105 13243301226 015621 0ustar00tmarkstaff000000 000000 /* dhcp4o6.c DHCPv4 over DHCPv6 shared code... */ /* * Copyright (c) 2016-2017 by 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #ifdef DHCP4o6 int dhcp4o6_fd = -1; omapi_object_t *dhcp4o6_object = NULL; omapi_object_type_t *dhcp4o6_type = NULL; static int dhcp4o6_readsocket(omapi_object_t *); /* * DHCPv4 over DHCPv6 Inter Process Communication setup * * A UDP socket is created between ::1 port and ::1 port + 1 * (port is given in network order, the DHCPv6 side is bound to port, * the DHCPv4 side to port + 1. The socket descriptor is stored into * dhcp4o6_fd and an OMAPI handler is registered. Any failure is fatal.) */ void dhcp4o6_setup(u_int16_t port) { struct sockaddr_in6 local6, remote6; int flag; isc_result_t status; /* Register DHCPv4 over DHCPv6 forwarding. */ memset(&local6, 0, sizeof(local6)); local6.sin6_family = AF_INET6; if (local_family == AF_INET6) local6.sin6_port = port; else local6.sin6_port = htons(ntohs(port) + 1); local6.sin6_addr.s6_addr[15] = 1; #ifdef HAVE_SA_LEN local6.sin6_len = sizeof(local6); #endif memset(&remote6, 0, sizeof(remote6)); remote6.sin6_family = AF_INET6; if (local_family == AF_INET6) remote6.sin6_port = htons(ntohs(port) + 1); else remote6.sin6_port = port; remote6.sin6_addr.s6_addr[15] = 1; #ifdef HAVE_SA_LEN remote6.sin6_len = sizeof(remote6); #endif dhcp4o6_fd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); if (dhcp4o6_fd < 0) log_fatal("Can't create dhcp4o6 socket: %m"); flag = 1; if (setsockopt(dhcp4o6_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag)) < 0) log_fatal("Can't set SO_REUSEADDR option " "on dhcp4o6 socket: %m"); if (bind(dhcp4o6_fd, (struct sockaddr *)&local6, sizeof(local6)) < 0) log_fatal("Can't bind dhcp4o6 socket: %m"); if (connect(dhcp4o6_fd, (struct sockaddr *)&remote6, sizeof(remote6)) < 0) log_fatal("Can't connect dhcp4o6 socket: %m"); /* Omapi stuff. */ /* TODO: add tracing support. */ status = omapi_object_type_register(&dhcp4o6_type, "dhcp4o6", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sizeof(*dhcp4o6_object), 0, RC_MISC); if (status != ISC_R_SUCCESS) log_fatal("Can't register dhcp4o6 type: %s", isc_result_totext(status)); status = omapi_object_allocate(&dhcp4o6_object, dhcp4o6_type, 0, MDL); if (status != ISC_R_SUCCESS) log_fatal("Can't allocate dhcp4o6 object: %s", isc_result_totext(status)); status = omapi_register_io_object(dhcp4o6_object, dhcp4o6_readsocket, 0, dhcpv4o6_handler, 0, 0); if (status != ISC_R_SUCCESS) log_fatal("Can't register dhcp4o6 handle: %s", isc_result_totext(status)); } static int dhcp4o6_readsocket(omapi_object_t *h) { IGNORE_UNUSED(h); return dhcp4o6_fd; } #endif /* DHCP4o6 */ dhcp-4.4.1/common/discover.c000644 000765 000024 00000123557 13243301226 016202 0ustar00tmarkstaff000000 000000 /* discover.c Find and identify the network interfaces. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1995-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" /* length of line we can read from the IF file, 256 is too small in some cases */ #define IF_LINE_LENGTH 1024 #define BSD_COMP /* needed on Solaris for SIOCGLIFNUM */ #include #include #ifdef HAVE_NET_IF6_H # include #endif struct interface_info *interfaces, *dummy_interfaces, *fallback_interface; int interfaces_invalidated; int quiet_interface_discovery; u_int16_t local_port; u_int16_t remote_port; u_int16_t relay_port = 0; int dhcpv4_over_dhcpv6 = 0; int (*dhcp_interface_setup_hook) (struct interface_info *, struct iaddr *); int (*dhcp_interface_discovery_hook) (struct interface_info *); isc_result_t (*dhcp_interface_startup_hook) (struct interface_info *); int (*dhcp_interface_shutdown_hook) (struct interface_info *); struct in_addr limited_broadcast; int local_family = AF_INET; struct in_addr local_address; #ifdef DHCPv6 /* * Another clear abuse of the fact that undefined IP addresses are all zeroes. */ struct in6_addr local_address6; int bind_local_address6 = 0; #endif /* DHCPv6 */ void (*bootp_packet_handler) (struct interface_info *, struct dhcp_packet *, unsigned, unsigned int, struct iaddr, struct hardware *); #ifdef DHCPv6 void (*dhcpv6_packet_handler)(struct interface_info *, const char *, int, int, const struct iaddr *, isc_boolean_t); #endif /* DHCPv6 */ omapi_object_type_t *dhcp_type_interface; #if defined (TRACING) trace_type_t *interface_trace; trace_type_t *inpacket_trace; trace_type_t *outpacket_trace; #endif struct interface_info **interface_vector; int interface_count; int interface_max; OMAPI_OBJECT_ALLOC (interface, struct interface_info, dhcp_type_interface) isc_result_t interface_setup () { isc_result_t status; status = omapi_object_type_register (&dhcp_type_interface, "interface", dhcp_interface_set_value, dhcp_interface_get_value, dhcp_interface_destroy, dhcp_interface_signal_handler, dhcp_interface_stuff_values, dhcp_interface_lookup, dhcp_interface_create, dhcp_interface_remove, 0, 0, 0, sizeof (struct interface_info), interface_initialize, RC_MISC); if (status != ISC_R_SUCCESS) log_fatal ("Can't register interface object type: %s", isc_result_totext (status)); return status; } #if defined (TRACING) void interface_trace_setup () { interface_trace = trace_type_register ("interface", (void *)0, trace_interface_input, trace_interface_stop, MDL); inpacket_trace = trace_type_register ("inpacket", (void *)0, trace_inpacket_input, trace_inpacket_stop, MDL); outpacket_trace = trace_type_register ("outpacket", (void *)0, trace_outpacket_input, trace_outpacket_stop, MDL); } #endif isc_result_t interface_initialize (omapi_object_t *ipo, const char *file, int line) { struct interface_info *ip = (struct interface_info *)ipo; ip -> rfdesc = ip -> wfdesc = -1; return ISC_R_SUCCESS; } /* * Scanning for Interfaces * ----------------------- * * To find interfaces, we create an iterator that abstracts out most * of the platform specifics. Use is fairly straightforward: * * - begin_iface_scan() starts the process. * - Use next_iface() until it returns 0. * - end_iface_scan() performs any necessary cleanup. * * We check for errors on each call to next_iface(), which returns a * description of the error as a string if any occurs. * * We currently have code for Solaris and Linux. Other systems need * to have code written. * * NOTE: the long-term goal is to use the interface code from BIND 9. */ #if defined(SIOCGLIFCONF) && defined(SIOCGLIFNUM) && defined(SIOCGLIFFLAGS) /* HP/UX doesn't define struct lifconf, instead they define struct * if_laddrconf. Similarly, 'struct lifreq' and 'struct lifaddrreq'. */ #ifdef ISC_PLATFORM_HAVEIF_LADDRCONF # define lifc_len iflc_len # define lifc_buf iflc_buf # define lifc_req iflc_req # define LIFCONF if_laddrconf #else # define ISC_HAVE_LIFC_FAMILY 1 # define ISC_HAVE_LIFC_FLAGS 1 # define LIFCONF lifconf #endif #ifdef ISC_PLATFORM_HAVEIF_LADDRREQ # define lifr_addr iflr_addr # define lifr_name iflr_name # define lifr_dstaddr iflr_dstaddr # define lifr_flags iflr_flags # define sockaddr_storage sockaddr_ext # define ss_family sa_family # define LIFREQ if_laddrreq #else # define LIFREQ lifreq #endif #ifndef IF_NAMESIZE # if defined(LIFNAMSIZ) # define IF_NAMESIZE LIFNAMSIZ # elif defined(IFNAMSIZ) # define IF_NAMESIZE IFNAMSIZ # else # define IF_NAMESIZE 16 # endif #endif #elif !defined(__linux) && !defined(HAVE_IFADDRS_H) # define SIOCGLIFCONF SIOCGIFCONF # define SIOCGLIFFLAGS SIOCGIFFLAGS # define LIFREQ ifreq # define LIFCONF ifconf # define lifr_name ifr_name # define lifr_addr ifr_addr # define lifr_flags ifr_flags # define lifc_len ifc_len # define lifc_buf ifc_buf # define lifc_req ifc_req #ifdef _AIX # define ss_family __ss_family #endif #endif #if defined(SIOCGLIFCONF) && defined(SIOCGLIFFLAGS) /* * Solaris support * --------------- * * The SIOCGLIFCONF ioctl() are the extension that you need to use * on Solaris to get information about IPv6 addresses. * * Solaris' extended interface is documented in the if_tcp man page. */ /* * Structure holding state about the scan. */ struct iface_conf_list { int sock; /* file descriptor used to get information */ int num; /* total number of interfaces */ struct LIFCONF conf; /* structure used to get information */ int next; /* next interface to retrieve when iterating */ }; /* * Structure used to return information about a specific interface. */ struct iface_info { char name[IF_NAMESIZE+1]; /* name of the interface, e.g. "bge0" */ struct sockaddr_storage addr; /* address information */ isc_uint64_t flags; /* interface flags, e.g. IFF_LOOPBACK */ }; /* * Start a scan of interfaces. * * The iface_conf_list structure maintains state for this process. */ int begin_iface_scan(struct iface_conf_list *ifaces) { #ifdef ISC_PLATFORM_HAVELIFNUM struct lifnum lifnum; #else int lifnum; #endif ifaces->sock = socket(local_family, SOCK_DGRAM, IPPROTO_UDP); if (ifaces->sock < 0) { log_error("Error creating socket to list interfaces; %m"); return 0; } memset(&lifnum, 0, sizeof(lifnum)); #ifdef ISC_PLATFORM_HAVELIFNUM lifnum.lifn_family = AF_UNSPEC; #endif #ifdef SIOCGLIFNUM if (ioctl(ifaces->sock, SIOCGLIFNUM, &lifnum) < 0) { log_error("Error finding total number of interfaces; %m"); close(ifaces->sock); ifaces->sock = -1; return 0; } #ifdef ISC_PLATFORM_HAVELIFNUM ifaces->num = lifnum.lifn_count; #else ifaces->num = lifnum; #endif #else ifaces->num = 64; #endif /* SIOCGLIFNUM */ memset(&ifaces->conf, 0, sizeof(ifaces->conf)); #ifdef ISC_HAVE_LIFC_FAMILY ifaces->conf.lifc_family = AF_UNSPEC; #endif ifaces->conf.lifc_len = ifaces->num * sizeof(struct LIFREQ); ifaces->conf.lifc_buf = dmalloc(ifaces->conf.lifc_len, MDL); if (ifaces->conf.lifc_buf == NULL) { log_fatal("Out of memory getting interface list."); } if (ioctl(ifaces->sock, SIOCGLIFCONF, &ifaces->conf) < 0) { log_error("Error getting interfaces configuration list; %m"); dfree(ifaces->conf.lifc_buf, MDL); close(ifaces->sock); ifaces->sock = -1; return 0; } ifaces->next = 0; return 1; } /* * Retrieve the next interface. * * Returns information in the info structure. * Sets err to 1 if there is an error, otherwise 0. */ int next_iface(struct iface_info *info, int *err, struct iface_conf_list *ifaces) { struct LIFREQ *p; struct LIFREQ tmp; isc_boolean_t foundif; #if defined(sun) || defined(__linux) /* Pointer used to remove interface aliases. */ char *s; #endif do { foundif = ISC_FALSE; if (ifaces->next >= ifaces->num) { *err = 0; return 0; } p = ifaces->conf.lifc_req; p += ifaces->next; if (strlen(p->lifr_name) >= sizeof(info->name)) { *err = 1; log_error("Interface name '%s' too long", p->lifr_name); return 0; } /* Reject if interface address family does not match */ if (p->lifr_addr.ss_family != local_family) { ifaces->next++; continue; } memset(info, 0, sizeof(struct iface_info)); strncpy(info->name, p->lifr_name, sizeof(info->name) - 1); memcpy(&info->addr, &p->lifr_addr, sizeof(p->lifr_addr)); #if defined(sun) || defined(__linux) /* interface aliases look like "eth0:1" or "wlan1:3" */ s = strchr(info->name, ':'); if (s != NULL) { *s = '\0'; } #endif /* defined(sun) || defined(__linux) */ foundif = ISC_TRUE; } while ((foundif == ISC_FALSE) || (strncmp(info->name, "dummy", 5) == 0)); memset(&tmp, 0, sizeof(tmp)); strncpy(tmp.lifr_name, info->name, sizeof(tmp.lifr_name) - 1); if (ioctl(ifaces->sock, SIOCGLIFFLAGS, &tmp) < 0) { log_error("Error getting interface flags for '%s'; %m", p->lifr_name); *err = 1; return 0; } info->flags = tmp.lifr_flags; ifaces->next++; *err = 0; return 1; } /* * End scan of interfaces. */ void end_iface_scan(struct iface_conf_list *ifaces) { dfree(ifaces->conf.lifc_buf, MDL); close(ifaces->sock); ifaces->sock = -1; } #else /* * BSD/Linux support * ----------- * * FreeBSD, NetBSD, OpenBSD, OS X/macOS and Linux all have the getifaddrs() * function. * * The getifaddrs() man page describes the use. */ #include /* * Structure holding state about the scan. */ struct iface_conf_list { struct ifaddrs *head; /* beginning of the list */ struct ifaddrs *next; /* current position in the list */ }; /* * Structure used to return information about a specific interface. */ struct iface_info { char name[IFNAMSIZ]; /* name of the interface, e.g. "bge0" */ struct sockaddr_storage addr; /* address information */ isc_uint64_t flags; /* interface flags, e.g. IFF_LOOPBACK */ }; /* * Start a scan of interfaces. * * The iface_conf_list structure maintains state for this process. */ int begin_iface_scan(struct iface_conf_list *ifaces) { if (getifaddrs(&ifaces->head) != 0) { log_error("Error getting interfaces; %m"); return 0; } ifaces->next = ifaces->head; return 1; } /* * Retrieve the next interface. * * Returns information in the info structure. * Sets err to 1 if there is an error, otherwise 0. */ int next_iface(struct iface_info *info, int *err, struct iface_conf_list *ifaces) { size_t sa_len = 0; if (ifaces->next == NULL) { *err = 0; return 0; } if (strlen(ifaces->next->ifa_name) >= sizeof(info->name)) { log_error("Interface name '%s' too long", ifaces->next->ifa_name); *err = 1; return 0; } memset(info, 0, sizeof(struct iface_info)); strncpy(info->name, ifaces->next->ifa_name, sizeof(info->name) - 1); memset(&info->addr, 0 , sizeof(info->addr)); /* * getifaddrs() can on Linux with some interfaces like PPP or TEQL * result in a record with no address (ifa_addr). */ if (ifaces->next->ifa_addr != NULL) { /* Linux lacks the sa_len member in struct sockaddr. */ #if defined(__linux) if (ifaces->next->ifa_addr->sa_family == AF_INET) sa_len = sizeof(struct sockaddr_in); else if (ifaces->next->ifa_addr->sa_family == AF_INET6) sa_len = sizeof(struct sockaddr_in6); #else sa_len = ifaces->next->ifa_addr->sa_len; #endif memcpy(&info->addr, ifaces->next->ifa_addr, sa_len); } info->flags = ifaces->next->ifa_flags; ifaces->next = ifaces->next->ifa_next; *err = 0; return 1; } /* * End scan of interfaces. */ void end_iface_scan(struct iface_conf_list *ifaces) { freeifaddrs(ifaces->head); ifaces->head = NULL; ifaces->next = NULL; } #endif /* XXX: perhaps create drealloc() rather than do it manually */ void add_ipv4_addr_to_interface(struct interface_info *iface, const struct in_addr *addr) { /* * We don't expect a lot of addresses per IPv4 interface, so * we use 4, as our "chunk size" for collecting addresses. */ if (iface->addresses == NULL) { iface->addresses = dmalloc(4 * sizeof(struct in_addr), MDL); if (iface->addresses == NULL) { log_fatal("Out of memory saving IPv4 address " "on interface."); } iface->address_count = 0; iface->address_max = 4; } else if (iface->address_count >= iface->address_max) { struct in_addr *tmp; int new_max; new_max = iface->address_max + 4; tmp = dmalloc(new_max * sizeof(struct in_addr), MDL); if (tmp == NULL) { log_fatal("Out of memory saving IPv4 address " "on interface."); } memcpy(tmp, iface->addresses, iface->address_max * sizeof(struct in_addr)); dfree(iface->addresses, MDL); iface->addresses = tmp; iface->address_max = new_max; } iface->addresses[iface->address_count++] = *addr; } #ifdef DHCPv6 /* XXX: perhaps create drealloc() rather than do it manually */ void add_ipv6_addr_to_interface(struct interface_info *iface, const struct in6_addr *addr) { /* * Each IPv6 interface will have at least two IPv6 addresses, * and likely quite a few more. So we use 8, as our "chunk size" for * collecting addresses. */ if (iface->v6addresses == NULL) { iface->v6addresses = dmalloc(8 * sizeof(struct in6_addr), MDL); if (iface->v6addresses == NULL) { log_fatal("Out of memory saving IPv6 address " "on interface."); } iface->v6address_count = 0; iface->v6address_max = 8; } else if (iface->v6address_count >= iface->v6address_max) { struct in6_addr *tmp; int new_max; new_max = iface->v6address_max + 8; tmp = dmalloc(new_max * sizeof(struct in6_addr), MDL); if (tmp == NULL) { log_fatal("Out of memory saving IPv6 address " "on interface."); } memcpy(tmp, iface->v6addresses, iface->v6address_max * sizeof(struct in6_addr)); dfree(iface->v6addresses, MDL); iface->v6addresses = tmp; iface->v6address_max = new_max; } iface->v6addresses[iface->v6address_count++] = *addr; } #endif /* DHCPv6 */ /* Use the SIOCGIFCONF ioctl to get a list of all the attached interfaces. For each interface that's of type INET and not the loopback interface, register that interface with the network I/O software, figure out what subnet it's on, and add it to the list of interfaces. */ void discover_interfaces(int state) { struct iface_conf_list ifaces; struct iface_info info; int err; struct interface_info *tmp; struct interface_info *last, *next; #ifdef DHCPv6 char abuf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; #endif /* DHCPv6 */ struct subnet *subnet; int ir; isc_result_t status; int wifcount = 0; #ifdef RELAY_PORT int updone = 0; int downdone = 0; #endif static int setup_fallback = 0; if (!begin_iface_scan(&ifaces)) { log_fatal("Can't get list of interfaces."); } /* If we already have a list of interfaces, and we're running as a DHCP server, the interfaces were requested. */ if (interfaces && (state == DISCOVER_SERVER || state == DISCOVER_RELAY || state == DISCOVER_REQUESTED)) ir = 0; else if (state == DISCOVER_UNCONFIGURED) ir = INTERFACE_REQUESTED | INTERFACE_AUTOMATIC; else { ir = INTERFACE_REQUESTED; if (state == DISCOVER_RELAY && local_family == AF_INET) { /* We're a v4 relay without specifically requested * interfaces, so mark them all as bidirectional. */ ir |= INTERFACE_STREAMS; } } /* Cycle through the list of interfaces looking for IP addresses. */ while (next_iface(&info, &err, &ifaces)) { /* See if we've seen an interface that matches this one. */ for (tmp = interfaces; tmp; tmp = tmp->next) { if (!strcmp(tmp->name, info.name)) break; } /* Skip non broadcast interfaces (plus loopback and point-to-point in case an OS incorrectly marks them as broadcast). Also skip down interfaces unless we're trying to get a list of configurable interfaces. */ if ((((local_family == AF_INET && !(info.flags & IFF_BROADCAST)) || #ifdef DHCPv6 (local_family == AF_INET6 && !(info.flags & IFF_MULTICAST)) || #endif info.flags & IFF_LOOPBACK || info.flags & IFF_POINTOPOINT) && !tmp) || (!(info.flags & IFF_UP) && state != DISCOVER_UNCONFIGURED)) continue; /* If there isn't already an interface by this name, allocate one. */ if (tmp == NULL) { status = interface_allocate(&tmp, MDL); if (status != ISC_R_SUCCESS) { log_fatal("Error allocating interface %s: %s", info.name, isc_result_totext(status)); } strncpy(tmp->name, info.name, sizeof(tmp->name) - 1); interface_snorf(tmp, ir); interface_dereference(&tmp, MDL); tmp = interfaces; /* XXX */ } if (dhcp_interface_discovery_hook) { (*dhcp_interface_discovery_hook)(tmp); } if ((info.addr.ss_family == AF_INET) && (local_family == AF_INET)) { struct sockaddr_in *a = (struct sockaddr_in*)&info.addr; struct iaddr addr; /* We don't want the loopback interface. */ if (a->sin_addr.s_addr == htonl(INADDR_LOOPBACK) && ((tmp->flags & INTERFACE_AUTOMATIC) && ((state == DISCOVER_SERVER) || (state == DISCOVER_SERVER46)))) continue; /* If the only address we have is 0.0.0.0, we shouldn't consider the interface configured. */ if (a->sin_addr.s_addr != htonl(INADDR_ANY)) tmp->configured = 1; add_ipv4_addr_to_interface(tmp, &a->sin_addr); /* invoke the setup hook */ addr.len = 4; memcpy(addr.iabuf, &a->sin_addr.s_addr, addr.len); if (dhcp_interface_setup_hook) { (*dhcp_interface_setup_hook)(tmp, &addr); } } #ifdef DHCPv6 else if ((info.addr.ss_family == AF_INET6) && (local_family == AF_INET6)) { struct sockaddr_in6 *a = (struct sockaddr_in6*)&info.addr; struct iaddr addr; /* We don't want the loopback interface. */ if (IN6_IS_ADDR_LOOPBACK(&a->sin6_addr) && ((tmp->flags & INTERFACE_AUTOMATIC) && ((state == DISCOVER_SERVER) || (state == DISCOVER_SERVER46)))) continue; /* If the only address we have is 0.0.0.0, we shouldn't consider the interface configured. */ if (IN6_IS_ADDR_UNSPECIFIED(&a->sin6_addr)) tmp->configured = 1; add_ipv6_addr_to_interface(tmp, &a->sin6_addr); /* invoke the setup hook */ addr.len = 16; memcpy(addr.iabuf, &a->sin6_addr, addr.len); if (dhcp_interface_setup_hook) { (*dhcp_interface_setup_hook)(tmp, &addr); } } #endif /* DHCPv6 */ } if (err) { log_fatal("Error getting interface information."); } end_iface_scan(&ifaces); /* Mock-up an 'ifp' structure which is no longer used in the * new interface-sensing code, but is used in higher layers * (for example to sense fallback interfaces). */ for (tmp = interfaces ; tmp != NULL ; tmp = tmp->next) { if (tmp->ifp == NULL) { struct ifreq *tif; tif = (struct ifreq *)dmalloc(sizeof(struct ifreq), MDL); if (tif == NULL) log_fatal("no space for ifp mockup."); strcpy(tif->ifr_name, tmp->name); tmp->ifp = tif; } } /* If we're just trying to get a list of interfaces that we might be able to configure, we can quit now. */ if (state == DISCOVER_UNCONFIGURED) { return; } /* Weed out the interfaces that did not have IP addresses. */ tmp = last = next = NULL; if (interfaces) interface_reference (&tmp, interfaces, MDL); while (tmp) { if (next) interface_dereference (&next, MDL); if (tmp -> next) interface_reference (&next, tmp -> next, MDL); /* skip interfaces that are running already */ if (tmp -> flags & INTERFACE_RUNNING) { interface_dereference(&tmp, MDL); if(next) interface_reference(&tmp, next, MDL); continue; } if ((tmp -> flags & INTERFACE_AUTOMATIC) && state == DISCOVER_REQUESTED) tmp -> flags &= ~(INTERFACE_AUTOMATIC | INTERFACE_REQUESTED); #ifdef DHCPv6 if (!(tmp->flags & INTERFACE_REQUESTED)) { #else if (!tmp -> ifp || !(tmp -> flags & INTERFACE_REQUESTED)) { #endif /* DHCPv6 */ if ((tmp -> flags & INTERFACE_REQUESTED) != ir) log_fatal ("%s: not found", tmp -> name); if (!last) { if (interfaces) interface_dereference (&interfaces, MDL); if (next) interface_reference (&interfaces, next, MDL); } else { interface_dereference (&last -> next, MDL); if (next) interface_reference (&last -> next, next, MDL); } if (tmp -> next) interface_dereference (&tmp -> next, MDL); /* Remember the interface in case we need to know about it later. */ if (dummy_interfaces) { interface_reference (&tmp -> next, dummy_interfaces, MDL); interface_dereference (&dummy_interfaces, MDL); } interface_reference (&dummy_interfaces, tmp, MDL); interface_dereference (&tmp, MDL); if (next) interface_reference (&tmp, next, MDL); continue; } last = tmp; /* We must have a subnet declaration for each interface. */ if (!tmp->shared_network && (state == DISCOVER_SERVER)) { log_error("%s", ""); if (local_family == AF_INET) { log_error("No subnet declaration for %s (%s).", tmp->name, (tmp->addresses == NULL) ? "no IPv4 addresses" : inet_ntoa(tmp->addresses[0])); #ifdef DHCPv6 } else { if (tmp->v6addresses != NULL) { inet_ntop(AF_INET6, &tmp->v6addresses[0], abuf, sizeof(abuf)); } else { strcpy(abuf, "no IPv6 addresses"); } log_error("No subnet6 declaration for %s (%s).", tmp->name, abuf); #endif /* DHCPv6 */ } if (supports_multiple_interfaces(tmp)) { log_error ("** Ignoring requests on %s. %s", tmp -> name, "If this is not what"); log_error (" you want, please write %s", #ifdef DHCPv6 (local_family != AF_INET) ? "a subnet6 declaration" : #endif "a subnet declaration"); log_error (" in your dhcpd.conf file %s", "for the network segment"); log_error (" to %s %s %s", "which interface", tmp -> name, "is attached. **"); log_error ("%s", ""); goto next; } else { log_error ("You must write a %s", #ifdef DHCPv6 (local_family != AF_INET) ? "subnet6 declaration for this" : #endif "subnet declaration for this"); log_error ("subnet. You cannot prevent %s", "the DHCP server"); log_error ("from listening on this subnet %s", "because your"); log_fatal ("operating system does not %s.", "support this capability"); } } /* Find subnets that don't have valid interface addresses... */ for (subnet = (tmp -> shared_network ? tmp -> shared_network -> subnets : (struct subnet *)0); subnet; subnet = subnet -> next_sibling) { /* Set the interface address for this subnet to the first address we found. */ if (subnet->interface_address.len == 0) { if (tmp->address_count > 0) { subnet->interface_address.len = 4; memcpy(subnet->interface_address.iabuf, &tmp->addresses[0].s_addr, 4); } else if (tmp->v6address_count > 0) { subnet->interface_address.len = 16; memcpy(subnet->interface_address.iabuf, &tmp->v6addresses[0].s6_addr, 16); } else { /* XXX: should be one */ log_error("%s missing an interface " "address", tmp->name); continue; } } } /* Flag the index as not having been set, so that the interface registerer can set it or not as it chooses. */ tmp -> index = -1; /* Register the interface... */ switch (local_family) { case AF_INET: if (!dhcpv4_over_dhcpv6) { if_register_receive(tmp); if_register_send(tmp); } else { /* get_hw_addr() was called by register. */ get_hw_addr(tmp->name, &tmp->hw_address); } break; #ifdef DHCPv6 case AF_INET6: if ((state == DISCOVER_SERVER) || (state == DISCOVER_RELAY)) { if_register6(tmp, 1); } else if (state == DISCOVER_SERVER46) { /* get_hw_addr() was called by if_register*6 so now we have to call it explicitly to not leave the hardware address unknown (some code expects it cannot be. */ get_hw_addr(tmp->name, &tmp->hw_address); } else { if_register_linklocal6(tmp); } break; #endif /* DHCPv6 */ } interface_stash (tmp); wifcount++; #if defined (F_SETFD) /* if_register*() are no longer always called so descriptors must be checked. */ if ((tmp -> rfdesc >= 0) && (fcntl (tmp -> rfdesc, F_SETFD, 1) < 0)) log_error ("Can't set close-on-exec on %s: %m", tmp -> name); if ((tmp -> wfdesc != tmp -> rfdesc) && (tmp -> wfdesc >= 0) && (fcntl (tmp -> wfdesc, F_SETFD, 1) < 0)) log_error ("Can't set close-on-exec on %s: %m", tmp -> name); #endif next: interface_dereference (&tmp, MDL); if (next) interface_reference (&tmp, next, MDL); } /* * Now register all the remaining interfaces as protocols. * We register with omapi to allow for control of the interface, * we've already registered the fd or socket with the socket * manager as part of if_register_receive(). */ for (tmp = interfaces; tmp; tmp = tmp -> next) { /* not if it's been registered before */ if (tmp -> flags & INTERFACE_RUNNING) continue; if (tmp -> rfdesc == -1) continue; switch (local_family) { #ifdef DHCPv6 case AF_INET6: #ifdef RELAY_PORT #define UPSTREAM(ifp) \ ((ifp->flags & INTERFACE_STREAMS) == INTERFACE_UPSTREAM) #define DOWNSTREAM(ifp) \ ((ifp->flags & INTERFACE_STREAMS) == INTERFACE_DOWNSTREAM) if (relay_port) { /* * The normal IPv6 relay only needs one * socket as long as we find an interface. * When user relay port is defined, and we * have two different UDP ports. One to * receive from DHCP client with port 547, * and the other is user defined for sending * to the server or upstream relay agent. * Thus we need to register sockets for one * upstream and one downstream interfaces. */ if (updone && UPSTREAM(tmp)) continue; if (downdone && DOWNSTREAM(tmp)) continue; } #endif status = omapi_register_io_object((omapi_object_t *)tmp, if_readsocket, 0, got_one_v6, 0, 0); #ifdef RELAY_PORT if (UPSTREAM(tmp)) updone++; else downdone++; #endif break; #endif /* DHCPv6 */ case AF_INET: default: status = omapi_register_io_object((omapi_object_t *)tmp, if_readsocket, 0, got_one, 0, 0); break; } if (status != ISC_R_SUCCESS) log_fatal ("Can't register I/O handle for %s: %s", tmp -> name, isc_result_totext (status)); #if defined(DHCPv6) /* Only register the first interface for V6, since * servers and relays all use the same socket. * XXX: This has some messy side effects if we start * dynamically adding and removing interfaces, but * we're well beyond that point in terms of mess. */ if (((state == DISCOVER_SERVER) || (state == DISCOVER_RELAY)) && (local_family == AF_INET6) #if defined(RELAY_PORT) && ((relay_port == 0) || (updone && downdone)) #endif ) break; #endif } /* for (tmp = interfaces; ... */ if (state == DISCOVER_SERVER && wifcount == 0) { log_info ("%s", ""); log_fatal ("Not configured to listen on any interfaces!"); } if ((local_family == AF_INET) && !setup_fallback && !dhcpv4_over_dhcpv6) { setup_fallback = 1; maybe_setup_fallback(); } #if defined (F_SETFD) if (fallback_interface) { if (fcntl (fallback_interface -> rfdesc, F_SETFD, 1) < 0) log_error ("Can't set close-on-exec on fallback: %m"); if (fallback_interface -> rfdesc != fallback_interface -> wfdesc) { if (fcntl (fallback_interface -> wfdesc, F_SETFD, 1) < 0) log_error ("Can't set close-on-exec on fallback: %m"); } } #endif /* F_SETFD */ } int if_readsocket (h) omapi_object_t *h; { struct interface_info *ip; if (h -> type != dhcp_type_interface) return -1; ip = (struct interface_info *)h; return ip -> rfdesc; } int setup_fallback (struct interface_info **fp, const char *file, int line) { isc_result_t status; status = interface_allocate (&fallback_interface, file, line); if (status != ISC_R_SUCCESS) log_fatal ("Error allocating fallback interface: %s", isc_result_totext (status)); strcpy (fallback_interface -> name, "fallback"); if (dhcp_interface_setup_hook) (*dhcp_interface_setup_hook) (fallback_interface, (struct iaddr *)0); status = interface_reference (fp, fallback_interface, file, line); fallback_interface -> index = -1; interface_stash (fallback_interface); return status == ISC_R_SUCCESS; } void reinitialize_interfaces () { struct interface_info *ip; for (ip = interfaces; ip; ip = ip -> next) { if_reinitialize_receive (ip); if_reinitialize_send (ip); } if (fallback_interface) if_reinitialize_send (fallback_interface); interfaces_invalidated = 1; } isc_result_t got_one (h) omapi_object_t *h; { struct sockaddr_in from; struct hardware hfrom; struct iaddr ifrom; int result; union { unsigned char packbuf [4095]; /* Packet input buffer. Must be as large as largest possible MTU. */ struct dhcp_packet packet; } u; struct interface_info *ip; if (h -> type != dhcp_type_interface) return DHCP_R_INVALIDARG; ip = (struct interface_info *)h; again: if ((result = receive_packet (ip, u.packbuf, sizeof u, &from, &hfrom)) < 0) { log_error ("receive_packet failed on %s: %m", ip -> name); return ISC_R_UNEXPECTED; } if (result == 0) return ISC_R_UNEXPECTED; /* * If we didn't at least get the fixed portion of the BOOTP * packet, drop the packet. * Previously we allowed packets with no sname or filename * as we were aware of at least one client that did. But * a bug caused short packets to not work and nobody has * complained, it seems rational to tighten up that * restriction. */ if (result < DHCP_FIXED_NON_UDP) return ISC_R_UNEXPECTED; #if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO) { /* We retrieve the ifindex from the unused hfrom variable */ unsigned int ifindex; memcpy(&ifindex, hfrom.hbuf, sizeof (ifindex)); /* * Seek forward from the first interface to find the matching * source interface by interface index. */ ip = interfaces; while ((ip != NULL) && (if_nametoindex(ip->name) != ifindex)) ip = ip->next; if (ip == NULL) return ISC_R_NOTFOUND; } #endif if (bootp_packet_handler) { ifrom.len = 4; memcpy (ifrom.iabuf, &from.sin_addr, ifrom.len); (*bootp_packet_handler) (ip, &u.packet, (unsigned)result, from.sin_port, ifrom, &hfrom); } /* If there is buffered data, read again. This is for, e.g., bpf, which may return two packets at once. */ if (ip -> rbuf_offset != ip -> rbuf_len) goto again; return ISC_R_SUCCESS; } #ifdef DHCPv6 isc_result_t got_one_v6(omapi_object_t *h) { struct sockaddr_in6 from; struct in6_addr to; struct iaddr ifrom; int result; char buf[65536]; /* maximum size for a UDP packet is 65536 */ struct interface_info *ip; int is_unicast; unsigned int if_idx = 0; if (h->type != dhcp_type_interface) { return DHCP_R_INVALIDARG; } ip = (struct interface_info *)h; result = receive_packet6(ip, (unsigned char *)buf, sizeof(buf), &from, &to, &if_idx); if (result < 0) { log_error("receive_packet6() failed on %s: %m", ip->name); return ISC_R_UNEXPECTED; } /* 0 is 'any' interface. */ if (if_idx == 0) return ISC_R_NOTFOUND; if (dhcpv6_packet_handler != NULL) { /* * If a packet is not multicast, we assume it is unicast. */ if (IN6_IS_ADDR_MULTICAST(&to)) { is_unicast = ISC_FALSE; } else { is_unicast = ISC_TRUE; } ifrom.len = 16; memcpy(ifrom.iabuf, &from.sin6_addr, ifrom.len); /* Seek forward to find the matching source interface. */ ip = interfaces; while ((ip != NULL) && (if_nametoindex(ip->name) != if_idx)) ip = ip->next; if (ip == NULL) return ISC_R_NOTFOUND; (*dhcpv6_packet_handler)(ip, buf, result, from.sin6_port, &ifrom, is_unicast); } return ISC_R_SUCCESS; } #endif /* DHCPv6 */ isc_result_t dhcp_interface_set_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_typed_data_t *value) { struct interface_info *interface; isc_result_t status; if (h -> type != dhcp_type_interface) return DHCP_R_INVALIDARG; interface = (struct interface_info *)h; if (!omapi_ds_strcmp (name, "name")) { if ((value -> type == omapi_datatype_data || value -> type == omapi_datatype_string) && value -> u.buffer.len < sizeof interface -> name) { memcpy (interface -> name, value -> u.buffer.value, value -> u.buffer.len); interface -> name [value -> u.buffer.len] = 0; } else return DHCP_R_INVALIDARG; return ISC_R_SUCCESS; } /* Try to find some inner object that can take the value. */ if (h -> inner && h -> inner -> type -> set_value) { status = ((*(h -> inner -> type -> set_value)) (h -> inner, id, name, value)); if (status == ISC_R_SUCCESS || status == DHCP_R_UNCHANGED) return status; } return ISC_R_NOTFOUND; } isc_result_t dhcp_interface_get_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_value_t **value) { return ISC_R_NOTIMPLEMENTED; } isc_result_t dhcp_interface_destroy (omapi_object_t *h, const char *file, int line) { struct interface_info *interface; if (h -> type != dhcp_type_interface) return DHCP_R_INVALIDARG; interface = (struct interface_info *)h; if (interface -> ifp) { dfree (interface -> ifp, file, line); interface -> ifp = 0; } if (interface -> next) interface_dereference (&interface -> next, file, line); if (interface -> rbuf) { dfree (interface -> rbuf, file, line); interface -> rbuf = (unsigned char *)0; } if (interface -> client) interface -> client = (struct client_state *)0; if (interface -> shared_network) omapi_object_dereference ((omapi_object_t **) &interface -> shared_network, MDL); return ISC_R_SUCCESS; } isc_result_t dhcp_interface_signal_handler (omapi_object_t *h, const char *name, va_list ap) { struct interface_info *ip, *interface; isc_result_t status; if (h -> type != dhcp_type_interface) return DHCP_R_INVALIDARG; interface = (struct interface_info *)h; /* If it's an update signal, see if the interface is dead right now, or isn't known at all, and if that's the case, revive it. */ if (!strcmp (name, "update")) { for (ip = dummy_interfaces; ip; ip = ip -> next) if (ip == interface) break; if (ip && dhcp_interface_startup_hook) return (*dhcp_interface_startup_hook) (ip); for (ip = interfaces; ip; ip = ip -> next) if (ip == interface) break; if (!ip && dhcp_interface_startup_hook) return (*dhcp_interface_startup_hook) (ip); } /* Try to find some inner object that can take the value. */ if (h -> inner && h -> inner -> type -> signal_handler) { status = ((*(h -> inner -> type -> signal_handler)) (h -> inner, name, ap)); if (status == ISC_R_SUCCESS) return status; } return ISC_R_NOTFOUND; } isc_result_t dhcp_interface_stuff_values (omapi_object_t *c, omapi_object_t *id, omapi_object_t *h) { struct interface_info *interface; isc_result_t status; if (h -> type != dhcp_type_interface) return DHCP_R_INVALIDARG; interface = (struct interface_info *)h; /* Write out all the values. */ status = omapi_connection_put_name (c, "state"); if (status != ISC_R_SUCCESS) return status; if ((interface->flags & INTERFACE_REQUESTED) != 0) status = omapi_connection_put_string (c, "up"); else status = omapi_connection_put_string (c, "down"); if (status != ISC_R_SUCCESS) return status; /* Write out the inner object, if any. */ if (h -> inner && h -> inner -> type -> stuff_values) { status = ((*(h -> inner -> type -> stuff_values)) (c, id, h -> inner)); if (status == ISC_R_SUCCESS) return status; } return ISC_R_SUCCESS; } isc_result_t dhcp_interface_lookup (omapi_object_t **ip, omapi_object_t *id, omapi_object_t *ref) { omapi_value_t *tv = (omapi_value_t *)0; isc_result_t status; struct interface_info *interface; if (!ref) return DHCP_R_NOKEYS; /* First see if we were sent a handle. */ status = omapi_get_value_str (ref, id, "handle", &tv); if (status == ISC_R_SUCCESS) { status = omapi_handle_td_lookup (ip, tv -> value); omapi_value_dereference (&tv, MDL); if (status != ISC_R_SUCCESS) return status; /* Don't return the object if the type is wrong. */ if ((*ip) -> type != dhcp_type_interface) { omapi_object_dereference (ip, MDL); return DHCP_R_INVALIDARG; } } /* Now look for an interface name. */ status = omapi_get_value_str (ref, id, "name", &tv); if (status == ISC_R_SUCCESS) { char *s; unsigned len; for (interface = interfaces; interface; interface = interface -> next) { s = memchr (interface -> name, 0, IFNAMSIZ); if (s) len = s - &interface -> name [0]; else len = IFNAMSIZ; if ((tv -> value -> u.buffer.len == len && !memcmp (interface -> name, (char *)tv -> value -> u.buffer.value, len))) break; } if (!interface) { for (interface = dummy_interfaces; interface; interface = interface -> next) { s = memchr (interface -> name, 0, IFNAMSIZ); if (s) len = s - &interface -> name [0]; else len = IFNAMSIZ; if ((tv -> value -> u.buffer.len == len && !memcmp (interface -> name, (char *) tv -> value -> u.buffer.value, len))) break; } } omapi_value_dereference (&tv, MDL); if (*ip && *ip != (omapi_object_t *)interface) { omapi_object_dereference (ip, MDL); return DHCP_R_KEYCONFLICT; } else if (!interface) { if (*ip) omapi_object_dereference (ip, MDL); return ISC_R_NOTFOUND; } else if (!*ip) omapi_object_reference (ip, (omapi_object_t *)interface, MDL); } /* If we get to here without finding an interface, no valid key was specified. */ if (!*ip) return DHCP_R_NOKEYS; return ISC_R_SUCCESS; } /* actually just go discover the interface */ isc_result_t dhcp_interface_create (omapi_object_t **lp, omapi_object_t *id) { struct interface_info *hp; isc_result_t status; hp = (struct interface_info *)0; status = interface_allocate (&hp, MDL); if (status != ISC_R_SUCCESS) return status; hp -> flags = INTERFACE_REQUESTED; status = interface_reference ((struct interface_info **)lp, hp, MDL); interface_dereference (&hp, MDL); return status; } isc_result_t dhcp_interface_remove (omapi_object_t *lp, omapi_object_t *id) { struct interface_info *interface, *ip, *last; interface = (struct interface_info *)lp; /* remove from interfaces */ last = 0; for (ip = interfaces; ip; ip = ip -> next) { if (ip == interface) { if (last) { interface_dereference (&last -> next, MDL); if (ip -> next) interface_reference (&last -> next, ip -> next, MDL); } else { interface_dereference (&interfaces, MDL); if (ip -> next) interface_reference (&interfaces, ip -> next, MDL); } if (ip -> next) interface_dereference (&ip -> next, MDL); break; } last = ip; } if (!ip) return ISC_R_NOTFOUND; /* add the interface to the dummy_interface list */ if (dummy_interfaces) { interface_reference (&interface -> next, dummy_interfaces, MDL); interface_dereference (&dummy_interfaces, MDL); } interface_reference (&dummy_interfaces, interface, MDL); /* do a DHCPRELEASE */ if (dhcp_interface_shutdown_hook) (*dhcp_interface_shutdown_hook) (interface); /* remove the io object */ omapi_unregister_io_object ((omapi_object_t *)interface); switch(local_family) { #ifdef DHCPv6 case AF_INET6: if_deregister6(interface); break; #endif /* DHCPv6 */ case AF_INET: default: if_deregister_send(interface); if_deregister_receive(interface); break; } return ISC_R_SUCCESS; } void interface_stash (struct interface_info *tptr) { struct interface_info **vec; int delta; /* If the registerer didn't assign an index, assign one now. */ if (tptr -> index == -1) { tptr -> index = interface_count++; while (tptr -> index < interface_max && interface_vector [tptr -> index]) tptr -> index = interface_count++; } if (interface_max <= tptr -> index) { delta = tptr -> index - interface_max + 10; vec = dmalloc ((interface_max + delta) * sizeof (struct interface_info *), MDL); if (!vec) { log_error ("interface_stash: allocation failed "); return; } memset (&vec [interface_max], 0, (sizeof (struct interface_info *)) * delta); interface_max += delta; if (interface_vector) { memcpy (vec, interface_vector, (interface_count * sizeof (struct interface_info *))); dfree (interface_vector, MDL); } interface_vector = vec; } interface_reference (&interface_vector [tptr -> index], tptr, MDL); if (tptr -> index >= interface_count) interface_count = tptr -> index + 1; #if defined (TRACING) trace_interface_register (interface_trace, tptr); #endif } void interface_snorf (struct interface_info *tmp, int ir) { tmp -> circuit_id = (u_int8_t *)tmp -> name; tmp -> circuit_id_len = strlen (tmp -> name); tmp -> remote_id = 0; tmp -> remote_id_len = 0; tmp -> flags = ir; if (interfaces) { interface_reference (&tmp -> next, interfaces, MDL); interface_dereference (&interfaces, MDL); } interface_reference (&interfaces, tmp, MDL); } dhcp-4.4.1/common/dispatch.c000644 000765 000024 00000026443 13243301226 016157 0ustar00tmarkstaff000000 000000 /* dispatch.c Network input dispatcher... */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1995-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #include struct timeout *timeouts; static struct timeout *free_timeouts; void set_time(TIME t) { /* Do any outstanding timeouts. */ if (cur_tv . tv_sec != t) { cur_tv . tv_sec = t; cur_tv . tv_usec = 0; process_outstanding_timeouts ((struct timeval *)0); } } struct timeval *process_outstanding_timeouts (struct timeval *tvp) { /* Call any expired timeouts, and then if there's still a timeout registered, time out the select call then. */ another: if (timeouts) { struct timeout *t; if ((timeouts -> when . tv_sec < cur_tv . tv_sec) || ((timeouts -> when . tv_sec == cur_tv . tv_sec) && (timeouts -> when . tv_usec <= cur_tv . tv_usec))) { t = timeouts; timeouts = timeouts -> next; (*(t -> func)) (t -> what); if (t -> unref) (*t -> unref) (&t -> what, MDL); t -> next = free_timeouts; free_timeouts = t; goto another; } if (tvp) { tvp -> tv_sec = timeouts -> when . tv_sec; tvp -> tv_usec = timeouts -> when . tv_usec; } return tvp; } else return (struct timeval *)0; } /* Wait for packets to come in using select(). When one does, call receive_packet to receive the packet and possibly strip hardware addressing information from it, and then call through the bootp_packet_handler hook to try to do something with it. */ /* * Use the DHCP timeout list as a place to store DHCP specific * information, but use the ISC timer system to actually dispatch * the events. * * There are several things that the DHCP timer code does that the * ISC code doesn't: * 1) It allows for negative times * 2) The cancel arguments are different. The DHCP code uses the * function and data to find the proper timer to cancel while the * ISC code uses a pointer to the timer. * 3) The DHCP code includes provision for incrementing and decrementing * a reference counter associated with the data. * The first one is fairly easy to fix but will take some time to go throuh * the callers and update them. The second is also not all that difficult * in concept - add a pointer to the appropriate structures to hold a pointer * to the timer and use that. The complications arise in trying to ensure * that all of the corner cases are covered. The last one is potentially * more painful and requires more investigation. * * The plan is continue with the older DHCP calls and timer list. The * calls will continue to manipulate the list but will also pass a * timer to the ISC timer code for the actual dispatch. Later, if desired, * we can go back and modify the underlying calls to use the ISC * timer functions directly without requiring all of the code to change * at the same time. */ void dispatch(void) { isc_result_t status; do { status = isc_app_ctxrun(dhcp_gbl_ctx.actx); /* * isc_app_ctxrun can be stopped by receiving a * signal. It will return ISC_R_RELOAD in that * case. That is a normal behavior. */ if (status == ISC_R_RELOAD) { /* * dhcp_set_control_state() will do the job. * Note its first argument is ignored. */ status = dhcp_set_control_state(server_shutdown, server_shutdown); if (status == ISC_R_SUCCESS) status = ISC_R_RELOAD; } } while (status == ISC_R_RELOAD); log_fatal ("Dispatch routine failed: %s -- exiting", isc_result_totext (status)); } void isclib_timer_callback(isc_task_t *taskp, isc_event_t *eventp) { struct timeout *t = (struct timeout *)eventp->ev_arg; struct timeout *q, *r; /* Get the current time... */ gettimeofday (&cur_tv, (struct timezone *)0); /* * Find the timeout on the dhcp list and remove it. * As the list isn't ordered we search the entire list */ r = NULL; for (q = timeouts; q; q = q->next) { if (q == t) { if (r) r->next = q->next; else timeouts = q->next; break; } r = q; } /* * The timer should always be on the list. If it is we do * the work and detach the timer block, if not we log an error. * In both cases we attempt free the ISC event and continue * processing. */ if (q != NULL) { /* call the callback function */ (*(q->func)) (q->what); if (q->unref) { (*q->unref) (&q->what, MDL); } q->next = free_timeouts; isc_timer_detach(&q->isc_timeout); free_timeouts = q; } else { /* * Hmm, we should clean up the timer structure but aren't * sure about the pointer to the timer block we got so * don't try to - may change this to a log_fatal */ log_error("Error finding timer structure"); } isc_event_free(&eventp); return; } /* maximum value for usec */ #define USEC_MAX 1000000 void add_timeout (when, where, what, ref, unref) struct timeval *when; void (*where) (void *); void *what; tvref_t ref; tvunref_t unref; { struct timeout *t, *q; int usereset = 0; isc_result_t status; int64_t sec; int usec; isc_interval_t interval; isc_time_t expires; /* See if this timeout supersedes an existing timeout. */ t = (struct timeout *)0; for (q = timeouts; q; q = q->next) { if ((where == NULL || q->func == where) && q->what == what) { if (t) t->next = q->next; else timeouts = q->next; usereset = 1; break; } t = q; } /* If we didn't supersede a timeout, allocate a timeout structure now. */ if (!q) { if (free_timeouts) { q = free_timeouts; free_timeouts = q->next; } else { q = ((struct timeout *) dmalloc(sizeof(struct timeout), MDL)); if (!q) { log_fatal("add_timeout: no memory!"); } } memset(q, 0, sizeof *q); q->func = where; q->ref = ref; q->unref = unref; if (q->ref) (*q->ref)(&q->what, what, MDL); else q->what = what; } /* * The value passed in is a time from an epoch but we need a relative * time so we need to do some math to try and recover the period. * This is complicated by the fact that not all of the calls cared * about the usec value, if it's zero we assume the caller didn't care. * * The ISC timer library doesn't seem to like negative values * and on 64-bit systems, isc_time_nowplusinterval() can generate range * errors on values sufficiently larger than 0x7FFFFFFF (TIME_MAX), so * we'll limit the interval to: * * 0 <= interval <= TIME_MAX - 1 * * We do it before checking the trace option so that both the trace * code and * the working code use the same values. */ sec = when->tv_sec - cur_tv.tv_sec; usec = when->tv_usec - cur_tv.tv_usec; if ((when->tv_usec != 0) && (usec < 0)) { sec--; usec += USEC_MAX; } if (sec < 0) { sec = 0; usec = 0; } else if (sec >= TIME_MAX) { log_error("Timeout too large " "reducing to: %lu (TIME_MAX - 1)", (unsigned long)(TIME_MAX - 1)); sec = TIME_MAX - 1; usec = 0; } else if (usec < 0) { usec = 0; } else if (usec >= USEC_MAX) { usec = USEC_MAX - 1; } /* * This is necessary for the tracing code but we put it * here in case we want to compare timing information * for some reason, like debugging. */ q->when.tv_sec = cur_tv.tv_sec + sec; q->when.tv_usec = usec; #if defined (TRACING) if (trace_playback()) { /* * If we are doing playback we need to handle the timers * within this code rather than having the isclib handle * them for us. We need to keep the timer list in order * to allow us to find the ones to timeout. * * By using a different timer setup in the playback we may * have variations between the orginal and the playback but * it's the best we can do for now. */ /* Beginning of list? */ if (!timeouts || (timeouts->when.tv_sec > q-> when.tv_sec) || ((timeouts->when.tv_sec == q->when.tv_sec) && (timeouts->when.tv_usec > q->when.tv_usec))) { q->next = timeouts; timeouts = q; return; } /* Middle of list? */ for (t = timeouts; t->next; t = t->next) { if ((t->next->when.tv_sec > q->when.tv_sec) || ((t->next->when.tv_sec == q->when.tv_sec) && (t->next->when.tv_usec > q->when.tv_usec))) { q->next = t->next; t->next = q; return; } } /* End of list. */ t->next = q; q->next = (struct timeout *)0; return; } #endif /* * Don't bother sorting the DHCP list, just add it to the front. * Eventually the list should be removed as we migrate the callers * to the native ISC timer functions, if it becomes a performance * problem before then we may need to order the list. */ q->next = timeouts; timeouts = q; isc_interval_set(&interval, sec, usec * 1000); status = isc_time_nowplusinterval(&expires, &interval); if (status != ISC_R_SUCCESS) { /* * The system time function isn't happy. Range errors * should not be possible with the check logic above. */ log_fatal("Unable to set up timer: %s", isc_result_totext(status)); } if (usereset == 0) { status = isc_timer_create(dhcp_gbl_ctx.timermgr, isc_timertype_once, &expires, NULL, dhcp_gbl_ctx.task, isclib_timer_callback, (void *)q, &q->isc_timeout); } else { status = isc_timer_reset(q->isc_timeout, isc_timertype_once, &expires, NULL, 0); } /* If it fails log an error and die */ if (status != ISC_R_SUCCESS) { log_fatal("Unable to add timeout to isclib\n"); } return; } void cancel_timeout (where, what) void (*where) (void *); void *what; { struct timeout *t, *q; /* Look for this timeout on the list, and unlink it if we find it. */ t = (struct timeout *)0; for (q = timeouts; q; q = q -> next) { if (q->func == where && q->what == what) { if (t) t->next = q->next; else timeouts = q->next; break; } t = q; } /* * If we found the timeout, cancel it and put it on the free list. * The TRACING stuff is ugly but we don't add a timer when doing * playback so we don't want to remove them then either. */ if (q) { #if defined (TRACING) if (!trace_playback()) { #endif isc_timer_detach(&q->isc_timeout); #if defined (TRACING) } #endif if (q->unref) (*q->unref) (&q->what, MDL); q->next = free_timeouts; free_timeouts = q; } } #if defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) void cancel_all_timeouts () { struct timeout *t, *n; for (t = timeouts; t; t = n) { n = t->next; isc_timer_detach(&t->isc_timeout); if (t->unref && t->what) (*t->unref) (&t->what, MDL); t->next = free_timeouts; free_timeouts = t; } } void relinquish_timeouts () { struct timeout *t, *n; for (t = free_timeouts; t; t = n) { n = t->next; dfree(t, MDL); } } #endif dhcp-4.4.1/common/dlpi.c000644 000765 000024 00000110114 13243301226 015275 0ustar00tmarkstaff000000 000000 /* dlpi.c Data Link Provider Interface (DLPI) network interface code. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1996-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * * This software was written for Internet Systems Consortium * by Eric James Negaard, . To learn more about * Internet Systems Consortium, see ``https://www.isc.org''. * * Joost Mulders has also done considerable work in debugging the DLPI API * support on Solaris and getting this code to work properly on a variety * of different Solaris platforms. */ /* * Based largely in part to the existing NIT code in nit.c. * * This code has been developed and tested on sparc-based machines running * SunOS 5.5.1, with le and hme network interfaces. It should be pretty * generic, though. */ /* * Implementation notes: * * I first tried to write this code to the "vanilla" DLPI 2.0 API. * It worked on a Sun Ultra-1 with a hme interface, but didn't work * on Sun SparcStation 5's with "le" interfaces (the packets sent out * via dlpiunitdatareq contained an Ethernet type of 0x0000 instead * of the expected 0x0800). * * Therefore I added the "DLPI_RAW" code which is a Sun extension to * the DLPI standard. This code works on both of the above machines. * This is configurable in the OS-dependent include file by defining * USE_DLPI_RAW. * * It quickly became apparant that I should also use the "pfmod" * STREAMS module to cut down on the amount of user level packet * processing. I don't know how widely available "pfmod" is, so it's * use is conditionally included. This is configurable in the * OS-dependent include file by defining USE_DLPI_PFMOD. * * A major quirk on the Sun's at least, is that no packets seem to get * sent out the interface until six seconds after the interface is * first "attached" to [per system reboot] (it's actually from when * the interface is attached, not when it is plumbed, so putting a * sleep into the dhclient-script at PREINIT time doesn't help). I * HAVE tried, without success to poll the fd to see when it is ready * for writing. This doesn't help at all. If the sleeps are not done, * the initial DHCPREQUEST or DHCPDISCOVER never gets sent out, so * I've put them here, when register_send and register_receive are * called (split up into two three-second sleeps between the notices, * so that it doesn't seem like so long when you're watching :-). The * amount of time to sleep is configurable in the OS-dependent include * file by defining DLPI_FIRST_SEND_WAIT to be the number of seconds * to sleep. */ /* * The Open Group Technical Standard can be found here: * http://www.opengroup.org/onlinepubs/009618899/index.htm * * The HP DLPI Programmer's Guide can be found here: * http://docs.hp.com/en/B2355-90139/index.html */ #include "dhcpd.h" #if defined (USE_DLPI_SEND) || defined (USE_DLPI_RECEIVE) || \ defined(USE_DLPI_HWADDR) # include # include # include # include # ifdef USE_DLPI_PFMOD # include # endif #include #include # include # include "includes/netinet/ip.h" # include "includes/netinet/udp.h" # include "includes/netinet/if_ether.h" # ifdef USE_DLPI_PFMOD # ifdef USE_DLPI_RAW # define DLPI_MODNAME "DLPI+RAW+PFMOD" # else # define DLPI_MODNAME "DLPI+PFMOD" # endif # else # ifdef USE_DLPI_RAW # define DLPI_MODNAME "DLPI+RAW" # else # define DLPI_MODNAME "DLPI" # endif # endif # ifndef ABS # define ABS(x) ((x) >= 0 ? (x) : 0-(x)) # endif #if defined(USE_DLPI_PFMOD) || defined(USE_DLPI_RAW) static int strioctl (int fd, int cmd, int timeout, int len, char *dp); #endif #define DLPI_MAXDLBUF 8192 /* Buffer size */ #define DLPI_MAXDLADDR 1024 /* Max address size */ /* Device directory */ #if defined(USE_DEV_NET) #define DLPI_DEVDIR "/dev/net/" /* Solaris 11 + */ #else #define DLPI_DEVDIR "/dev/" /* Pre Solaris 11 */ #endif static int dlpiopen(const char *ifname); static int dlpiunit (char *ifname); static int dlpiinforeq (int fd); static int dlpiphysaddrreq (int fd, unsigned long addrtype); static int dlpiattachreq (int fd, unsigned long ppa); static int dlpibindreq (int fd, unsigned long sap, unsigned long max_conind, unsigned long service_mode, unsigned long conn_mgmt, unsigned long xidtest); #if defined(UNUSED_DLPI_INTERFACE) /* These functions are unused at present, but may be used at a later date. * defined out to avoid compiler warnings about unused static functions. */ static int dlpidetachreq (int fd); static int dlpiunbindreq (int fd); #endif static int dlpiokack (int fd, char *bufp); static int dlpiinfoack (int fd, char *bufp); static int dlpiphysaddrack (int fd, char *bufp); static int dlpibindack (int fd, char *bufp); #if defined(USE_DLPI_SEND) || defined(USE_DLPI_RECEIVE) /* These functions are not used if we're only sourcing the get_hw_addr() * function (for USE_SOCKETS). */ static int dlpiunitdatareq (int fd, unsigned char *addr, int addrlen, unsigned long minpri, unsigned long maxpri, unsigned char *data, int datalen); static int dlpiunitdataind (int fd, unsigned char *dstaddr, unsigned long *dstaddrlen, unsigned char *srcaddr, unsigned long *srcaddrlen, unsigned long *grpaddr, unsigned char *data, int datalen); #endif /* !USE_DLPI_HWADDR: USE_DLPI_SEND || USE_DLPI_RECEIVE */ static int expected (unsigned long prim, union DL_primitives *dlp, int msgflags); static int strgetmsg (int fd, struct strbuf *ctlp, struct strbuf *datap, int *flagsp, char *caller); /* Reinitializes the specified interface after an address change. This is not required for packet-filter APIs. */ #ifdef USE_DLPI_SEND void if_reinitialize_send (info) struct interface_info *info; { } #endif #ifdef USE_DLPI_RECEIVE void if_reinitialize_receive (info) struct interface_info *info; { } #endif /* Called by get_interface_list for each interface that's discovered. Opens a packet filter for each interface and adds it to the select mask. */ int if_register_dlpi (info) struct interface_info *info; { int sock; int unit; long buf [DLPI_MAXDLBUF]; union DL_primitives *dlp; dlp = (union DL_primitives *)buf; /* Open a DLPI device */ if ((sock = dlpiopen (info -> name)) < 0) { log_fatal ("Can't open DLPI device for %s: %m", info -> name); } /* * Submit a DL_INFO_REQ request, to find the dl_mac_type and * dl_provider_style */ if (dlpiinforeq(sock) < 0 || dlpiinfoack(sock, (char *)buf) < 0) { log_fatal ("Can't get DLPI MAC type for %s: %m", info -> name); } else { switch (dlp -> info_ack.dl_mac_type) { case DL_CSMACD: /* IEEE 802.3 */ case DL_ETHER: info -> hw_address.hbuf [0] = HTYPE_ETHER; break; /* adding token ring 5/1999 - mayer@ping.at */ case DL_TPR: info -> hw_address.hbuf [0] = HTYPE_IEEE802; break; case DL_FDDI: info -> hw_address.hbuf [0] = HTYPE_FDDI; break; default: log_fatal("%s: unsupported DLPI MAC type %lu", info->name, (unsigned long)dlp->info_ack.dl_mac_type); break; } /* * copy the sap length and broadcast address of this interface * to interface_info. This fixes nothing but seemed nicer than to * assume -2 and ffffff. */ info -> dlpi_sap_length = dlp -> info_ack.dl_sap_length; info -> dlpi_broadcast_addr.hlen = dlp -> info_ack.dl_brdcst_addr_length; memcpy (info -> dlpi_broadcast_addr.hbuf, (char *)dlp + dlp -> info_ack.dl_brdcst_addr_offset, dlp -> info_ack.dl_brdcst_addr_length); } if (dlp -> info_ack.dl_provider_style == DL_STYLE2) { /* * Attach to the device. If this fails, the device * does not exist. */ unit = dlpiunit (info -> name); if (dlpiattachreq (sock, unit) < 0 || dlpiokack (sock, (char *)buf) < 0) { log_fatal ("Can't attach DLPI device for %s: %m", info -> name); } } /* * Bind to the IP service access point (SAP), connectionless (CLDLS). */ if (dlpibindreq (sock, ETHERTYPE_IP, 0, DL_CLDLS, 0, 0) < 0 || dlpibindack (sock, (char *)buf) < 0) { log_fatal ("Can't bind DLPI device for %s: %m", info -> name); } /* * Submit a DL_PHYS_ADDR_REQ request, to find * the hardware address */ if (dlpiphysaddrreq (sock, DL_CURR_PHYS_ADDR) < 0 || dlpiphysaddrack (sock, (char *)buf) < 0) { log_fatal ("Can't get DLPI hardware address for %s: %m", info -> name); } info -> hw_address.hlen = dlp -> physaddr_ack.dl_addr_length + 1; memcpy (&info -> hw_address.hbuf [1], (char *)buf + dlp -> physaddr_ack.dl_addr_offset, dlp -> physaddr_ack.dl_addr_length); #ifdef USE_DLPI_RAW if (strioctl (sock, DLIOCRAW, INFTIM, 0, 0) < 0) { log_fatal ("Can't set DLPI RAW mode for %s: %m", info -> name); } #endif #ifdef USE_DLPI_PFMOD if (ioctl (sock, I_PUSH, "pfmod") < 0) { log_fatal ("Can't push packet filter onto DLPI for %s: %m", info -> name); } #endif return sock; } #if defined(USE_DLPI_PFMOD) || defined(USE_DLPI_RAW) static int strioctl (fd, cmd, timeout, len, dp) int fd; int cmd; int timeout; int len; char *dp; { struct strioctl sio; int rslt; sio.ic_cmd = cmd; sio.ic_timout = timeout; sio.ic_len = len; sio.ic_dp = dp; if ((rslt = ioctl (fd, I_STR, &sio)) < 0) { return rslt; } else { return sio.ic_len; } } #endif /* USE_DPI_PFMOD || USE_DLPI_RAW */ #ifdef USE_DLPI_SEND void if_register_send (info) struct interface_info *info; { /* If we're using the DLPI API for sending and receiving, we don't need to register this interface twice. */ #ifndef USE_DLPI_RECEIVE # ifdef USE_DLPI_PFMOD struct packetfilt pf; # endif info -> wfdesc = if_register_dlpi (info); # ifdef USE_DLPI_PFMOD /* Set up an PFMOD filter that rejects everything... */ pf.Pf_Priority = 0; pf.Pf_FilterLen = 1; pf.Pf_Filter [0] = ENF_PUSHZERO; /* Install the filter */ if (strioctl (info -> wfdesc, PFIOCSETF, INFTIM, sizeof (pf), (char *)&pf) < 0) { log_fatal ("Can't set PFMOD send filter on %s: %m", info -> name); } # endif /* USE_DLPI_PFMOD */ #else /* !defined (USE_DLPI_RECEIVE) */ /* * If using DLPI for both send and receive, simply re-use * the read file descriptor that was set up earlier. */ info -> wfdesc = info -> rfdesc; #endif if (!quiet_interface_discovery) log_info ("Sending on DLPI/%s/%s%s%s", info -> name, print_hw_addr (info -> hw_address.hbuf [0], info -> hw_address.hlen - 1, &info -> hw_address.hbuf [1]), (info -> shared_network ? "/" : ""), (info -> shared_network ? info -> shared_network -> name : "")); #ifdef DLPI_FIRST_SEND_WAIT /* See the implementation notes at the beginning of this file */ # ifdef USE_DLPI_RECEIVE sleep (DLPI_FIRST_SEND_WAIT - (DLPI_FIRST_SEND_WAIT / 2)); # else sleep (DLPI_FIRST_SEND_WAIT); # endif #endif } void if_deregister_send (info) struct interface_info *info; { /* If we're using the DLPI API for sending and receiving, we don't need to register this interface twice. */ #ifndef USE_DLPI_RECEIVE close (info -> wfdesc); #endif info -> wfdesc = -1; if (!quiet_interface_discovery) log_info ("Disabling output on DLPI/%s/%s%s%s", info -> name, print_hw_addr (info -> hw_address.hbuf [0], info -> hw_address.hlen - 1, &info -> hw_address.hbuf [1]), (info -> shared_network ? "/" : ""), (info -> shared_network ? info -> shared_network -> name : "")); } #endif /* USE_DLPI_SEND */ #ifdef USE_DLPI_RECEIVE /* Packet filter program... XXX Changes to the filter program may require changes to the constant offsets used in if_register_send to patch the NIT program! XXX */ #if defined(RELAY_PORT) #error "Relay port is not yet supported for DLPI" #endif void if_register_receive (info) struct interface_info *info; { #ifdef USE_DLPI_PFMOD struct packetfilt pf; struct ip iphdr; u_int16_t offset; #endif /* Open a DLPI device and hang it on this interface... */ info -> rfdesc = if_register_dlpi (info); #ifdef USE_DLPI_PFMOD /* Set up the PFMOD filter program. */ /* XXX Unlike the BPF filter program, this one won't work if the XXX IP packet is fragmented or if there are options on the IP XXX header. */ pf.Pf_Priority = 0; pf.Pf_FilterLen = 0; #if defined (USE_DLPI_RAW) # define ETHER_H_PREFIX (14) /* sizeof (ethernet_header) */ /* * ethertype == ETHERTYPE_IP */ offset = 12; pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHWORD + (offset / 2); pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT | ENF_CAND; pf.Pf_Filter [pf.Pf_FilterLen++] = htons (ETHERTYPE_IP); # else # define ETHER_H_PREFIX (0) # endif /* USE_DLPI_RAW */ /* * The packets that will be received on this file descriptor * will be IP packets (due to the SAP that was specified in * the dlbind call). There will be no ethernet header. * Therefore, setup the packet filter to check the protocol * field for UDP, and the destination port number equal * to the local port. All offsets are relative to the start * of an IP packet. */ /* * BOOTPS destination port */ offset = ETHER_H_PREFIX + sizeof (iphdr) + sizeof (u_int16_t); pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHWORD + (offset / 2); pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT | ENF_CAND; pf.Pf_Filter [pf.Pf_FilterLen++] = local_port; /* * protocol should be udp. this is a byte compare, test for * endianess. */ offset = ETHER_H_PREFIX + ((u_int8_t *)&(iphdr.ip_p) - (u_int8_t *)&iphdr); pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHWORD + (offset / 2); pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT | ENF_AND; pf.Pf_Filter [pf.Pf_FilterLen++] = htons (0x00FF); pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT | ENF_CAND; pf.Pf_Filter [pf.Pf_FilterLen++] = htons (IPPROTO_UDP); /* Install the filter... */ if (strioctl (info -> rfdesc, PFIOCSETF, INFTIM, sizeof (pf), (char *)&pf) < 0) { log_fatal ("Can't set PFMOD receive filter on %s: %m", info -> name); } #endif /* USE_DLPI_PFMOD */ if (!quiet_interface_discovery) log_info ("Listening on DLPI/%s/%s%s%s", info -> name, print_hw_addr (info -> hw_address.hbuf [0], info -> hw_address.hlen - 1, &info -> hw_address.hbuf [1]), (info -> shared_network ? "/" : ""), (info -> shared_network ? info -> shared_network -> name : "")); #ifdef DLPI_FIRST_SEND_WAIT /* See the implementation notes at the beginning of this file */ # ifdef USE_DLPI_SEND sleep (DLPI_FIRST_SEND_WAIT / 2); # else sleep (DLPI_FIRST_SEND_WAIT); # endif #endif } void if_deregister_receive (info) struct interface_info *info; { /* If we're using the DLPI API for sending and receiving, we don't need to register this interface twice. */ #ifndef USE_DLPI_SEND close (info -> rfdesc); #endif info -> rfdesc = -1; if (!quiet_interface_discovery) log_info ("Disabling input on DLPI/%s/%s%s%s", info -> name, print_hw_addr (info -> hw_address.hbuf [0], info -> hw_address.hlen - 1, &info -> hw_address.hbuf [1]), (info -> shared_network ? "/" : ""), (info -> shared_network ? info -> shared_network -> name : "")); } #endif /* USE_DLPI_RECEIVE */ #ifdef USE_DLPI_SEND ssize_t send_packet (interface, packet, raw, len, from, to, hto) struct interface_info *interface; struct packet *packet; struct dhcp_packet *raw; size_t len; struct in_addr from; struct sockaddr_in *to; struct hardware *hto; { #ifdef USE_DLPI_RAW double hh [32]; int fudge; #endif double ih [1536 / sizeof (double)]; unsigned char *dbuf = (unsigned char *)ih; unsigned dbuflen; unsigned char dstaddr [DLPI_MAXDLADDR]; unsigned addrlen; int result; if (!strcmp (interface -> name, "fallback")) return send_fallback (interface, packet, raw, len, from, to, hto); if (hto == NULL && interface->anycast_mac_addr.hlen) hto = &interface->anycast_mac_addr; dbuflen = 0; /* Assemble the headers... */ #ifdef USE_DLPI_RAW assemble_hw_header (interface, (unsigned char *)hh, &dbuflen, hto); if (dbuflen > sizeof hh) log_fatal ("send_packet: hh buffer too small.\n"); fudge = dbuflen % 4; /* IP header must be word-aligned. */ memcpy (dbuf + fudge, (unsigned char *)hh, dbuflen); dbuflen += fudge; #endif assemble_udp_ip_header (interface, dbuf, &dbuflen, from.s_addr, to -> sin_addr.s_addr, to -> sin_port, (unsigned char *)raw, len); /* Copy the data into the buffer (yuk). */ memcpy (dbuf + dbuflen, raw, len); dbuflen += len; #ifdef USE_DLPI_RAW result = write (interface -> wfdesc, dbuf + fudge, dbuflen - fudge); #else /* * Setup the destination address (DLSAP) in dstaddr * * If sap_length < 0 we must deliver the DLSAP as phys+sap. * If sap_length > 0 we must deliver the DLSAP as sap+phys. * * sap = Service Access Point == ETHERTYPE_IP * sap + datalink address is called DLSAP in dlpi speak. */ { /* ENCODE DLSAP */ unsigned char phys [DLPI_MAXDLADDR]; unsigned char sap [4]; int sap_len = interface -> dlpi_sap_length; int phys_len = interface -> hw_address.hlen - 1; /* sap = htons (ETHERTYPE_IP) kludge */ memset (sap, 0, sizeof (sap)); # if (BYTE_ORDER == LITTLE_ENDIAN) sap [0] = 0x00; sap [1] = 0x08; # else sap [0] = 0x08; sap [1] = 0x00; # endif if (hto && hto -> hlen == interface -> hw_address.hlen) memcpy ( phys, (char *) &hto -> hbuf [1], phys_len); else memcpy ( phys, interface -> dlpi_broadcast_addr.hbuf, interface -> dlpi_broadcast_addr.hlen); if (sap_len < 0) { memcpy ( dstaddr, phys, phys_len); memcpy ( (char *) &dstaddr [phys_len], sap, ABS (sap_len)); } else { memcpy ( dstaddr, (void *) sap, sap_len); memcpy ( (char *) &dstaddr [sap_len], phys, phys_len); } addrlen = phys_len + ABS (sap_len); } /* ENCODE DLSAP */ result = dlpiunitdatareq (interface -> wfdesc, dstaddr, addrlen, 0, 0, dbuf, dbuflen); #endif /* USE_DLPI_RAW */ if (result < 0) log_error ("send_packet: %m"); return result; } #endif /* USE_DLPI_SEND */ #ifdef USE_DLPI_RECEIVE ssize_t receive_packet (interface, buf, len, from, hfrom) struct interface_info *interface; unsigned char *buf; size_t len; struct sockaddr_in *from; struct hardware *hfrom; { unsigned char dbuf [1536]; unsigned char srcaddr [DLPI_MAXDLADDR]; unsigned long srcaddrlen; int length = 0; int offset = 0; int bufix = 0; unsigned paylen; #ifdef USE_DLPI_RAW length = read (interface -> rfdesc, dbuf, sizeof (dbuf)); #else length = dlpiunitdataind (interface -> rfdesc, (unsigned char *)NULL, (unsigned long *)NULL, srcaddr, &srcaddrlen, (unsigned long *)NULL, dbuf, sizeof (dbuf)); #endif if (length <= 0) { log_error("receive_packet: %m"); return length; } # if !defined (USE_DLPI_RAW) /* * Copy the sender's hw address into hfrom * If sap_len < 0 the DLSAP is as phys+sap. * If sap_len > 0 the DLSAP is as sap+phys. * * sap is discarded here. */ { /* DECODE DLSAP */ int sap_len = interface -> dlpi_sap_length; int phys_len = interface -> hw_address.hlen - 1; if (hfrom && (srcaddrlen == ABS (sap_len) + phys_len )) { hfrom -> hbuf [0] = interface -> hw_address.hbuf [0]; hfrom -> hlen = interface -> hw_address.hlen; if (sap_len < 0) { memcpy ((char *) &hfrom -> hbuf [1], srcaddr, phys_len); } else { memcpy((char *)&hfrom->hbuf[1], srcaddr + sap_len, phys_len); } } else if (hfrom) { memset (hfrom, '\0', sizeof *hfrom); } } /* DECODE_DLSAP */ # endif /* !defined (USE_DLPI_RAW) */ /* Decode the IP and UDP headers... */ bufix = 0; #ifdef USE_DLPI_RAW /* Decode the physical header... */ offset = decode_hw_header (interface, dbuf, bufix, hfrom); /* If a physical layer checksum failed (dunno of any physical layer that supports this, but WTH), skip this packet. */ if (offset < 0) { return 0; } bufix += offset; length -= offset; #endif offset = decode_udp_ip_header (interface, dbuf, bufix, from, length, &paylen, 1); /* * If the IP or UDP checksum was bad, skip the packet... * * Note: this happens all the time when writing packets via the * fallback socket. The packet received by streams does not have * the IP or UDP checksums filled in, as those are calculated by * the hardware. */ if (offset < 0) { return 0; } bufix += offset; length -= offset; if (length < paylen) log_fatal("Internal inconsistency at %s:%d.", MDL); /* Copy out the data in the packet... */ memcpy(buf, &dbuf [bufix], paylen); return paylen; } #endif /* Common DLPI routines ... * * Written by Eric James Negaard, * * Based largely in part to the example code contained in the document * "How to Use the STREAMS Data Link Provider Interface (DLPI)", written * by Neal Nuckolls of SunSoft Internet Engineering. * * This code has been developed and tested on sparc-based machines running * SunOS 5.5.1, with le and hme network interfaces. It should be pretty * generic, though. * * The usual disclaimers apply. This code works for me. Don't blame me * if it makes your machine or network go down in flames. That taken * into consideration, use this code as you wish. If you make usefull * modifications I'd appreciate hearing about it. */ #define DLPI_MAXWAIT 15 /* Max timeout */ /* * Parse an interface name and extract the unit number */ static int dlpiunit (ifname) char *ifname; { char *cp; int unit; if (!ifname) { return 0; } /* Advance to the end of the name */ cp = ifname; while (*cp) cp++; /* Back up to the start of the first digit */ while ((*(cp-1) >= '0' && *(cp-1) <= '9') || *(cp - 1) == ':') cp--; /* Convert the unit number */ unit = 0; while (*cp >= '0' && *cp <= '9') { unit *= 10; unit += (*cp++ - '0'); } return unit; } /* * dlpiopen - open the DLPI device for a given interface name */ static int dlpiopen(const char *ifname) { char devname [50]; char *dp; const char *cp, *ep; if (!ifname) { return -1; } /* Open a DLPI device */ if (*ifname == '/') { dp = devname; } else { /* Prepend the device directory */ memcpy (devname, DLPI_DEVDIR, strlen (DLPI_DEVDIR)); dp = &devname [strlen (DLPI_DEVDIR)]; } /* Find the end of the interface name */ ep = cp = ifname; while (*ep) ep++; /* Before Solaris 11 we strip off the digit to open the base dev name */ #if !defined(USE_DEV_NET) /* And back up to the first digit (unit number) */ while ((*(ep - 1) >= '0' && *(ep - 1) <= '9') || *(ep - 1) == ':') ep--; #endif /* Copy everything up to the unit number */ while (cp < ep) { *dp++ = *cp++; } *dp = '\0'; return open (devname, O_RDWR, 0); } /* * dlpiinforeq - request information about the data link provider. */ static int dlpiinforeq (fd) int fd; { dl_info_req_t info_req; struct strbuf ctl; int flags; info_req.dl_primitive = DL_INFO_REQ; ctl.maxlen = 0; ctl.len = sizeof (info_req); ctl.buf = (char *)&info_req; flags = RS_HIPRI; return putmsg (fd, &ctl, (struct strbuf *)NULL, flags); } /* * dlpiphysaddrreq - request the current physical address. */ static int dlpiphysaddrreq (fd, addrtype) int fd; unsigned long addrtype; { dl_phys_addr_req_t physaddr_req; struct strbuf ctl; int flags; physaddr_req.dl_primitive = DL_PHYS_ADDR_REQ; physaddr_req.dl_addr_type = addrtype; ctl.maxlen = 0; ctl.len = sizeof (physaddr_req); ctl.buf = (char *)&physaddr_req; flags = RS_HIPRI; return putmsg (fd, &ctl, (struct strbuf *)NULL, flags); } /* * dlpiattachreq - send a request to attach to a specific unit. */ static int dlpiattachreq (fd, ppa) unsigned long ppa; int fd; { dl_attach_req_t attach_req; struct strbuf ctl; int flags; attach_req.dl_primitive = DL_ATTACH_REQ; attach_req.dl_ppa = ppa; ctl.maxlen = 0; ctl.len = sizeof (attach_req); ctl.buf = (char *)&attach_req; flags = 0; return putmsg (fd, &ctl, (struct strbuf*)NULL, flags); } /* * dlpibindreq - send a request to bind to a specific SAP address. */ static int dlpibindreq (fd, sap, max_conind, service_mode, conn_mgmt, xidtest) unsigned long sap; unsigned long max_conind; unsigned long service_mode; unsigned long conn_mgmt; unsigned long xidtest; int fd; { dl_bind_req_t bind_req; struct strbuf ctl; int flags; bind_req.dl_primitive = DL_BIND_REQ; bind_req.dl_sap = sap; bind_req.dl_max_conind = max_conind; bind_req.dl_service_mode = service_mode; bind_req.dl_conn_mgmt = conn_mgmt; bind_req.dl_xidtest_flg = xidtest; ctl.maxlen = 0; ctl.len = sizeof (bind_req); ctl.buf = (char *)&bind_req; flags = 0; return putmsg (fd, &ctl, (struct strbuf*)NULL, flags); } #if defined(UNUSED_DLPI_INTERFACE) /* * dlpiunbindreq - send a request to unbind. This function is not actually * used by ISC DHCP, but is included for completeness in case it is * ever required for new work. */ static int dlpiunbindreq (fd) int fd; { dl_unbind_req_t unbind_req; struct strbuf ctl; int flags; unbind_req.dl_primitive = DL_UNBIND_REQ; ctl.maxlen = 0; ctl.len = sizeof (unbind_req); ctl.buf = (char *)&unbind_req; flags = 0; return putmsg (fd, &ctl, (struct strbuf*)NULL, flags); } /* * dlpidetachreq - send a request to detach. This function is not actually * used by ISC DHCP, but is included for completeness in case it is * ever required for new work. */ static int dlpidetachreq (fd) int fd; { dl_detach_req_t detach_req; struct strbuf ctl; int flags; detach_req.dl_primitive = DL_DETACH_REQ; ctl.maxlen = 0; ctl.len = sizeof (detach_req); ctl.buf = (char *)&detach_req; flags = 0; return putmsg (fd, &ctl, (struct strbuf*)NULL, flags); } #endif /* UNUSED_DLPI_INTERFACE */ /* * dlpibindack - receive an ack to a dlbindreq. */ static int dlpibindack (fd, bufp) char *bufp; int fd; { union DL_primitives *dlp; struct strbuf ctl; int flags; ctl.maxlen = DLPI_MAXDLBUF; ctl.len = 0; ctl.buf = bufp; if (strgetmsg (fd, &ctl, (struct strbuf*)NULL, &flags, "dlpibindack") < 0) { return -1; } dlp = (union DL_primitives *)ctl.buf; if (expected (DL_BIND_ACK, dlp, flags) == -1) { return -1; } if (ctl.len < sizeof (dl_bind_ack_t)) { /* Returned structure is too short */ return -1; } return 0; } /* * dlpiokack - general acknowledgement reception. */ static int dlpiokack (fd, bufp) char *bufp; int fd; { union DL_primitives *dlp; struct strbuf ctl; int flags; ctl.maxlen = DLPI_MAXDLBUF; ctl.len = 0; ctl.buf = bufp; if (strgetmsg (fd, &ctl, (struct strbuf*)NULL, &flags, "dlpiokack") < 0) { return -1; } dlp = (union DL_primitives *)ctl.buf; if (expected (DL_OK_ACK, dlp, flags) == -1) { return -1; } if (ctl.len < sizeof (dl_ok_ack_t)) { /* Returned structure is too short */ return -1; } return 0; } /* * dlpiinfoack - receive an ack to a dlinforeq. */ static int dlpiinfoack (fd, bufp) char *bufp; int fd; { union DL_primitives *dlp; struct strbuf ctl; int flags; ctl.maxlen = DLPI_MAXDLBUF; ctl.len = 0; ctl.buf = bufp; if (strgetmsg (fd, &ctl, (struct strbuf *)NULL, &flags, "dlpiinfoack") < 0) { return -1; } dlp = (union DL_primitives *) ctl.buf; if (expected (DL_INFO_ACK, dlp, flags) == -1) { return -1; } if (ctl.len < sizeof (dl_info_ack_t)) { /* Returned structure is too short */ return -1; } return 0; } /* * dlpiphysaddrack - receive an ack to a dlpiphysaddrreq. */ int dlpiphysaddrack (fd, bufp) char *bufp; int fd; { union DL_primitives *dlp; struct strbuf ctl; int flags; ctl.maxlen = DLPI_MAXDLBUF; ctl.len = 0; ctl.buf = bufp; if (strgetmsg (fd, &ctl, (struct strbuf *)NULL, &flags, "dlpiphysaddrack") < 0) { return -1; } dlp = (union DL_primitives *)ctl.buf; if (expected (DL_PHYS_ADDR_ACK, dlp, flags) == -1) { return -1; } if (ctl.len < sizeof (dl_phys_addr_ack_t)) { /* Returned structure is too short */ return -1; } return 0; } #if defined(USE_DLPI_SEND) || defined(USE_DLPI_RECEIVE) int dlpiunitdatareq (fd, addr, addrlen, minpri, maxpri, dbuf, dbuflen) int fd; unsigned char *addr; int addrlen; unsigned long minpri; unsigned long maxpri; unsigned char *dbuf; int dbuflen; { long buf [DLPI_MAXDLBUF]; union DL_primitives *dlp; struct strbuf ctl, data; /* Set up the control information... */ dlp = (union DL_primitives *)buf; dlp -> unitdata_req.dl_primitive = DL_UNITDATA_REQ; dlp -> unitdata_req.dl_dest_addr_length = addrlen; dlp -> unitdata_req.dl_dest_addr_offset = sizeof (dl_unitdata_req_t); dlp -> unitdata_req.dl_priority.dl_min = minpri; dlp -> unitdata_req.dl_priority.dl_max = maxpri; /* Append the destination address */ memcpy ((char *)buf + dlp -> unitdata_req.dl_dest_addr_offset, addr, addrlen); ctl.maxlen = 0; ctl.len = dlp -> unitdata_req.dl_dest_addr_offset + addrlen; ctl.buf = (char *)buf; data.maxlen = 0; data.buf = (char *)dbuf; data.len = dbuflen; /* Send the packet down the wire... */ return putmsg (fd, &ctl, &data, 0); } static int dlpiunitdataind (fd, daddr, daddrlen, saddr, saddrlen, grpaddr, dbuf, dlen) int fd; unsigned char *daddr; unsigned long *daddrlen; unsigned char *saddr; unsigned long *saddrlen; unsigned long *grpaddr; unsigned char *dbuf; int dlen; { long buf [DLPI_MAXDLBUF]; union DL_primitives *dlp; struct strbuf ctl, data; int flags = 0; int result; /* Set up the msg_buf structure... */ dlp = (union DL_primitives *)buf; dlp -> unitdata_ind.dl_primitive = DL_UNITDATA_IND; ctl.maxlen = DLPI_MAXDLBUF; ctl.len = 0; ctl.buf = (char *)buf; data.maxlen = dlen; data.len = 0; data.buf = (char *)dbuf; result = getmsg (fd, &ctl, &data, &flags); if (result < 0) { log_debug("dlpiunitdataind: %m"); return -1; } if (ctl.len < sizeof (dl_unitdata_ind_t) || dlp -> unitdata_ind.dl_primitive != DL_UNITDATA_IND) { return -1; } if (data.len <= 0) { return data.len; } /* Copy sender info */ if (saddr) { memcpy (saddr, (char *)buf + dlp -> unitdata_ind.dl_src_addr_offset, dlp -> unitdata_ind.dl_src_addr_length); } if (saddrlen) { *saddrlen = dlp -> unitdata_ind.dl_src_addr_length; } /* Copy destination info */ if (daddr) { memcpy (daddr, (char *)buf + dlp -> unitdata_ind.dl_dest_addr_offset, dlp -> unitdata_ind.dl_dest_addr_length); } if (daddrlen) { *daddrlen = dlp -> unitdata_ind.dl_dest_addr_length; } if (grpaddr) { *grpaddr = dlp -> unitdata_ind.dl_group_address; } return data.len; } #endif /* !USE_DLPI_HWADDR: USE_DLPI_RECEIVE || USE_DLPI_SEND */ /* * expected - see if we got what we wanted. */ static int expected (prim, dlp, msgflags) unsigned long prim; union DL_primitives *dlp; int msgflags; { if (msgflags != RS_HIPRI) { /* Message was not M_PCPROTO */ return -1; } if (dlp->dl_primitive != prim) { /* Incorrect/unexpected return message */ return -1; } return 0; } /* * strgetmsg - get a message from a stream, with timeout. */ static int strgetmsg (fd, ctlp, datap, flagsp, caller) struct strbuf *ctlp, *datap; char *caller; int *flagsp; int fd; { int result; struct pollfd pfd; int count; time_t now; time_t starttime; int to_msec; pfd.fd = fd; pfd.events = POLLPRI; /* We're only interested in knowing * when we can receive the next high * priority message. */ pfd.revents = 0; now = time (&starttime); while (now <= starttime + DLPI_MAXWAIT) { to_msec = ((starttime + DLPI_MAXWAIT) - now) * 1000; count = poll (&pfd, 1, to_msec); if (count == 0) { /* log_fatal ("strgetmsg: timeout"); */ return -1; } else if (count < 0) { if (errno == EAGAIN || errno == EINTR) { time (&now); continue; } else { /* log_fatal ("poll: %m"); */ return -1; } } else { break; } } /* * Set flags argument and issue getmsg (). */ *flagsp = 0; if ((result = getmsg (fd, ctlp, datap, flagsp)) < 0) { return result; } /* * Check for MOREDATA and/or MORECTL. */ if (result & (MORECTL|MOREDATA)) { return -1; } /* * Check for at least sizeof (long) control data portion. */ if (ctlp -> len < sizeof (long)) { return -1; } return 0; } #if defined(USE_DLPI_SEND) int can_unicast_without_arp (ip) struct interface_info *ip; { return 1; } int can_receive_unicast_unconfigured (ip) struct interface_info *ip; { return 1; } int supports_multiple_interfaces (ip) struct interface_info *ip; { return 1; } void maybe_setup_fallback () { isc_result_t status; struct interface_info *fbi = (struct interface_info *)0; if (setup_fallback (&fbi, MDL)) { if_register_fallback (fbi); status = omapi_register_io_object ((omapi_object_t *)fbi, if_readsocket, 0, fallback_discard, 0, 0); if (status != ISC_R_SUCCESS) log_fatal ("Can't register I/O handle for %s: %s", fbi -> name, isc_result_totext (status)); interface_dereference (&fbi, MDL); } } #endif /* USE_DLPI_SEND */ void get_hw_addr(const char *name, struct hardware *hw) { int sock, unit; long buf[DLPI_MAXDLBUF]; union DL_primitives *dlp; dlp = (union DL_primitives *)buf; /* * Open a DLPI device. */ sock = dlpiopen(name); if (sock < 0) { log_fatal("Can't open DLPI device for %s: %m", name); } /* * Submit a DL_INFO_REQ request, to find the dl_mac_type and * dl_provider_style */ if (dlpiinforeq(sock) < 0) { log_fatal("Can't request DLPI MAC type for %s: %m", name); } if (dlpiinfoack(sock, (char *)buf) < 0) { log_fatal("Can't get DLPI MAC type for %s: %m", name); } switch (dlp->info_ack.dl_mac_type) { case DL_CSMACD: /* IEEE 802.3 */ case DL_ETHER: hw->hbuf[0] = HTYPE_ETHER; break; case DL_TPR: hw->hbuf[0] = HTYPE_IEEE802; break; case DL_FDDI: hw->hbuf[0] = HTYPE_FDDI; break; default: log_fatal("%s: unsupported DLPI MAC type %lu", name, (unsigned long)dlp->info_ack.dl_mac_type); } if (dlp->info_ack.dl_provider_style == DL_STYLE2) { /* * Attach to the device. If this fails, the device * does not exist. */ unit = dlpiunit((char *)name); if (dlpiattachreq(sock, unit) < 0 || dlpiokack(sock, (char *)buf) < 0) { log_fatal("Can't attach DLPI device for %s: %m", name); } } /* * Submit a DL_PHYS_ADDR_REQ request, to find * the hardware address. */ if (dlpiphysaddrreq(sock, DL_CURR_PHYS_ADDR) < 0) { log_fatal("Can't request DLPI hardware address for %s: %m", name); } if (dlpiphysaddrack(sock, (char *)buf) < 0) { log_fatal("Can't get DLPI hardware address for %s: %m", name); } if (dlp->physaddr_ack.dl_addr_length < sizeof(hw->hbuf)) { memcpy(hw->hbuf+1, (char *)buf + dlp->physaddr_ack.dl_addr_offset, dlp->physaddr_ack.dl_addr_length); hw->hlen = dlp->physaddr_ack.dl_addr_length + 1; } else { memcpy(hw->hbuf+1, (char *)buf + dlp->physaddr_ack.dl_addr_offset, sizeof(hw->hbuf)-1); hw->hlen = sizeof(hw->hbuf); } close(sock); } #endif /* USE_DLPI_SEND || USE_DLPI_RECEIVE || USE_DLPI_HWADDR */ dhcp-4.4.1/common/dns.c000644 000765 000024 00000263655 13243301226 015154 0ustar00tmarkstaff000000 000000 /* dns.c Domain Name Service subroutines. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 2001-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ /*! \file common/dns.c */ #include "dhcpd.h" #include "arpa/nameser.h" #include #include #include /* * This file contains code to connect the DHCP code to the libdns modules. * As part of that function it maintains a database of zone cuts that can * be used to figure out which server should be contacted to update any * given domain name. Included in the zone information may be a pointer * to a key in which case that key is used for the update. If no zone * is found then the DNS code determines the zone on its own. * * The way this works is that you define the domain name to which an * SOA corresponds, and the addresses of some primaries for that domain name: * * zone FOO.COM { * primary 10.0.17.1; * secondary 10.0.22.1, 10.0.23.1; * key "FOO.COM Key"; * } * * If an update is requested for GAZANGA.TOPANGA.FOO.COM, then the name * server looks in its database for a zone record for "GAZANGA.TOPANGA.FOO.COM", * doesn't find it, looks for one for "TOPANGA.FOO.COM", doesn't find *that*, * looks for "FOO.COM", finds it. So it * attempts the update to the primary for FOO.COM. If that times out, it * tries the secondaries. You can list multiple primaries if you have some * kind of magic name server that supports that. You shouldn't list * secondaries that don't know how to forward updates (e.g., BIND 8 doesn't * support update forwarding, AFAIK). If no TSIG key is listed, the update * is attempted without TSIG. * * You can also include IPv6 addresses via the primary6 and secondary6 * options. The search order for the addresses is primary, primary6, * secondary and lastly secondary6, with a limit on the number of * addresses used. Currently this limit is 3. * * The DHCP server tries to find an existing zone for any given name by * trying to look up a local zone structure for each domain containing * that name, all the way up to '.'. If it finds one cached, it tries * to use that one to do the update. That's why it tries to update * "FOO.COM" above, even though theoretically it should try GAZANGA... * and TOPANGA... first. * * If the update fails with a predefined zone the zone is marked as bad * and another search of the predefined zones is done. If no predefined * zone is found finding a zone is left to the DNS module via examination * of SOA records. If the DNS module finds a zone it may cache the zone * but the zone won't be cached here. * * TSIG updates are not performed on zones found by the DNS module - if * you want TSIG updates you _must_ write a zone definition linking the * key to the zone. In cases where you know for sure what the key is * but do not want to hardcode the IP addresses of the primary or * secondaries, a zone declaration can be made that doesn't include any * primary or secondary declarations. When the DHCP server encounters * this while hunting up a matching zone for a name, it looks up the SOA, * fills in the IP addresses, and uses that record for the update. * If the SOA lookup returns NXRRSET, a warning is printed and the zone is * discarded, TSIG key and all. The search for the zone then continues * as if the zone record hadn't been found. Zones without IP addresses * don't match when initially hunting for a zone to update. * * When an update is attempted and no predefined zone is found * that matches any enclosing domain of the domain being updated, the DHCP * server goes through the same process that is done when the update to a * predefined zone fails - starting with the most specific domain * name (GAZANGA.TOPANGA.FOO.COM) and moving to the least specific (the root), * it tries to look up an SOA record. * * TSIG keys are defined like this: * * key "FOO.COM Key" { * algorithm HMAC-MD5.SIG-ALG.REG.INT; * secret ; * } * * is a number expressed in base64 that represents the key. * It's also permissible to use a quoted string here - this will be * translated as the ASCII bytes making up the string, and will not * include any NUL termination. The key name can be any text string, * and the key type must be one of the key types defined in the draft * or by the IANA. Currently only the HMAC-MD5... key type is * supported. * * The DDNS processing has been split into two areas. One is the * control code that determines what should be done. That code is found * in the client or server directories. The other is the common code * that performs functions such as properly formatting the arguments. * That code is found in this file. The basic processing flow for a * DDNS update is: * In the client or server code determine what needs to be done and * collect the necesary information then pass it to a function from * this file. * In this code lookup the zone and extract the zone and key information * (if available) and prepare the arguments for the DNS module. * When the DNS module completes its work (times out or gets a reply) * it will trigger another function here which does generic processing * and then passes control back to the code from the server or client. * The server or client code then determines the next step which may * result in another call to this module in which case the process repeats. */ dns_zone_hash_t *dns_zone_hash; /* * DHCP dns structures * Normally the relationship between these structures isn't one to one * but in the DHCP case it (mostly) is. To make the allocations, frees, * and passing of the memory easier we make a single structure with all * the pieces. * * The maximum size of the data buffer should be large enough for any * items DHCP will generate */ typedef struct dhcp_ddns_rdata { dns_rdata_t rdata; dns_rdatalist_t rdatalist; dns_rdataset_t rdataset; } dhcp_ddns_data_t; /* Function pointer type for functions which build DDNS update contents */ typedef isc_result_t (*builder_func_t)(dhcp_ddns_cb_t *ddns_cb, dhcp_ddns_data_t *dataspace, dns_name_t *pname, dns_name_t *uname); #if defined (NSUPDATE) #if defined (DNS_ZONE_LOOKUP) /* * The structure used to find a nameserver if there wasn't a zone entry. * Currently we assume we won't have many of these outstanding at any * time so we go with a simple linked list. * In use find_zone_start() will fill in the oname with the name * requested by the DDNS code. zname will point to it and be * advanced as labels are removed. If the DNS client code returns * a set of name servers eventp and rdataset will be set. Then * the code will walk through the nameservers in namelist and * find addresses that are stored in addrs and addrs6. */ typedef struct dhcp_ddns_ns { struct dhcp_ddns_ns *next; struct data_string oname; /* the original name for DDNS */ char *zname; /* a pointer into the original name for the zone we are checking */ dns_clientresevent_t *eventp; /* pointer to the event that provided the namelist, we can't free the eventp until we free the namelist */ dns_name_t *ns_name; /* current name server we are examining */ dns_rdataset_t *rdataset; dns_rdatatype_t rdtype; /* type of address we want */ struct in_addr addrs[DHCP_MAXNS]; /* space for v4 addresses */ struct in6_addr addrs6[DHCP_MAXNS]; /* space for v6 addresses */ int num_addrs; int num_addrs6; int ttl; void *transaction; /* transaction id for DNS calls */ } dhcp_ddns_ns_t; /* * The list of DDNS names for which we are attempting to find a name server. * This list is used for finding the name server, it doesn't include the * information necessary to do the DDNS request after finding a name server. * The code attempts to minimize duplicate requests by examining the list * to see if we are already trying to find a substring of the new request. * For example imagine the first request is "a.b.c.d.e." and the server has * already discarded the first two lables and is trying "c.d.e.". If the * next request is for "x.y.c.d.e." the code assumes the in progress * request is sufficient and doesn't add a new request for the second name. * If the next request was for "x.y.z.d.e." the code doesn't assume they * will use the same nameserver and starts a second request. * This strategy will not eliminate all duplicates but is simple and * should be sufficient. */ dhcp_ddns_ns_t *dns_outstanding_ns = NULL; /* * Routines to manipulate the list of outstanding searches * * add_to_ns_queue() - adds the given control block to the queue * * remove_from_ns_queue() - removes the given control block from * the queue * * find_in_ns_queue() compares the name from the given control * block with the control blocks in the queue. It returns * success if a matching entry is found. In order to match * the entry already on the queue must be shorter than the * incoming name must match the ending substring of the name. */ void add_to_ns_queue(dhcp_ddns_ns_t *ns_cb) { ns_cb->next = dns_outstanding_ns; dns_outstanding_ns = ns_cb; } void remove_from_ns_queue(dhcp_ddns_ns_t *ns_cb) { dhcp_ddns_ns_t **foo; foo = &dns_outstanding_ns; while (*foo) { if (*foo == ns_cb) { *foo = ns_cb->next; break; } foo = &((*foo)->next); } ns_cb->next = NULL; } isc_result_t find_in_ns_queue(dhcp_ddns_ns_t *ns_cb) { dhcp_ddns_ns_t *temp_cb; int in_len, temp_len; in_len = strlen(ns_cb->zname); for(temp_cb = dns_outstanding_ns; temp_cb != NULL; temp_cb = temp_cb->next) { temp_len = strlen(temp_cb->zname); if (temp_len > in_len) continue; if (strcmp(temp_cb->zname, ns_cb->zname + (in_len - temp_len)) == 0) return(ISC_R_SUCCESS); } return(ISC_R_NOTFOUND); } void cache_found_zone (dhcp_ddns_ns_t *); #endif void ddns_interlude(isc_task_t *, isc_event_t *); #if defined (TRACING) /* * Code to support tracing DDNS packets. We trace packets going to and * coming from the libdns code but don't try to track the packets * exchanged between the libdns code and the dns server(s) it contacts. * * The code is split into two sets of routines * input refers to messages received from the dns module * output refers to messages sent to the dns module * Currently there are three routines in each set * write is used to write information about the message to the trace file * this routine is called directly from the proper place in the code. * read is used to read information about a message from the trace file * this routine is called from the trace loop as it reads through * the file and is registered via the trace_type_register routine. * When playing back a trace file we shall absorb records of output * messages as part of processing the write function, therefore * any output messages we encounter are flagged as errors. * stop isn't currently used in this code but is needed for the register * routine. * * We pass a pointer to a control block to the dns module which it returns * to use as part of the result. As the pointer may vary between traces * we need to map between those from the trace file and the new ones during * playback. * * The mapping is complicated a little as a pointer could be 4 or 8 bytes * long. We treat the old pointer as an 8 byte quantity and pad and compare * as necessary. */ /* * Structure used to map old pointers to new pointers. * Old pointers are 8 bytes long as we don't know if the trace was * done on a 64 bit or 32 bit machine. */ #define TRACE_PTR_LEN 8 typedef struct dhcp_ddns_map { char old_pointer[TRACE_PTR_LEN]; void *new_pointer; struct dhcp_ddns_map *next; } dhcp_ddns_map_t; /* The starting point for the map structure */ static dhcp_ddns_map_t *ddns_map; trace_type_t *trace_ddns_input; trace_type_t *trace_ddns_output; /* * The data written to the trace file is: * 32 bits result from dns * 64 bits pointer of cb */ void trace_ddns_input_write(dhcp_ddns_cb_t *ddns_cb, isc_result_t result) { trace_iov_t iov[2]; u_int32_t old_result; char old_pointer[TRACE_PTR_LEN]; old_result = htonl((u_int32_t)result); memset(old_pointer, 0, TRACE_PTR_LEN); memcpy(old_pointer, &ddns_cb, sizeof(ddns_cb)); iov[0].len = sizeof(old_result); iov[0].buf = (char *)&old_result; iov[1].len = TRACE_PTR_LEN; iov[1].buf = old_pointer; trace_write_packet_iov(trace_ddns_input, 2, iov, MDL); } /* * Process the result and pointer from the trace file. * We use the pointer map to find the proper pointer for this instance. * Then we need to construct an event to pass along to the interlude * function. */ static void trace_ddns_input_read(trace_type_t *ttype, unsigned length, char *buf) { u_int32_t old_result; char old_pointer[TRACE_PTR_LEN]; dns_clientupdateevent_t *eventp; void *new_pointer; dhcp_ddns_map_t *ddns_map_ptr; if (length < (sizeof(old_result) + TRACE_PTR_LEN)) { log_error("trace_ddns_input_read: data too short"); return; } memcpy(&old_result, buf, sizeof(old_result)); memcpy(old_pointer, buf + sizeof(old_result), TRACE_PTR_LEN); /* map the old pointer to a new pointer */ for (ddns_map_ptr = ddns_map; ddns_map_ptr != NULL; ddns_map_ptr = ddns_map_ptr->next) { if ((ddns_map_ptr->new_pointer != NULL) && memcmp(ddns_map_ptr->old_pointer, old_pointer, TRACE_PTR_LEN) == 0) { new_pointer = ddns_map_ptr->new_pointer; ddns_map_ptr->new_pointer = NULL; memset(ddns_map_ptr->old_pointer, 0, TRACE_PTR_LEN); break; } } if (ddns_map_ptr == NULL) { log_error("trace_dns_input_read: unable to map cb pointer"); return; } eventp = (dns_clientupdateevent_t *) isc_event_allocate(dhcp_gbl_ctx.mctx, dhcp_gbl_ctx.task, 0, ddns_interlude, new_pointer, sizeof(dns_clientupdateevent_t)); if (eventp == NULL) { log_error("trace_ddns_input_read: unable to allocate event"); return; } eventp->result = ntohl(old_result); ddns_interlude(dhcp_gbl_ctx.task, (isc_event_t *)eventp); return; } static void trace_ddns_input_stop(trace_type_t *ttype) { } /* * We use the same arguments as for the dns startupdate function to * allows us to choose between the two via a macro. If tracing isn't * in use we simply call the dns function directly. * * If we are doing playback we read the next packet from the file * and compare the type. If it matches we extract the results and pointer * from the trace file. The results are returned to the caller as if * they had called the dns routine. The pointer is used to construct a * map for when the "reply" is processed. * * The data written to trace file is: * 32 bits result * 64 bits pointer of cb (DDNS Control block) * contents of cb */ isc_result_t trace_ddns_output_write(dns_client_t *client, dns_rdataclass_t rdclass, dns_name_t *zonename, dns_namelist_t *prerequisites, dns_namelist_t *updates, isc_sockaddrlist_t *servers, dns_tsec_t *tsec, unsigned int options, isc_task_t *task, isc_taskaction_t action, void *arg, dns_clientupdatetrans_t **transp) { isc_result_t result; u_int32_t old_result; char old_pointer[TRACE_PTR_LEN]; dhcp_ddns_map_t *ddns_map_ptr; if (trace_playback() != 0) { /* We are doing playback, extract the entry from the file */ unsigned buflen = 0; char *inbuf = NULL; result = trace_get_packet(&trace_ddns_output, &buflen, &inbuf); if (result != ISC_R_SUCCESS) { log_error("trace_ddns_output_write: no input found"); return (ISC_R_FAILURE); } if (buflen < (sizeof(old_result) + TRACE_PTR_LEN)) { log_error("trace_ddns_output_write: data too short"); dfree(inbuf, MDL); return (ISC_R_FAILURE); } memcpy(&old_result, inbuf, sizeof(old_result)); result = ntohl(old_result); memcpy(old_pointer, inbuf + sizeof(old_result), TRACE_PTR_LEN); dfree(inbuf, MDL); /* add the pointer to the pointer map */ for (ddns_map_ptr = ddns_map; ddns_map_ptr != NULL; ddns_map_ptr = ddns_map_ptr->next) { if (ddns_map_ptr->new_pointer == NULL) { break; } } /* * If we didn't find an empty entry, allocate an entry and * link it into the list. The list isn't ordered. */ if (ddns_map_ptr == NULL) { ddns_map_ptr = dmalloc(sizeof(*ddns_map_ptr), MDL); if (ddns_map_ptr == NULL) { log_error("trace_ddns_output_write: " "unable to allocate map entry"); return(ISC_R_FAILURE); } ddns_map_ptr->next = ddns_map; ddns_map = ddns_map_ptr; } memcpy(ddns_map_ptr->old_pointer, old_pointer, TRACE_PTR_LEN); ddns_map_ptr->new_pointer = arg; } else { /* We aren't doing playback, make the actual call */ result = dns_client_startupdate(client, rdclass, zonename, prerequisites, updates, servers, tsec, options, task, action, arg, transp); } if (trace_record() != 0) { /* We are recording, save the information to the file */ trace_iov_t iov[3]; old_result = htonl((u_int32_t)result); memset(old_pointer, 0, TRACE_PTR_LEN); memcpy(old_pointer, &arg, sizeof(arg)); iov[0].len = sizeof(old_result); iov[0].buf = (char *)&old_result; iov[1].len = TRACE_PTR_LEN; iov[1].buf = old_pointer; /* Write out the entire cb, in case we want to look at it */ iov[2].len = sizeof(dhcp_ddns_cb_t); iov[2].buf = (char *)arg; trace_write_packet_iov(trace_ddns_output, 3, iov, MDL); } return(result); } static void trace_ddns_output_read(trace_type_t *ttype, unsigned length, char *buf) { log_error("unaccounted for ddns output."); } static void trace_ddns_output_stop(trace_type_t *ttype) { } void trace_ddns_init() { trace_ddns_output = trace_type_register("ddns-output", NULL, trace_ddns_output_read, trace_ddns_output_stop, MDL); trace_ddns_input = trace_type_register("ddns-input", NULL, trace_ddns_input_read, trace_ddns_input_stop, MDL); ddns_map = NULL; } #define ddns_update trace_ddns_output_write #else #define ddns_update dns_client_startupdate #endif /* TRACING */ #define zone_resolve dns_client_startresolve /* * Code to allocate and free a dddns control block. This block is used * to pass and track the information associated with a DDNS update request. */ dhcp_ddns_cb_t * ddns_cb_alloc(const char *file, int line) { dhcp_ddns_cb_t *ddns_cb; int i; ddns_cb = dmalloc(sizeof(*ddns_cb), file, line); if (ddns_cb != NULL) { ISC_LIST_INIT(ddns_cb->zone_server_list); for (i = 0; i < DHCP_MAXNS; i++) { ISC_LINK_INIT(&ddns_cb->zone_addrs[i], link); } } #if defined (DEBUG_DNS_UPDATES) log_info("%s(%d): Allocating ddns_cb=%p", file, line, ddns_cb); #endif return(ddns_cb); } void ddns_cb_free(dhcp_ddns_cb_t *ddns_cb, const char *file, int line) { #if defined (DEBUG_DNS_UPDATES) log_info("%s(%d): freeing ddns_cb=%p", file, line, ddns_cb); #endif data_string_forget(&ddns_cb->fwd_name, file, line); data_string_forget(&ddns_cb->rev_name, file, line); data_string_forget(&ddns_cb->dhcid, file, line); if (ddns_cb->zone != NULL) { forget_zone((struct dns_zone **)&ddns_cb->zone); } /* Should be freed by now, check just in case. */ if (ddns_cb->transaction != NULL) { log_error("Impossible memory leak at %s:%d (attempt to free " "DDNS Control Block before transaction).", MDL); } /* Should be freed by now, check just in case. */ if (ddns_cb->fixed6_ia) { log_error("Possible memory leak at %s:%d (attempt to free " "DDNS Control Block before fxed6_ia).", MDL); } dfree(ddns_cb, file, line); } void ddns_cb_forget_zone(dhcp_ddns_cb_t *ddns_cb) { int i; forget_zone(&ddns_cb->zone); ddns_cb->zone_name[0] = 0; ISC_LIST_INIT(ddns_cb->zone_server_list); for (i = 0; i < DHCP_MAXNS; i++) { ISC_LINK_INIT(&ddns_cb->zone_addrs[i], link); } } #endif isc_result_t remove_dns_zone (struct dns_zone *zone) { struct dns_zone *tz = NULL; if (dns_zone_hash) { dns_zone_hash_lookup(&tz, dns_zone_hash, zone->name, 0, MDL); if (tz != NULL) { dns_zone_hash_delete(dns_zone_hash, tz->name, 0, MDL); dns_zone_dereference(&tz, MDL); } } return (ISC_R_SUCCESS); } isc_result_t enter_dns_zone (struct dns_zone *zone) { struct dns_zone *tz = (struct dns_zone *)0; if (dns_zone_hash) { dns_zone_hash_lookup (&tz, dns_zone_hash, zone -> name, 0, MDL); if (tz == zone) { dns_zone_dereference (&tz, MDL); return ISC_R_SUCCESS; } if (tz) { dns_zone_hash_delete (dns_zone_hash, zone -> name, 0, MDL); dns_zone_dereference (&tz, MDL); } } else { if (!dns_zone_new_hash(&dns_zone_hash, DNS_HASH_SIZE, MDL)) return ISC_R_NOMEMORY; } dns_zone_hash_add (dns_zone_hash, zone -> name, 0, zone, MDL); return ISC_R_SUCCESS; } isc_result_t dns_zone_lookup (struct dns_zone **zone, const char *name) { int len; char *tname = (char *)0; isc_result_t status; if (!dns_zone_hash) return ISC_R_NOTFOUND; len = strlen (name); if (name [len - 1] != '.') { tname = dmalloc ((unsigned)len + 2, MDL); if (!tname) return ISC_R_NOMEMORY; strcpy (tname, name); tname [len] = '.'; tname [len + 1] = 0; name = tname; } if (!dns_zone_hash_lookup (zone, dns_zone_hash, name, 0, MDL)) status = ISC_R_NOTFOUND; else if ((*zone)->timeout && (*zone)->timeout < cur_time) { dns_zone_hash_delete(dns_zone_hash, (*zone)->name, 0, MDL); dns_zone_dereference(zone, MDL); status = ISC_R_NOTFOUND; } else status = ISC_R_SUCCESS; if (tname) dfree (tname, MDL); return status; } int dns_zone_dereference (ptr, file, line) struct dns_zone **ptr; const char *file; int line; { struct dns_zone *dns_zone; if ((ptr == NULL) || (*ptr == NULL)) { log_error("%s(%d): null pointer", file, line); #if defined (POINTER_DEBUG) abort(); #else return (0); #endif } dns_zone = *ptr; *ptr = NULL; --dns_zone->refcnt; rc_register(file, line, ptr, dns_zone, dns_zone->refcnt, 1, RC_MISC); if (dns_zone->refcnt > 0) return (1); if (dns_zone->refcnt < 0) { log_error("%s(%d): negative refcnt!", file, line); #if defined (DEBUG_RC_HISTORY) dump_rc_history(dns_zone); #endif #if defined (POINTER_DEBUG) abort(); #else return (0); #endif } if (dns_zone->name) dfree(dns_zone->name, file, line); if (dns_zone->key) omapi_auth_key_dereference(&dns_zone->key, file, line); if (dns_zone->primary) option_cache_dereference(&dns_zone->primary, file, line); if (dns_zone->secondary) option_cache_dereference(&dns_zone->secondary, file, line); if (dns_zone->primary6) option_cache_dereference(&dns_zone->primary6, file, line); if (dns_zone->secondary6) option_cache_dereference(&dns_zone->secondary6, file, line); dfree(dns_zone, file, line); return (1); } #if defined (NSUPDATE) #if defined (DNS_ZONE_LOOKUP) /* Helper function to copy the address from an rdataset to * the nameserver control block. Mostly to avoid really long * lines in the nested for loops */ void zone_addr_to_ns(dhcp_ddns_ns_t *ns_cb, dns_rdataset_t *rdataset) { dns_rdata_t rdata; dns_rdata_in_a_t a; dns_rdata_in_aaaa_t aaaa; dns_rdata_init(&rdata); dns_rdataset_current(rdataset, &rdata); switch (rdataset->type) { case dns_rdatatype_a: (void) dns_rdata_tostruct(&rdata, &a, NULL); memcpy(&ns_cb->addrs[ns_cb->num_addrs], &a.in_addr, 4); ns_cb->num_addrs++; dns_rdata_freestruct(&a); break; case dns_rdatatype_aaaa: (void) dns_rdata_tostruct(&rdata, &aaaa, NULL); memcpy(&ns_cb->addrs6[ns_cb->num_addrs6], &aaaa.in6_addr, 16); ns_cb->num_addrs6++; dns_rdata_freestruct(&aaaa); break; default: break; } if ((ns_cb->ttl == 0) || (ns_cb->ttl > rdataset->ttl)) ns_cb->ttl = rdataset->ttl; } /* * The following three routines co-operate to find the addresses of * the nameservers to use for a zone if we don't have a zone statement. * We strongly suggest the use of a zone statement to avoid problmes * and to allow for the use of TSIG and therefore better security, but * include this functionality for those that don't want such statements. * * find_zone_start(ddns_cb, direction) * This is the first of the routines, it is called from the rest of * the ddns code when we have received a request for DDNS for a name * and don't have a zone entry that would cover that name. The name * is in the ddns_cb as specified by the direction (forward or reverse). * The start function pulls the name out and constructs the name server * block then starts the process by calling the DNS client code. * * find_zone_ns(taskp, eventp) * This is the second step of the process. The DNS client code will * call this when it has gotten a response or timed out. If the response * doesn't have a list of nameservers we remove another label from the * zone name and try again. If the response does include a list of * nameservers we start walking through the list attempting to get * addresses for the nameservers. * * find_zone_addrs(taskp, eventp) * This is the third step of the process. In find_zone_ns we got * a list of nameserves and started walking through them. This continues * the walk and if we get back any addresses it adds them to our list. * When we get enough addresses or run out of nameservers we construct * a zone entry and insert it into the zone hash for the rest of the * DDNS code to use. */ void find_zone_addrs(isc_task_t *taskp, isc_event_t *eventp) { dns_clientresevent_t *ddns_event = (dns_clientresevent_t *)eventp; dhcp_ddns_ns_t *ns_cb = (dhcp_ddns_ns_t *)eventp->ev_arg; dns_name_t *ns_name = NULL; dns_rdataset_t *rdataset; isc_result_t result; dns_name_t *name; dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdata_ns_t ns; /* the transaction is done, get rid of the tag */ dns_client_destroyrestrans(&ns_cb->transaction); /* If we succeeded we try and extract the addresses, if we can * and we have enough we are done. If we didn't succeed or * we don't have enough addresses afterwards we drop through * and try the next item on the list. */ if (ddns_event->result == ISC_R_SUCCESS) { for (name = ISC_LIST_HEAD(ddns_event->answerlist); name != NULL; name = ISC_LIST_NEXT(name, link)) { for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; rdataset = ISC_LIST_NEXT(rdataset, link)) { for (result = dns_rdataset_first(rdataset); result == ISC_R_SUCCESS; result = dns_rdataset_next(rdataset)) { /* add address to cb */ zone_addr_to_ns(ns_cb, rdataset); /* We are done if we have * enough addresses */ if (ns_cb->num_addrs + ns_cb->num_addrs6 >= DHCP_MAXNS) goto done; } } } } /* We need more addresses. * We restart the loop we were in before. */ for (ns_name = ns_cb->ns_name; ns_name != NULL; ns_name = ISC_LIST_NEXT(ns_name, link)) { if (ns_name == ns_cb->ns_name) { /* first time through, use saved state */ rdataset = ns_cb->rdataset; } else { rdataset = ISC_LIST_HEAD(ns_name->list); } for (; rdataset != NULL; rdataset = ISC_LIST_NEXT(rdataset, link)) { if (rdataset->type != dns_rdatatype_ns) continue; dns_rdata_init(&rdata); if (rdataset == ns_cb->rdataset) { /* first time through use the saved state */ if (ns_cb->rdtype == dns_rdatatype_a) { ns_cb->rdtype = dns_rdatatype_aaaa; } else { ns_cb->rdtype = dns_rdatatype_a; if (dns_rdataset_next(rdataset) != ISC_R_SUCCESS) continue; } } else { if ((!dns_rdataset_isassociated(rdataset)) || (dns_rdataset_first(rdataset) != ISC_R_SUCCESS)) continue; } dns_rdataset_current(rdataset, &rdata); if (dns_rdata_tostruct(&rdata, &ns, NULL) != ISC_R_SUCCESS) continue; /* Save our current state */ ns_cb->ns_name = ns_name; ns_cb->rdataset = rdataset; /* And call out to DNS */ result = zone_resolve(dhcp_gbl_ctx.dnsclient, &ns.name, dns_rdataclass_in, ns_cb->rdtype, DNS_CLIENTRESOPT_NODNSSEC, dhcp_gbl_ctx.task, find_zone_addrs, (void *)ns_cb, &ns_cb->transaction); /* do we need to clean this? */ dns_rdata_freestruct(&ns); if (result == ISC_R_SUCCESS) /* we have started the next step, cleanup * the structures associated with this call * but leave the cb for the next round */ goto cleanup; log_error("find_zone_ns: unable to continue " "resolve: %s %s", ns_cb->zname, isc_result_totext(result)); /* The call to start a resolve transaction failed, * should we try to continue with any other names? * For now let's not, but let's use whatever we * may already have. */ goto done; } } done: /* we've either gotten our max number of addresses or * run out of nameservers to try. Convert the cb into * a zone and insert it into the zone hash. Then * we need to clean up the saved state. */ if ((ns_cb->num_addrs != 0) || (ns_cb->num_addrs6 != 0)) cache_found_zone(ns_cb); dns_client_freeresanswer(dhcp_gbl_ctx.dnsclient, &ns_cb->eventp->answerlist); isc_event_free((isc_event_t **)&ns_cb->eventp); remove_from_ns_queue(ns_cb); data_string_forget(&ns_cb->oname, MDL); dfree(ns_cb, MDL); cleanup: /* cleanup any of the new state information */ dns_client_freeresanswer(dhcp_gbl_ctx.dnsclient, &ddns_event->answerlist); isc_event_free(&eventp); return; } /* * Routine to continue the process of finding a nameserver via the DNS * This is routine is called when we are still trying to get a list * of nameservers to process. */ void find_zone_ns(isc_task_t *taskp, isc_event_t *eventp) { dns_clientresevent_t *ddns_event = (dns_clientresevent_t *)eventp; dhcp_ddns_ns_t *ns_cb = (dhcp_ddns_ns_t *)eventp->ev_arg; dns_fixedname_t zname0; dns_name_t *zname = NULL, *ns_name = NULL; dns_rdataset_t *rdataset; isc_result_t result; dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdata_ns_t ns; /* the transaction is done, get rid of the tag */ dns_client_destroyrestrans(&ns_cb->transaction); if (ddns_event->result != ISC_R_SUCCESS) { /* We didn't find any nameservers, try again */ /* Remove a label and continue */ ns_cb->zname = strchr(ns_cb->zname, '.'); if ((ns_cb->zname == NULL) || (ns_cb->zname[1] == 0)) { /* No more labels, all done */ goto cleanup; } ns_cb->zname++; /* Create a DNS version of the zone name and call the * resolver code */ if (((result = dhcp_isc_name((unsigned char *)ns_cb->zname, &zname0, &zname)) != ISC_R_SUCCESS) || ((result = zone_resolve(dhcp_gbl_ctx.dnsclient, zname, dns_rdataclass_in, dns_rdatatype_ns, DNS_CLIENTRESOPT_NODNSSEC, dhcp_gbl_ctx.task, find_zone_ns, (void *)ns_cb, &ns_cb->transaction)) != ISC_R_SUCCESS)) { log_error("find_zone_ns: Unable to build " "name or start resolve: %s %s", ns_cb->zname, isc_result_totext(result)); goto cleanup; } /* we have successfully started the next iteration * of this step, clean up from the call and continue */ dns_client_freeresanswer(dhcp_gbl_ctx.dnsclient, &ddns_event->answerlist); isc_event_free(&eventp); return; } /* We did get a set of nameservers, save the information and * start trying to get addresses */ ns_cb->eventp = ddns_event; for (ns_name = ISC_LIST_HEAD(ddns_event->answerlist); ns_name != NULL; ns_name = ISC_LIST_NEXT(ns_name, link)) { for (rdataset = ISC_LIST_HEAD(ns_name->list); rdataset != NULL; rdataset = ISC_LIST_NEXT(rdataset, link)) { if (rdataset->type != dns_rdatatype_ns) continue; if ((!dns_rdataset_isassociated(rdataset)) || (dns_rdataset_first(rdataset) != ISC_R_SUCCESS)) continue; dns_rdataset_current(rdataset, &rdata); if (dns_rdata_tostruct(&rdata, &ns, NULL) != ISC_R_SUCCESS) continue; /* Save our current state */ ns_cb->ns_name = ns_name; ns_cb->rdataset = rdataset; /* And call out to DNS */ result = zone_resolve(dhcp_gbl_ctx.dnsclient, &ns.name, dns_rdataclass_in, ns_cb->rdtype, DNS_CLIENTRESOPT_NODNSSEC, dhcp_gbl_ctx.task, find_zone_addrs, (void *)ns_cb, &ns_cb->transaction); /* do we need to clean this? */ dns_rdata_freestruct(&ns); if (result == ISC_R_SUCCESS) /* We have successfully started the next step * we don't cleanup the eventp block as we are * still using it. */ return; log_error("find_zone_ns: unable to continue " "resolve: %s %s", ns_cb->zname, isc_result_totext(result)); /* The call to start a resolve transaction failed, * should we try to continue with any other names? * For now let's not */ goto cleanup; } } cleanup: /* When we add a queue to manage the DDNS * requests we will need to remove any that * were waiting for this resolution */ dns_client_freeresanswer(dhcp_gbl_ctx.dnsclient, &ddns_event->answerlist); isc_event_free(&eventp); remove_from_ns_queue(ns_cb); data_string_forget(&ns_cb->oname, MDL); dfree(ns_cb, MDL); return; } /* * Start the process of finding nameservers via the DNS because * we don't have a zone entry already. * We construct a control block and fill in the DDNS name. As * the process continues we shall move the zname pointer to * indicate which labels we are still using. The rest of * the control block will be filled in as we continue processing. */ isc_result_t find_zone_start(dhcp_ddns_cb_t *ddns_cb, int direction) { isc_result_t status = ISC_R_NOTFOUND; dhcp_ddns_ns_t *ns_cb; dns_fixedname_t zname0; dns_name_t *zname = NULL; /* * We don't validate np as that was already done in find_cached_zone() */ /* Allocate the control block for this request */ ns_cb = dmalloc(sizeof(*ns_cb), MDL); if (ns_cb == NULL) { log_error("find_zone_start: unable to allocate cb"); return(ISC_R_FAILURE); } ns_cb->rdtype = dns_rdatatype_a; /* Copy the data string so the NS lookup is independent of the DDNS */ if (direction == FIND_FORWARD) { data_string_copy(&ns_cb->oname, &ddns_cb->fwd_name, MDL); } else { data_string_copy(&ns_cb->oname, &ddns_cb->rev_name, MDL); } ns_cb->zname = (char *)ns_cb->oname.data; /* * Check the dns_outstanding_ns queue to see if we are * already processing something that would cover this name */ if (find_in_ns_queue(ns_cb) == ISC_R_SUCCESS) { data_string_forget(&ns_cb->oname, MDL); dfree(ns_cb, MDL); return (ISC_R_SUCCESS); } /* Create a DNS version of the zone name and call the * resolver code */ if (((status = dhcp_isc_name((unsigned char *)ns_cb->zname, &zname0, &zname)) != ISC_R_SUCCESS) || ((status = zone_resolve(dhcp_gbl_ctx.dnsclient, zname, dns_rdataclass_in, dns_rdatatype_ns, DNS_CLIENTRESOPT_NODNSSEC, dhcp_gbl_ctx.task, find_zone_ns, (void *)ns_cb, &ns_cb->transaction)) != ISC_R_SUCCESS)) { log_error("find_zone_start: Unable to build " "name or start resolve: %s %s", ns_cb->zname, isc_result_totext(status)); /* We failed to start the process, clean up */ data_string_forget(&ns_cb->oname, MDL); dfree(ns_cb, MDL); } else { /* We started the process, attach the control block * to the queue */ add_to_ns_queue(ns_cb); } return (status); } #endif isc_result_t find_cached_zone(dhcp_ddns_cb_t *ddns_cb, int direction) { isc_result_t status = ISC_R_NOTFOUND; const char *np; struct dns_zone *zone = NULL; struct data_string nsaddrs; struct in_addr zone_addr; struct in6_addr zone_addr6; int ix; if (direction == FIND_FORWARD) { np = (const char *)ddns_cb->fwd_name.data; } else { np = (const char *)ddns_cb->rev_name.data; } /* We can't look up a null zone. */ if ((np == NULL) || (*np == '\0')) { return (DHCP_R_INVALIDARG); } /* * For each subzone, try to find a cached zone. */ for (;;) { status = dns_zone_lookup(&zone, np); if (status == ISC_R_SUCCESS) break; np = strchr(np, '.'); if (np == NULL) break; np++; } if (status != ISC_R_SUCCESS) return (status); /* Make sure the zone is valid, we've already gotten * rid of expired dynamic zones. Check to see if * we repudiated this zone. If so give up. */ if ((zone->flags & DNS_ZONE_INACTIVE) != 0) { dns_zone_dereference(&zone, MDL); return (ISC_R_FAILURE); } /* Make sure the zone name will fit. */ if (strlen(zone->name) > sizeof(ddns_cb->zone_name)) { dns_zone_dereference(&zone, MDL); return (ISC_R_NOSPACE); } strcpy((char *)&ddns_cb->zone_name[0], zone->name); memset (&nsaddrs, 0, sizeof nsaddrs); ix = 0; if (zone->primary) { if (evaluate_option_cache(&nsaddrs, NULL, NULL, NULL, NULL, NULL, &global_scope, zone->primary, MDL)) { int ip = 0; while (ix < DHCP_MAXNS) { if (ip + 4 > nsaddrs.len) break; memcpy(&zone_addr, &nsaddrs.data[ip], 4); isc_sockaddr_fromin(&ddns_cb->zone_addrs[ix], &zone_addr, NS_DEFAULTPORT); ISC_LIST_APPEND(ddns_cb->zone_server_list, &ddns_cb->zone_addrs[ix], link); ip += 4; ix++; } data_string_forget(&nsaddrs, MDL); } } if (zone->primary6) { if (evaluate_option_cache(&nsaddrs, NULL, NULL, NULL, NULL, NULL, &global_scope, zone->primary6, MDL)) { int ip = 0; while (ix < DHCP_MAXNS) { if (ip + 16 > nsaddrs.len) break; memcpy(&zone_addr6, &nsaddrs.data[ip], 16); isc_sockaddr_fromin6(&ddns_cb->zone_addrs[ix], &zone_addr6, NS_DEFAULTPORT); ISC_LIST_APPEND(ddns_cb->zone_server_list, &ddns_cb->zone_addrs[ix], link); ip += 16; ix++; } data_string_forget(&nsaddrs, MDL); } } if (zone->secondary) { if (evaluate_option_cache(&nsaddrs, NULL, NULL, NULL, NULL, NULL, &global_scope, zone->secondary, MDL)) { int ip = 0; while (ix < DHCP_MAXNS) { if (ip + 4 > nsaddrs.len) break; memcpy(&zone_addr, &nsaddrs.data[ip], 4); isc_sockaddr_fromin(&ddns_cb->zone_addrs[ix], &zone_addr, NS_DEFAULTPORT); ISC_LIST_APPEND(ddns_cb->zone_server_list, &ddns_cb->zone_addrs[ix], link); ip += 4; ix++; } data_string_forget (&nsaddrs, MDL); } } if (zone->secondary6) { if (evaluate_option_cache(&nsaddrs, NULL, NULL, NULL, NULL, NULL, &global_scope, zone->secondary6, MDL)) { int ip = 0; while (ix < DHCP_MAXNS) { if (ip + 16 > nsaddrs.len) break; memcpy(&zone_addr6, &nsaddrs.data[ip], 16); isc_sockaddr_fromin6(&ddns_cb->zone_addrs[ix], &zone_addr6, NS_DEFAULTPORT); ISC_LIST_APPEND(ddns_cb->zone_server_list, &ddns_cb->zone_addrs[ix], link); ip += 16; ix++; } data_string_forget (&nsaddrs, MDL); } } dns_zone_reference(&ddns_cb->zone, zone, MDL); dns_zone_dereference (&zone, MDL); return ISC_R_SUCCESS; } void forget_zone (struct dns_zone **zone) { dns_zone_dereference (zone, MDL); } void repudiate_zone (struct dns_zone **zone) { /* verify that we have a pointer at least */ if ((zone == NULL) || (*zone == NULL)) { log_info("Null argument to repudiate zone"); return; } (*zone)->flags |= DNS_ZONE_INACTIVE; dns_zone_dereference(zone, MDL); } #if defined (DNS_ZONE_LOOKUP) void cache_found_zone(dhcp_ddns_ns_t *ns_cb) { struct dns_zone *zone = NULL; int len, remove_zone = 0; /* See if there's already such a zone. */ if (dns_zone_lookup(&zone, ns_cb->zname) == ISC_R_SUCCESS) { /* If it's not a dynamic zone, leave it alone. */ if (zone->timeout == 0) return; /* Remove any old addresses in case they've changed */ if (zone->primary) option_cache_dereference(&zone->primary, MDL); if (zone->primary6) option_cache_dereference(&zone->primary6, MDL); /* Set the flag to remove the zone from the hash if we have problems */ remove_zone = 1; } else if (dns_zone_allocate(&zone, MDL) == 0) { return; } else { /* We've just allocated the zone, now we need * to allocate space for the name and addresses */ /* allocate space for the name */ len = strlen(ns_cb->zname); zone->name = dmalloc(len + 2, MDL); if (zone->name == NULL) { goto cleanup; } /* Copy the name and add a trailing '.' if necessary */ strcpy(zone->name, ns_cb->zname); if (zone->name[len-1] != '.') { zone->name[len] = '.'; zone->name[len+1] = 0; } } zone->timeout = cur_time + ns_cb->ttl; if (ns_cb->num_addrs != 0) { len = ns_cb->num_addrs * sizeof(struct in_addr); if ((!option_cache_allocate(&zone->primary, MDL)) || (!buffer_allocate(&zone->primary->data.buffer, len, MDL))) { if (remove_zone == 1) remove_dns_zone(zone); goto cleanup; } memcpy(zone->primary->data.buffer->data, ns_cb->addrs, len); zone->primary->data.data = &zone->primary->data.buffer->data[0]; zone->primary->data.len = len; } if (ns_cb->num_addrs6 != 0) { len = ns_cb->num_addrs6 * sizeof(struct in6_addr); if ((!option_cache_allocate(&zone->primary6, MDL)) || (!buffer_allocate(&zone->primary6->data.buffer, len, MDL))) { if (remove_zone == 1) remove_dns_zone(zone); goto cleanup; } memcpy(zone->primary6->data.buffer->data, ns_cb->addrs6, len); zone->primary6->data.data = &zone->primary6->data.buffer->data[0]; zone->primary6->data.len = len; } enter_dns_zone(zone); cleanup: dns_zone_dereference(&zone, MDL); return; } #endif /*! * \brief Create an id for a client * * This function is used to create an id for a client to use with DDNS * This version of the function is for the standard style, RFC 4701 * * This function takes information from the type and data fields and * mangles it into a dhcid string which it places in ddns_cb. It also * sets a field in ddns_cb to specify the class that should be used * when sending the dhcid, in this case it is a DHCID record so we use * dns_rdatatype_dhcid * * The DHCID we construct is: * 2 bytes - identifier type (see 4701 and IANA) * 1 byte - digest type, currently only SHA256 (1) * n bytes - digest, length depends on digest type, currently 32 for * SHA256 * * What we base the digest on is up to the calling code for an id type of * 0 - 1 octet htype followed by hlen octets of chaddr from v4 client request * 1 - data octets from a dhcpv4 client's client identifier option * 2 - the client DUID from a v4 or v6 client's client id option * This identifier is concatenated with the fqdn and the result is digested. */ int get_std_dhcid(dhcp_ddns_cb_t *ddns_cb, int type, const u_int8_t *identifier, unsigned id_len) { struct data_string *id = &ddns_cb->dhcid; isc_sha256_t sha256; unsigned char buf[ISC_SHA256_DIGESTLENGTH]; unsigned char fwd_buf[256]; unsigned fwd_buflen = 0; /* Types can only be 0..(2^16)-1. */ if (type < 0 || type > 65535) return (0); /* We need to convert the fwd name to wire representation */ if (MRns_name_pton((char *)ddns_cb->fwd_name.data, fwd_buf, 256) == -1) return (0); while(fwd_buf[fwd_buflen] != 0) { fwd_buflen += fwd_buf[fwd_buflen] + 1; } fwd_buflen++; if (!buffer_allocate(&id->buffer, ISC_SHA256_DIGESTLENGTH + 2 + 1, MDL)) return (0); id->data = id->buffer->data; /* The two first bytes contain the type identifier. */ putUShort(id->buffer->data, (unsigned)type); /* The next is the digest type, SHA-256 is 1 */ putUChar(id->buffer->data + 2, 1u); /* Computing the digest */ isc_sha256_init(&sha256); isc_sha256_update(&sha256, identifier, id_len); isc_sha256_update(&sha256, fwd_buf, fwd_buflen); isc_sha256_final(buf, &sha256); memcpy(id->buffer->data + 3, &buf, ISC_SHA256_DIGESTLENGTH); id->len = ISC_SHA256_DIGESTLENGTH + 2 + 1; return (1); } /*! * * \brief Create an id for a client * * This function is used to create an id for a client to use with DDNS * This version of the function is for the interim style. It is retained * to allow users to continue using the interim style but they should * switch to the standard style (which uses get_std_dhcid) for better * interoperability. * * This function takes information from the type and data fields and * mangles it into a dhcid string which it places in ddns_cb. It also * sets a field in ddns_cb to specify the class that should be used * when sending the dhcid, in this case it is a txt record so we use * dns_rdata_type_txt * * NOTE WELL: this function has issues with how it calculates the * dhcid, they can't be changed now as that would break the records * already in use. */ int get_int_dhcid (dhcp_ddns_cb_t *ddns_cb, int type, const u_int8_t *data, unsigned len) { struct data_string *id = &ddns_cb->dhcid; unsigned char buf[ISC_MD5_DIGESTLENGTH]; isc_md5_t md5; int i; /* Types can only be 0..(2^16)-1. */ if (type < 0 || type > 65535) return (0); /* * Hexadecimal MD5 digest plus two byte type, NUL, * and one byte for length for dns. */ if (!buffer_allocate(&id -> buffer, (ISC_MD5_DIGESTLENGTH * 2) + 4, MDL)) return (0); id->data = id->buffer->data; /* * We put the length into the first byte to turn * this into a dns text string. This avoid needing to * copy the string to add the byte later. */ id->buffer->data[0] = ISC_MD5_DIGESTLENGTH * 2 + 2; /* Put the type in the next two bytes. */ id->buffer->data[1] = "0123456789abcdef"[(type >> 4) & 0xf]; /* This should have been [type & 0xf] but now that * it is in use we need to leave it this way in order * to avoid disturbing customer's lease files */ id->buffer->data[2] = "0123456789abcdef"[type % 15]; /* Mash together an MD5 hash of the identifier. */ isc_md5_init(&md5); isc_md5_update(&md5, data, len); isc_md5_final(&md5, buf); /* Convert into ASCII. */ for (i = 0; i < ISC_MD5_DIGESTLENGTH; i++) { id->buffer->data[i * 2 + 3] = "0123456789abcdef"[(buf[i] >> 4) & 0xf]; id->buffer->data[i * 2 + 4] = "0123456789abcdef"[buf[i] & 0xf]; } id->len = ISC_MD5_DIGESTLENGTH * 2 + 3; id->buffer->data[id->len] = 0; id->terminated = 1; return (1); } int get_dhcid(dhcp_ddns_cb_t *ddns_cb, int type, const u_int8_t *identifier, unsigned id_len) { if (ddns_cb->dhcid_class == dns_rdatatype_dhcid) return get_std_dhcid(ddns_cb, type, identifier, id_len); else return get_int_dhcid(ddns_cb, type, identifier, id_len); } /* * The dhcid (text version) that we pass to DNS includes a length byte * at the start but the text we store in the lease doesn't include the * length byte. The following routines are to convert between the two * styles. * * When converting from a dhcid to a leaseid we reuse the buffer and * simply adjust the data pointer and length fields in the data string. * This avoids any prolems with allocating space. */ void dhcid_tolease(struct data_string *dhcid, struct data_string *leaseid) { /* copy the data string then update the fields */ data_string_copy(leaseid, dhcid, MDL); leaseid->data++; leaseid->len--; } isc_result_t dhcid_fromlease(struct data_string *dhcid, struct data_string *leaseid) { if (!buffer_allocate(&dhcid->buffer, leaseid->len + 2, MDL)) { return(ISC_R_FAILURE); } dhcid->data = dhcid->buffer->data; dhcid->buffer->data[0] = leaseid->len; memcpy(dhcid->buffer->data + 1, leaseid->data, leaseid->len); dhcid->len = leaseid->len + 1; if (leaseid->terminated == 1) { dhcid->buffer->data[dhcid->len] = 0; dhcid->terminated = 1; } return(ISC_R_SUCCESS); } /* * Construct the dataset for this item. * This is a fairly simple arrangement as the operations we do are simple. * If there is data we simply have the rdata point to it - the formatting * must be correct already. We then link the rdatalist to the rdata and * create a rdataset from the rdatalist. */ static isc_result_t make_dns_dataset(dns_rdataclass_t dataclass, dns_rdatatype_t datatype, dhcp_ddns_data_t *dataspace, unsigned char *data, int datalen, int ttl) { dns_rdata_t *rdata = &dataspace->rdata; dns_rdatalist_t *rdatalist = &dataspace->rdatalist; dns_rdataset_t *rdataset = &dataspace->rdataset; isc_region_t region; /* set up the rdata */ dns_rdata_init(rdata); if (data == NULL) { /* No data, set up the rdata fields we care about */ rdata->flags = DNS_RDATA_UPDATE; rdata->type = datatype; rdata->rdclass = dataclass; } else { switch(datatype) { case dns_rdatatype_a: case dns_rdatatype_aaaa: case dns_rdatatype_txt: case dns_rdatatype_dhcid: case dns_rdatatype_ptr: /* The data must be in the right format we simply * need to supply it via the correct structure */ region.base = data; region.length = datalen; dns_rdata_fromregion(rdata, dataclass, datatype, ®ion); break; default: return(DHCP_R_INVALIDARG); break; } } /* setup the datalist and attach the rdata to it */ dns_rdatalist_init(rdatalist); rdatalist->type = datatype; rdatalist->rdclass = dataclass; rdatalist->ttl = ttl; ISC_LIST_APPEND(rdatalist->rdata, rdata, link); /* convert the datalist to a dataset */ dns_rdataset_init(rdataset); dns_rdatalist_tordataset(rdatalist, rdataset); return(ISC_R_SUCCESS); } #if defined (DEBUG_DNS_UPDATES) static void log_call(char *text, dns_name_t* pname, dns_name_t* uname) { char buf1[512]; char buf2[512]; if (pname) { dns_name_format(pname, buf1, 512); } else { *buf1=0; } if (uname) { dns_name_format(uname, buf2, 512); } else { *buf2=0; } log_info ("DDNS: %s: pname:[%s] uname:[%s]", text, buf1, buf2); } #endif /* * When a DHCP client or server intends to update an A RR, it first * prepares a DNS UPDATE query which includes as a prerequisite the * assertion that the name does not exist. The update section of the * query attempts to add the new name and its IP address mapping (an A * RR), and the DHCID RR with its unique client-identity. * -- "Interaction between DHCP and DNS" * * There are two cases, one for the server and one for the client. * * For the server the first step will have a request of: * The name is not in use * Add an A RR * Add a DHCID RR * * For the client the first step will have a request of: * The A RR does not exist * Add an A RR * Add a DHCID RR */ static isc_result_t build_fwd_add1(dhcp_ddns_cb_t *ddns_cb, dhcp_ddns_data_t *dataspace, dns_name_t *pname, dns_name_t *uname) { isc_result_t result; #if defined (DEBUG_DNS_UPDATES) log_call("build_fwd_add1", pname, uname); #endif /* Construct the prerequisite list */ if ((ddns_cb->flags & DDNS_INCLUDE_RRSET) != 0) { /* The A RR shouldn't exist */ result = make_dns_dataset(dns_rdataclass_none, ddns_cb->address_type, dataspace, NULL, 0, 0); } else { /* The name is not in use */ result = make_dns_dataset(dns_rdataclass_none, dns_rdatatype_any, dataspace, NULL, 0, 0); } if (result != ISC_R_SUCCESS) { return(result); } ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link); dataspace++; /* Construct the update list */ /* Add the A RR */ result = make_dns_dataset(dns_rdataclass_in, ddns_cb->address_type, dataspace, (unsigned char *)ddns_cb->address.iabuf, ddns_cb->address.len, ddns_cb->ttl); if (result != ISC_R_SUCCESS) { return(result); } ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link); dataspace++; /* Add the DHCID RR */ result = make_dns_dataset(dns_rdataclass_in, ddns_cb->dhcid_class, dataspace, (unsigned char *)ddns_cb->dhcid.data, ddns_cb->dhcid.len, ddns_cb->ttl); if (result != ISC_R_SUCCESS) { return(result); } ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link); return(ISC_R_SUCCESS); } /* * If the first update operation fails with YXDOMAIN, the updater can * conclude that the intended name is in use. The updater then * attempts to confirm that the DNS name is not being used by some * other host. The updater prepares a second UPDATE query in which the * prerequisite is that the desired name has attached to it a DHCID RR * whose contents match the client identity. The update section of * this query deletes the existing A records on the name, and adds the * A record that matches the DHCP binding and the DHCID RR with the * client identity. * -- "Interaction between DHCP and DNS" * * The message for the second step depends on if we are doing conflict * resolution. If we are we include the prerequisite. The prerequiste * will either: * A. require the data value of the DHCID RR to match that of the client * or * B. required only that the DHCID RR of the configured class (DHCID or * TXT) exist * * based on whether DDNS_GUARD_ID_MUST_MATCH is on (default) or off. * * The prerequisite is omitted if conflict detection is off. * * If not we delete the DHCID in addition to all A rrsets. * * Conflict resolution: * DHCID RR exists, and matches client identity. * Delete A RRset. * Add A RR. * * Conflict override: * Delete DHCID RRs. * Add DHCID RR * Delete A RRset. * Add A RR. */ static isc_result_t build_fwd_add2(dhcp_ddns_cb_t *ddns_cb, dhcp_ddns_data_t *dataspace, dns_name_t *pname, dns_name_t *uname) { isc_result_t result = ISC_R_SUCCESS; #if defined (DEBUG_DNS_UPDATES) log_call("build_fwd_add2", pname, uname); #endif /* * If we are doing conflict detection we use a prereq list. * If not we delete the DHCID in addition to all A rrsets. */ if (ddns_cb->flags & DDNS_CONFLICT_DETECTION) { /* Construct the prereq list */ /* The DHCID RR exists and optionally matches the client's * identity. If matching is turned off, we use the presence * of a DHCID RR to signal that this is a dynamic entry and * thus eligible for us to overwrite. If matching is on * then we can only replace the entries if they belong to * this client. */ unsigned char *match_id = NULL; int match_id_len = 0; int match_class = dns_rdataclass_any; if (ddns_cb->flags & DDNS_GUARD_ID_MUST_MATCH) { match_id = (unsigned char*)(ddns_cb->dhcid.data); match_id_len = ddns_cb->dhcid.len; match_class = dns_rdataclass_in; } result = make_dns_dataset(match_class, ddns_cb->dhcid_class, dataspace, match_id, match_id_len, 0); if (result != ISC_R_SUCCESS) { return(result); } ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link); dataspace++; } else { /* Start constructing the update list. * Conflict detection override: delete DHCID RRs */ result = make_dns_dataset(dns_rdataclass_any, ddns_cb->dhcid_class, dataspace, NULL, 0, 0); if (result != ISC_R_SUCCESS) { return(result); } ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link); dataspace++; /* Add current DHCID RR, always include client id */ result = make_dns_dataset(dns_rdataclass_in, ddns_cb->dhcid_class, dataspace, (unsigned char *)ddns_cb->dhcid.data, ddns_cb->dhcid.len, ddns_cb->ttl); if (result != ISC_R_SUCCESS) { return(result); } ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link); dataspace++; } /* Start or continue constructing the update list */ /* Delete the address RRset */ result = make_dns_dataset(dns_rdataclass_any, ddns_cb->address_type, dataspace, NULL, 0, 0); if (result != ISC_R_SUCCESS) { return(result); } ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link); dataspace++; /* Add the address RR */ result = make_dns_dataset(dns_rdataclass_in, ddns_cb->address_type, dataspace, (unsigned char *)ddns_cb->address.iabuf, ddns_cb->address.len, ddns_cb->ttl); if (result != ISC_R_SUCCESS) { return(result); } ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link); return(ISC_R_SUCCESS); } /* * Creates the DNS foward update add used for DSMM add attempt #3 and * ddns-other-guard-is-dynamic is off * * * If the second update failed with NXRRSET, this indicates that: * * 1. our FQDN is in use * 2 no guard record (DHCID RR) for that FQDN, of our class (and optionally * client id) exists * * In Dual Stack Mixed Mode, we need to attempt a third add, to distinguish * between static entries that we cannot modify and dynamic entries belonging * to the "other" side of dual stack. The prerequisites for this add are: * * 1. No address record of my type exists * 2. No guard record of my type exists * 3. A guard record of the other type exists * * and updates which will add the new address and guard record: * * prereq nxrrset # no address record of my type * prereq nxrrset # no guard record of my type * prereq yxrrset # other guard type does exist * update add
# add the new address record * update add # add the new address record */ static isc_result_t build_dsmm_fwd_add3(dhcp_ddns_cb_t *ddns_cb, dhcp_ddns_data_t *dataspace, dns_name_t *pname, dns_name_t *uname) { isc_result_t result = ISC_R_SUCCESS; #if defined (DEBUG_DNS_UPDATES) log_call("build_fwd_add3", pname, uname); #endif /* Construct the prereq list */ /* No address record of my type exists */ result = make_dns_dataset(dns_rdataclass_none, ddns_cb->address_type, dataspace, NULL, 0, 0); if (result != ISC_R_SUCCESS) { return(result); } ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link); dataspace++; /* No guard record of my type exists */ result = make_dns_dataset(dns_rdataclass_none, ddns_cb->dhcid_class, dataspace, NULL, 0, 0); if (result != ISC_R_SUCCESS) { return(result); } ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link); dataspace++; /* Guard record of the other type DOES exist */ result = make_dns_dataset(dns_rdataclass_any, ddns_cb->other_dhcid_class, dataspace, NULL, 0, 0); if (result != ISC_R_SUCCESS) { return(result); } ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link); dataspace++; /* Start constructing the update list. */ /* Add the address RR */ result = make_dns_dataset(dns_rdataclass_in, ddns_cb->address_type, dataspace, (unsigned char *)ddns_cb->address.iabuf, ddns_cb->address.len, ddns_cb->ttl); if (result != ISC_R_SUCCESS) { return(result); } ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link); dataspace++; /* Add current DHCID RR */ result = make_dns_dataset(dns_rdataclass_in, ddns_cb->dhcid_class, dataspace, (unsigned char *)ddns_cb->dhcid.data, ddns_cb->dhcid.len, ddns_cb->ttl); if (result != ISC_R_SUCCESS) { return(result); } ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link); return(ISC_R_SUCCESS); } /* * Creates the DNS foward update add used for DSMM add attempt #3 and * ddns-other-guard-is-dynamic is ON * * If the second update failed with NXRRSET, this indicates that: * * 1. our FQDN is in use * 2 no guard record (DHCID RR) for that FQDN, of our class (and optionally * client id) exists * * When we're In Dual Stack Mixed Mode and ddns-other-guard-is-dynamic is ON * we need only determine if a guard record of the other type exists, to know * if we can add/replace and address record of our type. In other words, * the presence of a dynamic entry made belonging to the "other" stack means * all entries for this name should be dynamic and we overwrite an unguarded * address record of our type. * * The udpate will contain a single prequisite for a guard record of the * other type, an update to delete any address records of our type, and * updates to add the address and guard records: * * prereq yxrrset # other guard type exists * update delete # delete existing address record * # (if one) * update add
# add new address record * update add # add new guard record */ static isc_result_t build_dsmm_fwd_add3_other(dhcp_ddns_cb_t *ddns_cb, dhcp_ddns_data_t *dataspace, dns_name_t *pname, dns_name_t *uname) { isc_result_t result = ISC_R_SUCCESS; #if defined (DEBUG_DNS_UPDATES) log_call("build_fwd_add3_other", pname, uname); #endif /* Construct the prereq list */ /* A guard record of the other type exists */ result = make_dns_dataset(dns_rdataclass_any, ddns_cb->other_dhcid_class, dataspace, NULL, 0, 0); if (result != ISC_R_SUCCESS) { return(result); } ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link); dataspace++; /* Start constructing the update list. */ /* Delete the existing address record of my type (if one) */ result = make_dns_dataset(dns_rdataclass_any, ddns_cb->address_type, dataspace, NULL, 0, 0); if (result != ISC_R_SUCCESS) { return(result); } ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link); dataspace++; /* Add the address RR */ result = make_dns_dataset(dns_rdataclass_in, ddns_cb->address_type, dataspace, (unsigned char *)ddns_cb->address.iabuf, ddns_cb->address.len, ddns_cb->ttl); if (result != ISC_R_SUCCESS) { return(result); } ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link); dataspace++; /* Add current DHCID RR */ result = make_dns_dataset(dns_rdataclass_in, ddns_cb->dhcid_class, dataspace, (unsigned char *)ddns_cb->dhcid.data, ddns_cb->dhcid.len, ddns_cb->ttl); if (result != ISC_R_SUCCESS) { return(result); } ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link); return(ISC_R_SUCCESS); } /* * The entity chosen to handle the A record for this client (either the * client or the server) SHOULD delete the A (or AAAA) record that was * added when the lease was made to the client. * * If we are doing conflict resolution, the udpate will contain a prequisite * that will either: * A. require that a guard record of the configure class (DHCID or TXT) with * a data value matching that the client exist (per RFC 4703) * or * B. require only that the guard record of the configured class exist * * based on whether DDNS_GUARD_ID_MUST_MATCH is on (default) or off. * * The prerequisite is omitted if conflict detection is off. * */ static isc_result_t build_fwd_rem1(dhcp_ddns_cb_t *ddns_cb, dhcp_ddns_data_t *dataspace, dns_name_t *pname, dns_name_t *uname) { isc_result_t result = ISC_R_SUCCESS; #if defined (DEBUG_DNS_UPDATES) log_call("build_fwd_rem1", pname, uname); #endif /* If we're doing conflict detection, add the guard record pre-req */ if (ddns_cb->flags & DDNS_CONFLICT_DETECTION) { /* Construct the prereq list */ /* The guard record exists and optionally matches the client's * identity. If matching is turned off, we use the presence * of a DHCID RR to signal that this is a dynamic entry and * thus eligible for us to overwrite. If matching is on * then we can only delete the entries if they belong to * this client. */ unsigned char *match_id = NULL; int match_id_len = 0; int match_class = dns_rdataclass_any; if (ddns_cb->flags & DDNS_GUARD_ID_MUST_MATCH) { match_id = (unsigned char*)(ddns_cb->dhcid.data); match_id_len = ddns_cb->dhcid.len; match_class = dns_rdataclass_in; } result = make_dns_dataset(match_class, ddns_cb->dhcid_class, dataspace, match_id, match_id_len, 0); if (result != ISC_R_SUCCESS) { return(result); } ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link); dataspace++; } /* Construct the update list */ /* Delete A RRset */ result = make_dns_dataset(dns_rdataclass_none, ddns_cb->address_type, dataspace, (unsigned char *)ddns_cb->address.iabuf, ddns_cb->address.len, 0); if (result != ISC_R_SUCCESS) { return(result); } ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link); return(ISC_R_SUCCESS); } /* * If the deletion of the A succeeded, and there are no A or AAAA * records left for this domain, then we can blow away the DHCID * record as well. We can't blow away the DHCID record above * because it's possible that more than one record has been added * to this domain name. * * Second query has: * A RR does not exist. * AAAA RR does not exist. * Delete appropriate DHCID RR. */ static isc_result_t build_fwd_rem2(dhcp_ddns_cb_t *ddns_cb, dhcp_ddns_data_t *dataspace, dns_name_t *pname, dns_name_t *uname) { isc_result_t result; unsigned char *match_id = NULL; int match_id_len = 0; int match_class = dns_rdataclass_any; #if defined (DEBUG_DNS_UPDATES) log_call("build_fwd_rem2", pname, uname); #endif /* Construct the prereq list */ /* The A RR does not exist */ result = make_dns_dataset(dns_rdataclass_none, dns_rdatatype_a, dataspace, NULL, 0, 0); if (result != ISC_R_SUCCESS) { return(result); } ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link); dataspace++; /* The AAAA RR does not exist */ result = make_dns_dataset(dns_rdataclass_none, dns_rdatatype_aaaa, dataspace, NULL, 0, 0); if (result != ISC_R_SUCCESS) { return(result); } ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link); dataspace++; /* Construct the update list */ /* Delete DHCID RR */ /* We'll specify the client id in the guard record delete if * matching is enabled, otherwise we leave it off. */ if (ddns_cb->flags & DDNS_GUARD_ID_MUST_MATCH) { match_id = (unsigned char*)(ddns_cb->dhcid.data); match_id_len = ddns_cb->dhcid.len; match_class = dns_rdataclass_none; } result = make_dns_dataset(match_class, ddns_cb->dhcid_class, dataspace, match_id, match_id_len, 0); if (result != ISC_R_SUCCESS) { return(result); } ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link); return(ISC_R_SUCCESS); } /* * Constructs the second stage forward remove, when the first stage * succeeds and DSMM is enabled, and ddns-other-guard-is-dynamic is OFF * * Normal conflict detection requires that the guard record of the * configured type only be deleted if there are no address records of * any type. In Dual Stack Mixed Mode, we are only concerned with whether * there any records or our configured address type remaining. * * This update consists of a single prequisite that there be no address * records of our type followed by a delete of the guard record of our type * and optionally matching client-id. * * prereq nxrrset name # no records of this address type exist * update delete name # delete the existing guard record */ static isc_result_t build_fwd_rem2_dsmm (dhcp_ddns_cb_t *ddns_cb, dhcp_ddns_data_t *dataspace, dns_name_t *pname, dns_name_t *uname) { isc_result_t result; unsigned char *match_id = NULL; int match_id_len = 0; int match_class = dns_rdataclass_any; #if defined (DEBUG_DNS_UPDATES) log_call("build_fwd_rem2_dsmm", pname, uname); #endif /* Construct the prereq list */ /* The address RR does not exist */ result = make_dns_dataset(dns_rdataclass_none, ddns_cb->address_type, dataspace, NULL, 0, 0); if (result != ISC_R_SUCCESS) { return(result); } ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link); dataspace++; /* Construct the update list */ /* Delete DHCID RR */ /* We'll specify the client id in the guard record delete if * matching is enabled, otherwise we leave it off. */ if (ddns_cb->flags & DDNS_GUARD_ID_MUST_MATCH) { match_id = (unsigned char*)(ddns_cb->dhcid.data); match_id_len = ddns_cb->dhcid.len; match_class = dns_rdataclass_none; } result = make_dns_dataset(match_class, ddns_cb->dhcid_class, dataspace, match_id, match_id_len, 0); if (result != ISC_R_SUCCESS) { return(result); } ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link); return(ISC_R_SUCCESS); } /* * Constructs the second stage forward remove, when the first stage * succeeds and DSMM is enabled and ddns-other-guard-is-dynamic is ON * * This update addresses the case when an address record of our type exists * without a guard record of our type, yet a dynamic entry of the other type * exists. The presence of a guard of the other type indicates that all * entries for this name should be treated as dynamic, thus permitting us to * remove the address record of our type. * * prereq nxrrset # my guard type does not exist * prereq yxrrset # other guard type does exist * update delete address # delete the existing address record * */ static isc_result_t build_fwd_rem2_dsmm_other(dhcp_ddns_cb_t *ddns_cb, dhcp_ddns_data_t *dataspace, dns_name_t *pname, dns_name_t *uname) { isc_result_t result; #if defined (DEBUG_DNS_UPDATES) log_call("build_fwd_rem2_dsmm_other", pname, uname); #endif /* Construct the prereq list */ /* No guard record of my type exists */ result = make_dns_dataset(dns_rdataclass_none, ddns_cb->dhcid_class, dataspace, NULL, 0, 0); if (result != ISC_R_SUCCESS) { return(result); } ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link); dataspace++; /* Guard record of the OTHER type DOES exist */ result = make_dns_dataset(dns_rdataclass_any, ddns_cb->other_dhcid_class, dataspace, NULL, 0, 0); if (result != ISC_R_SUCCESS) { return(result); } ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link); dataspace++; /* Construct the update list */ /* Delete the address RRset */ result = make_dns_dataset(dns_rdataclass_none, ddns_cb->address_type, dataspace, (unsigned char *)ddns_cb->address.iabuf, ddns_cb->address.len, 0); if (result != ISC_R_SUCCESS) { return(result); } ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link); return(ISC_R_SUCCESS); } /* * This routine converts from the task action call into something * easier to work with. It also handles the common case of a signature * or zone not being correct. */ void ddns_interlude(isc_task_t *taskp, isc_event_t *eventp) { dhcp_ddns_cb_t *ddns_cb = (dhcp_ddns_cb_t *)eventp->ev_arg; dns_clientupdateevent_t *ddns_event = (dns_clientupdateevent_t *)eventp; isc_result_t eresult = ddns_event->result; isc_result_t result; /* We've extracted the information we want from it, get rid of * the event block.*/ isc_event_free(&eventp); #if defined (TRACING) if (trace_record()) { trace_ddns_input_write(ddns_cb, eresult); } #endif #if defined (DEBUG_DNS_UPDATES) print_dns_status(DDNS_PRINT_INBOUND, ddns_cb, eresult); #endif /* This transaction is complete, clear the value */ dns_client_destroyupdatetrans(&ddns_cb->transaction); /* If we cancelled or tried to cancel the operation we just * need to clean up. */ if ((eresult == ISC_R_CANCELED) || ((ddns_cb->flags & DDNS_ABORT) != 0)) { #if defined (DEBUG_DNS_UPDATES) log_info("DDNS: completeing transaction cancellation cb=%p, " "flags=%x, %s", ddns_cb, ddns_cb->flags, isc_result_totext(eresult)); #endif if ((ddns_cb->flags & DDNS_ABORT) == 0) { log_info("DDNS: cleaning up lease pointer for a cancel " "cb=%p", ddns_cb); /* * We shouldn't actually be able to get here but * we are. This means we haven't cleaned up * the lease pointer so we need to do that before * freeing the cb. */ ddns_cb->cur_func(ddns_cb, eresult); return; } if (ddns_cb->next_op != NULL) { /* if necessary cleanup up next op block */ ddns_cb_free(ddns_cb->next_op, MDL); } ddns_cb_free(ddns_cb, MDL); return; } /* If we had a problem with our key or zone try again */ if ((eresult == DNS_R_NOTAUTH) || (eresult == DNS_R_NOTZONE)) { int i; /* Our zone information was questionable, * repudiate it and try again */ log_error("DDNS: bad zone information, repudiating zone %s", ddns_cb->zone_name); repudiate_zone(&ddns_cb->zone); ddns_cb->zone_name[0] = 0; ISC_LIST_INIT(ddns_cb->zone_server_list); for (i = 0; i < DHCP_MAXNS; i++) { ISC_LINK_INIT(&ddns_cb->zone_addrs[i], link); } if ((ddns_cb->state == DDNS_STATE_ADD_PTR) || (ddns_cb->state == DDNS_STATE_REM_PTR)) { result = ddns_modify_ptr(ddns_cb, MDL); } else { result = ddns_modify_fwd(ddns_cb, MDL); } if (result != ISC_R_SUCCESS) { /* if we couldn't redo the query log it and * let the next function clean it up */ log_info("DDNS: Failed to retry after zone failure"); ddns_cb->cur_func(ddns_cb, result); } return; } else { /* pass it along to be processed */ ddns_cb->cur_func(ddns_cb, eresult); } return; } /* * This routine does the generic work for sending a ddns message to * modify the forward record (A or AAAA) and calls one of a set of * routines to build the specific message. */ isc_result_t ddns_modify_fwd(dhcp_ddns_cb_t *ddns_cb, const char *file, int line) { isc_result_t result; dns_tsec_t *tsec_key = NULL; #if defined (DEBUG_DNS_UPDATES) log_info("DDNS: ddns_modify_fwd"); #endif unsigned char *clientname; dhcp_ddns_data_t *dataspace = NULL; dns_namelist_t prereqlist, updatelist; dns_fixedname_t zname0, pname0, uname0; dns_name_t *zname = NULL, *pname, *uname; isc_sockaddrlist_t *zlist = NULL; /* Creates client context if we need to */ result = dns_client_init(); if (result != ISC_R_SUCCESS) { return result; } /* Get a pointer to the clientname to make things easier. */ clientname = (unsigned char *)ddns_cb->fwd_name.data; /* Extract and validate the type of the address. */ if (ddns_cb->address.len == 4) { ddns_cb->address_type = dns_rdatatype_a; } else if (ddns_cb->address.len == 16) { ddns_cb->address_type = dns_rdatatype_aaaa; } else { return DHCP_R_INVALIDARG; } /* * If we already have a zone use it, otherwise try to lookup the * zone in our cache. If we find one we will have a pointer to * the zone that needs to be dereferenced when we are done with it. * If we don't find one that is okay we'll let the DNS code try and * find the information for us. */ if (ddns_cb->zone == NULL) { result = find_cached_zone(ddns_cb, FIND_FORWARD); #if defined (DNS_ZONE_LOOKUP) if (result == ISC_R_NOTFOUND) { /* * We didn't find a cached zone, see if we can * can find a nameserver and create a zone. */ if (find_zone_start(ddns_cb, FIND_FORWARD) == ISC_R_SUCCESS) { /* * We have started the process to find a zone * queue the ddns_cb for processing after we * create the zone */ /* sar - not yet implemented, currently we just * arrange for things to get cleaned up */ goto cleanup; } } #endif if (result != ISC_R_SUCCESS) goto cleanup; } /* * If we have a zone try to get any information we need * from it - name, addresses and the key. The address * and key may be empty the name can't be. */ if (ddns_cb->zone) { /* Set up the zone name for use by DNS */ result = dhcp_isc_name(ddns_cb->zone_name, &zname0, &zname); if (result != ISC_R_SUCCESS) { log_error("Unable to build name for zone for " "fwd update: %s %s", ddns_cb->zone_name, isc_result_totext(result)); goto cleanup; } if (!(ISC_LIST_EMPTY(ddns_cb->zone_server_list))) { /* If we have any addresses get them */ zlist = &ddns_cb->zone_server_list; } if (ddns_cb->zone->key != NULL) { /* * Not having a key is fine, having a key * but not a tsec is odd so we warn the user. */ /*sar*/ /* should we do the warning? */ tsec_key = ddns_cb->zone->key->tsec_key; if (tsec_key == NULL) { log_error("No tsec for use with key %s", ddns_cb->zone->key->name); } } } /* Set up the DNS names for the prereq and update lists */ if (((result = dhcp_isc_name(clientname, &pname0, &pname)) != ISC_R_SUCCESS) || ((result = dhcp_isc_name(clientname, &uname0, &uname)) != ISC_R_SUCCESS)) { log_error("Unable to build name for fwd update: %s %s", clientname, isc_result_totext(result)); goto cleanup; } /* Allocate the various isc dns library structures we may require. */ dataspace = isc_mem_get(dhcp_gbl_ctx.mctx, sizeof(*dataspace) * 4); if (dataspace == NULL) { log_error("Unable to allocate memory for fwd update"); result = ISC_R_NOMEMORY; goto cleanup; } ISC_LIST_INIT(prereqlist); ISC_LIST_INIT(updatelist); switch(ddns_cb->state) { case DDNS_STATE_ADD_FW_NXDOMAIN: result = build_fwd_add1(ddns_cb, dataspace, pname, uname); if (result != ISC_R_SUCCESS) { goto cleanup; } ISC_LIST_APPEND(prereqlist, pname, link); break; case DDNS_STATE_ADD_FW_YXDHCID: result = build_fwd_add2(ddns_cb, dataspace, pname, uname); if (result != ISC_R_SUCCESS) { goto cleanup; } /* If we are doing conflict detection we have entries * in the pname list and we need to attach it to the * prereqlist */ if (ddns_cb->flags & DDNS_CONFLICT_DETECTION) { ISC_LIST_APPEND(prereqlist, pname, link); } break; case DDNS_STATE_DSMM_FW_ADD3: { /* We should only be here if we're doing DSMM */ builder_func_t builder; if (ddns_cb->flags & DDNS_OTHER_GUARD_IS_DYNAMIC) { builder = build_dsmm_fwd_add3_other; } else { builder = build_dsmm_fwd_add3; } result = (*builder)(ddns_cb, dataspace, pname, uname); if (result != ISC_R_SUCCESS) { goto cleanup; } ISC_LIST_APPEND(prereqlist, pname, link); break; } case DDNS_STATE_REM_FW_YXDHCID: result = build_fwd_rem1(ddns_cb, dataspace, pname, uname); if (result != ISC_R_SUCCESS) { goto cleanup; } ISC_LIST_APPEND(prereqlist, pname, link); break; case DDNS_STATE_REM_FW_NXRR: { builder_func_t builder; if (ddns_cb->flags & DDNS_DUAL_STACK_MIXED_MODE) { builder = build_fwd_rem2_dsmm; } else { builder = build_fwd_rem2; } result = (*builder)(ddns_cb, dataspace, pname, uname); if (result != ISC_R_SUCCESS) { goto cleanup; } ISC_LIST_APPEND(prereqlist, pname, link); break; } case DDNS_STATE_REM_FW_DSMM_OTHER: { result = build_fwd_rem2_dsmm_other(ddns_cb, dataspace, pname, uname); if (result != ISC_R_SUCCESS) { goto cleanup; } ISC_LIST_APPEND(prereqlist, pname, link); break; } default: log_error("ddns_modify_fwd: Invalid state: %d", ddns_cb->state); result = DHCP_R_INVALIDARG; goto cleanup; break; } /* * We always have an update list but may not have a prereqlist * if we are doing conflict override. */ ISC_LIST_APPEND(updatelist, uname, link); /* send the message, cleanup and return the result */ result = ddns_update(dhcp_gbl_ctx.dnsclient, dns_rdataclass_in, zname, &prereqlist, &updatelist, zlist, tsec_key, DNS_CLIENTRESOPT_ALLOWRUN, dhcp_gbl_ctx.task, ddns_interlude, (void *)ddns_cb, &ddns_cb->transaction); if (result == ISC_R_FAMILYNOSUPPORT) { log_info("Unable to perform DDNS update, " "address family not supported"); } #if defined (DEBUG_DNS_UPDATES) print_dns_status(DDNS_PRINT_OUTBOUND, ddns_cb, result); #endif cleanup: #if defined (DEBUG_DNS_UPDATES) if (result != ISC_R_SUCCESS) { log_info("DDNS: %s(%d): error in ddns_modify_fwd %s for %p", file, line, isc_result_totext(result), ddns_cb); } #endif if (dataspace != NULL) { isc_mem_put(dhcp_gbl_ctx.mctx, dataspace, sizeof(*dataspace) * 4); } return(result); } isc_result_t ddns_modify_ptr(dhcp_ddns_cb_t *ddns_cb, const char *file, int line) { isc_result_t result; dns_tsec_t *tsec_key = NULL; unsigned char *ptrname; dhcp_ddns_data_t *dataspace = NULL; dns_namelist_t updatelist; dns_fixedname_t zname0, uname0; dns_name_t *zname = NULL, *uname; isc_sockaddrlist_t *zlist = NULL; unsigned char buf[256]; int buflen; #if defined (DEBUG_DNS_UPDATES) log_info("DDNS: ddns_modify_ptr"); #endif /* Creates client context if we need to */ result = dns_client_init(); if (result != ISC_R_SUCCESS) { return result; } /* * Try to lookup the zone in the zone cache. As with the forward * case it's okay if we don't have one, the DNS code will try to * find something also if we succeed we will need to dereference * the zone later. Unlike with the forward case we assume we won't * have a pre-existing zone. */ result = find_cached_zone(ddns_cb, FIND_REVERSE); #if defined (DNS_ZONE_LOOKUP) if (result == ISC_R_NOTFOUND) { /* * We didn't find a cached zone, see if we can * can find a nameserver and create a zone. */ if (find_zone_start(ddns_cb, FIND_REVERSE) == ISC_R_SUCCESS) { /* * We have started the process to find a zone * queue the ddns_cb for processing after we * create the zone */ /* sar - not yet implemented, currently we just * arrange for things to get cleaned up */ goto cleanup; } } #endif if (result != ISC_R_SUCCESS) goto cleanup; if ((result == ISC_R_SUCCESS) && !(ISC_LIST_EMPTY(ddns_cb->zone_server_list))) { /* Set up the zone name for use by DNS */ result = dhcp_isc_name(ddns_cb->zone_name, &zname0, &zname); if (result != ISC_R_SUCCESS) { log_error("Unable to build name for zone for " "fwd update: %s %s", ddns_cb->zone_name, isc_result_totext(result)); goto cleanup; } /* If we have any addresses get them */ if (!(ISC_LIST_EMPTY(ddns_cb->zone_server_list))) { zlist = &ddns_cb->zone_server_list; } /* * If we now have a zone try to get the key, NULL is okay, * having a key but not a tsec is odd so we warn. */ /*sar*/ /* should we do the warning if we have a key but no tsec? */ if ((ddns_cb->zone != NULL) && (ddns_cb->zone->key != NULL)) { tsec_key = ddns_cb->zone->key->tsec_key; if (tsec_key == NULL) { log_error("No tsec for use with key %s", ddns_cb->zone->key->name); } } } /* We must have a name for the update list */ /* Get a pointer to the ptrname to make things easier. */ ptrname = (unsigned char *)ddns_cb->rev_name.data; if ((result = dhcp_isc_name(ptrname, &uname0, &uname)) != ISC_R_SUCCESS) { log_error("Unable to build name for fwd update: %s %s", ptrname, isc_result_totext(result)); goto cleanup; } /* * Allocate the various isc dns library structures we may require. * Allocating one blob avoids being halfway through the process * and being unable to allocate as well as making the free easy. */ dataspace = isc_mem_get(dhcp_gbl_ctx.mctx, sizeof(*dataspace) * 2); if (dataspace == NULL) { log_error("Unable to allocate memory for fwd update"); result = ISC_R_NOMEMORY; goto cleanup; } ISC_LIST_INIT(updatelist); /* * Construct the update list * We always delete what's currently there * Delete PTR RR. */ result = make_dns_dataset(dns_rdataclass_any, dns_rdatatype_ptr, &dataspace[0], NULL, 0, 0); if (result != ISC_R_SUCCESS) { goto cleanup; } ISC_LIST_APPEND(uname->list, &dataspace[0].rdataset, link); /* * If we are updating the pointer we then add the new one * Add PTR RR. */ if (ddns_cb->state == DDNS_STATE_ADD_PTR) { /* * Need to convert pointer into on the wire representation */ if (MRns_name_pton((char *)ddns_cb->fwd_name.data, buf, 256) == -1) { goto cleanup; } buflen = 0; while (buf[buflen] != 0) { buflen += buf[buflen] + 1; } buflen++; result = make_dns_dataset(dns_rdataclass_in, dns_rdatatype_ptr, &dataspace[1], buf, buflen, ddns_cb->ttl); if (result != ISC_R_SUCCESS) { goto cleanup; } ISC_LIST_APPEND(uname->list, &dataspace[1].rdataset, link); } ISC_LIST_APPEND(updatelist, uname, link); /*sar*/ /* * for now I'll cleanup the dataset immediately, it would be * more efficient to keep it around in case the signaturure failed * and we wanted to retry it. */ /* send the message, cleanup and return the result */ result = ddns_update((dns_client_t *)dhcp_gbl_ctx.dnsclient, dns_rdataclass_in, zname, NULL, &updatelist, zlist, tsec_key, DNS_CLIENTRESOPT_ALLOWRUN, dhcp_gbl_ctx.task, ddns_interlude, (void *)ddns_cb, &ddns_cb->transaction); if (result == ISC_R_FAMILYNOSUPPORT) { log_info("Unable to perform DDNS update, " "address family not supported"); } #if defined (DEBUG_DNS_UPDATES) print_dns_status(DDNS_PRINT_OUTBOUND, ddns_cb, result); #endif cleanup: #if defined (DEBUG_DNS_UPDATES) if (result != ISC_R_SUCCESS) { log_info("DDNS: %s(%d): error in ddns_modify_ptr %s for %p", file, line, isc_result_totext(result), ddns_cb); } #endif if (dataspace != NULL) { isc_mem_put(dhcp_gbl_ctx.mctx, dataspace, sizeof(*dataspace) * 2); } return(result); } void ddns_cancel(dhcp_ddns_cb_t *ddns_cb, const char *file, int line) { ddns_cb->flags |= DDNS_ABORT; if (ddns_cb->transaction != NULL) { dns_client_cancelupdate((dns_clientupdatetrans_t *) ddns_cb->transaction); } ddns_cb->lease = NULL; #if defined (DEBUG_DNS_UPDATES) log_info("DDNS: %s(%d): cancelling transaction for %p", file, line, ddns_cb); #endif } #endif /* NSUPDATE */ HASH_FUNCTIONS (dns_zone, const char *, struct dns_zone, dns_zone_hash_t, dns_zone_reference, dns_zone_dereference, do_case_hash) #if defined (NSUPDATE) #if defined (DEBUG_DNS_UPDATES) /* Defines a type for creating list of labeled integers */ typedef struct { int val; char *name; } LabeledInt; char* ddns_state_name(int state) { static LabeledInt ints[] = { { DDNS_STATE_CLEANUP, "DDNS_STATE_CLEANUP" }, { DDNS_STATE_ADD_FW_NXDOMAIN, "DDNS_STATE_ADD_FW_NXDOMAIN" }, { DDNS_STATE_ADD_FW_YXDHCID, "DDNS_STATE_ADD_FW_YXDHCID" }, { DDNS_STATE_ADD_PTR, "DDNS_STATE_ADD_PTR" }, { DDNS_STATE_DSMM_FW_ADD3, "DDNS_STATE_DSMM_FW_ADD3" }, { DDNS_STATE_REM_FW_YXDHCID, "DDNS_STATE_REM_FW_YXDHCID" }, { DDNS_STATE_REM_FW_NXRR, "DDNS_STATE_FW_NXRR" }, { DDNS_STATE_REM_PTR, "DDNS_STATE_REM_PTR" }, { -1, "unknown" }, }; LabeledInt* li = ints; while (li->val != -1 && li->val != state) { ++li; } return (li->name); } int add_nstring(char **orig, char *max, char *add, int add_len) { if (*orig && (*orig + add_len < max)) { strncpy(*orig, add, add_len); *orig += add_len; **orig = 0; return (0); } return (-1); } int add_string(char **orig, char *max, char *add) { return (add_nstring(orig, max, add, strlen(add))); } /* * direction outbound (messages to the dns server) * inbound (messages from the dns server) * ddns_cb is the control block associated with the message * result is the result from the dns code. For outbound calls * it is from the call to pass the message to the dns library. * For inbound calls it is from the event returned by the library. * * For outbound messages we print whatever we think is interesting * from the control block. * For inbound messages we only print the transaction id pointer * and the result and expect that the user will match them up as * necessary. Note well: the transaction information is opaque to * us so we simply print the pointer to it. This should be sufficient * to match requests and replys in a short sequence but is awkward * when trying to use it for longer sequences. */ void print_dns_status (int direction, struct dhcp_ddns_cb *ddns_cb, isc_result_t result) { char obuf[1024]; char *s = obuf, *end = &obuf[sizeof(obuf)-2]; char *en; const char *result_str; char ddns_address[ sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; if (direction == DDNS_PRINT_INBOUND) { log_info("DDNS reply: id ptr %p, result: %s", ddns_cb->transaction, isc_result_totext(result)); return; } /* * To avoid having to figure out if any of the strings * aren't NULL terminated, just 0 the whole string */ memset(obuf, 0, 1024); en = "DDNS request: id ptr "; if (s + strlen(en) + 16 < end) { sprintf(s, "%s%p", en, ddns_cb->transaction); s += strlen(s); } else { goto bailout; } en = ddns_state_name(ddns_cb->state); switch (ddns_cb->state) { case DDNS_STATE_ADD_FW_NXDOMAIN: case DDNS_STATE_ADD_FW_YXDHCID: case DDNS_STATE_REM_FW_YXDHCID: case DDNS_STATE_REM_FW_NXRR: case DDNS_STATE_DSMM_FW_ADD3: strcpy(ddns_address, piaddr(ddns_cb->address)); if (s + strlen(en) + strlen(ddns_address) + ddns_cb->fwd_name.len + 7 < end) { sprintf(s, " %s %s for %.*s", en, ddns_address, ddns_cb->fwd_name.len, ddns_cb->fwd_name.data); s += strlen(s); } else { goto bailout; } break; case DDNS_STATE_ADD_PTR: case DDNS_STATE_REM_PTR: if (s + strlen(en) + ddns_cb->fwd_name.len + ddns_cb->rev_name.len + 7 < end) { sprintf(s, " %s %.*s for %.*s", en, ddns_cb->fwd_name.len, ddns_cb->fwd_name.data, ddns_cb->rev_name.len, ddns_cb->rev_name.data); s += strlen(s); } else { goto bailout; } break; case DDNS_STATE_CLEANUP: default: if (s + strlen(en) < end) { sprintf(s, "%s", en); s += strlen(s); } else { goto bailout; } break; } en = " zone: "; if (s + strlen(en) + strlen((char *)ddns_cb->zone_name) < end) { sprintf(s, "%s%s", en, ddns_cb->zone_name); s += strlen(s); } else { goto bailout; } /* @todo replace with format that matches bind9 zone file */ if (ddns_cb->dhcid_class == dns_rdatatype_dhcid) { char *idbuf = NULL; if (add_string(&s, end, "dhcid: [")) { goto bailout; } idbuf = buf_to_hex(ddns_cb->dhcid.data, ddns_cb->dhcid.len, MDL); if (idbuf) { int ret = add_string(&s, end, idbuf); dfree(idbuf, MDL); if (!ret) { goto bailout; } } if (add_string(&s, end, "]")) { goto bailout; } } else { /* 1st byte of a txt dhcid is length, so we skip printing it * In the event it's empty, we end up not adding anything */ int skip_length_byte = (ddns_cb->dhcid.len > 0 ? 1 : 0); if (add_string (&s, end, "txt: [") || add_nstring (&s, end, (char *)ddns_cb->dhcid.data + skip_length_byte, ddns_cb->dhcid.len - skip_length_byte) || add_string (&s, end, "]")) { goto bailout; } } en = " ttl: "; if (s + strlen(en) + 10 < end) { sprintf(s, "%s%ld", en, ddns_cb->ttl); s += strlen(s); } else { goto bailout; } en = " result: "; result_str = isc_result_totext(result); if (s + strlen(en) + strlen(result_str) < end) { sprintf(s, "%s%s", en, result_str); s += strlen(s); } else { goto bailout; } bailout: /* * We either finished building the string or ran out * of space, print whatever we have in case it is useful */ log_info("%s", obuf); return; } #endif /* DEBUG_DNS_UPDATES */ #endif /* NSUPDATE */ dhcp-4.4.1/common/ethernet.c000644 000765 000024 00000005130 13243301226 016164 0ustar00tmarkstaff000000 000000 /* ethernet.c Packet assembly code, originally contributed by Archie Cobbs. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1996-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #if defined (PACKET_ASSEMBLY) || defined (PACKET_DECODING) #include "includes/netinet/if_ether.h" #endif /* PACKET_ASSEMBLY || PACKET_DECODING */ #if defined (PACKET_ASSEMBLY) /* Assemble an hardware header... */ void assemble_ethernet_header (interface, buf, bufix, to) struct interface_info *interface; unsigned char *buf; unsigned *bufix; struct hardware *to; { struct isc_ether_header eh; if (to && to -> hlen == 7) /* XXX */ memcpy (eh.ether_dhost, &to -> hbuf [1], sizeof eh.ether_dhost); else memset (eh.ether_dhost, 0xff, sizeof (eh.ether_dhost)); if (interface -> hw_address.hlen - 1 == sizeof (eh.ether_shost)) memcpy (eh.ether_shost, &interface -> hw_address.hbuf [1], sizeof (eh.ether_shost)); else memset (eh.ether_shost, 0x00, sizeof (eh.ether_shost)); eh.ether_type = htons (ETHERTYPE_IP); memcpy (&buf [*bufix], &eh, ETHER_HEADER_SIZE); *bufix += ETHER_HEADER_SIZE; } #endif /* PACKET_ASSEMBLY */ #ifdef PACKET_DECODING /* Decode a hardware header... */ ssize_t decode_ethernet_header (interface, buf, bufix, from) struct interface_info *interface; unsigned char *buf; unsigned bufix; struct hardware *from; { struct isc_ether_header eh; memcpy (&eh, buf + bufix, ETHER_HEADER_SIZE); #ifdef USERLAND_FILTER if (ntohs (eh.ether_type) != ETHERTYPE_IP) return -1; #endif memcpy (&from -> hbuf [1], eh.ether_shost, sizeof (eh.ether_shost)); from -> hbuf [0] = ARPHRD_ETHER; from -> hlen = (sizeof eh.ether_shost) + 1; return ETHER_HEADER_SIZE; } #endif /* PACKET_DECODING */ dhcp-4.4.1/common/execute.c000644 000765 000024 00000101110 13243301226 016003 0ustar00tmarkstaff000000 000000 /* execute.c Support for executable statements. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1998-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #include #include #include #include int execute_statements (result, packet, lease, client_state, in_options, out_options, scope, statements, on_star) struct binding_value **result; struct packet *packet; struct lease *lease; struct client_state *client_state; struct option_state *in_options; struct option_state *out_options; struct binding_scope **scope; struct executable_statement *statements; struct on_star *on_star; { struct executable_statement *r, *e, *next; int rc; int status; struct binding *binding; struct data_string ds; struct binding_scope *ns; if (!statements) return 1; r = NULL; next = NULL; e = NULL; executable_statement_reference (&r, statements, MDL); while (r && !(result && *result)) { if (r->next) executable_statement_reference (&next, r->next, MDL); switch (r->op) { case statements_statement: #if defined (DEBUG_EXPRESSIONS) log_debug ("exec: statements"); #endif status = execute_statements (result, packet, lease, client_state, in_options, out_options, scope, r->data.statements, on_star); #if defined (DEBUG_EXPRESSIONS) log_debug ("exec: statements returns %d", status); #endif if (!status) return 0; break; case on_statement: /* * if we haven't been passed an on_star block but * do have a lease, use the one from the lease * This handles the previous v4 calls. */ if ((on_star == NULL) && (lease != NULL)) on_star = &lease->on_star; if (on_star != NULL) { if (r->data.on.evtypes & ON_EXPIRY) { #if defined (DEBUG_EXPRESSIONS) log_debug ("exec: on expiry"); #endif if (on_star->on_expiry) executable_statement_dereference (&on_star->on_expiry, MDL); if (r->data.on.statements) executable_statement_reference (&on_star->on_expiry, r->data.on.statements, MDL); } if (r->data.on.evtypes & ON_RELEASE) { #if defined (DEBUG_EXPRESSIONS) log_debug ("exec: on release"); #endif if (on_star->on_release) executable_statement_dereference (&on_star->on_release, MDL); if (r->data.on.statements) executable_statement_reference (&on_star->on_release, r->data.on.statements, MDL); } if (r->data.on.evtypes & ON_COMMIT) { #if defined (DEBUG_EXPRESSIONS) log_debug ("exec: on commit"); #endif if (on_star->on_commit) executable_statement_dereference (&on_star->on_commit, MDL); if (r->data.on.statements) executable_statement_reference (&on_star->on_commit, r->data.on.statements, MDL); } } break; case switch_statement: #if defined (DEBUG_EXPRESSIONS) log_debug ("exec: switch"); #endif status = (find_matching_case (&e, packet, lease, client_state, in_options, out_options, scope, r->data.s_switch.expr, r->data.s_switch.statements)); #if defined (DEBUG_EXPRESSIONS) log_debug ("exec: switch: case %lx", (unsigned long)e); #endif if (status) { if (!(execute_statements (result, packet, lease, client_state, in_options, out_options, scope, e, on_star))) { executable_statement_dereference (&e, MDL); return 0; } executable_statement_dereference (&e, MDL); } break; /* These have no effect when executed. */ case case_statement: case default_statement: break; case if_statement: status = (evaluate_boolean_expression (&rc, packet, lease, client_state, in_options, out_options, scope, r->data.ie.expr)); #if defined (DEBUG_EXPRESSIONS) log_debug ("exec: if %s", (status ? (rc ? "true" : "false") : "NULL")); #endif /* XXX Treat NULL as false */ if (!status) rc = 0; if (!execute_statements (result, packet, lease, client_state, in_options, out_options, scope, rc ? r->data.ie.tc : r->data.ie.fc, on_star)) return 0; break; case eval_statement: status = evaluate_expression (NULL, packet, lease, client_state, in_options, out_options, scope, r->data.eval, MDL); #if defined (DEBUG_EXPRESSIONS) log_debug ("exec: evaluate: %s", (status ? "succeeded" : "failed")); #else POST(status); #endif break; case execute_statement: { #ifdef ENABLE_EXECUTE struct expression *expr; char **argv; int i, argc = r->data.execute.argc; pid_t p; /* save room for the command and the NULL terminator */ argv = dmalloc((argc + 2) * sizeof(*argv), MDL); if (!argv) break; argv[0] = dmalloc(strlen(r->data.execute.command) + 1, MDL); if (argv[0]) { strcpy(argv[0], r->data.execute.command); } else { goto execute_out; } log_debug("execute_statement argv[0] = %s", argv[0]); for (i = 1, expr = r->data.execute.arglist; expr; expr = expr->data.arg.next, i++) { memset (&ds, 0, sizeof(ds)); status = (evaluate_data_expression (&ds, packet, lease, client_state, in_options, out_options, scope, expr->data.arg.val, MDL)); if (status) { argv[i] = dmalloc(ds.len + 1, MDL); if (argv[i]) { memcpy(argv[i], ds.data, ds.len); argv[i][ds.len] = 0; log_debug("execute_statement argv[%d] = %s", i, argv[i]); } data_string_forget (&ds, MDL); if (!argv[i]) { log_debug("execute_statement failed argv[%d]", i); goto execute_out; } } else { log_debug("execute: bad arg %d", i); goto execute_out; } } argv[i] = NULL; if ((p = fork()) > 0) { int status; waitpid(p, &status, 0); if (status) { log_error("execute: %s exit status %d", argv[0], status); } } else if (p == 0) { execvp(argv[0], argv); log_error("Unable to execute %s: %m", argv[0]); _exit(127); } else { log_error("execute: fork() failed"); } execute_out: for (i = 0; i <= argc; i++) { if(argv[i]) dfree(argv[i], MDL); } dfree(argv, MDL); #else /* !ENABLE_EXECUTE */ log_fatal("Impossible case at %s:%d (ENABLE_EXECUTE " "is not defined).", MDL); #endif /* ENABLE_EXECUTE */ break; } case return_statement: status = evaluate_expression (result, packet, lease, client_state, in_options, out_options, scope, r -> data.retval, MDL); #if defined (DEBUG_EXPRESSIONS) log_debug ("exec: return: %s", (status ? "succeeded" : "failed")); #else POST(status); #endif break; case add_statement: #if defined (DEBUG_EXPRESSIONS) log_debug ("exec: add %s", (r->data.add->name ? r->data.add->name : "")); #endif classify (packet, r->data.add); break; case break_statement: #if defined (DEBUG_EXPRESSIONS) log_debug ("exec: break"); #endif return 1; case supersede_option_statement: case send_option_statement: #if defined (DEBUG_EXPRESSIONS) log_debug ("exec: %s option %s.%s", (r->op == supersede_option_statement ? "supersede" : "send"), r->data.option->option->universe->name, r->data.option->option->name); goto option_statement; #endif case default_option_statement: #if defined (DEBUG_EXPRESSIONS) log_debug ("exec: default option %s.%s", r->data.option->option->universe->name, r->data.option->option->name); goto option_statement; #endif case append_option_statement: #if defined (DEBUG_EXPRESSIONS) log_debug ("exec: append option %s.%s", r->data.option->option->universe->name, r->data.option->option->name); goto option_statement; #endif case prepend_option_statement: #if defined (DEBUG_EXPRESSIONS) log_debug ("exec: prepend option %s.%s", r->data.option->option->universe->name, r->data.option->option->name); option_statement: #endif set_option (r->data.option->option->universe, out_options, r->data.option, r->op); break; case set_statement: case define_statement: status = 1; if (!scope) { log_error("set %s: no scope", r->data.set.name); break; } if (!*scope) { if (!binding_scope_allocate(scope, MDL)) { log_error("set %s: can't allocate scope", r->data.set.name); break; } } binding = find_binding(*scope, r->data.set.name); #if defined (DEBUG_EXPRESSIONS) log_debug("exec: set %s", r->data.set.name); #else POST(status); #endif if (binding == NULL) { binding = dmalloc(sizeof(*binding), MDL); if (binding != NULL) { memset(binding, 0, sizeof(*binding)); binding->name = dmalloc(strlen (r->data.set.name) + 1, MDL); if (binding->name != NULL) { strcpy(binding->name, r->data.set.name); binding->next = (*scope)->bindings; (*scope)->bindings = binding; } else { dfree(binding, MDL); binding = NULL; } } } if (binding != NULL) { if (binding->value != NULL) binding_value_dereference (&binding->value, MDL); if (r->op == set_statement) { status = (evaluate_expression (&binding->value, packet, lease, client_state, in_options, out_options, scope, r->data.set.expr, MDL)); } else { if (!(binding_value_allocate (&binding->value, MDL))) { dfree(binding, MDL); binding = NULL; } if ((binding != NULL) && (binding->value != NULL)) { binding->value->type = binding_function; (fundef_reference (&binding->value->value.fundef, r->data.set.expr->data.func, MDL)); } } } #if defined (DEBUG_EXPRESSIONS) log_debug ("exec: set %s%s", r -> data.set.name, (binding && status ? "" : " (failed)")); #else POST(status); #endif break; case unset_statement: if (!scope || !*scope) break; binding = find_binding (*scope, r->data.unset); if (binding) { if (binding->value) binding_value_dereference (&binding->value, MDL); status = 1; } else status = 0; #if defined (DEBUG_EXPRESSIONS) log_debug ("exec: unset %s: %s", r->data.unset, (status ? "found" : "not found")); #else POST(status); #endif break; case let_statement: #if defined (DEBUG_EXPRESSIONS) log_debug("exec: let %s", r->data.let.name); #endif status = 0; ns = NULL; binding_scope_allocate (&ns, MDL); e = r; next_let: if (ns) { binding = dmalloc(sizeof(*binding), MDL); if (!binding) { blb: binding_scope_dereference(&ns, MDL); } else { memset(binding, 0, sizeof(*binding)); binding->name = dmalloc(strlen (e->data.let.name + 1), MDL); if (binding->name) strcpy(binding->name, e->data.let.name); else { dfree(binding, MDL); binding = NULL; goto blb; } } } else binding = NULL; if (ns && binding) { status = (evaluate_expression (&binding->value, packet, lease, client_state, in_options, out_options, scope, e->data.set.expr, MDL)); binding->next = ns->bindings; ns->bindings = binding; } #if defined (DEBUG_EXPRESSIONS) log_debug("exec: let %s%s", e->data.let.name, (binding && status ? "" : "failed")); #else POST(status); #endif if (!e->data.let.statements) { } else if (e->data.let.statements->op == let_statement) { e = e->data.let.statements; goto next_let; } else if (ns) { if (scope && *scope) binding_scope_reference(&ns->outer, *scope, MDL); execute_statements (result, packet, lease, client_state, in_options, out_options, &ns, e->data.let.statements, on_star); } if (ns) binding_scope_dereference(&ns, MDL); break; case log_statement: memset (&ds, 0, sizeof ds); status = (evaluate_data_expression (&ds, packet, lease, client_state, in_options, out_options, scope, r->data.log.expr, MDL)); #if defined (DEBUG_EXPRESSIONS) log_debug ("exec: log"); #endif if (status) { switch (r->data.log.priority) { case log_priority_fatal: log_fatal ("%.*s", (int)ds.len, ds.data); break; case log_priority_error: log_error ("%.*s", (int)ds.len, ds.data); break; case log_priority_debug: log_debug ("%.*s", (int)ds.len, ds.data); break; case log_priority_info: log_info ("%.*s", (int)ds.len, ds.data); break; } data_string_forget (&ds, MDL); } break; case vendor_opt_statement: /* If possible parse any options in a vendor option * encapsulation, this may add options to the in_options * option state */ parse_vendor_option(packet, lease, client_state, in_options, out_options, scope); break; default: log_error ("bogus statement type %d", r -> op); break; } executable_statement_dereference (&r, MDL); if (next) { executable_statement_reference (&r, next, MDL); executable_statement_dereference (&next, MDL); } } return 1; } /* Execute all the statements in a particular scope, and all statements in scopes outer from that scope, but if a particular limiting scope is reached, do not execute statements in that scope or in scopes outer from it. More specific scopes need to take precedence over less specific scopes, so we recursively traverse the scope list, executing the most outer scope first. */ void execute_statements_in_scope (result, packet, lease, client_state, in_options, out_options, scope, group, limiting_group, on_star) struct binding_value **result; struct packet *packet; struct lease *lease; struct client_state *client_state; struct option_state *in_options; struct option_state *out_options; struct binding_scope **scope; struct group *group; struct group *limiting_group; struct on_star *on_star; { struct group *limit; /* If we've recursed as far as we can, return. */ if (!group) return; /* As soon as we get to a scope that is outer than the limiting scope, we are done. This is so that if somebody does something like this, it does the expected thing: domain-name "example.com"; shared-network FOO { host bar { domain-name "othello.example.com"; fixed-address 10.20.30.40; } subnet 10.20.30.0 netmask 255.255.255.0 { domain-name "manhattan.example.com"; } } The problem with the above arrangement is that the host's group nesting will be host -> shared-network -> top-level, and the limiting scope when we evaluate the host's scope will be the subnet -> shared-network -> top-level, so we need to know when we evaluate the host's scope to stop before we evaluate the shared-networks scope, because it's outer than the limiting scope, which means we've already evaluated it. */ for (limit = limiting_group; limit; limit = limit -> next) { if (group == limit) return; } if (group -> next) execute_statements_in_scope (result, packet, lease, client_state, in_options, out_options, scope, group->next, limiting_group, on_star); execute_statements (result, packet, lease, client_state, in_options, out_options, scope, group->statements, on_star); } /* Dereference or free any subexpressions of a statement being freed. */ int executable_statement_dereference (ptr, file, line) struct executable_statement **ptr; const char *file; int line; { if (!ptr || !*ptr) { log_error ("%s(%d): null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } (*ptr) -> refcnt--; rc_register (file, line, ptr, *ptr, (*ptr) -> refcnt, 1, RC_MISC); if ((*ptr) -> refcnt > 0) { *ptr = (struct executable_statement *)0; return 1; } if ((*ptr) -> refcnt < 0) { log_error ("%s(%d): negative refcnt!", file, line); #if defined (DEBUG_RC_HISTORY) dump_rc_history (*ptr); #endif #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } if ((*ptr) -> next) executable_statement_dereference (&(*ptr) -> next, file, line); switch ((*ptr) -> op) { case statements_statement: if ((*ptr) -> data.statements) executable_statement_dereference (&(*ptr) -> data.statements, file, line); break; case on_statement: if ((*ptr) -> data.on.statements) executable_statement_dereference (&(*ptr) -> data.on.statements, file, line); break; case switch_statement: if ((*ptr) -> data.s_switch.statements) executable_statement_dereference (&(*ptr) -> data.on.statements, file, line); if ((*ptr) -> data.s_switch.expr) expression_dereference (&(*ptr) -> data.s_switch.expr, file, line); break; case case_statement: if ((*ptr) -> data.s_switch.expr) expression_dereference (&(*ptr) -> data.c_case, file, line); break; case if_statement: if ((*ptr) -> data.ie.expr) expression_dereference (&(*ptr) -> data.ie.expr, file, line); if ((*ptr) -> data.ie.tc) executable_statement_dereference (&(*ptr) -> data.ie.tc, file, line); if ((*ptr) -> data.ie.fc) executable_statement_dereference (&(*ptr) -> data.ie.fc, file, line); break; case eval_statement: if ((*ptr) -> data.eval) expression_dereference (&(*ptr) -> data.eval, file, line); break; case return_statement: if ((*ptr) -> data.eval) expression_dereference (&(*ptr) -> data.eval, file, line); break; case set_statement: if ((*ptr)->data.set.name) dfree ((*ptr)->data.set.name, file, line); if ((*ptr)->data.set.expr) expression_dereference (&(*ptr) -> data.set.expr, file, line); break; case unset_statement: if ((*ptr)->data.unset) dfree ((*ptr)->data.unset, file, line); break; case execute_statement: if ((*ptr)->data.execute.command) dfree ((*ptr)->data.execute.command, file, line); if ((*ptr)->data.execute.arglist) expression_dereference (&(*ptr) -> data.execute.arglist, file, line); break; case supersede_option_statement: case send_option_statement: case default_option_statement: case append_option_statement: case prepend_option_statement: if ((*ptr) -> data.option) option_cache_dereference (&(*ptr) -> data.option, file, line); break; default: /* Nothing to do. */ break; } dfree ((*ptr), file, line); *ptr = (struct executable_statement *)0; return 1; } void write_statements (file, statements, indent) FILE *file; struct executable_statement *statements; int indent; { #if defined ENABLE_EXECUTE struct expression *expr; #endif struct executable_statement *r, *x; const char *s, *t, *dot; int col; if (!statements) return; for (r = statements; r; r = r -> next) { switch (r -> op) { case statements_statement: write_statements (file, r -> data.statements, indent); break; case on_statement: indent_spaces (file, indent); fprintf (file, "on "); s = ""; if (r -> data.on.evtypes & ON_EXPIRY) { fprintf (file, "%sexpiry", s); s = " or "; } if (r -> data.on.evtypes & ON_COMMIT) { fprintf (file, "%scommit", s); s = " or "; } if (r -> data.on.evtypes & ON_RELEASE) { fprintf (file, "%srelease", s); /* s = " or "; */ } if (r -> data.on.statements) { fprintf (file, " {"); write_statements (file, r -> data.on.statements, indent + 2); indent_spaces (file, indent); fprintf (file, "}"); } else { fprintf (file, ";"); } break; case switch_statement: indent_spaces (file, indent); fprintf (file, "switch ("); col = write_expression (file, r -> data.s_switch.expr, indent + 7, indent + 7, 1); col = token_print_indent (file, col, indent + 7, "", "", ")"); token_print_indent (file, col, indent, " ", "", "{"); write_statements (file, r -> data.s_switch.statements, indent + 2); indent_spaces (file, indent); fprintf (file, "}"); break; case case_statement: indent_spaces (file, indent - 1); fprintf (file, "case "); col = write_expression (file, r -> data.s_switch.expr, indent + 5, indent + 5, 1); token_print_indent (file, col, indent + 5, "", "", ":"); break; case default_statement: indent_spaces (file, indent - 1); fprintf (file, "default: "); break; case if_statement: indent_spaces (file, indent); fprintf (file, "if "); x = r; col = write_expression (file, x -> data.ie.expr, indent + 3, indent + 3, 1); else_if: token_print_indent (file, col, indent, " ", "", "{"); write_statements (file, x -> data.ie.tc, indent + 2); if (x -> data.ie.fc && x -> data.ie.fc -> op == if_statement && !x -> data.ie.fc -> next) { indent_spaces (file, indent); fprintf (file, "} elsif "); x = x -> data.ie.fc; col = write_expression (file, x -> data.ie.expr, indent + 6, indent + 6, 1); goto else_if; } if (x -> data.ie.fc) { indent_spaces (file, indent); fprintf (file, "} else {"); write_statements (file, x -> data.ie.fc, indent + 2); } indent_spaces (file, indent); fprintf (file, "}"); break; case eval_statement: indent_spaces (file, indent); fprintf (file, "eval "); (void) write_expression (file, r -> data.eval, indent + 5, indent + 5, 1); fprintf (file, ";"); break; case return_statement: indent_spaces (file, indent); fprintf (file, "return;"); break; case add_statement: indent_spaces (file, indent); fprintf (file, "add \"%s\"", r -> data.add -> name); break; case break_statement: indent_spaces (file, indent); fprintf (file, "break;"); break; case supersede_option_statement: case send_option_statement: s = "supersede"; goto option_statement; case default_option_statement: s = "default"; goto option_statement; case append_option_statement: s = "append"; goto option_statement; case prepend_option_statement: s = "prepend"; option_statement: /* Note: the reason we don't try to pretty print the option here is that the format of the option may change in dhcpd.conf, and then when this statement was read back, it would cause a syntax error. */ if (r -> data.option -> option -> universe == &dhcp_universe) { t = ""; dot = ""; } else { t = (r -> data.option -> option -> universe -> name); dot = "."; } indent_spaces (file, indent); fprintf (file, "%s %s%s%s = ", s, t, dot, r -> data.option -> option -> name); col = (indent + strlen (s) + strlen (t) + strlen (dot) + strlen (r -> data.option -> option -> name) + 4); if (r -> data.option -> expression) write_expression (file, r -> data.option -> expression, col, indent + 8, 1); else token_indent_data_string (file, col, indent + 8, "", "", &r -> data.option -> data); fprintf (file, ";"); /* XXX */ break; case set_statement: indent_spaces (file, indent); fprintf (file, "set "); col = token_print_indent (file, indent + 4, indent + 4, "", "", r -> data.set.name); (void) token_print_indent (file, col, indent + 4, " ", " ", "="); col = write_expression (file, r -> data.set.expr, indent + 3, indent + 3, 0); (void) token_print_indent (file, col, indent + 4, " ", "", ";"); break; case unset_statement: indent_spaces (file, indent); fprintf (file, "unset "); col = token_print_indent (file, indent + 6, indent + 6, "", "", r -> data.set.name); (void) token_print_indent (file, col, indent + 6, " ", "", ";"); break; case log_statement: indent_spaces (file, indent); fprintf (file, "log "); col = token_print_indent (file, indent + 4, indent + 4, "", "", "("); switch (r -> data.log.priority) { case log_priority_fatal: (void) token_print_indent (file, col, indent + 4, "", " ", "fatal,"); break; case log_priority_error: (void) token_print_indent (file, col, indent + 4, "", " ", "error,"); break; case log_priority_debug: (void) token_print_indent (file, col, indent + 4, "", " ", "debug,"); break; case log_priority_info: (void) token_print_indent (file, col, indent + 4, "", " ", "info,"); break; } col = write_expression (file, r -> data.log.expr, indent + 4, indent + 4, 0); (void) token_print_indent (file, col, indent + 4, "", "", ");"); break; case execute_statement: #ifdef ENABLE_EXECUTE indent_spaces(file, indent); col = token_print_indent(file, indent + 4, indent + 4, "", "", "execute"); col = token_print_indent(file, col, indent + 4, " ", "", "("); col = token_print_indent_concat(file, col, indent + 4, "", "", "\"", r->data.execute.command, "\"", (char *)0); for (expr = r->data.execute.arglist; expr; expr = expr->data.arg.next) { col = token_print_indent(file, col, indent + 4, "", " ", ","); col = write_expression(file, expr->data.arg.val, col, indent + 4, 0); } (void) token_print_indent(file, col, indent + 4, "", "", ");"); #else /* !ENABLE_EXECUTE */ log_fatal("Impossible case at %s:%d (ENABLE_EXECUTE " "is not defined).", MDL); #endif /* ENABLE_EXECUTE */ break; case vendor_opt_statement: indent_spaces (file, indent); fprintf (file, "parse-vendor-option;"); break; default: log_fatal ("bogus statement type %d\n", r -> op); } } } /* Find a case statement in the sequence of executable statements that matches the expression, and if found, return the following statement. If no case statement matches, try to find a default statement and return that (the default statement can precede all the case statements). Otherwise, return the null statement. */ int find_matching_case (struct executable_statement **ep, struct packet *packet, struct lease *lease, struct client_state *client_state, struct option_state *in_options, struct option_state *out_options, struct binding_scope **scope, struct expression *expr, struct executable_statement *stmt) { int status, sub; struct executable_statement *s; if (is_data_expression (expr)) { struct data_string cd, ds; memset (&ds, 0, sizeof ds); memset (&cd, 0, sizeof cd); status = (evaluate_data_expression (&ds, packet, lease, client_state, in_options, out_options, scope, expr, MDL)); if (status) { for (s = stmt; s; s = s -> next) { if (s -> op == case_statement) { sub = (evaluate_data_expression (&cd, packet, lease, client_state, in_options, out_options, scope, s->data.c_case, MDL)); if (sub && cd.len == ds.len && !memcmp (cd.data, ds.data, cd.len)) { data_string_forget (&cd, MDL); data_string_forget (&ds, MDL); executable_statement_reference (ep, s->next, MDL); return 1; } data_string_forget (&cd, MDL); } } data_string_forget (&ds, MDL); } } else { unsigned long n, c; status = evaluate_numeric_expression (&n, packet, lease, client_state, in_options, out_options, scope, expr); if (status) { for (s = stmt; s; s = s->next) { if (s -> op == case_statement) { sub = (evaluate_numeric_expression (&c, packet, lease, client_state, in_options, out_options, scope, s->data.c_case)); if (sub && n == c) { executable_statement_reference (ep, s->next, MDL); return 1; } } } } } /* If we didn't find a matching case statement, look for a default statement and return the statement following it. */ for (s = stmt; s; s = s->next) if (s->op == default_statement) break; if (s) { executable_statement_reference (ep, s->next, MDL); return 1; } return 0; } int executable_statement_foreach (struct executable_statement *stmt, int (*callback) (struct executable_statement *, void *, int), void *vp, int condp) { struct executable_statement *foo; int ok = 0; for (foo = stmt; foo; foo = foo->next) { if ((*callback) (foo, vp, condp) != 0) ok = 1; switch (foo->op) { case null_statement: break; case if_statement: if (executable_statement_foreach (foo->data.ie.tc, callback, vp, 1)) ok = 1; if (executable_statement_foreach (foo->data.ie.fc, callback, vp, 1)) ok = 1; break; case add_statement: break; case eval_statement: break; case break_statement: break; case default_option_statement: break; case supersede_option_statement: break; case append_option_statement: break; case prepend_option_statement: break; case send_option_statement: break; case statements_statement: if ((executable_statement_foreach (foo->data.statements, callback, vp, condp))) ok = 1; break; case on_statement: if ((executable_statement_foreach (foo->data.on.statements, callback, vp, 1))) ok = 1; break; case switch_statement: if ((executable_statement_foreach (foo->data.s_switch.statements, callback, vp, 1))) ok = 1; break; case case_statement: break; case default_statement: break; case set_statement: break; case unset_statement: break; case let_statement: if ((executable_statement_foreach (foo->data.let.statements, callback, vp, 0))) ok = 1; break; case define_statement: break; case log_statement: case return_statement: case execute_statement: case vendor_opt_statement: break; } } return ok; } dhcp-4.4.1/common/fddi.c000644 000765 000024 00000005025 13243301226 015257 0ustar00tmarkstaff000000 000000 /* fddi.c Packet assembly code, originally contributed by Archie Cobbs. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1996-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #if defined (DEC_FDDI) #include #include #if defined (PACKET_ASSEMBLY) || defined (PACKET_DECODING) #include "includes/netinet/if_ether.h" #endif /* PACKET_ASSEMBLY || PACKET_DECODING */ #if defined (PACKET_ASSEMBLY) /* Assemble an hardware header... */ void assemble_fddi_header (interface, buf, bufix, to) struct interface_info *interface; unsigned char *buf; unsigned *bufix; struct hardware *to; { struct fddi_header fh; struct llc lh; if (to && to -> hlen == 7) memcpy (fh.fddi_dhost, &to -> hbuf [1], sizeof (fh.fddi_dhost)); memcpy (fh.fddi_shost, &interface -> hw_address.hbuf [1], sizeof (fh.fddi_shost)); fh.fddi_fc = FDDIFC_LLC_ASYNC; memcpy (&buf [*bufix], &fh, sizeof fh); *bufix += sizeof fh; lh.llc_dsap = LLC_SNAP_LSAP; lh.llc_ssap = LLC_SNAP_LSAP; lh.llc_un.type_snap.control = LLC_UI; lh.llc_un.type_snap.ether_type = htons (ETHERTYPE_IP); memcpy (&buf [*bufix], &lh, LLC_SNAP_LEN); *bufix += LLC_SNAP_LEN; } #endif /* PACKET_ASSEMBLY */ #ifdef PACKET_DECODING /* Decode a hardware header... */ ssize_t decode_fddi_header (interface, buf, bufix, from) struct interface_info *interface; unsigned char *buf; unsigned bufix; struct hardware *from; { struct fddi_header fh; struct llc lh; from -> hbuf [0] = HTYPE_FDDI; memcpy (&from -> hbuf [1], fh.fddi_shost, sizeof fh.fddi_shost); return FDDI_HEADER_SIZE + LLC_SNAP_LEN; } #endif /* PACKET_DECODING */ #endif /* DEC_FDDI */ dhcp-4.4.1/common/icmp.c000644 000765 000024 00000017070 13243301226 015304 0ustar00tmarkstaff000000 000000 /* dhcp.c ICMP Protocol engine - for sending out pings and receiving responses. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1996-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #include "netinet/ip.h" #include "netinet/ip_icmp.h" struct icmp_state *icmp_state; static omapi_object_type_t *dhcp_type_icmp; static int no_icmp; OMAPI_OBJECT_ALLOC (icmp_state, struct icmp_state, dhcp_type_icmp) #if defined (TRACING) trace_type_t *trace_icmp_input; trace_type_t *trace_icmp_output; #endif /* Initialize the ICMP protocol. */ void icmp_startup (routep, handler) int routep; void (*handler) (struct iaddr, u_int8_t *, int); { struct protoent *proto; int protocol = 1; int state; isc_result_t result; /* Only initialize icmp once. */ if (dhcp_type_icmp) log_fatal ("attempted to reinitialize icmp protocol"); result = omapi_object_type_register (&dhcp_type_icmp, "icmp", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sizeof (struct icmp_state), 0, RC_MISC); if (result != ISC_R_SUCCESS) log_fatal ("Can't register icmp object type: %s", isc_result_totext (result)); icmp_state_allocate (&icmp_state, MDL); icmp_state -> icmp_handler = handler; #if defined (TRACING) trace_icmp_input = trace_type_register ("icmp-input", (void *)0, trace_icmp_input_input, trace_icmp_input_stop, MDL); trace_icmp_output = trace_type_register ("icmp-output", (void *)0, trace_icmp_output_input, trace_icmp_output_stop, MDL); /* If we're playing back a trace file, don't create the socket or set up the callback. */ if (!trace_playback ()) { #endif /* Get the protocol number (should be 1). */ proto = getprotobyname ("icmp"); if (proto) protocol = proto -> p_proto; /* Get a raw socket for the ICMP protocol. */ icmp_state -> socket = socket (AF_INET, SOCK_RAW, protocol); if (icmp_state -> socket < 0) { no_icmp = 1; log_error ("unable to create icmp socket: %m"); return; } #if defined (HAVE_SETFD) if (fcntl (icmp_state -> socket, F_SETFD, 1) < 0) log_error ("Can't set close-on-exec on icmp: %m"); #endif /* Make sure it does routing... */ state = 0; if (setsockopt (icmp_state -> socket, SOL_SOCKET, SO_DONTROUTE, (char *)&state, sizeof state) < 0) log_fatal ("Can't disable SO_DONTROUTE on ICMP: %m"); result = (omapi_register_io_object ((omapi_object_t *)icmp_state, icmp_readsocket, 0, icmp_echoreply, 0, 0)); if (result != ISC_R_SUCCESS) log_fatal ("Can't register icmp handle: %s", isc_result_totext (result)); #if defined (TRACING) } #endif } int icmp_readsocket (h) omapi_object_t *h; { struct icmp_state *state; state = (struct icmp_state *)h; return state -> socket; } int icmp_echorequest (addr) struct iaddr *addr; { struct sockaddr_in to; struct icmp icmp; int status; #if defined (TRACING) trace_iov_t iov [2]; #endif if (no_icmp) return 1; if (!icmp_state) log_fatal ("ICMP protocol used before initialization."); memset (&to, 0, sizeof(to)); #ifdef HAVE_SA_LEN to.sin_len = sizeof to; #endif to.sin_family = AF_INET; to.sin_port = 0; /* unused. */ memcpy (&to.sin_addr, addr -> iabuf, sizeof to.sin_addr); /* XXX */ icmp.icmp_type = ICMP_ECHO; icmp.icmp_code = 0; icmp.icmp_cksum = 0; icmp.icmp_seq = 0; #if SIZEOF_STRUCT_IADDR_P == 8 icmp.icmp_id = (((u_int32_t)(u_int64_t)addr) ^ (u_int32_t)(((u_int64_t)addr) >> 32)); #else icmp.icmp_id = (u_int32_t)addr; #endif memset (&icmp.icmp_dun, 0, sizeof icmp.icmp_dun); icmp.icmp_cksum = wrapsum (checksum ((unsigned char *)&icmp, sizeof icmp, 0)); #if defined (TRACING) if (trace_playback ()) { char *buf = (char *)0; unsigned buflen = 0; /* Consume the ICMP event. */ status = trace_get_packet (&trace_icmp_output, &buflen, &buf); if (status != ISC_R_SUCCESS) log_error ("icmp_echorequest: %s", isc_result_totext (status)); if (buf) dfree (buf, MDL); } else { if (trace_record ()) { iov [0].buf = (char *)addr; iov [0].len = sizeof *addr; iov [1].buf = (char *)&icmp; iov [1].len = sizeof icmp; trace_write_packet_iov (trace_icmp_output, 2, iov, MDL); } #endif /* Send the ICMP packet... */ status = sendto (icmp_state -> socket, (char *)&icmp, sizeof icmp, 0, (struct sockaddr *)&to, sizeof to); if (status < 0) log_error ("icmp_echorequest %s: %m", inet_ntoa(to.sin_addr)); if (status != sizeof icmp) return 0; #if defined (TRACING) } #endif return 1; } isc_result_t icmp_echoreply (h) omapi_object_t *h; { struct icmp *icfrom; struct ip *ip; struct sockaddr_in from; u_int8_t icbuf [1500]; int status; SOCKLEN_T sl; int hlen, len; struct iaddr ia; struct icmp_state *state; #if defined (TRACING) trace_iov_t iov [2]; #endif state = (struct icmp_state *)h; sl = sizeof from; status = recvfrom (state -> socket, (char *)icbuf, sizeof icbuf, 0, (struct sockaddr *)&from, &sl); if (status < 0) { log_error ("icmp_echoreply: %m"); return ISC_R_UNEXPECTED; } /* Find the IP header length... */ ip = (struct ip *)icbuf; hlen = IP_HL (ip); /* Short packet? */ if (status < hlen + (sizeof *icfrom)) { return ISC_R_SUCCESS; } len = status - hlen; icfrom = (struct icmp *)(icbuf + hlen); /* Silently discard ICMP packets that aren't echoreplies. */ if (icfrom -> icmp_type != ICMP_ECHOREPLY) { return ISC_R_SUCCESS; } /* If we were given a second-stage handler, call it. */ if (state -> icmp_handler) { memcpy (ia.iabuf, &from.sin_addr, sizeof from.sin_addr); ia.len = sizeof from.sin_addr; #if defined (TRACING) if (trace_record ()) { ia.len = htonl(ia.len); iov [0].buf = (char *)&ia; iov [0].len = sizeof ia; iov [1].buf = (char *)icbuf; iov [1].len = len; trace_write_packet_iov (trace_icmp_input, 2, iov, MDL); ia.len = ntohl(ia.len); } #endif (*state -> icmp_handler) (ia, icbuf, len); } return ISC_R_SUCCESS; } #if defined (TRACING) void trace_icmp_input_input (trace_type_t *ttype, unsigned length, char *buf) { struct iaddr *ia; u_int8_t *icbuf; ia = (struct iaddr *)buf; ia->len = ntohl(ia->len); icbuf = (u_int8_t *)(ia + 1); if (icmp_state -> icmp_handler) (*icmp_state -> icmp_handler) (*ia, icbuf, (int)(length - sizeof ia)); } void trace_icmp_input_stop (trace_type_t *ttype) { } void trace_icmp_output_input (trace_type_t *ttype, unsigned length, char *buf) { struct iaddr ia; if (length != (sizeof (struct icmp) + sizeof (ia))) { log_error ("trace_icmp_output_input: data size mismatch %d:%d", length, (int)(sizeof (struct icmp) + sizeof (ia))); return; } ia.len = 4; memcpy (ia.iabuf, buf, 4); log_error ("trace_icmp_output_input: unsent ping to %s", piaddr (ia)); } void trace_icmp_output_stop (trace_type_t *ttype) { } #endif /* TRACING */ dhcp-4.4.1/common/inet.c000644 000765 000024 00000035645 13243301226 015323 0ustar00tmarkstaff000000 000000 /* inet.c Subroutines to manipulate internet addresses and ports in a safely portable way... */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1995-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" /* Return just the network number of an internet address... */ struct iaddr subnet_number (addr, mask) struct iaddr addr; struct iaddr mask; { int i; struct iaddr rv; if (addr.len > sizeof(addr.iabuf)) log_fatal("subnet_number():%s:%d: Invalid addr length.", MDL); if (addr.len != mask.len) log_fatal("subnet_number():%s:%d: Addr/mask length mismatch.", MDL); rv.len = 0; /* Both addresses must have the same length... */ if (addr.len != mask.len) return rv; rv.len = addr.len; for (i = 0; i < rv.len; i++) rv.iabuf [i] = addr.iabuf [i] & mask.iabuf [i]; return rv; } /* Combine a network number and a integer to produce an internet address. This won't work for subnets with more than 32 bits of host address, but maybe this isn't a problem. */ struct iaddr ip_addr (subnet, mask, host_address) struct iaddr subnet; struct iaddr mask; u_int32_t host_address; { int i, j, k; u_int32_t swaddr; struct iaddr rv; unsigned char habuf [sizeof swaddr]; if (subnet.len > sizeof(subnet.iabuf)) log_fatal("ip_addr():%s:%d: Invalid addr length.", MDL); if (subnet.len != mask.len) log_fatal("ip_addr():%s:%d: Addr/mask length mismatch.", MDL); swaddr = htonl (host_address); memcpy (habuf, &swaddr, sizeof swaddr); /* Combine the subnet address and the host address. If the host address is bigger than can fit in the subnet, return a zero-length iaddr structure. */ rv = subnet; j = rv.len - sizeof habuf; for (i = sizeof habuf - 1; i >= 0; i--) { if (mask.iabuf [i + j]) { if (habuf [i] > (mask.iabuf [i + j] ^ 0xFF)) { rv.len = 0; return rv; } for (k = i - 1; k >= 0; k--) { if (habuf [k]) { rv.len = 0; return rv; } } rv.iabuf [i + j] |= habuf [i]; break; } else rv.iabuf [i + j] = habuf [i]; } return rv; } /* Given a subnet number and netmask, return the address on that subnet for which the host portion of the address is all ones (the standard broadcast address). */ struct iaddr broadcast_addr (subnet, mask) struct iaddr subnet; struct iaddr mask; { int i; struct iaddr rv; if (subnet.len > sizeof(subnet.iabuf)) log_fatal("broadcast_addr():%s:%d: Invalid addr length.", MDL); if (subnet.len != mask.len) log_fatal("broadcast_addr():%s:%d: Addr/mask length mismatch.", MDL); if (subnet.len != mask.len) { rv.len = 0; return rv; } for (i = 0; i < subnet.len; i++) { rv.iabuf [i] = subnet.iabuf [i] | (~mask.iabuf [i] & 255); } rv.len = subnet.len; return rv; } u_int32_t host_addr (addr, mask) struct iaddr addr; struct iaddr mask; { int i; u_int32_t swaddr; struct iaddr rv; if (addr.len > sizeof(addr.iabuf)) log_fatal("host_addr():%s:%d: Invalid addr length.", MDL); if (addr.len != mask.len) log_fatal("host_addr():%s:%d: Addr/mask length mismatch.", MDL); rv.len = 0; /* Mask out the network bits... */ rv.len = addr.len; for (i = 0; i < rv.len; i++) rv.iabuf [i] = addr.iabuf [i] & ~mask.iabuf [i]; /* Copy out up to 32 bits... */ memcpy (&swaddr, &rv.iabuf [rv.len - sizeof swaddr], sizeof swaddr); /* Swap it and return it. */ return ntohl (swaddr); } int addr_eq (addr1, addr2) struct iaddr addr1, addr2; { if (addr1.len > sizeof(addr1.iabuf)) log_fatal("addr_eq():%s:%d: Invalid addr length.", MDL); if (addr1.len != addr2.len) return 0; return memcmp (addr1.iabuf, addr2.iabuf, addr1.len) == 0; } /* addr_match * * compares an IP address against a network/mask combination * by ANDing the IP with the mask and seeing whether the result * matches the masked network value. */ int addr_match(addr, match) struct iaddr *addr; struct iaddrmatch *match; { int i; if (addr->len != match->addr.len) return 0; for (i = 0 ; i < addr->len ; i++) { if ((addr->iabuf[i] & match->mask.iabuf[i]) != match->addr.iabuf[i]) return 0; } return 1; } /* * Compares the addresses a1 and a2. * * If a1 < a2, returns -1. * If a1 == a2, returns 0. * If a1 > a2, returns 1. * * WARNING: if a1 and a2 differ in length, returns 0. */ int addr_cmp(const struct iaddr *a1, const struct iaddr *a2) { int i; if (a1->len != a2->len) { return 0; } for (i=0; ilen; i++) { if (a1->iabuf[i] < a2->iabuf[i]) { return -1; } if (a1->iabuf[i] > a2->iabuf[i]) { return 1; } } return 0; } /* * Performs a bitwise-OR of two addresses. * * Returns 1 if the result is non-zero, or 0 otherwise. * * WARNING: if a1 and a2 differ in length, returns 0. */ int addr_or(struct iaddr *result, const struct iaddr *a1, const struct iaddr *a2) { int i; int all_zero; if (a1->len != a2->len) { return 0; } all_zero = 1; result->len = a1->len; for (i=0; ilen; i++) { result->iabuf[i] = a1->iabuf[i] | a2->iabuf[i]; if (result->iabuf[i] != 0) { all_zero = 0; } } return !all_zero; } /* * Performs a bitwise-AND of two addresses. * * Returns 1 if the result is non-zero, or 0 otherwise. * * WARNING: if a1 and a2 differ in length, returns 0. */ int addr_and(struct iaddr *result, const struct iaddr *a1, const struct iaddr *a2) { int i; int all_zero; if (a1->len != a2->len) { return 0; } all_zero = 1; result->len = a1->len; for (i=0; ilen; i++) { result->iabuf[i] = a1->iabuf[i] & a2->iabuf[i]; if (result->iabuf[i] != 0) { all_zero = 0; } } return !all_zero; } /* * Check if a bitmask of the given length is valid for the address. * This is not the case if any bits longer than the bitmask are 1. * * So, this is valid: * * 127.0.0.0/8 * * But this is not: * * 127.0.0.1/8 * * Because the final ".1" would get masked out by the /8. */ isc_boolean_t is_cidr_mask_valid(const struct iaddr *addr, int bits) { int zero_bits; int zero_bytes; int i; char byte; int shift_bits; /* * Check our bit boundaries. */ if (bits < 0) { return ISC_FALSE; } if (bits > (addr->len * 8)) { return ISC_FALSE; } /* * Figure out how many low-order bits need to be zero. */ zero_bits = (addr->len * 8) - bits; zero_bytes = zero_bits / 8; /* * Check to make sure the low-order bytes are zero. */ for (i=1; i<=zero_bytes; i++) { if (addr->iabuf[addr->len-i] != 0) { return ISC_FALSE; } } /* * Look to see if any bits not in right-hand bytes are * non-zero, by making a byte that has these bits set to zero * comparing to the original byte. If these two values are * equal, then the right-hand bits are zero, and we are * happy. */ shift_bits = zero_bits % 8; if (shift_bits == 0) return ISC_TRUE; byte = addr->iabuf[addr->len-zero_bytes-1]; return (((byte >> shift_bits) << shift_bits) == byte); } /* * range2cidr * * Converts a range of IP addresses to a set of CIDR networks. * * Examples: * 192.168.0.0 - 192.168.0.255 = 192.168.0.0/24 * 10.0.0.0 - 10.0.1.127 = 10.0.0.0/24, 10.0.1.0/25 * 255.255.255.32 - 255.255.255.255 = 255.255.255.32/27, 255.255.255.64/26, * 255.255.255.128/25 */ isc_result_t range2cidr(struct iaddrcidrnetlist **result, const struct iaddr *lo, const struct iaddr *hi) { struct iaddr addr; struct iaddr mask; int bit; struct iaddr end_addr; struct iaddr dummy; int ofs, val; struct iaddrcidrnetlist *net; int tmp; if (result == NULL) { return DHCP_R_INVALIDARG; } if (*result != NULL) { return DHCP_R_INVALIDARG; } if ((lo == NULL) || (hi == NULL) || (lo->len != hi->len)) { return DHCP_R_INVALIDARG; } /* * Put our start and end in the right order, if reversed. */ if (addr_cmp(lo, hi) > 0) { const struct iaddr *tmp; tmp = lo; lo = hi; hi = tmp; } /* * Theory of operation: * * ------------------- * Start at the low end, and keep trying larger networks * until we get one that is too big (explained below). * * We keep a "mask", which is the ones-complement of a * normal netmask. So, a /23 has a netmask of 255.255.254.0, * and a mask of 0.0.1.255. * * We know when a network is too big when we bitwise-AND the * mask with the starting address and we get a non-zero * result, like this: * * addr: 192.168.1.0, mask: 0.0.1.255 * bitwise-AND: 0.0.1.0 * * A network is also too big if the bitwise-OR of the mask * with the starting address is larger than the end address, * like this: * * start: 192.168.1.0, mask: 0.0.1.255, end: 192.168.0.255 * bitwise-OR: 192.168.1.255 * * ------------------- * Once we have found a network that is too big, we add the * appropriate CIDR network to our list of found networks. * * We then use the next IP address as our low address, and * begin the process of searching for a network that is * too big again, starting with an empty mask. */ addr = *lo; bit = 0; memset(&mask, 0, sizeof(mask)); mask.len = addr.len; while (addr_cmp(&addr, hi) <= 0) { /* * Bitwise-OR mask with (1 << bit) */ ofs = addr.len - (bit / 8) - 1; val = 1 << (bit % 8); if (ofs >= 0) { mask.iabuf[ofs] |= val; } /* * See if we're too big, and save this network if so. */ addr_or(&end_addr, &addr, &mask); if ((ofs < 0) || (addr_cmp(&end_addr, hi) > 0) || addr_and(&dummy, &addr, &mask)) { /* * Add a new prefix to our list. */ net = dmalloc(sizeof(*net), MDL); if (net == NULL) { while (*result != NULL) { net = (*result)->next; dfree(*result, MDL); *result = net; } return ISC_R_NOMEMORY; } net->cidrnet.lo_addr = addr; net->cidrnet.bits = (addr.len * 8) - bit; net->next = *result; *result = net; /* * Figure out our new starting address, * by adding (1 << bit) to our previous * starting address. */ tmp = addr.iabuf[ofs] + val; while ((ofs >= 0) && (tmp > 255)) { addr.iabuf[ofs] = tmp - 256; ofs--; tmp = addr.iabuf[ofs] + 1; } if (ofs < 0) { /* Gone past last address, we're done. */ break; } addr.iabuf[ofs] = tmp; /* * Reset our bit and mask. */ bit = 0; memset(mask.iabuf, 0, sizeof(mask.iabuf)); memset(end_addr.iabuf, 0, sizeof(end_addr.iabuf)); } else { /* * If we're not too big, increase our network size. */ bit++; } } /* * We're done. */ return ISC_R_SUCCESS; } /* * Free a list of CIDR networks, such as returned from range2cidr(). */ isc_result_t free_iaddrcidrnetlist(struct iaddrcidrnetlist **result) { struct iaddrcidrnetlist *p; if (result == NULL) { return DHCP_R_INVALIDARG; } if (*result == NULL) { return DHCP_R_INVALIDARG; } while (*result != NULL) { p = *result; *result = p->next; dfree(p, MDL); } return ISC_R_SUCCESS; } /* piaddr() turns an iaddr structure into a printable address. */ /* XXX: should use a const pointer rather than passing the structure */ const char * piaddr(const struct iaddr addr) { static char pbuf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; /* "255.255.255.255" */ /* INSIST((addr.len == 0) || (addr.len == 4) || (addr.len == 16)); */ if (addr.len == 0) { return ""; } if (addr.len == 4) { return inet_ntop(AF_INET, addr.iabuf, pbuf, sizeof(pbuf)); } if (addr.len == 16) { return inet_ntop(AF_INET6, addr.iabuf, pbuf, sizeof(pbuf)); } log_fatal("piaddr():%s:%d: Invalid address length %d.", MDL, addr.len); /* quell compiler warnings */ return NULL; } /* piaddrmask takes an iaddr structure mask, determines the bitlength of * the mask, and then returns the printable CIDR notation of the two. */ char * piaddrmask(struct iaddr *addr, struct iaddr *mask) { int mw; unsigned int oct, bit; if ((addr->len != 4) && (addr->len != 16)) log_fatal("piaddrmask():%s:%d: Address length %d invalid", MDL, addr->len); if (addr->len != mask->len) log_fatal("piaddrmask():%s:%d: Address and mask size mismatch", MDL); /* Determine netmask width in bits. */ for (mw = (mask->len * 8) ; mw > 0 ; ) { oct = (mw - 1) / 8; bit = 0x80 >> ((mw - 1) % 8); if (!mask->iabuf[oct]) mw -= 8; else if (mask->iabuf[oct] & bit) break; else mw--; } if (mw < 0) log_fatal("Impossible condition at %s:%d.", MDL); return piaddrcidr(addr, mw); } /* Format an address and mask-length into printable CIDR notation. */ char * piaddrcidr(const struct iaddr *addr, unsigned int bits) { static char ret[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255/128")]; /* "255.255.255.255/32" */ /* INSIST(addr != NULL); */ /* INSIST((addr->len == 4) || (addr->len == 16)); */ /* INSIST(bits <= (addr->len * 8)); */ if (bits > (addr->len * 8)) return NULL; sprintf(ret, "%s/%d", piaddr(*addr), bits); return ret; } /* Validate that the string represents a valid port number and * return it in network byte order */ u_int16_t validate_port(char *port) { long local_port = 0; long lower = 1; long upper = 65535; char *endptr; errno = 0; local_port = strtol(port, &endptr, 10); if ((*endptr != '\0') || (errno == ERANGE) || (errno == EINVAL)) log_fatal ("Invalid port number specification: %s", port); if (local_port < lower || local_port > upper) log_fatal("Port number specified is out of range (%ld-%ld).", lower, upper); return htons((u_int16_t)local_port); } /* \brief Validate that the string represents a valid port pair (i.e. n,n+1) * * \param the string to validate * \return the first port number in network byte order */ u_int16_t validate_port_pair(char *port) { long local_port = 0; long lower = 1; long upper = 65534; char *endptr; errno = 0; local_port = strtol(port, &endptr, 10); if ((*endptr != '\0') || (errno == ERANGE) || (errno == EINVAL)) log_fatal ("Invalid port pair specification: %s", port); if (local_port < lower || local_port > upper) log_fatal("Port pair specified is out of range (%ld-%ld).", lower, upper); return htons((u_int16_t)local_port); } #ifdef DHCPv6 /* Print a v6 address from an in6_addr struct */ const char * pin6_addr(const struct in6_addr *src){ if (!src) { return (""); } struct iaddr addr; addr.len = 16; memcpy(addr.iabuf, src->s6_addr, 16); return (piaddr(addr)); } #endif dhcp-4.4.1/common/lpf.c000644 000765 000024 00000037243 13243301226 015141 0ustar00tmarkstaff000000 000000 /* lpf.c Linux packet filter code, contributed by Brian Murrel at Interlinx Support Services in Vancouver, B.C. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1996-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ */ #include "dhcpd.h" #if defined (USE_LPF_SEND) || defined (USE_LPF_RECEIVE) #include #include #include #include #include #include #include #include "includes/netinet/ip.h" #include "includes/netinet/udp.h" #include "includes/netinet/if_ether.h" #endif #if defined (USE_LPF_RECEIVE) || defined (USE_LPF_HWADDR) #include #include #include #endif #if defined (USE_LPF_SEND) || defined (USE_LPF_RECEIVE) /* Reinitializes the specified interface after an address change. This is not required for packet-filter APIs. */ #ifdef USE_LPF_SEND void if_reinitialize_send (info) struct interface_info *info; { } #endif #ifdef USE_LPF_RECEIVE void if_reinitialize_receive (info) struct interface_info *info; { } #endif /* Called by get_interface_list for each interface that's discovered. Opens a packet filter for each interface and adds it to the select mask. */ int if_register_lpf (info) struct interface_info *info; { int sock; union { struct sockaddr_ll ll; struct sockaddr common; } sa; struct ifreq ifr; /* Make an LPF socket. */ if ((sock = socket(PF_PACKET, SOCK_RAW, htons((short)ETH_P_ALL))) < 0) { if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT || errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT || errno == EAFNOSUPPORT || errno == EINVAL) { log_error ("socket: %m - make sure"); log_error ("CONFIG_PACKET (Packet socket) %s", "and CONFIG_FILTER"); log_error ("(Socket Filtering) are enabled %s", "in your kernel"); log_fatal ("configuration!"); } log_fatal ("Open a socket for LPF: %m"); } memset (&ifr, 0, sizeof ifr); strncpy (ifr.ifr_name, (const char *)info -> ifp, sizeof ifr.ifr_name); ifr.ifr_name[IFNAMSIZ-1] = '\0'; if (ioctl (sock, SIOCGIFINDEX, &ifr)) log_fatal ("Failed to get interface index: %m"); /* Bind to the interface name */ memset (&sa, 0, sizeof sa); sa.ll.sll_family = AF_PACKET; sa.ll.sll_ifindex = ifr.ifr_ifindex; if (bind (sock, &sa.common, sizeof sa)) { if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT || errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT || errno == EAFNOSUPPORT || errno == EINVAL) { log_error ("socket: %m - make sure"); log_error ("CONFIG_PACKET (Packet socket) %s", "and CONFIG_FILTER"); log_error ("(Socket Filtering) are enabled %s", "in your kernel"); log_fatal ("configuration!"); } log_fatal ("Bind socket to interface: %m"); } get_hw_addr(info->name, &info->hw_address); return sock; } #endif /* USE_LPF_SEND || USE_LPF_RECEIVE */ #ifdef USE_LPF_SEND void if_register_send (info) struct interface_info *info; { /* If we're using the lpf API for sending and receiving, we don't need to register this interface twice. */ #ifndef USE_LPF_RECEIVE info -> wfdesc = if_register_lpf (info); #else info -> wfdesc = info -> rfdesc; #endif if (!quiet_interface_discovery) log_info ("Sending on LPF/%s/%s%s%s", info -> name, print_hw_addr (info -> hw_address.hbuf [0], info -> hw_address.hlen - 1, &info -> hw_address.hbuf [1]), (info -> shared_network ? "/" : ""), (info -> shared_network ? info -> shared_network -> name : "")); } void if_deregister_send (info) struct interface_info *info; { /* don't need to close twice if we are using lpf for sending and receiving */ #ifndef USE_LPF_RECEIVE /* for LPF this is simple, packet filters are removed when sockets are closed */ close (info -> wfdesc); #endif info -> wfdesc = -1; if (!quiet_interface_discovery) log_info ("Disabling output on LPF/%s/%s%s%s", info -> name, print_hw_addr (info -> hw_address.hbuf [0], info -> hw_address.hlen - 1, &info -> hw_address.hbuf [1]), (info -> shared_network ? "/" : ""), (info -> shared_network ? info -> shared_network -> name : "")); } #endif /* USE_LPF_SEND */ #ifdef USE_LPF_RECEIVE /* Defined in bpf.c. We can't extern these in dhcpd.h without pulling in bpf includes... */ extern struct sock_filter dhcp_bpf_filter []; extern int dhcp_bpf_filter_len; #if defined(RELAY_PORT) extern struct sock_filter dhcp_bpf_relay_filter []; extern int dhcp_bpf_relay_filter_len; #endif #if defined (HAVE_TR_SUPPORT) extern struct sock_filter dhcp_bpf_tr_filter []; extern int dhcp_bpf_tr_filter_len; static void lpf_tr_filter_setup (struct interface_info *); #endif static void lpf_gen_filter_setup (struct interface_info *); void if_register_receive (info) struct interface_info *info; { /* Open a LPF device and hang it on this interface... */ info -> rfdesc = if_register_lpf (info); #ifdef PACKET_AUXDATA { int val = 1; if (setsockopt(info->rfdesc, SOL_PACKET, PACKET_AUXDATA, &val, sizeof(val)) < 0) { if (errno != ENOPROTOOPT) { log_fatal ("Failed to set auxiliary packet data: %m"); } } } #endif #if defined (HAVE_TR_SUPPORT) if (info -> hw_address.hbuf [0] == HTYPE_IEEE802) lpf_tr_filter_setup (info); else #endif lpf_gen_filter_setup (info); if (!quiet_interface_discovery) log_info ("Listening on LPF/%s/%s%s%s", info -> name, print_hw_addr (info -> hw_address.hbuf [0], info -> hw_address.hlen - 1, &info -> hw_address.hbuf [1]), (info -> shared_network ? "/" : ""), (info -> shared_network ? info -> shared_network -> name : "")); } void if_deregister_receive (info) struct interface_info *info; { /* for LPF this is simple, packet filters are removed when sockets are closed */ close (info -> rfdesc); info -> rfdesc = -1; if (!quiet_interface_discovery) log_info ("Disabling input on LPF/%s/%s%s%s", info -> name, print_hw_addr (info -> hw_address.hbuf [0], info -> hw_address.hlen - 1, &info -> hw_address.hbuf [1]), (info -> shared_network ? "/" : ""), (info -> shared_network ? info -> shared_network -> name : "")); } static void lpf_gen_filter_setup (info) struct interface_info *info; { struct sock_fprog p; memset(&p, 0, sizeof(p)); /* Set up the bpf filter program structure. This is defined in bpf.c */ p.len = dhcp_bpf_filter_len; p.filter = dhcp_bpf_filter; /* Patch the server port into the LPF program... XXX changes to filter program may require changes to the insn number(s) used below! XXX */ #if defined(RELAY_PORT) if (relay_port) { /* * If user defined relay UDP port, we need to filter * also on the user UDP port. */ p.len = dhcp_bpf_relay_filter_len; p.filter = dhcp_bpf_relay_filter; dhcp_bpf_relay_filter [10].k = ntohs (relay_port); } #endif dhcp_bpf_filter [8].k = ntohs (local_port); if (setsockopt (info -> rfdesc, SOL_SOCKET, SO_ATTACH_FILTER, &p, sizeof p) < 0) { if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT || errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT || errno == EAFNOSUPPORT) { log_error ("socket: %m - make sure"); log_error ("CONFIG_PACKET (Packet socket) %s", "and CONFIG_FILTER"); log_error ("(Socket Filtering) are enabled %s", "in your kernel"); log_fatal ("configuration!"); } log_fatal ("Can't install packet filter program: %m"); } } #if defined (HAVE_TR_SUPPORT) static void lpf_tr_filter_setup (info) struct interface_info *info; { struct sock_fprog p; memset(&p, 0, sizeof(p)); /* Set up the bpf filter program structure. This is defined in bpf.c */ p.len = dhcp_bpf_tr_filter_len; p.filter = dhcp_bpf_tr_filter; /* Patch the server port into the LPF program... XXX changes to filter program may require changes XXX to the insn number(s) used below! XXX Token ring filter is null - when/if we have a filter XXX that's not, we'll need this code. XXX dhcp_bpf_filter [?].k = ntohs (local_port); */ if (setsockopt (info -> rfdesc, SOL_SOCKET, SO_ATTACH_FILTER, &p, sizeof p) < 0) { if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT || errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT || errno == EAFNOSUPPORT) { log_error ("socket: %m - make sure"); log_error ("CONFIG_PACKET (Packet socket) %s", "and CONFIG_FILTER"); log_error ("(Socket Filtering) are enabled %s", "in your kernel"); log_fatal ("configuration!"); } log_fatal ("Can't install packet filter program: %m"); } } #endif /* HAVE_TR_SUPPORT */ #endif /* USE_LPF_RECEIVE */ #ifdef USE_LPF_SEND ssize_t send_packet (interface, packet, raw, len, from, to, hto) struct interface_info *interface; struct packet *packet; struct dhcp_packet *raw; size_t len; struct in_addr from; struct sockaddr_in *to; struct hardware *hto; { unsigned hbufp = 0, ibufp = 0; double hh [16]; double ih [1536 / sizeof (double)]; unsigned char *buf = (unsigned char *)ih; int result; int fudge; if (!strcmp (interface -> name, "fallback")) return send_fallback (interface, packet, raw, len, from, to, hto); if (hto == NULL && interface->anycast_mac_addr.hlen) hto = &interface->anycast_mac_addr; /* Assemble the headers... */ assemble_hw_header (interface, (unsigned char *)hh, &hbufp, hto); fudge = hbufp % 4; /* IP header must be word-aligned. */ memcpy (buf + fudge, (unsigned char *)hh, hbufp); ibufp = hbufp + fudge; assemble_udp_ip_header (interface, buf, &ibufp, from.s_addr, to -> sin_addr.s_addr, to -> sin_port, (unsigned char *)raw, len); memcpy (buf + ibufp, raw, len); result = write(interface->wfdesc, buf + fudge, ibufp + len - fudge); if (result < 0) log_error ("send_packet: %m"); return result; } #endif /* USE_LPF_SEND */ #ifdef USE_LPF_RECEIVE ssize_t receive_packet (interface, buf, len, from, hfrom) struct interface_info *interface; unsigned char *buf; size_t len; struct sockaddr_in *from; struct hardware *hfrom; { int length = 0; int offset = 0; int csum_ready = 1; unsigned char ibuf [1536]; unsigned bufix = 0; unsigned paylen; struct iovec iov = { .iov_base = ibuf, .iov_len = sizeof ibuf, }; #ifdef PACKET_AUXDATA /* * We only need cmsgbuf if we are getting the aux data and we * only get the auxdata if it is actually defined */ unsigned char cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))]; struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1, .msg_control = cmsgbuf, .msg_controllen = sizeof(cmsgbuf), }; #else struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1, .msg_control = NULL, .msg_controllen = 0, }; #endif /* PACKET_AUXDATA */ length = recvmsg (interface->rfdesc, &msg, 0); if (length <= 0) return length; #ifdef PACKET_AUXDATA { /* Use auxiliary packet data to: * * a. Weed out extraneous VLAN-tagged packets - If the NIC driver is * handling VLAN encapsulation (i.e. stripping/adding VLAN tags), * then an inbound VLAN packet will be seen twice: Once by * the parent interface (e.g. eth0) with a VLAN tag != 0; and once * by the vlan interface (e.g. eth0.n) with a VLAN tag of 0 (i.e none). * We want to discard the packet sent to the parent and thus respond * only over the vlan interface. (Drivers for Intel PRO/1000 series * NICs perform VLAN encapsulation, while drivers for PCnet series * do not, for example. The linux kernel makes stripped vlan info * visible to user space via CMSG/auxdata, this appears to not be * true for BSD OSs.). NOTE: this is only supported on linux flavors * which define the tpacket_auxdata.tp_vlan_tci. * * b. Determine if checksum is valid for use. It may not be if * checksum offloading is enabled on the interface. */ struct cmsghdr *cmsg; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == SOL_PACKET && cmsg->cmsg_type == PACKET_AUXDATA) { struct tpacket_auxdata *aux = (void *)CMSG_DATA(cmsg); #ifdef VLAN_TCI_PRESENT /* Discard packets with stripped vlan id */ /* VLAN ID is only bottom 12-bits of TCI */ if (aux->tp_vlan_tci & 0x0fff) return 0; #endif csum_ready = ((aux->tp_status & TP_STATUS_CSUMNOTREADY) ? 0 : 1); } } } #endif /* PACKET_AUXDATA */ bufix = 0; /* Decode the physical header... */ offset = decode_hw_header (interface, ibuf, bufix, hfrom); /* If a physical layer checksum failed (dunno of any physical layer that supports this, but WTH), skip this packet. */ if (offset < 0) { return 0; } bufix += offset; length -= offset; /* Decode the IP and UDP headers... */ offset = decode_udp_ip_header (interface, ibuf, bufix, from, (unsigned)length, &paylen, csum_ready); /* If the IP or UDP checksum was bad, skip the packet... */ if (offset < 0) return 0; bufix += offset; length -= offset; if (length < paylen) log_fatal("Internal inconsistency at %s:%d.", MDL); /* Copy out the data in the packet... */ memcpy(buf, &ibuf[bufix], paylen); return paylen; } int can_unicast_without_arp (ip) struct interface_info *ip; { return 1; } int can_receive_unicast_unconfigured (ip) struct interface_info *ip; { return 1; } int supports_multiple_interfaces (ip) struct interface_info *ip; { return 1; } void maybe_setup_fallback () { isc_result_t status; struct interface_info *fbi = (struct interface_info *)0; if (setup_fallback (&fbi, MDL)) { if_register_fallback (fbi); status = omapi_register_io_object ((omapi_object_t *)fbi, if_readsocket, 0, fallback_discard, 0, 0); if (status != ISC_R_SUCCESS) log_fatal ("Can't register I/O handle for \"%s\": %s", fbi -> name, isc_result_totext (status)); interface_dereference (&fbi, MDL); } } #endif #if defined (USE_LPF_RECEIVE) || defined (USE_LPF_HWADDR) void get_hw_addr(const char *name, struct hardware *hw) { int sock; struct ifreq tmp; struct sockaddr *sa; if (strlen(name) >= sizeof(tmp.ifr_name)) { log_fatal("Device name too long: \"%s\"", name); } sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock < 0) { log_fatal("Can't create socket for \"%s\": %m", name); } memset(&tmp, 0, sizeof(tmp)); strcpy(tmp.ifr_name, name); if (ioctl(sock, SIOCGIFHWADDR, &tmp) < 0) { log_fatal("Error getting hardware address for \"%s\": %m", name); } sa = &tmp.ifr_hwaddr; switch (sa->sa_family) { case ARPHRD_ETHER: hw->hlen = 7; hw->hbuf[0] = HTYPE_ETHER; memcpy(&hw->hbuf[1], sa->sa_data, 6); break; case ARPHRD_IEEE802: #ifdef ARPHRD_IEEE802_TR case ARPHRD_IEEE802_TR: #endif /* ARPHRD_IEEE802_TR */ hw->hlen = 7; hw->hbuf[0] = HTYPE_IEEE802; memcpy(&hw->hbuf[1], sa->sa_data, 6); break; case ARPHRD_FDDI: hw->hlen = 7; hw->hbuf[0] = HTYPE_FDDI; memcpy(&hw->hbuf[1], sa->sa_data, 6); break; default: log_fatal("Unsupported device type %ld for \"%s\"", (long int)sa->sa_family, name); } close(sock); } #endif dhcp-4.4.1/common/Makefile.am000644 000765 000024 00000001417 13243313027 016244 0ustar00tmarkstaff000000 000000 AM_CPPFLAGS = -I$(top_srcdir) -DLOCALSTATEDIR='"@localstatedir@"' AM_CFLAGS = $(LDAP_CFLAGS) lib_LIBRARIES = libdhcp.a libdhcp_a_SOURCES = alloc.c bpf.c comapi.c conflex.c ctrace.c dhcp4o6.c \ discover.c dispatch.c dlpi.c dns.c ethernet.c execute.c \ fddi.c icmp.c inet.c lpf.c memory.c nit.c ns_name.c \ options.c packet.c parse.c print.c raw.c resolv.c \ socket.c tables.c tr.c tree.c upf.c man_MANS = dhcp-eval.5 dhcp-options.5 EXTRA_DIST = $(man_MANS) # We want to build this directory first, before descending into tests subdir. # The reason is that ideally the tests should link existing objects from this # directory. That eliminates any discrepancies between tested code and # production code. Sadly, we are not there yet. SUBDIRS = . tests dhcp-4.4.1/common/Makefile.am.in000644 000765 000024 00000001426 13243301226 016647 0ustar00tmarkstaff000000 000000 AM_CPPFLAGS = -I$(top_srcdir) -DLOCALSTATEDIR='"@Q@localstatedir@Q@"' AM_CFLAGS = $(LDAP_CFLAGS) lib_@DHLIBS@ = libdhcp.@A@ libdhcp_@A@_SOURCES = alloc.c bpf.c comapi.c conflex.c ctrace.c dhcp4o6.c \ discover.c dispatch.c dlpi.c dns.c ethernet.c execute.c \ fddi.c icmp.c inet.c lpf.c memory.c nit.c ns_name.c \ options.c packet.c parse.c print.c raw.c resolv.c \ socket.c tables.c tr.c tree.c upf.c man_MANS = dhcp-eval.5 dhcp-options.5 EXTRA_DIST = $(man_MANS) # We want to build this directory first, before descending into tests subdir. # The reason is that ideally the tests should link existing objects from this # directory. That eliminates any discrepancies between tested code and # production code. Sadly, we are not there yet. SUBDIRS = . tests dhcp-4.4.1/common/memory.c000644 000765 000024 00000010742 13243301226 015663 0ustar00tmarkstaff000000 000000 /* memory.c Memory-resident database... */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1995-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" struct group *root_group; group_hash_t *group_name_hash; int (*group_write_hook) (struct group_object *); isc_result_t delete_group (struct group_object *group, int writep) { struct group_object *d; /* The group should exist and be hashed - if not, it's invalid. */ if (group_name_hash) { d = (struct group_object *)0; group_hash_lookup (&d, group_name_hash, group -> name, strlen (group -> name), MDL); } else return DHCP_R_INVALIDARG; if (!d) return DHCP_R_INVALIDARG; /* Also not okay to delete a group that's not the one in the hash table. */ if (d != group) return DHCP_R_INVALIDARG; /* If it's dynamic, and we're deleting it, we can just blow away the hash table entry. */ if ((group -> flags & GROUP_OBJECT_DYNAMIC) && !(group -> flags & GROUP_OBJECT_STATIC)) { group_hash_delete (group_name_hash, group -> name, strlen (group -> name), MDL); } else { group -> flags |= GROUP_OBJECT_DELETED; if (group -> group) group_dereference (&group -> group, MDL); } /* Store the group declaration in the lease file. */ if (writep && group_write_hook) { if (!(*group_write_hook) (group)) return ISC_R_IOERROR; } return ISC_R_SUCCESS; } isc_result_t supersede_group (struct group_object *group, int writep) { struct group_object *t; /* Register the group in the group name hash table, so we can look it up later. */ if (group_name_hash) { t = (struct group_object *)0; group_hash_lookup (&t, group_name_hash, group -> name, strlen (group -> name), MDL); if (t && t != group) { /* If this isn't a dynamic entry, then we need to flag the replacement as not dynamic either - otherwise, if the dynamic entry is deleted later, the static entry will come back next time the server is stopped and restarted. */ if (!(t -> flags & GROUP_OBJECT_DYNAMIC)) group -> flags |= GROUP_OBJECT_STATIC; /* Delete the old object if it hasn't already been deleted. If it has already been deleted, get rid of the hash table entry. This is a legitimate situation - a deleted static object needs to be kept around so we remember it's deleted. */ if (!(t -> flags & GROUP_OBJECT_DELETED)) delete_group (t, 0); else { group_hash_delete (group_name_hash, group -> name, strlen (group -> name), MDL); group_object_dereference (&t, MDL); } } } else { group_new_hash(&group_name_hash, GROUP_HASH_SIZE, MDL); t = (struct group_object *)0; } /* Add the group to the group name hash if it's not already there, and also thread it into the list of dynamic groups if appropriate. */ if (!t) { group_hash_add (group_name_hash, group -> name, strlen (group -> name), group, MDL); } /* Store the group declaration in the lease file. */ if (writep && group_write_hook) { if (!(*group_write_hook) (group)) return ISC_R_IOERROR; } return ISC_R_SUCCESS; } int clone_group (struct group **gp, struct group *group, const char *file, int line) { struct group *g = (struct group *)0; /* Normally gp should contain the null pointer, but for convenience it's permissible to clone a group into itself. */ if (*gp && *gp != group) return 0; if (!group_allocate (&g, file, line)) return 0; if (group == *gp) *gp = (struct group *)0; group_reference (gp, g, file, line); g -> authoritative = group -> authoritative; group_reference (&g -> next, group, file, line); group_dereference (&g, file, line); return 1; } dhcp-4.4.1/common/nit.c000644 000765 000024 00000027017 13243301226 015150 0ustar00tmarkstaff000000 000000 /* nit.c Network Interface Tap (NIT) network interface code, by Ted Lemon with one crucial tidbit of help from Stu Grossmen. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1996-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #if defined (USE_NIT_SEND) || defined (USE_NIT_RECEIVE) #include #include #include #include #include #include #include #include #include #include #include "includes/netinet/ip.h" #include "includes/netinet/udp.h" #include "includes/netinet/if_ether.h" /* Reinitializes the specified interface after an address change. This is not required for packet-filter APIs. */ #ifdef USE_NIT_SEND void if_reinitialize_send (info) struct interface_info *info; { } #endif #ifdef USE_NIT_RECEIVE void if_reinitialize_receive (info) struct interface_info *info; { } #endif /* Called by get_interface_list for each interface that's discovered. Opens a packet filter for each interface and adds it to the select mask. */ int if_register_nit (info) struct interface_info *info; { int sock; char filename[50]; struct ifreq ifr; struct strioctl sio; /* Open a NIT device */ sock = open ("/dev/nit", O_RDWR); if (sock < 0) log_fatal ("Can't open NIT device for %s: %m", info -> name); /* Set the NIT device to point at this interface. */ sio.ic_cmd = NIOCBIND; sio.ic_len = sizeof *(info -> ifp); sio.ic_dp = (char *)(info -> ifp); sio.ic_timout = INFTIM; if (ioctl (sock, I_STR, &sio) < 0) log_fatal ("Can't attach interface %s to nit device: %m", info -> name); /* Get the low-level address... */ sio.ic_cmd = SIOCGIFADDR; sio.ic_len = sizeof ifr; sio.ic_dp = (char *)𝔦 sio.ic_timout = INFTIM; if (ioctl (sock, I_STR, &sio) < 0) log_fatal ("Can't get physical layer address for %s: %m", info -> name); /* XXX code below assumes ethernet interface! */ info -> hw_address.hlen = 7; info -> hw_address.hbuf [0] = ARPHRD_ETHER; memcpy (&info -> hw_address.hbuf [1], ifr.ifr_ifru.ifru_addr.sa_data, 6); if (ioctl (sock, I_PUSH, "pf") < 0) log_fatal ("Can't push packet filter onto NIT for %s: %m", info -> name); return sock; } #endif /* USE_NIT_SEND || USE_NIT_RECEIVE */ #ifdef USE_NIT_SEND void if_register_send (info) struct interface_info *info; { /* If we're using the nit API for sending and receiving, we don't need to register this interface twice. */ #ifndef USE_NIT_RECEIVE struct packetfilt pf; struct strioctl sio; info -> wfdesc = if_register_nit (info); pf.Pf_Priority = 0; pf.Pf_FilterLen = 1; pf.Pf_Filter [0] = ENF_PUSHZERO; /* Set up an NIT filter that rejects everything... */ sio.ic_cmd = NIOCSETF; sio.ic_len = sizeof pf; sio.ic_dp = (char *)&pf; sio.ic_timout = INFTIM; if (ioctl (info -> wfdesc, I_STR, &sio) < 0) log_fatal ("Can't set NIT filter: %m"); #else info -> wfdesc = info -> rfdesc; #endif if (!quiet_interface_discovery) log_info ("Sending on NIT/%s%s%s", print_hw_addr (info -> hw_address.hbuf [0], info -> hw_address.hlen - 1, &info -> hw_address.hbuf [1]), (info -> shared_network ? "/" : ""), (info -> shared_network ? info -> shared_network -> name : "")); } void if_deregister_send (info) struct interface_info *info; { /* If we're using the nit API for sending and receiving, we don't need to register this interface twice. */ #ifndef USE_NIT_RECEIVE close (info -> wfdesc); #endif info -> wfdesc = -1; if (!quiet_interface_discovery) log_info ("Disabling output on NIT/%s%s%s", print_hw_addr (info -> hw_address.hbuf [0], info -> hw_address.hlen - 1, &info -> hw_address.hbuf [1]), (info -> shared_network ? "/" : ""), (info -> shared_network ? info -> shared_network -> name : "")); } #endif /* USE_NIT_SEND */ #ifdef USE_NIT_RECEIVE /* Packet filter program... XXX Changes to the filter program may require changes to the constant offsets used in if_register_send to patch the NIT program! XXX */ #if defined(RELAY_PORT) #error "Relay port is not yet supported for NIT" #endif void if_register_receive (info) struct interface_info *info; { int flag = 1; u_int32_t x; struct packetfilt pf; struct strioctl sio; u_int16_t addr [2]; struct timeval t; /* Open a NIT device and hang it on this interface... */ info -> rfdesc = if_register_nit (info); /* Set the snap length to 0, which means always take the whole packet. */ x = 0; if (ioctl (info -> rfdesc, NIOCSSNAP, &x) < 0) log_fatal ("Can't set NIT snap length on %s: %m", info -> name); /* Set the stream to byte stream mode */ if (ioctl (info -> rfdesc, I_SRDOPT, RMSGN) != 0) log_info ("I_SRDOPT failed on %s: %m", info -> name); #if 0 /* Push on the chunker... */ if (ioctl (info -> rfdesc, I_PUSH, "nbuf") < 0) log_fatal ("Can't push chunker onto NIT STREAM: %m"); /* Set the timeout to zero. */ t.tv_sec = 0; t.tv_usec = 0; if (ioctl (info -> rfdesc, NIOCSTIME, &t) < 0) log_fatal ("Can't set chunk timeout: %m"); #endif /* Ask for no header... */ x = 0; if (ioctl (info -> rfdesc, NIOCSFLAGS, &x) < 0) log_fatal ("Can't set NIT flags on %s: %m", info -> name); /* Set up the NIT filter program. */ /* XXX Unlike the BPF filter program, this one won't work if the XXX IP packet is fragmented or if there are options on the IP XXX header. */ pf.Pf_Priority = 0; pf.Pf_FilterLen = 0; pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHWORD + 6; pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT + ENF_CAND; pf.Pf_Filter [pf.Pf_FilterLen++] = htons (ETHERTYPE_IP); pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT; pf.Pf_Filter [pf.Pf_FilterLen++] = htons (IPPROTO_UDP); pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHWORD + 11; pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT + ENF_AND; pf.Pf_Filter [pf.Pf_FilterLen++] = htons (0xFF); pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_CAND; pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHWORD + 18; pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT + ENF_CAND; pf.Pf_Filter [pf.Pf_FilterLen++] = local_port; /* Install the filter... */ sio.ic_cmd = NIOCSETF; sio.ic_len = sizeof pf; sio.ic_dp = (char *)&pf; sio.ic_timout = INFTIM; if (ioctl (info -> rfdesc, I_STR, &sio) < 0) log_fatal ("Can't set NIT filter on %s: %m", info -> name); if (!quiet_interface_discovery) log_info ("Listening on NIT/%s%s%s", print_hw_addr (info -> hw_address.hbuf [0], info -> hw_address.hlen - 1, &info -> hw_address.hbuf [1]), (info -> shared_network ? "/" : ""), (info -> shared_network ? info -> shared_network -> name : "")); } void if_deregister_receive (info) struct interface_info *info; { /* If we're using the nit API for sending and receiving, we don't need to register this interface twice. */ close (info -> rfdesc); info -> rfdesc = -1; if (!quiet_interface_discovery) log_info ("Disabling input on NIT/%s%s%s", print_hw_addr (info -> hw_address.hbuf [0], info -> hw_address.hlen - 1, &info -> hw_address.hbuf [1]), (info -> shared_network ? "/" : ""), (info -> shared_network ? info -> shared_network -> name : "")); } #endif /* USE_NIT_RECEIVE */ #ifdef USE_NIT_SEND ssize_t send_packet (interface, packet, raw, len, from, to, hto) struct interface_info *interface; struct packet *packet; struct dhcp_packet *raw; size_t len; struct in_addr from; struct sockaddr_in *to; struct hardware *hto; { unsigned hbufp, ibufp; double hh [16]; double ih [1536 / sizeof (double)]; unsigned char *buf = (unsigned char *)ih; struct sockaddr *junk; struct strbuf ctl, data; struct sockaddr_in foo; int result; if (!strcmp (interface -> name, "fallback")) return send_fallback (interface, packet, raw, len, from, to, hto); if (hto == NULL && interface->anycast_mac_addr.hlen) hto = &interface->anycast_mac_addr; /* Start with the sockaddr struct... */ junk = (struct sockaddr *)&hh [0]; hbufp = (((unsigned char *)&junk -> sa_data [0]) - (unsigned char *)&hh[0]); ibufp = 0; /* Assemble the headers... */ assemble_hw_header (interface, (unsigned char *)junk, &hbufp, hto); assemble_udp_ip_header (interface, buf, &ibufp, from.s_addr, to -> sin_addr.s_addr, to -> sin_port, (unsigned char *)raw, len); /* Copy the data into the buffer (yuk). */ memcpy (buf + ibufp, raw, len); /* Set up the sockaddr structure... */ #if USE_SIN_LEN junk -> sa_len = hbufp - 2; /* XXX */ #endif junk -> sa_family = AF_UNSPEC; /* Set up the msg_buf structure... */ ctl.buf = (char *)&hh [0]; ctl.maxlen = ctl.len = hbufp; data.buf = (char *)&ih [0]; data.maxlen = data.len = ibufp + len; result = putmsg (interface -> wfdesc, &ctl, &data, 0); if (result < 0) log_error ("send_packet: %m"); return result; } #endif /* USE_NIT_SEND */ #ifdef USE_NIT_RECEIVE ssize_t receive_packet (interface, buf, len, from, hfrom) struct interface_info *interface; unsigned char *buf; size_t len; struct sockaddr_in *from; struct hardware *hfrom; { int nread; int length = 0; int offset = 0; unsigned char ibuf [1536]; int bufix = 0; unsigned paylen; length = read (interface -> rfdesc, ibuf, sizeof ibuf); if (length <= 0) return length; /* Decode the physical header... */ offset = decode_hw_header (interface, ibuf, bufix, hfrom); /* If a physical layer checksum failed (dunno of any physical layer that supports this, but WTH), skip this packet. */ if (offset < 0) { return 0; } bufix += offset; length -= offset; /* Decode the IP and UDP headers... */ offset = decode_udp_ip_header (interface, ibuf, bufix, from, length, &paylen, 1); /* If the IP or UDP checksum was bad, skip the packet... */ if (offset < 0) return 0; bufix += offset; length -= offset; if (length < paylen) log_fatal("Internal inconsistency at %s:%d.", MDL); /* Copy out the data in the packet... */ memcpy(buf, &ibuf[bufix], paylen); return paylen; } int can_unicast_without_arp (ip) struct interface_info *ip; { return 1; } int can_receive_unicast_unconfigured (ip) struct interface_info *ip; { return 1; } int supports_multiple_interfaces (ip) struct interface_info *ip; { return 1; } void maybe_setup_fallback () { isc_result_t status; struct interface_info *fbi = (struct interface_info *)0; if (setup_fallback (&fbi, MDL)) { if_register_fallback (fbi); status = omapi_register_io_object ((omapi_object_t *)fbi, if_readsocket, 0, fallback_discard, 0, 0); if (status != ISC_R_SUCCESS) log_fatal ("Can't register I/O handle for %s: %s", fbi -> name, isc_result_totext (status)); interface_dereference (&fbi, MDL); } } #endif dhcp-4.4.1/common/ns_name.c000644 000765 000024 00000042120 13243301226 015766 0ustar00tmarkstaff000000 000000 /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1996-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * http://www.isc.org/ */ #include #include #include #include #include #include #include "ns_name.h" #include "arpa/nameser.h" /* Data. */ static const char digits[] = "0123456789"; /* Forward. */ static int special(int); static int printable(int); static int dn_find(const u_char *, const u_char *, const u_char * const *, const u_char * const *); /* Public. */ /* * MRns_name_ntop(src, dst, dstsiz) * Convert an encoded domain name to printable ascii as per RFC1035. * return: * Number of bytes written to buffer, or -1 (with errno set) * notes: * The root is returned as "." * All other domains are returned in non absolute form */ int MRns_name_ntop(const u_char *src, char *dst, size_t dstsiz) { const u_char *cp; char *dn, *eom; u_char c; u_int n; cp = src; dn = dst; eom = dst + dstsiz; while ((n = *cp++) != 0) { if ((n & NS_CMPRSFLGS) != 0) { /* Some kind of compression pointer. */ errno = EMSGSIZE; return (-1); } if (dn != dst) { if (dn >= eom) { errno = EMSGSIZE; return (-1); } *dn++ = '.'; } if (dn + n >= eom) { errno = EMSGSIZE; return (-1); } for ((void)NULL; n > 0; n--) { c = *cp++; if (special(c)) { if (dn + 1 >= eom) { errno = EMSGSIZE; return (-1); } *dn++ = '\\'; *dn++ = (char)c; } else if (!printable(c)) { if (dn + 3 >= eom) { errno = EMSGSIZE; return (-1); } *dn++ = '\\'; *dn++ = digits[c / 100]; *dn++ = digits[(c % 100) / 10]; *dn++ = digits[c % 10]; } else { if (dn >= eom) { errno = EMSGSIZE; return (-1); } *dn++ = (char)c; } } } if (dn == dst) { if (dn >= eom) { errno = EMSGSIZE; return (-1); } *dn++ = '.'; } if (dn >= eom) { errno = EMSGSIZE; return (-1); } *dn++ = '\0'; return (dn - dst); } /* * MRns_name_pton(src, dst, dstsiz) * Convert a ascii string into an encoded domain name as per RFC1035. * return: * -1 if it fails * 1 if string was fully qualified * 0 is string was not fully qualified * notes: * Enforces label and domain length limits. */ int MRns_name_pton(const char *src, u_char *dst, size_t dstsiz) { u_char *label, *bp, *eom; int c, n, escaped; char *cp; escaped = 0; bp = dst; eom = dst + dstsiz; label = bp++; while ((c = *src++) != 0) { if (escaped) { if ((cp = strchr(digits, c)) != NULL) { n = (cp - digits) * 100; if ((c = *src++) == 0 || (cp = strchr(digits, c)) == NULL) { errno = EMSGSIZE; return (-1); } n += (cp - digits) * 10; if ((c = *src++) == 0 || (cp = strchr(digits, c)) == NULL) { errno = EMSGSIZE; return (-1); } n += (cp - digits); if (n > 255) { errno = EMSGSIZE; return (-1); } c = n; } escaped = 0; } else if (c == '\\') { escaped = 1; continue; } else if (c == '.') { c = (bp - label - 1); if ((c & NS_CMPRSFLGS) != 0) { /* Label too big. */ errno = EMSGSIZE; return (-1); } if (label >= eom) { errno = EMSGSIZE; return (-1); } *label = c; /* Fully qualified ? */ if (*src == '\0') { if (c != 0) { if (bp >= eom) { errno = EMSGSIZE; return (-1); } *bp++ = '\0'; } if ((bp - dst) > MAXCDNAME) { errno = EMSGSIZE; return (-1); } return (1); } if (c == 0 || *src == '.') { errno = EMSGSIZE; return (-1); } label = bp++; continue; } if (bp >= eom) { errno = EMSGSIZE; return (-1); } *bp++ = (u_char)c; } c = (bp - label - 1); if ((c & NS_CMPRSFLGS) != 0) { /* Label too big. */ errno = EMSGSIZE; return (-1); } if (label >= eom) { errno = EMSGSIZE; return (-1); } *label = c; if (c != 0) { if (bp >= eom) { errno = EMSGSIZE; return (-1); } *bp++ = 0; } if ((bp - dst) > MAXCDNAME) { /* src too big */ errno = EMSGSIZE; return (-1); } return (0); } /* * MRns_name_ntol(src, dst, dstsiz) * Convert a network strings labels into all lowercase. * return: * Number of bytes written to buffer, or -1 (with errno set) * notes: * Enforces label and domain length limits. */ int MRns_name_ntol(const u_char *src, u_char *dst, size_t dstsiz) { const u_char *cp; u_char *dn, *eom; u_char c; u_int n; cp = src; dn = dst; eom = dst + dstsiz; if (dn >= eom) { errno = EMSGSIZE; return (-1); } while ((n = *cp++) != 0) { if ((n & NS_CMPRSFLGS) != 0) { /* Some kind of compression pointer. */ errno = EMSGSIZE; return (-1); } *dn++ = n; if (dn + n >= eom) { errno = EMSGSIZE; return (-1); } for ((void)NULL; n > 0; n--) { c = *cp++; if (isupper(c)) *dn++ = tolower(c); else *dn++ = c; } } *dn++ = '\0'; return (dn - dst); } /* * MRns_name_unpack(msg, eom, src, dst, dstsiz) * Unpack a domain name from a message, source may be compressed. * return: * -1 if it fails, or consumed octets if it succeeds. */ int MRns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src, u_char *dst, size_t dstsiz) { const u_char *srcp, *dstlim; u_char *dstp; unsigned n; int len; int checked; len = -1; checked = 0; dstp = dst; srcp = src; dstlim = dst + dstsiz; if (srcp < msg || srcp >= eom) { errno = EMSGSIZE; return (-1); } /* Fetch next label in domain name. */ while ((n = *srcp++) != 0) { /* Check for indirection. */ switch (n & NS_CMPRSFLGS) { case 0: /* Limit checks. */ if (dstp + n + 1 >= dstlim || srcp + n >= eom) { errno = EMSGSIZE; return (-1); } checked += n + 1; *dstp++ = n; memcpy(dstp, srcp, n); dstp += n; srcp += n; break; case NS_CMPRSFLGS: if (srcp >= eom) { errno = EMSGSIZE; return (-1); } if (len < 0) len = srcp - src + 1; srcp = msg + (((n & 0x3f) << 8) | (*srcp & 0xff)); if (srcp < msg || srcp >= eom) { /* Out of range. */ errno = EMSGSIZE; return (-1); } checked += 2; /* * Check for loops in the compressed name; * if we've looked at the whole message, * there must be a loop. */ if (checked >= eom - msg) { errno = EMSGSIZE; return (-1); } break; default: errno = EMSGSIZE; return (-1); /* flag error */ } } *dstp = '\0'; if (len < 0) len = srcp - src; return (len); } /* * MRns_name_pack(src, dst, dstsiz, dnptrs, lastdnptr) * Pack domain name 'domain' into 'comp_dn'. * return: * Size of the compressed name, or -1. * notes: * 'dnptrs' is an array of pointers to previous compressed names. * dnptrs[0] is a pointer to the beginning of the message. The array * ends with NULL. * 'lastdnptr' is a pointer to the end of the array pointed to * by 'dnptrs'. * Side effects: * The list of pointers in dnptrs is updated for labels inserted into * the message as we compress the name. If 'dnptr' is NULL, we don't * try to compress names. If 'lastdnptr' is NULL, we don't update the * list. */ int MRns_name_pack(const u_char *src, u_char *dst, unsigned dstsiz, const u_char **dnptrs, const u_char **lastdnptr) { u_char *dstp; const u_char **cpp, **lpp, *eob, *msg; const u_char *srcp; unsigned n; int l; srcp = src; dstp = dst; eob = dstp + dstsiz; lpp = cpp = NULL; if (dnptrs != NULL) { if ((msg = *dnptrs++) != NULL) { for (cpp = dnptrs; *cpp != NULL; cpp++) (void)NULL; lpp = cpp; /* end of list to search */ } } else msg = NULL; /* make sure the domain we are about to add is legal */ l = 0; do { n = *srcp; if ((n & NS_CMPRSFLGS) != 0) { errno = EMSGSIZE; return (-1); } l += n + 1; if (l > MAXCDNAME) { errno = EMSGSIZE; return (-1); } srcp += n + 1; } while (n != 0); /* from here on we need to reset compression pointer array on error */ srcp = src; do { /* Look to see if we can use pointers. */ n = *srcp; if (n != 0 && msg != NULL) { l = dn_find(srcp, msg, (const u_char * const *)dnptrs, (const u_char * const *)lpp); if (l >= 0) { if (dstp + 1 >= eob) { goto cleanup; } *dstp++ = (l >> 8) | NS_CMPRSFLGS; *dstp++ = l % 256; return (dstp - dst); } /* Not found, save it. */ if (lastdnptr != NULL && cpp < lastdnptr - 1 && (dstp - msg) < 0x4000) { *cpp++ = dstp; *cpp = NULL; } } /* copy label to buffer */ if (n & NS_CMPRSFLGS) { /* Should not happen. */ goto cleanup; } if (dstp + 1 + n >= eob) { goto cleanup; } memcpy(dstp, srcp, n + 1); srcp += n + 1; dstp += n + 1; } while (n != 0); if (dstp > eob) { cleanup: if (msg != NULL) *lpp = NULL; errno = EMSGSIZE; return (-1); } return (dstp - dst); } /* * MRns_name_uncompress(msg, eom, src, dst, dstsiz) * Expand compressed domain name to presentation format. * return: * Number of bytes read out of `src', or -1 (with errno set). * note: * Root domain returns as "." not "". */ int MRns_name_uncompress(const u_char *msg, const u_char *eom, const u_char *src, char *dst, size_t dstsiz) { u_char tmp[NS_MAXCDNAME]; int n; if ((n = MRns_name_unpack(msg, eom, src, tmp, sizeof tmp)) == -1) return (-1); if (MRns_name_ntop(tmp, dst, dstsiz) == -1) return (-1); return (n); } /* * MRns_name_compress(src, dst, dstsiz, dnptrs, lastdnptr) * Compress a domain name into wire format, using compression pointers. * return: * Number of bytes consumed in `dst' or -1 (with errno set). * notes: * 'dnptrs' is an array of pointers to previous compressed names. * dnptrs[0] is a pointer to the beginning of the message. * The list ends with NULL. 'lastdnptr' is a pointer to the end of the * array pointed to by 'dnptrs'. Side effect is to update the list of * pointers for labels inserted into the message as we compress the name. * If 'dnptr' is NULL, we don't try to compress names. If 'lastdnptr' * is NULL, we don't update the list. */ int MRns_name_compress(const char *src, u_char *dst, size_t dstsiz, const u_char **dnptrs, const u_char **lastdnptr) { u_char tmp[NS_MAXCDNAME]; if (MRns_name_pton(src, tmp, sizeof tmp) == -1) return (-1); return (MRns_name_pack(tmp, dst, dstsiz, dnptrs, lastdnptr)); } /* * MRns_name_skip(ptrptr, eom) * Advance *ptrptr to skip over the compressed name it points at. * return: * 0 on success, -1 (with errno set) on failure. */ int MRns_name_skip(const u_char **ptrptr, const u_char *eom) { const u_char *cp; u_int n; cp = *ptrptr; while (cp < eom && (n = *cp++) != 0) { /* Check for indirection. */ switch (n & NS_CMPRSFLGS) { case 0: /* normal case, n == len */ cp += n; continue; case NS_CMPRSFLGS: /* indirection */ cp++; break; default: /* illegal type */ errno = EMSGSIZE; return (-1); } break; } if (cp > eom) { errno = EMSGSIZE; return (-1); } *ptrptr = cp; return (0); } /* Private. */ /* * special(ch) * Thinking in noninternationalized USASCII (per the DNS spec), * is this characted special ("in need of quoting") ? * return: * boolean. */ static int special(int ch) { switch (ch) { case 0x22: /* '"' */ case 0x2E: /* '.' */ case 0x3B: /* ';' */ case 0x5C: /* '\\' */ /* Special modifiers in zone files. */ case 0x40: /* '@' */ case 0x24: /* '$' */ return (1); default: return (0); } } /* * printable(ch) * Thinking in noninternationalized USASCII (per the DNS spec), * is this character visible and not a space when printed ? * return: * boolean. */ static int printable(int ch) { return (ch > 0x20 && ch < 0x7f); } /* * Thinking in noninternationalized USASCII (per the DNS spec), * convert this character to lower case if it's upper case. */ static int mklower(int ch) { if (ch >= 0x41 && ch <= 0x5A) return (ch + 0x20); return (ch); } /* * dn_find(domain, msg, dnptrs, lastdnptr) * Search for the counted-label name in an array of compressed names. * return: * offset from msg if found, or -1. * notes: * dnptrs is the pointer to the first name on the list, * not the pointer to the start of the message. */ static int dn_find(const u_char *domain, const u_char *msg, const u_char * const *dnptrs, const u_char * const *lastdnptr) { const u_char *dn, *cp, *sp; const u_char * const *cpp; u_int n; for (cpp = dnptrs; cpp < lastdnptr; cpp++) { dn = domain; sp = cp = *cpp; while ((n = *cp++) != 0) { /* * check for indirection */ switch (n & NS_CMPRSFLGS) { case 0: /* normal case, n == len */ if (n != *dn++) goto next; for ((void)NULL; n > 0; n--) if (mklower(*dn++) != mklower(*cp++)) goto next; /* Is next root for both ? */ if (*dn == '\0' && *cp == '\0') return (sp - msg); if (*dn) continue; goto next; case NS_CMPRSFLGS: /* indirection */ cp = msg + (((n & 0x3f) << 8) | *cp); break; default: /* illegal type */ errno = EMSGSIZE; return (-1); } } next: ; } errno = ENOENT; return (-1); } /*! * \brief Creates a string of comma-separated domain-names from a * compressed list * * Produces a null-terminated string of comma-separated domain-names from * a buffer containing a compressed list of domain-names. The names will * be dotted and without enclosing quotes. For example: * If a compressed list contains the follwoing two domain names: * * a. one.two.com * b. three.four.com * * The compressed data will look like this: * * 03 6f 6e 65 03 74 77 6f 03 63 6f 6d 00 05 74 68 * 72 65 65 04 66 6f 75 72 c0 08 * * and will decompress into: * * one.two.com,three.four.com * * \param buf - buffer containing the compressed list of domain-names * \param buflen - length of compressed list of domain-names * \param dst_buf - buffer to receive the decompressed list * \param dst_size - size of the destination buffer * * \return the length of the decompressed string when successful, -1 on * error. */ int MRns_name_uncompress_list(const unsigned char* buf, int buflen, char* dst_buf, size_t dst_size) { const unsigned char* src = buf; char* dst = dst_buf; int consumed = 1; int dst_remaining = dst_size; int added_len = 0; int first_pass = 1; if (!buf || buflen == 0 || *buf == 0x00) { /* nothing to do */ *dst = 0; return (0); } while ((consumed > 0) && (src < (buf + buflen))) { if (dst_remaining <= 0) { errno = EMSGSIZE; return (-1); } if (!first_pass) { *dst++ = ','; *dst = '\0'; dst_remaining--; } consumed = MRns_name_uncompress(buf, buf + buflen, src, dst, dst_remaining); if (consumed < 0) { return (-1); } src += consumed; added_len = strlen(dst); dst_remaining -= added_len; dst += added_len; first_pass = 0; } *dst='\0'; /* return the length of the uncompressed list string */ return (strlen(dst_buf)); } /*! * \brief Creates a compressed list from a string of comma-separated * domain-names * * Produces a buffer containing a compressed data version of a list of * domain-names extracted from a comma-separated string. Given a string * containing: * * one.two.com,three.four.com * * It will compress this into: * * 03 6f 6e 65 03 74 77 6f 03 63 6f 6d 00 05 74 68 * 72 65 65 04 66 6f 75 72 c0 08 * * \param buf - buffer containing the uncompressed string of domain-names * \param buflen - length of uncompressed string of domain-names * \param compbuf - buffer to receive the compressed list * \param compbuf_size - size of the compression buffer * * \return the length of the compressed data when successful, -1 on error. */ int MRns_name_compress_list(const char* buf, int buflen, unsigned char* compbuf, size_t compbuf_size) { char cur_name[NS_MAXCDNAME]; const unsigned char *dnptrs[256], **lastdnptr; const char* src; const char* src_end; unsigned clen = 0; int result = 0; memset(compbuf, 0, compbuf_size); memset(dnptrs, 0, sizeof(dnptrs)); dnptrs[0] = compbuf; lastdnptr = &dnptrs[255]; src = buf; src_end = buf + buflen; while (src < src_end) { char *comma = strchr(src, ','); int copylen = ((comma != NULL) ? comma - src : strlen(src)); if (copylen > (sizeof(cur_name) - 1)) { errno = EMSGSIZE; return (-1); } memcpy(cur_name, src, copylen); cur_name[copylen] = '\0'; src += copylen + 1; result = MRns_name_compress(cur_name, compbuf + clen, compbuf_size - clen, dnptrs, lastdnptr); if (result < 0) { return (-1); } clen += result; } /* return size of compressed list */ return(clen); } dhcp-4.4.1/common/options.c000644 000765 000024 00000363134 13243301226 016054 0ustar00tmarkstaff000000 000000 /* options.c DHCP options parsing and reassembly. */ /* * Copyright (c) 2004-2018 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1995-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #define DHCP_OPTION_DATA #include "dhcpd.h" #include #include struct option *vendor_cfg_option; static int pretty_text(char **, char *, const unsigned char **, const unsigned char *, int); static int pretty_domain(char **, char *, const unsigned char **, const unsigned char *); static int prepare_option_buffer(struct universe *universe, struct buffer *bp, unsigned char *buffer, unsigned length, unsigned code, int terminatep, struct option_cache **opp); /* Parse all available options out of the specified packet. */ /* Note, the caller is responsible for allocating packet->options. */ int parse_options (packet) struct packet *packet; { struct option_cache *op = NULL; /* If we don't see the magic cookie, there's nothing to parse. */ if (memcmp (packet -> raw -> options, DHCP_OPTIONS_COOKIE, 4)) { packet -> options_valid = 0; return 1; } /* Go through the options field, up to the end of the packet or the End field. */ if (!parse_option_buffer (packet -> options, &packet -> raw -> options [4], (packet -> packet_length - DHCP_FIXED_NON_UDP - 4), &dhcp_universe)) { /* STSN servers have a bug where they send a mangled domain-name option, and whatever is beyond that in the packet is junk. Microsoft clients accept this, which is probably why whoever implemented the STSN server isn't aware of the problem yet. To work around this, we will accept corrupt packets from the server if they contain a valid DHCP_MESSAGE_TYPE option, but will not accept any corrupt client packets (the ISC DHCP server is sufficiently widely used that it is probably beneficial for it to be picky) and will not accept packets whose type can't be determined. */ if ((op = lookup_option (&dhcp_universe, packet -> options, DHO_DHCP_MESSAGE_TYPE))) { if (!op -> data.data || (op -> data.data [0] != DHCPOFFER && op -> data.data [0] != DHCPACK && op -> data.data [0] != DHCPNAK)) return 0; } else return 0; } /* If we parsed a DHCP Option Overload option, parse more options out of the buffer(s) containing them. */ if ((op = lookup_option (&dhcp_universe, packet -> options, DHO_DHCP_OPTION_OVERLOAD))) { if (op -> data.data [0] & 1) { if (!parse_option_buffer (packet -> options, (unsigned char *)packet -> raw -> file, sizeof packet -> raw -> file, &dhcp_universe)) return 0; } if (op -> data.data [0] & 2) { if (!parse_option_buffer (packet -> options, (unsigned char *)packet -> raw -> sname, sizeof packet -> raw -> sname, &dhcp_universe)) return 0; } } packet -> options_valid = 1; return 1; } /* Parse options out of the specified buffer, storing addresses of option * values in packet->options. */ int parse_option_buffer (options, buffer, length, universe) struct option_state *options; const unsigned char *buffer; unsigned length; struct universe *universe; { unsigned len, offset; unsigned code; struct option_cache *op = NULL, *nop = NULL; struct buffer *bp = (struct buffer *)0; struct option *option = NULL; char *reason = "general failure"; if (!buffer_allocate (&bp, length, MDL)) { log_error ("no memory for option buffer."); return 0; } memcpy (bp -> data, buffer, length); for (offset = 0; (offset + universe->tag_size) <= length && (code = universe->get_tag(buffer + offset)) != universe->end; ) { offset += universe->tag_size; /* Pad options don't have a length - just skip them. */ if (code == DHO_PAD) continue; /* Don't look for length if the buffer isn't that big. */ if ((offset + universe->length_size) > length) { reason = "code tag at end of buffer - missing " "length field"; goto bogus; } /* All other fields (except PAD and END handled above) * have a length field, unless it's a DHCPv6 zero-length * options space (eg any of the enterprise-id'd options). * * Zero-length-size option spaces basically consume the * entire options buffer, so have at it. */ if (universe->get_length != NULL) len = universe->get_length(buffer + offset); else if (universe->length_size == 0) len = length - universe->tag_size; else { log_fatal("Improperly configured option space(%s): " "may not have a nonzero length size " "AND a NULL get_length function.", universe->name); /* Silence compiler warnings. */ return 0; } offset += universe->length_size; option_code_hash_lookup(&option, universe->code_hash, &code, 0, MDL); /* If the length is outrageous, the options are bad. */ if (offset + len > length) { /* Avoid reference count overflow */ option_dereference(&option, MDL); reason = "option length exceeds option buffer length"; bogus: log_error("parse_option_buffer: malformed option " "%s.%s (code %u): %s.", universe->name, option ? option->name : "", code, reason); buffer_dereference (&bp, MDL); return 0; } /* If the option contains an encapsulation, parse it. In any case keep the raw data as well. (Previous to 4.4.0 we only kept the raw data if the parse failed, the option wasn't an encapsulation (by far the most common case), or the option wasn't entirely an encapsulation */ if (option && (option->format[0] == 'e' || option->format[0] == 'E')) { (void) parse_encapsulated_suboptions(options, option, bp->data + offset, len, universe, NULL); } if (universe == &dhcp_universe && code == DHO_HOST_NAME && len == 0) { /* non-compliant clients can send it * we'll just drop it and go on */ log_debug ("Ignoring empty DHO_HOST_NAME option"); option_dereference(&option, MDL); offset += len; continue; } op = lookup_option(universe, options, code); if (op == NULL) { /* If we don't have an option create one */ if (save_option_buffer(universe, options, bp, bp->data + offset, len, code, 1) == 0) { log_error("parse_option_buffer: " "save_option_buffer failed"); buffer_dereference(&bp, MDL); return (0); } } else if (universe->concat_duplicates) { /* If we do have an option either concat with what is there ...*/ struct data_string new; memset(&new, 0, sizeof new); if (!buffer_allocate(&new.buffer, op->data.len + len, MDL)) { log_error("parse_option_buffer: No memory."); buffer_dereference(&bp, MDL); return (0); } /* Copy old option to new data object. */ memcpy(new.buffer->data, op->data.data, op->data.len); /* Concat new option behind old. */ memcpy(new.buffer->data + op->data.len, bp->data + offset, len); new.len = op->data.len + len; new.data = new.buffer->data; /* Save new concat'd object. */ data_string_forget(&op->data, MDL); data_string_copy(&op->data, &new, MDL); data_string_forget(&new, MDL); } else { /* ... or we must append this statement onto the * end of the list. */ while (op->next != NULL) op = op->next; if (!option_cache_allocate(&nop, MDL)) { log_error("parse_option_buffer: No memory."); buffer_dereference(&bp, MDL); return (0); } option_reference(&nop->option, op->option, MDL); nop->data.buffer = NULL; buffer_reference(&nop->data.buffer, bp, MDL); nop->data.data = bp->data + offset; nop->data.len = len; option_cache_reference(&op->next, nop, MDL); option_cache_dereference(&nop, MDL); } option_dereference(&option, MDL); offset += len; } buffer_dereference (&bp, MDL); return (1); } /* If an option in an option buffer turns out to be an encapsulation, figure out what to do. If we don't know how to de-encapsulate it, or it's not well-formed, return zero; otherwise, return 1, indicating that we succeeded in de-encapsulating it. */ struct universe *find_option_universe (struct option *eopt, const char *uname) { int i; char *s, *t; struct universe *universe = (struct universe *)0; /* Look for the E option in the option format. */ s = strchr (eopt -> format, 'E'); if (!s) { log_error ("internal encapsulation format error 1."); return 0; } /* Look for the universe name in the option format. */ t = strchr (++s, '.'); /* If there was no trailing '.', or there's something after the trailing '.', the option is bogus and we can't use it. */ if (!t || t [1]) { log_error ("internal encapsulation format error 2."); return 0; } if (t == s && uname) { for (i = 0; i < universe_count; i++) { if (!strcmp (universes [i] -> name, uname)) { universe = universes [i]; break; } } } else if (t != s) { for (i = 0; i < universe_count; i++) { if (strlen (universes [i] -> name) == t - s && !memcmp (universes [i] -> name, s, (unsigned)(t - s))) { universe = universes [i]; break; } } } return universe; } /* If an option in an option buffer turns out to be an encapsulation, figure out what to do. If we don't know how to de-encapsulate it, or it's not well-formed, return zero; otherwise, return 1, indicating that we succeeded in de-encapsulating it. */ int parse_encapsulated_suboptions (struct option_state *options, struct option *eopt, const unsigned char *buffer, unsigned len, struct universe *eu, const char *uname) { int i; struct universe *universe = find_option_universe (eopt, uname); /* If we didn't find the universe, we can't do anything with it right now (e.g., we can't decode vendor options until we've decoded the packet and executed the scopes that it matches). */ if (!universe) return 0; /* If we don't have a decoding function for it, we can't decode it. */ if (!universe -> decode) return 0; i = (*universe -> decode) (options, buffer, len, universe); /* If there is stuff before the suboptions, we have to keep it. */ if (eopt -> format [0] != 'E') return 0; /* Otherwise, return the status of the decode function. */ return i; } int fqdn_universe_decode (struct option_state *options, const unsigned char *buffer, unsigned length, struct universe *u) { struct buffer *bp = (struct buffer *)0; /* FQDN options have to be at least four bytes long. */ if (length < 3) return 0; /* Save the contents of the option in a buffer. */ if (!buffer_allocate (&bp, length + 4, MDL)) { log_error ("no memory for option buffer."); return 0; } memcpy (&bp -> data [3], buffer + 1, length - 1); if (buffer [0] & 4) /* encoded */ bp -> data [0] = 1; else bp -> data [0] = 0; if (!save_option_buffer(&fqdn_universe, options, bp, bp->data, 1, FQDN_ENCODED, 0)) { bad: buffer_dereference (&bp, MDL); return 0; } if (buffer [0] & 1) /* server-update */ bp -> data [2] = 1; else bp -> data [2] = 0; if (buffer [0] & 2) /* no-client-update */ bp -> data [1] = 1; else bp -> data [1] = 0; /* XXX Ideally we should store the name in DNS format, so if the XXX label isn't in DNS format, we convert it to DNS format, XXX rather than converting labels specified in DNS format to XXX the plain ASCII representation. But that's hard, so XXX not now. */ /* Not encoded using DNS format? */ if (!bp -> data [0]) { unsigned i; /* Some broken clients NUL-terminate this option. */ if (buffer [length - 1] == 0) { --length; bp -> data [1] = 1; } /* Determine the length of the hostname component of the name. If the name contains no '.' character, it represents a non-qualified label. */ for (i = 3; i < length && buffer [i] != '.'; i++); i -= 3; /* Note: If the client sends a FQDN, the first '.' will be used as a NUL terminator for the hostname. */ if (i && (!save_option_buffer(&fqdn_universe, options, bp, &bp->data[5], i, FQDN_HOSTNAME, 0))) goto bad; /* Note: If the client sends a single label, the FQDN_DOMAINNAME option won't be set. */ if (length > 4 + i && (!save_option_buffer(&fqdn_universe, options, bp, &bp -> data[6 + i], length - 4 - i, FQDN_DOMAINNAME, 1))) goto bad; /* Also save the whole name. */ if (length > 3) { if (!save_option_buffer(&fqdn_universe, options, bp, &bp -> data [5], length - 3, FQDN_FQDN, 1)) goto bad; } } else { unsigned len; unsigned total_len = 0; unsigned first_len = 0; int terminated = 0; unsigned char *s; s = &bp -> data[5]; while (s < &bp -> data[0] + length + 2) { len = *s; if (len > 63) { log_info ("fancy bits in fqdn option"); return 0; } if (len == 0) { terminated = 1; break; } if (s + len > &bp -> data [0] + length + 3) { log_info ("fqdn tag longer than buffer"); return 0; } if (first_len == 0) { first_len = len; } *s = '.'; s += len + 1; total_len += len + 1; } /* We wind up with a length that's one too many because we shouldn't increment for the last label, but there's no way to tell we're at the last label until we exit the loop. :'*/ if (total_len > 0) total_len--; if (!terminated) { first_len = total_len; } if (first_len > 0 && !save_option_buffer(&fqdn_universe, options, bp, &bp -> data[6], first_len, FQDN_HOSTNAME, 0)) goto bad; if (total_len > 0 && first_len != total_len) { if (!save_option_buffer(&fqdn_universe, options, bp, &bp->data[6 + first_len], total_len - first_len, FQDN_DOMAINNAME, 1)) goto bad; } if (total_len > 0) if (!save_option_buffer (&fqdn_universe, options, bp, &bp -> data [6], total_len, FQDN_FQDN, 1)) goto bad; } if (!save_option_buffer (&fqdn_universe, options, bp, &bp -> data [1], 1, FQDN_NO_CLIENT_UPDATE, 0)) goto bad; if (!save_option_buffer (&fqdn_universe, options, bp, &bp -> data [2], 1, FQDN_SERVER_UPDATE, 0)) goto bad; if (!save_option_buffer (&fqdn_universe, options, bp, &bp -> data [3], 1, FQDN_RCODE1, 0)) goto bad; if (!save_option_buffer (&fqdn_universe, options, bp, &bp -> data [4], 1, FQDN_RCODE2, 0)) goto bad; buffer_dereference (&bp, MDL); return 1; } /* * Load all options into a buffer, and then split them out into the three * separate fields in the dhcp packet (options, file, and sname) where * options can be stored. * * returns 0 on error, length of packet on success */ int cons_options(struct packet *inpacket, struct dhcp_packet *outpacket, struct lease *lease, struct client_state *client_state, int mms, struct option_state *in_options, struct option_state *cfg_options, struct binding_scope **scope, int overload_avail, int terminate, int bootpp, struct data_string *prl, const char *vuname) { #define PRIORITY_COUNT 300 unsigned priority_list[PRIORITY_COUNT]; int priority_len; unsigned char buffer[4096], agentopts[1024]; unsigned index = 0; unsigned mb_size = 0, mb_max = 0; unsigned option_size = 0, agent_size = 0; unsigned length; int i; struct option_cache *op; struct data_string ds; pair pp, *hash; int overload_used = 0; int of1 = 0, of2 = 0; memset(&ds, 0, sizeof ds); /* * If there's a Maximum Message Size option in the incoming packet * and no alternate maximum message size has been specified, or * if the one specified in the packet is shorter than the * alternative, take the one in the packet. */ if (inpacket && (op = lookup_option(&dhcp_universe, inpacket->options, DHO_DHCP_MAX_MESSAGE_SIZE)) && (evaluate_option_cache(&ds, inpacket, lease, client_state, in_options, cfg_options, scope, op, MDL) != 0)) { if (ds.len >= sizeof (u_int16_t)) { i = getUShort(ds.data); if(!mms || (i < mms)) mms = i; } data_string_forget(&ds, MDL); } /* * If the client has provided a maximum DHCP message size, * use that, up to the MTU limit. Otherwise, if it's BOOTP, * only 64 bytes; otherwise use up to the minimum IP MTU size * (576 bytes). * * XXX if a BOOTP client specifies a max message size, we will * honor it. */ if (mms) { if (mms < DHCP_MTU_MIN) /* Enforce minimum packet size, per RFC 2132 */ mb_size = DHCP_MIN_OPTION_LEN; else if (mms > DHCP_MTU_MAX) /* * TODO: Packets longer than 1500 bytes really * should be allowed, but it requires upstream * changes to the way the packet is allocated. For * now, we forbid them. They won't be needed very * often anyway. */ mb_size = DHCP_MAX_OPTION_LEN; else mb_size = mms - DHCP_FIXED_LEN; } else if (bootpp) { mb_size = 64; if (inpacket != NULL && (inpacket->packet_length >= 64 + DHCP_FIXED_NON_UDP)) mb_size = inpacket->packet_length - DHCP_FIXED_NON_UDP; } else mb_size = DHCP_MIN_OPTION_LEN; /* * If answering a client message, see whether any relay agent * options were included with the message. If so, save them * to copy back in later, and make space in the main buffer * to accommodate them */ if (client_state == NULL) { priority_list[0] = DHO_DHCP_AGENT_OPTIONS; priority_len = 1; agent_size = store_options(NULL, agentopts, 0, sizeof(agentopts), inpacket, lease, client_state, in_options, cfg_options, scope, priority_list, priority_len, 0, 0, 0, NULL); mb_size += agent_size; if (mb_size > DHCP_MAX_OPTION_LEN) mb_size = DHCP_MAX_OPTION_LEN; } /* * Set offsets for buffer data to be copied into filename * and servername fields */ if (mb_size > agent_size) mb_max = mb_size - agent_size; else mb_max = mb_size; if (overload_avail & 1) { of1 = mb_max; mb_max += DHCP_FILE_LEN; } if (overload_avail & 2) { of2 = mb_max; mb_max += DHCP_SNAME_LEN; } /* * Preload the option priority list with protocol-mandatory options. * This effectively gives these options the highest priority. * This provides the order for any available options, the option * must be in the option cache in order to actually be included. */ priority_len = 0; priority_list[priority_len++] = DHO_DHCP_MESSAGE_TYPE; priority_list[priority_len++] = DHO_DHCP_SERVER_IDENTIFIER; priority_list[priority_len++] = DHO_DHCP_LEASE_TIME; priority_list[priority_len++] = DHO_DHCP_RENEWAL_TIME; priority_list[priority_len++] = DHO_DHCP_REBINDING_TIME; priority_list[priority_len++] = DHO_DHCP_MESSAGE; priority_list[priority_len++] = DHO_DHCP_REQUESTED_ADDRESS; priority_list[priority_len++] = DHO_ASSOCIATED_IP; if (prl != NULL && prl->len > 0) { if ((op = lookup_option(&dhcp_universe, cfg_options, DHO_SUBNET_SELECTION))) { if (priority_len < PRIORITY_COUNT) priority_list[priority_len++] = DHO_SUBNET_SELECTION; } /* If echo-client-id is on, then we add client identifier to * the priority_list. This way we'll send it whether or not it * is in the PRL. */ if ((inpacket != NULL) && (priority_len < PRIORITY_COUNT) && (inpacket->sv_echo_client_id == ISC_TRUE)) { priority_list[priority_len++] = DHO_DHCP_CLIENT_IDENTIFIER; } data_string_truncate(prl, (PRIORITY_COUNT - priority_len)); /* * Copy the client's PRL onto the priority_list after our high * priority header. */ for (i = 0; i < prl->len; i++) { /* * Prevent client from changing order of delivery * of relay agent information option. */ if (prl->data[i] != DHO_DHCP_AGENT_OPTIONS) priority_list[priority_len++] = prl->data[i]; } /* * If the client doesn't request the FQDN option explicitly, * to indicate priority, consider it lowest priority. Fit * in the packet if there is space. Note that the option * may only be included if the client supplied one. */ if ((inpacket != NULL) && (priority_len < PRIORITY_COUNT) && (lookup_option(&fqdn_universe, inpacket->options, FQDN_ENCODED) != NULL)) priority_list[priority_len++] = DHO_FQDN; /* * Some DHCP Servers will give the subnet-mask option if * it is not on the parameter request list - so some client * implementations have come to rely on this - so we will * also make sure we supply this, at lowest priority. * * This is only done in response to DHCPDISCOVER or * DHCPREQUEST messages, to avoid providing the option on * DHCPINFORM or DHCPLEASEQUERY responses (if the client * didn't request it). */ if ((inpacket != NULL) && (priority_len < PRIORITY_COUNT) && ((inpacket->packet_type == DHCPDISCOVER) || (inpacket->packet_type == DHCPREQUEST))) priority_list[priority_len++] = DHO_SUBNET_MASK; } else { /* * First, hardcode some more options that ought to be * sent first...these are high priority to have in the * packet. */ priority_list[priority_len++] = DHO_SUBNET_MASK; priority_list[priority_len++] = DHO_ROUTERS; priority_list[priority_len++] = DHO_DOMAIN_NAME_SERVERS; priority_list[priority_len++] = DHO_HOST_NAME; priority_list[priority_len++] = DHO_FQDN; /* * Append a list of the standard DHCP options from the * standard DHCP option space. Actually, if a site * option space hasn't been specified, we wind up * treating the dhcp option space as the site option * space, and the first for loop is skipped, because * it's slightly more general to do it this way, * taking the 1Q99 DHCP futures work into account. */ if (cfg_options->site_code_min) { for (i = 0; i < OPTION_HASH_SIZE; i++) { hash = cfg_options->universes[dhcp_universe.index]; if (hash) { for (pp = hash[i]; pp; pp = pp->cdr) { op = (struct option_cache *)(pp->car); if (op->option->code < cfg_options->site_code_min && priority_len < PRIORITY_COUNT && op->option->code != DHO_DHCP_AGENT_OPTIONS) priority_list[priority_len++] = op->option->code; } } } } /* * Now cycle through the site option space, or if there * is no site option space, we'll be cycling through the * dhcp option space. */ for (i = 0; i < OPTION_HASH_SIZE; i++) { hash = cfg_options->universes[cfg_options->site_universe]; if (hash != NULL) for (pp = hash[i]; pp; pp = pp->cdr) { op = (struct option_cache *)(pp->car); if (op->option->code >= cfg_options->site_code_min && priority_len < PRIORITY_COUNT && op->option->code != DHO_DHCP_AGENT_OPTIONS) priority_list[priority_len++] = op->option->code; } } /* * Put any spaces that are encapsulated on the list, * sort out whether they contain values later. */ for (i = 0; i < cfg_options->universe_count; i++) { if (universes[i]->enc_opt && priority_len < PRIORITY_COUNT && universes[i]->enc_opt->universe == &dhcp_universe) { if (universes[i]->enc_opt->code != DHO_DHCP_AGENT_OPTIONS) priority_list[priority_len++] = universes[i]->enc_opt->code; } } /* * The vendor option space can't stand on its own, so always * add it to the list. */ if (priority_len < PRIORITY_COUNT) priority_list[priority_len++] = DHO_VENDOR_ENCAPSULATED_OPTIONS; } /* Put the cookie up front... */ memcpy(buffer, DHCP_OPTIONS_COOKIE, 4); index += 4; /* Copy the options into the big buffer... */ option_size = store_options(&overload_used, buffer, index, mb_max, inpacket, lease, client_state, in_options, cfg_options, scope, priority_list, priority_len, of1, of2, terminate, vuname); /* If store_options() failed */ if (option_size == 0) return 0; /* How much was stored in the main buffer? */ index += option_size; /* * If we're going to have to overload, store the overload * option first. */ if (overload_used) { if (mb_size - agent_size - index < 3) return 0; buffer[index++] = DHO_DHCP_OPTION_OVERLOAD; buffer[index++] = 1; buffer[index++] = overload_used; if (overload_used & 1) memcpy(outpacket->file, &buffer[of1], DHCP_FILE_LEN); if (overload_used & 2) memcpy(outpacket->sname, &buffer[of2], DHCP_SNAME_LEN); } /* Now copy in preserved agent options, if any */ if (agent_size) { if (mb_size - index >= agent_size) { memcpy(&buffer[index], agentopts, agent_size); index += agent_size; } else log_error("Unable to store relay agent information " "in reply packet."); } /* Tack a DHO_END option onto the packet if we need to. */ if (index < mb_size) buffer[index++] = DHO_END; /* Copy main buffer into the options buffer of the packet */ memcpy(outpacket->options, buffer, index); /* Figure out the length. */ length = DHCP_FIXED_NON_UDP + index; return length; } /* * XXX: We currently special case collecting VSIO options. * We should be able to handle this in a more generic fashion, by * including any encapsulated options that are present and desired. * This will look something like the VSIO handling VSIO code. * We may also consider handling the ORO-like options within * encapsulated spaces. */ struct vsio_state { char *buf; int buflen; int bufpos; }; static void vsio_options(struct option_cache *oc, struct packet *packet, struct lease *dummy_lease, struct client_state *dummy_client_state, struct option_state *dummy_opt_state, struct option_state *opt_state, struct binding_scope **dummy_binding_scope, struct universe *universe, void *void_vsio_state) { struct vsio_state *vs = (struct vsio_state *)void_vsio_state; struct data_string ds; int total_len; memset(&ds, 0, sizeof(ds)); if (evaluate_option_cache(&ds, packet, NULL, NULL, opt_state, NULL, &global_scope, oc, MDL)) { total_len = ds.len + universe->tag_size + universe->length_size; if (total_len <= (vs->buflen - vs->bufpos)) { if (universe->tag_size == 1) { vs->buf[vs->bufpos++] = oc->option->code; } else if (universe->tag_size == 2) { putUShort((unsigned char *)vs->buf+vs->bufpos, oc->option->code); vs->bufpos += 2; } else if (universe->tag_size == 4) { putULong((unsigned char *)vs->buf+vs->bufpos, oc->option->code); vs->bufpos += 4; } if (universe->length_size == 1) { vs->buf[vs->bufpos++] = ds.len; } else if (universe->length_size == 2) { putUShort((unsigned char *)vs->buf+vs->bufpos, ds.len); vs->bufpos += 2; } else if (universe->length_size == 4) { putULong((unsigned char *)vs->buf+vs->bufpos, ds.len); vs->bufpos += 4; } memcpy(vs->buf + vs->bufpos, ds.data, ds.len); vs->bufpos += ds.len; } else { log_debug("No space for option %d in VSIO space %s.", oc->option->code, universe->name); } data_string_forget(&ds, MDL); } else { log_error("Error evaluating option %d in VSIO space %s.", oc->option->code, universe->name); } } /*! * * \brief Add a v6 option to the buffer * * Put the requested v6 option including tag, length and value * into the specified buffer. If there isn't enough space for * the entire option it is skipped. * * \param buf buffer to put the option * \param buflen total length of buffer * \param bufpos on input where to start putting the option * on output the starting point for the next option * \param code the option code number * \param ds the string to put into the option * * \return void */ static void add_option6_data(char *buf, int buflen, int* bufpos, uint16_t code, struct data_string* ds) { if ((ds->len + 4) > (buflen - *bufpos)) { log_debug("No space for option %d", code); } else { unsigned char* tmp = (unsigned char *)buf + *bufpos; /* option tag */ putUShort(tmp, code); /* option length */ putUShort(tmp+2, ds->len); /* option data */ memcpy(tmp+4, ds->data, ds->len); /* update position */ *bufpos += 4 + ds->len; } } /*! * * \brief Add a v6 encapsulated option to a buffer * * Find the universe for the requested option and if it exists * call it's encapsualtion routine to produce a data string which * can then be added to the current buffer. * * Note 1: currently we only do simple encapsulations, where the * entire value of the option is in the option universe. This is * the 'E' format, we don't handle the 'e' format as we haven't * defined any such universes yet. This means that if there is * a simple value for the option store_options6 should handle it * directly and not call this routine. * * \param buf buffer to put the option * \param buflen total length of buffer * \param bufpos on input where to start putting the option * on output the starting point for the next option * \param opt_state information about option values to use * \param packet structure containing what we know about the packet * \param encap_opt information about the structure of the option * \param code the option code number * * \return void */ static void store_encap6 (char *buf, int buflen, int* bufpos, struct option_state *opt_state, struct packet *packet, struct option* encap_opt, uint16_t code) { /* We need to extract the name of the universe * to use for this option. We expect a format string * of the form "Ename.". If we don't find a name we bail. */ struct data_string ds; struct data_string name; char* s = (char*)encap_opt->format; char* t; if ((s == NULL) || (*s != 'E') || (strlen(s) <= 2)) { return; } t = strchr(++s, '.'); if ((t == NULL) || (t == s)) { return; } memset(&ds, 0, sizeof(ds)); memset(&name, 0, sizeof(name)); name.data = (unsigned char *)s; name.len = t - s; /* Now we call the routine to find and encapsulate the requested * option/universe. A return of 0 means no option information was * available and nothing is added to the buffer */ if (option_space_encapsulate(&ds, packet, NULL, NULL, NULL, opt_state, &global_scope, &name) != 0) { add_option6_data(buf, buflen, bufpos, code, &ds); data_string_forget(&ds, MDL); } } /* * Stores the options from the DHCPv6 universe into the buffer given. * * Required options are given as a 0-terminated list of option codes. * Once those are added, the ORO is consulted. */ int store_options6(char *buf, int buflen, struct option_state *opt_state, struct packet *packet, const int *required_opts, struct data_string *oro) { int i, j; struct option_cache *oc; struct option *o; struct data_string ds; int bufpos; int oro_size; u_int16_t code; int in_required_opts; int vsio_option_code; int vsio_wanted; struct vsio_state vs; unsigned char *tmp; bufpos = 0; vsio_wanted = 0; /* * Find the option code for the VSIO universe. */ vsio_option_code = 0; o = vsio_universe.enc_opt; while (o != NULL) { if (o->universe == &dhcpv6_universe) { vsio_option_code = o->code; break; } o = o->universe->enc_opt; } if (vsio_option_code == 0) { log_fatal("No VSIO option code found."); } if (required_opts != NULL) { for (i=0; required_opts[i] != 0; i++) { if (required_opts[i] == vsio_option_code) { vsio_wanted = 1; } oc = lookup_option(&dhcpv6_universe, opt_state, required_opts[i]); if (oc == NULL) { continue; } memset(&ds, 0, sizeof(ds)); for (; oc != NULL ; oc = oc->next) { if (evaluate_option_cache(&ds, packet, NULL, NULL, opt_state, NULL, &global_scope, oc, MDL)) { add_option6_data(buf, buflen, &bufpos, (uint16_t)required_opts[i], &ds); data_string_forget(&ds, MDL); } else { log_error("Error evaluating option %d", required_opts[i]); } } } } if (oro == NULL) { oro_size = 0; } else { oro_size = oro->len / 2; } for (i=0; idata+(i*2), 2); code = ntohs(code); /* * See if we've already included this option because * it is required. */ in_required_opts = 0; if (required_opts != NULL) { for (j=0; required_opts[j] != 0; j++) { if (required_opts[j] == code) { in_required_opts = 1; break; } } } if (in_required_opts) { continue; } /* * If this is the VSIO option flag it so we'll know to * check the vsio space later on. However we still need * to check for the existence of any defined via * dhcp6.vendor-opts. Those are stored as simple values. */ if (code == vsio_option_code) { vsio_wanted = 1; } /* * Not already added, find this option. */ oc = lookup_option(&dhcpv6_universe, opt_state, code); memset(&ds, 0, sizeof(ds)); if (oc != NULL) { /* We have a simple value for the option */ for (; oc != NULL ; oc = oc->next) { if (evaluate_option_cache(&ds, packet, NULL, NULL, opt_state, NULL, &global_scope, oc, MDL)) { add_option6_data(buf, buflen, &bufpos, code, &ds); data_string_forget(&ds, MDL); } else { log_error("Error evaluating option %d", code); } } } else { /* * We don't have a simple value, check to see if we * have an universe to encapsulate into an option. */ struct option *encap_opt = NULL; unsigned int code_int = code; option_code_hash_lookup(&encap_opt, dhcpv6_universe.code_hash, &code_int, 0, MDL); if (encap_opt != NULL) { store_encap6(buf, buflen, &bufpos, opt_state, packet, encap_opt, code); option_dereference(&encap_opt, MDL); } } } if (vsio_wanted) { for (i=0; i < opt_state->universe_count; i++) { if (opt_state->universes[i] != NULL) { o = universes[i]->enc_opt; if ((o != NULL) && (o->universe == &vsio_universe)) { /* * Add the data from this VSIO option. */ vs.buf = buf; vs.buflen = buflen; vs.bufpos = bufpos+8; option_space_foreach(packet, NULL, NULL, NULL, opt_state, NULL, universes[i], (void *)&vs, vsio_options); /* * If there was actually data here, * add the "header". */ if (vs.bufpos > bufpos+8) { tmp = (unsigned char *)buf + bufpos; putUShort(tmp, vsio_option_code); putUShort(tmp+2, vs.bufpos-bufpos-4); putULong(tmp+4, o->code); bufpos = vs.bufpos; } } } } } return bufpos; } /* * Store all the requested options into the requested buffer. * XXX: ought to be static */ int store_options(int *ocount, unsigned char *buffer, unsigned index, unsigned buflen, struct packet *packet, struct lease *lease, struct client_state *client_state, struct option_state *in_options, struct option_state *cfg_options, struct binding_scope **scope, unsigned *priority_list, int priority_len, unsigned first_cutoff, int second_cutoff, int terminate, const char *vuname) { int bufix = 0, six = 0, tix = 0; int i; int ix; int tto; int bufend, sbufend; struct data_string od; struct option_cache *oc; struct option *option = NULL; unsigned code; /* * These arguments are relative to the start of the buffer, so * reduce them by the current buffer index, and advance the * buffer pointer to where we're going to start writing. */ buffer = &buffer[index]; buflen -= index; if (first_cutoff) first_cutoff -= index; if (second_cutoff) second_cutoff -= index; /* Calculate the start and end of each section of the buffer */ bufend = sbufend = buflen; if (first_cutoff) { if (first_cutoff >= buflen) log_fatal("%s:%d:store_options: Invalid first cutoff.", MDL); bufend = first_cutoff; if (second_cutoff) { if (second_cutoff >= buflen) log_fatal("%s:%d:store_options: Invalid second cutoff.", MDL); sbufend = second_cutoff; } } else if (second_cutoff) { if (second_cutoff >= buflen) log_fatal("%s:%d:store_options: Invalid second cutoff.", MDL); bufend = second_cutoff; } memset (&od, 0, sizeof od); /* Eliminate duplicate options from the parameter request list. * Enforce RFC-mandated ordering of options that are present. */ for (i = 0; i < priority_len; i++) { /* Eliminate duplicates. */ tto = 0; for (ix = i + 1; ix < priority_len + tto; ix++) { if (tto) priority_list [ix - tto] = priority_list [ix]; if (priority_list [i] == priority_list [ix]) { tto++; priority_len--; } } /* Enforce ordering of SUBNET_MASK options, according to * RFC2132 Section 3.3: * * If both the subnet mask and the router option are * specified in a DHCP reply, the subnet mask option MUST * be first. * * This guidance does not specify what to do if the client * PRL explicitly requests the options out of order, it is * a general statement. */ if (priority_list[i] == DHO_SUBNET_MASK) { for (ix = i - 1 ; ix >= 0 ; ix--) { if (priority_list[ix] == DHO_ROUTERS) { /* swap */ priority_list[ix] = DHO_SUBNET_MASK; priority_list[i] = DHO_ROUTERS; break; } } } } /* Copy out the options in the order that they appear in the priority list... */ for (i = 0; i < priority_len; i++) { /* Number of bytes left to store (some may already have been stored by a previous pass). */ unsigned length; int optstart, soptstart, toptstart; struct universe *u; int have_encapsulation = 0; struct data_string encapsulation; int splitup; memset (&encapsulation, 0, sizeof encapsulation); have_encapsulation = 0; if (option != NULL) option_dereference(&option, MDL); /* Code for next option to try to store. */ code = priority_list [i]; /* Look up the option in the site option space if the code is above the cutoff, otherwise in the DHCP option space. */ if (code >= cfg_options -> site_code_min) u = universes [cfg_options -> site_universe]; else u = &dhcp_universe; oc = lookup_option (u, cfg_options, code); if (oc && oc->option) option_reference(&option, oc->option, MDL); else option_code_hash_lookup(&option, u->code_hash, &code, 0, MDL); /* If it's a straight encapsulation, and the user supplied a * value for the entire option, use that. Otherwise, search * the encapsulated space. * * If it's a limited encapsulation with preceding data, and the * user supplied values for the preceding bytes, search the * encapsulated space. */ if ((option != NULL) && (((oc == NULL) && (option->format[0] == 'E')) || ((oc != NULL) && (option->format[0] == 'e')))) { static char *s, *t; struct option_cache *tmp; struct data_string name; s = strchr (option->format, 'E'); if (s) t = strchr (++s, '.'); if (s && t) { memset (&name, 0, sizeof name); /* A zero-length universe name means the vendor option space, if one is defined. */ if (t == s) { if (vendor_cfg_option) { tmp = lookup_option (vendor_cfg_option -> universe, cfg_options, vendor_cfg_option -> code); if (tmp) /* No need to check the return as we check name.len below */ (void) evaluate_option_cache (&name, packet, lease, client_state, in_options, cfg_options, scope, tmp, MDL); } else if (vuname) { name.data = (unsigned char *)s; name.len = strlen (s); } } else { name.data = (unsigned char *)s; name.len = t - s; } /* If we found a universe, and there are options configured for that universe, try to encapsulate it. */ if (name.len) { have_encapsulation = (option_space_encapsulate (&encapsulation, packet, lease, client_state, in_options, cfg_options, scope, &name)); data_string_forget (&name, MDL); } } } /* In order to avoid memory leaks, we have to get to here with any option cache that we allocated in tmp not being referenced by tmp, and whatever option cache is referenced by oc being an actual reference. lookup_option doesn't generate a reference (this needs to be fixed), so the preceding goop ensures that if we *didn't* generate a new option cache, oc still winds up holding an actual reference. */ /* If no data is available for this option, skip it. */ if (!oc && !have_encapsulation) { continue; } /* Find the value of the option... */ od.len = 0; if (oc) { /* No need to check the return as we check od.len below */ (void) evaluate_option_cache (&od, packet, lease, client_state, in_options, cfg_options, scope, oc, MDL); /* If we have encapsulation for this option, and an oc * lookup succeeded, but the evaluation failed, it is * either because this is a complex atom (atoms before * E on format list) and the top half of the option is * not configured, or this is a simple encapsulated * space and the evaluator is giving us a NULL. Prefer * the evaluator's opinion over the subspace. */ if (!od.len) { data_string_forget (&encapsulation, MDL); data_string_forget (&od, MDL); continue; } } /* We should now have a constant length for the option. */ length = od.len; if (have_encapsulation) { length += encapsulation.len; /* od.len can be nonzero if we got here without an * oc (cache lookup failed), but did have an encapsulated * simple encapsulation space. */ if (!od.len) { data_string_copy (&od, &encapsulation, MDL); data_string_forget (&encapsulation, MDL); } else { struct buffer *bp = (struct buffer *)0; if (!buffer_allocate (&bp, length, MDL)) { option_cache_dereference (&oc, MDL); data_string_forget (&od, MDL); data_string_forget (&encapsulation, MDL); continue; } memcpy (&bp -> data [0], od.data, od.len); memcpy (&bp -> data [od.len], encapsulation.data, encapsulation.len); data_string_forget (&od, MDL); data_string_forget (&encapsulation, MDL); od.data = &bp -> data [0]; buffer_reference (&od.buffer, bp, MDL); buffer_dereference (&bp, MDL); od.len = length; od.terminated = 0; } } /* Do we add a NUL? */ if (terminate && option && format_has_text(option->format)) { length++; tto = 1; } else { tto = 0; } /* Try to store the option. */ /* If the option's length is more than 255, we must store it in multiple hunks. Store 255-byte hunks first. However, in any case, if the option data will cross a buffer boundary, split it across that boundary. */ if (length > 255) splitup = 1; else splitup = 0; ix = 0; optstart = bufix; soptstart = six; toptstart = tix; while (length) { unsigned incr = length; int *pix; unsigned char *base; /* Try to fit it in the options buffer. */ if (!splitup && ((!six && !tix && (i == priority_len - 1) && (bufix + 2 + length < bufend)) || (bufix + 5 + length < bufend))) { base = buffer; pix = &bufix; /* Try to fit it in the second buffer. */ } else if (!splitup && first_cutoff && (first_cutoff + six + 3 + length < sbufend)) { base = &buffer[first_cutoff]; pix = &six; /* Try to fit it in the third buffer. */ } else if (!splitup && second_cutoff && (second_cutoff + tix + 3 + length < buflen)) { base = &buffer[second_cutoff]; pix = &tix; /* Split the option up into the remaining space. */ } else { splitup = 1; /* Use any remaining options space. */ if (bufix + 6 < bufend) { incr = bufend - bufix - 5; base = buffer; pix = &bufix; /* Use any remaining first_cutoff space. */ } else if (first_cutoff && (first_cutoff + six + 4 < sbufend)) { incr = sbufend - (first_cutoff + six) - 3; base = &buffer[first_cutoff]; pix = &six; /* Use any remaining second_cutoff space. */ } else if (second_cutoff && (second_cutoff + tix + 4 < buflen)) { incr = buflen - (second_cutoff + tix) - 3; base = &buffer[second_cutoff]; pix = &tix; /* Give up, roll back this option. */ } else { bufix = optstart; six = soptstart; tix = toptstart; break; } } if (incr > length) incr = length; if (incr > 255) incr = 255; /* Everything looks good - copy it in! */ base [*pix] = code; base [*pix + 1] = (unsigned char)incr; if (tto && incr == length) { if (incr > 1) memcpy (base + *pix + 2, od.data + ix, (unsigned)(incr - 1)); base [*pix + 2 + incr - 1] = 0; } else { memcpy (base + *pix + 2, od.data + ix, (unsigned)incr); } length -= incr; ix += incr; *pix += 2 + incr; } data_string_forget (&od, MDL); } if (option != NULL) option_dereference(&option, MDL); /* If we can overload, and we have, then PAD and END those spaces. */ if (first_cutoff && six) { if ((first_cutoff + six + 1) < sbufend) memset (&buffer[first_cutoff + six + 1], DHO_PAD, sbufend - (first_cutoff + six + 1)); else if (first_cutoff + six >= sbufend) log_fatal("Second buffer overflow in overloaded options."); buffer[first_cutoff + six] = DHO_END; if (ocount != NULL) *ocount |= 1; /* So that caller knows there's data there. */ } if (second_cutoff && tix) { if (second_cutoff + tix + 1 < buflen) { memset (&buffer[second_cutoff + tix + 1], DHO_PAD, buflen - (second_cutoff + tix + 1)); } else if (second_cutoff + tix >= buflen) log_fatal("Third buffer overflow in overloaded options."); buffer[second_cutoff + tix] = DHO_END; if (ocount != NULL) *ocount |= 2; /* So that caller knows there's data there. */ } if ((six || tix) && (bufix + 3 > bufend)) log_fatal("Not enough space for option overload option."); return bufix; } /* Return true if the format string has a variable length text option * ("t"), return false otherwise. */ int format_has_text(format) const char *format; { const char *p; p = format; while (*p != '\0') { switch (*p++) { case 'd': case 't': return 1; /* These symbols are arbitrary, not fixed or * determinable length...text options with them is * invalid (whatever the case, they are never NULL * terminated). */ case 'A': case 'a': case 'X': case 'x': case 'D': return 0; case 'c': /* 'c' only follows 'D' atoms, and indicates that * compression may be used. If there was a 'D' * atom already, we would have returned. So this * is an error, but continue looking for 't' anyway. */ log_error("format_has_text(%s): 'c' atoms are illegal " "except after 'D' atoms.", format); break; /* 'E' is variable length, but not arbitrary...you * can find its length if you can find an END option. * N is (n)-byte in length but trails a name of a * space defining the enumeration values. So treat * both the same - valid, fixed-length fields. */ case 'E': case 'N': /* Consume the space name. */ while ((*p != '\0') && (*p++ != '.')) ; break; default: break; } } return 0; } /* Determine the minimum length of a DHCP option prior to any variable * or inconsistent length formats, according to its configured format * variable (and possibly from supplied option cache contents for variable * length format symbols). */ int format_min_length(format, oc) const char *format; struct option_cache *oc; { const char *p, *name; int min_len = 0; int last_size = 0; struct enumeration *espace; p = format; while (*p != '\0') { switch (*p++) { case '6': /* IPv6 Address */ min_len += 16; last_size = 16; break; case 'I': /* IPv4 Address */ case 'l': /* int32_t */ case 'L': /* uint32_t */ case 'T': /* Lease Time, uint32_t equivalent */ min_len += 4; last_size = 4; break; case 's': /* int16_t */ case 'S': /* uint16_t */ min_len += 2; last_size = 2; break; case 'N': /* Enumeration value. */ /* Consume space name. */ name = p; p = strchr(p, '.'); if (p == NULL) log_fatal("Corrupt format: %s", format); espace = find_enumeration(name, p - name); if (espace == NULL) { log_error("Unknown enumeration: %s", format); /* Max is safest value to return. */ return INT_MAX; } min_len += espace->width; last_size = espace->width; p++; break; case 'b': /* int8_t */ case 'B': /* uint8_t */ case 'F': /* Flag that is always true. */ case 'f': /* Flag */ min_len++; last_size = 1; break; case 'o': /* Last argument is optional. */ min_len -= last_size; /* XXX: It MAY be possible to sense the end of an * encapsulated space, but right now this is too * hard to support. Return a safe value. */ case 'e': /* Encapsulation hint (there is an 'E' later). */ case 'E': /* Encapsulated options. */ return min_len; case 'd': /* "Domain name" */ case 'D': /* "rfc1035 formatted names" */ case 't': /* "ASCII Text" */ case 'X': /* "ASCII or Hex Conditional */ case 'x': /* "Hex" */ case 'A': /* Array of all that precedes. */ case 'a': /* Array of preceding symbol. */ case 'Z': /* nothing. */ return min_len; case 'c': /* Compress flag for D atom. */ log_error("format_min_length(%s): 'c' atom is illegal " "except after 'D' atom.", format); return INT_MAX; default: /* No safe value is known. */ log_error("format_min_length(%s): No safe value " "for unknown format symbols.", format); return INT_MAX; } } return min_len; } /* Format the specified option so that a human can easily read it. */ /* Maximum pretty printed size */ #define MAX_OUTPUT_SIZE 32*1024 const char *pretty_print_option (option, data, len, emit_commas, emit_quotes) struct option *option; const unsigned char *data; unsigned len; int emit_commas; int emit_quotes; { /* We add 128 byte pad so we don't have to add checks everywhere. */ static char optbuf [MAX_OUTPUT_SIZE + 128]; /* XXX */ static char *endbuf = optbuf + MAX_OUTPUT_SIZE; int hunksize = 0; int opthunk = 0; int hunkinc = 0; int numhunk = -1; int numelem = 0; int count; int i, j, k, l; char fmtbuf[32] = ""; struct iaddr iaddr; struct enumeration *enumbuf[32]; /* MUST be same as fmtbuf */ char *op = optbuf; const unsigned char *dp = data; char comma; unsigned long tval; isc_boolean_t a_array = ISC_FALSE; int len_used; if (emit_commas) comma = ','; else comma = ' '; memset (enumbuf, 0, sizeof enumbuf); /* Figure out the size of the data. */ for (l = i = 0; option -> format [i]; i++, l++) { if (l >= sizeof(fmtbuf) - 1) log_fatal("Bounds failure on internal buffer at " "%s:%d", MDL); if (!numhunk) { log_error ("%s: Extra codes in format string: %s", option -> name, &(option -> format [i])); break; } numelem++; fmtbuf [l] = option -> format [i]; switch (option -> format [i]) { case 'a': a_array = ISC_TRUE; /* Fall through */ case 'A': --numelem; fmtbuf [l] = 0; numhunk = 0; break; case 'E': /* Skip the universe name. */ while (option -> format [i] && option -> format [i] != '.') i++; /* Fall Through! */ case 'X': for (k = 0; k < len; k++) { if (!isascii (data [k]) || !isprint (data [k])) break; } /* If we found no bogus characters, or the bogus character we found is a trailing NUL, it's okay to print this option as text. */ if (k == len || (k + 1 == len && data [k] == 0)) { fmtbuf [l] = 't'; numhunk = -2; } else { fmtbuf [l] = 'x'; hunksize++; comma = ':'; numhunk = 0; a_array = ISC_TRUE; hunkinc = 1; } fmtbuf [l + 1] = 0; break; case 'c': /* The 'c' atom is a 'D' modifier only. */ log_error("'c' atom not following D atom in format " "string: %s", option->format); break; case 'D': /* * Skip the 'c' atom, if present. It does not affect * how we convert wire->text format (if compression is * present either way, we still process it). */ if (option->format[i+1] == 'c') i++; fmtbuf[l + 1] = 0; numhunk = -2; break; case 'd': fmtbuf[l] = 't'; /* Fall Through ! */ case 't': fmtbuf[l + 1] = 0; numhunk = -2; break; case 'N': k = i; while (option -> format [i] && option -> format [i] != '.') i++; enumbuf [l] = find_enumeration (&option -> format [k] + 1, i - k - 1); if (enumbuf[l] == NULL) { hunksize += 1; hunkinc = 1; } else { hunksize += enumbuf[l]->width; hunkinc = enumbuf[l]->width; } break; case '6': hunksize += 16; hunkinc = 16; break; case 'I': case 'l': case 'L': case 'T': hunksize += 4; hunkinc = 4; break; case 's': case 'S': hunksize += 2; hunkinc = 2; break; case 'b': case 'B': case 'f': case 'F': hunksize++; hunkinc = 1; break; case 'e': case 'Z': break; case 'o': opthunk += hunkinc; break; default: log_error ("%s: garbage in format string: %s", option -> name, &(option -> format [i])); break; } } /* Check for too few bytes... */ if (hunksize - opthunk > len) { log_error ("%s: expecting at least %d bytes; got %d", option -> name, hunksize, len); return ""; } /* Check for too many bytes... */ if (numhunk == -1 && hunksize < len) log_error ("%s: %d extra bytes", option -> name, len - hunksize); /* If this is an array, compute its size. */ if (numhunk == 0) { if (a_array == ISC_TRUE) { /* * It is an 'a' type array - we repeat the * last format type. A binary string for 'X' * is also like this. hunkinc is the size * of the last format type and we add 1 to * cover the entire first record. */ /* If format string had no valid entries prior to * 'a' hunkinc will be 0. Ex: "a", "oa", "aA" */ if (hunkinc == 0) { log_error ("%s: invalid 'a' format: %s", option->name, option->format); return (""); } numhunk = ((len - hunksize) / hunkinc) + 1; len_used = hunksize + ((numhunk - 1) * hunkinc); } else { /* * It is an 'A' type array - we repeat the * entire record */ /* If format string had no valid entries prior to * 'A' hunksize will be 0. Ex: "A", "oA", "foA" */ if (hunksize == 0) { log_error ("%s: invalid 'A' format: %s", option->name, option->format); return (""); } numhunk = len / hunksize; len_used = numhunk * hunksize; } /* See if we got an exact number of hunks. */ if (len_used < len) { log_error ("%s: %d extra bytes at end of array\n", option -> name, len - len_used); } } /* A one-hunk array prints the same as a single hunk. */ if (numhunk < 0) numhunk = 1; /* Cycle through the array (or hunk) printing the data. */ for (i = 0; i < numhunk; i++) { if ((a_array == ISC_TRUE) && (i != 0) && (numelem > 0)) { /* * For 'a' type of arrays we repeat * only the last format character * We should never hit the case of numelem == 0 * but let's include the check to be safe. */ j = numelem - 1; } else { /* * for other types of arrays or the first * time through for 'a' types, we go through * the entire set of format characters. */ j = 0; } for (; j < numelem; j++) { switch (fmtbuf [j]) { case 't': /* endbuf-1 leaves room for NULL. */ k = pretty_text(&op, endbuf - 1, &dp, data + len, emit_quotes); if (k == -1) { log_error("Error printing text."); break; } *op = 0; break; case 'D': /* RFC1035 format name list */ for( ; dp < (data + len) ; dp += k) { unsigned char nbuff[NS_MAXCDNAME]; const unsigned char *nbp, *nend; nend = &nbuff[sizeof(nbuff)]; /* If this is for ISC DHCP consumption * (emit_quotes), lay it out as a list * of STRING tokens. Otherwise, it is * a space-separated list of DNS- * escaped names as /etc/resolv.conf * might digest. */ if (dp != data) { if (op + 2 > endbuf) break; if (emit_quotes) *op++ = ','; *op++ = ' '; } /* XXX: if fmtbuf[j+1] != 'c', we * should warn if the data was * compressed anyway. */ k = MRns_name_unpack(data, data + len, dp, nbuff, sizeof(nbuff)); if (k == -1) { log_error("Invalid domain " "list."); break; } /* If emit_quotes, then use ISC DHCP * escapes. Otherwise, rely only on * MRns_name_ntop(). */ if (emit_quotes) { nbp = nbuff; pretty_domain(&op, endbuf-1, &nbp, nend); } else { /* MRns_name_ntop() includes * a trailing NUL in its * count. */ count = MRns_name_ntop( nbuff, op, (endbuf-op)-1); if (count <= 0) { log_error("Invalid " "domain name."); break; } /* Consume all but the trailing * NUL. */ op += count - 1; /* Replace the trailing NUL * with the implicit root * (in the unlikely event the * domain name /is/ the root). */ *op++ = '.'; } } *op = '\0'; break; /* pretty-printing an array of enums is going to get ugly. */ case 'N': if (!enumbuf [j]) { tval = *dp++; goto enum_as_num; } switch (enumbuf[j]->width) { case 1: tval = getUChar(dp); break; case 2: tval = getUShort(dp); break; case 4: tval = getULong(dp); break; default: log_fatal("Impossible case at %s:%d.", MDL); return ""; } for (i = 0; ;i++) { if (!enumbuf [j] -> values [i].name) goto enum_as_num; if (enumbuf [j] -> values [i].value == tval) break; } strcpy (op, enumbuf [j] -> values [i].name); dp += enumbuf[j]->width; break; enum_as_num: sprintf(op, "%lu", tval); break; case 'I': iaddr.len = 4; memcpy(iaddr.iabuf, dp, 4); strcpy(op, piaddr(iaddr)); dp += 4; break; case '6': iaddr.len = 16; memcpy(iaddr.iabuf, dp, 16); strcpy(op, piaddr(iaddr)); dp += 16; break; case 'l': sprintf (op, "%ld", (long)getLong (dp)); dp += 4; break; case 'T': tval = getULong (dp); if (tval == -1) sprintf (op, "%s", "infinite"); else sprintf(op, "%lu", tval); break; case 'L': sprintf(op, "%lu", (unsigned long)getULong(dp)); dp += 4; break; case 's': sprintf (op, "%d", (int)getShort (dp)); dp += 2; break; case 'S': sprintf(op, "%u", (unsigned)getUShort(dp)); dp += 2; break; case 'b': sprintf (op, "%d", *(const char *)dp++); break; case 'B': sprintf (op, "%d", *dp++); break; case 'X': case 'x': sprintf (op, "%x", *dp++); break; case 'f': strcpy (op, *dp++ ? "true" : "false"); break; case 'F': strcpy (op, "true"); break; case 'e': case 'Z': *op = '\0'; break; default: log_error ("Unexpected format code %c", fmtbuf [j]); } op += strlen (op); if (op >= endbuf) { log_error ("Option data exceeds" " maximum size %d", MAX_OUTPUT_SIZE); return (""); } if (dp == data + len) break; if (j + 1 < numelem && comma != ':') *op++ = ' '; } if (i + 1 < numhunk) { *op++ = comma; } if (dp == data + len) break; } return optbuf; } int get_option (result, universe, packet, lease, client_state, in_options, cfg_options, options, scope, code, file, line) struct data_string *result; struct universe *universe; struct packet *packet; struct lease *lease; struct client_state *client_state; struct option_state *in_options; struct option_state *cfg_options; struct option_state *options; struct binding_scope **scope; unsigned code; const char *file; int line; { struct option_cache *oc; if (!universe -> lookup_func) return 0; oc = ((*universe -> lookup_func) (universe, options, code)); if (!oc) return 0; if (!evaluate_option_cache (result, packet, lease, client_state, in_options, cfg_options, scope, oc, file, line)) return 0; return 1; } /* * Look for the option and dig out the value assoicated with it. * Currently this is used for 1 byte integers, it maybe expanded * in the future to handle other integers at which point it will * need a size argument. */ int get_option_int (result, universe, packet, lease, client_state, in_options, cfg_options, options, scope, code, file, line) int *result; struct universe *universe; struct packet *packet; struct lease *lease; struct client_state *client_state; struct option_state *in_options; struct option_state *cfg_options; struct option_state *options; struct binding_scope **scope; unsigned code; const char *file; int line; { struct option_cache *oc; struct data_string d1; int rcode = 0; /* basic sanity checks */ if ((options == NULL) || (universe->lookup_func == NULL)) return (0); /* find the option cache */ oc = ((*universe->lookup_func)(universe, options, code)); if (!oc) return (0); /* if there is a value get it into the string */ memset(&d1, 0, sizeof(d1)); if (!evaluate_option_cache(&d1, packet, lease, client_state, in_options, cfg_options, scope, oc, file, line)) return (0); /* If the length matches extract the value for the return */ if (d1.len == 1) { *result = d1.data[0]; rcode = 1; } data_string_forget(&d1, MDL); return (rcode); } void set_option (universe, options, option, op) struct universe *universe; struct option_state *options; struct option_cache *option; enum statement_op op; { struct option_cache *oc, *noc; switch (op) { case if_statement: case add_statement: case eval_statement: case break_statement: default: log_error ("bogus statement type in set_option."); break; case default_option_statement: oc = lookup_option (universe, options, option -> option -> code); if (oc) break; save_option (universe, options, option); break; case supersede_option_statement: case send_option_statement: /* Install the option, replacing any existing version. */ save_option (universe, options, option); break; case append_option_statement: case prepend_option_statement: oc = lookup_option (universe, options, option -> option -> code); if (!oc) { save_option (universe, options, option); break; } /* If it's not an expression, make it into one. */ if (!oc -> expression && oc -> data.len) { if (!expression_allocate (&oc -> expression, MDL)) { log_error ("Can't allocate const expression."); break; } oc -> expression -> op = expr_const_data; data_string_copy (&oc -> expression -> data.const_data, &oc -> data, MDL); data_string_forget (&oc -> data, MDL); } noc = (struct option_cache *)0; if (!option_cache_allocate (&noc, MDL)) break; if (op == append_option_statement) { if (!make_concat (&noc -> expression, oc -> expression, option -> expression)) { option_cache_dereference (&noc, MDL); break; } } else { if (!make_concat (&noc -> expression, option -> expression, oc -> expression)) { option_cache_dereference (&noc, MDL); break; } } /* If we are trying to combine compressed domain-lists then * we need to change the expression opcode. The lists must * be decompressed, combined, and then recompressed to work * correctly. You cannot simply add two compressed lists * together. */ switch (((memcmp(option->option->format, "Dc", 2) == 0) + (memcmp(oc->option->format, "Dc", 2) == 0))) { case 1: /* Only one is "Dc", this won't work * Not sure if you can make this occur, but just * in case. */ log_error ("Both options must be Dc format"); option_cache_dereference (&noc, MDL); return; case 2: /* Both are "Dc", change the code */ noc->expression->op = expr_concat_dclist; break; default: /* Neither are "Dc", so as you were */ break; } option_reference(&(noc->option), oc->option, MDL); save_option (universe, options, noc); option_cache_dereference (&noc, MDL); break; } } struct option_cache *lookup_option (universe, options, code) struct universe *universe; struct option_state *options; unsigned code; { if (!options) return (struct option_cache *)0; if (universe -> lookup_func) return (*universe -> lookup_func) (universe, options, code); else log_error ("can't look up options in %s space.", universe -> name); return (struct option_cache *)0; } struct option_cache *lookup_hashed_option (universe, options, code) struct universe *universe; struct option_state *options; unsigned code; { int hashix; pair bptr; pair *hash; /* Make sure there's a hash table. */ if (universe -> index >= options -> universe_count || !(options -> universes [universe -> index])) return (struct option_cache *)0; hash = options -> universes [universe -> index]; hashix = compute_option_hash (code); for (bptr = hash [hashix]; bptr; bptr = bptr -> cdr) { if (((struct option_cache *)(bptr -> car)) -> option -> code == code) return (struct option_cache *)(bptr -> car); } return (struct option_cache *)0; } /* Save a specified buffer into an option cache. */ int save_option_buffer(struct universe *universe, struct option_state *options, struct buffer *bp, unsigned char *buffer, unsigned length, unsigned code, int terminatep) { struct option_cache *op = NULL; int status = 1; status = prepare_option_buffer(universe, bp, buffer, length, code, terminatep, &op); if (status == 0) goto cleanup; save_option(universe, options, op); cleanup: if (op != NULL) option_cache_dereference(&op, MDL); return status; } /* Append a specified buffer onto the tail of an option cache. */ int append_option_buffer(struct universe *universe, struct option_state *options, struct buffer *bp, unsigned char *buffer, unsigned length, unsigned code, int terminatep) { struct option_cache *op = NULL; int status = 1; status = prepare_option_buffer(universe, bp, buffer, length, code, terminatep, &op); if (status == 0) goto cleanup; also_save_option(universe, options, op); cleanup: if (op != NULL) option_cache_dereference(&op, MDL); return status; } /* Create/copy a buffer into a new option cache. */ static int prepare_option_buffer(struct universe *universe, struct buffer *bp, unsigned char *buffer, unsigned length, unsigned code, int terminatep, struct option_cache **opp) { struct buffer *lbp = NULL; struct option *option = NULL; struct option_cache *op; int status = 1; /* Code sizes of 8, 16, and 32 bits are allowed. */ switch(universe->tag_size) { case 1: if (code > 0xff) return 0; break; case 2: if (code > 0xffff) return 0; break; case 4: if (code > 0xffffffff) return 0; break; default: log_fatal("Inconsistent universe tag size at %s:%d.", MDL); } option_code_hash_lookup(&option, universe->code_hash, &code, 0, MDL); /* If we created an option structure for each option a client * supplied, it's possible we may create > 2^32 option structures. * That's not feasible. So by failing to enter these option * structures into the code and name hash tables, references will * never be more than 1 - when the option cache is destroyed, this * will be cleaned up. */ if (!option) { char nbuf[sizeof("unknown-4294967295")]; sprintf(nbuf, "unknown-%u", code); option = new_option(nbuf, MDL); if (!option) return 0; option->format = default_option_format; option->universe = universe; option->code = code; /* new_option() doesn't set references, pretend. */ option->refcnt = 1; } if (!option_cache_allocate (opp, MDL)) { log_error("No memory for option code %s.%s.", universe->name, option->name); status = 0; goto cleanup; } /* Pointer rather than double pointer makes for less parens. */ op = *opp; option_reference(&op->option, option, MDL); /* If we weren't passed a buffer in which the data are saved and refcounted, allocate one now. */ if (!bp) { if (!buffer_allocate (&lbp, length + terminatep, MDL)) { log_error ("no memory for option buffer."); status = 0; goto cleanup; } memcpy (lbp -> data, buffer, length + terminatep); bp = lbp; buffer = &bp -> data [0]; /* Refer to saved buffer. */ } /* Reference buffer copy to option cache. */ op -> data.buffer = (struct buffer *)0; buffer_reference (&op -> data.buffer, bp, MDL); /* Point option cache into buffer. */ op -> data.data = buffer; op -> data.len = length; if (terminatep) { /* NUL terminate (we can get away with this because we (or the caller!) allocated one more than the buffer size, and because the byte following the end of an option is always the code of the next option, which the caller is getting out of the *original* buffer. */ buffer [length] = 0; op -> data.terminated = 1; } else op -> data.terminated = 0; /* If this option is ultimately a text option, null determinate to * comply with RFC2132 section 2. Mark a flag so this can be sensed * later to echo NULLs back to clients that supplied them (they * probably expect them). */ if (format_has_text(option->format)) { int min_len = format_min_length(option->format, op); while ((op->data.len > min_len) && (op->data.data[op->data.len-1] == '\0')) { op->data.len--; op->flags |= OPTION_HAD_NULLS; } } /* And let go of our references. */ cleanup: if (lbp != NULL) buffer_dereference(&lbp, MDL); option_dereference(&option, MDL); return status; } static void count_options(struct option_cache *dummy_oc, struct packet *dummy_packet, struct lease *dummy_lease, struct client_state *dummy_client_state, struct option_state *dummy_opt_state, struct option_state *opt_state, struct binding_scope **dummy_binding_scope, struct universe *dummy_universe, void *void_accumulator) { int *accumulator = (int *)void_accumulator; *accumulator += 1; } static void collect_oro(struct option_cache *oc, struct packet *dummy_packet, struct lease *dummy_lease, struct client_state *dummy_client_state, struct option_state *dummy_opt_state, struct option_state *opt_state, struct binding_scope **dummy_binding_scope, struct universe *dummy_universe, void *void_oro) { struct data_string *oro = (struct data_string *)void_oro; putUShort(oro->buffer->data + oro->len, oc->option->code); oro->len += 2; } /* build_server_oro() is presently unusued, but may be used at a future date * with support for Reconfigure messages (as a hint to the client about new * option value contents). */ void build_server_oro(struct data_string *server_oro, struct option_state *options, const char *file, int line) { int num_opts; int i; struct option *o; /* * Count the number of options, so we can allocate enough memory. * We want to mention sub-options too, so check all universes. */ num_opts = 0; option_space_foreach(NULL, NULL, NULL, NULL, options, NULL, &dhcpv6_universe, (void *)&num_opts, count_options); for (i=0; i < options->universe_count; i++) { if (options->universes[i] != NULL) { o = universes[i]->enc_opt; while (o != NULL) { if (o->universe == &dhcpv6_universe) { num_opts++; break; } o = o->universe->enc_opt; } } } /* * Allocate space. */ memset(server_oro, 0, sizeof(*server_oro)); if (!buffer_allocate(&server_oro->buffer, num_opts * 2, MDL)) { log_fatal("no memory to build server ORO"); } server_oro->data = server_oro->buffer->data; /* * Copy the data in. * We want to mention sub-options too, so check all universes. */ server_oro->len = 0; /* gets set in collect_oro */ option_space_foreach(NULL, NULL, NULL, NULL, options, NULL, &dhcpv6_universe, (void *)server_oro, collect_oro); for (i=0; i < options->universe_count; i++) { if (options->universes[i] != NULL) { o = universes[i]->enc_opt; while (o != NULL) { if (o->universe == &dhcpv6_universe) { unsigned char *tmp; tmp = server_oro->buffer->data; putUShort(tmp + server_oro->len, o->code); server_oro->len += 2; break; } o = o->universe->enc_opt; } } } } /* Wrapper function to put an option cache into an option state. */ void save_option(struct universe *universe, struct option_state *options, struct option_cache *oc) { if (universe->save_func) (*universe->save_func)(universe, options, oc, ISC_FALSE); else log_error("can't store options in %s space.", universe->name); } /* Wrapper function to append an option cache into an option state's list. */ void also_save_option(struct universe *universe, struct option_state *options, struct option_cache *oc) { if (universe->save_func) (*universe->save_func)(universe, options, oc, ISC_TRUE); else log_error("can't store options in %s space.", universe->name); } void save_hashed_option(struct universe *universe, struct option_state *options, struct option_cache *oc, isc_boolean_t appendp) { int hashix; pair bptr; pair *hash = options -> universes [universe -> index]; struct option_cache **ocloc; if (oc -> refcnt == 0) abort (); /* Compute the hash. */ hashix = compute_option_hash (oc -> option -> code); /* If there's no hash table, make one. */ if (!hash) { hash = (pair *)dmalloc (OPTION_HASH_SIZE * sizeof *hash, MDL); if (!hash) { log_error ("no memory to store %s.%s", universe -> name, oc -> option -> name); return; } memset (hash, 0, OPTION_HASH_SIZE * sizeof *hash); options -> universes [universe -> index] = (void *)hash; } else { /* Try to find an existing option matching the new one. */ for (bptr = hash [hashix]; bptr; bptr = bptr -> cdr) { if (((struct option_cache *) (bptr -> car)) -> option -> code == oc -> option -> code) break; } /* Deal with collisions on the hash list. */ if (bptr) { ocloc = (struct option_cache **)&bptr->car; /* * If appendp is set, append it onto the tail of the * ->next list. If it is not set, rotate it into * position at the head of the list. */ if (appendp) { do { ocloc = &(*ocloc)->next; } while (*ocloc != NULL); } else { option_cache_dereference(ocloc, MDL); } option_cache_reference(ocloc, oc, MDL); return; } } /* Otherwise, just put the new one at the head of the list. */ bptr = new_pair (MDL); if (!bptr) { log_error ("No memory for option_cache reference."); return; } bptr -> cdr = hash [hashix]; bptr -> car = 0; option_cache_reference ((struct option_cache **)&bptr -> car, oc, MDL); hash [hashix] = bptr; } void delete_option (universe, options, code) struct universe *universe; struct option_state *options; int code; { if (universe -> delete_func) (*universe -> delete_func) (universe, options, code); else log_error ("can't delete options from %s space.", universe -> name); } void delete_hashed_option (universe, options, code) struct universe *universe; struct option_state *options; int code; { int hashix; pair bptr, prev = (pair)0; pair *hash = options -> universes [universe -> index]; /* There may not be any options in this space. */ if (!hash) return; /* Try to find an existing option matching the new one. */ hashix = compute_option_hash (code); for (bptr = hash [hashix]; bptr; bptr = bptr -> cdr) { if (((struct option_cache *)(bptr -> car)) -> option -> code == code) break; prev = bptr; } /* If we found one, wipe it out... */ if (bptr) { if (prev) prev -> cdr = bptr -> cdr; else hash [hashix] = bptr -> cdr; option_cache_dereference ((struct option_cache **)(&bptr -> car), MDL); free_pair (bptr, MDL); } } extern struct option_cache *free_option_caches; /* XXX */ int option_cache_dereference (ptr, file, line) struct option_cache **ptr; const char *file; int line; { if (!ptr || !*ptr) { log_error ("Null pointer in option_cache_dereference: %s(%d)", file, line); #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } (*ptr) -> refcnt--; rc_register (file, line, ptr, *ptr, (*ptr) -> refcnt, 1, RC_MISC); if (!(*ptr) -> refcnt) { if ((*ptr) -> data.buffer) data_string_forget (&(*ptr) -> data, file, line); if ((*ptr)->option) option_dereference(&(*ptr)->option, MDL); if ((*ptr) -> expression) expression_dereference (&(*ptr) -> expression, file, line); if ((*ptr) -> next) option_cache_dereference (&((*ptr) -> next), file, line); /* Put it back on the free list... */ (*ptr) -> expression = (struct expression *)free_option_caches; free_option_caches = *ptr; dmalloc_reuse (free_option_caches, (char *)0, 0, 0); } if ((*ptr) -> refcnt < 0) { log_error ("%s(%d): negative refcnt!", file, line); #if defined (DEBUG_RC_HISTORY) dump_rc_history (*ptr); #endif #if defined (POINTER_DEBUG) abort (); #else *ptr = (struct option_cache *)0; return 0; #endif } *ptr = (struct option_cache *)0; return 1; } int hashed_option_state_dereference (universe, state, file, line) struct universe *universe; struct option_state *state; const char *file; int line; { pair *heads; pair cp, next; int i; /* Get the pointer to the array of hash table bucket heads. */ heads = (pair *)(state -> universes [universe -> index]); if (!heads) return 0; /* For each non-null head, loop through all the buckets dereferencing the attached option cache structures and freeing the buckets. */ for (i = 0; i < OPTION_HASH_SIZE; i++) { for (cp = heads [i]; cp; cp = next) { next = cp -> cdr; option_cache_dereference ((struct option_cache **)&cp -> car, file, line); free_pair (cp, file, line); } } dfree (heads, file, line); state -> universes [universe -> index] = (void *)0; return 1; } /* The 'data_string' primitive doesn't have an appension mechanism. * This function must then append a new option onto an existing buffer * by first duplicating the original buffer and appending the desired * values, followed by coping the new value into place. */ int append_option(struct data_string *dst, struct universe *universe, struct option *option, struct data_string *src) { struct data_string tmp; if (src->len == 0 && option->format[0] != 'Z') return 0; memset(&tmp, 0, sizeof(tmp)); /* Allocate a buffer to hold existing data, the current option's * tag and length, and the option's content. */ if (!buffer_allocate(&tmp.buffer, (dst->len + universe->length_size + universe->tag_size + src->len), MDL)) { /* XXX: This kills all options presently stored in the * destination buffer. This is the way the original code * worked, and assumes an 'all or nothing' approach to * eg encapsulated option spaces. It may or may not be * desirable. */ data_string_forget(dst, MDL); return 0; } tmp.data = tmp.buffer->data; /* Copy the existing data off the destination. */ if (dst->len != 0) memcpy(tmp.buffer->data, dst->data, dst->len); tmp.len = dst->len; /* Place the new option tag and length. */ (*universe->store_tag)(tmp.buffer->data + tmp.len, option->code); tmp.len += universe->tag_size; (*universe->store_length)(tmp.buffer->data + tmp.len, src->len); tmp.len += universe->length_size; /* Copy the option contents onto the end. */ memcpy(tmp.buffer->data + tmp.len, src->data, src->len); tmp.len += src->len; /* Play the shell game. */ data_string_forget(dst, MDL); data_string_copy(dst, &tmp, MDL); data_string_forget(&tmp, MDL); return 1; } int store_option(struct data_string *result, struct universe *universe, struct packet *packet, struct lease *lease, struct client_state *client_state, struct option_state *in_options, struct option_state *cfg_options, struct binding_scope **scope, struct option_cache *oc) { struct data_string tmp; struct universe *subu=NULL; int status; char *start, *end; memset(&tmp, 0, sizeof(tmp)); if (evaluate_option_cache(&tmp, packet, lease, client_state, in_options, cfg_options, scope, oc, MDL)) { /* If the option is an extended 'e'ncapsulation (not a * direct 'E'ncapsulation), append the encapsulated space * onto the currently prepared value. */ do { if (oc->option->format && oc->option->format[0] == 'e') { /* Skip forward to the universe name. */ start = strchr(oc->option->format, 'E'); if (start == NULL) break; /* Locate the name-terminating '.'. */ end = strchr(++start, '.'); /* A zero-length name is not allowed in * these kinds of encapsulations. */ if (end == NULL || start == end) break; universe_hash_lookup(&subu, universe_hash, start, end - start, MDL); if (subu == NULL) { log_error("store_option: option %d " "refers to unknown " "option space '%.*s'.", oc->option->code, (int)(end - start), start); break; } /* Append encapsulations, if any. We * already have the prepended values, so * we send those even if there are no * encapsulated options (and ->encapsulate() * returns zero). */ subu->encapsulate(&tmp, packet, lease, client_state, in_options, cfg_options, scope, subu); subu = NULL; } } while (ISC_FALSE); status = append_option(result, universe, oc->option, &tmp); data_string_forget(&tmp, MDL); return status; } return 0; } int option_space_encapsulate (result, packet, lease, client_state, in_options, cfg_options, scope, name) struct data_string *result; struct packet *packet; struct lease *lease; struct client_state *client_state; struct option_state *in_options; struct option_state *cfg_options; struct binding_scope **scope; struct data_string *name; { struct universe *u = NULL; int status = 0; universe_hash_lookup(&u, universe_hash, (const char *)name->data, name->len, MDL); if (u == NULL) { log_error("option_space_encapsulate: option space '%.*s' does " "not exist, but is configured.", (int)name->len, name->data); return status; } if (u->encapsulate != NULL) { if (u->encapsulate(result, packet, lease, client_state, in_options, cfg_options, scope, u)) status = 1; } else log_error("encapsulation requested for '%s' with no support.", name->data); return status; } /* Attempt to store any 'E'ncapsulated options that have not yet been * placed on the option buffer by the above (configuring a value in * the space over-rides any values in the child universe). * * Note that there are far fewer universes than there will ever be * options in any universe. So it is faster to traverse the * configured universes, checking if each is encapsulated in the * current universe, and if so attempting to do so. * * For each configured universe for this configuration option space, * which is encapsulated within the current universe, can not be found * by the lookup function (the universe-specific encapsulation * functions would already have stored such a value), and encapsulates * at least one option, append it. */ static int search_subencapsulation(struct data_string *result, struct packet *packet, struct lease *lease, struct client_state *client_state, struct option_state *in_options, struct option_state *cfg_options, struct binding_scope **scope, struct universe *universe) { struct data_string sub; struct universe *subu; int i, status = 0; memset(&sub, 0, sizeof(sub)); for (i = 0 ; i < cfg_options->universe_count ; i++) { subu = universes[i]; if (subu == NULL) log_fatal("Impossible condition at %s:%d.", MDL); if (subu->enc_opt != NULL && subu->enc_opt->universe == universe && subu->enc_opt->format != NULL && subu->enc_opt->format[0] == 'E' && lookup_option(universe, cfg_options, subu->enc_opt->code) == NULL && subu->encapsulate(&sub, packet, lease, client_state, in_options, cfg_options, scope, subu)) { if (append_option(result, universe, subu->enc_opt, &sub)) status = 1; data_string_forget(&sub, MDL); } } return status; } int hashed_option_space_encapsulate (result, packet, lease, client_state, in_options, cfg_options, scope, universe) struct data_string *result; struct packet *packet; struct lease *lease; struct client_state *client_state; struct option_state *in_options; struct option_state *cfg_options; struct binding_scope **scope; struct universe *universe; { pair p, *hash; int status; int i; if (universe -> index >= cfg_options -> universe_count) return 0; hash = cfg_options -> universes [universe -> index]; if (!hash) return 0; /* For each hash bucket, and each configured option cache within * that bucket, append the option onto the buffer in encapsulated * format appropriate to the universe. */ status = 0; for (i = 0; i < OPTION_HASH_SIZE; i++) { for (p = hash [i]; p; p = p -> cdr) { if (store_option(result, universe, packet, lease, client_state, in_options, cfg_options, scope, (struct option_cache *)p->car)) status = 1; } } if (search_subencapsulation(result, packet, lease, client_state, in_options, cfg_options, scope, universe)) status = 1; return status; } int nwip_option_space_encapsulate (result, packet, lease, client_state, in_options, cfg_options, scope, universe) struct data_string *result; struct packet *packet; struct lease *lease; struct client_state *client_state; struct option_state *in_options; struct option_state *cfg_options; struct binding_scope **scope; struct universe *universe; { pair ocp; int status; static struct option_cache *no_nwip; struct data_string ds; struct option_chain_head *head; if (universe -> index >= cfg_options -> universe_count) return 0; head = ((struct option_chain_head *) cfg_options -> universes [nwip_universe.index]); if (!head) return 0; status = 0; for (ocp = head -> first; ocp; ocp = ocp -> cdr) { if (store_option (result, universe, packet, lease, client_state, in_options, cfg_options, scope, (struct option_cache *)ocp -> car)) status = 1; } /* If there's no data, the nwip suboption is supposed to contain a suboption saying there's no data. */ if (!status) { if (!no_nwip) { unsigned one = 1; static unsigned char nni [] = { 1, 0 }; memset (&ds, 0, sizeof ds); ds.data = nni; ds.len = 2; if (option_cache_allocate (&no_nwip, MDL)) data_string_copy (&no_nwip -> data, &ds, MDL); if (!option_code_hash_lookup(&no_nwip->option, nwip_universe.code_hash, &one, 0, MDL)) log_fatal("Nwip option hash does not contain " "1 (%s:%d).", MDL); } if (no_nwip) { if (store_option (result, universe, packet, lease, client_state, in_options, cfg_options, scope, no_nwip)) status = 1; } } else { memset (&ds, 0, sizeof ds); /* If we have nwip options, the first one has to be the nwip-exists-in-option-area option. */ if (!buffer_allocate (&ds.buffer, result -> len + 2, MDL)) { data_string_forget (result, MDL); return 0; } ds.data = &ds.buffer -> data [0]; ds.buffer -> data [0] = 2; ds.buffer -> data [1] = 0; memcpy (&ds.buffer -> data [2], result -> data, result -> len); data_string_forget (result, MDL); data_string_copy (result, &ds, MDL); data_string_forget (&ds, MDL); } return status; } /* We don't want to use MRns_name_pton()...it doesn't tell us how many bytes * it has consumed, and it plays havoc with our escapes. * * So this function does DNS encoding, and returns either the number of * octects consumed (on success), or -1 on failure. */ static int fqdn_encode(unsigned char *dst, int dstlen, const unsigned char *src, int srclen) { unsigned char *out; int i, j, len, outlen=0; out = dst; for (i = 0, j = 0 ; i < srclen ; i = j) { while ((j < srclen) && (src[j] != '.') && (src[j] != '\0')) j++; len = j - i; if ((outlen + 1 + len) > dstlen) return -1; *out++ = len; outlen++; /* We only do one FQDN, ending in one root label. */ if (len == 0) return outlen; memcpy(out, src + i, len); out += len; outlen += len; /* Advance past the root label. */ j++; } if ((outlen + 1) > dstlen) return -1; /* Place the root label. */ *out++ = 0; outlen++; return outlen; } int fqdn_option_space_encapsulate (result, packet, lease, client_state, in_options, cfg_options, scope, universe) struct data_string *result; struct packet *packet; struct lease *lease; struct client_state *client_state; struct option_state *in_options; struct option_state *cfg_options; struct binding_scope **scope; struct universe *universe; { pair ocp; struct data_string results [FQDN_SUBOPTION_COUNT + 1]; int status = 1; int i; unsigned len; struct buffer *bp = (struct buffer *)0; struct option_chain_head *head; /* If there's no FQDN universe, don't encapsulate. */ if (fqdn_universe.index >= cfg_options -> universe_count) return 0; head = ((struct option_chain_head *) cfg_options -> universes [fqdn_universe.index]); if (!head) return 0; /* Figure out the values of all the suboptions. */ memset (results, 0, sizeof results); for (ocp = head -> first; ocp; ocp = ocp -> cdr) { struct option_cache *oc = (struct option_cache *)(ocp -> car); if (oc -> option -> code > FQDN_SUBOPTION_COUNT) continue; /* No need to check the return code, we check the length later */ (void) evaluate_option_cache (&results[oc->option->code], packet, lease, client_state, in_options, cfg_options, scope, oc, MDL); } /* We add a byte for the flags field. * We add two bytes for the two RCODE fields. * We add a byte because we will prepend a label count. * We add a byte because the input len doesn't count null termination, * and we will add a root label. */ len = 5 + results [FQDN_FQDN].len; /* Save the contents of the option in a buffer. */ if (!buffer_allocate (&bp, len, MDL)) { log_error ("no memory for option buffer."); status = 0; goto exit; } buffer_reference (&result -> buffer, bp, MDL); result -> len = 3; result -> data = &bp -> data [0]; memset (&bp -> data [0], 0, len); /* XXX: The server should set bit 4 (yes, 4, not 3) to 1 if it is * not going to perform any ddns updates. The client should set the * bit if it doesn't want the server to perform any updates. * The problem is at this layer of abstraction we have no idea if * the caller is a client or server. * * See RFC4702, Section 3.1, 'The "N" bit'. * * if (?) * bp->data[0] |= 8; */ if (results [FQDN_NO_CLIENT_UPDATE].len && results [FQDN_NO_CLIENT_UPDATE].data [0]) bp -> data [0] |= 2; if (results [FQDN_SERVER_UPDATE].len && results [FQDN_SERVER_UPDATE].data [0]) bp -> data [0] |= 1; if (results [FQDN_RCODE1].len) bp -> data [1] = results [FQDN_RCODE1].data [0]; if (results [FQDN_RCODE2].len) bp -> data [2] = results [FQDN_RCODE2].data [0]; if (results [FQDN_ENCODED].len && results [FQDN_ENCODED].data [0]) { bp->data[0] |= 4; if (results [FQDN_FQDN].len) { i = fqdn_encode(&bp->data[3], len - 3, results[FQDN_FQDN].data, results[FQDN_FQDN].len); if (i < 0) { status = 0; goto exit; } result->len += i; result->terminated = 0; } } else { if (results [FQDN_FQDN].len) { memcpy (&bp -> data [3], results [FQDN_FQDN].data, results [FQDN_FQDN].len); result -> len += results [FQDN_FQDN].len; result -> terminated = 0; } } exit: for (i = 1; i <= FQDN_SUBOPTION_COUNT; i++) { if (results [i].len) data_string_forget (&results [i], MDL); } buffer_dereference (&bp, MDL); if (!status) data_string_forget(result, MDL); return status; } /* * Trap invalid attempts to inspect FQND6 contents. */ struct option_cache * lookup_fqdn6_option(struct universe *universe, struct option_state *options, unsigned code) { log_fatal("Impossible condition at %s:%d.", MDL); return NULL; } /* * Trap invalid attempts to save options directly to FQDN6 rather than FQDN. */ void save_fqdn6_option(struct universe *universe, struct option_state *options, struct option_cache *oc, isc_boolean_t appendp) { log_fatal("Impossible condition at %s:%d.", MDL); } /* * Trap invalid attempts to delete an option out of the FQDN6 universe. */ void delete_fqdn6_option(struct universe *universe, struct option_state *options, int code) { log_fatal("Impossible condition at %s:%d.", MDL); } /* Shill to the DHCPv4 fqdn option cache any attempts to traverse the * V6's option cache entry. * * This function is called speculatively by dhclient to setup * environment variables. But it would have already called the * foreach on the normal fqdn universe, so this is superfluous. */ void fqdn6_option_space_foreach(struct packet *packet, struct lease *lease, struct client_state *client_state, struct option_state *in_options, struct option_state *cfg_options, struct binding_scope **scope, struct universe *u, void *stuff, void (*func)(struct option_cache *, struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct binding_scope **, struct universe *, void *)) { /* Pretend it is empty. */ return; } /* Turn the FQDN option space into a DHCPv6 FQDN option buffer. */ int fqdn6_option_space_encapsulate(struct data_string *result, struct packet *packet, struct lease *lease, struct client_state *client_state, struct option_state *in_options, struct option_state *cfg_options, struct binding_scope **scope, struct universe *universe) { pair ocp; struct option_chain_head *head; struct option_cache *oc; unsigned char *data; int i, len, rval = 0, count; struct data_string results[FQDN_SUBOPTION_COUNT + 1]; if (fqdn_universe.index >= cfg_options->universe_count) return 0; head = ((struct option_chain_head *) cfg_options->universes[fqdn_universe.index]); if (head == NULL) return 0; memset(results, 0, sizeof(results)); for (ocp = head->first ; ocp != NULL ; ocp = ocp->cdr) { oc = (struct option_cache *)(ocp->car); if (oc->option->code > FQDN_SUBOPTION_COUNT) log_fatal("Impossible condition at %s:%d.", MDL); /* No need to check the return code, we check the length later */ (void) evaluate_option_cache(&results[oc->option->code], packet, lease, client_state, in_options, cfg_options, scope, oc, MDL); } /* We add a byte for the flags field at the start of the option. * We add a byte because we will prepend a label count. * We add a byte because the input length doesn't include a trailing * NULL, and we will add a root label. */ len = results[FQDN_FQDN].len + 3; if (!buffer_allocate(&result->buffer, len, MDL)) { log_error("No memory for virtual option buffer."); goto exit; } data = result->buffer->data; result->data = data; /* The first byte is the flags field. */ result->len = 1; data[0] = 0; /* XXX: The server should set bit 3 (yes, 3, not 4) to 1 if we * are not going to perform any DNS updates. The problem is * that at this layer of abstraction, we do not know if the caller * is the client or the server. * * See RFC4704 Section 4.1, 'The "N" bit'. * * if (?) * data[0] |= 4; */ if (results[FQDN_NO_CLIENT_UPDATE].len && results[FQDN_NO_CLIENT_UPDATE].data[0]) data[0] |= 2; if (results[FQDN_SERVER_UPDATE].len && results[FQDN_SERVER_UPDATE].data[0]) data[0] |= 1; /* If there is no name, we're done. */ if (results[FQDN_FQDN].len == 0) { rval = 1; goto exit; } /* Convert textual representation to DNS format. */ count = fqdn_encode(data + 1, len - 1, results[FQDN_FQDN].data, results[FQDN_FQDN].len); if (count < 0) { rval = 0; data_string_forget(result, MDL); goto exit; } result->len += count; result->terminated = 0; /* Success! */ rval = 1; exit: for (i = 1 ; i <= FQDN_SUBOPTION_COUNT ; i++) { if (results[i].len) data_string_forget(&results[i], MDL); } return rval; } /* Read the DHCPv6 FQDN option's contents into the FQDN virtual space. */ int fqdn6_universe_decode(struct option_state *options, const unsigned char *buffer, unsigned length, struct universe *u) { struct buffer *bp = NULL; unsigned char *first_dot; int len, hlen, dlen; /* The FQDN option has to be at least 1 byte long. */ if (length < 1) return 0; /* Save the contents of the option in a buffer. There are 3 * one-byte values we record from the packet. The input is * DNS encoded and to be safe we'll assume that each character * is non-printable and will be converted to an escaped number: * "\\nnn". Yes, we'll have dead space pretty much all the time * but the alternative is to basically dry run the conversion * first to calculate the precise size or reallocate to a smaller * buffer later, either of which is a bigger performance hit than * just doing a generous allocation. */ unsigned bp_size = 3 + (length * 4); if (!buffer_allocate(&bp, bp_size, MDL)) { log_error("No memory for dhcp6.fqdn option buffer."); return 0; } /* The v6 FQDN is always 'encoded' per DNS. */ bp->data[0] = 1; if (!save_option_buffer(&fqdn_universe, options, bp, bp->data, 1, FQDN_ENCODED, 0)) goto error; /* XXX: We need to process 'The "N" bit'. */ if (buffer[0] & 1) /* server-update. */ bp->data[2] = 1; else bp->data[2] = 0; if (!save_option_buffer(&fqdn_universe, options, bp, bp->data + 2, 1, FQDN_SERVER_UPDATE, 0)) goto error; if (buffer[0] & 2) /* no-client-update. */ bp->data[1] = 1; else bp->data[1] = 0; if (!save_option_buffer(&fqdn_universe, options, bp, bp->data + 1, 1, FQDN_NO_CLIENT_UPDATE, 0)) goto error; /* Convert the domain name to textual representation for config. */ len = MRns_name_ntop(buffer + 1, (char *)bp->data + 3, bp_size - 3); if (len == -1) { log_error("Unable to convert dhcp6.fqdn domain name to " "printable form."); goto error; } /* Save the domain name. */ if (len > 0) { unsigned char *fqdn_start = bp->data + 3; if (!save_option_buffer(&fqdn_universe, options, bp, fqdn_start, len, FQDN_FQDN, 1)) goto error; first_dot = (unsigned char *)strchr((char *)fqdn_start, '.'); if (first_dot != NULL) { hlen = first_dot - fqdn_start; dlen = len - hlen; } else { hlen = len; dlen = 0; } if (!save_option_buffer(&fqdn_universe, options, bp, fqdn_start, len, FQDN_FQDN, 1) || ((hlen > 0) && !save_option_buffer(&fqdn_universe, options, bp, fqdn_start, hlen, FQDN_HOSTNAME, 0)) || ((dlen > 0) && !save_option_buffer(&fqdn_universe, options, bp, first_dot, dlen, FQDN_DOMAINNAME, 0))) goto error; } buffer_dereference(&bp, MDL); return 1; error: buffer_dereference(&bp, MDL); return 0; } void option_space_foreach (struct packet *packet, struct lease *lease, struct client_state *client_state, struct option_state *in_options, struct option_state *cfg_options, struct binding_scope **scope, struct universe *u, void *stuff, void (*func) (struct option_cache *, struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct binding_scope **, struct universe *, void *)) { if (u -> foreach) (*u -> foreach) (packet, lease, client_state, in_options, cfg_options, scope, u, stuff, func); } void suboption_foreach (struct packet *packet, struct lease *lease, struct client_state *client_state, struct option_state *in_options, struct option_state *cfg_options, struct binding_scope **scope, struct universe *u, void *stuff, void (*func) (struct option_cache *, struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct binding_scope **, struct universe *, void *), struct option_cache *oc, const char *vsname) { struct universe *universe = find_option_universe (oc -> option, vsname); if (universe -> foreach) (*universe -> foreach) (packet, lease, client_state, in_options, cfg_options, scope, universe, stuff, func); } void hashed_option_space_foreach (struct packet *packet, struct lease *lease, struct client_state *client_state, struct option_state *in_options, struct option_state *cfg_options, struct binding_scope **scope, struct universe *u, void *stuff, void (*func) (struct option_cache *, struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct binding_scope **, struct universe *, void *)) { pair *hash; int i; struct option_cache *oc; if (cfg_options -> universe_count <= u -> index) return; hash = cfg_options -> universes [u -> index]; if (!hash) return; for (i = 0; i < OPTION_HASH_SIZE; i++) { pair p; /* XXX save _all_ options! XXX */ for (p = hash [i]; p; p = p -> cdr) { oc = (struct option_cache *)p -> car; (*func) (oc, packet, lease, client_state, in_options, cfg_options, scope, u, stuff); } } } void save_linked_option(struct universe *universe, struct option_state *options, struct option_cache *oc, isc_boolean_t appendp) { pair *tail; struct option_chain_head *head; struct option_cache **ocloc; if (universe -> index >= options -> universe_count) return; head = ((struct option_chain_head *) options -> universes [universe -> index]); if (!head) { if (!option_chain_head_allocate (((struct option_chain_head **) &options -> universes [universe -> index]), MDL)) return; head = ((struct option_chain_head *) options -> universes [universe -> index]); } /* Find the tail of the list. */ for (tail = &head -> first; *tail; tail = &((*tail) -> cdr)) { ocloc = (struct option_cache **)&(*tail)->car; if (oc->option->code == (*ocloc)->option->code) { if (appendp) { do { ocloc = &(*ocloc)->next; } while (*ocloc != NULL); } else { option_cache_dereference(ocloc, MDL); } option_cache_reference(ocloc, oc, MDL); return; } } *tail = cons (0, 0); if (*tail) { option_cache_reference ((struct option_cache **) (&(*tail) -> car), oc, MDL); } } int linked_option_space_encapsulate (result, packet, lease, client_state, in_options, cfg_options, scope, universe) struct data_string *result; struct packet *packet; struct lease *lease; struct client_state *client_state; struct option_state *in_options; struct option_state *cfg_options; struct binding_scope **scope; struct universe *universe; { int status = 0; pair oc; struct option_chain_head *head; if (universe -> index >= cfg_options -> universe_count) return status; head = ((struct option_chain_head *) cfg_options -> universes [universe -> index]); if (!head) return status; for (oc = head -> first; oc; oc = oc -> cdr) { if (store_option (result, universe, packet, lease, client_state, in_options, cfg_options, scope, (struct option_cache *)(oc -> car))) status = 1; } if (search_subencapsulation(result, packet, lease, client_state, in_options, cfg_options, scope, universe)) status = 1; return status; } void delete_linked_option (universe, options, code) struct universe *universe; struct option_state *options; int code; { pair *tail, tmp = (pair)0; struct option_chain_head *head; if (universe -> index >= options -> universe_count) return; head = ((struct option_chain_head *) options -> universes [universe -> index]); if (!head) return; for (tail = &head -> first; *tail; tail = &((*tail) -> cdr)) { if (code == ((struct option_cache *)(*tail) -> car) -> option -> code) { tmp = (*tail) -> cdr; option_cache_dereference ((struct option_cache **) (&(*tail) -> car), MDL); dfree (*tail, MDL); (*tail) = tmp; break; } } } struct option_cache *lookup_linked_option (universe, options, code) struct universe *universe; struct option_state *options; unsigned code; { pair oc; struct option_chain_head *head; if (universe -> index >= options -> universe_count) return 0; head = ((struct option_chain_head *) options -> universes [universe -> index]); if (!head) return 0; for (oc = head -> first; oc; oc = oc -> cdr) { if (code == ((struct option_cache *)(oc -> car)) -> option -> code) { return (struct option_cache *)(oc -> car); } } return (struct option_cache *)0; } int linked_option_state_dereference (universe, state, file, line) struct universe *universe; struct option_state *state; const char *file; int line; { return (option_chain_head_dereference ((struct option_chain_head **) (&state -> universes [universe -> index]), MDL)); } void linked_option_space_foreach (struct packet *packet, struct lease *lease, struct client_state *client_state, struct option_state *in_options, struct option_state *cfg_options, struct binding_scope **scope, struct universe *u, void *stuff, void (*func) (struct option_cache *, struct packet *, struct lease *, struct client_state *, struct option_state *, struct option_state *, struct binding_scope **, struct universe *, void *)) { pair car; struct option_chain_head *head; if (u -> index >= cfg_options -> universe_count) return; head = ((struct option_chain_head *) cfg_options -> universes [u -> index]); if (!head) return; for (car = head -> first; car; car = car -> cdr) { (*func) ((struct option_cache *)(car -> car), packet, lease, client_state, in_options, cfg_options, scope, u, stuff); } } void do_packet (interface, packet, len, from_port, from, hfrom) struct interface_info *interface; struct dhcp_packet *packet; unsigned len; unsigned int from_port; struct iaddr from; struct hardware *hfrom; { struct option_cache *op; struct packet *decoded_packet; #if defined (DEBUG_MEMORY_LEAKAGE) unsigned long previous_outstanding = dmalloc_outstanding; #endif #if defined (TRACING) trace_inpacket_stash(interface, packet, len, from_port, from, hfrom); #endif decoded_packet = NULL; if (!packet_allocate(&decoded_packet, MDL)) { log_error("do_packet: no memory for incoming packet!"); return; } decoded_packet->raw = packet; decoded_packet->packet_length = len; decoded_packet->client_port = from_port; decoded_packet->client_addr = from; interface_reference(&decoded_packet->interface, interface, MDL); decoded_packet->haddr = hfrom; if (packet->hlen > sizeof packet->chaddr) { packet_dereference(&decoded_packet, MDL); log_info("Discarding packet with bogus hlen."); return; } /* Allocate packet->options now so it is non-null for all packets */ decoded_packet->options_valid = 0; if (!option_state_allocate (&decoded_packet->options, MDL)) { packet_dereference(&decoded_packet, MDL); return; } /* If there's an option buffer, try to parse it. */ if (decoded_packet->packet_length >= DHCP_FIXED_NON_UDP + 4) { if (!parse_options(decoded_packet)) { packet_dereference (&decoded_packet, MDL); return; } if (decoded_packet->options_valid && (op = lookup_option(&dhcp_universe, decoded_packet->options, DHO_DHCP_MESSAGE_TYPE))) { struct data_string dp; memset(&dp, 0, sizeof dp); evaluate_option_cache(&dp, decoded_packet, NULL, NULL, decoded_packet->options, NULL, NULL, op, MDL); if (dp.len > 0) decoded_packet->packet_type = dp.data[0]; else decoded_packet->packet_type = 0; data_string_forget(&dp, MDL); } } if (validate_packet(decoded_packet) != 0) { if (decoded_packet->packet_type) dhcp(decoded_packet); else bootp(decoded_packet); } /* If the caller kept the packet, they'll have upped the refcnt. */ packet_dereference(&decoded_packet, MDL); #if defined (DEBUG_MEMORY_LEAKAGE) log_info("generation %ld: %ld new, %ld outstanding, %ld long-term", dmalloc_generation, dmalloc_outstanding - previous_outstanding, dmalloc_outstanding, dmalloc_longterm); dmalloc_dump_outstanding(); #endif #if defined (DEBUG_RC_HISTORY_EXHAUSTIVELY) dump_rc_history(0); #endif } int packet6_len_okay(const char *packet, int len) { if (len < 1) { return 0; } if ((packet[0] == DHCPV6_RELAY_FORW) || (packet[0] == DHCPV6_RELAY_REPL)) { if (len >= offsetof(struct dhcpv6_relay_packet, options)) { return 1; } else { return 0; } } else { if (len >= offsetof(struct dhcpv6_packet, options)) { return 1; } else { return 0; } } } #ifdef DHCPv6 void do_packet6(struct interface_info *interface, const char *packet, int len, int from_port, const struct iaddr *from, isc_boolean_t was_unicast) { unsigned char msg_type; const struct dhcpv6_packet *msg; const struct dhcpv6_relay_packet *relay; #ifdef DHCP4o6 const struct dhcpv4_over_dhcpv6_packet *msg46; #endif struct packet *decoded_packet; #if defined (DEBUG_MEMORY_LEAKAGE) unsigned long previous_outstanding = dmalloc_outstanding; #endif if (!packet6_len_okay(packet, len)) { log_info("do_packet6: " "short packet from %s port %d, len %d, dropped", piaddr(*from), from_port, len); return; } decoded_packet = NULL; if (!packet_allocate(&decoded_packet, MDL)) { log_error("do_packet6: no memory for incoming packet."); return; } if (!option_state_allocate(&decoded_packet->options, MDL)) { log_error("do_packet6: no memory for options."); packet_dereference(&decoded_packet, MDL); return; } /* IPv4 information, already set to 0 */ /* decoded_packet->packet_type = 0; */ /* memset(&decoded_packet->haddr, 0, sizeof(decoded_packet->haddr)); */ /* decoded_packet->circuit_id = NULL; */ /* decoded_packet->circuit_id_len = 0; */ /* decoded_packet->remote_id = NULL; */ /* decoded_packet->remote_id_len = 0; */ decoded_packet->raw = (struct dhcp_packet *)packet; decoded_packet->packet_length = (unsigned)len; decoded_packet->client_port = from_port; decoded_packet->client_addr = *from; interface_reference(&decoded_packet->interface, interface, MDL); decoded_packet->unicast = was_unicast; msg_type = packet[0]; if ((msg_type == DHCPV6_RELAY_FORW) || (msg_type == DHCPV6_RELAY_REPL)) { int relaylen = (int)(offsetof(struct dhcpv6_relay_packet, options)); relay = (const struct dhcpv6_relay_packet *)packet; decoded_packet->dhcpv6_msg_type = relay->msg_type; /* relay-specific data */ decoded_packet->dhcpv6_hop_count = relay->hop_count; memcpy(&decoded_packet->dhcpv6_link_address, relay->link_address, sizeof(relay->link_address)); memcpy(&decoded_packet->dhcpv6_peer_address, relay->peer_address, sizeof(relay->peer_address)); if (!parse_option_buffer(decoded_packet->options, relay->options, len - relaylen, &dhcpv6_universe)) { /* no logging here, as parse_option_buffer() logs all cases where it fails */ packet_dereference(&decoded_packet, MDL); return; } #ifdef DHCP4o6 } else if ((msg_type == DHCPV6_DHCPV4_QUERY) || (msg_type == DHCPV6_DHCPV4_RESPONSE)) { int msglen = (int)(offsetof(struct dhcpv4_over_dhcpv6_packet, options)); msg46 = (struct dhcpv4_over_dhcpv6_packet *)packet; decoded_packet->dhcpv6_msg_type = msg46->msg_type; /* message-specific data */ memcpy(decoded_packet->dhcp4o6_flags, msg46->flags, sizeof(decoded_packet->dhcp4o6_flags)); if (!parse_option_buffer(decoded_packet->options, msg46->options, len - msglen, &dhcpv6_universe)) { /* no logging here, as parse_option_buffer() logs all cases where it fails */ packet_dereference(&decoded_packet, MDL); return; } #endif } else { int msglen = (int)(offsetof(struct dhcpv6_packet, options)); msg = (const struct dhcpv6_packet *)packet; decoded_packet->dhcpv6_msg_type = msg->msg_type; /* message-specific data */ memcpy(decoded_packet->dhcpv6_transaction_id, msg->transaction_id, sizeof(decoded_packet->dhcpv6_transaction_id)); if (!parse_option_buffer(decoded_packet->options, msg->options, len - msglen, &dhcpv6_universe)) { /* no logging here, as parse_option_buffer() logs all cases where it fails */ packet_dereference(&decoded_packet, MDL); return; } } dhcpv6(decoded_packet); packet_dereference(&decoded_packet, MDL); #if defined (DEBUG_MEMORY_LEAKAGE) log_info("generation %ld: %ld new, %ld outstanding, %ld long-term", dmalloc_generation, dmalloc_outstanding - previous_outstanding, dmalloc_outstanding, dmalloc_longterm); dmalloc_dump_outstanding(); #endif #if defined (DEBUG_RC_HISTORY_EXHAUSTIVELY) dump_rc_history(0); #endif } #endif /* DHCPv6 */ int pretty_escape(char **dst, char *dend, const unsigned char **src, const unsigned char *send) { int count = 0; /* If there aren't as many bytes left as there are in the source * buffer, don't even bother entering the loop. */ if (dst == NULL || dend == NULL || src == NULL || send == NULL || *dst == NULL || *src == NULL || (*dst >= dend) || (*src > send) || ((send - *src) > (dend - *dst))) return -1; for ( ; *src < send ; (*src)++) { if (!isascii (**src) || !isprint (**src)) { /* Skip trailing NUL. */ if ((*src + 1) != send || **src != '\0') { if (*dst + 4 > dend) return -1; sprintf(*dst, "\\%03o", **src); (*dst) += 4; count += 4; } } else if (**src == '"' || **src == '\'' || **src == '$' || **src == '`' || **src == '\\' || **src == '|' || **src == '&') { if (*dst + 2 > dend) return -1; **dst = '\\'; (*dst)++; **dst = **src; (*dst)++; count += 2; } else { if (*dst + 1 > dend) return -1; **dst = **src; (*dst)++; count++; } } return count; } static int pretty_text(char **dst, char *dend, const unsigned char **src, const unsigned char *send, int emit_quotes) { int count; if (dst == NULL || dend == NULL || src == NULL || send == NULL || *dst == NULL || *src == NULL || ((*dst + (emit_quotes ? 2 : 0)) > dend) || (*src > send)) return -1; if (emit_quotes) { **dst = '"'; (*dst)++; } /* dend-1 leaves 1 byte for the closing quote. */ count = pretty_escape(dst, dend - (emit_quotes ? 1 : 0), src, send); if (count == -1) return -1; if (emit_quotes && (*dst < dend)) { **dst = '"'; (*dst)++; /* Includes quote prior to pretty_escape(); */ count += 2; } return count; } static int pretty_domain(char **dst, char *dend, const unsigned char **src, const unsigned char *send) { const unsigned char *tend; int count = 2; int tsiz, status; if (dst == NULL || dend == NULL || src == NULL || send == NULL || *dst == NULL || *src == NULL || ((*dst + 2) > dend) || (*src >= send)) return -1; **dst = '"'; (*dst)++; do { /* Continue loop until end of src buffer. */ if (*src >= send) break; /* Consume tag size. */ tsiz = **src; (*src)++; /* At root, finis. */ if (tsiz == 0) break; tend = (*src) + tsiz; /* If the tag exceeds the source buffer, it's illegal. * This should also trap compression pointers (which should * not be in these buffers). */ if (tend > send) return -1; /* dend-2 leaves room for a trailing dot and quote. */ status = pretty_escape(dst, dend-2, src, tend); if ((status == -1) || ((*dst + 2) > dend)) return -1; **dst = '.'; (*dst)++; count += status + 1; } while(1); **dst = '"'; (*dst)++; return count; } /* * Add the option identified with the option number and data to the * options state. */ int add_option(struct option_state *options, unsigned int option_num, void *data, unsigned int data_len) { struct option_cache *oc; struct option *option; /* INSIST(options != NULL); */ /* INSIST(data != NULL); */ option = NULL; if (!option_code_hash_lookup(&option, dhcp_universe.code_hash, &option_num, 0, MDL)) { log_error("Attempting to add unknown option %d.", option_num); return 0; } oc = NULL; if (!option_cache_allocate(&oc, MDL)) { log_error("No memory for option cache adding %s (option %d).", option->name, option_num); return 0; } if (!make_const_data(&oc->expression, data, data_len, 0, 0, MDL)) { log_error("No memory for constant data adding %s (option %d).", option->name, option_num); option_cache_dereference(&oc, MDL); return 0; } option_reference(&(oc->option), option, MDL); save_option(&dhcp_universe, options, oc); option_cache_dereference(&oc, MDL); return 1; } /** * Checks if received BOOTP/DHCPv4 packet is sane * * @param packet received, decoded packet * * @return 1 if packet is sane, 0 if it is not */ int validate_packet(struct packet *packet) { struct option_cache *oc = NULL; oc = lookup_option (&dhcp_universe, packet->options, DHO_DHCP_CLIENT_IDENTIFIER); if (oc) { /* Let's check if client-identifier is sane */ if (oc->data.len == 0) { log_debug("Dropped DHCPv4 packet with zero-length client-id"); return (0); } else if (oc->data.len == 1) { /* * RFC2132, section 9.14 states that minimum length of client-id * is 2. We will allow single-character client-ids for now (for * backwards compatibility), but warn the user that support for * this is against the standard. */ log_debug("Accepted DHCPv4 packet with one-character client-id - " "a future version of ISC DHCP will reject this"); } } else { /* * If hlen is 0 we don't have any identifier, we warn the user * but continue processing the packet as we can. */ if (packet->raw->hlen == 0) { log_debug("Received DHCPv4 packet without client-id" " option and empty hlen field."); } } /* @todo: Add checks for other received options */ return (1); } /*! * * \brief Parse a vendor option (option 43) * * After the server has parsed most of the options and presented the result * to the user the user can set the proper vendor option space using * vendor-option-space in the config file and then cause this routine to be * called via parse-vendor-option in the config file. This routine will * then try and find the proper universe for the vendor-option-space and * parse the vendor option string based on that universe. * * If the information isn't available (no vendor space, no universe for the * vendor space, no vendor option in the options) or the decode fails we * simply ignore the option and continue processing. * * \param packet - structure to hold information about the packet being * processed * \param lease - lease structure * \param client_state * \param in_options - The incoming options, we expect to find the * vendor-option (option 43, containing the string * to parse) there. We shall attach decoded options * there. * \param out_options - The options we have added as we process the packet. * We expect to find the vendor-option-space there and * use that to find the name of the vendor universe to use * \param scope * * \return - void as there isn't much we can do about failures. */ void parse_vendor_option(packet, lease, client_state, in_options, out_options, scope) struct packet *packet; struct lease *lease; struct client_state *client_state; struct option_state *in_options; struct option_state *out_options; struct binding_scope **scope; { struct option_cache *oc = NULL; struct data_string name; struct option *option = NULL; unsigned int code = DHO_VENDOR_ENCAPSULATED_OPTIONS; /* check if we are processing a packet, if not we can return */ if ((packet == NULL) || (in_options == NULL) || (out_options == NULL)) return; /* Do we have any vendor option spaces? */ if (vendor_cfg_option == NULL) return; /* See if the admin has set a vendor option space name */ oc = lookup_option(vendor_cfg_option->universe, out_options, vendor_cfg_option->code); if (oc == NULL) return; memset(&name, 0, sizeof(name)); (void) evaluate_option_cache(&name, packet, lease, client_state, in_options, out_options, scope, oc, MDL); /* No name, all done */ if (name.len == 0) return; /* Get any vendor option information from the request */ oc = lookup_option(&dhcp_universe, in_options, code); /* No vendor option, all done */ if ((oc == NULL) || (oc->data.len == 0)) { data_string_forget(&name, MDL); return; } /* Get the proper option to pass to the parse routine */ option_code_hash_lookup(&option, dhcp_universe.code_hash, &code, 0, MDL); /* Now that we have the data from the vendor option and a vendor * option space try to parse things. On success the parsed options * will be added to the in_options list for future use. A return * return of 1 indicates success, but not much we can do on error */ (void) parse_encapsulated_suboptions(in_options, option, oc->data.data, oc->data.len, &dhcp_universe, (const char *)name.data); /* Lastly clean up any left overs */ data_string_forget(&name, MDL); option_dereference(&option, MDL); return; } dhcp-4.4.1/common/packet.c000644 000765 000024 00000027145 13243301226 015627 0ustar00tmarkstaff000000 000000 /* packet.c Packet assembly code, originally contributed by Archie Cobbs. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1996-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * * This code was originally contributed by Archie Cobbs, and is still * very similar to that contribution, although the packet checksum code * has been hacked significantly with the help of quite a few ISC DHCP * users, without whose gracious and thorough help the checksum code would * still be disabled. */ #include "dhcpd.h" #if defined (PACKET_ASSEMBLY) || defined (PACKET_DECODING) #include "includes/netinet/ip.h" #include "includes/netinet/udp.h" #include "includes/netinet/if_ether.h" #endif /* PACKET_ASSEMBLY || PACKET_DECODING */ /* Compute the easy part of the checksum on a range of bytes. */ u_int32_t checksum (buf, nbytes, sum) unsigned char *buf; unsigned nbytes; u_int32_t sum; { unsigned i; #ifdef DEBUG_CHECKSUM log_debug ("checksum (%x %d %x)", (unsigned)buf, nbytes, sum); #endif /* Checksum all the pairs of bytes first... */ for (i = 0; i < (nbytes & ~1U); i += 2) { #ifdef DEBUG_CHECKSUM_VERBOSE log_debug ("sum = %x", sum); #endif sum += (u_int16_t) ntohs(*((u_int16_t *)(buf + i))); /* Add carry. */ if (sum > 0xFFFF) sum -= 0xFFFF; } /* If there's a single byte left over, checksum it, too. Network byte order is big-endian, so the remaining byte is the high byte. */ if (i < nbytes) { #ifdef DEBUG_CHECKSUM_VERBOSE log_debug ("sum = %x", sum); #endif sum += buf [i] << 8; /* Add carry. */ if (sum > 0xFFFF) sum -= 0xFFFF; } return sum; } /* Finish computing the checksum, and then put it into network byte order. */ u_int32_t wrapsum (sum) u_int32_t sum; { #ifdef DEBUG_CHECKSUM log_debug ("wrapsum (%x)", sum); #endif sum = ~sum & 0xFFFF; #ifdef DEBUG_CHECKSUM_VERBOSE log_debug ("sum = %x", sum); #endif #ifdef DEBUG_CHECKSUM log_debug ("wrapsum returns %x", htons (sum)); #endif return htons(sum); } #ifdef PACKET_ASSEMBLY void assemble_hw_header (interface, buf, bufix, to) struct interface_info *interface; unsigned char *buf; unsigned *bufix; struct hardware *to; { switch (interface->hw_address.hbuf[0]) { #if defined(HAVE_TR_SUPPORT) case HTYPE_IEEE802: assemble_tr_header(interface, buf, bufix, to); break; #endif #if defined (DEC_FDDI) case HTYPE_FDDI: assemble_fddi_header(interface, buf, bufix, to); break; #endif case HTYPE_INFINIBAND: log_error("Attempt to assemble hw header for infiniband"); break; case HTYPE_ETHER: default: assemble_ethernet_header(interface, buf, bufix, to); break; } } /* UDP header and IP header assembled together for convenience. */ void assemble_udp_ip_header (interface, buf, bufix, from, to, port, data, len) struct interface_info *interface; unsigned char *buf; unsigned *bufix; u_int32_t from; u_int32_t to; u_int32_t port; unsigned char *data; unsigned len; { struct ip ip; struct udphdr udp; memset (&ip, 0, sizeof ip); /* Fill out the IP header */ IP_V_SET (&ip, 4); IP_HL_SET (&ip, 20); ip.ip_tos = IPTOS_LOWDELAY; ip.ip_len = htons(sizeof(ip) + sizeof(udp) + len); ip.ip_id = 0; ip.ip_off = 0; ip.ip_ttl = 128; ip.ip_p = IPPROTO_UDP; ip.ip_sum = 0; ip.ip_src.s_addr = from; ip.ip_dst.s_addr = to; /* Checksum the IP header... */ ip.ip_sum = wrapsum (checksum ((unsigned char *)&ip, sizeof ip, 0)); /* Copy the ip header into the buffer... */ memcpy (&buf [*bufix], &ip, sizeof ip); *bufix += sizeof ip; /* Fill out the UDP header */ udp.uh_sport = local_port; /* XXX */ udp.uh_dport = port; /* XXX */ #if defined(RELAY_PORT) /* Change to relay port defined if sending to server */ if (relay_port && (port == htons(67))) { udp.uh_sport = relay_port; } #endif udp.uh_ulen = htons(sizeof(udp) + len); memset (&udp.uh_sum, 0, sizeof udp.uh_sum); /* Compute UDP checksums, including the ``pseudo-header'', the UDP header and the data. */ udp.uh_sum = wrapsum (checksum ((unsigned char *)&udp, sizeof udp, checksum (data, len, checksum ((unsigned char *) &ip.ip_src, 2 * sizeof ip.ip_src, IPPROTO_UDP + (u_int32_t) ntohs (udp.uh_ulen))))); /* Copy the udp header into the buffer... */ memcpy (&buf [*bufix], &udp, sizeof udp); *bufix += sizeof udp; } #endif /* PACKET_ASSEMBLY */ #ifdef PACKET_DECODING /* Decode a hardware header... */ /* Support for ethernet, TR and FDDI * Doesn't support infiniband yet as the supported oses shouldn't get here */ ssize_t decode_hw_header (interface, buf, bufix, from) struct interface_info *interface; unsigned char *buf; unsigned bufix; struct hardware *from; { switch(interface->hw_address.hbuf[0]) { #if defined (HAVE_TR_SUPPORT) case HTYPE_IEEE802: return (decode_tr_header(interface, buf, bufix, from)); #endif #if defined (DEC_FDDI) case HTYPE_FDDI: return (decode_fddi_header(interface, buf, bufix, from)); #endif case HTYPE_INFINIBAND: log_error("Attempt to decode hw header for infiniband"); return (0); case HTYPE_ETHER: default: return (decode_ethernet_header(interface, buf, bufix, from)); } } /*! * * \brief UDP header and IP header decoded together for convenience. * * Attempt to decode the UDP and IP headers and, if necessary, checksum * the packet. * * \param inteface - the interface on which the packet was recevied * \param buf - a pointer to the buffer for the received packet * \param bufix - where to start processing the buffer, previous * routines may have processed parts of the buffer already * \param from - space to return the address of the packet sender * \param buflen - remaining length of the buffer, this will have been * decremented by bufix by the caller * \param rbuflen - space to return the length of the payload from the udp * header * \param csum_ready - indication if the checksum is valid for use * non-zero indicates the checksum should be validated * * \return - the index to the first byte of the udp payload (that is the * start of the DHCP packet */ ssize_t decode_udp_ip_header(struct interface_info *interface, unsigned char *buf, unsigned bufix, struct sockaddr_in *from, unsigned buflen, unsigned *rbuflen, int csum_ready) { unsigned char *data; struct ip ip; struct udphdr udp; unsigned char *upp; u_int32_t ip_len, ulen, pkt_len; static unsigned int ip_packets_seen = 0; static unsigned int ip_packets_bad_checksum = 0; static unsigned int udp_packets_seen = 0; static unsigned int udp_packets_bad_checksum = 0; static unsigned int udp_packets_length_checked = 0; static unsigned int udp_packets_length_overflow = 0; unsigned len; /* Assure there is at least an IP header there. */ if (sizeof(ip) > buflen) return -1; /* Copy the IP header into a stack aligned structure for inspection. * There may be bits in the IP header that we're not decoding, so we * copy out the bits we grok and skip ahead by ip.ip_hl * 4. */ upp = buf + bufix; memcpy(&ip, upp, sizeof(ip)); ip_len = (*upp & 0x0f) << 2; upp += ip_len; /* Check packet lengths are within the buffer: * first the ip header (ip_len) * then the packet length from the ip header (pkt_len) * then the udp header (ip_len + sizeof(udp) * We are liberal in what we accept, the udp payload should fit within * pkt_len, but we only check against the full buffer size. */ pkt_len = ntohs(ip.ip_len); if ((ip_len > buflen) || (pkt_len > buflen) || ((ip_len + sizeof(udp)) > buflen)) return -1; /* Copy the UDP header into a stack aligned structure for inspection. */ memcpy(&udp, upp, sizeof(udp)); #ifdef USERLAND_FILTER /* Is it a UDP packet? */ if (ip.ip_p != IPPROTO_UDP) return -1; /* Is it to the port we're serving? */ #if defined(RELAY_PORT) if ((udp.uh_dport != local_port) && ((relay_port == 0) || (udp.uh_dport != relay_port))) #else if (udp.uh_dport != local_port) #endif return -1; #endif /* USERLAND_FILTER */ ulen = ntohs(udp.uh_ulen); if (ulen < sizeof(udp)) return -1; udp_packets_length_checked++; /* verify that the payload length from the udp packet fits in the buffer */ if ((ip_len + ulen) > buflen) { udp_packets_length_overflow++; if (((udp_packets_length_checked > 4) && (udp_packets_length_overflow != 0)) && ((udp_packets_length_checked / udp_packets_length_overflow) < 2)) { log_info("%u udp packets in %u too long - dropped", udp_packets_length_overflow, udp_packets_length_checked); udp_packets_length_overflow = 0; udp_packets_length_checked = 0; } return -1; } /* If at least 5 with less than 50% bad, start over */ if (udp_packets_length_checked > 4) { udp_packets_length_overflow = 0; udp_packets_length_checked = 0; } /* Check the IP header checksum - it should be zero. */ ip_packets_seen++; if (wrapsum (checksum (buf + bufix, ip_len, 0))) { ++ip_packets_bad_checksum; if (((ip_packets_seen > 4) && (ip_packets_bad_checksum != 0)) && ((ip_packets_seen / ip_packets_bad_checksum) < 2)) { log_info ("%u bad IP checksums seen in %u packets", ip_packets_bad_checksum, ip_packets_seen); ip_packets_seen = ip_packets_bad_checksum = 0; } return -1; } /* If at least 5 with less than 50% bad, start over */ if (ip_packets_seen > 4) { ip_packets_bad_checksum = 0; ip_packets_seen = 0; } /* Copy out the IP source address... */ memcpy(&from->sin_addr, &ip.ip_src, 4); data = upp + sizeof(udp); len = ulen - sizeof(udp); /* UDP check sum may be optional (udp.uh_sum == 0) or not ready if checksum * offloading is in use */ udp_packets_seen++; if (udp.uh_sum && csum_ready) { /* Check the UDP header checksum - since the received packet header * contains the UDP checksum calculated by the transmitter, calculating * it now should come out to zero. */ if (wrapsum(checksum((unsigned char *)&udp, sizeof(udp), checksum(data, len, checksum((unsigned char *)&ip.ip_src, 8, IPPROTO_UDP + ulen))))) { udp_packets_bad_checksum++; if (((udp_packets_seen > 4) && (udp_packets_bad_checksum != 0)) && ((udp_packets_seen / udp_packets_bad_checksum) < 2)) { log_debug ("%u bad udp checksums in %u packets", udp_packets_bad_checksum, udp_packets_seen); udp_packets_seen = udp_packets_bad_checksum = 0; } return -1; } } /* If at least 5 with less than 50% bad, start over */ if (udp_packets_seen > 4) { udp_packets_bad_checksum = 0; udp_packets_seen = 0; } /* Copy out the port... */ memcpy (&from -> sin_port, &udp.uh_sport, sizeof udp.uh_sport); /* Save the length of the UDP payload. */ if (rbuflen != NULL) *rbuflen = len; /* Return the index to the UDP payload. */ return ip_len + sizeof udp; } #endif /* PACKET_DECODING */ dhcp-4.4.1/common/parse.c000644 000765 000024 00000423234 13243301226 015471 0ustar00tmarkstaff000000 000000 /* parse.c Common parser code for dhcpd and dhclient. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1995-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #include #include struct collection default_collection = { NULL, "default", NULL }; struct collection *collections = &default_collection; /* Enumerations can be specified in option formats, and are used for parsing, so we define the routines that manage them here. */ struct enumeration *enumerations; void add_enumeration (struct enumeration *enumeration) { enumeration -> next = enumerations; enumerations = enumeration; } struct enumeration *find_enumeration (const char *name, int length) { struct enumeration *e; for (e = enumerations; e; e = e -> next) if (strlen (e -> name) == length && !memcmp (e -> name, name, (unsigned)length)) return e; return (struct enumeration *)0; } struct enumeration_value *find_enumeration_value (const char *name, int length, unsigned *widthp, const char *value) { struct enumeration *e; int i; e = find_enumeration (name, length); if (e) { if (widthp != NULL) *widthp = e->width; for (i = 0; e -> values [i].name; i++) { if (!strcmp (value, e -> values [i].name)) return &e -> values [i]; } } return (struct enumeration_value *)0; } /* Skip to the semicolon ending the current statement. If we encounter braces, the matching closing brace terminates the statement. */ void skip_to_semi (cfile) struct parse *cfile; { skip_to_rbrace(cfile, 0); } /* Skips everything from the current point upto (and including) the given number of right braces. If we encounter a semicolon but haven't seen a left brace, consume it and return. This lets us skip over: statement; statement foo bar { } statement foo bar { statement { } } statement} ...et cetera. */ void skip_to_rbrace (cfile, brace_count) struct parse *cfile; int brace_count; { enum dhcp_token token; const char *val; #if defined (DEBUG_TOKENS) log_error("skip_to_rbrace: %d\n", brace_count); #endif do { token = peek_token(&val, NULL, cfile); if (token == RBRACE) { if (brace_count > 0) { --brace_count; } if (brace_count == 0) { /* Eat the brace and return. */ skip_token(&val, NULL, cfile); return; } } else if (token == LBRACE) { brace_count++; } else if (token == SEMI && (brace_count == 0)) { /* Eat the semicolon and return. */ skip_token(&val, NULL, cfile); return; } else if (token == EOL) { /* EOL only happens when parsing /etc/resolv.conf, and we treat it like a semicolon because the resolv.conf file is line-oriented. */ skip_token(&val, NULL, cfile); return; } /* Eat the current token */ token = next_token(&val, NULL, cfile); } while (token != END_OF_FILE); } int parse_semi (cfile) struct parse *cfile; { enum dhcp_token token; const char *val; token = next_token (&val, (unsigned *)0, cfile); if (token != SEMI) { parse_warn (cfile, "semicolon expected."); skip_to_semi (cfile); return 0; } return 1; } /* string-parameter :== STRING SEMI */ int parse_string (cfile, sptr, lptr) struct parse *cfile; char **sptr; unsigned *lptr; { const char *val; enum dhcp_token token; char *s; unsigned len; token = next_token (&val, &len, cfile); if (token != STRING) { parse_warn (cfile, "expecting a string"); skip_to_semi (cfile); return 0; } s = (char *)dmalloc (len + 1, MDL); if (!s) log_fatal ("no memory for string %s.", val); memcpy (s, val, len + 1); if (!parse_semi (cfile)) { dfree (s, MDL); return 0; } if (sptr) *sptr = s; else dfree (s, MDL); if (lptr) *lptr = len; return 1; } /* * hostname :== IDENTIFIER * | IDENTIFIER DOT * | hostname DOT IDENTIFIER */ char *parse_host_name (cfile) struct parse *cfile; { const char *val; enum dhcp_token token; unsigned len = 0; char *s; char *t; pair c = (pair)0; int ltid = 0; /* Read a dotted hostname... */ do { /* Read a token, which should be an identifier. */ token = peek_token (&val, (unsigned *)0, cfile); if (!is_identifier (token) && token != NUMBER) break; skip_token(&val, (unsigned *)0, cfile); /* Store this identifier... */ if (!(s = (char *)dmalloc (strlen (val) + 1, MDL))) log_fatal ("can't allocate temp space for hostname."); strcpy (s, val); c = cons ((caddr_t)s, c); len += strlen (s) + 1; /* Look for a dot; if it's there, keep going, otherwise we're done. */ token = peek_token (&val, (unsigned *)0, cfile); if (token == DOT) { token = next_token (&val, (unsigned *)0, cfile); ltid = 1; } else ltid = 0; } while (token == DOT); /* Should be at least one token. */ if (!len) return (char *)0; /* Assemble the hostname together into a string. */ if (!(s = (char *)dmalloc (len + ltid, MDL))) log_fatal ("can't allocate space for hostname."); t = s + len + ltid; *--t = 0; if (ltid) *--t = '.'; while (c) { pair cdr = c -> cdr; unsigned l = strlen ((char *)(c -> car)); t -= l; memcpy (t, (char *)(c -> car), l); /* Free up temp space. */ dfree (c -> car, MDL); dfree (c, MDL); c = cdr; if (t != s) *--t = '.'; } return s; } /* ip-addr-or-hostname :== ip-address | hostname ip-address :== NUMBER DOT NUMBER DOT NUMBER DOT NUMBER Parse an ip address or a hostname. If uniform is zero, put in an expr_substring node to limit hostnames that evaluate to more than one IP address. Note that RFC1123 permits hostnames to consist of all digits, making it difficult to quickly disambiguate them from ip addresses. */ int parse_ip_addr_or_hostname (expr, cfile, uniform) struct expression **expr; struct parse *cfile; int uniform; { const char *val; enum dhcp_token token; unsigned char addr [4]; unsigned len = sizeof addr; char *name; struct expression *x = (struct expression *)0; int ipaddr = 0; token = peek_token (&val, (unsigned *)0, cfile); if (token == NUMBER) { /* * a hostname may be numeric, but domain names must * start with a letter, so we can disambiguate by * looking ahead a few tokens. we save the parse * context first, and restore it after we know what * we're dealing with. */ save_parse_state(cfile); skip_token(NULL, NULL, cfile); if (next_token(NULL, NULL, cfile) == DOT && next_token(NULL, NULL, cfile) == NUMBER) ipaddr = 1; restore_parse_state(cfile); if (ipaddr && parse_numeric_aggregate (cfile, addr, &len, DOT, 10, 8)) return make_const_data (expr, addr, len, 0, 1, MDL); } if (is_identifier (token) || token == NUMBER) { name = parse_host_name (cfile); if (!name) return 0; if (!make_host_lookup (expr, name)) { dfree(name, MDL); return 0; } dfree(name, MDL); if (!uniform) { if (!make_limit (&x, *expr, 4)) return 0; expression_dereference (expr, MDL); *expr = x; } } else { if (token != RBRACE && token != LBRACE) token = next_token (&val, (unsigned *)0, cfile); parse_warn (cfile, "%s (%d): expecting IP address or hostname", val, token); if (token != SEMI) skip_to_semi (cfile); return 0; } return 1; } /* * ip-address :== NUMBER DOT NUMBER DOT NUMBER DOT NUMBER */ int parse_ip_addr (cfile, addr) struct parse *cfile; struct iaddr *addr; { addr -> len = 4; if (parse_numeric_aggregate (cfile, addr -> iabuf, &addr -> len, DOT, 10, 8)) return 1; return 0; } /* * Return true if every character in the string is hexadecimal. */ static int is_hex_string(const char *s) { while (*s != '\0') { if (!isxdigit((int)*s)) { return 0; } s++; } return 1; } /* * ip-address6 :== (complicated set of rules) * * See section 2.2 of RFC 1884 for details. * * We are lazy for this. We pull numbers, names, colons, and dots * together and then throw the resulting string at the inet_pton() * function. */ int parse_ip6_addr(struct parse *cfile, struct iaddr *addr) { enum dhcp_token token; const char *val; int val_len; char v6[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; int v6_len; /* * First token is non-raw. This way we eat any whitespace before * our IPv6 address begins, like one would expect. */ token = peek_token(&val, NULL, cfile); /* * Gather symbols. */ v6_len = 0; for (;;) { if ((((token == NAME) || (token == NUMBER_OR_NAME)) && is_hex_string(val)) || (token == NUMBER) || (token == TOKEN_ADD) || (token == DOT) || (token == COLON)) { next_raw_token(&val, NULL, cfile); val_len = strlen(val); if ((v6_len + val_len) >= sizeof(v6)) { parse_warn(cfile, "Invalid IPv6 address."); skip_to_semi(cfile); return 0; } memcpy(v6+v6_len, val, val_len); v6_len += val_len; } else { break; } token = peek_raw_token(&val, NULL, cfile); } v6[v6_len] = '\0'; /* * Use inet_pton() for actual work. */ if (inet_pton(AF_INET6, v6, addr->iabuf) <= 0) { parse_warn(cfile, "Invalid IPv6 address."); skip_to_semi(cfile); return 0; } addr->len = 16; return 1; } /* * Same as parse_ip6_addr() above, but returns the value in the * expression rather than in an address structure. */ int parse_ip6_addr_expr(struct expression **expr, struct parse *cfile) { struct iaddr addr; if (!parse_ip6_addr(cfile, &addr)) { return 0; } return make_const_data(expr, addr.iabuf, addr.len, 0, 1, MDL); } /* * ip6-prefix :== ip6-address "/" NUMBER */ int parse_ip6_prefix(struct parse *cfile, struct iaddr *addr, u_int8_t *plen) { enum dhcp_token token; const char *val; int n; if (!parse_ip6_addr(cfile, addr)) { return 0; } token = next_token(&val, NULL, cfile); if (token != SLASH) { parse_warn(cfile, "Slash expected."); if (token != SEMI) skip_to_semi(cfile); return 0; } token = next_token(&val, NULL, cfile); if (token != NUMBER) { parse_warn(cfile, "Number expected."); if (token != SEMI) skip_to_semi(cfile); return 0; } n = atoi(val); if ((n < 0) || (n > 128)) { parse_warn(cfile, "Invalid IPv6 prefix length."); skip_to_semi(cfile); return 0; } if (!is_cidr_mask_valid(addr, n)) { parse_warn(cfile, "network mask too short."); skip_to_semi(cfile); return 0; } *plen = n; return 1; } /* * ip-address-with-subnet :== ip-address | * ip-address "/" NUMBER */ int parse_ip_addr_with_subnet(cfile, match) struct parse *cfile; struct iaddrmatch *match; { const char *val, *orig; enum dhcp_token token; int prefixlen; int fflen; unsigned char newval, warnmask=0; if (parse_ip_addr(cfile, &match->addr)) { /* default to host mask */ prefixlen = match->addr.len * 8; token = peek_token(&val, NULL, cfile); if (token == SLASH) { skip_token(&val, NULL, cfile); token = next_token(&val, NULL, cfile); if (token != NUMBER) { parse_warn(cfile, "Invalid CIDR prefix length:" " expecting a number."); return 0; } prefixlen = atoi(val); if (prefixlen < 0 || prefixlen > (match->addr.len * 8)) { parse_warn(cfile, "subnet prefix is out of " "range [0..%d].", match->addr.len * 8); return 0; } } /* construct a suitable mask field */ /* copy length */ match->mask.len = match->addr.len; /* count of 0xff bytes in mask */ fflen = prefixlen / 8; /* set leading mask */ memset(match->mask.iabuf, 0xff, fflen); /* set zeroes */ if (fflen < match->mask.len) { match->mask.iabuf[fflen] = "\x00\x80\xc0\xe0\xf0\xf8\xfc\xfe"[prefixlen % 8]; memset(match->mask.iabuf+fflen+1, 0x00, match->mask.len - fflen - 1); /* AND-out insignificant bits from supplied netmask. */ orig = piaddr(match->addr); do { newval = match->addr.iabuf[fflen] & match->mask.iabuf[fflen]; if (newval != match->addr.iabuf[fflen]) { warnmask = 1; match->addr.iabuf[fflen] = newval; } } while (++fflen < match->mask.len); if (warnmask) { log_error("Warning: Extraneous bits removed " "in address component of %s/%d.", orig, prefixlen); log_error("New value: %s/%d.", piaddr(match->addr), prefixlen); } } return 1; } parse_warn(cfile, "expecting ip-address or ip-address/prefixlen"); return 0; /* let caller pick up pieces */ } /* * hardware-parameter :== HARDWARE hardware-type colon-separated-hex-list SEMI * hardware-type :== ETHERNET | TOKEN_RING | TOKEN_FDDI | INFINIBAND * Note that INFINIBAND may not be useful for some items, such as classification * as the hardware address won't always be available. */ void parse_hardware_param (cfile, hardware) struct parse *cfile; struct hardware *hardware; { const char *val; enum dhcp_token token; unsigned hlen; unsigned char *t; token = next_token(&val, NULL, cfile); switch (token) { case ETHERNET: hardware->hbuf[0] = HTYPE_ETHER; break; case TOKEN_RING: hardware->hbuf[0] = HTYPE_IEEE802; break; case TOKEN_FDDI: hardware->hbuf[0] = HTYPE_FDDI; break; case TOKEN_INFINIBAND: hardware->hbuf[0] = HTYPE_INFINIBAND; break; default: if (!strncmp(val, "unknown-", 8)) { hardware->hbuf[0] = atoi(&val[8]); } else { parse_warn(cfile, "expecting a network hardware type"); skip_to_semi(cfile); return; } } /* Parse the hardware address information. Technically, it would make a lot of sense to restrict the length of the data we'll accept here to the length of a particular hardware address type. Unfortunately, there are some broken clients out there that put bogus data in the chaddr buffer, and we accept that data in the lease file rather than simply failing on such clients. Yuck. */ hlen = 0; token = peek_token(&val, NULL, cfile); if (token == SEMI) { hardware->hlen = 1; goto out; } t = parse_numeric_aggregate(cfile, NULL, &hlen, COLON, 16, 8); if (t == NULL) { hardware->hlen = 1; return; } if (hlen + 1 > sizeof(hardware->hbuf)) { dfree(t, MDL); parse_warn(cfile, "hardware address too long"); } else { hardware->hlen = hlen + 1; memcpy((unsigned char *)&hardware->hbuf[1], t, hlen); if (hlen + 1 < sizeof(hardware->hbuf)) memset(&hardware->hbuf[hlen + 1], 0, (sizeof(hardware->hbuf)) - hlen - 1); dfree(t, MDL); } out: token = next_token(&val, NULL, cfile); if (token != SEMI) { parse_warn(cfile, "expecting semicolon."); skip_to_semi(cfile); } } /* lease-time :== NUMBER SEMI */ void parse_lease_time (cfile, timep) struct parse *cfile; TIME *timep; { const char *val; enum dhcp_token token; u_int32_t num; token = next_token (&val, (unsigned *)0, cfile); if (token != NUMBER) { parse_warn (cfile, "Expecting numeric lease time"); skip_to_semi (cfile); return; } convert_num(cfile, (unsigned char *)&num, val, 10, 32); /* Unswap the number - convert_num returns stuff in NBO. */ *timep = ntohl(num); parse_semi (cfile); } /* No BNF for numeric aggregates - that's defined by the caller. What this function does is to parse a sequence of numbers separated by the token specified in separator. If max is zero, any number of numbers will be parsed; otherwise, exactly max numbers are expected. Base and size tell us how to internalize the numbers once they've been tokenized. buf - A pointer to space to return the parsed value, if it is null then the function will allocate space for the return. max - The maximum number of items to store. If zero there is no maximum. When buf is null and the function needs to allocate space it will do an allocation of max size at the beginning if max is non zero. If max is zero then the allocation will be done later, after the function has determined the size necessary for the incoming string. returns NULL on errors or a pointer to the value string on success. The pointer will either be buf if it was non-NULL or newly allocated space if buf was NULL */ unsigned char *parse_numeric_aggregate (cfile, buf, max, separator, base, size) struct parse *cfile; unsigned char *buf; unsigned *max; int separator; int base; unsigned size; { const char *val; enum dhcp_token token; unsigned char *bufp = buf, *s, *t; unsigned count = 0; pair c = (pair)0; if (!bufp && *max) { bufp = (unsigned char *)dmalloc (*max * size / 8, MDL); if (!bufp) log_fatal ("no space for numeric aggregate"); } s = bufp; do { if (count) { token = peek_token (&val, (unsigned *)0, cfile); if (token != separator) { if (!*max) break; if (token != RBRACE && token != LBRACE) token = next_token (&val, (unsigned *)0, cfile); parse_warn (cfile, "too few numbers."); if (token != SEMI) skip_to_semi (cfile); /* free bufp if it was allocated */ if ((bufp != NULL) && (bufp != buf)) dfree(bufp, MDL); return (unsigned char *)0; } skip_token(&val, (unsigned *)0, cfile); } token = next_token (&val, (unsigned *)0, cfile); if (token == END_OF_FILE) { parse_warn (cfile, "unexpected end of file"); break; } /* Allow NUMBER_OR_NAME if base is 16. */ if (token != NUMBER && (base != 16 || token != NUMBER_OR_NAME)) { parse_warn (cfile, "expecting numeric value."); skip_to_semi (cfile); /* free bufp if it was allocated */ if ((bufp != NULL) && (bufp != buf)) dfree(bufp, MDL); /* free any linked numbers we may have allocated */ while (c) { pair cdr = c->cdr; dfree(c->car, MDL); dfree(c, MDL); c = cdr; } return (NULL); } /* If we can, convert the number now; otherwise, build a linked list of all the numbers. */ if (s) { convert_num (cfile, s, val, base, size); s += size / 8; } else { t = (unsigned char *)dmalloc (strlen (val) + 1, MDL); if (!t) log_fatal ("no temp space for number."); strcpy ((char *)t, val); c = cons ((caddr_t)t, c); } } while (++count != *max); /* If we had to cons up a list, convert it now. */ if (c) { /* * No need to cleanup bufp, to get here we didn't allocate * bufp above */ bufp = (unsigned char *)dmalloc (count * size / 8, MDL); if (!bufp) log_fatal ("no space for numeric aggregate."); s = bufp + count - size / 8; *max = count; } while (c) { pair cdr = c -> cdr; convert_num (cfile, s, (char *)(c -> car), base, size); s -= size / 8; /* Free up temp space. */ dfree (c -> car, MDL); dfree (c, MDL); c = cdr; } return bufp; } void convert_num (cfile, buf, str, base, size) struct parse *cfile; unsigned char *buf; const char *str; int base; unsigned size; { const unsigned char *ptr = (const unsigned char *)str; int negative = 0; u_int32_t val = 0; int tval; int max; if (*ptr == '-') { negative = 1; ++ptr; } /* If base wasn't specified, figure it out from the data. */ if (!base) { if (ptr [0] == '0') { if (ptr [1] == 'x') { base = 16; ptr += 2; } else if (isascii (ptr [1]) && isdigit (ptr [1])) { base = 8; ptr += 1; } else { base = 10; } } else { base = 10; } } do { tval = *ptr++; /* XXX assumes ASCII... */ if (tval >= 'a') tval = tval - 'a' + 10; else if (tval >= 'A') tval = tval - 'A' + 10; else if (tval >= '0') tval -= '0'; else { parse_warn (cfile, "Bogus number: %s.", str); break; } if (tval >= base) { parse_warn (cfile, "Bogus number %s: digit %d not in base %d", str, tval, base); break; } val = val * base + tval; } while (*ptr); if (negative) max = (1 << (size - 1)); else max = (1 << (size - 1)) + ((1 << (size - 1)) - 1); if (val > max) { switch (base) { case 8: parse_warn (cfile, "%s%lo exceeds max (%d) for precision.", negative ? "-" : "", (unsigned long)val, max); break; case 16: parse_warn (cfile, "%s%lx exceeds max (%d) for precision.", negative ? "-" : "", (unsigned long)val, max); break; default: parse_warn (cfile, "%s%lu exceeds max (%d) for precision.", negative ? "-" : "", (unsigned long)val, max); break; } } if (negative) { switch (size) { case 8: *buf = -(unsigned long)val; break; case 16: putShort (buf, -(long)val); break; case 32: putLong (buf, -(long)val); break; default: parse_warn (cfile, "Unexpected integer size: %d\n", size); break; } } else { switch (size) { case 8: *buf = (u_int8_t)val; break; case 16: putUShort (buf, (u_int16_t)val); break; case 32: putULong (buf, val); break; default: parse_warn (cfile, "Unexpected integer size: %d\n", size); break; } } } /* * date :== NUMBER NUMBER SLASH NUMBER SLASH NUMBER * NUMBER COLON NUMBER COLON NUMBER | * NUMBER NUMBER SLASH NUMBER SLASH NUMBER * NUMBER COLON NUMBER COLON NUMBER NUMBER | * EPOCH NUMBER | * NEVER * * Dates are stored in UTC or with a timezone offset; first number is day * of week; next is year/month/day; next is hours:minutes:seconds on a * 24-hour clock, followed by the timezone offset in seconds, which is * optional. */ /* * just parse the date * any trailing semi must be consumed by the caller of this routine */ TIME parse_date_core(cfile) struct parse *cfile; { int guess; int tzoff, year, mon, mday, hour, min, sec; const char *val; enum dhcp_token token; static int months[11] = { 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; /* "never", "epoch" or day of week */ token = peek_token(&val, NULL, cfile); if (token == NEVER) { skip_token(&val, NULL, cfile); /* consume NEVER */ return(MAX_TIME); } /* This indicates 'local' time format. */ if (token == EPOCH) { skip_token(&val, NULL, cfile); /* consume EPOCH */ token = peek_token(&val, NULL, cfile); if (token != NUMBER) { if (token != SEMI) skip_token(&val, NULL, cfile); parse_warn(cfile, "Seconds since epoch expected."); return((TIME)0); } skip_token(&val, NULL, cfile); /* consume number */ guess = atoi(val); return((TIME)guess); } if (token != NUMBER) { if (token != SEMI) skip_token(&val, NULL, cfile); parse_warn(cfile, "numeric day of week expected."); return((TIME)0); } skip_token(&val, NULL, cfile); /* consume day of week */ /* we are not using this for anything */ /* Year... */ token = peek_token(&val, NULL, cfile); if (token != NUMBER) { if (token != SEMI) skip_token(&val, NULL, cfile); parse_warn(cfile, "numeric year expected."); return((TIME)0); } skip_token(&val, NULL, cfile); /* consume year */ /* Note: the following is not a Y2K bug - it's a Y1.9K bug. Until somebody invents a time machine, I think we can safely disregard it. This actually works around a stupid Y2K bug that was present in a very early beta release of dhcpd. */ year = atoi(val); if (year > 1900) year -= 1900; /* Slash separating year from month... */ token = peek_token(&val, NULL, cfile); if (token != SLASH) { if (token != SEMI) skip_token(&val, NULL, cfile); parse_warn(cfile, "expected slash separating year from month."); return((TIME)0); } skip_token(&val, NULL, cfile); /* consume SLASH */ /* Month... */ token = peek_token(&val, NULL, cfile); if (token != NUMBER) { if (token != SEMI) skip_token(&val, NULL, cfile); parse_warn(cfile, "numeric month expected."); return((TIME)0); } skip_token(&val, NULL, cfile); /* consume month */ mon = atoi(val) - 1; /* Slash separating month from day... */ token = peek_token(&val, NULL, cfile); if (token != SLASH) { if (token != SEMI) skip_token(&val, NULL, cfile); parse_warn(cfile, "expected slash separating month from day."); return((TIME)0); } skip_token(&val, NULL, cfile); /* consume SLASH */ /* Day of month... */ token = peek_token(&val, NULL, cfile); if (token != NUMBER) { if (token != SEMI) skip_token(&val, NULL, cfile); parse_warn(cfile, "numeric day of month expected."); return((TIME)0); } skip_token(&val, NULL, cfile); /* consume day of month */ mday = atoi(val); /* Hour... */ token = peek_token(&val, NULL, cfile); if (token != NUMBER) { if (token != SEMI) skip_token(&val, NULL, cfile); parse_warn(cfile, "numeric hour expected."); return((TIME)0); } skip_token(&val, NULL, cfile); /* consume hour */ hour = atoi(val); /* Colon separating hour from minute... */ token = peek_token(&val, NULL, cfile); if (token != COLON) { if (token != SEMI) skip_token(&val, NULL, cfile); parse_warn(cfile, "expected colon separating hour from minute."); return((TIME)0); } skip_token(&val, NULL, cfile); /* consume colon */ /* Minute... */ token = peek_token(&val, NULL, cfile); if (token != NUMBER) { if (token != SEMI) skip_token(&val, NULL, cfile); parse_warn(cfile, "numeric minute expected."); return((TIME)0); } skip_token(&val, NULL, cfile); /* consume minute */ min = atoi(val); /* Colon separating minute from second... */ token = peek_token(&val, NULL, cfile); if (token != COLON) { if (token != SEMI) skip_token(&val, NULL, cfile); parse_warn(cfile, "expected colon separating minute from second."); return((TIME)0); } skip_token(&val, NULL, cfile); /* consume colon */ /* Second... */ token = peek_token(&val, NULL, cfile); if (token != NUMBER) { if (token != SEMI) skip_token(&val, NULL, cfile); parse_warn(cfile, "numeric second expected."); return((TIME)0); } skip_token(&val, NULL, cfile); /* consume second */ sec = atoi(val); tzoff = 0; token = peek_token(&val, NULL, cfile); if (token == NUMBER) { skip_token(&val, NULL, cfile); /* consume tzoff */ tzoff = atoi(val); } else if (token != SEMI) { skip_token(&val, NULL, cfile); parse_warn(cfile, "Time zone offset or semicolon expected."); return((TIME)0); } /* If the year is 2038 or greater return the max time to avoid * overflow issues. We could try and be more precise but there * doesn't seem to be a good reason to worry about it and waste * the cpu looking at the rest of the date. */ if (year >= 138) return(MAX_TIME); /* Guess the time value... */ guess = ((((((365 * (year - 70) + /* Days in years since '70 */ (year - 69) / 4 + /* Leap days since '70 */ (mon /* Days in months this year */ ? months [mon - 1] : 0) + (mon > 1 && /* Leap day this year */ !((year - 72) & 3)) + mday - 1) * 24) + /* Day of month */ hour) * 60) + min) * 60) + sec + tzoff; /* This guess could be wrong because of leap seconds or other weirdness we don't know about that the system does. For now, we're just going to accept the guess, but at some point it might be nice to do a successive approximation here to get an exact value. Even if the error is small, if the server is restarted frequently (and thus the lease database is reread), the error could accumulate into something significant. */ return((TIME)guess); } /* * Wrapper to consume the semicolon after the date * :== date semi */ TIME parse_date(cfile) struct parse *cfile; { TIME guess; guess = parse_date_core(cfile); /* Make sure the date ends in a semicolon... */ if (!parse_semi(cfile)) return((TIME)0); return(guess); } /* * option-name :== IDENTIFIER | IDENTIFIER . IDENTIFIER */ isc_result_t parse_option_name (cfile, allocate, known, opt) struct parse *cfile; int allocate; int *known; struct option **opt; { const char *val; enum dhcp_token token; char *uname; struct universe *universe; struct option *option; unsigned code; if (opt == NULL) return DHCP_R_INVALIDARG; token = next_token (&val, (unsigned *)0, cfile); if (!is_identifier (token)) { parse_warn (cfile, "expecting identifier after option keyword."); if (token != SEMI) skip_to_semi (cfile); return DHCP_R_BADPARSE; } uname = dmalloc (strlen (val) + 1, MDL); if (!uname) log_fatal ("no memory for uname information."); strcpy (uname, val); token = peek_token (&val, (unsigned *)0, cfile); if (token == DOT) { /* Go ahead and take the DOT token... */ skip_token(&val, (unsigned *)0, cfile); /* The next token should be an identifier... */ token = next_token (&val, (unsigned *)0, cfile); if (!is_identifier (token)) { parse_warn (cfile, "expecting identifier after '.'"); if (token != SEMI) skip_to_semi (cfile); return DHCP_R_BADPARSE; } /* Look up the option name hash table for the specified uname. */ universe = (struct universe *)0; if (!universe_hash_lookup (&universe, universe_hash, uname, 0, MDL)) { parse_warn (cfile, "no option space named %s.", uname); skip_to_semi (cfile); return ISC_R_NOTFOUND; } } else { /* Use the default hash table, which contains all the standard dhcp option names. */ val = uname; universe = &dhcp_universe; } /* Look up the actual option info... */ option_name_hash_lookup(opt, universe->name_hash, val, 0, MDL); option = *opt; /* If we didn't get an option structure, it's an undefined option. */ if (option) { if (known) *known = 1; /* If the option name is of the form unknown-[decimal], use * the trailing decimal value to find the option definition. * If there is no definition, construct one. This is to * support legacy use of unknown options in config files or * lease databases. */ } else if (strncasecmp(val, "unknown-", 8) == 0) { code = atoi(val+8); /* Option code 0 is always illegal for us, thanks * to the option decoder. */ if (code == 0 || code == universe->end) { parse_warn(cfile, "Option codes 0 and %u are illegal " "in the %s space.", universe->end, universe->name); skip_to_semi(cfile); dfree(uname, MDL); return ISC_R_FAILURE; } /* It's odd to think of unknown option codes as * being known, but this means we know what the * parsed name is talking about. */ if (known) *known = 1; option_code_hash_lookup(opt, universe->code_hash, &code, 0, MDL); option = *opt; /* If we did not find an option of that code, * manufacture an unknown-xxx option definition. * Its single reference will ensure that it is * deleted once the option is recycled out of * existence (by the parent). */ if (option == NULL) { option = new_option(val, MDL); option->universe = universe; option->code = code; option->format = default_option_format; option_reference(opt, option, MDL); } else log_info("option %s has been redefined as option %s. " "Please update your configs if necessary.", val, option->name); /* If we've been told to allocate, that means that this * (might) be an option code definition, so we'll create * an option structure and return it for the parent to * decide. */ } else if (allocate) { option = new_option(val, MDL); option -> universe = universe; option_reference(opt, option, MDL); } else { parse_warn(cfile, "no option named %s in space %s", val, universe->name); skip_to_semi (cfile); dfree(uname, MDL); return ISC_R_NOTFOUND; } /* Free the initial identifier token. */ dfree (uname, MDL); return ISC_R_SUCCESS; } /* IDENTIFIER [WIDTHS] SEMI * WIDTHS ~= LENGTH WIDTH NUMBER * CODE WIDTH NUMBER */ void parse_option_space_decl (cfile) struct parse *cfile; { int token; const char *val; struct universe **ua, *nu; char *nu_name; int tsize=1, lsize=1, hsize = 0; skip_token(&val, (unsigned *)0, cfile); /* Discard the SPACE token, which was checked by the caller. */ token = next_token (&val, (unsigned *)0, cfile); if (!is_identifier (token)) { parse_warn (cfile, "expecting identifier."); skip_to_semi (cfile); return; } nu = new_universe (MDL); if (!nu) log_fatal ("No memory for new option space."); /* Set up the server option universe... */ nu_name = dmalloc (strlen (val) + 1, MDL); if (!nu_name) log_fatal ("No memory for new option space name."); strcpy (nu_name, val); nu -> name = nu_name; do { token = next_token(&val, NULL, cfile); switch(token) { case SEMI: break; case CODE: token = next_token(&val, NULL, cfile); if (token != WIDTH) { parse_warn(cfile, "expecting width token."); goto bad; } token = next_token(&val, NULL, cfile); if (token != NUMBER) { parse_warn(cfile, "expecting number 1, 2, 4."); goto bad; } tsize = atoi(val); switch (tsize) { case 1: if (!hsize) hsize = BYTE_NAME_HASH_SIZE; break; case 2: if (!hsize) hsize = WORD_NAME_HASH_SIZE; break; case 4: if (!hsize) hsize = QUAD_NAME_HASH_SIZE; break; default: parse_warn(cfile, "invalid code width (%d), " "expecting a 1, 2 or 4.", tsize); goto bad; } break; case LENGTH: token = next_token(&val, NULL, cfile); if (token != WIDTH) { parse_warn(cfile, "expecting width token."); goto bad; } token = next_token(&val, NULL, cfile); if (token != NUMBER) { parse_warn(cfile, "expecting number 1 or 2."); goto bad; } lsize = atoi(val); if (lsize != 1 && lsize != 2) { parse_warn(cfile, "invalid length width (%d) " "expecting 1 or 2.", lsize); goto bad; } break; case HASH: token = next_token(&val, NULL, cfile); if (token != SIZE) { parse_warn(cfile, "expecting size token."); goto bad; } token = next_token(&val, NULL, cfile); if (token != NUMBER) { parse_warn(cfile, "expecting a 10base number"); goto bad; } /* (2^31)-1 is the highest Mersenne prime we should * probably allow... */ hsize = atoi(val); if (hsize < 0 || hsize > 0x7FFFFFFF) { parse_warn(cfile, "invalid hash length: %d", hsize); goto bad; } break; default: parse_warn(cfile, "Unexpected token."); } } while (token != SEMI); if (!hsize) hsize = DEFAULT_SPACE_HASH_SIZE; nu -> lookup_func = lookup_hashed_option; nu -> option_state_dereference = hashed_option_state_dereference; nu -> foreach = hashed_option_space_foreach; nu -> save_func = save_hashed_option; nu -> delete_func = delete_hashed_option; nu -> encapsulate = hashed_option_space_encapsulate; nu -> decode = parse_option_buffer; nu -> length_size = lsize; nu -> tag_size = tsize; switch(tsize) { case 1: nu->get_tag = getUChar; nu->store_tag = putUChar; break; case 2: nu->get_tag = getUShort; nu->store_tag = putUShort; break; case 4: nu->get_tag = getULong; nu->store_tag = putULong; break; default: log_fatal("Impossible condition at %s:%d.", MDL); } switch(lsize) { case 0: nu->get_length = NULL; nu->store_length = NULL; break; case 1: nu->get_length = getUChar; nu->store_length = putUChar; break; case 2: nu->get_length = getUShort; nu->store_length = putUShort; break; default: log_fatal("Impossible condition at %s:%d.", MDL); } nu -> index = universe_count++; if (nu -> index >= universe_max) { ua = dmalloc (universe_max * 2 * sizeof *ua, MDL); if (!ua) log_fatal ("No memory to expand option space array."); memcpy (ua, universes, universe_max * sizeof *ua); universe_max *= 2; dfree (universes, MDL); universes = ua; } universes [nu -> index] = nu; if (!option_name_new_hash(&nu->name_hash, hsize, MDL) || !option_code_new_hash(&nu->code_hash, hsize, MDL)) log_fatal("Can't allocate %s option hash table.", nu->name); universe_hash_add (universe_hash, nu -> name, 0, nu, MDL); return; bad: dfree(nu_name, MDL); dfree(nu, MDL); } /* This is faked up to look good right now. Ideally, this should do a recursive parse and allow arbitrary data structure definitions, but for now it just allows you to specify a single type, an array of single types, a sequence of types, or an array of sequences of types. ocd :== NUMBER EQUALS ocsd SEMI ocsd :== ocsd_type | ocsd_type_sequence | ARRAY OF ocsd_simple_type_sequence ocsd_type_sequence :== LBRACE ocsd_types RBRACE ocsd_simple_type_sequence :== LBRACE ocsd_simple_types RBRACE ocsd_types :== ocsd_type | ocsd_types ocsd_type ocsd_type :== ocsd_simple_type | ARRAY OF ocsd_simple_type ocsd_simple_types :== ocsd_simple_type | ocsd_simple_types ocsd_simple_type ocsd_simple_type :== BOOLEAN | INTEGER NUMBER | SIGNED INTEGER NUMBER | UNSIGNED INTEGER NUMBER | IP-ADDRESS | TEXT | STRING | ENCAPSULATE identifier */ int parse_option_code_definition (cfile, option) struct parse *cfile; struct option *option; { const char *val; enum dhcp_token token; struct option *oldopt; unsigned arrayp = 0; int recordp = 0; int no_more_in_record = 0; char tokbuf [128]; unsigned tokix = 0; char type; int is_signed; char *s; int has_encapsulation = 0; struct universe *encapsulated; /* Parse the option code. */ token = next_token (&val, (unsigned *)0, cfile); if (token != NUMBER) { parse_warn (cfile, "expecting option code number."); skip_to_semi (cfile); return 0; } option -> code = atoi (val); token = next_token (&val, (unsigned *)0, cfile); if (token != EQUAL) { parse_warn (cfile, "expecting \"=\""); skip_to_semi (cfile); return 0; } /* See if this is an array. */ token = next_token (&val, (unsigned *)0, cfile); if (token == ARRAY) { token = next_token (&val, (unsigned *)0, cfile); if (token != OF) { parse_warn (cfile, "expecting \"of\"."); skip_to_semi (cfile); return 0; } arrayp = 1; token = next_token (&val, (unsigned *)0, cfile); } if (token == LBRACE) { recordp = 1; token = next_token (&val, (unsigned *)0, cfile); } /* At this point we're expecting a data type. */ next_type: if (has_encapsulation) { parse_warn (cfile, "encapsulate must always be the last item."); skip_to_semi (cfile); return 0; } switch (token) { case ARRAY: if (arrayp) { parse_warn (cfile, "no nested arrays."); skip_to_rbrace (cfile, recordp); if (recordp) skip_to_semi (cfile); return 0; } token = next_token (&val, (unsigned *)0, cfile); if (token != OF) { parse_warn (cfile, "expecting \"of\"."); skip_to_semi (cfile); return 0; } arrayp = recordp + 1; token = next_token (&val, (unsigned *)0, cfile); if ((recordp) && (token == LBRACE)) { parse_warn (cfile, "only uniform array inside record."); skip_to_rbrace (cfile, recordp + 1); skip_to_semi (cfile); return 0; } goto next_type; case BOOLEAN: type = 'f'; break; case INTEGER: is_signed = 1; parse_integer: token = next_token (&val, (unsigned *)0, cfile); if (token != NUMBER) { parse_warn (cfile, "expecting number."); skip_to_rbrace (cfile, recordp); if (recordp) skip_to_semi (cfile); return 0; } switch (atoi (val)) { case 8: type = is_signed ? 'b' : 'B'; break; case 16: type = is_signed ? 's' : 'S'; break; case 32: type = is_signed ? 'l' : 'L'; break; default: parse_warn (cfile, "%s bit precision is not supported.", val); skip_to_rbrace (cfile, recordp); if (recordp) skip_to_semi (cfile); return 0; } break; case SIGNED: is_signed = 1; parse_signed: token = next_token (&val, (unsigned *)0, cfile); if (token != INTEGER) { parse_warn (cfile, "expecting \"integer\" keyword."); skip_to_rbrace (cfile, recordp); if (recordp) skip_to_semi (cfile); return 0; } goto parse_integer; case UNSIGNED: is_signed = 0; goto parse_signed; case IP_ADDRESS: type = 'I'; break; case IP6_ADDRESS: type = '6'; break; case DOMAIN_NAME: type = 'd'; goto no_arrays; case DOMAIN_LIST: /* Consume optional compression indicator. */ token = peek_token(&val, NULL, cfile); if (token == COMPRESSED) { skip_token(&val, NULL, cfile); tokbuf[tokix++] = 'D'; type = 'c'; } else type = 'D'; goto no_arrays; case TEXT: type = 't'; no_arrays: if (arrayp) { parse_warn (cfile, "arrays of text strings not %s", "yet supported."); skip_to_rbrace (cfile, recordp); if (recordp) skip_to_semi (cfile); return 0; } no_more_in_record = 1; break; case STRING_TOKEN: type = 'X'; goto no_arrays; case ENCAPSULATE: token = next_token (&val, (unsigned *)0, cfile); if (!is_identifier (token)) { parse_warn (cfile, "expecting option space identifier"); skip_to_semi (cfile); return 0; } encapsulated = NULL; if (!universe_hash_lookup(&encapsulated, universe_hash, val, strlen(val), MDL)) { parse_warn(cfile, "unknown option space %s", val); skip_to_semi (cfile); return 0; } if (strlen (val) + tokix + 2 > sizeof (tokbuf)) goto toobig; tokbuf [tokix++] = 'E'; strcpy (&tokbuf [tokix], val); tokix += strlen (val); type = '.'; has_encapsulation = 1; break; case ZEROLEN: type = 'Z'; if (arrayp) { parse_warn (cfile, "array incompatible with zerolen."); skip_to_rbrace (cfile, recordp); if (recordp) skip_to_semi (cfile); return 0; } no_more_in_record = 1; break; default: parse_warn (cfile, "unknown data type %s", val); skip_to_rbrace (cfile, recordp); if (recordp) skip_to_semi (cfile); return 0; } if (tokix == sizeof tokbuf) { toobig: parse_warn (cfile, "too many types in record."); skip_to_rbrace (cfile, recordp); if (recordp) skip_to_semi (cfile); return 0; } tokbuf [tokix++] = type; if (recordp) { token = next_token (&val, (unsigned *)0, cfile); if (arrayp > recordp) { if (tokix == sizeof tokbuf) { parse_warn (cfile, "too many types in record."); skip_to_rbrace (cfile, 1); skip_to_semi (cfile); return 0; } arrayp = 0; tokbuf[tokix++] = 'a'; } if (token == COMMA) { if (no_more_in_record) { parse_warn (cfile, "%s must be at end of record.", type == 't' ? "text" : "string"); skip_to_rbrace (cfile, 1); if (recordp) skip_to_semi (cfile); return 0; } token = next_token (&val, (unsigned *)0, cfile); goto next_type; } if (token != RBRACE) { parse_warn (cfile, "expecting right brace."); skip_to_rbrace (cfile, 1); if (recordp) skip_to_semi (cfile); return 0; } } if (!parse_semi (cfile)) { parse_warn (cfile, "semicolon expected."); skip_to_semi (cfile); if (recordp) skip_to_semi (cfile); return 0; } if (has_encapsulation && arrayp) { parse_warn (cfile, "Arrays of encapsulations don't make sense."); return 0; } s = dmalloc(tokix + (arrayp ? 1 : 0) + 1, MDL); if (s == NULL) { log_fatal("no memory for option format."); } memcpy(s, tokbuf, tokix); if (arrayp) { s[tokix++] = (arrayp > recordp) ? 'a' : 'A'; } s[tokix] = '\0'; option -> format = s; oldopt = NULL; option_code_hash_lookup(&oldopt, option->universe->code_hash, &option->code, 0, MDL); if (oldopt != NULL) { /* * XXX: This illegalizes a configuration syntax that was * valid in 3.0.x, where multiple name->code mappings are * given, but only one code->name mapping survives. It is * unclear what can or should be done at this point, but it * seems best to retain 3.0.x behaviour for upgrades to go * smoothly. * option_name_hash_delete(option->universe->name_hash, oldopt->name, 0, MDL); */ option_code_hash_delete(option->universe->code_hash, &oldopt->code, 0, MDL); option_dereference(&oldopt, MDL); } option_code_hash_add(option->universe->code_hash, &option->code, 0, option, MDL); option_name_hash_add(option->universe->name_hash, option->name, 0, option, MDL); if (has_encapsulation) { /* INSIST(tokbuf[0] == 'E'); */ /* INSIST(encapsulated != NULL); */ if (!option_code_hash_lookup(&encapsulated->enc_opt, option->universe->code_hash, &option->code, 0, MDL)) { log_fatal("error finding encapsulated option (%s:%d)", MDL); } } return 1; } /* * base64 :== NUMBER_OR_STRING */ int parse_base64 (data, cfile) struct data_string *data; struct parse *cfile; { const char *val; int i, j, k; unsigned acc = 0; static unsigned char from64 [] = {64, 64, 64, 64, 64, 64, 64, 64, /* \"#$%&' */ 64, 64, 64, 62, 64, 64, 64, 63, /* ()*+,-./ */ 52, 53, 54, 55, 56, 57, 58, 59, /* 01234567 */ 60, 61, 64, 64, 64, 64, 64, 64, /* 89:;<=>? */ 64, 0, 1, 2, 3, 4, 5, 6, /* @ABCDEFG */ 7, 8, 9, 10, 11, 12, 13, 14, /* HIJKLMNO */ 15, 16, 17, 18, 19, 20, 21, 22, /* PQRSTUVW */ 23, 24, 25, 64, 64, 64, 64, 64, /* XYZ[\]^_ */ 64, 26, 27, 28, 29, 30, 31, 32, /* 'abcdefg */ 33, 34, 35, 36, 37, 38, 39, 40, /* hijklmno */ 41, 42, 43, 44, 45, 46, 47, 48, /* pqrstuvw */ 49, 50, 51, 64, 64, 64, 64, 64}; /* xyz{|}~ */ struct string_list *bufs = NULL, *last = NULL, *t; int cc = 0; int terminated = 0; int valid_base64; /* It's possible for a + or a / to cause a base64 quantity to be tokenized into more than one token, so we have to parse them all in before decoding. */ do { unsigned l; (void)next_token(&val, &l, cfile); t = dmalloc(l + sizeof(*t), MDL); if (t == NULL) log_fatal("no memory for base64 buffer."); memset(t, 0, (sizeof(*t)) - 1); memcpy(t->string, val, l + 1); cc += l; if (last) last->next = t; else bufs = t; last = t; (void)peek_token(&val, NULL, cfile); valid_base64 = 1; for (i = 0; val[i]; i++) { /* Check to see if the character is valid. It may be out of range or within the right range but not used in the mapping */ if (((val[i] < ' ') || (val[i] > 'z')) || ((from64[val[i] - ' '] > 63) && (val[i] != '='))) { valid_base64 = 0; break; /* no need to continue for loop */ } } } while (valid_base64); data->len = cc; data->len = (data->len * 3) / 4; if (!buffer_allocate(&data->buffer, data->len, MDL)) { parse_warn (cfile, "can't allocate buffer for base64 data."); data->len = 0; data->data = NULL; goto out; } j = k = 0; for (t = bufs; t; t = t->next) { for (i = 0; t->string[i]; i++) { unsigned foo = t->string[i]; if (terminated && foo != '=') { parse_warn(cfile, "stuff after base64 '=' terminator: %s.", &t->string[i]); goto bad; } if ((foo < ' ') || (foo > 'z')) { bad64: parse_warn(cfile, "invalid base64 character %d.", t->string[i]); bad: data_string_forget(data, MDL); goto out; } if (foo == '=') terminated = 1; else { foo = from64[foo - ' ']; if (foo == 64) goto bad64; acc = (acc << 6) + foo; switch (k % 4) { case 0: break; case 1: data->buffer->data[j++] = (acc >> 4); acc = acc & 0x0f; break; case 2: data->buffer->data[j++] = (acc >> 2); acc = acc & 0x03; break; case 3: data->buffer->data[j++] = acc; acc = 0; break; } } k++; } } if (k % 4) { if (acc) { parse_warn(cfile, "partial base64 value left over: %d.", acc); } } data->len = j; data->data = data->buffer->data; out: for (t = bufs; t; t = last) { last = t->next; dfree(t, MDL); } if (data->len) return 1; else return 0; } /* * colon-separated-hex-list :== NUMBER | * NUMBER COLON colon-separated-hex-list */ int parse_cshl (data, cfile) struct data_string *data; struct parse *cfile; { u_int8_t ibuf [128]; unsigned ilen = 0; unsigned tlen = 0; struct option_tag *sl = (struct option_tag *)0; struct option_tag *next, **last = &sl; enum dhcp_token token; const char *val; unsigned char *rvp; do { token = next_token (&val, (unsigned *)0, cfile); if (token != NUMBER && token != NUMBER_OR_NAME) { parse_warn (cfile, "expecting hexadecimal number."); skip_to_semi (cfile); for (; sl; sl = next) { next = sl -> next; dfree (sl, MDL); } return 0; } if (ilen == sizeof ibuf) { next = (struct option_tag *) dmalloc (ilen - 1 + sizeof (struct option_tag), MDL); if (!next) log_fatal ("no memory for string list."); memcpy (next -> data, ibuf, ilen); *last = next; last = &next -> next; tlen += ilen; ilen = 0; } convert_num (cfile, &ibuf [ilen++], val, 16, 8); token = peek_token (&val, (unsigned *)0, cfile); if (token != COLON) break; skip_token(&val, (unsigned *)0, cfile); } while (1); if (!buffer_allocate (&data -> buffer, tlen + ilen, MDL)) log_fatal ("no memory to store octet data."); data -> data = &data -> buffer -> data [0]; data -> len = tlen + ilen; data -> terminated = 0; rvp = &data -> buffer -> data [0]; while (sl) { next = sl -> next; memcpy (rvp, sl -> data, sizeof ibuf); rvp += sizeof ibuf; dfree (sl, MDL); sl = next; } memcpy (rvp, ibuf, ilen); return 1; } /* * executable-statements :== executable-statement executable-statements | * executable-statement * * executable-statement :== * IF if-statement | * ADD class-name SEMI | * BREAK SEMI | * OPTION option-parameter SEMI | * SUPERSEDE option-parameter SEMI | * PREPEND option-parameter SEMI | * APPEND option-parameter SEMI */ int parse_executable_statements (statements, cfile, lose, case_context) struct executable_statement **statements; struct parse *cfile; int *lose; enum expression_context case_context; { struct executable_statement **next; next = statements; while (parse_executable_statement (next, cfile, lose, case_context)) next = &((*next) -> next); if (!*lose) return 1; return 0; } int parse_executable_statement (result, cfile, lose, case_context) struct executable_statement **result; struct parse *cfile; int *lose; enum expression_context case_context; { #if defined(ENABLE_EXECUTE) unsigned len; struct expression **ep; #endif enum dhcp_token token; const char *val; struct class *cta; struct option *option=NULL; struct option_cache *cache; int known; int flag; int i; struct dns_zone *zone; isc_result_t status; char *s; token = peek_token (&val, (unsigned *)0, cfile); switch (token) { case DB_TIME_FORMAT: skip_token(&val, NULL, cfile); token = next_token(&val, NULL, cfile); if (token == DEFAULT) { db_time_format = DEFAULT_TIME_FORMAT; } else if (token == LOCAL) { db_time_format = LOCAL_TIME_FORMAT; } else { parse_warn(cfile, "Expecting 'local' or 'default'."); if (token != SEMI) skip_to_semi(cfile); *lose = 1; return 0; } token = next_token(&val, NULL, cfile); if (token != SEMI) { parse_warn(cfile, "Expecting a semicolon."); *lose = 1; return 0; } /* We're done here. */ return 1; case IF: skip_token(&val, (unsigned *)0, cfile); return parse_if_statement (result, cfile, lose); case TOKEN_ADD: skip_token(&val, (unsigned *)0, cfile); token = next_token (&val, (unsigned *)0, cfile); if (token != STRING) { parse_warn (cfile, "expecting class name."); skip_to_semi (cfile); *lose = 1; return 0; } cta = (struct class *)0; status = find_class (&cta, val, MDL); if (status != ISC_R_SUCCESS) { parse_warn (cfile, "class %s: %s", val, isc_result_totext (status)); skip_to_semi (cfile); *lose = 1; return 0; } if (!parse_semi (cfile)) { *lose = 1; return 0; } if (!executable_statement_allocate (result, MDL)) log_fatal ("no memory for new statement."); (*result) -> op = add_statement; (*result) -> data.add = cta; break; case BREAK: skip_token(&val, (unsigned *)0, cfile); if (!parse_semi (cfile)) { *lose = 1; return 0; } if (!executable_statement_allocate (result, MDL)) log_fatal ("no memory for new statement."); (*result) -> op = break_statement; break; case SEND: skip_token(&val, (unsigned *)0, cfile); known = 0; status = parse_option_name (cfile, 0, &known, &option); if (status != ISC_R_SUCCESS || option == NULL) { *lose = 1; return 0; } status = parse_option_statement(result, cfile, 1, option, send_option_statement); option_dereference(&option, MDL); return status; case SUPERSEDE: case OPTION: skip_token(&val, (unsigned *)0, cfile); known = 0; status = parse_option_name (cfile, 0, &known, &option); if (status != ISC_R_SUCCESS || option == NULL) { *lose = 1; return 0; } status = parse_option_statement(result, cfile, 1, option, supersede_option_statement); option_dereference(&option, MDL); return status; case ALLOW: flag = 1; goto pad; case DENY: flag = 0; goto pad; case IGNORE: flag = 2; pad: skip_token(&val, (unsigned *)0, cfile); cache = (struct option_cache *)0; if (!parse_allow_deny (&cache, cfile, flag)) return 0; if (!executable_statement_allocate (result, MDL)) log_fatal ("no memory for new statement."); (*result) -> op = supersede_option_statement; (*result) -> data.option = cache; break; case DEFAULT: skip_token(&val, (unsigned *)0, cfile); token = peek_token (&val, (unsigned *)0, cfile); if (token == COLON) goto switch_default; known = 0; status = parse_option_name (cfile, 0, &known, &option); if (status != ISC_R_SUCCESS || option == NULL) { *lose = 1; return 0; } status = parse_option_statement(result, cfile, 1, option, default_option_statement); option_dereference(&option, MDL); return status; case PREPEND: skip_token(&val, (unsigned *)0, cfile); known = 0; status = parse_option_name (cfile, 0, &known, &option); if (status != ISC_R_SUCCESS || option == NULL) { *lose = 1; return 0; } status = parse_option_statement(result, cfile, 1, option, prepend_option_statement); option_dereference(&option, MDL); return status; case APPEND: skip_token(&val, (unsigned *)0, cfile); known = 0; status = parse_option_name (cfile, 0, &known, &option); if (status != ISC_R_SUCCESS || option == NULL) { *lose = 1; return 0; } status = parse_option_statement(result, cfile, 1, option, append_option_statement); option_dereference(&option, MDL); return status; case ON: skip_token(&val, (unsigned *)0, cfile); return parse_on_statement (result, cfile, lose); case SWITCH: skip_token(&val, (unsigned *)0, cfile); return parse_switch_statement (result, cfile, lose); case CASE: skip_token(&val, (unsigned *)0, cfile); if (case_context == context_any) { parse_warn (cfile, "case statement in inappropriate scope."); *lose = 1; skip_to_semi (cfile); return 0; } return parse_case_statement (result, cfile, lose, case_context); switch_default: skip_token(&val, (unsigned *)0, cfile); if (case_context == context_any) { parse_warn (cfile, "switch default statement in %s", "inappropriate scope."); *lose = 1; return 0; } else { if (!executable_statement_allocate (result, MDL)) log_fatal ("no memory for default statement."); (*result) -> op = default_statement; return 1; } case DEFINE: case TOKEN_SET: skip_token(&val, (unsigned *)0, cfile); if (token == DEFINE) flag = 1; else flag = 0; token = next_token (&val, (unsigned *)0, cfile); if (token != NAME && token != NUMBER_OR_NAME) { parse_warn (cfile, "%s can't be a variable name", val); badset: skip_to_semi (cfile); *lose = 1; return 0; } if (!executable_statement_allocate (result, MDL)) log_fatal ("no memory for set statement."); (*result) -> op = flag ? define_statement : set_statement; (*result) -> data.set.name = dmalloc (strlen (val) + 1, MDL); if (!(*result)->data.set.name) log_fatal ("can't allocate variable name"); strcpy ((*result) -> data.set.name, val); token = next_token (&val, (unsigned *)0, cfile); if (token == LPAREN) { struct string_list *head, *cur, *new; struct expression *expr; head = cur = (struct string_list *)0; do { token = next_token (&val, (unsigned *)0, cfile); if (token == RPAREN) break; if (token != NAME && token != NUMBER_OR_NAME) { parse_warn (cfile, "expecting argument name"); skip_to_rbrace (cfile, 0); *lose = 1; executable_statement_dereference (result, MDL); return 0; } new = ((struct string_list *) dmalloc (sizeof (struct string_list) + strlen (val), MDL)); if (!new) log_fatal ("can't allocate string."); memset (new, 0, sizeof *new); strcpy (new -> string, val); if (cur) { cur -> next = new; cur = new; } else { head = cur = new; } token = next_token (&val, (unsigned *)0, cfile); } while (token == COMMA); if (token != RPAREN) { parse_warn (cfile, "expecting right paren."); badx: skip_to_semi (cfile); *lose = 1; executable_statement_dereference (result, MDL); return 0; } token = next_token (&val, (unsigned *)0, cfile); if (token != LBRACE) { parse_warn (cfile, "expecting left brace."); goto badx; } expr = (struct expression *)0; if (!(expression_allocate (&expr, MDL))) log_fatal ("can't allocate expression."); expr -> op = expr_function; if (!fundef_allocate (&expr -> data.func, MDL)) log_fatal ("can't allocate fundef."); expr -> data.func -> args = head; (*result) -> data.set.expr = expr; if (!(parse_executable_statements (&expr -> data.func -> statements, cfile, lose, case_context))) { if (*lose) goto badx; } token = next_token (&val, (unsigned *)0, cfile); if (token != RBRACE) { parse_warn (cfile, "expecting rigt brace."); goto badx; } } else { if (token != EQUAL) { parse_warn (cfile, "expecting '=' in %s statement.", flag ? "define" : "set"); goto badset; } if (!parse_expression (&(*result) -> data.set.expr, cfile, lose, context_any, (struct expression **)0, expr_none)) { if (!*lose) parse_warn (cfile, "expecting expression."); else *lose = 1; skip_to_semi (cfile); executable_statement_dereference (result, MDL); return 0; } if (!parse_semi (cfile)) { *lose = 1; executable_statement_dereference (result, MDL); return 0; } } break; case UNSET: skip_token(&val, (unsigned *)0, cfile); token = next_token (&val, (unsigned *)0, cfile); if (token != NAME && token != NUMBER_OR_NAME) { parse_warn (cfile, "%s can't be a variable name", val); skip_to_semi (cfile); *lose = 1; return 0; } if (!executable_statement_allocate (result, MDL)) log_fatal ("no memory for set statement."); (*result) -> op = unset_statement; (*result) -> data.unset = dmalloc (strlen (val) + 1, MDL); if (!(*result)->data.unset) log_fatal ("can't allocate variable name"); strcpy ((*result) -> data.unset, val); if (!parse_semi (cfile)) { *lose = 1; executable_statement_dereference (result, MDL); return 0; } break; case EVAL: skip_token(&val, (unsigned *)0, cfile); if (!executable_statement_allocate (result, MDL)) log_fatal ("no memory for eval statement."); (*result) -> op = eval_statement; if (!parse_expression (&(*result) -> data.eval, cfile, lose, context_data, /* XXX */ (struct expression **)0, expr_none)) { if (!*lose) parse_warn (cfile, "expecting data expression."); else *lose = 1; skip_to_semi (cfile); executable_statement_dereference (result, MDL); return 0; } if (!parse_semi (cfile)) { *lose = 1; executable_statement_dereference (result, MDL); } break; case EXECUTE: #ifdef ENABLE_EXECUTE skip_token(&val, NULL, cfile); if (!executable_statement_allocate (result, MDL)) log_fatal ("no memory for execute statement."); (*result)->op = execute_statement; token = next_token(&val, NULL, cfile); if (token != LPAREN) { parse_warn(cfile, "left parenthesis expected."); skip_to_semi(cfile); *lose = 1; return 0; } token = next_token(&val, &len, cfile); if (token != STRING) { parse_warn(cfile, "Expecting a quoted string."); skip_to_semi(cfile); *lose = 1; return 0; } (*result)->data.execute.command = dmalloc(len + 1, MDL); if ((*result)->data.execute.command == NULL) log_fatal("can't allocate command name"); strcpy((*result)->data.execute.command, val); ep = &(*result)->data.execute.arglist; (*result)->data.execute.argc = 0; while((token = next_token(&val, NULL, cfile)) == COMMA) { if (!expression_allocate(ep, MDL)) log_fatal ("can't allocate expression"); if (!parse_data_expression (&(*ep) -> data.arg.val, cfile, lose)) { if (!*lose) { parse_warn (cfile, "expecting expression."); *lose = 1; } skip_to_semi(cfile); *lose = 1; return 0; } ep = &(*ep)->data.arg.next; (*result)->data.execute.argc++; } if (token != RPAREN) { parse_warn(cfile, "right parenthesis expected."); skip_to_semi(cfile); *lose = 1; return 0; } if (!parse_semi (cfile)) { *lose = 1; executable_statement_dereference (result, MDL); } #else /* ! ENABLE_EXECUTE */ parse_warn(cfile, "define ENABLE_EXECUTE in site.h to " "enable execute(); expressions."); skip_to_semi(cfile); *lose = 1; return 0; #endif /* ENABLE_EXECUTE */ break; case RETURN: skip_token(&val, (unsigned *)0, cfile); if (!executable_statement_allocate (result, MDL)) log_fatal ("no memory for return statement."); (*result) -> op = return_statement; if (!parse_expression (&(*result) -> data.retval, cfile, lose, context_data, (struct expression **)0, expr_none)) { if (!*lose) parse_warn (cfile, "expecting data expression."); else *lose = 1; skip_to_semi (cfile); executable_statement_dereference (result, MDL); return 0; } if (!parse_semi (cfile)) { *lose = 1; executable_statement_dereference (result, MDL); return 0; } break; case LOG: skip_token(&val, (unsigned *)0, cfile); if (!executable_statement_allocate (result, MDL)) log_fatal ("no memory for log statement."); (*result) -> op = log_statement; token = next_token (&val, (unsigned *)0, cfile); if (token != LPAREN) { parse_warn (cfile, "left parenthesis expected."); skip_to_semi (cfile); *lose = 1; return 0; } token = peek_token (&val, (unsigned *)0, cfile); i = 1; if (token == FATAL) { (*result) -> data.log.priority = log_priority_fatal; } else if (token == ERROR) { (*result) -> data.log.priority = log_priority_error; } else if (token == TOKEN_DEBUG) { (*result) -> data.log.priority = log_priority_debug; } else if (token == INFO) { (*result) -> data.log.priority = log_priority_info; } else { (*result) -> data.log.priority = log_priority_debug; i = 0; } if (i) { skip_token(&val, (unsigned *)0, cfile); token = next_token (&val, (unsigned *)0, cfile); if (token != COMMA) { parse_warn (cfile, "comma expected."); skip_to_semi (cfile); *lose = 1; return 0; } } if (!(parse_data_expression (&(*result) -> data.log.expr, cfile, lose))) { skip_to_semi (cfile); *lose = 1; return 0; } token = next_token (&val, (unsigned *)0, cfile); if (token != RPAREN) { parse_warn (cfile, "right parenthesis expected."); skip_to_semi (cfile); *lose = 1; return 0; } token = next_token (&val, (unsigned *)0, cfile); if (token != SEMI) { parse_warn (cfile, "semicolon expected."); skip_to_semi (cfile); *lose = 1; return 0; } break; case PARSE_VENDOR_OPT: /* The parse-vendor-option; The statement has no arguments. * We simply set up the statement and when it gets executed it * will find all information it needs in the packet and options. */ skip_token(&val, NULL, cfile); if (!parse_semi(cfile)) { *lose = 1; return (0); } if (!executable_statement_allocate(result, MDL)) log_fatal("no memory for execute statement."); (*result)->op = vendor_opt_statement; break; /* Not really a statement, but we parse it here anyway because it's appropriate for all DHCP agents with parsers. */ case ZONE: skip_token(&val, (unsigned *)0, cfile); zone = (struct dns_zone *)0; if (!dns_zone_allocate (&zone, MDL)) log_fatal ("no memory for new zone."); zone -> name = parse_host_name (cfile); if (!zone -> name) { parse_warn (cfile, "expecting hostname."); badzone: *lose = 1; skip_to_semi (cfile); dns_zone_dereference (&zone, MDL); return 0; } i = strlen (zone -> name); if (zone -> name [i - 1] != '.') { s = dmalloc ((unsigned)i + 2, MDL); if (!s) { parse_warn (cfile, "no trailing '.' on zone"); goto badzone; } strcpy (s, zone -> name); s [i] = '.'; s [i + 1] = 0; dfree (zone -> name, MDL); zone -> name = s; } if (!parse_zone (zone, cfile)) goto badzone; status = enter_dns_zone (zone); if (status != ISC_R_SUCCESS) { parse_warn (cfile, "dns zone key %s: %s", zone -> name, isc_result_totext (status)); dns_zone_dereference (&zone, MDL); return 0; } dns_zone_dereference (&zone, MDL); return 1; /* Also not really a statement, but same idea as above. */ case KEY: skip_token(&val, (unsigned *)0, cfile); if (!parse_key (cfile)) { *lose = 1; return 0; } return 1; default: if (config_universe && is_identifier (token)) { option = (struct option *)0; option_name_hash_lookup(&option, config_universe->name_hash, val, 0, MDL); if (option) { skip_token(&val, (unsigned *)0, cfile); status = parse_option_statement (result, cfile, 1, option, supersede_option_statement); option_dereference(&option, MDL); return status; } } if (token == NUMBER_OR_NAME || token == NAME) { /* This is rather ugly. Since function calls are data expressions, fake up an eval statement. */ if (!executable_statement_allocate (result, MDL)) log_fatal ("no memory for eval statement."); (*result) -> op = eval_statement; if (!parse_expression (&(*result) -> data.eval, cfile, lose, context_data, (struct expression **)0, expr_none)) { if (!*lose) parse_warn (cfile, "expecting " "function call."); else *lose = 1; skip_to_semi (cfile); executable_statement_dereference (result, MDL); return 0; } if (!parse_semi (cfile)) { *lose = 1; executable_statement_dereference (result, MDL); return 0; } break; } *lose = 0; return 0; } return 1; } /* zone-statements :== zone-statement | zone-statement zone-statements zone-statement :== PRIMARY ip-addresses SEMI | SECONDARY ip-addresses SEMI | PRIMARY6 ip-address6 SEMI | SECONDARY6 ip-address6 SEMI | key-reference SEMI ip-addresses :== ip-addr-or-hostname | ip-addr-or-hostname COMMA ip-addresses key-reference :== KEY STRING | KEY identifier */ int parse_zone (struct dns_zone *zone, struct parse *cfile) { int token; const char *val; char *key_name; struct option_cache *oc; int done = 0; token = next_token (&val, (unsigned *)0, cfile); if (token != LBRACE) { parse_warn (cfile, "expecting left brace"); return 0; } do { token = peek_token (&val, (unsigned *)0, cfile); switch (token) { case PRIMARY: if (zone -> primary) { parse_warn (cfile, "more than one primary."); skip_to_semi (cfile); return 0; } if (!option_cache_allocate (&zone -> primary, MDL)) log_fatal ("can't allocate primary option cache."); oc = zone -> primary; goto consemup; case SECONDARY: if (zone -> secondary) { parse_warn (cfile, "more than one secondary."); skip_to_semi (cfile); return 0; } if (!option_cache_allocate (&zone -> secondary, MDL)) log_fatal ("can't allocate secondary."); oc = zone -> secondary; consemup: skip_token(&val, (unsigned *)0, cfile); do { struct expression *expr = (struct expression *)0; if (!parse_ip_addr_or_hostname (&expr, cfile, 0)) { parse_warn (cfile, "expecting IP addr or hostname."); skip_to_semi (cfile); return 0; } if (oc -> expression) { struct expression *old = (struct expression *)0; expression_reference (&old, oc -> expression, MDL); expression_dereference (&oc -> expression, MDL); if (!make_concat (&oc -> expression, old, expr)) log_fatal ("no memory for concat."); expression_dereference (&expr, MDL); expression_dereference (&old, MDL); } else { expression_reference (&oc -> expression, expr, MDL); expression_dereference (&expr, MDL); } token = next_token (&val, (unsigned *)0, cfile); } while (token == COMMA); if (token != SEMI) { parse_warn (cfile, "expecting semicolon."); skip_to_semi (cfile); return 0; } break; case PRIMARY6: if (zone->primary6) { parse_warn(cfile, "more than one primary6."); skip_to_semi(cfile); return (0); } if (!option_cache_allocate (&zone->primary6, MDL)) log_fatal("can't allocate primary6 option cache."); oc = zone->primary6; goto consemup6; case SECONDARY6: if (zone->secondary6) { parse_warn(cfile, "more than one secondary6."); skip_to_semi(cfile); return (0); } if (!option_cache_allocate (&zone->secondary6, MDL)) log_fatal("can't allocate secondary6 " "option cache."); oc = zone->secondary6; consemup6: skip_token(&val, NULL, cfile); do { struct expression *expr = NULL; if (parse_ip6_addr_expr(&expr, cfile) == 0) { parse_warn(cfile, "expecting IPv6 addr."); skip_to_semi(cfile); return (0); } if (oc->expression) { struct expression *old = NULL; expression_reference(&old, oc->expression, MDL); expression_dereference(&oc->expression, MDL); if (!make_concat(&oc->expression, old, expr)) log_fatal("no memory for concat."); expression_dereference(&expr, MDL); expression_dereference(&old, MDL); } else { expression_reference(&oc->expression, expr, MDL); expression_dereference(&expr, MDL); } token = next_token(&val, NULL, cfile); } while (token == COMMA); if (token != SEMI) { parse_warn(cfile, "expecting semicolon."); skip_to_semi(cfile); return (0); } break; case KEY: skip_token(&val, NULL, cfile); token = peek_token(&val, NULL, cfile); if (token == STRING) { skip_token(&val, NULL, cfile); key_name = NULL; } else { key_name = parse_host_name(cfile); if (!key_name) { parse_warn(cfile, "expecting key name."); skip_to_semi(cfile); return (0); } val = key_name; } if (zone->key) { log_fatal("Multiple key definitions for zone %s.", zone->name); } if (omapi_auth_key_lookup_name(&zone->key, val) != ISC_R_SUCCESS) parse_warn(cfile, "unknown key %s", val); if (key_name) dfree(key_name, MDL); if (!parse_semi(cfile)) return (0); break; default: done = 1; break; } } while (!done); token = next_token(&val, NULL, cfile); if (token != RBRACE) { parse_warn(cfile, "expecting right brace."); return (0); } return (1); } /* key-statements :== key-statement | key-statement key-statements key-statement :== ALGORITHM host-name SEMI | secret-definition SEMI secret-definition :== SECRET base64val | SECRET STRING */ int parse_key (struct parse *cfile) { int token; const char *val; int done = 0; struct auth_key *key; struct data_string ds; isc_result_t status; char *s; key = (struct auth_key *)0; if (omapi_auth_key_new (&key, MDL) != ISC_R_SUCCESS) log_fatal ("no memory for key"); token = peek_token (&val, (unsigned *)0, cfile); if (token == STRING) { skip_token(&val, (unsigned *)0, cfile); key -> name = dmalloc (strlen (val) + 1, MDL); if (!key -> name) log_fatal ("no memory for key name."); strcpy (key -> name, val); } else { key -> name = parse_host_name (cfile); if (!key -> name) { parse_warn (cfile, "expecting key name."); skip_to_semi (cfile); goto bad; } } token = next_token (&val, (unsigned *)0, cfile); if (token != LBRACE) { parse_warn (cfile, "expecting left brace"); goto bad; } do { token = next_token (&val, (unsigned *)0, cfile); switch (token) { case ALGORITHM: if (key -> algorithm) { parse_warn (cfile, "key %s: too many algorithms", key -> name); goto rbad; } key -> algorithm = parse_host_name (cfile); if (!key -> algorithm) { parse_warn (cfile, "expecting key algorithm name."); goto rbad; } if (!parse_semi (cfile)) goto rbad; /* If the algorithm name isn't an FQDN, tack on the .SIG-ALG.REG.NET. domain. */ s = strrchr (key -> algorithm, '.'); if (!s) { static char add [] = ".SIG-ALG.REG.INT."; s = dmalloc (strlen (key -> algorithm) + sizeof (add), MDL); if (!s) { log_error ("no memory for key %s.", "algorithm"); goto rbad; } strcpy (s, key -> algorithm); strcat (s, add); dfree (key -> algorithm, MDL); key -> algorithm = s; } else if (s [1]) { /* If there is no trailing '.', hack one in. */ s = dmalloc (strlen (key -> algorithm) + 2, MDL); if (!s) { log_error ("no memory for key %s.", key -> algorithm); goto rbad; } strcpy (s, key -> algorithm); strcat (s, "."); dfree (key -> algorithm, MDL); key -> algorithm = s; } break; case SECRET: if (key -> key) { parse_warn (cfile, "key %s: too many secrets", key -> name); goto rbad; } memset (&ds, 0, sizeof(ds)); if (!parse_base64 (&ds, cfile)) goto rbad; status = omapi_data_string_new (&key -> key, ds.len, MDL); if (status != ISC_R_SUCCESS) goto rbad; memcpy (key -> key -> value, ds.buffer -> data, ds.len); data_string_forget (&ds, MDL); if (!parse_semi (cfile)) goto rbad; break; default: done = 1; break; } } while (!done); if (token != RBRACE) { parse_warn (cfile, "expecting right brace."); goto rbad; } /* Allow the BIND 8 syntax, which has a semicolon after each closing brace. */ token = peek_token (&val, (unsigned *)0, cfile); if (token == SEMI) { skip_token(&val, (unsigned *)0, cfile); } /* Remember the key. */ status = omapi_auth_key_enter (key); if (status != ISC_R_SUCCESS) { parse_warn (cfile, "tsig key %s: %s", key -> name, isc_result_totext (status)); goto bad; } omapi_auth_key_dereference (&key, MDL); return 1; rbad: skip_to_rbrace (cfile, 1); bad: omapi_auth_key_dereference (&key, MDL); return 0; } /* * on-statement :== event-types LBRACE executable-statements RBRACE * event-types :== event-type OR event-types | * event-type * event-type :== EXPIRY | COMMIT | RELEASE */ int parse_on_statement (result, cfile, lose) struct executable_statement **result; struct parse *cfile; int *lose; { enum dhcp_token token; const char *val; if (!executable_statement_allocate (result, MDL)) log_fatal ("no memory for new statement."); (*result) -> op = on_statement; do { token = next_token (&val, (unsigned *)0, cfile); switch (token) { case EXPIRY: (*result) -> data.on.evtypes |= ON_EXPIRY; break; case COMMIT: (*result) -> data.on.evtypes |= ON_COMMIT; break; case RELEASE: (*result) -> data.on.evtypes |= ON_RELEASE; break; case TRANSMISSION: (*result) -> data.on.evtypes |= ON_TRANSMISSION; break; default: parse_warn (cfile, "expecting a lease event type"); skip_to_semi (cfile); *lose = 1; executable_statement_dereference (result, MDL); return 0; } token = next_token (&val, (unsigned *)0, cfile); } while (token == OR); /* Semicolon means no statements. */ if (token == SEMI) return 1; if (token != LBRACE) { parse_warn (cfile, "left brace expected."); skip_to_semi (cfile); *lose = 1; executable_statement_dereference (result, MDL); return 0; } if (!parse_executable_statements (&(*result) -> data.on.statements, cfile, lose, context_any)) { if (*lose) { /* Try to even things up. */ do { token = next_token (&val, (unsigned *)0, cfile); } while (token != END_OF_FILE && token != RBRACE); executable_statement_dereference (result, MDL); return 0; } } token = next_token (&val, (unsigned *)0, cfile); if (token != RBRACE) { parse_warn (cfile, "right brace expected."); skip_to_semi (cfile); *lose = 1; executable_statement_dereference (result, MDL); return 0; } return 1; } /* * switch-statement :== LPAREN expr RPAREN LBRACE executable-statements RBRACE * */ int parse_switch_statement (result, cfile, lose) struct executable_statement **result; struct parse *cfile; int *lose; { enum dhcp_token token; const char *val; if (!executable_statement_allocate (result, MDL)) log_fatal ("no memory for new statement."); (*result) -> op = switch_statement; token = next_token (&val, (unsigned *)0, cfile); if (token != LPAREN) { parse_warn (cfile, "expecting left brace."); pfui: *lose = 1; skip_to_semi (cfile); gnorf: executable_statement_dereference (result, MDL); return 0; } if (!parse_expression (&(*result) -> data.s_switch.expr, cfile, lose, context_data_or_numeric, (struct expression **)0, expr_none)) { if (!*lose) { parse_warn (cfile, "expecting data or numeric expression."); goto pfui; } goto gnorf; } token = next_token (&val, (unsigned *)0, cfile); if (token != RPAREN) { parse_warn (cfile, "right paren expected."); goto pfui; } token = next_token (&val, (unsigned *)0, cfile); if (token != LBRACE) { parse_warn (cfile, "left brace expected."); goto pfui; } if (!(parse_executable_statements (&(*result) -> data.s_switch.statements, cfile, lose, (is_data_expression ((*result) -> data.s_switch.expr) ? context_data : context_numeric)))) { if (*lose) { skip_to_rbrace (cfile, 1); executable_statement_dereference (result, MDL); return 0; } } token = next_token (&val, (unsigned *)0, cfile); if (token != RBRACE) { parse_warn (cfile, "right brace expected."); goto pfui; } return 1; } /* * case-statement :== CASE expr COLON * */ int parse_case_statement (result, cfile, lose, case_context) struct executable_statement **result; struct parse *cfile; int *lose; enum expression_context case_context; { enum dhcp_token token; const char *val; if (!executable_statement_allocate (result, MDL)) log_fatal ("no memory for new statement."); (*result) -> op = case_statement; if (!parse_expression (&(*result) -> data.c_case, cfile, lose, case_context, (struct expression **)0, expr_none)) { if (!*lose) { parse_warn (cfile, "expecting %s expression.", (case_context == context_data ? "data" : "numeric")); } pfui: *lose = 1; skip_to_semi (cfile); executable_statement_dereference (result, MDL); return 0; } token = next_token (&val, (unsigned *)0, cfile); if (token != COLON) { parse_warn (cfile, "colon expected."); goto pfui; } return 1; } /* * if-statement :== boolean-expression LBRACE executable-statements RBRACE * else-statement * * else-statement :== | * ELSE LBRACE executable-statements RBRACE | * ELSE IF if-statement | * ELSIF if-statement */ int parse_if_statement (result, cfile, lose) struct executable_statement **result; struct parse *cfile; int *lose; { enum dhcp_token token; const char *val; int parenp; if (!executable_statement_allocate (result, MDL)) log_fatal ("no memory for if statement."); (*result) -> op = if_statement; token = peek_token (&val, (unsigned *)0, cfile); if (token == LPAREN) { parenp = 1; skip_token(&val, (unsigned *)0, cfile); } else parenp = 0; if (!parse_boolean_expression (&(*result) -> data.ie.expr, cfile, lose)) { if (!*lose) parse_warn (cfile, "boolean expression expected."); executable_statement_dereference (result, MDL); *lose = 1; return 0; } #if defined (DEBUG_EXPRESSION_PARSE) print_expression ("if condition", (*result) -> data.ie.expr); #endif if (parenp) { token = next_token (&val, (unsigned *)0, cfile); if (token != RPAREN) { parse_warn (cfile, "expecting right paren."); *lose = 1; executable_statement_dereference (result, MDL); return 0; } } token = next_token (&val, (unsigned *)0, cfile); if (token != LBRACE) { parse_warn (cfile, "left brace expected."); skip_to_semi (cfile); *lose = 1; executable_statement_dereference (result, MDL); return 0; } if (!parse_executable_statements (&(*result) -> data.ie.tc, cfile, lose, context_any)) { if (*lose) { /* Try to even things up. */ do { token = next_token (&val, (unsigned *)0, cfile); } while (token != END_OF_FILE && token != RBRACE); executable_statement_dereference (result, MDL); return 0; } } token = next_token (&val, (unsigned *)0, cfile); if (token != RBRACE) { parse_warn (cfile, "right brace expected."); skip_to_semi (cfile); *lose = 1; executable_statement_dereference (result, MDL); return 0; } token = peek_token (&val, (unsigned *)0, cfile); if (token == ELSE) { skip_token(&val, (unsigned *)0, cfile); token = peek_token (&val, (unsigned *)0, cfile); if (token == IF) { skip_token(&val, (unsigned *)0, cfile); if (!parse_if_statement (&(*result) -> data.ie.fc, cfile, lose)) { if (!*lose) parse_warn (cfile, "expecting if statement"); executable_statement_dereference (result, MDL); *lose = 1; return 0; } } else if (token != LBRACE) { parse_warn (cfile, "left brace or if expected."); skip_to_semi (cfile); *lose = 1; executable_statement_dereference (result, MDL); return 0; } else { skip_token(&val, (unsigned *)0, cfile); if (!(parse_executable_statements (&(*result) -> data.ie.fc, cfile, lose, context_any))) { executable_statement_dereference (result, MDL); return 0; } token = next_token (&val, (unsigned *)0, cfile); if (token != RBRACE) { parse_warn (cfile, "right brace expected."); skip_to_semi (cfile); *lose = 1; executable_statement_dereference (result, MDL); return 0; } } } else if (token == ELSIF) { skip_token(&val, (unsigned *)0, cfile); if (!parse_if_statement (&(*result) -> data.ie.fc, cfile, lose)) { if (!*lose) parse_warn (cfile, "expecting conditional."); executable_statement_dereference (result, MDL); *lose = 1; return 0; } } else (*result) -> data.ie.fc = (struct executable_statement *)0; return 1; } /* * boolean_expression :== CHECK STRING | * NOT boolean-expression | * data-expression EQUAL data-expression | * data-expression BANG EQUAL data-expression | * data-expression REGEX_MATCH data-expression | * boolean-expression AND boolean-expression | * boolean-expression OR boolean-expression * EXISTS OPTION-NAME */ int parse_boolean_expression (expr, cfile, lose) struct expression **expr; struct parse *cfile; int *lose; { /* Parse an expression... */ if (!parse_expression (expr, cfile, lose, context_boolean, (struct expression **)0, expr_none)) return 0; if (!is_boolean_expression (*expr) && (*expr) -> op != expr_variable_reference && (*expr) -> op != expr_funcall) { parse_warn (cfile, "Expecting a boolean expression."); *lose = 1; expression_dereference (expr, MDL); return 0; } return 1; } /* boolean :== ON SEMI | OFF SEMI | TRUE SEMI | FALSE SEMI */ int parse_boolean (cfile) struct parse *cfile; { const char *val; int rv; (void)next_token(&val, NULL, cfile); if (!strcasecmp (val, "true") || !strcasecmp (val, "on")) rv = 1; else if (!strcasecmp (val, "false") || !strcasecmp (val, "off")) rv = 0; else { parse_warn (cfile, "boolean value (true/false/on/off) expected"); skip_to_semi (cfile); return 0; } parse_semi (cfile); return rv; } /* * data_expression :== SUBSTRING LPAREN data-expression COMMA * numeric-expression COMMA * numeric-expression RPAREN | * CONCAT LPAREN data-expression COMMA * data-expression RPAREN * SUFFIX LPAREN data_expression COMMA * numeric-expression RPAREN | * LCASE LPAREN data_expression RPAREN | * UCASE LPAREN data_expression RPAREN | * OPTION option_name | * HARDWARE | * PACKET LPAREN numeric-expression COMMA * numeric-expression RPAREN | * V6RELAY LPAREN numeric-expression COMMA * data-expression RPAREN | * STRING | * colon_separated_hex_list */ int parse_data_expression (expr, cfile, lose) struct expression **expr; struct parse *cfile; int *lose; { /* Parse an expression... */ if (!parse_expression (expr, cfile, lose, context_data, (struct expression **)0, expr_none)) return 0; if (!is_data_expression (*expr) && (*expr) -> op != expr_variable_reference && (*expr) -> op != expr_funcall) { expression_dereference (expr, MDL); parse_warn (cfile, "Expecting a data expression."); *lose = 1; return 0; } return 1; } /* * numeric-expression :== EXTRACT_INT LPAREN data-expression * COMMA number RPAREN | * NUMBER */ int parse_numeric_expression (expr, cfile, lose) struct expression **expr; struct parse *cfile; int *lose; { /* Parse an expression... */ if (!parse_expression (expr, cfile, lose, context_numeric, (struct expression **)0, expr_none)) return 0; if (!is_numeric_expression (*expr) && (*expr) -> op != expr_variable_reference && (*expr) -> op != expr_funcall) { expression_dereference (expr, MDL); parse_warn (cfile, "Expecting a numeric expression."); *lose = 1; return 0; } return 1; } /* Parse a subexpression that does not contain a binary operator. */ int parse_non_binary (expr, cfile, lose, context) struct expression **expr; struct parse *cfile; int *lose; enum expression_context context; { enum dhcp_token token; const char *val; struct collection *col; struct expression *nexp, **ep; int known; char *cptr; isc_result_t status; unsigned len; token = peek_token (&val, (unsigned *)0, cfile); /* Check for unary operators... */ switch (token) { case CHECK: skip_token(&val, (unsigned *)0, cfile); token = next_token (&val, (unsigned *)0, cfile); if (token != STRING) { parse_warn (cfile, "string expected."); skip_to_semi (cfile); *lose = 1; return 0; } for (col = collections; col; col = col -> next) if (!strcmp (col -> name, val)) break; if (!col) { parse_warn (cfile, "unknown collection."); *lose = 1; return 0; } if (!expression_allocate (expr, MDL)) log_fatal ("can't allocate expression"); (*expr) -> op = expr_check; (*expr) -> data.check = col; break; case TOKEN_NOT: skip_token(&val, NULL, cfile); if (!expression_allocate (expr, MDL)) log_fatal ("can't allocate expression"); (*expr)->op = expr_not; if (!parse_non_binary (&(*expr)->data.not, cfile, lose, context_boolean)) { if (!*lose) { parse_warn (cfile, "expression expected"); skip_to_semi (cfile); } *lose = 1; expression_dereference (expr, MDL); return (0); } if (!is_boolean_expression ((*expr) -> data.not)) { *lose = 1; parse_warn (cfile, "boolean expression expected"); skip_to_semi (cfile); expression_dereference (expr, MDL); return 0; } break; case LPAREN: skip_token(&val, (unsigned *)0, cfile); if (!parse_expression (expr, cfile, lose, context, (struct expression **)0, expr_none)) { if (!*lose) { parse_warn (cfile, "expression expected"); skip_to_semi (cfile); } *lose = 1; return 0; } token = next_token (&val, (unsigned *)0, cfile); if (token != RPAREN) { *lose = 1; parse_warn (cfile, "right paren expected"); skip_to_semi (cfile); return 0; } break; case EXISTS: skip_token(&val, NULL, cfile); if (!expression_allocate (expr, MDL)) log_fatal ("can't allocate expression"); (*expr)->op = expr_exists; known = 0; /* Pass reference directly to expression structure. */ status = parse_option_name(cfile, 0, &known, &(*expr)->data.option); if (status != ISC_R_SUCCESS || (*expr)->data.option == NULL) { *lose = 1; expression_dereference (expr, MDL); return (0); } break; case STATIC: skip_token(&val, (unsigned *)0, cfile); if (!expression_allocate (expr, MDL)) log_fatal ("can't allocate expression"); (*expr) -> op = expr_static; break; case KNOWN: skip_token(&val, (unsigned *)0, cfile); if (!expression_allocate (expr, MDL)) log_fatal ("can't allocate expression"); (*expr) -> op = expr_known; break; case SUBSTRING: skip_token(&val, (unsigned *)0, cfile); if (!expression_allocate (expr, MDL)) log_fatal ("can't allocate expression"); (*expr) -> op = expr_substring; token = next_token (&val, (unsigned *)0, cfile); if (token != LPAREN) { nolparen: expression_dereference (expr, MDL); parse_warn (cfile, "left parenthesis expected."); *lose = 1; return 0; } if (!parse_data_expression (&(*expr) -> data.substring.expr, cfile, lose)) { nodata: expression_dereference (expr, MDL); if (!*lose) { parse_warn (cfile, "expecting data expression."); skip_to_semi (cfile); *lose = 1; } return 0; } token = next_token (&val, (unsigned *)0, cfile); if (token != COMMA) { nocomma: expression_dereference (expr, MDL); parse_warn (cfile, "comma expected."); *lose = 1; return 0; } if (!parse_numeric_expression (&(*expr) -> data.substring.offset,cfile, lose)) { nonum: if (!*lose) { parse_warn (cfile, "expecting numeric expression."); skip_to_semi (cfile); *lose = 1; } expression_dereference (expr, MDL); return 0; } token = next_token (&val, (unsigned *)0, cfile); if (token != COMMA) goto nocomma; if (!parse_numeric_expression (&(*expr) -> data.substring.len, cfile, lose)) goto nonum; token = next_token (&val, (unsigned *)0, cfile); if (token != RPAREN) { norparen: parse_warn (cfile, "right parenthesis expected."); *lose = 1; expression_dereference (expr, MDL); return 0; } break; case SUFFIX: skip_token(&val, (unsigned *)0, cfile); if (!expression_allocate (expr, MDL)) log_fatal ("can't allocate expression"); (*expr) -> op = expr_suffix; token = next_token (&val, (unsigned *)0, cfile); if (token != LPAREN) goto nolparen; if (!parse_data_expression (&(*expr) -> data.suffix.expr, cfile, lose)) goto nodata; token = next_token (&val, (unsigned *)0, cfile); if (token != COMMA) goto nocomma; if (!parse_numeric_expression (&(*expr) -> data.suffix.len, cfile, lose)) goto nonum; token = next_token (&val, (unsigned *)0, cfile); if (token != RPAREN) goto norparen; break; case LCASE: skip_token(&val, (unsigned *)0, cfile); if (!expression_allocate(expr, MDL)) log_fatal ("can't allocate expression"); (*expr)->op = expr_lcase; token = next_token(&val, (unsigned *)0, cfile); if (token != LPAREN) goto nolparen; if (!parse_data_expression(&(*expr)->data.lcase, cfile, lose)) goto nodata; token = next_token(&val, (unsigned *)0, cfile); if (token != RPAREN) goto norparen; break; case UCASE: skip_token(&val, (unsigned *)0, cfile); if (!expression_allocate(expr, MDL)) log_fatal ("can't allocate expression"); (*expr)->op = expr_ucase; token = next_token (&val, (unsigned *)0, cfile); if (token != LPAREN) goto nolparen; if (!parse_data_expression(&(*expr)->data.ucase, cfile, lose)) goto nodata; token = next_token(&val, (unsigned *)0, cfile); if (token != RPAREN) goto norparen; break; case CONCAT: skip_token(&val, (unsigned *)0, cfile); if (!expression_allocate (expr, MDL)) log_fatal ("can't allocate expression"); (*expr) -> op = expr_concat; token = next_token (&val, (unsigned *)0, cfile); if (token != LPAREN) goto nolparen; if (!parse_data_expression (&(*expr) -> data.concat [0], cfile, lose)) goto nodata; token = next_token (&val, (unsigned *)0, cfile); if (token != COMMA) goto nocomma; concat_another: if (!parse_data_expression (&(*expr) -> data.concat [1], cfile, lose)) goto nodata; token = next_token (&val, (unsigned *)0, cfile); if (token == COMMA) { nexp = (struct expression *)0; if (!expression_allocate (&nexp, MDL)) log_fatal ("can't allocate at CONCAT2"); nexp -> op = expr_concat; expression_reference (&nexp -> data.concat [0], *expr, MDL); expression_dereference (expr, MDL); expression_reference (expr, nexp, MDL); expression_dereference (&nexp, MDL); goto concat_another; } if (token != RPAREN) goto norparen; break; case BINARY_TO_ASCII: skip_token(&val, (unsigned *)0, cfile); if (!expression_allocate (expr, MDL)) log_fatal ("can't allocate expression"); (*expr) -> op = expr_binary_to_ascii; token = next_token (&val, (unsigned *)0, cfile); if (token != LPAREN) goto nolparen; if (!parse_numeric_expression (&(*expr) -> data.b2a.base, cfile, lose)) goto nodata; token = next_token (&val, (unsigned *)0, cfile); if (token != COMMA) goto nocomma; if (!parse_numeric_expression (&(*expr) -> data.b2a.width, cfile, lose)) goto nodata; token = next_token (&val, (unsigned *)0, cfile); if (token != COMMA) goto nocomma; if (!parse_data_expression (&(*expr) -> data.b2a.separator, cfile, lose)) goto nodata; token = next_token (&val, (unsigned *)0, cfile); if (token != COMMA) goto nocomma; if (!parse_data_expression (&(*expr) -> data.b2a.buffer, cfile, lose)) goto nodata; token = next_token (&val, (unsigned *)0, cfile); if (token != RPAREN) goto norparen; break; case REVERSE: skip_token(&val, (unsigned *)0, cfile); if (!expression_allocate (expr, MDL)) log_fatal ("can't allocate expression"); (*expr) -> op = expr_reverse; token = next_token (&val, (unsigned *)0, cfile); if (token != LPAREN) goto nolparen; if (!(parse_numeric_expression (&(*expr) -> data.reverse.width, cfile, lose))) goto nodata; token = next_token (&val, (unsigned *)0, cfile); if (token != COMMA) goto nocomma; if (!(parse_data_expression (&(*expr) -> data.reverse.buffer, cfile, lose))) goto nodata; token = next_token (&val, (unsigned *)0, cfile); if (token != RPAREN) goto norparen; break; case PICK: /* pick (a, b, c) actually produces an internal representation that looks like pick (a, pick (b, pick (c, nil))). */ skip_token(&val, (unsigned *)0, cfile); if (!(expression_allocate (expr, MDL))) log_fatal ("can't allocate expression"); token = next_token (&val, (unsigned *)0, cfile); if (token != LPAREN) goto nolparen; nexp = (struct expression *)0; expression_reference (&nexp, *expr, MDL); do { nexp -> op = expr_pick_first_value; if (!(parse_data_expression (&nexp -> data.pick_first_value.car, cfile, lose))) goto nodata; token = next_token (&val, (unsigned *)0, cfile); if (token == COMMA) { struct expression *foo = (struct expression *)0; if (!expression_allocate (&foo, MDL)) log_fatal ("can't allocate expr"); expression_reference (&nexp -> data.pick_first_value.cdr, foo, MDL); expression_dereference (&nexp, MDL); expression_reference (&nexp, foo, MDL); expression_dereference (&foo, MDL); } } while (token == COMMA); expression_dereference (&nexp, MDL); if (token != RPAREN) goto norparen; break; case OPTION: case CONFIG_OPTION: if (!expression_allocate (expr, MDL)) log_fatal ("can't allocate expression"); (*expr) -> op = (token == OPTION ? expr_option : expr_config_option); skip_token(&val, (unsigned *)0, cfile); known = 0; /* Pass reference directly to expression structure. */ status = parse_option_name(cfile, 0, &known, &(*expr)->data.option); if (status != ISC_R_SUCCESS || (*expr)->data.option == NULL) { *lose = 1; expression_dereference (expr, MDL); return 0; } break; case HARDWARE: skip_token(&val, (unsigned *)0, cfile); if (!expression_allocate (expr, MDL)) log_fatal ("can't allocate expression"); (*expr) -> op = expr_hardware; break; case LEASED_ADDRESS: skip_token(&val, (unsigned *)0, cfile); if (!expression_allocate (expr, MDL)) log_fatal ("can't allocate expression"); (*expr) -> op = expr_leased_address; break; case CLIENT_STATE: skip_token(&val, (unsigned *)0, cfile); if (!expression_allocate (expr, MDL)) log_fatal ("can't allocate expression"); (*expr) -> op = expr_client_state; break; case FILENAME: skip_token(&val, (unsigned *)0, cfile); if (!expression_allocate (expr, MDL)) log_fatal ("can't allocate expression"); (*expr) -> op = expr_filename; break; case SERVER_NAME: skip_token(&val, (unsigned *)0, cfile); if (!expression_allocate (expr, MDL)) log_fatal ("can't allocate expression"); (*expr) -> op = expr_sname; break; case LEASE_TIME: skip_token(&val, (unsigned *)0, cfile); if (!expression_allocate (expr, MDL)) log_fatal ("can't allocate expression"); (*expr) -> op = expr_lease_time; break; case TOKEN_NULL: skip_token(&val, (unsigned *)0, cfile); if (!expression_allocate (expr, MDL)) log_fatal ("can't allocate expression"); (*expr) -> op = expr_null; break; case HOST_DECL_NAME: skip_token(&val, (unsigned *)0, cfile); if (!expression_allocate (expr, MDL)) log_fatal ("can't allocate expression"); (*expr) -> op = expr_host_decl_name; break; case PACKET: skip_token(&val, (unsigned *)0, cfile); if (!expression_allocate (expr, MDL)) log_fatal ("can't allocate expression"); (*expr) -> op = expr_packet; token = next_token (&val, (unsigned *)0, cfile); if (token != LPAREN) goto nolparen; if (!parse_numeric_expression (&(*expr) -> data.packet.offset, cfile, lose)) goto nonum; token = next_token (&val, (unsigned *)0, cfile); if (token != COMMA) goto nocomma; if (!parse_numeric_expression (&(*expr) -> data.packet.len, cfile, lose)) goto nonum; token = next_token (&val, (unsigned *)0, cfile); if (token != RPAREN) goto norparen; break; case STRING: skip_token(&val, &len, cfile); if (!make_const_data (expr, (const unsigned char *)val, len, 1, 1, MDL)) log_fatal ("can't make constant string expression."); break; case EXTRACT_INT: skip_token(&val, (unsigned *)0, cfile); token = next_token (&val, (unsigned *)0, cfile); if (token != LPAREN) { parse_warn (cfile, "left parenthesis expected."); *lose = 1; return 0; } if (!expression_allocate (expr, MDL)) log_fatal ("can't allocate expression"); if (!parse_data_expression (&(*expr) -> data.extract_int, cfile, lose)) { if (!*lose) { parse_warn (cfile, "expecting data expression."); skip_to_semi (cfile); *lose = 1; } expression_dereference (expr, MDL); return 0; } token = next_token (&val, (unsigned *)0, cfile); if (token != COMMA) { parse_warn (cfile, "comma expected."); *lose = 1; expression_dereference (expr, MDL); return 0; } token = next_token (&val, (unsigned *)0, cfile); if (token != NUMBER) { parse_warn (cfile, "number expected."); *lose = 1; expression_dereference (expr, MDL); return 0; } switch (atoi (val)) { case 8: (*expr) -> op = expr_extract_int8; break; case 16: (*expr) -> op = expr_extract_int16; break; case 32: (*expr) -> op = expr_extract_int32; break; default: parse_warn (cfile, "unsupported integer size %d", atoi (val)); *lose = 1; skip_to_semi (cfile); expression_dereference (expr, MDL); return 0; } token = next_token (&val, (unsigned *)0, cfile); if (token != RPAREN) { parse_warn (cfile, "right parenthesis expected."); *lose = 1; expression_dereference (expr, MDL); return 0; } break; case ENCODE_INT: skip_token(&val, (unsigned *)0, cfile); token = next_token (&val, (unsigned *)0, cfile); if (token != LPAREN) { parse_warn (cfile, "left parenthesis expected."); *lose = 1; return 0; } if (!expression_allocate (expr, MDL)) log_fatal ("can't allocate expression"); if (!parse_numeric_expression (&(*expr) -> data.encode_int, cfile, lose)) { parse_warn (cfile, "expecting numeric expression."); skip_to_semi (cfile); *lose = 1; expression_dereference (expr, MDL); return 0; } token = next_token (&val, (unsigned *)0, cfile); if (token != COMMA) { parse_warn (cfile, "comma expected."); *lose = 1; expression_dereference (expr, MDL); return 0; } token = next_token (&val, (unsigned *)0, cfile); if (token != NUMBER) { parse_warn (cfile, "number expected."); *lose = 1; expression_dereference (expr, MDL); return 0; } switch (atoi (val)) { case 8: (*expr) -> op = expr_encode_int8; break; case 16: (*expr) -> op = expr_encode_int16; break; case 32: (*expr) -> op = expr_encode_int32; break; default: parse_warn (cfile, "unsupported integer size %d", atoi (val)); *lose = 1; skip_to_semi (cfile); expression_dereference (expr, MDL); return 0; } token = next_token (&val, (unsigned *)0, cfile); if (token != RPAREN) { parse_warn (cfile, "right parenthesis expected."); *lose = 1; expression_dereference (expr, MDL); return 0; } break; case NUMBER: /* If we're in a numeric context, this should just be a number, by itself. */ if (context == context_numeric || context == context_data_or_numeric) { skip_token(&val, (unsigned *)0, cfile); if (!expression_allocate (expr, MDL)) log_fatal ("can't allocate expression"); (*expr) -> op = expr_const_int; (*expr) -> data.const_int = atoi (val); break; } case NUMBER_OR_NAME: if (!expression_allocate (expr, MDL)) log_fatal ("can't allocate expression"); (*expr) -> op = expr_const_data; if (!parse_cshl (&(*expr) -> data.const_data, cfile)) { expression_dereference (expr, MDL); return 0; } break; case NS_FORMERR: known = FORMERR; goto ns_const; ns_const: skip_token(&val, (unsigned *)0, cfile); if (!expression_allocate (expr, MDL)) log_fatal ("can't allocate expression"); (*expr) -> op = expr_const_int; (*expr) -> data.const_int = known; break; case NS_NOERROR: known = ISC_R_SUCCESS; goto ns_const; case NS_NOTAUTH: known = DHCP_R_NOTAUTH; goto ns_const; case NS_NOTIMP: known = ISC_R_NOTIMPLEMENTED; goto ns_const; case NS_NOTZONE: known = DHCP_R_NOTZONE; goto ns_const; case NS_NXDOMAIN: known = DHCP_R_NXDOMAIN; goto ns_const; case NS_NXRRSET: known = DHCP_R_NXRRSET; goto ns_const; case NS_REFUSED: known = DHCP_R_REFUSED; goto ns_const; case NS_SERVFAIL: known = DHCP_R_SERVFAIL; goto ns_const; case NS_YXDOMAIN: known = DHCP_R_YXDOMAIN; goto ns_const; case NS_YXRRSET: known = DHCP_R_YXRRSET; goto ns_const; case BOOTING: known = S_INIT; goto ns_const; case REBOOT: known = S_REBOOTING; goto ns_const; case SELECT: known = S_SELECTING; goto ns_const; case REQUEST: known = S_REQUESTING; goto ns_const; case BOUND: known = S_BOUND; goto ns_const; case RENEW: known = S_RENEWING; goto ns_const; case REBIND: known = S_REBINDING; goto ns_const; case DEFINED: skip_token(&val, (unsigned *)0, cfile); token = next_token (&val, (unsigned *)0, cfile); if (token != LPAREN) goto nolparen; token = next_token (&val, (unsigned *)0, cfile); if (token != NAME && token != NUMBER_OR_NAME) { parse_warn (cfile, "%s can't be a variable name", val); skip_to_semi (cfile); *lose = 1; return 0; } if (!expression_allocate (expr, MDL)) log_fatal ("can't allocate expression"); (*expr) -> op = expr_variable_exists; (*expr) -> data.variable = dmalloc (strlen (val) + 1, MDL); if (!(*expr)->data.variable) log_fatal ("can't allocate variable name"); strcpy ((*expr) -> data.variable, val); token = next_token (&val, (unsigned *)0, cfile); if (token != RPAREN) goto norparen; break; /* This parses 'gethostname()'. */ case GETHOSTNAME: skip_token(&val, NULL, cfile); if (!expression_allocate(expr, MDL)) log_fatal("can't allocate expression"); (*expr)->op = expr_gethostname; token = next_token(NULL, NULL, cfile); if (token != LPAREN) goto nolparen; token = next_token(NULL, NULL, cfile); if (token != RPAREN) goto norparen; break; case GETHOSTBYNAME: skip_token(&val, NULL, cfile); token = next_token(NULL, NULL, cfile); if (token != LPAREN) goto nolparen; /* The argument is a quoted string. */ token = next_token(&val, NULL, cfile); if (token != STRING) { parse_warn(cfile, "Expecting quoted literal: " "\"foo.example.com\""); skip_to_semi(cfile); *lose = 1; return 0; } if (!make_host_lookup(expr, val)) log_fatal("Error creating gethostbyname() internal " "record. (%s:%d)", MDL); token = next_token(NULL, NULL, cfile); if (token != RPAREN) goto norparen; break; case V6RELAY: skip_token(&val, NULL, cfile); if (!expression_allocate (expr, MDL)) log_fatal ("can't allocate expression"); (*expr)->op = expr_v6relay; token = next_token (&val, NULL, cfile); if (token != LPAREN) goto nolparen; if (!parse_numeric_expression (&(*expr)->data.v6relay.relay, cfile, lose)) goto nodata; token = next_token (&val, NULL, cfile); if (token != COMMA) goto nocomma; if (!parse_data_expression (&(*expr)->data.v6relay.roption, cfile, lose)) goto nodata; token = next_token (&val, NULL, cfile); if (token != RPAREN) goto norparen; break; /* Not a valid start to an expression... */ default: if (token != NAME && token != NUMBER_OR_NAME) return 0; skip_token(&val, (unsigned *)0, cfile); /* Save the name of the variable being referenced. */ cptr = dmalloc (strlen (val) + 1, MDL); if (!cptr) log_fatal ("can't allocate variable name"); strcpy (cptr, val); /* Simple variable reference, as far as we can tell. */ token = peek_token (&val, (unsigned *)0, cfile); if (token != LPAREN) { if (!expression_allocate (expr, MDL)) log_fatal ("can't allocate expression"); (*expr) -> op = expr_variable_reference; (*expr) -> data.variable = cptr; break; } skip_token(&val, (unsigned *)0, cfile); if (!expression_allocate (expr, MDL)) log_fatal ("can't allocate expression"); (*expr) -> op = expr_funcall; (*expr) -> data.funcall.name = cptr; /* Now parse the argument list. */ ep = &(*expr) -> data.funcall.arglist; do { if (!expression_allocate (ep, MDL)) log_fatal ("can't allocate expression"); (*ep) -> op = expr_arg; if (!parse_expression (&(*ep) -> data.arg.val, cfile, lose, context_any, (struct expression **)0, expr_none)) { if (!*lose) { parse_warn (cfile, "expecting expression."); *lose = 1; } skip_to_semi (cfile); expression_dereference (expr, MDL); return 0; } ep = &((*ep) -> data.arg.next); token = next_token (&val, (unsigned *)0, cfile); } while (token == COMMA); if (token != RPAREN) { parse_warn (cfile, "Right parenthesis expected."); skip_to_semi (cfile); *lose = 1; expression_dereference (expr, MDL); return 0; } break; } return 1; } /* Parse an expression. */ int parse_expression (expr, cfile, lose, context, plhs, binop) struct expression **expr; struct parse *cfile; int *lose; enum expression_context context; struct expression **plhs; enum expr_op binop; { enum dhcp_token token; const char *val; struct expression *rhs = (struct expression *)0, *tmp; struct expression *lhs = (struct expression *)0; enum expr_op next_op; enum expression_context lhs_context = context_any, rhs_context = context_any; /* Consume the left hand side we were passed. */ if (plhs) { expression_reference (&lhs, *plhs, MDL); expression_dereference (plhs, MDL); } new_rhs: if (!parse_non_binary (&rhs, cfile, lose, context)) { /* If we already have a left-hand side, then it's not okay for there not to be a right-hand side here, so we need to flag it as an error. */ if (lhs) { if (!*lose) { parse_warn (cfile, "expecting right-hand side."); *lose = 1; skip_to_semi (cfile); } expression_dereference (&lhs, MDL); } return 0; } /* At this point, rhs contains either an entire subexpression, or at least a left-hand-side. If we do not see a binary token as the next token, we're done with the expression. */ token = peek_token (&val, (unsigned *)0, cfile); switch (token) { case BANG: skip_token(&val, (unsigned *)0, cfile); token = peek_token (&val, (unsigned *)0, cfile); if (token != EQUAL) { parse_warn (cfile, "! in boolean context without ="); *lose = 1; skip_to_semi (cfile); if (lhs) expression_dereference (&lhs, MDL); return 0; } next_op = expr_not_equal; context = expression_context (rhs); break; case EQUAL: next_op = expr_equal; context = expression_context (rhs); break; case TILDE: #ifdef HAVE_REGEX_H skip_token(&val, NULL, cfile); token = peek_token(&val, NULL, cfile); if (token == TILDE) next_op = expr_iregex_match; else if (token == EQUAL) next_op = expr_regex_match; else { parse_warn(cfile, "expecting ~= or ~~ operator"); *lose = 1; skip_to_semi(cfile); if (lhs) expression_dereference(&lhs, MDL); return 0; } context = expression_context(rhs); #else parse_warn(cfile, "No support for regex operator."); *lose = 1; skip_to_semi(cfile); if (lhs != NULL) expression_dereference(&lhs, MDL); return 0; #endif break; case AND: next_op = expr_and; context = expression_context (rhs); break; case OR: next_op = expr_or; context = expression_context (rhs); break; case PLUS: next_op = expr_add; context = expression_context (rhs); break; case MINUS: next_op = expr_subtract; context = expression_context (rhs); break; case SLASH: next_op = expr_divide; context = expression_context (rhs); break; case ASTERISK: next_op = expr_multiply; context = expression_context (rhs); break; case PERCENT: next_op = expr_remainder; context = expression_context (rhs); break; case AMPERSAND: next_op = expr_binary_and; context = expression_context (rhs); break; case PIPE: next_op = expr_binary_or; context = expression_context (rhs); break; case CARET: next_op = expr_binary_xor; context = expression_context (rhs); break; default: next_op = expr_none; } /* If we have no lhs yet, we just parsed it. */ if (!lhs) { /* If there was no operator following what we just parsed, then we're done - return it. */ if (next_op == expr_none) { *expr = rhs; return 1; } lhs = rhs; rhs = (struct expression *)0; binop = next_op; skip_token(&val, (unsigned *)0, cfile); goto new_rhs; } /* If the next binary operator is of greater precedence than the * current operator, then rhs we have parsed so far is actually * the lhs of the next operator. To get this value, we have to * recurse. */ if (binop != expr_none && next_op != expr_none && op_precedence (binop, next_op) < 0) { /* Eat the subexpression operator token, which we pass to * parse_expression...we only peek()'d earlier. */ skip_token(&val, (unsigned *)0, cfile); /* Continue parsing of the right hand side with that token. */ tmp = rhs; rhs = (struct expression *)0; if (!parse_expression (&rhs, cfile, lose, op_context (next_op), &tmp, next_op)) { if (!*lose) { parse_warn (cfile, "expecting a subexpression"); *lose = 1; } return 0; } next_op = expr_none; } if (binop != expr_none) { rhs_context = expression_context(rhs); lhs_context = expression_context(lhs); if ((rhs_context != context_any) && (lhs_context != context_any) && (rhs_context != lhs_context)) { parse_warn (cfile, "illegal expression relating different types"); skip_to_semi (cfile); expression_dereference (&rhs, MDL); expression_dereference (&lhs, MDL); *lose = 1; return 0; } switch(binop) { case expr_not_equal: case expr_equal: if ((rhs_context != context_data_or_numeric) && (rhs_context != context_data) && (rhs_context != context_numeric) && (rhs_context != context_any)) { parse_warn (cfile, "expecting data/numeric expression"); skip_to_semi (cfile); expression_dereference (&rhs, MDL); *lose = 1; return 0; } break; case expr_regex_match: #ifdef HAVE_REGEX_H if (expression_context(rhs) != context_data) { parse_warn(cfile, "expecting data expression"); skip_to_semi(cfile); expression_dereference(&rhs, MDL); *lose = 1; return 0; } #else /* It should not be possible to attempt to parse the right * hand side of an operator there is no support for. */ log_fatal("Impossible condition at %s:%d.", MDL); #endif break; case expr_and: case expr_or: if ((rhs_context != context_boolean) && (rhs_context != context_any)) { parse_warn (cfile, "expecting boolean expressions"); skip_to_semi (cfile); expression_dereference (&rhs, MDL); *lose = 1; return 0; } break; case expr_add: case expr_subtract: case expr_divide: case expr_multiply: case expr_remainder: case expr_binary_and: case expr_binary_or: case expr_binary_xor: if ((rhs_context != context_numeric) && (rhs_context != context_any)) { parse_warn (cfile, "expecting numeric expressions"); skip_to_semi (cfile); expression_dereference (&rhs, MDL); *lose = 1; return 0; } break; default: break; } } /* Now, if we didn't find a binary operator, we're done parsing this subexpression, so combine it with the preceding binary operator and return the result. */ if (next_op == expr_none) { if (!expression_allocate (expr, MDL)) log_fatal ("Can't allocate expression!"); (*expr) -> op = binop; /* All the binary operators' data union members are the same, so we'll cheat and use the member for the equals operator. */ (*expr) -> data.equal [0] = lhs; (*expr) -> data.equal [1] = rhs; return 1; } /* Eat the operator token - we now know it was a binary operator... */ skip_token(&val, (unsigned *)0, cfile); /* Now combine the LHS and the RHS using binop. */ tmp = (struct expression *)0; if (!expression_allocate (&tmp, MDL)) log_fatal ("No memory for equal precedence combination."); /* Store the LHS and RHS. */ tmp -> data.equal [0] = lhs; tmp -> data.equal [1] = rhs; tmp -> op = binop; lhs = tmp; tmp = (struct expression *)0; rhs = (struct expression *)0; binop = next_op; goto new_rhs; } int parse_option_data (expr, cfile, lookups, option) struct expression **expr; struct parse *cfile; int lookups; struct option *option; { const char *val; const char *fmt = NULL; struct expression *tmp; enum dhcp_token token; do { /* * Set a flag if this is an array of a simple type (i.e., * not an array of pairs of IP addresses, or something like * that. */ int uniform = 0; and_again: /* Set fmt to start of format for 'A' and one char back * for 'a'. */ if ((fmt != NULL) && (fmt != option->format) && (*fmt == 'a')) fmt -= 1; else if ((fmt == NULL) || (*fmt == 'A')) fmt = option->format; /* 'a' means always uniform */ if ((fmt[0] != 'Z') && (tolower((unsigned char)fmt[1]) == 'a')) uniform = 1; do { if ((*fmt == 'A') || (*fmt == 'a')) break; if (*fmt == 'o') { /* consume the optional flag */ fmt++; continue; } if (fmt[1] == 'o') { /* * A value for the current format is * optional - check to see if the next * token is a semi-colon if so we don't * need to parse it and doing so would * consume the semi-colon which our * caller is expecting to parse */ token = peek_token(&val, (unsigned *)0, cfile); if (token == SEMI) { fmt++; continue; } } tmp = *expr; *expr = NULL; if (!parse_option_token(expr, cfile, &fmt, tmp, uniform, lookups)) { if (fmt [1] != 'o') { if (tmp) expression_dereference (&tmp, MDL); return 0; } *expr = tmp; tmp = NULL; } if (tmp) expression_dereference (&tmp, MDL); fmt++; } while (*fmt != '\0'); if ((*fmt == 'A') || (*fmt == 'a')) { token = peek_token (&val, (unsigned *)0, cfile); /* Comma means: continue with next element in array */ if (token == COMMA) { skip_token(&val, (unsigned *)0, cfile); continue; } /* no comma: end of array. 'A' or end of string means: leave the loop */ if ((*fmt == 'A') || (fmt[1] == '\0')) break; /* 'a' means: go on with next char */ if (*fmt == 'a') { fmt++; goto and_again; } } } while ((*fmt == 'A') || (*fmt == 'a')); return 1; } /* option-statement :== identifier DOT identifier SEMI | identifier SEMI Option syntax is handled specially through format strings, so it would be painful to come up with BNF for it. However, it always starts as above and ends in a SEMI. */ int parse_option_statement (result, cfile, lookups, option, op) struct executable_statement **result; struct parse *cfile; int lookups; struct option *option; enum statement_op op; { const char *val; enum dhcp_token token; struct expression *expr = (struct expression *)0; int lose; token = peek_token (&val, (unsigned *)0, cfile); if ((token == SEMI) && (option->format[0] != 'Z')) { /* Eat the semicolon... */ /* * XXXSK: I'm not sure why we should ever get here, but we * do during our startup. This confuses things if * we are parsing a zero-length option, so don't * eat the semicolon token in that case. */ skip_token(&val, (unsigned *)0, cfile); } else if (token == EQUAL) { /* Eat the equals sign. */ skip_token(&val, (unsigned *)0, cfile); /* Parse a data expression and use its value for the data. */ if (!parse_data_expression (&expr, cfile, &lose)) { /* In this context, we must have an executable statement, so if we found something else, it's still an error. */ if (!lose) { parse_warn (cfile, "expecting a data expression."); skip_to_semi (cfile); } return 0; } } else { if (! parse_option_data(&expr, cfile, lookups, option)) return 0; } if (!parse_semi (cfile)) return 0; if (!executable_statement_allocate (result, MDL)) log_fatal ("no memory for option statement."); (*result)->op = op; if (expr && !option_cache (&(*result)->data.option, NULL, expr, option, MDL)) log_fatal ("no memory for option cache"); if (expr) expression_dereference (&expr, MDL); return 1; } int parse_option_token (rv, cfile, fmt, expr, uniform, lookups) struct expression **rv; struct parse *cfile; const char **fmt; struct expression *expr; int uniform; int lookups; { const char *val; enum dhcp_token token; struct expression *t = (struct expression *)0; unsigned char buf [4]; unsigned len; struct iaddr addr; int compress; isc_boolean_t freeval = ISC_FALSE; const char *f, *g; struct enumeration_value *e; switch (**fmt) { case 'U': token = next_token (&val, &len, cfile); if (!is_identifier (token)) { if ((*fmt) [1] != 'o') { parse_warn (cfile, "expecting identifier."); if (token != SEMI) skip_to_semi (cfile); } return 0; } if (!make_const_data (&t, (const unsigned char *)val, len, 1, 1, MDL)) log_fatal ("No memory for %s", val); break; case 'E': g = strchr (*fmt, '.'); if (!g) { parse_warn (cfile, "malformed encapsulation format (bug!)"); skip_to_semi (cfile); return 0; } *fmt = g; /* FALL THROUGH */ /* to get string value for the option */ case 'X': token = peek_token (&val, (unsigned *)0, cfile); if (token == NUMBER_OR_NAME || token == NUMBER) { if (!expression_allocate (&t, MDL)) return 0; if (!parse_cshl (&t -> data.const_data, cfile)) { expression_dereference (&t, MDL); return 0; } t -> op = expr_const_data; } else { token = next_token (&val, &len, cfile); if(token == STRING) { if (!make_const_data (&t, (const unsigned char *)val, len, 1, 1, MDL)) log_fatal ("No memory for \"%s\"", val); } else { if ((*fmt) [1] != 'o') { parse_warn (cfile, "expecting string " "or hexadecimal data."); skip_to_semi (cfile); } return 0; } } break; case 'D': /* Domain list... */ if ((*fmt)[1] == 'c') { compress = 1; /* Skip the compress-flag atom. */ (*fmt)++; } else compress = 0; t = parse_domain_list(cfile, compress); if (!t) { if ((*fmt)[1] != 'o') skip_to_semi(cfile); return 0; } break; case 'd': /* Domain name... */ val = parse_host_name (cfile); if (!val) { parse_warn (cfile, "not a valid domain name."); skip_to_semi (cfile); return 0; } len = strlen (val); freeval = ISC_TRUE; goto make_string; case 't': /* Text string... */ token = next_token (&val, &len, cfile); if (token != STRING && !is_identifier (token)) { if ((*fmt) [1] != 'o') { parse_warn (cfile, "expecting string."); if (token != SEMI) skip_to_semi (cfile); } return 0; } make_string: if (!make_const_data (&t, (const unsigned char *)val, len, 1, 1, MDL)) log_fatal ("No memory for concatenation"); if (freeval == ISC_TRUE) { dfree((char *)val, MDL); freeval = ISC_FALSE; POST(freeval); } break; case 'N': f = (*fmt) + 1; g = strchr (*fmt, '.'); if (!g) { parse_warn (cfile, "malformed %s (bug!)", "enumeration format"); foo: skip_to_semi (cfile); return 0; } *fmt = g; token = next_token (&val, (unsigned *)0, cfile); if (!is_identifier (token)) { parse_warn (cfile, "identifier expected"); goto foo; } e = find_enumeration_value (f, (*fmt) - f, &len, val); if (!e) { parse_warn (cfile, "unknown value"); goto foo; } if (!make_const_data (&t, &e -> value, len, 0, 1, MDL)) return 0; break; case 'I': /* IP address or hostname. */ if (lookups) { if (!parse_ip_addr_or_hostname (&t, cfile, uniform)) return 0; } else { if (!parse_ip_addr (cfile, &addr)) return 0; if (!make_const_data (&t, addr.iabuf, addr.len, 0, 1, MDL)) return 0; } break; case '6': /* IPv6 address. */ if (!parse_ip6_addr(cfile, &addr)) { return 0; } if (!make_const_data(&t, addr.iabuf, addr.len, 0, 1, MDL)) { return 0; } break; case 'T': /* Lease interval. */ token = next_token (&val, (unsigned *)0, cfile); if (token != INFINITE) goto check_number; putLong (buf, -1); if (!make_const_data (&t, buf, 4, 0, 1, MDL)) return 0; break; case 'L': /* Unsigned 32-bit integer... */ case 'l': /* Signed 32-bit integer... */ token = next_token (&val, (unsigned *)0, cfile); check_number: if ((token != NUMBER) && (token != NUMBER_OR_NAME)) { need_number: if ((*fmt) [1] != 'o') { parse_warn (cfile, "expecting number."); if (token != SEMI) skip_to_semi (cfile); } return 0; } convert_num (cfile, buf, val, 0, 32); if (!make_const_data (&t, buf, 4, 0, 1, MDL)) return 0; break; case 's': /* Signed 16-bit integer. */ case 'S': /* Unsigned 16-bit integer. */ token = next_token (&val, (unsigned *)0, cfile); if ((token != NUMBER) && (token != NUMBER_OR_NAME)) goto need_number; convert_num (cfile, buf, val, 0, 16); if (!make_const_data (&t, buf, 2, 0, 1, MDL)) return 0; break; case 'b': /* Signed 8-bit integer. */ case 'B': /* Unsigned 8-bit integer. */ token = next_token (&val, (unsigned *)0, cfile); if ((token != NUMBER) && (token != NUMBER_OR_NAME)) goto need_number; convert_num (cfile, buf, val, 0, 8); if (!make_const_data (&t, buf, 1, 0, 1, MDL)) return 0; break; case 'f': /* Boolean flag. */ token = next_token (&val, (unsigned *)0, cfile); if (!is_identifier (token)) { if ((*fmt) [1] != 'o') parse_warn (cfile, "expecting identifier."); bad_flag: if ((*fmt) [1] != 'o') { if (token != SEMI) skip_to_semi (cfile); } return 0; } if (!strcasecmp (val, "true") || !strcasecmp (val, "on")) buf [0] = 1; else if (!strcasecmp (val, "false") || !strcasecmp (val, "off")) buf [0] = 0; else if (!strcasecmp (val, "ignore")) buf [0] = 2; else { if ((*fmt) [1] != 'o') parse_warn (cfile, "expecting boolean."); goto bad_flag; } if (!make_const_data (&t, buf, 1, 0, 1, MDL)) return 0; break; case 'Z': /* Zero-length option. */ token = peek_token (&val, (unsigned *)0, cfile); if (token != SEMI) { parse_warn(cfile, "semicolon expected."); skip_to_semi(cfile); } buf[0] = '\0'; if (!make_const_data(&t, /* expression */ buf, /* buffer */ 0, /* length */ 0, /* terminated */ 1, /* allocate */ MDL)) return 0; break; default: parse_warn (cfile, "Bad format '%c' in parse_option_token.", **fmt); skip_to_semi (cfile); return 0; } if (expr) { if (!make_concat (rv, expr, t)) return 0; } else expression_reference (rv, t, MDL); expression_dereference (&t, MDL); return 1; } int parse_option_decl (oc, cfile) struct option_cache **oc; struct parse *cfile; { const char *val; int token; u_int8_t buf [4]; u_int8_t hunkbuf [1024]; unsigned hunkix = 0; const char *fmt, *f; struct option *option=NULL; struct iaddr ip_addr; u_int8_t *dp; const u_int8_t *cdp; unsigned len; int nul_term = 0; struct buffer *bp; int known = 0; int compress; struct expression *express = NULL; struct enumeration_value *e; isc_result_t status; status = parse_option_name (cfile, 0, &known, &option); if (status != ISC_R_SUCCESS || option == NULL) return 0; fmt = option->format; /* Parse the option data... */ do { for (; *fmt; fmt++) { if (*fmt == 'A') { /* 'A' is an array of records, start at * the beginning */ fmt = option->format; break; } if (*fmt == 'a') { /* 'a' is an array of the last field, * back up one format character */ fmt--; break; } if (*fmt == 'o' && fmt != option -> format) continue; switch (*fmt) { case 'E': fmt = strchr (fmt, '.'); if (!fmt) { parse_warn (cfile, "malformed %s (bug!)", "encapsulation format"); goto parse_exit; } /* FALL THROUGH */ /* to get string value for the option */ case 'X': len = parse_X (cfile, &hunkbuf [hunkix], sizeof hunkbuf - hunkix); hunkix += len; break; case 't': /* Text string... */ token = peek_token (&val, &len, cfile); if (token == SEMI && fmt[1] == 'o') { fmt++; break; } token = next_token (&val, &len, cfile); if (token != STRING) { parse_warn (cfile, "expecting string."); goto parse_exit; } if (hunkix + len + 1 > sizeof hunkbuf) { parse_warn (cfile, "option data buffer %s", "overflow"); goto parse_exit; } memcpy (&hunkbuf [hunkix], val, len + 1); nul_term = 1; hunkix += len; break; case 'D': if (fmt[1] == 'c') { compress = 1; fmt++; } else compress = 0; express = parse_domain_list(cfile, compress); if (express == NULL) goto exit; if (express->op != expr_const_data) { parse_warn(cfile, "unexpected " "expression"); goto parse_exit; } len = express->data.const_data.len; cdp = express->data.const_data.data; if ((hunkix + len) > sizeof(hunkbuf)) { parse_warn(cfile, "option data buffer " "overflow"); goto parse_exit; } memcpy(&hunkbuf[hunkix], cdp, len); hunkix += len; expression_dereference(&express, MDL); break; case 'N': f = fmt + 1; fmt = strchr (fmt, '.'); if (!fmt) { parse_warn (cfile, "malformed %s (bug!)", "enumeration format"); goto parse_exit; } token = next_token (&val, (unsigned *)0, cfile); if (!is_identifier (token)) { parse_warn (cfile, "identifier expected"); goto parse_exit; } e = find_enumeration_value (f, fmt - f, &len, val); if (!e) { parse_warn (cfile, "unknown value"); goto parse_exit; } dp = &e -> value; goto alloc; case '6': if (!parse_ip6_addr(cfile, &ip_addr)) goto exit; len = ip_addr.len; dp = ip_addr.iabuf; goto alloc; case 'I': /* IP address. */ if (!parse_ip_addr (cfile, &ip_addr)) goto exit; len = ip_addr.len; dp = ip_addr.iabuf; alloc: if (hunkix + len > sizeof hunkbuf) { parse_warn (cfile, "option data buffer %s", "overflow"); goto parse_exit; } memcpy (&hunkbuf [hunkix], dp, len); hunkix += len; break; case 'L': /* Unsigned 32-bit integer... */ case 'l': /* Signed 32-bit integer... */ token = next_token (&val, (unsigned *)0, cfile); if ((token != NUMBER) && (token != NUMBER_OR_NAME)) { need_number: parse_warn (cfile, "expecting number."); if (token != SEMI) goto parse_exit; else goto exit; } convert_num (cfile, buf, val, 0, 32); len = 4; dp = buf; goto alloc; case 's': /* Signed 16-bit integer. */ case 'S': /* Unsigned 16-bit integer. */ token = next_token (&val, (unsigned *)0, cfile); if ((token != NUMBER) && (token != NUMBER_OR_NAME)) goto need_number; convert_num (cfile, buf, val, 0, 16); len = 2; dp = buf; goto alloc; case 'b': /* Signed 8-bit integer. */ case 'B': /* Unsigned 8-bit integer. */ token = next_token (&val, (unsigned *)0, cfile); if ((token != NUMBER) && (token != NUMBER_OR_NAME)) goto need_number; convert_num (cfile, buf, val, 0, 8); len = 1; dp = buf; goto alloc; case 'f': /* Boolean flag. */ token = next_token (&val, (unsigned *)0, cfile); if (!is_identifier (token)) { parse_warn (cfile, "expecting identifier."); bad_flag: if (token != SEMI) goto parse_exit; else goto exit; } if (!strcasecmp (val, "true") || !strcasecmp (val, "on")) buf [0] = 1; else if (!strcasecmp (val, "false") || !strcasecmp (val, "off")) buf [0] = 0; else { parse_warn (cfile, "expecting boolean."); goto bad_flag; } len = 1; dp = buf; goto alloc; case 'Z': /* Zero-length option */ token = peek_token(&val, (unsigned *)0, cfile); if (token != SEMI) { parse_warn(cfile, "semicolon expected."); goto parse_exit; } len = 0; buf[0] = '\0'; break; default: log_error ("parse_option_param: Bad format %c", *fmt); goto parse_exit; } } token = next_token (&val, (unsigned *)0, cfile); } while (*fmt && token == COMMA); if (token != SEMI) { parse_warn (cfile, "semicolon expected."); goto parse_exit; } bp = (struct buffer *)0; if (!buffer_allocate (&bp, hunkix + nul_term, MDL)) log_fatal ("no memory to store option declaration."); memcpy (bp -> data, hunkbuf, hunkix + nul_term); if (!option_cache_allocate (oc, MDL)) log_fatal ("out of memory allocating option cache."); (*oc) -> data.buffer = bp; (*oc) -> data.data = &bp -> data [0]; (*oc) -> data.terminated = nul_term; (*oc) -> data.len = hunkix; option_reference(&(*oc)->option, option, MDL); option_dereference(&option, MDL); return 1; parse_exit: if (express != NULL) expression_dereference(&express, MDL); skip_to_semi (cfile); exit: option_dereference(&option, MDL); return 0; } /* Consider merging parse_cshl into this. */ int parse_X (cfile, buf, max) struct parse *cfile; u_int8_t *buf; unsigned max; { int token; const char *val; unsigned len; token = peek_token (&val, (unsigned *)0, cfile); if (token == NUMBER_OR_NAME || token == NUMBER) { len = 0; do { token = next_token (&val, (unsigned *)0, cfile); if (token != NUMBER && token != NUMBER_OR_NAME) { parse_warn (cfile, "expecting hexadecimal constant."); skip_to_semi (cfile); return 0; } convert_num (cfile, &buf [len], val, 16, 8); if (len++ > max) { parse_warn (cfile, "hexadecimal constant too long."); skip_to_semi (cfile); return 0; } token = peek_token (&val, (unsigned *)0, cfile); if (token == COLON) token = next_token (&val, (unsigned *)0, cfile); } while (token == COLON); val = (char *)buf; } else if (token == STRING) { skip_token(&val, &len, cfile); if (len + 1 > max) { parse_warn (cfile, "string constant too long."); skip_to_semi (cfile); return 0; } memcpy (buf, val, len + 1); } else { parse_warn (cfile, "expecting string or hexadecimal data"); skip_to_semi (cfile); return 0; } return len; } int parse_warn (struct parse *cfile, const char *fmt, ...) { va_list list; char lexbuf [256]; char mbuf [1024]; char fbuf [1024]; unsigned i, lix; do_percentm (mbuf, fmt); /* %Audit% This is log output. %2004.06.17,Safe% * If we truncate we hope the user can get a hint from the log. */ snprintf (fbuf, sizeof fbuf, "%s line %d: %s", cfile -> tlname, cfile -> lexline, mbuf); va_start (list, fmt); vsnprintf (mbuf, sizeof mbuf, fbuf, list); va_end (list); lix = 0; for (i = 0; cfile -> token_line [i] && i < (cfile -> lexchar - 1); i++) { if (lix < (sizeof lexbuf) - 1) lexbuf [lix++] = ' '; if (cfile -> token_line [i] == '\t') { for (; lix < (sizeof lexbuf) - 1 && (lix & 7); lix++) lexbuf [lix] = ' '; } } lexbuf [lix] = 0; #ifndef DEBUG syslog (LOG_ERR, "%s", mbuf); syslog (LOG_ERR, "%s", cfile -> token_line); if (cfile -> lexchar < 81) syslog (LOG_ERR, "%s^", lexbuf); #endif if (log_perror) { IGNORE_RET (write (STDERR_FILENO, mbuf, strlen (mbuf))); IGNORE_RET (write (STDERR_FILENO, "\n", 1)); IGNORE_RET (write (STDERR_FILENO, cfile -> token_line, strlen (cfile -> token_line))); IGNORE_RET (write (STDERR_FILENO, "\n", 1)); if (cfile -> lexchar < 81) IGNORE_RET (write (STDERR_FILENO, lexbuf, lix)); IGNORE_RET (write (STDERR_FILENO, "^\n", 2)); } cfile -> warnings_occurred = 1; return 0; } struct expression * parse_domain_list(struct parse *cfile, int compress) { const char *val; enum dhcp_token token = SEMI; struct expression *t = NULL; unsigned len, clen = 0; int result; unsigned char compbuf[256 * NS_MAXCDNAME]; const unsigned char *dnptrs[256], **lastdnptr; memset(compbuf, 0, sizeof(compbuf)); memset(dnptrs, 0, sizeof(dnptrs)); dnptrs[0] = compbuf; lastdnptr = &dnptrs[255]; do { /* Consume the COMMA token if peeked. */ if (token == COMMA) skip_token(&val, NULL, cfile); /* Get next (or first) value. */ token = next_token(&val, &len, cfile); if (token != STRING) { parse_warn(cfile, "Expecting a domain string."); return NULL; } /* If compression pointers are enabled, compress. If not, * just pack the names in series into the buffer. */ if (compress) { result = MRns_name_compress(val, compbuf + clen, sizeof(compbuf) - clen, dnptrs, lastdnptr); if (result < 0) { parse_warn(cfile, "Error compressing domain " "list: %m"); return NULL; } clen += result; } else { result = MRns_name_pton(val, compbuf + clen, sizeof(compbuf) - clen); /* result == 1 means the input was fully qualified. * result == 0 means the input wasn't. * result == -1 means bad things. */ if (result < 0) { parse_warn(cfile, "Error assembling domain " "list: %m"); return NULL; } /* * We need to figure out how many bytes to increment * our buffer pointer since pton doesn't tell us. */ while (compbuf[clen] != 0) clen += compbuf[clen] + 1; /* Count the last label (0). */ clen++; } if (clen > sizeof(compbuf)) log_fatal("Impossible error at %s:%d", MDL); token = peek_token(&val, NULL, cfile); } while (token == COMMA); if (!make_const_data(&t, compbuf, clen, 1, 1, MDL)) log_fatal("No memory for domain list object."); return t; } dhcp-4.4.1/common/print.c000644 000765 000024 00000076742 13243301226 015523 0ustar00tmarkstaff000000 000000 /* print.c Turn data structures into printable text. */ /* * Copyright (c) 2004-2018 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1995-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" int db_time_format = DEFAULT_TIME_FORMAT; char *quotify_string (const char *s, const char *file, int line) { unsigned len = 0; const char *sp; char *buf, *nsp; for (sp = s; sp && *sp; sp++) { if (*sp == ' ') len++; else if (!isascii ((int)*sp) || !isprint ((int)*sp)) len += 4; else if (*sp == '"' || *sp == '\\') len += 2; else len++; } buf = dmalloc (len + 1, file, line); if (buf) { nsp = buf; for (sp = s; sp && *sp; sp++) { if (*sp == ' ') *nsp++ = ' '; else if (!isascii ((int)*sp) || !isprint ((int)*sp)) { sprintf (nsp, "\\%03o", *(const unsigned char *)sp); nsp += 4; } else if (*sp == '"' || *sp == '\\') { *nsp++ = '\\'; *nsp++ = *sp; } else *nsp++ = *sp; } *nsp++ = 0; } return buf; } char *quotify_buf (const unsigned char *s, unsigned len, char enclose_char, const char *file, int line) { unsigned nulen = 0; char *buf, *nsp; int i; for (i = 0; i < len; i++) { if (s [i] == ' ') nulen++; else if (!isascii (s [i]) || !isprint (s [i])) nulen += 4; else if (s [i] == '"' || s [i] == '\\') nulen += 2; else nulen++; } if (enclose_char) { nulen +=2 ; } buf = dmalloc (nulen + 1, MDL); if (buf) { nsp = buf; if (enclose_char) { *nsp++ = enclose_char; } for (i = 0; i < len; i++) { if (s [i] == ' ') *nsp++ = ' '; else if (!isascii (s [i]) || !isprint (s [i])) { sprintf (nsp, "\\%03o", s [i]); nsp += 4; } else if (s [i] == '"' || s [i] == '\\') { *nsp++ = '\\'; *nsp++ = s [i]; } else *nsp++ = s [i]; } if (enclose_char) { *nsp++ = enclose_char; } *nsp++ = 0; } return buf; } char *print_base64 (const unsigned char *buf, unsigned len, const char *file, int line) { char *s, *b; unsigned bl; int i; unsigned val, extra; static char to64 [] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; bl = ((len * 4 + 2) / 3) + 1; b = dmalloc (bl + 1, file, line); if (!b) return (char *)0; i = 0; s = b; while (i != len) { val = buf [i++]; extra = val & 3; val = val >> 2; *s++ = to64 [val]; if (i == len) { *s++ = to64 [extra << 4]; *s++ = '='; break; } val = (extra << 8) + buf [i++]; extra = val & 15; val = val >> 4; *s++ = to64 [val]; if (i == len) { *s++ = to64 [extra << 2]; *s++ = '='; break; } val = (extra << 8) + buf [i++]; extra = val & 0x3f; val = val >> 6; *s++ = to64 [val]; *s++ = to64 [extra]; } if (!len) *s++ = '='; *s++ = 0; if (s > b + bl + 1) abort (); return b; } char *print_hw_addr (htype, hlen, data) const int htype; const int hlen; const unsigned char *data; { static char habuf [49]; char *s; int i; if (hlen <= 0) habuf [0] = 0; else { s = habuf; for (i = 0; i < hlen; i++) { sprintf (s, "%02x", data [i]); s += strlen (s); *s++ = ':'; } *--s = 0; } return habuf; } void print_lease (lease) struct lease *lease; { struct tm *t; char tbuf [32]; log_debug (" Lease %s", piaddr (lease -> ip_addr)); t = gmtime (&lease -> starts); strftime (tbuf, sizeof tbuf, "%Y/%m/%d %H:%M:%S", t); log_debug (" start %s", tbuf); t = gmtime (&lease -> ends); strftime (tbuf, sizeof tbuf, "%Y/%m/%d %H:%M:%S", t); log_debug (" end %s", tbuf); if (lease -> hardware_addr.hlen) log_debug (" hardware addr = %s", print_hw_addr (lease -> hardware_addr.hbuf [0], lease -> hardware_addr.hlen - 1, &lease -> hardware_addr.hbuf [1])); log_debug (" host %s ", lease -> host ? lease -> host -> name : ""); } #if defined (DEBUG_PACKET) void dump_packet_option (struct option_cache *oc, struct packet *packet, struct lease *lease, struct client_state *client, struct option_state *in_options, struct option_state *cfg_options, struct binding_scope **scope, struct universe *u, void *foo) { const char *name, *dot; struct data_string ds; memset (&ds, 0, sizeof ds); if (u != &dhcp_universe) { name = u -> name; dot = "."; } else { name = ""; dot = ""; } if (evaluate_option_cache (&ds, packet, lease, client, in_options, cfg_options, scope, oc, MDL)) { log_debug (" option %s%s%s %s;\n", name, dot, oc -> option -> name, pretty_print_option (oc -> option, ds.data, ds.len, 1, 1)); data_string_forget (&ds, MDL); } } void dump_packet (tp) struct packet *tp; { struct dhcp_packet *tdp = tp -> raw; log_debug ("packet length %d", tp -> packet_length); log_debug ("op = %d htype = %d hlen = %d hops = %d", tdp -> op, tdp -> htype, tdp -> hlen, tdp -> hops); log_debug ("xid = %x secs = %ld flags = %x", tdp -> xid, (unsigned long)tdp -> secs, tdp -> flags); log_debug ("ciaddr = %s", inet_ntoa (tdp -> ciaddr)); log_debug ("yiaddr = %s", inet_ntoa (tdp -> yiaddr)); log_debug ("siaddr = %s", inet_ntoa (tdp -> siaddr)); log_debug ("giaddr = %s", inet_ntoa (tdp -> giaddr)); log_debug ("chaddr = %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x", ((unsigned char *)(tdp -> chaddr)) [0], ((unsigned char *)(tdp -> chaddr)) [1], ((unsigned char *)(tdp -> chaddr)) [2], ((unsigned char *)(tdp -> chaddr)) [3], ((unsigned char *)(tdp -> chaddr)) [4], ((unsigned char *)(tdp -> chaddr)) [5]); log_debug ("filename = %s", tdp -> file); log_debug ("server_name = %s", tdp -> sname); if (tp -> options_valid) { int i; for (i = 0; i < tp -> options -> universe_count; i++) { if (tp -> options -> universes [i]) { option_space_foreach (tp, (struct lease *)0, (struct client_state *)0, (struct option_state *)0, tp -> options, &global_scope, universes [i], 0, dump_packet_option); } } } log_debug ("%s", ""); } #endif void dump_raw (buf, len) const unsigned char *buf; unsigned len; { int i; char lbuf [80]; int lbix = 0; /* 1 2 3 4 5 6 7 01234567890123456789012345678901234567890123456789012345678901234567890123 280: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................. */ memset(lbuf, ' ', 79); lbuf [79] = 0; for (i = 0; i < len; i++) { if ((i & 15) == 0) { if (lbix) { lbuf[53]=' '; lbuf[54]=' '; lbuf[55]=' '; lbuf[73]='\0'; log_info ("%s", lbuf); } memset(lbuf, ' ', 79); lbuf [79] = 0; sprintf (lbuf, "%03x:", i); lbix = 4; } else if ((i & 7) == 0) lbuf [lbix++] = ' '; if(isprint(buf[i])) { lbuf[56+(i%16)]=buf[i]; } else { lbuf[56+(i%16)]='.'; } sprintf (&lbuf [lbix], " %02x", buf [i]); lbix += 3; lbuf[lbix]=' '; } lbuf[53]=' '; lbuf[54]=' '; lbuf[55]=' '; lbuf[73]='\0'; log_info ("%s", lbuf); } void hash_dump (table) struct hash_table *table; { int i; struct hash_bucket *bp; if (!table) return; for (i = 0; i < table -> hash_count; i++) { if (!table -> buckets [i]) continue; log_info ("hash bucket %d:", i); for (bp = table -> buckets [i]; bp; bp = bp -> next) { if (bp -> len) dump_raw (bp -> name, bp -> len); else log_info ("%s", (const char *)bp -> name); } } } /* * print a string as hex. This only outputs * colon separated hex list no matter what * the input looks like. See print_hex * for a function that prints either cshl * or a string if all bytes are printible * It only uses limit characters from buf * and doesn't do anything if buf == NULL * * len - length of data * data - input data * limit - length of buf to use * buf - output buffer */ void print_hex_only (len, data, limit, buf) unsigned len; const u_int8_t *data; unsigned limit; char *buf; { char *bufptr = buf; int byte = 0; if (data == NULL || bufptr == NULL || limit == 0) { return; } if (((len == 0) || ((len * 3) > limit))) { *bufptr = 0x0; return; } for ( ; byte < len; ++byte) { if (byte > 0) { *bufptr++ = ':'; } sprintf(bufptr, "%02x", data[byte]); bufptr += 2; } return; } /* * print a string as either text if all the characters * are printable or colon separated hex if they aren't * * len - length of data * data - input data * limit - length of buf to use * buf - output buffer */ void print_hex_or_string (len, data, limit, buf) unsigned len; const u_int8_t *data; unsigned limit; char *buf; { unsigned i; if ((buf == NULL) || (limit < 3)) return; for (i = 0; (i < (limit - 3)) && (i < len); i++) { if (!isascii(data[i]) || !isprint(data[i])) { print_hex_only(len, data, limit, buf); return; } } buf[0] = '"'; i = len; if (i > (limit - 3)) i = limit - 3; memcpy(&buf[1], data, i); buf[i + 1] = '"'; buf[i + 2] = 0; return; } /* * print a string as either hex or text * using static buffers to hold the output * * len - length of data * data - input data * limit - length of buf * buf_num - the output buffer to use */ #define HBLEN 1024 char *print_hex(len, data, limit, buf_num) unsigned len; const u_int8_t *data; unsigned limit; unsigned buf_num; { static char hex_buf_1[HBLEN + 1]; static char hex_buf_2[HBLEN + 1]; static char hex_buf_3[HBLEN + 1]; char *hex_buf; switch(buf_num) { case 0: hex_buf = hex_buf_1; if (limit >= sizeof(hex_buf_1)) limit = sizeof(hex_buf_1); break; case 1: hex_buf = hex_buf_2; if (limit >= sizeof(hex_buf_2)) limit = sizeof(hex_buf_2); break; case 2: hex_buf = hex_buf_3; if (limit >= sizeof(hex_buf_3)) limit = sizeof(hex_buf_3); break; default: return(NULL); } print_hex_or_string(len, data, limit, hex_buf); return(hex_buf); } #define DQLEN 80 char *print_dotted_quads (len, data) unsigned len; const u_int8_t *data; { static char dq_buf [DQLEN + 1]; int i; char *s; s = &dq_buf [0]; i = 0; /* %Audit% Loop bounds checks to 21 bytes. %2004.06.17,Safe% * The sprintf can't exceed 18 bytes, and since the loop enforces * 21 bytes of space per iteration at no time can we exit the * loop without at least 3 bytes spare. */ do { sprintf (s, "%u.%u.%u.%u, ", data [i], data [i + 1], data [i + 2], data [i + 3]); s += strlen (s); i += 4; } while ((s - &dq_buf [0] > DQLEN - 21) && i + 3 < len); if (i == len) s [-2] = 0; else strcpy (s, "..."); return dq_buf; } char *print_dec_1 (val) unsigned long val; { static char vbuf [32]; sprintf (vbuf, "%lu", val); return vbuf; } char *print_dec_2 (val) unsigned long val; { static char vbuf [32]; sprintf (vbuf, "%lu", val); return vbuf; } static unsigned print_subexpression (struct expression *, char *, unsigned); static unsigned print_subexpression (expr, buf, len) struct expression *expr; char *buf; unsigned len; { unsigned rv, left; const char *s; switch (expr -> op) { case expr_none: if (len > 3) { strcpy (buf, "nil"); return 3; } break; case expr_match: if (len > 7) { strcpy (buf, "(match)"); return 7; } break; case expr_check: rv = 10 + strlen (expr -> data.check -> name); if (len > rv) { sprintf (buf, "(check %s)", expr -> data.check -> name); return rv; } break; case expr_equal: if (len > 6) { rv = 4; strcpy (buf, "(eq "); rv += print_subexpression (expr -> data.equal [0], buf + rv, len - rv - 2); buf [rv++] = ' '; rv += print_subexpression (expr -> data.equal [1], buf + rv, len - rv - 1); buf [rv++] = ')'; buf [rv] = 0; return rv; } break; case expr_not_equal: if (len > 7) { rv = 5; strcpy (buf, "(neq "); rv += print_subexpression (expr -> data.equal [0], buf + rv, len - rv - 2); buf [rv++] = ' '; rv += print_subexpression (expr -> data.equal [1], buf + rv, len - rv - 1); buf [rv++] = ')'; buf [rv] = 0; return rv; } break; case expr_regex_match: if (len > 10) { rv = 4; strcpy(buf, "(regex "); rv += print_subexpression(expr->data.equal[0], buf + rv, len - rv - 2); buf[rv++] = ' '; rv += print_subexpression(expr->data.equal[1], buf + rv, len - rv - 1); buf[rv++] = ')'; buf[rv] = 0; return rv; } break; case expr_substring: if (len > 11) { rv = 8; strcpy (buf, "(substr "); rv += print_subexpression (expr -> data.substring.expr, buf + rv, len - rv - 3); buf [rv++] = ' '; rv += print_subexpression (expr -> data.substring.offset, buf + rv, len - rv - 2); buf [rv++] = ' '; rv += print_subexpression (expr -> data.substring.len, buf + rv, len - rv - 1); buf [rv++] = ')'; buf [rv] = 0; return rv; } break; case expr_suffix: if (len > 10) { rv = 8; strcpy (buf, "(suffix "); rv += print_subexpression (expr -> data.suffix.expr, buf + rv, len - rv - 2); if (len > rv) buf [rv++] = ' '; rv += print_subexpression (expr -> data.suffix.len, buf + rv, len - rv - 1); if (len > rv) buf [rv++] = ')'; buf [rv] = 0; return rv; } break; case expr_lcase: if (len > 9) { rv = 7; strcpy(buf, "(lcase "); rv += print_subexpression(expr->data.lcase, buf + rv, len - rv - 1); buf[rv++] = ')'; buf[rv] = 0; return rv; } break; case expr_ucase: if (len > 9) { rv = 7; strcpy(buf, "(ucase "); rv += print_subexpression(expr->data.ucase, buf + rv, len - rv - 1); buf[rv++] = ')'; buf[rv] = 0; return rv; } break; case expr_concat: if (len > 10) { rv = 8; strcpy (buf, "(concat "); rv += print_subexpression (expr -> data.concat [0], buf + rv, len - rv - 2); buf [rv++] = ' '; rv += print_subexpression (expr -> data.concat [1], buf + rv, len - rv - 1); buf [rv++] = ')'; buf [rv] = 0; return rv; } break; case expr_pick_first_value: if (len > 8) { rv = 6; strcpy (buf, "(pick1st "); rv += print_subexpression (expr -> data.pick_first_value.car, buf + rv, len - rv - 2); buf [rv++] = ' '; rv += print_subexpression (expr -> data.pick_first_value.cdr, buf + rv, len - rv - 1); buf [rv++] = ')'; buf [rv] = 0; return rv; } break; case expr_host_lookup: rv = 15 + strlen (expr -> data.host_lookup -> hostname); if (len > rv) { sprintf (buf, "(dns-lookup %s)", expr -> data.host_lookup -> hostname); return rv; } break; case expr_and: s = "and"; binop: rv = strlen (s); if (len > rv + 4) { buf [0] = '('; strcpy (&buf [1], s); rv += 1; buf [rv++] = ' '; rv += print_subexpression (expr -> data.and [0], buf + rv, len - rv - 2); buf [rv++] = ' '; rv += print_subexpression (expr -> data.and [1], buf + rv, len - rv - 1); buf [rv++] = ')'; buf [rv] = 0; return rv; } break; case expr_or: s = "or"; goto binop; case expr_add: s = "+"; goto binop; case expr_subtract: s = "-"; goto binop; case expr_multiply: s = "*"; goto binop; case expr_divide: s = "/"; goto binop; case expr_remainder: s = "%"; goto binop; case expr_binary_and: s = "&"; goto binop; case expr_binary_or: s = "|"; goto binop; case expr_binary_xor: s = "^"; goto binop; case expr_not: if (len > 6) { rv = 5; strcpy (buf, "(not "); rv += print_subexpression (expr -> data.not, buf + rv, len - rv - 1); buf [rv++] = ')'; buf [rv] = 0; return rv; } break; case expr_config_option: s = "cfg-option"; goto dooption; case expr_option: s = "option"; dooption: rv = strlen (s) + 2 + (strlen (expr -> data.option -> name) + strlen (expr -> data.option -> universe -> name)); if (len > rv) { sprintf (buf, "(option %s.%s)", expr -> data.option -> universe -> name, expr -> data.option -> name); return rv; } break; case expr_hardware: if (len > 10) { strcpy (buf, "(hardware)"); return 10; } break; case expr_packet: if (len > 10) { rv = 8; strcpy (buf, "(substr "); rv += print_subexpression (expr -> data.packet.offset, buf + rv, len - rv - 2); buf [rv++] = ' '; rv += print_subexpression (expr -> data.packet.len, buf + rv, len - rv - 1); buf [rv++] = ')'; buf [rv] = 0; return rv; } break; case expr_const_data: s = print_hex_1 (expr -> data.const_data.len, expr -> data.const_data.data, len); rv = strlen (s); if (rv >= len) rv = len - 1; strncpy (buf, s, rv); buf [rv] = 0; return rv; case expr_encapsulate: rv = 13; strcpy (buf, "(encapsulate "); rv += expr -> data.encapsulate.len; if (rv + 2 > len) rv = len - 2; strncpy (buf, (const char *)expr -> data.encapsulate.data, rv - 13); buf [rv++] = ')'; buf [rv++] = 0; break; case expr_extract_int8: if (len > 7) { rv = 6; strcpy (buf, "(int8 "); rv += print_subexpression (expr -> data.extract_int, buf + rv, len - rv - 1); buf [rv++] = ')'; buf [rv] = 0; return rv; } break; case expr_extract_int16: if (len > 8) { rv = 7; strcpy (buf, "(int16 "); rv += print_subexpression (expr -> data.extract_int, buf + rv, len - rv - 1); buf [rv++] = ')'; buf [rv] = 0; return rv; } break; case expr_extract_int32: if (len > 8) { rv = 7; strcpy (buf, "(int32 "); rv += print_subexpression (expr -> data.extract_int, buf + rv, len - rv - 1); buf [rv++] = ')'; buf [rv] = 0; return rv; } break; case expr_encode_int8: if (len > 7) { rv = 6; strcpy (buf, "(to-int8 "); rv += print_subexpression (expr -> data.encode_int, buf + rv, len - rv - 1); buf [rv++] = ')'; buf [rv] = 0; return rv; } break; case expr_encode_int16: if (len > 8) { rv = 7; strcpy (buf, "(to-int16 "); rv += print_subexpression (expr -> data.encode_int, buf + rv, len - rv - 1); buf [rv++] = ')'; buf [rv] = 0; return rv; } break; case expr_encode_int32: if (len > 8) { rv = 7; strcpy (buf, "(to-int32 "); rv += print_subexpression (expr -> data.encode_int, buf + rv, len - rv - 1); buf [rv++] = ')'; buf [rv] = 0; return rv; } break; case expr_const_int: s = print_dec_1 (expr -> data.const_int); rv = strlen (s); if (len > rv) { strcpy (buf, s); return rv; } break; case expr_exists: rv = 10 + (strlen (expr -> data.option -> name) + strlen (expr -> data.option -> universe -> name)); if (len > rv) { sprintf (buf, "(exists %s.%s)", expr -> data.option -> universe -> name, expr -> data.option -> name); return rv; } break; case expr_variable_exists: rv = 10 + strlen (expr -> data.variable); if (len > rv) { sprintf (buf, "(defined %s)", expr -> data.variable); return rv; } break; case expr_variable_reference: rv = strlen (expr -> data.variable); if (len > rv) { sprintf (buf, "%s", expr -> data.variable); return rv; } break; case expr_known: s = "known"; astring: rv = strlen (s); if (len > rv) { strcpy (buf, s); return rv; } break; case expr_leased_address: s = "leased-address"; goto astring; case expr_client_state: s = "client-state"; goto astring; case expr_host_decl_name: s = "host-decl-name"; goto astring; case expr_lease_time: s = "lease-time"; goto astring; case expr_static: s = "static"; goto astring; case expr_filename: s = "filename"; goto astring; case expr_sname: s = "server-name"; goto astring; case expr_reverse: if (len > 11) { rv = 13; strcpy (buf, "(reverse "); rv += print_subexpression (expr -> data.reverse.width, buf + rv, len - rv - 2); buf [rv++] = ' '; rv += print_subexpression (expr -> data.reverse.buffer, buf + rv, len - rv - 1); buf [rv++] = ')'; buf [rv] = 0; return rv; } break; case expr_binary_to_ascii: if (len > 5) { rv = 9; strcpy (buf, "(b2a "); rv += print_subexpression (expr -> data.b2a.base, buf + rv, len - rv - 4); buf [rv++] = ' '; rv += print_subexpression (expr -> data.b2a.width, buf + rv, len - rv - 3); buf [rv++] = ' '; rv += print_subexpression (expr -> data.b2a.separator, buf + rv, len - rv - 2); buf [rv++] = ' '; rv += print_subexpression (expr -> data.b2a.buffer, buf + rv, len - rv - 1); buf [rv++] = ')'; buf [rv] = 0; return rv; } break; case expr_dns_transaction: rv = 10; if (len < rv + 2) { buf [0] = '('; strcpy (&buf [1], "ns-update "); while (len < rv + 2) { rv += print_subexpression (expr -> data.dns_transaction.car, buf + rv, len - rv - 2); buf [rv++] = ' '; expr = expr -> data.dns_transaction.cdr; } buf [rv - 1] = ')'; buf [rv] = 0; return rv; } return 0; case expr_ns_delete: s = "delete"; left = 4; goto dodnsupd; case expr_ns_exists: s = "exists"; left = 4; goto dodnsupd; case expr_ns_not_exists: s = "not_exists"; left = 4; goto dodnsupd; case expr_ns_add: s = "update"; left = 5; dodnsupd: rv = strlen (s); if (len > strlen (s) + 1) { buf [0] = '('; strcpy (buf + 1, s); rv++; buf [rv++] = ' '; s = print_dec_1 (expr -> data.ns_add.rrclass); if (len > rv + strlen (s) + left) { strcpy (&buf [rv], s); rv += strlen (&buf [rv]); } buf [rv++] = ' '; left--; s = print_dec_1 (expr -> data.ns_add.rrtype); if (len > rv + strlen (s) + left) { strcpy (&buf [rv], s); rv += strlen (&buf [rv]); } buf [rv++] = ' '; left--; rv += print_subexpression (expr -> data.ns_add.rrname, buf + rv, len - rv - left); buf [rv++] = ' '; left--; rv += print_subexpression (expr -> data.ns_add.rrdata, buf + rv, len - rv - left); buf [rv++] = ' '; left--; rv += print_subexpression (expr -> data.ns_add.ttl, buf + rv, len - rv - left); buf [rv++] = ')'; buf [rv] = 0; return rv; } break; case expr_null: if (len > 6) { strcpy (buf, "(null)"); return 6; } break; case expr_funcall: rv = 12 + strlen (expr -> data.funcall.name); if (len > rv + 1) { strcpy (buf, "(funcall "); strcpy (buf + 9, expr -> data.funcall.name); buf [rv++] = ' '; rv += print_subexpression (expr -> data.funcall.arglist, buf + rv, len - rv - 1); buf [rv++] = ')'; buf [rv] = 0; return rv; } break; case expr_arg: rv = print_subexpression (expr -> data.arg.val, buf, len); if (expr -> data.arg.next && rv + 2 < len) { buf [rv++] = ' '; rv += print_subexpression (expr -> data.arg.next, buf, len); if (rv + 1 < len) buf [rv++] = 0; return rv; } break; case expr_function: rv = 9; if (len > rv + 1) { struct string_list *foo; strcpy (buf, "(function"); for (foo = expr -> data.func -> args; foo; foo = foo -> next) { if (len > rv + 2 + strlen (foo -> string)) { buf [rv - 1] = ' '; strcpy (&buf [rv], foo -> string); rv += strlen (foo -> string); } } buf [rv++] = ')'; buf [rv] = 0; return rv; } break; case expr_gethostname: if (len > 13) { strcpy(buf, "(gethostname)"); return 13; } break; default: log_fatal("Impossible case at %s:%d (undefined expression " "%d).", MDL, expr->op); break; } return 0; } void print_expression (name, expr) const char *name; struct expression *expr; { char buf [1024]; print_subexpression (expr, buf, sizeof buf); log_info ("%s: %s", name, buf); } int token_print_indent_concat (FILE *file, int col, int indent, const char *prefix, const char *suffix, ...) { va_list list; unsigned len; char *s, *t, *u; va_start (list, suffix); s = va_arg (list, char *); len = 0; while (s) { len += strlen (s); s = va_arg (list, char *); } va_end (list); t = dmalloc (len + 1, MDL); if (!t) log_fatal ("token_print_indent: no memory for copy buffer"); va_start (list, suffix); s = va_arg (list, char *); u = t; while (s) { len = strlen (s); strcpy (u, s); u += len; s = va_arg (list, char *); } va_end (list); col = token_print_indent (file, col, indent, prefix, suffix, t); dfree (t, MDL); return col; } int token_indent_data_string (FILE *file, int col, int indent, const char *prefix, const char *suffix, struct data_string *data) { int i; char *buf; char obuf [3]; /* See if this is just ASCII. */ for (i = 0; i < data -> len; i++) if (!isascii (data -> data [i]) || !isprint (data -> data [i])) break; /* If we have a purely ASCII string, output it as text. */ if (i == data -> len) { buf = dmalloc (data -> len + 3, MDL); if (buf) { buf [0] = '"'; memcpy (buf + 1, data -> data, data -> len); buf [data -> len + 1] = '"'; buf [data -> len + 2] = 0; i = token_print_indent (file, col, indent, prefix, suffix, buf); dfree (buf, MDL); return i; } } for (i = 0; i < data -> len; i++) { sprintf (obuf, "%2.2x", data -> data [i]); col = token_print_indent (file, col, indent, i == 0 ? prefix : "", (i + 1 == data -> len ? suffix : ""), obuf); if (i + 1 != data -> len) col = token_print_indent (file, col, indent, prefix, suffix, ":"); } return col; } int token_print_indent (FILE *file, int col, int indent, const char *prefix, const char *suffix, const char *buf) { int len = 0; if (prefix != NULL) len += strlen (prefix); if (buf != NULL) len += strlen (buf); if (col + len > 79) { if (indent + len < 79) { indent_spaces (file, indent); col = indent; } else { indent_spaces (file, col); col = len > 79 ? 0 : 79 - len - 1; } } else if (prefix && *prefix) { fputs (prefix, file); col += strlen (prefix); } if ((buf != NULL) && (*buf != 0)) { fputs (buf, file); col += strlen(buf); } if (suffix && *suffix) { if (col + strlen (suffix) > 79) { indent_spaces (file, indent); col = indent; } else { fputs (suffix, file); col += strlen (suffix); } } return col; } void indent_spaces (FILE *file, int indent) { int i; fputc ('\n', file); for (i = 0; i < indent; i++) fputc (' ', file); } /* Format the given time as "A; # B", where A is the format * used by the parser, and B is the local time, for humans. */ const char * print_time(TIME t) { static char buf[sizeof("epoch 9223372036854775807; " "# Wed Jun 30 21:49:08 2147483647")]; static char buf1[sizeof("# Wed Jun 30 21:49:08 2147483647")]; time_t since_epoch; /* The string: "6 2147483647/12/31 23:59:60;" * is smaller than the other, used to declare the buffer size, so * we can use one buffer for both. */ if (t == MAX_TIME) return "never;"; if (t < 0) return NULL; /* For those lucky enough to have a 128-bit time_t, ensure that * whatever (corrupt) value we're given doesn't exceed the static * buffer. */ #if (MAX_TIME > 0x7fffffffffffffff) if (t > 0x7fffffffffffffff) return NULL; #endif if (db_time_format == LOCAL_TIME_FORMAT) { since_epoch = mktime(localtime(&t)); if ((strftime(buf1, sizeof(buf1), "# %a %b %d %H:%M:%S %Y", localtime(&t)) == 0) || (snprintf(buf, sizeof(buf), "epoch %lu; %s", (unsigned long)since_epoch, buf1) >= sizeof(buf))) return NULL; } else { /* No bounds check for the year is necessary - in this case, * strftime() will run out of space and assert an error. */ if (strftime(buf, sizeof(buf), "%w %Y/%m/%d %H:%M:%S;", gmtime(&t)) == 0) return NULL; } return buf; } /* !brief Return the given data as a string of hex digits "xx:xx:xx ..." * * Converts the given data into a null-terminated, string of hex digits, * stored in an allocated buffer. It is the caller's responsiblity to free * the buffer. * * \param s - pointer to the data to convert * \param len - length of the data to convert * \param file - source file of invocation * \param line - line number of invocation * * \return Returns an allocated buffer containing the hex string */ char *buf_to_hex (const unsigned char *s, unsigned len, const char *file, int line) { unsigned nulen = 0; char *buf; /* If somebody hands us length of zero, we'll give them * back an empty string */ if (!len) { buf = dmalloc (1, MDL); if (buf) { *buf = 0x0; } return (buf); } /* Figure out how big it needs to be. print_to_hex uses * "%02x:" per character. Note since there's no trailing colon * we'll have room for the null */ nulen = (len * 3); /* Allocate our buffer */ buf = dmalloc (nulen, MDL); /* Hex-ify it */ if (buf) { print_hex_only (len, s, nulen, buf); } return buf; } /* !brief Formats data into a string based on a lease id format * * Takes the given data and returns an allocated string whose contents are * the string version of that data, formatted according to the output lease * id format. Note it is the caller's responsiblity to delete the string. * * Currently two formats are supported: * * OCTAL - Default or "legacy" CSL format enclosed in quotes '"'. * * HEX - Bytes represented as string colon seperated of hex digit pairs * (xx:xx:xx...) * * \param s - data to convert * \param len - length of the data to convert * \param format - desired format of the result * \param file - source file of invocation * \param line - line number of invocation * * \return A pointer to the allocated, null-terminated string */ char *format_lease_id(const unsigned char *s, unsigned len, int format, const char *file, int line) { char *idstr = NULL; switch (format) { case TOKEN_HEX: idstr = buf_to_hex(s, len, MDL); break; case TOKEN_OCTAL: default: idstr = quotify_buf(s, len, '"', MDL); break; } return (idstr); } /* * Convert a relative path name to an absolute path name * * Not all versions of realpath() support NULL for * the second parameter and PATH_MAX isn't defined * on all systems. For the latter, we'll make what * ought to be a big enough buffer and let it fly. * If passed an absolute path it should return it * an allocated buffer. */ char *absolute_path(const char *orgpath) { char *abspath = NULL; if (orgpath) { #ifdef PATH_MAX char buf[PATH_MAX]; #else char buf[2048]; #endif errno = 0; if (realpath(orgpath, buf) == NULL) { const char* errmsg = strerror(errno); log_fatal("Failed to get realpath for %s: %s", orgpath, errmsg); } /* dup the result into an allocated buffer */ abspath = dmalloc(strlen(buf) + 1, MDL); if (abspath == NULL) { log_fatal("No memory for filename:%s\n", buf); } memcpy (abspath, buf, strlen(buf)); abspath[strlen(buf)] = 0x0; } return (abspath); } dhcp-4.4.1/common/raw.c000644 000765 000024 00000010026 13243301226 015137 0ustar00tmarkstaff000000 000000 /* raw.c BSD raw socket interface code... */ /* XXX It's not clear how this should work, and that lack of clarity is terribly detrimental to the NetBSD 1.1 kernel - it crashes and burns. Using raw sockets ought to be a big win over using BPF or something like it, because you don't need to deal with the complexities of the physical layer, but it appears not to be possible with existing raw socket implementations. This may be worth revisiting in the future. For now, this code can probably be considered a curiosity. Sigh. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1995-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #if defined (USE_RAW_SEND) #include /* Generic interface registration routine... */ void if_register_send (info) struct interface_info *info; { struct sockaddr_in name; int sock; struct socklist *tmp; int flag; /* Set up the address we're going to connect to. */ name.sin_family = AF_INET; name.sin_port = relay_port ? relay_port : local_port; name.sin_addr.s_addr = htonl (INADDR_BROADCAST); memset (name.sin_zero, 0, sizeof (name.sin_zero)); /* List addresses on which we're listening. */ if (!quiet_interface_discovery) log_info ("Sending on %s, port %d", piaddr (info -> address), htons (name.sin_port)); if ((sock = socket (AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) log_fatal ("Can't create dhcp socket: %m"); /* Set the BROADCAST option so that we can broadcast DHCP responses. */ flag = 1; if (setsockopt (sock, SOL_SOCKET, SO_BROADCAST, &flag, sizeof flag) < 0) log_fatal ("Can't set SO_BROADCAST option on dhcp socket: %m"); /* Set the IP_HDRINCL flag so that we can supply our own IP headers... */ if (setsockopt (sock, IPPROTO_IP, IP_HDRINCL, &flag, sizeof flag) < 0) log_fatal ("Can't set IP_HDRINCL flag: %m"); info -> wfdesc = sock; if (!quiet_interface_discovery) log_info ("Sending on Raw/%s%s%s", info -> name, (info -> shared_network ? "/" : ""), (info -> shared_network ? info -> shared_network -> name : "")); } void if_deregister_send (info) struct interface_info *info; { close (info -> wfdesc); info -> wfdesc = -1; if (!quiet_interface_discovery) log_info ("Disabling output on Raw/%s%s%s", info -> name, (info -> shared_network ? "/" : ""), (info -> shared_network ? info -> shared_network -> name : "")); } size_t send_packet (interface, packet, raw, len, from, to, hto) struct interface_info *interface; struct packet *packet; struct dhcp_packet *raw; size_t len; struct in_addr from; struct sockaddr_in *to; struct hardware *hto; { unsigned char buf [256]; int bufp = 0; struct iovec iov [2]; int result; /* Assemble the headers... */ assemble_udp_ip_header (interface, buf, &bufp, from.s_addr, to -> sin_addr.s_addr, to -> sin_port, (unsigned char *)raw, len); /* Fire it off */ iov [0].iov_base = (char *)buf; iov [0].iov_len = bufp; iov [1].iov_base = (char *)raw; iov [1].iov_len = len; result = writev(interface -> wfdesc, iov, 2); if (result < 0) log_error ("send_packet: %m"); return result; } #endif /* USE_SOCKET_SEND */ dhcp-4.4.1/common/resolv.c000644 000765 000024 00000011452 13243301226 015664 0ustar00tmarkstaff000000 000000 /* resolv.c Parser for /etc/resolv.conf file. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1996-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" struct name_server *name_servers; struct domain_search_list *domains; char path_resolv_conf [] = _PATH_RESOLV_CONF; void read_resolv_conf (parse_time) TIME parse_time; { int file; struct parse *cfile; const char *val; int token; struct name_server *sp, *sl, *ns; struct domain_search_list *dp, *dl, *nd; isc_result_t status; if ((file = open (path_resolv_conf, O_RDONLY)) < 0) { log_error ("Can't open %s: %m", path_resolv_conf); return; } cfile = NULL; status = new_parse(&cfile, file, NULL, 0, path_resolv_conf, 1); if (status != ISC_R_SUCCESS || cfile == NULL) return; do { token = next_token (&val, (unsigned *)0, cfile); if (token == END_OF_FILE) break; else if (token == EOL) continue; else if (token == DOMAIN || token == SEARCH) { do { struct domain_search_list *nd, **dp; char *dn; dn = parse_host_name (cfile); if (!dn) break; dp = &domains; for (nd = domains; nd; nd = nd -> next) { dp = &nd -> next; if (!strcmp (nd -> domain, dn)) break; } if (!nd) { nd = new_domain_search_list (MDL); if (!nd) log_fatal ("No memory for %s", dn); nd -> next = (struct domain_search_list *)0; *dp = nd; nd -> domain = dn; } nd -> rcdate = parse_time; token = peek_token (&val, (unsigned *)0, cfile); } while (token != EOL); if (token != EOL) { parse_warn (cfile, "junk after domain declaration"); skip_to_semi (cfile); } skip_token(&val, (unsigned *)0, cfile); } else if (token == NAMESERVER) { struct name_server *ns, **sp; struct iaddr iaddr; parse_ip_addr (cfile, &iaddr); sp = &name_servers; for (ns = name_servers; ns; ns = ns -> next) { sp = &ns -> next; if (!memcmp (&ns -> addr.sin_addr, iaddr.iabuf, iaddr.len)) break; } if (!ns) { ns = new_name_server (MDL); if (!ns) log_fatal ("No memory for nameserver %s", piaddr (iaddr)); ns -> next = (struct name_server *)0; *sp = ns; memcpy (&ns -> addr.sin_addr, iaddr.iabuf, iaddr.len); #ifdef HAVE_SA_LEN ns -> addr.sin_len = sizeof ns -> addr; #endif ns -> addr.sin_family = AF_INET; ns -> addr.sin_port = htons (53); memset (ns -> addr.sin_zero, 0, sizeof ns -> addr.sin_zero); } ns -> rcdate = parse_time; skip_to_semi (cfile); } else skip_to_semi (cfile); /* Ignore what we don't grok. */ } while (1); skip_token(&val, (unsigned *)0, cfile); /* Lose servers that are no longer in /etc/resolv.conf. */ sl = (struct name_server *)0; for (sp = name_servers; sp; sp = ns) { ns = sp -> next; if (sp -> rcdate != parse_time) { if (sl) sl -> next = sp -> next; else name_servers = sp -> next; /* We can't actually free the name server structure, because somebody might be hanging on to it. If your /etc/resolv.conf file changes a lot, this could be a noticeable memory leak. */ } else sl = sp; } /* Lose domains that are no longer in /etc/resolv.conf. */ dl = (struct domain_search_list *)0; for (dp = domains; dp; dp = nd) { nd = dp -> next; if (dp -> rcdate != parse_time) { if (dl) dl -> next = dp -> next; else domains = dp -> next; free_domain_search_list (dp, MDL); } else dl = dp; } end_parse (&cfile); } /* Pick a name server from the /etc/resolv.conf file. */ struct name_server *first_name_server () { static TIME rcdate; struct stat st; /* Check /etc/resolv.conf and reload it if it's changed. */ if (cur_time > rcdate) { if (stat (path_resolv_conf, &st) < 0) { log_error ("Can't stat %s", path_resolv_conf); return (struct name_server *)0; } if (st.st_mtime > rcdate) { rcdate = cur_time + 1; read_resolv_conf (rcdate); } } return name_servers; } dhcp-4.4.1/common/socket.c000644 000765 000024 00000105425 13243301226 015646 0ustar00tmarkstaff000000 000000 /* socket.c BSD socket interface code... */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1995-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ /* SO_BINDTODEVICE support added by Elliot Poger (poger@leland.stanford.edu). * This sockopt allows a socket to be bound to a particular interface, * thus enabling the use of DHCPD on a multihomed host. * If SO_BINDTODEVICE is defined in your system header files, the use of * this sockopt will be automatically enabled. * I have implemented it under Linux; other systems should be doable also. */ #include "dhcpd.h" #include #include #include #include #include #if defined(sun) && defined(USE_V4_PKTINFO) #include #include #include #include #include #endif #ifdef USE_SOCKET_FALLBACK # if !defined (USE_SOCKET_SEND) # define if_register_send if_register_fallback # define send_packet send_fallback # define if_reinitialize_send if_reinitialize_fallback # endif #endif #if defined(DHCPv6) /* * XXX: this is gross. we need to go back and overhaul the API for socket * handling. */ static int no_global_v6_socket = 0; static unsigned int global_v6_socket_references = 0; static int global_v6_socket = -1; #if defined(RELAY_PORT) static unsigned int relay_port_v6_socket_references = 0; static int relay_port_v6_socket = -1; #endif static void if_register_multicast(struct interface_info *info); #endif /* * We can use a single socket for AF_INET (similar to AF_INET6) on all * interfaces configured for DHCP if the system has support for IP_PKTINFO * and IP_RECVPKTINFO (for example Solaris 11). */ #if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO) static unsigned int global_v4_socket_references = 0; static int global_v4_socket = -1; #endif /* * If we can't bind() to a specific interface, then we can only have * a single socket. This variable insures that we don't try to listen * on two sockets. */ #if !defined(SO_BINDTODEVICE) && !defined(USE_FALLBACK) static int once = 0; #endif /* !defined(SO_BINDTODEVICE) && !defined(USE_FALLBACK) */ /* Reinitializes the specified interface after an address change. This is not required for packet-filter APIs. */ #if defined (USE_SOCKET_SEND) || defined (USE_SOCKET_FALLBACK) void if_reinitialize_send (info) struct interface_info *info; { #if 0 #ifndef USE_SOCKET_RECEIVE once = 0; close (info -> wfdesc); #endif if_register_send (info); #endif } #endif #ifdef USE_SOCKET_RECEIVE void if_reinitialize_receive (info) struct interface_info *info; { #if 0 once = 0; close (info -> rfdesc); if_register_receive (info); #endif } #endif #if defined (USE_SOCKET_SEND) || \ defined (USE_SOCKET_RECEIVE) || \ defined (USE_SOCKET_FALLBACK) /* Generic interface registration routine... */ int if_register_socket(struct interface_info *info, int family, int *do_multicast, struct in6_addr *linklocal6) { struct sockaddr_storage name; int name_len; int sock; int flag; int domain; #ifdef DHCPv6 struct sockaddr_in6 *addr6; #endif struct sockaddr_in *addr; /* INSIST((family == AF_INET) || (family == AF_INET6)); */ #if !defined(SO_BINDTODEVICE) && !defined(USE_FALLBACK) /* Make sure only one interface is registered. */ if (once) { log_fatal ("The standard socket API can only support %s", "hosts with a single network interface."); } once = 1; #endif /* * Set up the address we're going to bind to, depending on the * address family. */ memset(&name, 0, sizeof(name)); switch (family) { #ifdef DHCPv6 case AF_INET6: addr6 = (struct sockaddr_in6 *)&name; addr6->sin6_family = AF_INET6; addr6->sin6_port = local_port; #if defined(RELAY_PORT) if (relay_port && ((info->flags & INTERFACE_STREAMS) == INTERFACE_UPSTREAM)) addr6->sin6_port = relay_port; #endif /* A server feature */ if (bind_local_address6) { memcpy(&addr6->sin6_addr, &local_address6, sizeof(addr6->sin6_addr)); } /* A client feature */ if (linklocal6) { memcpy(&addr6->sin6_addr, linklocal6, sizeof(addr6->sin6_addr)); } if (IN6_IS_ADDR_LINKLOCAL(&addr6->sin6_addr)) { addr6->sin6_scope_id = if_nametoindex(info->name); } #ifdef HAVE_SA_LEN addr6->sin6_len = sizeof(*addr6); #endif name_len = sizeof(*addr6); domain = PF_INET6; if ((info->flags & INTERFACE_STREAMS) == INTERFACE_UPSTREAM) { *do_multicast = 0; } break; #endif /* DHCPv6 */ case AF_INET: default: addr = (struct sockaddr_in *)&name; addr->sin_family = AF_INET; addr->sin_port = relay_port ? relay_port : local_port; memcpy(&addr->sin_addr, &local_address, sizeof(addr->sin_addr)); #ifdef HAVE_SA_LEN addr->sin_len = sizeof(*addr); #endif name_len = sizeof(*addr); domain = PF_INET; break; } /* Make a socket... */ sock = socket(domain, SOCK_DGRAM, IPPROTO_UDP); if (sock < 0) { log_fatal("Can't create dhcp socket: %m"); } /* Set the REUSEADDR option so that we don't fail to start if we're being restarted. */ flag = 1; if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag)) < 0) { log_fatal("Can't set SO_REUSEADDR option on dhcp socket: %m"); } /* Set the BROADCAST option so that we can broadcast DHCP responses. We shouldn't do this for fallback devices, and we can detect that a device is a fallback because it has no ifp structure. */ if (info->ifp && (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&flag, sizeof(flag)) < 0)) { log_fatal("Can't set SO_BROADCAST option on dhcp socket: %m"); } #if defined(DHCPv6) && defined(SO_REUSEPORT) /* * We only set SO_REUSEPORT on AF_INET6 sockets, so that multiple * daemons can bind to their own sockets and get data for their * respective interfaces. This does not (and should not) affect * DHCPv4 sockets; we can't yet support BSD sockets well, much * less multiple sockets. Make sense only with multicast. * RedHat defines SO_REUSEPORT with a kernel which does not support * it and returns ENOPROTOOPT so in this case ignore the error. */ if ((local_family == AF_INET6) && *do_multicast) { flag = 1; if ((setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (char *)&flag, sizeof(flag)) < 0) && (errno != ENOPROTOOPT)) { log_fatal("Can't set SO_REUSEPORT option on dhcp " "socket: %m"); } } #endif /* Bind the socket to this interface's IP address. */ if (bind(sock, (struct sockaddr *)&name, name_len) < 0) { log_error("Can't bind to dhcp address: %m"); log_error("Please make sure there is no other dhcp server"); log_error("running and that there's no entry for dhcp or"); log_error("bootp in /etc/inetd.conf. Also make sure you"); log_error("are not running HP JetAdmin software, which"); log_fatal("includes a bootp server."); } #if defined(SO_BINDTODEVICE) /* Bind this socket to this interface. */ if ((local_family != AF_INET6) && (info->ifp != NULL) && setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, (char *)(info -> ifp), sizeof(*(info -> ifp))) < 0) { log_fatal("setsockopt: SO_BINDTODEVICE: %m"); } #endif /* IP_BROADCAST_IF instructs the kernel which interface to send * IP packets whose destination address is 255.255.255.255. These * will be treated as subnet broadcasts on the interface identified * by ip address (info -> primary_address). This is only known to * be defined in SCO system headers, and may not be defined in all * releases. */ #if defined(SCO) && defined(IP_BROADCAST_IF) if (info->address_count && setsockopt(sock, IPPROTO_IP, IP_BROADCAST_IF, &info->addresses[0], sizeof(info->addresses[0])) < 0) log_fatal("Can't set IP_BROADCAST_IF on dhcp socket: %m"); #endif #if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO) /* * If we turn on IP_RECVPKTINFO we will be able to receive * the interface index information of the received packet. */ if (family == AF_INET) { int on = 1; if (setsockopt(sock, IPPROTO_IP, IP_RECVPKTINFO, &on, sizeof(on)) != 0) { log_fatal("setsockopt: IPV_RECVPKTINFO: %m"); } } #endif #ifdef DHCPv6 /* * If we turn on IPV6_PKTINFO, we will be able to receive * additional information, such as the destination IP address. * We need this to spot unicast packets. */ if (family == AF_INET6) { int on = 1; #ifdef IPV6_RECVPKTINFO /* RFC3542 */ if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)) != 0) { log_fatal("setsockopt: IPV6_RECVPKTINFO: %m"); } #else /* RFC2292 */ if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, &on, sizeof(on)) != 0) { log_fatal("setsockopt: IPV6_PKTINFO: %m"); } #endif } #endif /* DHCPv6 */ return sock; } #ifdef DHCPv6 void set_multicast_hop_limit(struct interface_info* info, int hop_limit) { if (setsockopt(info->wfdesc, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hop_limit, sizeof(int)) < 0) { log_fatal("setMulticaseHopLimit: IPV6_MULTICAST_HOPS: %m"); } log_debug("Setting hop count limit to %d for interface %s", hop_limit, info->name); } #endif /* DHCPv6 */ #endif /* USE_SOCKET_SEND || USE_SOCKET_RECEIVE || USE_SOCKET_FALLBACK */ #if defined (USE_SOCKET_SEND) || defined (USE_SOCKET_FALLBACK) void if_register_send (info) struct interface_info *info; { #ifndef USE_SOCKET_RECEIVE info->wfdesc = if_register_socket(info, AF_INET, 0, NULL); /* If this is a normal IPv4 address, get the hardware address. */ if (strcmp(info->name, "fallback") != 0) get_hw_addr(info->name, &info->hw_address); #if defined (USE_SOCKET_FALLBACK) /* Fallback only registers for send, but may need to receive as well. */ info->rfdesc = info->wfdesc; #endif #else info->wfdesc = info->rfdesc; #endif if (!quiet_interface_discovery) log_info ("Sending on Socket/%s%s%s", info->name, (info->shared_network ? "/" : ""), (info->shared_network ? info->shared_network->name : "")); } #if defined (USE_SOCKET_SEND) void if_deregister_send (info) struct interface_info *info; { #ifndef USE_SOCKET_RECEIVE close (info -> wfdesc); #endif info -> wfdesc = -1; if (!quiet_interface_discovery) log_info ("Disabling output on Socket/%s%s%s", info -> name, (info -> shared_network ? "/" : ""), (info -> shared_network ? info -> shared_network -> name : "")); } #endif /* USE_SOCKET_SEND */ #endif /* USE_SOCKET_SEND || USE_SOCKET_FALLBACK */ #ifdef USE_SOCKET_RECEIVE void if_register_receive (info) struct interface_info *info; { #if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO) if (global_v4_socket_references == 0) { global_v4_socket = if_register_socket(info, AF_INET, 0, NULL); if (global_v4_socket < 0) { /* * if_register_socket() fatally logs if it fails to * create a socket, this is just a sanity check. */ log_fatal("Failed to create AF_INET socket %s:%d", MDL); } } info->rfdesc = global_v4_socket; global_v4_socket_references++; #else /* If we're using the socket API for sending and receiving, we don't need to register this interface twice. */ info->rfdesc = if_register_socket(info, AF_INET, 0, NULL); #endif /* IP_PKTINFO... */ /* If this is a normal IPv4 address, get the hardware address. */ if (strcmp(info->name, "fallback") != 0) get_hw_addr(info->name, &info->hw_address); if (!quiet_interface_discovery) log_info ("Listening on Socket/%s%s%s", info->name, (info->shared_network ? "/" : ""), (info->shared_network ? info->shared_network->name : "")); } void if_deregister_receive (info) struct interface_info *info; { #if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO) /* Dereference the global v4 socket. */ if ((info->rfdesc == global_v4_socket) && (global_v4_socket_references > 0)) { global_v4_socket_references--; info->rfdesc = -1; } else { log_fatal("Impossible condition at %s:%d", MDL); } if (global_v4_socket_references == 0) { close(global_v4_socket); global_v4_socket = -1; } #else close(info->rfdesc); info->rfdesc = -1; #endif /* IP_PKTINFO... */ if (!quiet_interface_discovery) log_info ("Disabling input on Socket/%s%s%s", info -> name, (info -> shared_network ? "/" : ""), (info -> shared_network ? info -> shared_network -> name : "")); } #endif /* USE_SOCKET_RECEIVE */ #ifdef DHCPv6 /* * This function joins the interface to DHCPv6 multicast groups so we will * receive multicast messages. */ static void if_register_multicast(struct interface_info *info) { int sock = info->rfdesc; struct ipv6_mreq mreq; if (inet_pton(AF_INET6, All_DHCP_Relay_Agents_and_Servers, &mreq.ipv6mr_multiaddr) <= 0) { log_fatal("inet_pton: unable to convert '%s'", All_DHCP_Relay_Agents_and_Servers); } mreq.ipv6mr_interface = if_nametoindex(info->name); if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) < 0) { log_fatal("setsockopt: IPV6_JOIN_GROUP: %m"); } /* * The relay agent code sets the streams so you know which way * is up and down. But a relay agent shouldn't join to the * Server address, or else you get fun loops. So up or down * doesn't matter, we're just using that config to sense this is * a relay agent. */ if ((info->flags & INTERFACE_STREAMS) == 0) { if (inet_pton(AF_INET6, All_DHCP_Servers, &mreq.ipv6mr_multiaddr) <= 0) { log_fatal("inet_pton: unable to convert '%s'", All_DHCP_Servers); } mreq.ipv6mr_interface = if_nametoindex(info->name); if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) < 0) { log_fatal("setsockopt: IPV6_JOIN_GROUP: %m"); } } } void if_register6(struct interface_info *info, int do_multicast) { /* Bounce do_multicast to a stack variable because we may change it. */ int req_multi = do_multicast; if (no_global_v6_socket) { log_fatal("Impossible condition at %s:%d", MDL); } #if defined(RELAY_PORT) if (!relay_port || ((info->flags & INTERFACE_STREAMS) == INTERFACE_DOWNSTREAM)) { #endif if (global_v6_socket_references == 0) { global_v6_socket = if_register_socket(info, AF_INET6, &req_multi, NULL); if (global_v6_socket < 0) { /* * if_register_socket() fatally logs if it fails to * create a socket, this is just a sanity check. */ log_fatal("Impossible condition at %s:%d", MDL); } else if (bind_local_address6) { char addr6_str[INET6_ADDRSTRLEN]; if (inet_ntop(AF_INET6, &local_address6, addr6_str, sizeof(addr6_str)) == NULL) { log_fatal("inet_ntop: unable to convert " "local-address6"); } log_info("Bound to [%s]:%d", addr6_str, (int) ntohs(local_port)); } else { log_info("Bound to *:%d", (int) ntohs(local_port)); } } info->rfdesc = global_v6_socket; info->wfdesc = global_v6_socket; global_v6_socket_references++; #if defined(RELAY_PORT) } else { /* * If relay port is defined, we need to register one * IPv6 UPD socket to handle upstream server or relay agent * with a non-547 UDP local port. */ if ((relay_port_v6_socket_references == 0) && ((info->flags & INTERFACE_STREAMS) == INTERFACE_UPSTREAM)) { relay_port_v6_socket = if_register_socket(info, AF_INET6, &req_multi, NULL); if (relay_port_v6_socket < 0) { log_fatal("Impossible condition at %s:%d", MDL); } else { log_info("Bound to relay port *:%d", (int) ntohs(relay_port)); } } info->rfdesc = relay_port_v6_socket; info->wfdesc = relay_port_v6_socket; relay_port_v6_socket_references++; } #endif if (req_multi) if_register_multicast(info); get_hw_addr(info->name, &info->hw_address); if (!quiet_interface_discovery) { if (info->shared_network != NULL) { log_info("Listening on Socket/%d/%s/%s", global_v6_socket, info->name, info->shared_network->name); log_info("Sending on Socket/%d/%s/%s", global_v6_socket, info->name, info->shared_network->name); } else { log_info("Listening on Socket/%s", info->name); log_info("Sending on Socket/%s", info->name); } } } /* * Register an IPv6 socket bound to the link-local address of * the argument interface (used by clients on a multiple interface box, * vs. a server or a relay using the global IPv6 socket and running * *only* in a single instance). */ void if_register_linklocal6(struct interface_info *info) { int sock; int count; struct in6_addr *addr6 = NULL; int req_multi = 0; if (global_v6_socket >= 0) { log_fatal("Impossible condition at %s:%d", MDL); } no_global_v6_socket = 1; /* get the (?) link-local address */ for (count = 0; count < info->v6address_count; count++) { addr6 = &info->v6addresses[count]; if (IN6_IS_ADDR_LINKLOCAL(addr6)) break; } if (!addr6) { log_fatal("no link-local IPv6 address for %s", info->name); } sock = if_register_socket(info, AF_INET6, &req_multi, addr6); if (sock < 0) { log_fatal("if_register_socket for %s fails", info->name); } info->rfdesc = sock; info->wfdesc = sock; get_hw_addr(info->name, &info->hw_address); if (!quiet_interface_discovery) { if (info->shared_network != NULL) { log_info("Listening on Socket/%d/%s/%s", global_v6_socket, info->name, info->shared_network->name); log_info("Sending on Socket/%d/%s/%s", global_v6_socket, info->name, info->shared_network->name); } else { log_info("Listening on Socket/%s", info->name); log_info("Sending on Socket/%s", info->name); } } } void if_deregister6(struct interface_info *info) { /* client case */ if (no_global_v6_socket) { close(info->rfdesc); info->rfdesc = -1; info->wfdesc = -1; } else if ((info->rfdesc == global_v6_socket) && (info->wfdesc == global_v6_socket) && (global_v6_socket_references > 0)) { /* Dereference the global v6 socket. */ global_v6_socket_references--; info->rfdesc = -1; info->wfdesc = -1; #if defined(RELAY_PORT) } else if (relay_port && (info->rfdesc == relay_port_v6_socket) && (info->wfdesc == relay_port_v6_socket) && (relay_port_v6_socket_references > 0)) { /* Dereference the relay port v6 socket. */ relay_port_v6_socket_references--; info->rfdesc = -1; info->wfdesc = -1; #endif } else { log_fatal("Impossible condition at %s:%d", MDL); } if (!quiet_interface_discovery) { if (info->shared_network != NULL) { log_info("Disabling input on Socket/%s/%s", info->name, info->shared_network->name); log_info("Disabling output on Socket/%s/%s", info->name, info->shared_network->name); } else { log_info("Disabling input on Socket/%s", info->name); log_info("Disabling output on Socket/%s", info->name); } } if (!no_global_v6_socket) { if (global_v6_socket_references == 0) { close(global_v6_socket); global_v6_socket = -1; log_info("Unbound from *:%d", (int) ntohs(local_port)); } #if defined(RELAY_PORT) if (relay_port && (relay_port_v6_socket_references == 0)) { close(relay_port_v6_socket); relay_port_v6_socket = -1; log_info("Unbound from relay port *:%d", (int) ntohs(relay_port)); } #endif } } #endif /* DHCPv6 */ #if defined (USE_SOCKET_SEND) || defined (USE_SOCKET_FALLBACK) ssize_t send_packet (interface, packet, raw, len, from, to, hto) struct interface_info *interface; struct packet *packet; struct dhcp_packet *raw; size_t len; struct in_addr from; struct sockaddr_in *to; struct hardware *hto; { int result; #ifdef IGNORE_HOSTUNREACH int retry = 0; do { #endif #if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO) struct in_pktinfo pktinfo; if (interface->ifp != NULL) { memset(&pktinfo, 0, sizeof (pktinfo)); pktinfo.ipi_ifindex = interface->ifp->ifr_index; if (setsockopt(interface->wfdesc, IPPROTO_IP, IP_PKTINFO, (char *)&pktinfo, sizeof(pktinfo)) < 0) log_fatal("setsockopt: IP_PKTINFO: %m"); } #endif result = sendto (interface -> wfdesc, (char *)raw, len, 0, (struct sockaddr *)to, sizeof *to); #ifdef IGNORE_HOSTUNREACH } while (to -> sin_addr.s_addr == htonl (INADDR_BROADCAST) && result < 0 && (errno == EHOSTUNREACH || errno == ECONNREFUSED) && retry++ < 10); #endif if (result < 0) { log_error ("send_packet: %m"); if (errno == ENETUNREACH) log_error ("send_packet: please consult README file%s", " regarding broadcast address."); } return result; } #endif /* USE_SOCKET_SEND || USE_SOCKET_FALLBACK */ #ifdef DHCPv6 /* * Solaris 9 is missing the CMSG_LEN and CMSG_SPACE macros, so we will * synthesize them (based on the BIND 9 technique). */ #ifndef CMSG_LEN static size_t CMSG_LEN(size_t len) { size_t hdrlen; /* * Cast NULL so that any pointer arithmetic performed by CMSG_DATA * is correct. */ hdrlen = (size_t)CMSG_DATA(((struct cmsghdr *)NULL)); return hdrlen + len; } #endif /* !CMSG_LEN */ #ifndef CMSG_SPACE static size_t CMSG_SPACE(size_t len) { struct msghdr msg; struct cmsghdr *cmsgp; /* * XXX: The buffer length is an ad-hoc value, but should be enough * in a practical sense. */ union { struct cmsghdr cmsg_sizer; u_int8_t pktinfo_sizer[sizeof(struct cmsghdr) + 1024]; } dummybuf; memset(&msg, 0, sizeof(msg)); msg.msg_control = &dummybuf; msg.msg_controllen = sizeof(dummybuf); cmsgp = (struct cmsghdr *)&dummybuf; cmsgp->cmsg_len = CMSG_LEN(len); cmsgp = CMSG_NXTHDR(&msg, cmsgp); if (cmsgp != NULL) { return (char *)cmsgp - (char *)msg.msg_control; } else { return 0; } } #endif /* !CMSG_SPACE */ #endif /* DHCPv6 */ #if defined(DHCPv6) || \ (defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && \ defined(USE_V4_PKTINFO)) /* * For both send_packet6() and receive_packet6() we need to allocate * space for the cmsg header information. We do this once and reuse * the buffer. We also need the control buf for send_packet() and * receive_packet() when we use a single socket and IP_PKTINFO to * send the packet out the correct interface. */ static void *control_buf = NULL; static size_t control_buf_len = 0; static void allocate_cmsg_cbuf(void) { control_buf_len = CMSG_SPACE(sizeof(struct in6_pktinfo)); control_buf = dmalloc(control_buf_len, MDL); return; } #endif /* DHCPv6, IP_PKTINFO ... */ #ifdef DHCPv6 /* * For both send_packet6() and receive_packet6() we need to use the * sendmsg()/recvmsg() functions rather than the simpler send()/recv() * functions. * * In the case of send_packet6(), we need to do this in order to insure * that the reply packet leaves on the same interface that it arrived * on. * * In the case of receive_packet6(), we need to do this in order to * get the IP address the packet was sent to. This is used to identify * whether a packet is multicast or unicast. * * Helpful man pages: recvmsg, readv (talks about the iovec stuff), cmsg. * * Also see the sections in RFC 3542 about IPV6_PKTINFO. */ /* Send an IPv6 packet */ ssize_t send_packet6(struct interface_info *interface, const unsigned char *raw, size_t len, struct sockaddr_in6 *to) { struct msghdr m; struct iovec v; struct sockaddr_in6 dst; int result; struct in6_pktinfo *pktinfo; struct cmsghdr *cmsg; unsigned int ifindex; /* * If necessary allocate space for the control message header. * The space is common between send and receive. */ if (control_buf == NULL) { allocate_cmsg_cbuf(); if (control_buf == NULL) { log_error("send_packet6: unable to allocate cmsg header"); return(ENOMEM); } } memset(control_buf, 0, control_buf_len); /* * Initialize our message header structure. */ memset(&m, 0, sizeof(m)); /* * Set the target address we're sending to. * Enforce the scope ID for bogus BSDs. */ memcpy(&dst, to, sizeof(dst)); m.msg_name = &dst; m.msg_namelen = sizeof(dst); ifindex = if_nametoindex(interface->name); if (no_global_v6_socket) dst.sin6_scope_id = ifindex; /* * Set the data buffer we're sending. (Using this wacky * "scatter-gather" stuff... we only have a single chunk * of data to send, so we declare a single vector entry.) */ v.iov_base = (char *)raw; v.iov_len = len; m.msg_iov = &v; m.msg_iovlen = 1; /* * Setting the interface is a bit more involved. * * We have to create a "control message", and set that to * define the IPv6 packet information. We could set the * source address if we wanted, but we can safely let the * kernel decide what that should be. */ m.msg_control = control_buf; m.msg_controllen = control_buf_len; cmsg = CMSG_FIRSTHDR(&m); INSIST(cmsg != NULL); cmsg->cmsg_level = IPPROTO_IPV6; cmsg->cmsg_type = IPV6_PKTINFO; cmsg->cmsg_len = CMSG_LEN(sizeof(*pktinfo)); pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg); memset(pktinfo, 0, sizeof(*pktinfo)); pktinfo->ipi6_addr = local_address6; pktinfo->ipi6_ifindex = ifindex; result = sendmsg(interface->wfdesc, &m, 0); if (result < 0) { log_error("send_packet6: %m"); } return result; } #endif /* DHCPv6 */ #ifdef USE_SOCKET_RECEIVE ssize_t receive_packet (interface, buf, len, from, hfrom) struct interface_info *interface; unsigned char *buf; size_t len; struct sockaddr_in *from; struct hardware *hfrom; { #if !(defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO)) SOCKLEN_T flen = sizeof *from; #endif int result; /* * The normal Berkeley socket interface doesn't give us any way * to know what hardware interface we received the message on, * but we should at least make sure the structure is emptied. */ memset(hfrom, 0, sizeof(*hfrom)); #ifdef IGNORE_HOSTUNREACH int retry = 0; do { #endif #if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO) struct msghdr m; struct iovec v; struct cmsghdr *cmsg; struct in_pktinfo *pktinfo; unsigned int ifindex; /* * If necessary allocate space for the control message header. * The space is common between send and receive. */ if (control_buf == NULL) { allocate_cmsg_cbuf(); if (control_buf == NULL) { log_error("receive_packet: unable to allocate cmsg " "header"); return(ENOMEM); } } memset(control_buf, 0, control_buf_len); /* * Initialize our message header structure. */ memset(&m, 0, sizeof(m)); /* * Point so we can get the from address. */ m.msg_name = from; m.msg_namelen = sizeof(*from); /* * Set the data buffer we're receiving. (Using this wacky * "scatter-gather" stuff... but we that doesn't really make * sense for us, so we use a single vector entry.) */ v.iov_base = buf; v.iov_len = len; m.msg_iov = &v; m.msg_iovlen = 1; /* * Getting the interface is a bit more involved. * * We set up some space for a "control message". We have * previously asked the kernel to give us packet * information (when we initialized the interface), so we * should get the interface index from that. */ m.msg_control = control_buf; m.msg_controllen = control_buf_len; result = recvmsg(interface->rfdesc, &m, 0); if (result >= 0) { /* * If we did read successfully, then we need to loop * through the control messages we received and * find the one with our inteface index. */ cmsg = CMSG_FIRSTHDR(&m); while (cmsg != NULL) { if ((cmsg->cmsg_level == IPPROTO_IP) && (cmsg->cmsg_type == IP_PKTINFO)) { pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg); ifindex = pktinfo->ipi_ifindex; /* * We pass the ifindex back to the caller * using the unused hfrom parameter avoiding * interface changes between sockets and * the discover code. */ memcpy(hfrom->hbuf, &ifindex, sizeof(ifindex)); return (result); } cmsg = CMSG_NXTHDR(&m, cmsg); } /* * We didn't find the necessary control message * flag it as an error */ result = -1; errno = EIO; } #else result = recvfrom(interface -> rfdesc, (char *)buf, len, 0, (struct sockaddr *)from, &flen); #endif /* IP_PKTINFO ... */ #ifdef IGNORE_HOSTUNREACH } while (result < 0 && (errno == EHOSTUNREACH || errno == ECONNREFUSED) && retry++ < 10); #endif return (result); } #endif /* USE_SOCKET_RECEIVE */ #ifdef DHCPv6 ssize_t receive_packet6(struct interface_info *interface, unsigned char *buf, size_t len, struct sockaddr_in6 *from, struct in6_addr *to_addr, unsigned int *if_idx) { struct msghdr m; struct iovec v; int result; struct cmsghdr *cmsg; struct in6_pktinfo *pktinfo; /* * If necessary allocate space for the control message header. * The space is common between send and receive. */ if (control_buf == NULL) { allocate_cmsg_cbuf(); if (control_buf == NULL) { log_error("receive_packet6: unable to allocate cmsg " "header"); return(ENOMEM); } } memset(control_buf, 0, control_buf_len); /* * Initialize our message header structure. */ memset(&m, 0, sizeof(m)); /* * Point so we can get the from address. */ m.msg_name = from; m.msg_namelen = sizeof(*from); /* * Set the data buffer we're receiving. (Using this wacky * "scatter-gather" stuff... but we that doesn't really make * sense for us, so we use a single vector entry.) */ v.iov_base = buf; v.iov_len = len; m.msg_iov = &v; m.msg_iovlen = 1; /* * Getting the interface is a bit more involved. * * We set up some space for a "control message". We have * previously asked the kernel to give us packet * information (when we initialized the interface), so we * should get the destination address from that. */ m.msg_control = control_buf; m.msg_controllen = control_buf_len; result = recvmsg(interface->rfdesc, &m, 0); if (result >= 0) { /* * If we did read successfully, then we need to loop * through the control messages we received and * find the one with our destination address. */ cmsg = CMSG_FIRSTHDR(&m); while (cmsg != NULL) { if ((cmsg->cmsg_level == IPPROTO_IPV6) && (cmsg->cmsg_type == IPV6_PKTINFO)) { pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg); *to_addr = pktinfo->ipi6_addr; *if_idx = pktinfo->ipi6_ifindex; return (result); } cmsg = CMSG_NXTHDR(&m, cmsg); } /* * We didn't find the necessary control message * flag is as an error */ result = -1; errno = EIO; } return (result); } #endif /* DHCPv6 */ #if defined (USE_SOCKET_FALLBACK) /* This just reads in a packet and silently discards it. */ isc_result_t fallback_discard (object) omapi_object_t *object; { char buf [1540]; struct sockaddr_in from; SOCKLEN_T flen = sizeof from; int status; struct interface_info *interface; if (object -> type != dhcp_type_interface) return DHCP_R_INVALIDARG; interface = (struct interface_info *)object; status = recvfrom (interface -> wfdesc, buf, sizeof buf, 0, (struct sockaddr *)&from, &flen); #if defined (DEBUG) /* Only report fallback discard errors if we're debugging. */ if (status < 0) { log_error ("fallback_discard: %m"); return ISC_R_UNEXPECTED; } #else /* ignore the fact that status value is never used */ IGNORE_UNUSED(status); #endif return ISC_R_SUCCESS; } #endif /* USE_SOCKET_FALLBACK */ #if defined (USE_SOCKET_SEND) int can_unicast_without_arp (ip) struct interface_info *ip; { return 0; } int can_receive_unicast_unconfigured (ip) struct interface_info *ip; { #if defined (SOCKET_CAN_RECEIVE_UNICAST_UNCONFIGURED) return 1; #else return 0; #endif } int supports_multiple_interfaces (ip) struct interface_info *ip; { #if defined(SO_BINDTODEVICE) || \ (defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && \ defined(USE_V4_PKTINFO)) return(1); #else return(0); #endif } /* If we have SO_BINDTODEVICE, set up a fallback interface; otherwise, do not. */ void maybe_setup_fallback () { #if defined (USE_SOCKET_FALLBACK) isc_result_t status; struct interface_info *fbi = (struct interface_info *)0; if (setup_fallback (&fbi, MDL)) { fbi -> wfdesc = if_register_socket (fbi, AF_INET, 0, NULL); fbi -> rfdesc = fbi -> wfdesc; log_info ("Sending on Socket/%s%s%s", fbi -> name, (fbi -> shared_network ? "/" : ""), (fbi -> shared_network ? fbi -> shared_network -> name : "")); status = omapi_register_io_object ((omapi_object_t *)fbi, if_readsocket, 0, fallback_discard, 0, 0); if (status != ISC_R_SUCCESS) log_fatal ("Can't register I/O handle for %s: %s", fbi -> name, isc_result_totext (status)); interface_dereference (&fbi, MDL); } #endif } #if defined(sun) && defined(USE_V4_PKTINFO) /* This code assumes the existence of SIOCGLIFHWADDR */ void get_hw_addr(const char *name, struct hardware *hw) { struct sockaddr_dl *dladdrp; int sock, i; struct lifreq lifr; memset(&lifr, 0, sizeof (lifr)); (void) strlcpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); /* * Check if the interface is a virtual or IPMP interface - in those * cases it has no hw address, so generate a random one. */ if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0 || ioctl(sock, SIOCGLIFFLAGS, &lifr) < 0) { if (sock != -1) (void) close(sock); #ifdef DHCPv6 /* * If approrpriate try this with an IPv6 socket */ if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) >= 0 && ioctl(sock, SIOCGLIFFLAGS, &lifr) >= 0) { goto flag_check; } if (sock != -1) (void) close(sock); #endif log_fatal("Couldn't get interface flags for %s: %m", name); } flag_check: if (lifr.lifr_flags & (IFF_VIRTUAL|IFF_IPMP)) { hw->hlen = sizeof (hw->hbuf); srandom((long)gethrtime()); hw->hbuf[0] = HTYPE_IPMP; for (i = 1; i < hw->hlen; ++i) { hw->hbuf[i] = random() % 256; } if (sock != -1) (void) close(sock); return; } if (ioctl(sock, SIOCGLIFHWADDR, &lifr) < 0) log_fatal("Couldn't get interface hardware address for %s: %m", name); dladdrp = (struct sockaddr_dl *)&lifr.lifr_addr; hw->hlen = dladdrp->sdl_alen+1; switch (dladdrp->sdl_type) { case DL_CSMACD: /* IEEE 802.3 */ case DL_ETHER: hw->hbuf[0] = HTYPE_ETHER; break; case DL_TPR: hw->hbuf[0] = HTYPE_IEEE802; break; case DL_FDDI: hw->hbuf[0] = HTYPE_FDDI; break; case DL_IB: hw->hbuf[0] = HTYPE_INFINIBAND; break; default: log_fatal("%s: unsupported DLPI MAC type %lu", name, (unsigned long)dladdrp->sdl_type); } memcpy(hw->hbuf+1, LLADDR(dladdrp), hw->hlen-1); if (sock != -1) (void) close(sock); } #endif /* defined(sun) */ #endif /* USE_SOCKET_SEND */ dhcp-4.4.1/common/tables.c000644 000765 000024 00000145436 13243301226 015636 0ustar00tmarkstaff000000 000000 /* tables.c Tables of information... */ /* * Copyright (c) 2004-2018 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1995-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" /* XXXDPN: Moved here from hash.c, when it moved to libomapi. Not sure where these really belong. */ HASH_FUNCTIONS (group, const char *, struct group_object, group_hash_t, group_reference, group_dereference, do_string_hash) HASH_FUNCTIONS (universe, const char *, struct universe, universe_hash_t, 0, 0, do_case_hash) HASH_FUNCTIONS (option_name, const char *, struct option, option_name_hash_t, option_reference, option_dereference, do_case_hash) HASH_FUNCTIONS (option_code, const unsigned *, struct option, option_code_hash_t, option_reference, option_dereference, do_number_hash) /* DHCP Option names, formats and codes, from RFC1533. Format codes: I - IPv4 address 6 - IPv6 address l - 32-bit signed integer L - 32-bit unsigned integer s - 16-bit signed integer S - 16-bit unsigned integer b - 8-bit signed integer B - 8-bit unsigned integer t - ASCII text T - Lease Time, 32-bit unsigned integer implying a number of seconds from some event. The special all-ones value means 'infinite'. May either be printed as a decimal, eg, "3600", or as this name, eg, "infinite". f - flag (true or false) A - array of all that precedes (e.g., fIA means array of records of a flag and an IP address) a - array of the preceding character (e.g., fIa means a single flag followed by an array of IP addresses) U - name of an option space (universe) F - implicit flag - the presence of the option indicates that the flag is true. o - the preceding value is optional. E - encapsulation, string or colon-separated hex list (the latter two for parsing). E is followed by a text string containing the name of the option space to encapsulate, followed by a '.'. If the E is immediately followed by '.', the applicable vendor option space is used if one is defined. e - If an encapsulation directive is not the first thing in the string, the option scanner requires an efficient way to find the encapsulation. This is done by placing a 'e' at the beginning of the option. The 'e' has no other purpose, and is not required if 'E' is the first thing in the option. X - either an ASCII string or binary data. On output, the string is scanned to see if it's printable ASCII and, if so, output as a quoted string. If not, it's output as colon-separated hex. On input, the option can be specified either as a quoted string or as a colon-separated hex list. N - enumeration. N is followed by a text string containing the name of the set of enumeration values to parse or emit, followed by a '.'. The width of the data is specified in the named enumeration. Named enumerations are tracked in parse.c. d - Domain name (i.e., FOO or FOO.BAR). D - Domain list (i.e., example.com eng.example.com) c - When following a 'D' atom, enables compression pointers. Z - Zero-length option */ struct universe dhcp_universe; static struct option dhcp_options[] = { { "subnet-mask", "I", &dhcp_universe, 1, 1 }, { "time-offset", "l", &dhcp_universe, 2, 1 }, { "routers", "IA", &dhcp_universe, 3, 1 }, { "time-servers", "IA", &dhcp_universe, 4, 1 }, { "ien116-name-servers", "IA", &dhcp_universe, 5, 1 }, { "domain-name-servers", "IA", &dhcp_universe, 6, 1 }, { "log-servers", "IA", &dhcp_universe, 7, 1 }, { "cookie-servers", "IA", &dhcp_universe, 8, 1 }, { "lpr-servers", "IA", &dhcp_universe, 9, 1 }, { "impress-servers", "IA", &dhcp_universe, 10, 1 }, { "resource-location-servers", "IA", &dhcp_universe, 11, 1 }, { "host-name", "t", &dhcp_universe, 12, 1 }, { "boot-size", "S", &dhcp_universe, 13, 1 }, { "merit-dump", "t", &dhcp_universe, 14, 1 }, { "domain-name", "t", &dhcp_universe, 15, 1 }, { "swap-server", "I", &dhcp_universe, 16, 1 }, { "root-path", "t", &dhcp_universe, 17, 1 }, { "extensions-path", "t", &dhcp_universe, 18, 1 }, { "ip-forwarding", "f", &dhcp_universe, 19, 1 }, { "non-local-source-routing", "f", &dhcp_universe, 20, 1 }, { "policy-filter", "IIA", &dhcp_universe, 21, 1 }, { "max-dgram-reassembly", "S", &dhcp_universe, 22, 1 }, { "default-ip-ttl", "B", &dhcp_universe, 23, 1 }, { "path-mtu-aging-timeout", "L", &dhcp_universe, 24, 1 }, { "path-mtu-plateau-table", "SA", &dhcp_universe, 25, 1 }, { "interface-mtu", "S", &dhcp_universe, 26, 1 }, { "all-subnets-local", "f", &dhcp_universe, 27, 1 }, { "broadcast-address", "I", &dhcp_universe, 28, 1 }, { "perform-mask-discovery", "f", &dhcp_universe, 29, 1 }, { "mask-supplier", "f", &dhcp_universe, 30, 1 }, { "router-discovery", "f", &dhcp_universe, 31, 1 }, { "router-solicitation-address", "I", &dhcp_universe, 32, 1 }, { "static-routes", "IIA", &dhcp_universe, 33, 1 }, { "trailer-encapsulation", "f", &dhcp_universe, 34, 1 }, { "arp-cache-timeout", "L", &dhcp_universe, 35, 1 }, { "ieee802-3-encapsulation", "f", &dhcp_universe, 36, 1 }, { "default-tcp-ttl", "B", &dhcp_universe, 37, 1 }, { "tcp-keepalive-interval", "L", &dhcp_universe, 38, 1 }, { "tcp-keepalive-garbage", "f", &dhcp_universe, 39, 1 }, { "nis-domain", "t", &dhcp_universe, 40, 1 }, { "nis-servers", "IA", &dhcp_universe, 41, 1 }, { "ntp-servers", "IA", &dhcp_universe, 42, 1 }, { "vendor-encapsulated-options", "E.", &dhcp_universe, 43, 1 }, { "netbios-name-servers", "IA", &dhcp_universe, 44, 1 }, { "netbios-dd-server", "IA", &dhcp_universe, 45, 1 }, { "netbios-node-type", "B", &dhcp_universe, 46, 1 }, { "netbios-scope", "t", &dhcp_universe, 47, 1 }, { "font-servers", "IA", &dhcp_universe, 48, 1 }, { "x-display-manager", "IA", &dhcp_universe, 49, 1 }, { "dhcp-requested-address", "I", &dhcp_universe, 50, 1 }, { "dhcp-lease-time", "L", &dhcp_universe, 51, 1 }, { "dhcp-option-overload", "B", &dhcp_universe, 52, 1 }, { "dhcp-message-type", "B", &dhcp_universe, 53, 1 }, { "dhcp-server-identifier", "I", &dhcp_universe, 54, 1 }, { "dhcp-parameter-request-list", "BA", &dhcp_universe, 55, 1 }, { "dhcp-message", "t", &dhcp_universe, 56, 1 }, { "dhcp-max-message-size", "S", &dhcp_universe, 57, 1 }, { "dhcp-renewal-time", "L", &dhcp_universe, 58, 1 }, { "dhcp-rebinding-time", "L", &dhcp_universe, 59, 1 }, { "vendor-class-identifier", "X", &dhcp_universe, 60, 1 }, { "dhcp-client-identifier", "X", &dhcp_universe, 61, 1 }, { "nwip-domain", "t", &dhcp_universe, 62, 1 }, { "nwip-suboptions", "Enwip.", &dhcp_universe, 63, 1 }, { "nisplus-domain", "t", &dhcp_universe, 64, 1 }, { "nisplus-servers", "IA", &dhcp_universe, 65, 1 }, { "tftp-server-name", "t", &dhcp_universe, 66, 1 }, { "bootfile-name", "t", &dhcp_universe, 67, 1 }, { "mobile-ip-home-agent", "IA", &dhcp_universe, 68, 1 }, { "smtp-server", "IA", &dhcp_universe, 69, 1 }, { "pop-server", "IA", &dhcp_universe, 70, 1 }, { "nntp-server", "IA", &dhcp_universe, 71, 1 }, { "www-server", "IA", &dhcp_universe, 72, 1 }, { "finger-server", "IA", &dhcp_universe, 73, 1 }, { "irc-server", "IA", &dhcp_universe, 74, 1 }, { "streettalk-server", "IA", &dhcp_universe, 75, 1 }, { "streettalk-directory-assistance-server", "IA", &dhcp_universe, 76, 1 }, { "user-class", "t", &dhcp_universe, 77, 1 }, { "slp-directory-agent", "fIa", &dhcp_universe, 78, 1 }, { "slp-service-scope", "fto", &dhcp_universe, 79, 1 }, /* 80 is the zero-length rapid-commit (RFC 4039) */ { "fqdn", "Efqdn.", &dhcp_universe, 81, 1 }, { "relay-agent-information", "Eagent.", &dhcp_universe, 82, 1 }, /* 83 is iSNS (RFC 4174) */ /* 84 is unassigned */ { "nds-servers", "IA", &dhcp_universe, 85, 1 }, { "nds-tree-name", "t", &dhcp_universe, 86, 1 }, { "nds-context", "t", &dhcp_universe, 87, 1 }, /* Note: RFC4280 fails to identify if the DHCPv4 option is to use * compression pointers or not. Assume not. */ { "bcms-controller-names", "D", &dhcp_universe, 88, 1 }, { "bcms-controller-address", "Ia", &dhcp_universe, 89, 1 }, /* 90 is the authentication option (RFC 3118) */ { "client-last-transaction-time", "L", &dhcp_universe, 91, 1 }, { "associated-ip", "Ia", &dhcp_universe, 92, 1 }, #if defined(RFC4578_OPTIONS) /* Defined by RFC 4578 */ { "pxe-system-type", "Sa", &dhcp_universe, 93, 1 }, { "pxe-interface-id", "BBB", &dhcp_universe, 94, 1 }, { "pxe-client-id", "BX", &dhcp_universe, 97, 1 }, #endif { "uap-servers", "t", &dhcp_universe, 98, 1 }, #if defined(RFC4776_OPTIONS) { "geoconf-civic", "X", &dhcp_universe, 99, 1 }, #endif #if defined(RFC4833_OPTIONS) { "pcode", "t", &dhcp_universe, 100, 1 }, { "tcode", "t", &dhcp_universe, 101, 1 }, #endif { "netinfo-server-address", "Ia", &dhcp_universe, 112, 1 }, { "netinfo-server-tag", "t", &dhcp_universe, 113, 1 }, { "default-url", "t", &dhcp_universe, 114, 1 }, #if defined(RFC2563_OPTIONS) { "auto-config", "B", &dhcp_universe, 116, 1 }, #endif #if defined(RFC2937_OPTIONS) { "name-service-search", "Sa", &dhcp_universe, 117, 1 }, #endif { "subnet-selection", "I", &dhcp_universe, 118, 1 }, { "domain-search", "Dc", &dhcp_universe, 119, 1 }, { "vivco", "Evendor-class.", &dhcp_universe, 124, 1 }, { "vivso", "Evendor.", &dhcp_universe, 125, 1 }, #if 0 /* Referenced by RFC 4578. * DO NOT UNCOMMENT THESE DEFINITIONS: these names are placeholders * and will not be used in future versions of the software. */ { "pxe-undefined-1", "X", &dhcp_universe, 128, 1 }, { "pxe-undefined-2", "X", &dhcp_universe, 129, 1 }, { "pxe-undefined-3", "X", &dhcp_universe, 130, 1 }, { "pxe-undefined-4", "X", &dhcp_universe, 131, 1 }, { "pxe-undefined-5", "X", &dhcp_universe, 132, 1 }, { "pxe-undefined-6", "X", &dhcp_universe, 133, 1 }, { "pxe-undefined-7", "X", &dhcp_universe, 134, 1 }, { "pxe-undefined-8", "X", &dhcp_universe, 135, 1 }, #endif #if defined(RFC5192_OPTIONS) {"pana-agent", "Ia", &dhcp_universe, 136, 1 }, #endif #if defined(RFC5223_OPTIONS) {"v4-lost", "d", &dhcp_universe, 137, 1 }, #endif #if defined(RFC5417_OPTIONS) {"capwap-ac-v4", "Ia", &dhcp_universe, 138, 1 }, #endif #if defined(RFC6011_OPTIONS) { "sip-ua-cs-domains", "Dc", &dhcp_universe, 141, 1 }, #endif #if defined(RFC6153_OPTIONS) { "ipv4-address-andsf", "IA", &dhcp_universe, 142, 1 }, #endif #if defined(RFC6731_OPTIONS) { "rdnss-selection", "BIID", &dhcp_universe, 146, 1 }, #endif #if defined(RFC5859_OPTIONS) { "tftp-server-address", "Ia", &dhcp_universe, 150, 1 }, #endif #if defined(RFC7618_OPTIONS) { "v4-portparams", "BBS", &dhcp_universe, 159, 1 }, #endif #if defined(RFC7710_OPTIONS) { "v4-captive-portal", "t", &dhcp_universe, 160, 1 }, #endif #if defined(RFC5071_OPTIONS) #if 0 /* Option 208 has been officially deprecated. Do NOT define it */ { "pxelinux-magic", "BBBB", &dhcp_universe, 208, 1 }, #endif { "loader-configfile", "t", &dhcp_universe, 209, 1 }, { "loader-pathprefix", "t", &dhcp_universe, 210, 1 }, { "loader-reboottime", "L", &dhcp_universe, 211, 1 }, #endif #if defined(RFC5969_OPTIONS) { "option-6rd", "BB6Ia", &dhcp_universe, 212, 1 }, #endif #if defined(RFC5986_OPTIONS) {"v4-access-domain", "d", &dhcp_universe, 213, 1 }, #endif { NULL, NULL, NULL, 0, 0 } }; struct universe nwip_universe; static struct option nwip_options[] = { { "illegal-1", "", &nwip_universe, 1, 1 }, { "illegal-2", "", &nwip_universe, 2, 1 }, { "illegal-3", "", &nwip_universe, 3, 1 }, { "illegal-4", "", &nwip_universe, 4, 1 }, { "nsq-broadcast", "f", &nwip_universe, 5, 1 }, { "preferred-dss", "IA", &nwip_universe, 6, 1 }, { "nearest-nwip-server", "IA", &nwip_universe, 7, 1 }, { "autoretries", "B", &nwip_universe, 8, 1 }, { "autoretry-secs", "B", &nwip_universe, 9, 1 }, { "nwip-1-1", "f", &nwip_universe, 10, 1 }, { "primary-dss", "I", &nwip_universe, 11, 1 }, { NULL, NULL, NULL, 0, 0 } }; /* Note that the "FQDN suboption space" does not reflect the FQDN option * format - rather, this is a handy "virtualization" of a flat option * which makes manual configuration and presentation of some of its * contents easier (each of these suboptions is a fixed-space field within * the fqdn contents - domain and host names are derived from a common field, * and differ in the left and right hand side of the leftmost dot, fqdn is * the combination of the two). * * Note further that the DHCPv6 and DHCPv4 'fqdn' options use the same * virtualized option space to store their work. */ struct universe fqdn_universe; struct universe fqdn6_universe; static struct option fqdn_options[] = { { "no-client-update", "f", &fqdn_universe, 1, 1 }, { "server-update", "f", &fqdn_universe, 2, 1 }, { "encoded", "f", &fqdn_universe, 3, 1 }, { "rcode1", "B", &fqdn_universe, 4, 1 }, { "rcode2", "B", &fqdn_universe, 5, 1 }, { "hostname", "t", &fqdn_universe, 6, 1 }, { "domainname", "t", &fqdn_universe, 7, 1 }, { "fqdn", "t", &fqdn_universe, 8, 1 }, { NULL, NULL, NULL, 0, 0 } }; struct universe vendor_class_universe; static struct option vendor_class_options[] = { { "isc", "X", &vendor_class_universe, 2495, 1 }, { NULL, NULL, NULL, 0, 0 } }; struct universe vendor_universe; static struct option vendor_options[] = { { "isc", "Eisc.", &vendor_universe, 2495, 1 }, { NULL, NULL, NULL, 0, 0 } }; struct universe isc_universe; static struct option isc_options [] = { { "media", "t", &isc_universe, 1, 1 }, { "update-assist", "X", &isc_universe, 2, 1 }, { NULL, NULL, NULL, 0, 0 } }; struct universe dhcpv6_universe; static struct option dhcpv6_options[] = { /* RFC3315 OPTIONS */ /* Client and server DUIDs are opaque fields, but marking them * up somewhat makes configuration easier. */ { "client-id", "X", &dhcpv6_universe, 1, 1 }, { "server-id", "X", &dhcpv6_universe, 2, 1 }, /* ia-* options actually have at their ends a space for options * that are specific to this instance of the option. We can not * handle this yet at this stage of development, so the encoding * of these options is unspecified ("X"). */ { "ia-na", "X", &dhcpv6_universe, 3, 1 }, { "ia-ta", "X", &dhcpv6_universe, 4, 1 }, { "ia-addr", "X", &dhcpv6_universe, 5, 1 }, /* "oro" is DHCPv6 speak for "parameter-request-list" */ { "oro", "SA", &dhcpv6_universe, 6, 1 }, { "preference", "B", &dhcpv6_universe, 7, 1 }, { "elapsed-time", "S", &dhcpv6_universe, 8, 1 }, { "relay-msg", "X", &dhcpv6_universe, 9, 1 }, /* Option code 10 is curiously unassigned. */ /* * In draft-ietf-dhc-dhcpv6-25 there were two OPTION_CLIENT_MSG and * OPTION_SERVER_MSG options. They were eventually unified as * OPTION_RELAY_MSG, hence no option with value of 10. */ #if 0 /* XXX: missing suitable atoms for the auth option. We may want * to 'virtually encapsulate' this option a la the fqdn option * seeing as it is processed explicitly by the server and unlikely * to be configured by hand by users as such. */ { "auth", "Nauth-protocol.Nauth-algorithm.Nrdm-type.LLX", &dhcpv6_universe, 11, 1 }, #endif { "unicast", "6", &dhcpv6_universe, 12, 1 }, { "status-code", "Nstatus-codes.to", &dhcpv6_universe, 13, 1 }, { "rapid-commit", "Z", &dhcpv6_universe, 14, 1 }, #if 0 /* XXX: user-class contents are of the form "StA" where the * integer describes the length of the text field. We don't have * an atom for pre-determined-length octet strings yet, so we * can't quite do these two. */ { "user-class", "X", &dhcpv6_universe, 15, 1 }, { "vendor-class", "X", &dhcpv6_universe, 16, 1 }, #endif { "vendor-opts", "Evsio.", &dhcpv6_universe, 17, 1 }, { "interface-id", "X", &dhcpv6_universe, 18, 1 }, { "reconf-msg", "Ndhcpv6-messages.", &dhcpv6_universe, 19, 1 }, { "reconf-accept", "Z", &dhcpv6_universe, 20, 1 }, /* RFC3319 OPTIONS */ /* Of course: we would HAVE to have a different atom for * domain names without compression. Typical. */ { "sip-servers-names", "D", &dhcpv6_universe, 21, 1 }, { "sip-servers-addresses", "6A", &dhcpv6_universe, 22, 1 }, /* RFC3646 OPTIONS */ { "name-servers", "6A", &dhcpv6_universe, 23, 1 }, { "domain-search", "D", &dhcpv6_universe, 24, 1 }, /* RFC3633 OPTIONS */ { "ia-pd", "X", &dhcpv6_universe, 25, 1 }, { "ia-prefix", "X", &dhcpv6_universe, 26, 1 }, /* RFC3898 OPTIONS */ { "nis-servers", "6A", &dhcpv6_universe, 27, 1 }, { "nisp-servers", "6A", &dhcpv6_universe, 28, 1 }, { "nis-domain-name", "D", &dhcpv6_universe, 29, 1 }, { "nisp-domain-name", "D", &dhcpv6_universe, 30, 1 }, /* RFC4075 OPTIONS */ { "sntp-servers", "6A", &dhcpv6_universe, 31, 1 }, /* RFC4242 OPTIONS */ { "info-refresh-time", "T", &dhcpv6_universe, 32, 1 }, /* RFC4280 OPTIONS */ { "bcms-server-d", "D", &dhcpv6_universe, 33, 1 }, { "bcms-server-a", "6A", &dhcpv6_universe, 34, 1 }, /* Note that 35 is not assigned. */ #if defined(RFC4776_OPTIONS) /* RFC4776 OPTIONS */ { "geoconf-civic", "X", &dhcpv6_universe, 36, 1 }, #endif /* RFC4649 OPTIONS */ /* The remote-id option looks like the VSIO option, but for all * intents and purposes we only need to treat the entire field * like a globally unique identifier (and if we create such an * option, ensure the first 4 bytes are our enterprise-id followed * by a globally unique ID so long as you're within that enterprise * id). So we'll use "X" for now unless someone grumbles. */ { "remote-id", "X", &dhcpv6_universe, 37, 1 }, /* RFC4580 OPTIONS */ { "subscriber-id", "X", &dhcpv6_universe, 38, 1 }, /* RFC4704 OPTIONS */ /* The DHCPv6 FQDN option is...weird. * * We use the same "virtual" encapsulated space as DHCPv4's FQDN * option, so it can all be configured in one place. Since the * options system does not support multiple inheritance, we use * a 'shill' layer to perform the different protocol conversions, * and to redirect any queries in the DHCPv4 FQDN's space. */ { "fqdn", "Efqdn6-if-you-see-me-its-a-bug-bug-bug.", &dhcpv6_universe, 39, 1 }, /* RFC5192 */ #if defined(RFC5192_OPTIONS) { "pana-agent", "6A", &dhcpv6_universe, 40, 1 }, #endif /* RFC4833 OPTIONS */ #if defined(RFC4833_OPTIONS) { "new-posix-timezone", "t", &dhcpv6_universe, 41, 1 }, { "new-tzdb-timezone", "t", &dhcpv6_universe, 42, 1 }, #endif /* RFC4994 OPTIONS */ #if defined(RFC4994_OPTIONS) { "ero", "SA", &dhcpv6_universe, 43, 1 }, #endif /* RFC5007 OPTIONS */ { "lq-query", "X", &dhcpv6_universe, 44, 1 }, { "client-data", "X", &dhcpv6_universe, 45, 1 }, { "clt-time", "L", &dhcpv6_universe, 46, 1 }, { "lq-relay-data", "6X", &dhcpv6_universe, 47, 1 }, { "lq-client-link", "6A", &dhcpv6_universe, 48, 1 }, /* RFC5223 OPTIONS */ #if defined(RFC5223_OPTIONS) { "v6-lost", "d", &dhcpv6_universe, 51, 1 }, #endif /* RFC5417 OPTIONS */ #if defined(RFC5417_OPTIONS) { "capwap-ac-v6", "6a", &dhcpv6_universe, 52, 1 }, #endif /* RFC5460 OPTIONS */ #if defined(RFC5460_OPTIONS) { "relay-id", "X", &dhcpv6_universe, 53, 1 }, #endif /* RFC5986 OPTIONS */ #if defined(RFC5986_OPTIONS) { "v6-access-domain", "d", &dhcpv6_universe, 57, 1 }, #endif /* RFC6011 OPTIONS */ #if defined(RFC6011_OPTIONS) { "sip-ua-cs-list", "D", &dhcpv6_universe, 58, 1 }, #endif /* RFC5970 OPTIONS */ #if defined(RFC5970_OPTIONS) { "bootfile-url", "t", &dhcpv6_universe, 59, 1 }, { "bootfile-param", "X", &dhcpv6_universe, 60, 1 }, { "client-arch-type", "SA", &dhcpv6_universe, 61, 1 }, { "nii", "BBB", &dhcpv6_universe, 62, 1 }, #endif /* RFC6334 OPTIONS */ #if defined(RFC6334_OPTIONS) { "aftr-name", "d", &dhcpv6_universe, 64, 1 }, #endif /* RFC6440 OPTIONS */ #if defined(RFC6440_OPTIONS) { "erp-local-domain-name", "d", &dhcpv6_universe, 65, 1 }, #endif /* RFC6731 OPTIONS */ #if defined(RFC6731_OPTIONS) { "rdnss-selection", "6BD", &dhcpv6_universe, 74, 1 }, #endif /* RFC6939 OPTIONS */ #if defined(RFC6939_OPTIONS) { "client-linklayer-addr", "X", &dhcpv6_universe, 79, 1 }, #endif /* RFC6977 OPTIONS */ #if defined(RFC6977_OPTIONS) { "link-address", "6", &dhcpv6_universe, 80, 1 }, #endif /* RFC7083 OPTIONS */ #if defined(RFC7083_OPTIONS) { "solmax-rt", "L", &dhcpv6_universe, 82, 1 }, { "inf-max-rt", "L", &dhcpv6_universe, 83, 1 }, #endif /* RFC7341 OPTIONS */ #if defined(RFC7341_OPTIONS) { "dhcpv4-msg", "X", &dhcpv6_universe, 87, 1 }, { "dhcp4-o-dhcp6-server", "6A", &dhcpv6_universe, 88, 1 }, #endif #if defined(RFC7710_OPTIONS) { "v6-captive-portal", "t", &dhcpv6_universe, 103, 1 }, #endif { "relay-source-port", "S", &dhcpv6_universe, 135, 1 }, #if defined(RFC6153_OPTIONS) { "ipv6-address-andsf", "6A", &dhcpv6_universe, 143, 1 }, #endif { NULL, NULL, NULL, 0, 0 } }; struct enumeration_value dhcpv6_duid_type_values[] = { { "duid-llt", DUID_LLT }, /* Link-Local Plus Time */ { "duid-en", DUID_EN }, /* DUID based upon enterprise-ID. */ { "duid-ll", DUID_LL }, /* DUID from Link Local address only. */ { "duid-uuid", DUID_UUID }, /* DUID based upon UUID */ { NULL, 0 } }; struct enumeration dhcpv6_duid_types = { NULL, "duid-types", 2, dhcpv6_duid_type_values }; struct enumeration_value dhcpv6_status_code_values[] = { { "success", 0 }, /* Success */ { "UnspecFail", 1 }, /* Failure, for unspecified reasons. */ { "NoAddrsAvail", 2 }, /* Server has no addresses to assign. */ { "NoBinding", 3 }, /* Client record (binding) unavailable. */ { "NotOnLink", 4 }, /* Bad prefix for the link. */ { "UseMulticast", 5 }, /* Not just good advice. It's the law. */ { "NoPrefixAvail", 6 }, /* Server has no prefixes to assign. */ { "UnknownQueryType", 7 }, /* Query-type unknown/unsupported. */ { "MalformedQuery", 8 }, /* Leasequery not valid. */ { "NotConfigured", 9 }, /* The target address is not in config. */ { "NotAllowed", 10 }, /* Server doesn't allow the leasequery. */ { "QueryTerminated", 11 }, /* Leasequery terminated. */ { NULL, 0 } }; struct enumeration dhcpv6_status_codes = { NULL, "status-codes", 2, dhcpv6_status_code_values }; struct enumeration_value lq6_query_type_values[] = { { "query-by-address", 1 }, { "query-by-clientid", 2 }, { "query-by-relay-id", 3 }, { "query-by-link-address", 4 }, { "query-by-remote-id", 5 }, { NULL, 0 } }; struct enumeration lq6_query_types = { NULL, "query-types", 2, lq6_query_type_values }; struct enumeration_value dhcpv6_message_values[] = { { "SOLICIT", 1 }, { "ADVERTISE", 2 }, { "REQUEST", 3 }, { "CONFIRM", 4 }, { "RENEW", 5 }, { "REBIND", 6 }, { "REPLY", 7 }, { "RELEASE", 8 }, { "DECLINE", 9 }, { "RECONFIGURE", 10 }, { "INFORMATION-REQUEST", 11 }, { "RELAY-FORW", 12 }, { "RELAY-REPL", 13 }, { "LEASEQUERY", 14 }, { "LEASEQUERY-REPLY", 15 }, { "LEASEQUERY-DONE", 16 }, { "LEASEQUERY-DATA", 17 }, { "RECONFIGURE-REQUEST", 18 }, { "RECONFIGURE-REPLY", 19 }, { "DHCPV4-QUERY", 20 }, { "DHCPV4-RESPONSE", 21 }, { NULL, 0 } }; /* Some code refers to a different table. */ const char *dhcpv6_type_names[] = { NULL, "Solicit", "Advertise", "Request", "Confirm", "Renew", "Rebind", "Reply", "Release", "Decline", "Reconfigure", "Information-request", "Relay-forward", "Relay-reply", "Leasequery", "Leasequery-reply", "Leasequery-done", "Leasequery-data", "Reconfigure-request", "Reconfigure-reply", "Dhcpv4-query", "Dhcpv4-response" }; const int dhcpv6_type_name_max = (sizeof(dhcpv6_type_names) / sizeof(dhcpv6_type_names[0])); struct enumeration dhcpv6_messages = { NULL, "dhcpv6-messages", 1, dhcpv6_message_values }; struct universe vsio_universe; static struct option vsio_options[] = { { "isc", "Eisc6.", &vsio_universe, 2495, 1 }, { NULL, NULL, NULL, 0, 0 } }; struct universe isc6_universe; static struct option isc6_options[] = { { "media", "t", &isc6_universe, 1, 1 }, { "update-assist", "X", &isc6_universe, 2, 1 }, { "4o6-interface", "t", &isc6_universe, 60000, 1 }, { "4o6-source-address", "6", &isc6_universe, 60001, 1 }, { NULL, NULL, NULL, 0, 0 } }; const char *hardware_types [] = { "unknown-0", "ethernet", "unknown-2", "unknown-3", "unknown-4", "unknown-5", "token-ring", "unknown-7", "fddi", "unknown-9", "unknown-10", "unknown-11", "unknown-12", "unknown-13", "unknown-14", "unknown-15", "unknown-16", "unknown-17", "unknown-18", "unknown-19", "unknown-20", "unknown-21", "unknown-22", "unknown-23", "unknown-24", "unknown-25", "unknown-26", "unknown-27", "unknown-28", "unknown-29", "unknown-30", "unknown-31", "infiniband", "unknown-33", "unknown-34", "unknown-35", "unknown-36", "unknown-37", "unknown-38", "unknown-39", "unknown-40", "unknown-41", "unknown-42", "unknown-43", "unknown-44", "unknown-45", "unknown-46", "unknown-47", "unknown-48", "unknown-49", "unknown-50", "unknown-51", "unknown-52", "unknown-53", "unknown-54", "unknown-55", "unknown-56", "unknown-57", "unknown-58", "unknown-59", "unknown-60", "unknown-61", "unknown-62", "unknown-63", "unknown-64", "unknown-65", "unknown-66", "unknown-67", "unknown-68", "unknown-69", "unknown-70", "unknown-71", "unknown-72", "unknown-73", "unknown-74", "unknown-75", "unknown-76", "unknown-77", "unknown-78", "unknown-79", "unknown-80", "unknown-81", "unknown-82", "unknown-83", "unknown-84", "unknown-85", "unknown-86", "unknown-87", "unknown-88", "unknown-89", "unknown-90", "unknown-91", "unknown-92", "unknown-93", "unknown-94", "unknown-95", "unknown-96", "unknown-97", "unknown-98", "unknown-99", "unknown-100", "unknown-101", "unknown-102", "unknown-103", "unknown-104", "unknown-105", "unknown-106", "unknown-107", "unknown-108", "unknown-109", "unknown-110", "unknown-111", "unknown-112", "unknown-113", "unknown-114", "unknown-115", "unknown-116", "unknown-117", "unknown-118", "unknown-119", "unknown-120", "unknown-121", "unknown-122", "unknown-123", "unknown-124", "unknown-125", "unknown-126", "unknown-127", "unknown-128", "unknown-129", "unknown-130", "unknown-131", "unknown-132", "unknown-133", "unknown-134", "unknown-135", "unknown-136", "unknown-137", "unknown-138", "unknown-139", "unknown-140", "unknown-141", "unknown-142", "unknown-143", "unknown-144", "unknown-145", "unknown-146", "unknown-147", "unknown-148", "unknown-149", "unknown-150", "unknown-151", "unknown-152", "unknown-153", "unknown-154", "unknown-155", "unknown-156", "unknown-157", "unknown-158", "unknown-159", "unknown-160", "unknown-161", "unknown-162", "unknown-163", "unknown-164", "unknown-165", "unknown-166", "unknown-167", "unknown-168", "unknown-169", "unknown-170", "unknown-171", "unknown-172", "unknown-173", "unknown-174", "unknown-175", "unknown-176", "unknown-177", "unknown-178", "unknown-179", "unknown-180", "unknown-181", "unknown-182", "unknown-183", "unknown-184", "unknown-185", "unknown-186", "unknown-187", "unknown-188", "unknown-189", "unknown-190", "unknown-191", "unknown-192", "unknown-193", "unknown-194", "unknown-195", "unknown-196", "unknown-197", "unknown-198", "unknown-199", "unknown-200", "unknown-201", "unknown-202", "unknown-203", "unknown-204", "unknown-205", "unknown-206", "unknown-207", "unknown-208", "unknown-209", "unknown-210", "unknown-211", "unknown-212", "unknown-213", "unknown-214", "unknown-215", "unknown-216", "unknown-217", "unknown-218", "unknown-219", "unknown-220", "unknown-221", "unknown-222", "unknown-223", "unknown-224", "unknown-225", "unknown-226", "unknown-227", "unknown-228", "unknown-229", "unknown-230", "unknown-231", "unknown-232", "unknown-233", "unknown-234", "unknown-235", "unknown-236", "unknown-237", "unknown-238", "unknown-239", "unknown-240", "unknown-241", "unknown-242", "unknown-243", "unknown-244", "unknown-245", "unknown-246", "unknown-247", "unknown-248", "unknown-249", "unknown-250", "unknown-251", "unknown-252", "unknown-253", "unknown-254", "unknown-255" }; universe_hash_t *universe_hash; struct universe **universes; int universe_count, universe_max; /* Universe containing names of configuration options, which, rather than writing "option universe-name.option-name ...;", can be set by writing "option-name ...;". */ struct universe *config_universe; /* XXX: omapi must die...all the below keeps us from having to make the * option structures omapi typed objects, which is a bigger headache. */ char *default_option_format = (char *) "X"; /* Must match hash_reference/dereference types in omapip/hash.h. */ int option_reference(struct option **dest, struct option *src, const char * file, int line) { if (!dest || !src) return DHCP_R_INVALIDARG; if (*dest) { #if defined(POINTER_DEBUG) log_fatal("%s(%d): reference store into non-null pointer!", file, line); #else return DHCP_R_INVALIDARG; #endif } *dest = src; src->refcnt++; rc_register(file, line, dest, src, src->refcnt, 0, RC_MISC); return(ISC_R_SUCCESS); } int option_dereference(struct option **dest, const char *file, int line) { if (!dest) return DHCP_R_INVALIDARG; if (!*dest) { #if defined (POINTER_DEBUG) log_fatal("%s(%d): dereference of null pointer!", file, line); #else return DHCP_R_INVALIDARG; #endif } if ((*dest)->refcnt <= 0) { #if defined (POINTER_DEBUG) log_fatal("%s(%d): dereference of <= 0 refcnt!", file, line); #else return DHCP_R_INVALIDARG; #endif } (*dest)->refcnt--; rc_register(file, line, dest, (*dest), (*dest)->refcnt, 1, RC_MISC); if ((*dest)->refcnt == 0) { /* The option name may be packed in the same alloc as the * option structure. */ if ((char *) (*dest)->name != (char *) ((*dest) + 1)) dfree((char *) (*dest)->name, file, line); /* It's either a user-configured format (allocated), or the * default static format. */ if (((*dest)->format != NULL) && ((*dest)->format != default_option_format)) { dfree((char *) (*dest)->format, file, line); } dfree(*dest, file, line); } *dest = NULL; return ISC_R_SUCCESS; } void initialize_common_option_spaces() { unsigned code; int i; /* The 'universes' table is dynamically grown to contain * universe as they're configured - except during startup. * Since we know how many we put down in .c files, we can * allocate a more-than-right-sized buffer now, leaving some * space for user-configured option spaces. * * 1: dhcp_universe (dhcpv4 options) * 2: nwip_universe (dhcpv4 NWIP option) * 3: fqdn_universe (dhcpv4 fqdn option - reusable for v6) * 4: vendor_class_universe (VIVCO) * 5: vendor_universe (VIVSO) * 6: isc_universe (dhcpv4 isc config space) * 7: dhcpv6_universe (dhcpv6 options) * 8: vsio_universe (DHCPv6 Vendor-Identified space) * 9: isc6_universe (ISC's Vendor universe in DHCPv6 VSIO) * 10: fqdn6_universe (dhcpv6 fqdn option shill to v4) * 11: agent_universe (dhcpv4 relay agent - see server/stables.c) * 12: server_universe (server's config, see server/stables.c) * 13: user-config * 14: more user-config * 15: more user-config * 16: more user-config */ universe_max = 16; i = universe_max * sizeof(struct universe *); if (i <= 0) log_fatal("Ludicrous initial size option space table."); universes = dmalloc(i, MDL); if (universes == NULL) log_fatal("Can't allocate option space table."); memset(universes, 0, i); /* Set up the DHCP option universe... */ dhcp_universe.name = "dhcp"; dhcp_universe.concat_duplicates = 1; dhcp_universe.lookup_func = lookup_hashed_option; dhcp_universe.option_state_dereference = hashed_option_state_dereference; dhcp_universe.save_func = save_hashed_option; dhcp_universe.delete_func = delete_hashed_option; dhcp_universe.encapsulate = hashed_option_space_encapsulate; dhcp_universe.foreach = hashed_option_space_foreach; dhcp_universe.decode = parse_option_buffer; dhcp_universe.length_size = 1; dhcp_universe.tag_size = 1; dhcp_universe.get_tag = getUChar; dhcp_universe.store_tag = putUChar; dhcp_universe.get_length = getUChar; dhcp_universe.store_length = putUChar; dhcp_universe.site_code_min = 0; dhcp_universe.end = DHO_END; dhcp_universe.index = universe_count++; universes [dhcp_universe.index] = &dhcp_universe; if (!option_name_new_hash(&dhcp_universe.name_hash, BYTE_NAME_HASH_SIZE, MDL) || !option_code_new_hash(&dhcp_universe.code_hash, BYTE_CODE_HASH_SIZE, MDL)) log_fatal ("Can't allocate dhcp option hash table."); for (i = 0 ; dhcp_options[i].name ; i++) { option_code_hash_add(dhcp_universe.code_hash, &dhcp_options[i].code, 0, &dhcp_options[i], MDL); option_name_hash_add(dhcp_universe.name_hash, dhcp_options [i].name, 0, &dhcp_options [i], MDL); } #if defined(REPORT_HASH_PERFORMANCE) log_info("DHCP name hash: %s", option_name_hash_report(dhcp_universe.name_hash)); log_info("DHCP code hash: %s", option_code_hash_report(dhcp_universe.code_hash)); #endif /* Set up the Novell option universe (for option 63)... */ nwip_universe.name = "nwip"; nwip_universe.concat_duplicates = 0; /* XXX: reference? */ nwip_universe.lookup_func = lookup_linked_option; nwip_universe.option_state_dereference = linked_option_state_dereference; nwip_universe.save_func = save_linked_option; nwip_universe.delete_func = delete_linked_option; nwip_universe.encapsulate = nwip_option_space_encapsulate; nwip_universe.foreach = linked_option_space_foreach; nwip_universe.decode = parse_option_buffer; nwip_universe.length_size = 1; nwip_universe.tag_size = 1; nwip_universe.get_tag = getUChar; nwip_universe.store_tag = putUChar; nwip_universe.get_length = getUChar; nwip_universe.store_length = putUChar; nwip_universe.site_code_min = 0; nwip_universe.end = 0; code = DHO_NWIP_SUBOPTIONS; nwip_universe.enc_opt = NULL; if (!option_code_hash_lookup(&nwip_universe.enc_opt, dhcp_universe.code_hash, &code, 0, MDL)) log_fatal("Unable to find NWIP parent option (%s:%d).", MDL); nwip_universe.index = universe_count++; universes [nwip_universe.index] = &nwip_universe; if (!option_name_new_hash(&nwip_universe.name_hash, NWIP_HASH_SIZE, MDL) || !option_code_new_hash(&nwip_universe.code_hash, NWIP_HASH_SIZE, MDL)) log_fatal ("Can't allocate nwip option hash table."); for (i = 0 ; nwip_options[i].name ; i++) { option_code_hash_add(nwip_universe.code_hash, &nwip_options[i].code, 0, &nwip_options[i], MDL); option_name_hash_add(nwip_universe.name_hash, nwip_options[i].name, 0, &nwip_options[i], MDL); } #if defined(REPORT_HASH_PERFORMANCE) log_info("NWIP name hash: %s", option_name_hash_report(nwip_universe.name_hash)); log_info("NWIP code hash: %s", option_code_hash_report(nwip_universe.code_hash)); #endif /* Set up the FQDN option universe... */ fqdn_universe.name = "fqdn"; fqdn_universe.concat_duplicates = 0; fqdn_universe.lookup_func = lookup_linked_option; fqdn_universe.option_state_dereference = linked_option_state_dereference; fqdn_universe.save_func = save_linked_option; fqdn_universe.delete_func = delete_linked_option; fqdn_universe.encapsulate = fqdn_option_space_encapsulate; fqdn_universe.foreach = linked_option_space_foreach; fqdn_universe.decode = fqdn_universe_decode; fqdn_universe.length_size = 1; fqdn_universe.tag_size = 1; fqdn_universe.get_tag = getUChar; fqdn_universe.store_tag = putUChar; fqdn_universe.get_length = getUChar; fqdn_universe.store_length = putUChar; fqdn_universe.site_code_min = 0; fqdn_universe.end = 0; fqdn_universe.index = universe_count++; code = DHO_FQDN; fqdn_universe.enc_opt = NULL; if (!option_code_hash_lookup(&fqdn_universe.enc_opt, dhcp_universe.code_hash, &code, 0, MDL)) log_fatal("Unable to find FQDN parent option (%s:%d).", MDL); universes [fqdn_universe.index] = &fqdn_universe; if (!option_name_new_hash(&fqdn_universe.name_hash, FQDN_HASH_SIZE, MDL) || !option_code_new_hash(&fqdn_universe.code_hash, FQDN_HASH_SIZE, MDL)) log_fatal ("Can't allocate fqdn option hash table."); for (i = 0 ; fqdn_options[i].name ; i++) { option_code_hash_add(fqdn_universe.code_hash, &fqdn_options[i].code, 0, &fqdn_options[i], MDL); option_name_hash_add(fqdn_universe.name_hash, fqdn_options[i].name, 0, &fqdn_options[i], MDL); } #if defined(REPORT_HASH_PERFORMANCE) log_info("FQDN name hash: %s", option_name_hash_report(fqdn_universe.name_hash)); log_info("FQDN code hash: %s", option_code_hash_report(fqdn_universe.code_hash)); #endif /* Set up the Vendor Identified Vendor Class options (for option * 125)... */ vendor_class_universe.name = "vendor-class"; vendor_class_universe.concat_duplicates = 0; /* XXX: reference? */ vendor_class_universe.lookup_func = lookup_hashed_option; vendor_class_universe.option_state_dereference = hashed_option_state_dereference; vendor_class_universe.save_func = save_hashed_option; vendor_class_universe.delete_func = delete_hashed_option; vendor_class_universe.encapsulate = hashed_option_space_encapsulate; vendor_class_universe.foreach = hashed_option_space_foreach; vendor_class_universe.decode = parse_option_buffer; vendor_class_universe.length_size = 1; vendor_class_universe.tag_size = 4; vendor_class_universe.get_tag = getULong; vendor_class_universe.store_tag = putULong; vendor_class_universe.get_length = getUChar; vendor_class_universe.store_length = putUChar; vendor_class_universe.site_code_min = 0; vendor_class_universe.end = 0; code = DHO_VIVCO_SUBOPTIONS; vendor_class_universe.enc_opt = NULL; if (!option_code_hash_lookup(&vendor_class_universe.enc_opt, dhcp_universe.code_hash, &code, 0, MDL)) log_fatal("Unable to find VIVCO parent option (%s:%d).", MDL); vendor_class_universe.index = universe_count++; universes[vendor_class_universe.index] = &vendor_class_universe; if (!option_name_new_hash(&vendor_class_universe.name_hash, VIVCO_HASH_SIZE, MDL) || !option_code_new_hash(&vendor_class_universe.code_hash, VIVCO_HASH_SIZE, MDL)) log_fatal("Can't allocate Vendor Identified Vendor Class " "option hash table."); for (i = 0 ; vendor_class_options[i].name ; i++) { option_code_hash_add(vendor_class_universe.code_hash, &vendor_class_options[i].code, 0, &vendor_class_options[i], MDL); option_name_hash_add(vendor_class_universe.name_hash, vendor_class_options[i].name, 0, &vendor_class_options[i], MDL); } #if defined(REPORT_HASH_PERFORMANCE) log_info("VIVCO name hash: %s", option_name_hash_report(vendor_class_universe.name_hash)); log_info("VIVCO code hash: %s", option_code_hash_report(vendor_class_universe.code_hash)); #endif /* Set up the Vendor Identified Vendor Sub-options (option 126)... */ vendor_universe.name = "vendor"; vendor_universe.concat_duplicates = 0; /* XXX: reference? */ vendor_universe.lookup_func = lookup_hashed_option; vendor_universe.option_state_dereference = hashed_option_state_dereference; vendor_universe.save_func = save_hashed_option; vendor_universe.delete_func = delete_hashed_option; vendor_universe.encapsulate = hashed_option_space_encapsulate; vendor_universe.foreach = hashed_option_space_foreach; vendor_universe.decode = parse_option_buffer; vendor_universe.length_size = 1; vendor_universe.tag_size = 4; vendor_universe.get_tag = getULong; vendor_universe.store_tag = putULong; vendor_universe.get_length = getUChar; vendor_universe.store_length = putUChar; vendor_universe.site_code_min = 0; vendor_universe.end = 0; code = DHO_VIVSO_SUBOPTIONS; vendor_universe.enc_opt = NULL; if (!option_code_hash_lookup(&vendor_universe.enc_opt, dhcp_universe.code_hash, &code, 0, MDL)) log_fatal("Unable to find VIVSO parent option (%s:%d).", MDL); vendor_universe.index = universe_count++; universes[vendor_universe.index] = &vendor_universe; if (!option_name_new_hash(&vendor_universe.name_hash, VIVSO_HASH_SIZE, MDL) || !option_code_new_hash(&vendor_universe.code_hash, VIVSO_HASH_SIZE, MDL)) log_fatal("Can't allocate Vendor Identified Vendor Sub-" "options hash table."); for (i = 0 ; vendor_options[i].name ; i++) { option_code_hash_add(vendor_universe.code_hash, &vendor_options[i].code, 0, &vendor_options[i], MDL); option_name_hash_add(vendor_universe.name_hash, vendor_options[i].name, 0, &vendor_options[i], MDL); } #if defined(REPORT_HASH_PERFORMANCE) log_info("VIVSO name hash: %s", option_name_hash_report(vendor_universe.name_hash)); log_info("VIVSO code hash: %s", option_code_hash_report(vendor_universe.code_hash)); #endif /* Set up the ISC Vendor-option universe (for option 125.2495)... */ isc_universe.name = "isc"; isc_universe.concat_duplicates = 0; /* XXX: check VIVSO ref */ isc_universe.lookup_func = lookup_linked_option; isc_universe.option_state_dereference = linked_option_state_dereference; isc_universe.save_func = save_linked_option; isc_universe.delete_func = delete_linked_option; isc_universe.encapsulate = linked_option_space_encapsulate; isc_universe.foreach = linked_option_space_foreach; isc_universe.decode = parse_option_buffer; isc_universe.length_size = 2; isc_universe.tag_size = 2; isc_universe.get_tag = getUShort; isc_universe.store_tag = putUShort; isc_universe.get_length = getUShort; isc_universe.store_length = putUShort; isc_universe.site_code_min = 0; isc_universe.end = 0; code = VENDOR_ISC_SUBOPTIONS; isc_universe.enc_opt = NULL; if (!option_code_hash_lookup(&isc_universe.enc_opt, vendor_universe.code_hash, &code, 0, MDL)) log_fatal("Unable to find ISC parent option (%s:%d).", MDL); isc_universe.index = universe_count++; universes[isc_universe.index] = &isc_universe; if (!option_name_new_hash(&isc_universe.name_hash, VIV_ISC_HASH_SIZE, MDL) || !option_code_new_hash(&isc_universe.code_hash, VIV_ISC_HASH_SIZE, MDL)) log_fatal("Can't allocate ISC Vendor options hash table."); for (i = 0 ; isc_options[i].name ; i++) { option_code_hash_add(isc_universe.code_hash, &isc_options[i].code, 0, &isc_options[i], MDL); option_name_hash_add(isc_universe.name_hash, isc_options[i].name, 0, &isc_options[i], MDL); } #if defined(REPORT_HASH_PERFORMANCE) log_info("ISC name hash: %s", option_name_hash_report(isc_universe.name_hash)); log_info("ISC code hash: %s", option_code_hash_report(isc_universe.code_hash)); #endif /* Set up the DHCPv6 root universe. */ dhcpv6_universe.name = "dhcp6"; dhcpv6_universe.concat_duplicates = 0; dhcpv6_universe.lookup_func = lookup_hashed_option; dhcpv6_universe.option_state_dereference = hashed_option_state_dereference; dhcpv6_universe.save_func = save_hashed_option; dhcpv6_universe.delete_func = delete_hashed_option; dhcpv6_universe.encapsulate = hashed_option_space_encapsulate; dhcpv6_universe.foreach = hashed_option_space_foreach; dhcpv6_universe.decode = parse_option_buffer; dhcpv6_universe.length_size = 2; dhcpv6_universe.tag_size = 2; dhcpv6_universe.get_tag = getUShort; dhcpv6_universe.store_tag = putUShort; dhcpv6_universe.get_length = getUShort; dhcpv6_universe.store_length = putUShort; dhcpv6_universe.site_code_min = 0; /* DHCPv6 has no END option. */ dhcpv6_universe.end = 0x00; dhcpv6_universe.index = universe_count++; universes[dhcpv6_universe.index] = &dhcpv6_universe; if (!option_name_new_hash(&dhcpv6_universe.name_hash, WORD_NAME_HASH_SIZE, MDL) || !option_code_new_hash(&dhcpv6_universe.code_hash, WORD_CODE_HASH_SIZE, MDL)) log_fatal("Can't allocate dhcpv6 option hash tables."); for (i = 0 ; dhcpv6_options[i].name ; i++) { option_code_hash_add(dhcpv6_universe.code_hash, &dhcpv6_options[i].code, 0, &dhcpv6_options[i], MDL); option_name_hash_add(dhcpv6_universe.name_hash, dhcpv6_options[i].name, 0, &dhcpv6_options[i], MDL); } /* Add DHCPv6 protocol enumeration sets. */ add_enumeration(&dhcpv6_duid_types); add_enumeration(&dhcpv6_status_codes); add_enumeration(&dhcpv6_messages); /* Set up DHCPv6 VSIO universe. */ vsio_universe.name = "vsio"; vsio_universe.concat_duplicates = 0; vsio_universe.lookup_func = lookup_hashed_option; vsio_universe.option_state_dereference = hashed_option_state_dereference; vsio_universe.save_func = save_hashed_option; vsio_universe.delete_func = delete_hashed_option; vsio_universe.encapsulate = hashed_option_space_encapsulate; vsio_universe.foreach = hashed_option_space_foreach; vsio_universe.decode = parse_option_buffer; vsio_universe.length_size = 0; vsio_universe.tag_size = 4; vsio_universe.get_tag = getULong; vsio_universe.store_tag = putULong; vsio_universe.get_length = NULL; vsio_universe.store_length = NULL; vsio_universe.site_code_min = 0; /* No END option. */ vsio_universe.end = 0x00; code = D6O_VENDOR_OPTS; if (!option_code_hash_lookup(&vsio_universe.enc_opt, dhcpv6_universe.code_hash, &code, 0, MDL)) log_fatal("Unable to find VSIO parent option (%s:%d).", MDL); vsio_universe.index = universe_count++; universes[vsio_universe.index] = &vsio_universe; if (!option_name_new_hash(&vsio_universe.name_hash, VSIO_HASH_SIZE, MDL) || !option_code_new_hash(&vsio_universe.code_hash, VSIO_HASH_SIZE, MDL)) log_fatal("Can't allocate Vendor Specific Information " "Options space."); for (i = 0 ; vsio_options[i].name != NULL ; i++) { option_code_hash_add(vsio_universe.code_hash, &vsio_options[i].code, 0, &vsio_options[i], MDL); option_name_hash_add(vsio_universe.name_hash, vsio_options[i].name, 0, &vsio_options[i], MDL); } /* Add ISC VSIO sub-sub-option space. */ isc6_universe.name = "isc6"; isc6_universe.concat_duplicates = 0; isc6_universe.lookup_func = lookup_hashed_option; isc6_universe.option_state_dereference = hashed_option_state_dereference; isc6_universe.save_func = save_hashed_option; isc6_universe.delete_func = delete_hashed_option; isc6_universe.encapsulate = hashed_option_space_encapsulate; isc6_universe.foreach = hashed_option_space_foreach; isc6_universe.decode = parse_option_buffer; isc6_universe.length_size = 0; isc6_universe.tag_size = 4; isc6_universe.get_tag = getULong; isc6_universe.store_tag = putULong; isc6_universe.get_length = NULL; isc6_universe.store_length = NULL; isc6_universe.site_code_min = 0; /* No END option. */ isc6_universe.end = 0x00; code = 2495; if (!option_code_hash_lookup(&isc6_universe.enc_opt, vsio_universe.code_hash, &code, 0, MDL)) log_fatal("Unable to find ISC parent option (%s:%d).", MDL); isc6_universe.index = universe_count++; universes[isc6_universe.index] = &isc6_universe; if (!option_name_new_hash(&isc6_universe.name_hash, VIV_ISC_HASH_SIZE, MDL) || !option_code_new_hash(&isc6_universe.code_hash, VIV_ISC_HASH_SIZE, MDL)) log_fatal("Can't allocate Vendor Specific Information " "Options space."); for (i = 0 ; isc6_options[i].name != NULL ; i++) { option_code_hash_add(isc6_universe.code_hash, &isc6_options[i].code, 0, &isc6_options[i], MDL); option_name_hash_add(isc6_universe.name_hash, isc6_options[i].name, 0, &isc6_options[i], MDL); } /* The fqdn6 option space is a protocol-wrapper shill for the * old DHCPv4 space. */ fqdn6_universe.name = "fqdn6-if-you-see-me-its-a-bug-bug-bug"; fqdn6_universe.lookup_func = lookup_fqdn6_option; fqdn6_universe.option_state_dereference = NULL; /* Covered by v4. */ fqdn6_universe.save_func = save_fqdn6_option; fqdn6_universe.delete_func = delete_fqdn6_option; fqdn6_universe.encapsulate = fqdn6_option_space_encapsulate; fqdn6_universe.foreach = fqdn6_option_space_foreach; fqdn6_universe.decode = fqdn6_universe_decode; /* This is not a 'normal' encapsulated space, so these values are * meaningless. */ fqdn6_universe.length_size = 0; fqdn6_universe.tag_size = 0; fqdn6_universe.get_tag = NULL; fqdn6_universe.store_tag = NULL; fqdn6_universe.get_length = NULL; fqdn6_universe.store_length = NULL; fqdn6_universe.site_code_min = 0; fqdn6_universe.end = 0; fqdn6_universe.index = universe_count++; code = D6O_CLIENT_FQDN; fqdn6_universe.enc_opt = NULL; if (!option_code_hash_lookup(&fqdn6_universe.enc_opt, dhcpv6_universe.code_hash, &code, 0, MDL)) log_fatal("Unable to find FQDN v6 parent option. (%s:%d).", MDL); universes[fqdn6_universe.index] = &fqdn6_universe; /* The fqdn6 space shares the same option space as the v4 space. * So there are no name or code hashes on the v6 side. */ fqdn6_universe.name_hash = NULL; fqdn6_universe.code_hash = NULL; /* Set up the hash of DHCPv4 universes. */ universe_new_hash(&universe_hash, UNIVERSE_HASH_SIZE, MDL); universe_hash_add(universe_hash, dhcp_universe.name, 0, &dhcp_universe, MDL); universe_hash_add(universe_hash, nwip_universe.name, 0, &nwip_universe, MDL); universe_hash_add(universe_hash, fqdn_universe.name, 0, &fqdn_universe, MDL); universe_hash_add(universe_hash, vendor_class_universe.name, 0, &vendor_class_universe, MDL); universe_hash_add(universe_hash, vendor_universe.name, 0, &vendor_universe, MDL); universe_hash_add(universe_hash, isc_universe.name, 0, &isc_universe, MDL); /* Set up hashes for DHCPv6 universes. */ universe_hash_add(universe_hash, dhcpv6_universe.name, 0, &dhcpv6_universe, MDL); universe_hash_add(universe_hash, vsio_universe.name, 0, &vsio_universe, MDL); universe_hash_add(universe_hash, isc6_universe.name, 0, &isc6_universe, MDL); /* previously this wasn't necessary, now that we can send * v6 encapsulated options it is. */ universe_hash_add(universe_hash, fqdn6_universe.name, 0, &fqdn6_universe, MDL); } dhcp-4.4.1/common/tests/000755 000765 000024 00000000000 13243313033 015344 5ustar00tmarkstaff000000 000000 dhcp-4.4.1/common/tr.c000644 000765 000024 00000025676 13243301226 015014 0ustar00tmarkstaff000000 000000 /* tr.c token ring interface support Contributed in May of 1999 by Andrew Chittenden */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1996-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ */ #include "dhcpd.h" #if defined (HAVE_TR_SUPPORT) && \ (defined (PACKET_ASSEMBLY) || defined (PACKET_DECODING)) #include "includes/netinet/ip.h" #include "includes/netinet/udp.h" #include "includes/netinet/if_ether.h" #include "netinet/if_tr.h" #include /* * token ring device handling subroutines. These are required as token-ring * does not have a simple on-the-wire header but requires the use of * source routing */ static int insert_source_routing (struct trh_hdr *trh, struct interface_info* interface); static void save_source_routing (struct trh_hdr *trh, struct interface_info* interface); static void expire_routes (void); /* * As we keep a list of interesting routing information only, a singly * linked list is all we need */ struct routing_entry { struct routing_entry *next; unsigned char addr[TR_ALEN]; unsigned char iface[5]; u_int16_t rcf; /* route control field */ u_int16_t rseg[8]; /* routing registers */ unsigned long access_time; /* time we last used this entry */ }; static struct routing_entry *routing_info = NULL; static int routing_timeout = 10; static struct timeval routing_timer; void assemble_tr_header (interface, buf, bufix, to) struct interface_info *interface; unsigned char *buf; unsigned *bufix; struct hardware *to; { struct trh_hdr *trh; int hdr_len; struct trllc *llc; /* set up the token header */ trh = (struct trh_hdr *) &buf[*bufix]; if (interface -> hw_address.hlen - 1 == sizeof (trh->saddr)) memcpy (trh->saddr, &interface -> hw_address.hbuf [1], sizeof (trh->saddr)); else memset (trh->saddr, 0x00, sizeof (trh->saddr)); if (to && to -> hlen == 7) /* XXX */ memcpy (trh->daddr, &to -> hbuf [1], sizeof trh->daddr); else memset (trh->daddr, 0xff, sizeof (trh->daddr)); hdr_len = insert_source_routing (trh, interface); trh->ac = AC; trh->fc = LLC_FRAME; /* set up the llc header for snap encoding after the tr header */ llc = (struct trllc *)(buf + *bufix + hdr_len); llc->dsap = EXTENDED_SAP; llc->ssap = EXTENDED_SAP; llc->llc = UI_CMD; llc->protid[0] = 0; llc->protid[1] = 0; llc->protid[2] = 0; llc->ethertype = htons(ETHERTYPE_IP); hdr_len += sizeof(struct trllc); *bufix += hdr_len; } static unsigned char tr_broadcast[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; /* * decoding the token header is a bit complex as you can see here. It is * further complicated by the linux kernel stripping off some valuable * information (see comment below) even though we've asked for the raw * packets. */ ssize_t decode_tr_header (interface, buf, bufix, from) struct interface_info *interface; unsigned char *buf; unsigned bufix; struct hardware *from; { struct trh_hdr *trh = (struct trh_hdr *) buf + bufix; struct trllc *llc; struct ip *ip; struct udphdr *udp; unsigned int route_len = 0; ssize_t hdr_len; struct timeval now; /* see whether any source routing information has expired */ gettimeofday(&now, NULL); if (routing_timer.tv_sec == 0) routing_timer.tv_sec = now.tv_sec + routing_timeout; else if ((now.tv_sec - routing_timer.tv_sec) > 0) expire_routes(); /* the kernel might have stripped off the source * routing bit. We try a heuristic to determine whether * this is the case and put it back on if so */ route_len = (ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8; llc = (struct trllc *)(buf + bufix + sizeof(struct trh_hdr)-TR_MAXRIFLEN+route_len); if (llc->dsap == EXTENDED_SAP && llc->ssap == EXTENDED_SAP && llc->llc == UI_CMD && llc->protid[0] == 0 && llc->protid[1] == 0 && llc->protid[2] == 0) { /* say there is source routing information present */ trh->saddr[0] |= TR_RII; } if (trh->saddr[0] & TR_RII) route_len = (ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8; else route_len = 0; hdr_len = sizeof (struct trh_hdr) - TR_MAXRIFLEN + route_len; /* now filter out unwanted packets: this is based on the packet * filter code in bpf.c */ llc = (struct trllc *)(buf + bufix + hdr_len); ip = (struct ip *) (llc + 1); udp = (struct udphdr *) ((unsigned char*) ip + IP_HL (ip)); /* make sure it is a snap encoded, IP, UDP, unfragmented packet sent * to our port */ if (llc->dsap != EXTENDED_SAP || ntohs(llc->ethertype) != ETHERTYPE_IP || ip->ip_p != IPPROTO_UDP || (ntohs (ip->ip_off) & IP_OFFMASK) != 0 || udp->uh_dport != local_port) return -1; /* only save source routing information for packets from valued hosts */ save_source_routing(trh, interface); return hdr_len + sizeof (struct trllc); } /* insert_source_routing inserts source route information into the token ring * header */ static int insert_source_routing (trh, interface) struct trh_hdr *trh; struct interface_info* interface; { struct routing_entry *rover; struct timeval now; unsigned int route_len = 0; gettimeofday(&now, NULL); /* single route broadcasts as per rfc 1042 */ if (memcmp(trh->daddr, tr_broadcast,TR_ALEN) == 0) { trh->saddr[0] |= TR_RII; trh->rcf = ((sizeof(trh->rcf)) << 8) & TR_RCF_LEN_MASK; trh->rcf |= (TR_RCF_FRAME2K | TR_RCF_LIMITED_BROADCAST); trh->rcf = htons(trh->rcf); } else { /* look for a routing entry */ for (rover = routing_info; rover != NULL; rover = rover->next) { if (memcmp(rover->addr, trh->daddr, TR_ALEN) == 0) break; } if (rover != NULL) { /* success: route that frame */ if ((rover->rcf & TR_RCF_LEN_MASK) >> 8) { u_int16_t rcf = rover->rcf; memcpy(trh->rseg,rover->rseg,sizeof(trh->rseg)); rcf ^= TR_RCF_DIR_BIT; rcf &= ~TR_RCF_BROADCAST_MASK; trh->rcf = htons(rcf); trh->saddr[0] |= TR_RII; } rover->access_time = now.tv_sec; } else { /* we don't have any routing information so send a * limited broadcast */ trh->saddr[0] |= TR_RII; trh->rcf = ((sizeof(trh->rcf)) << 8) & TR_RCF_LEN_MASK; trh->rcf |= (TR_RCF_FRAME2K | TR_RCF_LIMITED_BROADCAST); trh->rcf = htons(trh->rcf); } } /* return how much of the header we've actually used */ if (trh->saddr[0] & TR_RII) route_len = (ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8; else route_len = 0; return sizeof (struct trh_hdr) - TR_MAXRIFLEN + route_len; } /* * save any source routing information */ static void save_source_routing(trh, interface) struct trh_hdr *trh; struct interface_info *interface; { struct routing_entry *rover; struct timeval now; unsigned char saddr[TR_ALEN]; u_int16_t rcf = 0; gettimeofday(&now, NULL); memcpy(saddr, trh->saddr, sizeof(saddr)); saddr[0] &= 0x7f; /* strip off source routing present flag */ /* scan our table to see if we've got it */ for (rover = routing_info; rover != NULL; rover = rover->next) { if (memcmp(&rover->addr[0], &saddr[0], TR_ALEN) == 0) break; } /* found an entry so update it with fresh information */ if (rover != NULL) { if ((trh->saddr[0] & TR_RII) && ((ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8) > 2) { rcf = ntohs(trh->rcf); rcf &= ~TR_RCF_BROADCAST_MASK; memcpy(rover->rseg, trh->rseg, sizeof(rover->rseg)); } rover->rcf = rcf; rover->access_time = now.tv_sec; return; /* that's all folks */ } /* no entry found, so create one */ rover = dmalloc (sizeof (struct routing_entry), MDL); if (rover == NULL) { fprintf(stderr, "%s: unable to save source routing information\n", __FILE__); return; } memcpy(rover->addr, saddr, sizeof(rover->addr)); memcpy(rover->iface, interface->name, 5); rover->access_time = now.tv_sec; if (trh->saddr[0] & TR_RII) { if (((ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8) > 2) { rcf = ntohs(trh->rcf); rcf &= ~TR_RCF_BROADCAST_MASK; memcpy(rover->rseg, trh->rseg, sizeof(rover->rseg)); } rover->rcf = rcf; } /* insert into list */ rover->next = routing_info; routing_info = rover; return; } /* * get rid of old routes */ static void expire_routes() { struct routing_entry *rover; struct routing_entry **prover = &routing_info; struct timeval now; gettimeofday(&now, NULL); while((rover = *prover) != NULL) { if ((now.tv_sec - rover->access_time) > routing_timeout) { *prover = rover->next; dfree (rover, MDL); } else prover = &rover->next; } /* Reset the timer */ routing_timer.tv_sec = now.tv_sec + routing_timeout; routing_timer.tv_usec = now.tv_usec; } #endif dhcp-4.4.1/common/tree.c000644 000765 000024 00000337442 13243301226 015323 0ustar00tmarkstaff000000 000000 /* tree.c Routines for manipulating parse trees... */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1995-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #include #include #include #ifdef HAVE_REGEX_H # include #endif struct binding_scope *global_scope; static int do_host_lookup (struct data_string *, struct dns_host_entry *); #define DS_SPRINTF_SIZE 128 /* * If we are using a data_string structure to hold a NUL-terminated * ASCII string, this function can be used to append a printf-formatted * string to the end of it. The data_string structure will be resized to * be big enough to hold the new string. * * If the append works, then 1 is returned. * * If it is not possible to allocate a buffer big enough to hold the * new value, then the old data_string is unchanged, and 0 is returned. */ int data_string_sprintfa(struct data_string *ds, const char *fmt, ...) { va_list args; int cur_strlen; int max; int vsnprintf_ret; int new_len; struct buffer *tmp_buffer; /* * If the data_string is empty, then initialize it. */ if (ds->data == NULL) { /* INSIST(ds.buffer == NULL); */ if (!buffer_allocate(&ds->buffer, DS_SPRINTF_SIZE, MDL)) { return 0; } ds->data = ds->buffer->data; ds->len = DS_SPRINTF_SIZE; *((char *)ds->data) = '\0'; } /* * Get the length of the string, and figure out how much space * is left. */ cur_strlen = strlen((char *)ds->data); max = ds->len - cur_strlen; /* * Use vsnprintf(), which won't write past our space, but will * tell us how much space it wants. */ va_start(args, fmt); vsnprintf_ret = vsnprintf((char *)ds->data+cur_strlen, max, fmt, args); va_end(args); /* INSIST(vsnprintf_ret >= 0); */ /* * If our buffer is not big enough, we need a new buffer. */ if (vsnprintf_ret >= max) { /* * Figure out a size big enough. */ new_len = ds->len * 2; while (new_len <= cur_strlen + vsnprintf_ret) { new_len *= 2; } /* * Create a new buffer and fill it. */ tmp_buffer = NULL; if (!buffer_allocate(&tmp_buffer, new_len, MDL)) { /* * If we can't create a big enough buffer, * we should remove any truncated output that we had. */ *((char *)ds->data+cur_strlen) = '\0'; va_end(args); return 0; } memcpy(tmp_buffer->data, ds->data, cur_strlen); /* Rerun the vsprintf. */ va_start(args, fmt); vsprintf((char *)tmp_buffer->data + cur_strlen, fmt, args); va_end(args); /* * Replace our old buffer with the new buffer. */ buffer_dereference(&ds->buffer, MDL); buffer_reference(&ds->buffer, tmp_buffer, MDL); buffer_dereference(&tmp_buffer, MDL); ds->data = ds->buffer->data; ds->len = new_len; } return 1; } pair cons (car, cdr) caddr_t car; pair cdr; { pair foo = (pair)dmalloc (sizeof *foo, MDL); if (!foo) log_fatal ("no memory for cons."); foo -> car = car; foo -> cdr = cdr; return foo; } int make_const_option_cache (oc, buffer, data, len, option, file, line) struct option_cache **oc; struct buffer **buffer; u_int8_t *data; unsigned len; struct option *option; const char *file; int line; { struct buffer *bp; if (buffer) { bp = *buffer; *buffer = 0; } else { bp = (struct buffer *)0; if (!buffer_allocate (&bp, len, file, line)) { log_error ("%s(%d): can't allocate buffer.", file, line); return 0; } } if (!option_cache_allocate (oc, file, line)) { log_error ("%s(%d): can't allocate option cache.", file, line); buffer_dereference (&bp, file, line); return 0; } (*oc) -> data.len = len; (*oc) -> data.buffer = bp; (*oc) -> data.data = &bp -> data [0]; (*oc) -> data.terminated = 0; if (data) memcpy (&bp -> data [0], data, len); option_reference(&((*oc)->option), option, MDL); return 1; } int make_host_lookup (expr, name) struct expression **expr; const char *name; { if (!expression_allocate (expr, MDL)) { log_error ("No memory for host lookup tree node."); return 0; } (*expr) -> op = expr_host_lookup; if (!enter_dns_host (&((*expr) -> data.host_lookup), name)) { expression_dereference (expr, MDL); return 0; } return 1; } int enter_dns_host (dh, name) struct dns_host_entry **dh; const char *name; { /* XXX This should really keep a hash table of hostnames XXX and just add a new reference to a hostname that XXX already exists, if possible, rather than creating XXX a new structure. */ if (!dns_host_entry_allocate (dh, name, MDL)) { log_error ("Can't allocate space for new host."); return 0; } return 1; } int make_const_data (struct expression **expr, const unsigned char *data, unsigned len, int terminated, int allocate, const char *file, int line) { struct expression *nt; if (!expression_allocate (expr, file, line)) { log_error ("No memory for make_const_data tree node."); return 0; } nt = *expr; if (len) { if (allocate) { if (!buffer_allocate (&nt -> data.const_data.buffer, len + terminated, file, line)) { log_error ("Can't allocate const_data buffer"); expression_dereference (expr, file, line); return 0; } nt -> data.const_data.data = &nt -> data.const_data.buffer -> data [0]; memcpy (nt -> data.const_data.buffer -> data, data, len + terminated); } else nt -> data.const_data.data = data; nt -> data.const_data.terminated = terminated; } else nt -> data.const_data.data = 0; nt -> op = expr_const_data; nt -> data.const_data.len = len; return 1; } int make_const_int (expr, val) struct expression **expr; unsigned long val; { if (!expression_allocate (expr, MDL)) { log_error ("No memory for make_const_int tree node."); return 0; } (*expr) -> op = expr_const_int; (*expr) -> data.const_int = val; return 1; } int make_concat (expr, left, right) struct expression **expr; struct expression *left, *right; { /* If we're concatenating a null tree to a non-null tree, just return the non-null tree; if both trees are null, return a null tree. */ if (!left) { if (!right) return 0; expression_reference (expr, right, MDL); return 1; } if (!right) { expression_reference (expr, left, MDL); return 1; } /* Otherwise, allocate a new node to concatenate the two. */ if (!expression_allocate (expr, MDL)) { log_error ("No memory for concatenation expression node."); return 0; } (*expr) -> op = expr_concat; expression_reference (&(*expr) -> data.concat [0], left, MDL); expression_reference (&(*expr) -> data.concat [1], right, MDL); return 1; } int make_encapsulation (expr, name) struct expression **expr; struct data_string *name; { /* Allocate a new node to store the encapsulation. */ if (!expression_allocate (expr, MDL)) { log_error ("No memory for encapsulation expression node."); return 0; } (*expr) -> op = expr_encapsulate; data_string_copy (&(*expr) -> data.encapsulate, name, MDL); return 1; } int make_substring (new, expr, offset, length) struct expression **new; struct expression *expr; struct expression *offset; struct expression *length; { /* Allocate an expression node to compute the substring. */ if (!expression_allocate (new, MDL)) { log_error ("no memory for substring expression."); return 0; } (*new) -> op = expr_substring; expression_reference (&(*new) -> data.substring.expr, expr, MDL); expression_reference (&(*new) -> data.substring.offset, offset, MDL); expression_reference (&(*new) -> data.substring.len, length, MDL); return 1; } int make_limit (new, expr, limit) struct expression **new; struct expression *expr; int limit; { /* Allocate a node to enforce a limit on evaluation. */ if (!expression_allocate (new, MDL)) log_error ("no memory for limit expression"); (*new) -> op = expr_substring; expression_reference (&(*new) -> data.substring.expr, expr, MDL); /* Offset is a constant 0. */ if (!expression_allocate (&(*new) -> data.substring.offset, MDL)) { log_error ("no memory for limit offset expression"); expression_dereference (new, MDL); return 0; } (*new) -> data.substring.offset -> op = expr_const_int; (*new) -> data.substring.offset -> data.const_int = 0; /* Length is a constant: the specified limit. */ if (!expression_allocate (&(*new) -> data.substring.len, MDL)) { log_error ("no memory for limit length expression"); expression_dereference (new, MDL); return 0; } (*new) -> data.substring.len -> op = expr_const_int; (*new) -> data.substring.len -> data.const_int = limit; return 1; } int option_cache (struct option_cache **oc, struct data_string *dp, struct expression *expr, struct option *option, const char *file, int line) { if (!option_cache_allocate (oc, file, line)) return 0; if (dp) data_string_copy (&(*oc) -> data, dp, file, line); if (expr) expression_reference (&(*oc) -> expression, expr, file, line); option_reference(&(*oc)->option, option, MDL); return 1; } int make_let (result, name) struct executable_statement **result; const char *name; { if (!(executable_statement_allocate (result, MDL))) return 0; (*result) -> op = let_statement; (*result) -> data.let.name = dmalloc (strlen (name) + 1, MDL); if (!(*result) -> data.let.name) { executable_statement_dereference (result, MDL); return 0; } strcpy ((*result) -> data.let.name, name); return 1; } static int do_host_lookup (result, dns) struct data_string *result; struct dns_host_entry *dns; { struct hostent *h; unsigned i, count; unsigned new_len; #ifdef DEBUG_EVAL log_debug ("time: now = %d dns = %d diff = %d", cur_time, dns -> timeout, cur_time - dns -> timeout); #endif /* If the record hasn't timed out, just copy the data and return. */ if (cur_time <= dns -> timeout) { #ifdef DEBUG_EVAL log_debug ("easy copy: %d %s", dns -> data.len, (dns -> data.len > 4 ? inet_ntoa (*(struct in_addr *)(dns -> data.data)) : 0)); #endif data_string_copy (result, &dns -> data, MDL); return 1; } #ifdef DEBUG_EVAL log_debug ("Looking up %s", dns -> hostname); #endif /* Otherwise, look it up... */ h = gethostbyname (dns -> hostname); if (!h) { #ifndef NO_H_ERRNO switch (h_errno) { case HOST_NOT_FOUND: #endif log_error ("%s: host unknown.", dns -> hostname); #ifndef NO_H_ERRNO break; case TRY_AGAIN: log_error ("%s: temporary name server failure", dns -> hostname); break; case NO_RECOVERY: log_error ("%s: name server failed", dns -> hostname); break; case NO_DATA: log_error ("%s: no A record associated with address", dns -> hostname); } #endif /* !NO_H_ERRNO */ /* Okay to try again after a minute. */ dns -> timeout = cur_time + 60; data_string_forget (&dns -> data, MDL); return 0; } #ifdef DEBUG_EVAL log_debug ("Lookup succeeded; first address is %s", inet_ntoa (h -> h_addr_list [0])); #endif /* Count the number of addresses we got... */ for (count = 0; h -> h_addr_list [count]; count++) ; /* Dereference the old data, if any. */ data_string_forget (&dns -> data, MDL); /* Do we need to allocate more memory? */ new_len = count * h -> h_length; if (!buffer_allocate (&dns -> data.buffer, new_len, MDL)) { log_error ("No memory for %s.", dns -> hostname); return 0; } dns -> data.data = &dns -> data.buffer -> data [0]; dns -> data.len = new_len; dns -> data.terminated = 0; /* Addresses are conveniently stored one to the buffer, so we have to copy them out one at a time... :'( */ for (i = 0; i < count; i++) { memcpy (&dns -> data.buffer -> data [h -> h_length * i], h -> h_addr_list [i], (unsigned)(h -> h_length)); } #ifdef DEBUG_EVAL log_debug ("dns -> data: %x h -> h_addr_list [0]: %x", *(int *)(dns -> buffer), h -> h_addr_list [0]); #endif /* XXX Set the timeout for an hour from now. XXX This should really use the time on the DNS reply. */ dns -> timeout = cur_time + 3600; #ifdef DEBUG_EVAL log_debug ("hard copy: %d %s", dns -> data.len, (dns -> data.len > 4 ? inet_ntoa (*(struct in_addr *)(dns -> data.data)) : 0)); #endif data_string_copy (result, &dns -> data, MDL); return 1; } int evaluate_expression (result, packet, lease, client_state, in_options, cfg_options, scope, expr, file, line) struct binding_value **result; struct packet *packet; struct lease *lease; struct client_state *client_state; struct option_state *in_options; struct option_state *cfg_options; struct binding_scope **scope; struct expression *expr; const char *file; int line; { struct binding_value *bv; int status; struct binding *binding; bv = (struct binding_value *)0; if (expr -> op == expr_variable_reference) { if (!scope || !*scope) return 0; binding = find_binding (*scope, expr -> data.variable); if (binding && binding -> value) { if (result) binding_value_reference (result, binding -> value, file, line); return 1; } else return 0; } else if (expr -> op == expr_funcall) { struct string_list *s; struct expression *arg; struct binding_scope *ns; struct binding *nb; if (!scope || !*scope) { log_error ("%s: no such function.", expr -> data.funcall.name); return 0; } binding = find_binding (*scope, expr -> data.funcall.name); if (!binding || !binding -> value) { log_error ("%s: no such function.", expr -> data.funcall.name); return 0; } if (binding -> value -> type != binding_function) { log_error ("%s: not a function.", expr -> data.funcall.name); return 0; } /* Create a new binding scope in which to define the arguments to the function. */ ns = (struct binding_scope *)0; if (!binding_scope_allocate (&ns, MDL)) { log_error ("%s: can't allocate argument scope.", expr -> data.funcall.name); return 0; } arg = expr -> data.funcall.arglist; s = binding -> value -> value.fundef -> args; while (arg && s) { nb = dmalloc (sizeof *nb, MDL); if (!nb) { blb: binding_scope_dereference (&ns, MDL); return 0; } else { memset (nb, 0, sizeof *nb); nb -> name = dmalloc (strlen (s -> string) + 1, MDL); if (nb -> name) strcpy (nb -> name, s -> string); else { dfree (nb, MDL); goto blb; } } evaluate_expression (&nb -> value, packet, lease, client_state, in_options, cfg_options, scope, arg -> data.arg.val, file, line); nb -> next = ns -> bindings; ns -> bindings = nb; arg = arg -> data.arg.next; s = s -> next; } if (arg) { log_error ("%s: too many arguments.", expr -> data.funcall.name); binding_scope_dereference (&ns, MDL); return 0; } if (s) { log_error ("%s: too few arguments.", expr -> data.funcall.name); binding_scope_dereference (&ns, MDL); return 0; } if (scope && *scope) binding_scope_reference (&ns -> outer, *scope, MDL); status = (execute_statements (&bv, packet, lease, client_state, in_options, cfg_options, &ns, binding->value->value.fundef->statements, NULL)); binding_scope_dereference (&ns, MDL); if (!bv) return 1; } else if (is_boolean_expression (expr)) { if (!binding_value_allocate (&bv, MDL)) return 0; bv -> type = binding_boolean; status = (evaluate_boolean_expression (&bv -> value.boolean, packet, lease, client_state, in_options, cfg_options, scope, expr)); } else if (is_numeric_expression (expr)) { if (!binding_value_allocate (&bv, MDL)) return 0; bv -> type = binding_numeric; status = (evaluate_numeric_expression (&bv -> value.intval, packet, lease, client_state, in_options, cfg_options, scope, expr)); } else if (is_data_expression (expr)) { if (!binding_value_allocate (&bv, MDL)) return 0; bv -> type = binding_data; status = (evaluate_data_expression (&bv -> value.data, packet, lease, client_state, in_options, cfg_options, scope, expr, MDL)); } else { log_error ("%s: invalid expression type: %d", "evaluate_expression", expr -> op); return 0; } if (result && status) binding_value_reference (result, bv, file, line); binding_value_dereference (&bv, MDL); return status; } int binding_value_dereference (struct binding_value **v, const char *file, int line) { struct binding_value *bv = *v; *v = (struct binding_value *)0; /* Decrement the reference count. If it's nonzero, we're done. */ --(bv -> refcnt); rc_register (file, line, v, bv, bv -> refcnt, 1, RC_MISC); if (bv -> refcnt > 0) return 1; if (bv -> refcnt < 0) { log_error ("%s(%d): negative refcnt!", file, line); #if defined (DEBUG_RC_HISTORY) dump_rc_history (bv); #endif #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } switch (bv -> type) { case binding_boolean: case binding_numeric: break; case binding_data: if (bv -> value.data.buffer) data_string_forget (&bv -> value.data, file, line); break; default: log_error ("%s(%d): invalid binding type: %d", file, line, bv -> type); return 0; } free_binding_value(bv, file, line); return 1; } int evaluate_boolean_expression (result, packet, lease, client_state, in_options, cfg_options, scope, expr) int *result; struct packet *packet; struct lease *lease; struct client_state *client_state; struct option_state *in_options; struct option_state *cfg_options; struct binding_scope **scope; struct expression *expr; { struct data_string left, right; int bleft, bright; int sleft, sright; struct binding *binding; struct binding_value *bv, *obv; #ifdef HAVE_REGEX_H int regflags = REG_EXTENDED | REG_NOSUB; regex_t re; #endif switch (expr -> op) { case expr_check: *result = check_collection (packet, lease, expr -> data.check); #if defined (DEBUG_EXPRESSIONS) log_debug ("bool: check (%s) returns %s", expr -> data.check -> name, *result ? "true" : "false"); #endif return 1; case expr_equal: case expr_not_equal: bv = obv = (struct binding_value *)0; sleft = evaluate_expression (&bv, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.equal [0], MDL); sright = evaluate_expression (&obv, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.equal [1], MDL); if (sleft && sright) { if (bv -> type != obv -> type) *result = expr -> op == expr_not_equal; else { switch (obv -> type) { case binding_boolean: if (bv -> value.boolean == obv -> value.boolean) *result = expr -> op == expr_equal; else *result = expr -> op == expr_not_equal; break; case binding_data: if ((bv -> value.data.len == obv -> value.data.len) && !memcmp (bv -> value.data.data, obv -> value.data.data, obv -> value.data.len)) *result = expr -> op == expr_equal; else *result = expr -> op == expr_not_equal; break; case binding_numeric: if (bv -> value.intval == obv -> value.intval) *result = expr -> op == expr_equal; else *result = expr -> op == expr_not_equal; break; case binding_function: if (bv -> value.fundef == obv -> value.fundef) *result = expr -> op == expr_equal; else *result = expr -> op == expr_not_equal; break; default: *result = expr -> op == expr_not_equal; break; } } } else if (!sleft && !sright) *result = expr -> op == expr_equal; else *result = expr -> op == expr_not_equal; #if defined (DEBUG_EXPRESSIONS) log_debug ("bool: %sequal = %s", expr -> op == expr_not_equal ? "not" : "", (*result ? "true" : "false")); #endif if (sleft) binding_value_dereference (&bv, MDL); if (sright) binding_value_dereference (&obv, MDL); return 1; case expr_iregex_match: #ifdef HAVE_REGEX_H regflags |= REG_ICASE; #endif /* FALL THROUGH */ case expr_regex_match: #ifdef HAVE_REGEX_H memset(&left, 0, sizeof left); bleft = evaluate_data_expression(&left, packet, lease, client_state, in_options, cfg_options, scope, expr->data.equal[0], MDL); /* This is annoying, regexec requires the string being processed * to be NULL terminated, but left may not be, so pass it into * the termination function to ensure it's null terminated. */ if (bleft && (data_string_terminate(&left, MDL) == 0)) { /* failed to make a null terminated version, couldn't * create a copy, probably a memory issue, an error * message has already been logged */ bleft = 0; } memset(&right, 0, sizeof right); bright = evaluate_data_expression(&right, packet, lease, client_state, in_options, cfg_options, scope, expr->data.equal[1], MDL); *result = 0; memset(&re, 0, sizeof(re)); if (bleft && bright && (left.data != NULL) && (right.data != NULL) && (regcomp(&re, (char *)right.data, regflags) == 0) && (regexec(&re, (char *)left.data, (size_t)0, NULL, 0) == 0)) *result = 1; #if defined (DEBUG_EXPRESSIONS) log_debug("bool: %s ~= %s yields %s", bleft ? print_hex_1(left.len, left.data, 20) : "NULL", bright ? print_hex_2 (right.len, right.data, 20) : "NULL", *result ? "true" : "false"); #endif if (bleft) data_string_forget(&left, MDL); if (bright) data_string_forget(&right, MDL); regfree(&re); /* * If we have bleft and bright then we have a good * syntax, otherwise not. * * XXX: we don't warn on invalid regular expression * syntax, should we? */ return bleft && bright; #else /* It shouldn't be possible to configure a regex operator * when there's no support. */ log_fatal("Impossible condition at %s:%d.", MDL); break; #endif case expr_and: sleft = evaluate_boolean_expression (&bleft, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.and [0]); if (sleft && bleft) sright = evaluate_boolean_expression (&bright, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.and [1]); else sright = bright = 0; #if defined (DEBUG_EXPRESSIONS) log_debug ("bool: and (%s, %s) = %s", sleft ? (bleft ? "true" : "false") : "NULL", sright ? (bright ? "true" : "false") : "NULL", ((sleft && sright) ? (bleft && bright ? "true" : "false") : "NULL")); #endif if (sleft && sright) { *result = bleft && bright; return 1; } return 0; case expr_or: bleft = bright = 0; sleft = evaluate_boolean_expression (&bleft, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.or [0]); if (!sleft || !bleft) sright = evaluate_boolean_expression (&bright, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.or [1]); else sright = 0; #if defined (DEBUG_EXPRESSIONS) log_debug ("bool: or (%s, %s) = %s", sleft ? (bleft ? "true" : "false") : "NULL", sright ? (bright ? "true" : "false") : "NULL", ((sleft || sright) ? (bleft || bright ? "true" : "false") : "NULL")); #endif if (sleft || sright) { *result = bleft || bright; return 1; } return 0; case expr_not: sleft = evaluate_boolean_expression(&bleft, packet, lease, client_state, in_options, cfg_options, scope, expr->data.not); #if defined (DEBUG_EXPRESSIONS) log_debug("bool: not (%s) = %s", sleft ? (bleft ? "true" : "false") : "NULL", sleft ? (!bleft ? "true" : "false") : "NULL"); #endif if (sleft) { *result = !bleft; return 1; } return 0; case expr_exists: memset (&left, 0, sizeof left); if (!in_options || !get_option (&left, expr -> data.exists -> universe, packet, lease, client_state, in_options, cfg_options, in_options, scope, expr -> data.exists -> code, MDL)) *result = 0; else { *result = 1; data_string_forget (&left, MDL); } #if defined (DEBUG_EXPRESSIONS) log_debug ("bool: exists %s.%s = %s", expr -> data.option -> universe -> name, expr -> data.option -> name, *result ? "true" : "false"); #endif return 1; case expr_known: if (!packet) { #if defined (DEBUG_EXPRESSIONS) log_debug ("bool: known = NULL"); #endif return 0; } #if defined (DEBUG_EXPRESSIONS) log_debug ("bool: known = %s", packet -> known ? "true" : "false"); #endif *result = packet -> known; return 1; case expr_static: if (!lease || !(lease -> flags & STATIC_LEASE)) { #if defined (DEBUG_EXPRESSIONS) log_debug ("bool: static = false (%s %s %s %d)", lease ? "y" : "n", (lease && (lease -> flags & STATIC_LEASE) ? "y" : "n"), piaddr (lease -> ip_addr), lease ? lease -> flags : 0); #endif *result = 0; return 1; } #if defined (DEBUG_EXPRESSIONS) log_debug ("bool: static = true"); #endif *result = 1; return 1; case expr_variable_exists: if (scope && *scope) { binding = find_binding (*scope, expr -> data.variable); if (binding) { if (binding -> value) *result = 1; else *result = 0; } else *result = 0; } else *result = 0; #if defined (DEBUG_EXPRESSIONS) log_debug ("boolean: %s? = %s", expr -> data.variable, *result ? "true" : "false"); #endif return 1; case expr_variable_reference: if (scope && *scope) { binding = find_binding (*scope, expr -> data.variable); if (binding && binding -> value) { if (binding -> value -> type == binding_boolean) { *result = binding -> value -> value.boolean; sleft = 1; } else { log_error ("binding type %d in %s.", binding -> value -> type, "evaluate_boolean_expression"); sleft = 0; } } else sleft = 0; } else sleft = 0; #if defined (DEBUG_EXPRESSIONS) log_debug ("boolean: %s = %s", expr -> data.variable, sleft ? (*result ? "true" : "false") : "NULL"); #endif return sleft; case expr_funcall: bv = (struct binding_value *)0; sleft = evaluate_expression (&bv, packet, lease, client_state, in_options, cfg_options, scope, expr, MDL); if (sleft) { if (bv -> type != binding_boolean) log_error ("%s() returned type %d in %s.", expr -> data.funcall.name, bv -> type, "evaluate_boolean_expression"); else *result = bv -> value.boolean; binding_value_dereference (&bv, MDL); } #if defined (DEBUG_EXPRESSIONS) log_debug ("boolean: %s() = %s", expr -> data.funcall.name, sleft ? (*result ? "true" : "false") : "NULL"); #endif break; case expr_none: case expr_match: case expr_substring: case expr_suffix: case expr_lcase: case expr_ucase: case expr_option: case expr_hardware: case expr_const_data: case expr_packet: case expr_concat: case expr_encapsulate: case expr_host_lookup: case expr_encode_int8: case expr_encode_int16: case expr_encode_int32: case expr_binary_to_ascii: case expr_reverse: case expr_pick_first_value: case expr_host_decl_name: case expr_config_option: case expr_leased_address: case expr_null: case expr_filename: case expr_sname: case expr_gethostname: case expr_v6relay: case expr_concat_dclist: log_error ("Data opcode in evaluate_boolean_expression: %d", expr -> op); return 0; case expr_extract_int8: case expr_extract_int16: case expr_extract_int32: case expr_const_int: case expr_lease_time: case expr_dns_transaction: case expr_add: case expr_subtract: case expr_multiply: case expr_divide: case expr_remainder: case expr_binary_and: case expr_binary_or: case expr_binary_xor: case expr_client_state: log_error ("Numeric opcode in evaluate_boolean_expression: %d", expr -> op); return 0; case expr_ns_add: case expr_ns_delete: case expr_ns_exists: case expr_ns_not_exists: log_error ("dns opcode in evaluate_boolean_expression: %d", expr -> op); return 0; case expr_function: log_error ("function definition in evaluate_boolean_expr"); return 0; case expr_arg: break; } log_error ("Bogus opcode in evaluate_boolean_expression: %d", expr -> op); return 0; } int evaluate_data_expression (result, packet, lease, client_state, in_options, cfg_options, scope, expr, file, line) struct data_string *result; struct packet *packet; struct lease *lease; struct client_state *client_state; struct option_state *in_options; struct option_state *cfg_options; struct binding_scope **scope; struct expression *expr; const char *file; int line; { struct data_string data, other; unsigned long offset, len, i; int s0, s1, s2, s3; int status; struct binding *binding; unsigned char *s; struct binding_value *bv; struct packet *relay_packet; struct option_state *relay_options; switch (expr -> op) { /* Extract N bytes starting at byte M of a data string. */ case expr_substring: memset (&data, 0, sizeof data); s0 = evaluate_data_expression (&data, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.substring.expr, MDL); /* Evaluate the offset and length. */ s1 = evaluate_numeric_expression (&offset, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.substring.offset); s2 = evaluate_numeric_expression (&len, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.substring.len); if (s0 && s1 && s2) { /* If the offset is after end of the string, return an empty string. Otherwise, do the adjustments and return what's left. */ if (data.len > offset) { data_string_copy (result, &data, file, line); result -> len -= offset; if (result -> len > len) { result -> len = len; result -> terminated = 0; } result -> data += offset; } s3 = 1; } else s3 = 0; #if defined (DEBUG_EXPRESSIONS) log_debug ("data: substring (%s, %s, %s) = %s", s0 ? print_hex_1 (data.len, data.data, 30) : "NULL", s1 ? print_dec_1 (offset) : "NULL", s2 ? print_dec_2 (len) : "NULL", (s3 ? print_hex_2 (result -> len, result -> data, 30) : "NULL")); #endif if (s0) data_string_forget (&data, MDL); if (s3) return 1; return 0; /* Extract the last N bytes of a data string. */ case expr_suffix: memset (&data, 0, sizeof data); s0 = evaluate_data_expression (&data, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.suffix.expr, MDL); /* Evaluate the length. */ s1 = evaluate_numeric_expression (&len, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.suffix.len); if (s0 && s1) { data_string_copy (result, &data, file, line); /* If we are returning the last N bytes of a string whose length is <= N, just return the string - otherwise, compute a new starting address and decrease the length. */ if (data.len > len) { result -> data += data.len - len; result -> len = len; } data_string_forget (&data, MDL); } #if defined (DEBUG_EXPRESSIONS) log_debug ("data: suffix (%s, %s) = %s", s0 ? print_hex_1 (data.len, data.data, 30) : "NULL", s1 ? print_dec_1 (len) : "NULL", ((s0 && s1) ? print_hex_2 (result -> len, result -> data, 30) : "NULL")); #endif return s0 && s1; /* Convert string to lowercase. */ case expr_lcase: memset(&data, 0, sizeof data); s0 = evaluate_data_expression(&data, packet, lease, client_state, in_options, cfg_options, scope, expr->data.lcase, MDL); s1 = 0; if (s0) { result->len = data.len; if (buffer_allocate(&result->buffer, result->len + data.terminated, MDL)) { result->data = &result->buffer->data[0]; memcpy(result->buffer->data, data.data, data.len + data.terminated); result->terminated = data.terminated; s = (unsigned char *)result->data; for (i = 0; i < result->len; i++, s++) *s = tolower(*s); s1 = 1; } else { log_error("data: lcase: no buffer memory."); } } #if defined (DEBUG_EXPRESSIONS) log_debug("data: lcase (%s) = %s", s0 ? print_hex_1(data.len, data.data, 30) : "NULL", s1 ? print_hex_2(result->len, result->data, 30) : "NULL"); #endif if (s0) data_string_forget(&data, MDL); return s1; /* Convert string to uppercase. */ case expr_ucase: memset(&data, 0, sizeof data); s0 = evaluate_data_expression(&data, packet, lease, client_state, in_options, cfg_options, scope, expr->data.lcase, MDL); s1 = 0; if (s0) { result->len = data.len; if (buffer_allocate(&result->buffer, result->len + data.terminated, file, line)) { result->data = &result->buffer->data[0]; memcpy(result->buffer->data, data.data, data.len + data.terminated); result->terminated = data.terminated; s = (unsigned char *)result->data; for (i = 0; i < result->len; i++, s++) *s = toupper(*s); s1 = 1; } else { log_error("data: lcase: no buffer memory."); } } #if defined (DEBUG_EXPRESSIONS) log_debug("data: ucase (%s) = %s", s0 ? print_hex_1(data.len, data.data, 30) : "NULL", s1 ? print_hex_2(result->len, result->data, 30) : "NULL"); #endif if (s0) data_string_forget(&data, MDL); return s1; /* Extract an option. */ case expr_option: if (in_options) s0 = get_option (result, expr -> data.option -> universe, packet, lease, client_state, in_options, cfg_options, in_options, scope, expr -> data.option -> code, file, line); else s0 = 0; #if defined (DEBUG_EXPRESSIONS) log_debug ("data: option %s.%s = %s", expr -> data.option -> universe -> name, expr -> data.option -> name, s0 ? print_hex_1 (result -> len, result -> data, 60) : "NULL"); #endif return s0; case expr_config_option: if (cfg_options) s0 = get_option (result, expr -> data.option -> universe, packet, lease, client_state, in_options, cfg_options, cfg_options, scope, expr -> data.option -> code, file, line); else s0 = 0; #if defined (DEBUG_EXPRESSIONS) log_debug ("data: config-option %s.%s = %s", expr -> data.option -> universe -> name, expr -> data.option -> name, s0 ? print_hex_1 (result -> len, result -> data, 60) : "NULL"); #endif return s0; /* Combine the hardware type and address. */ case expr_hardware: /* On the client, hardware is our hardware. */ if (client_state) { memset(result, 0, sizeof(*result)); result->data = client_state->interface->hw_address.hbuf; result->len = client_state->interface->hw_address.hlen; #if defined (DEBUG_EXPRESSIONS) log_debug("data: hardware = %s", print_hex_1(result->len, result->data, 60)); #endif return (1); } /* The server cares about the client's hardware address, so only in the case where we are examining a packet or have a lease with a hardware address can we return anything. */ if (packet != NULL && packet->raw != NULL) { if (packet->raw->hlen > sizeof(packet->raw->chaddr)) { log_error("data: hardware: invalid hlen (%d)\n", packet->raw->hlen); return (0); } result->len = packet->raw->hlen + 1; if (buffer_allocate(&result->buffer, result->len, MDL)){ result->data = &result->buffer->data[0]; result->buffer->data[0] = packet->raw->htype; memcpy(&result->buffer->data[1], packet->raw->chaddr, packet->raw->hlen); result->terminated = 0; } else { log_error("data: hardware: " "no memory for buffer."); return (0); } } else if (lease != NULL) { result->len = lease->hardware_addr.hlen; if (buffer_allocate(&result->buffer, result->len, MDL)){ result->data = &result->buffer->data[0]; memcpy(result->buffer->data, lease->hardware_addr.hbuf, result->len); result->terminated = 0; } else { log_error("data: hardware: " "no memory for buffer."); return (0); } } else { log_error("data: hardware: no raw packet or lease " "is available"); return (0); } #if defined (DEBUG_EXPRESSIONS) log_debug("data: hardware = %s", print_hex_1(result->len, result->data, 60)); #endif return (1); /* Extract part of the raw packet. */ case expr_packet: if (!packet || !packet -> raw) { log_error ("data: packet: raw packet not available"); return 0; } s0 = evaluate_numeric_expression (&offset, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.packet.offset); s1 = evaluate_numeric_expression (&len, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.packet.len); if (s0 && s1 && offset < packet -> packet_length) { if (offset + len > packet -> packet_length) result -> len = packet -> packet_length - offset; else result -> len = len; if (buffer_allocate (&result -> buffer, result -> len, file, line)) { result -> data = &result -> buffer -> data [0]; memcpy (result -> buffer -> data, (((unsigned char *)(packet -> raw)) + offset), result -> len); result -> terminated = 0; } else { log_error ("data: packet: no buffer memory."); return 0; } s2 = 1; } else s2 = 0; #if defined (DEBUG_EXPRESSIONS) log_debug ("data: packet (%ld, %ld) = %s", offset, len, s2 ? print_hex_1 (result -> len, result -> data, 60) : NULL); #endif return s2; /* The encapsulation of all defined options in an option space... */ case expr_encapsulate: if (cfg_options) s0 = option_space_encapsulate (result, packet, lease, client_state, in_options, cfg_options, scope, &expr -> data.encapsulate); else s0 = 0; #if defined (DEBUG_EXPRESSIONS) log_debug ("data: encapsulate (%s) = %s", expr -> data.encapsulate.data, s0 ? print_hex_1 (result -> len, result -> data, 60) : "NULL"); #endif return s0; /* Some constant data... */ case expr_const_data: #if defined (DEBUG_EXPRESSIONS) log_debug ("data: const = %s", print_hex_1 (expr -> data.const_data.len, expr -> data.const_data.data, 60)); #endif data_string_copy (result, &expr -> data.const_data, file, line); return 1; /* Hostname lookup... */ case expr_host_lookup: s0 = do_host_lookup (result, expr -> data.host_lookup); #if defined (DEBUG_EXPRESSIONS) log_debug ("data: DNS lookup (%s) = %s", expr -> data.host_lookup -> hostname, (s0 ? print_dotted_quads (result -> len, result -> data) : "NULL")); #endif return s0; /* Concatenation... */ case expr_concat: memset (&data, 0, sizeof data); s0 = evaluate_data_expression (&data, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.concat [0], MDL); memset (&other, 0, sizeof other); s1 = evaluate_data_expression (&other, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.concat [1], MDL); if (s0 && s1) { result -> len = data.len + other.len; if (!buffer_allocate (&result -> buffer, (result -> len + other.terminated), file, line)) { log_error ("data: concat: no memory"); result -> len = 0; data_string_forget (&data, MDL); data_string_forget (&other, MDL); return 0; } result -> data = &result -> buffer -> data [0]; memcpy (result -> buffer -> data, data.data, data.len); memcpy (&result -> buffer -> data [data.len], other.data, other.len + other.terminated); } if (s0) data_string_forget (&data, MDL); if (s1) data_string_forget (&other, MDL); #if defined (DEBUG_EXPRESSIONS) log_debug ("data: concat (%s, %s) = %s", s0 ? print_hex_1 (data.len, data.data, 20) : "NULL", s1 ? print_hex_2 (other.len, other.data, 20) : "NULL", ((s0 && s1) ? print_hex_3 (result -> len, result -> data, 30) : "NULL")); #endif return s0 && s1; case expr_encode_int8: s0 = evaluate_numeric_expression (&len, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.encode_int); if (s0) { result -> len = 1; if (!buffer_allocate (&result -> buffer, 1, file, line)) { log_error ("data: encode_int8: no memory"); result -> len = 0; s0 = 0; } else { result -> data = &result -> buffer -> data [0]; result -> buffer -> data [0] = len; } } else result -> len = 0; #if defined (DEBUG_EXPRESSIONS) if (!s0) log_debug ("data: encode_int8 (NULL) = NULL"); else log_debug ("data: encode_int8 (%ld) = %s", len, print_hex_2 (result -> len, result -> data, 20)); #endif return s0; case expr_encode_int16: s0 = evaluate_numeric_expression (&len, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.encode_int); if (s0) { result -> len = 2; if (!buffer_allocate (&result -> buffer, 2, file, line)) { log_error ("data: encode_int16: no memory"); result -> len = 0; s0 = 0; } else { result -> data = &result -> buffer -> data [0]; putUShort (result -> buffer -> data, len); } } else result -> len = 0; #if defined (DEBUG_EXPRESSIONS) if (!s0) log_debug ("data: encode_int16 (NULL) = NULL"); else log_debug ("data: encode_int16 (%ld) = %s", len, print_hex_2 (result -> len, result -> data, 20)); #endif return s0; case expr_encode_int32: s0 = evaluate_numeric_expression (&len, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.encode_int); if (s0) { result -> len = 4; if (!buffer_allocate (&result -> buffer, 4, file, line)) { log_error ("data: encode_int32: no memory"); result -> len = 0; s0 = 0; } else { result -> data = &result -> buffer -> data [0]; putULong (result -> buffer -> data, len); } } else result -> len = 0; #if defined (DEBUG_EXPRESSIONS) if (!s0) log_debug ("data: encode_int32 (NULL) = NULL"); else log_debug ("data: encode_int32 (%ld) = %s", len, print_hex_2 (result -> len, result -> data, 20)); #endif return s0; case expr_binary_to_ascii: /* Evaluate the base (offset) and width (len): */ s0 = evaluate_numeric_expression (&offset, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.b2a.base); s1 = evaluate_numeric_expression (&len, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.b2a.width); /* Evaluate the separator string. */ memset (&data, 0, sizeof data); s2 = evaluate_data_expression (&data, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.b2a.separator, MDL); /* Evaluate the data to be converted. */ memset (&other, 0, sizeof other); s3 = evaluate_data_expression (&other, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.b2a.buffer, MDL); if (s0 && s1 && s2 && s3) { unsigned buflen, i; if (len != 8 && len != 16 && len != 32) { log_info ("binary_to_ascii: %s %ld!", "invalid width", len); status = 0; goto b2a_out; } len /= 8; /* The buffer must be a multiple of the number's width. */ if (other.len % len) { log_info ("binary-to-ascii: %s %d %s %ld!", "length of buffer", other.len, "not a multiple of width", len); status = 0; goto b2a_out; } /* Count the width of the output. */ buflen = 0; for (i = 0; i < other.len; i += len) { if (len == 1) { if (offset == 8) { if (other.data [i] < 8) buflen++; else if (other.data [i] < 64) buflen += 2; else buflen += 3; } else if (offset == 10) { if (other.data [i] < 10) buflen++; else if (other.data [i] < 100) buflen += 2; else buflen += 3; } else if (offset == 16) { if (other.data [i] < 16) buflen++; else buflen += 2; } else buflen += (converted_length (&other.data [i], offset, 1)); } else buflen += (converted_length (&other.data [i], offset, len)); if (i + len != other.len) buflen += data.len; } if (!buffer_allocate (&result -> buffer, buflen + 1, file, line)) { log_error ("data: binary-to-ascii: no memory"); status = 0; goto b2a_out; } result -> data = &result -> buffer -> data [0]; result -> len = buflen; result -> terminated = 1; buflen = 0; for (i = 0; i < other.len; i += len) { buflen += (binary_to_ascii (&result -> buffer -> data [buflen], &other.data [i], offset, len)); if (i + len != other.len) { memcpy (&result -> buffer -> data [buflen], data.data, data.len); buflen += data.len; } } /* NUL terminate. */ result -> buffer -> data [buflen] = 0; status = 1; } else status = 0; b2a_out: #if defined (DEBUG_EXPRESSIONS) log_debug ("data: binary-to-ascii (%s, %s, %s, %s) = %s", s0 ? print_dec_1 (offset) : "NULL", s1 ? print_dec_2 (len) : "NULL", s2 ? print_hex_1 (data.len, data.data, 30) : "NULL", s3 ? print_hex_2 (other.len, other.data, 30) : "NULL", (status ? print_hex_3 (result -> len, result -> data, 30) : "NULL")); #endif if (s2) data_string_forget (&data, MDL); if (s3) data_string_forget (&other, MDL); if (status) return 1; return 0; case expr_reverse: /* Evaluate the width (len): */ s0 = evaluate_numeric_expression (&len, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.reverse.width); /* Evaluate the data. */ memset (&data, 0, sizeof data); s1 = evaluate_data_expression (&data, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.reverse.buffer, MDL); if (s0 && s1) { int i; /* The buffer must be a multiple of the number's width. */ if (data.len % len) { log_info ("reverse: %s %d %s %ld!", "length of buffer", data.len, "not a multiple of width", len); status = 0; goto reverse_out; } /* XXX reverse in place? I don't think we can. */ if (!buffer_allocate (&result -> buffer, data.len, file, line)) { log_error ("data: reverse: no memory"); status = 0; goto reverse_out; } result -> data = &result -> buffer -> data [0]; result -> len = data.len; result -> terminated = 0; for (i = 0; i < data.len; i += len) { memcpy (&result -> buffer -> data [i], &data.data [data.len - i - len], len); } status = 1; } else status = 0; reverse_out: #if defined (DEBUG_EXPRESSIONS) log_debug ("data: reverse (%s, %s) = %s", s0 ? print_dec_1 (len) : "NULL", s1 ? print_hex_1 (data.len, data.data, 30) : "NULL", (status ? print_hex_3 (result -> len, result -> data, 30) : "NULL")); #endif if (s0) data_string_forget (&data, MDL); if (status) return 1; return 0; case expr_leased_address: if (!lease) { log_debug("data: \"leased-address\" configuration " "directive: there is no lease associated " "with this client."); return 0; } result -> len = lease -> ip_addr.len; if (buffer_allocate (&result -> buffer, result -> len, file, line)) { result -> data = &result -> buffer -> data [0]; memcpy (&result -> buffer -> data [0], lease -> ip_addr.iabuf, lease -> ip_addr.len); result -> terminated = 0; } else { log_error ("data: leased-address: no memory."); return 0; } #if defined (DEBUG_EXPRESSIONS) log_debug ("data: leased-address = %s", print_hex_1 (result -> len, result -> data, 60)); #endif return 1; case expr_pick_first_value: memset (&data, 0, sizeof data); if ((evaluate_data_expression (result, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.pick_first_value.car, MDL))) { #if defined (DEBUG_EXPRESSIONS) log_debug ("data: pick_first_value (%s, xxx)", print_hex_1 (result -> len, result -> data, 40)); #endif return 1; } if (expr -> data.pick_first_value.cdr && (evaluate_data_expression (result, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.pick_first_value.cdr, MDL))) { #if defined (DEBUG_EXPRESSIONS) log_debug ("data: pick_first_value (NULL, %s)", print_hex_1 (result -> len, result -> data, 40)); #endif return 1; } #if defined (DEBUG_EXPRESSIONS) log_debug ("data: pick_first_value (NULL, NULL) = NULL"); #endif return 0; case expr_host_decl_name: if (!lease || !lease -> host) { log_error ("data: host_decl_name: not available"); return 0; } result -> len = strlen (lease -> host -> name); if (buffer_allocate (&result -> buffer, result -> len + 1, file, line)) { result -> data = &result -> buffer -> data [0]; strcpy ((char *)&result -> buffer -> data [0], lease -> host -> name); result -> terminated = 1; } else { log_error ("data: host-decl-name: no memory."); return 0; } #if defined (DEBUG_EXPRESSIONS) log_debug ("data: host-decl-name = %s", lease -> host -> name); #endif return 1; case expr_null: #if defined (DEBUG_EXPRESSIONS) log_debug ("data: null = NULL"); #endif return 0; case expr_variable_reference: if (scope && *scope) { binding = find_binding (*scope, expr -> data.variable); if (binding && binding -> value) { if (binding -> value -> type == binding_data) { data_string_copy (result, &binding -> value -> value.data, file, line); s0 = 1; } else if (binding -> value -> type != binding_data) { log_error ("binding type %d in %s.", binding -> value -> type, "evaluate_data_expression"); s0 = 0; } else s0 = 0; } else s0 = 0; } else s0 = 0; #if defined (DEBUG_EXPRESSIONS) log_debug ("data: %s = %s", expr -> data.variable, s0 ? print_hex_1 (result -> len, result -> data, 50) : "NULL"); #endif return s0; case expr_funcall: bv = (struct binding_value *)0; s0 = evaluate_expression (&bv, packet, lease, client_state, in_options, cfg_options, scope, expr, MDL); if (s0) { if (bv -> type != binding_data) log_error ("%s() returned type %d in %s.", expr -> data.funcall.name, bv -> type, "evaluate_data_expression"); else data_string_copy (result, &bv -> value.data, file, line); binding_value_dereference (&bv, MDL); } #if defined (DEBUG_EXPRESSIONS) log_debug ("data: %s = %s", expr -> data.funcall.name, s0 ? print_hex_1 (result -> len, result -> data, 50) : "NULL"); #endif break; /* Extract the filename. */ case expr_filename: if (packet && packet -> raw -> file [0]) { char *fn = memchr (packet -> raw -> file, 0, sizeof packet -> raw -> file); if (!fn) fn = ((char *)packet -> raw -> file + sizeof packet -> raw -> file); result -> len = fn - &(packet -> raw -> file [0]); if (buffer_allocate (&result -> buffer, result -> len + 1, file, line)) { result -> data = &result -> buffer -> data [0]; memcpy (&result -> buffer -> data [0], packet -> raw -> file, result -> len); result -> buffer -> data [result -> len] = 0; result -> terminated = 1; s0 = 1; } else { log_error ("data: filename: no memory."); s0 = 0; } } else s0 = 0; #if defined (DEBUG_EXPRESSIONS) log_info ("data: filename = \"%s\"", s0 ? (const char *)(result -> data) : "NULL"); #endif return s0; /* Extract the server name. */ case expr_sname: if (packet && packet -> raw -> sname [0]) { char *fn = memchr (packet -> raw -> sname, 0, sizeof packet -> raw -> sname); if (!fn) fn = ((char *)packet -> raw -> sname + sizeof packet -> raw -> sname); result -> len = fn - &packet -> raw -> sname [0]; if (buffer_allocate (&result -> buffer, result -> len + 1, file, line)) { result -> data = &result -> buffer -> data [0]; memcpy (&result -> buffer -> data [0], packet -> raw -> sname, result -> len); result -> buffer -> data [result -> len] = 0; result -> terminated = 1; s0 = 1; } else { log_error ("data: sname: no memory."); s0 = 0; } } else s0 = 0; #if defined (DEBUG_EXPRESSIONS) log_info ("data: sname = \"%s\"", s0 ? (const char *)(result -> data) : "NULL"); #endif return s0; /* Provide the system's local hostname as a return value. */ case expr_gethostname: /* * Allocate a buffer to return. * * The largest valid hostname is maybe 64 octets at a single * label, or 255 octets if you think a hostname is allowed * to contain labels (plus termination). */ memset(result, 0, sizeof(*result)); if (!buffer_allocate(&result->buffer, 255, file, line)) { log_error("data: gethostname(): no memory for buffer"); return 0; } result->data = result->buffer->data; /* * On successful completion, gethostname() resturns 0. It may * not null-terminate the string if there was insufficient * space. */ if (!gethostname((char *)result->buffer->data, 255)) { if (result->buffer->data[255] == '\0') result->len = strlen((char *)result->buffer->data); else result->len = 255; return 1; } data_string_forget(result, MDL); return 0; /* Find an option within a v6relay context * * The numeric expression in relay indicates which relay * to try and use as the context. The relays are numbered * 1 to 32 with 1 being the one closest to the client and * 32 closest to the server. A value of greater than 33 * indicates using the one closest to the server whatever * the count. A value of 0 indicates not using the relay * options, this is included for completeness and consistency * with the host-identier code. * * The data expression in roption is evaluated in that * context and the result returned. */ case expr_v6relay: len = 0; s1 = 0; memset (&data, 0, sizeof data); /* Evaluate the relay count */ s0 = evaluate_numeric_expression(&len, packet, lease, client_state, in_options, cfg_options, scope, expr->data.v6relay.relay); /* no number or an obviously invalid number */ if ((s0 == 0) || ((len > 0) && ((packet == NULL) || (packet->dhcpv6_container_packet == NULL)))) { #if defined (DEBUG_EXPRESSIONS) log_debug("data: v6relay(%lu) = NULL", len); #endif return (0); } /* Find the correct packet for the requested relay */ i = len; relay_packet = packet; relay_options = in_options; while ((i != 0) && (relay_packet->dhcpv6_container_packet != NULL)) { relay_packet = relay_packet->dhcpv6_container_packet; relay_options = relay_packet->options; i--; } /* We wanted a specific relay but were unable to find it */ if ((len <= MAX_V6RELAY_HOPS) && (i != 0)) { #if defined (DEBUG_EXPRESSIONS) log_debug("data: v6relay(%lu) = NULL", len); #endif return (0); } s1 = evaluate_data_expression(&data, relay_packet, lease, client_state, relay_options, cfg_options, scope, expr->data.v6relay.roption, MDL); if (s1) { data_string_copy(result, &data, file, line); data_string_forget(&data, MDL); } #if defined (DEBUG_EXPRESSIONS) log_debug("data: v6relay(%lu) = %s", len, s1 ? print_hex_3(result->len, result->data, 30) : "NULL"); #endif return (s1); case expr_concat_dclist: { /* Operands are compressed domain-name lists ("Dc" format) * Fetch both compressed lists then call concat_dclists which * combines them into a single compressed list. */ memset(&data, 0, sizeof data); int outcome = 0; s0 = evaluate_data_expression(&data, packet, lease, client_state, in_options, cfg_options, scope, expr->data.concat[0], MDL); memset (&other, 0, sizeof other); s1 = evaluate_data_expression (&other, packet, lease, client_state, in_options, cfg_options, scope, expr->data.concat[1], MDL); if (s0 && s1) { outcome = concat_dclists(result, &data, &other); if (outcome == 0) { log_error ("data: concat_dclist failed"); } } #if defined (DEBUG_EXPRESSIONS) log_debug ("data: concat_dclists (%s, %s) = %s", (s0 ? print_hex_1(data.len, data.data, data.len) : "NULL"), (s1 ? print_hex_2(other.len, other.data, other.len) : "NULL"), (((s0 && s1) && result->len > 0) ? print_hex_3 (result->len, result->data, result->len) : "NULL")); #endif if (s0) data_string_forget (&data, MDL); if (s1) data_string_forget (&other, MDL); return (outcome); } /* expr_concat_dclist */ case expr_check: case expr_equal: case expr_not_equal: case expr_regex_match: case expr_iregex_match: case expr_and: case expr_or: case expr_not: case expr_match: case expr_static: case expr_known: case expr_none: case expr_exists: case expr_variable_exists: log_error ("Boolean opcode in evaluate_data_expression: %d", expr -> op); return 0; case expr_extract_int8: case expr_extract_int16: case expr_extract_int32: case expr_const_int: case expr_lease_time: case expr_dns_transaction: case expr_add: case expr_subtract: case expr_multiply: case expr_divide: case expr_remainder: case expr_binary_and: case expr_binary_or: case expr_binary_xor: case expr_client_state: log_error ("Numeric opcode in evaluate_data_expression: %d", expr -> op); return 0; case expr_ns_add: case expr_ns_delete: case expr_ns_exists: case expr_ns_not_exists: log_error ("dns opcode in evaluate_boolean_expression: %d", expr -> op); return 0; case expr_function: log_error ("function definition in evaluate_data_expression"); return 0; case expr_arg: break; } log_error ("Bogus opcode in evaluate_data_expression: %d", expr -> op); return 0; } int evaluate_numeric_expression (result, packet, lease, client_state, in_options, cfg_options, scope, expr) unsigned long *result; struct packet *packet; struct lease *lease; struct client_state *client_state; struct option_state *in_options; struct option_state *cfg_options; struct binding_scope **scope; struct expression *expr; { struct data_string data; int status, sleft, sright; struct binding *binding; struct binding_value *bv; unsigned long ileft, iright; int rc = 0; switch (expr -> op) { case expr_check: case expr_equal: case expr_not_equal: case expr_regex_match: case expr_iregex_match: case expr_and: case expr_or: case expr_not: case expr_match: case expr_static: case expr_known: case expr_none: case expr_exists: case expr_variable_exists: log_error ("Boolean opcode in evaluate_numeric_expression: %d", expr -> op); return 0; case expr_substring: case expr_suffix: case expr_lcase: case expr_ucase: case expr_option: case expr_hardware: case expr_const_data: case expr_packet: case expr_concat: case expr_encapsulate: case expr_host_lookup: case expr_encode_int8: case expr_encode_int16: case expr_encode_int32: case expr_binary_to_ascii: case expr_reverse: case expr_filename: case expr_sname: case expr_pick_first_value: case expr_host_decl_name: case expr_config_option: case expr_leased_address: case expr_null: case expr_gethostname: case expr_v6relay: log_error ("Data opcode in evaluate_numeric_expression: %d", expr -> op); return 0; case expr_extract_int8: memset (&data, 0, sizeof data); status = evaluate_data_expression (&data, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.extract_int, MDL); if (status) *result = data.data [0]; #if defined (DEBUG_EXPRESSIONS) log_debug ("num: extract_int8 (%s) = %s", status ? print_hex_1 (data.len, data.data, 60) : "NULL", status ? print_dec_1 (*result) : "NULL" ); #endif if (status) data_string_forget (&data, MDL); return status; case expr_extract_int16: memset(&data, 0, sizeof(data)); status = (evaluate_data_expression (&data, packet, lease, client_state, in_options, cfg_options, scope, expr->data.extract_int, MDL)); if (status && data.len >= 2) { *result = getUShort(data.data); rc = 1; } #if defined (DEBUG_EXPRESSIONS) if (rc == 1) { log_debug("num: extract_int16 (%s) = %ld", print_hex_1(data.len, data.data, 60), *result); } else { log_debug("num: extract_int16 (NULL) = NULL"); } #endif if (status) data_string_forget(&data, MDL); return (rc); case expr_extract_int32: memset (&data, 0, sizeof data); status = (evaluate_data_expression (&data, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.extract_int, MDL)); if (status && data.len >= 4) { *result = getULong (data.data); rc = 1; } #if defined (DEBUG_EXPRESSIONS) if (rc == 1) { log_debug ("num: extract_int32 (%s) = %ld", print_hex_1 (data.len, data.data, 60), *result); } else { log_debug ("num: extract_int32 (NULL) = NULL"); } #endif if (status) data_string_forget (&data, MDL); return (rc); case expr_const_int: *result = expr -> data.const_int; #if defined (DEBUG_EXPRESSIONS) log_debug ("number: CONSTANT = %ld", *result); #endif return 1; case expr_lease_time: if (!lease) { log_error("data: leased_lease: not available"); return (0); } if (lease->ends < cur_time) { log_error("%s %lu when it is now %lu", "data: lease_time: lease ends at", (long)(lease->ends), (long)cur_time); return (0); } *result = lease->ends - cur_time; #if defined (DEBUG_EXPRESSIONS) log_debug("number: lease-time = (%lu - %lu) = %ld", (long unsigned)lease->ends, (long unsigned)cur_time, *result); #endif return (1); case expr_variable_reference: if (scope && *scope) { binding = find_binding (*scope, expr -> data.variable); if (binding && binding -> value) { if (binding -> value -> type == binding_numeric) { *result = binding -> value -> value.intval; status = 1; } else { log_error ("binding type %d in %s.", binding -> value -> type, "evaluate_numeric_expression"); status = 0; } } else status = 0; } else status = 0; #if defined (DEBUG_EXPRESSIONS) if (status) log_debug ("numeric: %s = %ld", expr -> data.variable, *result); else log_debug ("numeric: %s = NULL", expr -> data.variable); #endif return status; case expr_funcall: bv = (struct binding_value *)0; status = evaluate_expression (&bv, packet, lease, client_state, in_options, cfg_options, scope, expr, MDL); if (status) { if (bv -> type != binding_numeric) log_error ("%s() returned type %d in %s.", expr -> data.funcall.name, bv -> type, "evaluate_numeric_expression"); else *result = bv -> value.intval; binding_value_dereference (&bv, MDL); } #if defined (DEBUG_EXPRESSIONS) log_debug ("data: %s = %ld", expr -> data.funcall.name, status ? *result : 0); #endif break; case expr_add: sleft = evaluate_numeric_expression (&ileft, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.and [0]); sright = evaluate_numeric_expression (&iright, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.and [1]); #if defined (DEBUG_EXPRESSIONS) if (sleft && sright) log_debug ("num: %ld + %ld = %ld", ileft, iright, ileft + iright); else if (sleft) log_debug ("num: %ld + NULL = NULL", ileft); else log_debug ("num: NULL + %ld = NULL", iright); #endif if (sleft && sright) { *result = ileft + iright; return 1; } return 0; case expr_subtract: sleft = evaluate_numeric_expression (&ileft, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.and [0]); sright = evaluate_numeric_expression (&iright, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.and [1]); #if defined (DEBUG_EXPRESSIONS) if (sleft && sright) log_debug ("num: %ld - %ld = %ld", ileft, iright, ileft - iright); else if (sleft) log_debug ("num: %ld - NULL = NULL", ileft); else log_debug ("num: NULL - %ld = NULL", iright); #endif if (sleft && sright) { *result = ileft - iright; return 1; } return 0; case expr_multiply: sleft = evaluate_numeric_expression (&ileft, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.and [0]); sright = evaluate_numeric_expression (&iright, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.and [1]); #if defined (DEBUG_EXPRESSIONS) if (sleft && sright) log_debug ("num: %ld * %ld = %ld", ileft, iright, ileft * iright); else if (sleft) log_debug ("num: %ld * NULL = NULL", ileft); else log_debug ("num: NULL * %ld = NULL", iright); #endif if (sleft && sright) { *result = ileft * iright; return 1; } return 0; case expr_divide: sleft = evaluate_numeric_expression (&ileft, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.and [0]); sright = evaluate_numeric_expression (&iright, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.and [1]); #if defined (DEBUG_EXPRESSIONS) if (sleft && sright) { if (iright != 0) log_debug ("num: %ld / %ld = %ld", ileft, iright, ileft / iright); else log_debug ("num: %ld / %ld = NULL", ileft, iright); } else if (sleft) log_debug ("num: %ld / NULL = NULL", ileft); else log_debug ("num: NULL / %ld = NULL", iright); #endif if (sleft && sright && iright) { *result = ileft / iright; return 1; } return 0; case expr_remainder: sleft = evaluate_numeric_expression (&ileft, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.and [0]); sright = evaluate_numeric_expression (&iright, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.and [1]); #if defined (DEBUG_EXPRESSIONS) if (sleft && sright) { if (iright != 0) log_debug ("num: %ld %% %ld = %ld", ileft, iright, ileft % iright); else log_debug ("num: %ld %% %ld = NULL", ileft, iright); } else if (sleft) log_debug ("num: %ld %% NULL = NULL", ileft); else log_debug ("num: NULL %% %ld = NULL", iright); #endif if (sleft && sright && iright) { *result = ileft % iright; return 1; } return 0; case expr_binary_and: sleft = evaluate_numeric_expression (&ileft, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.and [0]); sright = evaluate_numeric_expression (&iright, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.and [1]); #if defined (DEBUG_EXPRESSIONS) if (sleft && sright) log_debug ("num: %ld | %ld = %ld", ileft, iright, ileft & iright); else if (sleft) log_debug ("num: %ld & NULL = NULL", ileft); else log_debug ("num: NULL & %ld = NULL", iright); #endif if (sleft && sright) { *result = ileft & iright; return 1; } return 0; case expr_binary_or: sleft = evaluate_numeric_expression (&ileft, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.and [0]); sright = evaluate_numeric_expression (&iright, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.and [1]); #if defined (DEBUG_EXPRESSIONS) if (sleft && sright) log_debug ("num: %ld | %ld = %ld", ileft, iright, ileft | iright); else if (sleft) log_debug ("num: %ld | NULL = NULL", ileft); else log_debug ("num: NULL | %ld = NULL", iright); #endif if (sleft && sright) { *result = ileft | iright; return 1; } return 0; case expr_binary_xor: sleft = evaluate_numeric_expression (&ileft, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.and [0]); sright = evaluate_numeric_expression (&iright, packet, lease, client_state, in_options, cfg_options, scope, expr -> data.and [1]); #if defined (DEBUG_EXPRESSIONS) if (sleft && sright) log_debug ("num: %ld ^ %ld = %ld", ileft, iright, ileft ^ iright); else if (sleft) log_debug ("num: %ld ^ NULL = NULL", ileft); else log_debug ("num: NULL ^ %ld = NULL", iright); #endif if (sleft && sright) { *result = ileft ^ iright; return 1; } return 0; case expr_client_state: if (client_state) { #if defined (DEBUG_EXPRESSIONS) log_debug ("num: client-state = %d", client_state -> state); #endif *result = client_state -> state; return 1; } else { #if defined (DEBUG_EXPRESSIONS) log_debug ("num: client-state = NULL"); #endif return 0; } case expr_function: log_error ("function definition in evaluate_numeric_expr"); return 0; case expr_arg: break; default: log_fatal("Impossible case at %s:%d. Undefined operator " "%d.", MDL, expr->op); break; } log_error ("evaluate_numeric_expression: bogus opcode %d", expr -> op); return 0; } /* * Return data hanging off of an option cache structure, or if there * isn't any, evaluate the expression hanging off of it and return the * result of that evaluation. There should never be both an expression * and a valid data_string. * * returns 0 if there wasn't an expression or it couldn't be evaluated * returns non-zero if there was an expression or string that was evaluated * When it returns zero the arguements, in particualr resutl, should not * be modified */ int evaluate_option_cache (result, packet, lease, client_state, in_options, cfg_options, scope, oc, file, line) struct data_string *result; struct packet *packet; struct lease *lease; struct client_state *client_state; struct option_state *in_options; struct option_state *cfg_options; struct binding_scope **scope; struct option_cache *oc; const char *file; int line; { if (oc->data.data != NULL) { data_string_copy (result, &oc -> data, file, line); return 1; } if (!oc -> expression) return 0; return evaluate_data_expression (result, packet, lease, client_state, in_options, cfg_options, scope, oc -> expression, file, line); } /* Evaluate an option cache and extract a boolean from the result. * The boolean option cache is actually a trinary value where: * * 0 = return 0, ignore parameter 0 (also the case for no data) * 1 = return 1, ignore parameter 0 * 2 = return 0, ignore parameter 1 * * This supports both classic boolean flags on/off as well as the * allow/deny/ignore keywords */ int evaluate_boolean_option_cache (ignorep, packet, lease, client_state, in_options, cfg_options, scope, oc, file, line) int *ignorep; struct packet *packet; struct lease *lease; struct client_state *client_state; struct option_state *in_options; struct option_state *cfg_options; struct binding_scope **scope; struct option_cache *oc; const char *file; int line; { int result = 0; if (ignorep) *ignorep = 0; /* Only attempt to evaluate if option_cache is not null. This permits * us to be called with option_lookup() as an argument. */ if (oc && in_options) { struct data_string ds; memset(&ds, 0, sizeof ds); if (evaluate_option_cache(&ds, packet, lease, client_state, in_options, cfg_options, scope, oc, file, line)) { /* We have a value for the option set result and * ignore parameter accordingly. */ if (ds.len) { if (ds.data[0] == 1) result = 1; else if ((ds.data[0] == 2) && (ignorep != NULL)) *ignorep = 1; } data_string_forget(&ds, MDL); } } return (result); } /* Evaluate a boolean expression and return the result of the evaluation, or FALSE if it failed. */ int evaluate_boolean_expression_result (ignorep, packet, lease, client_state, in_options, cfg_options, scope, expr) int *ignorep; struct packet *packet; struct lease *lease; struct client_state *client_state; struct option_state *in_options; struct option_state *cfg_options; struct binding_scope **scope; struct expression *expr; { int result; /* So that we can be called with option_lookup as an argument. */ if (!expr) return 0; if (!evaluate_boolean_expression (&result, packet, lease, client_state, in_options, cfg_options, scope, expr)) return 0; if (result == 2) { *ignorep = 1; result = 0; } else *ignorep = 0; return result; } /* Dereference an expression node, and if the reference count goes to zero, dereference any data it refers to, and then free it. */ void expression_dereference (eptr, file, line) struct expression **eptr; const char *file; int line; { struct expression *expr = *eptr; /* Zero the pointer. */ *eptr = (struct expression *)0; /* Decrement the reference count. If it's nonzero, we're done. */ --(expr -> refcnt); rc_register (file, line, eptr, expr, expr -> refcnt, 1, RC_MISC); if (expr -> refcnt > 0) return; if (expr -> refcnt < 0) { log_error ("%s(%d): negative refcnt!", file, line); #if defined (DEBUG_RC_HISTORY) dump_rc_history (expr); #endif #if defined (POINTER_DEBUG) abort (); #else return; #endif } /* Dereference subexpressions. */ switch (expr -> op) { /* All the binary operators can be handled the same way. */ case expr_equal: case expr_not_equal: case expr_regex_match: case expr_iregex_match: case expr_concat: case expr_and: case expr_or: case expr_add: case expr_subtract: case expr_multiply: case expr_divide: case expr_remainder: case expr_binary_and: case expr_binary_or: case expr_binary_xor: case expr_client_state: if (expr -> data.equal [0]) expression_dereference (&expr -> data.equal [0], file, line); if (expr -> data.equal [1]) expression_dereference (&expr -> data.equal [1], file, line); break; case expr_substring: if (expr -> data.substring.expr) expression_dereference (&expr -> data.substring.expr, file, line); if (expr -> data.substring.offset) expression_dereference (&expr -> data.substring.offset, file, line); if (expr -> data.substring.len) expression_dereference (&expr -> data.substring.len, file, line); break; case expr_suffix: if (expr -> data.suffix.expr) expression_dereference (&expr -> data.suffix.expr, file, line); if (expr -> data.suffix.len) expression_dereference (&expr -> data.suffix.len, file, line); break; case expr_lcase: if (expr->data.lcase) expression_dereference(&expr->data.lcase, MDL); break; case expr_ucase: if (expr->data.ucase) expression_dereference(&expr->data.ucase, MDL); break; case expr_not: if (expr -> data.not) expression_dereference (&expr -> data.not, file, line); break; case expr_packet: if (expr -> data.packet.offset) expression_dereference (&expr -> data.packet.offset, file, line); if (expr -> data.packet.len) expression_dereference (&expr -> data.packet.len, file, line); break; case expr_extract_int8: case expr_extract_int16: case expr_extract_int32: if (expr -> data.extract_int) expression_dereference (&expr -> data.extract_int, file, line); break; case expr_encode_int8: case expr_encode_int16: case expr_encode_int32: if (expr -> data.encode_int) expression_dereference (&expr -> data.encode_int, file, line); break; case expr_encapsulate: case expr_const_data: data_string_forget (&expr -> data.const_data, file, line); break; case expr_host_lookup: if (expr -> data.host_lookup) dns_host_entry_dereference (&expr -> data.host_lookup, file, line); break; case expr_binary_to_ascii: if (expr -> data.b2a.base) expression_dereference (&expr -> data.b2a.base, file, line); if (expr -> data.b2a.width) expression_dereference (&expr -> data.b2a.width, file, line); if (expr -> data.b2a.separator) expression_dereference (&expr -> data.b2a.separator, file, line); if (expr -> data.b2a.buffer) expression_dereference (&expr -> data.b2a.buffer, file, line); break; case expr_pick_first_value: if (expr -> data.pick_first_value.car) expression_dereference (&expr -> data.pick_first_value.car, file, line); if (expr -> data.pick_first_value.cdr) expression_dereference (&expr -> data.pick_first_value.cdr, file, line); break; case expr_reverse: if (expr -> data.reverse.width) expression_dereference (&expr -> data.reverse.width, file, line); if (expr -> data.reverse.buffer) expression_dereference (&expr -> data.reverse.buffer, file, line); break; case expr_variable_reference: case expr_variable_exists: if (expr -> data.variable) dfree (expr -> data.variable, file, line); break; case expr_funcall: if (expr -> data.funcall.name) dfree (expr -> data.funcall.name, file, line); if (expr -> data.funcall.arglist) expression_dereference (&expr -> data.funcall.arglist, file, line); break; case expr_arg: if (expr -> data.arg.val) expression_dereference (&expr -> data.arg.val, file, line); if (expr -> data.arg.next) expression_dereference (&expr -> data.arg.next, file, line); break; case expr_function: fundef_dereference (&expr -> data.func, file, line); break; case expr_v6relay: if (expr->data.v6relay.relay) expression_dereference(&expr->data.v6relay.relay, file, line); if (expr->data.v6relay.roption) expression_dereference(&expr->data.v6relay.roption, file, line); break; /* No subexpressions. */ case expr_leased_address: case expr_lease_time: case expr_filename: case expr_sname: case expr_const_int: case expr_check: case expr_option: case expr_hardware: case expr_exists: case expr_known: case expr_null: case expr_gethostname: break; default: break; } free_expression (expr, MDL); } int is_boolean_expression (expr) struct expression *expr; { return (expr -> op == expr_check || expr -> op == expr_exists || expr -> op == expr_variable_exists || expr -> op == expr_equal || expr -> op == expr_not_equal || expr->op == expr_regex_match || expr->op == expr_iregex_match || expr -> op == expr_and || expr -> op == expr_or || expr -> op == expr_not || expr -> op == expr_known || expr -> op == expr_static); } int is_data_expression (expr) struct expression *expr; { return (expr->op == expr_substring || expr->op == expr_suffix || expr->op == expr_lcase || expr->op == expr_ucase || expr->op == expr_option || expr->op == expr_hardware || expr->op == expr_const_data || expr->op == expr_packet || expr->op == expr_concat || expr->op == expr_encapsulate || expr->op == expr_encode_int8 || expr->op == expr_encode_int16 || expr->op == expr_encode_int32 || expr->op == expr_host_lookup || expr->op == expr_binary_to_ascii || expr->op == expr_filename || expr->op == expr_sname || expr->op == expr_reverse || expr->op == expr_pick_first_value || expr->op == expr_host_decl_name || expr->op == expr_leased_address || expr->op == expr_config_option || expr->op == expr_null || expr->op == expr_gethostname || expr->op == expr_v6relay); } int is_numeric_expression (expr) struct expression *expr; { return (expr -> op == expr_extract_int8 || expr -> op == expr_extract_int16 || expr -> op == expr_extract_int32 || expr -> op == expr_const_int || expr -> op == expr_lease_time || expr -> op == expr_add || expr -> op == expr_subtract || expr -> op == expr_multiply || expr -> op == expr_divide || expr -> op == expr_remainder || expr -> op == expr_binary_and || expr -> op == expr_binary_or || expr -> op == expr_binary_xor || expr -> op == expr_client_state); } int is_compound_expression (expr) struct expression *expr; { return (expr -> op == expr_substring || expr -> op == expr_suffix || expr -> op == expr_option || expr -> op == expr_concat || expr -> op == expr_encode_int8 || expr -> op == expr_encode_int16 || expr -> op == expr_encode_int32 || expr -> op == expr_binary_to_ascii || expr -> op == expr_reverse || expr -> op == expr_pick_first_value || expr -> op == expr_config_option || expr -> op == expr_extract_int8 || expr -> op == expr_extract_int16 || expr -> op == expr_extract_int32 || expr -> op == expr_v6relay); } static int op_val (enum expr_op); static int op_val (op) enum expr_op op; { switch (op) { case expr_none: case expr_match: case expr_static: case expr_check: case expr_substring: case expr_suffix: case expr_lcase: case expr_ucase: case expr_concat: case expr_encapsulate: case expr_host_lookup: case expr_not: case expr_option: case expr_hardware: case expr_packet: case expr_const_data: case expr_extract_int8: case expr_extract_int16: case expr_extract_int32: case expr_encode_int8: case expr_encode_int16: case expr_encode_int32: case expr_const_int: case expr_exists: case expr_variable_exists: case expr_known: case expr_binary_to_ascii: case expr_reverse: case expr_filename: case expr_sname: case expr_pick_first_value: case expr_host_decl_name: case expr_config_option: case expr_leased_address: case expr_lease_time: case expr_dns_transaction: case expr_null: case expr_variable_reference: case expr_ns_add: case expr_ns_delete: case expr_ns_exists: case expr_ns_not_exists: case expr_arg: case expr_funcall: case expr_function: /* XXXDPN: Need to assign sane precedences to these. */ case expr_binary_and: case expr_binary_or: case expr_binary_xor: case expr_client_state: case expr_gethostname: case expr_v6relay: case expr_concat_dclist: return 100; case expr_equal: case expr_not_equal: case expr_regex_match: case expr_iregex_match: return 4; case expr_or: case expr_and: return 3; case expr_add: case expr_subtract: return 2; case expr_multiply: case expr_divide: case expr_remainder: return 1; } return 100; } int op_precedence (op1, op2) enum expr_op op1, op2; { return op_val (op1) - op_val (op2); } enum expression_context expression_context (struct expression *expr) { if (is_data_expression (expr)) return context_data; if (is_numeric_expression (expr)) return context_numeric; if (is_boolean_expression (expr)) return context_boolean; return context_any; } enum expression_context op_context (op) enum expr_op op; { switch (op) { /* XXX Why aren't these specific? */ case expr_none: case expr_match: case expr_static: case expr_check: case expr_substring: case expr_suffix: case expr_lcase: case expr_ucase: case expr_concat: case expr_encapsulate: case expr_host_lookup: case expr_not: case expr_option: case expr_hardware: case expr_packet: case expr_const_data: case expr_extract_int8: case expr_extract_int16: case expr_extract_int32: case expr_encode_int8: case expr_encode_int16: case expr_encode_int32: case expr_const_int: case expr_exists: case expr_variable_exists: case expr_known: case expr_binary_to_ascii: case expr_reverse: case expr_filename: case expr_sname: case expr_pick_first_value: case expr_host_decl_name: case expr_config_option: case expr_leased_address: case expr_lease_time: case expr_null: case expr_variable_reference: case expr_ns_add: case expr_ns_delete: case expr_ns_exists: case expr_ns_not_exists: case expr_dns_transaction: case expr_arg: case expr_funcall: case expr_function: case expr_gethostname: case expr_v6relay: case expr_concat_dclist: return context_any; case expr_equal: case expr_not_equal: case expr_regex_match: case expr_iregex_match: return context_data; case expr_and: return context_boolean; case expr_or: return context_boolean; case expr_add: case expr_subtract: case expr_multiply: case expr_divide: case expr_remainder: case expr_binary_and: case expr_binary_or: case expr_binary_xor: case expr_client_state: return context_numeric; } return context_any; } int write_expression (file, expr, col, indent, firstp) FILE *file; struct expression *expr; int col; int indent; int firstp; { struct expression *e; const char *s; char obuf [65]; int scol; int width; /* If this promises to be a fat expression, start a new line. */ if (!firstp && is_compound_expression (expr)) { indent_spaces (file, indent); col = indent; } switch (expr -> op) { case expr_none: col = token_print_indent (file, col, indent, "", "", "null"); break; case expr_check: col = token_print_indent (file, col, indent, "", "", "check"); col = token_print_indent_concat (file, col, indent, " ", "", "\"", expr -> data.check -> name, "\"", (char *)0); break; case expr_regex_match: s = "~="; goto binary; case expr_iregex_match: s = "~~"; goto binary; case expr_not_equal: s = "!="; goto binary; case expr_equal: s = "="; binary: col = write_expression (file, expr -> data.equal [0], col, indent, 1); col = token_print_indent (file, col, indent, " ", " ", s); col = write_expression (file, expr -> data.equal [1], col, indent + 2, 0); break; case expr_substring: col = token_print_indent (file, col, indent, "", "", "substring"); col = token_print_indent (file, col, indent, " ", "", "("); scol = col; col = write_expression (file, expr -> data.substring.expr, col, scol, 1); col = token_print_indent (file, col, indent, "", " ", ","); col = write_expression (file, expr -> data.substring.offset, col, indent, 0); col = token_print_indent (file, col, scol, "", " ", ","); col = write_expression (file, expr -> data.substring.len, col, scol, 0); col = token_print_indent (file, col, indent, "", "", ")"); break; case expr_suffix: col = token_print_indent (file, col, indent, "", "", "suffix"); col = token_print_indent (file, col, indent, " ", "", "("); scol = col; col = write_expression (file, expr -> data.suffix.expr, col, scol, 1); col = token_print_indent (file, col, scol, "", " ", ","); col = write_expression (file, expr -> data.suffix.len, col, scol, 0); col = token_print_indent (file, col, indent, "", "", ")"); break; case expr_lcase: col = token_print_indent(file, col, indent, "", "", "lcase"); col = token_print_indent(file, col, indent, " ", "", "("); scol = col; col = write_expression(file, expr->data.lcase, col, scol, 1); col = token_print_indent(file, col, indent, "", "", ")"); break; case expr_ucase: col = token_print_indent(file, col, indent, "", "", "ucase"); col = token_print_indent(file, col, indent, " ", "", "("); scol = col; col = write_expression(file, expr->data.ucase, col, scol, 1); col = token_print_indent(file, col, indent, "", "", ")"); break; case expr_concat: e = expr; col = token_print_indent (file, col, indent, "", "", "concat"); col = token_print_indent (file, col, indent, " ", "", "("); scol = col; firstp = 1; concat_again: col = write_expression (file, e -> data.concat [0], col, scol, firstp); firstp = 0; if (!e -> data.concat [1]) goto no_concat_cdr; col = token_print_indent (file, col, scol, "", " ", ","); if (e -> data.concat [1] -> op == expr_concat) { e = e -> data.concat [1]; goto concat_again; } col = write_expression (file, e -> data.concat [1], col, scol, 0); no_concat_cdr: col = token_print_indent (file, col, indent, "", "", ")"); break; case expr_host_lookup: col = token_print_indent (file, col, indent, "", "", "gethostbyname"); col = token_print_indent (file, col, indent, " ", "", "("); col = token_print_indent_concat (file, col, indent, "", "", "\"", expr -> data.host_lookup -> hostname, "\"", (char *)0); col = token_print_indent (file, col, indent, "", "", ")"); break; case expr_add: s = "+"; goto binary; case expr_subtract: s = "-"; goto binary; case expr_multiply: s = "*"; goto binary; case expr_divide: s = "/"; goto binary; case expr_remainder: s = "%"; goto binary; case expr_binary_and: s = "&"; goto binary; case expr_binary_or: s = "|"; goto binary; case expr_binary_xor: s = "^"; goto binary; case expr_and: s = "and"; goto binary; case expr_or: s = "or"; goto binary; case expr_not: col = token_print_indent (file, col, indent, "", " ", "not"); col = write_expression (file, expr -> data.not, col, indent + 2, 1); break; case expr_option: s = "option"; print_option_name: col = token_print_indent (file, col, indent, "", "", s); if (expr -> data.option -> universe != &dhcp_universe) { col = token_print_indent (file, col, indent, " ", "", (expr -> data.option -> universe -> name)); col = token_print_indent (file, col, indent, "", "", "."); col = token_print_indent (file, col, indent, "", "", expr -> data.option -> name); } else { col = token_print_indent (file, col, indent, " ", "", expr -> data.option -> name); } break; case expr_hardware: col = token_print_indent (file, col, indent, "", "", "hardware"); break; case expr_packet: col = token_print_indent (file, col, indent, "", "", "packet"); col = token_print_indent (file, col, indent, " ", "", "("); scol = col; col = write_expression (file, expr -> data.packet.offset, col, indent, 1); col = token_print_indent (file, col, scol, "", " ", ","); col = write_expression (file, expr -> data.packet.len, col, scol, 0); col = token_print_indent (file, col, indent, "", "", ")"); break; case expr_const_data: col = token_indent_data_string (file, col, indent, "", "", &expr -> data.const_data); break; case expr_extract_int8: width = 8; extract_int: col = token_print_indent (file, col, indent, "", "", "extract-int"); col = token_print_indent (file, col, indent, " ", "", "("); scol = col; col = write_expression (file, expr -> data.extract_int, col, indent, 1); col = token_print_indent (file, col, scol, "", " ", ","); sprintf (obuf, "%d", width); col = token_print_indent (file, col, scol, " ", "", obuf); col = token_print_indent (file, col, indent, "", "", ")"); break; case expr_extract_int16: width = 16; goto extract_int; case expr_extract_int32: width = 32; goto extract_int; case expr_encode_int8: width = 8; encode_int: col = token_print_indent (file, col, indent, "", "", "encode-int"); col = token_print_indent (file, col, indent, " ", "", "("); scol = col; col = write_expression (file, expr -> data.extract_int, col, indent, 1); col = token_print_indent (file, col, scol, "", " ", ","); sprintf (obuf, "%d", width); col = token_print_indent (file, col, scol, " ", "", obuf); col = token_print_indent (file, col, indent, "", "", ")"); break; case expr_encode_int16: width = 16; goto encode_int; case expr_encode_int32: width = 32; goto encode_int; case expr_const_int: sprintf (obuf, "%lu", expr -> data.const_int); col = token_print_indent (file, col, indent, "", "", obuf); break; case expr_exists: s = "exists"; goto print_option_name; case expr_encapsulate: col = token_print_indent (file, col, indent, "", "", "encapsulate"); col = token_indent_data_string (file, col, indent, " ", "", &expr -> data.encapsulate); break; case expr_known: col = token_print_indent (file, col, indent, "", "", "known"); break; case expr_reverse: col = token_print_indent (file, col, indent, "", "", "reverse"); col = token_print_indent (file, col, indent, " ", "", "("); scol = col; col = write_expression (file, expr -> data.reverse.width, col, scol, 1); col = token_print_indent (file, col, scol, "", " ", ","); col = write_expression (file, expr -> data.reverse.buffer, col, scol, 0); col = token_print_indent (file, col, indent, "", "", ")"); break; case expr_leased_address: col = token_print_indent (file, col, indent, "", "", "leased-address"); break; case expr_client_state: col = token_print_indent (file, col, indent, "", "", "client-state"); break; case expr_binary_to_ascii: col = token_print_indent (file, col, indent, "", "", "binary-to-ascii"); col = token_print_indent (file, col, indent, " ", "", "("); scol = col; col = write_expression (file, expr -> data.b2a.base, col, scol, 1); col = token_print_indent (file, col, scol, "", " ", ","); col = write_expression (file, expr -> data.b2a.width, col, scol, 0); col = token_print_indent (file, col, scol, "", " ", ","); col = write_expression (file, expr -> data.b2a.separator, col, scol, 0); col = token_print_indent (file, col, scol, "", " ", ","); col = write_expression (file, expr -> data.b2a.buffer, col, scol, 0); col = token_print_indent (file, col, indent, "", "", ")"); break; case expr_config_option: s = "config-option"; goto print_option_name; case expr_host_decl_name: col = token_print_indent (file, col, indent, "", "", "host-decl-name"); break; case expr_pick_first_value: e = expr; col = token_print_indent (file, col, indent, "", "", "pick-first-value"); col = token_print_indent (file, col, indent, " ", "", "("); scol = col; firstp = 1; pick_again: col = write_expression (file, e -> data.pick_first_value.car, col, scol, firstp); firstp = 0; /* We're being very lisp-like right now - instead of representing this expression as (first middle . last) we're representing it as (first middle last), which means that the tail cdr is always nil. Apologies to non-wisp-lizards - may this obscure way of describing the problem motivate you to learn more about the one true computing language. */ if (!e -> data.pick_first_value.cdr) goto no_pick_cdr; col = token_print_indent (file, col, scol, "", " ", ","); if (e -> data.pick_first_value.cdr -> op == expr_pick_first_value) { e = e -> data.pick_first_value.cdr; goto pick_again; } col = write_expression (file, e -> data.pick_first_value.cdr, col, scol, 0); no_pick_cdr: col = token_print_indent (file, col, indent, "", "", ")"); break; case expr_lease_time: col = token_print_indent (file, col, indent, "", "", "lease-time"); break; case expr_static: col = token_print_indent (file, col, indent, "", "", "static"); break; case expr_null: col = token_print_indent (file, col, indent, "", "", "null"); break; case expr_variable_reference: col = token_print_indent (file, indent, indent, "", "", expr -> data.variable); break; case expr_variable_exists: col = token_print_indent (file, indent, indent, "", "", "defined"); col = token_print_indent (file, col, indent, " ", "", "("); col = token_print_indent (file, col, indent, "", "", expr -> data.variable); col = token_print_indent (file, col, indent, "", "", ")"); break; case expr_gethostname: col = token_print_indent(file, col, indent, "", "", "gethostname()"); break; case expr_funcall: col = token_print_indent(file, indent, indent, "", "", expr->data.funcall.name); col = token_print_indent(file, col, indent, " ", "", "("); firstp = 1; e = expr->data.funcall.arglist; while (e != NULL) { if (!firstp) col = token_print_indent(file, col, indent, "", " ", ","); col = write_expression(file, e->data.arg.val, col, indent, firstp); firstp = 0; e = e->data.arg.next; } col = token_print_indent(file, col, indent, "", "", ")"); break; case expr_v6relay: col = token_print_indent(file, col, indent, "", "", "v6relay"); col = token_print_indent(file, col, indent, " ", "", "("); scol = col; col = write_expression(file, expr->data.v6relay.relay, col, scol, 1); col = token_print_indent (file, col, scol, "", " ", ","); col = write_expression(file, expr->data.v6relay.roption, col, scol, 0); col = token_print_indent(file, col, indent, "", "", ")"); break; default: log_fatal ("invalid expression type in print_expression: %d", expr -> op); } return col; } struct binding *find_binding (struct binding_scope *scope, const char *name) { struct binding *bp; struct binding_scope *s; for (s = scope; s; s = s -> outer) { for (bp = s -> bindings; bp; bp = bp -> next) { if (!strcasecmp (name, bp -> name)) { return bp; } } } return (struct binding *)0; } int free_bindings (struct binding_scope *scope, const char *file, int line) { struct binding *bp, *next; for (bp = scope -> bindings; bp; bp = next) { next = bp -> next; if (bp -> name) dfree (bp -> name, file, line); if (bp -> value) binding_value_dereference (&bp -> value, file, line); dfree (bp, file, line); } scope -> bindings = (struct binding *)0; return 1; } int binding_scope_dereference (ptr, file, line) struct binding_scope **ptr; const char *file; int line; { struct binding_scope *binding_scope; if (!ptr || !*ptr) { log_error ("%s(%d): null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } binding_scope = *ptr; *ptr = (struct binding_scope *)0; --binding_scope -> refcnt; rc_register (file, line, ptr, binding_scope, binding_scope -> refcnt, 1, RC_MISC); if (binding_scope -> refcnt > 0) return 1; if (binding_scope -> refcnt < 0) { log_error ("%s(%d): negative refcnt!", file, line); #if defined (DEBUG_RC_HISTORY) dump_rc_history (binding_scope); #endif #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } free_bindings (binding_scope, file, line); if (binding_scope -> outer) binding_scope_dereference (&binding_scope -> outer, MDL); dfree (binding_scope, file, line); return 1; } int fundef_dereference (ptr, file, line) struct fundef **ptr; const char *file; int line; { struct fundef *bp; struct string_list *sp, *next; if ((ptr == NULL) || (*ptr == NULL)) { log_error ("%s(%d): null pointer", file, line); #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } bp = *ptr; bp -> refcnt--; rc_register (file, line, ptr, bp, bp -> refcnt, 1, RC_MISC); if (bp -> refcnt < 0) { log_error ("%s(%d): negative refcnt!", file, line); #if defined (DEBUG_RC_HISTORY) dump_rc_history (bp); #endif #if defined (POINTER_DEBUG) abort (); #else return 0; #endif } if (!bp -> refcnt) { for (sp = bp -> args; sp; sp = next) { next = sp -> next; dfree (sp, file, line); } if (bp -> statements) executable_statement_dereference (&bp -> statements, file, line); dfree (bp, file, line); } *ptr = (struct fundef *)0; return 1; } #if defined (NOTYET) /* Post 3.0 final. */ int data_subexpression_length (int *rv, struct expression *expr) { int crhs, clhs, llhs, lrhs; switch (expr -> op) { case expr_substring: if (expr -> data.substring.len && expr -> data.substring.len -> op == expr_const_int) { (*rv = (int)expr -> data.substring.len -> data.const_int); return 1; } return 0; case expr_packet: case expr_suffix: if (expr -> data.suffix.len && expr -> data.suffix.len -> op == expr_const_int) { (*rv = (int)expr -> data.suffix.len -> data.const_int); return 1; } return 0; case expr_lcase: return data_subexpression_length(rv, expr->data.lcase); case expr_ucase: return data_subexpression_length(rv, expr->data.ucase); case expr_concat: clhs = data_subexpression_length (&llhs, expr -> data.concat [0]); crhs = data_subexpression_length (&lrhs, expr -> data.concat [1]); if (crhs == 0 || clhs == 0) return 0; *rv = llhs + lrhs; return 1; break; case expr_hardware: return 0; case expr_const_data: *rv = expr -> data.const_data.len; return 2; case expr_reverse: return data_subexpression_length (rv, expr -> data.reverse.buffer); case expr_leased_address: case expr_lease_time: *rv = 4; return 2; case expr_pick_first_value: clhs = data_subexpression_length (&llhs, expr -> data.concat [0]); crhs = data_subexpression_length (&lrhs, expr -> data.concat [1]); if (crhs == 0 || clhs == 0) return 0; if (llhs > lrhs) *rv = llhs; else *rv = lrhs; return 1; case expr_v6relay: clhs = data_subexpression_length (&llhs, expr -> data.v6relay.relay); crhs = data_subexpression_length (&lrhs, expr -> data.v6relay.roption); if (crhs == 0 || clhs == 0) return 0; *rv = llhs + lrhs; return 1; break; case expr_binary_to_ascii: case expr_config_option: case expr_host_decl_name: case expr_encapsulate: case expr_filename: case expr_sname: case expr_host_lookup: case expr_option: case expr_none: case expr_match: case expr_check: case expr_equal: case expr_regex_match: case expr_iregex_match: case expr_and: case expr_or: case expr_not: case expr_extract_int8: case expr_extract_int16: case expr_extract_int32: case expr_encode_int8: case expr_encode_int16: case expr_encode_int32: case expr_const_int: case expr_exists: case expr_known: case expr_static: case expr_not_equal: case expr_null: case expr_variable_exists: case expr_variable_reference: case expr_arg: case expr_funcall: case expr_function: case expr_add: case expr_subtract: case expr_multiply: case expr_divide: case expr_remainder: case expr_binary_and: case expr_binary_or: case expr_binary_xor: case expr_client_state: case expr_gethostname: return 0; } return 0; } int expr_valid_for_context (struct expression *expr, enum expression_context context) { /* We don't know at parse time what type of value a function may return, so we can't flag an error on it. */ if (expr -> op == expr_funcall || expr -> op == expr_variable_reference) return 1; switch (context) { case context_any: return 1; case context_boolean: if (is_boolean_expression (expr)) return 1; return 0; case context_data: if (is_data_expression (expr)) return 1; return 0; case context_numeric: if (is_numeric_expression (expr)) return 1; return 0; case context_data_or_numeric: if (is_numeric_expression (expr) || is_data_expression (expr)) { return 1; } return 0; case context_function: if (expr -> op == expr_function) return 1; return 0; } return 0; } #endif /* NOTYET */ struct binding *create_binding (struct binding_scope **scope, const char *name) { struct binding *binding; if (!*scope) { if (!binding_scope_allocate (scope, MDL)) return (struct binding *)0; } binding = find_binding (*scope, name); if (!binding) { binding = dmalloc (sizeof *binding, MDL); if (!binding) return (struct binding *)0; memset (binding, 0, sizeof *binding); binding -> name = dmalloc (strlen (name) + 1, MDL); if (!binding -> name) { dfree (binding, MDL); return (struct binding *)0; } strcpy (binding -> name, name); binding -> next = (*scope) -> bindings; (*scope) -> bindings = binding; } return binding; } int bind_ds_value (struct binding_scope **scope, const char *name, struct data_string *value) { struct binding *binding; binding = create_binding (scope, name); if (!binding) return 0; if (binding -> value) binding_value_dereference (&binding -> value, MDL); if (!binding_value_allocate (&binding -> value, MDL)) return 0; data_string_copy (&binding -> value -> value.data, value, MDL); binding -> value -> type = binding_data; return 1; } int find_bound_string (struct data_string *value, struct binding_scope *scope, const char *name) { struct binding *binding; binding = find_binding (scope, name); if (!binding || !binding -> value || binding -> value -> type != binding_data) return 0; if (binding -> value -> value.data.terminated) { data_string_copy (value, &binding -> value -> value.data, MDL); } else { if (buffer_allocate (&value->buffer, binding->value->value.data.len, MDL) == 0) { return 0; } memcpy (value -> buffer -> data, binding -> value -> value.data.data, binding -> value -> value.data.len); value -> data = value -> buffer -> data; value -> len = binding -> value -> value.data.len; } return 1; } int unset (struct binding_scope *scope, const char *name) { struct binding *binding; binding = find_binding (scope, name); if (binding) { if (binding -> value) binding_value_dereference (&binding -> value, MDL); return 1; } return 0; } /*! * \brief Adds two Dc-formatted lists into a single Dc-formatted list * * Given two data_strings containing compressed lists, it constructs a * third data_string containing a single compressed list: * * 1. Decompressing the first list into a buffer * 2. Decompressing the second list onto the end of the buffer * 3. Compressing the buffer into the result * * If either list is empty, the result will be the equal to the compressed * content of the non-empty list. If both lists are empty, the result will * be an "empty" list: a 1 byte buffer containing 0x00. * * It relies on two functions to decompress and compress: * * - MRns_name_uncompress_list() - produces a null-terminated string of * comma-separated domain-names from a buffer containing "Dc" formatted * data * * - MRns_name_compress_list() - produces a buffer containing "Dc" formatted * data from a null-terminated string containing comma-separated domain-names * * \param result data_string which will contain the combined list * in Dc format * \param list1 data_string containing first Dc formatted list * \param list2 data_string containing second Dc formatted list * \return 0 if there is an error, the length of the new list when successful */ int concat_dclists (struct data_string* result, struct data_string* list1, struct data_string* list2) { char uncompbuf[32*NS_MAXCDNAME]; char *uncomp = uncompbuf; int uncomp_len = 0; int compbuf_max = 0; int list_len = 0; int i; /* If not empty, uncompress first list into the uncompressed buffer */ if (list1 && (list1->data) && (list1->len)) { list_len = MRns_name_uncompress_list(list1->data, list1->len, uncomp, sizeof(uncompbuf)); if (list_len < 0) { log_error ("concat_dclists:" " error decompressing domain list 1"); return (0); } uncomp_len = list_len; uncomp += list_len; } /* If not empty, uncompress second list into the uncompressed buffer */ if (list2 && (list2->data) && (list2->len)) { /* If first list wasn't empty, add a comma */ if (uncomp_len > 0) { *uncomp++ = ','; uncomp_len++; } list_len = MRns_name_uncompress_list(list2->data, list2->len, uncomp, (sizeof(uncompbuf) - uncomp_len)); if (list_len < 0) { log_error ("concat_dclists:" " error decompressing domain list 2"); return (0); } uncomp_len += list_len; uncomp += list_len; } /* If both lists were empty, return an "empty" result */ if (uncomp_len == 0) { if (!buffer_allocate (&result->buffer, 1, MDL)) { log_error ("concat_dclists: empty list allocate fail"); result->len = 0; return (0); } result->len = 1; result->data = result->buffer->data; return (1); } /* Estimate the buffer size needed for decompression. The largest * decompression would if one where there are no repeated portions, * (i.e. no compressions). Therefore that size should be the * decompressed string length + 2 for each comma + a final null. Each * dot gets replaced with a length byte and is accounted for in string * length. Mininum length is * uncomp_len + 3. */ compbuf_max = uncomp_len + 3; uncomp = uncompbuf; for (i = 0; i < uncomp_len; i++) if (*uncomp++ == ',') compbuf_max += 2; /* Allocate compression buffer based on estimated max */ if (!buffer_allocate (&result->buffer, compbuf_max, MDL)) { log_error ("concat_dclists: No memory for result"); result->len = 0; return (0); } /* Compress the combined list into result */ list_len = MRns_name_compress_list(uncompbuf, uncomp_len, result->buffer->data, compbuf_max); if (list_len <= 0) { log_error ("concat_dlists: error compressing result"); data_string_forget(result, MDL); result->len = 0; return (0); } /* Update result length to actual size */ result->len = list_len; result->data = result->buffer->data; return (list_len); } /* vim: set tabstop=8: */ dhcp-4.4.1/common/upf.c000644 000765 000024 00000024237 13243301226 015151 0ustar00tmarkstaff000000 000000 /* upf.c Ultrix PacketFilter interface code. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1996-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #if defined (USE_UPF_SEND) || defined (USE_UPF_RECEIVE) #include #include #include #include #include "includes/netinet/ip.h" #include "includes/netinet/udp.h" #include "includes/netinet/if_ether.h" /* Reinitializes the specified interface after an address change. This is not required for packet-filter APIs. */ #ifdef USE_UPF_SEND void if_reinitialize_send (info) struct interface_info *info; { } #endif #ifdef USE_UPF_RECEIVE void if_reinitialize_receive (info) struct interface_info *info; { } #endif /* Called by get_interface_list for each interface that's discovered. Opens a packet filter for each interface and adds it to the select mask. */ int if_register_upf (info) struct interface_info *info; { int sock; char filename[50]; int b; struct endevp param; /* Open a UPF device */ for (b = 0; 1; b++) { /* %Audit% Cannot exceed 36 bytes. %2004.06.17,Safe% */ sprintf(filename, "/dev/pf/pfilt%d", b); sock = open (filename, O_RDWR, 0); if (sock < 0) { if (errno == EBUSY) { continue; } else { log_fatal ("Can't find free upf: %m"); } } else { break; } } /* Set the UPF device to point at this interface. */ if (ioctl (sock, EIOCSETIF, info -> ifp) < 0) log_fatal ("Can't attach interface %s to upf device %s: %m", info -> name, filename); /* Get the hardware address. */ if (ioctl (sock, EIOCDEVP, ¶m) < 0) log_fatal ("Can't get interface %s hardware address: %m", info -> name); /* We only know how to do ethernet. */ if (param.end_dev_type != ENDT_10MB) log_fatal ("Invalid device type on network interface %s: %d", info -> name, param.end_dev_type); if (param.end_addr_len != 6) log_fatal ("Invalid hardware address length on %s: %d", info -> name, param.end_addr_len); info -> hw_address.hlen = 7; info -> hw_address.hbuf [0] = ARPHRD_ETHER; memcpy (&info -> hw_address.hbuf [1], param.end_addr, 6); return sock; } #endif /* USE_UPF_SEND || USE_UPF_RECEIVE */ #ifdef USE_UPF_SEND void if_register_send (info) struct interface_info *info; { /* If we're using the upf API for sending and receiving, we don't need to register this interface twice. */ #ifndef USE_UPF_RECEIVE info -> wfdesc = if_register_upf (info, interface); #else info -> wfdesc = info -> rfdesc; #endif if (!quiet_interface_discovery) log_info ("Sending on UPF/%s/%s%s%s", info -> name, print_hw_addr (info -> hw_address.hbuf [0], info -> hw_address.hlen - 1, &info -> hw_address.hbuf [1]), (info -> shared_network ? "/" : ""), (info -> shared_network ? info -> shared_network -> name : "")); } void if_deregister_send (info) struct interface_info *info; { #ifndef USE_UPF_RECEIVE close (info -> wfdesc); #endif info -> wfdesc = -1; if (!quiet_interface_discovery) log_info ("Disabling output on UPF/%s/%s%s%s", info -> name, print_hw_addr (info -> hw_address.hbuf [0], info -> hw_address.hlen - 1, &info -> hw_address.hbuf [1]), (info -> shared_network ? "/" : ""), (info -> shared_network ? info -> shared_network -> name : "")); } #endif /* USE_UPF_SEND */ #ifdef USE_UPF_RECEIVE /* Packet filter program... XXX Changes to the filter program may require changes to the constant offsets used in if_register_send to patch the UPF program! XXX */ #if defined(RELAY_PORT) #error "Relay port is not yet supported for UPF" #endif void if_register_receive (info) struct interface_info *info; { int flag = 1; u_int32_t addr; struct enfilter pf; u_int32_t bits; /* Open a UPF device and hang it on this interface... */ info -> rfdesc = if_register_upf (info); /* Allow the copyall flag to be set... */ if (ioctl(info -> rfdesc, EIOCALLOWCOPYALL, &flag) < 0) log_fatal ("Can't set ALLOWCOPYALL: %m"); /* Clear all the packet filter mode bits first... */ flag = (ENHOLDSIG | ENBATCH | ENTSTAMP | ENPROMISC | ENNONEXCL | ENCOPYALL); if (ioctl (info -> rfdesc, EIOCMBIC, &flag) < 0) log_fatal ("Can't clear pfilt bits: %m"); /* Set the ENBATCH and ENCOPYALL bits... */ bits = ENBATCH | ENCOPYALL; if (ioctl (info -> rfdesc, EIOCMBIS, &bits) < 0) log_fatal ("Can't set ENBATCH|ENCOPYALL: %m"); /* Set up the UPF filter program. */ /* XXX Unlike the BPF filter program, this one won't work if the XXX IP packet is fragmented or if there are options on the IP XXX header. */ pf.enf_Priority = 0; pf.enf_FilterLen = 0; pf.enf_Filter [pf.enf_FilterLen++] = ENF_PUSHWORD + 6; pf.enf_Filter [pf.enf_FilterLen++] = ENF_PUSHLIT + ENF_CAND; pf.enf_Filter [pf.enf_FilterLen++] = htons (ETHERTYPE_IP); pf.enf_Filter [pf.enf_FilterLen++] = ENF_PUSHLIT; pf.enf_Filter [pf.enf_FilterLen++] = htons (IPPROTO_UDP); pf.enf_Filter [pf.enf_FilterLen++] = ENF_PUSHWORD + 11; pf.enf_Filter [pf.enf_FilterLen++] = ENF_PUSHLIT + ENF_AND; pf.enf_Filter [pf.enf_FilterLen++] = htons (0xFF); pf.enf_Filter [pf.enf_FilterLen++] = ENF_CAND; pf.enf_Filter [pf.enf_FilterLen++] = ENF_PUSHWORD + 18; pf.enf_Filter [pf.enf_FilterLen++] = ENF_PUSHLIT + ENF_CAND; pf.enf_Filter [pf.enf_FilterLen++] = local_port; if (ioctl (info -> rfdesc, EIOCSETF, &pf) < 0) log_fatal ("Can't install packet filter program: %m"); if (!quiet_interface_discovery) log_info ("Listening on UPF/%s/%s%s%s", info -> name, print_hw_addr (info -> hw_address.hbuf [0], info -> hw_address.hlen - 1, &info -> hw_address.hbuf [1]), (info -> shared_network ? "/" : ""), (info -> shared_network ? info -> shared_network -> name : "")); } void if_deregister_receive (info) struct interface_info *info; { close (info -> rfdesc); info -> rfdesc = -1; if (!quiet_interface_discovery) log_info ("Disabling input on UPF/%s/%s%s%s", info -> name, print_hw_addr (info -> hw_address.hbuf [0], info -> hw_address.hlen - 1, &info -> hw_address.hbuf [1]), (info -> shared_network ? "/" : ""), (info -> shared_network ? info -> shared_network -> name : "")); } #endif /* USE_UPF_RECEIVE */ #ifdef USE_UPF_SEND ssize_t send_packet (interface, packet, raw, len, from, to, hto) struct interface_info *interface; struct packet *packet; struct dhcp_packet *raw; size_t len; struct in_addr from; struct sockaddr_in *to; struct hardware *hto; { unsigned hbufp = 0, ibufp = 0; double hw [4]; double ip [32]; struct iovec iov [3]; int result; int fudge; if (!strcmp (interface -> name, "fallback")) return send_fallback (interface, packet, raw, len, from, to, hto); if (hto == NULL && interface->anycast_mac_addr.hlen) hto = &interface->anycast_mac_addr; /* Assemble the headers... */ assemble_hw_header (interface, (unsigned char *)hw, &hbufp, hto); assemble_udp_ip_header (interface, (unsigned char *)ip, &ibufp, from.s_addr, to -> sin_addr.s_addr, to -> sin_port, (unsigned char *)raw, len); /* Fire it off */ iov [0].iov_base = ((char *)hw); iov [0].iov_len = hbufp; iov [1].iov_base = ((char *)ip); iov [1].iov_len = ibufp; iov [2].iov_base = (char *)raw; iov [2].iov_len = len; result = writev(interface -> wfdesc, iov, 3); if (result < 0) log_error ("send_packet: %m"); return result; } #endif /* USE_UPF_SEND */ #ifdef USE_UPF_RECEIVE ssize_t receive_packet (interface, buf, len, from, hfrom) struct interface_info *interface; unsigned char *buf; size_t len; struct sockaddr_in *from; struct hardware *hfrom; { int nread; int length = 0; int offset = 0; unsigned char ibuf [1500 + sizeof (struct enstamp)]; int bufix = 0; unsigned paylen; length = read (interface -> rfdesc, ibuf, sizeof ibuf); if (length <= 0) return length; bufix = sizeof (struct enstamp); /* Decode the physical header... */ offset = decode_hw_header (interface, ibuf, bufix, hfrom); /* If a physical layer checksum failed (dunno of any physical layer that supports this, but WTH), skip this packet. */ if (offset < 0) { return 0; } bufix += offset; length -= offset; /* Decode the IP and UDP headers... */ offset = decode_udp_ip_header (interface, ibuf, bufix, from, length, &paylen, 1); /* If the IP or UDP checksum was bad, skip the packet... */ if (offset < 0) return 0; bufix += offset; length -= offset; if (length < paylen) log_fatal("Internal inconsistency at %s:%d.", MDL); /* Copy out the data in the packet... */ memcpy (buf, &ibuf[bufix], paylen); return paylen; } int can_unicast_without_arp (ip) struct interface_info *ip; { return 1; } int can_receive_unicast_unconfigured (ip) struct interface_info *ip; { return 1; } int supports_multiple_interfaces (ip) struct interface_info *ip; { return 1; } void maybe_setup_fallback () { isc_result_t status; struct interface_info *fbi = (struct interface_info *)0; if (setup_fallback (&fbi, MDL)) { if_register_fallback (fbi); status = omapi_register_io_object ((omapi_object_t *)fbi, if_readsocket, 0, fallback_discard, 0, 0); if (status != ISC_R_SUCCESS) log_fatal ("Can't register I/O handle for %s: %s", fbi -> name, isc_result_totext (status)); interface_dereference (&fbi, MDL); } } #endif dhcp-4.4.1/common/tests/Atffile000644 000765 000024 00000000144 13243301226 016641 0ustar00tmarkstaff000000 000000 Content-Type: application/X-atf-atffile; version="1" prop: test-suite = dhcp4 tp-glob: *_unittest dhcp-4.4.1/common/tests/dns_unittest.c000644 000765 000024 00000013637 13243301226 020246 0ustar00tmarkstaff000000 000000 /* * Copyright (C) 2013-2017 Internet Systems Consortium, Inc. ("ISC") * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "dhcpd.h" /* * This file provides unit tests for the dns and ddns code. * Currently this is limited to verifying the dhcid code is * working properly. In time we may be able to expand the * tests to cover other areas. * * The tests for the interim txt records comapre to previous * internally generated values. * * The tests for the standard dhcid records compare to values * from rfc 4701 */ #if defined (NSUPDATE) char *name_1 = "chi6.example.com"; u_int8_t clid_1[] = {0x00, 0x01, 0x00, 0x06, 0x41, 0x2d, 0xf1, 0x66, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06}; u_int8_t std_result_1[] = {0x00, 0x02, 0x01, 0x63, 0x6f, 0xc0, 0xb8, 0x27, 0x1c, 0x82, 0x82, 0x5b, 0xb1, 0xac, 0x5c, 0x41, 0xcf, 0x53, 0x51, 0xaa, 0x69, 0xb4, 0xfe, 0xbd, 0x94, 0xe8, 0xf1, 0x7c, 0xdb, 0x95, 0x00, 0x0d, 0xa4, 0x8c, 0x40}; char *int_result_1 = "\"02abf8cd3753dc1847be40858becd77865"; char *name_2 = "chi.example.com"; u_int8_t clid_2[] = {0x01, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c}; u_int8_t std_result_2[] = {0x00, 0x01, 0x01, 0x39, 0x20, 0xfe, 0x5d, 0x1d, 0xce, 0xb3, 0xfd, 0x0b, 0xa3, 0x37, 0x97, 0x56, 0xa7, 0x0d, 0x73, 0xb1, 0x70, 0x09, 0xf4, 0x1d, 0x58, 0xbd, 0xdb, 0xfc, 0xd6, 0xa2, 0x50, 0x39, 0x56, 0xd8, 0xda}; char *int_result_2 = "\"31934ffa9344a3ab86c380505a671e5113"; char *name_3 = "client.example.com"; u_int8_t clid_3[] = {0x01, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06}; u_int8_t std_result_3[] = {0x00, 0x00, 0x01, 0xc4, 0xb9, 0xa5, 0xb2, 0x49, 0x65, 0x13, 0x43, 0x15, 0x8d, 0xde, 0x7b, 0xcc, 0x77, 0x16, 0x98, 0x41, 0xf7, 0xa4, 0x24, 0x3a, 0x57, 0x2b, 0x5c, 0x28, 0x3f, 0xff, 0xed, 0xeb, 0x3f, 0x75, 0xe6}; char *int_result_3 = "\"0046b6cacea62dc1d4567b068175d1f808"; void call_get_std_dhcid(int test, int type, u_int8_t *clid, unsigned clidlen, char *name, unsigned namelen, u_int8_t *dhcid, unsigned dhcid_len) { dhcp_ddns_cb_t ddns_cb; struct data_string *id; memset(&ddns_cb, 0, sizeof(ddns_cb)); ddns_cb.dhcid_class = dns_rdatatype_dhcid;; id = &ddns_cb.fwd_name; if (!buffer_allocate(&id->buffer, namelen, MDL)) atf_tc_fail("Unable to allocate buffer for std test %d", test); id->data = id->buffer->data; memcpy(id->buffer->data, name, namelen); id->len = namelen; if (get_dhcid(&ddns_cb, type, clid, clidlen) != 1) { atf_tc_fail("Unable to get std dhcid for %d", test); } else if (ddns_cb.dhcid_class != dns_rdatatype_dhcid) { atf_tc_fail("Wrong class for std dhcid for %d", test); } else if (ddns_cb.dhcid.len != dhcid_len) { atf_tc_fail("Wrong length for std dhcid for %d", test); } else if (memcmp(ddns_cb.dhcid.data, dhcid, dhcid_len) != 0) { atf_tc_fail("Wrong digest for std dhcid for %d", test); } /* clean up */ data_string_forget(&ddns_cb.dhcid, MDL); return; } ATF_TC(standard_dhcid); ATF_TC_HEAD(standard_dhcid, tc) { atf_tc_set_md_var(tc, "descr", "Verify standard dhcid construction."); } ATF_TC_BODY(standard_dhcid, tc) { call_get_std_dhcid(1, 2, clid_1, sizeof(clid_1), name_1, strlen(name_1), std_result_1, 35); call_get_std_dhcid(2, 1, clid_2, sizeof(clid_2), name_2, strlen(name_2), std_result_2, 35); call_get_std_dhcid(3, 0, clid_3, sizeof(clid_3), name_3, strlen(name_3), std_result_3, 35); } void call_get_int_dhcid(int test, int type, u_int8_t *clid, unsigned clidlen, char *dhcid, unsigned dhcid_len) { dhcp_ddns_cb_t ddns_cb; memset(&ddns_cb, 0, sizeof(ddns_cb)); ddns_cb.dhcid_class = dns_rdatatype_txt;; if (get_dhcid(&ddns_cb, type, clid, clidlen) != 1) { atf_tc_fail("Unable to get txt dhcid for %d", test); } else if (ddns_cb.dhcid_class != dns_rdatatype_txt) { atf_tc_fail("Wrong class for txt dhcid for %d", test); } else if (ddns_cb.dhcid.len != dhcid_len) { atf_tc_fail("Wrong length for txt dhcid for %d", test); } else if (memcmp(ddns_cb.dhcid.data, dhcid, dhcid_len) != 0) { atf_tc_fail("Wrong digest for txt dhcid for %d", test); } /* clean up */ data_string_forget(&ddns_cb.dhcid, MDL); return; } ATF_TC(interim_dhcid); ATF_TC_HEAD(interim_dhcid, tc) { atf_tc_set_md_var(tc, "descr", "Verify interim dhcid construction."); } ATF_TC_BODY(interim_dhcid, tc) { call_get_int_dhcid(1, 2, clid_1, sizeof(clid_1), int_result_1, 35); call_get_int_dhcid(2, DHO_DHCP_CLIENT_IDENTIFIER, clid_2, sizeof(clid_2), int_result_2, 35); call_get_int_dhcid(3, 0, clid_3, sizeof(clid_3), int_result_3, 35); } /* This macro defines main() method that will call specified test cases. tp and simple_test_case names can be whatever you want as long as it is a valid variable identifier. */ ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, interim_dhcid); ATF_TP_ADD_TC(tp, standard_dhcid); return (atf_no_error()); } #else /* NSUPDATE */ ATF_TC(untested); ATF_TC_HEAD(untested, tc) { atf_tc_set_md_var(tc, "descr", "skipping dns test"); } ATF_TC_BODY(untested, tc) { IGNORE_UNUSED(tc); atf_tc_skip("NSUPDATE is not defined"); } ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, untested); return (atf_no_error()); } #endif /* NSUPDATE */ dhcp-4.4.1/common/tests/Kyuafile000644 000765 000024 00000000352 13243301226 017041 0ustar00tmarkstaff000000 000000 syntax(2) test_suite('isc-dhcp') atf_test_program{name='alloc_unittest'} atf_test_program{name='dns_unittest'} atf_test_program{name='misc_unittest'} atf_test_program{name='ns_name_unittest'} atf_test_program{name='option_unittest'} dhcp-4.4.1/common/tests/Makefile.am000644 000765 000024 00000004056 13243301226 017406 0ustar00tmarkstaff000000 000000 SUBDIRS = . AM_CPPFLAGS = $(ATF_CFLAGS) -I$(top_srcdir)/includes EXTRA_DIST = Atffile Kyuafile ATF_TESTS = if HAVE_ATF ATF_TESTS += alloc_unittest dns_unittest misc_unittest ns_name_unittest \ option_unittest alloc_unittest_SOURCES = test_alloc.c $(top_srcdir)/tests/t_api_dhcp.c alloc_unittest_LDADD = $(ATF_LDFLAGS) alloc_unittest_LDADD += ../libdhcp.@A@ ../../omapip/libomapi.@A@ \ @BINDLIBIRSDIR@/libirs.@A@ \ @BINDLIBDNSDIR@/libdns.@A@ \ @BINDLIBISCCFGDIR@/libisccfg.@A@ \ @BINDLIBISCDIR@/libisc.@A@ dns_unittest_SOURCES = dns_unittest.c $(top_srcdir)/tests/t_api_dhcp.c dns_unittest_LDADD = $(ATF_LDFLAGS) dns_unittest_LDADD += ../libdhcp.@A@ ../../omapip/libomapi.@A@ \ @BINDLIBIRSDIR@/libirs.@A@ \ @BINDLIBDNSDIR@/libdns.@A@ \ @BINDLIBISCCFGDIR@/libisccfg.@A@ \ @BINDLIBISCDIR@/libisc.@A@ misc_unittest_SOURCES = misc_unittest.c $(top_srcdir)/tests/t_api_dhcp.c misc_unittest_LDADD = $(ATF_LDFLAGS) misc_unittest_LDADD += ../libdhcp.@A@ ../../omapip/libomapi.@A@ \ @BINDLIBIRSDIR@/libirs.@A@ \ @BINDLIBDNSDIR@/libdns.@A@ \ @BINDLIBISCCFGDIR@/libisccfg.@A@ \ @BINDLIBISCDIR@/libisc.@A@ ns_name_unittest_SOURCES = ns_name_test.c $(top_srcdir)/tests/t_api_dhcp.c ns_name_unittest_LDADD = $(ATF_LDFLAGS) ns_name_unittest_LDADD += ../libdhcp.@A@ ../../omapip/libomapi.@A@ \ @BINDLIBIRSDIR@/libirs.@A@ \ @BINDLIBDNSDIR@/libdns.@A@ \ @BINDLIBISCCFGDIR@/libisccfg.@A@ \ @BINDLIBISCDIR@/libisc.@A@ option_unittest_SOURCES = option_unittest.c $(top_srcdir)/tests/t_api_dhcp.c option_unittest_LDADD = $(ATF_LDFLAGS) option_unittest_LDADD += ../libdhcp.@A@ ../../omapip/libomapi.@A@ \ @BINDLIBIRSDIR@/libirs.@A@ \ @BINDLIBDNSDIR@/libdns.@A@ \ @BINDLIBISCCFGDIR@/libisccfg.@A@ \ @BINDLIBISCDIR@/libisc.@A@ check: $(ATF_TESTS) @if test $(top_srcdir) != ${top_builddir}; then \ cp $(top_srcdir)/common/tests/Atffile Atffile; \ cp $(top_srcdir)/common/tests/Kyuafile Kyuafile; \ fi sh ${top_builddir}/tests/unittest.sh distclean-local: @if test $(top_srcdir) != ${top_builddir}; then \ rm -f Atffile Kyuafile; \ fi endif check_PROGRAMS = $(ATF_TESTS) dhcp-4.4.1/common/tests/misc_unittest.c000644 000765 000024 00000015000 13243301226 020377 0ustar00tmarkstaff000000 000000 /* * Copyright (C) 2014-2017 Internet Systems Consortium, Inc. ("ISC") * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "dhcpd.h" struct basic_test { int count; int used_0; int used_25; int used_50; int used_75; int used_100; }; struct basic_test basic_values[] = {{ 0, 0, 0, 0, 0, 0}, { 10, 0, 2, 5, 7, 10}, { 20, 0, 5, 10, 15, 20}, { 50, 0, 12, 25, 37, 50}, { 100, 0, 25, 50, 75, 100}, { 1000, 0, 250, 500, 750, 1000}, { 10000, 0, 2500, 5000, 7500, 10000}, { 100000, 0, 25000, 50000, 75000, 100000}, { 1000000, 0, 250000, 500000, 750000, 1000000}, { 10000000, 0, 2500000, 5000000, 7500000, 10000000}, { 100000000, 0, 25000000, 50000000, 75000000, 100000000}, {1000000000, 0, 250000000, 500000000, 750000000, 1000000000}, {2000000000, 0, 500000000, 1000000000, 1500000000, 2000000000}, {-1, 0, 0, 0, 0, 0}}; ATF_TC(find_percent_basic); ATF_TC_HEAD(find_percent_basic, tc) { atf_tc_set_md_var(tc, "descr", "Verify basic percent calculation."); } /* This test does a simple check to see if we get the right value * given a count and a percnetage of that count */ ATF_TC_BODY(find_percent_basic, tc) { struct basic_test *basic_value; int used; for (basic_value = &basic_values[0]; basic_value->count != -1; basic_value++) { used = FIND_PERCENT(basic_value->count, 0); if (used != basic_value->used_0) { atf_tc_fail("Wrong value for 0 - count: %d, used %d", basic_value->count, used); } used = FIND_PERCENT(basic_value->count, 25); if (used != basic_value->used_25) { atf_tc_fail("Wrong value for 25 - count: %d, used %d", basic_value->count, used); } used = FIND_PERCENT(basic_value->count, 50); if (used != basic_value->used_50) { atf_tc_fail("Wrong value for 50 - count: %d, used %d", basic_value->count, used); } used = FIND_PERCENT(basic_value->count, 75); if (used != basic_value->used_75) { atf_tc_fail("Wrong value for 75 - count: %d, used %d", basic_value->count, used); } used = FIND_PERCENT(basic_value->count, 100); if (used != basic_value->used_100) { atf_tc_fail("Wrong value for 100 - count: %d, used %d", basic_value->count, used); } } return; } ATF_TC(find_percent_adv); ATF_TC_HEAD(find_percent_adv, tc) { atf_tc_set_md_var(tc, "descr", "Verify advanced percent calculation."); } /* This test tries some more complicated items, such as using the macro * in a function or passing expressions into macro */ ATF_TC_BODY(find_percent_adv, tc) { if (FIND_PERCENT(10*10, 10) != 10) { atf_tc_fail("Wrong value for adv 1"); } if (FIND_PERCENT(20*20, 80) != 320) { atf_tc_fail("Wrong value for adv 2"); } if (FIND_PERCENT(1000000*1000, 50) != 500000000) { atf_tc_fail("Wrong value for adv 3"); } if (FIND_PERCENT(100+100, 10) != 20) { atf_tc_fail("Wrong value for adv 4"); } if (FIND_PERCENT(1000+2000, 90) != 2700) { atf_tc_fail("Wrong value for adv 5"); } if (FIND_PERCENT(10000 - 8000, 70) != 1400) { atf_tc_fail("Wrong value for adv 6"); } if (FIND_PERCENT(2000000000 / 1000, 25) != 500000) { atf_tc_fail("Wrong value for adv 7"); } if (FIND_PERCENT(10*10, 10)/2 != 5) { atf_tc_fail("Wrong value for adv 8"); } if (FIND_PERCENT(100*10, 50) * 2 != 1000) { atf_tc_fail("Wrong value for adv 9"); } if (FIND_PERCENT(100*10, 50) * 2 > 1000) { atf_tc_fail("Wrong value for adv 10"); } if (FIND_PERCENT(100+100, 20) * 2 < 60) { atf_tc_fail("Wrong value for adv 11"); } return; } ATF_TC(print_hex_only); ATF_TC_HEAD(print_hex_only, tc) { atf_tc_set_md_var(tc, "descr", "Verify hex data formatting."); } /* This test exercises the print_hex_only function */ ATF_TC_BODY(print_hex_only, tc) { unsigned char data[] = {0xaa,0xbb,0xcc,0xdd}; char* ref = "aa:bb:cc:dd"; char buf[14]; memset(buf, 'x', sizeof(buf)); int data_len = sizeof(data); int expected_len = 12; /* Proper input values should produce proper result */ print_hex_only (data_len, data, expected_len, buf); if (strlen(buf) != strlen(ref)) { atf_tc_fail("len of result is wrong"); } if (strcmp(buf, ref)) { atf_tc_fail("result doesn't match ref"); } /* Make sure we didn't overrun the buffer */ if (buf[expected_len] != 'x') { atf_tc_fail("data over run detected"); } /* Buffer == null doesn't crash */ print_hex_only (data_len, data, expected_len, NULL); /* Limit == 0 doesn't write (or crash) */ *buf = '-'; print_hex_only (data_len, data, 0, buf); if (*buf != '-') { atf_tc_fail("limit of zero, altered buffer"); } /* data == NULL doesn't write (or crash) */ print_hex_only (data_len, NULL, expected_len, buf); if (*buf != '-') { atf_tc_fail("limit of zero, altered buffer"); } /* Limit too small should produce zero length string */ *buf = '-'; print_hex_only (data_len, data, expected_len - 1, buf); if (*buf != 0x0) { atf_tc_fail("limit too small should have failed"); } /* Data length of 0 should produce zero length string */ *buf = '-'; print_hex_only (0, data, expected_len, buf); if (*buf != 0x0) { atf_tc_fail("limit too small should have failed"); } } /* This macro defines main() method that will call specified test cases. tp and simple_test_case names can be whatever you want as long as it is a valid variable identifier. */ ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, find_percent_basic); ATF_TP_ADD_TC(tp, find_percent_adv); ATF_TP_ADD_TC(tp, print_hex_only); return (atf_no_error()); } dhcp-4.4.1/common/tests/ns_name_test.c000644 000765 000024 00000014556 13243301226 020203 0ustar00tmarkstaff000000 000000 /* * Copyright (C) 2014-2017 Internet Systems Consortium, Inc. ("ISC") * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* Tests the newly added functions: MRns_name_compress_list and * MRns_name_uncompress_list. These two functions rely on most of * the other functions in ns_name.c. If these tests pass, then the * majority of those functions work. * * This is not exhaustive test of these functions, much more could be * done. */ #include "config.h" #include #include "dhcpd.h" ATF_TC(MRns_name_list_funcs); ATF_TC_HEAD(MRns_name_list_funcs, tc) { atf_tc_set_md_var(tc, "descr", "MRns_name list funcs test, " "compress from text, decompress to text"); } void concat_lists (const char* label, struct data_string* list1, struct data_string* list2, const char *refbuf, size_t reflen); ATF_TC_BODY(MRns_name_list_funcs, tc) { const char text_list[] = "one.two.com,three.two.com,four.two.com"; unsigned char comp_list[] = { 0x03,0x6f,0x6e,0x65,0x03,0x74,0x77,0x6f,0x03,0x63,0x6f, 0x6d,0x00,0x05,0x74,0x68,0x72,0x65,0x65,0xc0,0x04,0x04, 0x66,0x6f,0x75,0x72,0xc0,0x04}; unsigned char compbuf[sizeof(comp_list)]; char textbuf[sizeof(text_list)]; int ret; memset(compbuf, 0x00, sizeof(compbuf)); /* Compress the reference text list */ ret = MRns_name_compress_list(text_list, sizeof(text_list), compbuf, sizeof(compbuf)); /* Verify compressed length is correct */ ATF_REQUIRE_MSG((ret == sizeof(compbuf)), "compressed len %d wrong", ret); /* Verify compressed content is correct */ ATF_REQUIRE_MSG((memcmp(comp_list, compbuf, sizeof(compbuf)) == 0), "compressed buffer content wrong"); /* Decompress the new compressed list */ ret = MRns_name_uncompress_list(compbuf, ret, textbuf, sizeof(textbuf)); /* Verify decompressed length is correct */ ATF_REQUIRE_MSG((ret == strlen(text_list)), "uncompressed len %d wrong", ret); /* Verify decompressed content is correct */ ATF_REQUIRE_MSG((memcmp(textbuf, text_list, sizeof(textbuf)) == 0), "uncompressed buffer content wrong"); } ATF_TC(concat_dclists); ATF_TC_HEAD(concat_dclists, tc) { atf_tc_set_md_var(tc, "descr", "concat_dclists function test, " "permutate concating empty and non-empty lists"); } ATF_TC_BODY(concat_dclists, tc) { /* Compressed list version of "booya.com" */ const char data[] = {0x05, 0x62, 0x6f, 0x6f, 0x79, 0x61, 0x03, 0x63, 0x6f, 0x6d, 0x00 }; /* Concatenation of data with itself */ const char data2[] = {0x05, 0x62, 0x6f, 0x6f, 0x79, 0x61, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0xc0, 0x00 }; struct data_string nonempty; struct data_string empty; /* Make a non-empty compressed list from data[] */ nonempty.len = sizeof(data); buffer_allocate(&(nonempty.buffer), nonempty.len, MDL); memcpy(nonempty.buffer->data, data, nonempty.len); nonempty.data = nonempty.buffer->data; /* Permutate NULL with non-empty list */ concat_lists("null + null", NULL, NULL, "", 1); concat_lists("null + nonempty", NULL, &nonempty, data, sizeof(data)); concat_lists("nonempty + null", &nonempty, NULL, data, sizeof(data)); /* Permutate zeroed-out list with non-empty list */ memset (&empty, 0x00, sizeof(struct data_string)); concat_lists("zero-list + zero-list", &empty, &empty, "", 1); concat_lists("zero-list + nonempty", &empty, &nonempty, data, sizeof(data)); concat_lists("nonempty + zero-list", &nonempty, &empty, data, sizeof(data)); /* Create an empty list which is a buffer with 1 null in it */ /* Make sure those work the same as zeroed out data_strings */ buffer_allocate (&empty.buffer, 1, MDL); empty.len = 1; empty.data = empty.buffer->data; /* Permutate with empty list */ concat_lists("empty + empty", &empty, &empty, "", 1); concat_lists("empty + nonempty", &empty, &nonempty, data, sizeof(data)); concat_lists("nonempty + empty", &nonempty, &empty, data, sizeof(data)); /* Permutate with list len of 0 */ empty.len = 0; concat_lists("zero-len + zero-len", &empty, &empty, "", 1); concat_lists("zero-len + nonempty", &empty, &nonempty, data, sizeof(data)); concat_lists("nonempty + zero-len", &nonempty, &empty, data, sizeof(data)); /* Lastly, make sure two non-empty lists concat correctly */ concat_lists("nonempty + nonempty", &nonempty, &nonempty, data2, sizeof(data2)); data_string_forget(&empty, MDL); data_string_forget(&nonempty, MDL); } /* Helper function which tests concatenating two compressed lists * * \param label text to print in error message * \param list1 data_string containing first list * \param list2 data_string containing second list * \param refbuf buffer containing the expected concatentated data * \param reflen length of the expected data */ void concat_lists (const char* label, struct data_string* list1, struct data_string* list2, const char *refbuf, size_t reflen) { struct data_string result; int rc; memset (&result, 0x00, sizeof(struct data_string)); rc = concat_dclists (&result, list1, list2); ATF_REQUIRE_MSG((rc >= 0), "%s concat_dclists call failed %d", label, rc); ATF_REQUIRE_MSG(result.len == reflen, "%s result len: %d incorrect", label, result.len); if (refbuf != NULL) { ATF_REQUIRE_MSG((memcmp(result.data, refbuf, reflen) == 0), "%s content is incorrect", label); } data_string_forget(&result, MDL); } ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, MRns_name_list_funcs); ATF_TP_ADD_TC(tp, concat_dclists); return (atf_no_error()); } dhcp-4.4.1/common/tests/option_unittest.c000644 000765 000024 00000010117 13243301226 020760 0ustar00tmarkstaff000000 000000 /* * Copyright (C) 2018 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "dhcpd.h" ATF_TC(option_refcnt); ATF_TC_HEAD(option_refcnt, tc) { atf_tc_set_md_var(tc, "descr", "Verify option reference count does not overflow."); } /* This test does a simple check to see if option reference count is * decremented even an error path exiting parse_option_buffer() */ ATF_TC_BODY(option_refcnt, tc) { struct option_state *options; struct option *option; unsigned code; int refcnt; unsigned char buffer[3] = { 15, 255, 0 }; initialize_common_option_spaces(); options = NULL; if (!option_state_allocate(&options, MDL)) { atf_tc_fail("can't allocate option state"); } option = NULL; code = 15; /* domain-name */ if (!option_code_hash_lookup(&option, dhcp_universe.code_hash, &code, 0, MDL)) { atf_tc_fail("can't find option 15"); } if (option == NULL) { atf_tc_fail("option is NULL"); } refcnt = option->refcnt; buffer[0] = 15; buffer[1] = 255; /* invalid */ buffer[2] = 0; if (parse_option_buffer(options, buffer, 3, &dhcp_universe)) { atf_tc_fail("parse_option_buffer is expected to fail"); } if (refcnt != option->refcnt) { atf_tc_fail("refcnt changed from %d to %d", refcnt, option->refcnt); } } ATF_TC(pretty_print_option); ATF_TC_HEAD(pretty_print_option, tc) { atf_tc_set_md_var(tc, "descr", "Verify pretty_print_option does not overrun its buffer."); } /* * This test verifies that pretty_print_option() will not overrun its * internal, static buffer when given large 'x/X' format options. * */ ATF_TC_BODY(pretty_print_option, tc) { struct option *option; unsigned code; unsigned char bad_data[32*1024]; unsigned char good_data[] = { 1,2,3,4,5,6 }; int emit_commas = 1; int emit_quotes = 1; const char *output_buf; /* Initialize whole thing to non-printable chars */ memset(bad_data, 0x1f, sizeof(bad_data)); initialize_common_option_spaces(); /* We'll use dhcp_client_identitifer because it happens to be format X */ code = 61; option = NULL; if (!option_code_hash_lookup(&option, dhcp_universe.code_hash, &code, 0, MDL)) { atf_tc_fail("can't find option %d", code); } if (option == NULL) { atf_tc_fail("option is NULL"); } /* First we will try a good value we know should fit. */ output_buf = pretty_print_option (option, good_data, sizeof(good_data), emit_commas, emit_quotes); /* Make sure we get what we expect */ if (!output_buf || strcmp(output_buf, "1:2:3:4:5:6")) { atf_tc_fail("pretty_print_option did not return \"\""); } /* Now we'll try a data value that's too large */ output_buf = pretty_print_option (option, bad_data, sizeof(bad_data), emit_commas, emit_quotes); /* Make sure we safely get an error */ if (!output_buf || strcmp(output_buf, "")) { atf_tc_fail("pretty_print_option did not return \"\""); } } /* This macro defines main() method that will call specified test cases. tp and simple_test_case names can be whatever you want as long as it is a valid variable identifier. */ ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, option_refcnt); ATF_TP_ADD_TC(tp, pretty_print_option); return (atf_no_error()); } dhcp-4.4.1/common/tests/test_alloc.c000644 000765 000024 00000042244 13243301226 017650 0ustar00tmarkstaff000000 000000 /* * Copyright (c) 2007-2017 by 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* * We test the functions provided in alloc.c here. These are very * basic functions, and it is very important that they work correctly. * * You can see two different styles of testing: * * - In the first, we have a single test for each function that tests * all of the possible ways it can operate. (This is the case for * the buffer tests.) * * - In the second, we have a separate test for each of the ways a * function can operate. (This is the case for the data_string * tests.) * * The advantage of a single test per function is that you have fewer * tests, and less duplicated and extra code. The advantage of having * a separate test is that each test is simpler. Plus if you need to * allow certain tests to fail for some reason (known bugs that are * hard to fix for example), then */ /** @TODO: dmalloc() test */ #include "config.h" #include #include "dhcpd.h" #include "omapip/alloc.h" static const char* checkString (struct data_string* ds, const char *src); ATF_TC(buffer_allocate); ATF_TC_HEAD(buffer_allocate, tc) { atf_tc_set_md_var(tc, "descr", "buffer_allocate basic test"); } ATF_TC_BODY(buffer_allocate, tc) { struct buffer *buf = 0; /* * Check a 0-length buffer. */ buf = NULL; if (!buffer_allocate(&buf, 0, MDL)) { atf_tc_fail("failed on 0-len buffer"); } if (!buffer_dereference(&buf, MDL)) { atf_tc_fail("buffer_dereference() failed"); } if (buf != NULL) { atf_tc_fail("buffer_dereference() did not NULL-out buffer"); } /* * Check an actual buffer. */ buf = NULL; if (!buffer_allocate(&buf, 100, MDL)) { atf_tc_fail("failed on allocate 100 bytes\n"); } if (!buffer_dereference(&buf, MDL)) { atf_tc_fail("buffer_dereference() failed"); } if (buf != NULL) { atf_tc_fail("buffer_dereference() did not NULL-out buffer"); } /* * Okay, we're happy. */ atf_tc_pass(); } ATF_TC(buffer_reference); ATF_TC_HEAD(buffer_reference, tc) { atf_tc_set_md_var(tc, "descr", "buffer_reference basic test"); } ATF_TC_BODY(buffer_reference, tc) { struct buffer *a, *b; /* * Create a buffer. */ a = NULL; if (!buffer_allocate(&a, 100, MDL)) { atf_tc_fail("failed on allocate 100 bytes"); } /** * Confirm buffer_reference() doesn't work if we pass in NULL. * * @TODO: we should confirm we get an error message here. */ if (buffer_reference(NULL, a, MDL)) { atf_tc_fail("succeeded on an error input"); } /** * @TODO: we should confirm we get an error message if we pass * a non-NULL target. */ /* * Confirm we work under normal circumstances. */ b = NULL; if (!buffer_reference(&b, a, MDL)) { atf_tc_fail("buffer_reference() failed"); } if (b != a) { atf_tc_fail("incorrect pointer returned"); } if (b->refcnt != 2) { atf_tc_fail("incorrect refcnt"); } /* * Clean up. */ if (!buffer_dereference(&b, MDL)) { atf_tc_fail("buffer_dereference() failed"); } if (!buffer_dereference(&a, MDL)) { atf_tc_fail("buffer_dereference() failed"); } } ATF_TC(buffer_dereference); ATF_TC_HEAD(buffer_dereference, tc) { atf_tc_set_md_var(tc, "descr", "buffer_dereference basic test"); } ATF_TC_BODY(buffer_dereference, tc) { struct buffer *a, *b; /** * Confirm buffer_dereference() doesn't work if we pass in NULL. * * TODO: we should confirm we get an error message here. */ if (buffer_dereference(NULL, MDL)) { atf_tc_fail("succeeded on an error input"); } /** * Confirm buffer_dereference() doesn't work if we pass in * a pointer to NULL. * * @TODO: we should confirm we get an error message here. */ a = NULL; if (buffer_dereference(&a, MDL)) { atf_tc_fail("succeeded on an error input"); } /* * Confirm we work under normal circumstances. */ a = NULL; if (!buffer_allocate(&a, 100, MDL)) { atf_tc_fail("failed on allocate"); } if (!buffer_dereference(&a, MDL)) { atf_tc_fail("buffer_dereference() failed"); } if (a != NULL) { atf_tc_fail("non-null buffer after buffer_dereference()"); } /** * Confirm we get an error from negative refcnt. * * @TODO: we should confirm we get an error message here. */ a = NULL; if (!buffer_allocate(&a, 100, MDL)) { atf_tc_fail("failed on allocate"); } b = NULL; if (!buffer_reference(&b, a, MDL)) { atf_tc_fail("buffer_reference() failed"); } a->refcnt = 0; /* purposely set to invalid value */ if (buffer_dereference(&a, MDL)) { atf_tc_fail("buffer_dereference() succeeded on error input"); } a->refcnt = 2; if (!buffer_dereference(&b, MDL)) { atf_tc_fail("buffer_dereference() failed"); } if (!buffer_dereference(&a, MDL)) { atf_tc_fail("buffer_dereference() failed"); } } ATF_TC(data_string_forget); ATF_TC_HEAD(data_string_forget, tc) { atf_tc_set_md_var(tc, "descr", "data_string_forget basic test"); } ATF_TC_BODY(data_string_forget, tc) { struct buffer *buf; struct data_string a; const char *str = "Lorem ipsum dolor sit amet turpis duis."; /* * Create the string we want to forget. */ memset(&a, 0, sizeof(a)); a.len = strlen(str); buf = NULL; if (!buffer_allocate(&buf, a.len, MDL)) { atf_tc_fail("out of memory"); } if (!buffer_reference(&a.buffer, buf, MDL)) { atf_tc_fail("buffer_reference() failed"); } a.data = a.buffer->data; memcpy(a.buffer->data, str, a.len); /* * Forget and confirm we've forgotten. */ data_string_forget(&a, MDL); if (a.len != 0) { atf_tc_fail("incorrect length"); } if (a.data != NULL) { atf_tc_fail("incorrect data"); } if (a.terminated) { atf_tc_fail("incorrect terminated"); } if (a.buffer != NULL) { atf_tc_fail("incorrect buffer"); } if (buf->refcnt != 1) { atf_tc_fail("too many references to buf"); } /* * Clean up buffer. */ if (!buffer_dereference(&buf, MDL)) { atf_tc_fail("buffer_reference() failed"); } } ATF_TC(data_string_forget_nobuf); ATF_TC_HEAD(data_string_forget_nobuf, tc) { atf_tc_set_md_var(tc, "descr", "data_string_forget test, " "data_string without buffer"); } ATF_TC_BODY(data_string_forget_nobuf, tc) { struct data_string a; const char *str = "Lorem ipsum dolor sit amet massa nunc."; /* * Create the string we want to forget. */ memset(&a, 0, sizeof(a)); a.len = strlen(str); a.data = (const unsigned char *)str; a.terminated = 1; /* * Forget and confirm we've forgotten. */ data_string_forget(&a, MDL); if (a.len != 0) { atf_tc_fail("incorrect length"); } if (a.data != NULL) { atf_tc_fail("incorrect data"); } if (a.terminated) { atf_tc_fail("incorrect terminated"); } if (a.buffer != NULL) { atf_tc_fail("incorrect buffer"); } } ATF_TC(data_string_copy); ATF_TC_HEAD(data_string_copy, tc) { atf_tc_set_md_var(tc, "descr", "data_string_copy basic test"); } ATF_TC_BODY(data_string_copy, tc) { struct data_string a, b; const char *str = "Lorem ipsum dolor sit amet orci aliquam."; /* * Create the string we want to copy. */ memset(&a, 0, sizeof(a)); a.len = strlen(str); if (!buffer_allocate(&a.buffer, a.len, MDL)) { atf_tc_fail("out of memory"); } a.data = a.buffer->data; memcpy(a.buffer->data, str, a.len); /* * Copy the string, and confirm it works. */ memset(&b, 0, sizeof(b)); data_string_copy(&b, &a, MDL); if (b.len != a.len) { atf_tc_fail("incorrect length"); } if (b.data != a.data) { atf_tc_fail("incorrect data"); } if (b.terminated != a.terminated) { atf_tc_fail("incorrect terminated"); } if (b.buffer != a.buffer) { atf_tc_fail("incorrect buffer"); } /* * Clean up. */ data_string_forget(&b, MDL); data_string_forget(&a, MDL); } ATF_TC(data_string_copy_nobuf); ATF_TC_HEAD(data_string_copy_nobuf, tc) { atf_tc_set_md_var(tc, "descr", "data_string_copy test, " "data_string without buffer"); } ATF_TC_BODY(data_string_copy_nobuf, tc) { struct data_string a, b; const char *str = "Lorem ipsum dolor sit amet cras amet."; /* * Create the string we want to copy. */ memset(&a, 0, sizeof(a)); a.len = strlen(str); a.data = (const unsigned char *)str; a.terminated = 1; /* * Copy the string, and confirm it works. */ memset(&b, 0, sizeof(b)); data_string_copy(&b, &a, MDL); if (b.len != a.len) { atf_tc_fail("incorrect length"); } if (b.data != a.data) { atf_tc_fail("incorrect data"); } if (b.terminated != a.terminated) { atf_tc_fail("incorrect terminated"); } if (b.buffer != a.buffer) { atf_tc_fail("incorrect buffer"); } /* * Clean up. */ data_string_forget(&b, MDL); data_string_forget(&a, MDL); } ATF_TC(data_string_new); ATF_TC_HEAD(data_string_new, tc) { atf_tc_set_md_var(tc, "descr", "data_string_new test, " "exercises data_string_new function"); } ATF_TC_BODY(data_string_new, tc) { struct data_string new_string; const char *src = "Really? Latin? ... geeks"; int len_arg = 0; const char *error; /* Case 1: Call with an invalid data_string pointer, should fail */ if (data_string_new(NULL, src, len_arg, MDL)) { atf_tc_fail("case 1: call should have failed"); } /* Case 2: Passing in NULL src should fail */ if (data_string_new(&new_string, NULL, 10, MDL)) { atf_tc_fail("case 2: did not return success"); } /* Case 3: Call with valid params, length includes NULL */ len_arg = strlen(src) + 1; if (data_string_new(&new_string, src, len_arg, MDL) == 0) { atf_tc_fail("case 3: did not return success"); } error = checkString(&new_string, src); ATF_REQUIRE_MSG((error == NULL), "case 3: %s", error); data_string_forget(&new_string, MDL); /* Case 4: Call with valid params, length does not include NULL */ len_arg = 7; if (data_string_new(&new_string, src, len_arg, MDL) == 0) { atf_tc_fail("case 4: did not return success"); } error = checkString(&new_string, "Really?"); ATF_REQUIRE_MSG((error == NULL), "case 4: %s", error); data_string_forget(&new_string, MDL); /* Case 5: Call with valid params, source string is "" */ len_arg = 0; if (data_string_new(&new_string, "", len_arg, MDL) == 0) { atf_tc_fail("case 5: did not return success"); } error = checkString(&new_string, ""); ATF_REQUIRE_MSG((error == NULL), "case 4: %s", error); data_string_forget(&new_string, MDL); } /* Helper function which tests validity of a data_string * * Verifies that the given data_string contains a null-terminated string * equal to a given string. * * \param string data_string to test * \param src text content string should contain * \return returns NULL if data_string is validate or an error message * describing why it is invalid */ const char* checkString (struct data_string* string, const char* src) { int src_len = strlen(src); if (string->buffer == NULL) { return ("buffer is NULL"); } if (string->data != string->buffer->data) { return ("data not set to buffer->data"); } if (string->len != src_len) { return ("len is wrong "); } if (string->terminated != 1) { return ("terminated flag not set"); } if (memcmp(string->data, src, src_len + 1)) { return ("data content wrong"); } return (NULL); } ATF_TC(data_string_terminate); ATF_TC_HEAD(data_string_terminate, tc) { atf_tc_set_md_var(tc, "descr", "data_string_terminate test, " "exercises data_string_terminate function"); } ATF_TC_BODY(data_string_terminate, tc) { struct data_string new_string, copy_string; const char *src = "Boring test string"; /* Case 1: Call with an already terminated string. The * original structure shouldn't be touched. */ memset(&new_string, 0, sizeof(new_string)); memset(©_string, 0, sizeof(copy_string)); if (data_string_new(&new_string, src, strlen(src), MDL) == 0) { atf_tc_fail("Case 1: unable to create new string"); } memcpy(©_string, &new_string, sizeof(new_string)); if (data_string_terminate(&new_string, MDL) == 0) { atf_tc_fail("Case 1: unable to terminate string"); } if (memcmp(©_string, &new_string, sizeof(new_string)) != 0) { atf_tc_fail("Case 1: structure modified"); } /* Case 2: Call with an unterminated string. The * original structure should be modified with a pointer * to new memory for the string. */ /* clear the termination flag, and shrink the string */ new_string.terminated = 0; new_string.len -= 2; memcpy(©_string, &new_string, sizeof(new_string)); if (data_string_terminate(&new_string, MDL) == 0) { atf_tc_fail("Case 2: unable to terminate string"); } /* We expect the same string but in a differnet block of memory */ if ((new_string.terminated == 0) || (&new_string.buffer == ©_string.buffer) || (new_string.len != copy_string.len) || memcmp(new_string.data, src, new_string.len) || new_string.data[new_string.len] != 0) { atf_tc_fail("Case 2: structure not modified correctly"); } /* get rid of the string, no need to get rid of copy as the * string memory was freed during the terminate call */ data_string_forget(&new_string, MDL); } void checkBuffer(size_t test_size, const char *file, int line) { char *buf; size_t max_size; /* Determine the maximum size we may have * Depending on configuration options we may be adding some * space to the allocated buffer for debugging purposes * so remove that as well. */ max_size = ((size_t)-1) - DMDSIZE; if (test_size > max_size) { atf_tc_skip("Test size greater than max size, %zu", test_size); return; } /* We allocate the buffer and then try to set the last character * to a known value. */ buf = dmalloc(test_size, file, line); if (buf != NULL) { buf[test_size - 1] = 1; if (buf[test_size - 1] != 1) atf_tc_fail("Value mismatch for index %zu", test_size); dfree(buf, file, line); } else { atf_tc_skip("Unable to allocate memory %zu", test_size); } } #if 0 /* The max test presents some issues for some systems, * leave it out for now */ ATF_TC(dmalloc_max32); ATF_TC_HEAD(dmalloc_max32, tc) { atf_tc_set_md_var(tc, "descr", "dmalloc_max32 test, " "dmalloc 0xFFFFFFFF"); } ATF_TC_BODY(dmalloc_max32, tc) { checkBuffer(0XFFFFFFFF, MDL); } #endif ATF_TC(dmalloc_med1); ATF_TC_HEAD(dmalloc_med1, tc) { atf_tc_set_md_var(tc, "descr", "dmalloc_med1 test, " "dmalloc 0x80000000,"); } ATF_TC_BODY(dmalloc_med1, tc) { checkBuffer(0x80000000, MDL); } ATF_TC(dmalloc_med2); ATF_TC_HEAD(dmalloc_med2, tc) { atf_tc_set_md_var(tc, "descr", "dmalloc_med2 test, " "dmalloc 0x7FFFFFFF, "); } ATF_TC_BODY(dmalloc_med2, tc) { checkBuffer(0x7FFFFFFF, MDL); } ATF_TC(dmalloc_med3); ATF_TC_HEAD(dmalloc_med3, tc) { atf_tc_set_md_var(tc, "descr", "dmalloc_med3 test, " "dmalloc 0x10000000,"); } ATF_TC_BODY(dmalloc_med3, tc) { checkBuffer(0x10000000, MDL); } ATF_TC(dmalloc_small); ATF_TC_HEAD(dmalloc_small, tc) { atf_tc_set_md_var(tc, "descr", "dmalloc_small test, " "dmalloc 0x0FFFFFFF"); } ATF_TC_BODY(dmalloc_small, tc) { checkBuffer(0X0FFFFFFF, MDL); } ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, buffer_allocate); ATF_TP_ADD_TC(tp, buffer_reference); ATF_TP_ADD_TC(tp, buffer_dereference); ATF_TP_ADD_TC(tp, data_string_forget); ATF_TP_ADD_TC(tp, data_string_forget_nobuf); ATF_TP_ADD_TC(tp, data_string_copy); ATF_TP_ADD_TC(tp, data_string_copy_nobuf); ATF_TP_ADD_TC(tp, data_string_new); ATF_TP_ADD_TC(tp, data_string_terminate); #if 0 ATF_TP_ADD_TC(tp, dmalloc_max32); #endif ATF_TP_ADD_TC(tp, dmalloc_med1); ATF_TP_ADD_TC(tp, dmalloc_med2); ATF_TP_ADD_TC(tp, dmalloc_med3); ATF_TP_ADD_TC(tp, dmalloc_small); return (atf_no_error()); } dhcp-4.4.1/client/client_tables.c000644 000765 000024 00000005624 13243301226 017154 0ustar00tmarkstaff000000 000000 /* client_tables.c Tables of information only used by client... */ /* * Copyright (c) 2017 by 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" struct universe client_universe; static struct option client_options[] = { /* @todo dummy-client-parm should be removed with the first real param */ { "dummy-client-parm", "T", &client_universe, 1, 1 }, { NULL, NULL, NULL, 0, 0 } }; #define CLIENT_HASH_SIZE (2*(sizeof(client_options) / sizeof(struct option))) void initialize_client_option_spaces() { int i; /* Set up the client option universe... */ client_universe.name = "client"; client_universe.concat_duplicates = 0; client_universe.lookup_func = lookup_hashed_option; client_universe.option_state_dereference = hashed_option_state_dereference; client_universe.save_func = save_hashed_option; client_universe.delete_func = delete_hashed_option; client_universe.encapsulate = hashed_option_space_encapsulate; client_universe.foreach = hashed_option_space_foreach; client_universe.length_size = 1; /* Never used ... */ client_universe.tag_size = 4; client_universe.store_tag = putUChar; client_universe.store_length = putUChar; client_universe.site_code_min = 0; client_universe.end = 0; client_universe.index = universe_count++; universes [client_universe.index] = &client_universe; if (!option_name_new_hash(&client_universe.name_hash, CLIENT_HASH_SIZE, MDL) || !option_code_new_hash(&client_universe.code_hash, CLIENT_HASH_SIZE, MDL)) log_fatal ("Can't allocate client option hash table."); for (i = 0 ; client_options[i].name ; i++) { option_code_hash_add(client_universe.code_hash, &client_options[i].code, 0, &client_options[i], MDL); option_name_hash_add(client_universe.name_hash, client_options[i].name, 0, &client_options[i], MDL); } /* Add the client option space to the option space hash. */ universe_hash_add (universe_hash, client_universe.name, 0, &client_universe, MDL); /* Make the client universe the configuration option universe. */ config_universe = &client_universe; } dhcp-4.4.1/client/clparse.c000644 000765 000024 00000161004 13243301226 015770 0ustar00tmarkstaff000000 000000 /* clparse.c Parser for dhclient config and lease files... */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1996-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "dhcpd.h" #include struct client_config top_level_config; #define NUM_DEFAULT_REQUESTED_OPTS 9 /* There can be 2 extra requested options for DHCPv4-over-DHCPv6. */ struct option *default_requested_options[NUM_DEFAULT_REQUESTED_OPTS + 2 + 1]; static void parse_client_default_duid(struct parse *cfile); static void parse_client6_lease_statement(struct parse *cfile); #ifdef DHCPv6 static struct dhc6_ia *parse_client6_ia_na_statement(struct parse *cfile); static struct dhc6_ia *parse_client6_ia_ta_statement(struct parse *cfile); static struct dhc6_ia *parse_client6_ia_pd_statement(struct parse *cfile); static struct dhc6_addr *parse_client6_iaaddr_statement(struct parse *cfile); static struct dhc6_addr *parse_client6_iaprefix_statement(struct parse *cfile); #endif /* DHCPv6 */ static void parse_lease_id_format (struct parse *cfile); /* client-conf-file :== client-declarations END_OF_FILE client-declarations :== | client-declaration | client-declarations client-declaration */ isc_result_t read_client_conf () { struct client_config *config; struct interface_info *ip; isc_result_t status; unsigned code; /* * TODO: LATER constant is very undescriptive. We should review it and * change it to something more descriptive or even better remove it * completely as it is currently not used. */ #ifdef LATER struct parse *parse = NULL; #endif /* Initialize the default request list. */ memset(default_requested_options, 0, sizeof(default_requested_options)); /* 1 */ code = DHO_SUBNET_MASK; option_code_hash_lookup(&default_requested_options[0], dhcp_universe.code_hash, &code, 0, MDL); /* 2 */ code = DHO_BROADCAST_ADDRESS; option_code_hash_lookup(&default_requested_options[1], dhcp_universe.code_hash, &code, 0, MDL); /* 3 */ code = DHO_TIME_OFFSET; option_code_hash_lookup(&default_requested_options[2], dhcp_universe.code_hash, &code, 0, MDL); /* 4 */ code = DHO_ROUTERS; option_code_hash_lookup(&default_requested_options[3], dhcp_universe.code_hash, &code, 0, MDL); /* 5 */ code = DHO_DOMAIN_NAME; option_code_hash_lookup(&default_requested_options[4], dhcp_universe.code_hash, &code, 0, MDL); /* 6 */ code = DHO_DOMAIN_NAME_SERVERS; option_code_hash_lookup(&default_requested_options[5], dhcp_universe.code_hash, &code, 0, MDL); /* 7 */ code = DHO_HOST_NAME; option_code_hash_lookup(&default_requested_options[6], dhcp_universe.code_hash, &code, 0, MDL); /* 8 */ code = D6O_NAME_SERVERS; option_code_hash_lookup(&default_requested_options[7], dhcpv6_universe.code_hash, &code, 0, MDL); /* 9 */ code = D6O_DOMAIN_SEARCH; option_code_hash_lookup(&default_requested_options[8], dhcpv6_universe.code_hash, &code, 0, MDL); for (code = 0 ; code < NUM_DEFAULT_REQUESTED_OPTS ; code++) { if (default_requested_options[code] == NULL) log_fatal("Unable to find option definition for " "index %u during default parameter request " "assembly.", code); } #ifdef DHCP4o6 /* DHCPv4-over-DHCPv6 extra requested options in code order */ if (dhcpv4_over_dhcpv6 == 1) { /* The DHCP4o6 server option should be requested */ code = D6O_DHCP4_O_DHCP6_SERVER; option_code_hash_lookup(&default_requested_options[9], dhcpv6_universe.code_hash, &code, 0, MDL); if (default_requested_options[9] == NULL) { log_fatal("Unable to find option definition for " "index %u during default parameter request " "assembly.", code); } } else if (dhcpv4_over_dhcpv6 > 1) { /* Called from run_stateless so the IRT should be requested too */ code = D6O_INFORMATION_REFRESH_TIME; option_code_hash_lookup(&default_requested_options[9], dhcpv6_universe.code_hash, &code, 0, MDL); if (default_requested_options[9] == NULL) { log_fatal("Unable to find option definition for " "index %u during default parameter request " "assembly.", code); } code = D6O_DHCP4_O_DHCP6_SERVER; option_code_hash_lookup(&default_requested_options[10], dhcpv6_universe.code_hash, &code, 0, MDL); if (default_requested_options[10] == NULL) { log_fatal("Unable to find option definition for " "index %u during default parameter request " "assembly.", code); } } #endif /* Initialize the top level client configuration. */ memset (&top_level_config, 0, sizeof top_level_config); /* Set some defaults... */ top_level_config.timeout = 60; top_level_config.select_interval = 0; top_level_config.reboot_timeout = 10; top_level_config.retry_interval = 300; top_level_config.backoff_cutoff = 15; top_level_config.initial_interval = 3; top_level_config.lease_id_format = TOKEN_OCTAL; /* * RFC 2131, section 4.4.1 specifies that the client SHOULD wait a * random time between 1 and 10 seconds. However, we choose to not * implement this default. If user is inclined to really have that * delay, he is welcome to do so, using 'initial-delay X;' parameter * in config file. */ top_level_config.initial_delay = 0; top_level_config.bootp_policy = P_ACCEPT; top_level_config.script_name = path_dhclient_script; top_level_config.requested_options = default_requested_options; top_level_config.omapi_port = -1; top_level_config.do_forward_update = 1; /* Requested lease time, used by DHCPv6 (DHCPv4 uses the option cache) */ top_level_config.requested_lease = 7200; group_allocate (&top_level_config.on_receipt, MDL); if (!top_level_config.on_receipt) log_fatal ("no memory for top-level on_receipt group"); group_allocate (&top_level_config.on_transmission, MDL); if (!top_level_config.on_transmission) log_fatal ("no memory for top-level on_transmission group"); status = read_client_conf_file (path_dhclient_conf, (struct interface_info *)0, &top_level_config); if (status != ISC_R_SUCCESS) { ; #ifdef LATER /* Set up the standard name service updater routine. */ status = new_parse(&parse, -1, default_client_config, sizeof(default_client_config) - 1, "default client configuration", 0); if (status != ISC_R_SUCCESS) log_fatal ("can't begin default client config!"); } if (parse != NULL) { do { token = peek_token(&val, NULL, cfile); if (token == END_OF_FILE) break; parse_client_statement(cfile, NULL, &top_level_config); } while (1); end_parse(&parse); #endif } /* Set up state and config structures for clients that don't have per-interface configuration statements. */ config = (struct client_config *)0; for (ip = interfaces; ip; ip = ip -> next) { if (!ip -> client) { ip -> client = (struct client_state *) dmalloc (sizeof (struct client_state), MDL); if (!ip -> client) log_fatal ("no memory for client state."); memset (ip -> client, 0, sizeof *(ip -> client)); ip -> client -> interface = ip; } if (!ip -> client -> config) { if (!config) { config = (struct client_config *) dmalloc (sizeof (struct client_config), MDL); if (!config) log_fatal ("no memory for client config."); memcpy (config, &top_level_config, sizeof top_level_config); } ip -> client -> config = config; } } return status; } int read_client_conf_file (const char *name, struct interface_info *ip, struct client_config *client) { int file; struct parse *cfile; const char *val; int token; isc_result_t status; if ((file = open (name, O_RDONLY)) < 0) return uerr2isc (errno); cfile = NULL; status = new_parse(&cfile, file, NULL, 0, path_dhclient_conf, 0); if (status != ISC_R_SUCCESS || cfile == NULL) return status; do { token = peek_token (&val, (unsigned *)0, cfile); if (token == END_OF_FILE) break; parse_client_statement (cfile, ip, client); } while (1); skip_token(&val, (unsigned *)0, cfile); status = (cfile -> warnings_occurred ? DHCP_R_BADPARSE : ISC_R_SUCCESS); end_parse (&cfile); return status; } /* lease-file :== client-lease-statements END_OF_FILE client-lease-statements :== | client-lease-statements LEASE client-lease-statement * This routine looks through a lease file and only tries to parse * the duid statements. */ void read_client_duid () { int file; isc_result_t status; struct parse *cfile; const char *val; int token; /* Open the lease file. If we can't open it, just return - we can safely trust the server to remember our state. */ if ((file = open (path_dhclient_duid, O_RDONLY)) < 0) return; cfile = NULL; status = new_parse(&cfile, file, NULL, 0, path_dhclient_duid, 0); if (status != ISC_R_SUCCESS || cfile == NULL) return; while ((token = next_token(&val, NULL, cfile)) != END_OF_FILE) { /* * All we care about is DUIDs - if we get anything else * just toss it and continue looking for DUIDs until we * run out of file. */ if (token == DEFAULT_DUID) { parse_client_default_duid(cfile); } } end_parse(&cfile); } /* lease-file :== client-lease-statements END_OF_FILE client-lease-statements :== | client-lease-statements LEASE client-lease-statement */ void read_client_leases () { int file; isc_result_t status; struct parse *cfile; const char *val; int token; /* Open the lease file. If we can't open it, just return - we can safely trust the server to remember our state. */ if ((file = open (path_dhclient_db, O_RDONLY)) < 0) return; cfile = NULL; status = new_parse(&cfile, file, NULL, 0, path_dhclient_db, 0); if (status != ISC_R_SUCCESS || cfile == NULL) return; do { token = next_token (&val, (unsigned *)0, cfile); if (token == END_OF_FILE) break; switch (token) { case DEFAULT_DUID: parse_client_default_duid(cfile); break; case LEASE: parse_client_lease_statement(cfile, 0); break; case LEASE6: parse_client6_lease_statement(cfile); break; default: log_error ("Corrupt lease file - possible data loss!"); skip_to_semi (cfile); break; } } while (1); end_parse (&cfile); } /* client-declaration :== SEND option-decl | DEFAULT option-decl | SUPERSEDE option-decl | PREPEND option-decl | APPEND option-decl | hardware-declaration | ALSO REQUEST option-list | ALSO REQUIRE option-list | REQUEST option-list | REQUIRE option-list | TIMEOUT number | RETRY number | REBOOT number | SELECT_TIMEOUT number | SCRIPT string | VENDOR_SPACE string | interface-declaration | LEASE client-lease-statement | ALIAS client-lease-statement | KEY key-definition */ void parse_client_statement (cfile, ip, config) struct parse *cfile; struct interface_info *ip; struct client_config *config; { int token; const char *val; struct option *option = NULL; struct executable_statement *stmt; int lose; char *name; enum policy policy; int known; int tmp, i; isc_result_t status; struct option ***append_list, **new_list, **cat_list; switch (peek_token (&val, (unsigned *)0, cfile)) { case INCLUDE: skip_token(&val, (unsigned *)0, cfile); token = next_token (&val, (unsigned *)0, cfile); if (token != STRING) { parse_warn (cfile, "filename string expected."); skip_to_semi (cfile); } else { status = read_client_conf_file (val, ip, config); if (status != ISC_R_SUCCESS) parse_warn (cfile, "%s: bad parse.", val); parse_semi (cfile); } return; case KEY: skip_token(&val, (unsigned *)0, cfile); if (ip) { /* This may seem arbitrary, but there's a reason for doing it: the authentication key database is not scoped. If we allow the user to declare a key other than in the outer scope, the user is very likely to believe that the key will only be used in that scope. If the user only wants the key to be used on one interface, because it's known that the other interface may be connected to an insecure net and the secret key is considered sensitive, we don't want to lull them into believing they've gotten their way. This is a bit contrived, but people tend not to be entirely rational about security. */ parse_warn (cfile, "key definition not allowed here."); skip_to_semi (cfile); break; } parse_key (cfile); return; case TOKEN_ALSO: /* consume ALSO */ skip_token(&val, NULL, cfile); /* consume type of ALSO list. */ token = next_token(&val, NULL, cfile); if (token == REQUEST) { append_list = &config->requested_options; } else if (token == REQUIRE) { append_list = &config->required_options; } else { parse_warn(cfile, "expected REQUEST or REQUIRE list"); skip_to_semi(cfile); return; } /* If there is no list, cut the concat short. */ if (*append_list == NULL) { parse_option_list(cfile, append_list); return; } /* Count the length of the existing list. */ for (i = 0 ; (*append_list)[i] != NULL ; i++) ; /* This space intentionally left blank. */ /* If there's no codes on the list, cut the concat short. */ if (i == 0) { parse_option_list(cfile, append_list); return; } tmp = parse_option_list(cfile, &new_list); if (tmp == 0 || new_list == NULL) return; /* Allocate 'i + tmp' buckets plus a terminator. */ cat_list = dmalloc(sizeof(struct option *) * (i + tmp + 1), MDL); if (cat_list == NULL) { log_error("Unable to allocate memory for new " "request list."); skip_to_semi(cfile); return; } for (i = 0 ; (*append_list)[i] != NULL ; i++) option_reference(&cat_list[i], (*append_list)[i], MDL); tmp = i; for (i = 0 ; new_list[i] != 0 ; i++) option_reference(&cat_list[tmp++], new_list[i], MDL); cat_list[tmp] = 0; /* XXX: We cannot free the old list, because it may have been * XXX: assigned from an outer configuration scope (or may be * XXX: the static default setting). */ *append_list = cat_list; return; /* REQUIRE can either start a policy statement or a comma-separated list of names of required options. */ case REQUIRE: skip_token(&val, (unsigned *)0, cfile); token = peek_token (&val, (unsigned *)0, cfile); if (token == AUTHENTICATION) { policy = P_REQUIRE; goto do_policy; } parse_option_list (cfile, &config -> required_options); return; case IGNORE: skip_token(&val, (unsigned *)0, cfile); policy = P_IGNORE; goto do_policy; case ACCEPT: skip_token(&val, (unsigned *)0, cfile); policy = P_ACCEPT; goto do_policy; case PREFER: skip_token(&val, (unsigned *)0, cfile); policy = P_PREFER; goto do_policy; case DONT: skip_token(&val, (unsigned *)0, cfile); policy = P_DONT; goto do_policy; do_policy: token = next_token (&val, (unsigned *)0, cfile); if (token == AUTHENTICATION) { if (policy != P_PREFER && policy != P_REQUIRE && policy != P_DONT) { parse_warn (cfile, "invalid authentication policy."); skip_to_semi (cfile); return; } config -> auth_policy = policy; } else if (token != TOKEN_BOOTP) { if (policy != P_PREFER && policy != P_IGNORE && policy != P_ACCEPT) { parse_warn (cfile, "invalid bootp policy."); skip_to_semi (cfile); return; } config -> bootp_policy = policy; } else { parse_warn (cfile, "expecting a policy type."); skip_to_semi (cfile); return; } break; case OPTION: skip_token(&val, (unsigned *)0, cfile); token = peek_token (&val, (unsigned *)0, cfile); if (token == SPACE) { if (ip) { parse_warn (cfile, "option space definitions %s", " may not be scoped."); skip_to_semi (cfile); break; } parse_option_space_decl (cfile); return; } known = 0; status = parse_option_name(cfile, 1, &known, &option); if (status != ISC_R_SUCCESS || option == NULL) return; token = next_token (&val, (unsigned *)0, cfile); if (token != CODE) { parse_warn (cfile, "expecting \"code\" keyword."); skip_to_semi (cfile); option_dereference(&option, MDL); return; } if (ip) { parse_warn (cfile, "option definitions may only appear in %s", "the outermost scope."); skip_to_semi (cfile); option_dereference(&option, MDL); return; } /* * If the option was known, remove it from the code and name * hash tables before redefining it. */ if (known) { option_name_hash_delete(option->universe->name_hash, option->name, 0, MDL); option_code_hash_delete(option->universe->code_hash, &option->code, 0, MDL); } parse_option_code_definition(cfile, option); option_dereference(&option, MDL); return; case MEDIA: skip_token(&val, (unsigned *)0, cfile); parse_string_list (cfile, &config -> media, 1); return; case HARDWARE: skip_token(&val, (unsigned *)0, cfile); if (ip) { parse_hardware_param (cfile, &ip -> hw_address); } else { parse_warn (cfile, "hardware address parameter %s", "not allowed here."); skip_to_semi (cfile); } return; case ANYCAST_MAC: skip_token(&val, NULL, cfile); if (ip != NULL) { parse_hardware_param(cfile, &ip->anycast_mac_addr); } else { parse_warn(cfile, "anycast mac address parameter " "not allowed here."); skip_to_semi (cfile); } return; case REQUEST: skip_token(&val, (unsigned *)0, cfile); if (config -> requested_options == default_requested_options) config -> requested_options = NULL; parse_option_list (cfile, &config -> requested_options); return; case TIMEOUT: skip_token(&val, (unsigned *)0, cfile); parse_lease_time (cfile, &config -> timeout); return; case RETRY: skip_token(&val, (unsigned *)0, cfile); parse_lease_time (cfile, &config -> retry_interval); return; case SELECT_TIMEOUT: skip_token(&val, (unsigned *)0, cfile); parse_lease_time (cfile, &config -> select_interval); return; case OMAPI: skip_token(&val, (unsigned *)0, cfile); token = next_token (&val, (unsigned *)0, cfile); if (token != PORT) { parse_warn (cfile, "unexpected omapi subtype: %s", val); skip_to_semi (cfile); return; } token = next_token (&val, (unsigned *)0, cfile); if (token != NUMBER) { parse_warn (cfile, "invalid port number: `%s'", val); skip_to_semi (cfile); return; } tmp = atoi (val); if (tmp < 0 || tmp > 65535) parse_warn (cfile, "invalid omapi port %d.", tmp); else if (config != &top_level_config) parse_warn (cfile, "omapi port only works at top level."); else config -> omapi_port = tmp; parse_semi (cfile); return; case DO_FORWARD_UPDATE: skip_token(&val, (unsigned *)0, cfile); token = next_token (&val, (unsigned *)0, cfile); if (!strcasecmp (val, "on") || !strcasecmp (val, "true")) config -> do_forward_update = 1; else if (!strcasecmp (val, "off") || !strcasecmp (val, "false")) config -> do_forward_update = 0; else { parse_warn (cfile, "expecting boolean value."); skip_to_semi (cfile); return; } parse_semi (cfile); return; case REBOOT: skip_token(&val, (unsigned *)0, cfile); parse_lease_time (cfile, &config -> reboot_timeout); return; case BACKOFF_CUTOFF: skip_token(&val, (unsigned *)0, cfile); parse_lease_time (cfile, &config -> backoff_cutoff); return; case INITIAL_INTERVAL: skip_token(&val, (unsigned *)0, cfile); parse_lease_time (cfile, &config -> initial_interval); return; case INITIAL_DELAY: skip_token(&val, (unsigned *)0, cfile); parse_lease_time (cfile, &config -> initial_delay); return; case SCRIPT: skip_token(&val, (unsigned *)0, cfile); parse_string (cfile, &config -> script_name, (unsigned *)0); return; case VENDOR: skip_token(&val, (unsigned *)0, cfile); token = next_token (&val, (unsigned *)0, cfile); if (token != OPTION) { parse_warn (cfile, "expecting 'vendor option space'"); skip_to_semi (cfile); return; } token = next_token (&val, (unsigned *)0, cfile); if (token != SPACE) { parse_warn (cfile, "expecting 'vendor option space'"); skip_to_semi (cfile); return; } token = next_token (&val, (unsigned *)0, cfile); if (!is_identifier (token)) { parse_warn (cfile, "expecting an identifier."); skip_to_semi (cfile); return; } config -> vendor_space_name = dmalloc (strlen (val) + 1, MDL); if (!config -> vendor_space_name) log_fatal ("no memory for vendor option space name."); strcpy (config -> vendor_space_name, val); for (i = 0; i < universe_count; i++) if (!strcmp (universes [i] -> name, config -> vendor_space_name)) break; if (i == universe_count) { log_error ("vendor option space %s not found.", config -> vendor_space_name); } parse_semi (cfile); return; case INTERFACE: skip_token(&val, (unsigned *)0, cfile); if (ip) parse_warn (cfile, "nested interface declaration."); parse_interface_declaration (cfile, config, (char *)0); return; case PSEUDO: skip_token(&val, (unsigned *)0, cfile); token = next_token (&val, (unsigned *)0, cfile); name = dmalloc (strlen (val) + 1, MDL); if (!name) log_fatal ("no memory for pseudo interface name"); strcpy (name, val); parse_interface_declaration (cfile, config, name); return; case LEASE: skip_token(&val, (unsigned *)0, cfile); parse_client_lease_statement (cfile, 1); return; case ALIAS: skip_token(&val, (unsigned *)0, cfile); parse_client_lease_statement (cfile, 2); return; case REJECT: skip_token(&val, (unsigned *)0, cfile); parse_reject_statement (cfile, config); return; case LEASE_ID_FORMAT: skip_token(&val, (unsigned *)0, cfile); parse_lease_id_format(cfile); break; default: lose = 0; stmt = (struct executable_statement *)0; if (!parse_executable_statement (&stmt, cfile, &lose, context_any)) { if (!lose) { parse_warn (cfile, "expecting a statement."); skip_to_semi (cfile); } } else { struct executable_statement **eptr, *sptr; if (stmt && (stmt -> op == send_option_statement || (stmt -> op == on_statement && (stmt -> data.on.evtypes & ON_TRANSMISSION)))) { eptr = &config -> on_transmission -> statements; if (stmt -> op == on_statement) { sptr = (struct executable_statement *)0; executable_statement_reference (&sptr, stmt -> data.on.statements, MDL); executable_statement_dereference (&stmt, MDL); executable_statement_reference (&stmt, sptr, MDL); executable_statement_dereference (&sptr, MDL); } } else eptr = &config -> on_receipt -> statements; if (stmt) { for (; *eptr; eptr = &(*eptr) -> next) ; executable_statement_reference (eptr, stmt, MDL); } return; } break; } parse_semi (cfile); } /* option-list :== option_name | option_list COMMA option_name */ int parse_option_list(struct parse *cfile, struct option ***list) { int ix; int token; const char *val; pair p = (pair)0, q = (pair)0, r; struct option *option = NULL; isc_result_t status; ix = 0; do { token = peek_token (&val, (unsigned *)0, cfile); if (token == SEMI) { token = next_token (&val, (unsigned *)0, cfile); break; } if (!is_identifier (token)) { parse_warn (cfile, "%s: expected option name.", val); skip_token(&val, (unsigned *)0, cfile); skip_to_semi (cfile); return 0; } status = parse_option_name(cfile, 0, NULL, &option); if (status != ISC_R_SUCCESS || option == NULL) { parse_warn (cfile, "%s: expected option name.", val); return 0; } r = new_pair (MDL); if (!r) log_fatal ("can't allocate pair for option code."); /* XXX: we should probably carry a reference across this */ r->car = (caddr_t)option; option_dereference(&option, MDL); r -> cdr = (pair)0; if (p) q -> cdr = r; else p = r; q = r; ++ix; token = next_token (&val, (unsigned *)0, cfile); } while (token == COMMA); if (token != SEMI) { parse_warn (cfile, "expecting semicolon."); skip_to_semi (cfile); return 0; } /* XXX we can't free the list here, because we may have copied XXX it from an outer config state. */ *list = NULL; if (ix) { *list = dmalloc ((ix + 1) * sizeof(struct option *), MDL); if (!*list) log_error ("no memory for option list."); else { ix = 0; for (q = p; q; q = q -> cdr) option_reference(&(*list)[ix++], (struct option *)q->car, MDL); (*list)[ix] = NULL; } while (p) { q = p -> cdr; free_pair (p, MDL); p = q; } } return ix; } /* interface-declaration :== INTERFACE string LBRACE client-declarations RBRACE */ void parse_interface_declaration (cfile, outer_config, name) struct parse *cfile; struct client_config *outer_config; char *name; { int token; const char *val; struct client_state *client, **cp; struct interface_info *ip = (struct interface_info *)0; token = next_token (&val, (unsigned *)0, cfile); if (token != STRING) { parse_warn (cfile, "expecting interface name (in quotes)."); skip_to_semi (cfile); return; } if (!interface_or_dummy (&ip, val)) log_fatal ("Can't allocate interface %s.", val); /* If we were given a name, this is a pseudo-interface. */ if (name) { make_client_state (&client); client -> name = name; client -> interface = ip; for (cp = &ip -> client; *cp; cp = &((*cp) -> next)) ; *cp = client; } else { if (!ip -> client) { make_client_state (&ip -> client); ip -> client -> interface = ip; } client = ip -> client; } if (!client -> config) make_client_config (client, outer_config); ip -> flags &= ~INTERFACE_AUTOMATIC; interfaces_requested = 1; token = next_token (&val, (unsigned *)0, cfile); if (token != LBRACE) { parse_warn (cfile, "expecting left brace."); skip_to_semi (cfile); return; } do { token = peek_token (&val, (unsigned *)0, cfile); if (token == END_OF_FILE) { parse_warn (cfile, "unterminated interface declaration."); return; } if (token == RBRACE) break; parse_client_statement (cfile, ip, client -> config); } while (1); skip_token(&val, (unsigned *)0, cfile); } int interface_or_dummy (struct interface_info **pi, const char *name) { struct interface_info *i; struct interface_info *ip = (struct interface_info *)0; isc_result_t status; /* Find the interface (if any) that matches the name. */ for (i = interfaces; i; i = i -> next) { if (!strcmp (i -> name, name)) { interface_reference (&ip, i, MDL); break; } } /* If it's not a real interface, see if it's on the dummy list. */ if (!ip) { for (ip = dummy_interfaces; ip; ip = ip -> next) { if (!strcmp (ip -> name, name)) { interface_reference (&ip, i, MDL); break; } } } /* If we didn't find an interface, make a dummy interface as a placeholder. */ if (!ip) { if ((status = interface_allocate (&ip, MDL)) != ISC_R_SUCCESS) log_fatal ("Can't record interface %s: %s", name, isc_result_totext (status)); if (strlen(name) >= sizeof(ip->name)) { interface_dereference(&ip, MDL); return 0; } strcpy(ip->name, name); if (dummy_interfaces) { interface_reference (&ip -> next, dummy_interfaces, MDL); interface_dereference (&dummy_interfaces, MDL); } interface_reference (&dummy_interfaces, ip, MDL); } if (pi) status = interface_reference (pi, ip, MDL); else status = ISC_R_FAILURE; interface_dereference (&ip, MDL); if (status != ISC_R_SUCCESS) return 0; return 1; } void make_client_state (state) struct client_state **state; { *state = ((struct client_state *)dmalloc (sizeof **state, MDL)); if (!*state) log_fatal ("no memory for client state\n"); memset (*state, 0, sizeof **state); } void make_client_config (client, config) struct client_state *client; struct client_config *config; { client -> config = (((struct client_config *) dmalloc (sizeof (struct client_config), MDL))); if (!client -> config) log_fatal ("no memory for client config\n"); memcpy (client -> config, config, sizeof *config); if (!clone_group (&client -> config -> on_receipt, config -> on_receipt, MDL) || !clone_group (&client -> config -> on_transmission, config -> on_transmission, MDL)) log_fatal ("no memory for client state groups."); } /* client-lease-statement :== LBRACE client-lease-declarations RBRACE client-lease-declarations :== | client-lease-declaration | client-lease-declarations client-lease-declaration */ void parse_client_lease_statement (cfile, is_static) struct parse *cfile; int is_static; { struct client_lease *lease, *lp, *pl, *next; struct interface_info *ip = (struct interface_info *)0; int token; const char *val; struct client_state *client = (struct client_state *)0; token = next_token (&val, (unsigned *)0, cfile); if (token != LBRACE) { parse_warn (cfile, "expecting left brace."); skip_to_semi (cfile); return; } lease = ((struct client_lease *) dmalloc (sizeof (struct client_lease), MDL)); if (!lease) log_fatal ("no memory for lease.\n"); memset (lease, 0, sizeof *lease); lease -> is_static = is_static; if (!option_state_allocate (&lease -> options, MDL)) log_fatal ("no memory for lease options.\n"); do { token = peek_token (&val, (unsigned *)0, cfile); if (token == END_OF_FILE) { parse_warn (cfile, "unterminated lease declaration."); return; } if (token == RBRACE) break; parse_client_lease_declaration (cfile, lease, &ip, &client); } while (1); skip_token(&val, (unsigned *)0, cfile); /* If the lease declaration didn't include an interface declaration that we recognized, it's of no use to us. */ if (!ip) { destroy_client_lease (lease); return; } /* Make sure there's a client state structure... */ if (!ip -> client) { make_client_state (&ip -> client); ip -> client -> interface = ip; } if (!client) client = ip -> client; /* If this is an alias lease, it doesn't need to be sorted in. */ if (is_static == 2) { ip -> client -> alias = lease; return; } /* The new lease may supersede a lease that's not the active lease but is still on the lease list, so scan the lease list looking for a lease with the same address, and if we find it, toss it. */ pl = (struct client_lease *)0; for (lp = client -> leases; lp; lp = next) { next = lp -> next; if (lp -> address.len == lease -> address.len && !memcmp (lp -> address.iabuf, lease -> address.iabuf, lease -> address.len)) { if (pl) pl -> next = next; else client -> leases = next; destroy_client_lease (lp); break; } else pl = lp; } /* If this is a preloaded lease, just put it on the list of recorded leases - don't make it the active lease. */ if (is_static) { lease -> next = client -> leases; client -> leases = lease; return; } /* The last lease in the lease file on a particular interface is the active lease for that interface. Of course, we don't know what the last lease in the file is until we've parsed the whole file, so at this point, we assume that the lease we just parsed is the active lease for its interface. If there's already an active lease for the interface, and this lease is for the same ip address, then we just toss the old active lease and replace it with this one. If this lease is for a different address, then if the old active lease has expired, we dump it; if not, we put it on the list of leases for this interface which are still valid but no longer active. */ if (client -> active) { if (client -> active -> expiry < cur_time) destroy_client_lease (client -> active); else if (client -> active -> address.len == lease -> address.len && !memcmp (client -> active -> address.iabuf, lease -> address.iabuf, lease -> address.len)) destroy_client_lease (client -> active); else { client -> active -> next = client -> leases; client -> leases = client -> active; } } client -> active = lease; /* phew. */ } /* client-lease-declaration :== BOOTP | INTERFACE string | FIXED_ADDR ip_address | FILENAME string | SERVER_NAME string | OPTION option-decl | RENEW time-decl | REBIND time-decl | EXPIRE time-decl | KEY id */ void parse_client_lease_declaration (cfile, lease, ipp, clientp) struct parse *cfile; struct client_lease *lease; struct interface_info **ipp; struct client_state **clientp; { int token; const char *val; struct interface_info *ip; struct option_cache *oc; struct client_state *client = (struct client_state *)0; switch (next_token (&val, (unsigned *)0, cfile)) { case KEY: token = next_token (&val, (unsigned *)0, cfile); if (token != STRING && !is_identifier (token)) { parse_warn (cfile, "expecting key name."); skip_to_semi (cfile); break; } if (omapi_auth_key_lookup_name (&lease -> key, val) != ISC_R_SUCCESS) parse_warn (cfile, "unknown key %s", val); parse_semi (cfile); break; case TOKEN_BOOTP: lease -> is_bootp = 1; break; case INTERFACE: token = next_token (&val, (unsigned *)0, cfile); if (token != STRING) { parse_warn (cfile, "expecting interface name (in quotes)."); skip_to_semi (cfile); break; } if (!interface_or_dummy (ipp, val)) log_fatal ("Can't allocate interface %s.", val); break; case NAME: token = next_token (&val, (unsigned *)0, cfile); ip = *ipp; if (!ip) { parse_warn (cfile, "state name precedes interface."); break; } for (client = ip -> client; client; client = client -> next) if (client -> name && !strcmp (client -> name, val)) break; if (!client) parse_warn (cfile, "lease specified for unknown pseudo."); *clientp = client; break; case FIXED_ADDR: if (!parse_ip_addr (cfile, &lease -> address)) return; break; case MEDIUM: parse_string_list (cfile, &lease -> medium, 0); return; case FILENAME: parse_string (cfile, &lease -> filename, (unsigned *)0); return; case SERVER_NAME: parse_string (cfile, &lease -> server_name, (unsigned *)0); return; case RENEW: lease -> renewal = parse_date (cfile); return; case REBIND: lease -> rebind = parse_date (cfile); return; case EXPIRE: lease -> expiry = parse_date (cfile); return; case OPTION: oc = (struct option_cache *)0; if (parse_option_decl (&oc, cfile)) { save_option(oc->option->universe, lease->options, oc); option_cache_dereference (&oc, MDL); } return; default: parse_warn (cfile, "expecting lease declaration."); skip_to_semi (cfile); break; } token = next_token (&val, (unsigned *)0, cfile); if (token != SEMI) { parse_warn (cfile, "expecting semicolon."); skip_to_semi (cfile); } } /* Parse a default-duid ""; statement. */ static void parse_client_default_duid(struct parse *cfile) { struct data_string new_duid; u_int8_t buf[128]; unsigned len; len = parse_X(cfile, buf, sizeof(buf)); if (len <= 2) { parse_warn(cfile, "Invalid DUID contents."); skip_to_semi(cfile); return; } memset(&new_duid, 0, sizeof(new_duid)); if (!buffer_allocate(&new_duid.buffer, len, MDL)) { parse_warn(cfile, "Out of memory parsing default DUID."); skip_to_semi(cfile); return; } new_duid.data = new_duid.buffer->data; new_duid.len = len; memcpy(new_duid.buffer->data, buf, len); /* Rotate the last entry into place. */ if (default_duid.buffer != NULL) data_string_forget(&default_duid, MDL); data_string_copy(&default_duid, &new_duid, MDL); data_string_forget(&new_duid, MDL); parse_semi(cfile); } /* Parse a lease6 {} construct. The v6 client is a little different * than the v4 client today, in that it only retains one lease, the * active lease, and discards any less recent information. It may * be useful in the future to cache additional information, but it * is not worth the effort for the moment. */ static void parse_client6_lease_statement(struct parse *cfile) { #if !defined(DHCPv6) parse_warn(cfile, "No DHCPv6 support."); skip_to_semi(cfile); #else /* defined(DHCPv6) */ struct option_cache *oc = NULL; struct dhc6_lease *lease; struct dhc6_ia **ia; struct client_state *client = NULL; struct interface_info *iface = NULL; struct data_string ds; const char *val; unsigned len; int token, has_ia, no_semi, has_name; token = next_token(NULL, NULL, cfile); if (token != LBRACE) { parse_warn(cfile, "Expecting open curly brace."); skip_to_semi(cfile); return; } lease = dmalloc(sizeof(*lease), MDL); if (lease == NULL) { parse_warn(cfile, "Unable to allocate lease state."); skip_to_rbrace(cfile, 1); return; } option_state_allocate(&lease->options, MDL); if (lease->options == NULL) { parse_warn(cfile, "Unable to allocate option cache."); skip_to_rbrace(cfile, 1); dfree(lease, MDL); return; } has_ia = 0; has_name = 0; ia = &lease->bindings; token = next_token(&val, NULL, cfile); while (token != RBRACE) { no_semi = 0; switch(token) { case IA_NA: *ia = parse_client6_ia_na_statement(cfile); if (*ia != NULL) { ia = &(*ia)->next; has_ia = 1; } no_semi = 1; break; case IA_TA: *ia = parse_client6_ia_ta_statement(cfile); if (*ia != NULL) { ia = &(*ia)->next; has_ia = 1; } no_semi = 1; break; case IA_PD: *ia = parse_client6_ia_pd_statement(cfile); if (*ia != NULL) { ia = &(*ia)->next; has_ia = 1; } no_semi = 1; break; case INTERFACE: if (iface != NULL) { parse_warn(cfile, "Multiple interface names?"); skip_to_semi(cfile); no_semi = 1; break; } token = next_token(&val, &len, cfile); if (token != STRING) { strerror: parse_warn(cfile, "Expecting a string."); skip_to_semi(cfile); no_semi = 1; break; } for (iface = interfaces ; iface != NULL ; iface = iface->next) { if (strcmp(iface->name, val) == 0) break; } if (iface == NULL) { parse_warn(cfile, "Unknown interface."); break; } break; case NAME: has_name = 1; if (client != NULL) { parse_warn(cfile, "Multiple state names?"); skip_to_semi(cfile); no_semi = 1; break; } if (iface == NULL) { parse_warn(cfile, "Client name without " "interface."); skip_to_semi(cfile); no_semi = 1; break; } token = next_token(&val, &len, cfile); if (token != STRING) goto strerror; for (client = iface->client ; client != NULL ; client = client->next) { if ((client->name != NULL) && (strcmp(client->name, val) == 0)) break; } if (client == NULL) { parse_warn(cfile, "Unknown client state %s.", val); break; } break; case OPTION: if (parse_option_decl(&oc, cfile)) { save_option(oc->option->universe, lease->options, oc); option_cache_dereference(&oc, MDL); } no_semi = 1; break; case TOKEN_RELEASED: case TOKEN_ABANDONED: lease->released = ISC_TRUE; break; default: parse_warn(cfile, "Unexpected token, %s.", val); no_semi = 1; skip_to_semi(cfile); break; } if (!no_semi) parse_semi(cfile); token = next_token(&val, NULL, cfile); if (token == END_OF_FILE) { parse_warn(cfile, "Unexpected end of file."); break; } } if (!has_ia) { log_debug("Lease with no IA's discarded from lease db."); dhc6_lease_destroy(&lease, MDL); return; } if (iface == NULL) parse_warn(cfile, "Lease has no interface designation."); else if (!has_name && (client == NULL)) { for (client = iface->client ; client != NULL ; client = client->next) { if (client->name == NULL) break; } } if (client == NULL) { parse_warn(cfile, "No matching client state."); dhc6_lease_destroy(&lease, MDL); return; } /* Fetch Preference option from option cache. */ memset(&ds, 0, sizeof(ds)); oc = lookup_option(&dhcpv6_universe, lease->options, D6O_PREFERENCE); if ((oc != NULL) && evaluate_option_cache(&ds, NULL, NULL, NULL, lease->options, NULL, &global_scope, oc, MDL)) { if (ds.len != 1) { log_error("Invalid length of DHCPv6 Preference option " "(%d != 1)", ds.len); data_string_forget(&ds, MDL); dhc6_lease_destroy(&lease, MDL); return; } else lease->pref = ds.data[0]; data_string_forget(&ds, MDL); } /* Fetch server-id option from option cache. */ oc = lookup_option(&dhcpv6_universe, lease->options, D6O_SERVERID); if ((oc == NULL) || !evaluate_option_cache(&lease->server_id, NULL, NULL, NULL, lease->options, NULL, &global_scope, oc, MDL) || (lease->server_id.len == 0)) { /* This should be impossible... */ log_error("Invalid SERVERID option cache."); dhc6_lease_destroy(&lease, MDL); return; } if (client->active_lease != NULL) dhc6_lease_destroy(&client->active_lease, MDL); client->active_lease = lease; #endif /* defined(DHCPv6) */ } /* Parse an ia_na object from the client lease. */ #ifdef DHCPv6 static struct dhc6_ia * parse_client6_ia_na_statement(struct parse *cfile) { struct option_cache *oc = NULL; struct dhc6_ia *ia; struct dhc6_addr **addr; const char *val; int token, no_semi, len; u_int8_t buf[5]; ia = dmalloc(sizeof(*ia), MDL); if (ia == NULL) { parse_warn(cfile, "Out of memory allocating IA_NA state."); skip_to_semi(cfile); return NULL; } ia->ia_type = D6O_IA_NA; /* Get IAID. */ len = parse_X(cfile, buf, 5); if (len == 4) { memcpy(ia->iaid, buf, 4); } else { parse_warn(cfile, "Expecting IAID of length 4, got %d.", len); skip_to_semi(cfile); dfree(ia, MDL); return NULL; } token = next_token(NULL, NULL, cfile); if (token != LBRACE) { parse_warn(cfile, "Expecting open curly brace."); skip_to_semi(cfile); dfree(ia, MDL); return NULL; } option_state_allocate(&ia->options, MDL); if (ia->options == NULL) { parse_warn(cfile, "Unable to allocate option state."); skip_to_rbrace(cfile, 1); dfree(ia, MDL); return NULL; } addr = &ia->addrs; token = next_token(&val, NULL, cfile); while (token != RBRACE) { no_semi = 0; switch (token) { case STARTS: token = next_token(&val, NULL, cfile); if (token == NUMBER) { ia->starts = atoi(val); } else { parse_warn(cfile, "Expecting a number."); skip_to_semi(cfile); no_semi = 1; } break; case RENEW: token = next_token(&val, NULL, cfile); if (token == NUMBER) { ia->renew = atoi(val); } else { parse_warn(cfile, "Expecting a number."); skip_to_semi(cfile); no_semi = 1; } break; case REBIND: token = next_token(&val, NULL, cfile); if (token == NUMBER) { ia->rebind = atoi(val); } else { parse_warn(cfile, "Expecting a number."); skip_to_semi(cfile); no_semi = 1; } break; case IAADDR: *addr = parse_client6_iaaddr_statement(cfile); if (*addr != NULL) addr = &(*addr)->next; no_semi = 1; break; case OPTION: if (parse_option_decl(&oc, cfile)) { save_option(oc->option->universe, ia->options, oc); option_cache_dereference(&oc, MDL); } no_semi = 1; break; default: parse_warn(cfile, "Unexpected token."); no_semi = 1; skip_to_semi(cfile); break; } if (!no_semi) parse_semi(cfile); token = next_token(&val, NULL, cfile); if (token == END_OF_FILE) { parse_warn(cfile, "Unexpected end of file."); break; } } return ia; } #endif /* DHCPv6 */ /* Parse an ia_ta object from the client lease. */ #ifdef DHCPv6 static struct dhc6_ia * parse_client6_ia_ta_statement(struct parse *cfile) { struct option_cache *oc = NULL; struct dhc6_ia *ia; struct dhc6_addr **addr; const char *val; int token, no_semi, len; u_int8_t buf[5]; ia = dmalloc(sizeof(*ia), MDL); if (ia == NULL) { parse_warn(cfile, "Out of memory allocating IA_TA state."); skip_to_semi(cfile); return NULL; } ia->ia_type = D6O_IA_TA; /* Get IAID. */ len = parse_X(cfile, buf, 5); if (len == 4) { memcpy(ia->iaid, buf, 4); } else { parse_warn(cfile, "Expecting IAID of length 4, got %d.", len); skip_to_semi(cfile); dfree(ia, MDL); return NULL; } token = next_token(NULL, NULL, cfile); if (token != LBRACE) { parse_warn(cfile, "Expecting open curly brace."); skip_to_semi(cfile); dfree(ia, MDL); return NULL; } option_state_allocate(&ia->options, MDL); if (ia->options == NULL) { parse_warn(cfile, "Unable to allocate option state."); skip_to_rbrace(cfile, 1); dfree(ia, MDL); return NULL; } addr = &ia->addrs; token = next_token(&val, NULL, cfile); while (token != RBRACE) { no_semi = 0; switch (token) { case STARTS: token = next_token(&val, NULL, cfile); if (token == NUMBER) { ia->starts = atoi(val); } else { parse_warn(cfile, "Expecting a number."); skip_to_semi(cfile); no_semi = 1; } break; /* No RENEW or REBIND */ case IAADDR: *addr = parse_client6_iaaddr_statement(cfile); if (*addr != NULL) addr = &(*addr)->next; no_semi = 1; break; case OPTION: if (parse_option_decl(&oc, cfile)) { save_option(oc->option->universe, ia->options, oc); option_cache_dereference(&oc, MDL); } no_semi = 1; break; default: parse_warn(cfile, "Unexpected token."); no_semi = 1; skip_to_semi(cfile); break; } if (!no_semi) parse_semi(cfile); token = next_token(&val, NULL, cfile); if (token == END_OF_FILE) { parse_warn(cfile, "Unexpected end of file."); break; } } return ia; } #endif /* DHCPv6 */ /* Parse an ia_pd object from the client lease. */ #ifdef DHCPv6 static struct dhc6_ia * parse_client6_ia_pd_statement(struct parse *cfile) { struct option_cache *oc = NULL; struct dhc6_ia *ia; struct dhc6_addr **pref; const char *val; int token, no_semi, len; u_int8_t buf[5]; ia = dmalloc(sizeof(*ia), MDL); if (ia == NULL) { parse_warn(cfile, "Out of memory allocating IA_PD state."); skip_to_semi(cfile); return NULL; } ia->ia_type = D6O_IA_PD; /* Get IAID. */ len = parse_X(cfile, buf, 5); if (len == 4) { memcpy(ia->iaid, buf, 4); } else { parse_warn(cfile, "Expecting IAID of length 4, got %d.", len); skip_to_semi(cfile); dfree(ia, MDL); return NULL; } token = next_token(NULL, NULL, cfile); if (token != LBRACE) { parse_warn(cfile, "Expecting open curly brace."); skip_to_semi(cfile); dfree(ia, MDL); return NULL; } option_state_allocate(&ia->options, MDL); if (ia->options == NULL) { parse_warn(cfile, "Unable to allocate option state."); skip_to_rbrace(cfile, 1); dfree(ia, MDL); return NULL; } pref = &ia->addrs; token = next_token(&val, NULL, cfile); while (token != RBRACE) { no_semi = 0; switch (token) { case STARTS: token = next_token(&val, NULL, cfile); if (token == NUMBER) { ia->starts = atoi(val); } else { parse_warn(cfile, "Expecting a number."); skip_to_semi(cfile); no_semi = 1; } break; case RENEW: token = next_token(&val, NULL, cfile); if (token == NUMBER) { ia->renew = atoi(val); } else { parse_warn(cfile, "Expecting a number."); skip_to_semi(cfile); no_semi = 1; } break; case REBIND: token = next_token(&val, NULL, cfile); if (token == NUMBER) { ia->rebind = atoi(val); } else { parse_warn(cfile, "Expecting a number."); skip_to_semi(cfile); no_semi = 1; } break; case IAPREFIX: *pref = parse_client6_iaprefix_statement(cfile); if (*pref != NULL) pref = &(*pref)->next; no_semi = 1; break; case OPTION: if (parse_option_decl(&oc, cfile)) { save_option(oc->option->universe, ia->options, oc); option_cache_dereference(&oc, MDL); } no_semi = 1; break; default: parse_warn(cfile, "Unexpected token."); no_semi = 1; skip_to_semi(cfile); break; } if (!no_semi) parse_semi(cfile); token = next_token(&val, NULL, cfile); if (token == END_OF_FILE) { parse_warn(cfile, "Unexpected end of file."); break; } } return ia; } #endif /* DHCPv6 */ /* Parse an iaaddr {} structure. */ #ifdef DHCPv6 static struct dhc6_addr * parse_client6_iaaddr_statement(struct parse *cfile) { struct option_cache *oc = NULL; struct dhc6_addr *addr; const char *val; int token, no_semi; addr = dmalloc(sizeof(*addr), MDL); if (addr == NULL) { parse_warn(cfile, "Unable to allocate IAADDR state."); skip_to_semi(cfile); return NULL; } /* Get IP address. */ if (!parse_ip6_addr(cfile, &addr->address)) { skip_to_semi(cfile); dfree(addr, MDL); return NULL; } token = next_token(NULL, NULL, cfile); if (token != LBRACE) { parse_warn(cfile, "Expecting open curly bracket."); skip_to_semi(cfile); dfree(addr, MDL); return NULL; } option_state_allocate(&addr->options, MDL); if (addr->options == NULL) { parse_warn(cfile, "Unable to allocate option state."); skip_to_semi(cfile); dfree(addr, MDL); return NULL; } token = next_token(&val, NULL, cfile); while (token != RBRACE) { no_semi = 0; switch (token) { case STARTS: token = next_token(&val, NULL, cfile); if (token == NUMBER) { addr->starts = atoi(val); } else { parse_warn(cfile, "Expecting a number."); skip_to_semi(cfile); no_semi = 1; } break; case PREFERRED_LIFE: token = next_token(&val, NULL, cfile); if (token == NUMBER) { addr->preferred_life = atoi(val); } else { parse_warn(cfile, "Expecting a number."); skip_to_semi(cfile); no_semi = 1; } break; case MAX_LIFE: token = next_token(&val, NULL, cfile); if (token == NUMBER) { addr->max_life = atoi(val); } else { parse_warn(cfile, "Expecting a number."); skip_to_semi(cfile); no_semi = 1; } break; case OPTION: if (parse_option_decl(&oc, cfile)) { save_option(oc->option->universe, addr->options, oc); option_cache_dereference(&oc, MDL); } no_semi = 1; break; default: parse_warn(cfile, "Unexpected token."); skip_to_rbrace(cfile, 1); no_semi = 1; break; } if (!no_semi) parse_semi(cfile); token = next_token(&val, NULL, cfile); if (token == END_OF_FILE) { parse_warn(cfile, "Unexpected end of file."); break; } } return addr; } #endif /* DHCPv6 */ /* Parse an iaprefix {} structure. */ #ifdef DHCPv6 static struct dhc6_addr * parse_client6_iaprefix_statement(struct parse *cfile) { struct option_cache *oc = NULL; struct dhc6_addr *pref; const char *val; int token, no_semi; pref = dmalloc(sizeof(*pref), MDL); if (pref == NULL) { parse_warn(cfile, "Unable to allocate IAPREFIX state."); skip_to_semi(cfile); return NULL; } /* Get IP prefix. */ if (!parse_ip6_prefix(cfile, &pref->address, &pref->plen)) { skip_to_semi(cfile); dfree(pref, MDL); return NULL; } token = next_token(NULL, NULL, cfile); if (token != LBRACE) { parse_warn(cfile, "Expecting open curly bracket."); skip_to_semi(cfile); dfree(pref, MDL); return NULL; } option_state_allocate(&pref->options, MDL); if (pref->options == NULL) { parse_warn(cfile, "Unable to allocate option state."); skip_to_semi(cfile); dfree(pref, MDL); return NULL; } token = next_token(&val, NULL, cfile); while (token != RBRACE) { no_semi = 0; switch (token) { case STARTS: token = next_token(&val, NULL, cfile); if (token == NUMBER) { pref->starts = atoi(val); } else { parse_warn(cfile, "Expecting a number."); skip_to_semi(cfile); no_semi = 1; } break; case PREFERRED_LIFE: token = next_token(&val, NULL, cfile); if (token == NUMBER) { pref->preferred_life = atoi(val); } else { parse_warn(cfile, "Expecting a number."); skip_to_semi(cfile); no_semi = 1; } break; case MAX_LIFE: token = next_token(&val, NULL, cfile); if (token == NUMBER) { pref->max_life = atoi(val); } else { parse_warn(cfile, "Expecting a number."); skip_to_semi(cfile); no_semi = 1; } break; case OPTION: if (parse_option_decl(&oc, cfile)) { save_option(oc->option->universe, pref->options, oc); option_cache_dereference(&oc, MDL); } no_semi = 1; break; default: parse_warn(cfile, "Unexpected token."); skip_to_rbrace(cfile, 1); no_semi = 1; break; } if (!no_semi) parse_semi(cfile); token = next_token(&val, NULL, cfile); if (token == END_OF_FILE) { parse_warn(cfile, "Unexpected end of file."); break; } } return pref; } #endif /* DHCPv6 */ void parse_string_list (cfile, lp, multiple) struct parse *cfile; struct string_list **lp; int multiple; { int token; const char *val; struct string_list *cur, *tmp; /* Find the last medium in the media list. */ if (*lp) { for (cur = *lp; cur -> next; cur = cur -> next) ; } else { cur = (struct string_list *)0; } do { token = next_token (&val, (unsigned *)0, cfile); if (token != STRING) { parse_warn (cfile, "Expecting media options."); skip_to_semi (cfile); return; } tmp = ((struct string_list *) dmalloc (strlen (val) + sizeof (struct string_list), MDL)); if (!tmp) log_fatal ("no memory for string list entry."); strcpy (tmp -> string, val); tmp -> next = (struct string_list *)0; /* Store this medium at the end of the media list. */ if (cur) cur -> next = tmp; else *lp = tmp; cur = tmp; token = next_token (&val, (unsigned *)0, cfile); } while (multiple && token == COMMA); if (token != SEMI) { parse_warn (cfile, "expecting semicolon."); skip_to_semi (cfile); } } void parse_reject_statement (cfile, config) struct parse *cfile; struct client_config *config; { int token; const char *val; struct iaddrmatch match; struct iaddrmatchlist *list; int i; do { if (!parse_ip_addr_with_subnet (cfile, &match)) { /* no warn: parser will have reported what's wrong */ skip_to_semi (cfile); return; } /* check mask is not all zeros (because that would * reject EVERY address). This check could be * simplified if we assume that the mask *always* * represents a prefix .. but perhaps it might be * useful to have a mask which is not a proper prefix * (perhaps for ipv6?). The following is almost as * efficient as inspection of match.mask.iabuf[0] when * it IS a true prefix, and is more general when it is * not. */ for (i=0 ; i < match.mask.len ; i++) { if (match.mask.iabuf[i]) { break; } } if (i == match.mask.len) { /* oops we found all zeros */ parse_warn(cfile, "zero-length prefix is not permitted " "for reject statement"); skip_to_semi(cfile); return; } list = dmalloc(sizeof(struct iaddrmatchlist), MDL); if (!list) log_fatal ("no memory for reject list!"); list->match = match; list->next = config->reject_list; config->reject_list = list; token = next_token (&val, (unsigned *)0, cfile); } while (token == COMMA); if (token != SEMI) { parse_warn (cfile, "expecting semicolon."); skip_to_semi (cfile); } } /* allow-deny-keyword :== BOOTP | BOOTING | DYNAMIC_BOOTP | UNKNOWN_CLIENTS */ int parse_allow_deny (oc, cfile, flag) struct option_cache **oc; struct parse *cfile; int flag; { parse_warn (cfile, "allow/deny/ignore not permitted here."); skip_to_semi (cfile); return 0; } /*! * \brief Parses an lease-id-format statement * * A valid statement looks like this: * * lease-id-format :== * LEASE_ID_FORMAT TOKEN_OCTAL | TOKEN_HEX ; * * This function is used to parse the lease-id-format statement. It sets * top_level_config.lease_id_format. * * \param cfile the current parse file * */ void parse_lease_id_format (struct parse *cfile) { enum dhcp_token token; const char *val; token = next_token(&val, NULL, cfile); switch(token) { case TOKEN_OCTAL: top_level_config.lease_id_format = TOKEN_OCTAL; break; case TOKEN_HEX: top_level_config.lease_id_format = TOKEN_HEX; break; default: parse_warn(cfile, "lease-id-format is invalid: " " it must be octal or hex."); skip_to_semi(cfile); return; } log_debug("lease_id_format is: %s", (top_level_config.lease_id_format == TOKEN_OCTAL ? "octal" : "hex")); } dhcp-4.4.1/client/dhc6.c000644 000765 000024 00000511732 13243301226 015172 0ustar00tmarkstaff000000 000000 /* dhc6.c - DHCPv6 client routines. */ /* * Copyright (c) 2012-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 2006-2010 by 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ */ #include "dhcpd.h" #ifdef DHCPv6 struct sockaddr_in6 DHCPv6DestAddr; /* * Option definition structures that are used by the software - declared * here once and assigned at startup to save lookups. */ struct option *clientid_option = NULL; struct option *elapsed_option = NULL; struct option *ia_na_option = NULL; struct option *ia_ta_option = NULL; struct option *ia_pd_option = NULL; struct option *iaaddr_option = NULL; struct option *iaprefix_option = NULL; struct option *oro_option = NULL; struct option *irt_option = NULL; static struct dhc6_lease *dhc6_dup_lease(struct dhc6_lease *lease, const char *file, int line); static struct dhc6_ia *dhc6_dup_ia(struct dhc6_ia *ia, const char *file, int line); static struct dhc6_addr *dhc6_dup_addr(struct dhc6_addr *addr, const char *file, int line); static void dhc6_ia_destroy(struct dhc6_ia **src, const char *file, int line); static isc_result_t dhc6_parse_ia_na(struct dhc6_ia **pia, struct packet *packet, struct option_state *options, unsigned code); static isc_result_t dhc6_parse_ia_ta(struct dhc6_ia **pia, struct packet *packet, struct option_state *options, unsigned code); static isc_result_t dhc6_parse_ia_pd(struct dhc6_ia **pia, struct packet *packet, struct option_state *options, unsigned code); static isc_result_t dhc6_parse_addrs(struct dhc6_addr **paddr, struct packet *packet, struct option_state *options); static isc_result_t dhc6_parse_prefixes(struct dhc6_addr **ppref, struct packet *packet, struct option_state *options); static struct dhc6_ia *find_ia(struct dhc6_ia *head, u_int16_t type, const char *id); static struct dhc6_addr *find_addr(struct dhc6_addr *head, struct iaddr *address); static struct dhc6_addr *find_pref(struct dhc6_addr *head, struct iaddr *prefix, u_int8_t plen); void init_handler(struct packet *packet, struct client_state *client); void info_request_handler(struct packet *packet, struct client_state *client); void rapid_commit_handler(struct packet *packet, struct client_state *client); void do_init6(void *input); void do_info_request6(void *input); void do_confirm6(void *input); void reply_handler(struct packet *packet, struct client_state *client); static isc_result_t dhc6_create_iaid(struct client_state *client, struct data_string *ia, int idx, unsigned len); static int dhc6_count_ia(struct dhc6_lease *lease, u_int16_t ia_type); static isc_result_t dhc6_bare_ia_xx(struct client_state *client, struct data_string *packet, int wanted, u_int16_t ia_type); static isc_result_t dhc6_add_ia_na(struct client_state *client, struct data_string *packet, struct dhc6_lease *lease, u_int8_t message, int wanted, int *added); static isc_result_t dhc6_add_ia_ta(struct client_state *client, struct data_string *packet, struct dhc6_lease *lease, u_int8_t message, int wanted, int *added); static isc_result_t dhc6_add_ia_pd(struct client_state *client, struct data_string *packet, struct dhc6_lease *lease, u_int8_t message, int wanted, int *added); static isc_boolean_t stopping_finished(void); static void dhc6_merge_lease(struct dhc6_lease *src, struct dhc6_lease *dst); void do_select6(void *input); void do_refresh6(void *input); static void do_release6(void *input); static void start_bound(struct client_state *client); static void start_decline6(struct client_state *client); static void do_decline6(void *input); static void start_informed(struct client_state *client); void informed_handler(struct packet *packet, struct client_state *client); void bound_handler(struct packet *packet, struct client_state *client); void start_renew6(void *input); void start_rebind6(void *input); void do_depref(void *input); void do_expire(void *input); static void make_client6_options(struct client_state *client, struct option_state **op, struct dhc6_lease *lease, u_int8_t message); static void script_write_params6(struct client_state *client, const char *prefix, struct option_state *options); static void script_write_requested6(struct client_state *client); static isc_boolean_t active_prefix(struct client_state *client); static int check_timing6(struct client_state *client, u_int8_t msg_type, char *msg_str, struct dhc6_lease *lease, struct data_string *ds); static isc_result_t dhc6_get_status_code(struct option_state *options, unsigned *code, struct data_string *msg); static isc_result_t dhc6_check_status(isc_result_t rval, struct option_state *options, const char *scope, unsigned *code); static int dhc6_score_lease(struct client_state *client, struct dhc6_lease *lease); static isc_result_t dhc6_add_ia_na_decline(struct client_state *client, struct data_string *packet, struct dhc6_lease *lease); static int drop_declined_addrs(struct dhc6_lease *lease); static isc_boolean_t unexpired_address_in_lease(struct dhc6_lease *lease); extern int onetry; extern int stateless; extern int prefix_len_hint; extern int address_prefix_len; /* * Assign DHCPv6 port numbers as a client. */ void dhcpv6_client_assignments(void) { struct servent *ent; unsigned code; if (path_dhclient_pid == NULL) path_dhclient_pid = _PATH_DHCLIENT6_PID; if (path_dhclient_db == NULL) path_dhclient_db = _PATH_DHCLIENT6_DB; if (local_port == 0) { ent = getservbyname("dhcpv6-client", "udp"); if (ent == NULL) local_port = htons(546); else local_port = ent->s_port; } if (remote_port == 0) { ent = getservbyname("dhcpv6-server", "udp"); if (ent == NULL) remote_port = htons(547); else remote_port = ent->s_port; } memset(&DHCPv6DestAddr, 0, sizeof(DHCPv6DestAddr)); DHCPv6DestAddr.sin6_family = AF_INET6; DHCPv6DestAddr.sin6_port = remote_port; if (inet_pton(AF_INET6, All_DHCP_Relay_Agents_and_Servers, &DHCPv6DestAddr.sin6_addr) <= 0) { log_fatal("Bad address %s", All_DHCP_Relay_Agents_and_Servers); } code = D6O_CLIENTID; if (!option_code_hash_lookup(&clientid_option, dhcpv6_universe.code_hash, &code, 0, MDL)) log_fatal("Unable to find the CLIENTID option definition."); code = D6O_ELAPSED_TIME; if (!option_code_hash_lookup(&elapsed_option, dhcpv6_universe.code_hash, &code, 0, MDL)) log_fatal("Unable to find the ELAPSED_TIME option definition."); code = D6O_IA_NA; if (!option_code_hash_lookup(&ia_na_option, dhcpv6_universe.code_hash, &code, 0, MDL)) log_fatal("Unable to find the IA_NA option definition."); code = D6O_IA_TA; if (!option_code_hash_lookup(&ia_ta_option, dhcpv6_universe.code_hash, &code, 0, MDL)) log_fatal("Unable to find the IA_TA option definition."); code = D6O_IA_PD; if (!option_code_hash_lookup(&ia_pd_option, dhcpv6_universe.code_hash, &code, 0, MDL)) log_fatal("Unable to find the IA_PD option definition."); code = D6O_IAADDR; if (!option_code_hash_lookup(&iaaddr_option, dhcpv6_universe.code_hash, &code, 0, MDL)) log_fatal("Unable to find the IAADDR option definition."); code = D6O_IAPREFIX; if (!option_code_hash_lookup(&iaprefix_option, dhcpv6_universe.code_hash, &code, 0, MDL)) log_fatal("Unable to find the IAPREFIX option definition."); code = D6O_ORO; if (!option_code_hash_lookup(&oro_option, dhcpv6_universe.code_hash, &code, 0, MDL)) log_fatal("Unable to find the ORO option definition."); code = D6O_INFORMATION_REFRESH_TIME; if (!option_code_hash_lookup(&irt_option, dhcpv6_universe.code_hash, &code, 0, MDL)) log_fatal("Unable to find the IRT option definition."); #ifndef __CYGWIN32__ /* XXX */ endservent(); #endif } /* * Instead of implementing RFC3315 RAND (section 14) as a float "between" * -0.1 and 0.1 non-inclusive, we implement it as an integer. * * The result is expected to follow this table: * * split range answer * - ERROR - base <= 0 * 0 1 0..0 1 <= base <= 10 * 1 3 -1..1 11 <= base <= 20 * 2 5 -2..2 21 <= base <= 30 * 3 7 -3..3 31 <= base <= 40 * ... * * XXX: For this to make sense, we really need to do timing on a * XXX: usec scale...we currently can assume zero for any value less than * XXX: 11, which are very common in early stages of transmission for most * XXX: messages. */ static TIME dhc6_rand(TIME base) { TIME rval; TIME range; TIME split; /* * A zero or less timeout is a bad thing...we don't want to * DHCP-flood anyone. */ if (base <= 0) log_fatal("Impossible condition at %s:%d.", MDL); /* * The first thing we do is count how many random integers we want * in either direction (best thought of as the maximum negative * integer, as we will subtract this potentially from a random 0). */ split = (base - 1) / 10; /* Don't bother with the rest of the math if we know we'll get 0. */ if (split == 0) return 0; /* * Then we count the total number of integers in this set. This * is twice the number of integers in positive and negative * directions, plus zero (-1, 0, 1 is 3, -2..2 adds 2 to 5, so forth). */ range = (split * 2) + 1; /* Take a random number from [0..(range-1)]. */ rval = random(); rval %= range; /* Offset it to uncover potential negative values. */ rval -= split; return rval; } /* Initialize message exchange timers (set RT from Initial-RT). */ static void dhc6_retrans_init(struct client_state *client) { int xid; /* Initialize timers. */ client->txcount = 0; client->RT = client->IRT + dhc6_rand(client->IRT); /* Generate a new random 24-bit transaction ID for this exchange. */ #if (RAND_MAX >= 0x00ffffff) xid = random(); #elif (RAND_MAX >= 0x0000ffff) xid = (random() << 16) ^ random(); #elif (RAND_MAX >= 0x000000ff) xid = (random() << 16) ^ (random() << 8) ^ random(); #else # error "Random number generator of less than 8 bits not supported." #endif client->dhcpv6_transaction_id[0] = (xid >> 16) & 0xff; client->dhcpv6_transaction_id[1] = (xid >> 8) & 0xff; client->dhcpv6_transaction_id[2] = xid & 0xff; } /* Advance the DHCPv6 retransmission state once. */ static void dhc6_retrans_advance(struct client_state *client) { struct timeval elapsed, elapsed_plus_rt; /* elapsed = cur - start */ elapsed.tv_sec = cur_tv.tv_sec - client->start_time.tv_sec; elapsed.tv_usec = cur_tv.tv_usec - client->start_time.tv_usec; if (elapsed.tv_usec < 0) { elapsed.tv_sec -= 1; elapsed.tv_usec += 1000000; } /* retrans_advance is called after consuming client->RT. */ /* elapsed += RT */ elapsed.tv_sec += client->RT / 100; elapsed.tv_usec += (client->RT % 100) * 10000; if (elapsed.tv_usec >= 1000000) { elapsed.tv_sec += 1; elapsed.tv_usec -= 1000000; } /* * Save what the time will be after the current RT to determine * what the delta to MRD will be. */ elapsed_plus_rt.tv_sec = elapsed.tv_sec; elapsed_plus_rt.tv_usec = elapsed.tv_usec; /* * RT for each subsequent message transmission is based on the previous * value of RT: * * RT = 2*RTprev + RAND*RTprev */ client->RT += client->RT + dhc6_rand(client->RT); /* * MRT specifies an upper bound on the value of RT (disregarding the * randomization added by the use of RAND). If MRT has a value of 0, * there is no upper limit on the value of RT. Otherwise: * * if (RT > MRT) * RT = MRT + RAND*MRT */ if ((client->MRT != 0) && (client->RT > client->MRT)) client->RT = client->MRT + dhc6_rand(client->MRT); /* * Further, if there's an MRD, we should wake up upon reaching * the MRD rather than at some point after it. */ if (client->MRD == 0) { /* Done. */ client->txcount++; return; } /* elapsed += client->RT */ elapsed.tv_sec += client->RT / 100; elapsed.tv_usec += (client->RT % 100) * 10000; if (elapsed.tv_usec >= 1000000) { elapsed.tv_sec += 1; elapsed.tv_usec -= 1000000; } if (elapsed.tv_sec >= client->MRD) { /* * The desired RT is the time that will be remaining in MRD * when the current timeout finishes. We then have * desired RT = MRD - (elapsed time + previous RT); or * desired RT = MRD - elapsed_plut_rt; */ client->RT = client->MRD - elapsed_plus_rt.tv_sec; client->RT = (client->RT * 100) - (elapsed_plus_rt.tv_usec / 10000); if (client->RT < 0) client->RT = 0; } client->txcount++; } /* Quick validation of DHCPv6 ADVERTISE packet contents. */ static int valid_reply(struct packet *packet, struct client_state *client) { struct data_string sid, cid; struct option_cache *oc; int rval = ISC_TRUE; memset(&sid, 0, sizeof(sid)); memset(&cid, 0, sizeof(cid)); if (!lookup_option(&dhcpv6_universe, packet->options, D6O_SERVERID)) { log_error("Response without a server identifier received."); rval = ISC_FALSE; } oc = lookup_option(&dhcpv6_universe, packet->options, D6O_CLIENTID); if (!oc || !evaluate_option_cache(&sid, packet, NULL, client, packet->options, client->sent_options, &global_scope, oc, MDL)) { log_error("Response without a client identifier."); rval = ISC_FALSE; } oc = lookup_option(&dhcpv6_universe, client->sent_options, D6O_CLIENTID); if (!oc || !evaluate_option_cache(&cid, packet, NULL, client, client->sent_options, NULL, &global_scope, oc, MDL)) { log_error("Local client identifier is missing!"); rval = ISC_FALSE; } if (sid.len == 0 || sid.len != cid.len || memcmp(sid.data, cid.data, sid.len)) { log_error("Advertise with matching transaction ID, but " "mismatching client id."); rval = ISC_FALSE; } /* clean up pointers to the strings */ if (sid.data != NULL) data_string_forget(&sid, MDL); if (cid.data != NULL) data_string_forget(&cid, MDL); return rval; } /* * Create a complete copy of a DHCPv6 lease structure. */ static struct dhc6_lease * dhc6_dup_lease(struct dhc6_lease *lease, const char *file, int line) { struct dhc6_lease *copy; struct dhc6_ia **insert_ia, *ia; copy = dmalloc(sizeof(*copy), file, line); if (copy == NULL) { log_error("Out of memory for v6 lease structure."); return NULL; } data_string_copy(©->server_id, &lease->server_id, file, line); copy->pref = lease->pref; memcpy(copy->dhcpv6_transaction_id, lease->dhcpv6_transaction_id, sizeof(copy->dhcpv6_transaction_id)); option_state_reference(©->options, lease->options, file, line); insert_ia = ©->bindings; for (ia = lease->bindings ; ia != NULL ; ia = ia->next) { *insert_ia = dhc6_dup_ia(ia, file, line); if (*insert_ia == NULL) { dhc6_lease_destroy(©, file, line); return NULL; } insert_ia = &(*insert_ia)->next; } return copy; } /* * Duplicate an IA structure. */ static struct dhc6_ia * dhc6_dup_ia(struct dhc6_ia *ia, const char *file, int line) { struct dhc6_ia *copy; struct dhc6_addr **insert_addr, *addr; copy = dmalloc(sizeof(*ia), file, line); if (copy == NULL) { log_error("Out of memory for v6 duplicate IA structure."); return NULL; } memcpy(copy->iaid, ia->iaid, sizeof(copy->iaid)); copy->ia_type = ia->ia_type; copy->starts = ia->starts; copy->renew = ia->renew; copy->rebind = ia->rebind; insert_addr = ©->addrs; for (addr = ia->addrs ; addr != NULL ; addr = addr->next) { *insert_addr = dhc6_dup_addr(addr, file, line); if (*insert_addr == NULL) { dhc6_ia_destroy(©, file, line); return NULL; } insert_addr = &(*insert_addr)->next; } if (ia->options != NULL) option_state_reference(©->options, ia->options, file, line); return copy; } /* * Duplicate an IAADDR or IAPREFIX structure. */ static struct dhc6_addr * dhc6_dup_addr(struct dhc6_addr *addr, const char *file, int line) { struct dhc6_addr *copy; copy = dmalloc(sizeof(*addr), file, line); if (copy == NULL) return NULL; memcpy(©->address, &addr->address, sizeof(copy->address)); copy->plen = addr->plen; copy->flags = addr->flags; copy->starts = addr->starts; copy->preferred_life = addr->preferred_life; copy->max_life = addr->max_life; if (addr->options != NULL) option_state_reference(©->options, addr->options, file, line); return copy; } /* * Form a DHCPv6 lease structure based upon packet contents. Creates and * populates IA's and any IAADDR/IAPREFIX's they contain. * Parsed options are deleted in order to not save them in the lease file. * * If we get a status code of NoAddrs or NoPrefix we toss the affected * IAs. If it as at the top level we toss all IAs of that type. If it * is in an IA we only toss that one. According to the spec we shouldn't * get a NoPrefix status at the top level but we will allow it. * */ static struct dhc6_lease * dhc6_leaseify(struct packet *packet, struct client_state* client) { struct data_string ds; struct dhc6_lease *lease; struct option_cache *oc; unsigned code; lease = dmalloc(sizeof(*lease), MDL); if (lease == NULL) { log_error("Out of memory for v6 lease structure."); return NULL; } memcpy(lease->dhcpv6_transaction_id, packet->dhcpv6_transaction_id, 3); option_state_reference(&lease->options, packet->options, MDL); memset(&ds, 0, sizeof(ds)); /* Determine preference (default zero). */ oc = lookup_option(&dhcpv6_universe, lease->options, D6O_PREFERENCE); if (oc && evaluate_option_cache(&ds, packet, NULL, NULL, lease->options, NULL, &global_scope, oc, MDL)) { if (ds.len != 1) { log_error("Invalid length of DHCPv6 Preference option " "(%d != 1)", ds.len); data_string_forget(&ds, MDL); dhc6_lease_destroy(&lease, MDL); return NULL; } else { lease->pref = ds.data[0]; log_debug("RCV: X-- Preference %u.", (unsigned)lease->pref); } data_string_forget(&ds, MDL); } delete_option(&dhcpv6_universe, lease->options, D6O_PREFERENCE); /* Get the top level status code. If the code is NoAddrsAvail * or NoPrefixAvail strip it from the options as we don't * want it to show up in check_[advertise reply]. We * pass it along to the parse_ia_xx routines and they * will drop the affected IAs for NoAddrs or NoPrefix, * other status codes will be ignored and handled by * the check_[advertise reply] routines. */ code = STATUS_Success; if ((dhc6_get_status_code(lease->options, &code, NULL) == ISC_R_SUCCESS) && ((code == STATUS_NoAddrsAvail) || (code == STATUS_NoPrefixAvail))) { delete_option(&dhcpv6_universe, lease->options, D6O_STATUS_CODE); } /* * Dig into recursive DHCPv6 pockets for IA_NA and contained IAADDR * options. */ if (dhc6_parse_ia_na(&lease->bindings, packet, lease->options, code) != ISC_R_SUCCESS) { /* Error conditions are logged by the caller. */ dhc6_lease_destroy(&lease, MDL); return NULL; } /* * Dig into recursive DHCPv6 pockets for IA_TA and contained IAADDR * options. */ if (dhc6_parse_ia_ta(&lease->bindings, packet, lease->options, code) != ISC_R_SUCCESS) { /* Error conditions are logged by the caller. */ dhc6_lease_destroy(&lease, MDL); return NULL; } /* * Dig into recursive DHCPv6 pockets for IA_PD and contained IAPREFIX * options. */ if (dhc6_parse_ia_pd(&lease->bindings, packet, lease->options, code) != ISC_R_SUCCESS) { /* Error conditions are logged by the caller. */ dhc6_lease_destroy(&lease, MDL); return NULL; } /* * This is last because in the future we may want to make a different * key based upon additional information from the packet (we may need * to allow multiple leases in one client state per server, but we're * not sure based on what additional keys now). */ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_SERVERID); if ((oc == NULL) || !evaluate_option_cache(&lease->server_id, packet, NULL, NULL, lease->options, NULL, &global_scope, oc, MDL) || lease->server_id.len == 0) { /* This should be impossible due to validation checks earlier. */ log_error("Invalid SERVERID option cache."); dhc6_lease_destroy(&lease, MDL); return NULL; } else { log_debug("RCV: X-- Server ID: %s", print_hex_1(lease->server_id.len, lease->server_id.data, 52)); } execute_statements_in_scope(NULL, (struct packet *)packet, NULL, client, lease->options, lease->options, &global_scope, client->config->on_receipt, NULL, NULL); return lease; } static isc_result_t dhc6_parse_ia_na(struct dhc6_ia **pia, struct packet *packet, struct option_state *options, unsigned code) { struct data_string ds; struct dhc6_ia *ia; struct option_cache *oc; isc_result_t result; unsigned ia_code; memset(&ds, 0, sizeof(ds)); oc = lookup_option(&dhcpv6_universe, options, D6O_IA_NA); for ( ; oc != NULL ; oc = oc->next) { ia = dmalloc(sizeof(*ia), MDL); if (ia == NULL) { log_error("Out of memory allocating IA_NA structure."); return ISC_R_NOMEMORY; } else if (evaluate_option_cache(&ds, packet, NULL, NULL, options, NULL, &global_scope, oc, MDL) && ds.len >= 12) { memcpy(ia->iaid, ds.data, 4); ia->ia_type = D6O_IA_NA; ia->starts = cur_time; ia->renew = getULong(ds.data + 4); ia->rebind = getULong(ds.data + 8); log_debug("RCV: X-- IA_NA %s", print_hex_1(4, ia->iaid, 59)); /* XXX: This should be the printed time I think. */ log_debug("RCV: | X-- starts %u", (unsigned)ia->starts); log_debug("RCV: | X-- t1 - renew +%u", ia->renew); log_debug("RCV: | X-- t2 - rebind +%u", ia->rebind); /* * RFC3315 section 22.4, discard IA_NA's that * have t1 greater than t2, and both not zero. * Since RFC3315 defines this behaviour, it is not * an error - just normal operation. * * Note that RFC3315 says we MUST honor these values * if they are not zero. So insane values are * totally OK. */ if ((ia->renew > 0) && (ia->rebind > 0) && (ia->renew > ia->rebind)) { log_debug("RCV: | !-- INVALID renew/rebind " "times, IA_NA discarded."); dfree(ia, MDL); data_string_forget(&ds, MDL); continue; } if (ds.len > 12) { log_debug("RCV: | X-- [Options]"); if (!option_state_allocate(&ia->options, MDL)) { log_error("Out of memory allocating " "IA_NA option state."); dfree(ia, MDL); data_string_forget(&ds, MDL); return ISC_R_NOMEMORY; } if (!parse_option_buffer(ia->options, ds.data + 12, ds.len - 12, &dhcpv6_universe)) { log_error("Corrupt IA_NA options."); option_state_dereference(&ia->options, MDL); dfree(ia, MDL); data_string_forget(&ds, MDL); return DHCP_R_BADPARSE; } } data_string_forget(&ds, MDL); if (ia->options != NULL) { result = dhc6_parse_addrs(&ia->addrs, packet, ia->options); if (result != ISC_R_SUCCESS) { option_state_dereference(&ia->options, MDL); dfree(ia, MDL); return result; } } /* If we have no addresses or the top level status code * or the status code in this IA indicate no addresses * toss the IA. */ ia_code = STATUS_Success; if ((ia->addrs == NULL) || (code == STATUS_NoAddrsAvail) || ((ia->options != NULL) && (dhc6_get_status_code(ia->options, &ia_code, NULL) == ISC_R_SUCCESS) && (ia_code == STATUS_NoAddrsAvail))) { log_debug("RCV: | !-- Status code of " "no addrs, IA_NA discarded."); dhc6_ia_destroy(&ia, MDL); continue; } while (*pia != NULL) pia = &(*pia)->next; *pia = ia; pia = &ia->next; } else { log_error("Invalid IA_NA option cache."); dfree(ia, MDL); if (ds.len != 0) data_string_forget(&ds, MDL); return ISC_R_UNEXPECTED; } } delete_option(&dhcpv6_universe, options, D6O_IA_NA); return ISC_R_SUCCESS; } static isc_result_t dhc6_parse_ia_ta(struct dhc6_ia **pia, struct packet *packet, struct option_state *options, unsigned code) { struct data_string ds; struct dhc6_ia *ia; struct option_cache *oc; isc_result_t result; unsigned ia_code; memset(&ds, 0, sizeof(ds)); oc = lookup_option(&dhcpv6_universe, options, D6O_IA_TA); for ( ; oc != NULL ; oc = oc->next) { ia = dmalloc(sizeof(*ia), MDL); if (ia == NULL) { log_error("Out of memory allocating IA_TA structure."); return ISC_R_NOMEMORY; } else if (evaluate_option_cache(&ds, packet, NULL, NULL, options, NULL, &global_scope, oc, MDL) && ds.len >= 4) { memcpy(ia->iaid, ds.data, 4); ia->ia_type = D6O_IA_TA; ia->starts = cur_time; log_debug("RCV: X-- IA_TA %s", print_hex_1(4, ia->iaid, 59)); /* XXX: This should be the printed time I think. */ log_debug("RCV: | X-- starts %u", (unsigned)ia->starts); if (ds.len > 4) { log_debug("RCV: | X-- [Options]"); if (!option_state_allocate(&ia->options, MDL)) { log_error("Out of memory allocating " "IA_TA option state."); dfree(ia, MDL); data_string_forget(&ds, MDL); return ISC_R_NOMEMORY; } if (!parse_option_buffer(ia->options, ds.data + 4, ds.len - 4, &dhcpv6_universe)) { log_error("Corrupt IA_TA options."); option_state_dereference(&ia->options, MDL); dfree(ia, MDL); data_string_forget(&ds, MDL); return DHCP_R_BADPARSE; } } data_string_forget(&ds, MDL); if (ia->options != NULL) { result = dhc6_parse_addrs(&ia->addrs, packet, ia->options); if (result != ISC_R_SUCCESS) { option_state_dereference(&ia->options, MDL); dfree(ia, MDL); return result; } } /* If we have no addresses or the top level status code * or the status code in this IA indicate no addresses * toss the IA. */ ia_code = STATUS_Success; if ((ia->addrs == NULL) || (code == STATUS_NoAddrsAvail) || ((ia->options != NULL) && (dhc6_get_status_code(ia->options, &ia_code, NULL) == ISC_R_SUCCESS) && (ia_code == STATUS_NoAddrsAvail))) { log_debug("RCV: | !-- Status code of " "no addrs, IA_TA discarded."); dhc6_ia_destroy(&ia, MDL); continue; } while (*pia != NULL) pia = &(*pia)->next; *pia = ia; pia = &ia->next; } else { log_error("Invalid IA_TA option cache."); dfree(ia, MDL); if (ds.len != 0) data_string_forget(&ds, MDL); return ISC_R_UNEXPECTED; } } delete_option(&dhcpv6_universe, options, D6O_IA_TA); return ISC_R_SUCCESS; } static isc_result_t dhc6_parse_ia_pd(struct dhc6_ia **pia, struct packet *packet, struct option_state *options, unsigned code) { struct data_string ds; struct dhc6_ia *ia; struct option_cache *oc; isc_result_t result; unsigned ia_code; memset(&ds, 0, sizeof(ds)); oc = lookup_option(&dhcpv6_universe, options, D6O_IA_PD); for ( ; oc != NULL ; oc = oc->next) { ia = dmalloc(sizeof(*ia), MDL); if (ia == NULL) { log_error("Out of memory allocating IA_PD structure."); return ISC_R_NOMEMORY; } else if (evaluate_option_cache(&ds, packet, NULL, NULL, options, NULL, &global_scope, oc, MDL) && ds.len >= 12) { memcpy(ia->iaid, ds.data, 4); ia->ia_type = D6O_IA_PD; ia->starts = cur_time; ia->renew = getULong(ds.data + 4); ia->rebind = getULong(ds.data + 8); log_debug("RCV: X-- IA_PD %s", print_hex_1(4, ia->iaid, 59)); /* XXX: This should be the printed time I think. */ log_debug("RCV: | X-- starts %u", (unsigned)ia->starts); log_debug("RCV: | X-- t1 - renew +%u", ia->renew); log_debug("RCV: | X-- t2 - rebind +%u", ia->rebind); /* * RFC3633 section 9, discard IA_PD's that * have t1 greater than t2, and both not zero. * Since RFC3633 defines this behaviour, it is not * an error - just normal operation. */ if ((ia->renew > 0) && (ia->rebind > 0) && (ia->renew > ia->rebind)) { log_debug("RCV: | !-- INVALID renew/rebind " "times, IA_PD discarded."); dfree(ia, MDL); data_string_forget(&ds, MDL); continue; } if (ds.len > 12) { log_debug("RCV: | X-- [Options]"); if (!option_state_allocate(&ia->options, MDL)) { log_error("Out of memory allocating " "IA_PD option state."); dfree(ia, MDL); data_string_forget(&ds, MDL); return ISC_R_NOMEMORY; } if (!parse_option_buffer(ia->options, ds.data + 12, ds.len - 12, &dhcpv6_universe)) { log_error("Corrupt IA_PD options."); option_state_dereference(&ia->options, MDL); dfree(ia, MDL); data_string_forget(&ds, MDL); return DHCP_R_BADPARSE; } } data_string_forget(&ds, MDL); if (ia->options != NULL) { result = dhc6_parse_prefixes(&ia->addrs, packet, ia->options); if (result != ISC_R_SUCCESS) { option_state_dereference(&ia->options, MDL); dfree(ia, MDL); return result; } } /* If we have no prefixes or the top level status code * or the status code in this IA indicate no prefixes * toss the IA. */ ia_code = STATUS_Success; if ((ia->addrs == NULL) || (code == STATUS_NoPrefixAvail) || ((ia->options != NULL) && (dhc6_get_status_code(ia->options, &ia_code, NULL) == ISC_R_SUCCESS) && (ia_code == STATUS_NoPrefixAvail))) { log_debug("RCV: | !-- Status code of " "no prefix, IA_PD discarded."); dhc6_ia_destroy(&ia, MDL); continue; } while (*pia != NULL) pia = &(*pia)->next; *pia = ia; pia = &ia->next; } else { log_error("Invalid IA_PD option cache."); dfree(ia, MDL); if (ds.len != 0) data_string_forget(&ds, MDL); return ISC_R_UNEXPECTED; } } delete_option(&dhcpv6_universe, options, D6O_IA_PD); return ISC_R_SUCCESS; } static isc_result_t dhc6_parse_addrs(struct dhc6_addr **paddr, struct packet *packet, struct option_state *options) { struct data_string ds; struct option_cache *oc; struct dhc6_addr *addr; isc_result_t rval = ISC_R_SUCCESS; unsigned code; memset(&ds, 0, sizeof(ds)); oc = lookup_option(&dhcpv6_universe, options, D6O_IAADDR); for ( ; oc != NULL ; oc = oc->next) { addr = dmalloc(sizeof(*addr), MDL); if (addr == NULL) { log_error("Out of memory allocating " "address structure."); return ISC_R_NOMEMORY; } else if (evaluate_option_cache(&ds, packet, NULL, NULL, options, NULL, &global_scope, oc, MDL) && (ds.len >= 24)) { addr->address.len = 16; memcpy(addr->address.iabuf, ds.data, 16); addr->starts = cur_time; addr->preferred_life = getULong(ds.data + 16); addr->max_life = getULong(ds.data + 20); log_debug("RCV: | | X-- IAADDR %s", piaddr(addr->address)); log_debug("RCV: | | | X-- Preferred lifetime %u.", addr->preferred_life); log_debug("RCV: | | | X-- Max lifetime %u.", addr->max_life); /* * RFC 3315 section 22.6 says we must discard * addresses whose pref is later than valid. */ if ((addr->preferred_life > addr->max_life)) { log_debug("RCV: | | | !-- INVALID lifetimes, " "IAADDR discarded. Check your " "server configuration."); dfree(addr, MDL); data_string_forget(&ds, MDL); continue; } /* * Fortunately this is the last recursion in the * protocol. */ if (ds.len > 24) { if (!option_state_allocate(&addr->options, MDL)) { log_error("Out of memory allocating " "IAADDR option state."); dfree(addr, MDL); data_string_forget(&ds, MDL); return ISC_R_NOMEMORY; } if (!parse_option_buffer(addr->options, ds.data + 24, ds.len - 24, &dhcpv6_universe)) { log_error("Corrupt IAADDR options."); option_state_dereference(&addr->options, MDL); dfree(addr, MDL); data_string_forget(&ds, MDL); return DHCP_R_BADPARSE; } } data_string_forget(&ds, MDL); if (addr->options != NULL) { log_debug("RCV: | | | X-- [Options]"); /* Get the status code if the return value * indicates an error or the status code * indicates no address toss the address */ code = STATUS_Success; rval = dhc6_check_status(ISC_R_SUCCESS, addr->options, "IAADDR", &code); if (rval != ISC_R_SUCCESS) { log_debug("RCV: | | | X-- Status code" " issue, IAADDR discarded."); option_state_dereference(&addr->options, MDL); dfree(addr, MDL); continue; } } *paddr = addr; paddr = &addr->next; } else { log_error("Invalid IAADDR option cache."); dfree(addr, MDL); if (ds.len != 0) data_string_forget(&ds, MDL); return ISC_R_UNEXPECTED; } } delete_option(&dhcpv6_universe, options, D6O_IAADDR); return ISC_R_SUCCESS; } static isc_result_t dhc6_parse_prefixes(struct dhc6_addr **ppfx, struct packet *packet, struct option_state *options) { struct data_string ds; struct option_cache *oc; struct dhc6_addr *pfx; isc_result_t rval = ISC_R_SUCCESS; unsigned code; memset(&ds, 0, sizeof(ds)); oc = lookup_option(&dhcpv6_universe, options, D6O_IAPREFIX); for ( ; oc != NULL ; oc = oc->next) { pfx = dmalloc(sizeof(*pfx), MDL); if (pfx == NULL) { log_error("Out of memory allocating " "prefix structure."); return ISC_R_NOMEMORY; } else if (evaluate_option_cache(&ds, packet, NULL, NULL, options, NULL, &global_scope, oc, MDL) && (ds.len >= 25)) { pfx->preferred_life = getULong(ds.data); pfx->max_life = getULong(ds.data + 4); pfx->plen = getUChar(ds.data + 8); pfx->address.len = 16; memcpy(pfx->address.iabuf, ds.data + 9, 16); pfx->starts = cur_time; log_debug("RCV: | | X-- IAPREFIX %s/%d", piaddr(pfx->address), (int)pfx->plen); log_debug("RCV: | | | X-- Preferred lifetime %u.", pfx->preferred_life); log_debug("RCV: | | | X-- Max lifetime %u.", pfx->max_life); /* Sanity check over the prefix length */ if ((pfx->plen < 4) || (pfx->plen > 128)) { log_debug("RCV: | | | !-- INVALID prefix " "length, IAPREFIX discarded. " "Check your server configuration."); dfree(pfx, MDL); data_string_forget(&ds, MDL); continue; } /* * RFC 3633 section 10 says we must discard * prefixes whose pref is later than valid. */ if ((pfx->preferred_life > pfx->max_life)) { log_debug("RCV: | | | !-- INVALID lifetimes, " "IAPREFIX discarded. Check your " "server configuration."); dfree(pfx, MDL); data_string_forget(&ds, MDL); continue; } /* * Fortunately this is the last recursion in the * protocol. */ if (ds.len > 25) { if (!option_state_allocate(&pfx->options, MDL)) { log_error("Out of memory allocating " "IAPREFIX option state."); dfree(pfx, MDL); data_string_forget(&ds, MDL); return ISC_R_NOMEMORY; } if (!parse_option_buffer(pfx->options, ds.data + 25, ds.len - 25, &dhcpv6_universe)) { log_error("Corrupt IAPREFIX options."); option_state_dereference(&pfx->options, MDL); dfree(pfx, MDL); data_string_forget(&ds, MDL); return DHCP_R_BADPARSE; } } data_string_forget(&ds, MDL); if (pfx->options != NULL) { log_debug("RCV: | | | X-- [Options]"); /* Get the status code if the return value * indicates an error or the status code * indicates no prefix toss the prefix */ code = STATUS_Success; rval = dhc6_check_status(ISC_R_SUCCESS, pfx->options, "IAPREFIX", &code); if (rval != ISC_R_SUCCESS) { log_debug("RCV: | | | X-- Status code" " issue IAPREFIX discarded."); option_state_dereference(&pfx->options, MDL); dfree(pfx, MDL); continue; } } *ppfx = pfx; ppfx = &pfx->next; } else { log_error("Invalid IAPREFIX option cache."); dfree(pfx, MDL); if (ds.len != 0) data_string_forget(&ds, MDL); return ISC_R_UNEXPECTED; } } delete_option(&dhcpv6_universe, options, D6O_IAPREFIX); return ISC_R_SUCCESS; } /* Clean up a lease object, deallocate all its parts, and set it to NULL. */ void dhc6_lease_destroy(struct dhc6_lease **src, const char *file, int line) { struct dhc6_ia *ia, *nia; struct dhc6_lease *lease; if (src == NULL || *src == NULL) { log_error("Attempt to destroy null lease."); return; } lease = *src; if (lease->server_id.len != 0) data_string_forget(&lease->server_id, file, line); for (ia = lease->bindings ; ia != NULL ; ia = nia) { nia = ia->next; dhc6_ia_destroy(&ia, file, line); } if (lease->options != NULL) option_state_dereference(&lease->options, file, line); dfree(lease, file, line); *src = NULL; } /* * Traverse the addresses list, and destroy their contents, and NULL the * list pointer. */ static void dhc6_ia_destroy(struct dhc6_ia **src, const char *file, int line) { struct dhc6_addr *addr, *naddr; struct dhc6_ia *ia; if (src == NULL || *src == NULL) { log_error("Attempt to destroy null IA."); return; } ia = *src; for (addr = ia->addrs ; addr != NULL ; addr = naddr) { naddr = addr->next; if (addr->options != NULL) option_state_dereference(&addr->options, file, line); dfree(addr, file, line); } if (ia->options != NULL) option_state_dereference(&ia->options, file, line); dfree(ia, file, line); *src = NULL; } /* * For a given lease, insert it into the tail of the lease list. Upon * finding a duplicate by server id, remove it and take over its position. */ static void insert_lease(struct dhc6_lease **head, struct dhc6_lease *new) { while (*head != NULL) { if ((*head)->server_id.len == new->server_id.len && memcmp((*head)->server_id.data, new->server_id.data, new->server_id.len) == 0) { new->next = (*head)->next; dhc6_lease_destroy(head, MDL); break; } head= &(*head)->next; } *head = new; return; } /*! * * \brief Determine a score for a lease. We use this to * compare and choose leases if we receive multiple candidates. * * We originally started with scores of 50 for a binding and 100 for * an address. This would select multiple adresses over multiple * bindings. As part of the 7550 work I've changed this to be * 10000 for a binding, 100 for an address and 1 for an option. * This will cause us to choose a lease with more bindings over * a lease with less bindings but more addresses which seems * to be the best selection criteria to me. * In theory we could end up with a lease with enough addresses * or options being better but at 100 to 1 I don't think it's likely. * * \param client = the state of the entire client * \param lease = the lease to score. * * \retrun the score of the lease */ /* The scores for individual items. */ #ifdef USE_ORIGINAL_CLIENT_LEASE_WEIGHTS #define SCORE_BINDING 50 #define SCORE_ADDRESS 100 #else #define SCORE_BINDING 10000 #define SCORE_ADDRESS 100 #endif #define SCORE_OPTION 1 /* We need a lease with at least 1 binding and 1 address */ #define SCORE_MIN (SCORE_BINDING + SCORE_ADDRESS) static int dhc6_score_lease(struct client_state *client, struct dhc6_lease *lease) { struct dhc6_ia *ia; struct dhc6_addr *addr; struct option **req; int i; if (lease->score) return lease->score; lease->score = SCORE_OPTION; /* If this lease lacks a required option, dump it. */ /* XXX: we should be able to cache the failure... */ req = client->config->required_options; if (req != NULL) { for (i = 0 ; req[i] != NULL ; i++) { if (lookup_option(&dhcpv6_universe, lease->options, req[i]->code) == NULL) { lease->score = 0; return lease->score; } } } /* If this lease contains a requested option, improve its score. */ req = client->config->requested_options; if (req != NULL) { for (i = 0 ; req[i] != NULL ; i++) { if (lookup_option(&dhcpv6_universe, lease->options, req[i]->code) != NULL) lease->score += SCORE_OPTION; } } for (ia = lease->bindings ; ia != NULL ; ia = ia->next) { lease->score += SCORE_BINDING; for (addr = ia->addrs ; addr != NULL ; addr = addr->next) { lease->score += SCORE_ADDRESS; } } return lease->score; } /* * start_init6() kicks off the process, transmitting a packet and * scheduling a retransmission event. */ void start_init6(struct client_state *client) { struct timeval tv; log_debug("PRC: Soliciting for leases (INIT)."); client->state = S_INIT; /* Initialize timers, RFC3315 section 17.1.2. */ client->IRT = SOL_TIMEOUT * 100; client->MRT = SOL_MAX_RT * 100; client->MRC = 0; /* Default is 0 (no max) but -1 changes this. */ if (!onetry) client->MRD = 0; else client->MRD = client->config->timeout; dhc6_retrans_init(client); /* * RFC3315 section 17.1.2 goes out of its way: * Also, the first RT MUST be selected to be strictly greater than IRT * by choosing RAND to be strictly greater than 0. */ /* if RAND < 0 then RAND = -RAND */ if (client->RT <= client->IRT) client->RT = client->IRT + (client->IRT - client->RT); /* if RAND == 0 then RAND = 1 */ if (client->RT <= client->IRT) client->RT = client->IRT + 1; client->v6_handler = init_handler; /* * RFC3315 section 17.1.2 says we MUST start the first packet * between 0 and SOL_MAX_DELAY seconds. The good news is * SOL_MAX_DELAY is 1. */ tv.tv_sec = cur_tv.tv_sec; tv.tv_usec = cur_tv.tv_usec; tv.tv_usec += (random() % (SOL_MAX_DELAY * 100)) * 10000; if (tv.tv_usec >= 1000000) { tv.tv_sec += 1; tv.tv_usec -= 1000000; } add_timeout(&tv, do_init6, client, NULL, NULL); if (nowait) detach(); } /* * start_info_request6() kicks off the process, transmitting an info * request packet and scheduling a retransmission event. */ void start_info_request6(struct client_state *client) { struct timeval tv; log_debug("PRC: Requesting information (INIT)."); client->state = S_INIT; /* Initialize timers, RFC3315 section 18.1.5. */ client->IRT = INF_TIMEOUT * 100; client->MRT = INF_MAX_RT * 100; client->MRC = 0; /* Default is 0 (no max) but -1 changes this. */ if (!onetry) client->MRD = 0; else client->MRD = client->config->timeout; dhc6_retrans_init(client); client->v6_handler = info_request_handler; /* * RFC3315 section 18.1.5 says we MUST start the first packet * between 0 and INF_MAX_DELAY seconds. The good news is * INF_MAX_DELAY is 1. */ tv.tv_sec = cur_tv.tv_sec; tv.tv_usec = cur_tv.tv_usec; tv.tv_usec += (random() % (INF_MAX_DELAY * 100)) * 10000; if (tv.tv_usec >= 1000000) { tv.tv_sec += 1; tv.tv_usec -= 1000000; } add_timeout(&tv, do_info_request6, client, NULL, NULL); if (nowait) detach(); } /* * start_confirm6() kicks off an "init-reboot" version of the process, at * startup to find out if old bindings are 'fair' and at runtime whenever * a link cycles state we'll eventually want to do this. */ void start_confirm6(struct client_state *client) { struct timeval tv; /* If there is no active lease, there is nothing to check. */ if ((client->active_lease == NULL) || !active_prefix(client) || client->active_lease->released || !unexpired_address_in_lease(client->active_lease)) { dhc6_lease_destroy(&client->active_lease, MDL); start_init6(client); return; } log_debug("PRC: Confirming active lease (INIT-REBOOT)."); client->state = S_REBOOTING; /* Initialize timers, RFC3315 section 17.1.3. */ client->IRT = CNF_TIMEOUT * 100; client->MRT = CNF_MAX_RT * 100; client->MRC = 0; client->MRD = CNF_MAX_RD; dhc6_retrans_init(client); client->v6_handler = reply_handler; /* * RFC3315 section 18.1.2 says we MUST start the first packet * between 0 and CNF_MAX_DELAY seconds. The good news is * CNF_MAX_DELAY is 1. */ tv.tv_sec = cur_tv.tv_sec; tv.tv_usec = cur_tv.tv_usec; tv.tv_usec += (random() % (CNF_MAX_DELAY * 100)) * 10000; if (tv.tv_usec >= 1000000) { tv.tv_sec += 1; tv.tv_usec -= 1000000; } /* We do a rebind instead of a confirm if the user * is requesting PDs or previously requesed PDs or * increased the number of NAs or TAs they want * Confirms don't tell us if PDs are still on-link and * we won't add new IAs on a confirm. */ if ((wanted_ia_pd != 0) || (dhc6_count_ia(client->active_lease, D6O_IA_PD) != 0) || (dhc6_count_ia(client->active_lease, D6O_IA_NA) < wanted_ia_na) || (dhc6_count_ia(client->active_lease, D6O_IA_TA) < wanted_ia_ta)) { client->state = S_REBINDING; client->refresh_type = DHCPV6_REBIND; add_timeout(&tv, do_refresh6, client, NULL, NULL); } else add_timeout(&tv, do_confirm6, client, NULL, NULL); } /* * check_timing6() check on the timing for sending a v6 message * and then do the basic initialization for a v6 message. */ #define CHK_TIM_SUCCESS 0 #define CHK_TIM_MRC_EXCEEDED 1 #define CHK_TIM_MRD_EXCEEDED 2 #define CHK_TIM_ALLOC_FAILURE 3 int check_timing6 (struct client_state *client, u_int8_t msg_type, char *msg_str, struct dhc6_lease *lease, struct data_string *ds) { struct timeval elapsed; /* * Start_time starts at the first transmission. */ if (client->txcount == 0) { client->start_time.tv_sec = cur_tv.tv_sec; client->start_time.tv_usec = cur_tv.tv_usec; } else if ((client->MRC != 0) && (client->txcount > client->MRC)) { log_info("Max retransmission count exceeded."); return(CHK_TIM_MRC_EXCEEDED); } /* elapsed = cur - start */ elapsed.tv_sec = cur_tv.tv_sec - client->start_time.tv_sec; elapsed.tv_usec = cur_tv.tv_usec - client->start_time.tv_usec; if (elapsed.tv_usec < 0) { elapsed.tv_sec -= 1; elapsed.tv_usec += 1000000; } /* Check if finished (-1 argument). */ if ((client->MRD != 0) && (elapsed.tv_sec >= client->MRD)) { log_info("Max retransmission duration exceeded."); return(CHK_TIM_MRD_EXCEEDED); } memset(ds, 0, sizeof(*ds)); if (!buffer_allocate(&(ds->buffer), 4, MDL)) { log_error("Unable to allocate memory for %s.", msg_str); return(CHK_TIM_ALLOC_FAILURE); } ds->data = ds->buffer->data; ds->len = 4; ds->buffer->data[0] = msg_type; memcpy(ds->buffer->data + 1, client->dhcpv6_transaction_id, 3); /* Form an elapsed option. */ /* Maximum value is 65535 1/100s coded as 0xffff. */ if ((elapsed.tv_sec < 0) || (elapsed.tv_sec > 655) || ((elapsed.tv_sec == 655) && (elapsed.tv_usec > 350000))) { client->elapsed = 0xffff; } else { client->elapsed = elapsed.tv_sec * 100; client->elapsed += elapsed.tv_usec / 10000; } if (client->elapsed == 0) log_debug("XMT: Forming %s, 0 ms elapsed.", msg_str); else log_debug("XMT: Forming %s, %u0 ms elapsed.", msg_str, (unsigned)client->elapsed); client->elapsed = htons(client->elapsed); make_client6_options(client, &client->sent_options, lease, msg_type); return(CHK_TIM_SUCCESS); } /*! * * \brief Create an iaid from information from the client. * * \param client = the state of the entire client * \param ia = the ia to fill in * \param idx = index of the ia in case we are doing multiples * \param len = length of the base IA (4 for TA, 12 for NA & PD) * * \return ISC_R_SUCCESS - all is well continue, any other return indicates * an error and the packet should be tossed */ static isc_result_t dhc6_create_iaid(struct client_state *client, struct data_string *ia, int idx, unsigned len) { int start_idx, copy_len; memset(ia, 0, sizeof(*ia)); if (!buffer_allocate(&ia->buffer, len, MDL)) { return (ISC_R_NOMEMORY); } ia->data = ia->buffer->data; ia->len = len; /* * A simple IAID is the last 4 bytes * of the hardware address. */ if (client->interface->hw_address.hlen > 4) { start_idx = client->interface->hw_address.hlen - 4; copy_len = 4; } else { start_idx = 0; copy_len = client->interface->hw_address.hlen; } memcpy(ia->buffer->data, client->interface->hw_address.hbuf + start_idx, copy_len); if (idx) ia->buffer->data[3] += idx; return (ISC_R_SUCCESS); } /*! * * \brief Add bare IA_NAs, IA_TAs or IA_PDs to the packet we are building. * * Attempt to add the number of bare IAs indicated by wanted to * the packet. As we have already added a number of IAs based * on what is in the current lease after we create an IAID we check * it against the current lease and skip any that are already in use. * * \param client = the state of the entire client * \param packet = the packet we are building and where we * shall append the IA_NA, IA_TA or IA_PDs we create * \param wanted = the number of IA_NA, IA_TA or IA_PDs we want to create * \param ia_type = the type of the IAs we want to create: NA, TA or PD. * * \return ISC_R_SUCCESS - all is well continue, any other return indicates * an error and the packet should be tossed */ static isc_result_t dhc6_bare_ia_xx(struct client_state *client, struct data_string *packet, int wanted, u_int16_t ia_type) { struct dhc6_ia *old_ia; struct data_string ia; u_int32_t t1, t2; int i, len; isc_result_t rval; char *type_string; struct option *type_option; /* figure out what type of option we are working with */ switch (ia_type) { case D6O_IA_NA: type_string = "IA_NA"; type_option = ia_na_option; len = IA_NA_OFFSET; break; case D6O_IA_TA: type_string = "IA_TA"; type_option = ia_ta_option; len = IA_TA_OFFSET; break; case D6O_IA_PD: type_string = "IA_PD"; type_option = ia_pd_option; len = IA_PD_OFFSET; if (prefix_len_hint > 0) { len += IASUBOPT_PD_LEN; } break; default: return (ISC_R_FAILURE); } for (i = 0; wanted != 0; i++) { rval = dhc6_create_iaid(client, &ia, i, len); if (rval != ISC_R_SUCCESS) { log_error("Unable to allocate memory for %s.", type_string); return (rval); } /* If we are already using this IAID, skip it and try again */ if ((client->active_lease != NULL) && ((old_ia = find_ia(client->active_lease->bindings, ia_type, (char *)ia.buffer->data)) != NULL)) { data_string_forget(&ia, MDL); continue; } /* We have a good IAID, log it */ log_debug("XMT: X-- %s %s", type_string, print_hex_1(4, ia.buffer->data, 55)); /* If we are requesting an NA or a PD we also want to add * the renew and rebind times we are requesting. */ if (ia_type != D6O_IA_TA) { t1 = client->config->requested_lease / 2; t2 = t1 + (t1 / 2); putULong(ia.buffer->data + 4, t1); putULong(ia.buffer->data + 8, t2); log_debug("XMT: | X-- Request renew in +%u", (unsigned)t1); log_debug("XMT: | X-- Request rebind in +%u", (unsigned)t2); } if (ia_type == D6O_IA_PD && prefix_len_hint > 0) { unsigned char *ptr = ia.buffer->data + IA_NA_OFFSET; putUShort(ptr, D6O_IAPREFIX); ptr += 2; putUShort(ptr, IASUBOPT_PD_LEN); ptr += 2; putUChar(ptr + IASUBOPT_PD_PREFLEN_OFFSET, prefix_len_hint); log_debug("XMT: | | X-- Request prefix ::/%u.", prefix_len_hint); } /* and append it to the packet */ append_option(packet, &dhcpv6_universe, type_option, &ia); data_string_forget(&ia, MDL); /* decrement the number of IAs we want */ wanted--; } return (ISC_R_SUCCESS); } /* * do_init6() marshals and transmits a solicit. */ void do_init6(void *input) { struct client_state *client; struct dhc6_ia *old_ia; struct dhc6_addr *old_addr; struct data_string ds; struct data_string ia; struct data_string addr; struct timeval tv; u_int32_t t1, t2; int i, send_ret; client = input; /* * In RFC3315 section 17.1.2, the retransmission timer is * used as the selecting timer. */ if (client->advertised_leases != NULL) { start_selecting6(client); return; } switch(check_timing6(client, DHCPV6_SOLICIT, "Solicit", NULL, &ds)) { case CHK_TIM_MRC_EXCEEDED: case CHK_TIM_ALLOC_FAILURE: return; case CHK_TIM_MRD_EXCEEDED: client->state = S_STOPPED; if (client->active_lease != NULL) { dhc6_lease_destroy(&client->active_lease, MDL); client->active_lease = NULL; } /* Stop if and only if this is the last client. */ if (stopping_finished()) finish(2); return; } /* * Fetch any configured 'sent' options (includes DUID) in wire format. */ dhcpv6_universe.encapsulate(&ds, NULL, NULL, client, NULL, client->sent_options, &global_scope, &dhcpv6_universe); /* Use a specific handler with rapid-commit. */ if (lookup_option(&dhcpv6_universe, client->sent_options, D6O_RAPID_COMMIT) != NULL) { client->v6_handler = rapid_commit_handler; } /* Append IA_NA. */ for (i = 0; i < wanted_ia_na; i++) { /* * XXX: maybe the IA_NA('s) should be put into the sent_options * cache. They'd have to be pulled down as they also contain * different option caches in the same universe... */ if (dhc6_create_iaid(client, &ia, i, 12) != ISC_R_SUCCESS) { log_error("Unable to allocate memory for IA_NA."); data_string_forget(&ds, MDL); return; } t1 = client->config->requested_lease / 2; t2 = t1 + (t1 / 2); putULong(ia.buffer->data + 4, t1); putULong(ia.buffer->data + 8, t2); log_debug("XMT: X-- IA_NA %s", print_hex_1(4, ia.buffer->data, 55)); log_debug("XMT: | X-- Request renew in +%u", (unsigned)t1); log_debug("XMT: | X-- Request rebind in +%u", (unsigned)t2); if ((client->active_lease != NULL) && ((old_ia = find_ia(client->active_lease->bindings, D6O_IA_NA, (char *)ia.buffer->data)) != NULL)) { /* * For each address in the old IA_NA, * request a binding. */ memset(&addr, 0, sizeof(addr)); for (old_addr = old_ia->addrs ; old_addr != NULL ; old_addr = old_addr->next) { if (old_addr->address.len != 16) { log_error("Invalid IPv6 address " "length %d. " "Ignoring. (%s:%d)", old_addr->address.len, MDL); continue; } if (!buffer_allocate(&addr.buffer, 24, MDL)) { log_error("Unable to allocate memory " "for IAADDR."); data_string_forget(&ia, MDL); data_string_forget(&ds, MDL); return; } addr.data = addr.buffer->data; addr.len = 24; memcpy(addr.buffer->data, old_addr->address.iabuf, 16); t1 = client->config->requested_lease; t2 = t1 + (t1 / 2); putULong(addr.buffer->data + 16, t1); putULong(addr.buffer->data + 20, t2); log_debug("XMT: | X-- Request address %s.", piaddr(old_addr->address)); log_debug("XMT: | | X-- Request " "preferred in +%u", (unsigned)t1); log_debug("XMT: | | X-- Request valid " "in +%u", (unsigned)t2); append_option(&ia, &dhcpv6_universe, iaaddr_option, &addr); data_string_forget(&addr, MDL); } } append_option(&ds, &dhcpv6_universe, ia_na_option, &ia); data_string_forget(&ia, MDL); } /* Append IA_TA. */ for (i = 0; i < wanted_ia_ta; i++) { /* * XXX: maybe the IA_TA('s) should be put into the sent_options * cache. They'd have to be pulled down as they also contain * different option caches in the same universe... */ if (dhc6_create_iaid(client, &ia, i, 4) != ISC_R_SUCCESS) { log_error("Unable to allocate memory for IA_TA."); data_string_forget(&ds, MDL); return; } log_debug("XMT: X-- IA_TA %s", print_hex_1(4, ia.buffer->data, 55)); if ((client->active_lease != NULL) && ((old_ia = find_ia(client->active_lease->bindings, D6O_IA_TA, (char *)ia.buffer->data)) != NULL)) { /* * For each address in the old IA_TA, * request a binding. */ memset(&addr, 0, sizeof(addr)); for (old_addr = old_ia->addrs ; old_addr != NULL ; old_addr = old_addr->next) { if (old_addr->address.len != 16) { log_error("Invalid IPv6 address " "length %d. " "Ignoring. (%s:%d)", old_addr->address.len, MDL); continue; } if (!buffer_allocate(&addr.buffer, 24, MDL)) { log_error("Unable to allocate memory " "for IAADDR."); data_string_forget(&ia, MDL); data_string_forget(&ds, MDL); return; } addr.data = addr.buffer->data; addr.len = 24; memcpy(addr.buffer->data, old_addr->address.iabuf, 16); t1 = client->config->requested_lease; t2 = t1 + (t1 / 2); putULong(addr.buffer->data + 16, t1); putULong(addr.buffer->data + 20, t2); log_debug("XMT: | X-- Request address %s.", piaddr(old_addr->address)); log_debug("XMT: | | X-- Request " "preferred in +%u", (unsigned)t1); log_debug("XMT: | | X-- Request valid " "in +%u", (unsigned)t2); append_option(&ia, &dhcpv6_universe, iaaddr_option, &addr); data_string_forget(&addr, MDL); } } append_option(&ds, &dhcpv6_universe, ia_ta_option, &ia); data_string_forget(&ia, MDL); } /* Append IA_PD. */ for (i = 0; i < wanted_ia_pd; i++) { /* * XXX: maybe the IA_PD('s) should be put into the sent_options * cache. They'd have to be pulled down as they also contain * different option caches in the same universe... */ memset(&ia, 0, sizeof(ia)); if (dhc6_create_iaid(client, &ia, i, 12) != ISC_R_SUCCESS) { log_error("Unable to allocate memory for IA_PD."); data_string_forget(&ds, MDL); return; } t1 = client->config->requested_lease / 2; t2 = t1 + (t1 / 2); putULong(ia.buffer->data + 4, t1); putULong(ia.buffer->data + 8, t2); log_debug("XMT: X-- IA_PD %s", print_hex_1(4, ia.buffer->data, 55)); log_debug("XMT: | X-- Request renew in +%u", (unsigned)t1); log_debug("XMT: | X-- Request rebind in +%u", (unsigned)t2); if ((client->active_lease != NULL) && ((old_ia = find_ia(client->active_lease->bindings, D6O_IA_PD, (char *)ia.buffer->data)) != NULL)) { /* * For each prefix in the old IA_PD, * request a binding. */ memset(&addr, 0, sizeof(addr)); for (old_addr = old_ia->addrs ; old_addr != NULL ; old_addr = old_addr->next) { if (old_addr->address.len != 16) { log_error("Invalid IPv6 prefix, " "Ignoring. (%s:%d)", MDL); continue; } if (!buffer_allocate(&addr.buffer, 25, MDL)) { log_error("Unable to allocate memory " "for IAPREFIX."); data_string_forget(&ia, MDL); data_string_forget(&ds, MDL); return; } addr.data = addr.buffer->data; addr.len = 25; t1 = client->config->requested_lease; t2 = t1 + (t1 / 2); putULong(addr.buffer->data, t1); putULong(addr.buffer->data + 4, t2); putUChar(addr.buffer->data + 8, old_addr->plen); memcpy(addr.buffer->data + 9, old_addr->address.iabuf, 16); log_debug("XMT: | X-- Request prefix %s/%u.", piaddr(old_addr->address), (unsigned) old_addr->plen); log_debug("XMT: | | X-- Request " "preferred in +%u", (unsigned)t1); log_debug("XMT: | | X-- Request valid " "in +%u", (unsigned)t2); append_option(&ia, &dhcpv6_universe, iaprefix_option, &addr); data_string_forget(&addr, MDL); } } else if (prefix_len_hint > 0) { memset(&addr, 0, sizeof(addr)); if (!buffer_allocate(&addr.buffer, 25, MDL)) { log_error("Unable to allocate memory " "for IAPREFIX."); data_string_forget(&ia, MDL); data_string_forget(&ds, MDL); return; } addr.data = addr.buffer->data; addr.len = 25; putUChar(addr.buffer->data + 8, prefix_len_hint); log_debug("XMT: | | X-- Request prefix ::/%u.", prefix_len_hint); append_option(&ia, &dhcpv6_universe, iaprefix_option, &addr); data_string_forget(&addr, MDL); } append_option(&ds, &dhcpv6_universe, ia_pd_option, &ia); data_string_forget(&ia, MDL); } /* Transmit and wait. */ log_info("XMT: Solicit on %s, interval %ld0ms.", client->name ? client->name : client->interface->name, (long int)client->RT); send_ret = send_packet6(client->interface, ds.data, ds.len, &DHCPv6DestAddr); if (send_ret != ds.len) { log_error("dhc6: send_packet6() sent %d of %d bytes", send_ret, ds.len); } data_string_forget(&ds, MDL); /* Wait RT */ tv.tv_sec = cur_tv.tv_sec + client->RT / 100; tv.tv_usec = cur_tv.tv_usec + (client->RT % 100) * 10000; if (tv.tv_usec >= 1000000) { tv.tv_sec += 1; tv.tv_usec -= 1000000; } add_timeout(&tv, do_init6, client, NULL, NULL); dhc6_retrans_advance(client); } /* do_info_request6() marshals and transmits an information-request. */ void do_info_request6(void *input) { struct client_state *client; struct data_string ds; struct timeval tv; int send_ret; client = input; switch(check_timing6(client, DHCPV6_INFORMATION_REQUEST, "Info-Request", NULL, &ds)) { case CHK_TIM_MRC_EXCEEDED: case CHK_TIM_ALLOC_FAILURE: return; case CHK_TIM_MRD_EXCEEDED: finish(2); case CHK_TIM_SUCCESS: break; } /* Fetch any configured 'sent' options (includes DUID) in wire format. */ dhcpv6_universe.encapsulate(&ds, NULL, NULL, client, NULL, client->sent_options, &global_scope, &dhcpv6_universe); /* Transmit and wait. */ log_info("XMT: Info-Request on %s, interval %ld0ms.", client->name ? client->name : client->interface->name, (long int)client->RT); send_ret = send_packet6(client->interface, ds.data, ds.len, &DHCPv6DestAddr); if (send_ret != ds.len) { log_error("dhc6: send_packet6() sent %d of %d bytes", send_ret, ds.len); } data_string_forget(&ds, MDL); /* Wait RT */ tv.tv_sec = cur_tv.tv_sec + client->RT / 100; tv.tv_usec = cur_tv.tv_usec + (client->RT % 100) * 10000; if (tv.tv_usec >= 1000000) { tv.tv_sec += 1; tv.tv_usec -= 1000000; } add_timeout(&tv, do_info_request6, client, NULL, NULL); dhc6_retrans_advance(client); } /* do_confirm6() creates a Confirm packet and transmits it. This function * is called on every timeout to (re)transmit. */ void do_confirm6(void *input) { struct client_state *client; struct data_string ds; int send_ret, added; struct timeval tv; client = input; if (client->active_lease == NULL) log_fatal("Impossible condition at %s:%d.", MDL); /* In section 17.1.3, it is said: * * If the client receives no responses before the message * transmission process terminates, as described in section 14, * the client SHOULD continue to use any IP addresses, using the * last known lifetimes for those addresses, and SHOULD continue * to use any other previously obtained configuration parameters. * * So if confirm times out, we go active. * * XXX: Should we reduce all IA's t1 to 0, so that we renew and * stick there until we get a reply? */ switch(check_timing6(client, DHCPV6_CONFIRM, "Confirm", client->active_lease, &ds)) { case CHK_TIM_MRC_EXCEEDED: case CHK_TIM_MRD_EXCEEDED: start_bound(client); return; case CHK_TIM_ALLOC_FAILURE: return; case CHK_TIM_SUCCESS: break; } /* Fetch any configured 'sent' options (includes DUID') in wire format. */ dhcpv6_universe.encapsulate(&ds, NULL, NULL, client, NULL, client->sent_options, &global_scope, &dhcpv6_universe); /* Append IA's. */ if (wanted_ia_na && dhc6_add_ia_na(client, &ds, client->active_lease, DHCPV6_CONFIRM, 0, &added) != ISC_R_SUCCESS) { data_string_forget(&ds, MDL); return; } if (wanted_ia_ta && dhc6_add_ia_ta(client, &ds, client->active_lease, DHCPV6_CONFIRM, 0, &added) != ISC_R_SUCCESS) { data_string_forget(&ds, MDL); return; } /* Transmit and wait. */ log_info("XMT: Confirm on %s, interval %ld0ms.", client->name ? client->name : client->interface->name, (long int)client->RT); send_ret = send_packet6(client->interface, ds.data, ds.len, &DHCPv6DestAddr); if (send_ret != ds.len) { log_error("dhc6: sendpacket6() sent %d of %d bytes", send_ret, ds.len); } data_string_forget(&ds, MDL); /* Wait RT */ tv.tv_sec = cur_tv.tv_sec + client->RT / 100; tv.tv_usec = cur_tv.tv_usec + (client->RT % 100) * 10000; if (tv.tv_usec >= 1000000) { tv.tv_sec += 1; tv.tv_usec -= 1000000; } add_timeout(&tv, do_confirm6, client, NULL, NULL); dhc6_retrans_advance(client); } /* * Release addresses. */ void start_release6(struct client_state *client) { /* Cancel any pending transmissions */ cancel_timeout(do_confirm6, client); cancel_timeout(do_select6, client); cancel_timeout(do_refresh6, client); cancel_timeout(do_release6, client); cancel_timeout(do_decline6, client); client->state = S_STOPPED; /* * It is written: "The client MUST NOT use any of the addresses it * is releasing as the source address in the Release message or in * any subsequently transmitted message." So unconfigure now. */ unconfigure6(client, "RELEASE6"); /* Note this in the lease file. */ if (client->active_lease == NULL) return; client->active_lease->released = ISC_TRUE; write_client6_lease(client, client->active_lease, 0, 1); /* Set timers per RFC3315 section 18.1.6. */ client->IRT = REL_TIMEOUT * 100; client->MRT = 0; client->MRC = REL_MAX_RC; client->MRD = 0; dhc6_retrans_init(client); client->v6_handler = reply_handler; do_release6(client); } /* * do_release6() creates a Release packet and transmits it. */ static void do_release6(void *input) { struct client_state *client; struct data_string ds; int send_ret, added; struct timeval tv; client = input; if ((client->active_lease == NULL) || !active_prefix(client)) return; switch(check_timing6(client, DHCPV6_RELEASE, "Release", client->active_lease, &ds)) { case CHK_TIM_MRC_EXCEEDED: case CHK_TIM_ALLOC_FAILURE: case CHK_TIM_MRD_EXCEEDED: goto release_done; case CHK_TIM_SUCCESS: break; } /* * Don't use unicast as we don't know if we still have an * available address with enough scope. */ dhcpv6_universe.encapsulate(&ds, NULL, NULL, client, NULL, client->sent_options, &global_scope, &dhcpv6_universe); /* Append IA's (but don't release temporary addresses). */ if (wanted_ia_na && dhc6_add_ia_na(client, &ds, client->active_lease, DHCPV6_RELEASE, 0, &added) != ISC_R_SUCCESS) { data_string_forget(&ds, MDL); goto release_done; } if (wanted_ia_pd && dhc6_add_ia_pd(client, &ds, client->active_lease, DHCPV6_RELEASE, 0, &added) != ISC_R_SUCCESS) { data_string_forget(&ds, MDL); goto release_done; } /* Transmit and wait. */ log_info("XMT: Release on %s, interval %ld0ms.", client->name ? client->name : client->interface->name, (long int)client->RT); send_ret = send_packet6(client->interface, ds.data, ds.len, &DHCPv6DestAddr); if (send_ret != ds.len) { log_error("dhc6: sendpacket6() sent %d of %d bytes", send_ret, ds.len); } data_string_forget(&ds, MDL); /* Wait RT */ tv.tv_sec = cur_tv.tv_sec + client->RT / 100; tv.tv_usec = cur_tv.tv_usec + (client->RT % 100) * 10000; if (tv.tv_usec >= 1000000) { tv.tv_sec += 1; tv.tv_usec -= 1000000; } add_timeout(&tv, do_release6, client, NULL, NULL); dhc6_retrans_advance(client); return; release_done: dhc6_lease_destroy(&client->active_lease, MDL); client->active_lease = NULL; if (stopping_finished()) finish(0); } /* status_log() just puts a status code into displayable form and logs it * to info level. */ static void status_log(int code, const char *scope, const char *additional, int len) { const char *msg = NULL; switch(code) { case STATUS_Success: msg = "Success"; break; case STATUS_UnspecFail: msg = "UnspecFail"; break; case STATUS_NoAddrsAvail: msg = "NoAddrsAvail"; break; case STATUS_NoBinding: msg = "NoBinding"; break; case STATUS_NotOnLink: msg = "NotOnLink"; break; case STATUS_UseMulticast: msg = "UseMulticast"; break; case STATUS_NoPrefixAvail: msg = "NoPrefixAvail"; break; default: msg = "UNKNOWN"; break; } if (len > 0) log_info("%s status code %s: %s", scope, msg, print_hex_1(len, (const unsigned char *)additional, 50)); else log_info("%s status code %s.", scope, msg); } /* Acquire a status code. */ static isc_result_t dhc6_get_status_code(struct option_state *options, unsigned *code, struct data_string *msg) { struct option_cache *oc; struct data_string ds; isc_result_t rval = ISC_R_SUCCESS; if ((options == NULL) || (code == NULL)) return DHCP_R_INVALIDARG; if ((msg != NULL) && (msg->len != 0)) return DHCP_R_INVALIDARG; memset(&ds, 0, sizeof(ds)); /* Assume success if there is no option. */ *code = STATUS_Success; oc = lookup_option(&dhcpv6_universe, options, D6O_STATUS_CODE); if ((oc != NULL) && evaluate_option_cache(&ds, NULL, NULL, NULL, options, NULL, &global_scope, oc, MDL)) { if (ds.len < 2) { log_error("Invalid status code length %d.", ds.len); rval = DHCP_R_FORMERR; } else *code = getUShort(ds.data); if ((msg != NULL) && (ds.len > 2)) { data_string_copy(msg, &ds, MDL); msg->data += 2; msg->len -= 2; } data_string_forget(&ds, MDL); return rval; } return ISC_R_NOTFOUND; } /* Look at status codes in an advertise, and reform the return value. */ static isc_result_t dhc6_check_status(isc_result_t rval, struct option_state *options, const char *scope, unsigned *code) { struct data_string msg; isc_result_t status; if ((scope == NULL) || (code == NULL)) return DHCP_R_INVALIDARG; /* If we don't find a code, we assume success. */ *code = STATUS_Success; /* If there is no options cache, then there is no code. */ if (options != NULL) { memset(&msg, 0, sizeof(msg)); status = dhc6_get_status_code(options, code, &msg); if (status == ISC_R_SUCCESS) { status_log(*code, scope, (char *)msg.data, msg.len); data_string_forget(&msg, MDL); if (*code != STATUS_Success) rval = ISC_R_FAILURE; } else if (status != ISC_R_NOTFOUND) rval = status; } return rval; } /* Determine if this packet could provide usable information. * We check the status codes at the top level and at the IA level, * IAADDRS have already been checked in the leaseify step and any with * a bad format or status code that wasn't success have been dropped. * * leaseify has also already removed any IAs for which the top level status * code or the IA status code indicated no addresses or prefixes were * available. */ static isc_result_t dhc6_check_advertise(struct dhc6_lease *lease) { struct dhc6_ia *ia; isc_result_t rval = ISC_R_SUCCESS; int have_addrs = ISC_FALSE; unsigned code; const char *scope; int got_na = 0, got_ta = 0, got_pd = 0; rval = dhc6_check_status(rval, lease->options, "message", &code); for (ia = lease->bindings ; ia != NULL ; ia = ia->next) { switch (ia->ia_type) { case D6O_IA_NA: scope = "IA_NA"; got_na++; break; case D6O_IA_TA: scope = "IA_TA"; got_ta++; break; case D6O_IA_PD: scope = "IA_PD"; got_pd++; break; default: log_error("dhc6_check_advertise: no type."); return ISC_R_FAILURE; } /* Currently we toss packets if we have an error getting a * status code or if the status code isn't success, so * no need to loop through the addresses */ rval = dhc6_check_status(rval, ia->options, scope, &code); if (rval != ISC_R_SUCCESS) continue; /* We don't need to check status on IAADDRS here as we already * did it as part of the leaseify step and tossed bad IAADDRS. * We are just checking to see if we have any addrs. * Should we check the addr itself for usability? */ if (ia->addrs != NULL) { have_addrs = ISC_TRUE; } } /* If we didn't get some addrs or the user required us to * get all of the requested IAs and we didn't return an error */ if ((have_addrs != ISC_TRUE) || ((require_all_ias != 0) && ((got_na < wanted_ia_na) || (got_ta < wanted_ia_ta) || (got_pd < wanted_ia_pd)))) rval = ISC_R_ADDRNOTAVAIL; return rval; } /* status code <-> action matrix for the client in INIT state * (rapid/commit). Returns always false as no action is defined. */ static isc_boolean_t dhc6_init_action(struct client_state *client, isc_result_t *rvalp, unsigned code) { if (rvalp == NULL) log_fatal("Impossible condition at %s:%d.", MDL); if (client == NULL) { *rvalp = DHCP_R_INVALIDARG; return ISC_FALSE; } if (*rvalp == ISC_R_SUCCESS) return ISC_FALSE; /* No possible action in any case... */ return ISC_FALSE; } /* status code <-> action matrix for the client in SELECT state * (request/reply). Returns true if action was taken (and the * packet should be ignored), or false if no action was taken. */ static isc_boolean_t dhc6_select_action(struct client_state *client, isc_result_t *rvalp, unsigned code) { struct dhc6_lease *lease; isc_result_t rval; if (rvalp == NULL) log_fatal("Impossible condition at %s:%d.", MDL); if (client == NULL) { *rvalp = DHCP_R_INVALIDARG; return ISC_FALSE; } rval = *rvalp; if (rval == ISC_R_SUCCESS) return ISC_FALSE; switch (code) { /* We may have an earlier failure status code (so no * success rval), and a success code now. This * doesn't upgrade the rval to success, but it does * mean we take no action here. */ case STATUS_Success: /* Gimpy server, or possibly an attacker. */ case STATUS_NoBinding: case STATUS_UseMulticast: /* Take no action. */ return ISC_FALSE; /* If the server can't deal with us, either try the * next advertised server, or continue retrying if there * weren't any. */ default: case STATUS_UnspecFail: if (client->advertised_leases != NULL) { dhc6_lease_destroy(&client->selected_lease, MDL); client->selected_lease = NULL; start_selecting6(client); break; } else /* Take no action - continue to retry. */ return ISC_FALSE; /* If the server has no addresses, try other servers if * we got some, otherwise go to INIT to hope for more * servers. */ case STATUS_NoAddrsAvail: case STATUS_NoPrefixAvail: if (client->state == S_REBOOTING) return ISC_FALSE; if (client->selected_lease == NULL) log_fatal("Impossible case at %s:%d.", MDL); dhc6_lease_destroy(&client->selected_lease, MDL); client->selected_lease = NULL; if (client->advertised_leases != NULL) start_selecting6(client); else start_init6(client); break; /* If we got a NotOnLink from a Confirm, then we're not * on link. Kill the old-active binding and start over. * * If we got a NotOnLink from our Request, something weird * happened. Start over from scratch anyway. */ case STATUS_NotOnLink: if (client->state == S_REBOOTING) { if (client->active_lease == NULL) log_fatal("Impossible case at %s:%d.", MDL); dhc6_lease_destroy(&client->active_lease, MDL); } else { if (client->selected_lease == NULL) log_fatal("Impossible case at %s:%d.", MDL); dhc6_lease_destroy(&client->selected_lease, MDL); client->selected_lease = NULL; while (client->advertised_leases != NULL) { lease = client->advertised_leases; client->advertised_leases = lease->next; dhc6_lease_destroy(&lease, MDL); } } start_init6(client); break; } return ISC_TRUE; } static void dhc6_withdraw_lease(struct client_state *client) { struct dhc6_ia *ia; struct dhc6_addr *addr; if ((client == NULL) || (client->active_lease == NULL)) return; for (ia = client->active_lease->bindings ; ia != NULL ; ia = ia->next) { for (addr = ia->addrs ; addr != NULL ; addr = addr->next) { addr->max_life = addr->preferred_life = 0; } } /* Perform expiry. */ do_expire(client); } /* status code <-> action matrix for the client in BOUND state * (request/reply). Returns true if action was taken (and the * packet should be ignored), or false if no action was taken. */ static isc_boolean_t dhc6_reply_action(struct client_state *client, isc_result_t *rvalp, unsigned code) { isc_result_t rval; if (rvalp == NULL) log_fatal("Impossible condition at %s:%d.", MDL); if (client == NULL) { *rvalp = DHCP_R_INVALIDARG; return ISC_FALSE; } rval = *rvalp; if (rval == ISC_R_SUCCESS) return ISC_FALSE; switch (code) { /* It's possible an earlier status code set rval to a failure * code, and we've encountered a later success. */ case STATUS_Success: /* In "refreshes" (where we get replies), we probably * still have a valid lease. So "take no action" and * the upper levels will keep retrying until the lease * expires (or we rebind). */ case STATUS_UnspecFail: /* For unknown codes...it's a soft (retryable) error. */ default: return ISC_FALSE; /* The server is telling us to use a multicast address, so * we have to delete the unicast option from the active * lease, then allow retransmission to occur normally. * (XXX: It might be preferable in this case to retransmit * sooner than the current interval, but for now we don't.) */ case STATUS_UseMulticast: if (client->active_lease != NULL) delete_option(&dhcp_universe, client->active_lease->options, D6O_UNICAST); return ISC_FALSE; /* "When the client receives a NotOnLink status from the * server in response to a Request, the client can either * re-issue the Request without specifying any addresses * or restart the DHCP server discovery process." * * This is strange. If competing server evaluation is * useful (and therefore in the protocol), then why would * a client's first reaction be to request from the same * server on a different link? Surely you'd want to * re-evaluate your server selection. * * Well, I guess that's the answer. */ case STATUS_NotOnLink: /* In this case, we need to rescind all current active * bindings (just 'expire' them all normally, if early). * They're no use to us on the wrong link. Then head back * to init, redo server selection and get new addresses. */ dhc6_withdraw_lease(client); break; /* "If the status code is NoAddrsAvail, the client has * received no usable addresses in the IA and may choose * to try obtaining addresses for the IA from another * server." */ case STATUS_NoAddrsAvail: case STATUS_NoPrefixAvail: /* Head back to init, keeping any active bindings (!). */ start_init6(client); break; /* - sends a Request message if the IA contained a Status * Code option with the NoBinding status (and does not * send any additional Renew/Rebind messages) */ case STATUS_NoBinding: if (client->advertised_leases != NULL) log_fatal("Impossible condition at %s:%d.", MDL); client->advertised_leases = dhc6_dup_lease(client->active_lease, MDL); start_selecting6(client); break; } return ISC_TRUE; } /* status code <-> action matrix for the client in STOPPED state * (release/decline). Returns true if action was taken (and the * packet should be ignored), or false if no action was taken. * NoBinding is translated into Success. */ static isc_boolean_t dhc6_stop_action(struct client_state *client, isc_result_t *rvalp, unsigned code) { isc_result_t rval; if (rvalp == NULL) log_fatal("Impossible condition at %s:%d.", MDL); if (client == NULL) { *rvalp = DHCP_R_INVALIDARG; return ISC_FALSE; } rval = *rvalp; if (rval == ISC_R_SUCCESS) return ISC_FALSE; switch (code) { /* It's possible an earlier status code set rval to a failure * code, and we've encountered a later success. */ case STATUS_Success: /* For unknown codes...it's a soft (retryable) error. */ case STATUS_UnspecFail: default: return ISC_FALSE; /* NoBinding is not an error */ case STATUS_NoBinding: if (rval == ISC_R_FAILURE) *rvalp = ISC_R_SUCCESS; return ISC_FALSE; /* Should not happen */ case STATUS_NoAddrsAvail: case STATUS_NoPrefixAvail: break; /* Give up on it */ case STATUS_NotOnLink: break; /* The server is telling us to use a multicast address, so * we have to delete the unicast option from the active * lease, then allow retransmission to occur normally. * (XXX: It might be preferable in this case to retransmit * sooner than the current interval, but for now we don't.) */ case STATUS_UseMulticast: if (client->active_lease != NULL) delete_option(&dhcp_universe, client->active_lease->options, D6O_UNICAST); return ISC_FALSE; } return ISC_TRUE; } static isc_boolean_t dhc6_decline_action(struct client_state *client, isc_result_t *rvalp, unsigned code) { isc_result_t rval; if (rvalp == NULL) log_fatal("Impossible condition at %s:%d.", MDL); if (client == NULL) { *rvalp = DHCP_R_INVALIDARG; return ISC_FALSE; } rval = *rvalp; if (rval == ISC_R_SUCCESS) { return ISC_FALSE; } switch (code) { case STATUS_UseMulticast: /* The server is telling us to use a multicast address, so * we have to delete the unicast option from the active * lease, then allow retransmission to occur normally. * (XXX: It might be preferable in this case to retransmit * sooner than the current interval, but for now we don't.) */ if (client->active_lease != NULL) delete_option(&dhcp_universe, client->active_lease->options, D6O_UNICAST); return ISC_FALSE; default: /* Anything else is basically meaningless */ break; } return ISC_TRUE; } /* Look at a new and old lease, and make sure the new information is not * losing us any state. */ static isc_result_t dhc6_check_reply(struct client_state *client, struct dhc6_lease *new) { isc_boolean_t (*action)(struct client_state *, isc_result_t *, unsigned); struct dhc6_ia *ia; isc_result_t rval = ISC_R_SUCCESS; unsigned code; const char *scope; int nscore, sscore; int have_addrs = ISC_FALSE; int got_na = 0, got_ta = 0, got_pd = 0; if ((client == NULL) || (new == NULL)) return DHCP_R_INVALIDARG; switch (client->state) { case S_INIT: action = dhc6_init_action; break; case S_SELECTING: case S_REBOOTING: action = dhc6_select_action; break; case S_RENEWING: case S_REBINDING: action = dhc6_reply_action; break; case S_STOPPED: action = dhc6_stop_action; break; case S_DECLINING: action = dhc6_decline_action; break; default: log_fatal("Impossible condition at %s:%d.", MDL); return ISC_R_CANCELED; } /* If there is a code to extract, and if there is some * action to take based on that code, then take the action * and do not continue. */ rval = dhc6_check_status(rval, new->options, "message", &code); if (action(client, &rval, code)) return ISC_R_CANCELED; for (ia = new->bindings ; ia != NULL ; ia = ia->next) { switch (ia->ia_type) { case D6O_IA_NA: scope = "IA_NA"; got_na++; break; case D6O_IA_TA: scope = "IA_TA"; got_ta++; break; case D6O_IA_PD: scope = "IA_PD"; got_pd++; break; default: log_error("dhc6_check_reply: no type."); return DHCP_R_INVALIDARG; } rval = dhc6_check_status(rval, ia->options, scope, &code); if (action(client, &rval, code)) return ISC_R_CANCELED; if (ia->addrs != NULL) { have_addrs = ISC_TRUE; } } /* A Confirm->Reply is unsuitable for comparison to the old lease. */ if (client->state == S_REBOOTING) return rval; /* We expect the lease to have at least one address and if * required all of the requested IAs if not flag it as * NoAddrs and call the action routine to try again. * * Currently we don't completely handle TAs in all cases * so we don't check them for requires. I've left the * check in and commented it as I eventually do want * us to check for TAs as well. SAR */ if ((have_addrs != ISC_TRUE) || ((require_all_ias != 0) && ((got_na < wanted_ia_na) || /*(got_ta < wanted_ia_ta) ||*/ (got_pd < wanted_ia_pd)))) { rval = ISC_R_FAILURE; if (action(client, &rval, STATUS_NoAddrsAvail) == ISC_TRUE) { return ISC_R_CANCELED; } } /* No old lease in rapid-commit. */ if (client->state == S_INIT) return rval; switch (client->state) { case S_SELECTING: /* Compare the new lease with the selected lease to make * sure there is no risky business. */ nscore = dhc6_score_lease(client, new); sscore = dhc6_score_lease(client, client->selected_lease); if ((client->advertised_leases != NULL) && (nscore < (sscore / 2))) { /* XXX: An attacker might reply this way to make * XXX: sure we latch onto their configuration. * XXX: We might want to ignore the packet and * XXX: schedule re-selection at the next timeout? */ log_error("PRC: BAIT AND SWITCH detected. Score of " "supplied lease (%d) is substantially " "smaller than the advertised score (%d). " "Trying other servers.", nscore, sscore); dhc6_lease_destroy(&client->selected_lease, MDL); client->selected_lease = NULL; start_selecting6(client); return ISC_R_CANCELED; } break; case S_RENEWING: case S_REBINDING: /* This leaves one RFC3315 status check unimplemented: * * - sends a Renew/Rebind if the IA is not in the Reply * message * * We rely on the scheduling system to note that the IA has * not left Renewal/Rebinding/whatever since it still carries * old times from the last successful binding. So this is * implemented actually, just not explicitly. */ break; case S_STOPPED: case S_DECLINING: /* Nothing critical to do at this stage. */ break; default: log_fatal("REALLY impossible condition at %s:%d.", MDL); return ISC_R_CANCELED; } return rval; } /* While in init state, we only collect advertisements. If there happens * to be an advertisement with a preference option of 255, that's an * automatic exit. Otherwise, we collect advertisements until our timeout * expires (client->RT). */ void init_handler(struct packet *packet, struct client_state *client) { struct dhc6_lease *lease; /* In INIT state, we send solicits, we only expect to get * advertises (rapid commit has its own handler). */ if (packet->dhcpv6_msg_type != DHCPV6_ADVERTISE) return; /* RFC3315 section 15.3 validation (same as 15.10 since we * always include a client id). */ if (!valid_reply(packet, client)) { log_error("Invalid Advertise - rejecting."); return; } lease = dhc6_leaseify(packet, client); /* Out of memory or corrupt packet condition...hopefully a temporary * problem. Returning now makes us try to retransmit later. */ if (lease == NULL) return; if (dhc6_check_advertise(lease) != ISC_R_SUCCESS) { log_debug("PRC: Lease failed to satisfy."); dhc6_lease_destroy(&lease, MDL); return; } int lease_score = dhc6_score_lease(client, lease); #ifdef ENFORCE_DHCPV6_CLIENT_REQUIRE if (lease_score == 0) { log_debug("RCV:Advertised lease scored 0, toss it."); dhc6_lease_destroy(&lease, MDL); return; } #endif insert_lease(&client->advertised_leases, lease); /* According to RFC3315 section 17.1.2, the client MUST wait for * the first RT before selecting a lease. But on the 400th RT, * we dont' want to wait the full timeout if we finally get an * advertise. We could probably wait a second, but ohwell, * RFC3315 doesn't say so. * * If the lease is highest possible preference, 255, RFC3315 claims * we should continue immediately even on the first RT. We probably * should not if the advertise contains less than one IA and address. */ if ((client->txcount > 1) || ((lease->pref == 255) && (lease_score > SCORE_MIN))) { log_debug("RCV: Advertisement immediately selected."); cancel_timeout(do_init6, client); start_selecting6(client); } else log_debug("RCV: Advertisement recorded."); } /* info_request_handler() accepts a Reply to an Info-request. */ void info_request_handler(struct packet *packet, struct client_state *client) { isc_result_t check_status; unsigned code; if (packet->dhcpv6_msg_type != DHCPV6_REPLY) return; /* RFC3315 section 15.10 validation (same as 15.3 since we * always include a client id). */ if (!valid_reply(packet, client)) { log_error("Invalid Reply - rejecting."); return; } check_status = dhc6_check_status(ISC_R_SUCCESS, packet->options, "message", &code); if (check_status != ISC_R_SUCCESS) { /* If no action was taken, but there is an error, then * we wait for a retransmission. */ if (check_status != ISC_R_CANCELED) return; } /* We're done retransmitting at this point. */ cancel_timeout(do_info_request6, client); /* Action was taken, so now that we've torn down our scheduled * retransmissions, return. */ if (check_status == ISC_R_CANCELED) return; /* Cleanup if a previous attempt to go bound failed. */ if (client->old_lease != NULL) { dhc6_lease_destroy(&client->old_lease, MDL); client->old_lease = NULL; } /* Cache options in the active_lease. */ if (client->active_lease != NULL) client->old_lease = client->active_lease; client->active_lease = dmalloc(sizeof(struct dhc6_lease), MDL); if (client->active_lease == NULL) log_fatal("Out of memory for v6 lease structure."); option_state_reference(&client->active_lease->options, packet->options, MDL); execute_statements_in_scope(NULL, (struct packet *)packet, NULL, client, client->active_lease->options, client->active_lease->options, &global_scope, client->config->on_receipt, NULL, NULL); start_informed(client); } /* Specific version of init_handler() for rapid-commit. */ void rapid_commit_handler(struct packet *packet, struct client_state *client) { struct dhc6_lease *lease; isc_result_t check_status; /* On ADVERTISE just fall back to the init_handler(). */ if (packet->dhcpv6_msg_type == DHCPV6_ADVERTISE) { init_handler(packet, client); return; } else if (packet->dhcpv6_msg_type != DHCPV6_REPLY) return; /* RFC3315 section 15.10 validation (same as 15.3 since we * always include a client id). */ if (!valid_reply(packet, client)) { log_error("Invalid Reply - rejecting."); return; } /* A rapid-commit option MUST be here. */ if (lookup_option(&dhcpv6_universe, packet->options, D6O_RAPID_COMMIT) == 0) { log_error("Reply without Rapid-Commit - rejecting."); return; } lease = dhc6_leaseify(packet, client); /* Out of memory or corrupt packet condition...hopefully a temporary * problem. Returning now makes us try to retransmit later. */ if (lease == NULL) return; check_status = dhc6_check_reply(client, lease); if (check_status != ISC_R_SUCCESS) { dhc6_lease_destroy(&lease, MDL); return; } /* Jump to the selecting state. */ cancel_timeout(do_init6, client); client->state = S_SELECTING; /* Merge any bindings in the active lease (if there is one) into * the new active lease. */ dhc6_merge_lease(client->active_lease, lease); /* Cleanup if a previous attempt to go bound failed. */ if (client->old_lease != NULL) { dhc6_lease_destroy(&client->old_lease, MDL); client->old_lease = NULL; } /* Make this lease active and BIND to it. */ if (client->active_lease != NULL) client->old_lease = client->active_lease; client->active_lease = lease; /* We're done with the ADVERTISEd leases, if any. */ while(client->advertised_leases != NULL) { lease = client->advertised_leases; client->advertised_leases = lease->next; dhc6_lease_destroy(&lease, MDL); } start_bound(client); } /* Find the 'best' lease in the cache of advertised leases (usually). From * RFC3315 Section 17.1.3: * * Upon receipt of one or more valid Advertise messages, the client * selects one or more Advertise messages based upon the following * criteria. * * - Those Advertise messages with the highest server preference value * are preferred over all other Advertise messages. * * - Within a group of Advertise messages with the same server * preference value, a client MAY select those servers whose * Advertise messages advertise information of interest to the * client. For example, the client may choose a server that returned * an advertisement with configuration options of interest to the * client. * * - The client MAY choose a less-preferred server if that server has a * better set of advertised parameters, such as the available * addresses advertised in IAs. * * Note that the first and third contradict each other. The third should * probably be taken to mean that the client should prefer answers that * offer bindings, even if that violates the preference rule. * * The above also isn't deterministic where there are ties. So the final * tiebreaker we add, if all other values are equal, is to compare the * server identifiers and to select the numerically lower one. */ static struct dhc6_lease * dhc6_best_lease(struct client_state *client, struct dhc6_lease **head) { struct dhc6_lease **rpos, *rval, **candp, *cand; int cscore, rscore; if (head == NULL || *head == NULL) return NULL; rpos = head; rval = *rpos; rscore = dhc6_score_lease(client, rval); candp = &rval->next; cand = *candp; log_debug("PRC: Considering best lease."); log_debug("PRC: X-- Initial candidate %s (s: %d, p: %u).", print_hex_1(rval->server_id.len, rval->server_id.data, 48), rscore, (unsigned)rval->pref); for (; cand != NULL ; candp = &cand->next, cand = *candp) { cscore = dhc6_score_lease(client, cand); log_debug("PRC: X-- Candidate %s (s: %d, p: %u).", print_hex_1(cand->server_id.len, cand->server_id.data, 48), cscore, (unsigned)cand->pref); /* Above you'll find quoted RFC3315 Section 17.1.3. * * The third clause tells us to give up on leases that * have no bindings even if their preference is better. * So where our 'selected' lease's score is less than * SCORE_MIN (1 ia + 1 addr), choose any candidate >= SCORE_MIN. * * The first clause tells us to make preference the primary * deciding factor. So if it's lower, reject, if it's * higher, select. * * The second clause tells us where the preference is * equal, we should use 'our judgement' of what we like * to see in an advertisement primarily. * * But there can still be a tie. To make this deterministic, * we compare the server identifiers and select the binary * lowest. * * Since server id's are unique in this list, there is * no further tie to break. */ if ((rscore < SCORE_MIN) && (cscore >= SCORE_MIN)) { log_debug("PRC: | X-- Selected, has bindings."); } else if (cand->pref < rval->pref) { log_debug("PRC: | X-- Rejected, lower preference."); continue; } else if (cand->pref > rval->pref) { log_debug("PRC: | X-- Selected, higher preference."); } else if (cscore > rscore) { log_debug("PRC: | X-- Selected, equal preference, " "higher score."); } else if (cscore < rscore) { log_debug("PRC: | X-- Rejected, equal preference, " "lower score."); continue; } else if ((cand->server_id.len < rval->server_id.len) || ((cand->server_id.len == rval->server_id.len) && (memcmp(cand->server_id.data, rval->server_id.data, cand->server_id.len) < 0))) { log_debug("PRC: | X-- Selected, equal preference, " "equal score, binary lesser server ID."); } else { log_debug("PRC: | X-- Rejected, equal preference, " "equal score, binary greater server ID."); continue; } rpos = candp; rval = cand; rscore = cscore; } /* Remove the selected lease from the chain. */ *rpos = rval->next; return rval; } /* Select a lease out of the advertised leases and setup state to try and * acquire that lease. */ void start_selecting6(struct client_state *client) { struct dhc6_lease *lease; if (client->advertised_leases == NULL) { log_error("Can not enter DHCPv6 SELECTING state with no " "leases to select from!"); return; } log_debug("PRC: Selecting best advertised lease."); client->state = S_SELECTING; lease = dhc6_best_lease(client, &client->advertised_leases); if (lease == NULL) log_fatal("Impossible error at %s:%d.", MDL); client->selected_lease = lease; /* Set timers per RFC3315 section 18.1.1. */ client->IRT = REQ_TIMEOUT * 100; client->MRT = REQ_MAX_RT * 100; client->MRC = REQ_MAX_RC; client->MRD = 0; dhc6_retrans_init(client); client->v6_handler = reply_handler; /* ("re")transmit the first packet. */ do_select6(client); } /* Transmit a Request to select a lease offered in Advertisements. In * the event of failure, either move on to the next-best advertised lease, * or head back to INIT state if there are none. */ void do_select6(void *input) { struct client_state *client; struct dhc6_lease *lease; struct data_string ds; struct timeval tv; int send_ret, added; client = input; /* 'lease' is fewer characters to type. */ lease = client->selected_lease; if (lease == NULL || lease->bindings == NULL) { log_error("Illegal to attempt selection without selecting " "a lease."); return; } switch(check_timing6(client, DHCPV6_REQUEST, "Request", lease, &ds)) { case CHK_TIM_MRC_EXCEEDED: case CHK_TIM_MRD_EXCEEDED: log_debug("PRC: Lease %s failed.", print_hex_1(lease->server_id.len, lease->server_id.data, 56)); /* Get rid of the lease that timed/counted out. */ dhc6_lease_destroy(&lease, MDL); client->selected_lease = NULL; /* If there are more leases great. If not, get more. */ if (client->advertised_leases != NULL) start_selecting6(client); else start_init6(client); return; case CHK_TIM_ALLOC_FAILURE: return; case CHK_TIM_SUCCESS: break; } /* Now make a packet that looks suspiciously like the one we * got from the server. But different. * * XXX: I guess IAID is supposed to be something the client * indicates and uses as a key to its internal state. It is * kind of odd to ask the server for IA's whose IAID the client * did not manufacture. We first need a formal dhclient.conf * construct for the iaid, then we can delve into this matter * more properly. In the time being, this will work. */ /* Fetch any configured 'sent' options (includes DUID) in wire format. */ dhcpv6_universe.encapsulate(&ds, NULL, NULL, client, NULL, client->sent_options, &global_scope, &dhcpv6_universe); /* Now append any IA's, and within them any IAADDR/IAPREFIXs. * For each type of IA (na, ta, pd) we start with the ones for * which we already have addresses (dhc6_add_ia_xx) and then * if we still want more we add aditional IAs (dhc6_bare_ia_xx) */ if (wanted_ia_na && ((dhc6_add_ia_na(client, &ds, lease, DHCPV6_REQUEST, wanted_ia_na, &added) != ISC_R_SUCCESS) || (dhc6_bare_ia_xx(client, &ds, wanted_ia_na - added, D6O_IA_NA) != ISC_R_SUCCESS))) { data_string_forget(&ds, MDL); return; } if (wanted_ia_ta && ((dhc6_add_ia_ta(client, &ds, lease, DHCPV6_REQUEST, wanted_ia_ta, &added) != ISC_R_SUCCESS) || (dhc6_bare_ia_xx(client, &ds, wanted_ia_ta - added, D6O_IA_TA) != ISC_R_SUCCESS))) { data_string_forget(&ds, MDL); return; } if (wanted_ia_pd && ((dhc6_add_ia_pd(client, &ds, lease, DHCPV6_REQUEST, wanted_ia_pd, &added) != ISC_R_SUCCESS) || (dhc6_bare_ia_xx(client, &ds, wanted_ia_pd - added, D6O_IA_PD) != ISC_R_SUCCESS))) { data_string_forget(&ds, MDL); return; } log_info("XMT: Request on %s, interval %ld0ms.", client->name ? client->name : client->interface->name, (long int)client->RT); send_ret = send_packet6(client->interface, ds.data, ds.len, &DHCPv6DestAddr); if (send_ret != ds.len) { log_error("dhc6: send_packet6() sent %d of %d bytes", send_ret, ds.len); } data_string_forget(&ds, MDL); /* Wait RT */ tv.tv_sec = cur_tv.tv_sec + client->RT / 100; tv.tv_usec = cur_tv.tv_usec + (client->RT % 100) * 10000; if (tv.tv_usec >= 1000000) { tv.tv_sec += 1; tv.tv_usec -= 1000000; } add_timeout(&tv, do_select6, client, NULL, NULL); dhc6_retrans_advance(client); } /*! * * \brief Count the number of IAs in the bindings * * \param lease the lease to count * \param ia_type the type of the IA we wish to count * * \return The number of IAs of the specified type we found */ static int dhc6_count_ia(struct dhc6_lease *lease, u_int16_t ia_type) { struct dhc6_ia *ia; int i = 0; for (ia = lease->bindings; ia != NULL; ia = ia->next) { if (ia->ia_type == ia_type) /* bump the counter for the correct types */ i++; } return (i); } /*! * * \brief Add IA_NA information from the lease to the packet * we are building. * * Walk through the lease and for each IA_NA in the lease * and for each address in the IA_NA append that information * onto the packet-so-far. If wanted is 0 include all IA_NAs * in the lease if wanted is non-zero include only that many * IA_NAs (this may occur if sommebody restarts a client with * arugments for a smaller number of NAs than before). * * \param client = the state of the entire client * \param packet = the packet we are building and where we * shall append the IA_NAs we create * \param lease = the current lease * \param message = the type of the packet * \param wanted = the number of IA_NAs to include in the packet * 0 means include all * \param added = the number of IA_NAs that were added to the packet * * \return ISC_R_SUCCESS - all is well continue, any other return * indicates an error (most likely memory issues) * and the packet should be tossed. */ static isc_result_t dhc6_add_ia_na(struct client_state *client, struct data_string *packet, struct dhc6_lease *lease, u_int8_t message, int wanted, int *added) { struct data_string iads; struct data_string addrds; struct dhc6_addr *addr; struct dhc6_ia *ia; isc_result_t rval = ISC_R_SUCCESS; TIME t1, t2; int i; *added = 0; memset(&iads, 0, sizeof(iads)); memset(&addrds, 0, sizeof(addrds)); for (ia = lease->bindings, i = 0; ia != NULL && rval == ISC_R_SUCCESS && (wanted == 0 || i < wanted); ia = ia->next) { if (ia->ia_type != D6O_IA_NA) continue; /* Now that we know this is an NA bump the counter */ i++; if (!buffer_allocate(&iads.buffer, 12, MDL)) { log_error("Unable to allocate memory for IA_NA."); rval = ISC_R_NOMEMORY; break; } /* Copy the IAID into the packet buffer. */ memcpy(iads.buffer->data, ia->iaid, 4); iads.data = iads.buffer->data; iads.len = 12; switch (message) { case DHCPV6_REQUEST: case DHCPV6_RENEW: case DHCPV6_REBIND: t1 = client->config->requested_lease / 2; t2 = t1 + (t1 / 2); #if MAX_TIME > 0xffffffff if (t1 > 0xffffffff) t1 = 0xffffffff; if (t2 > 0xffffffff) t2 = 0xffffffff; #endif putULong(iads.buffer->data + 4, t1); putULong(iads.buffer->data + 8, t2); log_debug("XMT: X-- IA_NA %s", print_hex_1(4, iads.data, 59)); log_debug("XMT: | X-- Requested renew +%u", (unsigned) t1); log_debug("XMT: | X-- Requested rebind +%u", (unsigned) t2); break; case DHCPV6_CONFIRM: case DHCPV6_RELEASE: case DHCPV6_DECLINE: /* Set t1 and t2 to zero; server will ignore them */ memset(iads.buffer->data + 4, 0, 8); log_debug("XMT: X-- IA_NA %s", print_hex_1(4, iads.buffer->data, 55)); break; default: log_fatal("Impossible condition at %s:%d.", MDL); } for (addr = ia->addrs ; addr != NULL ; addr = addr->next) { /* * Do not confirm expired addresses, do not request * expired addresses (but we keep them around for * solicit). */ if (addr->flags & DHC6_ADDR_EXPIRED) continue; if (addr->address.len != 16) { log_error("Illegal IPv6 address length (%d), " "ignoring. (%s:%d)", addr->address.len, MDL); continue; } if (!buffer_allocate(&addrds.buffer, 24, MDL)) { log_error("Unable to allocate memory for " "IAADDR."); rval = ISC_R_NOMEMORY; break; } addrds.data = addrds.buffer->data; addrds.len = 24; /* Copy the address into the packet buffer. */ memcpy(addrds.buffer->data, addr->address.iabuf, 16); /* Copy in additional information as appropriate */ switch (message) { case DHCPV6_REQUEST: case DHCPV6_RENEW: case DHCPV6_REBIND: t1 = client->config->requested_lease; t2 = t1 + 300; putULong(addrds.buffer->data + 16, t1); putULong(addrds.buffer->data + 20, t2); log_debug("XMT: | | X-- IAADDR %s", piaddr(addr->address)); log_debug("XMT: | | | X-- Preferred " "lifetime +%u", (unsigned)t1); log_debug("XMT: | | | X-- Max lifetime +%u", (unsigned)t2); break; case DHCPV6_CONFIRM: /* * Set preferred and max life to zero, * per 17.1.3. */ memset(addrds.buffer->data + 16, 0, 8); log_debug("XMT: | X-- Confirm Address %s", piaddr(addr->address)); break; case DHCPV6_RELEASE: /* Preferred and max life are irrelevant */ memset(addrds.buffer->data + 16, 0, 8); log_debug("XMT: | X-- Release Address %s", piaddr(addr->address)); break; case DHCPV6_DECLINE: /* Preferred and max life are irrelevant */ memset(addrds.buffer->data + 16, 0, 8); log_debug("XMT: | X-- Decline Address %s", piaddr(addr->address)); break; default: log_fatal("Impossible condition at %s:%d.", MDL); } append_option(&iads, &dhcpv6_universe, iaaddr_option, &addrds); data_string_forget(&addrds, MDL); } /* * It doesn't make sense to make a request without an * address. */ if (ia->addrs == NULL) { log_debug("!!!: V IA_NA has no IAADDRs - removed."); rval = ISC_R_FAILURE; } else if (rval == ISC_R_SUCCESS) { log_debug("XMT: V IA_NA appended."); append_option(packet, &dhcpv6_universe, ia_na_option, &iads); } data_string_forget(&iads, MDL); } if (rval == ISC_R_SUCCESS) *added = i; return (rval); } /*! * * \brief Add IA_TA information from the lease to the packet * we are building. * * Walk through the lease and for each IA_TA in the lease * and for each address in the IA_TA append that information * onto the packet-so-far. If wanted is 0 include all IA_TAs * in the lease if wanted is non-zero include only that many * IA_TAs (this may occur if sommebody restarts a client with * arugments for a smaller number of TAs than before). * * \param client = the state of the entire client * \param packet = the packet we are building and where we * shall append the IA_TAs we create * \param lease = the current lease * \param message = the type of the packet * \param wanted = the number of IA_TAs to include in the packet * 0 means include all * \param added = the number of IA_TAs that were added to the packet * * \return ISC_R_SUCCESS - all is well continue, any other return * indicates an error (most likely memory issues) * and the packet should be tossed. */ static isc_result_t dhc6_add_ia_ta(struct client_state *client, struct data_string *packet, struct dhc6_lease *lease, u_int8_t message, int wanted, int *added) { struct data_string iads; struct data_string addrds; struct dhc6_addr *addr; struct dhc6_ia *ia; isc_result_t rval = ISC_R_SUCCESS; TIME t1, t2; int i; *added = 0; memset(&iads, 0, sizeof(iads)); memset(&addrds, 0, sizeof(addrds)); for (ia = lease->bindings, i = 0; ia != NULL && rval == ISC_R_SUCCESS && (wanted == 0 || i < wanted); ia = ia->next) { if (ia->ia_type != D6O_IA_TA) continue; /* Now that we know this is an TA bump the counter */ i++; if (!buffer_allocate(&iads.buffer, 4, MDL)) { log_error("Unable to allocate memory for IA_TA."); rval = ISC_R_NOMEMORY; break; } /* Copy the IAID into the packet buffer. */ memcpy(iads.buffer->data, ia->iaid, 4); iads.data = iads.buffer->data; iads.len = 4; log_debug("XMT: X-- IA_TA %s", print_hex_1(4, iads.buffer->data, 55)); for (addr = ia->addrs ; addr != NULL ; addr = addr->next) { /* * Do not confirm expired addresses, do not request * expired addresses (but we keep them around for * solicit). */ if (addr->flags & DHC6_ADDR_EXPIRED) continue; if (addr->address.len != 16) { log_error("Illegal IPv6 address length (%d), " "ignoring. (%s:%d)", addr->address.len, MDL); continue; } if (!buffer_allocate(&addrds.buffer, 24, MDL)) { log_error("Unable to allocate memory for " "IAADDR."); rval = ISC_R_NOMEMORY; break; } addrds.data = addrds.buffer->data; addrds.len = 24; /* Copy the address into the packet buffer. */ memcpy(addrds.buffer->data, addr->address.iabuf, 16); /* Copy in additional information as appropriate */ switch (message) { case DHCPV6_REQUEST: case DHCPV6_RENEW: case DHCPV6_REBIND: t1 = client->config->requested_lease; t2 = t1 + 300; putULong(addrds.buffer->data + 16, t1); putULong(addrds.buffer->data + 20, t2); log_debug("XMT: | | X-- IAADDR %s", piaddr(addr->address)); log_debug("XMT: | | | X-- Preferred " "lifetime +%u", (unsigned)t1); log_debug("XMT: | | | X-- Max lifetime +%u", (unsigned)t2); break; case DHCPV6_CONFIRM: /* * Set preferred and max life to zero, * per 17.1.3. */ memset(addrds.buffer->data + 16, 0, 8); log_debug("XMT: | X-- Confirm Address %s", piaddr(addr->address)); break; case DHCPV6_RELEASE: /* Preferred and max life are irrelevant */ memset(addrds.buffer->data + 16, 0, 8); log_debug("XMT: | X-- Release Address %s", piaddr(addr->address)); break; default: log_fatal("Impossible condition at %s:%d.", MDL); } append_option(&iads, &dhcpv6_universe, iaaddr_option, &addrds); data_string_forget(&addrds, MDL); } /* * It doesn't make sense to make a request without an * address. */ if (ia->addrs == NULL) { log_debug("!!!: V IA_TA has no IAADDRs - removed."); rval = ISC_R_FAILURE; } else if (rval == ISC_R_SUCCESS) { log_debug("XMT: V IA_TA appended."); append_option(packet, &dhcpv6_universe, ia_ta_option, &iads); } data_string_forget(&iads, MDL); } if (rval == ISC_R_SUCCESS) *added = i; return (rval); } /*! * * \brief Add IA_PD information from the lease to the packet * we are building. * * Walk through the lease and for each IA_PD in the lease * and for each address in the IA_PD append that information * onto the packet-so-far. If wanted is 0 include all IA_PDs * in the lease if wanted is non-zero include only that many * IA_PDs (this may occur if sommebody restarts a client with * arugments for a smaller number of PDs than before). * * \param client = the state of the entire client * \param packet = the packet we are building and where we * shall append the IA_PDs we create * \param lease = the current lease * \param message = the type of the packet * \param wanted = the number of IA_PDs to include in the packet * 0 means include all * \param added = the number of IA_PDs that were added to the packet * * \return ISC_R_SUCCESS - all is well continue, any other return * indicates an error (most likely memory issues) * and the packet should be tossed. */ static isc_result_t dhc6_add_ia_pd(struct client_state *client, struct data_string *packet, struct dhc6_lease *lease, u_int8_t message, int wanted, int *added) { struct data_string iads; struct data_string prefds; struct dhc6_addr *pref; struct dhc6_ia *ia; isc_result_t rval = ISC_R_SUCCESS; TIME t1, t2; int i; *added = 0; memset(&iads, 0, sizeof(iads)); memset(&prefds, 0, sizeof(prefds)); for (ia = lease->bindings, i = 0; ia != NULL && rval == ISC_R_SUCCESS && (wanted == 0 || i < wanted); ia = ia->next) { if (ia->ia_type != D6O_IA_PD) continue; /* Now that we know this is an PD bump the counter */ i++; if (!buffer_allocate(&iads.buffer, 12, MDL)) { log_error("Unable to allocate memory for IA_PD."); rval = ISC_R_NOMEMORY; break; } /* Copy the IAID into the packet buffer. */ memcpy(iads.buffer->data, ia->iaid, 4); iads.data = iads.buffer->data; iads.len = 12; switch (message) { case DHCPV6_REQUEST: case DHCPV6_RENEW: case DHCPV6_REBIND: t1 = client->config->requested_lease / 2; t2 = t1 + (t1 / 2); #if MAX_TIME > 0xffffffff if (t1 > 0xffffffff) t1 = 0xffffffff; if (t2 > 0xffffffff) t2 = 0xffffffff; #endif putULong(iads.buffer->data + 4, t1); putULong(iads.buffer->data + 8, t2); log_debug("XMT: X-- IA_PD %s", print_hex_1(4, iads.data, 59)); log_debug("XMT: | X-- Requested renew +%u", (unsigned) t1); log_debug("XMT: | X-- Requested rebind +%u", (unsigned) t2); break; case DHCPV6_RELEASE: /* Set t1 and t2 to zero; server will ignore them */ memset(iads.buffer->data + 4, 0, 8); log_debug("XMT: X-- IA_PD %s", print_hex_1(4, iads.buffer->data, 55)); break; default: log_fatal("Impossible condition at %s:%d.", MDL); } for (pref = ia->addrs ; pref != NULL ; pref = pref->next) { /* * Do not confirm expired prefixes, do not request * expired prefixes (but we keep them around for * solicit). */ if (pref->flags & DHC6_ADDR_EXPIRED) continue; if (pref->address.len != 16) { log_error("Illegal IPv6 prefix " "ignoring. (%s:%d)", MDL); continue; } if (pref->plen == 0) { log_info("Null IPv6 prefix, " "ignoring. (%s:%d)", MDL); } if (!buffer_allocate(&prefds.buffer, 25, MDL)) { log_error("Unable to allocate memory for " "IAPREFIX."); rval = ISC_R_NOMEMORY; break; } prefds.data = prefds.buffer->data; prefds.len = 25; /* Copy the prefix into the packet buffer. */ putUChar(prefds.buffer->data + 8, pref->plen); memcpy(prefds.buffer->data + 9, pref->address.iabuf, 16); /* Copy in additional information as appropriate */ switch (message) { case DHCPV6_REQUEST: case DHCPV6_RENEW: case DHCPV6_REBIND: t1 = client->config->requested_lease; t2 = t1 + 300; putULong(prefds.buffer->data, t1); putULong(prefds.buffer->data + 4, t2); log_debug("XMT: | | X-- IAPREFIX %s/%u", piaddr(pref->address), (unsigned) pref->plen); log_debug("XMT: | | | X-- Preferred " "lifetime +%u", (unsigned)t1); log_debug("XMT: | | | X-- Max lifetime +%u", (unsigned)t2); break; case DHCPV6_RELEASE: /* Preferred and max life are irrelevant */ memset(prefds.buffer->data, 0, 8); log_debug("XMT: | X-- Release Prefix %s/%u", piaddr(pref->address), (unsigned) pref->plen); break; default: log_fatal("Impossible condition at %s:%d.", MDL); } append_option(&iads, &dhcpv6_universe, iaprefix_option, &prefds); data_string_forget(&prefds, MDL); } /* * It doesn't make sense to make a request without an * address. */ if (ia->addrs == NULL) { log_debug("!!!: V IA_PD has no IAPREFIXs - removed."); rval = ISC_R_FAILURE; } else if (rval == ISC_R_SUCCESS) { log_debug("XMT: V IA_PD appended."); append_option(packet, &dhcpv6_universe, ia_pd_option, &iads); } data_string_forget(&iads, MDL); } if (rval == ISC_R_SUCCESS) *added = i; return (rval); } /* stopping_finished() checks if there is a remaining work to do. */ static isc_boolean_t stopping_finished(void) { struct interface_info *ip; struct client_state *client; for (ip = interfaces; ip; ip = ip -> next) { for (client = ip -> client; client; client = client -> next) { if (client->state != S_STOPPED) return ISC_FALSE; if (client->active_lease != NULL) return ISC_FALSE; } } return ISC_TRUE; } /* reply_handler() accepts a Reply while we're attempting Select or Renew or * Rebind. Basically any Reply packet. */ void reply_handler(struct packet *packet, struct client_state *client) { struct dhc6_lease *lease; isc_result_t check_status; if (packet->dhcpv6_msg_type != DHCPV6_REPLY) return; /* RFC3315 section 15.10 validation (same as 15.3 since we * always include a client id). */ if (!valid_reply(packet, client)) { log_error("Invalid Reply - rejecting."); return; } lease = dhc6_leaseify(packet, client); /* Out of memory or corrupt packet condition...hopefully a temporary * problem. Returning now makes us try to retransmit later. */ if (lease == NULL) return; check_status = dhc6_check_reply(client, lease); if (check_status != ISC_R_SUCCESS) { dhc6_lease_destroy(&lease, MDL); /* If no action was taken, but there is an error, then * we wait for a retransmission. */ if (check_status != ISC_R_CANCELED) return; } /* We're done retransmitting at this point. */ cancel_timeout(do_confirm6, client); cancel_timeout(do_select6, client); cancel_timeout(do_refresh6, client); cancel_timeout(do_release6, client); cancel_timeout(do_decline6, client); /* If this is in response to a Release, clean up and return. */ if (client->state == S_STOPPED) { if (client->active_lease != NULL) { dhc6_lease_destroy(&client->active_lease, MDL); client->active_lease = NULL; /* We should never wait for nothing!? */ if (stopping_finished()) { finish(0); } } return; } if (client->state == S_DECLINING) { /* Weed thru the lease and delete all declined addresses. * Toss the lease if there aren't any addresses left */ int live_cnt = drop_declined_addrs(client->active_lease); if (live_cnt == 0) { dhc6_lease_destroy(&client->active_lease, MDL); client->active_lease = NULL; } /* Solicit with any live addresses we have so far, and * add additional empty NA iasubopts for those we had * to decline. */ start_init6(client); return; } /* Action was taken, so now that we've torn down our scheduled * retransmissions, return. */ if (check_status == ISC_R_CANCELED) return; if (client->selected_lease != NULL) { dhc6_lease_destroy(&client->selected_lease, MDL); client->selected_lease = NULL; } /* If this is in response to a confirm, we use the lease we've * already got, not the reply we were sent. */ if (client->state == S_REBOOTING) { if (client->active_lease == NULL) log_fatal("Impossible condition at %s:%d.", MDL); dhc6_lease_destroy(&lease, MDL); start_bound(client); return; } /* Merge any bindings in the active lease (if there is one) into * the new active lease. */ dhc6_merge_lease(client->active_lease, lease); /* Cleanup if a previous attempt to go bound failed. */ if (client->old_lease != NULL) { dhc6_lease_destroy(&client->old_lease, MDL); client->old_lease = NULL; } /* Make this lease active and BIND to it. */ if (client->active_lease != NULL) client->old_lease = client->active_lease; client->active_lease = lease; /* We're done with the ADVERTISEd leases, if any. */ while(client->advertised_leases != NULL) { lease = client->advertised_leases; client->advertised_leases = lease->next; dhc6_lease_destroy(&lease, MDL); } start_bound(client); } /* DHCPv6 packets are a little sillier than they needed to be - the root * packet contains options, then IA's which contain options, then within * that IAADDR's which contain options. * * To sort this out at dhclient-script time (which fetches config parameters * in environment variables), start_bound() iterates over each IAADDR, and * calls this function to marshall an environment variable set that includes * the most-specific option values related to that IAADDR in particular. * * To achieve this, we load environment variables for the root options space, * then the IA, then the IAADDR. Any duplicate option names will be * over-written by the later versions. */ static void dhc6_marshall_values(const char *prefix, struct client_state *client, struct dhc6_lease *lease, struct dhc6_ia *ia, struct dhc6_addr *addr) { /* Option cache contents, in descending order of * scope. */ if ((lease != NULL) && (lease->options != NULL)) script_write_params6(client, prefix, lease->options); if ((ia != NULL) && (ia->options != NULL)) script_write_params6(client, prefix, ia->options); if ((addr != NULL) && (addr->options != NULL)) script_write_params6(client, prefix, addr->options); /* addr fields. */ if (addr != NULL) { if ((ia != NULL) && (ia->ia_type == D6O_IA_PD)) { client_envadd(client, prefix, "ip6_prefix", "%s/%u", piaddr(addr->address), (unsigned) addr->plen); } else { client_envadd(client, prefix, "ip6_prefixlen", "%d", address_prefix_len); client_envadd(client, prefix, "ip6_address", "%s", piaddr(addr->address)); } if ((ia != NULL) && (ia->ia_type == D6O_IA_TA)) { client_envadd(client, prefix, "ip6_type", "temporary"); } client_envadd(client, prefix, "life_starts", "%d", (int)(addr->starts)); client_envadd(client, prefix, "preferred_life", "%u", addr->preferred_life); client_envadd(client, prefix, "max_life", "%u", addr->max_life); } /* ia fields. */ if (ia != NULL) { client_envadd(client, prefix, "iaid", "%s", print_hex_1(4, ia->iaid, 12)); client_envadd(client, prefix, "starts", "%d", (int)(ia->starts)); client_envadd(client, prefix, "renew", "%u", ia->renew); client_envadd(client, prefix, "rebind", "%u", ia->rebind); } } /* Look at where the client's active lease is sitting. If it's looking to * time out on renew, rebind, depref, or expiration, do those things. */ static void dhc6_check_times(struct client_state *client) { struct dhc6_lease *lease; struct dhc6_ia *ia; struct dhc6_addr *addr; TIME renew=MAX_TIME, rebind=MAX_TIME, depref=MAX_TIME, lo_expire=MAX_TIME, hi_expire=0, max_ia_starts = 0, tmp; int has_addrs = ISC_FALSE; int has_preferred_addrs = ISC_FALSE; struct timeval tv; lease = client->active_lease; /* Bit spammy. We should probably keep record of scheduled * events instead. */ cancel_timeout(start_renew6, client); cancel_timeout(start_rebind6, client); cancel_timeout(do_depref, client); cancel_timeout(do_expire, client); for(ia = lease->bindings ; ia != NULL ; ia = ia->next) { TIME this_ia_lo_expire, this_ia_hi_expire, use_expire; this_ia_lo_expire = MAX_TIME; this_ia_hi_expire = 0; for (addr = ia->addrs ; addr != NULL ; addr = addr->next) { if(!(addr->flags & DHC6_ADDR_DEPREFFED)) { if (addr->preferred_life == 0xffffffff) tmp = MAX_TIME; else tmp = addr->starts + addr->preferred_life; if (tmp < depref) depref = tmp; if (!(addr->flags & DHC6_ADDR_EXPIRED)) { has_preferred_addrs = ISC_TRUE; } } if (!(addr->flags & DHC6_ADDR_EXPIRED)) { /* Find EPOCH-relative expiration. */ if (addr->max_life == 0xffffffff) tmp = MAX_TIME; else tmp = addr->starts + addr->max_life; /* Make the times ia->starts relative. */ tmp -= ia->starts; if (tmp > this_ia_hi_expire) this_ia_hi_expire = tmp; if (tmp < this_ia_lo_expire) this_ia_lo_expire = tmp; has_addrs = ISC_TRUE; } } /* These times are ia->starts relative. */ if (this_ia_lo_expire <= (this_ia_hi_expire / 2)) use_expire = this_ia_hi_expire; else use_expire = this_ia_lo_expire; /* * If the auto-selected expiration time is "infinite", or * zero, assert a reasonable default. */ if ((use_expire == MAX_TIME) || (use_expire <= 1)) use_expire = client->config->requested_lease / 2; else use_expire /= 2; /* Don't renew/rebind temporary addresses. */ /* For NA and PD we find the most recent IA and the smallest * values for the renew and rebind then base the timer on * the sum of the them. * Normally all the IAs will have the same time as they * are requested and served as a group but in some cases the * client isn't asking for all of the IAs (for example * restarted with a different set of arguments) or the server * isn't updating the client on all of them (probably a * broken server). */ if (ia->ia_type != D6O_IA_TA) { if (ia->starts > max_ia_starts) max_ia_starts = ia->starts; if (ia->renew == 0) { tmp = use_expire; } else if (ia->renew == 0xffffffff) tmp = MAX_TIME; else tmp = ia->renew; if (tmp < renew) renew = tmp; if (ia->rebind == 0) { /* Set rebind to 3/4 expiration interval. */ tmp = use_expire + (use_expire / 2); } else if (ia->rebind == 0xffffffff) tmp = MAX_TIME; else tmp = ia->rebind; if (tmp < rebind) rebind = tmp; } /* * Return expiration ranges to EPOCH relative for event * scheduling (add_timeout()). */ this_ia_hi_expire += ia->starts; this_ia_lo_expire += ia->starts; if (this_ia_hi_expire > hi_expire) hi_expire = this_ia_hi_expire; if (this_ia_lo_expire < lo_expire) lo_expire = this_ia_lo_expire; } /* If there are no addresses, give up, go to INIT. * Note that if an address is unexpired with a date in the past, * we're scheduling an expiration event to ocurr in the past. We * could probably optimize this to expire now (but then there's * recursion). * * In the future, we may decide that we're done here, or to * schedule a future request (using 4-pkt info-request model). */ if (has_addrs == ISC_FALSE) { dhc6_lease_destroy(&client->active_lease, MDL); client->active_lease = NULL; /* Go back to the beginning. */ start_init6(client); return; } /* Second part of calculating the renew and rebind times. * We have the start time and the desired periods for renew * and rebind, just add them to get the desired end time. */ if (renew != MAX_TIME) renew += max_ia_starts; if (rebind != MAX_TIME) rebind += max_ia_starts; switch(client->state) { case S_BOUND: /* We'd like to hit renewing, but if rebinding has already * passed (time warp), head straight there. */ if ((rebind > cur_time) && (renew < rebind)) { log_debug("PRC: Renewal event scheduled in %d seconds, " "to run for %u seconds.", (int)(renew - cur_time), (unsigned)(rebind - renew)); client->next_MRD = rebind; tv.tv_sec = renew; tv.tv_usec = 0; add_timeout(&tv, start_renew6, client, NULL, NULL); break; } /* FALL THROUGH */ case S_RENEWING: /* While actively renewing, MRD is bounded by the time * we stop renewing and start rebinding. This helps us * process the state change on time. */ client->MRD = rebind - cur_time; if (rebind != MAX_TIME) { log_debug("PRC: Rebind event scheduled in %d seconds, " "to run for %d seconds.", (int)(rebind - cur_time), (int)(hi_expire - rebind)); client->next_MRD = hi_expire; tv.tv_sec = rebind; tv.tv_usec = 0; add_timeout(&tv, start_rebind6, client, NULL, NULL); } break; case S_REBINDING: /* For now, we rebind up until the last lease expires. In * the future, we might want to start SOLICITing when we've * depreffed an address. */ client->MRD = hi_expire - cur_time; break; default: if (has_preferred_addrs) { log_fatal("Impossible condition, state %d at %s:%d.", client->state, MDL); } } /* Separately, set a time at which we will depref and expire * leases. This might happen with multiple addresses while we * keep trying to refresh. */ if (depref != MAX_TIME) { log_debug("PRC: Depreference scheduled in %d seconds.", (int)(depref - cur_time)); tv.tv_sec = depref; tv.tv_usec = 0; add_timeout(&tv, do_depref, client, NULL, NULL); } if (lo_expire != MAX_TIME) { log_debug("PRC: Expiration scheduled in %d seconds.", (int)(lo_expire - cur_time)); tv.tv_sec = lo_expire; tv.tv_usec = 0; add_timeout(&tv, do_expire, client, NULL, NULL); } } /* In a given IA chain, find the IA with the same type and 'iaid'. */ static struct dhc6_ia * find_ia(struct dhc6_ia *head, u_int16_t type, const char *id) { struct dhc6_ia *ia; for (ia = head ; ia != NULL ; ia = ia->next) { if (ia->ia_type != type) continue; if (memcmp(ia->iaid, id, 4) == 0) return ia; } return NULL; } /* In a given address chain, find a matching address. */ static struct dhc6_addr * find_addr(struct dhc6_addr *head, struct iaddr *address) { struct dhc6_addr *addr; for (addr = head ; addr != NULL ; addr = addr->next) { if ((addr->address.len == address->len) && (memcmp(addr->address.iabuf, address->iabuf, address->len) == 0)) return addr; } return NULL; } /* In a given prefix chain, find a matching prefix. */ static struct dhc6_addr * find_pref(struct dhc6_addr *head, struct iaddr *prefix, u_int8_t plen) { struct dhc6_addr *pref; for (pref = head ; pref != NULL ; pref = pref->next) { if ((pref->address.len == prefix->len) && (pref->plen == plen) && (memcmp(pref->address.iabuf, prefix->iabuf, prefix->len) == 0)) return pref; } return NULL; } /* * * \brief Merge the bindings from the source lease into the destination * lease structure, where they are missing. * * This is used to merge any extra information we have in the current * (older, src) lease into the lease we have just received. For example * the src lease might include a binding for an NA that is still usable * but that we didn't request or that the server is no longer serving. * We want to keep that information until we toss the binding (expire, * release) so we move it to the new lease. * * We have to copy the stateful objects rather than move them over, * because later code needs to be able to compare new versus old if * they contain any bindings. * * \param src The older lease to copy the objects from * \param dst The newer lease to copy the objects to */ static void dhc6_merge_lease(struct dhc6_lease *src, struct dhc6_lease *dst) { struct dhc6_ia *sia, *dia, *tia, **eia; struct dhc6_addr *saddr, *daddr, *taddr; int changes = 0; if ((dst == NULL) || (src == NULL)) return; for (sia = src->bindings ; sia != NULL ; sia = sia->next) { dia = find_ia(dst->bindings, sia->ia_type, (char *)sia->iaid); if (dia == NULL) { tia = dhc6_dup_ia(sia, MDL); if (tia == NULL) log_fatal("Out of memory merging lease - " "Unable to continue without losing " "state! (%s:%d)", MDL); /* Put any bindings that aren't in the new lease at the * end of the list. If the user or server reduces the * number of IAs the ones in use will be at the front * and will be used when building the next requests * We could be more efficient by finding the end * of the list once but we don't expect to do this * often. */ for (eia = &dst->bindings; *eia != NULL; eia = &(*eia)->next) { ; /* no work just find the end */ } *eia = tia; changes = 1; } else { for (saddr = sia->addrs ; saddr != NULL ; saddr = saddr->next) { if (sia->ia_type != D6O_IA_PD) daddr = find_addr(dia->addrs, &saddr->address); else daddr = find_pref(dia->addrs, &saddr->address, saddr->plen); if (daddr == NULL) { taddr = dhc6_dup_addr(saddr, MDL); if (taddr == NULL) log_fatal("Out of memory " "merging lease - " "Unable to continue " "without losing " "state! (%s:%d)", MDL); /* XXX: consider sorting? */ taddr->next = dia->addrs; dia->addrs = taddr; changes = 1; } } } } /* If we made changes, reset the score to 0 so it is recalculated. */ if (changes) dst->score = 0; } /* We've either finished selecting or succeeded in Renew or Rebinding our * lease. In all cases we got a Reply. Give dhclient-script a tickle * to inform it about the new values, and then lay in wait for the next * event. */ static void start_bound(struct client_state *client) { struct dhc6_ia *ia, *oldia; struct dhc6_addr *addr, *oldaddr; struct dhc6_lease *lease, *old; const char *reason; int decline_cnt = 0; #if defined (NSUPDATE) TIME dns_update_offset = 1; #endif lease = client->active_lease; if (lease == NULL) { log_error("Cannot enter bound state unless an active lease " "is selected."); return; } lease->released = ISC_FALSE; old = client->old_lease; client->v6_handler = bound_handler; switch (client->state) { case S_SELECTING: case S_REBOOTING: /* Pretend we got bound. */ reason = "BOUND6"; break; case S_RENEWING: reason = "RENEW6"; break; case S_REBINDING: reason = "REBIND6"; break; default: log_fatal("Impossible condition at %s:%d.", MDL); /* Silence compiler warnings. */ return; } log_debug("PRC: Bound to lease %s.", print_hex_1(client->active_lease->server_id.len, client->active_lease->server_id.data, 55)); client->state = S_BOUND; write_client6_lease(client, lease, 0, 1); oldia = NULL; for (ia = lease->bindings ; ia != NULL ; ia = ia->next) { if (old != NULL) oldia = find_ia(old->bindings, ia->ia_type, (char *)ia->iaid); else oldia = NULL; for (addr = ia->addrs ; addr != NULL ; addr = addr->next) { /* Don't try to use the address if it's already expired */ if (addr->flags & DHC6_ADDR_EXPIRED) continue; if (oldia != NULL) { if (ia->ia_type != D6O_IA_PD) oldaddr = find_addr(oldia->addrs, &addr->address); else oldaddr = find_pref(oldia->addrs, &addr->address, addr->plen); } else oldaddr = NULL; #if defined (NSUPDATE) if ((oldaddr == NULL) && (ia->ia_type == D6O_IA_NA)) dhclient_schedule_updates(client, &addr->address, dns_update_offset++); #endif /* Shell out to setup the new binding. */ script_init(client, reason, NULL); if (old != NULL) dhc6_marshall_values("old_", client, old, oldia, oldaddr); dhc6_marshall_values("new_", client, lease, ia, addr); script_write_requested6(client); /* When script returns 3, DAD failed */ if (script_go(client) == 3) { if (ia->ia_type == D6O_IA_NA) { addr->flags |= DHC6_ADDR_DECLINED; log_debug ("Flag address declined:%s", piaddr(addr->address)); decline_cnt++; } } } /* If the client script DAD failed any addresses we need * build and issue a DECLINE */ if (decline_cnt) { start_decline6(client); return; } /* XXX: maybe we should loop on the old values instead? */ if (ia->addrs == NULL) { script_init(client, reason, NULL); if (old != NULL) dhc6_marshall_values("old_", client, old, oldia, oldia != NULL ? oldia->addrs : NULL); dhc6_marshall_values("new_", client, lease, ia, NULL); script_write_requested6(client); script_go(client); } } /* XXX: maybe we should loop on the old values instead? */ if (lease->bindings == NULL) { script_init(client, reason, NULL); if (old != NULL) dhc6_marshall_values("old_", client, old, old->bindings, (old->bindings != NULL) ? old->bindings->addrs : NULL); dhc6_marshall_values("new_", client, lease, NULL, NULL); script_write_requested6(client); script_go(client); } #ifdef DHCP4o6 if (dhcpv4_over_dhcpv6) dhcp4o6_start(); #endif detach(); if (client->old_lease != NULL) { dhc6_lease_destroy(&client->old_lease, MDL); client->old_lease = NULL; } /* Schedule events. */ dhc6_check_times(client); } /* While bound, ignore packets. In the future we'll want to answer * Reconfigure-Request messages and the like. */ void bound_handler(struct packet *packet, struct client_state *client) { log_debug("RCV: Input packets are ignored once bound."); } /* * start_decline6() kicks off the decline process, transmitting * an decline packet and scheduling a retransmission event. */ void start_decline6(struct client_state *client) { /* Cancel any pending transmissions */ cancel_timeout(do_confirm6, client); cancel_timeout(do_select6, client); cancel_timeout(do_refresh6, client); cancel_timeout(do_release6, client); cancel_timeout(do_decline6, client); client->state = S_DECLINING; if (client->active_lease == NULL) return; /* Set timers per RFC3315 section 18.1.7. */ client->IRT = DEC_TIMEOUT * 100; client->MRT = 0; client->MRC = DEC_MAX_RC; client->MRD = 0; dhc6_retrans_init(client); client->v6_handler = reply_handler; client->refresh_type = DHCPV6_DECLINE; do_decline6(client); } /* * do_decline6() creates a Decline packet and transmits it. * The decline will contain an IA_NA with iasubopt(s) for * each IA_NA containing declined address(es) in the active * lease. */ static void do_decline6(void *input) { struct client_state *client; struct data_string ds; int send_ret; struct timeval elapsed, tv; client = input; if (client == NULL || client->active_lease == NULL) { return; } if ((client->MRC != 0) && (client->txcount > client->MRC)) { log_info("Max retransmission count exceeded."); goto decline_done; } /* * Start_time starts at the first transmission. */ if (client->txcount == 0) { client->start_time.tv_sec = cur_tv.tv_sec; client->start_time.tv_usec = cur_tv.tv_usec; } /* elapsed = cur - start */ elapsed.tv_sec = cur_tv.tv_sec - client->start_time.tv_sec; elapsed.tv_usec = cur_tv.tv_usec - client->start_time.tv_usec; if (elapsed.tv_usec < 0) { elapsed.tv_sec -= 1; elapsed.tv_usec += 1000000; } memset(&ds, 0, sizeof(ds)); if (!buffer_allocate(&ds.buffer, 4, MDL)) { log_error("Unable to allocate memory for Decline."); goto decline_done; } ds.data = ds.buffer->data; ds.len = 4; ds.buffer->data[0] = DHCPV6_DECLINE; memcpy(ds.buffer->data + 1, client->dhcpv6_transaction_id, 3); /* Form an elapsed option. */ /* Maximum value is 65535 1/100s coded as 0xffff. */ if ((elapsed.tv_sec < 0) || (elapsed.tv_sec > 655) || ((elapsed.tv_sec == 655) && (elapsed.tv_usec > 350000))) { client->elapsed = 0xffff; } else { client->elapsed = elapsed.tv_sec * 100; client->elapsed += elapsed.tv_usec / 10000; } client->elapsed = htons(client->elapsed); log_debug("XMT: Forming Decline."); make_client6_options(client, &client->sent_options, client->active_lease, DHCPV6_DECLINE); dhcpv6_universe.encapsulate(&ds, NULL, NULL, client, NULL, client->sent_options, &global_scope, &dhcpv6_universe); /* Append IA_NA's. */ if (dhc6_add_ia_na_decline(client, &ds, client->active_lease) != ISC_R_SUCCESS) { data_string_forget(&ds, MDL); goto decline_done; } /* Transmit and wait. */ log_info("XMT: Decline on %s, interval %ld0ms.", client->name ? client->name : client->interface->name, (long int)client->RT); send_ret = send_packet6(client->interface, ds.data, ds.len, &DHCPv6DestAddr); if (send_ret != ds.len) { log_error("dhc6: sendpacket6() sent %d of %d bytes", send_ret, ds.len); } data_string_forget(&ds, MDL); /* Wait RT */ tv.tv_sec = cur_tv.tv_sec + client->RT / 100; tv.tv_usec = cur_tv.tv_usec + (client->RT % 100) * 10000; if (tv.tv_usec >= 1000000) { tv.tv_sec += 1; tv.tv_usec -= 1000000; } add_timeout(&tv, do_decline6, client, NULL, NULL); dhc6_retrans_advance(client); return; decline_done: /* We here because we've exhausted our retry limits or * something else has gone wrong with the decline process. * So let's just toss the existing lease and start over. */ dhc6_lease_destroy(&client->active_lease, MDL); client->active_lease = NULL; start_init6(client); return; } /* start_renew6() gets us all ready to go to start transmitting Renew packets. * Note that client->next_MRD must be set before entering this function - * it must be set to the time at which the client should start Rebinding. */ void start_renew6(void *input) { struct client_state *client; client = (struct client_state *)input; log_info("PRC: Renewing lease on %s.", client->name ? client->name : client->interface->name); client->state = S_RENEWING; client->v6_handler = reply_handler; /* Times per RFC3315 section 18.1.3. */ client->IRT = REN_TIMEOUT * 100; client->MRT = REN_MAX_RT * 100; client->MRC = 0; /* MRD is special in renew - we need to set it by checking timer * state. */ client->MRD = client->next_MRD - cur_time; dhc6_retrans_init(client); client->refresh_type = DHCPV6_RENEW; do_refresh6(client); } /* do_refresh6() transmits one DHCPv6 packet, be it a Renew or Rebind, and * gives the retransmission state a bump for the next time. Note that * client->refresh_type must be set before entering this function. */ void do_refresh6(void *input) { struct option_cache *oc; struct sockaddr_in6 unicast, *dest_addr = &DHCPv6DestAddr; struct data_string ds; struct client_state *client; struct dhc6_lease *lease; struct timeval elapsed, tv; int send_ret, added; client = (struct client_state *)input; memset(&ds, 0, sizeof(ds)); lease = client->active_lease; if (lease == NULL) { log_error("Cannot renew without an active binding."); return; } /* Ensure we're emitting a valid message type. */ switch (client->refresh_type) { case DHCPV6_RENEW: case DHCPV6_REBIND: break; default: log_fatal("Internal inconsistency (%d) at %s:%d.", client->refresh_type, MDL); } /* * Start_time starts at the first transmission. */ if (client->txcount == 0) { client->start_time.tv_sec = cur_tv.tv_sec; client->start_time.tv_usec = cur_tv.tv_usec; } /* elapsed = cur - start */ elapsed.tv_sec = cur_tv.tv_sec - client->start_time.tv_sec; elapsed.tv_usec = cur_tv.tv_usec - client->start_time.tv_usec; if (elapsed.tv_usec < 0) { elapsed.tv_sec -= 1; elapsed.tv_usec += 1000000; } if (((client->MRC != 0) && (client->txcount > client->MRC)) || ((client->MRD != 0) && (elapsed.tv_sec >= client->MRD))) { /* We're done. Move on to the next phase, if any. */ dhc6_check_times(client); return; } /* * Check whether the server has sent a unicast option; if so, we can * use the address it specified for RENEWs. */ oc = lookup_option(&dhcpv6_universe, lease->options, D6O_UNICAST); if (oc && evaluate_option_cache(&ds, NULL, NULL, NULL, lease->options, NULL, &global_scope, oc, MDL)) { if (ds.len < 16) { log_error("Invalid unicast option length %d.", ds.len); } else { memset(&unicast, 0, sizeof(DHCPv6DestAddr)); unicast.sin6_family = AF_INET6; unicast.sin6_port = remote_port; memcpy(&unicast.sin6_addr, ds.data, 16); if (client->refresh_type == DHCPV6_RENEW) { dest_addr = &unicast; } } data_string_forget(&ds, MDL); } /* Commence forming a renew packet. */ memset(&ds, 0, sizeof(ds)); if (!buffer_allocate(&ds.buffer, 4, MDL)) { log_error("Unable to allocate memory for packet."); return; } ds.data = ds.buffer->data; ds.len = 4; ds.buffer->data[0] = client->refresh_type; memcpy(ds.buffer->data + 1, client->dhcpv6_transaction_id, 3); /* Form an elapsed option. */ /* Maximum value is 65535 1/100s coded as 0xffff. */ if ((elapsed.tv_sec < 0) || (elapsed.tv_sec > 655) || ((elapsed.tv_sec == 655) && (elapsed.tv_usec > 350000))) { client->elapsed = 0xffff; } else { client->elapsed = elapsed.tv_sec * 100; client->elapsed += elapsed.tv_usec / 10000; } if (client->elapsed == 0) log_debug("XMT: Forming %s, 0 ms elapsed.", dhcpv6_type_names[client->refresh_type]); else log_debug("XMT: Forming %s, %u0 ms elapsed.", dhcpv6_type_names[client->refresh_type], (unsigned)client->elapsed); client->elapsed = htons(client->elapsed); make_client6_options(client, &client->sent_options, lease, client->refresh_type); /* Put in any options from the sent cache. */ dhcpv6_universe.encapsulate(&ds, NULL, NULL, client, NULL, client->sent_options, &global_scope, &dhcpv6_universe); /* Now append any IA's, and within them any IAADDR/IAPREFIXs. * For each type of IA (na, ta, pd) we start with the ones for * which we already have addresses (dhc6_add_ia_xx) and then * if we still want more we add aditional IAs (dhc6_bare_ia_xx) */ if (wanted_ia_na && ((dhc6_add_ia_na(client, &ds, lease, client->refresh_type, wanted_ia_na, &added) != ISC_R_SUCCESS) || (dhc6_bare_ia_xx(client, &ds, wanted_ia_na - added, D6O_IA_NA) != ISC_R_SUCCESS))) { data_string_forget(&ds, MDL); return; } if (wanted_ia_pd && ((dhc6_add_ia_pd(client, &ds, lease, client->refresh_type, wanted_ia_pd, &added) != ISC_R_SUCCESS) || (dhc6_bare_ia_xx(client, &ds, wanted_ia_pd - added, D6O_IA_PD) != ISC_R_SUCCESS))) { data_string_forget(&ds, MDL); return; } log_info("XMT: %s on %s, interval %ld0ms.", dhcpv6_type_names[client->refresh_type], client->name ? client->name : client->interface->name, (long int)client->RT); send_ret = send_packet6(client->interface, ds.data, ds.len, dest_addr); if (send_ret != ds.len) { log_error("dhc6: send_packet6() sent %d of %d bytes", send_ret, ds.len); } data_string_forget(&ds, MDL); /* Wait RT */ tv.tv_sec = cur_tv.tv_sec + client->RT / 100; tv.tv_usec = cur_tv.tv_usec + (client->RT % 100) * 10000; if (tv.tv_usec >= 1000000) { tv.tv_sec += 1; tv.tv_usec -= 1000000; } add_timeout(&tv, do_refresh6, client, NULL, NULL); dhc6_retrans_advance(client); } /* start_rebind6() gets us all set up to go and rebind a lease. Note that * client->next_MRD must be set before entering this function. In this case, * MRD must be set to the maximum time any address in the packet will * expire. */ void start_rebind6(void *input) { struct client_state *client; client = (struct client_state *)input; log_info("PRC: Rebinding lease on %s.", client->name ? client->name : client->interface->name); client->state = S_REBINDING; client->v6_handler = reply_handler; /* Times per RFC3315 section 18.1.4. */ client->IRT = REB_TIMEOUT * 100; client->MRT = REB_MAX_RT * 100; client->MRC = 0; /* MRD is special in rebind - it's determined by the timer * state. */ client->MRD = client->next_MRD - cur_time; dhc6_retrans_init(client); client->refresh_type = DHCPV6_REBIND; do_refresh6(client); } /* do_depref() runs through a given lease's addresses, for each that has * not yet been depreffed, shells out to the dhclient-script to inform it * of the status change. The dhclient-script should then do...something... * to encourage applications to move off the address and onto one of the * remaining 'preferred' addresses. */ void do_depref(void *input) { struct client_state *client; struct dhc6_lease *lease; struct dhc6_ia *ia; struct dhc6_addr *addr; client = (struct client_state *)input; lease = client->active_lease; if (lease == NULL) return; for (ia = lease->bindings ; ia != NULL ; ia = ia->next) { for (addr = ia->addrs ; addr != NULL ; addr = addr->next) { if (addr->flags & DHC6_ADDR_DEPREFFED) continue; if (addr->starts + addr->preferred_life <= cur_time) { script_init(client, "DEPREF6", NULL); dhc6_marshall_values("cur_", client, lease, ia, addr); script_write_requested6(client); script_go(client); addr->flags |= DHC6_ADDR_DEPREFFED; if (ia->ia_type != D6O_IA_PD) log_info("PRC: Address %s depreferred.", piaddr(addr->address)); else log_info("PRC: Prefix %s/%u depreferred.", piaddr(addr->address), (unsigned) addr->plen); #if defined (NSUPDATE) /* Remove DDNS bindings at depref time. */ if ((ia->ia_type == D6O_IA_NA) && client->config->do_forward_update) client_dns_remove(client, &addr->address); #endif } } } dhc6_check_times(client); } /* do_expire() searches through all the addresses on a given lease, and * expires/removes any addresses that are no longer valid. */ void do_expire(void *input) { struct client_state *client; struct dhc6_lease *lease; struct dhc6_ia *ia, **tia; struct dhc6_addr *addr; int has_addrs = ISC_FALSE; int ia_has_addrs = ISC_FALSE; client = (struct client_state *)input; lease = client->active_lease; if (lease == NULL) return; for (ia = lease->bindings, tia = &lease->bindings; ia != NULL ; ) { ia_has_addrs = ISC_FALSE; for (addr = ia->addrs ; addr != NULL ; addr = addr->next) { if (addr->flags & DHC6_ADDR_EXPIRED) continue; if (addr->starts + addr->max_life <= cur_time) { script_init(client, "EXPIRE6", NULL); dhc6_marshall_values("old_", client, lease, ia, addr); script_write_requested6(client); script_go(client); addr->flags |= DHC6_ADDR_EXPIRED; if (ia->ia_type != D6O_IA_PD) log_info("PRC: Address %s expired.", piaddr(addr->address)); else log_info("PRC: Prefix %s/%u expired.", piaddr(addr->address), (unsigned) addr->plen); #if defined (NSUPDATE) /* We remove DNS records at depref time, but * it is possible that we might get here * without depreffing. */ if ((ia->ia_type == D6O_IA_NA) && client->config->do_forward_update && !(addr->flags & DHC6_ADDR_DEPREFFED)) client_dns_remove(client, &addr->address); #endif continue; } ia_has_addrs = ISC_TRUE; has_addrs = ISC_TRUE; } /* Update to the next ia and git rid of this ia * if it doesn't have any leases. */ if (ia_has_addrs == ISC_TRUE) { /* leases, just advance the list pointer */ tia = &(*tia)->next; } else { /* no leases, update the list pointer * and free the ia */ *tia = ia->next; dhc6_ia_destroy(&ia, MDL); } /* lastly update the ia pointer to our new ia */ ia = *tia; } /* Clean up empty leases. */ if (has_addrs == ISC_FALSE) { log_info("PRC: Bound lease is devoid of active addresses." " Re-initializing."); dhc6_lease_destroy(&lease, MDL); client->active_lease = NULL; start_init6(client); return; } /* Schedule the next run through. */ dhc6_check_times(client); } /* * Run client script to unconfigure interface. * Called with reason STOP6 when dhclient -x is run, or with reason * RELEASE6 when server has replied to a Release message. * Stateless is a special case. */ void unconfigure6(struct client_state *client, const char *reason) { struct dhc6_ia *ia; struct dhc6_addr *addr; if (stateless) { script_init(client, reason, NULL); if (client->active_lease != NULL) script_write_params6(client, "old_", client->active_lease->options); script_write_requested6(client); script_go(client); return; } if (client->active_lease == NULL) return; for (ia = client->active_lease->bindings ; ia != NULL ; ia = ia->next) { if (ia->ia_type == D6O_IA_TA) continue; for (addr = ia->addrs ; addr != NULL ; addr = addr->next) { script_init(client, reason, NULL); dhc6_marshall_values("old_", client, client->active_lease, ia, addr); script_write_requested6(client); script_go(client); #if defined (NSUPDATE) if ((ia->ia_type == D6O_IA_NA) && client->config->do_forward_update) client_dns_remove(client, &addr->address); #endif } } } void refresh_info_request6(void *input) { struct client_state *client; client = (struct client_state *)input; start_info_request6(client); } /* Timeout for Information-Request (using the IRT option). */ static void dhc6_check_irt(struct client_state *client) { struct option **req; struct option_cache *oc; TIME expire = MAX_TIME; struct timeval tv; int i; isc_boolean_t found = ISC_FALSE; cancel_timeout(refresh_info_request6, client); req = client->config->requested_options; for (i = 0; req[i] != NULL; i++) { if (req[i] == irt_option) { found = ISC_TRUE; break; } } /* Simply return gives a endless loop waiting for nothing. */ if (!found) { #ifdef DHCP4o6 if (!dhcpv4_over_dhcpv6) #endif finish(0); } oc = lookup_option(&dhcpv6_universe, client->active_lease->options, D6O_INFORMATION_REFRESH_TIME); if (oc != NULL) { struct data_string irt; memset(&irt, 0, sizeof(irt)); if (!evaluate_option_cache(&irt, NULL, NULL, client, client->active_lease->options, NULL, &global_scope, oc, MDL) || (irt.len < 4)) { log_error("Can't evaluate IRT."); } else { expire = getULong(irt.data); if (expire < IRT_MINIMUM) expire = IRT_MINIMUM; if (expire == 0xffffffff) expire = MAX_TIME; } data_string_forget(&irt, MDL); } else expire = IRT_DEFAULT; if (expire != MAX_TIME) { log_debug("PRC: Refresh event scheduled in %u seconds.", (unsigned) expire); tv.tv_sec = cur_time + expire; tv.tv_usec = 0; add_timeout(&tv, refresh_info_request6, client, NULL, NULL); } } /* We got a Reply. Give dhclient-script a tickle to inform it about * the new values, and then lay in wait for the next event. */ static void start_informed(struct client_state *client) { client->v6_handler = informed_handler; log_debug("PRC: Done."); client->state = S_BOUND; script_init(client, "RENEW6", NULL); if (client->old_lease != NULL) script_write_params6(client, "old_", client->old_lease->options); script_write_params6(client, "new_", client->active_lease->options); script_write_requested6(client); script_go(client); #ifdef DHCP4o6 if (dhcpv4_over_dhcpv6) dhcp4o6_start(); #endif detach(); if (client->old_lease != NULL) { dhc6_lease_destroy(&client->old_lease, MDL); client->old_lease = NULL; } /* Schedule events. */ dhc6_check_irt(client); } /* While informed, ignore packets. */ void informed_handler(struct packet *packet, struct client_state *client) { log_debug("RCV: Input packets are ignored once bound."); } /* make_client6_options() fetches option caches relevant to the client's * scope and places them into the sent_options cache. This cache is later * used to populate DHCPv6 output packets with options. */ static void make_client6_options(struct client_state *client, struct option_state **op, struct dhc6_lease *lease, u_int8_t message) { struct option_cache *oc; struct option **req; struct buffer *buffer; int buflen, i, oro_len; if ((op == NULL) || (client == NULL)) return; if (*op) option_state_dereference(op, MDL); /* Create a cache to carry options to transmission. */ option_state_allocate(op, MDL); /* Create and store an 'elapsed time' option in the cache. */ oc = NULL; if (option_cache_allocate(&oc, MDL)) { const unsigned char *cdata; cdata = (unsigned char *)&client->elapsed; if (make_const_data(&oc->expression, cdata, 2, 0, 0, MDL)) { option_reference(&oc->option, elapsed_option, MDL); save_option(&dhcpv6_universe, *op, oc); } option_cache_dereference(&oc, MDL); } /* Bring in any configured options to send. */ if (client->config->on_transmission) execute_statements_in_scope(NULL, NULL, NULL, client, lease ? lease->options : NULL, *op, &global_scope, client->config->on_transmission, NULL, NULL); /* Rapid-commit is only for SOLICITs. */ if (message != DHCPV6_SOLICIT) delete_option(&dhcpv6_universe, *op, D6O_RAPID_COMMIT); /* See if the user configured a DUID in a relevant scope. If not, * introduce our default manufactured id. */ if ((oc = lookup_option(&dhcpv6_universe, *op, D6O_CLIENTID)) == NULL) { if (!option_cache(&oc, &default_duid, NULL, clientid_option, MDL)) log_fatal("Failure assembling a DUID."); save_option(&dhcpv6_universe, *op, oc); option_cache_dereference(&oc, MDL); } /* In cases where we're responding to a single server, put the * server's id in the response. * * Note that lease is NULL for SOLICIT or INFO request messages, * and otherwise MUST be present. */ if (lease == NULL) { if ((message != DHCPV6_SOLICIT) && (message != DHCPV6_INFORMATION_REQUEST)) log_fatal("Impossible condition at %s:%d.", MDL); } else if ((message != DHCPV6_REBIND) && (message != DHCPV6_CONFIRM)) { oc = lookup_option(&dhcpv6_universe, lease->options, D6O_SERVERID); if (oc != NULL) save_option(&dhcpv6_universe, *op, oc); } /* 'send dhcp6.oro foo;' syntax we used in 4.0.0a1/a2 has been * deprecated by adjustments to the 'request' syntax also used for * DHCPv4. */ if (lookup_option(&dhcpv6_universe, *op, D6O_ORO) != NULL) log_error("'send dhcp6.oro' syntax is deprecated, please " "use the 'request' syntax (\"man dhclient.conf\")."); /* Construct and store an ORO (Option Request Option). It is a * fatal error to fail to send an ORO (of at least zero length). * * Discussion: RFC3315 appears to be inconsistent in its statements * of whether or not the ORO is mandatory. In section 18.1.1 * ("Creation and Transmission of Request Messages"): * * The client MUST include an Option Request option (see section * 22.7) to indicate the options the client is interested in * receiving. The client MAY include options with data values as * hints to the server about parameter values the client would like * to have returned. * * This MUST is missing from the creation/transmission of other * messages (such as Renew and Rebind), and the section 22.7 ("Option * Request Option" format and definition): * * A client MAY include an Option Request option in a Solicit, * Request, Renew, Rebind, Confirm or Information-request message to * inform the server about options the client wants the server to * send to the client. A server MAY include an Option Request * option in a Reconfigure option to indicate which options the * client should request from the server. * * seems to relax the requirement from MUST to MAY (and still other * language in RFC3315 supports this). * * In lieu of a clarification of RFC3315, we will conform with the * MUST. Instead of an absent ORO, we will if there are no options * to request supply an empty ORO. Theoretically, an absent ORO is * difficult to interpret (does the client want all options or no * options?). A zero-length ORO is intuitively clear: requesting * nothing. */ buffer = NULL; oro_len = 0; buflen = 32; if (!buffer_allocate(&buffer, buflen, MDL)) log_fatal("Out of memory constructing DHCPv6 ORO."); req = client->config->requested_options; if (req != NULL) { for (i = 0 ; req[i] != NULL ; i++) { if (buflen == oro_len) { struct buffer *tmpbuf = NULL; buflen += 32; /* Shell game. */ buffer_reference(&tmpbuf, buffer, MDL); buffer_dereference(&buffer, MDL); if (!buffer_allocate(&buffer, buflen, MDL)) log_fatal("Out of memory resizing " "DHCPv6 ORO buffer."); memcpy(buffer->data, tmpbuf->data, oro_len); buffer_dereference(&tmpbuf, MDL); } if (req[i]->universe == &dhcpv6_universe) { /* Append the code to the ORO. */ putUShort(buffer->data + oro_len, req[i]->code); oro_len += 2; } } } oc = NULL; if (make_const_option_cache(&oc, &buffer, NULL, oro_len, oro_option, MDL)) { save_option(&dhcpv6_universe, *op, oc); } else { log_fatal("Unable to create ORO option cache."); } /* * Note: make_const_option_cache() consumes the buffer, we do not * need to dereference it (XXX). */ option_cache_dereference(&oc, MDL); } /* A clone of the DHCPv4 script_write_params() minus the DHCPv4-specific * filename, server-name, etc specifics. * * Simply, store all values present in all universes of the option state * (probably derived from a DHCPv6 packet) into environment variables * named after the option names (and universe names) but with the 'prefix' * prepended. * * Later, dhclient-script may compare for example "new_time_servers" and * "old_time_servers" for differences, and only upon detecting a change * bother to rewrite ntp.conf and restart it. Or something along those * generic lines. */ static void script_write_params6(struct client_state *client, const char *prefix, struct option_state *options) { struct envadd_state es; int i; if (options == NULL) return; es.client = client; es.prefix = prefix; for (i = 0 ; i < options->universe_count ; i++) { option_space_foreach(NULL, NULL, client, NULL, options, &global_scope, universes[i], &es, client_option_envadd); } } /* * A clone of the DHCPv4 routine. * Write out the environment variables for the objects that the * client requested. If the object was requested the variable will be: * requested_=1 * If it wasn't requested there won't be a variable. */ static void script_write_requested6(client) struct client_state *client; { int i; struct option **req; char name[256]; req = client->config->requested_options; if (req == NULL) return; for (i = 0 ; req[i] != NULL ; i++) { if ((req[i]->universe == &dhcpv6_universe) && dhcp_option_ev_name (name, sizeof(name), req[i])) { client_envadd(client, "requested_", name, "%d", 1); } } } /* * Check if there is something not fully defined in the active lease. */ static isc_boolean_t active_prefix(struct client_state *client) { struct dhc6_lease *lease; struct dhc6_ia *ia; struct dhc6_addr *pref; char zeros[16]; lease = client->active_lease; if (lease == NULL) return ISC_FALSE; memset(zeros, 0, 16); for (ia = lease->bindings; ia != NULL; ia = ia->next) { if (ia->ia_type != D6O_IA_PD) continue; for (pref = ia->addrs; pref != NULL; pref = pref->next) { if (pref->plen == 0) return ISC_FALSE; if (pref->address.len != 16) return ISC_FALSE; if (memcmp(pref->address.iabuf, zeros, 16) == 0) return ISC_FALSE; } } return ISC_TRUE; } /* Adds a leases's declined addreses to the outbound packet * * For each IA_NA in the lease that contains one or more declined * addresses, an IA_NA option with an iasubopt for each declined * address is added to the outbound packet. * * We skip PDs and TAs as declines are undefined for them. */ static isc_result_t dhc6_add_ia_na_decline(struct client_state *client, struct data_string *packet, struct dhc6_lease *lease) { struct data_string iads; struct data_string addrds; struct dhc6_addr *addr; struct dhc6_ia *ia; isc_result_t rval = ISC_R_SUCCESS; memset(&iads, 0, sizeof(iads)); memset(&addrds, 0, sizeof(addrds)); for (ia = lease->bindings; ia != NULL && rval == ISC_R_SUCCESS; ia = ia->next) { if (ia->ia_type != D6O_IA_NA) continue; int start_new_ia = 1; for (addr = ia->addrs ; addr != NULL ; addr = addr->next) { /* * Do not confirm expired addresses, do not request * expired addresses (but we keep them around for * solicit). */ if (!(addr->flags & DHC6_ADDR_DECLINED)) { continue; } if (start_new_ia) { if (!buffer_allocate(&iads.buffer, 12, MDL)) { log_error("Unable to allocate memory" " for IA_NA."); rval = ISC_R_NOMEMORY; break; } /* Copy the IAID into the packet buffer. */ memcpy(iads.buffer->data, ia->iaid, 4); iads.data = iads.buffer->data; iads.len = 12; /* Set t1/t2 to zero; server will ignore them */ memset(iads.buffer->data + 4, 0, 8); log_debug("XMT: X-- IA_NA %s", print_hex_1(4, iads.buffer->data, 55)); start_new_ia = 0; } if (addr->address.len != 16) { log_error("Illegal IPv6 address length (%d), " "ignoring. (%s:%d)", addr->address.len, MDL); continue; } if (!buffer_allocate(&addrds.buffer, 24, MDL)) { log_error("Unable to allocate memory for " "IAADDR."); rval = ISC_R_NOMEMORY; break; } addrds.data = addrds.buffer->data; addrds.len = 24; /* Copy the address into the packet buffer. */ memcpy(addrds.buffer->data, addr->address.iabuf, 16); /* Preferred and max life are irrelevant */ memset(addrds.buffer->data + 16, 0, 8); log_debug("XMT: | X-- Decline Address %s", piaddr(addr->address)); append_option(&iads, &dhcpv6_universe, iaaddr_option, &addrds); data_string_forget(&addrds, MDL); } /* * It doesn't make sense to make a request without an * address. */ if (ia->addrs == NULL) { log_debug("!!!: V IA_NA has no IAADDRs - removed."); rval = ISC_R_FAILURE; } else if (rval == ISC_R_SUCCESS) { log_debug("XMT: V IA_NA appended."); append_option(packet, &dhcpv6_universe, ia_na_option, &iads); } data_string_forget(&iads, MDL); } return (rval); } /* * Remove any declined NA addresses from the lease. * * Returns zero if the all of the bindings on the lease * were removed, non-zero if there are PD, TA, or usuable NA * bindings */ int drop_declined_addrs(struct dhc6_lease *lease) { struct dhc6_ia *ia; int live_cnt = 0; for (ia = lease->bindings; ia != NULL; ia = ia->next) { struct dhc6_addr* prev_addr; struct dhc6_addr* addr; struct dhc6_addr* next; /* If it's a PD or TA, we assume it has at least * one usuable binding */ if (ia->ia_type != D6O_IA_NA) { live_cnt++; continue; } prev_addr = NULL; for (addr = ia->addrs ; addr != NULL ; ) { if (!(addr->flags & DHC6_ADDR_DECLINED)) { live_cnt++; addr = addr->next; prev_addr = addr; continue; } /* If we're deleting head, move it up one */ if (ia->addrs == addr) { ia->addrs = addr->next; prev_addr = addr->next; } else { prev_addr->next = addr->next; } if (addr->options != NULL) { option_state_dereference(&addr->options, MDL); } next = addr->next; dfree(addr, MDL); addr = next; } } return (live_cnt); } /* Run through the addresses in lease and return true if there's any unexpired. * Return false otherwise. */ static isc_boolean_t unexpired_address_in_lease(struct dhc6_lease *lease) { struct dhc6_ia *ia; struct dhc6_addr *addr; if (lease == NULL) { return ISC_FALSE; } for (ia = lease->bindings ; ia != NULL ; ia = ia->next) { for (addr = ia->addrs ; addr != NULL ; addr = addr->next) { if (!(addr->flags & DHC6_ADDR_EXPIRED) && (addr->starts + addr->max_life > cur_time)) { return ISC_TRUE; } } } log_debug("PRC: Previous lease is devoid of active addresses."); return ISC_FALSE; } #endif /* DHCPv6 */ dhcp-4.4.1/client/dhclient-script.8000644 000765 000024 00000026273 13243301226 017370 0ustar00tmarkstaff000000 000000 .\" dhclient-script.8 .\" .\" Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") .\" Copyright (c) 1996-2003 by Internet Software Consortium .\" .\" 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/. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT .\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" Internet Systems Consortium, Inc. .\" 950 Charter Street .\" Redwood City, CA 94063 .\" .\" https://www.isc.org/ .\" .\" Support and other services are available for ISC products - see .\" https://www.isc.org for more information or to learn more about ISC. .\" .\" $Id: dhclient-script.8,v 1.14 2010/07/02 23:09:14 sar Exp $ .\" .TH dhclient-script 8 .SH NAME dhclient-script - DHCP client network configuration script .SH DESCRIPTION The DHCP client network configuration script is invoked from time to time by \fBdhclient(8)\fR. This script is used by the dhcp client to set each interface's initial configuration prior to requesting an address, to test the address once it has been offered, and to set the interface's final configuration once a lease has been acquired. If no lease is acquired, the script is used to test predefined leases, if any, and also called once if no valid lease can be identified. .PP This script is not meant to be customized by the end user. If local customizations are needed, they should be possible using the enter and exit hooks provided (see HOOKS for details). These hooks will allow the user to override the default behaviour of the client in creating a .B /etc/resolv.conf file. .PP No standard client script exists for some operating systems, even though the actual client may work, so a pioneering user may well need to create a new script or modify an existing one. In general, customizations specific to a particular computer should be done in the .B ETCDIR/dhclient.conf file. If you find that you can't make such a customization without customizing .B ETCDIR/dhclient.conf or using the enter and exit hooks, please submit a bug report. .SH HOOKS When it starts, the client script first defines a shell function, .B make_resolv_conf , which is later used to create the .B /etc/resolv.conf file. To override the default behaviour, redefine this function in the enter hook script. .PP On after defining the make_resolv_conf function, the client script checks for the presence of an executable .B ETCDIR/dhclient-enter-hooks script, and if present, it invokes the script inline, using the Bourne shell \'.\' command. The entire environment documented under OPERATION is available to this script, which may modify the environment if needed to change the behaviour of the script. If an error occurs during the execution of the script, it can set the exit_status variable to a nonzero value, and .B CLIENTBINDIR/dhclient-script will exit with that error code immediately after the client script exits. .PP After all processing has completed, .B CLIENTBINDIR/dhclient-script checks for the presence of an executable .B ETCDIR/dhclient-exit-hooks script, which if present is invoked using the \'.\' command. The exit status of dhclient-script will be passed to dhclient-exit-hooks in the exit_status shell variable, and will always be zero if the script succeeded at the task for which it was invoked. The rest of the environment as described previously for dhclient-enter-hooks is also present. The .B ETCDIR/dhclient-exit-hooks script can modify the valid of exit_status to change the exit status of dhclient-script. .SH OPERATION When dhclient needs to invoke the client configuration script, it defines a set of variables in the environment, and then invokes .B CLIENTBINDIR/dhclient-script. In all cases, $reason is set to the name of the reason why the script has been invoked. The following reasons are currently defined: MEDIUM, PREINIT, BOUND, RENEW, REBIND, REBOOT, EXPIRE, FAIL, STOP, RELEASE, NBI and TIMEOUT. .PP .SH MEDIUM The DHCP client is requesting that an interface's media type be set. The interface name is passed in $interface, and the media type is passed in $medium. .SH PREINIT The DHCP client is requesting that an interface be configured as required in order to send packets prior to receiving an actual address. For clients which use the BSD socket library, this means configuring the interface with an IP address of 0.0.0.0 and a broadcast address of 255.255.255.255. For other clients, it may be possible to simply configure the interface up without actually giving it an IP address at all. The interface name is passed in $interface, and the media type in $medium. .PP If an IP alias has been declared in dhclient.conf, its address will be passed in $alias_ip_address, and that ip alias should be deleted from the interface, along with any routes to it. .SH BOUND The DHCP client has done an initial binding to a new address. The new ip address is passed in $new_ip_address, and the interface name is passed in $interface. The media type is passed in $medium. Any options acquired from the server are passed using the option name described in \fBdhcp-options\fR, except that dashes (\'-\') are replaced by underscores (\'_\') in order to make valid shell variables, and the variable names start with new_. So for example, the new subnet mask would be passed in $new_subnet_mask. Options from a non-default universe will have the universe name prepended to the option name, for example $new_dhcp6_server_id. The options that the client explicitly requested via a PRL or ORO option are passed with the same option name as above but prepended with requested_ and with a value of 1, for example requested_subnet_mask=1. No such variable is defined for options not requested by the client or options that don't require a request option, such as the ip address (*_ip_address) or expiration time (*_expiry). .PP Before actually configuring the address, dhclient-script should somehow ARP for it and exit with a nonzero status if it receives a reply. In this case, the client will send a DHCPDECLINE message to the server and acquire a different address. This may also be done in the RENEW, REBIND, or REBOOT states, but is not required, and indeed may not be desirable. .PP When a binding has been completed, a lot of network parameters are likely to need to be set up. A new /etc/resolv.conf needs to be created, using the values of $new_domain_name and $new_domain_name_servers (which may list more than one server, separated by spaces). A default route should be set using $new_routers, and static routes may need to be set up using $new_static_routes. .PP If an IP alias has been declared, it must be set up here. The alias IP address will be written as $alias_ip_address, and other DHCP options that are set for the alias (e.g., subnet mask) will be passed in variables named as described previously except starting with $alias_ instead of $new_. Care should be taken that the alias IP address not be used if it is identical to the bound IP address ($new_ip_address), since the other alias parameters may be incorrect in this case. .SH RENEW When a binding has been renewed, the script is called as in BOUND, except that in addition to all the variables starting with $new_, and $requested_ there is another set of variables starting with $old_. Persistent settings that may have changed need to be deleted - for example, if a local route to the bound address is being configured, the old local route should be deleted. If the default route has changed, the old default route should be deleted. If the static routes have changed, the old ones should be deleted. Otherwise, processing can be done as with BOUND. .SH REBIND The DHCP client has rebound to a new DHCP server. This can be handled as with RENEW, except that if the IP address has changed, the ARP table should be cleared. .SH REBOOT The DHCP client has successfully reacquired its old address after a reboot. This can be processed as with BOUND. .SH EXPIRE The DHCP client has failed to renew its lease or acquire a new one, and the lease has expired. The IP address must be relinquished, and all related parameters should be deleted, as in RENEW and REBIND. .SH FAIL The DHCP client has been unable to contact any DHCP servers, and any leases that have been tested have not proved to be valid. The parameters from the last lease tested should be deconfigured. This can be handled in the same way as EXPIRE. .SH STOP The dhclient has been informed to shut down gracefully, the dhclient-script should unconfigure or shutdown the interface as appropriate. .SH RELEASE The dhclient has been executed using the -r flag, indicating that the administrator wishes it to release its lease(s). dhclient-script should unconfigure or shutdown the interface. .SH NBI No-Broadcast-Interfaces...dhclient was unable to find any interfaces upon which it believed it should commence DHCP. What dhclient-script should do in this situation is entirely up to the implementor. .SH TIMEOUT The DHCP client has been unable to contact any DHCP servers. However, an old lease has been identified, and its parameters have been passed in as with BOUND. The client configuration script should test these parameters and, if it has reason to believe they are valid, should exit with a value of zero. If not, it should exit with a nonzero value. .PP The usual way to test a lease is to set up the network as with REBIND (since this may be called to test more than one lease) and then ping the first router defined in $routers. If a response is received, the lease must be valid for the network to which the interface is currently connected. It would be more complete to try to ping all of the routers listed in $new_routers, as well as those listed in $new_static_routes, but current scripts do not do this. .SH FILES Each operating system should generally have its own script file, although the script files for similar operating systems may be similar or even identical. The script files included in Internet Systems Consortium DHCP distribution appear in the distribution tree under client/scripts, and bear the names of the operating systems on which they are intended to work. .SH BUGS If more than one interface is being used, there's no obvious way to avoid clashes between server-supplied configuration parameters - for example, the stock dhclient-script rewrites /etc/resolv.conf. If more than one interface is being configured, /etc/resolv.conf will be repeatedly initialized to the values provided by one server, and then the other. Assuming the information provided by both servers is valid, this shouldn't cause any real problems, but it could be confusing. .SH SEE ALSO dhclient(8), dhcpd(8), dhcrelay(8), dhclient.conf(5) and dhclient.leases(5). .SH AUTHOR .B dhclient-script(8) To learn more about Internet Systems Consortium, see .B https://www.isc.org. dhcp-4.4.1/client/dhclient.8000644 000765 000024 00000050272 13243301226 016062 0ustar00tmarkstaff000000 000000 .\" $Id: dhclient.8,v 1.36 2011/04/15 21:58:12 sar Exp $ .\" .\" Copyright (c) 2004-2018 by Internet Systems Consortium, Inc. ("ISC") .\" Copyright (c) 1996-2003 by Internet Software Consortium .\" .\" 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/. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT .\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" Internet Systems Consortium, Inc. .\" 950 Charter Street .\" Redwood City, CA 94063 .\" .\" https://www.isc.org/ .\" .\" Support and other services are available for ISC products - see .\" https://www.isc.org for more information or to learn more about ISC. .\" .TH dhclient 8 .SH NAME dhclient - Dynamic Host Configuration Protocol Client .SH SYNOPSIS .B dhclient [ .B -4 | .B -6 ] [ .B -S ] [ .B -N [ .B -N... ] ] [ .B -T [ .B -T... ] ] [ .B -P [ .B -P... ] ] .B -R ] [ .B -i ] [ .B -I ] [ .B -4o6 .I port ] [ .B -D .I LL|LLT ] [ .B -p .I port-number ] [ .B -d ] [ .B -df .I duid-lease-file ] [ .B -e .I VAR=value ] [ .B -q ] [ .B -1 ] [ .B -r | .B -x ] [ .B -lf .I lease-file ] [ .B -pf .I pid-file ] [ .B --no-pid ] [ .B -cf .I config-file ] [ .B -sf .I script-file ] [ .B -s .I server-addr ] [ .B -g .I relay ] [ .B -n ] [ .B -nw ] [ .B -w ] [ .B --dad-wait-time .I seconds ] [ .B --prefix-len-hint .I length ] [ .B --decline-wait-time .I seconds ] [ .B -v ] [ .B --version ] [ .I if0 [ .I ...ifN ] ] .SH DESCRIPTION The Internet Systems Consortium DHCP Client, \fBdhclient\fR, provides a means for configuring one or more network interfaces using the Dynamic Host Configuration Protocol, BOOTP protocol, or if these protocols fail, by statically assigning an address. .SH OPERATION .PP The DHCP protocol allows a host to contact a central server which maintains a list of IP addresses which may be assigned on one or more subnets. A DHCP client may request an address from this pool, and then use it on a temporary basis for communication on network. The DHCP protocol also provides a mechanism whereby a client can learn important details about the network to which it is attached, such as the location of a default router, the location of a name server, and so on. .PP There are two versions of the DHCP protocol DHCPv4 and DHCPv6. At startup the client may be started for one or the other via the .B -4 or .B -6 options. .PP On startup, \fBdhclient\fR reads the dhclient.conf for configuration instructions. It then gets a list of all the network interfaces that are configured in the current system. For each interface, it attempts to configure the interface using the DHCP protocol. .PP In order to keep track of leases across system reboots and server restarts, \fBdhclient\fR keeps a list of leases it has been assigned in the dhclient.leases file. On startup, after reading the dhclient.conf file, \fBdhclient\fR reads the dhclient.leases file to refresh its memory about what leases it has been assigned. .PP When a new lease is acquired, it is appended to the end of the dhclient.leases file. In order to prevent the file from becoming arbitrarily large, from time to time \fBdhclient\fR creates a new dhclient.leases file from its in-core lease database. The old version of the dhclient.leases file is retained under the name .IR dhclient.leases~ until the next time \fBdhclient\fR rewrites the database. .PP Old leases are kept around in case the DHCP server is unavailable when \fBdhclient\fR is first invoked (generally during the initial system boot process). In that event, old leases from the dhclient.leases file which have not yet expired are tested, and if they are determined to be valid, they are used until either they expire or the DHCP server becomes available. .PP A mobile host which may sometimes need to access a network on which no DHCP server exists may be preloaded with a lease for a fixed address on that network. When all attempts to contact a DHCP server have failed, \fBdhclient\fR will try to validate the static lease, and if it succeeds, will use that lease until it is restarted. .PP A mobile host may also travel to some networks on which DHCP is not available but BOOTP is. In that case, it may be advantageous to arrange with the network administrator for an entry on the BOOTP database, so that the host can boot quickly on that network rather than cycling through the list of old leases. .SH COMMAND LINE .PP The names of the network interfaces that \fBdhclient\fR should attempt to configure may be specified on the command line. If no interface names are specified on the command line \fBdhclient\fR will normally identify all network interfaces, eliminating non-broadcast interfaces if possible, and attempt to configure each interface. .PP It is also possible to specify interfaces by name in the dhclient.conf file. If interfaces are specified in this way, then the client will only configure interfaces that are either specified in the configuration file or on the command line, and will ignore all other interfaces. .PP The client normally prints no output during its startup sequence. It can be made to emit verbose messages displaying the startup sequence events until it has acquired an address by supplying the .B -v command line argument. In either case, the client logs messages using the .B syslog(3) facility. .SH OPTIONS .TP .BI \-4 Use the DHCPv4 protocol to obtain an IPv4 address and configuration parameters. This is the default and cannot be combined with \fB\-6\fR. .TP .BI \-6 Use the DHCPv6 protocol to obtain whatever IPv6 addresses are available along with configuration parameters. It cannot be combined with \fB\-4\fR. The \fB\-S -T -P -N\fR and \fB\-D\fR arguments provide more control over aspects of the DHCPv6 processing. Note: it is not recommended to mix queries of different types together or even to share the lease file between them. .TP .BI \-4o6 \ port Participate in the DHCPv4 over DHCPv6 protocol specified by RFC 7341. This associates a DHCPv4 and a DHCPv6 client to allow the v4 client to send v4 requests encapsulated in a v6 packet. Communication between the two clients is done on a pair of UDP sockets bound to ::1 \fIport\fR and \fIport + 1\fR. Both clients must be launched using the same \fIport\fR argument. .TP .BI \-1 Try to get a lease once. On failure exit with code 2. In DHCPv6 this sets the maximum duration of the initial exchange to .I timeout (from dhclient.conf with a default of sixty seconds). .TP .BI \-d .\" This is not intuitive. Force .B dhclient to run as a foreground process. Normally the DHCP client will run in the foreground until is has configured an interface at which time it will revert to running in the background. This option is useful when running the client under a debugger, or when running it out of inittab on System V systems. This implies \fB-v\fR. .TP .BI \-nw Become a daemon immediately (nowait) rather than waiting until an IP address has been acquired. .TP .BI \-q Be quiet at startup, this is the default. .TP .BI \-v Enable verbose log messages. .\" This prints the version, copyright and URL. .TP .BI \-w Continue running even if no broadcast interfaces were found. Normally DHCP client will exit if it isn't able to identify any network interfaces to configure. On laptop computers and other computers with hot-swappable I/O buses, it is possible that a broadcast interface may be added after system startup. This flag can be used to cause the client not to exit when it doesn't find any such interfaces. The .B omshell(1) program can then be used to notify the client when a network interface has been added or removed, so that the client can attempt to configure an IP address on that interface. .TP .BI \-n Do not configure any interfaces. This is most likely to be useful in combination with the .B -w flag. .TP .BI \-e \ VAR=value Define additional environment variables for the environment where .B dhclient-script executes. You may specify multiple .B \-e options on the command line. .TP .BI \-r Release the current lease and stop the running DHCP client as previously recorded in the PID file. When shutdown via this method .B dhclient-script will be executed with the specific reason for calling the script set. The client normally doesn't release the current lease as this is not required by the DHCP protocol but some cable ISPs require their clients to notify the server if they wish to release an assigned IP address. .\" TODO what dhclient-script argument? .\" When released, .TP .BI \-x Stop the running DHCP client without releasing the current lease. Kills existing \fBdhclient\fR process as previously recorded in the PID file. When shutdown via this method .B dhclient-script will be executed with the specific reason for calling the script set. .TP .BI \-p \ port-number The UDP port number on which the DHCP client should listen and transmit. If unspecified, .B dhclient uses the default port of 68. This is mostly useful for debugging purposes. If a different port is specified on which the client should listen and transmit, the client will also use a different destination port - one less than the specified port. .TP .BI \-s \ server-addr Specify the server IP address or fully qualified domain name to use as a destination for DHCP protocol messages before .B dhclient has acquired an IP address. Normally, .B dhclient transmits these messages to 255.255.255.255 (the IP limited broadcast address). Overriding this is mostly useful for debugging purposes. This feature is not supported in DHCPv6 (\fB-6\fR) mode. .TP .BI \-g \ relay .\" mockup relay Set the giaddr field of all packets to the \fIrelay\fR IP address simulating a relay agent. This is for testing purposes only and should not be expected to work in any consistent or useful way. .TP .BI \-i Use a DUID with DHCPv4 clients. If no DUID is available in the lease file one will be constructed and saved. The DUID will be used to construct a RFC4361 style client id that will be included in the client's messages. This client id can be overridden by setting a client id in the configuration file. Overriding the client id in this fashion is discouraged. .TP .BI \-I Use the standard DDNS scheme from RFCs 4701 & 4702. .TP .TP .BI \--decline-wait-time \ seconds Specify the time (in seconds) that an IPv4 client should wait after declining an address before issuing a discover. The default is 10 seconds as recommended by RFC 2131, Section 3.1.5. A value of zero equates to no wait at all. .PP .BI \--version Print version number and exit. .PP .I Options available for DHCPv6 mode: .TP .BI \-S .\" TODO: mention DUID? Use Information-request to get only stateless configuration parameters (i.e., without address). This implies \fB\-6\fR. It also doesn't rewrite the lease database. .\" TODO: May not be used with -N -P or -T. ?? .TP .BI \-T .\" TODO wanted_ia_ta++ Ask for IPv6 temporary addresses, one set per \fB\-T\fR flag. This implies \fB\-6\fR and also disables the normal address query. See \fB\-N\fR to restore it. .TP .BI \-P Enable IPv6 prefix delegation. This implies \fB\-6\fR and also disables the normal address query. See \fB\-N\fR to restore it. Multiple prefixes can be requested with multiple \fB\-P\fR flags. Note only one requested interface is allowed. .TP .BI \-R Require that responses include all of the items requested by any \fB\-N\fR, \fB\-T\fR, or \fB\-P\fR options. Normally even if the command line includes a number of these the client will be willing to accept the best lease it can even if the lease doesn't include all of the requested items. This option causes the client to only accept leases that include all of the requested items. Note well: enabling this may prevent the client from using any leases it receives if the servers aren't configured to supply all of the items. .TP .BI \-D \ LL\ or\ LLT Override the default when selecting the type of DUID to use. By default, DHCPv6 \fBdhclient\fR creates an identifier based on the link-layer address (DUID-LL) if it is running in stateless mode (with \fB\-S\fR, not requesting an address), or it creates an identifier based on the link-layer address plus a timestamp (DUID-LLT) if it is running in stateful mode (without \fB\-S\fR, requesting an address). When DHCPv4 is configured to use a DUID using \fB\-i\fR option the default is to use a DUID-LLT. \fB\-D\fR overrides these default, with a value of either \fILL\fR or \fILLT\fR. .TP .BI \-N .\" TODO: is this for telling an already running dhclient? Restore normal address query for IPv6. This implies \fB-6\fR. It is used to restore normal operation after using \fB-T\fR or \fB-P\fR. Multiple addresses can be requested with multiple \fB\-N\fR flags. .TP .BI \--address-prefix-len \ length Specify the length of the prefix for IPv6 addresses. This value is passed by dhclient into the client script via the environment variable, ip6_prefixlen, when binding IPv6 addresses. The default value is 128. Alternatively you may change the default at compile time by setting DHCLIENT_DEFAULT_PREFIX_LEN in includes/site.h. .PP .TP .BI \--dad-wait-time \ seconds Specify maximum time (in seconds) that the client should wait for the duplicate address detection (DAD) to complete on an interface. This value is propagated to the dhclient script in a dad_wait_time environment variable. If any of the IPv6 addresses on the interface are tentative (DAD is in progress), the script will wait for the specified number of seconds for DAD to complete. If the script ignores this variable the parameter has no effect. .PP .TP .BI \--prefix-len-hint \ length When used in conjunction with -P, it directs the client to use the given length to use a prefix hint of, "::/length", when requesting new prefixes. .PP .I Modifying default file locations: The following options can be used to modify the locations a client uses for its files. They can be particularly useful if, for example, .B DBDIR or .B RUNDIR have not been mounted when the DHCP client is started. .TP .BI \-cf \ config-file Path to the client configuration file. If unspecified, the default .B ETCDIR/dhclient.conf is used. See \fBdhclient.conf(5)\fR for a description of this file. .TP .BI \-df \ duid-lease-file Path to a secondary lease file. If the primary lease file doesn't contain a DUID this file will be searched. The DUID read from the secondary will be written to the primary. This option can be used to allow an IPv4 instance of the client to share a DUID with an IPv6 instance. After starting one of the instances the second can be started with this option pointing to the lease file of the first instance. There is no default. If no file is specified no search is made for a DUID should one not be found in the main lease file. .TP .BI \-lf \ lease-file Path to the lease database file. If unspecified, the default .B DBDIR/dhclient.leases is used. See \fBdhclient.leases(5)\fR for a description of this file. .TP .BI \-pf \ pid-file Path to the process ID file. If unspecified, the default .B RUNDIR/dhclient.pid is used. .TP .BI \--no-pid Option to disable writing pid files. By default the program will write a pid file. If the program is invoked with this option it will not attempt to kill any existing client processes even if invoked with \fB-r\fR or \fB-x\fR. .TP .BI \-sf \ script-file Path to the network configuration script invoked by .B dhclient when it gets a lease. If unspecified, the default .B CLIENTBINDIR/dhclient-script is used. See \fBdhclient-script(8)\fR for a description of this file. .PP .SH PORTS During operations the client may use multiple UDP ports to provide different functions. Which ports are opened depends on both the way you compiled your code and the configuration you supply. The following should provide you an idea of what ports may be in use. Normally a DHCPv4 client will open a raw UDP socket to receive and send most DHCPv4 packets. It also opens a fallback UDP socket for use in sending unicast packets. Normally these will both use the well known port number for BOOTPC. For DHCPv6 the client opens a UDP socket on the well known client port and a fallback UDP socket on a random port for use in sending unicast messages. Unlike DHCPv4 the well known socket doesn't need to be opened in raw mode. If you have included an omapi port statement in your configuration file then the client will open a TCP socket on that port to listen for OMPAI connections. When something connects another port will be used for the established connection. When DDNS is enabled at compile time (see includes/site.h) the client will open both a v4 and a v6 UDP socket on random ports. These ports are not opened unless/until the client first attempts to do an update. If the client is not configured to do updates, the ports will never be opened. .PP .SH CONFIGURATION The syntax of the \fBdhclient.conf(5)\fR file is discussed separately. .SH OMAPI The DHCP client provides some ability to control it while it is running, without stopping it. This capability is provided using OMAPI, an API for manipulating remote objects. OMAPI clients connect to the client using TCP/IP, authenticate, and can then examine the client's current status and make changes to it. .PP Rather than implementing the underlying OMAPI protocol directly, user programs should use the dhcpctl API or OMAPI itself. Dhcpctl is a wrapper that handles some of the housekeeping chores that OMAPI does not do automatically. Dhcpctl and OMAPI are documented in \fBdhcpctl(3)\fR and \fBomapi(3)\fR. Most things you'd want to do with the client can be done directly using the \fBomshell(1)\fR command, rather than having to write a special program. .SH THE CONTROL OBJECT The control object allows you to shut the client down, releasing all leases that it holds and deleting any DNS records it may have added. It also allows you to pause the client - this unconfigures any interfaces the client is using. You can then restart it, which causes it to reconfigure those interfaces. You would normally pause the client prior to going into hibernation or sleep on a laptop computer. You would then resume it after the power comes back. This allows PC cards to be shut down while the computer is hibernating or sleeping, and then reinitialized to their previous state once the computer comes out of hibernation or sleep. .PP The control object has one attribute - the state attribute. To shut the client down, set its state attribute to 2. It will automatically do a DHCPRELEASE. To pause it, set its state attribute to 3. To resume it, set its state attribute to 4. .PP .SH ENVIRONMENT VARIABLES .PP The following environment variables may be defined to override the builtin defaults for file locations. Note that use of the related command-line options will ignore the corresponding environment variable settings. .TP .B PATH_DHCLIENT_CONF The dhclient.conf configuration file. .TP .B PATH_DHCLIENT_DB The dhclient.leases database. .TP .B PATH_DHCLIENT_PID The dhclient PID file. .TP .B PATH_DHCLIENT_SCRIPT The dhclient-script file. .PP .SH FILES .B CLIENTBINDIR/dhclient-script, .B ETCDIR/dhclient.conf, DBDIR/dhclient.leases, RUNDIR/dhclient.pid, .B DBDIR/dhclient.leases~. .SH SEE ALSO dhcpd(8), dhcrelay(8), dhclient-script(8), dhclient.conf(5), dhclient.leases(5), dhcp-eval(5). .SH AUTHOR .B dhclient(8) To learn more about Internet Systems Consortium, see .B https://www.isc.org .PP This client was substantially modified and enhanced by Elliot Poger for use on Linux while he was working on the MosquitoNet project at Stanford. .PP The current version owes much to Elliot's Linux enhancements, but was substantially reorganized and partially rewritten by Ted Lemon so as to use the same networking framework that the Internet Systems Consortium DHCP server uses. Much system-specific configuration code was moved into a shell script so that as support for more operating systems is added, it will not be necessary to port and maintain system-specific configuration code to these operating systems - instead, the shell script can invoke the native tools to accomplish the same purpose. .PP dhcp-4.4.1/client/dhclient.c000644 000765 000024 00000457431 13243301226 016145 0ustar00tmarkstaff000000 000000 /* dhclient.c DHCP Client. */ /* * Copyright (c) 2004-2018 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1995-2003 by Internet Software Consortium * * 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * * This code is based on the original client state machine that was * written by Elliot Poger. The code has been extensively hacked on * by Ted Lemon since then, so any mistakes you find are probably his * fault and not Elliot's. */ #include "dhcpd.h" #include #include #include #include #include #include #include #include #include TIME default_lease_time = 43200; /* 12 hours... */ TIME max_lease_time = 86400; /* 24 hours... */ const char *path_dhclient_conf = _PATH_DHCLIENT_CONF; const char *path_dhclient_db = NULL; const char *path_dhclient_pid = NULL; static char path_dhclient_script_array[] = _PATH_DHCLIENT_SCRIPT; char *path_dhclient_script = path_dhclient_script_array; const char *path_dhclient_duid = NULL; /* False (default) => we write and use a pid file */ isc_boolean_t no_pid_file = ISC_FALSE; int dhcp_max_agent_option_packet_length = 0; int interfaces_requested = 0; struct iaddr iaddr_broadcast = { 4, { 255, 255, 255, 255 } }; struct iaddr iaddr_any = { 4, { 0, 0, 0, 0 } }; struct in_addr inaddr_any; struct sockaddr_in sockaddr_broadcast; struct in_addr giaddr; struct data_string default_duid; int duid_type = 0; int duid_v4 = 0; int std_dhcid = 0; int decline_wait_time = 10; /* Default to 10 secs per, RFC 2131, 3.1.5 */ /* ASSERT_STATE() does nothing now; it used to be assert (state_is == state_shouldbe). */ #define ASSERT_STATE(state_is, state_shouldbe) {} #ifndef UNIT_TEST static const char copyright[] = "Copyright 2004-2018 Internet Systems Consortium."; static const char arr [] = "All rights reserved."; static const char message [] = "Internet Systems Consortium DHCP Client"; static const char url [] = "For info, please visit https://www.isc.org/software/dhcp/"; #endif /* UNIT_TEST */ u_int16_t local_port = 0; u_int16_t remote_port = 0; #if defined(DHCPv6) && defined(DHCP4o6) int dhcp4o6_state = -1; /* -1 = stopped, 0 = polling, 1 = started */ #endif int no_daemon = 0; int dfd[2] = { -1, -1 }; struct string_list *client_env = NULL; int client_env_count = 0; int onetry = 0; int quiet = 1; int nowait = 0; int stateless = 0; int wanted_ia_na = -1; /* the absolute value is the real one. */ int wanted_ia_ta = 0; int wanted_ia_pd = 0; int require_all_ias = 0; /* If the user requires all of the IAs to be available before accepting a lease 0 = no, 1 = requries */ #if defined(DHCPv6) int dad_wait_time = 0; int prefix_len_hint = 0; #endif int address_prefix_len = DHCLIENT_DEFAULT_PREFIX_LEN; char *mockup_relay = NULL; char *progname = NULL; void run_stateless(int exit_mode, u_int16_t port); static isc_result_t write_duid(struct data_string *duid); static void add_reject(struct packet *packet); static int check_domain_name(const char *ptr, size_t len, int dots); static int check_domain_name_list(const char *ptr, size_t len, int dots); static int check_option_values(struct universe *universe, unsigned int opt, const char *ptr, size_t len); static void dhclient_ddns_cb_free(dhcp_ddns_cb_t *ddns_cb, char* file, int line); /*! * * \brief Print the generic usage message * * If the user has provided an incorrect command line print out * the description of the command line. The arguments provide * a way for the caller to request more specific information about * the error be printed as well. Mostly this will be that some * comamnd doesn't include its argument. * * \param sfmt - The basic string and format for the specific error * \param sarg - Generally the offending argument from the comamnd line. * * \return Nothing */ #if defined(DHCPv6) && defined(DHCP4o6) static void dhcp4o6_poll(void *dummy); static void dhcp4o6_resume(void); static void recv_dhcpv4_response(struct data_string *raw); static int send_dhcpv4_query(struct client_state *client, int broadcast); static void dhcp4o6_stop(void); static void forw_dhcpv4_response(struct packet *packet); static void forw_dhcpv4_query(struct data_string *raw); #endif #ifndef UNIT_TEST /* These are only used when we call usage() from the main routine * which isn't compiled when building for unit tests */ static const char use_noarg[] = "No argument for command: %s"; #ifdef DHCPv6 static const char use_v6command[] = "Command not used for DHCPv4: %s"; #endif #ifdef DHCPv6 #ifdef DHCP4o6 #define DHCLIENT_USAGE0 \ "[-4|-6] [-SNTPRI1dvrxi] [-nw] -4o6 ] [-p ] [-D LL|LLT]\n" \ " [--dad-wait-time ] [--prefix-len-hint ]\n" \ " [--decline-wait-time ]\n" \ " [--address-prefix-len ]\n" #else /* DHCP4o6 */ #define DHCLIENT_USAGE0 \ "[-4|-6] [-SNTPRI1dvrxi] [-nw] [-p ] [-D LL|LLT]\n" \ " [--dad-wait-time ] [--prefix-len-hint ]\n" \ " [--decline-wait-time ]\n" \ " [--address-prefix-len ]\n" #endif #else /* DHCPv6 */ #define DHCLIENT_USAGE0 \ "[-I1dvrxi] [-nw] [-p ] [-D LL|LLT] \n" \ " [--decline-wait-time ]\n" #endif #define DHCLIENT_USAGEC \ " [-s server-addr] [-cf config-file]\n" \ " [-df duid-file] [-lf lease-file]\n" \ " [-pf pid-file] [--no-pid] [-e VAR=val]\n" \ " [-sf script-file] [interface]*" #define DHCLIENT_USAGEH "{--version|--help|-h}" static void usage(const char *sfmt, const char *sarg) { log_info("%s %s", message, PACKAGE_VERSION); log_info(copyright); log_info(arr); log_info(url); /* If desired print out the specific error message */ #ifdef PRINT_SPECIFIC_CL_ERRORS if (sfmt != NULL) log_error(sfmt, sarg); #endif log_fatal("Usage: %s %s%s\n %s %s", isc_file_basename(progname), DHCLIENT_USAGE0, DHCLIENT_USAGEC, isc_file_basename(progname), DHCLIENT_USAGEH); } extern void initialize_client_option_spaces(); int main(int argc, char **argv) { int fd; int i; struct interface_info *ip; struct client_state *client; unsigned seed; char *server = NULL; isc_result_t status; int exit_mode = 0; int release_mode = 0; struct timeval tv; omapi_object_t *listener; isc_result_t result; int persist = 0; int no_dhclient_conf = 0; int no_dhclient_db = 0; int no_dhclient_pid = 0; int no_dhclient_script = 0; #ifdef DHCPv6 int local_family_set = 0; #ifdef DHCP4o6 u_int16_t dhcp4o6_port = 0; #endif /* DHCP4o6 */ #endif /* DHCPv6 */ char *s; #ifdef OLD_LOG_NAME progname = "dhclient"; #else progname = argv[0]; #endif /* Initialize client globals. */ memset(&default_duid, 0, sizeof(default_duid)); /* Make sure that file descriptors 0 (stdin), 1, (stdout), and 2 (stderr) are open. To do this, we assume that when we open a file the lowest available file descriptor is used. */ fd = open("/dev/null", O_RDWR); if (fd == 0) fd = open("/dev/null", O_RDWR); if (fd == 1) fd = open("/dev/null", O_RDWR); if (fd == 2) log_perror = 0; /* No sense logging to /dev/null. */ else if (fd != -1) close(fd); openlog(isc_file_basename(progname), DHCP_LOG_OPTIONS, LOG_DAEMON); #if !(defined(DEBUG) || defined(__CYGWIN32__)) setlogmask(LOG_UPTO(LOG_INFO)); #endif /* Parse arguments changing no_daemon */ for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "-r")) { no_daemon = 1; } else if (!strcmp(argv[i], "-x")) { no_daemon = 0; } else if (!strcmp(argv[i], "-d")) { no_daemon = 1; } else if (!strcmp(argv[i], "--version")) { const char vstring[] = "isc-dhclient-"; IGNORE_RET(write(STDERR_FILENO, vstring, strlen(vstring))); IGNORE_RET(write(STDERR_FILENO, PACKAGE_VERSION, strlen(PACKAGE_VERSION))); IGNORE_RET(write(STDERR_FILENO, "\n", 1)); exit(0); } else if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "-h")) { const char *pname = isc_file_basename(progname); IGNORE_RET(write(STDERR_FILENO, "Usage: ", 7)); IGNORE_RET(write(STDERR_FILENO, pname, strlen(pname))); IGNORE_RET(write(STDERR_FILENO, " ", 1)); IGNORE_RET(write(STDERR_FILENO, DHCLIENT_USAGE0, strlen(DHCLIENT_USAGE0))); IGNORE_RET(write(STDERR_FILENO, DHCLIENT_USAGEC, strlen(DHCLIENT_USAGEC))); IGNORE_RET(write(STDERR_FILENO, "\n", 1)); IGNORE_RET(write(STDERR_FILENO, " ", 7)); IGNORE_RET(write(STDERR_FILENO, pname, strlen(pname))); IGNORE_RET(write(STDERR_FILENO, " ", 1)); IGNORE_RET(write(STDERR_FILENO, DHCLIENT_USAGEH, strlen(DHCLIENT_USAGEH))); IGNORE_RET(write(STDERR_FILENO, "\n", 1)); exit(0); } } /* When not forbidden prepare to become a daemon */ if (!no_daemon) { int pid; if (pipe(dfd) == -1) log_fatal("Can't get pipe: %m"); if ((pid = fork ()) < 0) log_fatal("Can't fork daemon: %m"); if (pid != 0) { /* Parent: wait for the child to start */ int n; (void) close(dfd[1]); do { char buf; n = read(dfd[0], &buf, 1); if (n == 1) _exit((int)buf); } while (n == -1 && errno == EINTR); _exit(1); } /* Child */ (void) close(dfd[0]); } /* Set up the isc and dns library managers */ status = dhcp_context_create(DHCP_CONTEXT_PRE_DB | DHCP_CONTEXT_POST_DB | DHCP_DNS_CLIENT_LAZY_INIT, NULL, NULL); if (status != ISC_R_SUCCESS) log_fatal("Can't initialize context: %s", isc_result_totext(status)); /* Set up the OMAPI. */ status = omapi_init(); if (status != ISC_R_SUCCESS) log_fatal("Can't initialize OMAPI: %s", isc_result_totext(status)); /* Set up the OMAPI wrappers for various server database internal objects. */ dhcp_common_objects_setup(); dhcp_interface_discovery_hook = dhclient_interface_discovery_hook; dhcp_interface_shutdown_hook = dhclient_interface_shutdown_hook; dhcp_interface_startup_hook = dhclient_interface_startup_hook; for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "-r")) { release_mode = 1; /* no_daemon = 1; */ #ifdef DHCPv6 } else if (!strcmp(argv[i], "-4")) { if (local_family_set && local_family != AF_INET) log_fatal("Client can only do v4 or v6, not " "both."); local_family_set = 1; local_family = AF_INET; } else if (!strcmp(argv[i], "-6")) { if (local_family_set && local_family != AF_INET6) log_fatal("Client can only do v4 or v6, not " "both."); local_family_set = 1; local_family = AF_INET6; #ifdef DHCP4o6 } else if (!strcmp(argv[i], "-4o6")) { if (++i == argc) usage(use_noarg, argv[i-1]); dhcp4o6_port = validate_port_pair(argv[i]); log_debug("DHCPv4 over DHCPv6 over ::1 port %d and %d", ntohs(dhcp4o6_port), ntohs(dhcp4o6_port) + 1); dhcpv4_over_dhcpv6 = 1; #endif /* DHCP4o6 */ #endif /* DHCPv6 */ } else if (!strcmp(argv[i], "-x")) { /* eXit, no release */ release_mode = 0; /* no_daemon = 0; */ exit_mode = 1; } else if (!strcmp(argv[i], "-p")) { if (++i == argc) usage(use_noarg, argv[i-1]); local_port = validate_port(argv[i]); log_debug("binding to user-specified port %d", ntohs(local_port)); } else if (!strcmp(argv[i], "-d")) { /* no_daemon = 1; */ quiet = 0; } else if (!strcmp(argv[i], "-pf")) { if (++i == argc) usage(use_noarg, argv[i-1]); path_dhclient_pid = argv[i]; no_dhclient_pid = 1; } else if (!strcmp(argv[i], "--no-pid")) { no_pid_file = ISC_TRUE; } else if (!strcmp(argv[i], "-cf")) { if (++i == argc) usage(use_noarg, argv[i-1]); path_dhclient_conf = argv[i]; no_dhclient_conf = 1; } else if (!strcmp(argv[i], "-df")) { if (++i == argc) usage(use_noarg, argv[i-1]); path_dhclient_duid = argv[i]; } else if (!strcmp(argv[i], "-lf")) { if (++i == argc) usage(use_noarg, argv[i-1]); path_dhclient_db = argv[i]; no_dhclient_db = 1; } else if (!strcmp(argv[i], "-sf")) { if (++i == argc) usage(use_noarg, argv[i-1]); path_dhclient_script = argv[i]; no_dhclient_script = 1; } else if (!strcmp(argv[i], "-1")) { onetry = 1; } else if (!strcmp(argv[i], "-q")) { quiet = 1; } else if (!strcmp(argv[i], "-s")) { if (++i == argc) usage(use_noarg, argv[i-1]); server = argv[i]; } else if (!strcmp(argv[i], "-g")) { if (++i == argc) usage(use_noarg, argv[i-1]); mockup_relay = argv[i]; } else if (!strcmp(argv[i], "-nw")) { nowait = 1; } else if (!strcmp(argv[i], "-n")) { /* do not start up any interfaces */ interfaces_requested = -1; } else if (!strcmp(argv[i], "-w")) { /* do not exit if there are no broadcast interfaces. */ persist = 1; } else if (!strcmp(argv[i], "-e")) { struct string_list *tmp; if (++i == argc) usage(use_noarg, argv[i-1]); tmp = dmalloc(strlen(argv[i]) + sizeof *tmp, MDL); if (!tmp) log_fatal("No memory for %s", argv[i]); strcpy(tmp->string, argv[i]); tmp->next = client_env; client_env = tmp; client_env_count++; #ifdef DHCPv6 } else if (!strcmp(argv[i], "-S")) { if (local_family_set && (local_family == AF_INET)) { usage(use_v6command, argv[i]); } local_family_set = 1; local_family = AF_INET6; wanted_ia_na = 0; stateless = 1; } else if (!strcmp(argv[i], "-N")) { if (local_family_set && (local_family == AF_INET)) { usage(use_v6command, argv[i]); } local_family_set = 1; local_family = AF_INET6; if (wanted_ia_na < 0) { wanted_ia_na = 0; } wanted_ia_na++; } else if (!strcmp(argv[i], "-T")) { if (local_family_set && (local_family == AF_INET)) { usage(use_v6command, argv[i]); } local_family_set = 1; local_family = AF_INET6; if (wanted_ia_na < 0) { wanted_ia_na = 0; } wanted_ia_ta++; } else if (!strcmp(argv[i], "-P")) { if (local_family_set && (local_family == AF_INET)) { usage(use_v6command, argv[i]); } local_family_set = 1; local_family = AF_INET6; if (wanted_ia_na < 0) { wanted_ia_na = 0; } wanted_ia_pd++; } else if (!strcmp(argv[i], "-R")) { if (local_family_set && (local_family == AF_INET)) { usage(use_v6command, argv[i]); } local_family_set = 1; local_family = AF_INET6; require_all_ias = 1; } else if (!strcmp(argv[i], "--dad-wait-time")) { if (++i == argc) { usage(use_noarg, argv[i-1]); } errno = 0; dad_wait_time = (int)strtol(argv[i], &s, 10); if (errno || (*s != '\0') || (dad_wait_time < 0)) { usage("Invalid value for --dad-wait-time: %s", argv[i]); } } else if (!strcmp(argv[i], "--prefix-len-hint")) { if (++i == argc) { usage(use_noarg, argv[i-1]); } errno = 0; prefix_len_hint = (int)strtol(argv[i], &s, 10); if (errno || (*s != '\0') || (prefix_len_hint < 0)) { usage("Invalid value for --prefix-len-hint: %s", argv[i]); } } else if (!strcmp(argv[i], "--address-prefix-len")) { if (++i == argc) { usage(use_noarg, argv[i-1]); } errno = 0; address_prefix_len = (int)strtol(argv[i], &s, 10); if (errno || (*s != '\0') || (address_prefix_len < 0)) { usage("Invalid value for" " --address-prefix-len: %s", argv[i]); } #endif /* DHCPv6 */ } else if (!strcmp(argv[i], "--decline-wait-time")) { if (++i == argc) { usage(use_noarg, argv[i-1]); } errno = 0; decline_wait_time = (int)strtol(argv[i], &s, 10); if (errno || (*s != '\0') || (decline_wait_time < 0)) { usage("Invalid value for " "--decline-wait-time: %s", argv[i]); } } else if (!strcmp(argv[i], "-D")) { duid_v4 = 1; if (++i == argc) usage(use_noarg, argv[i-1]); if (!strcasecmp(argv[i], "LL")) { duid_type = DUID_LL; } else if (!strcasecmp(argv[i], "LLT")) { duid_type = DUID_LLT; } else { usage("Unknown argument to -D: %s", argv[i]); } } else if (!strcmp(argv[i], "-i")) { /* enable DUID support for DHCPv4 clients */ duid_v4 = 1; } else if (!strcmp(argv[i], "-I")) { /* enable standard DHCID support for DDNS updates */ std_dhcid = 1; } else if (!strcmp(argv[i], "-v")) { quiet = 0; } else if (argv[i][0] == '-') { usage("Unknown command: %s", argv[i]); } else if (interfaces_requested < 0) { usage("No interfaces comamnd -n and " " requested interface %s", argv[i]); } else { struct interface_info *tmp = NULL; status = interface_allocate(&tmp, MDL); if (status != ISC_R_SUCCESS) log_fatal("Can't record interface %s:%s", argv[i], isc_result_totext(status)); if (strlen(argv[i]) >= sizeof(tmp->name)) log_fatal("%s: interface name too long (is %ld)", argv[i], (long)strlen(argv[i])); strcpy(tmp->name, argv[i]); if (interfaces) { interface_reference(&tmp->next, interfaces, MDL); interface_dereference(&interfaces, MDL); } interface_reference(&interfaces, tmp, MDL); tmp->flags = INTERFACE_REQUESTED; interfaces_requested++; } } if (wanted_ia_na < 0) { wanted_ia_na = 1; } /* Support only one (requested) interface for Prefix Delegation. */ if (wanted_ia_pd && (interfaces_requested != 1)) { usage("PD %s only supports one requested interface", "-P"); } #if defined(DHCPv6) && defined(DHCP4o6) if ((local_family == AF_INET6) && dhcpv4_over_dhcpv6 && (exit_mode || release_mode)) log_error("Can't relay DHCPv4-over-DHCPv6 " "without a persistent DHCPv6 client"); if ((local_family == AF_INET) && dhcpv4_over_dhcpv6 && (interfaces_requested != 1)) log_fatal("DHCPv4-over-DHCPv6 requires an explicit " "interface on which to be applied"); #endif if (!no_dhclient_conf && (s = getenv("PATH_DHCLIENT_CONF"))) { path_dhclient_conf = s; } if (!no_dhclient_db && (s = getenv("PATH_DHCLIENT_DB"))) { path_dhclient_db = s; } if (!no_dhclient_pid && (s = getenv("PATH_DHCLIENT_PID"))) { path_dhclient_pid = s; } if (!no_dhclient_script && (s = getenv("PATH_DHCLIENT_SCRIPT"))) { path_dhclient_script = s; } /* Set up the initial dhcp option universe. */ initialize_common_option_spaces(); /* Set up the initial client option universe. */ initialize_client_option_spaces(); /* Assign v4 or v6 specific running parameters. */ if (local_family == AF_INET) dhcpv4_client_assignments(); #ifdef DHCPv6 else if (local_family == AF_INET6) dhcpv6_client_assignments(); #endif /* DHCPv6 */ else log_fatal("Impossible condition at %s:%d.", MDL); /* * convert relative path names to absolute, for files that need * to be reopened after chdir() has been called */ if (path_dhclient_db[0] != '/') { path_dhclient_db = absolute_path(path_dhclient_db); } if (path_dhclient_script[0] != '/') { path_dhclient_script = absolute_path(path_dhclient_script); } /* * See if we should kill off any currently running client * we don't try to kill it off if the user told us not * to write a pid file - we assume they are controlling * the process in some other fashion. */ if ((release_mode || exit_mode) && (no_pid_file == ISC_FALSE)) { FILE *pidfd; pid_t oldpid; long temp; int e; if ((pidfd = fopen(path_dhclient_pid, "r")) != NULL) { e = fscanf(pidfd, "%ld\n", &temp); oldpid = (pid_t)temp; if (e != 0 && e != EOF && oldpid) { if (kill(oldpid, SIGTERM) == 0) { log_info("Killed old client process"); (void) unlink(path_dhclient_pid); /* * wait for the old process to * cleanly terminate. * Note kill() with sig=0 could * detect termination but only * the parent can be signaled... */ sleep(1); } else if (errno == ESRCH) { log_info("Removed stale PID file"); (void) unlink(path_dhclient_pid); } } fclose(pidfd); } } if (!quiet) { log_info("%s %s", message, PACKAGE_VERSION); log_info(copyright); log_info(arr); log_info(url); log_info("%s", ""); } else { log_perror = 0; quiet_interface_discovery = 1; } /* If we're given a relay agent address to insert, for testing purposes, figure out what it is. */ if (mockup_relay) { if (!inet_aton(mockup_relay, &giaddr)) { struct hostent *he; he = gethostbyname(mockup_relay); if (he) { memcpy(&giaddr, he->h_addr_list[0], sizeof giaddr); } else { log_fatal("%s: no such host", mockup_relay); } } } /* Get the current time... */ gettimeofday(&cur_tv, NULL); sockaddr_broadcast.sin_family = AF_INET; sockaddr_broadcast.sin_port = remote_port; if (server) { if (!inet_aton(server, &sockaddr_broadcast.sin_addr)) { struct hostent *he; he = gethostbyname(server); if (he) { memcpy(&sockaddr_broadcast.sin_addr, he->h_addr_list[0], sizeof sockaddr_broadcast.sin_addr); } else sockaddr_broadcast.sin_addr.s_addr = INADDR_BROADCAST; } } else { sockaddr_broadcast.sin_addr.s_addr = INADDR_BROADCAST; } inaddr_any.s_addr = INADDR_ANY; /* Stateless special case. */ if (stateless) { if (release_mode || (wanted_ia_na > 0) || wanted_ia_ta || wanted_ia_pd || (interfaces_requested != 1)) { usage("Stateless command: %s incompatibile with " "other commands", "-S"); } #if defined(DHCPv6) && defined(DHCP4o6) run_stateless(exit_mode, dhcp4o6_port); #else run_stateless(exit_mode, 0); #endif finish(0); } /* Discover all the network interfaces. */ discover_interfaces(DISCOVER_UNCONFIGURED); /* Parse the dhclient.conf file. */ read_client_conf(); /* Parse the lease database. */ read_client_leases(); /* If desired parse the secondary lease database for a DUID */ if ((default_duid.len == 0) && (path_dhclient_duid != NULL)) { read_client_duid(); } /* Rewrite the lease database... */ rewrite_client_leases(); /* XXX */ /* config_counter(&snd_counter, &rcv_counter); */ /* * If no broadcast interfaces were discovered, call the script * and tell it so. */ if (!interfaces) { /* * Call dhclient-script with the NBI flag, * in case somebody cares. */ script_init(NULL, "NBI", NULL); script_go(NULL); /* * If we haven't been asked to persist, waiting for new * interfaces, then just exit. */ if (!persist) { /* Nothing more to do. */ log_info("No broadcast interfaces found - exiting."); finish(0); } } else if (!release_mode && !exit_mode) { /* Call the script with the list of interfaces. */ for (ip = interfaces; ip; ip = ip->next) { /* * If interfaces were specified, don't configure * interfaces that weren't specified! */ if ((interfaces_requested > 0) && ((ip->flags & (INTERFACE_REQUESTED | INTERFACE_AUTOMATIC)) != INTERFACE_REQUESTED)) continue; if (local_family == AF_INET6) { script_init(ip->client, "PREINIT6", NULL); } else { script_init(ip->client, "PREINIT", NULL); if (ip->client->alias != NULL) script_write_params(ip->client, "alias_", ip->client->alias); } script_go(ip->client); } } /* At this point, all the interfaces that the script thinks are relevant should be running, so now we once again call discover_interfaces(), and this time ask it to actually set up the interfaces. */ discover_interfaces(interfaces_requested != 0 ? DISCOVER_REQUESTED : DISCOVER_RUNNING); /* Make up a seed for the random number generator from current time plus the sum of the last four bytes of each interface's hardware address interpreted as an integer. Not much entropy, but we're booting, so we're not likely to find anything better. */ seed = 0; for (ip = interfaces; ip; ip = ip->next) { int junk; memcpy(&junk, &ip->hw_address.hbuf[ip->hw_address.hlen - sizeof seed], sizeof seed); seed += junk; } srandom(seed + cur_time + (unsigned)getpid()); /* * Establish a default DUID. We always do so for v6 and * do so if desired for v4 via the -D or -i options */ if ((local_family == AF_INET6) || ((local_family == AF_INET) && (duid_v4 == 1))) { if (default_duid.len == 0) { if (default_duid.buffer != NULL) data_string_forget(&default_duid, MDL); form_duid(&default_duid, MDL); write_duid(&default_duid); } } #if defined(DHCPv6) && defined(DHCP4o6) if (dhcpv4_over_dhcpv6 && !exit_mode) dhcp4o6_setup(dhcp4o6_port); #endif /* Start a configuration state machine for each interface. */ #ifdef DHCPv6 if (local_family == AF_INET6) { for (ip = interfaces ; ip != NULL ; ip = ip->next) { for (client = ip->client ; client != NULL ; client = client->next) { if (release_mode) { start_release6(client); continue; } else if (exit_mode) { unconfigure6(client, "STOP6"); continue; } /* If we have a previous binding, Confirm * that we can (or can't) still use it. */ if ((client->active_lease != NULL) && !client->active_lease->released) start_confirm6(client); else start_init6(client); } } } else #endif /* DHCPv6 */ { for (ip = interfaces ; ip ; ip = ip->next) { ip->flags |= INTERFACE_RUNNING; for (client = ip->client ; client ; client = client->next) { if (exit_mode) state_stop(client); if (release_mode) do_release(client); else { client->state = S_INIT; if (top_level_config.initial_delay>0) { tv.tv_sec = 0; if (top_level_config. initial_delay>1) tv.tv_sec = cur_time + random() % (top_level_config. initial_delay-1); tv.tv_usec = random() % 1000000; /* * this gives better * distribution than just *whole seconds */ add_timeout(&tv, state_reboot, client, 0, 0); } else { state_reboot(client); } } } } } if (exit_mode) finish(0); if (release_mode) { #ifndef DHCPv6 finish(0); #else if ((local_family == AF_INET6) || dhcpv4_over_dhcpv6) { if (onetry) finish(0); } else finish(0); #endif /* DHCPv6 */ } /* Start up a listener for the object management API protocol. */ if (top_level_config.omapi_port != -1) { listener = NULL; result = omapi_generic_new(&listener, MDL); if (result != ISC_R_SUCCESS) log_fatal("Can't allocate new generic object: %s\n", isc_result_totext(result)); result = omapi_protocol_listen(listener, (unsigned) top_level_config.omapi_port, 1); if (result != ISC_R_SUCCESS) log_fatal("Can't start OMAPI protocol: %s", isc_result_totext (result)); } /* Set up the bootp packet handler... */ bootp_packet_handler = do_packet; #ifdef DHCPv6 dhcpv6_packet_handler = do_packet6; #endif /* DHCPv6 */ #if defined(DEBUG_MEMORY_LEAKAGE) || defined(DEBUG_MALLOC_POOL) || \ defined(DEBUG_MEMORY_LEAKAGE_ON_EXIT) dmalloc_cutoff_generation = dmalloc_generation; dmalloc_longterm = dmalloc_outstanding; dmalloc_outstanding = 0; #endif #if defined(ENABLE_GENTLE_SHUTDOWN) /* no signal handlers until we deal with the side effects */ /* install signal handlers */ signal(SIGINT, dhcp_signal_handler); /* control-c */ signal(SIGTERM, dhcp_signal_handler); /* kill */ #endif /* If we're not supposed to wait before getting the address, don't. */ if (nowait) detach(); /* If we're not going to daemonize, write the pid file now. */ if (no_daemon || nowait) write_client_pid_file(); /* Start dispatching packets and timeouts... */ dispatch(); /* In fact dispatch() never returns. */ return 0; } /* * \brief Run the DHCPv6 stateless client (dhclient -6 -S) * * \param exist_mode set to 1 when dhclient was called with -x * \param port DHCPv4-over-DHCPv6 client inter-process communication * UDP port pair (port,port+1 with port in network byte order) */ void run_stateless(int exit_mode, u_int16_t port) { #ifdef DHCPv6 struct client_state *client; omapi_object_t *listener; isc_result_t result; #ifndef DHCP4o6 IGNORE_UNUSED(port); #endif /* Discover the network interface. */ discover_interfaces(DISCOVER_REQUESTED); if (!interfaces) usage("No interfaces available for stateless command: %s", "-S"); /* Parse the dhclient.conf file. */ #ifdef DHCP4o6 if (dhcpv4_over_dhcpv6) { /* Mark we want to request IRT too! */ dhcpv4_over_dhcpv6++; } #endif read_client_conf(); /* Parse the lease database. */ read_client_leases(); /* If desired parse the secondary lease database for a DUID */ if ((default_duid.len == 0) && (path_dhclient_duid != NULL)) { read_client_duid(); } /* Establish a default DUID. */ if (default_duid.len == 0) { if (default_duid.buffer != NULL) data_string_forget(&default_duid, MDL); form_duid(&default_duid, MDL); } #ifdef DHCP4o6 if (dhcpv4_over_dhcpv6 && !exit_mode) dhcp4o6_setup(port); #endif /* Start a configuration state machine. */ for (client = interfaces->client ; client != NULL ; client = client->next) { if (exit_mode) { unconfigure6(client, "STOP6"); continue; } start_info_request6(client); } if (exit_mode) return; /* Start up a listener for the object management API protocol. */ if (top_level_config.omapi_port != -1) { listener = NULL; result = omapi_generic_new(&listener, MDL); if (result != ISC_R_SUCCESS) log_fatal("Can't allocate new generic object: %s\n", isc_result_totext(result)); result = omapi_protocol_listen(listener, (unsigned) top_level_config.omapi_port, 1); if (result != ISC_R_SUCCESS) log_fatal("Can't start OMAPI protocol: %s", isc_result_totext(result)); } /* Set up the packet handler... */ dhcpv6_packet_handler = do_packet6; #if defined(DEBUG_MEMORY_LEAKAGE) || defined(DEBUG_MALLOC_POOL) || \ defined(DEBUG_MEMORY_LEAKAGE_ON_EXIT) dmalloc_cutoff_generation = dmalloc_generation; dmalloc_longterm = dmalloc_outstanding; dmalloc_outstanding = 0; #endif /* If we're not supposed to wait before getting the address, don't. */ if (nowait) detach(); /* If we're not going to daemonize, write the pid file now. */ if (no_daemon || nowait) write_client_pid_file(); /* Start dispatching packets and timeouts... */ dispatch(); #endif /* DHCPv6 */ return; } #endif /* !UNIT_TEST */ isc_result_t find_class (struct class **c, const char *s, const char *file, int line) { return 0; } int check_collection (packet, lease, collection) struct packet *packet; struct lease *lease; struct collection *collection; { return 0; } void classify (packet, class) struct packet *packet; struct class *class; { } void unbill_class (lease) struct lease *lease; { } int find_subnet (struct subnet **sp, struct iaddr addr, const char *file, int line) { return 0; } /* Individual States: * * Each routine is called from the dhclient_state_machine() in one of * these conditions: * -> entering INIT state * -> recvpacket_flag == 0: timeout in this state * -> otherwise: received a packet in this state * * Return conditions as handled by dhclient_state_machine(): * Returns 1, sendpacket_flag = 1: send packet, reset timer. * Returns 1, sendpacket_flag = 0: just reset the timer (wait for a milestone). * Returns 0: finish the nap which was interrupted for no good reason. * * Several per-interface variables are used to keep track of the process: * active_lease: the lease that is being used on the interface * (null pointer if not configured yet). * offered_leases: leases corresponding to DHCPOFFER messages that have * been sent to us by DHCP servers. * acked_leases: leases corresponding to DHCPACK messages that have been * sent to us by DHCP servers. * sendpacket: DHCP packet we're trying to send. * destination: IP address to send sendpacket to * In addition, there are several relevant per-lease variables. * T1_expiry, T2_expiry, lease_expiry: lease milestones * In the active lease, these control the process of renewing the lease; * In leases on the acked_leases list, this simply determines when we * can no longer legitimately use the lease. */ void state_reboot (cpp) void *cpp; { struct client_state *client = cpp; #if defined(DHCPv6) && defined(DHCP4o6) if (dhcpv4_over_dhcpv6 && (dhcp4o6_state <= 0)) { if (dhcp4o6_state < 0) dhcp4o6_poll(NULL); client->pending = P_REBOOT; return; } #endif client->pending= P_NONE; /* If we don't remember an active lease, go straight to INIT. */ if (!client -> active || client -> active -> is_bootp || client -> active -> expiry <= cur_time) { state_init (client); return; } /* We are in the rebooting state. */ client -> state = S_REBOOTING; /* * make_request doesn't initialize xid because it normally comes * from the DHCPDISCOVER, but we haven't sent a DHCPDISCOVER, * so pick an xid now. */ client -> xid = random (); /* * Make a DHCPREQUEST packet, and set * appropriate per-interface flags. */ make_request (client, client -> active); client -> destination = iaddr_broadcast; client -> first_sending = cur_time; client -> interval = client -> config -> initial_interval; /* Zap the medium list... */ client -> medium = NULL; /* Send out the first DHCPREQUEST packet. */ send_request (client); } /* Called when a lease has completely expired and we've been unable to renew it. */ void state_init (cpp) void *cpp; { struct client_state *client = cpp; ASSERT_STATE(state, S_INIT); /* Make a DHCPDISCOVER packet, and set appropriate per-interface flags. */ make_discover (client, client -> active); client -> xid = client -> packet.xid; client -> destination = iaddr_broadcast; client -> state = S_SELECTING; client -> first_sending = cur_time; client -> interval = client -> config -> initial_interval; /* Add an immediate timeout to cause the first DHCPDISCOVER packet to go out. */ send_discover (client); } /* * state_selecting is called when one or more DHCPOFFER packets have been * received and a configurable period of time has passed. */ void state_selecting (cpp) void *cpp; { struct client_state *client = cpp; struct client_lease *lp, *next, *picked; ASSERT_STATE(state, S_SELECTING); /* * Cancel state_selecting and send_discover timeouts, since either * one could have got us here. */ cancel_timeout (state_selecting, client); cancel_timeout (send_discover, client); /* * We have received one or more DHCPOFFER packets. Currently, * the only criterion by which we judge leases is whether or * not we get a response when we arp for them. */ picked = NULL; for (lp = client -> offered_leases; lp; lp = next) { next = lp -> next; /* * Check to see if we got an ARPREPLY for the address * in this particular lease. */ if (!picked) { picked = lp; picked -> next = NULL; } else { destroy_client_lease (lp); } } client -> offered_leases = NULL; /* * If we just tossed all the leases we were offered, go back * to square one. */ if (!picked) { client -> state = S_INIT; state_init (client); return; } /* If it was a BOOTREPLY, we can just take the address right now. */ if (picked -> is_bootp) { client -> new = picked; /* Make up some lease expiry times XXX these should be configurable. */ client -> new -> expiry = cur_time + 12000; client -> new -> renewal += cur_time + 8000; client -> new -> rebind += cur_time + 10000; client -> state = S_REQUESTING; /* Bind to the address we received. */ bind_lease (client); return; } /* Go to the REQUESTING state. */ client -> destination = iaddr_broadcast; client -> state = S_REQUESTING; client -> first_sending = cur_time; client -> interval = client -> config -> initial_interval; /* Make a DHCPREQUEST packet from the lease we picked. */ make_request (client, picked); client -> xid = client -> packet.xid; /* Toss the lease we picked - we'll get it back in a DHCPACK. */ destroy_client_lease (picked); /* Add an immediate timeout to send the first DHCPREQUEST packet. */ send_request (client); } /* state_requesting is called when we receive a DHCPACK message after having sent out one or more DHCPREQUEST packets. */ void dhcpack (packet) struct packet *packet; { struct interface_info *ip = packet -> interface; struct client_state *client; struct client_lease *lease; struct option_cache *oc; struct data_string ds; /* If we're not receptive to an offer right now, or if the offer has an unrecognizable transaction id, then just drop it. */ for (client = ip -> client; client; client = client -> next) { if (client -> xid == packet -> raw -> xid) break; } if (!client || (packet -> interface -> hw_address.hlen - 1 != packet -> raw -> hlen) || (memcmp (&packet -> interface -> hw_address.hbuf [1], packet -> raw -> chaddr, packet -> raw -> hlen))) { #if defined (DEBUG) log_debug ("DHCPACK in wrong transaction."); #endif return; } if (client -> state != S_REBOOTING && client -> state != S_REQUESTING && client -> state != S_RENEWING && client -> state != S_REBINDING) { #if defined (DEBUG) log_debug ("DHCPACK in wrong state."); #endif return; } log_info ("DHCPACK of %s from %s", inet_ntoa(packet->raw->yiaddr), piaddr (packet->client_addr)); lease = packet_to_lease (packet, client); if (!lease) { log_info ("packet_to_lease failed."); return; } client -> new = lease; /* Stop resending DHCPREQUEST. */ cancel_timeout (send_request, client); /* Figure out the lease time. */ oc = lookup_option (&dhcp_universe, client -> new -> options, DHO_DHCP_LEASE_TIME); memset (&ds, 0, sizeof ds); if (oc && evaluate_option_cache (&ds, packet, (struct lease *)0, client, packet -> options, client -> new -> options, &global_scope, oc, MDL)) { if (ds.len > 3) client -> new -> expiry = getULong (ds.data); else client -> new -> expiry = 0; data_string_forget (&ds, MDL); } else client -> new -> expiry = 0; if (client->new->expiry == 0) { struct timeval tv; log_error ("no expiry time on offered lease."); /* Quench this (broken) server. Return to INIT to reselect. */ add_reject(packet); /* 1/2 second delay to restart at INIT. */ tv.tv_sec = cur_tv.tv_sec; tv.tv_usec = cur_tv.tv_usec + 500000; if (tv.tv_usec >= 1000000) { tv.tv_sec++; tv.tv_usec -= 1000000; } add_timeout(&tv, state_init, client, 0, 0); return; } /* * A number that looks negative here is really just very large, * because the lease expiry offset is unsigned. */ if (client->new->expiry < 0) client->new->expiry = TIME_MAX; /* Take the server-provided renewal time if there is one. */ oc = lookup_option (&dhcp_universe, client -> new -> options, DHO_DHCP_RENEWAL_TIME); if (oc && evaluate_option_cache (&ds, packet, (struct lease *)0, client, packet -> options, client -> new -> options, &global_scope, oc, MDL)) { if (ds.len > 3) client -> new -> renewal = getULong (ds.data); else client -> new -> renewal = 0; data_string_forget (&ds, MDL); } else client -> new -> renewal = 0; /* If it wasn't specified by the server, calculate it. */ if (!client -> new -> renewal) client -> new -> renewal = client -> new -> expiry / 2 + 1; if (client -> new -> renewal <= 0) client -> new -> renewal = TIME_MAX; /* Now introduce some randomness to the renewal time: */ if (client->new->renewal <= ((TIME_MAX / 3) - 3)) client->new->renewal = (((client->new->renewal * 3) + 3) / 4) + (((random() % client->new->renewal) + 3) / 4); /* Same deal with the rebind time. */ oc = lookup_option (&dhcp_universe, client -> new -> options, DHO_DHCP_REBINDING_TIME); if (oc && evaluate_option_cache (&ds, packet, (struct lease *)0, client, packet -> options, client -> new -> options, &global_scope, oc, MDL)) { if (ds.len > 3) client -> new -> rebind = getULong (ds.data); else client -> new -> rebind = 0; data_string_forget (&ds, MDL); } else client -> new -> rebind = 0; if (client -> new -> rebind <= 0) { if (client -> new -> expiry <= TIME_MAX / 7) client -> new -> rebind = client -> new -> expiry * 7 / 8; else client -> new -> rebind = client -> new -> expiry / 8 * 7; } /* Make sure our randomness didn't run the renewal time past the rebind time. */ if (client -> new -> renewal > client -> new -> rebind) { if (client -> new -> rebind <= TIME_MAX / 3) client -> new -> renewal = client -> new -> rebind * 3 / 4; else client -> new -> renewal = client -> new -> rebind / 4 * 3; } client -> new -> expiry += cur_time; /* Lease lengths can never be negative. */ if (client -> new -> expiry < cur_time) client -> new -> expiry = TIME_MAX; client -> new -> renewal += cur_time; if (client -> new -> renewal < cur_time) client -> new -> renewal = TIME_MAX; client -> new -> rebind += cur_time; if (client -> new -> rebind < cur_time) client -> new -> rebind = TIME_MAX; bind_lease (client); } void bind_lease (client) struct client_state *client; { struct timeval tv; /* Remember the medium. */ client->new->medium = client->medium; /* Run the client script with the new parameters. */ script_init(client, (client->state == S_REQUESTING ? "BOUND" : (client->state == S_RENEWING ? "RENEW" : (client->state == S_REBOOTING ? "REBOOT" : "REBIND"))), client->new->medium); if (client->active && client->state != S_REBOOTING) script_write_params(client, "old_", client->active); script_write_params(client, "new_", client->new); script_write_requested(client); if (client->alias) script_write_params(client, "alias_", client->alias); /* If the BOUND/RENEW code detects another machine using the offered address, it exits nonzero. We need to send a DHCPDECLINE and toss the lease. */ if (script_go(client)) { make_decline(client, client->new); send_decline(client); destroy_client_lease(client->new); client->new = NULL; if (onetry) { if (!quiet) { log_info("Unable to obtain a lease on first " "try (declined). Exiting."); } #if defined (CALL_SCRIPT_ON_ONETRY_FAIL) /* Let's call a script and we're done */ script_init(client, "FAIL", (struct string_list *)0); script_go(client); #endif finish(2); } else { struct timeval tv; tv.tv_sec = cur_tv.tv_sec + decline_wait_time; tv.tv_usec = cur_tv.tv_usec; add_timeout(&tv, state_init, client, 0, 0); return; } } /* Write out the new lease if it has been long enough. */ if (!client->last_write || (cur_time - client->last_write) >= MIN_LEASE_WRITE) write_client_lease(client, client->new, 0, 1); /* Replace the old active lease with the new one. */ if (client->active) destroy_client_lease(client->active); client->active = client->new; client->new = NULL; /* Set up a timeout to start the renewal process. */ tv.tv_sec = client->active->renewal; tv.tv_usec = ((client->active->renewal - cur_tv.tv_sec) > 1) ? random() % 1000000 : cur_tv.tv_usec; add_timeout(&tv, state_bound, client, 0, 0); log_info("bound to %s -- renewal in %ld seconds.", piaddr(client->active->address), (long)(client->active->renewal - cur_time)); client->state = S_BOUND; reinitialize_interfaces(); detach(); #if defined (NSUPDATE) if (client->config->do_forward_update) dhclient_schedule_updates(client, &client->active->address, 1); #endif } /* state_bound is called when we've successfully bound to a particular lease, but the renewal time on that lease has expired. We are expected to unicast a DHCPREQUEST to the server that gave us our original lease. */ void state_bound (cpp) void *cpp; { struct client_state *client = cpp; struct option_cache *oc; struct data_string ds; ASSERT_STATE(state, S_BOUND); /* T1 has expired. */ make_request (client, client -> active); client -> xid = client -> packet.xid; memset (&ds, 0, sizeof ds); oc = lookup_option (&dhcp_universe, client -> active -> options, DHO_DHCP_SERVER_IDENTIFIER); if (oc && evaluate_option_cache (&ds, (struct packet *)0, (struct lease *)0, client, (struct option_state *)0, client -> active -> options, &global_scope, oc, MDL)) { if (ds.len > 3) { memcpy (client -> destination.iabuf, ds.data, 4); client -> destination.len = 4; } else client -> destination = iaddr_broadcast; data_string_forget (&ds, MDL); } else client -> destination = iaddr_broadcast; client -> first_sending = cur_time; client -> interval = client -> config -> initial_interval; client -> state = S_RENEWING; /* Send the first packet immediately. */ send_request (client); } /* state_stop is called when we've been told to shut down. We unconfigure the interfaces, and then stop operating until told otherwise. */ void state_stop (cpp) void *cpp; { struct client_state *client = cpp; client->pending = P_NONE; /* Cancel all timeouts. */ cancel_timeout(state_selecting, client); cancel_timeout(send_discover, client); cancel_timeout(send_request, client); cancel_timeout(state_bound, client); /* If we have an address, unconfigure it. */ if (client->active) { script_init(client, "STOP", client->active->medium); script_write_params(client, "old_", client->active); script_write_requested(client); if (client->alias) script_write_params(client, "alias_", client->alias); script_go(client); } } int commit_leases () { return 0; } int write_lease (lease) struct lease *lease; { return 0; } int write_host (host) struct host_decl *host; { return 0; } void db_startup (testp) int testp; { } void bootp (packet) struct packet *packet; { struct iaddrmatchlist *ap; char addrbuf[4*16]; char maskbuf[4*16]; if (packet -> raw -> op != BOOTREPLY) return; /* If there's a reject list, make sure this packet's sender isn't on it. */ for (ap = packet -> interface -> client -> config -> reject_list; ap; ap = ap -> next) { if (addr_match(&packet->client_addr, &ap->match)) { /* piaddr() returns its result in a static buffer sized 4*16 (see common/inet.c). */ strcpy(addrbuf, piaddr(ap->match.addr)); strcpy(maskbuf, piaddr(ap->match.mask)); log_info("BOOTREPLY from %s rejected by rule %s " "mask %s.", piaddr(packet->client_addr), addrbuf, maskbuf); return; } } dhcpoffer (packet); } void dhcp (packet) struct packet *packet; { struct iaddrmatchlist *ap; void (*handler) (struct packet *); const char *type; char addrbuf[4*16]; char maskbuf[4*16]; switch (packet -> packet_type) { case DHCPOFFER: handler = dhcpoffer; type = "DHCPOFFER"; break; case DHCPNAK: handler = dhcpnak; type = "DHCPNACK"; break; case DHCPACK: handler = dhcpack; type = "DHCPACK"; break; default: return; } /* If there's a reject list, make sure this packet's sender isn't on it. */ for (ap = packet -> interface -> client -> config -> reject_list; ap; ap = ap -> next) { if (addr_match(&packet->client_addr, &ap->match)) { /* piaddr() returns its result in a static buffer sized 4*16 (see common/inet.c). */ strcpy(addrbuf, piaddr(ap->match.addr)); strcpy(maskbuf, piaddr(ap->match.mask)); log_info("%s from %s rejected by rule %s mask %s.", type, piaddr(packet->client_addr), addrbuf, maskbuf); return; } } (*handler) (packet); } #ifdef DHCPv6 void dhcpv6(struct packet *packet) { struct iaddrmatchlist *ap; struct client_state *client; char addrbuf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")]; /* Silently drop bogus messages. */ if (packet->dhcpv6_msg_type >= dhcpv6_type_name_max) return; /* Discard, with log, packets from quenched sources. */ for (ap = packet->interface->client->config->reject_list ; ap ; ap = ap->next) { if (addr_match(&packet->client_addr, &ap->match)) { strcpy(addrbuf, piaddr(packet->client_addr)); log_info("%s from %s rejected by rule %s", dhcpv6_type_names[packet->dhcpv6_msg_type], addrbuf, piaddrmask(&ap->match.addr, &ap->match.mask)); return; } } /* Screen out nonsensical messages. */ switch(packet->dhcpv6_msg_type) { #ifdef DHCP4o6 case DHCPV6_DHCPV4_RESPONSE: if (dhcpv4_over_dhcpv6) { log_info("RCV: %s message on %s from %s.", dhcpv6_type_names[packet->dhcpv6_msg_type], packet->interface->name, piaddr(packet->client_addr)); forw_dhcpv4_response(packet); } return; #endif case DHCPV6_ADVERTISE: case DHCPV6_RECONFIGURE: if (stateless) return; /* Falls through */ case DHCPV6_REPLY: log_info("RCV: %s message on %s from %s.", dhcpv6_type_names[packet->dhcpv6_msg_type], packet->interface->name, piaddr(packet->client_addr)); break; default: return; } /* Find a client state that matches the incoming XID. */ for (client = packet->interface->client ; client ; client = client->next) { if (memcmp(&client->dhcpv6_transaction_id, packet->dhcpv6_transaction_id, 3) == 0) { client->v6_handler(packet, client); return; } } /* XXX: temporary log for debugging */ log_info("Packet received, but nothing done with it."); } #ifdef DHCP4o6 /* * \brief Forward a DHCPv4-response to the DHCPv4 client. * (DHCPv6 client function) * * The DHCPv6 client receives a DHCPv4-response which is forwarded * to the DHCPv4 client. * Format: address:16 + DHCPv4 message content * (we have no state to keep the address so it is transported in * DHCPv6 <-> DHCPv6 inter-process messages) * * \param packet the DHCPv4-response packet */ static void forw_dhcpv4_response(struct packet *packet) { struct option_cache *oc; struct data_string enc_opt_data; struct data_string ds; int cc; /* * Discard if relay is not ready. */ if (dhcp4o6_state == -1) { log_info("forw_dhcpv4_response: not ready."); return; } if (packet->client_addr.len != 16) { log_error("forw_dhcpv4_response: bad address"); return; } /* * Get our encapsulated DHCPv4 message. */ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_DHCPV4_MSG); if (oc == NULL) { log_info("DHCPv4-response from %s missing " "DHCPv4 Message option.", piaddr(packet->client_addr)); return; } memset(&enc_opt_data, 0, sizeof(enc_opt_data)); if (!evaluate_option_cache(&enc_opt_data, NULL, NULL, NULL, NULL, NULL, &global_scope, oc, MDL)) { log_error("forw_dhcpv4_response: error evaluating " "DHCPv4 message."); data_string_forget(&enc_opt_data, MDL); return; } if (enc_opt_data.len < DHCP_FIXED_NON_UDP) { log_error("forw_dhcpv4_response: " "no memory for encapsulated packet."); data_string_forget(&enc_opt_data, MDL); return; } /* * Append address. */ memset(&ds, 0, sizeof(ds)); if (!buffer_allocate(&ds.buffer, enc_opt_data.len + 16, MDL)) { log_error("forw_dhcpv4_response: no memory buffer."); data_string_forget(&enc_opt_data, MDL); return; } ds.data = ds.buffer->data; ds.len = enc_opt_data.len + 16; memcpy(ds.buffer->data, enc_opt_data.data, enc_opt_data.len); memcpy(ds.buffer->data + enc_opt_data.len, packet->client_addr.iabuf, 16); data_string_forget(&enc_opt_data, MDL); /* * Forward them. */ cc = send(dhcp4o6_fd, ds.data, ds.len, 0); if (cc < 0) log_error("forw_dhcpv4_response: send(): %m"); data_string_forget(&ds, MDL); } /* * \brief Receive a DHCPv4-response from the DHCPv6 client. * (DHCPv4 client function) * * The DHCPv4 client receives a DHCPv4-response forwarded * by the DHCPv6 client (using \ref forw_dhcpv4_response()) * * \param raw the DHCPv4-response raw packet */ static void recv_dhcpv4_response(struct data_string *raw) { struct packet *packet; struct iaddr from; if (interfaces == NULL) { log_error("recv_dhcpv4_response: no interfaces."); return; } from.len = 16; memcpy(from.iabuf, raw->data + (raw->len - 16), 16); /* * Build a packet structure. */ packet = NULL; if (!packet_allocate(&packet, MDL)) { log_error("recv_dhcpv4_response: no memory for packet."); return; } packet->raw = (struct dhcp_packet *) raw->data; packet->packet_length = raw->len - 16; packet->client_port = remote_port; packet->client_addr = from; interface_reference(&packet->interface, interfaces, MDL); /* Allocate packet->options now so it is non-null for all packets */ if (!option_state_allocate (&packet->options, MDL)) { log_error("recv_dhcpv4_response: no memory for options."); packet_dereference (&packet, MDL); return; } /* If there's an option buffer, try to parse it. */ if (packet->packet_length >= DHCP_FIXED_NON_UDP + 4) { struct option_cache *op; if (!parse_options(packet)) { if (packet->options) option_state_dereference (&packet->options, MDL); packet_dereference (&packet, MDL); return; } if (packet->options_valid && (op = lookup_option(&dhcp_universe, packet->options, DHO_DHCP_MESSAGE_TYPE))) { struct data_string dp; memset(&dp, 0, sizeof dp); evaluate_option_cache(&dp, packet, NULL, NULL, packet->options, NULL, NULL, op, MDL); if (dp.len > 0) packet->packet_type = dp.data[0]; else packet->packet_type = 0; data_string_forget(&dp, MDL); } } if (validate_packet(packet) != 0) { if (packet->packet_type) dhcp(packet); else bootp(packet); } /* If the caller kept the packet, they'll have upped the refcnt. */ packet_dereference(&packet, MDL); } #endif /* DHCP4o6 */ #endif /* DHCPv6 */ void dhcpoffer (packet) struct packet *packet; { struct interface_info *ip = packet -> interface; struct client_state *client; struct client_lease *lease, *lp; struct option **req; int i; int stop_selecting; const char *name = packet -> packet_type ? "DHCPOFFER" : "BOOTREPLY"; char obuf [1024]; struct timeval tv; #ifdef DEBUG_PACKET dump_packet (packet); #endif /* Find a client state that matches the xid... */ for (client = ip -> client; client; client = client -> next) if (client -> xid == packet -> raw -> xid) break; /* If we're not receptive to an offer right now, or if the offer has an unrecognizable transaction id, then just drop it. */ if (!client || client -> state != S_SELECTING || (packet -> interface -> hw_address.hlen - 1 != packet -> raw -> hlen) || (memcmp (&packet -> interface -> hw_address.hbuf [1], packet -> raw -> chaddr, packet -> raw -> hlen))) { #if defined (DEBUG) log_debug ("%s in wrong transaction.", name); #endif return; } sprintf (obuf, "%s of %s from %s", name, inet_ntoa(packet->raw->yiaddr), piaddr(packet->client_addr)); /* If this lease doesn't supply the minimum required DHCPv4 parameters, * ignore it. */ req = client->config->required_options; if (req != NULL) { for (i = 0 ; req[i] != NULL ; i++) { if ((req[i]->universe == &dhcp_universe) && !lookup_option(&dhcp_universe, packet->options, req[i]->code)) { struct option *option = NULL; unsigned code = req[i]->code; option_code_hash_lookup(&option, dhcp_universe.code_hash, &code, 0, MDL); if (option) log_info("%s: no %s option.", obuf, option->name); else log_info("%s: no unknown-%u option.", obuf, code); option_dereference(&option, MDL); return; } } } /* If we've already seen this lease, don't record it again. */ for (lease = client -> offered_leases; lease; lease = lease -> next) { if (lease -> address.len == sizeof packet -> raw -> yiaddr && !memcmp (lease -> address.iabuf, &packet -> raw -> yiaddr, lease -> address.len)) { log_debug ("%s: already seen.", obuf); return; } } lease = packet_to_lease (packet, client); if (!lease) { log_info ("%s: packet_to_lease failed.", obuf); return; } /* log it now, so it emits before the request goes out */ log_info("%s", obuf); /* If this lease was acquired through a BOOTREPLY, record that fact. */ if (!packet -> options_valid || !packet -> packet_type) lease -> is_bootp = 1; /* Record the medium under which this lease was offered. */ lease -> medium = client -> medium; /* Figure out when we're supposed to stop selecting. */ stop_selecting = (client -> first_sending + client -> config -> select_interval); /* If this is the lease we asked for, put it at the head of the list, and don't mess with the arp request timeout. */ if (lease -> address.len == client -> requested_address.len && !memcmp (lease -> address.iabuf, client -> requested_address.iabuf, client -> requested_address.len)) { lease -> next = client -> offered_leases; client -> offered_leases = lease; } else { /* Put the lease at the end of the list. */ lease -> next = (struct client_lease *)0; if (!client -> offered_leases) client -> offered_leases = lease; else { for (lp = client -> offered_leases; lp -> next; lp = lp -> next) ; lp -> next = lease; } } /* If the selecting interval has expired, go immediately to state_selecting(). Otherwise, time out into state_selecting at the select interval. */ if (stop_selecting <= cur_tv.tv_sec) state_selecting (client); else { tv.tv_sec = stop_selecting; tv.tv_usec = cur_tv.tv_usec; add_timeout(&tv, state_selecting, client, 0, 0); cancel_timeout(send_discover, client); } } /* Allocate a client_lease structure and initialize it from the parameters in the specified packet. */ struct client_lease *packet_to_lease (packet, client) struct packet *packet; struct client_state *client; { struct client_lease *lease; unsigned i; struct option_cache *oc; struct option *option = NULL; struct data_string data; lease = (struct client_lease *)new_client_lease (MDL); if (!lease) { log_error("packet_to_lease: no memory to record lease.\n"); return NULL; } memset(lease, 0, sizeof(*lease)); /* Copy the lease options. */ option_state_reference(&lease->options, packet->options, MDL); lease->address.len = sizeof(packet->raw->yiaddr); memcpy(lease->address.iabuf, &packet->raw->yiaddr, lease->address.len); lease->next_srv_addr.len = sizeof(packet->raw->siaddr); memcpy(lease->next_srv_addr.iabuf, &packet->raw->siaddr, lease->next_srv_addr.len); memset(&data, 0, sizeof(data)); if (client -> config -> vendor_space_name) { i = DHO_VENDOR_ENCAPSULATED_OPTIONS; /* See if there was a vendor encapsulation option. */ oc = lookup_option (&dhcp_universe, lease -> options, i); if (oc && client -> config -> vendor_space_name && evaluate_option_cache (&data, packet, (struct lease *)0, client, packet -> options, lease -> options, &global_scope, oc, MDL)) { if (data.len) { if (!option_code_hash_lookup(&option, dhcp_universe.code_hash, &i, 0, MDL)) log_fatal("Unable to find VENDOR " "option (%s:%d).", MDL); parse_encapsulated_suboptions (packet -> options, option, data.data, data.len, &dhcp_universe, client -> config -> vendor_space_name ); option_dereference(&option, MDL); } data_string_forget (&data, MDL); } } else i = 0; /* Figure out the overload flag. */ oc = lookup_option (&dhcp_universe, lease -> options, DHO_DHCP_OPTION_OVERLOAD); if (oc && evaluate_option_cache (&data, packet, (struct lease *)0, client, packet -> options, lease -> options, &global_scope, oc, MDL)) { if (data.len > 0) i = data.data [0]; else i = 0; data_string_forget (&data, MDL); } else i = 0; /* If the server name was filled out, copy it. */ if (!(i & 2) && packet -> raw -> sname [0]) { unsigned len; /* Don't count on the NUL terminator. */ for (len = 0; len < DHCP_SNAME_LEN; len++) if (!packet -> raw -> sname [len]) break; lease -> server_name = dmalloc (len + 1, MDL); if (!lease -> server_name) { log_error ("dhcpoffer: no memory for server name.\n"); destroy_client_lease (lease); return (struct client_lease *)0; } else { memcpy (lease -> server_name, packet -> raw -> sname, len); lease -> server_name [len] = 0; } } /* Ditto for the filename. */ if (!(i & 1) && packet -> raw -> file [0]) { unsigned len; /* Don't count on the NUL terminator. */ for (len = 0; len < DHCP_FILE_LEN; len++) if (!packet -> raw -> file [len]) break; lease -> filename = dmalloc (len + 1, MDL); if (!lease -> filename) { log_error ("dhcpoffer: no memory for filename.\n"); destroy_client_lease (lease); return (struct client_lease *)0; } else { memcpy (lease -> filename, packet -> raw -> file, len); lease -> filename [len] = 0; } } execute_statements_in_scope(NULL, (struct packet *)packet, NULL, client, lease->options, lease->options, &global_scope, client->config->on_receipt, NULL, NULL); return lease; } void dhcpnak (packet) struct packet *packet; { struct interface_info *ip = packet -> interface; struct client_state *client; /* Find a client state that matches the xid... */ for (client = ip -> client; client; client = client -> next) if (client -> xid == packet -> raw -> xid) break; /* If we're not receptive to an offer right now, or if the offer has an unrecognizable transaction id, then just drop it. */ if (!client || (packet -> interface -> hw_address.hlen - 1 != packet -> raw -> hlen) || (memcmp (&packet -> interface -> hw_address.hbuf [1], packet -> raw -> chaddr, packet -> raw -> hlen))) { #if defined (DEBUG) log_debug ("DHCPNAK in wrong transaction."); #endif return; } if (client -> state != S_REBOOTING && client -> state != S_REQUESTING && client -> state != S_RENEWING && client -> state != S_REBINDING) { #if defined (DEBUG) log_debug ("DHCPNAK in wrong state."); #endif return; } log_info ("DHCPNAK from %s", piaddr (packet -> client_addr)); if (!client -> active) { #if defined (DEBUG) log_info ("DHCPNAK with no active lease.\n"); #endif return; } /* If we get a DHCPNAK, we use the EXPIRE dhclient-script state * to indicate that we want all old bindings to be removed. (It * is possible that we may get a NAK while in the RENEW state, * so we might have bindings active at that time) */ script_init(client, "EXPIRE", NULL); script_write_params(client, "old_", client->active); script_write_requested(client); if (client->alias) script_write_params(client, "alias_", client->alias); script_go(client); destroy_client_lease (client -> active); client -> active = (struct client_lease *)0; /* Stop sending DHCPREQUEST packets... */ cancel_timeout (send_request, client); /* On some scripts, 'EXPIRE' causes the interface to be ifconfig'd * down (this expunges any routes and arp cache). This makes the * interface unusable by state_init(), which we call next. So, we * need to 'PREINIT' the interface to bring it back up. */ script_init(client, "PREINIT", NULL); if (client->alias) script_write_params(client, "alias_", client->alias); script_go(client); client -> state = S_INIT; state_init (client); } /* Send out a DHCPDISCOVER packet, and set a timeout to send out another one after the right interval has expired. If we don't get an offer by the time we reach the panic interval, call the panic function. */ void send_discover (cpp) void *cpp; { struct client_state *client = cpp; int result; int interval; int increase = 1; struct timeval tv; /* Figure out how long it's been since we started transmitting. */ interval = cur_time - client -> first_sending; /* If we're past the panic timeout, call the script and tell it we haven't found anything for this interface yet. */ if (interval > client -> config -> timeout) { state_panic (client); return; } /* If we're selecting media, try the whole list before doing the exponential backoff, but if we've already received an offer, stop looping, because we obviously have it right. */ if (!client -> offered_leases && client -> config -> media) { int fail = 0; again: if (client -> medium) { client -> medium = client -> medium -> next; increase = 0; } if (!client -> medium) { if (fail) log_fatal ("No valid media types for %s!", client -> interface -> name); client -> medium = client -> config -> media; increase = 1; } log_info ("Trying medium \"%s\" %d", client -> medium -> string, increase); script_init(client, "MEDIUM", client -> medium); if (script_go(client)) { fail = 1; goto again; } } /* If we're supposed to increase the interval, do so. If it's currently zero (i.e., we haven't sent any packets yet), set it to initial_interval; otherwise, add to it a random number between zero and two times itself. On average, this means that it will double with every transmission. */ if (increase) { if (!client->interval) client->interval = client->config->initial_interval; else client->interval += random() % (2 * client->interval); /* Don't backoff past cutoff. */ if (client->interval > client->config->backoff_cutoff) client->interval = (client->config->backoff_cutoff / 2) + (random() % client->config->backoff_cutoff); } else if (!client->interval) client->interval = client->config->initial_interval; /* If the backoff would take us to the panic timeout, just use that as the interval. */ if (cur_time + client -> interval > client -> first_sending + client -> config -> timeout) client -> interval = (client -> first_sending + client -> config -> timeout) - cur_time + 1; /* Record the number of seconds since we started sending. */ if (interval < 65536) client -> packet.secs = htons (interval); else client -> packet.secs = htons (65535); client -> secs = client -> packet.secs; #if defined(DHCPv6) && defined(DHCP4o6) if (dhcpv4_over_dhcpv6) { log_info ("DHCPDISCOVER interval %ld", (long)(client -> interval)); } else #endif log_info ("DHCPDISCOVER on %s to %s port %d interval %ld", client -> name ? client -> name : client -> interface -> name, inet_ntoa (sockaddr_broadcast.sin_addr), ntohs (sockaddr_broadcast.sin_port), (long)(client -> interval)); /* Send out a packet. */ #if defined(DHCPv6) && defined(DHCP4o6) if (dhcpv4_over_dhcpv6) { result = send_dhcpv4_query(client, 1); } else #endif result = send_packet(client->interface, NULL, &client->packet, client->packet_length, inaddr_any, &sockaddr_broadcast, NULL); if (result < 0) { #if defined(DHCPv6) && defined(DHCP4o6) if (dhcpv4_over_dhcpv6) { log_error("%s:%d: Failed to send %d byte long packet.", MDL, client->packet_length); } else #endif log_error("%s:%d: Failed to send %d byte long packet over %s " "interface.", MDL, client->packet_length, client->interface->name); } /* * If we used 0 microseconds here, and there were other clients on the * same network with a synchronized local clock (ntp), and a similar * zero-microsecond-scheduler behavior, then we could be participating * in a sub-second DOS ttck. */ tv.tv_sec = cur_tv.tv_sec + client->interval; tv.tv_usec = client->interval > 1 ? random() % 1000000 : cur_tv.tv_usec; add_timeout(&tv, send_discover, client, 0, 0); } /* state_panic gets called if we haven't received any offers in a preset amount of time. When this happens, we try to use existing leases that haven't yet expired, and failing that, we call the client script and hope it can do something. */ void state_panic (cpp) void *cpp; { struct client_state *client = cpp; struct client_lease *loop; struct client_lease *lp; struct timeval tv; loop = lp = client -> active; log_info ("No DHCPOFFERS received."); /* We may not have an active lease, but we may have some predefined leases that we can try. */ if (!client -> active && client -> leases) goto activate_next; /* Run through the list of leases and see if one can be used. */ while (client -> active) { if (client -> active -> expiry > cur_time) { log_info ("Trying recorded lease %s", piaddr (client -> active -> address)); /* Run the client script with the existing parameters. */ script_init(client, "TIMEOUT", client -> active -> medium); script_write_params(client, "new_", client -> active); script_write_requested(client); if (client -> alias) script_write_params(client, "alias_", client -> alias); /* If the old lease is still good and doesn't yet need renewal, go into BOUND state and timeout at the renewal time. */ if (!script_go(client)) { if (cur_time < client -> active -> renewal) { client -> state = S_BOUND; log_info ("bound: renewal in %ld %s.", (long)(client -> active -> renewal - cur_time), "seconds"); tv.tv_sec = client->active->renewal; tv.tv_usec = ((client->active->renewal - cur_time) > 1) ? random() % 1000000 : cur_tv.tv_usec; add_timeout(&tv, state_bound, client, 0, 0); } else { client -> state = S_BOUND; log_info ("bound: immediate renewal."); state_bound (client); } reinitialize_interfaces (); detach (); return; } } /* If there are no other leases, give up. */ if (!client -> leases) { client -> leases = client -> active; client -> active = (struct client_lease *)0; break; } activate_next: /* Otherwise, put the active lease at the end of the lease list, and try another lease.. */ for (lp = client -> leases; lp -> next; lp = lp -> next) ; lp -> next = client -> active; if (lp -> next) { lp -> next -> next = (struct client_lease *)0; } client -> active = client -> leases; client -> leases = client -> leases -> next; /* If we already tried this lease, we've exhausted the set of leases, so we might as well give up for now. */ if (client -> active == loop) break; else if (!loop) loop = client -> active; } /* No leases were available, or what was available didn't work, so tell the shell script that we failed to allocate an address, and try again later. */ if (onetry) { if (!quiet) { log_info ("Unable to obtain a lease on first try.%s", " Exiting."); } #if defined (CALL_SCRIPT_ON_ONETRY_FAIL) /* Let's call a script and we're done */ script_init(client, "FAIL", (struct string_list *)0); script_go(client); #endif finish(2); } log_info ("No working leases in persistent database - sleeping."); script_init(client, "FAIL", (struct string_list *)0); if (client -> alias) script_write_params(client, "alias_", client -> alias); script_go(client); client -> state = S_INIT; tv.tv_sec = cur_tv.tv_sec + ((client->config->retry_interval + 1) / 2 + (random() % client->config->retry_interval)); tv.tv_usec = ((tv.tv_sec - cur_tv.tv_sec) > 1) ? random() % 1000000 : cur_tv.tv_usec; add_timeout(&tv, state_init, client, 0, 0); detach (); } void send_request (cpp) void *cpp; { struct client_state *client = cpp; int result; int interval; struct sockaddr_in destination; struct in_addr from; struct timeval tv; char rip_buf[128]; const char* rip_str = ""; /* Figure out how long it's been since we started transmitting. */ interval = cur_time - client -> first_sending; /* If we're in the INIT-REBOOT or REQUESTING state and we're past the reboot timeout, go to INIT and see if we can DISCOVER an address... */ /* XXX In the INIT-REBOOT state, if we don't get an ACK, it means either that we're on a network with no DHCP server, or that our server is down. In the latter case, assuming that there is a backup DHCP server, DHCPDISCOVER will get us a new address, but we could also have successfully reused our old address. In the former case, we're hosed anyway. This is not a win-prone situation. */ if ((client -> state == S_REBOOTING || client -> state == S_REQUESTING) && interval > client -> config -> reboot_timeout) { cancel: client -> state = S_INIT; cancel_timeout (send_request, client); state_init (client); return; } /* If we're in the reboot state, make sure the media is set up correctly. */ if (client -> state == S_REBOOTING && !client -> medium && client -> active -> medium ) { script_init(client, "MEDIUM", client -> active -> medium); /* If the medium we chose won't fly, go to INIT state. */ if (script_go(client)) goto cancel; /* Record the medium. */ client -> medium = client -> active -> medium; } /* If the lease has expired, relinquish the address and go back to the INIT state. */ if (client -> state != S_REQUESTING && cur_time > client -> active -> expiry) { /* Run the client script with the new parameters. */ script_init(client, "EXPIRE", (struct string_list *)0); script_write_params(client, "old_", client -> active); script_write_requested(client); if (client -> alias) script_write_params(client, "alias_", client -> alias); script_go(client); /* Now do a preinit on the interface so that we can discover a new address. */ script_init(client, "PREINIT", (struct string_list *)0); if (client -> alias) script_write_params(client, "alias_", client -> alias); script_go(client); client -> state = S_INIT; state_init (client); return; } /* Do the exponential backoff... */ if (!client -> interval) client -> interval = client -> config -> initial_interval; else { client -> interval += ((random () >> 2) % (2 * client -> interval)); } /* Don't backoff past cutoff. */ if (client -> interval > client -> config -> backoff_cutoff) client -> interval = ((client -> config -> backoff_cutoff / 2) + ((random () >> 2) % client -> config -> backoff_cutoff)); /* If the backoff would take us to the expiry time, just set the timeout to the expiry time. */ if (client -> state != S_REQUESTING && cur_time + client -> interval > client -> active -> expiry) client -> interval = client -> active -> expiry - cur_time + 1; /* If the lease T2 time has elapsed, or if we're not yet bound, broadcast the DHCPREQUEST rather than unicasting. */ if (client -> state == S_REQUESTING || client -> state == S_REBOOTING || cur_time > client -> active -> rebind) destination.sin_addr = sockaddr_broadcast.sin_addr; else memcpy (&destination.sin_addr.s_addr, client -> destination.iabuf, sizeof destination.sin_addr.s_addr); destination.sin_port = remote_port; destination.sin_family = AF_INET; #ifdef HAVE_SA_LEN destination.sin_len = sizeof destination; #endif if (client -> state == S_RENEWING || client -> state == S_REBINDING) memcpy (&from, client -> active -> address.iabuf, sizeof from); else from.s_addr = INADDR_ANY; /* Record the number of seconds since we started sending. */ if (client -> state == S_REQUESTING) client -> packet.secs = client -> secs; else { if (interval < 65536) client -> packet.secs = htons (interval); else client -> packet.secs = htons (65535); } #if defined(DHCPv6) && defined(DHCP4o6) if (dhcpv4_over_dhcpv6) { log_info ("DHCPREQUEST"); } else #endif memset(rip_buf, 0x0, sizeof(rip_buf)); if (client->state == S_BOUND || client->state == S_RENEWING || client->state == S_REBINDING) { rip_str = inet_ntoa(client->packet.ciaddr); } else { rip_str = piaddr(client->requested_address); } strncpy(rip_buf, rip_str, sizeof(rip_buf)-1); log_info ("DHCPREQUEST for %s on %s to %s port %d", rip_buf, client->name ? client->name : client->interface->name, inet_ntoa(destination.sin_addr), ntohs (destination.sin_port)); #if defined(DHCPv6) && defined(DHCP4o6) if (dhcpv4_over_dhcpv6) { int broadcast = 0; if (destination.sin_addr.s_addr == INADDR_BROADCAST) broadcast = 1; result = send_dhcpv4_query(client, broadcast); if (result < 0) { log_error("%s:%d: Failed to send %d byte long packet.", MDL, client->packet_length); } } else #endif if (destination.sin_addr.s_addr != INADDR_BROADCAST && fallback_interface) { result = send_packet(fallback_interface, NULL, &client->packet, client->packet_length, from, &destination, NULL); if (result < 0) { log_error("%s:%d: Failed to send %d byte long packet " "over %s interface.", MDL, client->packet_length, fallback_interface->name); } } else { /* Send out a packet. */ result = send_packet(client->interface, NULL, &client->packet, client->packet_length, from, &destination, NULL); if (result < 0) { log_error("%s:%d: Failed to send %d byte long packet" " over %s interface.", MDL, client->packet_length, client->interface->name); } } tv.tv_sec = cur_tv.tv_sec + client->interval; tv.tv_usec = ((tv.tv_sec - cur_tv.tv_sec) > 1) ? random() % 1000000 : cur_tv.tv_usec; add_timeout(&tv, send_request, client, 0, 0); } void send_decline (cpp) void *cpp; { struct client_state *client = cpp; int result; #if defined(DHCPv6) && defined(DHCP4o6) if (dhcpv4_over_dhcpv6) { log_info ("DHCPDECLINE"); } else #endif log_info ("DHCPDECLINE of %s on %s to %s port %d", piaddr(client->requested_address), (client->name ? client->name : client->interface->name), inet_ntoa(sockaddr_broadcast.sin_addr), ntohs(sockaddr_broadcast.sin_port)); /* Send out a packet. */ #if defined(DHCPv6) && defined(DHCP4o6) if (dhcpv4_over_dhcpv6) { result = send_dhcpv4_query(client, 1); } else #endif result = send_packet(client->interface, NULL, &client->packet, client->packet_length, inaddr_any, &sockaddr_broadcast, NULL); if (result < 0) { #if defined(DHCPv6) && defined(DHCP4o6) if (dhcpv4_over_dhcpv6) { log_error("%s:%d: Failed to send %d byte long packet.", MDL, client->packet_length); } else #endif log_error("%s:%d: Failed to send %d byte long packet over %s" " interface.", MDL, client->packet_length, client->interface->name); } } void send_release (cpp) void *cpp; { struct client_state *client = cpp; int result; struct sockaddr_in destination; struct in_addr from; memcpy (&from, client -> active -> address.iabuf, sizeof from); memcpy (&destination.sin_addr.s_addr, client -> destination.iabuf, sizeof destination.sin_addr.s_addr); destination.sin_port = remote_port; destination.sin_family = AF_INET; #ifdef HAVE_SA_LEN destination.sin_len = sizeof destination; #endif /* Set the lease to end now, so that we don't accidentally reuse it if we restart before the old expiry time. */ client -> active -> expiry = client -> active -> renewal = client -> active -> rebind = cur_time; if (!write_client_lease (client, client -> active, 1, 1)) { log_error ("Can't release lease: lease write failed."); return; } #if defined(DHCPv6) && defined(DHCP4o6) if (dhcpv4_over_dhcpv6) { log_info ("DHCPRELEASE"); } else #endif log_info ("DHCPRELEASE of %s on %s to %s port %d", piaddr(client->active->address), client->name ? client->name : client->interface->name, inet_ntoa (destination.sin_addr), ntohs (destination.sin_port)); #if defined(DHCPv6) && defined(DHCP4o6) if (dhcpv4_over_dhcpv6) { int broadcast = 0; if (destination.sin_addr.s_addr == INADDR_BROADCAST) broadcast = 1; result = send_dhcpv4_query(client, broadcast); if (result < 0) { log_error("%s:%d: Failed to send %d byte long packet.", MDL, client->packet_length); } } else #endif if (fallback_interface) { result = send_packet(fallback_interface, NULL, &client->packet, client->packet_length, from, &destination, NULL); if (result < 0) { log_error("%s:%d: Failed to send %d byte long packet" " over %s interface.", MDL, client->packet_length, fallback_interface->name); } } else { /* Send out a packet. */ result = send_packet(client->interface, NULL, &client->packet, client->packet_length, from, &destination, NULL); if (result < 0) { log_error ("%s:%d: Failed to send %d byte long packet" " over %s interface.", MDL, client->packet_length, client->interface->name); } } } #if defined(DHCPv6) && defined(DHCP4o6) /* * \brief Send a DHCPv4-query to the DHCPv6 client * (DHCPv4 client function) * * The DHCPv4 client sends a DHCPv4-query to the DHCPv6 client over * the inter-process communication socket. * * \param client the DHCPv4 client state * \param broadcast the broadcast flag * \return the sent byte count (-1 on error) */ static int send_dhcpv4_query(struct client_state *client, int broadcast) { struct data_string ds; struct dhcpv4_over_dhcpv6_packet *query; int ofs, len, cc; if (dhcp4o6_state <= 0) { log_info("send_dhcpv4_query: not ready."); return -1; } /* * Compute buffer length and allocate it. */ len = ofs = (int)(offsetof(struct dhcpv4_over_dhcpv6_packet, options)); len += dhcpv6_universe.tag_size + dhcpv6_universe.length_size; len += client->packet_length; memset(&ds, 0, sizeof(ds)); if (!buffer_allocate(&ds.buffer, len, MDL)) { log_error("Unable to allocate memory for DHCPv4-query."); return -1; } ds.data = ds.buffer->data; ds.len = len; /* * Fill header. */ query = (struct dhcpv4_over_dhcpv6_packet *)ds.data; query->msg_type = DHCPV6_DHCPV4_QUERY; query->flags[0] = query->flags[1] = query->flags[2] = 0; if (!broadcast) query->flags[0] |= DHCP4O6_QUERY_UNICAST; /* * Append DHCPv4 message. */ dhcpv6_universe.store_tag(ds.buffer->data + ofs, D6O_DHCPV4_MSG); ofs += dhcpv6_universe.tag_size; dhcpv6_universe.store_length(ds.buffer->data + ofs, client->packet_length); ofs += dhcpv6_universe.length_size; memcpy(ds.buffer->data + ofs, &client->packet, client->packet_length); /* * Send DHCPv6 message. */ cc = send(dhcp4o6_fd, ds.data, ds.len, 0); if (cc < 0) log_error("send_dhcpv4_query: send(): %m"); data_string_forget(&ds, MDL); return cc; } /* * \brief Forward a DHCPv4-query to all DHCPv4 over DHCPv6 server addresses. * (DHCPv6 client function) * * \param raw the DHCPv6 DHCPv4-query message raw content */ static void forw_dhcpv4_query(struct data_string *raw) { struct interface_info *ip; struct client_state *client; struct dhc6_lease *lease; struct option_cache *oc; struct data_string addrs; struct sockaddr_in6 sin6; int i, send_ret, attempt, success; attempt = success = 0; memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; sin6.sin6_port = remote_port; #ifdef HAVE_SA_LEN sin6.sin6_len = sizeof(sin6); #endif memset(&addrs, 0, sizeof(addrs)); for (ip = interfaces; ip != NULL; ip = ip->next) { for (client = ip->client; client != NULL; client = client->next) { if ((client->state != S_BOUND) && (client->state != S_RENEWING) && (client->state != S_REBINDING)) continue; lease = client->active_lease; if ((lease == NULL) || lease->released) continue; oc = lookup_option(&dhcpv6_universe, lease->options, D6O_DHCP4_O_DHCP6_SERVER); if ((oc == NULL) || !evaluate_option_cache(&addrs, NULL, NULL, NULL, lease->options, NULL, &global_scope, oc, MDL) || ((addrs.len % sizeof(sin6.sin6_addr)) != 0)) { data_string_forget(&addrs, MDL); continue; } if (addrs.len == 0) { /* note there is nothing to forget */ inet_pton(AF_INET6, All_DHCP_Relay_Agents_and_Servers, &sin6.sin6_addr); attempt++; send_ret = send_packet6(ip, raw->data, raw->len, &sin6); if (send_ret == raw->len) success++; continue; } for (i = 0; i < addrs.len; i += sizeof(sin6.sin6_addr)) { memcpy(&sin6.sin6_addr, addrs.data + i, sizeof(sin6.sin6_addr)); attempt++; send_ret = send_packet6(ip, raw->data, raw->len, &sin6); if (send_ret == raw->len) success++; } data_string_forget(&addrs, MDL); } } log_info("forw_dhcpv4_query: sent(%d): %d/%d", raw->len, success, attempt); if (attempt == 0) dhcp4o6_stop(); } #endif void make_client_options(struct client_state *client, struct client_lease *lease, u_int8_t *type, struct option_cache *sid, struct iaddr *rip, struct option **prl, struct option_state **op) { unsigned i; struct option_cache *oc; struct option *option = NULL; struct buffer *bp = NULL; /* If there are any leftover options, get rid of them. */ if (*op) option_state_dereference(op, MDL); /* Allocate space for options. */ option_state_allocate(op, MDL); /* Send the server identifier if provided. */ if (sid) save_option(&dhcp_universe, *op, sid); oc = NULL; /* Send the requested address if provided. */ if (rip) { client->requested_address = *rip; i = DHO_DHCP_REQUESTED_ADDRESS; if (!(option_code_hash_lookup(&option, dhcp_universe.code_hash, &i, 0, MDL) && make_const_option_cache(&oc, NULL, rip->iabuf, rip->len, option, MDL))) log_error ("can't make requested address cache."); else { save_option(&dhcp_universe, *op, oc); option_cache_dereference(&oc, MDL); } option_dereference(&option, MDL); } else { client->requested_address.len = 0; } i = DHO_DHCP_MESSAGE_TYPE; if (!(option_code_hash_lookup(&option, dhcp_universe.code_hash, &i, 0, MDL) && make_const_option_cache(&oc, NULL, type, 1, option, MDL))) log_error("can't make message type."); else { save_option(&dhcp_universe, *op, oc); option_cache_dereference(&oc, MDL); } option_dereference(&option, MDL); if (prl) { int len; /* Probe the length of the list. */ len = 0; for (i = 0 ; prl[i] != NULL ; i++) if (prl[i]->universe == &dhcp_universe) len++; if (!buffer_allocate(&bp, len, MDL)) log_error("can't make parameter list buffer."); else { unsigned code = DHO_DHCP_PARAMETER_REQUEST_LIST; len = 0; for (i = 0 ; prl[i] != NULL ; i++) if (prl[i]->universe == &dhcp_universe) bp->data[len++] = prl[i]->code; if (!(option_code_hash_lookup(&option, dhcp_universe.code_hash, &code, 0, MDL) && make_const_option_cache(&oc, &bp, NULL, len, option, MDL))) { if (bp != NULL) buffer_dereference(&bp, MDL); log_error ("can't make option cache"); } else { save_option(&dhcp_universe, *op, oc); option_cache_dereference(&oc, MDL); } option_dereference(&option, MDL); } } /* * If requested (duid_v4 == 1) add an RFC4361 compliant client-identifier * This can be overridden by including a client id in the configuration * file. */ if (duid_v4 == 1) { struct data_string client_identifier; int hw_idx, hw_len; memset(&client_identifier, 0, sizeof(client_identifier)); client_identifier.len = 1 + 4 + default_duid.len; if (!buffer_allocate(&client_identifier.buffer, client_identifier.len, MDL)) log_fatal("no memory for default DUID!"); client_identifier.data = client_identifier.buffer->data; i = DHO_DHCP_CLIENT_IDENTIFIER; /* Client-identifier type : 1 byte */ *client_identifier.buffer->data = 255; /* IAID : 4 bytes * we use the low 4 bytes from the interface address */ if (client->interface->hw_address.hlen > 4) { hw_idx = client->interface->hw_address.hlen - 4; hw_len = 4; } else { hw_idx = 0; hw_len = client->interface->hw_address.hlen; } memcpy(&client_identifier.buffer->data + 5 - hw_len, client->interface->hw_address.hbuf + hw_idx, hw_len); /* Add the default duid */ memcpy(&client_identifier.buffer->data+(1+4), default_duid.data, default_duid.len); /* And save the option */ if (!(option_code_hash_lookup(&option, dhcp_universe.code_hash, &i, 0, MDL) && make_const_option_cache(&oc, NULL, (u_int8_t *)client_identifier.data, client_identifier.len, option, MDL))) log_error ("can't make requested client id cache.."); else { save_option (&dhcp_universe, *op, oc); option_cache_dereference (&oc, MDL); } option_dereference(&option, MDL); } /* Run statements that need to be run on transmission. */ if (client->config->on_transmission) execute_statements_in_scope(NULL, NULL, NULL, client, (lease ? lease->options : NULL), *op, &global_scope, client->config->on_transmission, NULL, NULL); } void make_discover (client, lease) struct client_state *client; struct client_lease *lease; { unsigned char discover = DHCPDISCOVER; struct option_state *options = (struct option_state *)0; memset (&client -> packet, 0, sizeof (client -> packet)); make_client_options (client, lease, &discover, (struct option_cache *)0, lease ? &lease -> address : (struct iaddr *)0, client -> config -> requested_options, &options); /* Set up the option buffer... */ client -> packet_length = cons_options ((struct packet *)0, &client -> packet, (struct lease *)0, client, /* maximum packet size */1500, (struct option_state *)0, options, /* scope */ &global_scope, /* overload */ 0, /* terminate */0, /* bootpp */0, (struct data_string *)0, client -> config -> vendor_space_name); option_state_dereference (&options, MDL); if (client -> packet_length < BOOTP_MIN_LEN) client -> packet_length = BOOTP_MIN_LEN; client -> packet.op = BOOTREQUEST; client -> packet.htype = client -> interface -> hw_address.hbuf [0]; /* Assumes hw_address is known, otherwise a random value may result */ client -> packet.hlen = client -> interface -> hw_address.hlen - 1; client -> packet.hops = 0; client -> packet.xid = random (); client -> packet.secs = 0; /* filled in by send_discover. */ if (can_receive_unicast_unconfigured (client -> interface)) client -> packet.flags = 0; else client -> packet.flags = htons (BOOTP_BROADCAST); memset (&(client -> packet.ciaddr), 0, sizeof client -> packet.ciaddr); memset (&(client -> packet.yiaddr), 0, sizeof client -> packet.yiaddr); memset (&(client -> packet.siaddr), 0, sizeof client -> packet.siaddr); client -> packet.giaddr = giaddr; if (client -> interface -> hw_address.hlen > 0) memcpy (client -> packet.chaddr, &client -> interface -> hw_address.hbuf [1], (unsigned)(client -> interface -> hw_address.hlen - 1)); #ifdef DEBUG_PACKET dump_raw ((unsigned char *)&client -> packet, client -> packet_length); #endif } void make_request (client, lease) struct client_state *client; struct client_lease *lease; { unsigned char request = DHCPREQUEST; struct option_cache *oc; memset (&client -> packet, 0, sizeof (client -> packet)); if (client -> state == S_REQUESTING) oc = lookup_option (&dhcp_universe, lease -> options, DHO_DHCP_SERVER_IDENTIFIER); else oc = (struct option_cache *)0; if (client -> sent_options) option_state_dereference (&client -> sent_options, MDL); make_client_options (client, lease, &request, oc, ((client -> state == S_REQUESTING || client -> state == S_REBOOTING) ? &lease -> address : (struct iaddr *)0), client -> config -> requested_options, &client -> sent_options); /* Set up the option buffer... */ client -> packet_length = cons_options ((struct packet *)0, &client -> packet, (struct lease *)0, client, /* maximum packet size */1500, (struct option_state *)0, client -> sent_options, /* scope */ &global_scope, /* overload */ 0, /* terminate */0, /* bootpp */0, (struct data_string *)0, client -> config -> vendor_space_name); if (client -> packet_length < BOOTP_MIN_LEN) client -> packet_length = BOOTP_MIN_LEN; client -> packet.op = BOOTREQUEST; client -> packet.htype = client -> interface -> hw_address.hbuf [0]; /* Assumes hw_address is known, otherwise a random value may result */ client -> packet.hlen = client -> interface -> hw_address.hlen - 1; client -> packet.hops = 0; client -> packet.xid = client -> xid; client -> packet.secs = 0; /* Filled in by send_request. */ /* If we own the address we're requesting, put it in ciaddr; otherwise set ciaddr to zero. */ if (client -> state == S_BOUND || client -> state == S_RENEWING || client -> state == S_REBINDING) { memcpy (&client -> packet.ciaddr, lease -> address.iabuf, lease -> address.len); client -> packet.flags = 0; } else { memset (&client -> packet.ciaddr, 0, sizeof client -> packet.ciaddr); if (can_receive_unicast_unconfigured (client -> interface)) client -> packet.flags = 0; else client -> packet.flags = htons (BOOTP_BROADCAST); } memset (&client -> packet.yiaddr, 0, sizeof client -> packet.yiaddr); memset (&client -> packet.siaddr, 0, sizeof client -> packet.siaddr); if (client -> state != S_BOUND && client -> state != S_RENEWING) client -> packet.giaddr = giaddr; else memset (&client -> packet.giaddr, 0, sizeof client -> packet.giaddr); if (client -> interface -> hw_address.hlen > 0) memcpy (client -> packet.chaddr, &client -> interface -> hw_address.hbuf [1], (unsigned)(client -> interface -> hw_address.hlen - 1)); #ifdef DEBUG_PACKET dump_raw ((unsigned char *)&client -> packet, client -> packet_length); #endif } void make_decline (client, lease) struct client_state *client; struct client_lease *lease; { unsigned char decline = DHCPDECLINE; struct option_cache *oc; struct option_state *options = (struct option_state *)0; /* Create the options cache. */ oc = lookup_option (&dhcp_universe, lease -> options, DHO_DHCP_SERVER_IDENTIFIER); make_client_options(client, lease, &decline, oc, &lease->address, NULL, &options); /* Consume the options cache into the option buffer. */ memset (&client -> packet, 0, sizeof (client -> packet)); client -> packet_length = cons_options ((struct packet *)0, &client -> packet, (struct lease *)0, client, 0, (struct option_state *)0, options, &global_scope, 0, 0, 0, (struct data_string *)0, client -> config -> vendor_space_name); /* Destroy the options cache. */ option_state_dereference (&options, MDL); if (client -> packet_length < BOOTP_MIN_LEN) client -> packet_length = BOOTP_MIN_LEN; client -> packet.op = BOOTREQUEST; client -> packet.htype = client -> interface -> hw_address.hbuf [0]; /* Assumes hw_address is known, otherwise a random value may result */ client -> packet.hlen = client -> interface -> hw_address.hlen - 1; client -> packet.hops = 0; client -> packet.xid = client -> xid; client -> packet.secs = 0; /* Filled in by send_request. */ if (can_receive_unicast_unconfigured (client -> interface)) client -> packet.flags = 0; else client -> packet.flags = htons (BOOTP_BROADCAST); /* ciaddr must always be zero. */ memset (&client -> packet.ciaddr, 0, sizeof client -> packet.ciaddr); memset (&client -> packet.yiaddr, 0, sizeof client -> packet.yiaddr); memset (&client -> packet.siaddr, 0, sizeof client -> packet.siaddr); client -> packet.giaddr = giaddr; memcpy (client -> packet.chaddr, &client -> interface -> hw_address.hbuf [1], client -> interface -> hw_address.hlen); #ifdef DEBUG_PACKET dump_raw ((unsigned char *)&client -> packet, client -> packet_length); #endif } void make_release (client, lease) struct client_state *client; struct client_lease *lease; { unsigned char request = DHCPRELEASE; struct option_cache *oc; struct option_state *options = (struct option_state *)0; memset (&client -> packet, 0, sizeof (client -> packet)); oc = lookup_option (&dhcp_universe, lease -> options, DHO_DHCP_SERVER_IDENTIFIER); make_client_options(client, lease, &request, oc, NULL, NULL, &options); /* Set up the option buffer... */ client -> packet_length = cons_options ((struct packet *)0, &client -> packet, (struct lease *)0, client, /* maximum packet size */1500, (struct option_state *)0, options, /* scope */ &global_scope, /* overload */ 0, /* terminate */0, /* bootpp */0, (struct data_string *)0, client -> config -> vendor_space_name); if (client -> packet_length < BOOTP_MIN_LEN) client -> packet_length = BOOTP_MIN_LEN; option_state_dereference (&options, MDL); client -> packet.op = BOOTREQUEST; client -> packet.htype = client -> interface -> hw_address.hbuf [0]; /* Assumes hw_address is known, otherwise a random value may result */ client -> packet.hlen = client -> interface -> hw_address.hlen - 1; client -> packet.hops = 0; client -> packet.xid = random (); client -> packet.secs = 0; client -> packet.flags = 0; memcpy (&client -> packet.ciaddr, lease -> address.iabuf, lease -> address.len); memset (&client -> packet.yiaddr, 0, sizeof client -> packet.yiaddr); memset (&client -> packet.siaddr, 0, sizeof client -> packet.siaddr); client -> packet.giaddr = giaddr; memcpy (client -> packet.chaddr, &client -> interface -> hw_address.hbuf [1], client -> interface -> hw_address.hlen); #ifdef DEBUG_PACKET dump_raw ((unsigned char *)&client -> packet, client -> packet_length); #endif } void destroy_client_lease (lease) struct client_lease *lease; { if (lease -> server_name) dfree (lease -> server_name, MDL); if (lease -> filename) dfree (lease -> filename, MDL); option_state_dereference (&lease -> options, MDL); free_client_lease (lease, MDL); } FILE *leaseFile = NULL; int leases_written = 0; void rewrite_client_leases () { struct interface_info *ip; struct client_state *client; struct client_lease *lp; if (leaseFile != NULL) fclose (leaseFile); leaseFile = fopen (path_dhclient_db, "w"); if (leaseFile == NULL) { log_error ("can't create %s: %m", path_dhclient_db); return; } /* If there is a default duid, write it out. */ if (default_duid.len != 0) write_duid(&default_duid); /* Write out all the leases attached to configured interfaces that we know about. */ for (ip = interfaces; ip; ip = ip -> next) { for (client = ip -> client; client; client = client -> next) { for (lp = client -> leases; lp; lp = lp -> next) { write_client_lease (client, lp, 1, 0); } if (client -> active) write_client_lease (client, client -> active, 1, 0); if (client->active_lease != NULL) write_client6_lease(client, client->active_lease, 1, 0); /* Reset last_write after rewrites. */ client->last_write = 0; } } /* Write out any leases that are attached to interfaces that aren't currently configured. */ for (ip = dummy_interfaces; ip; ip = ip -> next) { for (client = ip -> client; client; client = client -> next) { for (lp = client -> leases; lp; lp = lp -> next) { write_client_lease (client, lp, 1, 0); } if (client -> active) write_client_lease (client, client -> active, 1, 0); if (client->active_lease != NULL) write_client6_lease(client, client->active_lease, 1, 0); /* Reset last_write after rewrites. */ client->last_write = 0; } } fflush (leaseFile); } void write_lease_option (struct option_cache *oc, struct packet *packet, struct lease *lease, struct client_state *client_state, struct option_state *in_options, struct option_state *cfg_options, struct binding_scope **scope, struct universe *u, void *stuff) { const char *name, *dot; struct data_string ds; char *preamble = stuff; memset (&ds, 0, sizeof ds); if (u != &dhcp_universe) { name = u -> name; dot = "."; } else { name = ""; dot = ""; } if (evaluate_option_cache (&ds, packet, lease, client_state, in_options, cfg_options, scope, oc, MDL)) { /* The option name */ fprintf(leaseFile, "%soption %s%s%s", preamble, name, dot, oc->option->name); /* The option value if there is one */ if ((oc->option->format == NULL) || (oc->option->format[0] != 'Z')) { fprintf(leaseFile, " %s", pretty_print_option(oc->option, ds.data, ds.len, 1, 1)); } /* The closing semi-colon and newline */ fprintf(leaseFile, ";\n"); data_string_forget (&ds, MDL); } } /* Write an option cache to the lease store. */ static void write_options(struct client_state *client, struct option_state *options, const char *preamble) { int i; for (i = 0; i < options->universe_count; i++) { option_space_foreach(NULL, NULL, client, NULL, options, &global_scope, universes[i], (char *)preamble, write_lease_option); } } /* * The "best" default DUID, since we cannot predict any information * about the system (such as whether or not the hardware addresses are * integrated into the motherboard or similar), is the "LLT", link local * plus time, DUID. For real stateless "LL" is better. * * Once generated, this duid is stored into the state database, and * retained across restarts. * * For the time being, there is probably a different state database for * every daemon, so this winds up being a per-interface identifier...which * is not how it is intended. Upcoming rearchitecting the client should * address this "one daemon model." */ void form_duid(struct data_string *duid, const char *file, int line) { struct interface_info *ip; int len; char *str; /* For now, just use the first interface on the list. */ ip = interfaces; if (ip == NULL) log_fatal("Impossible condition at %s:%d.", MDL); if ((ip->hw_address.hlen == 0) || (ip->hw_address.hlen > sizeof(ip->hw_address.hbuf))) log_fatal("Impossible hardware address length at %s:%d.", MDL); if (duid_type == 0) duid_type = stateless ? DUID_LL : DUID_LLT; /* * 2 bytes for the 'duid type' field. * 2 bytes for the 'htype' field. * (DUID_LLT) 4 bytes for the 'current time'. * enough bytes for the hardware address (note that hw_address has * the 'htype' on byte zero). */ len = 4 + (ip->hw_address.hlen - 1); if (duid_type == DUID_LLT) len += 4; if (!buffer_allocate(&duid->buffer, len, MDL)) log_fatal("no memory for default DUID!"); duid->data = duid->buffer->data; duid->len = len; /* Basic Link Local Address type of DUID. */ if (duid_type == DUID_LLT) { putUShort(duid->buffer->data, DUID_LLT); putUShort(duid->buffer->data + 2, ip->hw_address.hbuf[0]); putULong(duid->buffer->data + 4, cur_time - DUID_TIME_EPOCH); memcpy(duid->buffer->data + 8, ip->hw_address.hbuf + 1, ip->hw_address.hlen - 1); } else { putUShort(duid->buffer->data, DUID_LL); putUShort(duid->buffer->data + 2, ip->hw_address.hbuf[0]); memcpy(duid->buffer->data + 4, ip->hw_address.hbuf + 1, ip->hw_address.hlen - 1); } /* Now format the output based on lease-id-format */ str = format_lease_id(duid->data, duid->len, top_level_config.lease_id_format, MDL); if (str == NULL) { log_info("form_duid: Couldn't allocate memory to log duid!"); } else { log_info("Created duid %s.", str); dfree(str, MDL); } } /* Write the default DUID to the lease store. */ static isc_result_t write_duid(struct data_string *duid) { char *str; int stat; if ((duid == NULL) || (duid->len <= 2)) return DHCP_R_INVALIDARG; if (leaseFile == NULL) { /* XXX? */ leaseFile = fopen(path_dhclient_db, "w"); if (leaseFile == NULL) { log_error("can't create %s: %m", path_dhclient_db); return ISC_R_IOERROR; } } /* Generate a formatted duid string per lease-id-format */ str = format_lease_id(duid->data, duid->len, top_level_config.lease_id_format, MDL); if (str == NULL) return ISC_R_NOMEMORY; stat = fprintf(leaseFile, "default-duid %s;\n", str); dfree(str, MDL); if (stat <= 0) return ISC_R_IOERROR; if (fflush(leaseFile) != 0) return ISC_R_IOERROR; return ISC_R_SUCCESS; } /* Write a DHCPv6 lease to the store. */ isc_result_t write_client6_lease(struct client_state *client, struct dhc6_lease *lease, int rewrite, int sync) { struct dhc6_ia *ia; struct dhc6_addr *addr; int stat; const char *ianame; /* This should include the current lease. */ if (!rewrite && (leases_written++ > 20)) { rewrite_client_leases(); leases_written = 0; return ISC_R_SUCCESS; } if (client == NULL || lease == NULL) return DHCP_R_INVALIDARG; if (leaseFile == NULL) { /* XXX? */ leaseFile = fopen(path_dhclient_db, "w"); if (leaseFile == NULL) { log_error("can't create %s: %m", path_dhclient_db); return ISC_R_IOERROR; } } stat = fprintf(leaseFile, "lease6 {\n"); if (stat <= 0) return ISC_R_IOERROR; stat = fprintf(leaseFile, " interface \"%s\";\n", client->interface->name); if (stat <= 0) return ISC_R_IOERROR; for (ia = lease->bindings ; ia != NULL ; ia = ia->next) { switch (ia->ia_type) { case D6O_IA_NA: default: ianame = "ia-na"; break; case D6O_IA_TA: ianame = "ia-ta"; break; case D6O_IA_PD: ianame = "ia-pd"; break; } /* For some reason IAID was never octal or hex, but string or * hex. Go figure. So for compatibilty's sake we will either * do hex or "legacy" i.e string rather than octal. What a * cluster. */ switch(top_level_config.lease_id_format) { case TOKEN_HEX: { char* iaid_str = format_lease_id( (const unsigned char *) &ia->iaid, 4, top_level_config.lease_id_format, MDL); if (!iaid_str) { log_error("Can't format iaid"); return ISC_R_IOERROR; } stat = fprintf(leaseFile, " %s %s {\n", ianame, iaid_str); dfree(iaid_str, MDL); break; } case TOKEN_OCTAL: default: stat = fprintf(leaseFile, " %s %s {\n", ianame, print_hex_1(4, ia->iaid, 12)); break; } if (stat <= 0) return ISC_R_IOERROR; if (ia->ia_type != D6O_IA_TA) stat = fprintf(leaseFile, " starts %d;\n" " renew %u;\n" " rebind %u;\n", (int)ia->starts, ia->renew, ia->rebind); else stat = fprintf(leaseFile, " starts %d;\n", (int)ia->starts); if (stat <= 0) return ISC_R_IOERROR; for (addr = ia->addrs ; addr != NULL ; addr = addr->next) { if (ia->ia_type != D6O_IA_PD) stat = fprintf(leaseFile, " iaaddr %s {\n", piaddr(addr->address)); else stat = fprintf(leaseFile, " iaprefix %s/%d {\n", piaddr(addr->address), (int)addr->plen); if (stat <= 0) return ISC_R_IOERROR; stat = fprintf(leaseFile, " starts %d;\n" " preferred-life %u;\n" " max-life %u;\n", (int)addr->starts, addr->preferred_life, addr->max_life); if (stat <= 0) return ISC_R_IOERROR; if (addr->options != NULL) write_options(client, addr->options, " "); stat = fprintf(leaseFile, " }\n"); if (stat <= 0) return ISC_R_IOERROR; } if (ia->options != NULL) write_options(client, ia->options, " "); stat = fprintf(leaseFile, " }\n"); if (stat <= 0) return ISC_R_IOERROR; } if (lease->released) { stat = fprintf(leaseFile, " released;\n"); if (stat <= 0) return ISC_R_IOERROR; } if (lease->options != NULL) write_options(client, lease->options, " "); stat = fprintf(leaseFile, "}\n"); if (stat <= 0) return ISC_R_IOERROR; if (fflush(leaseFile) != 0) return ISC_R_IOERROR; if (sync) { if (fsync(fileno(leaseFile)) < 0) { log_error("write_client_lease: fsync(): %m"); return ISC_R_IOERROR; } } return ISC_R_SUCCESS; } int write_client_lease (client, lease, rewrite, makesure) struct client_state *client; struct client_lease *lease; int rewrite; int makesure; { struct data_string ds; int errors = 0; char *s; const char *tval; if (!rewrite) { if (leases_written++ > 20) { rewrite_client_leases (); leases_written = 0; } } /* If the lease came from the config file, we don't need to stash a copy in the lease database. */ if (lease -> is_static) return 1; if (leaseFile == NULL) { /* XXX */ leaseFile = fopen (path_dhclient_db, "w"); if (leaseFile == NULL) { log_error ("can't create %s: %m", path_dhclient_db); return 0; } } errno = 0; fprintf (leaseFile, "lease {\n"); if (lease -> is_bootp) { fprintf (leaseFile, " bootp;\n"); if (errno) { ++errors; errno = 0; } } fprintf (leaseFile, " interface \"%s\";\n", client -> interface -> name); if (errno) { ++errors; errno = 0; } if (client -> name) { fprintf (leaseFile, " name \"%s\";\n", client -> name); if (errno) { ++errors; errno = 0; } } fprintf (leaseFile, " fixed-address %s;\n", piaddr (lease -> address)); if (errno) { ++errors; errno = 0; } if (lease -> filename) { s = quotify_string (lease -> filename, MDL); if (s) { fprintf (leaseFile, " filename \"%s\";\n", s); if (errno) { ++errors; errno = 0; } dfree (s, MDL); } else errors++; } if (lease->server_name != NULL) { s = quotify_string(lease->server_name, MDL); if (s != NULL) { fprintf(leaseFile, " server-name \"%s\";\n", s); if (errno) { ++errors; errno = 0; } dfree(s, MDL); } else ++errors; } if (lease -> medium) { s = quotify_string (lease -> medium -> string, MDL); if (s) { fprintf (leaseFile, " medium \"%s\";\n", s); if (errno) { ++errors; errno = 0; } dfree (s, MDL); } else errors++; } if (errno != 0) { errors++; errno = 0; } memset (&ds, 0, sizeof ds); write_options(client, lease->options, " "); tval = print_time(lease->renewal); if (tval == NULL || fprintf(leaseFile, " renew %s\n", tval) < 0) errors++; tval = print_time(lease->rebind); if (tval == NULL || fprintf(leaseFile, " rebind %s\n", tval) < 0) errors++; tval = print_time(lease->expiry); if (tval == NULL || fprintf(leaseFile, " expire %s\n", tval) < 0) errors++; if (fprintf(leaseFile, "}\n") < 0) errors++; if (fflush(leaseFile) != 0) errors++; client->last_write = cur_time; if (!errors && makesure) { if (fsync (fileno (leaseFile)) < 0) { log_info ("write_client_lease: %m"); return 0; } } return errors ? 0 : 1; } /* Variables holding name of script and file pointer for writing to script. Needless to say, this is not reentrant - only one script can be invoked at a time. */ char scriptName [256]; FILE *scriptFile; /** * @brief Initializes basic variables for a script * * This function is called as an initial preparation for calling a script. * It sets up a number of common env. variables that will be passed to * the script. For actual script calling, see @ref script_go . * * @param client variables will be stored here (if null, the whole function * is no-op) * @param reason specified the reason for calling a script (must be non-null) * @param medium if specified, defines medium type (may be null) */ void script_init(struct client_state *client, const char *reason, struct string_list *medium) { struct string_list *sl, *next; if (client) { for (sl = client -> env; sl; sl = next) { next = sl -> next; dfree (sl, MDL); } client -> env = (struct string_list *)0; client -> envc = 0; if (client -> interface) { client_envadd (client, "", "interface", "%s", client -> interface -> name); } if (client -> name) client_envadd (client, "", "client", "%s", client -> name); if (medium) client_envadd (client, "", "medium", "%s", medium -> string); client_envadd (client, "", "reason", "%s", reason); client_envadd (client, "", "pid", "%ld", (long int)getpid ()); #if defined(DHCPv6) client_envadd (client, "", "dad_wait_time", "%ld", (long int)dad_wait_time); #endif } } void client_option_envadd (struct option_cache *oc, struct packet *packet, struct lease *lease, struct client_state *client_state, struct option_state *in_options, struct option_state *cfg_options, struct binding_scope **scope, struct universe *u, void *stuff) { struct envadd_state *es = stuff; struct data_string data; memset (&data, 0, sizeof data); if (evaluate_option_cache (&data, packet, lease, client_state, in_options, cfg_options, scope, oc, MDL)) { if (data.len) { char name [256]; if (dhcp_option_ev_name (name, sizeof name, oc->option)) { const char *value; size_t length; value = pretty_print_option(oc->option, data.data, data.len, 0, 0); length = strlen(value); if (check_option_values(oc->option->universe, oc->option->code, value, length) == 0) { client_envadd(es->client, es->prefix, name, "%s", value); } else { log_error("suspect value in %s " "option - discarded", name); } data_string_forget (&data, MDL); } } } } /** * @brief Adds parameters to environment variables for a script * * This function add details of specified lease to a list of env. variables * to be passed to a script. The lease details will be prepended with * specified prefix (e.g. "old_") and added to the list stored in client. * Following variables may be set: * - ip_address * - next_server * - network_number * - broadcast_address * - filename * - server_name * - expiry * * @param client env. variables will be stored here * @param prefix textual prefix to be added to each variable (e.g. "old_") * @param lease lease details will be extracted from here */ void script_write_params(struct client_state *client, const char *prefix, struct client_lease *lease) { int i; struct data_string data; struct option_cache *oc; struct envadd_state es; es.client = client; es.prefix = prefix; client_envadd (client, prefix, "ip_address", "%s", piaddr (lease -> address)); /* If we've set the next server address in the lease structure put it into an environment variable for the script */ if (lease->next_srv_addr.len != 0) { client_envadd(client, prefix, "next_server", "%s", piaddr(lease->next_srv_addr)); } /* For the benefit of Linux (and operating systems which may have similar needs), compute the network address based on the supplied ip address and netmask, if provided. Also compute the broadcast address (the host address all ones broadcast address, not the host address all zeroes broadcast address). */ memset (&data, 0, sizeof data); oc = lookup_option (&dhcp_universe, lease -> options, DHO_SUBNET_MASK); if (oc && evaluate_option_cache (&data, (struct packet *)0, (struct lease *)0, client, (struct option_state *)0, lease -> options, &global_scope, oc, MDL)) { if (data.len > 3) { struct iaddr netmask, subnet, broadcast; /* * No matter the length of the subnet-mask option, * use only the first four octets. Note that * subnet-mask options longer than 4 octets are not * in conformance with RFC 2132, but servers with this * flaw do exist. */ memcpy(netmask.iabuf, data.data, 4); netmask.len = 4; data_string_forget (&data, MDL); subnet = subnet_number (lease -> address, netmask); if (subnet.len) { client_envadd (client, prefix, "network_number", "%s", piaddr (subnet)); oc = lookup_option (&dhcp_universe, lease -> options, DHO_BROADCAST_ADDRESS); if (!oc || !(evaluate_option_cache (&data, (struct packet *)0, (struct lease *)0, client, (struct option_state *)0, lease -> options, &global_scope, oc, MDL))) { broadcast = broadcast_addr (subnet, netmask); if (broadcast.len) { client_envadd (client, prefix, "broadcast_address", "%s", piaddr (broadcast)); } } } } data_string_forget (&data, MDL); } if (lease->filename) { if (check_option_values(NULL, DHO_ROOT_PATH, lease->filename, strlen(lease->filename)) == 0) { client_envadd(client, prefix, "filename", "%s", lease->filename); } else { log_error("suspect value in %s " "option - discarded", lease->filename); } } if (lease->server_name) { if (check_option_values(NULL, DHO_HOST_NAME, lease->server_name, strlen(lease->server_name)) == 0 ) { client_envadd (client, prefix, "server_name", "%s", lease->server_name); } else { log_error("suspect value in %s " "option - discarded", lease->server_name); } } for (i = 0; i < lease -> options -> universe_count; i++) { option_space_foreach ((struct packet *)0, (struct lease *)0, client, (struct option_state *)0, lease -> options, &global_scope, universes [i], &es, client_option_envadd); } client_envadd (client, prefix, "expiry", "%lu", (unsigned long)(lease -> expiry)); } /** * @brief Write out the environent variable the client requested. * Write out the environment variables for the objects that the * client requested. If the object was requested the variable will be: * requested_=1 * If it wasn't requested there won't be a variable. * * @param client client structure */ void script_write_requested(struct client_state *client) { int i; struct option **req; char name[256]; req = client->config->requested_options; if (req == NULL) return; for (i = 0 ; req[i] != NULL ; i++) { if ((req[i]->universe == &dhcp_universe) && dhcp_option_ev_name(name, sizeof(name), req[i])) { client_envadd(client, "requested_", name, "%d", 1); } } } /** * @brief Calls external script. * * External script is specified either using -sf command line or * script parameter in the configuration file. * * @param client specifies client information (environment variables, * and other parameters will be extracted and passed to the script. * @return If positive, it contains exit code of the process running script. * If negative, returns the signal number that cause the script process * to terminate. */ int script_go(struct client_state *client) { char *scriptName; char *argv [2]; char **envp; char reason [] = "REASON=NBI"; static char client_path [] = CLIENT_PATH; int i; struct string_list *sp, *next; int pid, wpid, wstatus; if (client) scriptName = client -> config -> script_name; else scriptName = top_level_config.script_name; envp = dmalloc (((client ? client -> envc : 2) + client_env_count + 2) * sizeof (char *), MDL); if (!envp) { log_error ("No memory for client script environment."); return 0; } i = 0; /* Copy out the environment specified on the command line, if any. */ for (sp = client_env; sp; sp = sp -> next) { envp [i++] = sp -> string; } /* Copy out the environment specified by dhclient. */ if (client) { for (sp = client -> env; sp; sp = sp -> next) { envp [i++] = sp -> string; } } else { envp [i++] = reason; } /* Set $PATH. */ envp [i++] = client_path; envp [i] = (char *)0; argv [0] = scriptName; argv [1] = (char *)0; pid = fork (); if (pid < 0) { log_error ("fork: %m"); wstatus = 0; } else if (pid) { do { wpid = wait (&wstatus); } while (wpid != pid && wpid > 0); if (wpid < 0) { log_error ("wait: %m"); wstatus = 0; } } else { /* We don't want to pass an open file descriptor for * dhclient.leases when executing dhclient-script. */ if (leaseFile != NULL) fclose(leaseFile); execve (scriptName, argv, envp); log_error ("execve (%s, ...): %m", scriptName); exit (0); } if (client) { for (sp = client -> env; sp; sp = next) { next = sp -> next; dfree (sp, MDL); } client -> env = (struct string_list *)0; client -> envc = 0; } dfree (envp, MDL); gettimeofday(&cur_tv, NULL); return (WIFEXITED (wstatus) ? WEXITSTATUS (wstatus) : -WTERMSIG (wstatus)); } void client_envadd (struct client_state *client, const char *prefix, const char *name, const char *fmt, ...) { char spbuf [1024]; char *s; unsigned len; struct string_list *val; va_list list; va_start (list, fmt); len = vsnprintf (spbuf, sizeof spbuf, fmt, list); va_end (list); val = dmalloc (strlen (prefix) + strlen (name) + 1 /* = */ + len + sizeof *val, MDL); if (!val) { log_error ("client_envadd: cannot allocate space for variable"); return; } s = val -> string; strcpy (s, prefix); strcat (s, name); s += strlen (s); *s++ = '='; if (len >= sizeof spbuf) { va_start (list, fmt); vsnprintf (s, len + 1, fmt, list); va_end (list); } else { strcpy (s, spbuf); } val -> next = client -> env; client -> env = val; client -> envc++; } int dhcp_option_ev_name (buf, buflen, option) char *buf; size_t buflen; struct option *option; { int i, j; const char *s; j = 0; if (option -> universe != &dhcp_universe) { s = option -> universe -> name; i = 0; } else { s = option -> name; i = 1; } do { while (*s) { if (j + 1 == buflen) return 0; if (*s == '-') buf [j++] = '_'; else buf [j++] = *s; ++s; } if (!i) { s = option -> name; if (j + 1 == buflen) return 0; buf [j++] = '_'; } ++i; } while (i != 2); buf [j] = 0; return 1; } void finish (char ret) { if (no_daemon || dfd[0] == -1 || dfd[1] == -1) exit((int)ret); if (write(dfd[1], &ret, 1) != 1) log_fatal("write to parent: %m"); (void) close(dfd[1]); dfd[0] = dfd[1] = -1; exit((int)ret); } void detach () { char buf = 0; /* Don't become a daemon if the user requested otherwise. */ if (no_daemon) { write_client_pid_file (); return; } /* Only do it once. */ if (dfd[0] == -1 || dfd[1] == -1) return; /* Signal parent we started successfully. */ if (write(dfd[1], &buf, 1) != 1) log_fatal("write to parent: %m"); (void) close(dfd[1]); dfd[0] = dfd[1] = -1; /* Stop logging to stderr... */ log_perror = 0; /* Become session leader and get pid... */ (void) setsid (); /* Close standard I/O descriptors. */ (void) close(0); (void) close(1); (void) close(2); /* Reopen them on /dev/null. */ (void) open("/dev/null", O_RDWR); (void) open("/dev/null", O_RDWR); (void) open("/dev/null", O_RDWR); write_client_pid_file (); IGNORE_RET (chdir("/")); } void write_client_pid_file () { FILE *pf; int pfdesc; /* nothing to do if the user doesn't want a pid file */ if (no_pid_file == ISC_TRUE) { return; } pfdesc = open (path_dhclient_pid, O_CREAT | O_TRUNC | O_WRONLY, 0644); if (pfdesc < 0) { log_error ("Can't create %s: %m", path_dhclient_pid); return; } pf = fdopen (pfdesc, "w"); if (!pf) { close(pfdesc); log_error ("Can't fdopen %s: %m", path_dhclient_pid); } else { fprintf (pf, "%ld\n", (long)getpid ()); fclose (pf); } } void client_location_changed () { struct interface_info *ip; struct client_state *client; for (ip = interfaces; ip; ip = ip -> next) { for (client = ip -> client; client; client = client -> next) { switch (client -> state) { case S_SELECTING: cancel_timeout (send_discover, client); break; case S_BOUND: cancel_timeout (state_bound, client); break; case S_REBOOTING: case S_REQUESTING: case S_RENEWING: cancel_timeout (send_request, client); break; case S_INIT: case S_REBINDING: case S_STOPPED: case S_DECLINING: break; } client -> state = S_INIT; state_reboot (client); } } } void do_release(client) struct client_state *client; { struct data_string ds; struct option_cache *oc; #if defined(DHCPv6) && defined(DHCP4o6) if (dhcpv4_over_dhcpv6 && (dhcp4o6_state <= 0)) { if (dhcp4o6_state < 0) dhcp4o6_poll(NULL); client->pending = P_RELEASE; return; } #endif /* Pick a random xid. */ client -> xid = random (); /* is there even a lease to release? */ if (client -> active) { /* Make a DHCPRELEASE packet, and set appropriate per-interface flags. */ make_release (client, client -> active); memset (&ds, 0, sizeof ds); oc = lookup_option (&dhcp_universe, client -> active -> options, DHO_DHCP_SERVER_IDENTIFIER); if (oc && evaluate_option_cache (&ds, (struct packet *)0, (struct lease *)0, client, (struct option_state *)0, client -> active -> options, &global_scope, oc, MDL)) { if (ds.len > 3) { memcpy (client -> destination.iabuf, ds.data, 4); client -> destination.len = 4; } else client -> destination = iaddr_broadcast; data_string_forget (&ds, MDL); } else client -> destination = iaddr_broadcast; client -> first_sending = cur_time; client -> interval = client -> config -> initial_interval; /* Zap the medium list... */ client -> medium = (struct string_list *)0; /* Send out the first and only DHCPRELEASE packet. */ send_release (client); /* Do the client script RELEASE operation. */ script_init (client, "RELEASE", (struct string_list *)0); if (client -> alias) script_write_params(client, "alias_", client -> alias); script_write_params(client, "old_", client -> active); script_write_requested(client); script_go(client); } /* Cancel any timeouts. */ cancel_timeout (state_bound, client); cancel_timeout (send_discover, client); cancel_timeout (state_init, client); cancel_timeout (send_request, client); cancel_timeout (state_reboot, client); client -> state = S_STOPPED; #if defined(DHCPv6) && defined(DHCP4o6) if (dhcpv4_over_dhcpv6) finish(0); #endif } int dhclient_interface_shutdown_hook (struct interface_info *interface) { do_release (interface -> client); return 1; } int dhclient_interface_discovery_hook (struct interface_info *tmp) { struct interface_info *last, *ip; /* See if we can find the client from dummy_interfaces */ last = 0; for (ip = dummy_interfaces; ip; ip = ip -> next) { if (!strcmp (ip -> name, tmp -> name)) { /* Remove from dummy_interfaces */ if (last) { ip = (struct interface_info *)0; interface_reference (&ip, last -> next, MDL); interface_dereference (&last -> next, MDL); if (ip -> next) { interface_reference (&last -> next, ip -> next, MDL); interface_dereference (&ip -> next, MDL); } } else { ip = (struct interface_info *)0; interface_reference (&ip, dummy_interfaces, MDL); interface_dereference (&dummy_interfaces, MDL); if (ip -> next) { interface_reference (&dummy_interfaces, ip -> next, MDL); interface_dereference (&ip -> next, MDL); } } /* Copy "client" to tmp */ if (ip -> client) { tmp -> client = ip -> client; tmp -> client -> interface = tmp; } interface_dereference (&ip, MDL); break; } last = ip; } return 1; } isc_result_t dhclient_interface_startup_hook (struct interface_info *interface) { struct interface_info *ip; struct client_state *client; /* This code needs some rethinking. It doesn't test against a signal name, and it just kind of bulls into doing something that may or may not be appropriate. */ if (interfaces) { interface_reference (&interface -> next, interfaces, MDL); interface_dereference (&interfaces, MDL); } interface_reference (&interfaces, interface, MDL); discover_interfaces (DISCOVER_UNCONFIGURED); for (ip = interfaces; ip; ip = ip -> next) { /* If interfaces were specified, don't configure interfaces that weren't specified! */ if (ip -> flags & INTERFACE_RUNNING || (ip -> flags & (INTERFACE_REQUESTED | INTERFACE_AUTOMATIC)) != INTERFACE_REQUESTED) continue; script_init (ip -> client, "PREINIT", (struct string_list *)0); if (ip -> client -> alias) script_write_params(ip -> client, "alias_", ip -> client -> alias); script_go(ip -> client); } discover_interfaces (interfaces_requested != 0 ? DISCOVER_REQUESTED : DISCOVER_RUNNING); for (ip = interfaces; ip; ip = ip -> next) { if (ip -> flags & INTERFACE_RUNNING) continue; ip -> flags |= INTERFACE_RUNNING; for (client = ip->client ; client ; client = client->next) { client->state = S_INIT; state_reboot(client); } } return ISC_R_SUCCESS; } /* The client should never receive a relay agent information option, so if it does, log it and discard it. */ int parse_agent_information_option (packet, len, data) struct packet *packet; int len; u_int8_t *data; { return 1; } /* The client never sends relay agent information options. */ unsigned cons_agent_information_options (cfg_options, outpacket, agentix, length) struct option_state *cfg_options; struct dhcp_packet *outpacket; unsigned agentix; unsigned length; { return length; } static void shutdown_exit (void *foo) { /* get rid of the pid if we can */ if (no_pid_file == ISC_FALSE) (void) unlink(path_dhclient_pid); finish(0); } #if defined (NSUPDATE) /* * If the first query fails, the updater MUST NOT delete the DNS name. It * may be that the host whose lease on the server has expired has moved * to another network and obtained a lease from a different server, * which has caused the client's A RR to be replaced. It may also be * that some other client has been configured with a name that matches * the name of the DHCP client, and the policy was that the last client * to specify the name would get the name. In this case, the DHCID RR * will no longer match the updater's notion of the client-identity of * the host pointed to by the DNS name. * -- "Interaction between DHCP and DNS" */ /* The first and second stages are pretty similar so we combine them */ void client_dns_remove_action(dhcp_ddns_cb_t *ddns_cb, isc_result_t eresult) { isc_result_t result; if ((eresult == ISC_R_SUCCESS) && (ddns_cb->state == DDNS_STATE_REM_FW_YXDHCID)) { /* Do the second stage of the FWD removal */ ddns_cb->state = DDNS_STATE_REM_FW_NXRR; result = ddns_modify_fwd(ddns_cb, MDL); if (result == ISC_R_SUCCESS) { return; } } /* If we are done or have an error clean up */ dhclient_ddns_cb_free(ddns_cb, MDL); return; } void client_dns_remove(struct client_state *client, struct iaddr *addr) { dhcp_ddns_cb_t *ddns_cb; isc_result_t result; /* if we have an old ddns request for this client, cancel it */ if (client->ddns_cb != NULL) { ddns_cancel(client->ddns_cb, MDL); client->ddns_cb = NULL; } ddns_cb = ddns_cb_alloc(MDL); if (ddns_cb != NULL) { ddns_cb->address = *addr; ddns_cb->timeout = 0; ddns_cb->state = DDNS_STATE_REM_FW_YXDHCID; ddns_cb->flags = DDNS_UPDATE_ADDR; ddns_cb->cur_func = client_dns_remove_action; result = client_dns_update(client, ddns_cb); if (result != ISC_R_TIMEDOUT) { dhclient_ddns_cb_free(ddns_cb, MDL); } } } #endif isc_result_t dhcp_set_control_state (control_object_state_t oldstate, control_object_state_t newstate) { struct interface_info *ip; struct client_state *client; struct timeval tv; if (newstate == server_shutdown) { /* Re-entry */ if (shutdown_signal == SIGUSR1) return ISC_R_SUCCESS; /* Log shutdown on signal. */ if ((shutdown_signal == SIGINT) || (shutdown_signal == SIGTERM)) { log_info("Received signal %d, initiating shutdown.", shutdown_signal); } /* Mark it was called. */ shutdown_signal = SIGUSR1; } /* Do the right thing for each interface. */ for (ip = interfaces; ip; ip = ip -> next) { for (client = ip -> client; client; client = client -> next) { switch (newstate) { case server_startup: return ISC_R_SUCCESS; case server_running: return ISC_R_SUCCESS; case server_shutdown: if (client -> active && client -> active -> expiry > cur_time) { #if defined (NSUPDATE) if (client->config->do_forward_update) { client_dns_remove(client, &client->active->address); } #endif do_release (client); } break; case server_hibernate: state_stop (client); break; case server_awaken: state_reboot (client); break; } } } if (newstate == server_shutdown) { tv.tv_sec = cur_tv.tv_sec; tv.tv_usec = cur_tv.tv_usec + 1; add_timeout(&tv, shutdown_exit, 0, 0, 0); } return ISC_R_SUCCESS; } #if defined (NSUPDATE) /* * Called after a timeout if the DNS update failed on the previous try. * Starts the retry process. If the retry times out it will schedule * this routine to run again after a 10x wait. */ void client_dns_update_timeout (void *cp) { dhcp_ddns_cb_t *ddns_cb = (dhcp_ddns_cb_t *)cp; struct client_state *client = (struct client_state *)ddns_cb->lease; isc_result_t status = ISC_R_FAILURE; if ((client != NULL) && ((client->active != NULL) || (client->active_lease != NULL))) status = client_dns_update(client, ddns_cb); /* * A status of timedout indicates that we started the update and * have released control of the control block. Any other status * indicates that we should clean up the control block. We either * got a success which indicates that we didn't really need to * send an update or some other error in which case we weren't able * to start the update process. In both cases we still own * the control block and should free it. */ if (status != ISC_R_TIMEDOUT) { dhclient_ddns_cb_free(ddns_cb, MDL); } } /* * If the first query succeeds, the updater can conclude that it * has added a new name whose only RRs are the A and DHCID RR records. * The A RR update is now complete (and a client updater is finished, * while a server might proceed to perform a PTR RR update). * -- "Interaction between DHCP and DNS" * * If the second query succeeds, the updater can conclude that the current * client was the last client associated with the domain name, and that * the name now contains the updated A RR. The A RR update is now * complete (and a client updater is finished, while a server would * then proceed to perform a PTR RR update). * -- "Interaction between DHCP and DNS" * * If the second query fails with NXRRSET, the updater must conclude * that the client's desired name is in use by another host. At this * juncture, the updater can decide (based on some administrative * configuration outside of the scope of this document) whether to let * the existing owner of the name keep that name, and to (possibly) * perform some name disambiguation operation on behalf of the current * client, or to replace the RRs on the name with RRs that represent * the current client. If the configured policy allows replacement of * existing records, the updater submits a query that deletes the * existing A RR and the existing DHCID RR, adding A and DHCID RRs that * represent the IP address and client-identity of the new client. * -- "Interaction between DHCP and DNS" */ /* The first and second stages are pretty similar so we combine them */ void client_dns_update_action(dhcp_ddns_cb_t *ddns_cb, isc_result_t eresult) { isc_result_t result; struct timeval tv; switch(eresult) { case ISC_R_SUCCESS: default: /* Either we succeeded or broke in a bad way, clean up */ break; case DNS_R_YXRRSET: /* * This is the only difference between the two stages, * check to see if it is the first stage, in which case * start the second stage */ if (ddns_cb->state == DDNS_STATE_ADD_FW_NXDOMAIN) { ddns_cb->state = DDNS_STATE_ADD_FW_YXDHCID; ddns_cb->cur_func = client_dns_update_action; result = ddns_modify_fwd(ddns_cb, MDL); if (result == ISC_R_SUCCESS) { return; } } break; case ISC_R_TIMEDOUT: /* * We got a timeout response from the DNS module. Schedule * another attempt for later. We forget the name, dhcid and * zone so if it gets changed we will get the new information. */ data_string_forget(&ddns_cb->fwd_name, MDL); data_string_forget(&ddns_cb->dhcid, MDL); if (ddns_cb->zone != NULL) { forget_zone((struct dns_zone **)&ddns_cb->zone); } /* Reset to doing the first stage */ ddns_cb->state = DDNS_STATE_ADD_FW_NXDOMAIN; ddns_cb->cur_func = client_dns_update_action; /* and update our timer */ if (ddns_cb->timeout < 3600) ddns_cb->timeout *= 10; tv.tv_sec = cur_tv.tv_sec + ddns_cb->timeout; tv.tv_usec = cur_tv.tv_usec; add_timeout(&tv, client_dns_update_timeout, ddns_cb, NULL, NULL); return; } dhclient_ddns_cb_free(ddns_cb, MDL); return; } /* See if we should do a DNS update, and if so, do it. */ isc_result_t client_dns_update(struct client_state *client, dhcp_ddns_cb_t *ddns_cb) { struct data_string client_identifier; struct option_cache *oc; int ignorep; int result; int ddns_v4_type; isc_result_t rcode; /* If we didn't send an FQDN option, we certainly aren't going to be doing an update. */ if (!client -> sent_options) return ISC_R_SUCCESS; /* If we don't have a lease, we can't do an update. */ if ((client->active == NULL) && (client->active_lease == NULL)) return ISC_R_SUCCESS; /* If we set the no client update flag, don't do the update. */ if ((oc = lookup_option (&fqdn_universe, client -> sent_options, FQDN_NO_CLIENT_UPDATE)) && evaluate_boolean_option_cache (&ignorep, (struct packet *)0, (struct lease *)0, client, client -> sent_options, (struct option_state *)0, &global_scope, oc, MDL)) return ISC_R_SUCCESS; /* If we set the "server, please update" flag, or didn't set it to false, don't do the update. */ if (!(oc = lookup_option (&fqdn_universe, client -> sent_options, FQDN_SERVER_UPDATE)) || evaluate_boolean_option_cache (&ignorep, (struct packet *)0, (struct lease *)0, client, client -> sent_options, (struct option_state *)0, &global_scope, oc, MDL)) return ISC_R_SUCCESS; /* If no FQDN option was supplied, don't do the update. */ if (!(oc = lookup_option (&fqdn_universe, client -> sent_options, FQDN_FQDN)) || !evaluate_option_cache (&ddns_cb->fwd_name, (struct packet *)0, (struct lease *)0, client, client -> sent_options, (struct option_state *)0, &global_scope, oc, MDL)) return ISC_R_SUCCESS; /* * Construct the DHCID value for use in the DDNS update process * We have the newer standard version and the older interim version * chosen by the '-I' option. The interim version is left as is * for backwards compatibility. The standard version is based on * RFC 4701 section 3.3 */ result = 0; POST(result); memset(&client_identifier, 0, sizeof(client_identifier)); if (std_dhcid == 1) { /* standard style */ ddns_cb->dhcid_class = dns_rdatatype_dhcid; ddns_v4_type = 1; } else { /* interim style */ ddns_cb->dhcid_class = dns_rdatatype_txt; /* for backwards compatibility */ ddns_v4_type = DHO_DHCP_CLIENT_IDENTIFIER; } if (client->active_lease != NULL) { /* V6 request, get the client identifier, then * construct the dhcid for either standard * or interim */ if (((oc = lookup_option(&dhcpv6_universe, client->sent_options, D6O_CLIENTID)) != NULL) && evaluate_option_cache(&client_identifier, NULL, NULL, client, client->sent_options, NULL, &global_scope, oc, MDL)) { result = get_dhcid(ddns_cb, 2, client_identifier.data, client_identifier.len); data_string_forget(&client_identifier, MDL); } else log_fatal("Impossible condition at %s:%d.", MDL); } else { /* * V4 request, use the client id if there is one or the * mac address if there isn't. If we have a client id * we check to see if it is an embedded DUID. */ if (((oc = lookup_option(&dhcp_universe, client->sent_options, DHO_DHCP_CLIENT_IDENTIFIER)) != NULL) && evaluate_option_cache(&client_identifier, NULL, NULL, client, client->sent_options, NULL, &global_scope, oc, MDL)) { if ((std_dhcid == 1) && (duid_v4 == 1) && (client_identifier.data[0] == 255)) { /* * This appears to be an embedded DUID, * extract it and treat it as such */ if (client_identifier.len <= 5) log_fatal("Impossible condition at %s:%d.", MDL); result = get_dhcid(ddns_cb, 2, client_identifier.data + 5, client_identifier.len - 5); } else { result = get_dhcid(ddns_cb, ddns_v4_type, client_identifier.data, client_identifier.len); } data_string_forget(&client_identifier, MDL); } else result = get_dhcid(ddns_cb, 0, client->interface->hw_address.hbuf, client->interface->hw_address.hlen); } if (!result) { return ISC_R_SUCCESS; } /* * Perform updates. */ if (ddns_cb->fwd_name.len && ddns_cb->dhcid.len) { rcode = ddns_modify_fwd(ddns_cb, MDL); } else rcode = ISC_R_FAILURE; /* * A success from the modify routine means we are performing * async processing, for which we use the timedout error message. */ if (rcode == ISC_R_SUCCESS) { rcode = ISC_R_TIMEDOUT; } return rcode; } /* * Schedule the first update. They will continue to retry occasionally * until they no longer time out (or fail). */ void dhclient_schedule_updates(struct client_state *client, struct iaddr *addr, int offset) { dhcp_ddns_cb_t *ddns_cb; struct timeval tv; if (!client->config->do_forward_update) return; /* cancel any outstanding ddns requests */ if (client->ddns_cb != NULL) { ddns_cancel(client->ddns_cb, MDL); client->ddns_cb = NULL; } ddns_cb = ddns_cb_alloc(MDL); if (ddns_cb != NULL) { ddns_cb->lease = (void *)client; ddns_cb->address = *addr; ddns_cb->timeout = 1; /* * XXX: DNS TTL is a problem we need to solve properly. * Until that time, 300 is a placeholder default for * something that is less insane than a value scaled * by lease timeout. */ ddns_cb->ttl = 300; ddns_cb->state = DDNS_STATE_ADD_FW_NXDOMAIN; ddns_cb->cur_func = client_dns_update_action; ddns_cb->flags = DDNS_UPDATE_ADDR | DDNS_INCLUDE_RRSET; client->ddns_cb = ddns_cb; tv.tv_sec = cur_tv.tv_sec + offset; tv.tv_usec = cur_tv.tv_usec; add_timeout(&tv, client_dns_update_timeout, ddns_cb, NULL, NULL); } else { log_error("Unable to allocate dns update state for %s", piaddr(*addr)); } } #endif void dhcpv4_client_assignments(void) { struct servent *ent; if (path_dhclient_pid == NULL) path_dhclient_pid = _PATH_DHCLIENT_PID; if (path_dhclient_db == NULL) path_dhclient_db = _PATH_DHCLIENT_DB; /* Default to the DHCP/BOOTP port. */ if (!local_port) { /* If we're faking a relay agent, and we're not using loopback, use the server port, not the client port. */ if (mockup_relay && giaddr.s_addr != htonl(INADDR_LOOPBACK)) { local_port = htons(67); } else { ent = getservbyname("dhcpc", "udp"); if (ent == NULL) ent = getservbyname("bootpc", "udp"); if (ent == NULL) local_port = htons(68); else local_port = ent->s_port; #ifndef __CYGWIN32__ endservent (); #endif } } /* If we're faking a relay agent, and we're not using loopback, we're using the server port, not the client port. */ if (mockup_relay && giaddr.s_addr != htonl(INADDR_LOOPBACK)) { remote_port = local_port; } else remote_port = htons(ntohs(local_port) - 1); /* XXX */ } /* * The following routines are used to check that certain * strings are reasonable before we pass them to the scripts. * This avoids some problems with scripts treating the strings * as commands - see ticket 23722 * The domain checking code should be done as part of assembling * the string but we are doing it here for now due to time * constraints. */ static int check_domain_name(const char *ptr, size_t len, int dots) { const char *p; /* not empty or complete length not over 255 characters */ if ((len == 0) || (len > 256)) return(-1); /* consists of [[:alnum:]-]+ labels separated by [.] */ /* a [_] is against RFC but seems to be "widely used"... */ for (p=ptr; (*p != 0) && (len-- > 0); p++) { if ((*p == '-') || (*p == '_')) { /* not allowed at begin or end of a label */ if (((p - ptr) == 0) || (len == 0) || (p[1] == '.')) return(-1); } else if (*p == '.') { /* each label has to be 1-63 characters; we allow [.] at the end ('foo.bar.') */ size_t d = p - ptr; if ((d <= 0) || (d >= 64)) return(-1); ptr = p + 1; /* jump to the next label */ if ((dots > 0) && (len > 0)) dots--; } else if (isalnum((unsigned char)*p) == 0) { /* also numbers at the begin are fine */ return(-1); } } return(dots ? -1 : 0); } static int check_domain_name_list(const char *ptr, size_t len, int dots) { const char *p; int ret = -1; /* at least one needed */ if ((ptr == NULL) || (len == 0)) return(-1); for (p=ptr; (*p != 0) && (len > 0); p++, len--) { if (*p != ' ') continue; if (p > ptr) { if (check_domain_name(ptr, p - ptr, dots) != 0) return(-1); ret = 0; } ptr = p + 1; } if (p > ptr) return(check_domain_name(ptr, p - ptr, dots)); else return(ret); } static int check_option_values(struct universe *universe, unsigned int opt, const char *ptr, size_t len) { if (ptr == NULL) return(-1); /* just reject options we want to protect, will be escaped anyway */ if ((universe == NULL) || (universe == &dhcp_universe)) { switch(opt) { case DHO_DOMAIN_NAME: #ifdef ACCEPT_LIST_IN_DOMAIN_NAME return check_domain_name_list(ptr, len, 0); #else return check_domain_name(ptr, len, 0); #endif case DHO_HOST_NAME: case DHO_NIS_DOMAIN: case DHO_NETBIOS_SCOPE: return check_domain_name(ptr, len, 0); break; case DHO_DOMAIN_SEARCH: return check_domain_name_list(ptr, len, 0); break; case DHO_ROOT_PATH: if (len == 0) return(-1); for (; (*ptr != 0) && (len-- > 0); ptr++) { if(!(isalnum((unsigned char)*ptr) || *ptr == '#' || *ptr == '%' || *ptr == '+' || *ptr == '-' || *ptr == '_' || *ptr == ':' || *ptr == '.' || *ptr == ',' || *ptr == '@' || *ptr == '~' || *ptr == '\\' || *ptr == '/' || *ptr == '[' || *ptr == ']' || *ptr == '=' || *ptr == ' ')) return(-1); } return(0); break; } } #ifdef DHCPv6 if (universe == &dhcpv6_universe) { switch(opt) { case D6O_SIP_SERVERS_DNS: case D6O_DOMAIN_SEARCH: case D6O_NIS_DOMAIN_NAME: case D6O_NISP_DOMAIN_NAME: return check_domain_name_list(ptr, len, 0); break; } } #endif return(0); } static void add_reject(struct packet *packet) { struct iaddrmatchlist *list; list = dmalloc(sizeof(struct iaddrmatchlist), MDL); if (!list) log_fatal ("no memory for reject list!"); /* * client_addr is misleading - it is set to source address in common * code. */ list->match.addr = packet->client_addr; /* Set mask to indicate host address. */ list->match.mask.len = list->match.addr.len; memset(list->match.mask.iabuf, 0xff, sizeof(list->match.mask.iabuf)); /* Append to reject list for the source interface. */ list->next = packet->interface->client->config->reject_list; packet->interface->client->config->reject_list = list; /* * We should inform user that we won't be accepting this server * anymore. */ log_info("Server added to list of rejected servers."); } /* Wrapper function around common ddns_cb_free function that ensures * we set the client_state pointer to the control block to NULL. */ static void dhclient_ddns_cb_free(dhcp_ddns_cb_t *ddns_cb, char* file, int line) { if (ddns_cb) { struct client_state *client = (struct client_state *)ddns_cb->lease; if (client != NULL) { client->ddns_cb = NULL; } ddns_cb_free(ddns_cb, file, line); } } #if defined(DHCPv6) && defined(DHCP4o6) /* * \brief Omapi I/O handler * * The inter-process communication receive handler. * * On the DHCPv6 side, the message is either a POLL (which is answered * by a START or a STOP) or a DHCPv4-QUERY (which is forwarded to * DHCPv4 over DHCPv6 servers by forw_dhcpv4_query()). * * On the DHCPv4 side, the message is either a START, a STOP * (both for the DHCP4 over DHCPv6 state machine) or a DHCPv4-RESPONSE * (which is processed by recv_dhcpv4_response()). * * \param h the OMAPI object * \return a result for I/O success or error (used by the I/O subsystem) */ isc_result_t dhcpv4o6_handler(omapi_object_t *h) { char buf[65536]; char start_msg[5] = { 'S', 'T', 'A', 'R', 'T' }; char stop_msg[4] = { 'S', 'T', 'O', 'P' }; char poll_msg[4] = { 'P', 'O', 'L', 'L' }; struct data_string raw; int cc; if (h->type != dhcp4o6_type) return DHCP_R_INVALIDARG; cc = recv(dhcp4o6_fd, buf, sizeof(buf), 0); if (cc <= 0) return ISC_R_UNEXPECTED; if (local_family == AF_INET6) { if ((cc == 4) && (memcmp(buf, poll_msg, sizeof(poll_msg)) == 0)) { log_info("RCV: POLL"); if (dhcp4o6_state < 0) cc = send(dhcp4o6_fd, stop_msg, sizeof(stop_msg), 0); else cc = send(dhcp4o6_fd, start_msg, sizeof(start_msg), 0); if (cc < 0) { log_error("dhcpv4o6_handler: send(): %m"); return ISC_R_IOERROR; } } else { if (cc < DHCP_FIXED_NON_UDP + 8) return ISC_R_UNEXPECTED; memset(&raw, 0, sizeof(raw)); if (!buffer_allocate(&raw.buffer, cc, MDL)) { log_error("dhcpv4o6_handler: " "no memory buffer."); return ISC_R_NOMEMORY; } raw.data = raw.buffer->data; raw.len = cc; memcpy(raw.buffer->data, buf, cc); forw_dhcpv4_query(&raw); data_string_forget(&raw, MDL); } } else { if ((cc == 4) && (memcmp(buf, stop_msg, sizeof(stop_msg)) == 0)) { log_info("RCV: STOP"); if (dhcp4o6_state > 0) { dhcp4o6_state = 0; dhcp4o6_poll(NULL); } } else if ((cc == 5) && (memcmp(buf, start_msg, sizeof(start_msg)) == 0)) { log_info("RCV: START"); if (dhcp4o6_state == 0) cancel_timeout(dhcp4o6_poll, NULL); dhcp4o6_state = 1; dhcp4o6_resume(); } else { if (cc < DHCP_FIXED_NON_UDP + 16) return ISC_R_UNEXPECTED; memset(&raw, 0, sizeof(raw)); if (!buffer_allocate(&raw.buffer, cc, MDL)) { log_error("dhcpv4o6_handler: " "no memory buffer."); return ISC_R_NOMEMORY; } raw.data = raw.buffer->data; raw.len = cc; memcpy(raw.buffer->data, buf, cc); recv_dhcpv4_response(&raw); data_string_forget(&raw, MDL); } } return ISC_R_SUCCESS; } /* * \brief Poll the DHCPv6 client * (DHCPv4 client function) * * A POLL message is sent to the DHCPv6 client periodically to check * if the DHCPv6 is ready (i.e., has a valid DHCPv4-over-DHCPv6 server * address option). */ static void dhcp4o6_poll(void *dummy) { char msg[4] = { 'P', 'O', 'L', 'L' }; struct timeval tv; int cc; IGNORE_UNUSED(dummy); if (dhcp4o6_state < 0) dhcp4o6_state = 0; log_info("POLL"); cc = send(dhcp4o6_fd, msg, sizeof(msg), 0); if (cc < 0) log_error("dhcp4o6_poll: send(): %m"); tv.tv_sec = cur_time + 60; tv.tv_usec = random() % 1000000; add_timeout(&tv, dhcp4o6_poll, NULL, 0, 0); } /* * \brief Resume pending operations * (DHCPv4 client function) * * A START message was received from the DHCPv6 client so pending * operations (RELEASE or REBOOT) must be resumed. */ static void dhcp4o6_resume() { struct interface_info *ip; struct client_state *client; for (ip = interfaces; ip != NULL; ip = ip->next) { for (client = ip->client; client != NULL; client = client->next) { if (client->pending == P_RELEASE) do_release(client); else if (client->pending == P_REBOOT) state_reboot(client); } } } /* * \brief Send a START to the DHCPv4 client * (DHCPv6 client function) * * First check if there is a valid DHCPv4-over-DHCPv6 server address option, * and when found go UP and on a transition from another state send * a START message to the DHCPv4 client. */ void dhcp4o6_start() { struct interface_info *ip; struct client_state *client; struct dhc6_lease *lease; struct option_cache *oc; struct data_string addrs; char msg[5] = { 'S', 'T', 'A', 'R', 'T' }; int cc; memset(&addrs, 0, sizeof(addrs)); for (ip = interfaces; ip != NULL; ip = ip->next) { for (client = ip->client; client != NULL; client = client->next) { if ((client->state != S_BOUND) && (client->state != S_RENEWING) && (client->state != S_REBINDING)) continue; lease = client->active_lease; if ((lease == NULL) || lease->released) continue; oc = lookup_option(&dhcpv6_universe, lease->options, D6O_DHCP4_O_DHCP6_SERVER); if ((oc == NULL) || !evaluate_option_cache(&addrs, NULL, NULL, NULL, lease->options, NULL, &global_scope, oc, MDL)) continue; if ((addrs.len % 16) != 0) { data_string_forget(&addrs, MDL); continue; } data_string_forget(&addrs, MDL); goto found; } } log_info("dhcp4o6_start: failed"); dhcp4o6_stop(); return; found: if (dhcp4o6_state == 1) return; log_info("dhcp4o6_start: go to UP"); dhcp4o6_state = 1; cc = send(dhcp4o6_fd, msg, sizeof(msg), 0); if (cc < 0) log_info("dhcp4o6_start: send(): %m"); } /* * Send a STOP to the DHCPv4 client * (DHCPv6 client function) * * Go DOWN and on a transition from another state send a STOP message * to the DHCPv4 client. */ static void dhcp4o6_stop() { char msg[4] = { 'S', 'T', 'O', 'P' }; int cc; if (dhcp4o6_state == -1) return; log_info("dhcp4o6_stop: go to DOWN"); dhcp4o6_state = -1; cc = send(dhcp4o6_fd, msg, sizeof(msg), 0); if (cc < 0) log_error("dhcp4o6_stop: send(): %m"); } #endif /* DHCPv6 && DHCP4o6 */ dhcp-4.4.1/client/dhclient.conf.5000644 000765 000024 00000075524 13243301226 017012 0ustar00tmarkstaff000000 000000 .\" $Id: dhclient.conf.5,v 1.34 2012/01/24 22:23:39 sar Exp $ .\" .\" Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") .\" Copyright (c) 1996-2003 by Internet Software Consortium .\" .\" 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/. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT .\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" Internet Systems Consortium, Inc. .\" 950 Charter Street .\" Redwood City, CA 94063 .\" .\" https://www.isc.org/ .\" .\" Support and other services are available for ISC products - see .\" https://www.isc.org for more information or to learn more about ISC. .\" .TH dhclient.conf 5 .SH NAME dhclient.conf - DHCP client configuration file .SH DESCRIPTION The dhclient.conf file contains configuration information for .IR dhclient, the Internet Systems Consortium DHCP Client. .PP The dhclient.conf file is a free-form ASCII text file. It is parsed by the recursive-descent parser built into dhclient. The file may contain extra tabs and newlines for formatting purposes. Keywords in the file are case-insensitive. Comments may be placed anywhere within the file (except within quotes). Comments begin with the # character and end at the end of the line. .PP The dhclient.conf file can be used to configure the behaviour of the client in a wide variety of ways: protocol timing, information requested from the server, information required of the server, defaults to use if the server does not provide certain information, values with which to override information provided by the server, or values to prepend or append to information provided by the server. The configuration file can also be preinitialized with addresses to use on networks that don't have DHCP servers. .SH PROTOCOL TIMING The timing behaviour of the client need not be configured by the user. If no timing configuration is provided by the user, a fairly reasonable timing behaviour will be used by default - one which results in fairly timely updates without placing an inordinate load on the server. .PP If required the following statements can be used to adjust the timing behaviour of the DHCPv4 client. The DHCPv6 protocol provides values to use and they are not currently configurable. .PP .I The .B timeout .I statement .PP \fBtimeout \fItime\fR\fB;\fR .PP The .I timeout statement determines the amount of time that must pass between the time that the client begins to try to determine its address and the time that it decides that it's not going to be able to contact a server. By default, this timeout is sixty seconds. After the timeout has passed, if there are any static leases defined in the configuration file, or any leases remaining in the lease database that have not yet expired, the client will loop through these leases attempting to validate them, and if it finds one that appears to be valid, it will use that lease's address. If there are no valid static leases or unexpired leases in the lease database, the client will restart the protocol after the defined retry interval. .PP .I The .B retry .I statement .PP \fBretry \fItime\fR\fB;\fR .PP The .I retry statement determines the time that must pass after the client has determined that there is no DHCP server present before it tries again to contact a DHCP server. By default, this is five minutes. .PP .I The .B select-timeout .I statement .PP \fBselect-timeout \fItime\fR\fB;\fR .PP It is possible (some might say desirable) for there to be more than one DHCP server serving any given network. In this case, it is possible that a client may be sent more than one offer in response to its initial lease discovery message. It may be that one of these offers is preferable to the other (e.g., one offer may have the address the client previously used, and the other may not). .PP The .I select-timeout is the time after the client sends its first lease discovery request at which it stops waiting for offers from servers, assuming that it has received at least one such offer. If no offers have been received by the time the .I select-timeout has expired, the client will accept the first offer that arrives. .PP By default, the select-timeout is zero seconds - that is, the client will take the first offer it sees. .PP .I The .B reboot .I statement .PP \fBreboot \fItime\fR\fB;\fR .PP When the client is restarted, it first tries to reacquire the last address it had. This is called the INIT-REBOOT state. If it is still attached to the same network it was attached to when it last ran, this is the quickest way to get started. The .I reboot statement sets the time that must elapse after the client first tries to reacquire its old address before it gives up and tries to discover a new address. By default, the reboot timeout is ten seconds. .PP .I The .B backoff-cutoff .I statement .PP \fBbackoff-cutoff \fItime\fR\fB;\fR .PP The client uses an exponential backoff algorithm with some randomness, so that if many clients try to configure themselves at the same time, they will not make their requests in lockstep. The .I backoff-cutoff statement determines the maximum amount of time that the client is allowed to back off, the actual value will be evaluated randomly between 1/2 to 1 1/2 times the \fItime\fR specified. It defaults to fifteen seconds. .PP .I The .B initial-interval .I statement .PP \fBinitial-interval \fItime\fR\fB;\fR .PP The .I initial-interval statement sets the amount of time between the first attempt to reach a server and the second attempt to reach a server. Each time a message is sent, the interval between messages is incremented by twice the current interval multiplied by a random number between zero and one. If it is greater than the backoff-cutoff amount, it is set to that amount. It defaults to ten seconds. .PP .I The initial-delay .I statement .PP \fBinitial-delay \fItime\fR\fB;\fR .PP .I initial-delay parameter sets the maximum time client can wait after start before commencing first transmission. According to RFC2131 Section 4.4.1, client should wait a random time between startup and the actual first transmission. Previous versions of ISC DHCP client used to wait random time up to 5 seconds, but that was unwanted due to impact on startup time. As such, new versions have the default initial delay set to 0. To restore old behavior, please set initial-delay to 5. .SH DHCPv6 LEASE SELECTION In the DHCPv6 protocol the client will wait a small amount of time to allow ADVERTISE messages from multiple servers to arrive. It will then need to choose from all of the messages that may have arrived before proceeding to making a request of the selected server. The first selection criteria is the set of options and addresses in the message. Messages that don't include an option specified as required will be given a score of 0 and not used. If the \fI-R\fR option is given on the command line then messages that don't include the correct number of bindings (IA-NA, IA-TA or IA-PD) will be discarded. The next criteria is the preference value from the message. With the highest preference value being used even if leases with better addresses or options are available. Finally the lease is scored and the lease with the highest score is selected. A lease's score is based on the number of bindings, number of addresses and number of options it contains: .nf bindings * X + addresses * Y + options .fi By default X = 10000 and Y = 100, this will cause the client to select a lease with more bindings over a lease with less bindings but more addresses. The weightings were changed as part of implementing RFC 7550. Previously they were X = 50 and Y = 100 meaning more addresses were preferred over more bindings. If you wish to continue using the old style you may do so by editing the file includes/site.h and uncommenting the define for USE_ORIGINAL_CLIENT_LEASE_WEIGHTS. .SH LEASE REQUIREMENTS AND REQUESTS The DHCP protocol allows the client to request that the server send it specific information, and not send it other information that it is not prepared to accept. The protocol also allows the client to reject offers from servers if they don't contain information the client needs, or if the information provided is not satisfactory. .PP There is a variety of data contained in offers that DHCP servers send to DHCP clients. The data that can be specifically requested is what are called \fIDHCP Options\fR. DHCP Options are defined in \fBdhcp-options(5)\fR. .PP .I The .B request .I statement .PP \fB[ also ] request [ [ \fIoption-space\fR . ] \fIoption\fR ] [\fB,\fI ... ]\fB;\fR .PP The request statement causes the client to request that any server responding to the client send the client its values for the specified options. Only the option names should be specified in the request statement - not option parameters. By default, the DHCPv4 client requests the subnet-mask, broadcast-address, time-offset, routers, domain-name, domain-name-servers and host-name options while the DHCPv6 client requests the dhcp6 name-servers and domain-search options. Note that if you enter a \'request\' statement, you over-ride these defaults and these options will not be requested. .PP In some cases, it may be desirable to send no parameter request list at all. To do this, simply write the request statement but specify no parameters: .PP .nf request; .fi .PP In most cases, it is desirable to simply add one option to the request list which is of interest to the client in question. In this case, it is best to \'also request\' the additional options: .PP .nf also request domain-search, dhcp6.sip-servers-addresses; .fi .PP .I The .B require .I statement .PP \fB[ also ] require [ [ \fIoption-space\fR . ] \fIoption\fR ] [\fB,\fI ... ]\fB;\fR .PP The require statement lists options that must be sent in order for an offer to be accepted. Offers that do not contain all the listed options will be ignored. There is no default require list. .PP .nf require name-servers; interface eth0 { also require domain-search; } .fi .PP .I The .B send .I statement .PP \fBsend [ \fIoption declaration\fR ] \fB;\fR .PP The send statement causes the client to send the specified option to the server with the specified value. This is a full option declaration as described in \fBdhcp-options(5)\fR. Options that are always sent in the DHCP protocol should not be specified here, except that the client can specify a requested \fBdhcp-lease-time\fR option other than the default requested lease time, which is two hours. The other obvious use for this statement is to send information to the server that will allow it to differentiate between this client and other clients or kinds of clients. .SH DYNAMIC DNS The client now has some very limited support for doing DNS updates when a lease is acquired. This is prototypical, and probably doesn't do what you want. It also only works if you happen to have control over your DNS server, which isn't very likely. .PP Note that everything in this section is true whether you are using DHCPv4 or DHCPv6. The exact same syntax is used for both. .PP To make it work, you have to declare a key and zone as in the DHCP server (see \fBdhcpd.conf\fR(5) for details). You also need to configure the \fIfqdn\fR option on the client, as follows: .PP .nf send fqdn.fqdn "grosse.example.com."; send fqdn.encoded on; send fqdn.server-update off; also request fqdn, dhcp6.fqdn; .fi .PP The \fIfqdn.fqdn\fR option \fBMUST\fR be a fully-qualified domain name. You \fBMUST\fR define a zone statement for the zone to be updated. The \fIfqdn.encoded\fR option may need to be set to \fIon\fR or \fIoff\fR, depending on the DHCP server you are using. .PP .I The .B do-forward-updates .I statement .PP \fBdo-forward-updates [ \fIflag\fR ] \fB;\fR .PP If you want to do DNS updates in the DHCP client script (see \fBdhclient-script(8)\fR) rather than having the DHCP client do the update directly (for example, if you want to use SIG(0) authentication, which is not supported directly by the DHCP client, you can instruct the client not to do the update using the \fBdo-forward-updates\fR statement. \fIFlag\fR should be \fBtrue\fR if you want the DHCP client to do the update, and \fBfalse\fR if you don't want the DHCP client to do the update. By default, the DHCP client will do the DNS update. .SH OPTION MODIFIERS In some cases, a client may receive option data from the server which is not really appropriate for that client, or may not receive information that it needs, and for which a useful default value exists. It may also receive information which is useful, but which needs to be supplemented with local information. To handle these needs, several option modifiers are available. .PP .I The .B default .I statement .PP \fBdefault [ \fIoption declaration\fR ] \fB;\fR .PP If for some option the client should use the value supplied by the server, but needs to use some default value if no value was supplied by the server, these values can be defined in the .B default statement. .PP .I The .B supersede .I statement .PP \fBsupersede [ \fIoption declaration\fR ] \fB;\fR .PP If for some option the client should always use a locally-configured value or values rather than whatever is supplied by the server, these values can be defined in the .B supersede statement. .PP .I The .B prepend .I statement .PP \fBprepend [ \fIoption declaration\fR ] \fB;\fR .PP If for some set of options the client should use a value you supply, and then use the values supplied by the server, if any, these values can be defined in the .B prepend statement. The .B prepend statement can only be used for options which allow more than one value to be given. This restriction is not enforced - if you ignore it, the behaviour will be unpredictable. .PP .I The .B append .I statement .PP \fBappend [ \fIoption declaration\fR ] \fB;\fR .PP If for some set of options the client should first use the values supplied by the server, if any, and then use values you supply, these values can be defined in the .B append statement. The .B append statement can only be used for options which allow more than one value to be given. This restriction is not enforced - if you ignore it, the behaviour will be unpredictable. .SH LEASE DECLARATIONS .PP .I The .B lease .I declaration .PP \fBlease {\fR \fIlease-declaration\fR [ ... \fIlease-declaration ] \fB}\fR .PP The DHCP client may decide after some period of time (see \fBPROTOCOL TIMING\fR) that it is not going to succeed in contacting a server. At that time, it consults its own database of old leases and tests each one that has not yet timed out by pinging the listed router for that lease to see if that lease could work. It is possible to define one or more \fIfixed\fR leases in the client configuration file for networks where there is no DHCP or BOOTP service, so that the client can still automatically configure its address. This is done with the .B lease statement. .PP NOTE: the lease statement is also used in the dhclient.leases file in order to record leases that have been received from DHCP servers. Some of the syntax for leases as described below is only needed in the dhclient.leases file. Such syntax is documented here for completeness. .PP A lease statement consists of the lease keyword, followed by a left curly brace, followed by one or more lease declaration statements, followed by a right curly brace. The following lease declarations are possible: .PP \fBbootp;\fR .PP The .B bootp statement is used to indicate that the lease was acquired using the BOOTP protocol rather than the DHCP protocol. It is never necessary to specify this in the client configuration file. The client uses this syntax in its lease database file. .PP \fBinterface\fR \fB"\fR\fIstring\fR\fB";\fR .PP The .B interface lease statement is used to indicate the interface on which the lease is valid. If set, this lease will only be tried on a particular interface. When the client receives a lease from a server, it always records the interface number on which it received that lease. If predefined leases are specified in the dhclient.conf file, the interface should also be specified, although this is not required. .PP \fBfixed-address\fR \fIip-address\fR\fB;\fR .PP The .B fixed-address statement is used to set the ip address of a particular lease. This is required for all lease statements. The IP address must be specified as a dotted quad (e.g., 12.34.56.78). .PP \fBfilename "\fR\fIstring\fR\fB";\fR .PP The .B filename statement specifies the name of the boot filename to use. This is not used by the standard client configuration script, but is included for completeness. .PP \fBserver-name "\fR\fIstring\fR\fB";\fR .PP The .B server-name statement specifies the name of the boot server name to use. This is also not used by the standard client configuration script. .PP \fBoption\fR \fIoption-declaration\fR\fB;\fR .PP The .B option statement is used to specify the value of an option supplied by the server, or, in the case of predefined leases declared in dhclient.conf, the value that the user wishes the client configuration script to use if the predefined lease is used. .PP \fBscript "\fIscript-name\fB";\fR .PP The .B script statement is used to specify the pathname of the dhcp client configuration script. This script is used by the dhcp client to set each interface's initial configuration prior to requesting an address, to test the address once it has been offered, and to set the interface's final configuration once a lease has been acquired. If no lease is acquired, the script is used to test predefined leases, if any, and also called once if no valid lease can be identified. For more information, see .B dhclient-script(8). .PP \fBvendor option space "\fIname\fB";\fR .PP The .B vendor option space statement is used to specify which option space should be used for decoding the vendor-encapsulate-options option if one is received. The \fIdhcp-vendor-identifier\fR can be used to request a specific class of vendor options from the server. See .B dhcp-options(5) for details. .PP \fBmedium "\fImedia setup\fB";\fR .PP The .B medium statement can be used on systems where network interfaces cannot automatically determine the type of network to which they are connected. The media setup string is a system-dependent parameter which is passed to the dhcp client configuration script when initializing the interface. On Unix and Unix-like systems, the argument is passed on the ifconfig command line when configuring the interface. .PP The dhcp client automatically declares this parameter if it uses a media type (see the .B media statement) when configuring the interface in order to obtain a lease. This statement should be used in predefined leases only if the network interface requires media type configuration. .PP \fBrenew\fR \fIdate\fB;\fR .PP \fBrebind\fR \fIdate\fB;\fR .PP \fBexpire\fR \fIdate\fB;\fR .PP The \fBrenew\fR statement defines the time at which the dhcp client should begin trying to contact its server to renew a lease that it is using. The \fBrebind\fR statement defines the time at which the dhcp client should begin to try to contact \fIany\fR dhcp server in order to renew its lease. The \fBexpire\fR statement defines the time at which the dhcp client must stop using a lease if it has not been able to contact a server in order to renew it. .PP These declarations are automatically set in leases acquired by the DHCP client, but must also be configured in predefined leases - a predefined lease whose expiry time has passed will not be used by the DHCP client. .PP Dates are specified in one of two ways. The software will output times in these two formats depending on if the \fBdb-time-format\fR configuration parameter has been set to \fIdefault\fR or \fIlocal\fR. .PP If it is set to \fIdefault\fR, then \fIdate\fR values appear as follows: .PP \fI \fB/\fI\fB/\fI \fB:\fI\fB:\fI\fR .PP The weekday is present to make it easy for a human to tell when a lease expires - it's specified as a number from zero to six, with zero being Sunday. When declaring a predefined lease, it can always be specified as zero. The year is specified with the century, so it should generally be four digits except for really long leases. The month is specified as a number starting with 1 for January. The day of the month is likewise specified starting with 1. The hour is a number between 0 and 23, the minute a number between 0 and 59, and the second also a number between 0 and 59. .PP If the \fBdb-time-format\fR configuration was set to \fIlocal\fR, then the \fIdate\fR values appear as follows: .PP \fBepoch\fR \fI\fR\fB; #\fR \fI \fR\fB:\fR\fI\fR\fB:\fR\fI \fR .PP The \fIseconds-since-epoch\fR is as according to the system's local clock (often referred to as "unix time"). The \fB#\fR symbol supplies a comment that describes what actual time this is as according to the system's configured timezone, at the time the value was written. It is provided only for human inspection, the epoch time is the only recommended value for machine inspection. .PP Note that when defining a static lease, one may use either time format one wishes, and need not include the comment or values after it. .PP If the time is infinite in duration, then the \fIdate\fR is \fBnever\fR instead of an actual date. .SH ALIAS DECLARATIONS \fBalias { \fI declarations ... \fB}\fR .PP Some DHCP clients running TCP/IP roaming protocols may require that in addition to the lease they may acquire via DHCP, their interface also be configured with a predefined IP alias so that they can have a permanent IP address even while roaming. The Internet Systems Consortium DHCP client doesn't support roaming with fixed addresses directly, but in order to facilitate such experimentation, the dhcp client can be set up to configure an IP alias using the .B alias declaration. .PP The alias declaration resembles a lease declaration, except that options other than the subnet-mask option are ignored by the standard client configuration script, and expiry times are ignored. A typical alias declaration includes an interface declaration, a fixed-address declaration for the IP alias address, and a subnet-mask option declaration. A medium statement should never be included in an alias declaration. .SH OTHER DECLARATIONS \fBdb-time-format\fR [ \fIdefault\fR | \fIlocal\fR ] \fB;\fR .PP The \fBdb-time-format\fR option determines which of two output methods are used for printing times in leases files. The \fIdefault\fR format provides day-and-time in UTC, whereas \fIlocal\fR uses a seconds-since-epoch to store the time value, and helpfully places a local timezone time in a comment on the same line. The formats are described in detail in this manpage, within the LEASE DECLARATIONS section. .PP The .I lease-id-format parameter .RS 0.25i .PP .B lease-id-format \fIformat\fB;\fR .PP The \fIformat\fR parameter must be either \fBoctal\fR or \fBhex\fR. This parameter governs the format used to write certain values to lease files. With the default format, octal, values are written as quoted strings in which non-printable characters are represented as octal escapes - a backslash character followed by three octal digits. When the hex format is specified, values are written as an unquoted series of hexadecimal digit pairs, separated by colons. Currently, the values written out based on lease-id-format are the default-duid and the IAID value (DHCPv6 only). The client automatically reads the values in either format. Note that when the format is octal, rather than as an octal string, IAID is output as hex if it contains no printable characters or as a string if contains only printable characters. This is done to maintain backward compatibility. .PP \fBreject \fIcidr-ip-address\fR [\fB,\fR \fI...\fB \fIcidr-ip-address\fR ] \fB;\fR .PP The .B reject statement causes the DHCP client to reject offers from servers whose server identifier matches any of the specified hosts or subnets. This can be used to avoid being configured by rogue or misconfigured dhcp servers, although it should be a last resort - better to track down the bad DHCP server and fix it. .PP The \fIcidr-ip-address\fR configuration type is of the form \fIip-address\fR[\fB/\fIprefixlen\fR], where \fIip-address\fR is a dotted quad IP address, and \fRprefixlen\fR is the CIDR prefix length of the subnet, counting the number of significant bits in the netmask starting from the leftmost end. Example configuration syntax: .PP .I \fIreject\fR 192.168.0.0\fB/\fR16\fB,\fR 10.0.0.5\fB;\fR .PP The above example would cause offers from any server identifier in the entire RFC 1918 "Class C" network 192.168.0.0/16, or the specific single address 10.0.0.5, to be rejected. .PP \fBinterface "\fIname\fB" { \fIdeclarations ... \fB } .PP A client with more than one network interface may require different behaviour depending on which interface is being configured. All timing parameters and declarations other than lease and alias declarations can be enclosed in an interface declaration, and those parameters will then be used only for the interface that matches the specified name. Interfaces for which there is no interface declaration will use the parameters declared outside of any interface declaration, or the default settings. .PP .B Note well: ISC dhclient only maintains one list of interfaces, which is either determined at startup from command line arguments, or otherwise is autodetected. If you supplied the list of interfaces on the command line, this configuration clause will add the named interface to the list in such a way that will cause it to be configured by DHCP. Which may not be the result you had intended. This is an undesirable side effect that will be addressed in a future release. .PP \fBpseudo "\fIname\fR" "\fIreal-name\fB" { \fIdeclarations ... \fB } .PP Under some circumstances it can be useful to declare a pseudo-interface and have the DHCP client acquire a configuration for that interface. Each interface that the DHCP client is supporting normally has a DHCP client state machine running on it to acquire and maintain its lease. A pseudo-interface is just another state machine running on the interface named \fIreal-name\fR, with its own lease and its own state. If you use this feature, you must provide a client identifier for both the pseudo-interface and the actual interface, and the two identifiers must be different. You must also provide a separate client script for the pseudo-interface to do what you want with the IP address. For example: .PP .nf interface "ep0" { send dhcp-client-identifier "my-client-ep0"; } pseudo "secondary" "ep0" { send dhcp-client-identifier "my-client-ep0-secondary"; script "/etc/dhclient-secondary"; } .fi .PP The client script for the pseudo-interface should not configure the interface up or down - essentially, all it needs to handle are the states where a lease has been acquired or renewed, and the states where a lease has expired. See \fBdhclient-script(8)\fR for more information. .PP \fBmedia "\fImedia setup\fB"\fI [ \fB, "\fImedia setup\fB", \fI... ]\fB;\fR .PP The .B media statement defines one or more media configuration parameters which may be tried while attempting to acquire an IP address. The dhcp client will cycle through each media setup string on the list, configuring the interface using that setup and attempting to boot, and then trying the next one. This can be used for network interfaces which aren't capable of sensing the media type unaided - whichever media type succeeds in getting a request to the server and hearing the reply is probably right (no guarantees). .PP The media setup is only used for the initial phase of address acquisition (the DHCPDISCOVER and DHCPOFFER packets). Once an address has been acquired, the dhcp client will record it in its lease database and will record the media type used to acquire the address. Whenever the client tries to renew the lease, it will use that same media type. The lease must expire before the client will go back to cycling through media types. .PP \fBhardware\fR \fIlink-type mac-address\fR\fB;\fR .PP The .B hardware statement defines the hardware MAC address to use for this interface, for DHCP servers or relays to direct their replies. dhclient will determine the interface's MAC address automatically, so use of this parameter is not recommended. The \fIlink-type\fR corresponds to the interface's link layer type (example: \'ethernet\'), while the \fImac-address\fR is a string of colon-separated hexadecimal values for octets. .PP \fBanycast-mac\fR \fIlink-type mac-address\fR\fB;\fR .PP The .B anycast-mac statement over-rides the all-ones broadcast MAC address dhclient will use when it is transmitting packets to the all-ones limited broadcast IPv4 address. This configuration parameter is useful to reduce the number of broadcast packets transmitted by DHCP clients, but is only useful if you know the DHCP service(s) anycast MAC address prior to configuring your client. The \fIlink-type\fR and \fImac-address\fR parameters are configured in a similar manner to the \fBhardware\fR statement. .PP .SH SAMPLE The following configuration file was used on a laptop running NetBSD 1.3, though the domains have been modified. The laptop has an IP alias of 192.5.5.213, and has one interface, ep0 (a 3com 3C589C). Booting intervals have been shortened somewhat from the default, because the client is known to spend most of its time on networks with little DHCP activity. The laptop does roam to multiple networks. .nf timeout 60; retry 60; reboot 10; select-timeout 5; initial-interval 2; reject 192.33.137.209; interface "ep0" { send host-name "andare.example.com"; hardware ethernet 00:a0:24:ab:fb:9c; send dhcp-client-identifier 1:0:a0:24:ab:fb:9c; send dhcp-lease-time 3600; supersede domain-search "example.com", "rc.isc.org", "home.isc.org"; prepend domain-name-servers 127.0.0.1; request subnet-mask, broadcast-address, time-offset, routers, domain-name, domain-name-servers, host-name; require subnet-mask, domain-name-servers; script "CLIENTBINDIR/dhclient-script"; media "media 10baseT/UTP", "media 10base2/BNC"; } alias { interface "ep0"; fixed-address 192.5.5.213; option subnet-mask 255.255.255.255; } .fi This is a very complicated dhclient.conf file - in general, yours should be much simpler. In many cases, it's sufficient to just create an empty dhclient.conf file - the defaults are usually fine. .SH SEE ALSO dhcp-options(5), dhcp-eval(5), dhclient.leases(5), dhcpd(8), dhcpd.conf(5), RFC2132, RFC2131. .SH AUTHOR .B dhclient(8) Information about Internet Systems Consortium can be found at .B https://www.isc.org. dhcp-4.4.1/client/dhclient.conf.example000644 000765 000024 00000001772 13243301226 020273 0ustar00tmarkstaff000000 000000 send host-name = pick-first-value(gethostname(), "ISC-dhclient"); send dhcp-client-identifier 1:0:a0:24:ab:fb:9c; send dhcp-lease-time 3600; supersede domain-search "example.com", "isc.org"; prepend domain-name-servers 127.0.0.1; request subnet-mask, broadcast-address, time-offset, routers, domain-name, domain-name-servers, host-name; require subnet-mask, domain-name-servers; timeout 60; retry 60; reboot 10; select-timeout 5; initial-interval 2; script "/etc/dhclient-script"; media "-link0 -link1 -link2", "link0 link1"; reject 192.33.137.209; alias { interface "ep0"; fixed-address 192.5.5.213; option subnet-mask 255.255.255.255; } lease { interface "ep0"; fixed-address 192.33.137.200; medium "link0 link1"; option host-name "andare.example.com"; option subnet-mask 255.255.255.0; option broadcast-address 192.33.137.255; option routers 192.33.137.250; option domain-name-servers 127.0.0.1; renew 2 2000/1/12 00:00:01; rebind 2 2000/1/12 00:00:01; expire 2 2000/1/12 00:00:01; } dhcp-4.4.1/client/dhclient.leases.5000644 000765 000024 00000004025 13243301226 017325 0ustar00tmarkstaff000000 000000 .\" $Id: dhclient.leases.5,v 1.8 2011/02/23 23:50:55 sar Exp $ .\" .\" Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") .\" Copyright (c) 1997-2003 by Internet Software Consortium .\" .\" 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/. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT .\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" Internet Systems Consortium, Inc. .\" 950 Charter Street .\" Redwood City, CA 94063 .\" .\" https://www.isc.org/ .\" .\" Support and other services are available for ISC products - see .\" https://www.isc.org for more information or to learn more about ISC. .\" .\" $Id: dhclient.leases.5,v 1.8 2011/02/23 23:50:55 sar Exp $ .\" .TH dhclient.leases 5 .SH NAME dhclient.leases - DHCP client lease database .SH DESCRIPTION The Internet Systems Consortium DHCP client keeps a persistent database of leases that it has acquired that are still valid. The database is a free-form ASCII file containing one valid declaration per lease. If more than one declaration appears for a given lease, the last one in the file is used. The file is written as a log, so this is not an unusual occurrence. .PP The format of the lease declarations is described in .B dhclient.conf(5). .SH FILES .B DBDIR/dhclient.leases .SH SEE ALSO dhclient(8), dhcp-options(5), dhclient.conf(5), dhcpd(8), dhcpd.conf(5), RFC2132, RFC2131. .SH AUTHOR .B dhclient(8) Information about Internet Systems Consortium can be found at .B https://www.isc.org. dhcp-4.4.1/client/Makefile.am000644 000765 000024 00000001761 13243301226 016232 0ustar00tmarkstaff000000 000000 # We want to build this directory first, before descending into tests subdir. # The reason is that ideally the tests should link existing objects from this # directory. That eliminates any discrepancies between tested code and # production code. Sadly, we are not there yet. SUBDIRS = . tests AM_CPPFLAGS = -DCLIENT_PATH='"PATH=$(sbindir):/sbin:/bin:/usr/sbin:/usr/bin"' AM_CPPFLAGS += -DLOCALSTATEDIR='"$(localstatedir)"' dist_sysconf_DATA = dhclient.conf.example sbin_PROGRAMS = dhclient dhclient_SOURCES = client_tables.c clparse.c dhclient.c dhc6.c \ scripts/bsdos scripts/freebsd scripts/linux scripts/macos \ scripts/netbsd scripts/nextstep scripts/openbsd \ scripts/solaris scripts/openwrt dhclient_LDADD = ../common/libdhcp.@A@ ../omapip/libomapi.@A@ \ @BINDLIBIRSDIR@/libirs.@A@ \ @BINDLIBDNSDIR@/libdns.@A@ \ @BINDLIBISCCFGDIR@/libisccfg.@A@ \ @BINDLIBISCDIR@/libisc.@A@ man_MANS = dhclient.8 dhclient-script.8 dhclient.conf.5 dhclient.leases.5 EXTRA_DIST = $(man_MANS) dhcp-4.4.1/client/scripts/000755 000765 000024 00000000000 13243313033 015657 5ustar00tmarkstaff000000 000000 dhcp-4.4.1/client/tests/000755 000765 000024 00000000000 13243313033 015332 5ustar00tmarkstaff000000 000000 dhcp-4.4.1/client/tests/Atffile000644 000765 000024 00000000145 13243301226 016630 0ustar00tmarkstaff000000 000000 Content-Type: application/X-atf-atffile; version="1" prop: test-suite = dhcp4 tp-glob: *_unittests dhcp-4.4.1/client/tests/duid0_test.txt000644 000765 000024 00000001466 13243301226 020147 0ustar00tmarkstaff000000 000000 lease { interface "em1"; fixed-address 172.16.132.152; option subnet-mask 255.255.255.0; option dhcp-lease-time 60; option routers 17.132.16.200; option dhcp-message-type 5; option dhcp-server-identifier 172.16.132.200; option dhcp-renewal-time 30; option dhcp-rebinding-time 45; option domain-name "example.org"; renew 2 2014/05/13 23:11:53; rebind 2 2014/05/13 23:11:53; expire 2 2014/05/13 23:11:53; } default-duid "\000\001"; lease6 { interface "em1"; ia-na 56:32:02:02 { starts 1399436400; renew 3; rebind 15; iaaddr 2000::37a { starts 1399436400; preferred-life 18; max-life 30; } } option dhcp6.client-id 0:1:0:1:1a:1:2d:7c:0:50:56:32:2:2; option dhcp6.server-id 0:1:0:1:19:fd:aa:20:0:50:56:2f:de:8a; option dhcp6.name-servers 2000::2; } dhcp-4.4.1/client/tests/duid1_test.txt000644 000765 000024 00000000113 13243301226 020134 0ustar00tmarkstaff000000 000000 default-duid "\000\001\000\001\001\002\003\004\005\006\007\010\011\012"; dhcp-4.4.1/client/tests/duid2_test.txt000644 000765 000024 00000003233 13243301226 020143 0ustar00tmarkstaff000000 000000 default-duid "\000\003\000\001\005\006\007\010\011\012"; lease { interface "em1"; fixed-address 172.16.132.152; option subnet-mask 255.255.255.0; option dhcp-lease-time 60; option routers 17.132.16.200; option dhcp-message-type 5; option dhcp-server-identifier 172.16.132.200; option dhcp-renewal-time 30; option dhcp-rebinding-time 45; option domain-name "example.org"; renew 2 2014/05/13 22:11:53; rebind 2 2014/05/13 22:11:53; expire 2 2014/05/13 22:11:53; } lease { interface "em1"; fixed-address 172.16.132.152; option subnet-mask 255.255.255.0; option dhcp-lease-time 60; option routers 17.132.16.200; option dhcp-message-type 5; option dhcp-server-identifier 172.16.132.200; option dhcp-renewal-time 30; option dhcp-rebinding-time 45; option domain-name "example.org"; foooo; } lease { interface "em1"; fixed-address 172.16.132.152; option subnet-mask 255.255.255.0; option dhcp-lease-time 60; option routers 17.132.16.200; option dhcp-message-type 5; option dhcp-server-identifier 172.16.132.200; option dhcp-renewal-time 30; option dhcp-rebinding-time 45; option domain-name "example.org"; renew 2 2014/05/13 23:11:53; rebind 2 2014/05/13 23:11:53; expire 2 2014/05/13 23:11:53; } lease6 { interface "em1"; ia-na 56:32:02:02 { starts 1399436400; renew 3; rebind 15; iaaddr 2000::37a { starts 1399436400; preferred-life 18; max-life 30; } } option dhcp6.client-id 0:1:0:1:1a:1:2d:7c:0:50:56:32:2:2; option dhcp6.server-id 0:1:0:1:19:fd:aa:20:0:50:56:2f:de:8a; option dhcp6.name-servers 2000::2; } default-duid "\000\003\000\001\017\020\021\022\023\024"; dhcp-4.4.1/client/tests/duid3_test.txt000644 000765 000024 00000003272 13243301226 020147 0ustar00tmarkstaff000000 000000 default-duid "\000\003\000\001\005\006\007\010\011\012"; lease { interface "em1"; fixed-address 172.16.132.152; option subnet-mask 255.255.255.0; option dhcp-lease-time 60; option routers 17.132.16.200; option dhcp-message-type 5; option dhcp-server-identifier 172.16.132.200; option dhcp-renewal-time 30; option dhcp-rebinding-time 45; option domain-name "example.org"; renew 2 2014/05/13 22:11:53; rebind 2 2014/05/13 22:11:53; expire 2 2014/05/13 22:11:53; } lease { interface "em1"; fixed-address 172.16.132.152; option subnet-mask 255.255.255.0; option dhcp-lease-time 60; option routers 17.132.16.200; option dhcp-message-type 5; option dhcp-server-identifier 172.16.132.200; option dhcp-renewal-time 30; option dhcp-rebinding-time 45; option domain-name "example.org"; lease { interface "em1"; fixed-address 172.16.132.152; option subnet-mask 255.255.255.0; option dhcp-lease-time 60; option routers 17.132.16.200; option dhcp-message-type 5; option dhcp-server-identifier 172.16.132.200; option dhcp-renewal-time 30; option dhcp-rebinding-time 45; option domain-name "example.org"; renew 2 2014/05/13 23:11:53; rebind 2 2014/05/13 23:11:53; expire 2 2014/05/13 23:11:53; } default-duid "\000\001"; lease6 { interface "em1"; ia-na 56:32:02:02 { starts 1399436400; renew 3; rebind 15; iaaddr 2000::37a { starts 1399436400; preferred-life 18; max-life 30; } } option dhcp6.client-id 0:1:0:1:1a:1:2d:7c:0:50:56:32:2:2; option dhcp6.server-id 0:1:0:1:19:fd:aa:20:0:50:56:2f:de:8a; option dhcp6.name-servers 2000::2; } default-duid "\000\001\000\001\025\026\027\030\031\032\033\034\035\036"; dhcp-4.4.1/client/tests/duid_unittest.c000644 000765 000024 00000010043 13243301226 020361 0ustar00tmarkstaff000000 000000 /* * Copyright (c) 2017 by 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/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * * https://www.isc.org/ * */ #include "config.h" #include #include #include "dhcpd.h" /* Tests to see if the routine to read a secondary lease file * for the DUID works properly. The tests: * Test file x: * no test file - should result in no duid * Test filx 0: * A test file but no DUID def, no duid * Test file 1: * Can it read a single DUID in the file? * Test file 2: * Can it find a second DUID in the file after a good lease and * a badly formatted lease? * Test file 3: * Can it find a later DUID after a good one and a bad one? * and to give a bit more coverage test file 1 should use LLT * test file 2 should use LL and test file 3 should use LL for * the first one and LLT for the third one. */ int duidx_len = 0; int duid0_len = 0; int duid1_len = 14; char duid1_data[] = {0, 1, 0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int duid2_len = 10; char duid2_data[] = {0, 3, 0, 1, 15, 16, 17, 18, 19, 20}; int duid3_len = 14; char duid3_data[] = {0, 1, 0, 1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30}; ATF_TC(read_duid_test); ATF_TC_HEAD(read_duid_test, tc) { atf_tc_set_md_var(tc, "descr", "read secondary file looking for duid"); } ATF_TC_BODY(read_duid_test, tc) { static const char *srcdir; char duid_fname[1024]; /* Get the srcidr so we can find our test files */ if (atf_tc_has_config_var(tc, "srcdir")) srcdir = atf_tc_get_config_var(tc, "srcdir"); /* point the duid file at our filename space We will update it per test below */ path_dhclient_duid = duid_fname; /* Initialize client globals. */ memset(&default_duid, 0, sizeof(default_duid)); /* Try to read a nonexistent test file */ sprintf(duid_fname, "%s/duidx_test.txt", srcdir); read_client_duid(); if (default_duid.len != duidx_len) { atf_tc_fail("failed to properly read duid1"); } /* Try to read test file 0 * This doesn't have a DUID. */ sprintf(duid_fname, "%s/duid0_test.txt", srcdir); read_client_duid(); if (default_duid.len != duid0_len) { atf_tc_fail("failed to properly read duid0"); } /* Try to read test file 1 * This has a single good LLT DUID in it */ sprintf(duid_fname, "%s/duid1_test.txt", srcdir); read_client_duid(); if ((default_duid.len != duid1_len) || (memcmp(default_duid.data, duid1_data, duid1_len) != 0)) { atf_tc_fail("failed to properly read duid1"); } /* Try to read test file 2 * This has two good LL DUIDs in it with several good and bad leases between them. */ sprintf(duid_fname, "%s/duid2_test.txt", srcdir); read_client_duid(); if ((default_duid.len != duid2_len) || (memcmp(default_duid.data, duid2_data, duid2_len) != 0)) { atf_tc_fail("failed to properly read duid2"); } /* Try to read test file 3 * This has a good LL DUID, a bad LLT DUID and a good LLT DUID */ sprintf(duid_fname, "%s/duid3_test.txt", srcdir); read_client_duid(); if ((default_duid.len != duid3_len) || (memcmp(default_duid.data, duid3_data, duid3_len) != 0)) { atf_tc_fail("failed to properly read duid3"); } } ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, read_duid_test); return (atf_no_error()); } dhcp-4.4.1/client/tests/Kyuafile000644 000765 000024 00000000112 13243301226 017021 0ustar00tmarkstaff000000 000000 syntax(2) test_suite('isc-dhcp') atf_test_program{name='duid_unittests'} dhcp-4.4.1/client/tests/Makefile.am000644 000765 000024 00000003215 13243301226 017370 0ustar00tmarkstaff000000 000000 SUBDIRS = . AM_CPPFLAGS = $(ATF_CFLAGS) -DUNIT_TEST -I$(top_srcdir)/includes AM_CPPFLAGS += -I@BINDDIR@/include -I$(top_srcdir) AM_CPPFLAGS += -DLOCALSTATEDIR='"."' AM_CPPFLAGS += -DCLIENT_PATH='"."' EXTRA_DIST = Atffile Kyuafile EXTRA_DIST += duid0_test.txt duid1_test.txt duid2_test.txt duid3_test.txt # for autotools debugging only info: @echo "ATF_CFLAGS=$(ATF_CFLAGS)" @echo "ATF_LDFLAGS=$(ATF_LDFLAGS)" @echo "ATF_LIBS=$(ATF_LIBS)" DHCPSRC = ../clparse.c ../dhc6.c ../dhclient.c DHCPLIBS = $(top_builddir)/common/libdhcp.@A@ \ $(top_builddir)/omapip/libomapi.@A@ \ $(top_builddir)/dhcpctl/libdhcpctl.@A@ \ @BINDLIBIRSDIR@/libirs.@A@ \ @BINDLIBDNSDIR@/libdns.@A@ \ @BINDLIBISCCFGDIR@/libisccfg.@A@ \ @BINDLIBISCDIR@/libisc.@A@ ATF_TESTS = if HAVE_ATF ATF_TESTS += duid_unittests duid_unittests_SOURCES = $(DHCPSRC) duid_unittests_SOURCES += duid_unittest.c duid_unittests_LDADD = $(ATF_LDFLAGS) duid_unittests_LDADD += $(DHCPLIBS) check: $(ATF_TESTS) @if test $(top_srcdir) != ${top_builddir}; then \ cp $(top_srcdir)/client/tests/Atffile Atffile; \ cp $(top_srcdir)/client/tests/Kyuafile Kyuafile; \ cp $(top_srcdir)/client/tests/duid0_test.txt duid0_test.txt; \ cp $(top_srcdir)/client/tests/duid1_test.txt duid1_test.txt; \ cp $(top_srcdir)/client/tests/duid2_test.txt duid2_test.txt; \ cp $(top_srcdir)/client/tests/duid3_test.txt duid3_test.txt; \ fi sh ${top_builddir}/tests/unittest.sh distclean-local: @if test $(top_srcdir) != ${top_builddir}; then \ rm -f Atffile Kyuafile; \ rm -f duid0_test.txt duid1_test.txt; \ rm -f duid2_test.txt duid3_test.txt; \ fi endif check_PROGRAMS = $(ATF_TESTS) dhcp-4.4.1/client/scripts/bsdos000755 000765 000024 00000023106 13243301226 016722 0ustar00tmarkstaff000000 000000 #!/bin/sh make_resolv_conf() { if [ x"$new_domain_name_servers" != x ]; then cat /dev/null > /etc/resolv.conf.dhclient if [ "x$new_domain_search" != x ]; then echo search $new_domain_search >> /etc/resolv.conf.dhclient elif [ "x$new_domain_name" != x ]; then # Note that the DHCP 'Domain Name Option' is really just a domain # name, and that this practice of using the domain name option as # a search path is both nonstandard and deprecated. echo search $new_domain_name >> /etc/resolv.conf.dhclient fi for nameserver in $new_domain_name_servers; do echo nameserver $nameserver >> /etc/resolv.conf.dhclient done mv /etc/resolv.conf.dhclient /etc/resolv.conf elif [ "x${new_dhcp6_name_servers}" != x ] ; then cat /dev/null > /etc/resolv.conf.dhclient6 chmod 644 /etc/resolv.conf.dhclient6 if [ "x${new_dhcp6_domain_search}" != x ] ; then echo search ${new_dhcp6_domain_search} >> /etc/resolv.conf.dhclient6 fi for nameserver in ${new_dhcp6_name_servers} ; do # If the nameserver has a link-local address # add a (interface name) to it. case $nameserver in fe80:*) zone_id="%$interface";; FE80:*) zone_id="%$interface";; *) zone_id="";; esac echo nameserver ${nameserver}$zone_id >> /etc/resolv.conf.dhclient6 done mv /etc/resolv.conf.dhclient6 /etc/resolv.conf fi } # Must be used on exit. Invokes the local dhcp client exit hooks, if any. exit_with_hooks() { exit_status=$1 if [ -f /etc/dhclient-exit-hooks ]; then . /etc/dhclient-exit-hooks fi # probably should do something with exit status of the local script exit $exit_status } # Invoke the local dhcp client enter hooks, if they exist. if [ -f /etc/dhclient-enter-hooks ]; then exit_status=0 . /etc/dhclient-enter-hooks # allow the local script to abort processing of this state # local script must set exit_status variable to nonzero. if [ $exit_status -ne 0 ]; then exit $exit_status fi fi if [ x$new_network_number != x ]; then echo New Network Number: $new_network_number fi if [ x$new_broadcast_address != x ]; then echo New Broadcast Address: $new_broadcast_address new_broadcast_arg="broadcast $new_broadcast_address" fi if [ x$old_broadcast_address != x ]; then old_broadcast_arg="broadcast $old_broadcast_address" fi if [ x$new_subnet_mask != x ]; then new_netmask_arg="netmask $new_subnet_mask" fi if [ x$old_subnet_mask != x ]; then old_netmask_arg="netmask $old_subnet_mask" fi if [ x$alias_subnet_mask != x ]; then alias_subnet_arg="netmask $alias_subnet_mask" fi if [ x$new_interface_mtu != x ]; then mtu_arg="mtu $new_interface_mtu" fi if [ x$IF_METRIC != x ]; then metric_arg="metric $IF_METRIC" fi if [ x$reason = xMEDIUM ]; then eval "ifconfig $interface $medium" eval "ifconfig $interface inet -alias 0.0.0.0 $medium" >/dev/null 2>&1 sleep 1 exit_with_hooks 0 fi ### ### DHCPv4 Handlers ### if [ x$reason = xPREINIT ]; then if [ x$alias_ip_address != x ]; then ifconfig $interface inet -alias $alias_ip_address > /dev/null 2>&1 route delete $alias_ip_address 127.0.0.1 > /dev/null 2>&1 fi ifconfig $interface inet 0.0.0.0 netmask 0.0.0.0 \ broadcast 255.255.255.255 up exit_with_hooks 0 fi if [ x$reason = xARPCHECK ] || [ x$reason = xARPSEND ]; then exit_with_hooks 0; fi if [ x$reason = xBOUND ] || [ x$reason = xRENEW ] || \ [ x$reason = xREBIND ] || [ x$reason = xREBOOT ]; then current_hostname=`hostname` if [ x$current_hostname = x ] || \ [ x$current_hostname = x$old_host_name ]; then if [ x$current_hostname = x ] || \ [ x$new_host_name != x$old_host_name ]; then hostname $new_host_name fi fi if [ x$old_ip_address != x ] && [ x$alias_ip_address != x ] && \ [ x$alias_ip_address != x$old_ip_address ]; then ifconfig $interface inet -alias $alias_ip_address > /dev/null 2>&1 route delete $alias_ip_address 127.0.0.1 > /dev/null 2>&1 fi if [ x$old_ip_address != x ] && [ x$old_ip_address != x$new_ip_address ] then eval "ifconfig $interface inet -alias $old_ip_address $medium" route delete $old_ip_address 127.1 >/dev/null 2>&1 for router in $old_routers; do route delete default $router >/dev/null 2>&1 done if [ "$old_static_routes" != "" ]; then set $old_static_routes while [ $# -gt 1 ]; do route delete $1 $2 shift; shift done fi arp -n -a | sed -n -e 's/^.*(\(.*\)) at .*$/arp -n -d \1/p' |sh fi if [ x$old_ip_address = x ] || [ x$old_ip_address != x$new_ip_address ] || \ [ x$reason = xBOUND ] || [ x$reason = xREBOOT ]; then eval "ifconfig $interface inet $new_ip_address $new_netmask_arg \ $new_broadcast_arg $mtu_arg $metric_arg $medium" route add $new_ip_address 127.1 >/dev/null 2>&1 for router in $new_routers; do route add default $router >/dev/null 2>&1 done if [ "$new_static_routes" != "" ]; then set $new_static_routes while [ $# -gt 1 ]; do route add $1 $2 shift; shift done fi else # we haven't changed the address, have we changed other options # that we wish to update? if [ x$new_routers != x ] && [ x$new_routers != x$old_routers ] ; then # if we've changed routers delete the old and add the new. $LOGGER "New Routers: $new_routers" for router in $old_routers; do route delete default $router >/dev/null 2>&1 done for router in $new_routers; do route add default $router >/dev/null 2>&1 done fi fi if [ x$new_ip_address != x$alias_ip_address ] && [ x$alias_ip_address != x ]; then ifconfig $interface inet alias $alias_ip_address $alias_subnet_arg route add $alias_ip_address 127.0.0.1 fi make_resolv_conf exit_with_hooks 0 fi if [ x$reason = xEXPIRE ] || [ x$reason = xFAIL ] || [ x$reason = xRELEASE ] \ || [ x$reason = xSTOP ]; then if [ x$alias_ip_address != x ]; then ifconfig $interface inet -alias $alias_ip_address > /dev/null 2>&1 route delete $alias_ip_address 127.0.0.1 > /dev/null 2>&1 fi if [ x$old_ip_address != x ]; then eval "ifconfig $interface inet -alias $old_ip_address $medium" route delete $old_ip_address 127.1 >/dev/null 2>&1 for router in $old_routers; do route delete default $router >/dev/null 2>&1 done if [ "$old_static_routes" != "" ]; then set $old_static_routes while [ $# -gt 1 ]; do route delete $1 $2 shift; shift done fi arp -n -a | sed -n -e 's/^.*(\(.*\)) at .*$/arp -n -d \1/p' \ |sh >/dev/null 2>&1 fi if [ x$alias_ip_address != x ]; then ifconfig $interface inet alias $alias_ip_address $alias_subnet_arg route add $alias_ip_address 127.0.0.1 fi exit_with_hooks 0 fi if [ x$reason = xTIMEOUT ]; then if [ x$alias_ip_address != x ]; then ifconfig $interface inet -alias $alias_ip_address > /dev/null 2>&1 route delete $alias_ip_address 127.0.0.1 > /dev/null 2>&1 fi eval "ifconfig $interface inet $new_ip_address $new_netmask_arg \ $new_broadcast_arg $mtu_arg $metric_arg $medium" sleep 1 if [ "$new_routers" != "" ]; then set $new_routers if ping -q -c 1 -w 1 $1; then if [ x$new_ip_address != x$alias_ip_address ] && \ [ x$alias_ip_address != x ]; then ifconfig $interface inet alias $alias_ip_address $alias_subnet_arg route add $alias_ip_address 127.0.0.1 fi route add $new_ip_address 127.1 >/dev/null 2>&1 for router in $new_routers; do route add default $router >/dev/null 2>&1 done set $new_static_routes while [ $# -gt 1 ]; do route add $0 $1 shift; shift done make_resolv_conf exit_with_hooks 0 fi fi eval "ifconfig $interface inet -alias $new_ip_address $medium" for router in $old_routers; do route delete default $router >/dev/null 2>&1 done if [ "$old_static_routes" != "" ]; then set $old_static_routes while [ $# -gt 1 ]; do route delete $1 $2 shift; shift done fi arp -n -a | sed -n -e 's/^.*(\(.*\)) at .*$/arp -n -d \1/p' \ |sh >/dev/null 2>&1 exit_with_hooks 1 fi ### ### DHCPv6 Handlers ### if [ ${reason} = PREINIT6 ] ; then # Ensure interface is up. ifconfig ${interface} up # XXX: Remove any stale addresses from aborted clients. exit_with_hooks 0 fi if [ x${old_ip6_prefix} != x ] || [ x${new_ip6_prefix} != x ] ; then echo Prefix ${reason} old=${old_ip6_prefix} new=${new_ip6_prefix} exit_with_hooks 0 fi if [ ${reason} = BOUND6 ] ; then if [ x${new_ip6_address} = x ] || [ x${new_ip6_prefixlen} = x ] ; then exit_with_hooks 2; fi ifconfig ${interface} inet6 add ${new_ip6_address}/${new_ip6_prefixlen} # Check for nameserver options. make_resolv_conf exit_with_hooks 0 fi if [ ${reason} = RENEW6 ] || [ ${reason} = REBIND6 ] ; then # Make sure nothing has moved around on us. # Nameservers/domains/etc. if [ "x${new_dhcp6_name_servers}" != "x${old_dhcp6_name_servers}" ] || [ "x${new_dhcp6_domain_search}" != "x${old_dhcp6_domain_search}" ] ; then make_resolv_conf fi exit_with_hooks 0 fi if [ ${reason} = DEPREF6 ] ; then if [ x${new_ip6_prefixlen} = x ] ; then exit_with_hooks 2; fi # XXX: # There doesn't appear to be a way to update an addr to indicate # preference. exit_with_hooks 0 fi if [ ${reason} = EXPIRE6 -o ${reason} = RELEASE6 -o ${reason} = STOP6 ] ; then if [ x${old_ip6_address} = x ] || [ x${old_ip6_prefixlen} = x ] ; then exit_with_hooks 2; fi ifconfig ${interface} inet6 delete ${old_ip6_address}/${old_ip6_prefixlen} exit_with_hooks 0 fi exit_with_hooks 0 dhcp-4.4.1/client/scripts/freebsd000755 000765 000024 00000034546 13243301226 017234 0ustar00tmarkstaff000000 000000 #!/bin/sh # # $Id: freebsd,v 1.24 2011/05/18 19:55:44 sar Exp $ # # $FreeBSD$ if [ -x /usr/bin/logger ]; then LOGGER="/usr/bin/logger -s -p user.notice -t dhclient" else LOGGER=echo fi make_resolv_conf() { if [ x"$new_domain_name_servers" != x ]; then ( cat /dev/null > /etc/resolv.conf.dhclient ) exit_status=$? if [ $exit_status -ne 0 ]; then $LOGGER "Unable to create /etc/resolv.conf.dhclient: Error $exit_status" else if [ "x$new_domain_search" != x ]; then ( echo search $new_domain_search >> /etc/resolv.conf.dhclient ) exit_status=$? elif [ "x$new_domain_name" != x ]; then # Note that the DHCP 'Domain Name Option' is really just a domain # name, and that this practice of using the domain name option as # a search path is both nonstandard and deprecated. ( echo search $new_domain_name >> /etc/resolv.conf.dhclient ) exit_status=$? fi for nameserver in $new_domain_name_servers; do if [ $exit_status -ne 0 ]; then break fi ( echo nameserver $nameserver >>/etc/resolv.conf.dhclient ) exit_status=$? done # If there were no errors, attempt to mv the new file into place. if [ $exit_status -eq 0 ]; then ( mv /etc/resolv.conf.dhclient /etc/resolv.conf ) exit_status=$? fi if [ $exit_status -ne 0 ]; then $LOGGER "Error while writing new /etc/resolv.conf." fi fi elif [ "x${new_dhcp6_name_servers}" != x ] ; then ( cat /dev/null > /etc/resolv.conf.dhclient6 ) exit_status=$? if [ $exit_status -ne 0 ] ; then $LOGGER "Unable to create /etc/resolv.conf.dhclient6: Error $exit_status" else if [ "x${new_dhcp6_domain_search}" != x ] ; then ( echo search ${new_dhcp6_domain_search} >> /etc/resolv.conf.dhclient6 ) exit_status=$? fi for nameserver in ${new_dhcp6_name_servers} ; do if [ $exit_status -ne 0 ] ; then break fi # If the nameserver has a link-local address # add a (interface name) to it. case $nameserver in fe80:*) zone_id="%$interface";; FE80:*) zone_id="%$interface";; *) zone_id="";; esac ( echo nameserver ${nameserver}$zone_id >> /etc/resolv.conf.dhclient6 ) exit_status=$? done if [ $exit_status -eq 0 ] ; then ( mv /etc/resolv.conf.dhclient6 /etc/resolv.conf ) exit_status=$? fi if [ $exit_status -ne 0 ] ; then $LOGGER "Error while writing new /etc/resolv.conf." fi fi fi } # Must be used on exit. Invokes the local dhcp client exit hooks, if any. exit_with_hooks() { exit_status=$1 if [ -f /etc/dhclient-exit-hooks ]; then . /etc/dhclient-exit-hooks fi # probably should do something with exit status of the local script exit $exit_status } # This function was largely borrowed from dhclient-script that # ships with Centos, authored by Jiri Popelka and David Cantrell # of Redhat. Thanks guys. add_ipv6_addr_with_DAD() { ifconfig ${interface} inet6 ${new_ip6_address}/${new_ip6_prefixlen} alias if [ ${dad_wait_time} -le 0 ] then # if we're not waiting for DAD, assume we're good return 0 fi # Repeatedly test whether newly added address passed # duplicate address detection (DAD) for i in $(seq 1 ${dad_wait_time}); do sleep 1 # give the DAD some time addr=$(ifconfig ${interface} \ | grep "${new_ip6_address} prefixlen ${new_ip6_prefixlen}") # tentative flag == DAD is still not complete tentative=$(echo "${addr}" | grep tentative) # dadfailed flag == address is already in use somewhere else dadfailed=$(echo "${addr}" | grep duplicated) if [ -n "${dadfailed}" ] ; then # dad failed, remove the address ifconfig ${interface} inet6 ${new_ip6_address}/${new_ip6_prefixlen} -alias exit_with_hooks 3 fi if [ -z "${tentative}" ] ; then if [ -n "${addr}" ]; then # DAD is over return 0 else # address was auto-removed (or not added at all) exit_with_hooks 3 fi fi done return 0 } # Invoke the local dhcp client enter hooks, if they exist. if [ -f /etc/dhclient-enter-hooks ]; then exit_status=0 . /etc/dhclient-enter-hooks # allow the local script to abort processing of this state # local script must set exit_status variable to nonzero. if [ $exit_status -ne 0 ]; then exit $exit_status fi fi if [ x$new_network_number != x ]; then $LOGGER New Network Number: $new_network_number fi if [ x$new_broadcast_address != x ]; then $LOGGER New Broadcast Address: $new_broadcast_address new_broadcast_arg="broadcast $new_broadcast_address" fi if [ x$old_broadcast_address != x ]; then old_broadcast_arg="broadcast $old_broadcast_address" fi if [ x$new_subnet_mask != x ]; then new_netmask_arg="netmask $new_subnet_mask" fi if [ x$old_subnet_mask != x ]; then old_netmask_arg="netmask $old_subnet_mask" fi if [ x$alias_subnet_mask != x ]; then alias_subnet_arg="netmask $alias_subnet_mask" fi if [ x$new_interface_mtu != x ]; then mtu_arg="mtu $new_interface_mtu" fi if [ x$IF_METRIC != x ]; then metric_arg="metric $IF_METRIC" fi if [ x$reason = xMEDIUM ]; then eval "ifconfig $interface $medium" eval "ifconfig $interface inet -alias 0.0.0.0 $medium" >/dev/null 2>&1 sleep 1 exit_with_hooks 0 fi ### ### DHCPv4 Handlers ### if [ x$reason = xPREINIT ]; then if [ x$alias_ip_address != x ]; then ifconfig $interface inet -alias $alias_ip_address > /dev/null 2>&1 route delete $alias_ip_address 127.0.0.1 > /dev/null 2>&1 fi ifconfig $interface inet 0.0.0.0 netmask 0.0.0.0 \ broadcast 255.255.255.255 up exit_with_hooks 0 fi if [ x$reason = xARPCHECK ] || [ x$reason = xARPSEND ]; then exit_with_hooks 0; fi if [ x$reason = xBOUND ] || [ x$reason = xRENEW ] || \ [ x$reason = xREBIND ] || [ x$reason = xREBOOT ]; then current_hostname=`/bin/hostname` if [ x$current_hostname = x ] || \ [ x$current_hostname = x$old_host_name ]; then if [ x$current_hostname = x ] || \ [ x$new_host_name != x$old_host_name ]; then $LOGGER "New Hostname: $new_host_name" hostname $new_host_name fi fi if [ x$old_ip_address != x ] && [ x$alias_ip_address != x ] && \ [ x$alias_ip_address != x$old_ip_address ]; then ifconfig $interface inet -alias $alias_ip_address > /dev/null 2>&1 route delete $alias_ip_address 127.0.0.1 > /dev/null 2>&1 fi if [ x$old_ip_address != x ] && [ x$old_ip_address != x$new_ip_address ] then eval "ifconfig $interface inet -alias $old_ip_address $medium" route delete $old_ip_address 127.1 >/dev/null 2>&1 for router in $old_routers; do route delete default $router >/dev/null 2>&1 done if [ -n "$old_static_routes" ]; then set -- $old_static_routes while [ $# -gt 1 ]; do route delete $1 $2 shift; shift done fi arp -n -a | sed -n -e 's/^.*(\(.*\)) at .*$/arp -d \1/p' |sh fi if [ x$old_ip_address = x ] || [ x$old_ip_address != x$new_ip_address ] || \ [ x$reason = xBOUND ] || [ x$reason = xREBOOT ]; then eval "ifconfig $interface inet $new_ip_address $new_netmask_arg \ $new_broadcast_arg $mtu_arg $metric_arg $medium" $LOGGER "New IP Address ($interface): $new_ip_address" $LOGGER "New Subnet Mask ($interface): $new_subnet_mask" $LOGGER "New Broadcast Address ($interface): $new_broadcast_address" if [ -n "$new_routers" ]; then $LOGGER "New Routers: $new_routers" fi route add $new_ip_address 127.1 >/dev/null 2>&1 for router in $new_routers; do # If the subnet is captive, eg the netmask is /32 but the default # gateway is (obviously) outside of this, then we need to produce a # host route to reach the gateway. if [ "x$new_subnet_mask" = "x255.255.255.255" ] ; then route add -host $router -interface $interface fi route add default $router >/dev/null 2>&1 done if [ -n "$new_static_routes" ]; then $LOGGER "New Static Routes: $new_static_routes" set -- $new_static_routes while [ $# -gt 1 ]; do route add $1 $2 shift; shift done fi else # we haven't changed the address, have we changed other options # that we wish to update? if [ x$new_routers != x ] && [ x$new_routers != x$old_routers ] ; then # if we've changed routers delete the old and add the new. $LOGGER "New Routers: $new_routers" for router in $old_routers; do route delete default $router >/dev/null 2>&1 done for router in $new_routers; do # If the subnet is captive, eg the netmask is /32 but the default # gateway is (obviously) outside of this, then we need to produce a # host route to reach the gateway. if [ "x$new_subnet_mask" = "x255.255.255.255" ] ; then route add -host $router -interface $interface fi route add default $router >/dev/null 2>&1 done fi fi if [ x$new_ip_address != x$alias_ip_address ] && [ x$alias_ip_address != x ]; then ifconfig $interface inet alias $alias_ip_address $alias_subnet_arg route add $alias_ip_address 127.0.0.1 fi make_resolv_conf exit_with_hooks 0 fi if [ x$reason = xEXPIRE ] || [ x$reason = xFAIL ] || [ x$reason = xRELEASE ] \ || [ x$reason = xSTOP ]; then if [ x$alias_ip_address != x ]; then ifconfig $interface inet -alias $alias_ip_address > /dev/null 2>&1 route delete $alias_ip_address 127.0.0.1 > /dev/null 2>&1 fi if [ x$old_ip_address != x ]; then eval "ifconfig $interface inet -alias $old_ip_address $medium" route delete $old_ip_address 127.1 >/dev/null 2>&1 for router in $old_routers; do route delete default $router >/dev/null 2>&1 done if [ -n "$old_static_routes" ]; then set -- $old_static_routes while [ $# -gt 1 ]; do route delete $1 $2 shift; shift done fi arp -n -a | sed -n -e 's/^.*(\(.*\)) at .*$/arp -d \1/p' \ |sh >/dev/null 2>&1 fi if [ x$alias_ip_address != x ]; then ifconfig $interface inet alias $alias_ip_address $alias_subnet_arg route add $alias_ip_address 127.0.0.1 fi exit_with_hooks 0 fi if [ x$reason = xTIMEOUT ]; then if [ x$alias_ip_address != x ]; then ifconfig $interface inet -alias $alias_ip_address > /dev/null 2>&1 route delete $alias_ip_address 127.0.0.1 > /dev/null 2>&1 fi eval "ifconfig $interface inet $new_ip_address $new_netmask_arg \ $new_broadcast_arg $mtu_arg $metric_arg $medium" $LOGGER "New IP Address ($interface): $new_ip_address" $LOGGER "New Subnet Mask ($interface): $new_subnet_mask" $LOGGER "New Broadcast Address ($interface): $new_broadcast_address" sleep 1 if [ -n "$new_routers" ]; then $LOGGER "New Routers: $new_routers" set -- $new_routers if ping -q -c 1 $1; then if [ x$new_ip_address != x$alias_ip_address ] && \ [ x$alias_ip_address != x ]; then ifconfig $interface inet alias $alias_ip_address $alias_subnet_arg route add $alias_ip_address 127.0.0.1 fi route add $new_ip_address 127.1 >/dev/null 2>&1 for router in $new_routers; do if [ "x$new_subnet_mask" = "x255.255.255.255" ] ; then route add -host $router -interface $interface fi route add default $router >/dev/null 2>&1 done set -- $new_static_routes while [ $# -gt 1 ]; do route add $1 $2 shift; shift done make_resolv_conf exit_with_hooks 0 fi fi eval "ifconfig $interface inet -alias $new_ip_address $medium" for router in $old_routers; do route delete default $router >/dev/null 2>&1 done if [ -n "$old_static_routes" ]; then set -- $old_static_routes while [ $# -gt 1 ]; do route delete $1 $2 shift; shift done fi arp -n -a | sed -n -e 's/^.*(\(.*\)) at .*$/arp -d \1/p' \ |sh >/dev/null 2>&1 exit_with_hooks 1 fi ### ### DHCPv6 Handlers ### if [ ${reason} = PREINIT6 ] ; then # Ensure interface is up. ifconfig ${interface} up # XXX: Remove any stale addresses from aborted clients. # We need to give the kernel some time to active interface interface_up_wait_time=5 for i in $(seq 0 ${interface_up_wait_time}) do ifconfig ${interface} | grep inactive >/dev/null 2>&1 if [ $? -ne 0 ]; then break; fi sleep 1 done # Wait for duplicate address detection for this interface if the # --dad-wait-time parameter has been specified and is greater than # zero. if [ ${dad_wait_time} -gt 0 ]; then # Check if any IPv6 address on this interface is marked as # tentative. ifconfig ${interface} | grep inet6 | grep tentative \ >/dev/null 2>&1 if [ $? -eq 0 ]; then # Wait for duplicate address detection to complete or for # the timeout specified as --dad-wait-time. for i in $(seq 0 $dad_wait_time) do # We're going to poll for the tentative flag every second. sleep 1 ifconfig ${interface} | grep inet6 | grep tentative \ >/dev/null 2>&1 if [ $? -ne 0 ]; then break; fi done fi fi exit_with_hooks 0 fi if [ x${old_ip6_prefix} != x ] || [ x${new_ip6_prefix} != x ] ; then echo Prefix ${reason} old=${old_ip6_prefix} new=${new_ip6_prefix} exit_with_hooks 0 fi if [ ${reason} = BOUND6 ] ; then if [ x${new_ip6_address} = x ] || [ x${new_ip6_prefixlen} = x ] ; then exit_with_hooks 2; fi # Add address to interface, check for DAD if dad_wait_time > 0 add_ipv6_addr_with_DAD # Check for nameserver options. make_resolv_conf exit_with_hooks 0 fi if [ ${reason} = RENEW6 ] || [ ${reason} = REBIND6 ] ; then # Make sure nothing has moved around on us. # Nameservers/domains/etc. if [ "x${new_dhcp6_name_servers}" != "x${old_dhcp6_name_servers}" ] || [ "x${new_dhcp6_domain_search}" != "x${old_dhcp6_domain_search}" ] ; then make_resolv_conf fi exit_with_hooks 0 fi if [ ${reason} = DEPREF6 ] ; then if [ x${new_ip6_address} = x ] ; then exit_with_hooks 2; fi ifconfig ${interface} inet6 ${new_ip6_address} deprecated exit_with_hooks 0 fi if [ ${reason} = EXPIRE6 -o ${reason} = RELEASE6 -o ${reason} = STOP6 ] ; then if [ x${old_ip6_address} = x ] || [ x${old_ip6_prefixlen} = x ] ; then exit_with_hooks 2; fi ifconfig ${interface} inet6 ${old_ip6_address}/${old_ip6_prefixlen} -alias exit_with_hooks 0 fi exit_with_hooks 0 dhcp-4.4.1/client/scripts/linux000755 000765 000024 00000037752 13243301226 016763 0ustar00tmarkstaff000000 000000 #!/bin/bash # dhclient-script for Linux. Dan Halbert, March, 1997. # Updated for Linux 2.[12] by Brian J. Murrell, January 1999. # No guarantees about this. I'm a novice at the details of Linux # networking. # Notes: # 0. This script is based on the netbsd script supplied with dhcp-970306. # 1. ifconfig down apparently deletes all relevant routes and flushes # the arp cache, so this doesn't need to be done explicitly. # 2. The alias address handling here has not been tested AT ALL. # I'm just going by the doc of modern Linux ip aliasing, which uses # notations like eth0:0, eth0:1, for each alias. # 3. I have to calculate the network address, and calculate the broadcast # address if it is not supplied. This might be much more easily done # by the dhclient C code, and passed on. # 4. TIMEOUT not tested. ping has a flag I don't know, and I'm suspicious # of the $1 in its args. # 5. Script refresh in 2017. The aliasing code was too convoluted and needs # to go away. Migrated DHCPv4 script to ip command from iproute2 suite. # This is based on Debian script with some tweaks. ifconfig is no longer # used. Everything is done using ip tool from ip-route2. # 'ip' just looks too weird. Also, we now have unit-tests! Those unit-tests # overwirte this line to use a fake ip-echo tool. It's also convenient # if your system holds ip tool in a non-standard location. ip=/sbin/ip # update /etc/resolv.conf based on received values # This updated version mostly follows Debian script by Andrew Pollock et al. make_resolv_conf() { local new_resolv_conf # DHCPv4 if [ -n "$new_domain_search" ] || [ -n "$new_domain_name" ] || [ -n "$new_domain_name_servers" ]; then new_resolv_conf=/etc/resolv.conf.dhclient-new rm -f $new_resolv_conf if [ -n "$new_domain_name" ]; then echo domain ${new_domain_name%% *} >>$new_resolv_conf fi if [ -n "$new_domain_search" ]; then if [ -n "$new_domain_name" ]; then domain_in_search_list="" for domain in $new_domain_search; do if [ "$domain" = "${new_domain_name}" ] || [ "$domain" = "${new_domain_name}." ]; then domain_in_search_list="Yes" fi done if [ -z "$domain_in_search_list" ]; then new_domain_search="$new_domain_name $new_domain_search" fi fi echo "search ${new_domain_search}" >> $new_resolv_conf elif [ -n "$new_domain_name" ]; then echo "search ${new_domain_name}" >> $new_resolv_conf fi if [ -n "$new_domain_name_servers" ]; then for nameserver in $new_domain_name_servers; do echo nameserver $nameserver >>$new_resolv_conf done else # keep 'old' nameservers sed -n /^\w*[Nn][Aa][Mm][Ee][Ss][Ee][Rr][Vv][Ee][Rr]/p /etc/resolv.conf >>$new_resolv_conf fi if [ -f /etc/resolv.conf ]; then chown --reference=/etc/resolv.conf $new_resolv_conf chmod --reference=/etc/resolv.conf $new_resolv_conf fi mv -f $new_resolv_conf /etc/resolv.conf # DHCPv6 elif [ -n "$new_dhcp6_domain_search" ] || [ -n "$new_dhcp6_name_servers" ]; then new_resolv_conf=/etc/resolv.conf.dhclient-new rm -f $new_resolv_conf if [ -n "$new_dhcp6_domain_search" ]; then echo "search ${new_dhcp6_domain_search}" >> $new_resolv_conf fi if [ -n "$new_dhcp6_name_servers" ]; then for nameserver in $new_dhcp6_name_servers; do # append %interface to link-local-address nameservers if [ "${nameserver##fe80::}" != "$nameserver" ] || [ "${nameserver##FE80::}" != "$nameserver" ]; then nameserver="${nameserver}%${interface}" fi echo nameserver $nameserver >>$new_resolv_conf done else # keep 'old' nameservers sed -n /^\w*[Nn][Aa][Mm][Ee][Ss][Ee][Rr][Vv][Ee][Rr]/p /etc/resolv.conf >>$new_resolv_conf fi if [ -f /etc/resolv.conf ]; then chown --reference=/etc/resolv.conf $new_resolv_conf chmod --reference=/etc/resolv.conf $new_resolv_conf fi mv -f $new_resolv_conf /etc/resolv.conf fi } # set host name set_hostname() { local current_hostname if [ -n "$new_host_name" ]; then current_hostname=$(hostname) # current host name is empty, '(none)' or 'localhost' or differs from new one from DHCP if [ -z "$current_hostname" ] || [ "$current_hostname" = '(none)' ] || [ "$current_hostname" = 'localhost' ] || [ "$current_hostname" = "$old_host_name" ]; then if [ "$new_host_name" != "$old_host_name" ]; then hostname "$new_host_name" fi fi fi } # run given script run_hook() { local script local exit_status script="$1" if [ -f $script ]; then . $script fi if [ -n "$exit_status" ] && [ "$exit_status" -ne 0 ]; then logger -p daemon.err "$script returned non-zero exit status $exit_status" fi return $exit_status } # run scripts in given directory run_hookdir() { local dir local exit_status dir="$1" if [ -d "$dir" ]; then for script in $(run-parts --list $dir); do run_hook $script || true exit_status=$? done fi return $exit_status } # Must be used on exit. Invokes the local dhcp client exit hooks, if any. exit_with_hooks() { exit_status=$1 # Source the documented exit-hook script, if it exists if ! run_hook /etc/dhclient-exit-hooks; then exit_status=$? fi # Now run scripts in the Debian-specific directory. if ! run_hookdir /etc/dhclient-exit-hooks.d; then exit_status=$? fi exit $exit_status } # This function was largely borrowed from dhclient-script that # ships with Centos, authored by Jiri Popelka and David Cantrell # of Redhat. Thanks guys. add_ipv6_addr_with_DAD() { ${ip} -6 addr replace ${new_ip6_address}/${new_ip6_prefixlen} \ dev ${interface} scope global valid_lft ${new_max_life} \ preferred_lft ${new_preferred_life} if [ ${dad_wait_time} -le 0 ] then # if we're not waiting for DAD, assume we're good return 0 fi # Repeatedly test whether newly added address passed # duplicate address detection (DAD) for i in $(seq 1 ${dad_wait_time}); do sleep 1 # give the DAD some time addr=$(${ip} -6 addr show dev ${interface} \ | grep ${new_ip6_address}/${new_ip6_prefixlen}) # tentative flag == DAD is still not complete tentative=$(echo "${addr}" | grep tentative) # dadfailed flag == address is already in use somewhere else dadfailed=$(echo "${addr}" | grep dadfailed) if [ -n "${dadfailed}" ] ; then # address was added with valid_lft/preferred_lft 'forever', # remove it ${ip} -6 addr del ${new_ip6_address}/${new_ip6_prefixlen} \ dev ${interface} exit_with_hooks 3 fi if [ -z "${tentative}" ] ; then if [ -n "${addr}" ]; then # DAD is over return 0 else # address was auto-removed (or not added at all) exit_with_hooks 3 fi fi done return 0 } # Invoke the local dhcp client enter hooks, if they exist. run_hook /etc/dhclient-enter-hooks run_hookdir /etc/dhclient-enter-hooks.d # Execute the operation case "$reason" in ### DHCPv4 Handlers MEDIUM|ARPCHECK|ARPSEND) # Do nothing ;; PREINIT) # The DHCP client is requesting that an interface be # configured as required in order to send packets prior to # receiving an actual address. - dhclient-script(8) # ensure interface is up ${ip} link set dev ${interface} up if [ -n "$alias_ip_address" ]; then # flush alias IP from interface ${ip} -4 addr flush dev ${interface} label ${interface}:0 fi ;; BOUND|RENEW|REBIND|REBOOT) set_hostname if [ -n "$old_ip_address" ] && [ -n "$alias_ip_address" ] && [ "$alias_ip_address" != "$old_ip_address" ]; then # alias IP may have changed => flush it ${ip} -4 addr flush dev ${interface} label ${interface}:0 fi if [ -n "$old_ip_address" ] && [ "$old_ip_address" != "$new_ip_address" ]; then # leased IP has changed => flush it ${ip} -4 addr flush dev ${interface} label ${interface} fi if [ -z "$old_ip_address" ] || [ "$old_ip_address" != "$new_ip_address" ] || [ "$reason" = "BOUND" ] || [ "$reason" = "REBOOT" ]; then # new IP has been leased or leased IP changed => set it ${ip} -4 addr add ${new_ip_address}${new_subnet_mask:+/$new_subnet_mask} \ ${new_broadcast_address:+broadcast $new_broadcast_address} \ dev ${interface} label ${interface} if [ -n "$new_interface_mtu" ]; then # set MTU ${ip} link set dev ${interface} mtu ${new_interface_mtu} fi # if we have $new_rfc3442_classless_static_routes then we have to # ignore $new_routers entirely if [ ! "$new_rfc3442_classless_static_routes" ]; then # set if_metric if IF_METRIC is set or there's more than one router if_metric="$IF_METRIC" if [ "${new_routers%% *}" != "${new_routers}" ]; then if_metric=${if_metric:-1} fi for router in $new_routers; do if [ "$new_subnet_mask" = "255.255.255.255" ]; then # point-to-point connection => set explicit route ${ip} -4 route add ${router} dev $interface >/dev/null 2>&1 fi # set default route ${ip} -4 route add default via ${router} dev ${interface} \ ${if_metric:+metric $if_metric} >/dev/null 2>&1 if [ -n "$if_metric" ]; then if_metric=$((if_metric+1)) fi done fi fi if [ -n "$alias_ip_address" ] && [ "$new_ip_address" != "$alias_ip_address" ]; then # separate alias IP given, which may have changed # => flush it, set it & add host route to it ${ip} -4 addr flush dev ${interface} label ${interface}:0 ${ip} -4 addr add ${alias_ip_address}${alias_subnet_mask:+/$alias_subnet_mask} \ dev ${interface} label ${interface}:0 ${ip} -4 route add ${alias_ip_address} dev ${interface} >/dev/null 2>&1 fi # update /etc/resolv.conf make_resolv_conf ;; EXPIRE|FAIL|RELEASE|STOP) if [ -n "$alias_ip_address" ]; then # flush alias IP ${ip} -4 addr flush dev ${interface} label ${interface}:0 fi if [ -n "$old_ip_address" ]; then # flush leased IP ${ip} -4 addr flush dev ${interface} label ${interface} fi if [ -n "$alias_ip_address" ]; then # alias IP given => set it & add host route to it ${ip} -4 addr add ${alias_ip_address}${alias_subnet_mask:+/$alias_subnet_mask} \ dev ${interface} label ${interface}:0 ${ip} -4 route add ${alias_ip_address} dev ${interface} >/dev/null 2>&1 fi ;; TIMEOUT) if [ -n "$alias_ip_address" ]; then # flush alias IP ${ip} -4 addr flush dev ${interface} label ${interface}:0 fi # set IP from recorded lease ${ip} -4 addr add ${new_ip_address}${new_subnet_mask:+/$new_subnet_mask} \ ${new_broadcast_address:+broadcast $new_broadcast_address} \ dev ${interface} label ${interface} if [ -n "$new_interface_mtu" ]; then # set MTU ${ip} link set dev ${interface} mtu ${new_interface_mtu} fi # if there is no router recorded in the lease or the 1st router answers pings if [ -z "$new_routers" ] || ping -q -c 1 "${new_routers%% *}"; then # if we have $new_rfc3442_classless_static_routes then we have to # ignore $new_routers entirely if [ ! "$new_rfc3442_classless_static_routes" ]; then if [ -n "$alias_ip_address" ] && [ "$new_ip_address" != "$alias_ip_address" ]; then # separate alias IP given => set up the alias IP & add host route to it ${ip} -4 addr add \ ${alias_ip_address}${alias_subnet_mask:+/$alias_subnet_mask} \ dev ${interface} label ${interface}:0 ${ip} -4 route add ${alias_ip_address} dev ${interface} >/dev/null 2>&1 fi # set if_metric if IF_METRIC is set or there's more than one router if_metric="$IF_METRIC" if [ "${new_routers%% *}" != "${new_routers}" ]; then if_metric=${if_metric:-1} fi # set default route for router in $new_routers; do ${ip} -4 route add default via ${router} dev ${interface} \ ${if_metric:+metric $if_metric} >/dev/null 2>&1 if [ -n "$if_metric" ]; then if_metric=$((if_metric+1)) fi done fi # update /etc/resolv.conf make_resolv_conf else # flush all IPs from interface ip -4 addr flush dev ${interface} exit_with_hooks 2 fi ;; ### DHCPv6 Handlers # TODO handle prefix change: ?based on ${old_ip6_prefix} and ${new_ip6_prefix}? PREINIT6) # ensure interface is up ${ip} link set ${interface} up # We need to give the kernel some time to active interface interface_up_wait_time=5 for i in $(seq 0 ${interface_up_wait_time}) do ifconfig ${interface} | grep RUNNING >/dev/null 2>&1 if [ $? -eq 0 ]; then break; fi sleep 1 done # flush any stale global permanent IPs from interface ${ip} -6 addr flush dev ${interface} scope global permanent # Wait for duplicate address detection for this interface if the # --dad-wait-time parameter has been specified and is greater than # zero. if [ ${dad_wait_time} -gt 0 ]; then # Check if any IPv6 address on this interface is marked as # tentative. ${ip} addr show ${interface} | grep inet6 | grep tentative \ &> /dev/null if [ $? -eq 0 ]; then # Wait for duplicate address detection to complete or for # the timeout specified as --dad-wait-time. for i in $(seq 0 $dad_wait_time) do # We're going to poll for the tentative flag every second. sleep 1 ${ip} addr show ${interface} | grep inet6 | grep tentative \ &> /dev/null if [ $? -ne 0 ]; then break; fi done fi fi ;; BOUND6|RENEW6|REBIND6) if [ "${new_ip6_address}" ] && [ "${new_ip6_prefixlen}" ]; then # set leased IP add_ipv6_addr_with_DAD fi # update /etc/resolv.conf if [ "${reason}" = BOUND6 ] || [ "${new_dhcp6_name_servers}" != "${old_dhcp6_name_servers}" ] || [ "${new_dhcp6_domain_search}" != "${old_dhcp6_domain_search}" ]; then make_resolv_conf fi ;; DEPREF6) if [ -z "${cur_ip6_prefixlen}" ]; then exit_with_hooks 2 fi # set preferred lifetime of leased IP to 0 ${ip} -6 addr change ${cur_ip6_address}/${cur_ip6_prefixlen} \ dev ${interface} scope global preferred_lft 0 ;; EXPIRE6|RELEASE6|STOP6) if [ -z "${old_ip6_address}" ] || [ -z "${old_ip6_prefixlen}" ]; then exit_with_hooks 2 fi # delete leased IP ${ip} -6 addr del ${old_ip6_address}/${old_ip6_prefixlen} \ dev ${interface} ;; esac exit_with_hooks 0 dhcp-4.4.1/client/scripts/macos000755 000765 000024 00000017760 13243301226 016723 0ustar00tmarkstaff000000 000000 #!/bin/sh # # $Id: macos,v 1.4 2011/09/20 16:59:54 sar Exp $ # # automous run of this script will commit the DNS setting # if [ -x /usr/bin/logger ]; then LOGGER="/usr/bin/logger -s -p user.notice -t dhclient" else LOGGER=echo fi to_commit="yes" make_resolv_conf() { to_commit="no" if [ "x${new_dhcp6_name_servers}" != x ]; then ( cat /dev/null > /var/run/resolv.conf.dhclient6 ) exit_status=$? if [ $exit_status -ne 0 ]; then $LOGGER "Unable to create /var/run/resolv.conf.dhclient6: Error $exit_status" else if [ "x${new_dhcp6_domain_search}" != x ]; then ( echo search ${new_dhcp6_domain_search} >> /var/run/resolv.conf.dhclient6 ) exit_status=$? fi for nameserver in ${new_dhcp6_name_servers} ; do if [ $exit_status -ne 0 ]; then break fi # If the nameserver has a link-local address # add a (interface name) to it. case $nameserver in fe80:*) zone_id="%$interface";; FE80:*) zone_id="%$interface";; *) zone_id="";; esac ( echo nameserver ${nameserver}$zone_id >> /etc/resolv.conf.dhclient6 ) exit_status=$? done if [ $exit_status -eq 0 ]; then to_commit="force" commit_resolv_conf fi fi fi } # Try to commit /var/run/resolv.conf.dhclient6 contents to # System Configuration framework's Dynamic Store. # Note this will be cleared by the next location change # or preempted by IPv4. # # The System Configuration agent "IPMonitor" gets the DNS configuration # from the IPv4 or IPv6 primary service in the Dynamic Store # (managed by configd). commit_resolv_conf() { if [ -f /var/run/resolv.conf.dhclient6 ]; then if [ -x /usr/sbin/scutil ]; then serviceID=`echo show State:/Network/Global/IPv6 | \ /usr/sbin/scutil | \ awk '/PrimaryService/ { print $3 }'` echo $serviceID if [ x$serviceID = x ]; then $LOGGER "Can't find the primary IPv6 service" else tmp=`mktemp SC_dhclient6.XXXXXXXXXX` echo list | /usr/sbin/scutil > /tmp/$tmp grep -q State:/Network/Service/$serviceID/DNS /tmp/$tmp grep_status=$? if [ $grep_status -eq 0 ]; then $LOGGER "DNS service already set in primary IPv6 service" rm /tmp/$tmp else res=/var/run/resolv.conf.dhclient6 cp /dev/null /tmp/$tmp grep -q '^nameserver' $res grep_status=$? if [ $grep_status -eq 0 ]; then echo d.add ServerAddresses '*' \ `awk 'BEGIN { n="" } \ /^nameserver/ { n=n " " $2 } \ END { print n}' < $res` >> /tmp/$tmp fi grep -q '^search' $res grep_status=$? if [ $grep_status -eq 0 ]; then echo d.add SearchDomains '*' \ `sed 's/^search//' < $res` >> /tmp/$tmp fi echo set State:/Network/Service/$serviceID/DNS >> /tmp/$tmp echo quit >> /tmp/$tmp cat /tmp/$tmp /usr/sbin/scutil < /tmp/$tmp rm /tmp/$tmp fi fi else $LOGGER "Can't find SystemConfiguration tools." fi else if [ $to_commit = force ]; then $LOGGER "Can't find /var/run/resolv.conf.dhclient6" fi fi to_commit="done" } # This function was largely borrowed from dhclient-script that # ships with Centos, authored by Jiri Popelka and David Cantrell # of Redhat. Thanks guys. add_ipv6_addr_with_DAD() { ifconfig ${interface} inet6 ${new_ip6_address}/${new_ip6_prefixlen} alias if [ ${dad_wait_time} -le 0 ] then # if we're not waiting for DAD, assume we're good return 0 fi # Repeatedly test whether newly added address passed # duplicate address detection (DAD) for i in $(seq 1 ${dad_wait_time}); do sleep 1 # give the DAD some time addr=$(ifconfig ${interface} \ | grep "${new_ip6_address} prefixlen ${new_ip6_prefixlen}") # tentative flag == DAD is still not complete tentative=$(echo "${addr}" | grep tentative) # dadfailed flag == address is already in use somewhere else dadfailed=$(echo "${addr}" | grep duplicated) if [ -n "${dadfailed}" ] ; then # dad failed, remove the address ifconfig ${interface} inet6 ${new_ip6_address}/${new_ip6_prefixlen} -alias exit_with_hooks 3 fi if [ -z "${tentative}" ] ; then if [ -n "${addr}" ]; then # DAD is over return 0 else # address was auto-removed (or not added at all) exit_with_hooks 3 fi fi done return 0 } # Must be used on exit. Invokes the local dhcp client exit hooks, if any. exit_with_hooks() { exit_status=$1 if [ -f /etc/dhclient-exit-hooks ]; then . /etc/dhclient-exit-hooks fi # probably should do something with exit status of the local script exit $exit_status } # Invoke the local dhcp client enter hooks, if they exist. if [ -f /etc/dhclient-enter-hooks ]; then exit_status=0 . /etc/dhclient-enter-hooks # allow the local script to abort processing of this state # local script must set exit_status variable to nonzero. if [ $exit_status -ne 0 ]; then exit $exit_status fi fi if [ x$reason = xMEDIUM ]; then eval "ifconfig $interface $medium" eval "ifconfig $interface inet -alias 0.0.0.0 $medium" >/dev/null 2>&1 sleep 1 exit_with_hooks 0 fi ### ### DHCPv6 Handlers ### if [ x$reason = xPREINIT6 ]; then # Ensure interface is up. ifconfig ${interface} up # We need to give the kernel some time to active interface interface_up_wait_time=5 for i in $(seq 0 ${interface_up_wait_time}) do ifconfig ${interface} | grep inactive &> /dev/null if [ $? -ne 0 ]; then break; fi sleep 1 done # XXX: Remove any stale addresses from aborted clients. # Wait for duplicate address detection for this interface if the # --dad-wait-time parameter has been specified and is greater than # zero. if [ ${dad_wait_time} -gt 0 ]; then # Check if any IPv6 address on this interface is marked as # tentative. ifconfig ${interface} | grep inet6 | grep tentative \ &> /dev/null if [ $? -eq 0 ]; then # Wait for duplicate address detection to complete or for # the timeout specified as --dad-wait-time. for i in $(seq 0 $dad_wait_time) do # We're going to poll for the tentative flag every second. sleep 1 ifconfig ${interface} | grep inet6 | grep tentative \ &> /dev/null if [ $? -ne 0 ]; then break; fi done fi fi exit_with_hooks 0 fi if [ x${old_ip6_prefix} != x ] || [ x${new_ip6_prefix} != x ]; then echo Prefix $reason old=${old_ip6_prefix} new=${new_ip6_prefix} exit_with_hooks 0 fi if [ x$reason = xBOUND6 ]; then if [ x${new_ip6_address} = x ] || [ x${new_ip6_prefixlen} = x ]; then exit_with_hooks 2; fi # Add address to interface, check for DAD if dad_wait_time > 0 add_ipv6_addr_with_DAD # Check for nameserver options. make_resolv_conf exit_with_hooks 0 fi if [ x$reason = xRENEW6 ] || [ x$reason = xREBIND6 ]; then # Make sure nothing has moved around on us. # Nameservers/domains/etc. if [ "x${new_dhcp6_name_servers}" != "x${old_dhcp6_name_servers}" ] || [ "x${new_dhcp6_domain_search}" != "x${old_dhcp6_domain_search}" ]; then make_resolv_conf fi exit_with_hooks 0 fi if [ x$reason = xDEPREF6 ]; then if [ x${new_ip6_address} = x ]; then exit_with_hooks 2; fi ifconfig ${interface} inet6 ${new_ip6_address} deprecated exit_with_hooks 0 fi if [ x$reason = xEXPIRE6 -o x$reason = xRELEASE6 -o x$reason = xSTOP6 ]; then if [ x${old_ip6_address} = x ] || [ x${old_ip6_prefixlen} = x ]; then exit_with_hooks 2; fi ifconfig ${interface} inet6 ${old_ip6_address}/${old_ip6_prefixlen} -alias exit_with_hooks 0 fi if [ $to_commit = yes ]; then commit_resolv_conf fi exit_with_hooks 0 dhcp-4.4.1/client/scripts/netbsd000755 000765 000024 00000030167 13243301226 017074 0ustar00tmarkstaff000000 000000 #!/bin/sh make_resolv_conf() { if [ "x$new_domain_name" != x ] && [ x"$new_domain_name_servers" != x ]; then cat /dev/null > /etc/resolv.conf.dhclient if [ "x$new_domain_search" != x ]; then echo search $new_domain_search >> /etc/resolv.conf.dhclient elif [ "x$new_domain_name" != x ]; then # Note that the DHCP 'Domain Name Option' is really just a domain # name, and that this practice of using the domain name option as # a search path is both nonstandard and deprecated. echo search $new_domain_name >> /etc/resolv.conf.dhclient fi for nameserver in $new_domain_name_servers; do echo nameserver $nameserver >>/etc/resolv.conf.dhclient done mv /etc/resolv.conf.dhclient /etc/resolv.conf elif [ "x${new_dhcp6_name_servers}" != x ] ; then cat /dev/null > /etc/resolv.conf.dhclient6 chmod 644 /etc/resolv.conf.dhclient6 if [ "x${new_dhcp6_domain_search}" != x ] ; then echo search ${new_dhcp6_domain_search} >> /etc/resolv.conf.dhclient6 fi for nameserver in ${new_dhcp6_name_servers} ; do # If the nameserver has a link-local address # add a (interface name) to it. case $nameserver in fe80:*) zone_id="%$interface";; FE80:*) zone_id="%$interface";; *) zone_id="";; esac echo nameserver ${nameserver}$zone_id >> /etc/resolv.conf.dhclient6 done mv /etc/resolv.conf.dhclient6 /etc/resolv.conf fi } # Must be used on exit. Invokes the local dhcp client exit hooks, if any. exit_with_hooks() { exit_status=$1 if [ -f /etc/dhclient-exit-hooks ]; then . /etc/dhclient-exit-hooks fi # probably should do something with exit status of the local script exit $exit_status } # This function was largely borrowed from dhclient-script that # ships with Centos, authored by Jiri Popelka and David Cantrell # of Redhat. Thanks guys. add_ipv6_addr_with_DAD() { ifconfig ${interface} inet6 ${new_ip6_address}/${new_ip6_prefixlen} alias if [ ${dad_wait_time} -le 0 ] then # if we're not waiting for DAD, assume we're good return 0 fi # Repeatedly test whether newly added address passed # duplicate address detection (DAD) for i in $(seq 1 ${dad_wait_time}); do sleep 1 # give the DAD some time addr=$(ifconfig ${interface} \ | grep "${new_ip6_address} prefixlen ${new_ip6_prefixlen}") # tentative flag == DAD is still not complete tentative=$(echo "${addr}" | grep tentative) # dadfailed flag == address is already in use somewhere else dadfailed=$(echo "${addr}" | grep duplicated) if [ -n "${dadfailed}" ] ; then # dad failed, remove the address ifconfig ${interface} inet6 ${new_ip6_address}/${new_ip6_prefixlen} -alias exit_with_hooks 3 fi if [ -z "${tentative}" ] ; then if [ -n "${addr}" ]; then # DAD is over return 0 else # address was auto-removed (or not added at all) exit_with_hooks 3 fi fi done return 0 } # Invoke the local dhcp client enter hooks, if they exist. if [ -f /etc/dhclient-enter-hooks ]; then exit_status=0 . /etc/dhclient-enter-hooks # allow the local script to abort processing of this state # local script must set exit_status variable to nonzero. if [ $exit_status -ne 0 ]; then exit $exit_status fi fi if [ x$new_network_number != x ]; then echo New Network Number: $new_network_number fi if [ x$new_broadcast_address != x ]; then echo New Broadcast Address: $new_broadcast_address new_broadcast_arg="broadcast $new_broadcast_address" fi if [ x$old_broadcast_address != x ]; then old_broadcast_arg="broadcast $old_broadcast_address" fi if [ x$new_subnet_mask != x ]; then new_netmask_arg="netmask $new_subnet_mask" fi if [ x$old_subnet_mask != x ]; then old_netmask_arg="netmask $old_subnet_mask" fi if [ x$alias_subnet_mask != x ]; then alias_subnet_arg="netmask $alias_subnet_mask" fi if [ x$new_interface_mtu != x ]; then mtu_arg="mtu $new_interface_mtu" fi if [ x$IF_METRIC != x ]; then metric_arg="metric $IF_METRIC" fi if [ x$reason = xMEDIUM ]; then eval "ifconfig $interface $medium" eval "ifconfig $interface inet -alias 0.0.0.0 $medium" >/dev/null 2>&1 sleep 1 exit_with_hooks 0 fi ### ### DHCPv4 Handlers ### if [ x$reason = xPREINIT ]; then if [ x$alias_ip_address != x ]; then ifconfig $interface inet -alias $alias_ip_address > /dev/null 2>&1 route delete $alias_ip_address 127.0.0.1 > /dev/null 2>&1 fi ifconfig $interface inet 0.0.0.0 netmask 0.0.0.0 \ broadcast 255.255.255.255 up exit_with_hooks 0 fi if [ x$reason = xARPCHECK ] || [ x$reason = xARPSEND ]; then exit_with_hooks 0 fi if [ x$reason = xBOUND ] || [ x$reason = xRENEW ] || \ [ x$reason = xREBIND ] || [ x$reason = xREBOOT ]; then current_hostname=`hostname` if [ x$current_hostname = x ] || \ [ x$current_hostname = x$old_host_name ]; then if [ x$current_hostname = x ] || \ [ x$new_host_name != x$old_host_name ]; then hostname $new_host_name fi fi if [ x$old_ip_address != x ] && [ x$alias_ip_address != x ] && \ [ x$alias_ip_address != x$old_ip_address ]; then ifconfig $interface inet -alias $alias_ip_address > /dev/null 2>&1 route delete $alias_ip_address 127.0.0.1 > /dev/null 2>&1 fi if [ x$old_ip_address != x ] && [ x$old_ip_address != x$new_ip_address ] then eval "ifconfig $interface inet -alias $old_ip_address $medium" route delete $old_ip_address 127.1 >/dev/null 2>&1 for router in $old_routers; do route delete default $router >/dev/null 2>&1 done if [ "$old_static_routes" != "" ]; then set $old_static_routes while [ $# -gt 1 ]; do route delete $1 $2 shift; shift done fi arp -n -a | sed -n -e 's/^.*(\(.*\)) at .*$/arp -n -d \1/p' |sh fi if [ x$old_ip_address = x ] || [ x$old_ip_address != x$new_ip_address ] || \ [ x$reason = xBOUND ] || [ x$reason = xREBOOT ]; then eval "ifconfig $interface inet $new_ip_address $new_netmask_arg \ $new_broadcast_arg $mtu_arg $metric_arg $medium" route add $new_ip_address 127.1 >/dev/null 2>&1 for router in $new_routers; do route add default $router >/dev/null 2>&1 done if [ "$new_static_routes" != "" ]; then set $new_static_routes while [ $# -gt 1 ]; do route add $1 $2 shift; shift done fi else # we haven't changed the address, have we changed other options # that we wish to update? if [ x$new_routers != x ] && [ x$new_routers != x$old_routers ] ; then # if we've changed routers delete the old and add the new. $LOGGER "New Routers: $new_routers" for router in $old_routers; do route delete default $router >/dev/null 2>&1 done for router in $new_routers; do route add default $router >/dev/null 2>&1 done fi fi if [ x$new_ip_address != x$alias_ip_address ] && [ x$alias_ip_address != x ]; then ifconfig $interface inet alias $alias_ip_address $alias_subnet_arg route add $alias_ip_address 127.0.0.1 fi make_resolv_conf exit_with_hooks 0 fi if [ x$reason = xEXPIRE ] || [ x$reason = xFAIL ] || [ x$reason = xRELEASE ] \ || [ x$reason = xSTOP ]; then if [ x$alias_ip_address != x ]; then ifconfig $interface inet -alias $alias_ip_address > /dev/null 2>&1 route delete $alias_ip_address 127.0.0.1 > /dev/null 2>&1 fi if [ x$old_ip_address != x ]; then eval "ifconfig $interface inet -alias $old_ip_address $medium" route delete $old_ip_address 127.1 >/dev/null 2>&1 for router in $old_routers; do route delete default $router >/dev/null 2>&1 done if [ "$old_static_routes" != "" ]; then set $old_static_routes while [ $# -gt 1 ]; do route delete $1 $2 shift; shift done fi arp -n -a | sed -n -e 's/^.*(\(.*\)) at .*$/arp -n -d \1/p' \ |sh >/dev/null 2>&1 fi if [ x$alias_ip_address != x ]; then ifconfig $interface inet alias $alias_ip_address $alias_subnet_arg route add $alias_ip_address 127.0.0.1 fi exit_with_hooks 0 fi if [ x$reason = xTIMEOUT ]; then if [ x$alias_ip_address != x ]; then ifconfig $interface inet -alias $alias_ip_address > /dev/null 2>&1 route delete $alias_ip_address 127.0.0.1 > /dev/null 2>&1 fi eval "ifconfig $interface inet $new_ip_address $new_netmask_arg \ $new_broadcast_arg $mtu_arg $metric_arg $medium" sleep 1 if [ "$new_routers" != "" ]; then set $new_routers if ping -q -c 1 -w 1 $1; then if [ x$new_ip_address != x$alias_ip_address ] && \ [ x$alias_ip_address != x ]; then ifconfig $interface inet alias $alias_ip_address $alias_subnet_arg route add $alias_ip_address 127.0.0.1 fi route add $new_ip_address 127.1 >/dev/null 2>&1 for router in $new_routers; do route add default $router >/dev/null 2>&1 done set $new_static_routes while [ $# -gt 1 ]; do route add $0 $1 shift; shift done make_resolv_conf exit_with_hooks 0 fi fi eval "ifconfig $interface inet -alias $new_ip_address $medium" for router in $old_routers; do route delete default $router >/dev/null 2>&1 done if [ "$old_static_routes" != "" ]; then set $old_static_routes while [ $# -gt 1 ]; do route delete $1 $2 shift; shift done fi arp -n -a | sed -n -e 's/^.*(\(.*\)) at .*$/arp -n -d \1/p' \ |sh >/dev/null 2>&1 exit_with_hooks 1 fi ### ### DHCPv6 Handlers ### if [ ${reason} = PREINIT6 ] ; then # Ensure interface is up. ifconfig ${interface} up # XXX: Remove any stale addresses from aborted clients. # We need to give the kernel some time to active interface interface_up_wait_time=5 for i in $(seq 0 ${interface_up_wait_time}) do ifconfig ${interface} | grep inactive >/dev/null 2>&1 if [ $? -ne 0 ]; then break; fi sleep 1 done # Wait for duplicate address detection for this interface if the # --dad-wait-time parameter has been specified and is greater than # zero. if [ ${dad_wait_time} -gt 0 ]; then # Check if any IPv6 address on this interface is marked as # tentative. ifconfig ${interface} | grep inet6 | grep tentative \ >/dev/null 2>&1 if [ $? -eq 0 ]; then # Wait for duplicate address detection to complete or for # the timeout specified as --dad-wait-time. for i in $(seq 0 $dad_wait_time) do # We're going to poll for the tentative flag every second. sleep 1 ifconfig ${interface} | grep inet6 | grep tentative \ >/dev/null 2>&1 if [ $? -ne 0 ]; then break; fi done fi fi exit_with_hooks 0 fi if [ x${old_ip6_prefix} != x ] || [ x${new_ip6_prefix} != x ] ; then echo Prefix ${reason} old=${old_ip6_prefix} new=${new_ip6_prefix} exit_with_hooks 0 fi if [ ${reason} = BOUND6 ] ; then if [ x${new_ip6_address} = x ] || [ x${new_ip6_prefixlen} = x ] ; then exit_with_hooks 2; fi # Add address to interface, check for DAD if dad_wait_time > 0 add_ipv6_addr_with_DAD # Check for nameserver options. make_resolv_conf exit_with_hooks 0 fi if [ ${reason} = RENEW6 ] || [ ${reason} = REBIND6 ] ; then # Make sure nothing has moved around on us. # Nameservers/domains/etc. if [ "x${new_dhcp6_name_servers}" != "x${old_dhcp6_name_servers}" ] || [ "x${new_dhcp6_domain_search}" != "x${old_dhcp6_domain_search}" ] ; then make_resolv_conf fi exit_with_hooks 0 fi if [ ${reason} = DEPREF6 ] ; then if [ x${new_ip6_prefixlen} = x ] ; then exit_with_hooks 2; fi # XXX: # There doesn't appear to be a way to update an addr to indicate # preference. exit_with_hooks 0 fi if [ ${reason} = EXPIRE6 -o ${reason} = RELEASE6 -o ${reason} = STOP6 ] ; then if [ x${old_ip6_address} = x ] || [ x${old_ip6_prefixlen} = x ] ; then exit_with_hooks 2; fi ifconfig ${interface} inet6 -alias ${old_ip6_address}/${old_ip6_prefixlen} exit_with_hooks 0 fi exit_with_hooks 0 dhcp-4.4.1/client/scripts/nextstep000644 000765 000024 00000004042 13243301226 017455 0ustar00tmarkstaff000000 000000 #!/bin/sh # # simplified dhclient-script for NeXTSTEP/OPENSTEP # # removed a lot of the cruft from the netbsd version since NeXTSTEP doesn't # support aliases and lots of things were breaking for no good reason # # 14 Sep 1997, David W. Young # if [ x$reason = xPREINIT ]; then ifconfig $interface inet 0.0.0.0 netmask 0.0.0.0 up >/dev/null 2>&1 exit 0 fi if [ x$reason = xBOUND ] || [ x$reason = xRENEW ] || \ [ x$reason = xREBIND ] || [ x$reason = xREBOOT ]; then current_hostname=`hostname` if [ x$current_hostname = x ] || \ [ x$current_hostname = x$old_host_name ]; then if [ x$current_hostname = x ] || \ [ x$new_host_name != x$old_host_name ]; then hostname $new_host_name fi fi if [ x$old_ip_address != x ] && [ x$old_ip_address != x$new_ip_address ] then ifconfig $interface $new_ip_address netmask $new_subnet_mask \ >/dev/null 2>&1 route add $new_ip_address 127.1 0 >/dev/null 2>&1 for router in $new_routers ; do route add default $router 1 >/dev/null 2>&1 done fi if [ x"$new_domain_name_servers" != x ]; then cat /dev/null > /etc/resolv.conf.dhclient if [ "x$new_domain_search" != x ]; then echo search $new_domain_search >> /etc/resolv.conf.dhclient elif [ "x$new_domain_name" != x ]; then # Note that the DHCP 'Domain Name Option' is really just a domain # name, and that this practice of using the domain name option as # a search path is both nonstandard and deprecated. echo search $new_domain_name >> /etc/resolv.conf.dhclient fi for nameserver in $new_domain_name_servers; do echo nameserver $nameserver >>/etc/resolv.conf.dhclient done mv /etc/resolv.conf.dhclient /etc/resolv.conf fi exit 0 fi if [ x$reason = xEXPIRE ] || [ x$reason = xFAIL ] || [ x$reason = xRELEASE ] \ || [ x$reason = xSTOP ]; then if [ x$old_ip_address != x ]; then route delete $old_ip_address 127.1 >/dev/null 2>&1 for $router in $old_routers ; do route delete default $router >/dev/null 2>&1 done fi exit 0 fi dhcp-4.4.1/client/scripts/openbsd000755 000765 000024 00000027772 13243301226 017257 0ustar00tmarkstaff000000 000000 #!/bin/sh make_resolv_conf() { if [ x"$new_domain_name_servers" != x ]; then cat /dev/null > /etc/resolv.conf.dhclient if [ x"$new_domain_search" != x ]; then echo search $new_domain_search >> /etc/resolv.conf.dhclient elif [ x"$new_domain_name" != x ]; then # Note that the DHCP 'Domain Name Option' is really just a domain # name, and that this practice of using the domain name option as # a search path is both nonstandard and deprecated. echo search $new_domain_name >> /etc/resolv.conf.dhclient fi for nameserver in $new_domain_name_servers; do echo nameserver $nameserver >>/etc/resolv.conf.dhclient done mv /etc/resolv.conf.dhclient /etc/resolv.conf elif [ "x${new_dhcp6_name_servers}" != x ] ; then cat /dev/null > /etc/resolv.conf.dhclient6 chmod 644 /etc/resolv.conf.dhclient6 if [ "x${new_dhcp6_domain_search}" != x ] ; then echo search ${new_dhcp6_domain_search} >> /etc/resolv.conf.dhclient6 fi for nameserver in ${new_dhcp6_name_servers} ; do # If the nameserver has a link-local address # add a (interface name) to it. case $nameserver in fe80:*) zone_id="%$interface";; FE80:*) zone_id="%$interface";; *) zone_id="";; esac echo nameserver ${nameserver}$zone_id >> /etc/resolv.conf.dhclient6 done mv /etc/resolv.conf.dhclient6 /etc/resolv.conf fi } # Must be used on exit. Invokes the local dhcp client exit hooks, if any. exit_with_hooks() { exit_status=$1 if [ -f /etc/dhclient-exit-hooks ]; then . /etc/dhclient-exit-hooks fi # probably should do something with exit status of the local script exit $exit_status } # This function was largely borrowed from dhclient-script that # ships with Centos, authored by Jiri Popelka and David Cantrell # of Redhat. Thanks guys. add_ipv6_addr_with_DAD() { ifconfig ${interface} inet6 ${new_ip6_address}/${new_ip6_prefixlen} alias if [ ${dad_wait_time} -le 0 ] then # if we're not waiting for DAD, assume we're good return 0 fi # Repeatedly test whether newly added address passed # duplicate address detection (DAD) i=0 while [ $i -lt ${dad_wait_time} ]; do sleep 1 # give the DAD some time addr=$(ifconfig ${interface} \ | grep "${new_ip6_address} prefixlen ${new_ip6_prefixlen}") # tentative flag == DAD is still not complete tentative=$(echo "${addr}" | grep tentative) # dadfailed flag == address is already in use somewhere else dadfailed=$(echo "${addr}" | grep duplicated) if [ -n "${dadfailed}" ] ; then # dad failed, remove the address ifconfig ${interface} inet6 ${new_ip6_address}/${new_ip6_prefixlen} -alias exit_with_hooks 3 fi if [ -z "${tentative}" ] ; then if [ -n "${addr}" ]; then # DAD is over return 0 else # address was auto-removed (or not added at all) exit_with_hooks 3 fi fi true $(( i++ )) done return 0 } # Invoke the local dhcp client enter hooks, if they exist. if [ -f /etc/dhclient-enter-hooks ]; then exit_status=0 . /etc/dhclient-enter-hooks # allow the local script to abort processing of this state # local script must set exit_status variable to nonzero. if [ $exit_status -ne 0 ]; then exit $exit_status fi fi if [ x$new_network_number != x ]; then echo New Network Number: $new_network_number fi if [ x$new_broadcast_address != x ]; then echo New Broadcast Address: $new_broadcast_address new_broadcast_arg="broadcast $new_broadcast_address" fi if [ x$old_broadcast_address != x ]; then old_broadcast_arg="broadcast $old_broadcast_address" fi if [ x$new_subnet_mask != x ]; then new_netmask_arg="netmask $new_subnet_mask" fi if [ x$old_subnet_mask != x ]; then old_netmask_arg="netmask $old_subnet_mask" fi if [ x$alias_subnet_mask != x ]; then alias_subnet_arg="netmask $alias_subnet_mask" fi if [ x$reason = xMEDIUM ]; then eval "ifconfig $interface $medium" eval "ifconfig $interface inet -alias 0.0.0.0 $medium" >/dev/null 2>&1 sleep 1 exit_with_hooks 0 fi ### ### DHCPv4 Handlers ### if [ x$reason = xPREINIT ]; then if [ x$alias_ip_address != x ]; then ifconfig $interface inet -alias $alias_ip_address > /dev/null 2>&1 route delete $alias_ip_address 127.0.0.1 > /dev/null 2>&1 fi ifconfig $interface inet 0.0.0.0 netmask 0.0.0.0 \ broadcast 255.255.255.255 up exit_with_hooks 0 fi if [ x$reason = xARPCHECK ] || [ x$reason = xARPSEND ]; then exit_with_hooks 0; fi if [ x$reason = xBOUND ] || [ x$reason = xRENEW ] || \ [ x$reason = xREBIND ] || [ x$reason = xREBOOT ]; then current_hostname=`hostname` if [ x$current_hostname = x ] || \ [ x$current_hostname = x$old_host_name ]; then if [ x$current_hostname = x ] || \ [ x$new_host_name != x$old_host_name ]; then hostname $new_host_name fi fi if [ x$old_ip_address != x ] && [ x$alias_ip_address != x ] && \ [ x$alias_ip_address != x$old_ip_address ]; then ifconfig $interface inet -alias $alias_ip_address > /dev/null 2>&1 route delete $alias_ip_address 127.0.0.1 > /dev/null 2>&1 fi if [ x$old_ip_address != x ] && [ x$old_ip_address != x$new_ip_address ] then eval "ifconfig $interface inet -alias $old_ip_address $medium" route delete $old_ip_address 127.1 >/dev/null 2>&1 for router in $old_routers; do route delete default $router >/dev/null 2>&1 done if [ "$old_static_routes" != "" ]; then set $old_static_routes while [ $# -gt 1 ]; do route delete $1 $2 shift; shift done fi arp -n -a | sed -n -e 's/^.*(\(.*\)) at .*$/arp -n -d \1/p' |sh fi if [ x$old_ip_address = x ] || [ x$old_ip_address != x$new_ip_address ] || \ [ x$reason = xBOUND ] || [ x$reason = xREBOOT ]; then eval "ifconfig $interface inet $new_ip_address $new_netmask_arg \ $new_broadcast_arg $medium" route add $new_ip_address 127.1 >/dev/null 2>&1 for router in $new_routers; do route add default $router >/dev/null 2>&1 done if [ "$new_static_routes" != "" ]; then set $new_static_routes while [ $# -gt 1 ]; do route add $1 $2 shift; shift done fi else # we haven't changed the address, have we changed other options # that we wish to update? if [ x$new_routers != x ] && [ x$new_routers != x$old_routers ] ; then # if we've changed routers delete the old and add the new. $LOGGER "New Routers: $new_routers" for router in $old_routers; do route delete default $router >/dev/null 2>&1 done for router in $new_routers; do route add default $router >/dev/null 2>&1 done fi fi if [ x$new_ip_address != x$alias_ip_address ] && [ x$alias_ip_address != x ]; then ifconfig $interface inet alias $alias_ip_address $alias_subnet_arg route add $alias_ip_address 127.0.0.1 fi make_resolv_conf exit_with_hooks 0 fi if [ x$reason = xEXPIRE ] || [ x$reason = xFAIL ] || [ x$reason = xRELEASE ] \ || [ x$reason = xSTOP ]; then if [ x$alias_ip_address != x ]; then ifconfig $interface inet -alias $alias_ip_address > /dev/null 2>&1 route delete $alias_ip_address 127.0.0.1 > /dev/null 2>&1 fi if [ x$old_ip_address != x ]; then eval "ifconfig $interface inet -alias $old_ip_address $medium" route delete $old_ip_address 127.1 >/dev/null 2>&1 for router in $old_routers; do route delete default $router >/dev/null 2>&1 done if [ "$old_static_routes" != "" ]; then set $old_static_routes while [ $# -gt 1 ]; do route delete $1 $2 shift; shift done fi arp -n -a | sed -n -e 's/^.*(\(.*\)) at .*$/arp -n -d \1/p' \ |sh >/dev/null 2>&1 fi if [ x$alias_ip_address != x ]; then ifconfig $interface inet alias $alias_ip_address $alias_subnet_arg route add $alias_ip_address 127.0.0.1 fi exit_with_hooks 0 fi if [ x$reason = xTIMEOUT ]; then if [ x$alias_ip_address != x ]; then ifconfig $interface inet -alias $alias_ip_address > /dev/null 2>&1 route delete $alias_ip_address 127.0.0.1 > /dev/null 2>&1 fi eval "ifconfig $interface inet $new_ip_address $new_netmask_arg \ $new_broadcast_arg $medium" sleep 1 if [ "$new_routers" != "" ]; then set $new_routers if ping -q -c 1 -w 1 $1; then if [ x$new_ip_address != x$alias_ip_address ] && \ [ x$alias_ip_address != x ]; then ifconfig $interface inet alias $alias_ip_address $alias_subnet_arg route add $alias_ip_address 127.0.0.1 fi route add $new_ip_address 127.1 >/dev/null 2>&1 for router in $new_routers; do route add default $router >/dev/null 2>&1 done set $new_static_routes while [ $# -gt 1 ]; do route add $0 $1 shift; shift done make_resolv_conf exit_with_hooks 0 fi fi eval "ifconfig $interface inet -alias $new_ip_address $medium" for router in $old_routers; do route delete default $router >/dev/null 2>&1 done if [ "$old_static_routes" != "" ]; then set $old_static_routes while [ $# -gt 1 ]; do route delete $1 $2 shift; shift done fi arp -n -a | sed -n -e 's/^.*(\(.*\)) at .*$/arp -n -d \1/p' \ |sh >/dev/null 2>&1 exit_with_hooks 1 fi ### ### DHCPv6 Handlers ### if [ ${reason} = PREINIT6 ] ; then # Ensure interface is up. ifconfig ${interface} up # XXX: Remove any stale addresses from aborted clients. # We need to give the kernel some time to active interface interface_up_wait_time=5 i=0 while [ $i -lt ${interface_up_wait_time} ]; do ifconfig ${interface} | grep inactive >/dev/null 2>&1 if [ $? -ne 0 ]; then break; fi sleep 1 true $(( i++ )) done # Wait for duplicate address detection for this interface if the # --dad-wait-time parameter has been specified and is greater than # zero. if [ ${dad_wait_time} -gt 0 ]; then # Check if any IPv6 address on this interface is marked as # tentative. ifconfig ${interface} | grep inet6 | grep tentative \ >/dev/null 2>&1 if [ $? -eq 0 ]; then # Wait for duplicate address detection to complete or for # the timeout specified as --dad-wait-time. i=0 while [ $i -lt ${dad_wait_time} ]; do # We're going to poll for the tentative flag every second. sleep 1 ifconfig ${interface} | grep inet6 | grep tentative \ >/dev/null 2>&1 if [ $? -ne 0 ]; then break; fi true $(( i++ )) done fi fi exit_with_hooks 0 fi if [ x${old_ip6_prefix} != x ] || [ x${new_ip6_prefix} != x ] ; then echo Prefix ${reason} old=${old_ip6_prefix} new=${new_ip6_prefix} exit_with_hooks 0 fi if [ ${reason} = BOUND6 ] ; then if [ x${new_ip6_address} = x ] || [ x${new_ip6_prefixlen} = x ] ; then exit_with_hooks 2; fi # Add address to interface, check for DAD if dad_wait_time > 0 add_ipv6_addr_with_DAD # Check for nameserver options. make_resolv_conf exit_with_hooks 0 fi if [ ${reason} = RENEW6 ] || [ ${reason} = REBIND6 ] ; then # Make sure nothing has moved around on us. # Nameservers/domains/etc. if [ "x${new_dhcp6_name_servers}" != "x${old_dhcp6_name_servers}" ] || [ "x${new_dhcp6_domain_search}" != "x${old_dhcp6_domain_search}" ] ; then make_resolv_conf fi exit_with_hooks 0 fi if [ ${reason} = DEPREF6 ] ; then if [ x${new_ip6_prefixlen} = x ] ; then exit_with_hooks 2; fi # XXX: # There doesn't appear to be a way to update an addr to indicate # preference. exit_with_hooks 0 fi if [ ${reason} = EXPIRE6 -o ${reason} = RELEASE6 -o ${reason} = STOP6 ] ; then if [ x${old_ip6_address} = x ] || [ x${old_ip6_prefixlen} = x ] ; then exit_with_hooks 2; fi ifconfig ${interface} inet6 -alias ${old_ip6_address}/${old_ip6_prefixlen} exit_with_hooks 0 fi exit_with_hooks 0 dhcp-4.4.1/client/scripts/openwrt000755 000765 000024 00000020301 13243301226 017300 0ustar00tmarkstaff000000 000000 #!/bin/sh # 'ip' just looks too weird. /sbin/ip looks less weird. ip=/usr/sbin/ip make_resolv_conf() { if [ x"$new_domain_name_servers" != x ]; then cat /dev/null > /etc/resolv.conf.dhclient chmod 644 /etc/resolv.conf.dhclient if [ x"$new_domain_search" != x ]; then echo search $new_domain_search >> /etc/resolv.conf.dhclient elif [ x"$new_domain_name" != x ]; then # Note that the DHCP 'Domain Name Option' is really just a domain # name, and that this practice of using the domain name option as # a search path is both nonstandard and deprecated. echo search $new_domain_name >> /etc/resolv.conf.dhclient fi for nameserver in $new_domain_name_servers; do echo nameserver $nameserver >>/etc/resolv.conf.dhclient done mv /etc/resolv.conf.dhclient /etc/resolv.conf elif [ "x${new_dhcp6_name_servers}" != x ] ; then cat /dev/null > /etc/resolv.conf.dhclient6 chmod 644 /etc/resolv.conf.dhclient6 if [ "x${new_dhcp6_domain_search}" != x ] ; then echo search ${new_dhcp6_domain_search} >> /etc/resolv.conf.dhclient6 fi for nameserver in ${new_dhcp6_name_servers} ; do # If the nameserver has a link-local address # add a (interface name) to it. case $nameserver in fe80:*) zone_id="%$interface";; FE80:*) zone_id="%$interface";; *) zone_id="";; esac echo nameserver ${nameserver}$zone_id >> /etc/resolv.conf.dhclient6 done mv /etc/resolv.conf.dhclient6 /etc/resolv.conf fi } # Must be used on exit. Invokes the local dhcp client exit hooks, if any. exit_with_hooks() { exit_status=$1 if [ -f /etc/dhclient-exit-hooks ]; then . /etc/dhclient-exit-hooks fi # probably should do something with exit status of the local script exit $exit_status } # Invoke the local dhcp client enter hooks, if they exist. if [ -f /etc/dhclient-enter-hooks ]; then exit_status=0 . /etc/dhclient-enter-hooks # allow the local script to abort processing of this state # local script must set exit_status variable to nonzero. if [ $exit_status -ne 0 ]; then exit $exit_status fi fi ### ### DHCPv4 Handlers ### if [ x$new_broadcast_address != x ]; then new_broadcast_arg="broadcast $new_broadcast_address" fi if [ x$new_subnet_mask != x ]; then new_subnet_arg="netmask $new_subnet_mask" fi if [ x$alias_subnet_mask != x ]; then alias_subnet_arg="netmask $alias_subnet_mask" fi if [ x$new_interface_mtu != x ]; then mtu_arg="mtu $new_interface_mtu" fi if [ x$IF_METRIC != x ]; then metric_arg="metric $IF_METRIC" fi if [ x$reason = xMEDIUM ]; then # Linux doesn't do mediums (ok, ok, media). exit_with_hooks 0 fi if [ x$reason = xPREINIT ]; then if [ x$alias_ip_address != x ]; then # Bring down alias interface. Its routes will disappear too. ifconfig $interface:0- 0.0.0.0 fi ifconfig $interface 0.0.0.0 up # We need to give the kernel some time to get the interface up. sleep 1 exit_with_hooks 0 fi if [ x$reason = xARPCHECK ] || [ x$reason = xARPSEND ]; then exit_with_hooks 0 fi if [ x$reason = xBOUND ] || [ x$reason = xRENEW ] || \ [ x$reason = xREBIND ] || [ x$reason = xREBOOT ]; then current_hostname=`hostname` if [ x$current_hostname = x ] || \ [ x$current_hostname = x$old_host_name ]; then if [ x$current_hostname = x ] || \ [ x$new_host_name != x$old_host_name ]; then hostname $new_host_name fi fi if [ x$old_ip_address != x ] && [ x$alias_ip_address != x ] && \ [ x$alias_ip_address != x$old_ip_address ]; then # Possible new alias. Remove old alias. ifconfig $interface:0- 0.0.0.0 fi if [ x$old_ip_address != x ] && \ [ x$old_ip_address != x$new_ip_address ]; then # IP address changed. Bringing down the interface will delete all routes, # and clear the ARP cache. ifconfig $interface 0.0.0.0 down fi if [ x$old_ip_address = x ] || [ x$old_ip_address != x$new_ip_address ] || \ [ x$reason = xBOUND ] || [ x$reason = xREBOOT ]; then ifconfig $interface $new_ip_address $new_subnet_arg \ $new_broadcast_arg $mtu_arg for router in $new_routers; do if [ "x$new_subnet_mask" = "x255.255.255.255" ] ; then route add -host $router dev $interface fi route add default gw $router $metric_arg dev $interface done else # we haven't changed the address, have we changed other options # that we wish to update? if [ x$new_routers != x ] && [ x$new_routers != x$old_routers ] ; then # if we've changed routers delete the old and add the new. $LOGGER "New Routers: $new_routers" for router in $old_routers; do route del default gw $router done for router in $new_routers; do if [ "x$new_subnet_mask" = "x255.255.255.255" ] ; then route add -host $router dev $interface fi route add default gw $router $metric_arg dev $interface done fi fi if [ x$new_ip_address != x$alias_ip_address ] && [ x$alias_ip_address != x ]; then ifconfig $interface:0- 0.0.0.0 ifconfig $interface:0 $alias_ip_address $alias_subnet_arg route add -host $alias_ip_address $interface:0 fi make_resolv_conf exit_with_hooks 0 fi if [ x$reason = xEXPIRE ] || [ x$reason = xFAIL ] || [ x$reason = xRELEASE ] \ || [ x$reason = xSTOP ]; then if [ x$alias_ip_address != x ]; then # Turn off alias interface. ifconfig $interface:0- 0.0.0.0 fi if [ x$old_ip_address != x ]; then # Shut down interface, which will delete routes and clear arp cache. ifconfig $interface 0.0.0.0 down fi if [ x$alias_ip_address != x ]; then ifconfig $interface:0 $alias_ip_address $alias_subnet_arg route add -host $alias_ip_address $interface:0 fi exit_with_hooks 0 fi if [ x$reason = xTIMEOUT ]; then if [ x$alias_ip_address != x ]; then ifconfig $interface:0- 0.0.0.0 fi ifconfig $interface $new_ip_address $new_subnet_arg \ $new_broadcast_arg $mtu_arg set $new_routers if ping -q -c 1 $1; then if [ x$new_ip_address != x$alias_ip_address ] && \ [ x$alias_ip_address != x ]; then ifconfig $interface:0 $alias_ip_address $alias_subnet_arg route add -host $alias_ip_address dev $interface:0 fi for router in $new_routers; do if [ "x$new_subnet_mask" = "x255.255.255.255" ] ; then route add -host $router dev $interface fi route add default gw $router $metric_arg dev $interface done make_resolv_conf exit_with_hooks 0 fi ifconfig $interface 0.0.0.0 down exit_with_hooks 1 fi ### ### DHCPv6 Handlers ### if [ x$reason = xPREINIT6 ]; then # Ensure interface is up. ${ip} link set ${interface} up # Remove any stale addresses from aborted clients. ${ip} -f inet6 addr flush dev ${interface} scope global permanent exit_with_hooks 0 fi if [ x${old_ip6_prefix} != x ] || [ x${new_ip6_prefix} != x ] ; then echo Prefix ${reason} old=${old_ip6_prefix} new=${new_ip6_prefix} exit_with_hooks 0 fi if [ x$reason = xBOUND6 ]; then if [ x${new_ip6_address} = x ] || [ x${new_ip6_prefixlen} = x ] ; then exit_with_hooks 2; fi ${ip} -f inet6 addr add ${new_ip6_address}/${new_ip6_prefixlen} \ dev ${interface} scope global # Check for nameserver options. make_resolv_conf exit_with_hooks 0 fi if [ x$reason = xRENEW6 ] || [ x$reason = xREBIND6 ]; then if [ x${new_ip6_address} != x ] && [ x${new_ip6_prefixlen} != x ] ; then ${ip} -f inet6 addr add ${new_ip6_address}/${new_ip6_prefixlen} \ dev ${interface} scope global fi # Make sure nothing has moved around on us. # Nameservers/domains/etc. if [ "x${new_dhcp6_name_servers}" != "x${old_dhcp6_name_servers}" ] || [ "x${new_dhcp6_domain_search}" != "x${old_dhcp6_domain_search}" ] ; then make_resolv_conf fi exit_with_hooks 0 fi if [ x$reason = xDEPREF6 ]; then if [ x${new_ip6_prefixlen} = x ] ; then exit_with_hooks 2; fi ${ip} -f inet6 addr change ${new_ip6_address}/${new_ip6_prefixlen} \ dev ${interface} scope global preferred_lft 0 exit_with_hooks 0 fi if [ x$reason = xEXPIRE6 -o x$reason = xRELEASE6 -o x$reason = xSTOP6 ]; then if [ x${old_ip6_address} = x ] || [ x${old_ip6_prefixlen} = x ] ; then exit_with_hooks 2; fi ${ip} -f inet6 addr del ${old_ip6_address}/${old_ip6_prefixlen} \ dev ${interface} exit_with_hooks 0 fi exit_with_hooks 0 dhcp-4.4.1/client/scripts/solaris000755 000765 000024 00000014751 13243301226 017272 0ustar00tmarkstaff000000 000000 #!/bin/sh make_resolv_conf() { if [ x"$new_domain_name_servers" != x ]; then cat /dev/null > /etc/resolv.conf.dhclient if [ x"$new_domain_search" != x ]; then echo search $new_domain_search >> /etc/resolv.conf.dhclient elif [ x"$new_domain_name" != x ]; then # Note that the DHCP 'Domain Name Option' is really just a domain # name, and that this practice of using the domain name option as # a search path is both nonstandard and deprecated. echo search $new_domain_name >> /etc/resolv.conf.dhclient fi for nameserver in $new_domain_name_servers; do echo nameserver $nameserver >>/etc/resolv.conf.dhclient done mv /etc/resolv.conf.dhclient /etc/resolv.conf fi } # Must be used on exit. Invokes the local dhcp client exit hooks, if any. exit_with_hooks() { exit_status=$1 if [ -f /etc/dhclient-exit-hooks ]; then . /etc/dhclient-exit-hooks fi # probably should do something with exit status of the local script exit $exit_status } # Invoke the local dhcp client enter hooks, if they exist. if [ -f /etc/dhclient-enter-hooks ]; then exit_status=0 . /etc/dhclient-enter-hooks # allow the local script to abort processing of this state # local script must set exit_status variable to nonzero. if [ $exit_status -ne 0 ]; then exit $exit_status fi fi if [ x$new_broadcast_address != x ]; then new_broadcast_arg="broadcast $new_broadcast_address" fi if [ x$old_broadcast_address != x ]; then old_broadcast_arg="broadcast $old_broadcast_address" fi if [ x$new_subnet_mask != x ]; then new_netmask_arg="netmask $new_subnet_mask" fi if [ x$old_subnet_mask != x ]; then old_netmask_arg="netmask $old_subnet_mask" fi if [ x$alias_subnet_mask != x ]; then alias_subnet_arg="netmask $alias_subnet_mask" fi if [ x$new_interface_mtu != x ]; then mtu_arg="mtu $new_interface_mtu" fi if [ x$IF_METRIC != x ]; then metric_arg="metric $IF_METRIC" fi ifconfig=/sbin/ifconfig release=`uname -r` release=`expr $release : '\(.*\)\..*'` relmajor=`echo $release |sed -e 's/^\([^\.]*\)\..*$/\1/'` relminor=`echo $release |sed -e 's/^.*\.\([^\.]*\)$/\1/'` if [ x$reason = xMEDIUM ]; then eval "$ifconfig $interface $medium" $ifconfig $interface sleep 1 exit_with_hooks 0 fi if [ x$reason = xPREINIT ]; then if [ x$alias_ip_address != x ]; then $ifconfig ${interface}:1 0 down > /dev/null 2>&1 route delete $alias_ip_address 127.0.0.1 > /dev/null 2>&1 fi if [ $relmajor -gt 5 ] || ( [ $relmajor -eq 5 ] && [ $relminor -ge 5 ] ) then # Turn the interface on $ifconfig $interface plumb $ifconfig $interface up else $ifconfig $interface inet 0.0.0.0 netmask 0.0.0.0 \ broadcast 255.255.255.255 up fi exit_with_hooks 0 fi if [ x$reason = xARPCHECK ] || [ x$reason = xARPSEND ]; then exit_with_hooks 0; fi if [ x$reason = xBOUND ] || [ x$reason = xRENEW ] || \ [ x$reason = xREBIND ] || [ x$reason = xREBOOT ]; then current_hostname=`hostname` if [ x$current_hostname = x ] || \ [ x$current_hostname = x$old_host_name ]; then if [ x$current_hostname = x ] || \ [ x$new_host_name != x$old_host_name ]; then hostname $new_host_name fi fi if [ x$old_ip_address != x ] && [ x$alias_ip_address != x ] && \ [ x$alias_ip_address != x$old_ip_address ]; then $ifconfig ${interface}:1 inet 0 down > /dev/null 2>&1 route delete $alias_ip_address 127.0.0.1 > /dev/null 2>&1 fi if [ x$old_ip_address != x ] && [ x$old_ip_address != x$new_ip_address ]; then $ifconfig ${interface} inet 0 down route delete $old_ip_address 127.1 >/dev/null 2>&1 for router in $old_routers; do route delete default $router >/dev/null 2>&1 done fi if [ x$old_ip_address = x ] || [ x$old_ip_address != x$new_ip_address ] || \ [ x$reason = xBOUND ] || [ x$reason = xREBOOT ]; then eval "$ifconfig $interface inet $new_ip_address $new_netmask_arg \ $new_broadcast_arg $mtu_arg $metric_arg $medium" route add $new_ip_address 127.1 1 >/dev/null 2>&1 for router in $new_routers; do route add default $router 1 >/dev/null 2>&1 done else # we haven't changed the address, have we changed other options # that we wish to update? if [ x$new_routers != x ] && [ x$new_routers != x$old_routers ] ; then # if we've changed routers delete the old and add the new. $LOGGER "New Routers: $new_routers" for router in $old_routers; do route delete default $router >/dev/null 2>&1 done for router in $new_routers; do route add default $router 1 >/dev/null 2>&1 done fi fi if [ x$new_ip_address != x$alias_ip_address ] && [ x$alias_ip_address != x ]; then $ifconfig ${interface}:1 inet $alias_ip_address $alias_subnet_arg route add $alias_ip_address 127.0.0.1 1 fi make_resolv_conf exit_with_hooks 0 fi if [ x$reason = xEXPIRE ] || [ x$reason = xFAIL ] || [ x$reason = xRELEASE ] \ || [ x$reason = xSTOP ]; then if [ x$alias_ip_address != x ]; then $ifconfig ${interface}:1 0 down > /dev/null 2>&1 route delete $alias_ip_address 127.0.0.1 > /dev/null 2>&1 fi if [ x$old_ip_address != x ]; then $ifconfig $interface inet 0 down route delete $old_ip_address 127.1 >/dev/null 2>&1 for router in $old_routers; do route delete default $router >/dev/null 2>&1 done fi if [ x$alias_ip_address != x ]; then $ifconfig ${interface}:1 inet $alias_ip_address $alias_subnet_arg route add $alias_ip_address 127.0.0.1 1 fi exit_with_hooks 0 fi if [ x$reason = xTIMEOUT ]; then if [ x$alias_ip_address != x ]; then $ifconfig ${interface}:1 0 down > /dev/null 2>&1 route delete $alias_ip_address 127.0.0.1 > /dev/null 2>&1 fi eval "$ifconfig $interface inet $new_ip_address $new_netmask_arg \ $new_broadcast_arg $mtu_arg $metric_arg $medium" sleep 1 set $new_routers if ping -s -n -I 1 $1 64 1; then if [ x$new_ip_address != x$alias_ip_address ] && \ [ x$alias_ip_address != x ]; then $ifconfig ${interface}:1 inet $alias_ip_address $alias_subnet_arg route add $alias_ip_address 127.0.0.1 1 fi route add $new_ip_address 127.1 1 >/dev/null 2>&1 for router in $new_routers; do route add default $router 1 >/dev/null 2>&1 done make_resolv_conf exit_with_hooks 0 fi $ifconfig $interface inet 0 down for router in $old_routers; do route delete default $router >/dev/null 2>&1 done exit_with_hooks 1 fi exit_with_hooks 0