summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.conf1
-rw-r--r--README38
-rw-r--r--RELNOTES107
-rw-r--r--client/Makefile.dist4
-rw-r--r--client/clparse.c534
-rw-r--r--client/dhc6.c3043
-rw-r--r--client/dhclient.816
-rw-r--r--client/dhclient.c584
-rw-r--r--client/dhclient.conf.518
-rwxr-xr-xclient/scripts/linux97
-rw-r--r--common/Makefile.dist4
-rw-r--r--common/alloc.c16
-rw-r--r--common/conflex.c39
-rw-r--r--common/ctrace.c17
-rw-r--r--common/dhcp-eval.52
-rw-r--r--common/dhcp-options.535
-rw-r--r--common/discover.c1012
-rw-r--r--common/dlpi.c93
-rw-r--r--common/dns.c81
-rw-r--r--common/heap.c242
-rw-r--r--common/inet.c349
-rw-r--r--common/lpf.c55
-rw-r--r--common/options.c1145
-rw-r--r--common/packet.c2
-rw-r--r--common/parse.c259
-rw-r--r--common/print.c5
-rw-r--r--common/socket.c361
-rw-r--r--common/tables.c454
-rw-r--r--common/tree.c95
-rw-r--r--dhcpctl/omshell.c10
-rw-r--r--doc/Makefile29
-rw-r--r--doc/References.html804
-rw-r--r--doc/References.txt840
-rw-r--r--doc/References.xml668
-rw-r--r--doc/draft-ietf-dhc-authentication-14.txt893
-rw-r--r--doc/draft-ietf-dhc-dhcp-dns-12.txt1072
-rw-r--r--doc/draft-ietf-dhc-failover-12.txt7451
-rw-r--r--doc/rfc1542.txt1291
-rw-r--r--doc/rfc2131.txt2523
-rw-r--r--doc/rfc2132.txt1907
-rw-r--r--doc/rfc2485.txt227
-rw-r--r--doc/rfc2489.txt283
-rw-r--r--doc/rfc951.txt684
-rw-r--r--includes/cf/aix.h5
-rw-r--r--includes/cf/alphaosf.h3
-rw-r--r--includes/cf/bsdos.h6
-rw-r--r--includes/cf/cygwin32.h6
-rw-r--r--includes/cf/freebsd.h6
-rw-r--r--includes/cf/hpux.h3
-rw-r--r--includes/cf/irix.h3
-rw-r--r--includes/cf/linux.h3
-rw-r--r--includes/cf/netbsd.h6
-rw-r--r--includes/cf/nextstep.h4
-rw-r--r--includes/cf/openbsd.h6
-rw-r--r--includes/cf/qnx.h3
-rw-r--r--includes/cf/rhapsody.h6
-rw-r--r--includes/cf/sco.h6
-rw-r--r--includes/cf/sunos4.h3
-rw-r--r--includes/cf/sunos5-5.h4
-rw-r--r--includes/cf/ultrix.h3
-rw-r--r--includes/dhcp.h8
-rw-r--r--includes/dhcp6.h160
-rw-r--r--includes/dhcpd.h441
-rw-r--r--includes/dhctoken.h18
-rw-r--r--includes/heap.h163
-rw-r--r--includes/inet.h16
-rw-r--r--includes/tree.h4
-rw-r--r--includes/version.h2
-rw-r--r--minires/res_mkupdate.c13
-rw-r--r--omapip/hash.c2
-rw-r--r--relay/dhcrelay.82
-rw-r--r--relay/dhcrelay.c47
-rw-r--r--server/Makefile.dist21
-rw-r--r--server/bootp.c16
-rw-r--r--server/confpars.c1056
-rw-r--r--server/db.c133
-rw-r--r--server/ddns.c401
-rw-r--r--server/dhcp.c51
-rw-r--r--server/dhcpd.814
-rw-r--r--server/dhcpd.c93
-rw-r--r--server/dhcpd.conf.5132
-rw-r--r--server/dhcpv6.c3010
-rw-r--r--server/mdb.c254
-rw-r--r--server/mdb6.c1737
-rw-r--r--server/stables.c9
-rw-r--r--tests/DHCPv6/000-badmsgtype.pl144
-rw-r--r--tests/DHCPv6/010-solicit-noclientid.pl192
-rw-r--r--tests/DHCPv6/011-solicit-serverid.pl195
-rw-r--r--tests/DHCPv6/020-advertise-mcast.pl144
-rw-r--r--tests/DHCPv6/030-request-noclientid.pl192
-rw-r--r--tests/DHCPv6/031-request-noserverid.pl192
-rw-r--r--tests/DHCPv6/032-request-badduid.pl196
-rw-r--r--tests/DHCPv6/110-information-request-ia_na.pl192
-rw-r--r--tests/DHCPv6/111-information-request-ia_ta.pl192
-rw-r--r--tests/DHCPv6/112-badduid.pl188
-rw-r--r--tests/DHCPv6/210-solicit-nohost.pl192
-rw-r--r--tests/DHCPv6/211-solicit-opt-in-na.pl198
-rw-r--r--tests/DHCPv6/212-solicit-opt-in-na-norapidcommit.pl198
-rw-r--r--tests/DHCPv6/280-release-nohost.pl155
-rw-r--r--tests/DHCPv6/281-release-bad-address.pl167
-rw-r--r--tests/DHCPv6/282-release-no-address.pl163
-rw-r--r--tests/DHCPv6/283-release.pl169
-rw-r--r--tests/DHCPv6/290-decline-nohost.pl155
-rw-r--r--tests/DHCPv6/291-decline-bad-address.pl167
-rw-r--r--tests/DHCPv6/292-decline-no-address.pl163
-rw-r--r--tests/DHCPv6/293-decline.pl169
-rw-r--r--tests/DHCPv6/README62
-rw-r--r--tests/DHCPv6/dhcp_client.pm435
-rw-r--r--tests/DHCPv6/stubcli-opt-in-na.pl197
-rw-r--r--tests/DHCPv6/stubcli.pl192
-rw-r--r--tests/DHCPv6/test-a.conf67
-rw-r--r--tests/DHCPv6/test-b.conf60
112 files changed, 22565 insertions, 17440 deletions
diff --git a/Makefile.conf b/Makefile.conf
index 8d6cb796..adfb5cbf 100644
--- a/Makefile.conf
+++ b/Makefile.conf
@@ -131,6 +131,7 @@ MINORVERSION=MinorVersion
#COPTS = $(BINDDEF) -Wall -Wno-unused -Wno-implicit -Wno-comment \
# -Wno-uninitialized -Wno-char-subscripts -Wno-switch $(WARNERR) \
# -DSOLARIS_MAJOR=$(MAJORVERSION) -DSOLARIS_MINOR=$(MINORVERSION) \
+# -D_XPG4_2 -D__EXTENSIONS__ \
# $(CC_OPTIONS)
#CF = cf/sunos5-5.h
#ADMMANDIR = /usr/share/man/man1m
diff --git a/README b/README
index c055cf52..44fec624 100644
--- a/README
+++ b/README
@@ -1,9 +1,13 @@
Internet Systems Consortium DHCP Distribution
- Version 3.1.0a1
- August 1, 2006
+ Version 4.0.0-2007041300
+ April 13, 2007
README FILE
+ **********************************************************************
+ *** This is NOT a public release, and should not be redistributed. ***
+ **********************************************************************
+
You should read this file carefully before trying to install or use
the ISC DHCP Distribution.
@@ -92,24 +96,18 @@ directory, it may not have up-to-date information).
RELEASE STATUS
-This is the first ALPHA quality release of ISC DHCP 3.1.0, a feature
-release based upon the work present in the 3.0.x development track.
-It contains a large number of new features, and with that most likely
-many new bugs.
+This is a snapshot release of work leading up to the first alpha
+of ISC DHCP 4.0.0, a feature release bent to the purpose of implemeting
+DHCPv6.
In this release, the server and relay agent are currently fully
-functional on NetBSD, Linux systems with kernel version 2.2 or later,
-FreeBSD, OpenBSD, BSD/OS, Digital Tru64 Unix and Solaris. The software
-will also run on AIX and HP-UX, but only supports a single network
-interface. Ports also exist for QNX, SCO, NeXTStep, and MacOS X, but
-are not in wide use, with all that implies. We are not aware of an
-easy way to get this software running on HP-UX.
+functional on Linux, and Solaris. The software may also runon other
+systems.
The DHCP client currently only knows how to configure the network on
-NetBSD, FreeBSD, OpenBSD, BSD/os, Linux, Solaris and NextStep. The
-client depends on a system-dependent shell script to do network
-configuration - support for other operating systems is simply a matter
-of porting this shell script to the new platform.
+Linux. The client depends on a system-dependent shell script to do
+network configuration - support for other operating systems is simply
+a matter of porting this shell script to the new platform.
If you are running the DHCP distribution on a machine which is a
firewall, or if there is a firewall between your DHCP server(s) and
@@ -138,13 +136,13 @@ information. On Digital Unix, type ``man pfilt''.
To build the DHCP Distribution, unpack the compressed tar file using
the tar utility and the gzip command - type something like:
- gunzip dhcp-3.1.0a1.tar.gz
- tar xvf dhcp-3.1.0a1.tar
+ gunzip dhcp-4.0.0-2007041300.tar.gz
+ tar xvf dhcp-4.0.0-2007041300.tar
CONFIGURING IT
-Now, cd to the dhcp-3.1.0a1 subdirectory that you've just created and
-configure the source tree by typing:
+Now, cd to the dhcp-dhcp-4.0.0-2007041300 subdirectory that you've
+just created and configure the source tree by typing:
./configure
diff --git a/RELNOTES b/RELNOTES
index bcd9e470..37ce554e 100644
--- a/RELNOTES
+++ b/RELNOTES
@@ -1,31 +1,102 @@
Internet Systems Consortium DHCP Distribution
- Version 3.1.0a1
- August 1, 2006
+ Version V4.0.0-2007041300
+ April 13, 2007
+
+ **********************************************************************
+ *** This is NOT a public release, and should not be redistributed. ***
+ **********************************************************************
Release Notes
NEW FEATURES
-Version 3.1.x of the ISC DHCP Distribution includes the following major
-new features compared to its 3.0.x derivative:
+The DHCP server in version 4.0.x of the ISC DHCP Distribution supports
+DHCPv6 in addition to DHCP for IPv4.
+
+Several new options have been added to the dhcpd executable, which are
+documented in the man page. Several changes have been made to the
+configuration file as well, which are also documented in the man
+pages.
+
+There are a number of DHCPv6 limitiations and features missing in this
+release, which will be addressed shortly:
+
+- Only Solaris and Linux are supported.
+
+- There is no DHCPv6 Relay support.
+
+- Only a single fixed address is supported per IA.
+
+- IA_TA addresses are not supported.
- - Failover protocol 'MAC Address Affinity' to reduce pool churn.
- - Support for the 'reserved' and 'bootp' failover flags, which
- means in lay terms that static allocations can be made to
- clients in which 'on events' can be supported.
- - Several other failover optimizations and changes.
- - Management of class and subclass statements via OMAPI.
- - Many new configuration statement functions.
- - Initial formal support for VIVCO/VIVSO options.
+- IA_PD prefixes are not supported.
-For a full list of new features added in this release, please observe
-the changes list directly following this section.
+- DHCPv6 includes human-readable text in status code messages. These
+ should be configurable, and probably localized via gettext() or the
+ like.
+
+- The "host-identifier" option is limited to a simple token.
+
+- Dynamically allocated leases do not respond to Confirm messages.
+
+- Old (expired) leases are never cleaned.
+
+- The client and server can only operate DHCPv4 or DHCPv6 at a time,
+ not both, so two instances of the daemons are required with the
+ "-6" command line option.
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.
- Changes since 3.1.0b1
+
+ Changes since 4.0.0-20070413
+
+- An obviated option code hash lookup to find D6O_CLIENTID was removed.
+
+ 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.
+
+ Chagnes since 3.1.0b1
- Fixed a bug that caused OMAPI clients to freeze when opening lease
objects.
@@ -104,7 +175,7 @@ the README file.
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' (psuedo-static) and BOOTP leases via failover
+- Support for 'reserved' (pseudo-static) and BOOTP leases via failover
was introduced.
- Support for adding, removing, and managing class and subclass statements
@@ -409,7 +480,7 @@ the README file.
- 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
- wether freely-allocated or not, unless the server doubts the peer has
+ 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
@@ -498,7 +569,7 @@ the README file.
Thanks to a patch from Jason Vas Dias at RedHat.
- File handlers on configuration state (config files and lease dbs) should
- be treated consistently, regardless of wether TRACING is defined or not.
+ 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),
diff --git a/client/Makefile.dist b/client/Makefile.dist
index c1239632..a946b176 100644
--- a/client/Makefile.dist
+++ b/client/Makefile.dist
@@ -25,8 +25,8 @@ CATMANPAGES = dhclient.cat8 dhclient.conf.cat5 dhclient-script.cat8 \
dhclient.leases.cat5
SEDMANPAGES = dhclient.man8 dhclient.conf.man5 dhclient-script.man8 \
dhclient.leases.man5
-SRCS = dhclient.c clparse.c
-OBJS = dhclient.o clparse.o
+SRCS = dhclient.c clparse.c dhc6.c
+OBJS = dhclient.o clparse.o dhc6.o
PROG = dhclient
MAN = dhclient.8 dhclient.conf.5 dhclient-script.8 dhclient.leases.5
diff --git a/client/clparse.c b/client/clparse.c
index 04f12b63..cd530160 100644
--- a/client/clparse.c
+++ b/client/clparse.c
@@ -34,7 +34,7 @@
#ifndef lint
static char copyright[] =
-"$Id: clparse.c,v 1.68 2007/01/29 10:25:54 shane Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
+"$Id: clparse.c,v 1.69 2007/05/08 23:05:20 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
@@ -54,6 +54,11 @@ u_int32_t default_requested_options [] = {
0
};
+static void parse_client_default_duid(struct parse *cfile);
+static void parse_client6_lease_statement(struct parse *cfile);
+static struct dhc6_ia *parse_client6_ia_statement(struct parse *cfile);
+static struct dhc6_addr *parse_client6_iaaddr_statement(struct parse *cfile);
+
/* client-conf-file :== client-declarations END_OF_FILE
client-declarations :== <nil>
| client-declaration
@@ -66,9 +71,6 @@ isc_result_t read_client_conf ()
struct interface_info *ip;
isc_result_t status;
- /* Set up the initial dhcp option universe. */
- initialize_common_option_spaces ();
-
/* Initialize the top level client configuration. */
memset (&top_level_config, 0, sizeof top_level_config);
@@ -84,6 +86,9 @@ isc_result_t read_client_conf ()
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)
@@ -200,13 +205,25 @@ void read_client_leases ()
token = next_token (&val, (unsigned *)0, cfile);
if (token == END_OF_FILE)
break;
- if (token != LEASE) {
+
+ 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;
- } else
- parse_client_lease_statement (cfile, 0);
-
+ }
} while (1);
end_parse (&cfile);
@@ -825,7 +842,7 @@ void make_client_config (client, config)
}
/* client-lease-statement :==
- RBRACE client-lease-declarations LBRACE
+ LBRACE client-lease-declarations RBRACE
client-lease-declarations :==
<nil> |
@@ -1071,6 +1088,505 @@ void parse_client_lease_declaration (cfile, lease, ipp, clientp)
}
}
+/* Parse a default-duid ""; statement.
+ */
+static void
+parse_client_default_duid(struct parse *cfile)
+{
+ struct data_string new_duid;
+ const char *val = NULL;
+ unsigned len;
+ int token;
+
+ memset(&new_duid, 0, sizeof(new_duid));
+
+ token = next_token(&val, &len, cfile);
+ if (token != STRING) {
+ parse_warn(cfile, "Expected DUID string.");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ if (len <= 2) {
+ parse_warn(cfile, "Invalid DUID contents.");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ 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, val, 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)
+{
+ 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_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;
+
+ 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.");
+
+ 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;
+}
+
+/* Parse an ia_na object from the client lease.
+ */
+static struct dhc6_ia *
+parse_client6_ia_statement(struct parse *cfile)
+{
+ struct data_string id;
+ struct option_cache *oc = NULL;
+ struct dhc6_ia *ia;
+ struct dhc6_addr **addr;
+ const char *val;
+ unsigned len;
+ int token, no_semi;
+
+ ia = dmalloc(sizeof(*ia), MDL);
+ if (ia == NULL) {
+ parse_warn(cfile, "Out of memory allocating IA_NA state.");
+ skip_to_semi(cfile);
+ return NULL;
+ }
+
+ /* Get IAID. */
+ memset(&id, 0, sizeof(id));
+ if (parse_cshl(&id, cfile)) {
+ if (id.len == 4)
+ memcpy(ia->iaid, id.data, 4);
+ else {
+ parse_warn(cfile, "Expecting IAID of length 4, got %d.",
+ id.len);
+ skip_to_semi(cfile);
+ dfree(ia, MDL);
+ return NULL;
+ }
+ data_string_forget(&id, MDL);
+ } else {
+ parse_warn(cfile, "Expecting IAID.");
+ 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;
+}
+
+/* Parse an iaaddr {} structure. */
+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;
+}
+
void parse_string_list (cfile, lp, multiple)
struct parse *cfile;
struct string_list **lp;
diff --git a/client/dhc6.c b/client/dhc6.c
new file mode 100644
index 00000000..5752336b
--- /dev/null
+++ b/client/dhc6.c
@@ -0,0 +1,3043 @@
+/* dhc6.c - DHCPv6 client routines. */
+
+/*
+ * Copyright (c) 2006 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
+ * <info@isc.org>
+ * http://www.isc.org/
+ */
+
+#ifndef lint
+static char ocopyright[] =
+"$Id: dhc6.c,v 1.2 2007/05/08 23:05:20 dhankins Exp $ Copyright (c) 2006 Internet Systems Consortium. All rights reserved.\n";
+#endif /* not lint */
+
+#include "dhcpd.h"
+
+struct sockaddr_in6 DHCPv6DestAddr;
+struct option *clientid_option = NULL;
+struct option *ia_na_option = NULL;
+struct option *iaaddr_option = NULL;
+struct option *elapsed_option = NULL;
+
+static struct dhc6_lease *dhc6_dup_lease(struct dhc6_lease *lease,
+ char *file, int line);
+static struct dhc6_ia *dhc6_dup_ia(struct dhc6_ia *ia, char *file, int line);
+static struct dhc6_addr *dhc6_dup_addr(struct dhc6_addr *addr,
+ char *file, int line);
+static void dhc6_ia_destroy(struct dhc6_ia *ia, char *file, int line);
+static isc_result_t dhc6_parse_ia_na(struct dhc6_ia **pia,
+ struct packet *packet,
+ struct option_state *options);
+static isc_result_t dhc6_parse_addrs(struct dhc6_addr **paddr,
+ struct packet *packet,
+ struct option_state *options);
+static struct dhc6_ia *find_ia(struct dhc6_ia *head, const char *id);
+static struct dhc6_addr *find_addr(struct dhc6_addr *head,
+ struct iaddr *address);
+void init_handler(struct packet *packet, struct client_state *client);
+void do_init6(void *input);
+void do_confirm6(void *input);
+void reply_handler(struct packet *packet, struct client_state *client);
+static isc_result_t dhc6_add_ia(struct client_state *client,
+ struct data_string *packet,
+ struct dhc6_lease *lease);
+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 start_bound(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, char *prefix,
+ struct option_state *options);
+
+/* 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.
+ *
+ * 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, char *file, int line)
+{
+ struct interface_info *ip;
+ int len;
+
+ /* 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);
+
+ /* 2 bytes for the 'duid type' field.
+ * 2 bytes for the 'htype' field.
+ * 4 bytes for the 'current time'.
+ * enough bytes for the hardware address (note that hw_address has
+ * the 'htype' on byte zero).
+ */
+ len = 8 + (ip->hw_address.hlen - 1);
+ 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. */
+ putUShort(duid->buffer->data, DUID_LLT);
+ putUShort(duid->buffer->data + 2, ip->hw_address.hbuf[0]);
+ putULong(duid->buffer->data + 4, cur_time);
+ memcpy(duid->buffer->data + 8, ip->hw_address.hbuf + 1,
+ ip->hw_address.hlen - 1);
+}
+
+/* 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;
+ inet_pton(AF_INET6, All_DHCP_Relay_Agents_and_Servers,
+ &DHCPv6DestAddr.sin6_addr);
+
+ 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_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_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_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.");
+
+#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;
+}
+
+/* Get a new dhcpv6_transaction_id and store it to the client state. */
+static void
+dhc6_new_xid(struct client_state *client)
+{
+ int xid;
+
+ if (RAND_MAX >= 0x00ffffff)
+ xid = random();
+ else if (RAND_MAX >= 0x0000ffff)
+ xid = (random() << 16) | random();
+ else
+ xid = (random() << 24) | (random() << 16) | random();
+
+ client->dhcpv6_transaction_id[0] = (xid >> 16) & 0xff;
+ client->dhcpv6_transaction_id[1] = (xid >> 8) & 0xff;
+ client->dhcpv6_transaction_id[2] = xid & 0xff;
+}
+
+/* Set RT from initial RT. */
+static void
+dhc6_retrans_init(struct client_state *client)
+{
+ client->start_time = cur_time;
+ client->txcount = 0;
+ client->RT = client->IRT + dhc6_rand(client->IRT);
+}
+
+/* Advance the DHCPv6 retransmission state once. */
+static void
+dhc6_retrans_advance(struct client_state *client)
+{
+ TIME elapsed;
+
+ elapsed = cur_time - client->start_time;
+ /* retrans_advance is called after consuming client->RT. */
+ elapsed += client->RT;
+
+ /* 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) && ((elapsed + client->RT) > client->MRD)) {
+ client->RT = (client->start_time + client->MRD) - cur_time;
+ }
+
+ 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("Advertise 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("Advertise 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;
+ }
+
+ return rval;
+}
+
+/* Create a complete copy of a DHCPv6 lease structure.
+ */
+static struct dhc6_lease *
+dhc6_dup_lease(struct dhc6_lease *lease, 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(&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(&copy->options, lease->options, file, line);
+
+ insert_ia = &copy->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(copy, 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, char *file, int line)
+{
+ struct dhc6_ia *copy;
+ struct dhc6_addr **insert_addr, *addr;
+
+ copy = dmalloc(sizeof(*ia), file, line);
+
+ memcpy(copy->iaid, ia->iaid, sizeof(copy->iaid));
+
+ copy->starts = ia->starts;
+ copy->renew = ia->renew;
+ copy->rebind = ia->rebind;
+
+ insert_addr = &copy->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(copy, file, line);
+ return NULL;
+ }
+
+ insert_addr = &(*insert_addr)->next;
+ }
+
+ if (ia->options != NULL)
+ option_state_reference(&copy->options, ia->options,
+ file, line);
+
+ return copy;
+}
+
+/* Duplicate an IAADDR structure.
+ */
+static struct dhc6_addr *
+dhc6_dup_addr(struct dhc6_addr *addr, char *file, int line)
+{
+ struct dhc6_addr *copy;
+
+ copy = dmalloc(sizeof(*addr), file, line);
+
+ if (copy == NULL)
+ return NULL;
+
+ memcpy(&copy->address, &addr->address, sizeof(copy->address));
+
+ 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(&copy->options, addr->options,
+ file, line);
+
+ return copy;
+}
+
+/* Form a DHCPv6 lease structure based upon packet contents. Creates and
+ * populates IA's and any IAADDR's they contain.
+ */
+static struct dhc6_lease *
+dhc6_leaseify(struct packet *packet)
+{
+ struct data_string ds;
+ struct dhc6_lease *lease;
+ struct option_cache *oc;
+
+ 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);
+ }
+
+ /* Dig into recursive DHCPv6 pockets for IA_NA and contained IAADDR
+ * options.
+ */
+ if (dhc6_parse_ia_na(&lease->bindings, packet,
+ lease->options) != 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 (!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));
+ }
+
+ return lease;
+}
+
+static isc_result_t
+dhc6_parse_ia_na(struct dhc6_ia **pia, struct packet *packet,
+ struct option_state *options)
+{
+ struct data_string ds;
+ struct dhc6_ia *ia;
+ struct option_cache *oc;
+ isc_result_t result;
+
+ 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->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 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 ISC_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;
+ }
+ }
+
+ *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;
+ }
+ }
+
+ 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;
+
+ 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 ISC_R_BADPARSE;
+ }
+ }
+
+ if (addr->options != NULL)
+ log_debug("RCV: | | | X-- "
+ "[Options]");
+
+ data_string_forget(&ds, MDL);
+
+ *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;
+ }
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+/* Clean up a lease object and deallocate all its parts. */
+void
+dhc6_lease_destroy(struct dhc6_lease *lease, char *file, int line)
+{
+ struct dhc6_ia *ia, *nia;
+
+ /* no-op */
+ if (lease == NULL)
+ return;
+
+ 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);
+}
+
+/* Traverse the addresses list, and destroy their contents. */
+static void
+dhc6_ia_destroy(struct dhc6_ia *ia, char *file, int line)
+{
+ struct dhc6_addr *addr, *naddr;
+
+ 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);
+}
+
+/* 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;
+}
+
+/* Not really clear what to do here yet.
+ */
+static int
+dhc6_score_lease(struct dhc6_lease *lease)
+{
+ struct dhc6_ia *ia;
+ struct dhc6_addr *addr;
+
+ if (lease->score)
+ return lease->score;
+
+ lease->score = 1;
+
+#if 0 /* XXX: oro is a bit...weird...still */
+ for (i = 0 ; i < oro_len ; i++) {
+ if (lookup_option(&dhcpv6_universe, lease->options,
+ oro[i]) != NULL)
+ lease->score++;
+ }
+#endif
+
+ for (ia = lease->bindings ; ia != NULL ; ia = ia->next) {
+ lease->score += 50;
+
+ for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
+ lease->score += 100;
+ }
+ }
+
+ return lease->score;
+}
+
+/* start_init6() kicks off the process, transmitting a packet and
+ * scheduling a retransmission event.
+ */
+void
+start_init6(struct client_state *client)
+{
+ log_debug("PRC: Soliciting for leases (INIT).");
+ client->state = S_INIT;
+
+ /* Fetch a 24-bit transaction ID. */
+ dhc6_new_xid(client);
+
+ /* Initialize timers, RFC3315 section 17.1.2. */
+ client->IRT = SOL_TIMEOUT;
+ client->MRT = SOL_MAX_RT;
+ client->MRC = 0;
+ client->MRD = 0;
+
+ 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 (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.
+ */
+ add_timeout(cur_time + (random() % SOL_MAX_DELAY), do_init6, client,
+ NULL, NULL);
+}
+
+/* start_init6() 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)
+{
+ /* If there is no active lease, there is nothing to check. */
+ if (client->active_lease == NULL) {
+ start_init6(client);
+ return;
+ }
+
+ log_debug("PRC: Confirming active lease (INIT-REBOOT).");
+ client->state = S_REBOOTING;
+
+ /* Fetch a 24-bit transaction ID. */
+ dhc6_new_xid(client);
+
+ /* Initialize timers, RFC3315 section 17.1.3. */
+ client->IRT = CNF_TIMEOUT;
+ client->MRT = CNF_MAX_RT;
+ client->MRC = 0;
+ client->MRD = CNF_MAX_RD;
+
+ dhc6_retrans_init(client);
+
+ client->v6_handler = reply_handler;
+
+ add_timeout(cur_time + (random() % CNF_MAX_DELAY), do_confirm6, client,
+ NULL, NULL);
+}
+
+/* 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 option_cache *oc;
+ TIME elapsed;
+ u_int32_t t1, t2;
+ int idx, len, send_ret, code;
+
+ 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;
+ }
+
+ if ((client->MRC != 0) && (client->txcount > client->MRC)) {
+ log_info("Max retransmission count exceeded.");
+ return;
+ }
+
+ elapsed = cur_time - client->start_time;
+ if ((client->MRD != 0) && (elapsed > client->MRD)) {
+ log_info("Max retransmission duration exceeded.");
+ return;
+ }
+
+ memset(&ds, 0, sizeof(ds));
+ if (!buffer_allocate(&ds.buffer, 4, MDL)) {
+ log_error("Unable to allocate memory for SOLICIT.");
+ return;
+ }
+ ds.data = ds.buffer->data;
+ ds.len = 4;
+
+ ds.buffer->data[0] = DHCPV6_SOLICIT;
+ memcpy(ds.buffer->data + 1, client->dhcpv6_transaction_id, 3);
+
+ /* Form an elapsed option. */
+ if ((elapsed < 0) || (elapsed > 655))
+ client->elapsed = 0xffff;
+ else
+ client->elapsed = elapsed * 100;
+
+ log_debug("XMT: Forming Solicit, %u ms elapsed.",
+ (unsigned)client->elapsed);
+
+ client->elapsed = htons(client->elapsed);
+
+ make_client6_options(client, &client->sent_options, NULL,
+ DHCPV6_SOLICIT);
+
+ /* 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 an IA_NA. */
+ /* 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...
+ */
+ memset(&ia, 0, sizeof(ia));
+ if (!buffer_allocate(&ia.buffer, 12, MDL)) {
+ log_error("Unable to allocate memory for IA_NA.");
+ data_string_forget(&ds, MDL);
+ return;
+ }
+ ia.data = ia.buffer->data;
+ ia.len = 12;
+
+ /* A simple IAID is the last 4 bytes of the hardware address. */
+ if (client->interface->hw_address.hlen > 4) {
+ idx = client->interface->hw_address.hlen - 4;
+ len = 4;
+ } else {
+ idx = 0;
+ len = client->interface->hw_address.hlen;
+ }
+ memcpy(ia.buffer->data, client->interface->hw_address.hbuf + idx, len);
+
+ 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,
+ ia.data)) != NULL)) {
+ /* For each address in the old IA, 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);
+
+ /* Transmit and wait. */
+
+ log_info("XMT: Solicit on %s, interval %ld",
+ client->name ? client->name : client->interface->name,
+ 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);
+
+ add_timeout(cur_time + client->RT, do_init6, 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 dhc6_ia *ia;
+ struct dhc6_addr *addr;
+ struct data_string ds;
+ struct data_string ia_data;
+ struct data_string addr_data;
+ int send_ret;
+ TIME elapsed;
+
+ 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?
+ */
+
+ if ((client->MRC != 0) && (client->txcount > client->MRC)) {
+ log_info("Max retransmission count exceeded.");
+ start_bound(client);
+ return;
+ }
+
+ elapsed = cur_time - client->start_time;
+ if ((client->MRD != 0) && (elapsed > client->MRD)) {
+ log_info("Max retransmission duration exceeded.");
+ start_bound(client);
+ return;
+ }
+
+ memset(&ds, 0, sizeof(ds));
+ if (!buffer_allocate(&ds.buffer, 4, MDL)) {
+ log_error("Unable to allocate memory for Confirm.");
+ return;
+ }
+ ds.data = ds.buffer->data;
+ ds.len = 4;
+
+ ds.buffer->data[0] = DHCPV6_CONFIRM;
+ memcpy(ds.buffer->data + 1, client->dhcpv6_transaction_id, 3);
+
+ /* Form an elapsed option. */
+ if ((elapsed < 0) || (elapsed > 655))
+ client->elapsed = 0xffff;
+ else
+ client->elapsed = elapsed * 100;
+
+ log_debug("XMT: Forming Confirm, %u ms elapsed.",
+ (unsigned)client->elapsed);
+
+ client->elapsed = htons(client->elapsed);
+
+ make_client6_options(client, &client->sent_options,
+ client->active_lease, DHCPV6_CONFIRM);
+
+ /* 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. */
+ memset(&ia_data, 0, sizeof(ia_data));
+ memset(&addr_data, 0, sizeof(addr_data));
+ for (ia = client->active_lease->bindings ; ia != NULL ;
+ ia = ia->next) {
+ if (!buffer_allocate(&ia_data.buffer, 12, MDL)) {
+ log_error("Unable to allocate memory for IA_NA.");
+ data_string_forget(&ds, MDL);
+ return;
+ }
+
+ /* Copy the IAID into the packet buffer. */
+ memcpy(ia_data.buffer->data, ia->iaid, 4);
+ /* Set t1 and t2 to zero (RFC3315 section 17.1.3) */
+ memset(ia_data.buffer->data + 4, 0, 8);
+
+ log_debug("XMT: X-- IA_NA %s",
+ print_hex_1(4, ia_data.buffer->data, 55));
+
+ for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
+ if (addr->address.len != 16) {
+ log_error("Illegal IPv6 address length (%d), "
+ "ignoring. (%s:%d)",
+ addr->address.len, MDL);
+ continue;
+ }
+
+ if (!buffer_allocate(&addr_data.buffer, 24, MDL)) {
+ log_error("Unable to allocate memory for "
+ "IAADDR.");
+ data_string_forget(&ia_data, MDL);
+ data_string_forget(&ds, MDL);
+ return;
+ }
+
+ /* Copy the address into the packet buffer. */
+ memcpy(addr_data.buffer->data, addr->address.iabuf,
+ 16);
+
+ /* Set preferred and max life to zero, per 17.1.3. */
+ memset(addr_data.buffer->data + 16, 0, 8);
+
+ log_debug("XMT: | X-- Confirm Address %s",
+ piaddr(addr->address));
+
+ append_option(&ia_data, &dhcpv6_universe,
+ iaaddr_option, &addr_data);
+
+ data_string_forget(&addr_data, MDL);
+ }
+
+ append_option(&ds, &dhcpv6_universe, ia_na_option, &ia_data);
+
+ data_string_forget(&ia_data, MDL);
+ }
+
+ /* Transmit and wait. */
+
+ log_info("XMT: Confirm on %s, interval %ld.",
+ client->name ? client->name : client->interface->name,
+ 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);
+
+ add_timeout(cur_time + client->RT, do_confirm6, client, NULL, NULL);
+
+ dhc6_retrans_advance(client);
+}
+
+/* status_log() just puts a status code into displayable form and logs it
+ * to info level.
+ */
+static void
+status_log(int code, char *scope, const char *additional, int len)
+{
+ char *msg = NULL;
+
+ switch(code) {
+ case STATUS_Success:
+ msg = "Succes";
+ 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;
+
+ default:
+ msg = "UNKNOWN";
+ break;
+ }
+
+ if (len > 0)
+ log_info("%s status code %s: %s", scope, msg,
+ print_hex_1(len, 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 ISC_R_INVALIDARG;
+
+ if ((msg != NULL) && (msg->len != 0))
+ return ISC_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 = ISC_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,
+ char *scope, unsigned *code)
+{
+ struct data_string msg;
+ isc_result_t status;
+
+ if ((scope == NULL) || (code == NULL))
+ return ISC_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, 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;
+}
+
+/* Look in the packet, any IA's, and any IAADDR's within those IA's to find
+ * status code options that are not SUCCESS.
+ */
+static isc_result_t
+dhc6_check_advertise(struct dhc6_lease *lease)
+{
+ struct dhc6_ia *ia;
+ struct dhc6_addr *addr;
+ isc_result_t rval = ISC_R_SUCCESS;
+ int have_addrs = ISC_FALSE;
+ unsigned code;
+
+ rval = dhc6_check_status(rval, lease->options, "message", &code);
+
+ for (ia = lease->bindings ; ia != NULL ; ia = ia->next) {
+ rval = dhc6_check_status(rval, ia->options, "IA_NA", &code);
+
+ for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
+ rval = dhc6_check_status(rval, addr->options,
+ "IAADDR", &code);
+ have_addrs = ISC_TRUE;
+ }
+ }
+
+ if (have_addrs != ISC_TRUE)
+ rval = ISC_R_ADDRNOTAVAIL;
+
+ return rval;
+}
+
+/* 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 rval,
+ unsigned code)
+{
+ struct dhc6_lease *lease;
+
+ if (client == NULL)
+ return ISC_R_INVALIDARG;
+
+ 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:
+ 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;
+ TIME future;
+
+ 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 rval,
+ unsigned code)
+{
+
+ if (client == NULL)
+ return ISC_R_INVALIDARG;
+
+ 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);
+ return ISC_TRUE;
+
+ /* "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:
+ /* 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;
+}
+
+/* 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;
+ struct dhc6_addr *addr;
+ isc_result_t rval = ISC_R_SUCCESS, status;
+ unsigned code;
+ int nscore, sscore;
+
+ if ((client == NULL) || (new == NULL))
+ return ISC_R_INVALIDARG;
+
+ switch (client->state) {
+ case S_SELECTING:
+ case S_REBOOTING:
+ action = dhc6_select_action;
+ break;
+
+ case S_RENEWING:
+ case S_REBINDING:
+ action = dhc6_reply_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) {
+ rval = dhc6_check_status(rval, ia->options, "IA_NA",
+ &code);
+ if (action(client, rval, code))
+ return ISC_R_CANCELED;
+
+ for (addr = ia->addrs ; addr != NULL ;
+ addr = addr->next) {
+ rval = dhc6_check_status(rval, addr->options,
+ "IAADDR", &code);
+ if (action(client, rval, code))
+ return ISC_R_CANCELED;
+ }
+ }
+
+ 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(new);
+ sscore = dhc6_score_lease(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;
+
+ 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, **idx;
+
+ /* In INIT state, we send solicits, we only expect to get
+ * advertises (we don't support rapid commit yet).
+ */
+ 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);
+
+ if (dhc6_check_advertise(lease) != ISC_R_SUCCESS) {
+ log_debug("PRC: Lease failed to satisfy.");
+ dhc6_lease_destroy(lease, MDL);
+ return;
+ }
+
+ 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) && (dhc6_score_lease(lease) > 150))) {
+ log_debug("RCV: Advertisement immediately selected.");
+ cancel_timeout(do_init6, client);
+ start_selecting6(client);
+ } else
+ log_debug("RCV: Advertisement recorded.");
+}
+
+/* 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 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(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(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 150
+ * (1 ia + 1 addr), choose any candidate >= 150.
+ *
+ * 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 < 150) && (cscore >= 150)) {
+ log_debug("PRC: | X-- Selected, has bndings.");
+ } 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;
+ struct data_string packet;
+
+ 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->advertised_leases);
+
+ if (lease == NULL)
+ log_fatal("Impossible error at %s:%d.", MDL);
+
+ client->selected_lease = lease;
+
+ /* Fetch a 24-bit transaction ID. */
+ dhc6_new_xid(client);
+
+ /* Set timers per RFC3315 section 18.1.1. */
+ client->IRT = REQ_TIMEOUT;
+ client->MRT = REQ_MAX_RT;
+ 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 dhc6_ia *ia;
+ struct dhc6_addr *addr;
+ struct option_cache *oc;
+ struct data_string ds;
+ TIME elapsed, t1, t2;
+ int abort = ISC_FALSE;
+ int code, send_ret;
+
+ 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;
+ }
+
+ if ((client->MRC != 0) && (client->txcount > client->MRC)) {
+ log_info("Max retransmission count exceeded.");
+ abort = ISC_TRUE;
+ }
+
+ elapsed = cur_time - client->start_time;
+ if ((client->MRD != 0) && (elapsed > client->MRD)) {
+ log_info("Max retransmission duration exceeded.");
+ abort = ISC_TRUE;
+ }
+
+ if (abort) {
+ 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;
+ }
+
+ /* 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.
+ */
+ memset(&ds, 0, sizeof(ds));
+ if (!buffer_allocate(&ds.buffer, 4, MDL)) {
+ log_error("Unable to allocate memory for REQUEST.");
+ return;
+ }
+ ds.data = ds.buffer->data;
+ ds.len = 4;
+
+ ds.buffer->data[0] = DHCPV6_REQUEST;
+ memcpy(ds.buffer->data + 1, client->dhcpv6_transaction_id, 3);
+
+ /* Form an elapsed option. */
+ if ((elapsed < 0) || (elapsed > 655))
+ client->elapsed = 0xffff;
+ else
+ client->elapsed = elapsed * 100;
+
+ log_debug("XMT: Forming Request, %u ms elapsed.",
+ (unsigned)client->elapsed);
+
+ client->elapsed = htons(client->elapsed);
+
+ make_client6_options(client, &client->sent_options, lease,
+ DHCPV6_REQUEST);
+
+ /* 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_NA's, and within them any IAADDRs. */
+ if (dhc6_add_ia(client, &ds, lease) != ISC_R_SUCCESS) {
+ data_string_forget(&ds, MDL);
+ return;
+ }
+
+ log_info("XMT: Request on %s, interval %ld",
+ client->name ? client->name : client->interface->name,
+ 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);
+
+ add_timeout(cur_time + client->RT, do_select6, client, NULL, NULL);
+
+ dhc6_retrans_advance(client);
+}
+
+/* For each IA in the lease, for each address in the IA, append that
+ * information onto the packet-so-far in a "what I would like to have
+ * please" fashion.
+ */
+static isc_result_t
+dhc6_add_ia(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;
+ TIME t1, t2;
+
+ /* Now appended any IA_NA's, and within them any IAADDRs. */
+ memset(&iads, 0, sizeof(iads));
+ memset(&addrds, 0, sizeof(addrds));
+ for (ia = lease->bindings ; ia ; ia = ia->next) {
+ if (!buffer_allocate(&iads.buffer, 12, MDL)) {
+ log_error("Unable to allocate memory for IA.");
+ break;
+ }
+ iads.data = iads.buffer->data;
+ iads.len = 12;
+
+ memcpy(iads.buffer->data, ia->iaid, 4);
+
+ 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);
+
+ for (addr = ia->addrs ; addr ; addr = addr->next) {
+ if (!buffer_allocate(&addrds.buffer, 24, MDL)) {
+ log_error("Unable to allocate memory for "
+ "IAADDR.");
+ break;
+ }
+ addrds.data = addrds.buffer->data;
+ addrds.len = 24;
+
+ memcpy(addrds.buffer->data, addr->address.iabuf, 16);
+ 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);
+
+ 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("XMT: V IA_NA appended.");
+ append_option(packet, &dhcpv6_universe, ia_na_option,
+ &iads);
+ } else {
+ log_debug("!!!: V IA_NA has no IAADDRs - removed.");
+ rval = ISC_R_FAILURE;
+ }
+
+ data_string_forget(&iads, MDL);
+ }
+
+ return rval;
+}
+
+/* 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, *old;
+ 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 Advertise - rejecting.");
+ return;
+ }
+
+ lease = dhc6_leaseify(packet);
+
+ /* This is an out of memory 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 retransmititng at this point. */
+ cancel_timeout(do_confirm6, client);
+ cancel_timeout(do_select6, client);
+ cancel_timeout(do_refresh6, client);
+
+ /* 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 acheive 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(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) {
+ /* Current practice is that all subnets are /64's, but
+ * some suspect this may not be permanent.
+ */
+ client_envadd(client, prefix, "ip6_prefixlen", "%d", 64);
+ client_envadd(client, prefix, "ip6_address", "%s",
+ piaddr(addr->address));
+ client_envadd(client, prefix, "life_starts", "%d",
+ (int)(addr->starts));
+ client_envadd(client, prefix, "preferred_life", "%d",
+ (int)(addr->preferred_life));
+ client_envadd(client, prefix, "max_life", "%d",
+ (int)(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, tmp;
+ int has_addrs = ISC_FALSE;
+
+ 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) {
+ 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)) {
+ if (addr->max_life == 0xffffffff)
+ tmp = MAX_TIME;
+ else
+ tmp = addr->starts + addr->max_life;
+
+ if (tmp > hi_expire)
+ hi_expire = tmp;
+ if (tmp < lo_expire)
+ lo_expire = tmp;
+
+ has_addrs = ISC_TRUE;
+ }
+ }
+
+ if (ia->renew == 0) {
+ if (lo_expire != MAX_TIME)
+ tmp = (lo_expire - ia->starts) / 2;
+ else
+ tmp = client->config->requested_lease / 2;
+
+ tmp += ia->starts;
+ } else if(ia->renew == 0xffffffff)
+ tmp = MAX_TIME;
+ else
+ tmp = ia->starts + ia->renew;
+
+ if (tmp < renew)
+ renew = tmp;
+
+ if (ia->rebind == 0) {
+ if (lo_expire != MAX_TIME)
+ tmp = (lo_expire - ia->starts) / 2;
+ else
+ tmp = client->config->requested_lease / 2;
+
+ tmp += ia->starts + (tmp / 2);
+ } else if (ia->renew == 0xffffffff)
+ tmp = MAX_TIME;
+ else
+ tmp = ia->starts + ia->rebind;
+
+ if (tmp < rebind)
+ rebind = tmp;
+ }
+
+ /* 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;
+ }
+
+ 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 - cur_time;
+ add_timeout(renew, 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 - cur_time;
+ add_timeout(rebind, 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:
+ log_fatal("Impossible condition at %s:%d.", 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));
+ add_timeout(depref, do_depref, client, NULL, NULL);
+ }
+ if (lo_expire != MAX_TIME) {
+ log_debug("PRC: Expiration scheduled in %d seconds.",
+ (int)(lo_expire - cur_time));
+ add_timeout(lo_expire, do_expire, client, NULL, NULL);
+ }
+}
+
+/* In a given IA chain, find the IA with the same 'iaid'. */
+static struct dhc6_ia *
+find_ia(struct dhc6_ia *head, const char *id)
+{
+ struct dhc6_ia *ia;
+
+ for (ia = head ; ia != NULL ; ia = ia->next) {
+ 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;
+}
+
+/* Merge the bindings from the source lease into the destination lease
+ * structure, where they are missing. 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.
+ */
+static void
+dhc6_merge_lease(struct dhc6_lease *src, struct dhc6_lease *dst)
+{
+ struct dhc6_ia *sia, *dia, *tia;
+ 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->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);
+
+ /* XXX: consider sorting? */
+ tia->next = dst->bindings;
+ dst->bindings = tia;
+ changes = 1;
+ } else {
+ for (saddr = sia->addrs ; saddr != NULL ;
+ saddr = saddr->next) {
+ daddr = find_addr(dia->addrs,
+ &saddr->address);
+
+ 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.
+ */
+void
+start_bound(struct client_state *client)
+{
+ struct dhc6_ia *ia, *oldia;
+ struct dhc6_addr *addr, *oldaddr;
+ struct dhc6_lease *lease, *old;
+ char *reason;
+ TIME dns_update_offset = 1;
+
+ lease = client->active_lease;
+ if (lease == NULL) {
+ log_error("Cannot enter bound state unless an active lease "
+ "is selected.");
+ return;
+ }
+ 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);
+ }
+
+ 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->iaid);
+ else
+ oldia = NULL;
+
+ for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
+ if (oldia != NULL)
+ oldaddr = find_addr(oldia->addrs,
+ &addr->address);
+ else
+ oldaddr = NULL;
+
+ if (oldaddr == NULL)
+ dhclient_schedule_updates(client,
+ &addr->address,
+ dns_update_offset++);
+
+ /* 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_go(client);
+ }
+
+ /* 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->addrs);
+
+ dhc6_marshall_values("new_", client, lease, ia,
+ NULL);
+ }
+ }
+
+ /* 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);
+ }
+
+ 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_renew6() gets us all ready to go to start transmitting Renew packets.
+ * Note that client->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;
+ client->MRT = REN_MAX_RT;
+ client->MRC = 0;
+ /* MRD is special in renew - we need to set it by checking timer
+ * state.
+ */
+ client->MRD = client->next_MRD;
+
+ 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;
+ TIME elapsed, next;
+ int send_ret;
+
+ client = (struct client_state *)input;
+
+ 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);
+ }
+
+ elapsed = cur_time - client->start_time;
+ if (((client->MRC != 0) && (client->txcount > client->MRC)) ||
+ ((client->MRD != 0) && (elapsed >= 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.
+ */
+ 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);
+ 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);
+
+ if ((elapsed < 0) || (elapsed > 655))
+ client->elapsed = 0xffff;
+ else
+ client->elapsed = elapsed * 100;
+
+ log_debug("XMT: Forming %s, %u 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);
+
+ if (dhc6_add_ia(client, &ds, lease) != ISC_R_SUCCESS) {
+ data_string_forget(&ds, MDL);
+ return;
+ }
+
+ log_info("XMT: %s on %s, interval %ld",
+ dhcpv6_type_names[client->refresh_type],
+ client->name ? client->name : client->interface->name,
+ 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);
+
+ add_timeout(cur_time + client->RT, 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->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;
+ client->MRT = REB_MAX_RT;
+ client->MRC = 0;
+ /* MRD is special in rebind - it's determined by the timer
+ * state.
+ */
+ client->MRD = client->next_MRD;
+
+ 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_go(client);
+
+ log_info("PRC: Address %s depreferred.",
+ print_hex_1(addr->address.len,
+ addr->address.iabuf,
+ 50));
+
+
+ addr->flags |= DHC6_ADDR_DEPREFFED;
+
+ /* Remove DDNS bindings at depref time. */
+ if (client->config->do_forward_update)
+ client_dns_update(client, 0, 0,
+ &addr->address);
+ }
+ }
+ }
+
+ 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;
+ struct dhc6_addr *addr;
+ int has_addrs = ISC_FALSE;
+
+ 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_EXPIRED)
+ continue;
+
+ if (addr->starts + addr->max_life <= cur_time) {
+ script_init(client, "EXPIRE6", NULL);
+ dhc6_marshall_values("old_", client, lease,
+ ia, addr);
+ script_go(client);
+
+ addr->flags |= DHC6_ADDR_EXPIRED;
+
+ log_info("PRC: Address %s expired.",
+ print_hex_1(addr->address.len,
+ addr->address.iabuf,
+ 50));
+
+ /* We remove DNS records at depref time, but
+ * it is possible that we might get here
+ * without depreffing.
+ */
+ if (client->config->do_forward_update &&
+ !(addr->flags & DHC6_ADDR_DEPREFFED))
+ client_dns_update(client, 0, 0,
+ &addr->address);
+
+ continue;
+ }
+
+ has_addrs = ISC_TRUE;
+ }
+ }
+
+ /* 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);
+}
+
+/* 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)
+{
+ int code;
+ struct option_cache *oc;
+
+ if ((op == NULL) || (client == NULL))
+ return;
+
+ if (*op)
+ option_state_dereference(op, MDL);
+
+ option_state_allocate(op, MDL);
+
+ 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);
+ }
+
+ /* 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);
+ }
+
+ 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);
+
+ /* RFC3315 says we MUST inclue an ORO in Requests. If we have it
+ * in the cache for one, we have it for both, so it's fatal either
+ * way (may as well fail early).
+ */
+ if (lookup_option(&dhcpv6_universe, *op, D6O_ORO) == NULL)
+ log_fatal("You must configure a dhcp6.oro!");
+}
+
+/* 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, 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);
+ }
+}
+
diff --git a/client/dhclient.8 b/client/dhclient.8
index 7971fca5..f82ce161 100644
--- a/client/dhclient.8
+++ b/client/dhclient.8
@@ -1,4 +1,4 @@
-.\" dhclient.8
+.\" $Id: dhclient.8,v 1.21 2007/05/08 23:05:20 dhankins Exp $
.\"
.\" Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
.\" Copyright (c) 1996-2003 by Internet Software Consortium
@@ -24,14 +24,17 @@
.\" Support and other services are available for ISC products - see
.\" http://www.isc.org for more information.
.\"
-.\" $Id: dhclient.8,v 1.20 2007/04/19 21:35:11 dhankins Exp $
-.\"
.TH dhclient 8
.SH NAME
dhclient - Dynamic Host Configuration Protocol Client
.SH SYNOPSIS
.B dhclient
[
+.B -4
+|
+.B -6
+]
+[
.B -p
.I port
]
@@ -106,6 +109,13 @@ 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
+If given the -4 command line argument (default), dhclient will use the
+DHCPv4 protocol to obtain an IPv4 address and configuration parameters.
+.PP
+If given the -6 command line argument, dhclient will use the DHCPv6
+protocol to obtain whatever PIv6 addresses are available along with
+configuration parameters. Information-request is not yet supported.
+.PP
On startup, dhclient reads the
.IR dhclient.conf
for configuration instructions. It then gets a list of all the
diff --git a/client/dhclient.c b/client/dhclient.c
index c3324b08..c20462fd 100644
--- a/client/dhclient.c
+++ b/client/dhclient.c
@@ -3,7 +3,7 @@
DHCP Client. */
/*
- * Copyright (c) 2004-2006 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004-2007 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
@@ -32,7 +32,7 @@
#ifndef lint
static char ocopyright[] =
-"$Id: dhclient.c,v 1.146 2007/04/26 19:31:58 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
+"$Id: dhclient.c,v 1.147 2007/05/08 23:05:20 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
@@ -42,8 +42,8 @@ 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 = _PATH_DHCLIENT_DB;
-const char *path_dhclient_pid = _PATH_DHCLIENT_PID;
+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;
@@ -56,12 +56,13 @@ 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;
/* ASSERT_STATE() does nothing now; it used to be
assert (state_is == state_shouldbe). */
#define ASSERT_STATE(state_is, state_shouldbe) {}
-static char copyright[] = "Copyright 2004-2006 Internet Systems Consortium.";
+static char copyright[] = "Copyright 2004-2007 Internet Systems Consortium.";
static char arr [] = "All rights reserved.";
static char message [] = "Internet Systems Consortium DHCP Client";
static char url [] = "For info, please visit http://www.isc.org/sw/dhcp/";
@@ -74,23 +75,22 @@ int client_env_count=0;
int onetry=0;
int quiet=0;
int nowait=0;
+char *mockup_relay = NULL;
static void usage PROTO ((void));
void do_release(struct client_state *);
-int main (argc, argv, envp)
- int argc;
- char **argv, **envp;
-{
+static isc_result_t write_duid(struct data_string *duid);
+
+int
+main(int argc, char **argv) {
int fd;
int i;
- struct servent *ent;
struct interface_info *ip;
struct client_state *client;
unsigned seed;
char *server = (char *)0;
- char *relay = (char *)0;
isc_result_t status;
int release_mode = 0;
omapi_object_t *listener;
@@ -101,8 +101,12 @@ int main (argc, argv, envp)
int no_dhclient_db = 0;
int no_dhclient_pid = 0;
int no_dhclient_script = 0;
+ int local_family_set = 0;
char *s;
+ /* 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 decriptor is used. */
@@ -142,7 +146,19 @@ int main (argc, argv, envp)
dhcp_interface_startup_hook = dhclient_interface_startup_hook;
for (i = 1; i < argc; i++) {
- if (!strcmp (argv [i], "-r")) {
+ 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;
+ } else if (!strcmp(argv[i], "-r")) {
release_mode = 1;
no_daemon = 1;
} else if (!strcmp (argv [i], "-p")) {
@@ -185,7 +201,7 @@ int main (argc, argv, envp)
} else if (!strcmp (argv [i], "-g")) {
if (++i == argc)
usage ();
- relay = argv [i];
+ mockup_relay = argv [i];
} else if (!strcmp (argv [i], "-nw")) {
nowait = 1;
} else if (!strcmp (argv [i], "-n")) {
@@ -244,6 +260,17 @@ int main (argc, argv, envp)
path_dhclient_script = s;
}
+ /* Set up the initial dhcp option universe. */
+ initialize_common_option_spaces();
+
+ /* Assign v4 or v6 specific running parameters. */
+ if (local_family == AF_INET)
+ dhcpv4_client_assignments();
+ else if (local_family == AF_INET6)
+ dhcpv6_client_assignments();
+ else
+ log_fatal("Impossible condition at %s:%d.", MDL);
+
/* first kill of any currently running client */
if (release_mode) {
FILE *pidfd;
@@ -277,44 +304,19 @@ int main (argc, argv, envp)
/* If we're given a relay agent address to insert, for testing
purposes, figure out what it is. */
- if (relay) {
- if (!inet_aton (relay, &giaddr)) {
+ if (mockup_relay) {
+ if (!inet_aton (mockup_relay, &giaddr)) {
struct hostent *he;
- he = gethostbyname (relay);
+ he = gethostbyname (mockup_relay);
if (he) {
memcpy (&giaddr, he -> h_addr_list [0],
sizeof giaddr);
} else {
- log_fatal ("%s: no such host", relay);
+ log_fatal ("%s: no such host", mockup_relay);
}
}
}
- /* 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 (relay && giaddr.s_addr != htonl (INADDR_LOOPBACK)) {
- local_port = htons(67);
- } else {
- ent = getservbyname ("dhcpc", "udp");
- if (!ent)
- 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 (relay && giaddr.s_addr != htonl (INADDR_LOOPBACK)) {
- remote_port = local_port;
- } else
- remote_port = htons (ntohs (local_port) - 1); /* XXX */
-
/* Get the current time... */
GET_TIME (&cur_time);
@@ -379,12 +381,17 @@ int main (argc, argv, envp)
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);
+
+ 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);
}
}
@@ -412,17 +419,47 @@ int main (argc, argv, envp)
srandom (seed + cur_time);
/* Start a configuration state machine for each interface. */
- for (ip = interfaces; ip; ip = ip -> next) {
- ip -> flags |= INTERFACE_RUNNING;
- for (client = ip -> client; client; client = client -> next) {
- if (release_mode)
- do_release (client);
- else {
- client -> state = S_INIT;
- /* Set up a timeout to start the initialization
- process. */
- add_timeout (cur_time + random () % 5,
- state_reboot, client, 0, 0);
+ if (local_family == AF_INET6) {
+ struct option_cache *oc;
+
+ /* Establish a default DUID. This may be moved to the
+ * DHCPv4 area later.
+ */
+ 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);
+ }
+
+ for (ip = interfaces ; ip != NULL ; ip = ip->next) {
+ for (client = ip->client ; client != NULL ;
+ client = client->next) {
+ /* If we have a previous binding, Confirm
+ * that we can (or can't) still use it.
+ */
+ if (client->active_lease != NULL)
+ start_confirm6(client);
+ else
+ start_init6(client);
+ }
+ }
+ } else {
+ for (ip = interfaces ; ip ; ip = ip->next) {
+ ip->flags |= INTERFACE_RUNNING;
+ for (client = ip->client ; client ;
+ client = client->next) {
+ if (release_mode)
+ do_release(client);
+ else {
+ client->state = S_INIT;
+ /* Set up a timeout to start the
+ * initialization process.
+ */
+ add_timeout(cur_time + random() % 5,
+ state_reboot, client, 0, 0);
+ }
}
}
}
@@ -448,6 +485,7 @@ int main (argc, argv, envp)
/* Set up the bootp packet handler... */
bootp_packet_handler = do_packet;
+ dhcpv6_packet_handler = do_packet6;
#if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) || \
defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
@@ -899,11 +937,9 @@ void bind_lease (client)
client -> state = S_BOUND;
reinitialize_interfaces ();
go_daemon ();
- if (client -> config -> do_forward_update) {
- client -> dns_update_timeout = 1;
- add_timeout (cur_time + 1, client_dns_update_timeout,
- client, 0, 0);
- }
+ if (client->config->do_forward_update)
+ dhclient_schedule_updates(client, &client->active->address,
+ 1);
}
/* state_bound is called when we've successfully bound to a particular
@@ -1082,6 +1118,57 @@ void dhcp (packet)
(*handler) (packet);
}
+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) {
+ case DHCPV6_ADVERTISE:
+ case DHCPV6_REPLY:
+ case DHCPV6_RECONFIGURE:
+ 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 debuggin */
+ log_info("Packet received, but nothing done with it.");
+}
+
void dhcpoffer (packet)
struct packet *packet;
{
@@ -2213,7 +2300,8 @@ void destroy_client_lease (lease)
free_client_lease (lease, MDL);
}
-FILE *leaseFile;
+FILE *leaseFile = NULL;
+int leases_written = 0;
void rewrite_client_leases ()
{
@@ -2221,14 +2309,18 @@ void rewrite_client_leases ()
struct client_state *client;
struct client_lease *lp;
- if (leaseFile)
+ if (leaseFile != NULL)
fclose (leaseFile);
leaseFile = fopen (path_dhclient_db, "w");
- if (!leaseFile) {
+ 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) {
@@ -2239,6 +2331,11 @@ void rewrite_client_leases ()
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);
}
}
@@ -2252,6 +2349,11 @@ void rewrite_client_leases ()
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);
}
}
fflush (leaseFile);
@@ -2269,6 +2371,7 @@ void write_lease_option (struct option_cache *oc,
struct data_string ds;
int status;
struct client_state *client;
+ char *preamble = stuff;
memset (&ds, 0, sizeof ds);
@@ -2281,15 +2384,161 @@ void write_lease_option (struct option_cache *oc,
}
if (evaluate_option_cache (&ds, packet, lease, client_state,
in_options, cfg_options, scope, oc, MDL)) {
- fprintf (leaseFile,
- " option %s%s%s %s;\n",
- name, dot, oc -> option -> name,
- pretty_print_option (oc -> option,
- ds.data, ds.len, 1, 1));
+ fprintf(leaseFile, "%soption %s%s%s %s;\n", preamble,
+ name, dot, oc->option->name,
+ pretty_print_option(oc->option, ds.data, ds.len,
+ 1, 1));
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,
+ char *preamble)
+{
+ int i;
+
+ for (i = 0; i < options->universe_count; i++) {
+ option_space_foreach(NULL, NULL, client, NULL, options,
+ &global_scope, universes[i], preamble,
+ write_lease_option);
+ }
+}
+
+/* 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 ISC_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;
+ }
+ }
+
+ /* It would make more sense to write this as a hex string,
+ * but our function to do that (print_hex_n) uses a fixed
+ * length buffer...and we can't guarantee a duid would be
+ * less than the fixed length.
+ */
+ str = quotify_buf(duid->data, duid->len, 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;
+}
+
+/* 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;
+
+ /* 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 ISC_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) {
+ stat = fprintf(leaseFile, " ia_na %s {\n",
+ print_hex_1(4, ia->iaid, 12));
+ if (stat <= 0)
+ return ISC_R_IOERROR;
+
+ stat = fprintf(leaseFile, " starts %d;\n"
+ " renew %u;\n"
+ " rebind %u;\n",
+ ia->starts, ia->renew, ia->rebind);
+ if (stat <= 0)
+ return ISC_R_IOERROR;
+
+ for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
+ stat = fprintf(leaseFile, " iaaddr %s {\n",
+ piaddr(addr->address));
+ if (stat <= 0)
+ return ISC_R_IOERROR;
+
+ stat = fprintf(leaseFile, " starts %d;\n"
+ " preferred-life %u;\n"
+ " max-life %u;\n",
+ 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->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;
+ }
+ }
+}
+
int write_client_lease (client, lease, rewrite, makesure)
struct client_state *client;
struct client_lease *lease;
@@ -2298,7 +2547,6 @@ int write_client_lease (client, lease, rewrite, makesure)
{
int i;
struct tm *t;
- static int leases_written;
struct option_cache *oc;
struct data_string ds;
pair *hash;
@@ -2318,9 +2566,9 @@ int write_client_lease (client, lease, rewrite, makesure)
if (lease -> is_static)
return 1;
- if (!leaseFile) { /* XXX */
+ if (leaseFile == NULL) { /* XXX */
leaseFile = fopen (path_dhclient_db, "w");
- if (!leaseFile) {
+ if (leaseFile == NULL) {
log_error ("can't create %s: %m", path_dhclient_db);
return 0;
}
@@ -2398,13 +2646,7 @@ int write_client_lease (client, lease, rewrite, makesure)
memset (&ds, 0, sizeof ds);
- 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],
- client, write_lease_option);
- }
+ write_options(client, lease->options, " ");
tval = print_time(lease->renewal);
if (tval == NULL ||
@@ -2473,11 +2715,6 @@ void script_init (client, reason, medium)
}
}
-struct envadd_state {
- struct client_state *client;
- const char *prefix;
-};
-
void client_option_envadd (struct option_cache *oc,
struct packet *packet, struct lease *lease,
struct client_state *client_state,
@@ -3055,7 +3292,8 @@ isc_result_t dhcp_set_control_state (control_object_state_t oldstate,
if (client -> active &&
client -> active -> expiry > cur_time) {
if (client -> config -> do_forward_update)
- client_dns_update (client, 0, 0);
+ client_dns_update(client, 0, 0,
+ &client->active->address);
do_release (client);
}
break;
@@ -3075,30 +3313,63 @@ isc_result_t dhcp_set_control_state (control_object_state_t oldstate,
return ISC_R_SUCCESS;
}
+/* Schedule updates to retry occaisionally until it no longer times out.
+ */
+void
+dhclient_schedule_updates(struct client_state *client, struct iaddr *addr,
+ int offset)
+{
+ struct dns_update_state *ustate;
+
+ if (!client->config->do_forward_update)
+ return;
+
+ ustate = dmalloc(sizeof(*ustate), MDL);
+
+ if (ustate != NULL) {
+ ustate->client = client;
+ ustate->address = *addr;
+ ustate->dns_update_timeout = 1;
+
+ add_timeout(cur_time + offset, client_dns_update_timeout,
+ ustate, NULL, NULL);
+ } else {
+ log_error("Unable to allocate dns update state for %s.",
+ piaddr(*addr));
+ }
+}
+
/* Called after a timeout if the DNS update failed on the previous try.
Retries the update, and if it times out, schedules a retry after
ten times as long of a wait. */
void client_dns_update_timeout (void *cp)
{
- struct client_state *client = cp;
- isc_result_t status;
+ struct dns_update_state *ustate = cp;
+ isc_result_t status = ISC_R_FAILURE;
- if (client -> active) {
- status = client_dns_update (client, 1,
- (client -> active -> renewal -
- cur_time));
- if (status == ISC_R_TIMEDOUT) {
- client -> dns_update_timeout *= 10;
- add_timeout (cur_time + client -> dns_update_timeout,
- client_dns_update_timeout, client, 0, 0);
- }
- }
+ /* 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.
+ */
+ if ((ustate->client->active != NULL) ||
+ (ustate->client->active_lease != NULL))
+ status = client_dns_update(ustate->client, 1, 300,
+ &ustate->address);
+
+ if (status == ISC_R_TIMEDOUT) {
+ if (ustate->dns_update_timeout < 3600)
+ ustate->dns_update_timeout *= 10;
+ add_timeout(cur_time + ustate->dns_update_timeout,
+ client_dns_update_timeout, ustate, NULL, NULL);
+ } else
+ dfree(ustate, MDL);
}
/* See if we should do a DNS update, and if so, do it. */
-isc_result_t client_dns_update (struct client_state *client, int addp, int ttl)
+isc_result_t client_dns_update (struct client_state *client, int addp,
+ int ttl, struct iaddr *address)
{
struct data_string ddns_fqdn, ddns_fwd_name,
ddns_dhcid, client_identifier;
@@ -3113,7 +3384,7 @@ isc_result_t client_dns_update (struct client_state *client, int addp, int ttl)
return ISC_R_SUCCESS;
/* If we don't have a lease, we can't do an update. */
- if (!client -> active)
+ 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. */
@@ -3148,30 +3419,50 @@ isc_result_t client_dns_update (struct client_state *client, int addp, int ttl)
&global_scope, oc, MDL))
return ISC_R_SUCCESS;
- /* Make a dhcid string out of either the client identifier,
- if we are sending one, or the interface's MAC address,
- otherwise. */
+ /* If this is a DHCPv6 client update, make a dhcid string out of
+ * the DUID. If this is a DHCPv4 client update, choose either
+ * the client identifier, if there is one, or the interface's
+ * MAC address.
+ */
memset (&ddns_dhcid, 0, sizeof ddns_dhcid);
- memset (&client_identifier, 0, sizeof client_identifier);
- if ((oc = lookup_option (&dhcp_universe, client -> sent_options,
- DHO_DHCP_CLIENT_IDENTIFIER)) &&
- evaluate_option_cache (&client_identifier, (struct packet *)0,
- (struct lease *)0, client,
- client -> sent_options,
- (struct option_state *)0,
- &global_scope, oc, MDL)) {
- result = get_dhcid (&ddns_dhcid,
- DHO_DHCP_CLIENT_IDENTIFIER,
- client_identifier.data,
- client_identifier.len);
- data_string_forget (&client_identifier, MDL);
- } else
- result = get_dhcid (&ddns_dhcid, 0,
- client -> interface -> hw_address.hbuf,
- client -> interface -> hw_address.hlen);
+ memset(&client_identifier, 0, sizeof(client_identifier));
+ if (client->active_lease != NULL) {
+ 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)) {
+ /* RFC4701 defines type '2' as being for the DUID
+ * field. We aren't using RFC4701 DHCID RR's yet,
+ * but this is as good a value as any.
+ */
+ result = get_dhcid(&ddns_dhcid, 2,
+ client_identifier.data,
+ client_identifier.len);
+ data_string_forget(&client_identifier, MDL);
+ } else
+ log_fatal("Impossible condition at %s:%d.", MDL);
+ } else {
+ 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)) {
+ result = get_dhcid(&ddns_dhcid,
+ DHO_DHCP_CLIENT_IDENTIFIER,
+ client_identifier.data,
+ client_identifier.len);
+ data_string_forget(&client_identifier, MDL);
+ } else
+ result = get_dhcid(&ddns_dhcid, 0,
+ client->interface->hw_address.hbuf,
+ client->interface->hw_address.hlen);
+ }
if (!result) {
- data_string_forget (&ddns_fwd_name, MDL);
+ data_string_forget(&ddns_fwd_name, MDL);
return ISC_R_SUCCESS;
}
@@ -3188,14 +3479,11 @@ isc_result_t client_dns_update (struct client_state *client, int addp, int ttl)
*/
if (ddns_fwd_name.len && ddns_dhcid.len) {
if (addp)
- rcode = ddns_update_a (&ddns_fwd_name,
- client -> active -> address,
- &ddns_dhcid, ttl,
- 1, 1);
+ rcode = ddns_update_fwd(&ddns_fwd_name, *address,
+ &ddns_dhcid, ttl, 1, 1);
else
- rcode = ddns_remove_a (&ddns_fwd_name,
- client -> active -> address,
- &ddns_dhcid);
+ rcode = ddns_remove_fwd(&ddns_fwd_name, *address,
+ &ddns_dhcid);
} else
rcode = ISC_R_FAILURE;
@@ -3203,3 +3491,39 @@ isc_result_t client_dns_update (struct client_state *client, int addp, int ttl)
data_string_forget (&ddns_dhcid, MDL);
return rcode;
}
+
+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)
+ 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 */
+}
diff --git a/client/dhclient.conf.5 b/client/dhclient.conf.5
index caf8a274..9bed6987 100644
--- a/client/dhclient.conf.5
+++ b/client/dhclient.conf.5
@@ -1,4 +1,4 @@
-.\" $Id: dhclient.conf.5,v 1.18 2007/04/19 21:35:11 dhankins Exp $
+.\" $Id: dhclient.conf.5,v 1.19 2007/05/08 23:05:20 dhankins Exp $
.\"
.\" Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
.\" Copyright (c) 1996-2003 by Internet Software Consortium
@@ -28,8 +28,6 @@
.\" see ``http://www.vix.com''. To learn more about Nominum, Inc., see
.\" ``http://www.nominum.com''.
.\"
-.\" $Id: dhclient.conf.5,v 1.18 2007/04/19 21:35:11 dhankins Exp $
-.\"
.TH dhclient.conf 5
.SH NAME
dhclient.conf - DHCP client configuration file
@@ -224,12 +222,26 @@ 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 DHCPV6 OPERATION
+The client does not yet have a default DHCPv6 Option Request Option (ORO),
+nor has it been integrated with the 'request' and 'require' syntax above.
+It is neccessary to configure an ORO then.
+.PP
+.nf
+ send dhcp6.oro 1, 2, 7, 12, 13, 23, 24, 39;
+.fi
+.PP
+The above ORO will request both identifiers (server, client), the preference,
+unicast, nameservers, domain-search, and FQDN(v6) options.
.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 fqdn option on the client, as follows:
diff --git a/client/scripts/linux b/client/scripts/linux
index 787efff2..72e37ee1 100755
--- a/client/scripts/linux
+++ b/client/scripts/linux
@@ -1,6 +1,103 @@
#!/bin/bash
# dhclient-script for Linux. Dan Halbert, March, 1997.
# Updated for Linux 2.[12] by Brian J. Murrell, January 1999.
+# Updated for ipv6 by David W. Hankins, Janurary 2007.
+
+ip=/sbin/ip
+
+make_resolv_conf() {
+ if [ "x${new_dhcp6_name_servers}" != x ] ; then
+ cat /dev/null > /etc/resolv.conf.dhclient
+ chmod 644 /etc/resolv.conf.dhclient
+
+ if [ "x${new_dhcp6_domain_search}" != x ] ; then
+ echo search ${new_dhcp6_domain_search} >> /etc/resolv.conf.dhclient
+ fi
+ for nameserver in ${new_dhcp6_name_servers} ; do
+ echo nameserver ${nameserver} >> /etc/resolv.conf.dhclient
+ done
+
+ mv /etc/resolv.conf.dhclient /etc/resolv.conf
+ fi
+}
+
+# Check for valid constant input values.
+if [ x${reason} = x ] || [ x${interface} = x ] ; then
+ exit 1;
+fi
+
+
+if [ ${reason} = PREINIT6 ] ; then
+ # Ensure interface is up.
+ ${ip} link set ${interface} up
+
+ # Remove any stale addresses.
+ ${ip} -f inet6 addr flush dev ${interface} scope global permanent
+
+ exit 0
+fi
+
+
+if [ ${reason} = BOUND6 ] ; then
+ if [ x${new_ip6_address} = x ] || [ x${new_ip6_prefixlen} = x ] ; then
+ exit 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 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 0
+fi
+
+
+if [ ${reason} = DEPREF6 ] ; then
+ if [ x${new_ip6_prefixlen} = x ] ; then
+ exit 2;
+ fi
+
+ # There doesn't appear to be a way to update an addr to indicate
+ # preference.
+# ${ip} -f inet6 addr ??? ${new_ip6_address}/${new_ip6_prefixlen} \
+# dev ${interface} scope global deprecated?
+
+ exit 0
+fi
+
+
+if [ ${reason} = EXPIRE6 ] ; then
+ if [ x${old_ip6_address} = x ] || [ x${old_ip6_prefixlen} = x ] ; then
+ exit 2;
+ fi
+
+ ${ip} -f inet6 addr del ${old_ip6_address}/${old_ip6_prefixlen} \
+ dev ${interface}
+
+ exit 0
+fi
+
+
+# Old DHCPv4 script below, need to review and reintegrate.
+
+exit
+
+# 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.
diff --git a/common/Makefile.dist b/common/Makefile.dist
index 5e40a0f5..be951c2f 100644
--- a/common/Makefile.dist
+++ b/common/Makefile.dist
@@ -25,11 +25,11 @@ SEDMANPAGES = dhcp-options.man5 dhcp-eval.man5
SRC = raw.c parse.c nit.c icmp.c dispatch.c conflex.c upf.c bpf.c socket.c \
lpf.c dlpi.c packet.c tr.c ethernet.c memory.c print.c options.c \
inet.c tree.c tables.c alloc.c fddi.c ctrace.c dns.c resolv.c \
- execute.c discover.c comapi.c
+ execute.c discover.c comapi.c heap.c
OBJ = raw.o parse.o nit.o icmp.o dispatch.o conflex.o upf.o bpf.o socket.o \
lpf.o dlpi.o packet.o tr.o ethernet.o memory.o print.o options.o \
inet.o tree.o tables.o alloc.o fddi.o ctrace.o dns.o resolv.o \
- execute.o discover.o comapi.o
+ execute.o discover.o comapi.o heap.o
MAN = dhcp-options.5 dhcp-eval.5
INCLUDES = -I$(TOP) $(BINDINC) -I$(TOP)/includes
diff --git a/common/alloc.c b/common/alloc.c
index 6204a7f3..12abc71c 100644
--- a/common/alloc.c
+++ b/common/alloc.c
@@ -34,7 +34,7 @@
#ifndef lint
static char copyright[] =
-"$Id: alloc.c,v 1.57 2006/06/01 20:23:17 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
+"$Id: alloc.c,v 1.58 2007/05/08 23:05:20 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
@@ -700,9 +700,12 @@ int buffer_allocate (ptr, len, file, 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);
@@ -967,6 +970,7 @@ int option_state_dereference (ptr, file, line)
universes [i] -> option_state_dereference)
((*(universes [i] -> option_state_dereference))
(universes [i], options, file, line));
+
dfree (options, file, line);
return 1;
}
@@ -1279,8 +1283,11 @@ void data_string_copy (dest, src, file, line)
const char *file;
int line;
{
- if (src -> buffer)
+ 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;
@@ -1299,13 +1306,14 @@ void data_string_forget (data, file, line)
memset (data, 0, sizeof *data);
}
-/* Make a copy of the data in data_string, upping the buffer reference
- count if there's a buffer. */
+/* 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;
diff --git a/common/conflex.c b/common/conflex.c
index 9ae06533..f3a79654 100644
--- a/common/conflex.c
+++ b/common/conflex.c
@@ -34,7 +34,7 @@
#ifndef lint
static char copyright[] =
-"$Id: conflex.c,v 1.105 2006/08/04 10:59:32 shane Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
+"$Id: conflex.c,v 1.106 2007/05/08 23:05:20 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
@@ -621,6 +621,8 @@ static enum dhcp_token intern (atom, dfv)
return CLASS;
if (!strcasecmp (atom + 1, "lose"))
return TOKEN_CLOSE;
+ if (!strcasecmp(atom + 1, "ompressed"))
+ return COMPRESSED;
if (!strcasecmp (atom + 1, "reate"))
return TOKEN_CREATE;
if (!strcasecmp (atom + 1, "iaddr"))
@@ -674,6 +676,8 @@ static enum dhcp_token intern (atom, dfv)
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;
@@ -738,6 +742,9 @@ static enum dhcp_token intern (atom, dfv)
return ENCAPSULATE;
if (!strcasecmp(atom + 1, "xecute"))
return EXECUTE;
+ if (!strcasecmp(atom+1, "n")) {
+ return EN;
+ }
break;
case 'f':
if (!strcasecmp (atom + 1, "atal"))
@@ -746,6 +753,8 @@ static enum dhcp_token intern (atom, dfv)
return FILENAME;
if (!strcasecmp (atom + 1, "ixed-address"))
return FIXED_ADDR;
+ if (!strcasecmp (atom + 1, "ixed-address6"))
+ return FIXED_ADDR6;
if (!strcasecmp (atom + 1, "ddi"))
return FDDI;
if (!strcasecmp (atom + 1, "ormerr"))
@@ -774,6 +783,8 @@ static enum dhcp_token intern (atom, dfv)
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"))
@@ -782,6 +793,10 @@ static enum dhcp_token intern (atom, dfv)
return TOKEN_HELP;
break;
case 'i':
+ if (!strcasecmp(atom+1, "a-na"))
+ return IA_NA;
+ if (!strcasecmp(atom+1, "aaddr"))
+ return IAADDR;
if (!strcasecmp (atom + 1, "nclude"))
return INCLUDE;
if (!strcasecmp (atom + 1, "nteger"))
@@ -792,6 +807,8 @@ static enum dhcp_token intern (atom, dfv)
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, "nterface"))
@@ -821,6 +838,8 @@ static enum dhcp_token intern (atom, dfv)
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"))
@@ -839,6 +858,12 @@ static enum dhcp_token intern (atom, dfv)
return LOCAL;
if (!strcasecmp (atom + 1, "og"))
return LOG;
+ if (!strcasecmp(atom+1, "lt")) {
+ return LLT;
+ }
+ if (!strcasecmp(atom+1, "l")) {
+ return LL;
+ }
break;
case 'm':
if (!strncasecmp (atom + 1, "ax", 2)) {
@@ -854,6 +879,8 @@ static enum dhcp_token intern (atom, dfv)
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"))
@@ -943,6 +970,8 @@ static enum dhcp_token intern (atom, dfv)
case 'p':
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"))
@@ -974,6 +1003,9 @@ static enum dhcp_token intern (atom, dfv)
return RESOLUTION_INTERRUPTED;
if (!strcasecmp (atom + 1, "ange"))
return RANGE;
+ if (!strcasecmp(atom + 1, "ange6")) {
+ return RANGE6;
+ }
if (!strcasecmp (atom + 1, "ecover"))
return RECOVER;
if (!strcasecmp (atom + 1, "ecover-done"))
@@ -1049,6 +1081,9 @@ static enum dhcp_token intern (atom, dfv)
if (atom[6] == '\0')
return SERVER;
if (atom[6] == '-') {
+ if (!strcasecmp(atom + 7,
+ "duid"))
+ return SERVER_DUID;
if (!strcasecmp(atom + 7,
"name"))
return SERVER_NAME;
@@ -1122,6 +1157,8 @@ static enum dhcp_token intern (atom, dfv)
return SUBCLASS;
if (!strcasecmp(atom + 3, "net"))
return SUBNET;
+ if (!strcasecmp(atom + 3, "net6"))
+ return SUBNET6;
if (!strcasecmp(atom + 3, "string"))
return SUBSTRING;
break;
diff --git a/common/ctrace.c b/common/ctrace.c
index 8bcb9674..39dfdce4 100644
--- a/common/ctrace.c
+++ b/common/ctrace.c
@@ -32,7 +32,7 @@
#ifndef lint
static char copyright[] =
-"$Id: ctrace.c,v 1.6 2006/08/09 14:57:47 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n";
+"$Id: ctrace.c,v 1.7 2007/05/08 23:05:20 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
@@ -46,8 +46,9 @@ void trace_interface_register (trace_type_t *ttype, struct interface_info *ip)
memset (&tipkt, 0, sizeof tipkt);
memcpy (&tipkt.hw_address,
&ip -> hw_address, sizeof ip -> hw_address);
- memcpy (&tipkt.primary_address,
- &ip -> primary_address, sizeof ip -> primary_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);
@@ -87,8 +88,12 @@ void trace_interface_input (trace_type_t *ttype, unsigned len, char *buf)
memcpy (&ip -> hw_address, &tipkt -> hw_address,
sizeof ip -> hw_address);
- memcpy (&ip -> primary_address, &tipkt -> primary_address,
- sizeof ip -> primary_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);
+ memcpy(ip->addresses, &tipkt->primary_address, sizeof(*ip->addresses));
memcpy (ip -> name, tipkt -> name, sizeof ip -> name);
ip -> index = ntohl (tipkt -> index);
@@ -102,7 +107,7 @@ void trace_interface_input (trace_type_t *ttype, unsigned len, char *buf)
ip -> ifp -> ifr_addr.sa_len = sizeof (struct sockaddr_in);
#endif
sin = (struct sockaddr_in *)&ip -> ifp -> ifr_addr;
- sin -> sin_addr = ip -> primary_address;
+ sin->sin_addr = ip->addresses[0];
addr.len = 4;
memcpy (addr.iabuf, &sin -> sin_addr.s_addr, addr.len);
diff --git a/common/dhcp-eval.5 b/common/dhcp-eval.5
index f2b30f3b..00b06687 100644
--- a/common/dhcp-eval.5
+++ b/common/dhcp-eval.5
@@ -1,4 +1,4 @@
-.\" $Id: dhcp-eval.5,v 1.24 2007/01/28 23:00:19 each Exp $
+.\" $Id: dhcp-eval.5,v 1.25 2007/05/08 23:05:20 dhankins Exp $
.\"
.\" Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
.\" Copyright (c) 1996-2003 by Internet Software Consortium
diff --git a/common/dhcp-options.5 b/common/dhcp-options.5
index 35a668ec..7399c97f 100644
--- a/common/dhcp-options.5
+++ b/common/dhcp-options.5
@@ -1,4 +1,4 @@
-.\" $Id: dhcp-options.5,v 1.32 2007/04/18 15:53:14 dhankins Exp $
+.\" $Id: dhcp-options.5,v 1.33 2007/05/08 23:05:20 dhankins Exp $
.\"
.\" Copyright (c) 2004-2006 by Internet Systems Consortium, Inc. ("ISC")
.\" Copyright (c) 1996-2003 by Internet Software Consortium
@@ -59,6 +59,10 @@ 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
@@ -91,11 +95,11 @@ existing DHCP options. The domain name is stored just as if it were
a text option.
.PP
The
-.B domain-list
+.B domain-list [compressed]
data type specifies a list of domain names, a space between each name and
-the entire string enclosed in double quotes. These types of data are used
-for the domain-search option for example, and encodes an RFC1035 compressed
-DNS label list on the wire.
+the entire string enclosed in double quotes. On the wire, these names are
+formatted as per RFC1035. The optional '\fBcompressed\fR' keyword can be
+used to indicate that RFC1035 name compression should be used.
.PP
The
.B flag
@@ -1408,6 +1412,25 @@ 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
@@ -1559,7 +1582,7 @@ hash tables to hold options in this space.
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 1 or 2.
+Valid values for length widths are 0, 1 or 2.
.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
diff --git a/common/discover.c b/common/discover.c
index e6921867..f2442916 100644
--- a/common/discover.c
+++ b/common/discover.c
@@ -1,6 +1,6 @@
-/* dispatch.c
+/* discover.c
- Network input dispatcher... */
+ Find and identify the network interfaces. */
/*
* Copyright (c) 2004-2006 by Internet Systems Consortium, Inc. ("ISC")
@@ -34,7 +34,7 @@
#ifndef lint
static char copyright[] =
-"$Id: discover.c,v 1.52 2006/11/07 23:40:14 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
+"$Id: discover.c,v 1.53 2007/05/08 23:05:20 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
@@ -51,12 +51,20 @@ 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_INET6;
struct in_addr local_address;
+struct in6_addr local_address6;
void (*bootp_packet_handler) PROTO ((struct interface_info *,
struct dhcp_packet *, unsigned,
unsigned int,
struct iaddr, struct hardware *));
+void (*dhcpv6_packet_handler)(struct interface_info *,
+ const char *, int,
+ int, const struct iaddr *,
+ isc_boolean_t);
+
omapi_object_type_t *dhcp_type_interface;
#if defined (TRACING)
@@ -116,94 +124,650 @@ isc_result_t interface_initialize (omapi_object_t *ipo,
return ISC_R_SUCCESS;
}
-/* 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 (state)
- int state;
-{
- struct interface_info *tmp, *ip;
- struct interface_info *last, *next;
- char buf [2048];
- struct ifconf ic;
- struct ifreq ifr;
- int i;
- int sock;
- int address_count = 0;
- struct subnet *subnet;
- struct shared_network *share;
- struct sockaddr_in foo;
- int ir;
- struct ifreq *tif;
-#ifdef ALIAS_NAMES_PERMUTED
+/*
+ * 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(SIOCGLIFNUM)
+/*
+ * 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[LIFNAMSIZ]; /* 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) {
+ struct lifnum lifnum;
+
+ ifaces->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (ifaces->sock < 0) {
+ log_error("Error creating socket to list interfaces; %m");
+ return 0;
+ }
+
+ memset(&lifnum, 0, sizeof(lifnum));
+ lifnum.lifn_family = AF_UNSPEC;
+ if (ioctl(ifaces->sock, SIOCGLIFNUM, &lifnum) < 0) {
+ log_error("Error finding total number of interfaces; %m");
+ close(ifaces->sock);
+ ifaces->sock = -1;
+ return 0;
+ }
+
+ ifaces->num = lifnum.lifn_count;
+ memset(&ifaces->conf, 0, sizeof(ifaces->conf));
+ ifaces->conf.lifc_family = AF_UNSPEC;
+ 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 1.
+ */
+int
+next_iface(struct iface_info *info, int *err, struct iface_conf_list *ifaces) {
+ struct lifreq *p;
+ struct lifreq tmp;
char *s;
-#endif
- isc_result_t status;
- static int setup_fallback = 0;
- int wifcount = 0;
- /* Create an unbound datagram socket to do the SIOCGIFADDR ioctl on. */
- if ((sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
- log_fatal ("Can't create addrlist socket");
+ do {
+ if (ifaces->next >= ifaces->num) {
+ *err = 0;
+ return 0;
+ }
- /* Get the interface configuration information... */
+ p = ifaces->conf.lifc_req;
+ p += ifaces->next;
-#ifdef SIOCGIFCONF_ZERO_PROBE
- /* linux will only tell us how long a buffer it wants if we give it
- * a null buffer first. So, do a dry run to figure out the length.
- *
- * XXX this code is duplicated from below because trying to fold
- * the logic into the if statement and goto resulted in excesssive
- * obfuscation. The intent is that unless you run Linux you shouldn't
- * have to deal with this. */
+ if (strlen(p->lifr_name) >= sizeof(info->name)) {
+ *err = 1;
+ log_error("Interface name '%s' too long", p->lifr_name);
+ return 0;
+ }
+ strcpy(info->name, p->lifr_name);
+ info->addr = p->lifr_addr;
- ic.ifc_len = 0;
- ic.ifc_ifcu.ifcu_buf = (caddr_t)NULL;
+#ifdef ALIAS_NAMES_PERMUTED
+ /* interface aliases look like "eth0:1" or "wlan1:3" */
+ s = strchr(info->name, ':');
+ if (s != NULL) {
+ *s = '\0';
+ }
+#endif
+
+#ifdef SKIP_DUMMY_INTERFACES
+ } while (strncmp(info->name, "dummy", 5) == 0);
#else
- /* otherwise, we just feed it a starting size, and it'll tell us if
- * it needs more */
+ } while (0);
+#endif
+
+ memset(&tmp, 0, sizeof(tmp));
+ strcpy(tmp.lifr_name, info->name);
+ 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;
+}
+
+#elif __linux /* !HAVE_SIOCGLIFCONF */
+/*
+ * Linux support
+ * -------------
+ *
+ * In Linux, we use the /proc pseudo-filesystem to get information
+ * about interfaces, along with selected ioctl() calls.
+ *
+ * Linux low level access is documented in the netdevice man page.
+ */
- ic.ifc_len = sizeof buf;
- ic.ifc_ifcu.ifcu_buf = (caddr_t)buf;
+/*
+ * Structure holding state about the scan.
+ */
+struct iface_conf_list {
+ int sock; /* file descriptor used to get information */
+ FILE *fp; /* input from /proc/net/dev */
+#ifdef DHCPv6
+ FILE *fp6; /* input from /proc/net/if_inet6 */
#endif
+};
- gifconf_again:
- i = ioctl(sock, SIOCGIFCONF, &ic);
+/*
+ * Structure used to return information about a specific interface.
+ */
+struct iface_info {
+ char name[IFNAMSIZ]; /* name of the interface, e.g. "eth0" */
+ 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) {
+ char buf[256];
+ int len;
+ int i;
- if (i < 0)
- log_fatal ("ioctl: SIOCGIFCONF: %m");
+ ifaces->fp = fopen(PROCDEV_DEVICE, "r");
+ if (ifaces->fp == NULL) {
+ log_error("Error opening '%s' to list interfaces",
+ PROCDEV_DEVICE);
+ return 0;
+ }
-#ifdef SIOCGIFCONF_ZERO_PROBE
- /* Workaround for SIOCGIFCONF bug on some Linux versions. */
- if (ic.ifc_ifcu.ifcu_buf == 0 && ic.ifc_len == 0) {
- ic.ifc_len = sizeof buf;
- ic.ifc_ifcu.ifcu_buf = (caddr_t)buf;
- goto gifconf_again;
+ /*
+ * The first 2 lines are header information, so read and ignore them.
+ */
+ for (i=0; i<2; i++) {
+ if (fgets(buf, sizeof(buf), ifaces->fp) == NULL) {
+ log_error("Error reading headers from '%s'",
+ PROCDEV_DEVICE);
+ fclose(ifaces->fp);
+ ifaces->fp = NULL;
+ return 0;
+ }
+ len = strlen(buf);
+ if ((len <= 0) || (buf[len-1] != '\n')) {
+ log_error("Bad header line in '%s'", PROCDEV_DEVICE);
+ fclose(ifaces->fp);
+ ifaces->fp = NULL;
+ return 0;
+ }
+ }
+
+ ifaces->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (ifaces->sock < 0) {
+ log_error("Error creating socket to list interfaces; %m");
+ fclose(ifaces->fp);
+ ifaces->fp = NULL;
+ return 0;
+ }
+
+#ifdef DHCPv6
+ ifaces->fp6 = fopen("/proc/net/if_inet6", "r");
+ if (ifaces->fp6 == NULL) {
+ log_error("Error opening '/proc/net/if_inet6' to "
+ "list IPv6 interfaces; %m");
+ close(ifaces->sock);
+ ifaces->sock = -1;
+ fclose(ifaces->fp);
+ ifaces->fp = NULL;
+ return 0;
}
#endif
- /* If the SIOCGIFCONF resulted in more data than would fit in
- a buffer, allocate a bigger buffer. */
- if ((ic.ifc_ifcu.ifcu_buf == buf
-#ifdef SIOCGIFCONF_ZERO_PROBE
- || ic.ifc_ifcu.ifcu_buf == 0
+ return 1;
+}
+
+/*
+ * Read our IPv4 interfaces from /proc/net/dev.
+ *
+ * The file looks something like this:
+ *
+ * Inter-| Receive ...
+ * face |bytes packets errs drop fifo frame ...
+ * lo: 1580562 4207 0 0 0 0 ...
+ * eth0: 0 0 0 0 0 0 ...
+ * eth1:1801552440 37895 0 14 0 ...
+ *
+ * We only care about the interface name, which is at the start of
+ * each line.
+ *
+ * We use an ioctl() to get the address and flags for each interface.
+ */
+static int
+next_iface4(struct iface_info *info, int *err, struct iface_conf_list *ifaces) {
+ char buf[256];
+ int len;
+ char *p;
+ char *name;
+ struct ifreq tmp;
+
+ /*
+ * Loop exits when we find an interface that has an address, or
+ * when we run out of interfaces.
+ */
+ for (;;) {
+ do {
+ /*
+ * Read the next line in the file.
+ */
+ if (fgets(buf, sizeof(buf), ifaces->fp) == NULL) {
+ if (ferror(ifaces->fp)) {
+ *err = 1;
+ log_error("Error reading interface "
+ "information");
+ } else {
+ *err = 0;
+ }
+ return 0;
+ }
+
+ /*
+ * Make sure the line is a nice,
+ * newline-terminated line.
+ */
+ len = strlen(buf);
+ if ((len <= 0) || (buf[len-1] != '\n')) {
+ log_error("Bad line reading interface "
+ "information");
+ *err = 1;
+ return 0;
+ }
+
+ /*
+ * Figure out our name.
+ */
+ p = strrchr(buf, ':');
+ if (p == NULL) {
+ log_error("Bad line reading interface "
+ "information (no colon)");
+ *err = 1;
+ return 0;
+ }
+ *p = '\0';
+ name = buf;
+ while (isspace(*name)) {
+ name++;
+ }
+
+ /*
+ * Copy our name into our interface structure.
+ */
+ len = p - name;
+ if (len >= sizeof(info->name)) {
+ *err = 1;
+ log_error("Interface name '%s' too long", name);
+ return 0;
+ }
+ strcpy(info->name, name);
+
+#ifdef ALIAS_NAMED_PERMUTED
+ /* interface aliases look like "eth0:1" or "wlan1:3" */
+ s = strchr(info->name, ':');
+ if (s != NULL) {
+ *s = '\0';
+ }
#endif
- ) && ic.ifc_len > sizeof buf) {
- ic.ifc_ifcu.ifcu_buf = dmalloc ((size_t)ic.ifc_len, MDL);
- if (!ic.ifc_ifcu.ifcu_buf)
- log_fatal ("Can't allocate SIOCGIFCONF buffer.");
- goto gifconf_again;
-#ifdef SIOCGIFCONF_ZERO_PROBE
- } else if (ic.ifc_ifcu.ifcu_buf == 0) {
- ic.ifc_ifcu.ifcu_buf = (caddr_t)buf;
- ic.ifc_len = sizeof buf;
- goto gifconf_again;
+
+#ifdef SKIP_DUMMY_INTERFACES
+ } while (strncmp(info->name, "dummy", 5) == 0);
+#else
+ } while (0);
#endif
+
+ memset(&tmp, 0, sizeof(tmp));
+ strcpy(tmp.ifr_name, name);
+ if (ioctl(ifaces->sock, SIOCGIFADDR, &tmp) < 0) {
+ if (errno == EADDRNOTAVAIL) {
+ continue;
+ }
+ log_error("Error getting interface address "
+ "for '%s'; %m", name);
+ *err = 1;
+ return 0;
+ }
+ memcpy(&info->addr, &tmp.ifr_addr, sizeof(tmp.ifr_addr));
+
+ memset(&tmp, 0, sizeof(tmp));
+ strcpy(tmp.ifr_name, name);
+ if (ioctl(ifaces->sock, SIOCGIFFLAGS, &tmp) < 0) {
+ log_error("Error getting interface flags for '%s'; %m",
+ name);
+ *err = 1;
+ return 0;
+ }
+ info->flags = tmp.ifr_flags;
+
+ *err = 0;
+ return 1;
+ }
+}
+
+#ifdef DHCPv6
+/*
+ * Read our IPv6 interfaces from /proc/net/if_inet6.
+ *
+ * The file looks something like this:
+ *
+ * fe80000000000000025056fffec00008 05 40 20 80 vmnet8
+ * 00000000000000000000000000000001 01 80 10 80 lo
+ * fe80000000000000025056fffec00001 06 40 20 80 vmnet1
+ * 200108881936000202166ffffe497d9b 03 40 00 00 eth1
+ * fe8000000000000002166ffffe497d9b 03 40 20 80 eth1
+ *
+ * We get IPv6 address from the start, the interface name from the end,
+ * and ioctl() to get flags.
+ */
+static int
+next_iface6(struct iface_info *info, int *err, struct iface_conf_list *ifaces) {
+ char buf[256];
+ int len;
+ char *p;
+ char *name;
+ int i;
+ struct sockaddr_in6 addr;
+ struct ifreq tmp;
+
+ do {
+ /*
+ * Read the next line in the file.
+ */
+ if (fgets(buf, sizeof(buf), ifaces->fp6) == NULL) {
+ if (ferror(ifaces->fp6)) {
+ *err = 1;
+ log_error("Error reading IPv6 "
+ "interface information");
+ } else {
+ *err = 0;
+ }
+ return 0;
+ }
+
+ /*
+ * Make sure the line is a nice, newline-terminated line.
+ */
+ len = strlen(buf);
+ if ((len <= 0) || (buf[len-1] != '\n')) {
+ log_error("Bad line reading IPv6 "
+ "interface information");
+ *err = 1;
+ return 0;
+ }
+
+ /*
+ * Figure out our name.
+ */
+ buf[--len] = '\0';
+ p = strrchr(buf, ' ');
+ if (p == NULL) {
+ log_error("Bad line reading IPv6 interface "
+ "information (no space)");
+ *err = 1;
+ return 0;
+ }
+ name = p+1;
+
+ /*
+ * Copy our name into our interface structure.
+ */
+ len = strlen(name);
+ if (len >= sizeof(info->name)) {
+ *err = 1;
+ log_error("IPv6 interface name '%s' too long", name);
+ return 0;
+ }
+ strcpy(info->name, name);
+
+#ifdef SKIP_DUMMY_INTERFACES
+ } while (strncmp(info->name, "dummy", 5) == 0);
+#else
+ } while (0);
+#endif
+
+ /*
+ * Double-check we start with the IPv6 address.
+ */
+ for (i=0; i<32; i++) {
+ if (!isxdigit(buf[i]) || isupper(buf[i])) {
+ *err = 1;
+ log_error("Bad line reading IPv6 interface address "
+ "for '%s'", name);
+ return 0;
+ }
+ }
+
+ /*
+ * Load our socket structure.
+ */
+ memset(&addr, 0, sizeof(addr));
+ addr.sin6_family = AF_INET6;
+ for (i=0; i<16; i++) {
+ unsigned char byte;
+ static const char hex[] = "0123456789abcdef";
+ byte = ((index(hex, buf[i * 2]) - hex) << 4) |
+ (index(hex, buf[i * 2 + 1]) - hex);
+ addr.sin6_addr.s6_addr[i] = byte;
+ }
+ memcpy(&info->addr, &addr, sizeof(addr));
+
+ /*
+ * Get our flags.
+ */
+ memset(&tmp, 0, sizeof(tmp));
+ strcpy(tmp.ifr_name, name);
+ if (ioctl(ifaces->sock, SIOCGIFFLAGS, &tmp) < 0) {
+ log_error("Error getting interface flags for '%s'; %m", name);
+ *err = 1;
+ return 0;
+ }
+ info->flags = tmp.ifr_flags;
+
+ *err = 0;
+ return 1;
+}
+#endif /* DHCPv6 */
+
+/*
+ * Retrieve the next interface.
+ *
+ * Returns information in the info structure.
+ * Sets err to 1 if there is an error, otherwise 1.
+ */
+int
+next_iface(struct iface_info *info, int *err, struct iface_conf_list *ifaces) {
+ if (next_iface4(info, err, ifaces)) {
+ return 1;
+ }
+#ifdef DHCPv6
+ if (!(*err)) {
+ return next_iface6(info, err, ifaces);
+ }
+#endif
+ return 0;
+}
+
+/*
+ * End scan of interfaces.
+ */
+void
+end_iface_scan(struct iface_conf_list *ifaces) {
+ fclose(ifaces->fp);
+ ifaces->fp = NULL;
+ close(ifaces->sock);
+ ifaces->sock = -1;
+#ifdef DHCPv6
+ fclose(ifaces->fp6);
+ ifaces->fp6 = NULL;
+#endif
+}
+#else
+/* XXX: need to define non-Solaris, non-Linux iterators */
+#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;
+}
+
+/* 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;
+}
+
+/* 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, *ip;
+ struct interface_info *last, *next;
+
+ char abuf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
+
+ struct subnet *subnet;
+ int ir;
+ isc_result_t status;
+ int wifcount = 0;
+
+ 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 ||
@@ -216,100 +780,129 @@ void discover_interfaces (state)
ir = INTERFACE_REQUESTED;
/* Cycle through the list of interfaces looking for IP addresses. */
- for (i = 0; i < ic.ifc_len;) {
- struct ifreq *ifp = (struct ifreq *)((caddr_t)ic.ifc_req + i);
-#ifdef HAVE_SA_LEN
- if (ifp -> ifr_addr.sa_len > sizeof (struct sockaddr))
- i += (sizeof ifp -> ifr_name) + ifp -> ifr_addr.sa_len;
- else
-#endif
- i += sizeof *ifp;
-
-#ifdef ALIAS_NAMES_PERMUTED
- if ((s = strrchr (ifp -> ifr_name, ':'))) {
- *s = 0;
- }
-#endif
-
-#ifdef SKIP_DUMMY_INTERFACES
- if (!strncmp (ifp -> ifr_name, "dummy", 5))
- continue;
-#endif
-
-
- /* See if this is the sort of interface we want to
- deal with. */
- strcpy (ifr.ifr_name, ifp -> ifr_name);
- if (ioctl (sock, SIOCGIFFLAGS, &ifr) < 0)
- log_fatal ("Can't get interface flags for %s: %m",
- ifr.ifr_name);
+ 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, ifp -> ifr_name))
+ 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 (((!(ifr.ifr_flags & IFF_BROADCAST) ||
- ifr.ifr_flags & IFF_LOOPBACK ||
- ifr.ifr_flags & IFF_POINTOPOINT) && !tmp) ||
- (!(ifr.ifr_flags & IFF_UP) &&
+ if (((!(info.flags & IFF_BROADCAST) ||
+ 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) {
- tmp = (struct interface_info *)0;
- status = interface_allocate (&tmp, MDL);
- if (status != ISC_R_SUCCESS)
- log_fatal ("Error allocating interface %s: %s",
- ifp -> ifr_name,
- isc_result_totext (status));
- strcpy (tmp -> name, ifp -> ifr_name);
- interface_snorf (tmp, ir);
- interface_dereference (&tmp, MDL);
+ 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));
+ }
+ strcpy(tmp->name, info.name);
+ interface_snorf(tmp, ir);
+ interface_dereference(&tmp, MDL);
tmp = interfaces; /* XXX */
}
- if (dhcp_interface_discovery_hook)
- (*dhcp_interface_discovery_hook) (tmp);
+ if (dhcp_interface_discovery_hook) {
+ (*dhcp_interface_discovery_hook)(tmp);
+ }
/* If we have the capability, extract link information
- and record it in a linked list. */
+ and record it in. */
#ifdef HAVE_AF_LINK
- if (ifp -> ifr_addr.sa_family == AF_LINK) {
- struct sockaddr_dl *foo = ((struct sockaddr_dl *)
- (&ifp -> ifr_addr));
-#if defined (HAVE_SIN_LEN)
- tmp -> hw_address.hlen = foo -> sdl_alen;
-#else
- tmp -> hw_address.hlen = 6; /* XXX!!! */
-#endif
- tmp -> hw_address.hbuf [0] = HTYPE_ETHER; /* XXX */
- memcpy (&tmp -> hw_address.hbuf [1],
- LLADDR (foo), tmp -> hw_address.hlen);
- tmp -> hw_address.hlen++; /* for type. */
+ if (info.addr.ss_family == AF_LINK) {
+ struct sockaddr_dl *d = (struct sockaddr_dl*)&info.addr;
+ tmp->hw_address.hlen = d->sdl_alen;
+ tmp->hw_address.hbuf[0] = HTYPE_ETHER; /* XXX */
+ memcpy(&tmp->hw_address.hbuf[1],
+ LLADDR(d),
+ tmp->hw_address.hlen);
+ tmp->hw_address.hlen++; /* for type. */
} else
#endif /* AF_LINK */
- if (ifp -> ifr_addr.sa_family == AF_INET) {
+ if ((info.addr.ss_family == AF_INET) &&
+ (local_family == AF_INET)) {
+ struct sockaddr_in *a = (struct sockaddr_in*)&info.addr;
struct iaddr addr;
- /* Get a pointer to the address... */
- memcpy (&foo, &ifp -> ifr_addr,
- sizeof ifp -> ifr_addr);
+ /* We don't want the loopback interface. */
+ if (a->sin_addr.s_addr == htonl(INADDR_LOOPBACK) &&
+ ((tmp->flags & INTERFACE_AUTOMATIC) &&
+ state == DISCOVER_SERVER))
+ 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);
+
+/*
+ * XXX: We don't have ifreq in Solaris-land if we want IPv6. Fortunately,
+ * we don't actually need this for anything on Solaris.
+ */
+#if 0
+ /* If this is the first real IP address we've
+ found, keep a pointer to ifreq structure in
+ which we found it. */
+ if (!tmp -> ifp) {
+#ifdef HAVE_SA_LEN
+ unsigned len = ((sizeof ifp -> ifr_name) +
+ ifp -> ifr_addr.sa_len);
+#else
+ unsigned len = sizeof *ifp;
+#endif
+ tif = (struct ifreq *)dmalloc (len, MDL);
+ if (!tif)
+ log_fatal ("no space for ifp.");
+ memcpy (tif, ifp, len);
+ tmp -> ifp = tif;
+ }
+#endif /* 0 */
+
+ /* 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);
+ }
+ }
+ 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 (foo.sin_addr.s_addr == htonl (INADDR_LOOPBACK) &&
- ((tmp -> flags & INTERFACE_AUTOMATIC) &&
+ if (IN6_IS_ADDR_LOOPBACK(&a->sin6_addr) &&
+ ((tmp->flags & INTERFACE_AUTOMATIC) &&
state == DISCOVER_SERVER))
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);
+
+/*
+ * XXX: We don't have ifreq in Solaris-land if we want IPv6. Fortunately,
+ * we don't actually need this for anything on Solaris.
+ */
+#if 0
/* If this is the first real IP address we've
found, keep a pointer to ifreq structure in
which we found it. */
@@ -327,20 +920,24 @@ void discover_interfaces (state)
tmp -> ifp = tif;
tmp -> primary_address = foo.sin_addr;
}
+#endif /* 0 */
- /* Grab the address... */
- addr.len = 4;
- memcpy (addr.iabuf, &foo.sin_addr.s_addr,
- addr.len);
- if (dhcp_interface_setup_hook)
- (*dhcp_interface_setup_hook) (tmp, &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);
+ }
}
}
- /* If we allocated a buffer, free it. */
- if (ic.ifc_ifcu.ifcu_buf != buf)
- dfree (ic.ifc_ifcu.ifcu_buf, MDL);
+ if (err) {
+ log_fatal("Error getting interface information.");
+ }
+
+ end_iface_scan(&ifaces);
+#if 0
#if defined (LINUX_SLASHPROC_DISCOVERY)
/* On Linux, interfaces that don't have IP addresses don't
show up in the SIOCGIFCONF syscall. This only matters for
@@ -428,11 +1025,14 @@ void discover_interfaces (state)
fclose (proc_dev);
}
#endif
+#endif /* 0 */
/* Now cycle through all the interfaces we found, looking for
hardware addresses. */
+ /* XXX: The dlpi interface code will get this information in Solaris */
+#if 0
#if defined (HAVE_SIOCGIFHWADDR) && !defined (HAVE_AF_LINK)
- for (tmp = interfaces; tmp; tmp = tmp -> next) {
+ for (tmp = interfaces; tmp != NULL; tmp = tmp->next) {
struct ifreq ifr;
struct sockaddr sa;
int b, sk;
@@ -539,16 +1139,16 @@ void discover_interfaces (state)
}
}
#endif /* defined (HAVE_SIOCGIFHWADDR) && !defined (HAVE_AF_LINK) */
+#endif /* 0 */
/* 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) {
- close (sock);
return;
}
/* Weed out the interfaces that did not have IP addresses. */
- tmp = last = next = (struct interface_info *)0;
+ tmp = last = next = NULL;
if (interfaces)
interface_reference (&tmp, interfaces, MDL);
while (tmp) {
@@ -567,7 +1167,9 @@ void discover_interfaces (state)
state == DISCOVER_REQUESTED)
tmp -> flags &= ~(INTERFACE_AUTOMATIC |
INTERFACE_REQUESTED);
- if (!tmp -> ifp || !(tmp -> flags & INTERFACE_REQUESTED)) {
+/* XXX: no ifp in Solaris */
+/* if (!tmp -> ifp || !(tmp -> flags & INTERFACE_REQUESTED)) {*/
+ if (!(tmp->flags & INTERFACE_REQUESTED)) {
if ((tmp -> flags & INTERFACE_REQUESTED) != ir)
log_fatal ("%s: not found", tmp -> name);
if (!last) {
@@ -600,15 +1202,31 @@ void discover_interfaces (state)
}
last = tmp;
- memcpy (&foo, &tmp -> ifp -> ifr_addr,
- sizeof tmp -> ifp -> ifr_addr);
+/* XXX: no ifp in Solaris */
+/* memcpy (&foo, &tmp -> ifp -> ifr_addr,
+ sizeof tmp -> ifp -> ifr_addr);*/
/* We must have a subnet declaration for each interface. */
- if (!tmp -> shared_network && (state == DISCOVER_SERVER)) {
- log_error ("%s", "");
- log_error ("No subnet declaration for %s (%s).",
- tmp -> name, inet_ntoa (foo.sin_addr));
- if (supports_multiple_interfaces (tmp)) {
+ 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,
+ inet_ntoa(tmp->addresses[0]));
+ } else {
+ if (tmp->v6addresses != NULL) {
+ inet_ntop(AF_INET6,
+ &tmp->v6addresses[0],
+ abuf,
+ sizeof(abuf));
+ } else {
+ strcpy(abuf, "no addresses");
+ }
+ log_error("No subnet declaration for %s (%s).",
+ tmp->name,
+ abuf);
+ }
+ 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",
@@ -643,7 +1261,7 @@ void discover_interfaces (state)
to the first address we found. */
subnet -> interface_address.len = 4;
memcpy (subnet -> interface_address.iabuf,
- &foo.sin_addr.s_addr, 4);
+ &tmp->addresses[0].s_addr, 4);
}
}
@@ -652,8 +1270,16 @@ void discover_interfaces (state)
tmp -> index = -1;
/* Register the interface... */
- if_register_receive (tmp);
- if_register_send (tmp);
+ if (local_family == AF_INET) {
+ if_register_receive(tmp);
+ if_register_send(tmp);
+ } else {
+ if (state == DISCOVER_SERVER) {
+ if_register6(tmp, 1);
+ } else {
+ if_register6(tmp, 0);
+ }
+ }
interface_stash (tmp);
wifcount++;
@@ -680,24 +1306,28 @@ void discover_interfaces (state)
continue;
if (tmp -> rfdesc == -1)
continue;
- status = omapi_register_io_object ((omapi_object_t *)tmp,
- if_readsocket, 0,
- got_one, 0, 0);
+ if (local_family == AF_INET) {
+ status = omapi_register_io_object((omapi_object_t *)tmp,
+ if_readsocket,
+ 0, got_one, 0, 0);
+ } else {
+ status = omapi_register_io_object((omapi_object_t *)tmp,
+ if_readsocket,
+ 0, got_one_v6, 0, 0);
+ }
if (status != ISC_R_SUCCESS)
log_fatal ("Can't register I/O handle for %s: %s",
tmp -> name, isc_result_totext (status));
}
- close (sock);
-
if (state == DISCOVER_SERVER && wifcount == 0) {
log_info ("%s", "");
log_fatal ("Not configured to listen on any interfaces!");
}
- if (!setup_fallback) {
+ if ((local_family == AF_INET) && !setup_fallback) {
setup_fallback = 1;
- maybe_setup_fallback ();
+ maybe_setup_fallback();
}
#if defined (HAVE_SETFD)
@@ -808,6 +1438,48 @@ isc_result_t got_one (h)
return ISC_R_SUCCESS;
}
+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;
+
+ if (h->type != dhcp_type_interface) {
+ return ISC_R_INVALIDARG;
+ }
+ ip = (struct interface_info *)h;
+
+ result = receive_packet6(ip, buf, sizeof(buf), &from, &to);
+ if (result < 0) {
+ log_error("receive_packet6() failed on %s: %m", ip->name);
+ return ISC_R_UNEXPECTED;
+ }
+
+ 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);
+
+ (*dhcpv6_packet_handler)(ip, buf,
+ result, from.sin6_port,
+ &ifrom, is_unicast);
+ }
+
+ return ISC_R_SUCCESS;
+}
+
isc_result_t dhcp_interface_set_value (omapi_object_t *h,
omapi_object_t *id,
omapi_data_string_t *name,
@@ -1102,8 +1774,12 @@ isc_result_t dhcp_interface_remove (omapi_object_t *lp,
/* remove the io object */
omapi_unregister_io_object ((omapi_object_t *)interface);
- if_deregister_send (interface);
- if_deregister_receive (interface);
+ if (local_family == AF_INET) {
+ if_deregister_send(interface);
+ if_deregister_receive(interface);
+ } else {
+ if_deregister6(interface);
+ }
return ISC_R_SUCCESS;
}
diff --git a/common/dlpi.c b/common/dlpi.c
index 46d3da08..7c0b3ccd 100644
--- a/common/dlpi.c
+++ b/common/dlpi.c
@@ -77,9 +77,17 @@
* 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
+ */
+
#ifndef lint
static char copyright[] =
-"$Id: dlpi.c,v 1.30 2007/04/27 23:54:05 each Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n";
+"$Id: dlpi.c,v 1.31 2007/05/08 23:05:20 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
@@ -126,7 +134,7 @@ static int strioctl PROTO ((int fd, int cmd, int timeout, int len, char *dp));
#define DLPI_MAXDLADDR 1024 /* Max address size */
#define DLPI_DEVDIR "/dev/" /* Device directory */
-static int dlpiopen PROTO ((char *ifname));
+static int dlpiopen(const char *ifname);
static int dlpiunit PROTO ((char *ifname));
static int dlpiinforeq PROTO ((int fd));
static int dlpiphysaddrreq PROTO ((int fd, unsigned long addrtype));
@@ -200,7 +208,7 @@ int if_register_dlpi (info)
/*
- * Submit a DL_INFO_REQ request, to find the dl_mac_type and
+ * 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) {
@@ -252,7 +260,7 @@ int if_register_dlpi (info)
/*
* Bind to the IP service access point (SAP), connectionless (CLDLS).
*/
- if (dlpibindreq (sock, ETHERTYPE_IP, 0, DL_CLDLS, 0, 0) < 0
+ 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);
}
@@ -754,11 +762,11 @@ static int dlpiunit (ifname)
/*
* dlpiopen - open the DLPI device for a given interface name
*/
-static int dlpiopen (ifname)
- char *ifname;
-{
+static int
+dlpiopen(const char *ifname) {
char devname [50];
- char *cp, *dp, *ep;
+ char *dp;
+ const char *cp, *ep;
if (!ifname) {
return -1;
@@ -1337,4 +1345,73 @@ void maybe_setup_fallback ()
interface_dereference (&fbi, MDL);
}
}
+
+void
+get_hw_addr(const char *name, struct hardware *hw) {
+ int sock;
+ 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 %ld",
+ name, dlp->info_ack.dl_mac_type);
+ }
+
+ /*
+ * 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 */
diff --git a/common/dns.c b/common/dns.c
index 44efb7ee..5f13e036 100644
--- a/common/dns.c
+++ b/common/dns.c
@@ -33,7 +33,7 @@
#ifndef lint
static char copyright[] =
-"$Id: dns.c,v 1.40 2006/07/19 17:14:55 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
+"$Id: dns.c,v 1.41 2007/05/08 23:05:20 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
@@ -519,23 +519,29 @@ int get_dhcid (struct data_string *id,
server... */
isc_result_t
-ddns_update_a(struct data_string *ddns_fwd_name, struct iaddr ddns_addr,
- struct data_string *ddns_dhcid, unsigned long ttl,
- unsigned rrsetp, unsigned conflict)
-{
+ddns_update_fwd(struct data_string *ddns_fwd_name, struct iaddr ddns_addr,
+ struct data_string *ddns_dhcid, unsigned long ttl,
+ unsigned rrsetp, unsigned conflict) {
ns_updque updqueue;
ns_updrec *updrec;
isc_result_t result;
- char ddns_address [16];
const char *logstr;
+ char ddns_address[
+ sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
+ int ddns_address_type;
- if (ddns_addr.len != 4)
+ /*
+ * We want to delete either A or AAAA records, depending on
+ * whether we have an IPv4 or an IPv6 address.
+ */
+ if (ddns_addr.len == 4) {
+ ddns_address_type = T_A;
+ } else if (ddns_addr.len == 16) {
+ ddns_address_type = T_AAAA;
+ } else {
return ISC_R_INVALIDARG;
-
- /* %Audit% Cannot exceed 16 bytes. %2004.06.17,Safe% */
- sprintf (ddns_address, "%u.%u.%u.%u",
- ddns_addr.iabuf[0], ddns_addr.iabuf[1],
- ddns_addr.iabuf[2], ddns_addr.iabuf[3]);
+ }
+ strcpy(ddns_address, piaddr(ddns_addr));
/*
* When a DHCP client or server intends to update an A RR, it first
@@ -553,7 +559,7 @@ ddns_update_a(struct data_string *ddns_fwd_name, struct iaddr ddns_addr,
*/
updrec = minires_mkupdrec (S_PREREQ,
(const char *)ddns_fwd_name -> data,
- C_IN, T_A, 0);
+ C_IN, ddns_address_type, 0);
if (!updrec) {
result = ISC_R_NOMEMORY;
goto error;
@@ -571,7 +577,7 @@ ddns_update_a(struct data_string *ddns_fwd_name, struct iaddr ddns_addr,
*/
updrec = minires_mkupdrec (S_UPDATE,
(const char *)ddns_fwd_name -> data,
- C_IN, T_A, ttl);
+ C_IN, ddns_address_type, ttl);
if (!updrec) {
result = ISC_R_NOMEMORY;
goto error;
@@ -699,7 +705,7 @@ ddns_update_a(struct data_string *ddns_fwd_name, struct iaddr ddns_addr,
*/
updrec = minires_mkupdrec (S_UPDATE,
(const char *)ddns_fwd_name -> data,
- C_IN, T_A, 0);
+ C_IN, ddns_address_type, 0);
if (!updrec) {
result = ISC_R_NOMEMORY;
goto error;
@@ -717,7 +723,7 @@ ddns_update_a(struct data_string *ddns_fwd_name, struct iaddr ddns_addr,
*/
updrec = minires_mkupdrec (S_UPDATE,
(const char *)ddns_fwd_name -> data,
- C_IN, T_A, ttl);
+ C_IN, ddns_address_type, ttl);
if (!updrec) {
result = ISC_R_NOMEMORY;
goto error;
@@ -747,7 +753,7 @@ ddns_update_a(struct data_string *ddns_fwd_name, struct iaddr ddns_addr,
case ISC_R_NXRRSET:
case ISC_R_NXDOMAIN:
- logstr = "Has an A record but no DHCID, not mine.";
+ logstr = "Has an address record but no DHCID, not mine.";
break;
default:
@@ -803,22 +809,29 @@ ddns_update_a(struct data_string *ddns_fwd_name, struct iaddr ddns_addr,
return result;
}
-isc_result_t ddns_remove_a (struct data_string *ddns_fwd_name,
- struct iaddr ddns_addr,
- struct data_string *ddns_dhcid)
-{
+isc_result_t
+ddns_remove_fwd(struct data_string *ddns_fwd_name,
+ struct iaddr ddns_addr,
+ struct data_string *ddns_dhcid) {
ns_updque updqueue;
ns_updrec *updrec;
isc_result_t result = SERVFAIL;
- char ddns_address [16];
+ char ddns_address[
+ sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
+ int ddns_address_type;
- if (ddns_addr.len != 4)
+ /*
+ * We want to delete either A or AAAA records, depending on
+ * whether we have an IPv4 or an IPv6 address.
+ */
+ if (ddns_addr.len == 4) {
+ ddns_address_type = T_A;
+ } else if (ddns_addr.len == 16) {
+ ddns_address_type = T_AAAA;
+ } else {
return ISC_R_INVALIDARG;
-
- /* %Audit% Cannot exceed 16 bytes. %2004.06.17,Safe% */
- sprintf (ddns_address, "%u.%u.%u.%u",
- ddns_addr.iabuf[0], ddns_addr.iabuf[1],
- ddns_addr.iabuf[2], ddns_addr.iabuf[3]);
+ }
+ strcpy(ddns_address, piaddr(ddns_addr));
/*
* The entity chosen to handle the A record for this client (either the
@@ -859,7 +872,7 @@ isc_result_t ddns_remove_a (struct data_string *ddns_fwd_name,
*/
updrec = minires_mkupdrec (S_PREREQ,
(const char *)ddns_fwd_name -> data,
- C_IN, T_A, 0);
+ C_IN, ddns_address_type, 0);
if (!updrec) {
result = ISC_R_NOMEMORY;
goto error;
@@ -877,7 +890,7 @@ isc_result_t ddns_remove_a (struct data_string *ddns_fwd_name,
*/
updrec = minires_mkupdrec (S_UPDATE,
(const char *)ddns_fwd_name -> data,
- C_IN, T_A, 0);
+ C_IN, ddns_address_type, 0);
if (!updrec) {
result = ISC_R_NOMEMORY;
goto error;
@@ -934,15 +947,15 @@ isc_result_t ddns_remove_a (struct data_string *ddns_fwd_name,
*/
updrec = minires_mkupdrec (S_PREREQ,
(const char *)ddns_fwd_name -> data,
- C_IN, T_A, 0);
+ C_IN, ddns_address_type, 0);
if (!updrec) {
result = ISC_R_NOMEMORY;
goto error;
}
- updrec -> r_data = (unsigned char *)0;
- updrec -> r_size = 0;
- updrec -> r_opcode = NXRRSET;
+ updrec->r_data = NULL;
+ updrec->r_size = 0;
+ updrec->r_opcode = NXRRSET;
ISC_LIST_APPEND (updqueue, updrec, r_link);
diff --git a/common/heap.c b/common/heap.c
new file mode 100644
index 00000000..3ce6b0c9
--- /dev/null
+++ b/common/heap.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1997-2001 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.
+ */
+
+/* $Id: heap.c,v 1.2 2007/05/08 23:05:20 dhankins Exp $ */
+
+/*
+ * This heap implementation is taken from the BIND 9 code. It has been
+ * modified to use the DHCP memory management rather than BIND memory
+ * contexts.
+ */
+
+/*! \file
+ * Heap implementation of priority queues adapted from the following:
+ *
+ * \li "Introduction to Algorithms," Cormen, Leiserson, and Rivest,
+ * MIT Press / McGraw Hill, 1990, ISBN 0-262-03141-8, chapter 7.
+ *
+ * \li "Algorithms," Second Edition, Sedgewick, Addison-Wesley, 1988,
+ * ISBN 0-201-06673-4, chapter 11.
+ */
+
+#include "dhcpd.h"
+#include "omapip/omapip.h"
+#include "heap.h"
+
+#include <assert.h>
+#define REQUIRE assert
+#define INSIST assert
+
+/*@{*/
+/*%
+ * Note: to make heap_parent and heap_left easy to compute, the first
+ * element of the heap array is not used; i.e. heap subscripts are 1-based,
+ * not 0-based. The parent is index/2, and the left-child is index*2.
+ * The right child is index*2+1.
+ */
+#define heap_parent(i) ((i) >> 1)
+#define heap_left(i) ((i) << 1)
+/*@}*/
+
+#define SIZE_INCREMENT 1024
+
+/*%
+ * When the heap is in a consistent state, the following invariant
+ * holds true: for every element i > 1, heap_parent(i) has a priority
+ * higher than or equal to that of i.
+ */
+#define HEAPCONDITION(i) ((i) == 1 || \
+ ! heap->compare(heap->array[(i)], \
+ heap->array[heap_parent(i)]))
+
+/*% ISC heap structure. */
+struct isc_heap {
+ unsigned int size;
+ unsigned int size_increment;
+ unsigned int last;
+ void **array;
+ isc_heapcompare_t compare;
+ isc_heapindex_t index;
+};
+
+isc_result_t
+isc_heap_create(isc_heapcompare_t compare,
+ isc_heapindex_t index, unsigned int size_increment,
+ isc_heap_t **heapp)
+{
+ isc_heap_t *heap;
+
+ REQUIRE(heapp != NULL && *heapp == NULL);
+ REQUIRE(compare != NULL);
+
+ heap = dmalloc(sizeof(*heap), MDL);
+ if (heap == NULL)
+ return (ISC_R_NOMEMORY);
+ heap->size = 0;
+ if (size_increment == 0)
+ heap->size_increment = SIZE_INCREMENT;
+ else
+ heap->size_increment = size_increment;
+ heap->last = 0;
+ heap->array = NULL;
+ heap->compare = compare;
+ heap->index = index;
+
+ *heapp = heap;
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+isc_heap_destroy(isc_heap_t **heapp) {
+ isc_heap_t *heap;
+
+ REQUIRE(heapp != NULL);
+ heap = *heapp;
+
+ if (heap->array != NULL)
+ dfree(heap->array, MDL);
+ dfree(heap, MDL);
+
+ *heapp = NULL;
+}
+
+static isc_boolean_t
+resize(isc_heap_t *heap) {
+ void **new_array;
+ size_t new_size;
+
+ new_size = heap->size + heap->size_increment;
+ new_array = dmalloc(new_size * sizeof(void *), MDL);
+ if (new_array == NULL)
+ return (ISC_FALSE);
+ if (heap->array != NULL) {
+ memcpy(new_array, heap->array, heap->size * sizeof(void *));
+ dfree(heap->array, MDL);
+ }
+ heap->size = new_size;
+ heap->array = new_array;
+
+ return (ISC_TRUE);
+}
+
+static void
+float_up(isc_heap_t *heap, unsigned int i, void *elt) {
+ unsigned int p;
+
+ for (p = heap_parent(i) ;
+ i > 1 && heap->compare(elt, heap->array[p]) ;
+ i = p, p = heap_parent(i)) {
+ heap->array[i] = heap->array[p];
+ if (heap->index != NULL)
+ (heap->index)(heap->array[i], i);
+ }
+ heap->array[i] = elt;
+ if (heap->index != NULL)
+ (heap->index)(heap->array[i], i);
+
+ INSIST(HEAPCONDITION(i));
+}
+
+static void
+sink_down(isc_heap_t *heap, unsigned int i, void *elt) {
+ unsigned int j, size, half_size;
+ size = heap->last;
+ half_size = size / 2;
+ while (i <= half_size) {
+ /* Find the smallest of the (at most) two children. */
+ j = heap_left(i);
+ if (j < size && heap->compare(heap->array[j+1],
+ heap->array[j]))
+ j++;
+ if (heap->compare(elt, heap->array[j]))
+ break;
+ heap->array[i] = heap->array[j];
+ if (heap->index != NULL)
+ (heap->index)(heap->array[i], i);
+ i = j;
+ }
+ heap->array[i] = elt;
+ if (heap->index != NULL)
+ (heap->index)(heap->array[i], i);
+
+ INSIST(HEAPCONDITION(i));
+}
+
+isc_result_t
+isc_heap_insert(isc_heap_t *heap, void *elt) {
+ unsigned int i;
+
+ i = ++heap->last;
+ if (heap->last >= heap->size && !resize(heap))
+ return (ISC_R_NOMEMORY);
+
+ float_up(heap, i, elt);
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+isc_heap_delete(isc_heap_t *heap, unsigned int index) {
+ void *elt;
+ isc_boolean_t less;
+
+ REQUIRE(index >= 1 && index <= heap->last);
+
+ if (index == heap->last) {
+ heap->last--;
+ } else {
+ elt = heap->array[heap->last--];
+ less = heap->compare(elt, heap->array[index]);
+ heap->array[index] = elt;
+ if (less)
+ float_up(heap, index, heap->array[index]);
+ else
+ sink_down(heap, index, heap->array[index]);
+ }
+}
+
+void
+isc_heap_increased(isc_heap_t *heap, unsigned int index) {
+ REQUIRE(index >= 1 && index <= heap->last);
+
+ float_up(heap, index, heap->array[index]);
+}
+
+void
+isc_heap_decreased(isc_heap_t *heap, unsigned int index) {
+ REQUIRE(index >= 1 && index <= heap->last);
+
+ sink_down(heap, index, heap->array[index]);
+}
+
+void *
+isc_heap_element(isc_heap_t *heap, unsigned int index) {
+ REQUIRE(index >= 1 && index <= heap->last);
+
+ return (heap->array[index]);
+}
+
+void
+isc_heap_foreach(isc_heap_t *heap, isc_heapaction_t action, void *uap) {
+ unsigned int i;
+
+ REQUIRE(action != NULL);
+
+ for (i = 1 ; i <= heap->last ; i++)
+ (action)(heap->array[i], uap);
+}
diff --git a/common/inet.c b/common/inet.c
index 934eba19..cadea305 100644
--- a/common/inet.c
+++ b/common/inet.c
@@ -35,7 +35,7 @@
#ifndef lint
static char copyright[] =
-"$Id: inet.c,v 1.11 2006/05/15 15:07:49 dhankins Exp $ Copyright (c) 2004-2005 Internet Systems Consortium. All rights reserved.\n";
+"$Id: inet.c,v 1.12 2007/05/08 23:05:20 dhankins Exp $ Copyright (c) 2004-2005 Internet Systems Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
@@ -210,63 +210,342 @@ addr_match(addr, match)
return 1;
}
-char *piaddr (addr)
- struct iaddr addr;
-{
- static char pbuf [4 * 16];
- char *s = pbuf;
+/*
+ * 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 (addr.len > sizeof(addr.iabuf))
- log_fatal("piaddr():%s:%d: Address length too long.", MDL);
+ if (a1->len != a2->len) {
+ return 0;
+ }
+
+ for (i=0; i<a1->len; 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; i<a1->len; 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; i<a1->len; i++) {
+ result->iabuf[i] = a1->iabuf[i] & a2->iabuf[i];
+ if (result->iabuf[i] != 0) {
+ all_zero = 0;
+ }
+ }
+
+ return !all_zero;
+}
+
+/*
+ * 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 ISC_R_INVALIDARG;
+ }
+ if (*result != NULL) {
+ return ISC_R_INVALIDARG;
+ }
+ if ((lo == NULL) || (hi == NULL) || (lo->len != hi->len)) {
+ return ISC_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 ISC_R_INVALIDARG;
+ }
+ if (*result == NULL) {
+ return ISC_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) {
- strcpy (s, "<null address>");
+ return "<null address>";
}
- for (i = 0; i < addr.len; i++) {
- sprintf (s, "%s%d", i ? "." : "", addr.iabuf [i]);
- s += strlen (s);
+ 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));
}
- return pbuf;
+
+ log_fatal("piaddr():%s:%d: Invalid address length %d.", MDL,
+ addr.len);
+ /* quell compiler warnings */
+ return NULL;
}
-char *piaddrmask (struct iaddr addr, struct iaddr mask,
- const char *file, int line)
-{
- char *s, tbuf[sizeof("255.255.255.255/32")];
+/* 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 i, oct, bit;
+ unsigned int oct, bit;
- if (addr.len != 4)
+ if ((addr->len != 4) && (addr->len != 16))
log_fatal("piaddrmask():%s:%d: Address length %d invalid",
- MDL, addr.len);
- if (addr.len != mask.len)
+ 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 = 32; mw > 0; ) {
+ for (mw = (mask->len * 8) ; mw > 0 ; ) {
oct = (mw - 1) / 8;
bit = 0x80 >> ((mw - 1) % 8);
- if (!mask.iabuf [oct])
+ if (!mask->iabuf[oct])
mw -= 8;
- else if (mask.iabuf [oct] & bit)
+ else if (mask->iabuf[oct] & bit)
break;
else
mw--;
}
- s = tbuf;
- for (i = 0 ; i <= oct ; i++) {
- sprintf(s, "%s%d", i ? "." : "", addr.iabuf[i]);
- s += strlen(s);
- }
- sprintf(s, "/%d", 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);
- s = dmalloc (strlen(tbuf) + 1, file, line);
- if (!s)
- return s;
- strcpy(s, tbuf);
- return s;
+ return ret;
}
diff --git a/common/lpf.c b/common/lpf.c
index 12c46bc1..255fea8f 100644
--- a/common/lpf.c
+++ b/common/lpf.c
@@ -28,7 +28,7 @@
#ifndef lint
static char copyright[] =
-"$Id: lpf.c,v 1.31 2007/04/27 23:54:05 each Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n";
+"$Id: lpf.c,v 1.32 2007/05/08 23:05:20 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
@@ -43,6 +43,7 @@ static char copyright[] =
#include "includes/netinet/ip.h"
#include "includes/netinet/udp.h"
#include "includes/netinet/if_ether.h"
+#include <net/if.h>
/* Reinitializes the specified interface after an address change. This
is not required for packet-filter APIs. */
@@ -406,9 +407,59 @@ void maybe_setup_fallback ()
if_readsocket, 0,
fallback_discard, 0, 0);
if (status != ISC_R_SUCCESS)
- log_fatal ("Can't register I/O handle for %s: %s",
+ log_fatal ("Can't register I/O handle for \"%s\": %s",
fbi -> name, isc_result_totext (status));
interface_dereference (&fbi, MDL);
}
}
+
+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 ARPHDR_IEEE802_TR
+ case ARPHRD_IEEE802_TR:
+#endif /* ARPHDR_IEEE802_TR */
+ hw->hlen = 7;
+ hw->hbuf[0] = HTYPE_IEEE802;
+ memcpy(&hw->hbuf[1], sa->sa_data, 6);
+ break;
+ case ARPHRD_FDDI:
+ hw->hlen = 17;
+ hw->hbuf[0] = HTYPE_FDDI;
+ memcpy(&hw->hbuf[1], sa->sa_data, 16);
+ break;
+ default:
+ log_fatal("Unsupported device type %ld for \"%s\"",
+ sa->sa_family, name);
+ }
+
+ close(sock);
+}
#endif
diff --git a/common/options.c b/common/options.c
index a05c14ec..e192d9fd 100644
--- a/common/options.c
+++ b/common/options.c
@@ -34,7 +34,7 @@
#ifndef lint
static char copyright[] =
-"$Id: options.c,v 1.105 2007/04/12 16:15:31 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
+"$Id: options.c,v 1.106 2007/05/08 23:05:20 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
#endif /* not lint */
#define DHCP_OPTION_DATA
@@ -141,7 +141,7 @@ int parse_option_buffer (options, buffer, length, universe)
const unsigned char *end = buffer + length;
unsigned len, offset;
unsigned code;
- struct option_cache *op = (struct option_cache *)0;
+ struct option_cache *op = NULL, *nop = NULL;
struct buffer *bp = (struct buffer *)0;
struct option *option = NULL;
@@ -167,9 +167,21 @@ int parse_option_buffer (options, buffer, length, universe)
}
/* All other fields (except PAD and END handled above)
- * have a length field.
+ * 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 basicaly consume the
+ * entire options buffer, so have at it.
*/
- len = universe->get_length(buffer + offset);
+ 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);
offset += universe->length_size;
@@ -193,36 +205,62 @@ int parse_option_buffer (options, buffer, length, universe)
if (!(option &&
(option->format[0] == 'e' ||
option->format[0] == 'E') &&
- (parse_encapsulated_suboptions
- (options, option,
- bp->data + offset, len,
- universe, (const char *)0)))) {
- op = lookup_option (universe, options, code);
- if (op) {
- 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.");
- return 0;
+ (parse_encapsulated_suboptions(options, option,
+ bp->data + offset, len,
+ universe, NULL)))) {
+ op = lookup_option(universe, options, code);
+
+ if (op != NULL && universe->concat_duplicates) {
+ 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 if (op != NULL) {
+ /* 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;
+ nop->data.len = len;
+
+ option_cache_reference(&op->next, nop, MDL);
+ option_cache_dereference(&nop, MDL);
+ } else {
+ save_option_buffer(universe, options, bp,
+ bp->data + offset, len,
+ code, 1);
}
- /* 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 {
- save_option_buffer (universe, options, bp,
- bp->data + offset, len,
- code, 1);
- }
}
option_dereference(&option, MDL);
offset += len;
@@ -665,7 +703,7 @@ int cons_options (inpacket, outpacket, lease, client_state,
}
/* Put any spaces that are encapsulated on the list,
- * sort out wether they contain values later.
+ * sort out whether they contain values later.
*/
for (i = 0; i < cfg_options -> universe_count; i++) {
if (universes[i]->enc_opt &&
@@ -771,6 +809,257 @@ int cons_options (inpacket, outpacket, lease, client_state,
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(vs->buf+vs->bufpos, oc->option->code);
+ vs->bufpos += 2;
+ } else if (universe->tag_size == 4) {
+ putULong(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(vs->buf+vs->bufpos, ds.len);
+ vs->bufpos += 2;
+ } else if (universe->length_size == 4) {
+ putULong(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);
+ }
+}
+
+/*
+ * 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 len;
+ int oro_size;
+ u_int16_t code;
+ int in_required_opts;
+ struct universe *u;
+ int vsio_option_code;
+ int vsio_wanted;
+ struct vsio_state vs;
+
+ 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));
+
+ if (evaluate_option_cache(&ds, packet, NULL,
+ NULL, opt_state, NULL,
+ &global_scope, oc, MDL)) {
+ if ((ds.len + 4) <= (buflen - bufpos)) {
+ /* option tag */
+ putUShort(buf+bufpos, required_opts[i]);
+ /* option length */
+ putUShort(buf+bufpos+2, ds.len);
+ /* option data */
+ memcpy(buf+bufpos+4, ds.data, ds.len);
+ /* update position */
+ bufpos += (4 + ds.len);
+ } else {
+ log_debug("No space for option %d",
+ required_opts[i]);
+ }
+ 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; i<oro_size; i++) {
+ memcpy(&code, oro->data+(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;
+ }
+
+ /*
+ * See if this is the VSIO option.
+ */
+ if (code == vsio_option_code) {
+ vsio_wanted = 1;
+ }
+
+ /*
+ * Not already added, find this option.
+ */
+ oc = lookup_option(&dhcpv6_universe, opt_state, code);
+ if (oc == NULL) {
+ continue;
+ }
+ memset(&ds, 0, sizeof(ds));
+ if (evaluate_option_cache(&ds, packet, NULL, NULL, opt_state,
+ NULL, &global_scope, oc, MDL)) {
+ if ((ds.len + 4) <= (buflen - bufpos)) {
+ /* option tag */
+ putUShort(buf+bufpos, code);
+ /* option length */
+ putUShort(buf+bufpos+2, ds.len);
+ /* option data */
+ memcpy(buf+bufpos+4, ds.data, ds.len);
+ /* update position */
+ bufpos += (4 + ds.len);
+ } else {
+ log_debug("No space for option %d", code);
+ }
+ data_string_forget(&ds, MDL);
+ } else {
+ log_error("Error evaluating option %d", code);
+ }
+ }
+
+ 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) {
+ putUShort(buf+bufpos,
+ vsio_option_code);
+ putUShort(buf+bufpos+2,
+ vs.bufpos-bufpos-4);
+ putULong(buf+bufpos+4, o->code);
+
+ bufpos = vs.bufpos;
+ }
+ }
+ }
+ }
+ }
+
+ return bufpos;
+}
+
/* Store all the requested options into the requested buffer. */
int store_options (ocount, buffer, buflen, packet, lease, client_state,
@@ -1144,7 +1433,8 @@ format_has_text(format)
/* These symbols are arbitrary, not fixed or
* determinable length...text options with them is
- * invalid.
+ * invalid (whatever the case, they are never NULL
+ * terminated).
*/
case 'A':
case 'a':
@@ -1153,9 +1443,19 @@ format_has_text(format)
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 one-byte in length but trails a name of a
+ * 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.
*/
@@ -1185,9 +1485,10 @@ format_min_length(format, oc)
const char *format;
struct option_cache *oc;
{
- const char *p;
+ const char *p, *name;
int min_len = 0;
int last_size = 0;
+ struct enumeration *espace;
p = format;
while (*p != '\0') {
@@ -1206,12 +1507,25 @@ format_min_length(format, oc)
last_size = 2;
break;
- case 'N': /* Enumeration in 1-byte values. */
+ case 'N': /* Enumeration value. */
/* Consume space name. */
- while ((*p != '\0') && (*p++ != '.'))
- ;
+ 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;
+ }
- /* Fall Through to handle as one-byte field */
+ min_len += espace->width;
+ last_size = espace->width;
+ p++;
+
+ break;
case 'b': /* int8_t */
case 'B': /* uint8_t */
@@ -1223,53 +1537,17 @@ format_min_length(format, oc)
case 'o': /* Last argument is optional. */
min_len -= last_size;
- case 'e': /* Encapsulation hint (there is an 'E' later). */
- last_size = 0;
- break;
+ /* 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. */
- /* Consume space name. */
- while ((*p != '\0') && (*p++ != '.'))
- ;
-
- /* Find an end option, or find that the encaps options
- * go all the way to the end (or beyond) of the data
- * portion of the option.
- */
- last_size = 0;
- while (min_len < oc->data.len) {
- if (oc->data.data[min_len] == DHO_END) {
- min_len++;
- last_size++;
- break;
- } else if (oc->data.data[min_len] == DHO_PAD) {
- min_len++;
- last_size++;
- } else if ((min_len + 1) < oc->data.len) {
- min_len += oc->data.data[min_len+1]+2;
- last_size += oc->data.data[min_len+1]+2;
- } else {
- /* Suboption length is out of bounds,
- * advance beyond the code/length pair
- * to trigger below error conditonal.
- */
- min_len += 2;
- last_size += 2;
- break;
- }
- }
-
- if (min_len > oc->data.len) {
- log_error("format_min_length(%s): "
- "Encapsulated options exceed "
- "supplied buffer.", format);
- return INT_MAX;
- }
-
- break;
+ return min_len;
case 'd': /* "Domain name" */
- case 'D': /* "rfc1035 compressed names" */
+ case 'D': /* "rfc1035 formatted names" */
case 't': /* "ASCII Text" */
case 'X': /* "ASCII or Hex Conditional */
case 'x': /* "Hex" */
@@ -1277,6 +1555,11 @@ format_min_length(format, oc)
case 'a': /* Array of preceding symbol. */
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 "
@@ -1308,10 +1591,10 @@ const char *pretty_print_option (option, data, len, emit_commas, emit_quotes)
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;
- struct in_addr foo;
char comma;
unsigned long tval;
@@ -1326,7 +1609,7 @@ const char *pretty_print_option (option, data, len, emit_commas, emit_quotes)
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);
+ "%s:%d", MDL);
if (!numhunk) {
log_error ("%s: Extra codes in format string: %s",
@@ -1371,7 +1654,7 @@ const char *pretty_print_option (option, data, len, emit_commas, emit_quotes)
break;
case 'd':
fmtbuf[l] = 't';
- /* Fall Through! */
+ /* Fall Through ! */
case 't':
case 'D':
fmtbuf [l + 1] = 0;
@@ -1385,8 +1668,17 @@ const char *pretty_print_option (option, data, len, emit_commas, emit_quotes)
enumbuf [l] =
find_enumeration (&option -> format [k] + 1,
i - k - 1);
- hunksize += 1;
- hunkinc = 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':
@@ -1482,6 +1774,10 @@ const char *pretty_print_option (option, data, len, emit_commas, emit_quotes)
*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,
@@ -1502,17 +1798,31 @@ const char *pretty_print_option (option, data, len, emit_commas, emit_quotes)
pretty_domain(&op, endbuf-1,
&nbp, nend);
} else {
+ /* ns_name_ntop() includes
+ * a trailing NUL in its
+ * count.
+ */
count = MRns_name_ntop(
nbuff, op,
(endbuf-op)-1);
- if (count == -1) {
+ if (count <= 0) {
log_error("Invalid "
"domain name.");
break;
}
- op += count;
+ /* 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';
@@ -1520,22 +1830,56 @@ const char *pretty_print_option (option, data, len, emit_commas, emit_quotes)
/* pretty-printing an array of enums is
going to get ugly. */
case 'N':
- if (!enumbuf [j])
+ 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);
+ }
+
for (i = 0; ;i++) {
if (!enumbuf [j] -> values [i].name)
goto enum_as_num;
if (enumbuf [j] -> values [i].value ==
- *dp)
+ tval)
break;
}
strcpy (op, enumbuf [j] -> values [i].name);
+ dp += enumbuf[j]->width;
+ break;
+
+ enum_as_num:
+ sprintf(op, "%u", tval);
break;
+
case 'I':
- foo.s_addr = htonl (getULong (dp));
- strcpy (op, inet_ntoa (foo));
+ 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;
@@ -1564,7 +1908,6 @@ const char *pretty_print_option (option, data, len, emit_commas, emit_quotes)
sprintf (op, "%d", *(const char *)dp++);
break;
case 'B':
- enum_as_num:
sprintf (op, "%d", *dp++);
break;
case 'x':
@@ -1861,6 +2204,100 @@ int save_option_buffer (struct universe *universe,
return 1;
}
+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((char *)(oro->data + oro->len), oc->option->code);
+ oro->len += 2;
+}
+
+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) {
+ putUShort((char *)server_oro->data +
+ server_oro->len,
+ o->code);
+ server_oro->len += 2;
+ break;
+ }
+ o = o->universe->enc_opt;
+ }
+ }
+ }
+}
+
void save_option (struct universe *universe,
struct option_state *options, struct option_cache *oc)
{
@@ -1879,6 +2316,7 @@ void save_hashed_option (universe, options, oc)
int hashix;
pair bptr;
pair *hash = options -> universes [universe -> index];
+ struct option_cache **ocloc;
if (oc -> refcnt == 0)
abort ();
@@ -1908,11 +2346,10 @@ void save_hashed_option (universe, options, oc)
/* If we find one, dereference it and put the new one
in its place. */
if (bptr) {
- option_cache_dereference
- ((struct option_cache **)&bptr -> car, MDL);
- option_cache_reference
- ((struct option_cache **)&bptr -> car,
- oc, MDL);
+ ocloc = (struct option_cache **)&bptr->car;
+
+ option_cache_dereference(ocloc, MDL);
+ option_cache_reference(ocloc, oc, MDL);
return;
}
}
@@ -1949,6 +2386,7 @@ void delete_hashed_option (universe, options, code)
int hashix;
pair bptr, prev = (pair)0;
pair *hash = options -> universes [universe -> index];
+ struct option_cache *oc;
/* There may not be any options in this space. */
if (!hash)
@@ -2063,7 +2501,7 @@ int hashed_option_state_dereference (universe, state, file, line)
* by first duplicating the original buffer and appending the desired
* values, followed by coping the new value into place.
*/
-static int
+int
append_option(struct data_string *dst, struct universe *universe,
struct option *option, struct data_string *src)
{
@@ -2396,6 +2834,53 @@ int nwip_option_space_encapsulate (result, packet, lease, client_state,
return status;
}
+/* We don't want to use ns_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;
@@ -2409,7 +2894,8 @@ int fqdn_option_space_encapsulate (result, packet, lease, client_state,
{
pair ocp;
struct data_string results [FQDN_SUBOPTION_COUNT + 1];
- unsigned i;
+ int status = 1;
+ int i;
unsigned len;
struct buffer *bp = (struct buffer *)0;
struct option_chain_head *head;
@@ -2432,17 +2918,35 @@ int fqdn_option_space_encapsulate (result, packet, lease, client_state,
packet, lease, client_state, in_options,
cfg_options, scope, oc, MDL);
}
- len = 4 + results [FQDN_FQDN].len;
+ /* 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.");
- return 0;
+ 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;
@@ -2456,31 +2960,19 @@ int fqdn_option_space_encapsulate (result, packet, lease, client_state,
if (results [FQDN_ENCODED].len &&
results [FQDN_ENCODED].data [0]) {
- unsigned char *out;
- int i;
- bp -> data [0] |= 4;
- out = &bp -> data [3];
+ bp->data[0] |= 4;
if (results [FQDN_FQDN].len) {
- i = 0;
- while (i < results [FQDN_FQDN].len) {
- int j;
- for (j = i; ('.' !=
- results [FQDN_FQDN].data [j]) &&
- j < results [FQDN_FQDN].len; j++)
- ;
- *out++ = j - i;
- memcpy (out, &results [FQDN_FQDN].data [i],
- (unsigned)(j - i));
- out += j - i;
- i = j;
- if (results [FQDN_FQDN].data [j] == '.')
- i++;
+ i = fqdn_encode(&bp->data[3], len - 3,
+ results[FQDN_FQDN].data,
+ results[FQDN_FQDN].len);
+
+ if (i < 0) {
+ status = 0;
+ goto exit;
}
- if ((results [FQDN_FQDN].data
- [results [FQDN_FQDN].len - 1] == '.'))
- *out++ = 0;
- result -> len = out - result -> data;
- result -> terminated = 0;
+
+ result->len += i;
+ result->terminated = 0;
}
} else {
if (results [FQDN_FQDN].len) {
@@ -2490,12 +2982,278 @@ int fqdn_option_space_encapsulate (result, packet, lease, client_state,
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;
+}
+
+/* Shill to the DHCPv4 fqdn option cache any lookups in the fqdn6 universe.
+ *
+ * XXX: Is this necessary? There shouldn't be any lookups directly...
+ */
+struct option_cache *
+lookup_fqdn6_option(struct universe *universe, struct option_state *options,
+ unsigned code)
+{
+ log_fatal("Impossible condition at %s:%d.", MDL);
+
+ return fqdn_universe.lookup_func(&fqdn_universe, options, code);
+}
+
+/* Shill to the DHCPv4 fqdn option cache any direct saves to the fqdn6
+ * universe.
+ *
+ * XXX: Should this even be possible? Never excercised code?
+ */
+void
+save_fqdn6_option(struct universe *universe, struct option_state *options,
+ struct option_cache *oc)
+{
+ log_fatal("Impossible condition at %s:%d.", MDL);
+
+ fqdn_universe.save_func(&fqdn_universe, options, oc);
+}
+
+/* Shill to the DHCPv4 fqdn option cache any attempts to remove entries.
+ *
+ * XXX: Again...should this even be possible?
+ */
+void
+delete_fqdn6_option(struct universe *universe, struct option_state *options,
+ int code)
+{
+ log_fatal("Impossible condition at %s:%d.", MDL);
+
+ fqdn_universe.delete_func(&fqdn_universe, options, code);
+}
+
+/* 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);
+
+ 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 (result[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, so we go ahead
+ * and allocate a bigger buffer to accomodate them. But the
+ * 'length' we got (because it is a DNS encoded string) is
+ * one longer than we need...so we only add two extra octets.
+ */
+ if (!buffer_allocate(&bp, length + 2, 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, bp->data + 3, length - 1);
+ if (len == -1) {
+ log_error("Unable to convert dhcp6.fqdn domain name to "
+ "printable form.");
+ goto error;
+ }
+
+ /* Save the domain name. */
+ if (len > 0) {
+ if (!save_option_buffer(&fqdn_universe, options, bp,
+ bp->data + 3, len, FQDN_FQDN, 1))
+ goto error;
+
+ first_dot = strchr(bp->data + 3, '.');
+
+ if (first_dot != NULL) {
+ hlen = first_dot - bp->data + 3;
+ dlen = len - hlen;
+ } else {
+ hlen = len;
+ dlen = 0;
+ }
+
+ if (!save_option_buffer(&fqdn_universe, options, bp,
+ bp->data + 3, len, FQDN_FQDN, 1) ||
+ ((hlen > 0) &&
+ !save_option_buffer(&fqdn_universe, options, bp,
+ bp->data + 3, 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,
@@ -2587,6 +3345,7 @@ void save_linked_option (universe, options, oc)
pair *tail;
pair np = (pair )0;
struct option_chain_head *head;
+ struct option_cache **ocloc;
if (universe -> index >= options -> universe_count)
return;
@@ -2603,12 +3362,11 @@ void save_linked_option (universe, options, oc)
/* Find the tail of the list. */
for (tail = &head -> first; *tail; tail = &((*tail) -> cdr)) {
- if (oc -> option ==
- ((struct option_cache *)((*tail) -> car)) -> option) {
- option_cache_dereference ((struct option_cache **)
- (&(*tail) -> car), MDL);
- option_cache_reference ((struct option_cache **)
- (&(*tail) -> car), oc, MDL);
+ ocloc = (struct option_cache **)&(*tail)->car;
+
+ if (oc->option->code == (*ocloc)->option->code) {
+ option_cache_dereference(ocloc, MDL);
+ option_cache_reference(ocloc, oc, MDL);
return;
}
}
@@ -2843,6 +3601,115 @@ void do_packet (interface, packet, len, from_port, from, hfrom)
}
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 >= sizeof(struct dhcpv6_relay_packet)) {
+ return 1;
+ } else {
+ return 0;
+ }
+ } else {
+ if (len >= sizeof(struct dhcpv6_packet)) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+}
+
+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;
+ struct packet *decoded_packet;
+
+ 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->raw = NULL; */
+ /* decoded_packet->packet_length = 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->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)) {
+ relay = (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-sizeof(*relay),
+ &dhcpv6_universe)) {
+ /* no logging here, as parse_option_buffer() logs all
+ cases where it fails */
+ packet_dereference(&decoded_packet, MDL);
+ return;
+ }
+ } else {
+ msg = (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-sizeof(*msg),
+ &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);
+}
+
+int
pretty_escape(char **dst, char *dend, const unsigned char **src,
const unsigned char *send)
{
@@ -2938,7 +3805,7 @@ pretty_domain(char **dst, char *dend, const unsigned char **src,
return -1;
**dst = '"';
- *dst++;
+ (*dst)++;
do {
/* Continue loop until end of src buffer. */
@@ -2947,13 +3814,13 @@ pretty_domain(char **dst, char *dend, const unsigned char **src,
/* Consume tag size. */
tsiz = **src;
- *src++;
+ (*src)++;
/* At root, finis. */
if (tsiz == 0)
break;
- tend = *src + tsiz;
+ tend = (*src) + tsiz;
/* If the tag exceeds the source buffer, it's illegal.
* This should also trap compression pointers (which should
@@ -2969,13 +3836,13 @@ pretty_domain(char **dst, char *dend, const unsigned char **src,
return -1;
**dst = '.';
- *dst++;
+ (*dst)++;
count += status + 1;
}
while(1);
**dst = '"';
- *dst++;
+ (*dst)++;
return count;
}
diff --git a/common/packet.c b/common/packet.c
index 351a4530..3067ac0f 100644
--- a/common/packet.c
+++ b/common/packet.c
@@ -33,7 +33,7 @@
#ifndef lint
static char copyright[] =
-"$Id: packet.c,v 1.46 2007/04/27 23:54:05 each Exp $ Copyright (c) 2004-2005 Internet Systems Consortium. All rights reserved.\n";
+"$Id: packet.c,v 1.47 2007/05/08 23:05:20 dhankins Exp $ Copyright (c) 2004-2005 Internet Systems Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
diff --git a/common/parse.c b/common/parse.c
index 7639d14f..10b37fac 100644
--- a/common/parse.c
+++ b/common/parse.c
@@ -34,7 +34,7 @@
#ifndef lint
static char copyright[] =
-"$Id: parse.c,v 1.120 2007/04/16 17:32:02 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
+"$Id: parse.c,v 1.121 2007/05/08 23:05:20 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
@@ -322,6 +322,88 @@ int parse_ip_addr (cfile, addr)
}
/*
+ * Return true if every character in the string is hexidecimal.
+ */
+static int
+is_hex_string(const char *s) {
+ while (*s != '\0') {
+ if (!isxdigit(*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;
+
+ v6_len = 0;
+ for (;;) {
+ token = peek_token(&val, NULL, cfile);
+ if ((((token == NAME) || (token == NUMBER_OR_NAME)) &&
+ is_hex_string(val)) ||
+ (token == NUMBER) ||
+ (token == DOT) ||
+ (token == COLON)) {
+
+ next_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;
+ }
+ }
+ v6[v6_len] = '\0';
+
+ 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);
+}
+
+/*
* ip-address-with-subnet :== ip-address |
* ip-address "/" NUMBER
*/
@@ -1227,6 +1309,10 @@ void parse_option_space_decl (cfile)
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;
@@ -1310,6 +1396,7 @@ int parse_option_code_definition (cfile, option)
int is_signed;
char *s;
int has_encapsulation = 0;
+ struct universe *encapsulated;
/* Parse the option code. */
token = next_token (&val, (unsigned *)0, cfile);
@@ -1431,11 +1518,21 @@ int parse_option_code_definition (cfile, option)
case IP_ADDRESS:
type = 'I';
break;
+ case IP6_ADDRESS:
+ type = '6';
+ break;
case DOMAIN_NAME:
type = 'd';
goto no_arrays;
case DOMAIN_LIST:
- type = 'D';
+ /* Consume optional compression indicator. */
+ token = peek_token(&val, NULL, cfile);
+ if (token == COMPRESSED) {
+ token = next_token(&val, NULL, cfile);
+ tokbuf[tokix++] = 'D';
+ type = 'c';
+ } else
+ type = 'D';
goto no_arrays;
case TEXT:
type = 't';
@@ -1462,6 +1559,13 @@ int parse_option_code_definition (cfile, option)
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", s);
+ skip_to_semi (cfile);
+ return 0;
+ }
if (strlen (val) + tokix + 2 > sizeof (tokbuf))
goto toobig;
tokbuf [tokix++] = 'E';
@@ -1535,20 +1639,16 @@ int parse_option_code_definition (cfile, option)
"Arrays of encapsulations don't make sense.");
return 0;
}
- if (has_encapsulation && tokbuf [0] == 'E')
- has_encapsulation = 0;
- s = dmalloc (tokix +
- (arrayp ? 1 : 0) +
- (has_encapsulation ? 1 : 0) + 1, MDL);
- if (!s)
- log_fatal ("no memory for option format.");
- if (has_encapsulation)
- s [0] = 'e';
- memcpy (s + has_encapsulation, tokbuf, tokix);
- tokix += has_encapsulation;
- if (arrayp)
- s [tokix++] = (arrayp > recordp) ? 'a' : 'A';
- s [tokix] = 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;
@@ -1566,6 +1666,16 @@ int parse_option_code_definition (cfile, option)
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;
}
@@ -3633,6 +3743,8 @@ int parse_non_binary (expr, cfile, lose, context)
if (!strcasecmp (val, "a"))
u = T_A;
+ else if (!strcasecmp (val, "aaaa"))
+ u = T_AAAA;
else if (!strcasecmp (val, "ptr"))
u = T_PTR;
else if (!strcasecmp (val, "mx"))
@@ -3827,6 +3939,8 @@ int parse_non_binary (expr, cfile, lose, context)
(*expr) -> data.ns_add.rrtype = atoi (val);
else if (!strcasecmp (val, "a"))
(*expr) -> data.ns_add.rrtype = T_A;
+ else if (!strcasecmp (val, "aaaa"))
+ (*expr) -> data.ns_add.rrtype = T_AAAA;
else if (!strcasecmp (val, "ptr"))
(*expr) -> data.ns_add.rrtype = T_PTR;
else if (!strcasecmp (val, "mx"))
@@ -4766,7 +4880,7 @@ int parse_option_token (rv, cfile, fmt, expr, uniform, lookups)
unsigned len;
unsigned char *ob;
struct iaddr addr;
- int num;
+ int num, compress;
const char *f, *g;
struct enumeration_value *e;
@@ -4824,7 +4938,14 @@ int parse_option_token (rv, cfile, fmt, expr, uniform, lookups)
break;
case 'D': /* Domain list... */
- t = parse_domain_list(cfile);
+ 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')
@@ -4898,6 +5019,15 @@ int parse_option_token (rv, cfile, fmt, expr, uniform, lookups)
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);
@@ -5003,10 +5133,13 @@ int parse_option_decl (oc, cfile)
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;
@@ -5058,6 +5191,38 @@ int parse_option_decl (oc, cfile)
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;
fmt = strchr (fmt, '.');
@@ -5084,6 +5249,13 @@ int parse_option_decl (oc, cfile)
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;
@@ -5204,6 +5376,8 @@ int parse_option_decl (oc, cfile)
return 1;
parse_exit:
+ if (express != NULL)
+ expression_dereference(&express, MDL);
skip_to_semi (cfile);
exit:
option_dereference(&option, MDL);
@@ -5319,8 +5493,7 @@ int parse_warn (struct parse *cfile, const char *fmt, ...)
}
struct expression *
-parse_domain_list (cfile)
- struct parse *cfile;
+parse_domain_list(struct parse *cfile, int compress)
{
const char *val;
enum dhcp_token token = SEMI;
@@ -5348,16 +5521,48 @@ parse_domain_list (cfile)
return NULL;
}
- result = MRns_name_compress(val, compbuf + clen,
- sizeof(compbuf) - clen,
- dnptrs, lastdnptr);
+ /* 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;
+ }
- 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++;
}
- clen += result;
+ if (clen > sizeof(compbuf))
+ log_fatal("Impossible error at %s:%d", MDL);
token = peek_token(&val, NULL, cfile);
} while (token == COMMA);
diff --git a/common/print.c b/common/print.c
index f8251777..e00e2d1c 100644
--- a/common/print.c
+++ b/common/print.c
@@ -34,7 +34,7 @@
#ifndef lint
static char copyright[] =
-"$Id: print.c,v 1.63 2007/01/29 10:25:54 shane Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
+"$Id: print.c,v 1.64 2007/05/08 23:05:20 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
@@ -1295,6 +1295,9 @@ void print_dns_status (int status, ns_updque *uq)
case T_A:
en = "A";
break;
+ case T_AAAA:
+ en = "AAAA";
+ break;
case T_PTR:
en = "PTR";
break;
diff --git a/common/socket.c b/common/socket.c
index 1700612f..91ee49de 100644
--- a/common/socket.c
+++ b/common/socket.c
@@ -3,7 +3,7 @@
BSD socket interface code... */
/*
- * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004-2006 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
@@ -42,7 +42,8 @@
#ifndef lint
static char copyright[] =
-"$Id: socket.c,v 1.59 2006/07/25 17:41:18 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n";
+"$Id: socket.c,v 1.60 2007/05/08 23:05:20 dhankins Exp $ "
+"Copyright (c) 2004-2006 Internet Systems Consortium.\n";
#endif /* not lint */
#include "dhcpd.h"
@@ -90,62 +91,89 @@ void if_reinitialize_receive (info)
defined (USE_SOCKET_RECEIVE) || \
defined (USE_SOCKET_FALLBACK)
/* Generic interface registration routine... */
-int if_register_socket (info)
- struct interface_info *info;
-{
- struct sockaddr_in name;
+int
+if_register_socket(struct interface_info *info, int family, int do_multicast) {
+ struct sockaddr_storage name;
+ int name_len;
int sock;
int flag;
+ int domain;
+
+ /* INSIST((family == AF_INET) || (family == AF_INET6)); */
#if !defined (HAVE_SO_BINDTODEVICE) && !defined (USE_FALLBACK)
/* Make sure only one interface is registered. */
- if (once)
+ if (once) {
log_fatal ("The standard socket API can only support %s",
"hosts with a single network interface.");
+ }
once = 1;
#endif
- memset (&name, 0, sizeof (name));
- /* Set up the address we're going to bind to. */
- name.sin_family = AF_INET;
- name.sin_port = local_port;
- name.sin_addr = local_address;
+ /*
+ * Set up the address we're going to bind to, depending on the
+ * address family.
+ */
+ memset(&name, 0, sizeof(name));
+ if (family == AF_INET) {
+ struct sockaddr_in *addr = (struct sockaddr_in *)&name;
+ addr->sin_family = AF_INET;
+ addr->sin_port = local_port;
+ memcpy(&addr->sin_addr,
+ &local_address,
+ sizeof(addr->sin_addr));
+ name_len = sizeof(*addr);
+ domain = PF_INET;
+ } else {
+ struct sockaddr_in6 *addr = (struct sockaddr_in6 *)&name;
+ addr->sin6_family = AF_INET6;
+ addr->sin6_port = local_port;
+ memcpy(&addr->sin6_addr,
+ &local_address6,
+ sizeof(addr->sin6_addr));
+ name_len = sizeof(*addr);
+ domain = PF_INET6;
+ }
/* Make a socket... */
- if ((sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
- log_fatal ("Can't create dhcp socket: %m");
+ 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");
+ 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 (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");
+ }
/* Bind the socket to this interface's IP address. */
- if (bind (sock, (struct sockaddr *)&name, sizeof name) < 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 (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 (HAVE_SO_BINDTODEVICE)
/* Bind this socket to this interface. */
- if (info -> ifp &&
- setsockopt (sock, SOL_SOCKET, SO_BINDTODEVICE,
- (char *)(info -> ifp), sizeof *(info -> ifp)) < 0) {
- log_fatal ("setsockopt: SO_BINDTODEVICE: %m");
+ if (info->ifp &&
+ setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE,
+ (char *)(info -> ifp), sizeof(*(info -> ifp))) < 0) {
+ log_fatal("setsockopt: SO_BINDTODEVICE: %m");
}
#endif
@@ -157,12 +185,63 @@ int if_register_socket (info)
* releases.
*/
#if defined(SCO) && defined(IP_BROADCAST_IF)
- if (setsockopt (sock, IPPROTO_IP, IP_BROADCAST_IF,
- &info -> primary_address,
- sizeof (info -> primary_address)) < 0)
- log_fatal ("Can't set IP_BROADCAST_IF on dhcp socket: %m");
+ 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 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
+ }
+
+ if ((family == AF_INET6) && do_multicast) {
+ struct ipv6_mreq mreq;
+
+ /*
+ * Join the DHCPv6 multicast groups so we will receive
+ * multicast messages.
+ */
+ 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");
+ }
+ 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");
+ }
+ }
+
return sock;
}
#endif /* USE_SOCKET_SEND || USE_SOCKET_RECEIVE || USE_SOCKET_FALLBACK */
@@ -172,7 +251,7 @@ void if_register_send (info)
struct interface_info *info;
{
#ifndef USE_SOCKET_RECEIVE
- info -> wfdesc = if_register_socket (info);
+ info -> wfdesc = if_register_socket (info, AF_INET, 0);
#if defined (USE_SOCKET_FALLBACK)
/* Fallback only registers for send, but may need to receive as
well. */
@@ -214,7 +293,7 @@ void if_register_receive (info)
{
/* 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);
+ info -> rfdesc = if_register_socket (info, AF_INET, 0);
if (!quiet_interface_discovery)
log_info ("Listening on Socket/%s%s%s",
info -> name,
@@ -238,6 +317,49 @@ void if_deregister_receive (info)
}
#endif /* USE_SOCKET_RECEIVE */
+
+void
+if_register6(struct interface_info *info, int do_multicast) {
+ info->rfdesc = if_register_socket(info, AF_INET6, do_multicast);
+ info->wfdesc = info->rfdesc;
+ get_hw_addr(info->name, &info->hw_address);
+ if (!quiet_interface_discovery) {
+ if (info->shared_network != NULL) {
+ log_info("Listening on Socket/%s/%s", info->name,
+ info->shared_network->name);
+ log_info("Sending on Socket/%s/%s", 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) {
+ /*
+ * XXX: it would be nice to check for >= 0, but we need to change
+ * interface_allocate() to set the file descriptors for that.
+ */
+ close(info->rfdesc);
+ info->rfdesc = -1;
+ close(info->wfdesc);
+ info->wfdesc = -1;
+
+ 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 defined (USE_SOCKET_SEND) || defined (USE_SOCKET_FALLBACK)
ssize_t send_packet (interface, packet, raw, len, from, to, hto)
struct interface_info *interface;
@@ -270,8 +392,86 @@ ssize_t send_packet (interface, packet, raw, len, from, to, hto)
}
return result;
}
+
#endif /* USE_SOCKET_SEND || USE_SOCKET_FALLBACK */
+/*
+ * For both send_packet6() and receive_packet6() we need to use the
+ * sendmsg()/recvmsg() functions rather than the simplier 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,
+ struct dhcp_packet *raw,
+ size_t len,
+ struct sockaddr_in6 *to) {
+ struct msghdr m;
+ struct iovec v;
+ int result;
+ struct in6_pktinfo *pktinfo;
+ char pbuf[CMSG_SPACE(sizeof(struct in6_pktinfo))];
+ struct cmsghdr *cmsg;
+
+ /*
+ * Initialize our message header structure.
+ */
+ memset(&m, 0, sizeof(m));
+
+ /*
+ * Set the target address we're sending to.
+ */
+ m.msg_name = to;
+ m.msg_namelen = sizeof(*to);
+
+ /*
+ * 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 = pbuf;
+ m.msg_controllen = sizeof(pbuf);
+ cmsg = CMSG_FIRSTHDR(&m);
+ 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_ifindex = if_nametoindex(interface->name);
+ m.msg_controllen = cmsg->cmsg_len;
+
+ result = sendmsg(interface->wfdesc, &m, 0);
+ if (result < 0) {
+ log_error("send_packet6: %m");
+ }
+ return result;
+}
+
#ifdef USE_SOCKET_RECEIVE
ssize_t receive_packet (interface, buf, len, from, hfrom)
struct interface_info *interface;
@@ -283,6 +483,13 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
SOCKLEN_T flen = sizeof *from;
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 {
@@ -297,8 +504,84 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
#endif
return result;
}
+
#endif /* USE_SOCKET_RECEIVE */
+ssize_t
+receive_packet6(struct interface_info *interface,
+ unsigned char *buf, size_t len,
+ struct sockaddr_in6 *from, struct in6_addr *to_addr) {
+ struct msghdr m;
+ struct iovec v;
+ char pbuf[CMSG_SPACE(sizeof(struct in6_pktinfo))];
+ int result;
+ struct cmsghdr *cmsg;
+ struct in6_pktinfo *pktinfo;
+ int found_to_addr;
+
+ /*
+ * 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 = pbuf;
+ m.msg_controllen = sizeof(pbuf);
+
+ 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.
+ *
+ * We also keep a flag to see if we found it. If we
+ * didn't, then we consider this to be an error.
+ */
+ found_to_addr = 0;
+ 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;
+ found_to_addr = 1;
+ }
+ cmsg = CMSG_NXTHDR(&m, cmsg);
+ }
+ if (!found_to_addr) {
+ result = -1;
+ errno = EIO;
+ }
+ }
+
+ return result;
+}
+
#if defined (USE_SOCKET_FALLBACK)
/* This just reads in a packet and silently discards it. */
@@ -364,7 +647,7 @@ void maybe_setup_fallback ()
isc_result_t status;
struct interface_info *fbi = (struct interface_info *)0;
if (setup_fallback (&fbi, MDL)) {
- fbi -> wfdesc = if_register_socket (fbi);
+ fbi -> wfdesc = if_register_socket (fbi, AF_INET, 0);
fbi -> rfdesc = fbi -> wfdesc;
log_info ("Sending on Socket/%s%s%s",
fbi -> name,
diff --git a/common/tables.c b/common/tables.c
index 9740257c..fb8efe3b 100644
--- a/common/tables.c
+++ b/common/tables.c
@@ -34,7 +34,7 @@
#ifndef lint
static char copyright[] =
-"$Id: tables.c,v 1.61 2007/01/29 10:25:54 shane Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
+"$Id: tables.c,v 1.62 2007/05/08 23:05:20 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
@@ -55,7 +55,8 @@ HASH_FUNCTIONS (option_code, const unsigned *, struct option,
Format codes:
- I - IP address
+ I - IPv4 address
+ 6 - IPv6 address
l - 32-bit signed integer
L - 32-bit unsigned integer
s - 16-bit signed integer
@@ -95,6 +96,7 @@ HASH_FUNCTIONS (option_code, const unsigned *, struct option,
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.
*/
struct universe dhcp_universe;
@@ -184,21 +186,27 @@ static struct option dhcp_options[] = {
{ "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 },
+
+ { "client-last-transaction-time", "L", &dhcp_universe, 91, 1 },
+ { "associated-ip", "Ia", &dhcp_universe, 92, 1 },
#if 0
/* Not defined by RFC yet */
{ "pxe-system-type", "S", &dhcp_universe, 93, 1 },
{ "pxe-interface-id", "BBB", &dhcp_universe, 94, 1 },
{ "pxe-client-id", "BX", &dhcp_universe, 97, 1 },
#endif
- { "client-last-transaction-time", "L", &dhcp_universe, 91, 1 },
- { "associated-ip", "Ia", &dhcp_universe, 92, 1 },
{ "uap-servers", "t", &dhcp_universe, 98, 1 },
{ "netinfo-server-address", "Ia", &dhcp_universe, 112, 1 },
{ "netinfo-server-tag", "t", &dhcp_universe, 113, 1 },
{ "default-url", "t", &dhcp_universe, 114, 1 },
{ "subnet-selection", "I", &dhcp_universe, 118, 1 },
- { "domain-search", "D", &dhcp_universe, 119, 1 },
+ { "domain-search", "Dc", &dhcp_universe, 119, 1 },
{ "vivco", "Evendor-class.", &dhcp_universe, 124, 1 },
{ "vivso", "Evendor.", &dhcp_universe, 125, 1 },
#if 0
@@ -256,9 +264,13 @@ static struct option nwip_options[] = {
* 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 },
@@ -290,6 +302,221 @@ static struct option isc_options [] = {
{ 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", "Nduid-types.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. */
+#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.t", &dhcpv6_universe, 13, 1 },
+ { "rapid-commit", "", &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", "", &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. */
+
+ /* Not yet considering for inclusion. */
+#if 0
+ /* RFC-ietf-geopriv-dhcp-civil-09.txt */
+
+ { "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 globlaly 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 },
+ { 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. */
+ { 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. */
+ { NULL, 0 }
+};
+
+struct enumeration dhcpv6_status_codes = {
+ NULL,
+ "status-codes", 2,
+ dhcpv6_status_code_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 },
+ { "RELY-REPL", 13 },
+ { NULL, 0 }
+};
+
+/* Some code refers to a different table. */
+char *dhcpv6_type_names[] = {
+ NULL,
+ "Solicit",
+ "Advertise",
+ "Request",
+ "Confirm",
+ "Renew",
+ "Rebind",
+ "Reply",
+ "Release",
+ "Decline",
+ "Reconfigure",
+ "Information-request",
+ "Relay-forward",
+ "Relay-reply"
+};
+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 },
+ { NULL, NULL, NULL, 0, 0 }
+};
+
const char *hardware_types [] = {
"unknown-0",
"ethernet",
@@ -623,8 +850,10 @@ option_dereference(struct option **dest, const char *file, int line)
/* It's either a user-configured format (allocated), or the
* default static format.
*/
- if ((*dest)->format != default_option_format)
+ if (((*dest)->format != NULL) &&
+ ((*dest)->format != default_option_format)) {
dfree((*dest)->format, file, line);
+ }
dfree(*dest, file, line);
}
@@ -638,15 +867,41 @@ void initialize_common_option_spaces()
unsigned code;
int i;
- universe_max = 10;
- universes = ((struct universe **)
- dmalloc (universe_max * sizeof (struct universe *), MDL));
- if (!universes)
- log_fatal ("Can't allocate option space table.");
- memset (universes, 0, universe_max * sizeof (struct universe *));
+ /* 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-configged 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;
@@ -686,6 +941,7 @@ void initialize_common_option_spaces()
/* 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;
@@ -730,6 +986,7 @@ void initialize_common_option_spaces()
/* 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;
@@ -776,6 +1033,7 @@ void initialize_common_option_spaces()
* 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;
@@ -821,6 +1079,7 @@ void initialize_common_option_spaces()
/* 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;
@@ -866,6 +1125,7 @@ void initialize_common_option_spaces()
/* 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;
@@ -908,7 +1168,163 @@ void initialize_common_option_spaces()
option_code_hash_report(isc_universe.code_hash));
#endif
- /* Set up the hash of universes. */
+ /* 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 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;
+ /* 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;
+ /* 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.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);
@@ -922,4 +1338,16 @@ void initialize_common_option_spaces()
&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);
+/* This should not be neccessary. Listing here just for consistency.
+ * universe_hash_add(universe_hash, fqdn6_universe.name, 0,
+ * &fqdn6_universe, MDL);
+ */
}
diff --git a/common/tree.c b/common/tree.c
index 8f2b7879..ab2015e6 100644
--- a/common/tree.c
+++ b/common/tree.c
@@ -34,7 +34,7 @@
#ifndef lint
static char copyright[] =
-"$Id: tree.c,v 1.112 2007/01/29 10:25:54 shane Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
+"$Id: tree.c,v 1.113 2007/05/08 23:05:20 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
@@ -106,6 +106,97 @@ data_string_to_char_string(struct data_string *d)
return str;
}
+#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(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);
+ /* 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);
+ vsprintf(tmp_buffer->data + cur_strlen, fmt, 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;
+ }
+ va_end(args);
+ return 1;
+}
+
pair cons (car, cdr)
caddr_t car;
pair cdr;
@@ -2774,7 +2865,7 @@ int evaluate_option_cache (result, packet, lease, client_state,
const char *file;
int line;
{
- if (oc -> data.len) {
+ if (oc->data.data != NULL) {
data_string_copy (result, &oc -> data, file, line);
return 1;
}
diff --git a/dhcpctl/omshell.c b/dhcpctl/omshell.c
index ffbde434..1107eeff 100644
--- a/dhcpctl/omshell.c
+++ b/dhcpctl/omshell.c
@@ -34,7 +34,7 @@
#ifndef lint
static char copyright[] =
-"$Id: omshell.c,v 1.12 2007/01/29 10:25:55 shane Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
+"$Id: omshell.c,v 1.13 2007/05/08 23:05:20 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
#endif /* not lint */
#include <time.h>
@@ -58,6 +58,10 @@ int parse_allow_deny (struct option_cache **oc, struct parse *cfile, int flag)
}
void dhcp (struct packet *packet) { }
void bootp (struct packet *packet) { }
+
+/* XXX: should we warn or something here? */
+void dhcpv6(struct packet *packet) { }
+
int check_collection (struct packet *p, struct lease *l, struct collection *c)
{
return 0;
@@ -76,8 +80,8 @@ static void check (isc_result_t status, const char *func) {
}
}
-int main (int argc, char **argv, char **envp)
-{
+int
+main(int argc, char **argv) {
isc_result_t status, waitstatus;
dhcpctl_handle connection;
dhcpctl_handle authenticator;
diff --git a/doc/Makefile b/doc/Makefile
new file mode 100644
index 00000000..03b27dcf
--- /dev/null
+++ b/doc/Makefile
@@ -0,0 +1,29 @@
+# Copyright (c) 2004-2006 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
+# <info@isc.org>
+# http://www.isc.org/
+
+all: References.txt References.html
+
+References.txt: References.xml
+ xml2txt References.xml
+
+References.html: References.xml
+ xml2html References.xml
+
diff --git a/doc/References.html b/doc/References.html
new file mode 100644
index 00000000..8f8a6814
--- /dev/null
+++ b/doc/References.html
@@ -0,0 +1,804 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html lang="en"><head><title>ISC-DHCP-REFERENCES: ISC DHCP References Collection</title>
+<meta http-equiv="Expires" content="Fri, 13 Apr 2007 18:37:11 +0000">
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<meta name="description" content="ISC DHCP References Collection">
+<meta name="keywords" content="ISC, DHCP, Reference Implementation">
+<meta name="generator" content="xml2rfc v1.30 (http://xml.resource.org/)">
+<style type='text/css'>
+<!--
+ body {
+ font-family: verdana, charcoal, helvetica, arial, sans-serif;
+ margin: 2em;
+ font-size: small ; color: #000000 ; background-color: #ffffff ; }
+ .title { color: #990000; font-size: x-large ;
+ font-weight: bold; text-align: right;
+ font-family: helvetica, monaco, "MS Sans Serif", arial, sans-serif;
+ background-color: transparent; }
+ .filename { color: #666666; font-size: 18px; line-height: 28px;
+ font-weight: bold; text-align: right;
+ font-family: helvetica, arial, sans-serif;
+ background-color: transparent; }
+ td.rfcbug { background-color: #000000 ; width: 30px ; height: 30px ;
+ text-align: justify; vertical-align: middle ; padding-top: 2px ; }
+ td.rfcbug span.RFC { color: #666666; font-weight: bold; text-decoration: none;
+ background-color: #000000 ;
+ font-family: monaco, charcoal, geneva, "MS Sans Serif", helvetica, verdana, sans-serif;
+ font-size: x-small ; }
+ td.rfcbug span.hotText { color: #ffffff; font-weight: normal; text-decoration: none;
+ text-align: center ;
+ font-family: charcoal, monaco, geneva, "MS Sans Serif", helvetica, verdana, sans-serif;
+ font-size: x-small ; background-color: #000000; }
+ /* info code from SantaKlauss at http://www.madaboutstyle.com/tooltip2.html */
+ div#counter{margin-top: 100px}
+
+ a.info{
+ position:relative; /*this is the key*/
+ z-index:24;
+ text-decoration:none}
+
+ a.info:hover{z-index:25; background-color:#990000 ; color: #ffffff ;}
+
+ a.info span{display: none}
+
+ a.info:hover span.info{ /*the span will display just on :hover state*/
+ display:block;
+ position:absolute;
+ font-size: smaller ;
+ top:2em; left:2em; width:15em;
+ padding: 2px ;
+ border:1px solid #333333;
+ background-color:#eeeeee; color:#990000;
+ text-align: left ;}
+
+ A { font-weight: bold; }
+ A:link { color: #990000; background-color: transparent ; }
+ A:visited { color: #333333; background-color: transparent ; }
+ A:active { color: #333333; background-color: transparent ; }
+
+ p { margin-left: 2em; margin-right: 2em; }
+ p.copyright { font-size: x-small ; }
+ p.toc { font-size: small ; font-weight: bold ; margin-left: 3em ;}
+ table.toc { margin: 0 0 0 3em; padding: 0; border: 0; vertical-align: text-top; }
+ td.toc { font-size: small; font-weight: bold; vertical-align: text-top; }
+
+ span.emph { font-style: italic; }
+ span.strong { font-weight: bold; }
+ span.verb, span.vbare { font-family: "Courier New", Courier, monospace ; }
+
+ span.vemph { font-style: italic; font-family: "Courier New", Courier, monospace ; }
+ span.vstrong { font-weight: bold; font-family: "Courier New", Courier, monospace ; }
+ span.vdeluxe { font-weight: bold; font-style: italic; font-family: "Courier New", Courier, monospace ; }
+
+ ol.text { margin-left: 2em; margin-right: 2em; }
+ ul.text { margin-left: 2em; margin-right: 2em; }
+ li { margin-left: 3em; }
+
+ pre { margin-left: 3em; color: #333333; background-color: transparent;
+ font-family: "Courier New", Courier, monospace ; font-size: small ;
+ text-align: left;
+ }
+
+ h3 { color: #333333; font-size: medium ;
+ font-family: helvetica, arial, sans-serif ;
+ background-color: transparent; }
+ h4 { font-size: small; font-family: helvetica, arial, sans-serif ; }
+
+ table.bug { width: 30px ; height: 15px ; }
+ td.bug { color: #ffffff ; background-color: #990000 ;
+ text-align: center ; width: 30px ; height: 15px ;
+ }
+ td.bug A.link2 { color: #ffffff ; font-weight: bold;
+ text-decoration: none;
+ font-family: monaco, charcoal, geneva, "MS Sans Serif", helvetica, sans-serif;
+ font-size: x-small ; background-color: transparent }
+
+ td.header { color: #ffffff; font-size: x-small ;
+ font-family: arial, helvetica, sans-serif; vertical-align: top;
+ background-color: #666666 ; width: 33% ; }
+ td.author { font-weight: bold; margin-left: 4em; font-size: x-small ; }
+ td.author-text { font-size: x-small; }
+ table.full { vertical-align: top ; border-collapse: collapse ;
+ border-style: solid solid solid solid ;
+ border-color: black black black black ;
+ font-size: small ; text-align: center ; }
+ table.headers, table.none { vertical-align: top ; border-collapse: collapse ;
+ border-style: none;
+ font-size: small ; text-align: center ; }
+ table.full th { font-weight: bold ;
+ border-style: solid ;
+ border-color: black black black black ; }
+ table.headers th { font-weight: bold ;
+ border-style: none none solid none;
+ border-color: black black black black ; }
+ table.none th { font-weight: bold ;
+ border-style: none; }
+ table.full td {
+ border-style: solid solid solid solid ;
+ border-color: #333333 #333333 #333333 #333333 ; }
+ table.headers td, table.none td { border-style: none; }
+
+ hr { height: 1px }
+-->
+</style>
+</head>
+<body>
+<table summary="layout" cellpadding="0" cellspacing="2" class="bug" align="right"><tr><td class="bug"><a href="#toc" class="link2">&nbsp;TOC&nbsp;</a></td></tr></table>
+<table summary="layout" width="66%" border="0" cellpadding="0" cellspacing="0"><tr><td><table summary="layout" width="100%" border="0" cellpadding="2" cellspacing="1">
+<tr><td class="header">ISC-DHCP-REFERENCES</td><td class="header">D. Hankins</td></tr>
+<tr><td class="header">&nbsp;</td><td class="header">ISC</td></tr>
+<tr><td class="header">&nbsp;</td><td class="header">August 2006</td></tr>
+</table></td></tr></table>
+<div align="right"><span class="title"><br />ISC DHCP References Collection</span></div>
+
+<h3>Copyright Notice</h3>
+
+<p>Copyright (c) 2006-2007 by Internet Systems Consortium, Inc.
+ ("ISC")
+</p>
+<p>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.
+</p>
+<p>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.
+</p>
+<h3>Abstract</h3>
+
+<p>This document describes a collection of Reference material that
+ ISC DHCP has been implemented to.
+</p><a name="toc"></a><br /><hr />
+<h3>Table of Contents</h3>
+<p class="toc">
+<a href="#anchor1">1.</a>&nbsp;
+Introduction<br />
+<br />
+<a href="#anchor2">2.</a>&nbsp;
+Definition: Reference Implementation<br />
+<br />
+<a href="#anchor3">3.</a>&nbsp;
+Low Layer References<br />
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#anchor4">3.1.</a>&nbsp;
+Ethernet Protocol References<br />
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#anchor5">3.2.</a>&nbsp;
+Token Ring Protocol References<br />
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#anchor6">3.3.</a>&nbsp;
+FDDI Protocol References<br />
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#anchor7">3.4.</a>&nbsp;
+Internet Protocol Version 4 References<br />
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#anchor8">3.5.</a>&nbsp;
+Unicast Datagram Protocol References<br />
+<br />
+<a href="#anchor9">4.</a>&nbsp;
+BOOTP Protocol References<br />
+<br />
+<a href="#anchor10">5.</a>&nbsp;
+DHCP Protocol References<br />
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#anchor11">5.1.</a>&nbsp;
+DHCPv4 Protocol<br />
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#anchor12">5.1.1.</a>&nbsp;
+Core Protocol References<br />
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#anchor13">5.2.</a>&nbsp;
+DHCPv6 Protocol References<br />
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#anchor14">5.3.</a>&nbsp;
+DHCP Option References<br />
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#anchor15">5.3.1.</a>&nbsp;
+Relay Agent Information Option Options<br />
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#anchor16">5.3.2.</a>&nbsp;
+Dynamic DNS Updates References<br />
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#anchor17">5.3.3.</a>&nbsp;
+Experimental: Failover References<br />
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#anchor18">5.4.</a>&nbsp;
+DHCP Procedures<br />
+<br />
+<a href="#rfc.references1">6.</a>&nbsp;
+References<br />
+<br />
+<a href="#rfc.authors">&#167;</a>&nbsp;
+Author's Address<br />
+</p>
+<br clear="all" />
+
+<a name="anchor1"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="bug" align="right"><tr><td class="bug"><a href="#toc" class="link2">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.1"></a><h3>1.&nbsp;Introduction</h3>
+
+<p>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).
+</p>
+<p>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.
+</p>
+<a name="anchor2"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="bug" align="right"><tr><td class="bug"><a href="#toc" class="link2">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.2"></a><h3>2.&nbsp;Definition: Reference Implementation</h3>
+
+<p>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.
+</p>
+<p>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."
+</p>
+<p>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.
+</p>
+<p>Anything else is a bug, the only question is whether the
+ bug is in reference or software (failing to implement the
+ reference).
+</p>
+<p>This means:
+</p>
+<ul class="text">
+<li>To produce new externally-visible behaviour, one must first
+ provide a reference.
+</li>
+<li>Before changing externally visible behaviour to work around
+ simple incompatibilities in any other implementation, one must
+ first provide a reference.
+</li>
+</ul>
+<p>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.
+</p>
+<p>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".
+</p>
+<p>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.
+</p>
+<a name="anchor3"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="bug" align="right"><tr><td class="bug"><a href="#toc" class="link2">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.3"></a><h3>3.&nbsp;Low Layer References</h3>
+
+<p>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.
+</p>
+<p>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.
+</p>
+<p>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 <a class="info" href="#RFC2131">RFC2131<span> (</span><span class="info">Droms, R., &ldquo;Dynamic Host Configuration Protocol,&rdquo; March&nbsp;1997.</span><span>)</span></a> [8].
+</p>
+<ol class="text">
+<li>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.
+</li>
+<li>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.
+</li>
+<li>Transmit a UDP packet from IP:Self, Ethernet:Seelf, destined to
+ IP:remote-system LinkLayer:remote-system, without transmitting a
+ single ARP.
+</li>
+<li>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.
+</li>
+</ol>
+<p>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, <a class="info" href="#RFC2131">RFC2131<span> (</span><span class="info">Droms, R., &ldquo;Dynamic Host Configuration Protocol,&rdquo; March&nbsp;1997.</span><span>)</span></a> [8] very explicitly demands that these packets' IP
+ destination addresses be set to 255.255.255.255.
+</p>
+<p>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.
+</p>
+<p>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.
+</p>
+<p>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 the
+ need.
+</p>
+<p>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 absolved. 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.
+</p>
+<a name="anchor4"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="bug" align="right"><tr><td class="bug"><a href="#toc" class="link2">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.3.1"></a><h3>3.1.&nbsp;Ethernet Protocol References</h3>
+
+<p>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 <a class="info" href="#RFC0894">RFC894<span> (</span><span class="info">Hornig, C., &ldquo;Standard for the transmission of IP datagrams over Ethernet networks,&rdquo; April&nbsp;1984.</span><span>)</span></a> [3] (see the section titled "Packet format"), and
+ the following URL is also thought to be useful.
+</p>
+<p>http://en.wikipedia.org/wiki/DIX
+</p>
+<a name="anchor5"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="bug" align="right"><tr><td class="bug"><a href="#toc" class="link2">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.3.2"></a><h3>3.2.&nbsp;Token Ring Protocol References</h3>
+
+<p>IEEE 802.5 defines the Token Ring framing format used by ISC
+ DHCP.
+</p>
+<a name="anchor6"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="bug" align="right"><tr><td class="bug"><a href="#toc" class="link2">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.3.3"></a><h3>3.3.&nbsp;FDDI Protocol References</h3>
+
+<p><a class="info" href="#RFC1188">RFC1188<span> (</span><span class="info">Katz, D., &ldquo;Proposed Standard for the Transmission of IP Datagrams over FDDI Networks,&rdquo; October&nbsp;1990.</span><span>)</span></a> [6] is the most helpful
+ reference ISC DHCP has used to form FDDI packets.
+</p>
+<a name="anchor7"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="bug" align="right"><tr><td class="bug"><a href="#toc" class="link2">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.3.4"></a><h3>3.4.&nbsp;Internet Protocol Version 4 References</h3>
+
+<p><a class="info" href="#RFC0760">RFC760<span> (</span><span class="info">Postel, J., &ldquo;DoD standard Internet Protocol,&rdquo; January&nbsp;1980.</span><span>)</span></a> [1] fundamentally defines the
+ bare IPv4 protocol which ISC DHCP implements.
+</p>
+<a name="anchor8"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="bug" align="right"><tr><td class="bug"><a href="#toc" class="link2">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.3.5"></a><h3>3.5.&nbsp;Unicast Datagram Protocol References</h3>
+
+<p><a class="info" href="#RFC0768">RFC768<span> (</span><span class="info">Postel, J., &ldquo;User Datagram Protocol,&rdquo; August&nbsp;1980.</span><span>)</span></a> [2] 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.
+</p>
+<a name="anchor9"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="bug" align="right"><tr><td class="bug"><a href="#toc" class="link2">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.4"></a><h3>4.&nbsp;BOOTP Protocol References</h3>
+
+<p>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.
+</p>
+<p>The ISC DHCP server supports BOOTP clients conforming to
+ <a class="info" href="#RFC0951">RFC951<span> (</span><span class="info">Croft, B. and J. Gilmore, &ldquo;Bootstrap Protocol,&rdquo; September&nbsp;1985.</span><span>)</span></a> [4] and <a class="info" href="#RFC1542">RFC1542<span> (</span><span class="info">Wimer, W., &ldquo;Clarifications and Extensions for the Bootstrap Protocol,&rdquo; October&nbsp;1993.</span><span>)</span></a> [7].
+</p>
+<a name="anchor10"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="bug" align="right"><tr><td class="bug"><a href="#toc" class="link2">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.5"></a><h3>5.&nbsp;DHCP Protocol References</h3>
+
+<a name="anchor11"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="bug" align="right"><tr><td class="bug"><a href="#toc" class="link2">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.5.1"></a><h3>5.1.&nbsp;DHCPv4 Protocol</h3>
+
+<p>"The DHCP[v4] Protocol" is not defined in a single document. The
+ following collection of references of what ISC DHCP terms "The
+ DHCPv4 Protocol".
+</p>
+<a name="anchor12"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="bug" align="right"><tr><td class="bug"><a href="#toc" class="link2">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.5.1.1"></a><h3>5.1.1.&nbsp;Core Protocol References</h3>
+
+<p><a class="info" href="#RFC2131">RFC2131<span> (</span><span class="info">Droms, R., &ldquo;Dynamic Host Configuration Protocol,&rdquo; March&nbsp;1997.</span><span>)</span></a> [8] 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.
+</p>
+<p><a class="info" href="#RFC2132">RFC2132<span> (</span><span class="info">Alexander, S. and R. Droms, &ldquo;DHCP Options and BOOTP Vendor Extensions,&rdquo; March&nbsp;1997.</span><span>)</span></a> [9] 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.
+</p>
+<a name="anchor13"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="bug" align="right"><tr><td class="bug"><a href="#toc" class="link2">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.5.2"></a><h3>5.2.&nbsp;DHCPv6 Protocol References</h3>
+
+<p>For now there is only one document that specifies the DHCPv6
+ protocol (there have been no updates yet), <a class="info" href="#RFC3315">RFC3315<span> (</span><span class="info">Droms, R., Bound, J., Volz, B., Lemon, T., Perkins, C., and M. Carney, &ldquo;Dynamic Host Configuration Protocol for IPv6 (DHCPv6),&rdquo; July&nbsp;2003.</span><span>)</span></a> [21].
+</p>
+<p>Support for DHCPv6 was added first 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.
+</p>
+<p>DHCPv6 introduces some new and uncomfortable ideas to the common
+ software library.
+</p>
+<ol class="text">
+<li>Options of zero length are normal in DHCPv6. Currently, all
+ ISC DHCP software treats zero-length options as errors.
+</li>
+<li>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.
+</li>
+<li>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.
+</li>
+</ol>
+<p>Precisely how to correctly support the above conundrums has not
+ quite yet been settled, so support is incomplete.
+</p>
+<a name="anchor14"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="bug" align="right"><tr><td class="bug"><a href="#toc" class="link2">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.5.3"></a><h3>5.3.&nbsp;DHCP Option References</h3>
+
+<p><a class="info" href="#RFC2241">RFC2241<span> (</span><span class="info">Provan, D., &ldquo;DHCP Options for Novell Directory Services,&rdquo; November&nbsp;1997.</span><span>)</span></a> [10] defines options for
+ Novell Directory Services.
+</p>
+<p><a class="info" href="#RFC2242">RFC2242<span> (</span><span class="info">Droms, R. and K. Fong, &ldquo;NetWare/IP Domain Name and Information,&rdquo; November&nbsp;1997.</span><span>)</span></a> [11] defines an encapsulated
+ option space for NWIP configuration.
+</p>
+<p><a class="info" href="#RFC2485">RFC2485<span> (</span><span class="info">Drach, S., &ldquo;DHCP Option for The Open Group&apos;s User Authentication Protocol,&rdquo; January&nbsp;1999.</span><span>)</span></a> [12] defines the Open Group's
+ UAP option.
+</p>
+<p><a class="info" href="#RFC2610">RFC2610<span> (</span><span class="info">Perkins, C. and E. Guttman, &ldquo;DHCP Options for Service Location Protocol,&rdquo; June&nbsp;1999.</span><span>)</span></a> [13] defines options for
+ the Service Location Protocol (SLP).
+</p>
+<p><a class="info" href="#RFC2937">RFC2937<span> (</span><span class="info">Smith, C., &ldquo;The Name Service Search Option for DHCP,&rdquo; September&nbsp;2000.</span><span>)</span></a> [14] 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.
+</p>
+<p><a class="info" href="#RFC3004">RFC3004<span> (</span><span class="info">Stump, G., Droms, R., Gu, Y., Vyaghrapuri, R., Demirtjis, A., Beser, B., and J. Privat, &ldquo;The User Class Option for DHCP,&rdquo; November&nbsp;2000.</span><span>)</span></a> [16] 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.
+</p>
+<p><a class="info" href="#RFC3011">RFC3011<span> (</span><span class="info">Waters, G., &ldquo;The IPv4 Subnet Selection Option for DHCP,&rdquo; November&nbsp;2000.</span><span>)</span></a> [17] 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.
+</p>
+<p><a class="info" href="#RFC3319">RFC3319<span> (</span><span class="info">Schulzrinne, H. and B. Volz, &ldquo;Dynamic Host Configuration Protocol (DHCPv6) Options for Session Initiation Protocol (SIP) Servers,&rdquo; July&nbsp;2003.</span><span>)</span></a> [22] defines the SIP server
+ options for DHCPv6.
+</p>
+<p><a class="info" href="#RFC3396">RFC3396<span> (</span><span class="info">Lemon, T. and S. Cheshire, &ldquo;Encoding Long Options in the Dynamic Host Configuration Protocol (DHCPv4),&rdquo; November&nbsp;2002.</span><span>)</span></a> [23] 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.
+</p>
+<p><a class="info" href="#RFC3397">RFC3397<span> (</span><span class="info">Aboba, B. and S. Cheshire, &ldquo;Dynamic Host Configuration Protocol (DHCP) Domain Search Option,&rdquo; November&nbsp;2002.</span><span>)</span></a> [24] documents the Domain-Search
+ Option, which allows the configuration of the /etc/resolv.conf
+ 'search' parameter in a way that is <a class="info" href="#RFC1035">RFC1035<span> (</span><span class="info">Mockapetris, P., &ldquo;Domain names - implementation and specification,&rdquo; November&nbsp;1987.</span><span>)</span></a> [5] wire format compatible (in fact, it uses the RFC1035 wire
+ format). ISC DHCP has both client and server support, and supports
+ RFC1035 name compression.
+</p>
+<p><a class="info" href="#RFC3646">RFC3646<span> (</span><span class="info">Droms, R., &ldquo;DNS Configuration options for Dynamic Host Configuration Protocol for IPv6 (DHCPv6),&rdquo; December&nbsp;2003.</span><span>)</span></a> [27] documents the DHCPv6
+ name-servers and domain-search options.
+</p>
+<p><a class="info" href="#RFC3633">RFC3633<span> (</span><span class="info">Troan, O. and R. Droms, &ldquo;IPv6 Prefix Options for Dynamic Host Configuration Protocol (DHCP) version 6,&rdquo; December&nbsp;2003.</span><span>)</span></a> [26] documents the Identity
+ Association Prefix Delegation, which is included here for protocol
+ wire reference, but which is not supported by ISC DHCP.
+</p>
+<p><a class="info" href="#RFC3679">RFC3679<span> (</span><span class="info">Droms, R., &ldquo;Unused Dynamic Host Configuration Protocol (DHCP) Option Codes,&rdquo; January&nbsp;2004.</span><span>)</span></a> [28] documents a number of
+ options that were documented earlier in history, but were not
+ made use of.
+</p>
+<p><a class="info" href="#RFC3898">RFC3898<span> (</span><span class="info">Kalusivalingam, V., &ldquo;Network Information Service (NIS) Configuration Options for Dynamic Host Configuration Protocol for IPv6 (DHCPv6),&rdquo; October&nbsp;2004.</span><span>)</span></a> [29] documents four NIS options
+ for delivering NIS servers and domain information in DHCPv6.
+</p>
+<p><a class="info" href="#RFC3925">RFC3925<span> (</span><span class="info">Littlefield, J., &ldquo;Vendor-Identifying Vendor Options for Dynamic Host Configuration Protocol version 4 (DHCPv4),&rdquo; October&nbsp;2004.</span><span>)</span></a> [30] 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.
+</p>
+<p><a class="info" href="#RFC3942">RFC3942<span> (</span><span class="info">Volz, B., &ldquo;Reclassifying Dynamic Host Configuration Protocol version 4 (DHCPv4) Options,&rdquo; November&nbsp;2004.</span><span>)</span></a> [31] redefined the 'site local'
+ option space.
+</p>
+<p><a class="info" href="#RFC4075">RFC4075<span> (</span><span class="info">Kalusivalingam, V., &ldquo;Simple Network Time Protocol (SNTP) Configuration Option for DHCPv6,&rdquo; May&nbsp;2005.</span><span>)</span></a> [32] defines the DHCPv6 SNTP
+ Servers option.
+</p>
+<p><a class="info" href="#RFC4242">RFC4242<span> (</span><span class="info">Venaas, S., Chown, T., and B. Volz, &ldquo;Information Refresh Time Option for Dynamic Host Configuration Protocol for IPv6 (DHCPv6),&rdquo; November&nbsp;2005.</span><span>)</span></a> [33] defines the Information
+ Refresh Time option, which advises DHCPv6 Information-Request
+ clients to return for updated information.
+</p>
+<p><a class="info" href="#RFC4280">RFC4280<span> (</span><span class="info">Chowdhury, K., Yegani, P., and L. Madour, &ldquo;Dynamic Host Configuration Protocol (DHCP) Options for Broadcast and Multicast Control Servers,&rdquo; November&nbsp;2005.</span><span>)</span></a> [34] defines two BCMS server
+ options.
+</p>
+<p><a class="info" href="#RFC4388">RFC4388<span> (</span><span class="info">Woundy, R. and K. Kinnear, &ldquo;Dynamic Host Configuration Protocol (DHCP) Leasequery,&rdquo; February&nbsp;2006.</span><span>)</span></a> [35] 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.
+</p>
+<p><a class="info" href="#RFC4580">RFC4580><span> (</span><span class="info">Volz, B., &ldquo;Dynamic Host Configuration Protocol for IPv6 (DHCPv6) Relay Agent Subscriber-ID Option,&rdquo; June&nbsp;2006.</span><span>)</span></a> [36] defines a DHCPv6
+ subscriber-id option, which is similar in principle to the DHCPv4
+ relay agent option of the same name.
+</p>
+<p><a class="info" href="#RFC4649">RFC4649<span> (</span><span class="info">Volz, B., &ldquo;Dynamic Host Configuration Protocol for IPv6 (DHCPv6) Relay Agent Remote-ID Option,&rdquo; August&nbsp;2006.</span><span>)</span></a> [37] defines a DHCPv6 remote-id
+ option, which is similar in principle to the DHCPv4 relay agent
+ remote-id.
+</p>
+<a name="anchor15"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="bug" align="right"><tr><td class="bug"><a href="#toc" class="link2">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.5.3.1"></a><h3>5.3.1.&nbsp;Relay Agent Information Option Options</h3>
+
+<p><a class="info" href="#RFC3046">RFC3046<span> (</span><span class="info">Patrick, M., &ldquo;DHCP Relay Agent Information Option,&rdquo; January&nbsp;2001.</span><span>)</span></a> [18] defines the Relay Agent
+ Information Option and provides a number of sub-option
+ definitions.
+</p>
+<p><a class="info" href="#RFC3256">RFC3256<span> (</span><span class="info">Jones, D. and R. Woundy, &ldquo;The DOCSIS (Data-Over-Cable Service Interface Specifications) Device Class DHCP (Dynamic Host Configuration Protocol) Relay Agent Information Sub-option,&rdquo; April&nbsp;2002.</span><span>)</span></a> [20] defines the DOCSIS Device
+ Class sub-option.
+</p>
+<p><a class="info" href="#RFC3527">RFC3527<span> (</span><span class="info">Kinnear, K., Stapp, M., Johnson, R., and J. Kumarasamy, &ldquo;Link Selection sub-option for the Relay Agent Information Option for DHCPv4,&rdquo; April&nbsp;2003.</span><span>)</span></a> [25] defines the Link Selection
+ sub-option.
+</p>
+<a name="anchor16"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="bug" align="right"><tr><td class="bug"><a href="#toc" class="link2">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.5.3.2"></a><h3>5.3.2.&nbsp;Dynamic DNS Updates References</h3>
+
+<p>The collection of documents that describe the standards-based
+ method to update dns names of DHCP clients starts most easily
+ with <a class="info" href="#RFC4703">RFC4703<span> (</span><span class="info">Stapp, M. and B. Volz, &ldquo;Resolution of Fully Qualified Domain Name (FQDN) Conflicts among Dynamic Host Configuration Protocol (DHCP) Clients,&rdquo; October&nbsp;2006.</span><span>)</span></a> [40] to define the overall
+ architecture, travels through RFCs <a class="info" href="#RFC4702">4702<span> (</span><span class="info">Stapp, M., Volz, B., and Y. Rekhter, &ldquo;The Dynamic Host Configuration Protocol (DHCP) Client Fully Qualified Domain Name (FQDN) Option,&rdquo; October&nbsp;2006.</span><span>)</span></a> [39]
+ and <a class="info" href="#RFC4704">4704<span> (</span><span class="info">Volz, B., &ldquo;The Dynamic Host Configuration Protocol for IPv6 (DHCPv6) Client Fully Qualified Domain Name (FQDN) Option,&rdquo; October&nbsp;2006.</span><span>)</span></a> [41] to describe the DHCPv4 and
+ DHCPv6 FQDN options (to carry the client name), and ends up at
+ <a class="info" href="#RFC4701">RFC4701<span> (</span><span class="info">Stapp, M., Lemon, T., and A. Gustafsson, &ldquo;A DNS Resource Record (RR) for Encoding Dynamic Host Configuration Protocol (DHCP) Information (DHCID RR),&rdquo; October&nbsp;2006.</span><span>)</span></a> [38] which describes the DHCID
+ RR used in DNS to perform a kind of atomic locking.
+</p>
+<p>ISC DHCP adoped early versions of these documents, and has not
+ yet synched up with the final standards versions.
+</p>
+<p>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.
+</p>
+<p>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.
+</p>
+<a name="anchor17"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="bug" align="right"><tr><td class="bug"><a href="#toc" class="link2">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.5.3.3"></a><h3>5.3.3.&nbsp;Experimental: Failover References</h3>
+
+<p>The Failover Protocol defines a 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.
+</p>
+<p>Unfortunately it has been quite some years since the last time
+ this document was edited, and the authors no longer show any
+ interest in fielding comments or improving the document.
+</p>
+<p>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.
+</p>
+<p><a class="info" href="#draft-failover">draft-ietf-dhc-failover-12.txt<span> (</span><span class="info">Droms, R., &ldquo;DHCP Failover Protocol,&rdquo; March&nbsp;2003.</span><span>)</span></a> [42]
+ 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.
+</p>
+<p><a class="info" href="#RFC3074">RFC3074<span> (</span><span class="info">Volz, B., Gonczi, S., Lemon, T., and R. Stevens, &ldquo;DHC Load Balancing Algorithm,&rdquo; February&nbsp;2001.</span><span>)</span></a> [19] 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).
+</p>
+<a name="anchor18"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="bug" align="right"><tr><td class="bug"><a href="#toc" class="link2">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.5.4"></a><h3>5.4.&nbsp;DHCP Procedures</h3>
+
+<p><a class="info" href="#RFC2939">RFC2939<span> (</span><span class="info">Droms, R., &ldquo;Procedures and IANA Guidelines for Definition of New DHCP Options and Message Types,&rdquo; September&nbsp;2000.</span><span>)</span></a> [15] explains how to go about
+ obtaining a new DHCP Option code assignment.
+</p>
+<a name="rfc.references1"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="bug" align="right"><tr><td class="bug"><a href="#toc" class="link2">&nbsp;TOC&nbsp;</a></td></tr></table>
+<h3>6.&nbsp;References</h3>
+<table width="99%" border="0">
+<tr><td class="author-text" valign="top"><a name="RFC0760">[1]</a></td>
+<td class="author-text">Postel, J., &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc760.txt">DoD standard Internet Protocol</a>,&rdquo; RFC&nbsp;760, January&nbsp;1980.</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC0768">[2]</a></td>
+<td class="author-text">Postel, J., &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc768.txt">User Datagram Protocol</a>,&rdquo; STD&nbsp;6, RFC&nbsp;768, August&nbsp;1980.</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC0894">[3]</a></td>
+<td class="author-text">Hornig, C., &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc894.txt">Standard for the transmission of IP datagrams over Ethernet networks</a>,&rdquo; STD&nbsp;41, RFC&nbsp;894, April&nbsp;1984.</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC0951">[4]</a></td>
+<td class="author-text">Croft, B. and J. Gilmore, &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc951.txt">Bootstrap Protocol</a>,&rdquo; RFC&nbsp;951, September&nbsp;1985.</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC1035">[5]</a></td>
+<td class="author-text">Mockapetris, P., &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc1035.txt">Domain names - implementation and specification</a>,&rdquo; STD&nbsp;13, RFC&nbsp;1035, November&nbsp;1987.</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC1188">[6]</a></td>
+<td class="author-text"><a href="mailto:dkatz@merit.edu">Katz, D.</a>, &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc1188.txt">Proposed Standard for the Transmission of IP Datagrams over FDDI Networks</a>,&rdquo; RFC&nbsp;1188, October&nbsp;1990.</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC1542">[7]</a></td>
+<td class="author-text"><a href="mailto:Walter.Wimer@CMU.EDU">Wimer, W.</a>, &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc1542.txt">Clarifications and Extensions for the Bootstrap Protocol</a>,&rdquo; RFC&nbsp;1542, October&nbsp;1993.</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC2131">[8]</a></td>
+<td class="author-text"><a href="mailto:droms@bucknell.edu">Droms, R.</a>, &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc2131.txt">Dynamic Host Configuration Protocol</a>,&rdquo; RFC&nbsp;2131, March&nbsp;1997 (<a href="ftp://ftp.isi.edu/in-notes/rfc2131.txt">TXT</a>, <a href="http://xml.resource.org/public/rfc/html/rfc2131.html">HTML</a>, <a href="http://xml.resource.org/public/rfc/xml/rfc2131.xml">XML</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC2132">[9]</a></td>
+<td class="author-text"><a href="mailto:sca@engr.sgi.com">Alexander, S.</a> and <a href="mailto:droms@bucknell.edu">R. Droms</a>, &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc2132.txt">DHCP Options and BOOTP Vendor Extensions</a>,&rdquo; RFC&nbsp;2132, March&nbsp;1997 (<a href="ftp://ftp.isi.edu/in-notes/rfc2132.txt">TXT</a>, <a href="http://xml.resource.org/public/rfc/html/rfc2132.html">HTML</a>, <a href="http://xml.resource.org/public/rfc/xml/rfc2132.xml">XML</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC2241">[10]</a></td>
+<td class="author-text"><a href="mailto:donp@Novell.Com">Provan, D.</a>, &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc2241.txt">DHCP Options for Novell Directory Services</a>,&rdquo; RFC&nbsp;2241, November&nbsp;1997 (<a href="ftp://ftp.isi.edu/in-notes/rfc2241.txt">TXT</a>, <a href="http://xml.resource.org/public/rfc/html/rfc2241.html">HTML</a>, <a href="http://xml.resource.org/public/rfc/xml/rfc2241.xml">XML</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC2242">[11]</a></td>
+<td class="author-text"><a href="mailto:droms@bucknell.edu">Droms, R.</a> and <a href="mailto:kfong@novell.com">K. Fong</a>, &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc2242.txt">NetWare/IP Domain Name and Information</a>,&rdquo; RFC&nbsp;2242, November&nbsp;1997 (<a href="ftp://ftp.isi.edu/in-notes/rfc2242.txt">TXT</a>, <a href="http://xml.resource.org/public/rfc/html/rfc2242.html">HTML</a>, <a href="http://xml.resource.org/public/rfc/xml/rfc2242.xml">XML</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC2485">[12]</a></td>
+<td class="author-text"><a href="mailto:drach@sun.com">Drach, S.</a>, &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc2485.txt">DHCP Option for The Open Group&#039;s User Authentication Protocol</a>,&rdquo; RFC&nbsp;2485, January&nbsp;1999 (<a href="ftp://ftp.isi.edu/in-notes/rfc2485.txt">TXT</a>, <a href="http://xml.resource.org/public/rfc/html/rfc2485.html">HTML</a>, <a href="http://xml.resource.org/public/rfc/xml/rfc2485.xml">XML</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC2610">[13]</a></td>
+<td class="author-text"><a href="mailto:Charles.Perkins@Sun.Com">Perkins, C.</a> and <a href="mailto:Erik.Guttman@Sun.Com">E. Guttman</a>, &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc2610.txt">DHCP Options for Service Location Protocol</a>,&rdquo; RFC&nbsp;2610, June&nbsp;1999.</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC2937">[14]</a></td>
+<td class="author-text">Smith, C., &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc2937.txt">The Name Service Search Option for DHCP</a>,&rdquo; RFC&nbsp;2937, September&nbsp;2000.</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC2939">[15]</a></td>
+<td class="author-text">Droms, R., &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc2939.txt">Procedures and IANA Guidelines for Definition of New DHCP Options and Message Types</a>,&rdquo; BCP&nbsp;43, RFC&nbsp;2939, September&nbsp;2000.</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3004">[16]</a></td>
+<td class="author-text">Stump, G., Droms, R., Gu, Y., Vyaghrapuri, R., Demirtjis, A., Beser, B., and J. Privat, &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc3004.txt">The User Class Option for DHCP</a>,&rdquo; RFC&nbsp;3004, November&nbsp;2000.</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3011">[17]</a></td>
+<td class="author-text">Waters, G., &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc3011.txt">The IPv4 Subnet Selection Option for DHCP</a>,&rdquo; RFC&nbsp;3011, November&nbsp;2000.</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3046">[18]</a></td>
+<td class="author-text">Patrick, M., &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc3046.txt">DHCP Relay Agent Information Option</a>,&rdquo; RFC&nbsp;3046, January&nbsp;2001.</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3074">[19]</a></td>
+<td class="author-text">Volz, B., Gonczi, S., Lemon, T., and R. Stevens, &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc3074.txt">DHC Load Balancing Algorithm</a>,&rdquo; RFC&nbsp;3074, February&nbsp;2001.</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3256">[20]</a></td>
+<td class="author-text">Jones, D. and R. Woundy, &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc3256.txt">The DOCSIS (Data-Over-Cable Service Interface Specifications) Device Class DHCP (Dynamic Host Configuration Protocol) Relay Agent Information Sub-option</a>,&rdquo; RFC&nbsp;3256, April&nbsp;2002.</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3315">[21]</a></td>
+<td class="author-text">Droms, R., Bound, J., Volz, B., Lemon, T., Perkins, C., and M. Carney, &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc3315.txt">Dynamic Host Configuration Protocol for IPv6 (DHCPv6)</a>,&rdquo; RFC&nbsp;3315, July&nbsp;2003.</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3319">[22]</a></td>
+<td class="author-text">Schulzrinne, H. and B. Volz, &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc3319.txt">Dynamic Host Configuration Protocol (DHCPv6) Options for Session Initiation Protocol (SIP) Servers</a>,&rdquo; RFC&nbsp;3319, July&nbsp;2003.</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3396">[23]</a></td>
+<td class="author-text">Lemon, T. and S. Cheshire, &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc3396.txt">Encoding Long Options in the Dynamic Host Configuration Protocol (DHCPv4)</a>,&rdquo; RFC&nbsp;3396, November&nbsp;2002.</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3397">[24]</a></td>
+<td class="author-text">Aboba, B. and S. Cheshire, &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc3397.txt">Dynamic Host Configuration Protocol (DHCP) Domain Search Option</a>,&rdquo; RFC&nbsp;3397, November&nbsp;2002.</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3527">[25]</a></td>
+<td class="author-text">Kinnear, K., Stapp, M., Johnson, R., and J. Kumarasamy, &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc3527.txt">Link Selection sub-option for the Relay Agent Information Option for DHCPv4</a>,&rdquo; RFC&nbsp;3527, April&nbsp;2003.</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3633">[26]</a></td>
+<td class="author-text">Troan, O. and R. Droms, &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc3633.txt">IPv6 Prefix Options for Dynamic Host Configuration Protocol (DHCP) version 6</a>,&rdquo; RFC&nbsp;3633, December&nbsp;2003.</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3646">[27]</a></td>
+<td class="author-text">Droms, R., &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc3646.txt">DNS Configuration options for Dynamic Host Configuration Protocol for IPv6 (DHCPv6)</a>,&rdquo; RFC&nbsp;3646, December&nbsp;2003.</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3679">[28]</a></td>
+<td class="author-text">Droms, R., &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc3679.txt">Unused Dynamic Host Configuration Protocol (DHCP) Option Codes</a>,&rdquo; RFC&nbsp;3679, January&nbsp;2004.</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3898">[29]</a></td>
+<td class="author-text">Kalusivalingam, V., &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc3898.txt">Network Information Service (NIS) Configuration Options for Dynamic Host Configuration Protocol for IPv6 (DHCPv6)</a>,&rdquo; RFC&nbsp;3898, October&nbsp;2004.</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3925">[30]</a></td>
+<td class="author-text">Littlefield, J., &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc3925.txt">Vendor-Identifying Vendor Options for Dynamic Host Configuration Protocol version 4 (DHCPv4)</a>,&rdquo; RFC&nbsp;3925, October&nbsp;2004.</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3942">[31]</a></td>
+<td class="author-text">Volz, B., &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc3942.txt">Reclassifying Dynamic Host Configuration Protocol version 4 (DHCPv4) Options</a>,&rdquo; RFC&nbsp;3942, November&nbsp;2004.</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC4075">[32]</a></td>
+<td class="author-text">Kalusivalingam, V., &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc4075.txt">Simple Network Time Protocol (SNTP) Configuration Option for DHCPv6</a>,&rdquo; RFC&nbsp;4075, May&nbsp;2005.</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC4242">[33]</a></td>
+<td class="author-text">Venaas, S., Chown, T., and B. Volz, &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc4242.txt">Information Refresh Time Option for Dynamic Host Configuration Protocol for IPv6 (DHCPv6)</a>,&rdquo; RFC&nbsp;4242, November&nbsp;2005.</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC4280">[34]</a></td>
+<td class="author-text">Chowdhury, K., Yegani, P., and L. Madour, &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc4280.txt">Dynamic Host Configuration Protocol (DHCP) Options for Broadcast and Multicast Control Servers</a>,&rdquo; RFC&nbsp;4280, November&nbsp;2005.</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC4388">[35]</a></td>
+<td class="author-text">Woundy, R. and K. Kinnear, &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc4388.txt">Dynamic Host Configuration Protocol (DHCP) Leasequery</a>,&rdquo; RFC&nbsp;4388, February&nbsp;2006.</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC4580">[36]</a></td>
+<td class="author-text">Volz, B., &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc4580.txt">Dynamic Host Configuration Protocol for IPv6 (DHCPv6) Relay Agent Subscriber-ID Option</a>,&rdquo; RFC&nbsp;4580, June&nbsp;2006.</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC4649">[37]</a></td>
+<td class="author-text">Volz, B., &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc4649.txt">Dynamic Host Configuration Protocol for IPv6 (DHCPv6) Relay Agent Remote-ID Option</a>,&rdquo; RFC&nbsp;4649, August&nbsp;2006.</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC4701">[38]</a></td>
+<td class="author-text">Stapp, M., Lemon, T., and A. Gustafsson, &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc4701.txt">A DNS Resource Record (RR) for Encoding Dynamic Host Configuration Protocol (DHCP) Information (DHCID RR)</a>,&rdquo; RFC&nbsp;4701, October&nbsp;2006.</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC4702">[39]</a></td>
+<td class="author-text">Stapp, M., Volz, B., and Y. Rekhter, &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc4702.txt">The Dynamic Host Configuration Protocol (DHCP) Client Fully Qualified Domain Name (FQDN) Option</a>,&rdquo; RFC&nbsp;4702, October&nbsp;2006.</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC4703">[40]</a></td>
+<td class="author-text">Stapp, M. and B. Volz, &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc4703.txt">Resolution of Fully Qualified Domain Name (FQDN) Conflicts among Dynamic Host Configuration Protocol (DHCP) Clients</a>,&rdquo; RFC&nbsp;4703, October&nbsp;2006.</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC4704">[41]</a></td>
+<td class="author-text">Volz, B., &ldquo;<a href="ftp://ftp.isi.edu/in-notes/rfc4704.txt">The Dynamic Host Configuration Protocol for IPv6 (DHCPv6) Client Fully Qualified Domain Name (FQDN) Option</a>,&rdquo; RFC&nbsp;4704, October&nbsp;2006.</td></tr>
+<tr><td class="author-text" valign="top"><a name="draft-failover">[42]</a></td>
+<td class="author-text">Droms, R., &ldquo;<a href="http://www.isc.org/sw/dhcp/drafts/draft-ietf-dhc-failover-12.txt">DHCP Failover Protocol</a>,&rdquo; March&nbsp;2003.</td></tr>
+</table>
+
+<a name="rfc.authors"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="bug" align="right"><tr><td class="bug"><a href="#toc" class="link2">&nbsp;TOC&nbsp;</a></td></tr></table>
+<h3>Author's Address</h3>
+<table width="99%" border="0" cellpadding="0" cellspacing="0">
+<tr><td class="author-text">&nbsp;</td>
+<td class="author-text">David W. Hankins</td></tr>
+<tr><td class="author-text">&nbsp;</td>
+<td class="author-text">Internet Systems Consortium,
+ Inc.</td></tr>
+<tr><td class="author-text">&nbsp;</td>
+<td class="author-text">950 Charter Street</td></tr>
+<tr><td class="author-text">&nbsp;</td>
+<td class="author-text">Redwood City, CA 94063</td></tr>
+<tr><td class="author" align="right">Phone:&nbsp;</td>
+<td class="author-text">+1 650 423 1300</td></tr>
+<tr><td class="author" align="right">Email:&nbsp;</td>
+<td class="author-text"><a href="mailto:David_Hankins@isc.org">David_Hankins@isc.org</a></td></tr>
+</table>
+</body></html>
diff --git a/doc/References.txt b/doc/References.txt
new file mode 100644
index 00000000..8e656efe
--- /dev/null
+++ b/doc/References.txt
@@ -0,0 +1,840 @@
+
+
+
+ISC-DHCP-REFERENCES D. Hankins
+ ISC
+ August 2006
+
+
+ ISC DHCP References Collection
+
+
+Copyright Notice
+
+ Copyright (c) 2006-2007 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.
+
+Abstract
+
+ This document describes a collection of Reference material that ISC
+ DHCP has been implemented to.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Hankins [Page 1]
+
+ ISC DHCP References Collection August 2006
+
+
+Table of Contents
+
+ 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 3
+
+ 2. Definition: Reference Implementation . . . . . . . . . . . . . 3
+
+ 3. Low Layer References . . . . . . . . . . . . . . . . . . . . . 4
+ 3.1. Ethernet Protocol References . . . . . . . . . . . . . . . 6
+ 3.2. Token Ring Protocol References . . . . . . . . . . . . . . 6
+ 3.3. FDDI Protocol References . . . . . . . . . . . . . . . . . 6
+ 3.4. Internet Protocol Version 4 References . . . . . . . . . . 6
+ 3.5. Unicast Datagram Protocol References . . . . . . . . . . . 6
+
+ 4. BOOTP Protocol References . . . . . . . . . . . . . . . . . . 6
+
+ 5. DHCP Protocol References . . . . . . . . . . . . . . . . . . . 7
+ 5.1. DHCPv4 Protocol . . . . . . . . . . . . . . . . . . . . . 7
+ 5.1.1. Core Protocol References . . . . . . . . . . . . . . . 7
+ 5.2. DHCPv6 Protocol References . . . . . . . . . . . . . . . . 7
+ 5.3. DHCP Option References . . . . . . . . . . . . . . . . . . 8
+ 5.3.1. Relay Agent Information Option Options . . . . . . . . 10
+ 5.3.2. Dynamic DNS Updates References . . . . . . . . . . . . 10
+ 5.3.3. Experimental: Failover References . . . . . . . . . . 10
+ 5.4. DHCP Procedures . . . . . . . . . . . . . . . . . . . . . 11
+
+ 6. References . . . . . . . . . . . . . . . . . . . . . . . . . . 11
+
+ Author's Address . . . . . . . . . . . . . . . . . . . . . . . . . 15
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Hankins [Page 2]
+
+ ISC DHCP References Collection August 2006
+
+
+1. Introduction
+
+ 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.
+
+
+2. Definition: Reference Implementation
+
+ 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:
+
+
+
+Hankins [Page 3]
+
+ ISC DHCP References Collection August 2006
+
+
+ o To produce new externally-visible behaviour, one must first
+ provide a reference.
+
+ o 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.
+
+
+3. Low Layer References
+
+ 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 RFC2131 [8].
+
+ 1. 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
+
+
+
+Hankins [Page 4]
+
+ ISC DHCP References Collection August 2006
+
+
+ address yet) interface.
+
+ 2. 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.
+
+ 3. Transmit a UDP packet from IP:Self, Ethernet:Seelf, destined to
+ IP:remote-system LinkLayer:remote-system, without transmitting a
+ single ARP.
+
+ 4. 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, RFC2131 [8] 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 the need.
+
+ 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 absolved. In particular, one might
+
+
+
+Hankins [Page 5]
+
+ ISC DHCP References Collection August 2006
+
+
+ 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.
+
+3.1. Ethernet Protocol References
+
+ 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 RFC894 [3] (see the section
+ titled "Packet format"), and the following URL is also thought to be
+ useful.
+
+ http://en.wikipedia.org/wiki/DIX
+
+3.2. Token Ring Protocol References
+
+ IEEE 802.5 defines the Token Ring framing format used by ISC DHCP.
+
+3.3. FDDI Protocol References
+
+ RFC1188 [6] is the most helpful reference ISC DHCP has used to form
+ FDDI packets.
+
+3.4. Internet Protocol Version 4 References
+
+ RFC760 [1] fundamentally defines the bare IPv4 protocol which ISC
+ DHCP implements.
+
+3.5. Unicast Datagram Protocol References
+
+ RFC768 [2] 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.
+
+
+4. BOOTP Protocol References
+
+ 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 [4]
+ and RFC1542 [7].
+
+
+
+
+Hankins [Page 6]
+
+ ISC DHCP References Collection August 2006
+
+
+5. DHCP Protocol References
+
+5.1. DHCPv4 Protocol
+
+ "The DHCP[v4] Protocol" is not defined in a single document. The
+ following collection of references of what ISC DHCP terms "The DHCPv4
+ Protocol".
+
+5.1.1. Core Protocol References
+
+ RFC2131 [8] 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 [9] 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.
+
+5.2. DHCPv6 Protocol References
+
+ For now there is only one document that specifies the DHCPv6 protocol
+ (there have been no updates yet), RFC3315 [21].
+
+ Support for DHCPv6 was added first 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
+
+
+
+Hankins [Page 7]
+
+ ISC DHCP References Collection August 2006
+
+
+ sending one. There is no relay support.
+
+ DHCPv6 introduces some new and uncomfortable ideas to the common
+ software library.
+
+ 1. Options of zero length are normal in DHCPv6. Currently, all ISC
+ DHCP software treats zero-length options as errors.
+
+ 2. 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.
+
+ 3. 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.
+
+5.3. DHCP Option References
+
+ RFC2241 [10] defines options for Novell Directory Services.
+
+ RFC2242 [11] defines an encapsulated option space for NWIP
+ configuration.
+
+ RFC2485 [12] defines the Open Group's UAP option.
+
+ RFC2610 [13] defines options for the Service Location Protocol (SLP).
+
+ RFC2937 [14] 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 [16] 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.
+
+
+
+Hankins [Page 8]
+
+ ISC DHCP References Collection August 2006
+
+
+ RFC3011 [17] 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.
+
+ RFC3319 [22] defines the SIP server options for DHCPv6.
+
+ RFC3396 [23] 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 [24] documents the Domain-Search Option, which allows the
+ configuration of the /etc/resolv.conf 'search' parameter in a way
+ that is RFC1035 [5] wire format compatible (in fact, it uses the
+ RFC1035 wire format). ISC DHCP has both client and server support,
+ and supports RFC1035 name compression.
+
+ RFC3646 [27] documents the DHCPv6 name-servers and domain-search
+ options.
+
+ RFC3633 [26] documents the Identity Association Prefix Delegation,
+ which is included here for protocol wire reference, but which is not
+ supported by ISC DHCP.
+
+ RFC3679 [28] documents a number of options that were documented
+ earlier in history, but were not made use of.
+
+ RFC3898 [29] documents four NIS options for delivering NIS servers
+ and domain information in DHCPv6.
+
+ RFC3925 [30] 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 [31] redefined the 'site local' option space.
+
+ RFC4075 [32] defines the DHCPv6 SNTP Servers option.
+
+ RFC4242 [33] defines the Information Refresh Time option, which
+ advises DHCPv6 Information-Request clients to return for updated
+ information.
+
+ RFC4280 [34] defines two BCMS server options.
+
+ RFC4388 [35] 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.
+
+
+
+Hankins [Page 9]
+
+ ISC DHCP References Collection August 2006
+
+
+ RFC4580> [36] defines a DHCPv6 subscriber-id option, which is similar
+ in principle to the DHCPv4 relay agent option of the same name.
+
+ RFC4649 [37] defines a DHCPv6 remote-id option, which is similar in
+ principle to the DHCPv4 relay agent remote-id.
+
+5.3.1. Relay Agent Information Option Options
+
+ RFC3046 [18] defines the Relay Agent Information Option and provides
+ a number of sub-option definitions.
+
+ RFC3256 [20] defines the DOCSIS Device Class sub-option.
+
+ RFC3527 [25] defines the Link Selection sub-option.
+
+5.3.2. Dynamic DNS Updates References
+
+ The collection of documents that describe the standards-based method
+ to update dns names of DHCP clients starts most easily with RFC4703
+ [40] to define the overall architecture, travels through RFCs 4702
+ [39] and 4704 [41] to describe the DHCPv4 and DHCPv6 FQDN options (to
+ carry the client name), and ends up at RFC4701 [38] which describes
+ the DHCID RR used in DNS to perform a kind of atomic locking.
+
+ ISC DHCP adoped early versions of these documents, and has not yet
+ synched up 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.
+
+5.3.3. Experimental: Failover References
+
+ The Failover Protocol defines a means by which two DHCP Servers can
+
+
+
+Hankins [Page 10]
+
+ ISC DHCP References Collection August 2006
+
+
+ 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 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 [42] 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.
+
+ RFC3074 [19] 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).
+
+5.4. DHCP Procedures
+
+ RFC2939 [15] explains how to go about obtaining a new DHCP Option
+ code assignment.
+
+6. References
+
+ [1] Postel, J., "DoD standard Internet Protocol", RFC 760,
+ January 1980.
+
+ [2] Postel, J., "User Datagram Protocol", STD 6, RFC 768,
+ August 1980.
+
+ [3] Hornig, C., "Standard for the transmission of IP datagrams over
+ Ethernet networks", STD 41, RFC 894, April 1984.
+
+ [4] Croft, B. and J. Gilmore, "Bootstrap Protocol", RFC 951,
+
+
+
+Hankins [Page 11]
+
+ ISC DHCP References Collection August 2006
+
+
+ September 1985.
+
+ [5] Mockapetris, P., "Domain names - implementation and
+ specification", STD 13, RFC 1035, November 1987.
+
+ [6] Katz, D., "Proposed Standard for the Transmission of IP
+ Datagrams over FDDI Networks", RFC 1188, October 1990.
+
+ [7] Wimer, W., "Clarifications and Extensions for the Bootstrap
+ Protocol", RFC 1542, October 1993.
+
+ [8] Droms, R., "Dynamic Host Configuration Protocol", RFC 2131,
+ March 1997.
+
+ [9] Alexander, S. and R. Droms, "DHCP Options and BOOTP Vendor
+ Extensions", RFC 2132, March 1997.
+
+ [10] Provan, D., "DHCP Options for Novell Directory Services",
+ RFC 2241, November 1997.
+
+ [11] Droms, R. and K. Fong, "NetWare/IP Domain Name and
+ Information", RFC 2242, November 1997.
+
+ [12] Drach, S., "DHCP Option for The Open Group's User
+ Authentication Protocol", RFC 2485, January 1999.
+
+ [13] Perkins, C. and E. Guttman, "DHCP Options for Service Location
+ Protocol", RFC 2610, June 1999.
+
+ [14] Smith, C., "The Name Service Search Option for DHCP", RFC 2937,
+ September 2000.
+
+ [15] Droms, R., "Procedures and IANA Guidelines for Definition of
+ New DHCP Options and Message Types", BCP 43, RFC 2939,
+ September 2000.
+
+ [16] Stump, G., Droms, R., Gu, Y., Vyaghrapuri, R., Demirtjis, A.,
+ Beser, B., and J. Privat, "The User Class Option for DHCP",
+ RFC 3004, November 2000.
+
+ [17] Waters, G., "The IPv4 Subnet Selection Option for DHCP",
+ RFC 3011, November 2000.
+
+ [18] Patrick, M., "DHCP Relay Agent Information Option", RFC 3046,
+ January 2001.
+
+ [19] Volz, B., Gonczi, S., Lemon, T., and R. Stevens, "DHC Load
+ Balancing Algorithm", RFC 3074, February 2001.
+
+
+
+Hankins [Page 12]
+
+ ISC DHCP References Collection August 2006
+
+
+ [20] Jones, D. and R. Woundy, "The DOCSIS (Data-Over-Cable Service
+ Interface Specifications) Device Class DHCP (Dynamic Host
+ Configuration Protocol) Relay Agent Information Sub-option",
+ RFC 3256, April 2002.
+
+ [21] Droms, R., Bound, J., Volz, B., Lemon, T., Perkins, C., and M.
+ Carney, "Dynamic Host Configuration Protocol for IPv6
+ (DHCPv6)", RFC 3315, July 2003.
+
+ [22] Schulzrinne, H. and B. Volz, "Dynamic Host Configuration
+ Protocol (DHCPv6) Options for Session Initiation Protocol (SIP)
+ Servers", RFC 3319, July 2003.
+
+ [23] Lemon, T. and S. Cheshire, "Encoding Long Options in the
+ Dynamic Host Configuration Protocol (DHCPv4)", RFC 3396,
+ November 2002.
+
+ [24] Aboba, B. and S. Cheshire, "Dynamic Host Configuration Protocol
+ (DHCP) Domain Search Option", RFC 3397, November 2002.
+
+ [25] Kinnear, K., Stapp, M., Johnson, R., and J. Kumarasamy, "Link
+ Selection sub-option for the Relay Agent Information Option for
+ DHCPv4", RFC 3527, April 2003.
+
+ [26] Troan, O. and R. Droms, "IPv6 Prefix Options for Dynamic Host
+ Configuration Protocol (DHCP) version 6", RFC 3633,
+ December 2003.
+
+ [27] Droms, R., "DNS Configuration options for Dynamic Host
+ Configuration Protocol for IPv6 (DHCPv6)", RFC 3646,
+ December 2003.
+
+ [28] Droms, R., "Unused Dynamic Host Configuration Protocol (DHCP)
+ Option Codes", RFC 3679, January 2004.
+
+ [29] Kalusivalingam, V., "Network Information Service (NIS)
+ Configuration Options for Dynamic Host Configuration Protocol
+ for IPv6 (DHCPv6)", RFC 3898, October 2004.
+
+ [30] Littlefield, J., "Vendor-Identifying Vendor Options for Dynamic
+ Host Configuration Protocol version 4 (DHCPv4)", RFC 3925,
+ October 2004.
+
+ [31] Volz, B., "Reclassifying Dynamic Host Configuration Protocol
+ version 4 (DHCPv4) Options", RFC 3942, November 2004.
+
+ [32] Kalusivalingam, V., "Simple Network Time Protocol (SNTP)
+ Configuration Option for DHCPv6", RFC 4075, May 2005.
+
+
+
+Hankins [Page 13]
+
+ ISC DHCP References Collection August 2006
+
+
+ [33] Venaas, S., Chown, T., and B. Volz, "Information Refresh Time
+ Option for Dynamic Host Configuration Protocol for IPv6
+ (DHCPv6)", RFC 4242, November 2005.
+
+ [34] Chowdhury, K., Yegani, P., and L. Madour, "Dynamic Host
+ Configuration Protocol (DHCP) Options for Broadcast and
+ Multicast Control Servers", RFC 4280, November 2005.
+
+ [35] Woundy, R. and K. Kinnear, "Dynamic Host Configuration Protocol
+ (DHCP) Leasequery", RFC 4388, February 2006.
+
+ [36] Volz, B., "Dynamic Host Configuration Protocol for IPv6
+ (DHCPv6) Relay Agent Subscriber-ID Option", RFC 4580,
+ June 2006.
+
+ [37] Volz, B., "Dynamic Host Configuration Protocol for IPv6
+ (DHCPv6) Relay Agent Remote-ID Option", RFC 4649, August 2006.
+
+ [38] Stapp, M., Lemon, T., and A. Gustafsson, "A DNS Resource Record
+ (RR) for Encoding Dynamic Host Configuration Protocol (DHCP)
+ Information (DHCID RR)", RFC 4701, October 2006.
+
+ [39] Stapp, M., Volz, B., and Y. Rekhter, "The Dynamic Host
+ Configuration Protocol (DHCP) Client Fully Qualified Domain
+ Name (FQDN) Option", RFC 4702, October 2006.
+
+ [40] Stapp, M. and B. Volz, "Resolution of Fully Qualified Domain
+ Name (FQDN) Conflicts among Dynamic Host Configuration Protocol
+ (DHCP) Clients", RFC 4703, October 2006.
+
+ [41] Volz, B., "The Dynamic Host Configuration Protocol for IPv6
+ (DHCPv6) Client Fully Qualified Domain Name (FQDN) Option",
+ RFC 4704, October 2006.
+
+ [42] Droms, R., "DHCP Failover Protocol", March 2003.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Hankins [Page 14]
+
+ ISC DHCP References Collection August 2006
+
+
+Author's Address
+
+ David W. Hankins
+ Internet Systems Consortium, Inc.
+ 950 Charter Street
+ Redwood City, CA 94063
+
+ Phone: +1 650 423 1300
+ Email: David_Hankins@isc.org
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Hankins [Page 15]
+
diff --git a/doc/References.xml b/doc/References.xml
new file mode 100644
index 00000000..5500ef46
--- /dev/null
+++ b/doc/References.xml
@@ -0,0 +1,668 @@
+<?xml version='1.0' ?>
+
+<!-- $Id: References.xml,v 1.2 2007/05/08 23:05:21 dhankins Exp $ -->
+
+<?rfc private="ISC-DHCP-REFERENCES" ?>
+
+<?rfc toc="yes"?>
+
+<?rfc compact="yes"?>
+<?rfc subcompact="no"?>
+<?rfc tocompact="no"?>
+
+<!DOCTYPE rfc SYSTEM 'rfc2629bis.dtd' [
+ <!ENTITY rfc760 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.0760.xml'>
+ <!ENTITY rfc768 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.0768.xml'>
+ <!ENTITY rfc894 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.0894.xml'>
+ <!ENTITY rfc951 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.0951.xml'>
+ <!ENTITY rfc1035 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.1035.xml'>
+ <!ENTITY rfc1188 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.1188.xml'>
+ <!ENTITY rfc1542 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.1542.xml'>
+ <!ENTITY rfc2131 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.2131.xml'>
+ <!ENTITY rfc2132 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.2132.xml'>
+ <!ENTITY rfc2241 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.2241.xml'>
+ <!ENTITY rfc2242 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.2242.xml'>
+ <!ENTITY rfc2485 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.2485.xml'>
+ <!ENTITY rfc2610 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.2610.xml'>
+ <!ENTITY rfc2937 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.2937.xml'>
+ <!ENTITY rfc2939 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.2939.xml'>
+ <!ENTITY rfc3004 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.3004.xml'>
+ <!ENTITY rfc3011 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.3011.xml'>
+ <!ENTITY rfc3046 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.3046.xml'>
+ <!ENTITY rfc3074 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.3074.xml'>
+ <!ENTITY rfc3256 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.3256.xml'>
+ <!ENTITY rfc3315 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.3315.xml'>
+ <!ENTITY rfc3319 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.3319.xml'>
+ <!ENTITY rfc3396 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.3396.xml'>
+ <!ENTITY rfc3397 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.3397.xml'>
+ <!ENTITY rfc3527 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.3527.xml'>
+ <!ENTITY rfc3633 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.3633.xml'>
+ <!ENTITY rfc3646 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.3646.xml'>
+ <!ENTITY rfc3679 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.3679.xml'>
+ <!ENTITY rfc3898 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.3898.xml'>
+ <!ENTITY rfc3925 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.3925.xml'>
+ <!ENTITY rfc3942 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.3942.xml'>
+ <!ENTITY rfc4075 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.4075.xml'>
+ <!ENTITY rfc4242 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.4242.xml'>
+ <!ENTITY rfc4280 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.4280.xml'>
+ <!ENTITY rfc4388 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.4388.xml'>
+ <!ENTITY rfc4580 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.4580.xml'>
+ <!ENTITY rfc4649 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.4649.xml'>
+ <!ENTITY rfc4701 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.4701.xml'>
+ <!ENTITY rfc4702 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.4702.xml'>
+ <!ENTITY rfc4703 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.4703.xml'>
+ <!ENTITY rfc4704 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.4704.xml'>
+]>
+
+<rfc ipr="none">
+ <front>
+ <title>ISC DHCP References Collection</title>
+
+ <author initials="D.H." surname="Hankins" fullname="David W. Hankins">
+ <organization abbrev="ISC">Internet Systems Consortium,
+ Inc.
+ </organization>
+
+ <address>
+ <postal>
+ <street>950 Charter Street</street>
+ <city>Redwood City</city>
+ <region>CA</region>
+ <code>94063</code>
+ </postal>
+
+ <phone>+1 650 423 1300</phone>
+ <email>David_Hankins@isc.org</email>
+ </address>
+ </author>
+
+ <date month="August" year="2006"/>
+
+ <note title="Copyright Notice">
+ <t>Copyright (c) 2006-2007 by Internet Systems Consortium, Inc.
+ ("ISC")</t>
+
+ <t>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.</t>
+
+ <t>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.</t>
+ </note>
+
+ <keyword>ISC</keyword>
+ <keyword>DHCP</keyword>
+ <keyword>Reference Implementation</keyword>
+
+ <abstract>
+ <t>This document describes a collection of Reference material that
+ ISC DHCP has been implemented to.</t>
+ </abstract>
+ </front>
+
+ <middle>
+ <section title="Introduction">
+ <t>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).</t>
+
+ <t>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.</t>
+ </section>
+
+ <section title="Definition: Reference Implementation">
+ <t>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.</t>
+
+ <t>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."</t>
+
+ <t>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.</t>
+
+ <t>Anything else is a bug, the only question is whether the
+ bug is in reference or software (failing to implement the
+ reference).</t>
+
+ <t>This means:</t>
+
+ <list style="symbols">
+ <t>To produce new externally-visible behaviour, one must first
+ provide a reference.</t>
+
+ <t>Before changing externally visible behaviour to work around
+ simple incompatibilities in any other implementation, one must
+ first provide a reference.</t>
+ </list>
+
+ <t>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.</t>
+
+ <t>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".</t>
+
+ <t>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.</t>
+ </section>
+
+ <section title="Low Layer References">
+ <t>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.</t>
+
+ <t>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.</t>
+
+ <t>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 <xref target="RFC2131">RFC2131</xref>.</t>
+
+ <list style="numbers">
+ <t>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.</t>
+
+ <t>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.</t>
+
+ <t>Transmit a UDP packet from IP:Self, Ethernet:Seelf, destined to
+ IP:remote-system LinkLayer:remote-system, without transmitting a
+ single ARP.</t>
+
+ <t>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.</t>
+ </list>
+
+ <t>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, <xref target="RFC2131">
+ RFC2131</xref> very explicitly demands that these packets' IP
+ destination addresses be set to 255.255.255.255.</t>
+
+ <t>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.</t>
+
+ <t>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.</t>
+
+ <t>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 the
+ need.</t>
+
+ <t>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 absolved. 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.</t>
+
+ <section title="Ethernet Protocol References">
+ <t>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 <xref target="RFC0894">
+ RFC894</xref> (see the section titled "Packet format"), and
+ the following URL is also thought to be useful.</t>
+
+ <t>http://en.wikipedia.org/wiki/DIX</t>
+ </section>
+
+ <section title="Token Ring Protocol References">
+ <t>IEEE 802.5 defines the Token Ring framing format used by ISC
+ DHCP.</t>
+ </section>
+
+ <section title="FDDI Protocol References">
+ <t><xref target="RFC1188">RFC1188</xref> is the most helpful
+ reference ISC DHCP has used to form FDDI packets.</t>
+ </section>
+
+ <section title="Internet Protocol Version 4 References">
+ <t><xref target="RFC0760">RFC760</xref> fundamentally defines the
+ bare IPv4 protocol which ISC DHCP implements.</t>
+ </section>
+
+ <section title="Unicast Datagram Protocol References">
+ <t><xref target="RFC0768">RFC768</xref> 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.</t>
+ </section>
+ </section>
+
+ <section title="BOOTP Protocol References">
+ <t>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.</t>
+
+ <t>The ISC DHCP server supports BOOTP clients conforming to
+ <xref target="RFC0951">RFC951</xref> and <xref target="RFC1542">
+ RFC1542</xref>.</t>
+ </section>
+
+ <section title="DHCP Protocol References">
+ <section title="DHCPv4 Protocol">
+ <t>"The DHCP[v4] Protocol" is not defined in a single document. The
+ following collection of references of what ISC DHCP terms "The
+ DHCPv4 Protocol".</t>
+
+ <section title="Core Protocol References">
+ <t><xref target="RFC2131">RFC2131</xref> 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.</t>
+
+ <t><xref target="RFC2132">RFC2132</xref> 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.</t>
+ </section>
+ </section>
+
+ <section title="DHCPv6 Protocol References">
+ <t>For now there is only one document that specifies the DHCPv6
+ protocol (there have been no updates yet), <xref target="RFC3315">
+ RFC3315</xref>.</t>
+
+ <t>Support for DHCPv6 was added first 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.</t>
+
+ <t>DHCPv6 introduces some new and uncomfortable ideas to the common
+ software library.</t>
+
+ <list style="numbers">
+ <t>Options of zero length are normal in DHCPv6. Currently, all
+ ISC DHCP software treats zero-length options as errors.</t>
+
+ <t>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.</t>
+
+ <t>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.</t>
+ </list>
+
+ <t>Precisely how to correctly support the above conundrums has not
+ quite yet been settled, so support is incomplete.</t>
+ </section>
+
+ <section title="DHCP Option References">
+ <t><xref target="RFC2241">RFC2241</xref> defines options for
+ Novell Directory Services.</t>
+
+ <t><xref target="RFC2242">RFC2242</xref> defines an encapsulated
+ option space for NWIP configuration.</t>
+
+ <t><xref target="RFC2485">RFC2485</xref> defines the Open Group's
+ UAP option.</t>
+
+ <t><xref target="RFC2610">RFC2610</xref> defines options for
+ the Service Location Protocol (SLP).</t>
+
+ <t><xref target="RFC2937">RFC2937</xref> 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.</t>
+
+ <t><xref target="RFC3004">RFC3004</xref> 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.</t>
+
+ <t><xref target="RFC3011">RFC3011</xref> 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.</t>
+
+ <t><xref target="RFC3319">RFC3319</xref> defines the SIP server
+ options for DHCPv6.</t>
+
+ <t><xref target="RFC3396">RFC3396</xref> 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.</t>
+
+ <t><xref target="RFC3397">RFC3397</xref> documents the Domain-Search
+ Option, which allows the configuration of the /etc/resolv.conf
+ 'search' parameter in a way that is <xref target="RFC1035">RFC1035
+ </xref> wire format compatible (in fact, it uses the RFC1035 wire
+ format). ISC DHCP has both client and server support, and supports
+ RFC1035 name compression.</t>
+
+ <t><xref target="RFC3646">RFC3646</xref> documents the DHCPv6
+ name-servers and domain-search options.</t>
+
+ <t><xref target="RFC3633">RFC3633</xref> documents the Identity
+ Association Prefix Delegation, which is included here for protocol
+ wire reference, but which is not supported by ISC DHCP.</t>
+
+ <t><xref target="RFC3679">RFC3679</xref> documents a number of
+ options that were documented earlier in history, but were not
+ made use of.</t>
+
+ <t><xref target="RFC3898">RFC3898</xref> documents four NIS options
+ for delivering NIS servers and domain information in DHCPv6.</t>
+
+ <t><xref target="RFC3925">RFC3925</xref> 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.</t>
+
+ <t><xref target="RFC3942">RFC3942</xref> redefined the 'site local'
+ option space.</t>
+
+ <t><xref target="RFC4075">RFC4075</xref> defines the DHCPv6 SNTP
+ Servers option.</t>
+
+ <t><xref target="RFC4242">RFC4242</xref> defines the Information
+ Refresh Time option, which advises DHCPv6 Information-Request
+ clients to return for updated information.</t>
+
+ <t><xref target="RFC4280">RFC4280</xref> defines two BCMS server
+ options.</t>
+
+ <t><xref target="RFC4388">RFC4388</xref> 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.</t>
+
+ <t><xref target="RFC4580">RFC4580></xref> defines a DHCPv6
+ subscriber-id option, which is similar in principle to the DHCPv4
+ relay agent option of the same name.</t>
+
+ <t><xref target="RFC4649">RFC4649</xref> defines a DHCPv6 remote-id
+ option, which is similar in principle to the DHCPv4 relay agent
+ remote-id.</t>
+
+ <section title="Relay Agent Information Option Options">
+ <t><xref target="RFC3046">RFC3046</xref> defines the Relay Agent
+ Information Option and provides a number of sub-option
+ definitions.</t>
+
+ <t><xref target="RFC3256">RFC3256</xref> defines the DOCSIS Device
+ Class sub-option.</t>
+
+ <t><xref target="RFC3527">RFC3527</xref> defines the Link Selection
+ sub-option.</t>
+ </section>
+
+ <section title="Dynamic DNS Updates References">
+ <t>The collection of documents that describe the standards-based
+ method to update dns names of DHCP clients starts most easily
+ with <xref target="RFC4703">RFC4703</xref> to define the overall
+ architecture, travels through RFCs <xref target="RFC4702">4702</xref>
+ and <xref target="RFC4704">4704</xref> to describe the DHCPv4 and
+ DHCPv6 FQDN options (to carry the client name), and ends up at
+ <xref target="RFC4701">RFC4701</xref> which describes the DHCID
+ RR used in DNS to perform a kind of atomic locking.</t>
+
+ <t>ISC DHCP adoped early versions of these documents, and has not
+ yet synched up with the final standards versions.</t>
+
+ <t>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.</t>
+
+ <t>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.</t>
+ </section>
+
+ <section title="Experimental: Failover References">
+ <t>The Failover Protocol defines a 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.</t>
+
+ <t>Unfortunately it has been quite some years since the last time
+ this document was edited, and the authors no longer show any
+ interest in fielding comments or improving the document.</t>
+
+ <t>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.</t>
+
+ <t><xref target="draft-failover">draft-ietf-dhc-failover-12.txt</xref>
+ 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.</t>
+
+ <t><xref target="RFC3074">RFC3074</xref> 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).</t>
+ </section>
+ </section>
+
+ <section title="DHCP Procedures">
+ <t><xref target="RFC2939">RFC2939</xref> explains how to go about
+ obtaining a new DHCP Option code assignment.</t>
+ </section>
+ </section>
+ </middle>
+
+ <back>
+ <references>
+ &rfc760;
+ &rfc768;
+ &rfc894;
+ &rfc951;
+ &rfc1035;
+ &rfc1188;
+ &rfc1542;
+ &rfc2131;
+ &rfc2132;
+ &rfc2241;
+ &rfc2242;
+ &rfc2485;
+ &rfc2610;
+ &rfc2937;
+ &rfc2939;
+ &rfc3004;
+ &rfc3011;
+ &rfc3046;
+ &rfc3074;
+ &rfc3256;
+ &rfc3315;
+ &rfc3319;
+ &rfc3396;
+ &rfc3397;
+ &rfc3527;
+ &rfc3633;
+ &rfc3646;
+ &rfc3679;
+ &rfc3898;
+ &rfc3925;
+ &rfc3942;
+ &rfc4075;
+ &rfc4242;
+ &rfc4280;
+ &rfc4388;
+ &rfc4580;
+ &rfc4649;
+ &rfc4701;
+ &rfc4702;
+ &rfc4703;
+ &rfc4704;
+
+ <reference anchor='draft-failover'>
+ <front>
+ <title>DHCP Failover Protocol</title>
+
+ <author initials='R.' surname='Droms' fullname='Ralph Droms'>
+ <organization abbrev='Cisco'>Cisco Systems</organization>
+ </author>
+
+ <date month='March' year='2003'/>
+ </front>
+ <format type="TXT" octets="312151" target="http://www.isc.org/sw/dhcp/drafts/draft-ietf-dhc-failover-12.txt"/>
+ </reference>
+ </references>
+ </back>
+</rfc>
+
diff --git a/doc/draft-ietf-dhc-authentication-14.txt b/doc/draft-ietf-dhc-authentication-14.txt
deleted file mode 100644
index 43a1f8ae..00000000
--- a/doc/draft-ietf-dhc-authentication-14.txt
+++ /dev/null
@@ -1,893 +0,0 @@
-Network Working Group R. Droms, Editor
-INTERNET DRAFT Bucknell University
-Obsoletes: draft-ietf-dhc-authentication-13.txt W. Arbaugh, Editor
- University of Maryland
- July 2000
- Expires December 2000
-
-
- Authentication for DHCP Messages
- <draft-ietf-dhc-authentication-14.txt>
-
-Status of this memo
-
- This document is an Internet-Draft and is in full conformance with
- all provisions of Section 10 of RFC2026. Internet-Drafts are working
- documents of the Internet Engineering Task Force (IETF), its areas,
- and its working groups. Note that other groups may also distribute
- working documents as Internet-Drafts.
-
- Internet-Drafts are draft documents valid for a maximum of six months
- and may be updated, replaced, or obsoleted by other documents at any
- time. It is inappropriate to use Internet- Drafts as reference
- material or to cite them other than as "work in progress."
-
- The list of current Internet-Drafts can be accessed at
- http://www.ietf.org/ietf/1id-abstracts.txt, and the list of
- Internet-Draft Shadow Directories can be accessed at
- http://www.ietf.org/shadow.html.
-
-
-Abstract
-
- The Dynamic Host Configuration Protocol (DHCP) provides a framework
- for passing configuration information to hosts on a TCP/IP network.
- In some situations, network administrators may wish to constrain the
- allocation of addresses to authorized hosts. Additionally, some
- network administrators may wish to provide for authentication of the
- source and contents of DHCP messages. This document defines a new
- DHCP option through which authorization tickets can be easily
- generated and newly attached hosts with proper authorization can be
- automatically configured from an authenticated DHCP server.
-
-1. Introduction
-
- DHCP [1] transports protocol stack configuration parameters from
- centrally administered servers to TCP/IP hosts. Among those
- parameters are an IP address. DHCP servers can be configured to
- dynamically allocate addresses from a pool of addresses, eliminating
-
-
-
-Droms, Arbaugh [Page 1]
-
-DRAFT Authentication for DHCP Messages March 2000
-
-
- a manual step in configuration of TCP/IP hosts.
-
- Some network administrators may wish to provide authentication of the
- source and contents of DHCP messages. For example, clients may be
- subject to denial of service attacks through the use of bogus DHCP
- servers, or may simply be misconfigured due to unintentionally
- instantiated DHCP servers. Network administrators may wish to
- constrain the allocation of addresses to authorized hosts to avoid
- denial of service attacks in "hostile" environments where the network
- medium is not physically secured, such as wireless networks or
- college residence halls.
-
- This document defines a technique that can provide both entity
- authentication and message authentication.
-
- DISCUSSION:
-
- This draft combines the original Schiller-Huitema-Droms
- authentication mechanism defined in a previous Internet Draft with
- the "delayed authentication" proposal developed by Bill Arbaugh.
-
-1.1 DHCP threat model
-
- The threat to DHCP is inherently an insider threat (assuming a
- properly configured network where BOOTP ports are blocked on the
- enterprise's perimeter gateways.) Regardless of the gateway
- configuration, however, the potential attacks by insiders and
- outsiders are the same.
-
- The attack specific to a DHCP client is the possibility of the
- establishment of a "rogue" server with the intent of providing
- incorrect configuration information to the client. The motivation for
- doing so may be to establish a "man in the middle" attack or it may
- be for a "denial of service" attack.
-
- There is another threat to DHCP clients from mistakenly or
- accidentally configured DHCP servers that answer DHCP client requests
- with unintentionally incorrect configuration parameters.
-
- The threat specific to a DHCP server is an invalid client
- masquerading as a valid client. The motivation for this may be for
- "theft of service", or to circumvent auditing for any number of
- nefarious purposes.
-
- The threat common to both the client and the server is the resource
- "denial of service" (DoS) attack. These attacks typically involve the
- exhaustion of valid addresses, or the exhaustion of CPU or network
- bandwidth, and are present anytime there is a shared resource. In
-
-
-
-Droms, Arbaugh [Page 2]
-
-DRAFT Authentication for DHCP Messages March 2000
-
-
- current practice, redundancy mitigates DoS attacks the best.
-
-1.2 Design goals
-
- These are the goals that were used in the development of the
- authentication protocol, listed in order of importance:
-
- 1. Address the threats presented in Section 1.1.
- 2. Avoid changing the current protocol.
- 3. Limit state required by the server.
- 4. Limit complexity (complexity breeds design and implementation
- errors).
-
-1.3 Requirements Terminology
-
- The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
- "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY" and "OPTIONAL" in this
- document are to be interpreted as described in RFC 2119 [5].
-
-1.4 DHCP Terminology
-
- This document uses the following terms:
-
- o "DHCP client"
-
- A DHCP client or "client" is an Internet host using DHCP to obtain
- configuration parameters such as a network address.
-
- o "DHCP server"
-
- A DHCP server or "server" is an Internet host that returns
- configuration parameters to DHCP clients.
-
-2. Format of the authentication option
-
- The following diagram defines the format of the DHCP
- authentication option:
-
-
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Code | Length | Protocol | Algorithm |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | RDM | Replay Detection (64 bits) |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Replay cont. | |
- +-+-+-+-+-+-+-+-+ |
-
-
-
-Droms, Arbaugh [Page 3]
-
-DRAFT Authentication for DHCP Messages March 2000
-
-
- | |
- | Authentication Information |
- | |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-
-
- The code for the authentication option is TBD, and the length field
- contains the length of the protocol, RDM, algorithm, Replay Detection
- fields and authentication information fields in octets.
-
- The protocol field defines the particular technique for
- authentication used in the option. New protocols are defined as
- described in Section 6.
-
- The algorithm field defines the specific algorithm within the
- technique identified by the protocol field.
-
- The Replay Detection field is per the RDM, and the authentication
- information field is per the protocol in use.
-
- The Replay Detection Method (RDM) field determines the type of replay
- detection used in the Replay Detection field.
-
- If the RDM field contains 0x00, the replay detection field MUST be
- set to the value of a monotonically increasing counter. Using a
- counter value such as the current time of day (e.g., an NTP-format
- timestamp [4]) can reduce the danger of replay attacks. This
- method MUST be supported by all protocols.
-
- Other values of the RDM field are reserved for future definition
- according to the procedures described in section 6.
-
- This document defines two protocols in sections 4 and 5, encoded with
- protocol field values 0 and 1. Protocol field values 2-254 are
- reserved for future use. Other protocols may be defined according to
- the procedure described in section 6.
-
-3. Interaction with Relay Agents
-
- Because a DHCP relay agent may alter the values of the 'giaddr' and
- 'hops' fields in the DHCP message, the contents of those two fields
- MUST be set to zero for the computation of any hash function over the
- message header. Additionally, a relay agent may append the DHCP relay
- agent information option 82 [7] as the last option in a message to
- servers. If a server finds option 82 included in a received message,
- the server MUST compute any hash function as if the option were NOT
- included in the message without changing the order of options.
-
-
-
-Droms, Arbaugh [Page 4]
-
-DRAFT Authentication for DHCP Messages March 2000
-
-
- Whenever the server sends back option 82 to a relay agent, the server
- MUST not include the option in the computation of any hash function
- over the message.
-
-
-4. Protocol 0
-
- If the protocol field is 0, the authentication information field
- holds a simple authentication token:
-
-
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Code | Length |0 0 0 0 0 0 0 0|0 0 0 0 0 0 0 0|
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- |0 0 0 0 0 0 0 0| Replay Detection (64 bits) |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Replay cont. | |
- +-+-+-+-+-+-+-+-+ |
- | |
- | Authentication Information |
- | |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-
- The authentication token is an opaque, unencoded value known to both
- the sender and receiver. The sender inserts the authentication token
- in the DHCP message and the receiver matches the token from the
- message to the shared token. If the authentication option is present
- and the token from the message does not match the shared token, the
- receiver MUST discard the message.
-
- Protocol 0 may be used to pass a plain-text password and provides
- only weak entity authentication and no message authentication. This
- protocol is only useful for rudimentary protection against
- inadvertently instantiated DHCP servers.
-
- DISCUSSION:
-
- The intent here is to pass a constant, non-computed token such as
- a plain-text password. Other types of entity authentication using
- computed tokens such as Kerberos tickets or one-time passwords
- will be defined as separate protocols.
-
-
-5. Protocol 1
-
-
-
-
-Droms, Arbaugh [Page 5]
-
-DRAFT Authentication for DHCP Messages March 2000
-
-
- If the protocol field is 1, the message is using the "delayed
- authentication" mechanism. In delayed authentication, the client
- requests authentication in its DHCPDISCOVER message and the server
- replies with a DHCPOFFER message that includes authentication
- information. This authentication information contains a nonce value
- generated by the source as a message authentication code (MAC) to
- provide message authentication and entity authentication.
-
- This document defines the use of a particular technique based on the
- HMAC protocol [3] using the MD5 hash [2].
-
-5.1 Management Issues
-
- The "delayed authentication" protocol does not attempt to address
- situations where a client may roam from one administrative domain to
- another, i.e. interdomain roaming. This protocol is focused on
- solving the intradomain problem where the out-of-band exchange of a
- shared secret is feasible.
-
-5.2 Format
-
- The format of the authentication request in a DHCPDISCOVER message
- for protocol 1 is:
-
-
-
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Code | Length |0 0 0 0 0 0 0 1| Algorithm |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | RDM | Replay Detection (64 bits) |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Replay cont. |
- +-+-+-+-+-+-+-+-+
-
-
- The format of the authentication information for protocol 1 is:
-
-
-
-
-
-
-
-
-
-
-
-
-
-Droms, Arbaugh [Page 6]
-
-DRAFT Authentication for DHCP Messages March 2000
-
-
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Code | Length |0 0 0 0 0 0 0 1| Algorithm |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | RDM | Replay Detection (64 bits) |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Replay cont. | Secret ID (32 bits) |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | secret id cont| HMAC-MD5 (128 bits) ....
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-
- This document defines one technique for use with protocol 1, which is
- identified by setting the algorithm field to 1. Other techniques
- that use different algorithms may be defined by future
- specifications, see section 6. The following definitions will be
- used in the description of the authentication information for
- protocol 1, algorithm 1:
-
- Replay Detection - as defined by the RDM field
- K - a secret value shared between the source and
- destination of the message; each secret has a
- unique identifier (not shown in figures)
- secret ID - the unique identifier for the secret value
- used to generate the MAC for this message
- HMAC-MD5 - the MAC generating function [3, 2].
-
- The sender computes the MAC using the HMAC generation algorithm [3]
- and the MD5 hash function [2]. The entire DHCP message (except as
- noted below), including the DHCP message header and the options
- field, is used as input to the HMAC-MD5 computation function. The
- 'secret ID' field MUST be set to the identifier of the secret used to
- generate the MAC.
-
- DISCUSSION:
-
- Algorithm 1 specifies the use of HMAC-MD5. Use of a different
- technique, such as HMAC-SHA, will be specified as a separate
- protocol.
-
- Protocol 1 requires a shared secret key for each client on each
- DHCP server with which that client may wish to use the DHCP
- protocol. Each secret key has a unique identifier that can be
- used by a receiver to determine which secret was used to generate
- the MAC in the DHCP message. Therefore, protocol 1 may not scale
- well in an architecture in which a DHCP client connects to
- multiple administrative domains.
-
-
-
-Droms, Arbaugh [Page 7]
-
-DRAFT Authentication for DHCP Messages March 2000
-
-
- Note that the meaning of an authentication option can be changed
- by removing the secret ID, and MAC, transforming an authentication
- option with authentication information into a request for
- authentication. Therefore, the authentication request form of
- this option can only appear in a DHCPDISCOVER message or a
- DHCPINFORM message.
-
-5.3 Message validation
-
- To validate an incoming message, the receiver first checks that
- the value in the replay detection field is acceptable according to
- the replay detection method specified by the RDM field. Next, the
- receiver computes the MAC as described in [3]. The receiver MUST
- set the 'MAC' field of the authentication option to all 0s for
- computation of the MAC, and because a DHCP relay agent may alter
- the values of the 'giaddr' and 'hops' fields in the DHCP message,
- the contents of those two fields MUST also be set to zero for the
- computation of the MAC. If the MAC computed by the receiver does
- not match the MAC contained in the authentication option, the
- receiver MUST discard the DHCP message.
-
- Section 3 provides additional information on handling messages
- that include option 82 (Relay Agents).
-
-5.4 Key utilization
-
- Each DHCP client has a key, K. The client uses its key to encode
- any messages it sends to the server and to authenticate and verify
- any messages it receives from the server. The client's key SHOULD
- be initially distributed to the client through some out-of-band
- mechanism, and SHOULD be stored locally on the client for use in
- all authenticated DHCP messages. Once the client has been given
- its key, it SHOULD use that key for all transactions even if the
- client's configuration changes; e.g., if the client is assigned a
- new network address.
-
- Each DHCP server MUST know, or be able to obtain in a secure
- manner, the keys for all authorized clients. If all clients use
- the same key, clients can perform both entity and message
- authentication for all messages received from servers. However,
- the sharing of keys is strongly discouraged as it allows for
- unauthorized clients to masquerade as authorized clients by
- obtaining a copy of the shared key. To authenticate the identity
- of individual clients, each client MUST be configured with a
- unique key. Appendix A describes a technique for key management.
-
-5.5 Client considerations
-
-
-
-
-Droms, Arbaugh [Page 8]
-
-DRAFT Authentication for DHCP Messages March 2000
-
-
- This section describes the behavior of a DHCP client using
- authentication protocol 1.
-
-5.5.1 INIT state
-
- When in INIT state, the client uses protocol 1 as follows:
-
- 1. The client MUST include the authentication request option in
- its DHCPDISCOVER message along with option 61 [6] to identify
- itself uniquely to the server.
-
- 2. The client MUST validate any DHCPOFFER messages that include
- authentication information using the mechanism specified in
- section 5.3. The client MUST discard any messages which fail
- to pass validation and MAY log the validation failure. The
- client selects one DHCPOFFER message as its selected
- configuration. If none of the DHCPOFFER messages received by
- the client include authentication information, the client MAY
- choose an unauthenticated message as its selected
- configuration. The client SHOULD be configurable to accept or
- reject unauthenticated DHCPOFFER messages.
- 3. The client replies with a DHCPREQUEST message that MUST include
- authentication information encoded with the same secret used by
- the server in the selected DHCPOFFER message.
- 4. The client MUST validate the DHCPACK message from the server.
- The client MUST discard the DHCPACK if the message fails to
- pass validation and MAY log the validation failure. If the
- DHCPACK fails to pass validation, the client MUST revert to
- INIT state and returns to step 1. The client MAY choose to
- remember which server replied with a DHCPACK message that
- failed to pass validation and discard subsequent messages from
- that server.
-
-5.5.2 INIT-REBOOT state
-
- When in INIT-REBOOT state, the client MUST use the secret it used
- in its DHCPREQUEST message to obtain its current configuration to
- generate authentication information for the DHCPREQUEST message.
- The client MAY choose to accept unauthenticated DHCPACK/DHCPNAK
- messages if no authenticated messages were received. The client
- MUST treat the receipt (or lack thereof) of any DHCPACK/DHCPNAK
- messages as specified in section 3.2 of [1].
-
-5.5.3 RENEWING state
-
- When in RENEWING state, the client uses the secret it used in its
- initial DHCPREQUEST message to obtain its current configuration to
- generate authentication information for the DHCPREQUEST message.
-
-
-
-Droms, Arbaugh [Page 9]
-
-DRAFT Authentication for DHCP Messages March 2000
-
-
- If client receives no DHCPACK messages or none of the DHCPACK
- messages pass validation, the client behaves as if it had not
- received a DHCPACK message in section 4.4.5 of the DHCP
- specification [1].
-
-5.5.4 REBINDING state
-
- When in REBINDING state, the client uses the secret it used in its
- initial DHCPREQUEST message to obtain its current configuration to
- generate authentication information for the DHCPREQUEST message.
- If client receives no DHCPACK messages or none of the DHCPACK
- messages pass validation, the client behaves as if it had not
- received a DHCPACK message in section 4.4.5 of the DHCP
- specification [1].
-
-5.5.5 DHCPINFORM message
-
- Since the client already has some configuration information, the
- client may also have established a shared secret value, K, with a
- server. Therefore, the client SHOULD use the authentication
- request as in a DHCPDISCOVER message when a shared secret value
- exists. The client MUST treat any received DHCPACK messages as it
- does DHCPOFFER messages, see section 5.5.1.
-
-5.5.6 DHCPRELEASE message
-
- Since the client is already in the BOUND state, the client will
- have a security association already established with the server.
- Therefore, the client MUST include authentication information with
- the DHCPRELEASE message.
-
-5.6 Server considerations
-
- This section describes the behavior of a server in response to
- client messages using authentication protocol 1.
-
-5.6.1 General considerations
-
- Each server maintains a list of secrets and identifiers for those
- secrets that it shares with clients and potential clients. This
- information must be maintained in such a way that the server can:
-
- * Identify an appropriate secret and the identifier for that
- secret for use with a client that the server may not have
- previously communicated with
- * Retrieve the secret and identifier used by a client to which the
- server has provided previous configuration information
-
-
-
-
-Droms, Arbaugh [Page 10]
-
-DRAFT Authentication for DHCP Messages March 2000
-
-
- Each server MUST save the counter from the previous authenticated
- message. A server MUST discard any incoming message which fails
- the replay detection check as defined by the RDM avoid replay
- attacks.
-
- DISCUSSION:
-
- The authenticated DHCPREQUEST message from a client in INIT-
- REBOOT state can only be validated by servers that used the
- same secret in their DHCPOFFER messages. Other servers will
- discard the DHCPREQUEST messages. Thus, only servers that used
- the secret selected by the client will be able to determine
- that their offered configuration information was not selected
- and the offered network address can be returned to the server's
- pool of available addresses. The servers that cannot validate
- the DHCPREQUEST message will eventually return their offered
- network addresses to their pool of available addresses as
- described in section 3.1 of the DHCP specification [1].
-
-5.6.2 After receiving a DHCPDISCOVER message
-
- The server selects a secret for the client and includes
- authentication information in the DHCPOFFER message as specified
- in section 5, above. The server MUST record the identifier of the
- secret selected for the client and use that same secret for
- validating subsequent messages with the client.
-
-5.6.3 After receiving a DHCPREQUEST message
-
- The server uses the secret identified in the message and validates
- the message as specified in section 5.3. If the message fails to
- pass validation or the server does not know the secret identified
- by the 'secret ID' field, the server MUST discard the message and
- MAY choose to log the validation failure.
-
- If the message passes the validation procedure, the server
- responds as described in the DHCP specification. The server MUST
- include authentication information generated as specified in
- section 5.2.
-
-5.6.4 After receiving a DHCPINFORM message
-
- The server MAY choose to accept unauthenticated DHCPINFORM
- messages, or only accept authenticated DHCPINFORM messages based
- on a site policy.
-
- When a client includes the authentication request in a DHCPINFORM
- message, the server MUST respond with an authenticated DHCPACK
-
-
-
-Droms, Arbaugh [Page 11]
-
-DRAFT Authentication for DHCP Messages March 2000
-
-
- message. If the server does not have a shared secret value
- established with the sender of the DHCPINFORM message, then the
- server MAY respond with an unauthenticated DHCPACK message, or a
- DHCPNAK if the server does not accept unauthenticated clients
- based on the site policy, or the server MAY choose not to respond
- to the DHCPINFORM message.
-
-6. IANA Considerations
-
- The author of a new DHCP authentication protocol, algorithm or
- replay detection method will follow these steps to obtain
- acceptance of the new procedure as a part of the DHCP Internet
- Standard:
-
- 1. The author devises the new authentication protocol, algorithm
- or replay detection method.
- 2. The author documents the new technique as an Internet Draft.
- The protocol, algorithm or RDM code for any new procedure is
- left as "To Be Determined" (TBD).
- 3. The author submits the Internet Draft for review through the
- IETF standards process as defined in "Internet Official
- Protocol Standards" (STD 1).
- 4. The new protocol progresses through the IETF standards process;
- the specification of the new protocol will be reviewed by the
- Dynamic Host Configuration Working Group (if that group still
- exists), or as an Internet Draft not submitted by an IETF
- working group. If the option is accepted as a Standard, the
- specification for the option is published as a separate RFC.
- 5. At the time of acceptance as a Proposed Internet Standard and
- publication as an RFC, IANA assigns a DHCP authentication
- protocol number to the new protocol.
-
- This procedure for defining new authentication protocols will
- ensure that:
-
- * allocation of new protocol numbers is coordinated from a single
- authority,
- * new protocols are reviewed for technical correctness and
- appropriateness, and
- * documentation for new protocols is complete and published.
-
-
- DISCUSSION:
- This procedure is patterned after the procedure for acceptance
- of new DHCP options.
-
-7. References
-
-
-
-
-Droms, Arbaugh [Page 12]
-
-DRAFT Authentication for DHCP Messages March 2000
-
-
- [1] Droms, R., "Dynamic Host Configuration Protocol", RFC 2131,
- Bucknell University, March 1997.
-
- [2] Rivest, R., "The MD5 Message-Digest Algorithm",
- RFC-1321, April 1992.
-
- [3] Krawczyk H., M. Bellare and R. Canetti, "HMAC: Keyed-Hashing for
- Message Authentication," RFC-2104, February 1997.
-
- [4] Mills, D., "Network Time Protocol (Version 3)", RFC-1305, March
- 1992.
-
- [5] Bradner, S., "Key words for use in RFCs to Indicate Requirement
- Levels," RFC-2219, March 1997.
-
- [6] Henry, M., "DHCP Option 61 UUID Type Definition,"
- <draft-henry-DHCP-opt61-UUID-type-00.txt> (work in
- progress, November 1998.
-
- [7] Patrick, M., "DHCP Relay Agent Information Option,"
- <draft-ietf-dhc-agent-options-05.txt> (work in progress),
- November 1998.
-
- [8] Gupta, V., "Flexible Authentication for DHCP Messages,"
- <draft-gupta-dhcp-auth-00.txt> (work in progress, June
- 1998.
-
-8. Acknowledgments
-
- Jeff Schiller and Christian Huitema developed this scheme during a
- terminal room BOF at the Dallas IETF meeting, December 1995. The
- editor transcribed the notes from that discussion, which form the
- basis for this document. The editor appreciates Jeff's and
- Christian's patience in reviewing this document and its earlier
- drafts.
-
- The "delayed authentication" mechanism used in section 5 is due to
- Bill Arbaugh. The threat model and requirements in sections 1.1
- and 1.2 come from Bill's negotiation protocol proposal. The
- attendees of an interim meeting of the DHC WG held in June, 1998,
- including Peter Ford, Kim Kinnear, Glenn Waters, Rob Stevens, Bill
- Arbaugh, Baiju Patel, Carl Smith, Thomas Narten, Stewart Kwan,
- Munil Shah, Olafur Gudmundsson, Robert Watson, Ralph Droms, Mike
- Dooley, Greg Rabil and Arun Kapur, developed the threat model and
- reviewed several alternative proposals.
-
- The replay detection method field is due to Vipul Gupta [8].
-
-
-
-
-Droms, Arbaugh [Page 13]
-
-DRAFT Authentication for DHCP Messages March 2000
-
-
- Other input from Bill Sommerfield is gratefully acknowledged.
-
- Thanks also to John Wilkins, Ran Atkinson, Shawn Mamros and Thomas
- Narten for reviewing earlier drafts of this document.
-
-9. Security considerations
-
- This document describes authentication and verification mechanisms
- for DHCP.
-
-10. Editors' addresses
-
- Ralph Droms
- Computer Science Department
- 323 Dana Engineering
- Bucknell University
- Lewisburg, PA 17837
-
- Phone: (717) 524-1145
- EMail: droms@bucknell.edu
-
- Bill Arbaugh
- Department of Computer Science
- University of Maryland
- A.V. Williams Building
- College Park, MD 20742
-
- Phone: (301) 455-2774
- Email: waa@cs.umd.edu
-
-10. Expiration
-
- This document will expire on December 31, 2000.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Droms, Arbaugh [Page 14]
-
-DRAFT Authentication for DHCP Messages March 2000
-
-
- Full Copyright Statement
-
- Copyright (C) The Internet Society (2000). All Rights Reserved.
-
- This document and translations of it may be copied and furnished to
- others, and derivative works that comment on or otherwise explain it
- or assist in its implementation may be prepared, copied, published and
- distributed, in whole or in part, without restriction of any kind,
- provided that the above copyright notice and this paragraph are
- included on all such copies and derivative works. However, this
- document itself may not be modified in any way, such as by removing
- the copyright notice or references to the Internet Society or other
- Internet organizations, except as needed for the purpose of developing
- Internet standards in which case the procedures for copyrights defined
- in the Internet Standards process must be followed, or as required to
- translate it into languages other than English.
-
- The limited permissions granted above are perpetual and will not be
- revoked by the Internet Society or its successors or assigns.
-
- This document and the information contained herein is provided on an
- "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
- TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT
- NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN
- WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
- MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Droms, Arbaugh [Page 15]
-
-DRAFT Authentication for DHCP Messages March 2000
-
-
- Appendix A - Key Management Technique
-
- To avoid centralized management of a list of random keys, suppose K
- for each client is generated from the pair (client identifier [6],
- subnet address, e.g. 192.168.1.0), which must be unique to that
- client. That is, K = MAC(MK, unique-id), where MK is a secret master
- key and MAC is a keyed one-way function such as HMAC-MD5.
-
- Without knowledge of the master key MK, an unauthorized client cannot
- generate its own key K. The server can quickly validate an incoming
- message from a new client by regenerating K from the client-id. For
- known clients, the server can choose to recover the client's K
- dynamically from the client-id in the DHCP message, or can choose to
- precompute and cache all of the Ks a priori.
-
- To avoid compromis of this key management system, the master key, MK,
- MUST NOT be stored by any clients. The client SHOULD only be given
- its key, K. If MK is compromised, a new MK SHOULD be chosen and all
- clients given new individual keys.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Droms, Arbaugh [Page 16]
-
diff --git a/doc/draft-ietf-dhc-dhcp-dns-12.txt b/doc/draft-ietf-dhc-dhcp-dns-12.txt
deleted file mode 100644
index c97ba625..00000000
--- a/doc/draft-ietf-dhc-dhcp-dns-12.txt
+++ /dev/null
@@ -1,1072 +0,0 @@
-
-
-DHC Working Group M. Stapp
-Internet-Draft Y. Rekhter
-Expires: September 2000 Cisco Systems, Inc.
- March 10, 2000
-
-
- Interaction between DHCP and DNS
- <draft-ietf-dhc-dhcp-dns-12.txt>
-
-Status of this Memo
-
- This document is an Internet-Draft and is in full conformance with
- all provisions of Section 10 of RFC2026.
-
- Internet-Drafts are working documents of the Internet Engineering
- Task Force (IETF), its areas, and its working groups. Note that
- other groups may also distribute working documents as
- Internet-Drafts.
-
- Internet-Drafts are draft documents valid for a maximum of six
- months and may be updated, replaced, or obsoleted by other documents
- at any time. It is inappropriate to use Internet-Drafts as reference
- material or to cite them other than as "work in progress."
-
- To view the entire list of Internet-Draft Shadow Directories, see
- http://www.ietf.org/shadow.html.
-
- This Internet-Draft will expire on September 2000.
-
-Copyright Notice
-
- Copyright (C) The Internet Society (2000). All Rights Reserved.
-
-Abstract
-
- DHCP provides a powerful mechanism for IP host configuration.
- However, the configuration capability provided by DHCP does not
- include updating DNS, and specifically updating the name to address
- and address to name mappings maintained in the DNS.
-
- This document specifies how DHCP clients and servers should use the
- Dynamic DNS Updates mechanism in RFC2136[5] to update the DNS name
- to address and address to name mappings so that the mappings for
- DHCP clients will be consistent with the IP addresses that the
- clients acquire via DHCP.
-
-
-
-
-
-
-
-Stapp & Rekhter Expires September 2000 [Page 1]
-
-Internet-Draft Interaction between DHCP and DNS March 2000
-
-
-Table of Contents
-
- 1. Terminology . . . . . . . . . . . . . . . . . . . . . . . . 3
- 2. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 3
- 3. Models of Operation . . . . . . . . . . . . . . . . . . . . 3
- 4. Issues with DDNS in DHCP Environments . . . . . . . . . . . 4
- 4.1 Name Collisions . . . . . . . . . . . . . . . . . . . . . . 5
- 4.2 Multiple DHCP servers . . . . . . . . . . . . . . . . . . . 6
- 4.3 Use of the DHCID RR . . . . . . . . . . . . . . . . . . . . 6
- 4.3.1 Format of the DHCID RRDATA . . . . . . . . . . . . . . . . . 6
- 4.4 DNS RR TTLs . . . . . . . . . . . . . . . . . . . . . . . . 8
- 5. Client FQDN Option . . . . . . . . . . . . . . . . . . . . . 8
- 5.1 The Flags Field . . . . . . . . . . . . . . . . . . . . . . 9
- 5.2 The RCODE Fields . . . . . . . . . . . . . . . . . . . . . . 10
- 5.3 The Domain Name Field . . . . . . . . . . . . . . . . . . . 10
- 6. DHCP Client behavior . . . . . . . . . . . . . . . . . . . . 10
- 7. DHCP Server behavior . . . . . . . . . . . . . . . . . . . . 12
- 8. Procedures for performing DNS updates . . . . . . . . . . . 14
- 8.1 Adding A RRs to DNS . . . . . . . . . . . . . . . . . . . . 14
- 8.2 Adding PTR RR Entries to DNS . . . . . . . . . . . . . . . . 15
- 8.3 Removing Entries from DNS . . . . . . . . . . . . . . . . . 15
- 8.4 Updating other RRs . . . . . . . . . . . . . . . . . . . . . 16
- 9. Security Considerations . . . . . . . . . . . . . . . . . . 16
- 10. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . 17
- References . . . . . . . . . . . . . . . . . . . . . . . . . 17
- Authors' Addresses . . . . . . . . . . . . . . . . . . . . . 18
- Full Copyright Statement . . . . . . . . . . . . . . . . . . 19
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Stapp & Rekhter Expires September 2000 [Page 2]
-
-Internet-Draft Interaction between DHCP and DNS March 2000
-
-
-1. Terminology
-
- The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
- "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
- document are to be interpreted as described in RFC 2119[6].
-
-2. Introduction
-
- DNS (RFC1034[1], RFC1035[2]) maintains (among other things) the
- information about mapping between hosts' Fully Qualified Domain
- Names (FQDNs) RFC1594[4] and IP addresses assigned to the hosts. The
- information is maintained in two types of Resource Records (RRs): A
- and PTR. The A RR contains mapping from a FQDN to an IP address; the
- PTR RR contains mapping from an IP address to a FQDN. The Dynamic
- DNS Updates specification (RFC2136[5]) describes a mechanism that
- enables DNS information to be updated over a network.
-
- DHCP RFC2131[3] provides a mechanism by which a host (a DHCP client)
- can acquire certain configuration information, along with its IP
- address(es). However, DHCP does not provide any mechanisms to update
- the DNS RRs that contain the information about mapping between the
- host's FQDN and its IP address(es) (A and PTR RRs). Thus the
- information maintained by DNS for a DHCP client may be incorrect - a
- host (the client) could acquire its address by using DHCP, but the A
- RR for the host's FQDN wouldn't reflect the address that the host
- acquired, and the PTR RR for the acquired address wouldn't reflect
- the host's FQDN.
-
- The Dynamic DNS Update protocol can be used to maintain consistency
- between the information stored in the A and PTR RRs and the actual
- address assignment done via DHCP. When a host with a particular FQDN
- acquires its IP address via DHCP, the A RR associated with the
- host's FQDN would be updated (by using the Dynamic DNS Updates
- protocol) to reflect the new address. Likewise, when an IP address
- is assigned to a host with a particular FQDN, the PTR RR associated
- with this address would be updated (using the Dynamic DNS Updates
- protocol) to reflect the new FQDN.
-
- Although this document refers to the A and PTR DNS record types and
- to DHCP assignment of IPv4 addresses, the same procedures and
- requirements apply for updates to the analogous RR types that are
- used when clients are assigned IPv6 addresses via DHCPv6.
-
-3. Models of Operation
-
- When a DHCP client acquires a new address, a site's administrator
- may desire that one or both of the A RR for the client's FQDN and
- the PTR RR for the acquired address be updated. Therefore, two
- separate Dynamic DNS Update transactions occur. Acquiring an address
-
-
-Stapp & Rekhter Expires September 2000 [Page 3]
-
-Internet-Draft Interaction between DHCP and DNS March 2000
-
-
- via DHCP involves two entities: a DHCP client and a DHCP server. In
- principle each of these entities could perform none, one, or both of
- the transactions. However, in practice not all permutations make
- sense. This document covers these possible design permutations:
-
- 1. DHCP client updates the A RR, DHCP server updates the PTR RR
- 2. DHCP server updates both the A and the PTR RRs
-
- The only difference between these two cases is whether the FQDN to
- IP address mapping is updated by a DHCP client or by a DHCP server.
- The IP address to FQDN mapping is updated by a DHCP server in both
- cases.
-
- The reason these two are important, while others are unlikely, has
- to do with authority over the respective DNS domain names. A DHCP
- client may be given authority over mapping its own A RRs, or that
- authority may be restricted to a server to prevent the client from
- listing arbitrary addresses or associating its address with
- arbitrary domain names. In all cases, the only reasonable place for
- the authority over the PTR RRs associated with the address is in the
- DHCP server that allocates the address.
-
- In any case, whether a site permits all, some, or no DHCP servers
- and clients to perform DNS updates into the zones which it controls
- is entirely a matter of local administrative policy. This document
- does not require any specific administrative policy, and does not
- propose one. The range of possible policies is very broad, from
- sites where only the DHCP servers have been given credentials that
- the DNS servers will accept, to sites where each individual DHCP
- client has been configured with credentials which allow the client
- to modify its own domain name. Compliant implementations MAY support
- some or all of these possibilities. Furthermore, this specification
- applies only to DHCP client and server processes: it does not apply
- to other processes which initiate dynamic DNS updates.
-
- This document describes a new DHCP option which a client can use to
- convey all or part of its domain name to a DHCP server.
- Site-specific policy determines whether DHCP servers use the names
- that clients offer or not, and what DHCP servers may do in cases
- where clients do not supply domain names.
-
-4. Issues with DDNS in DHCP Environments
-
- There are two DNS update situations that require special
- consideration in DHCP environments: cases where more than one DHCP
- client has been configured with the same FQDN, and cases where more
- than one DHCP server has been given authority to perform DNS updates
- in a zone. In these cases, it is possible for DNS records to be
- modified in inconsistent ways unless the updaters have a mechanism
- that allows them to detect anomolous situations. If DNS updaters can
-
-
-Stapp & Rekhter Expires September 2000 [Page 4]
-
-Internet-Draft Interaction between DHCP and DNS March 2000
-
-
- detect these situations, site administrators can configure the
- updaters' behavior so that the site's policies can be enforced. We
- use the term "Name Collisions" to refer to cases where more than one
- DHCP client has been associated with a single FQDN. This
- specification describes a mechanism designed to allow updaters to
- detect these situations, and requires that DHCP implementations use
- this mechanism by default.
-
-4.1 Name Collisions
-
- How can the entity updating an A RR (either the DHCP client or DHCP
- server) detect that a domain name has an A RR which is already in
- use by a different DHCP client? Similarly, should a DHCP client or
- server update a domain name which has an A RR that has been
- configured by an administrator? In either of these cases, the
- domain name in question would either have an additional A RR, or
- would have its original A RR replaced by the new record. Either of
- these effects may be considered undesirable by some sites. Different
- authority and credential models have different levels of exposure to
- name collisions.
-
- 1. Client updates A RR, uses Secure DNS Update with credentials
- that are associated with the client's FQDN, and exclusive to the
- client. Name collisions in this scenario are unlikely (though
- not impossible), since the client has received credentials
- specific to the name it desires to use. This implies that the
- name has already been allocated (through some implementation- or
- organization-specific procedure) to that client.
-
- 2. Client updates A RR, uses Secure DNS Update with credentials
- that are valid for any name in the zone. Name collisions in this
- scenario are possible, since the credentials necessary for the
- client to update DNS are not necessarily name-specific. Thus,
- for the client to be attempting to update a unique name requires
- the existence of some administrative procedure to ensure client
- configuration with unique names.
-
- 3. Server updates the A RR, uses a name for the client which is
- known to the server. Name collisions in this scenario are likely
- unless prevented by the server's name configuration procedures.
- See Section 9 for security issues with this form of deployment.
-
- 4. Server updates the A RR, uses a name supplied by the client.
- Name collisions in this scenario are highly likely, even with
- administrative procedures designed to prevent them. (This
- scenario is a popular one in real-world deployments in many
- types of organizations.) See Section 9 for security issues with
- this type of deployment.
-
-
- Scenarios 2, 3, and 4 rely on administrative procedures to ensure
- name uniqueness for DNS updates, and these procedures may break
- down. Experience has shown that, in fact, these procedures will
- break down at least occasionally. The question is what to do when
-
-
-Stapp & Rekhter Expires September 2000 [Page 5]
-
-Internet-Draft Interaction between DHCP and DNS March 2000
-
-
- these procedures break down or, for example in scenario #4, may not
- even exist.
-
- In all cases of name collisions, the desire is to offer two modes of
- operation to the administrator of the combined DHCP-DNS capability:
- first-update-wins (i.e., the first updating entity gets the name) or
- most-recent-update-wins (i.e., the last updating entity for a name
- gets the name).
-
-4.2 Multiple DHCP servers
-
- If multiple DHCP servers are able to update the same DNS zones, or
- if DHCP servers are performing A RR updates on behalf of DHCP
- clients, and more than one DHCP server may be able to serve
- addresses to the same DHCP clients, the DHCP servers should be able
- to provide reasonable and consistent DNS name update behavior for
- DHCP clients.
-
-4.3 Use of the DHCID RR
-
- A solution to both of these problems is for the updating entities
- (both DHCP clients and DHCP servers) to be able to detect that
- another entity has been associated with a DNS name, and to offer
- administrators the opportunity to configure update behavior.
-
- Specifically, a DHCID RR, described in DHCID RR[12] is used to
- associate client identification information with a DNS name and the
- A RR associated with that name. When either a client or server adds
- an A RR for a client, it also adds a DHCID RR which specifies a
- unique client identity (based on a "client specifier" created from
- the client's client-id or MAC address). In this model, only one A
- RR is associated with a given DNS name at a time.
-
- By associating this ownership information with each A RR,
- cooperating DNS updating entities may determine whether their client
- is the first or last updater of the name (and implement the
- appropriately configured administrative policy), and DHCP clients
- which currently have a host name may move from one DHCP server to
- another without losing their DNS name.
-
- The specific algorithms utilizing the DHCID RR to signal client
- ownership are explained below. The algorithms only work in the case
- where the updating entities all cooperate -- this approach is
- advisory only and is not substitute for DNS security, nor is it
- replaced by DNS security.
-
-4.3.1 Format of the DHCID RRDATA
-
- The DHCID RR used to hold the DHCP client's identity is formatted as
-
-
-Stapp & Rekhter Expires September 2000 [Page 6]
-
-Internet-Draft Interaction between DHCP and DNS March 2000
-
-
- follows:
-
- The name of the DHCID RR is the name of the A or PTR RR which refers
- to the DHCP client.
-
- The RDATA section of a DHCID RR in transmission contains RDLENGTH
- bytes of binary data. From the perspective of DHCP clients and
- servers, the DHC resource record consists of a 16-bit identifier
- type, followed by one or more bytes representing the actual
- identifier. There are two possible forms for a DHCID RR - one that
- is used when the client's link-layer address is being used to
- identify it, and one that is used when some DHCP option that the
- DHCP client has sent is being used to identify it.
-
-
- DISCUSSION:
- Implementors should note that the actual identifying data is
- never placed into the DNS directly. Instead, the client-identity
- data is used as the input into a one-way hash algorithm, and the
- output of that hash is then used as DNS RRDATA. This has been
- specified in order to avoid placing data about DHCP clients that
- some sites might consider sensitive into the DNS.
-
- When the updater is using the client's link-layer address, the first
- two bytes of the DHCID RRDATA MUST be zero. To generate the rest of
- the resource record, the updater MUST compute a one-way hash using
- the MD5[13] algorithm across a buffer containing the client's
- network hardware type and link-layer address. Specifically, the
- first byte of the buffer contains the network hardware type as it
- appears in the DHCP htype field of the client's DHCPREQUEST message.
- All of the significant bytes of the chaddr field in the client's
- DHCPREQUEST message follow, in the same order in which the bytes
- appear in the DHCPREQUEST message. The number of significant bytes
- in the chaddr field is specified in the hlen field of the
- DHCPREQUEST message.
-
- When the updater is using a DHCP option sent by the client in its
- DHCPREQUEST message, the first two bytes of the DHCID RR MUST be the
- option code of that option, in network byte order. For example, if
- the DHCP client identifier option is being used, the first byte of
- the DHCID RR should be zero, and the second byte should be 61
- decimal. The rest of the DHCID RR MUST contain the results of
- computing a one-way hash across the payload of the option being
- used, using the MD5 algorithm. The payload of a DHCP option consists
- of the bytes of the option following the option code and length.
-
- In order for independent DHCP implementations to be able to use the
- DHCID RR as a prerequisite in dynamic DNS updates, each updater must
- be able to reliably choose the same identifier that any other would
- choose. To make this possible, we specify a prioritization which
-
-
-Stapp & Rekhter Expires September 2000 [Page 7]
-
-Internet-Draft Interaction between DHCP and DNS March 2000
-
-
- will ensure that for any given DHCP client request, any updater will
- select the same client-identity data. All updaters MUST use this
- order of prioritization by default, but all implementations SHOULD
- be configurable to use a different prioritization if so desired by
- the site administrators. Because of the possibility of future
- changes in the DHCP protocol, implementors SHOULD check for updated
- versions of this draft when implementing new DHCP clients and
- servers which can perform DDNS updates, and also when releasing new
- versions of existing clients and servers.
-
- DHCP clients and servers should use the following forms of client
- identification, starting with the most preferable, and finishing
- with the least preferable. If the client does not send any of these
- forms of identification, the DHCP/DDNS interaction is not defined by
- this specification. The most preferable form of identification is
- the Globally Unique Identifier Option [TBD]. Next is the DHCP
- Client Identifier option. Last is the client's link-layer address,
- as conveyed in its DHCPREQUEST message. Implementors should note
- that the link-layer address cannot be used if there are no
- significant bytes in the chaddr field of the DHCP client's request,
- because this does not constitute a unique identifier.
-
-4.4 DNS RR TTLs
-
- RRs associated with DHCP clients may be more volatile than
- statically configured RRs. DHCP clients and servers which perform
- dynamic updates should attempt to specify resource record TTLs which
- reflect this volatility, in order to minimize the possibility that
- there will be stale records in resolvers' caches. A reasonable basis
- for RR TTLs is the lease duration itself: TTLs of 1/2 or 1/3 the
- expected lease duration might be reasonable defaults. Because
- configured DHCP lease times vary widely from site to site, it may
- also be desirable to establish a fixed TTL ceiling. DHCP clients and
- servers MAY allow administrators to configure the TTLs they will
- supply, possibly as a fraction of the actual lease time, or as a
- fixed value.
-
-5. Client FQDN Option
-
- To update the IP address to FQDN mapping a DHCP server needs to know
- the FQDN of the client to which the server leases the address. To
- allow the client to convey its FQDN to the server this document
- defines a new DHCP option, called "Client FQDN". The FQDN Option
- also contains Flags and RCode fields which DHCP servers can use to
- convey information about DNS updates to clients.
-
- Clients MAY send the FQDN option, setting appropriate Flags values,
- in both their DISCOVER and REQUEST messages. If a client sends the
- FQDN option in its DISCOVER message, it MUST send the option in
-
-
-Stapp & Rekhter Expires September 2000 [Page 8]
-
-Internet-Draft Interaction between DHCP and DNS March 2000
-
-
- subsequent REQUEST messages.
-
- The code for this option is 81. Its minimum length is 4.
-
-
- Code Len Flags RCODE1 RCODE2 Domain Name
- +------+------+------+------+------+------+--
- | 81 | n | | | | ...
- +------+------+------+------+------+------+--
-
-
-5.1 The Flags Field
-
-
- 0 1 2 3 4 5 6 7
- +-+-+-+-+-+-+-+-+
- | MBZ |E|O|S|
- +-+-+-+-+-+-+-+-+
-
-
- When a DHCP client sends the FQDN option in its DHCPDISCOVER and/or
- DHCPREQUEST messages, it sets the right-most bit (labelled "S") to
- indicate that it will not perform any Dynamic DNS updates, and that
- it expects the DHCP server to perform any FQDN-to-IP (the A RR) DNS
- update on its behalf. If this bit is clear, the client indicates
- that it intends to maintain its own FQDN-to-IP mapping update.
-
- If a DHCP server intends to take responsibility for the A RR update
- whether or not the client sending the FQDN option has set the "S"
- bit, it sets both the "O" bit and the "S" bit, and sends the FQDN
- option in its DHCPOFFER and/or DHCPACK messages.
-
- The data in the Domain Name field may appear in one of two formats:
- ASCII, or DNS-style binary encoding (without compression, of
- course), as described in RFC1035[2]. A client which sends the FQDN
- option MUST set the "E" bit to indicate that the data in the Domain
- Name field is DNS binary encoded. If a server receives an FQDN
- option from a client, and intends to include an FQDN option in its
- reply, it MUST use the same encoding that the client used. The DNS
- encoding is recommended. The use of ASCII-encoded domain-names is
- fragile, and the use of ASCII encoding in this option should be
- considered deprecated.
-
- The remaining bits in the Flags field are reserved for future
- assignment. DHCP clients and servers which send the FQDN option MUST
- set the MBZ bits to 0, and they MUST ignore values in the part of
- the field labelled "MBZ".
-
-
-
-
-Stapp & Rekhter Expires September 2000 [Page 9]
-
-Internet-Draft Interaction between DHCP and DNS March 2000
-
-
-5.2 The RCODE Fields
-
- The RCODE1 and RCODE2 fields are used by a DHCP server to indicate
- to a DHCP client the Response Code from any A or PTR RR Dynamic DNS
- Updates it has performed. The server may also use these fields to
- indicate whether it has attempted such an update before sending the
- DHCPACK message. Each of these fields is one byte long.
-
- Implementors should note that EDNS0 describes a mechanism for
- extending the length of a DNS RCODE to 12 bits. EDNS0 is specified
- in RFC2671[8]. Only the least-significant 8 bits of the RCODE from a
- Dynamic DNS Update will be carried in the Client FQDN DHCP Option.
- This provides enough number space to accomodate the RCODEs defined
- in the Dynamic DNS Update specification.
-
-5.3 The Domain Name Field
-
- The Domain Name part of the option carries all or part of the FQDN
- of a DHCP client. A client may be configured with a fully-qualified
- domain name, or with a partial name that is not fully-qualified. If
- a client knows only part of its name, it MAY send a single label,
- indicating that it knows part of the name but does not necessarily
- know the zone in which the name is to be embedded. The data in the
- Domain Name field may appear in one of two formats: ASCII (with no
- terminating NULL), or DNS encoding as specified in RFC1035[2]. If
- the DHCP client wishes to use DNS encoding, it MUST set the
- third-from-rightmost bit in the Flags field (the "E" bit); if it
- uses ASCII encoding, it MUST clear the "E" bit.
-
- A DHCP client that can only send a single label using ASCII encoding
- includes a series of ASCII characters in the Domain Name field,
- excluding the "." (dot) character. The client SHOULD follow the
- character-set recommendations of RFC1034[1] and RFC1035[2]. A client
- using DNS binary encoding which wants to suggest part of its FQDN
- MAY send a non-terminal sequence of labels in the Domain Name part
- of the option.
-
-6. DHCP Client behavior
-
- The following describes the behavior of a DHCP client that
- implements the Client FQDN option.
-
- If a client that owns/maintains its own FQDN wants to be responsible
- for updating the FQDN to IP address mapping for the FQDN and
- address(es) used by the client, then the client MUST include the
- Client FQDN option in the DHCPREQUEST message originated by the
- client. A DHCP client MAY choose to include the Client FQDN option
- in its DISCOVER messages as well as its REQUEST messages. The
- rightmost ("S") bit in the Flags field in the option MUST be set to
-
-
-Stapp & Rekhter Expires September 2000 [Page 10]
-
-Internet-Draft Interaction between DHCP and DNS March 2000
-
-
- 0. Once the client's DHCP configuration is completed (the client
- receives a DHCPACK message, and successfully completes a final check
- on the parameters passed in the message), the client MAY originate
- an update for the A RR (associated with the client's FQDN). The
- update MUST be originated following the procedures described in
- RFC2136[5] and Section 8. If the DHCP server from which the client
- is requesting a lease includes the FQDN option in its ACK message,
- and if the server sets both the "S" and the "O" bits (the two
- rightmost bits) in the option's flags field, the DHCP client MUST
- NOT initiate an update for the name in the Domain Name field.
-
- A client can choose to delegate the responsibility for updating the
- FQDN to IP address mapping for the FQDN and address(es) used by the
- client to the server. In order to inform the server of this choice,
- the client SHOULD include the Client FQDN option in its DHCPREQUEST
- message. The rightmost (or "S") bit in the Flags field in the option
- MUST be set to 1. A client which delegates this responsibility MUST
- NOT attempt to perform a Dynamic DNS update for the name in the
- Domain Name field of the FQDN option. The client MAY supply an FQDN
- in the Client FQDN option, or it MAY supply a single label (the
- most-specific label), or it MAY leave that field empty as a signal
- to the server to generate an FQDN for the client in any manner the
- server chooses.
-
- Since there is a possibility that the DHCP server may be configured
- to complete or replace a domain name that the client was configured
- to send, the client might find it useful to send the FQDN option in
- its DISCOVER messages. If the DHCP server returns different Domain
- Name data in its OFFER message, the client could use that data in
- performing its own eventual A RR update, or in forming the FQDN
- option that it sends in its REQUEST message. There is no requirement
- that the client send identical FQDN option data in its DISCOVER and
- REQUEST messages. In particular, if a client has sent the FQDN
- option to its server, and the configuration of the client changes so
- that its notion of its domain name changes, it MAY send the new name
- data in an FQDN option when it communicates with the server again.
- This may allow the DHCP server to update the name associated with
- the PTR record, and, if the server updated the A record representing
- the client, to delete that record and attempt an update for the
- client's current domain name.
-
- A client that delegates the responsibility for updating the FQDN to
- IP address mapping to a server might not receive any indication
- (either positive or negative) from the server whether the server was
- able to perform the update. In this case the client MAY use a DNS
- query to check whether the mapping is updated.
-
- A client MUST set the RCODE1 and RCODE2 fields in the Client FQDN
- option to 0 when sending the option.
-
-
-Stapp & Rekhter Expires September 2000 [Page 11]
-
-Internet-Draft Interaction between DHCP and DNS March 2000
-
-
- If a client releases its lease prior to the lease expiration time
- and the client is responsible for updating its A RR, the client
- SHOULD delete the A RR (following the procedures described in
- Section 8) associated with the leased address before sending a DHCP
- RELEASE message. Similarly, if a client was responsible for updating
- its A RR, but is unable to renew its lease, the client SHOULD
- attempt to delete the A RR before its lease expires. A DHCP client
- which has not been able to delete an A RR which it added (because it
- has lost the use of its DHCP IP address) should attempt to notify
- its administrator.
-
-7. DHCP Server behavior
-
- When a server receives a DHCPREQUEST message from a client, if the
- message contains the Client FQDN option, and the server replies to
- the message with a DHCPACK message, the server may be configured to
- originate an update for the PTR RR (associated with the address
- leased to the client). Any such update MUST be originated following
- the procedures described in Section 8. The server MAY complete the
- update before the server sends the DHCPACK message to the client. In
- this case the RCODE from the update MUST be carried to the client in
- the RCODE1 field of the Client FQDN option in the DHCPACK message.
- Alternatively, the server MAY send the DHCPACK message to the client
- without waiting for the update to be completed. In this case the
- RCODE1 field of the Client FQDN option in the DHCPACK message MUST
- be set to 255. The choice between the two alternatives is entirely
- determined by the configuration of the DHCP server. Servers SHOULD
- support both configuration options.
-
- When a server receives a DHCPREQUEST message containing the Client
- FQDN option, the server MUST ignore the values carried in the RCODE1
- and RCODE2 fields of the option.
-
- In addition, if the Client FQDN option carried in the DHCPREQUEST
- message has the "S" bit in its Flags field set, then the server MAY
- originate an update for the A RR (associated with the FQDN carried
- in the option) if it is configured to do so by the site's
- administrator, and if it has the necessary credentials. The server
- MAY be configured to use the name supplied in the client's FQDN
- option, or it MAY be configured to modify the supplied name, or
- substitute a different name.
-
- Any such update MUST be originated following the procedures
- described in Section 8. The server MAY originate the update before
- the server sends the DHCPACK message to the client. In this case the
- RCODE from the update [RFC2136] MUST be carried to the client in the
- RCODE2 field of the Client FQDN option in the DHCPACK message.
- Alternatively the server MAY send the DHCPACK message to the client
- without waiting for the update to be completed. In this case the
-
-
-Stapp & Rekhter Expires September 2000 [Page 12]
-
-Internet-Draft Interaction between DHCP and DNS March 2000
-
-
- RCODE2 field of the Client FQDN option in the DHCPACK message MUST
- be set to 255. The choice between the two alternatives is entirely
- up to the DHCP server. In either case, if the server intends to
- perform the DNS update and the client's REQUEST message included the
- FQDN option, the server SHOULD include the FQDN option in its ACK
- message, and MUST set the "S" bit in the option's Flags field.
-
- Even if the Client FQDN option carried in the DHCPREQUEST message
- has the "S" bit in its Flags field clear (indicating that the client
- wants to update the A RR), the server MAY be configured by the local
- administrator to update the A RR on the client's behalf. A server
- which is configured to override the client's preference SHOULD
- include an FQDN option in its ACK message, and MUST set both the "O"
- and "S" bits in the FQDN option's Flags field. The update MUST be
- originated following the procedures described in Section 8. The
- server MAY originate the update before the server sends the DHCPACK
- message to the client. In this case the RCODE from the update
- [RFC2136] MUST be carried to the client in the RCODE2 field of the
- Client FQDN option in the DHCPACK message. Alternatively, the server
- MAY send the DHCPACK message to the client without waiting for the
- update to be completed. In this case the RCODE2 field of the Client
- FQDN option in the DHCPACK message MUST be set to 255. Whether the
- DNS update occurs before or after the DHCPACK is sent is entirely up
- to the DHCP server's configuration.
-
- When a DHCP server sends the Client FQDN option to a client in the
- DHCPACK message, the DHCP server SHOULD send its notion of the
- complete FQDN for the client in the Domain Name field. The server
- MAY simply copy the Domain Name field from the Client FQDN option
- that the client sent to the server in the DHCPREQUEST message. The
- DHCP server MAY be configured to complete or modify the domain name
- which a client sent, or it MAY be configured to substitute a
- different name. If the server initiates a DDNS update which is not
- complete until after the server has replied to the DHCP client, the
- server's The server MUST use the same encoding format (ASCII or DNS
- binary encoding) that the client used in the FQDN option in its
- DHCPREQUEST, and MUST set the "E" bit in the option's Flags field
- accordingly.
-
- If a client's DHCPREQUEST message doesn't carry the Client FQDN
- option (e.g., the client doesn't implement the Client FQDN option),
- the server MAY be configured to update either or both of the A and
- PTR RRs. The updates MUST be originated following the procedures
- described in Section 8.
-
- If a server detects that a lease on an address that the server
- leases to a client has expired, the server SHOULD delete any PTR RR
- which it added via dynamic update. In addition, if the server added
- an A RR on the client's behalf, the server SHOULD also delete the A
-
-
-Stapp & Rekhter Expires September 2000 [Page 13]
-
-Internet-Draft Interaction between DHCP and DNS March 2000
-
-
- RR. The deletion MUST follow the procedures described in Section 8.
-
- If a server terminates a lease on an address prior to the lease's
- expiration time, for instance by sending a DHCPNAK to a client, the
- server SHOULD delete any PTR RR which it associated with the address
- via DNS Dynamic Update. In addition, if the server took
- responsibility for an A RR, the server SHOULD also delete that A RR.
- The deletion MUST follow the procedures described in Section 8.
-
-8. Procedures for performing DNS updates
-
-8.1 Adding A RRs to DNS
-
- 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.
-
- If this update operation 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).
-
- 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.
-
- If this 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).
-
- 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
-
-
-Stapp & Rekhter Expires September 2000 [Page 14]
-
-Internet-Draft Interaction between DHCP and DNS March 2000
-
-
- 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.
-
-
- DISCUSSION:
- The updating entity may be configured to allow the existing DNS
- records on the domain name to remain unchanged, and to perform
- disambiguation on the name of the current client in order to
- attempt to generate a similar but unique name for the current
- client. In this case, once another candidate name has been
- generated, the updater should restart the process of adding an A
- RR as specified in this section.
-
-8.2 Adding PTR RR Entries to DNS
-
- The DHCP server submits a DNS query which deletes all of the PTR RRs
- associated with the lease IP address, and adds a PTR RR whose data
- is the client's (possibly disambiguated) host name. The server also
- adds a DHCID RR specified in Section 4.3.
-
-8.3 Removing Entries from DNS
-
- The most important consideration in removing DNS entries is be sure
- that an entity removing a DNS entry is only removing an entry that
- it added, or for which an administrator has explicitly assigned it
- responsibility.
-
- When a lease expires or a DHCP client issues a DHCPRELEASE request,
- the DHCP server SHOULD delete the PTR RR that matches the DHCP
- binding, if one was successfully added. The server's update query
- SHOULD assert that the name in the PTR record matches the name of
- the client whose lease has expired or been released.
-
- The entity chosen to handle the A record for this client (either the
- client or the server) SHOULD delete the A record that was added when
- the lease was made to the client.
-
- In order to perform this delete, the updater prepares an UPDATE
- query which contains two prerequisites. The first prerequisite
- asserts that the DHCID RR exists whose data is the client identity
- described in Section 4.3. The second prerequisite asserts that the
- data in the A RR contains the IP address of the lease that has
- expired or been released.
-
- If the 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
-
-
-Stapp & Rekhter Expires September 2000 [Page 15]
-
-Internet-Draft Interaction between DHCP and DNS March 2000
-
-
- 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.
-
-8.4 Updating other RRs
-
- The procedures described in this document only cover updates to the
- A and PTR RRs. Updating other types of RRs is outside the scope of
- this document.
-
-9. Security Considerations
-
- Unauthenticated updates to the DNS can lead to tremendous confusion,
- through malicious attack or through inadvertent misconfiguration.
- Administrators should be wary of permitting unsecured DNS updates to
- zones which are exposed to the global Internet. Both DHCP clients
- and servers SHOULD use some form of update request origin
- authentication procedure (e.g., Simple Secure DNS Update[11]) when
- performing DNS updates.
-
- Whether a DHCP client may be responsible for updating an FQDN to IP
- address mapping, or whether this is the responsibility of the DHCP
- server is a site-local matter. The choice between the two
- alternatives may be based on the security model that is used with
- the Dynamic DNS Update protocol (e.g., only a client may have
- sufficient credentials to perform updates to the FQDN to IP address
- mapping for its FQDN).
-
- Whether a DHCP server is always responsible for updating the FQDN to
- IP address mapping (in addition to updating the IP to FQDN mapping),
- regardless of the wishes of an individual DHCP client, is also a
- site-local matter. The choice between the two alternatives may be
- based on the security model that is being used with dynamic DNS
- updates. In cases where a DHCP server is performing DNS updates on
- behalf of a client, the DHCP server should be sure of the DNS name
- to use for the client, and of the identity of the client.
-
- Currently, it is difficult for DHCP servers to develop much
- confidence in the identities of its clients, given the absence of
- entity authentication from the DHCP protocol itself. There are many
- ways for a DHCP server to develop a DNS name to use for a client,
- but only in certain relatively unusual circumstances will the DHCP
- server know for certain the identity of the client. If DHCP
- Authentication[10] becomes widely deployed this may become more
- customary.
-
- One example of a situation which offers some extra assurances is one
- where the DHCP client is connected to a network through an MCNS
- cable modem, and the CMTS (head-end) of the cable modem ensures that
-
-
-Stapp & Rekhter Expires September 2000 [Page 16]
-
-Internet-Draft Interaction between DHCP and DNS March 2000
-
-
- MAC address spoofing simply does not occur. Another example of a
- configuration that might be trusted is one where clients obtain
- network access via a network access server using PPP. The NAS itself
- might be obtaining IP addresses via DHCP, encoding a client
- identification into the DHCP client-id option. In this case, the
- network access server as well as the DHCP server might be operating
- within a trusted environment, in which case the DHCP server could be
- configured to trust that the user authentication and authorization
- procedure of the remote access server was sufficient, and would
- therefore trust the client identification encoded within the DHCP
- client-id.
-
-10. Acknowledgements
-
- Many thanks to Mark Beyer, Jim Bound, Ralph Droms, Robert Elz, Peter
- Ford, Edie Gunter, Andreas Gustafsson, R. Barr Hibbs, Kim Kinnear,
- Stuart Kwan, Ted Lemon, Ed Lewis, Michael Lewis, Josh Littlefield,
- Michael Patton, and Glenn Stump for their review and comments.
-
-References
-
- [1] Mockapetris, P., "Domain names - Concepts and Facilities", RFC
- 1034, Nov 1987.
-
- [2] Mockapetris, P., "Domain names - Implementation and
- Specification", RFC 1035, Nov 1987.
-
- [3] Droms, R., "Dynamic Host Configuration Protocol", RFC 2131,
- March 1997.
-
- [4] Marine, A., Reynolds, J. and G. Malkin, "FYI on Questions and
- Answers to Commonly asked ``New Internet User'' Questions", RFC
- 1594, March 1994.
-
- [5] Vixie, P., Thomson, S., Rekhter, Y. and J. Bound, "Dynamic
- Updates in the Domain Name System", RFC 2136, April 1997.
-
- [6] Bradner, S., "Key words for use in RFCs to Indicate Requirement
- Levels", RFC 2119, March 1997.
-
- [7] Eastlake, D., "Domain Name System Security Extensions", RFC
- 2535, March 1999.
-
- [8] Vixie, P., "Extension Mechanisms for DNS (EDNS0)", RFC 2671,
- August 1999.
-
- [9] Vixie, P., Gudmundsson, O., Eastlake, D. and B. Wellington,
- "Secret Key Transaction Authentication for DNS (TSIG)
- (draft-ietf-dnsext-tsig-*)", July 1999.
-
-
-Stapp & Rekhter Expires September 2000 [Page 17]
-
-Internet-Draft Interaction between DHCP and DNS March 2000
-
-
- [10] Droms, R. and W. Arbaugh, "Authentication for DHCP Messages
- (draft-ietf-dhc-authentication-*)", June 1999.
-
- [11] Wellington, B., "Simple Secure DNS Dynamic Updates
- (draft-ietf-dnsext-simple-secure-update-*)", June 1999.
-
- [12] Gustafsson, A., "A DNS RR for encoding DHCP client identity
- (draft-ietf-dnsext-dhcid-rr-*)", October 1999.
-
- [13] Rivest, R., "The MD5 Message Digest Algorithm", RFC 1321,
- April 1992.
-
-Authors' Addresses
-
- Mark Stapp
- Cisco Systems, Inc.
- 250 Apollo Dr.
- Chelmsford, MA 01824
- US
-
- Phone: 978.244.8498
- EMail: mjs@cisco.com
-
- Yakov Rekhter
- Cisco Systems, Inc.
- 170 Tasman Dr.
- San Jose, CA 95134
- US
-
- Phone: 914.235.2128
- EMail: yakov@cisco.com
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Stapp & Rekhter Expires September 2000 [Page 18]
-
-Internet-Draft Interaction between DHCP and DNS March 2000
-
-
-Full Copyright Statement
-
- Copyright (C) The Internet Society (2000). All Rights Reserved.
-
- This document and translations of it may be copied and furnished to
- others, and derivative works that comment on or otherwise explain it
- or assist in its implmentation may be prepared, copied, published
- and distributed, in whole or in part, without restriction of any
- kind, provided that the above copyright notice and this paragraph
- are included on all such copies and derivative works. However, this
- document itself may not be modified in any way, such as by removing
- the copyright notice or references to the Internet Society or other
- Internet organizations, except as needed for the purpose of
- developing Internet standards in which case the procedures for
- copyrights defined in the Internet Standards process must be
- followed, or as required to translate it into languages other than
- English.
-
- The limited permissions granted above are perpetual and will not be
- revoked by the Internet Society or its successors or assigns.
-
- This document and the information contained herein is provided on an
- "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
- TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
- BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
- HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
- MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
-
-Acknowledgement
-
- Funding for the RFC editor function is currently provided by the
- Internet Society.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Stapp & Rekhter Expires September 2000 [Page 19]
-
diff --git a/doc/draft-ietf-dhc-failover-12.txt b/doc/draft-ietf-dhc-failover-12.txt
deleted file mode 100644
index 6d632e08..00000000
--- a/doc/draft-ietf-dhc-failover-12.txt
+++ /dev/null
@@ -1,7451 +0,0 @@
-
-
-
-
-
-
-Network Working Group Ralph Droms
-INTERNET DRAFT Kim Kinnear
- Mark Stapp
- Cisco Systems
-
- Bernie Volz
- Ericsson
-
- Steve Gonczi
- Relicore
-
- Greg Rabil
- Lucent Technologies
-
- Michael Dooley
- Diamond IP Technologies
-
- Arun Kapur
- K5 Networks
-
- March 2003
- Expires September 2003
-
-
- DHCP Failover Protocol
- <draft-ietf-dhc-failover-12.txt>
-
-Status of this Memo
-
- This document is an Internet-Draft and is in full conformance with
- all provisions of Section 10 of RFC2026.
-
- Internet-Drafts are working documents of the Internet Engineering
- Task Force (IETF), its areas, and its working groups. Note that
- other groups may also distribute working documents as Internet-
- Drafts.
-
- Internet-Drafts are draft documents valid for a maximum of six months
- and may be updated, replaced, or obsoleted by other documents at any
- time. It is inappropriate to use Internet- Drafts as reference
- material or to cite them other than as "work in progress."
-
- The list of current Internet-Drafts can be accessed at
- http://www.ietf.org/ietf/1id-abstracts.txt
-
- The list of Internet-Draft Shadow Directories can be accessed at
- http://www.ietf.org/shadow.html.
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 1]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-Copyright Notice
-
- Copyright (C) The Internet Society (2003). All Rights Reserved.
-
-Abstract
-
- DHCP [RFC 2131] allows for multiple servers to be operating on a
- single network. Some sites are interested in running multiple
- servers in such a way so as to provide redundancy in case of server
- failure. In order for this to work reliably, the cooperating primary
- and secondary servers must maintain a consistent database of the
- lease information. This implies that servers will need to coordinate
- any and all lease activity so that this information is synchronized
- in case of failover.
-
- This document defines a protocol to provide such synchronization
- between two servers. One server is designated the "primary" server,
- the other is the "secondary" server. This document also describes a
- way to integrate the failover protocol with the DHCP load balancing
- approach.
-
-
-Table of Contents
-
-
- 1. Introduction................................................. 4
- 2. Terminology.................................................. 5
- 2.1. Requirements terminology................................... 5
- 2.2. DHCP and failover terminology.............................. 5
- 3. Background and External Requirements......................... 9
- 3.1. Key aspects of the DHCP protocol........................... 9
- 3.2. BOOTP relay agent implementation........................... 11
- 3.3. What does it mean if a server can't communicate with its partner? 12
- 3.4. Challenging scenarios for a Failover protocol.............. 13
- 3.5. Using TCP to detect partner server failure................. 14
- 4. Design Goals................................................. 15
- 4.1. Design goals for this protocol............................. 15
- 4.2. Limitations of this protocol............................... 17
- 5. Protocol Overview............................................ 17
- 5.1. Messages and States........................................ 18
- 5.2. Fundamental guarantees..................................... 20
- 5.3. Load balancing............................................. 27
- 5.4. IP address allocations between servers..................... 28
- 5.5. Operating in NORMAL state.................................. 30
- 5.6. Operating in COMMUNICATIONS-INTERRUPTED state.............. 31
- 5.7. Operating in PARTNER-DOWN state............................ 31
-
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 2]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-
- 5.8. Operating in RECOVER state................................. 31
- 5.9. Operating in STARTUP state................................. 31
- 5.10. Time synchronization between servers...................... 32
- 5.11. IP address binding-status................................. 33
- 5.12. DNS dynamic update considerations......................... 36
- 5.13. Reservations and failover................................. 41
- 5.14. Dynamic BOOTP and failover................................ 42
- 5.15. Guidelines for selecting MCLT............................. 43
- 5.16. What is sent in response to an UPDREQ or UPDREQALL message? 43
- 5.17. How do you determine that your partner is "up to date" for 45
- 6. Common Message Format........................................ 45
- 6.1. Message header format...................................... 46
- 6.2. Common option format....................................... 48
- 6.3. Batching multiple binding update transactions in one BNDUPD mes- 49
- 7. Protocol Messages............................................ 51
- 7.1. BNDUPD message [3]......................................... 51
- 7.2. BNDACK message [4]......................................... 62
- 7.3. UPDREQ message [9]......................................... 65
- 7.4. UPDREQALL message [7]...................................... 66
- 7.5. UPDDONE message [8]........................................ 67
- 7.6. POOLREQ message [1]........................................ 68
- 7.7. POOLRESP message [2]....................................... 69
- 7.8. CONNECT message [5]........................................ 70
- 7.9. CONNECTACK message [6]..................................... 74
- 7.10. STATE message [10]........................................ 78
- 7.11. CONTACT message [11]...................................... 79
- 7.12. DISCONNECT message [12]................................... 80
- 8. Connection Management........................................ 81
- 8.1. Connection granularity..................................... 81
- 8.2. Creating the TCP connection................................ 81
- 8.3. Using the TCP connection for determining communications status 83
- 8.4. Using the TCP connection for binding data.................. 85
- 8.5. Using the TCP connection for control messages.............. 85
- 8.6. Losing the TCP connection.................................. 85
- 9. Failover Endpoint States..................................... 86
- 9.1. Server Initialization...................................... 86
- 9.2. Server State Transitions................................... 86
- 9.3. STARTUP state.............................................. 90
- 9.4. PARTNER-DOWN state......................................... 93
- 9.5. RECOVER state.............................................. 95
- 9.6. RECOVER-WAIT state......................................... 97
- 9.7. RECOVER-DONE state......................................... 98
- 9.9. COMMUNICATIONS-INTERRUPTED State........................... 101
- 9.10. POTENTIAL-CONFLICT state.................................. 105
- 9.11. RESOLUTION-INTERRUPTED state.............................. 107
- 9.12. CONFLICT-DONE state....................................... 108
- 9.13. PAUSED state.............................................. 108
-
-
-
-Droms, et. al. Expires September 2003 [Page 3]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- 9.14. SHUTDOWN state............................................ 109
- 10. Safe Period................................................. 110
- 11. Security.................................................... 111
- 11.1. Simple shared secret...................................... 112
- 11.2. TLS....................................................... 113
- 12. Failover Options............................................ 113
- 12.1. addresses-transferred..................................... 114
- 12.2. assigned-IP-address....................................... 114
- 12.3. binding-status............................................ 114
- 12.4. client-identifier......................................... 115
- 12.5. client-hardware-address................................... 115
- 12.6. client-last-transaction-time.............................. 115
- 12.7. client-reply-options...................................... 116
- 12.8. client-request-options.................................... 116
- 12.9. DDNS...................................................... 117
- 12.10. delayed-service-parameter................................ 118
- 12.11. hash-bucket-assignment................................... 118
- 12.12. IP-flags................................................. 119
- 12.13. lease-expiration-time.................................... 120
- 12.14. max-unacked-bndupd....................................... 120
- 12.15. MCLT..................................................... 120
- 12.16. message.................................................. 121
- 12.17. message-digest........................................... 121
- 12.18. potential-expiration-time................................ 122
- 12.19. receive-timer............................................ 122
- 12.20. protocol-version......................................... 122
- 12.21. reject-reason............................................ 123
- 12.22. relationship-name........................................ 124
- 12.23. server-flags............................................. 124
- 12.24. server-state............................................. 125
- 12.25. start-time-of-state...................................... 125
- 12.26. TLS-reply................................................ 126
- 12.27. TLS-request.............................................. 126
- 12.28. vendor-class-identifier.................................. 126
- 12.29. vendor-specific-options.................................. 127
- 13. IANA Considerations......................................... 127
- 14. Acknowledgments............................................. 127
- 15. References.................................................. 129
- 16. Author's information........................................ 131
- 17. Full Copyright Statement.................................... 132
-
-
-1. Introduction
-
- DHCP [RFC 2131] allows for multiple servers to be operating on a sin-
- gle network. Some sites are interested in running multiple servers
- in such a way so as to provide redundancy in case of server failure
- since the DHCP subsystem is in many cases a critical part of the
-
-
-
-Droms, et. al. Expires September 2003 [Page 4]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- network infrastructure.
-
- This document defines a protocol to provide synchronization between
- two servers in order that each can take over for the other should
- either one fail or become unreachable.
-
- One server is designated the "primary" server, the other is the
- "secondary" server, and most DHCP client requests are sent to each
- server (see section 3.1.1 for details).
-
- In order to provide a high availability DHCP service, these
- cooperating primary and secondary servers must maintain a consistent
- database of lease information. This implies that servers will need
- to coordinate all lease activity so that this information is syn-
- chronized in case failover is required. The protocol messages and
- processing techniques required to maintain a consistent database are
- specified in the protocol described here.
-
- The failover protocol also contains a way to integrate the DHCP load-
- balancing algorithm described in [RFC 3074] with the failover proto-
- col.
-
-2. Terminology
-
- This section discusses both the generic requirements terminology com-
- mon to many IETF protocol specifications as well as specialized DHCP
- and failover protocol specific terminology.
-
-2.1. Requirements terminology
-
- The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
- "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
- document are to be interpreted as described in RFC 2119 [RFC 2119].
-
-
-2.2. DHCP and failover terminology
-
- This document uses the following terms:
-
- o "available IP address"
-
- An IP address is "available" if it may be allocated by a
- specific DHCP server. An IP address is considered (for the
- purposes of this document) to be available to a single server
- for allocation unless otherwise noted. An IP address available
- for allocation on a primary server has state FREE, and an IP
- address available for allocation on a secondary server has
- state BACKUP.
-
-
-
-Droms, et. al. Expires September 2003 [Page 5]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- o "binding"
-
- A binding is a collection of configuration parameters, includ-
- ing at least an IP address, associated with or "bound to" a
- DHCP client. Bindings are managed by DHCP servers.
-
- o "binding database"
-
- The collection of bindings managed by a primary and secondary.
-
- o "binding update transaction"
-
- A binding update transaction refers to the set of information
- (contained in options) necessary to perform a binding update
- for a single IP address. It will be comprised of the
- assigned-IP-address option, the binding-status option, along
- with other options as appropriate.
-
- o "binding-status"
-
- The binding-status is the status of an IP address with respect
- to its association with a client. There are specific binding-
- status values defined for use by the failover protocol, e.g.,
- ACTIVE, FREE, RELEASED, ABANDONED, etc. These are designed to
- map more or less directly onto the binding-status values used
- internally in most DHCP server implementations. The term
- binding-status refers to the concept also sometimes known as
- "lease state" or "IP address state", but in this document the
- term "state" is reserved for the failover state of a failover
- endpoint, and binding-status is always used to refer to the
- state associated with an IP address or lease.
-
- o "DHCP client" or "client"
-
- A DHCP client is an Internet host using DHCP to obtain confi-
- guration parameters such as a network address. The term
- "client" used within this document always means a DHCP client,
- and never one of the two failover servers.
-
- o "DHCP server" or "server"
-
- A DHCP server is an Internet host that returns configuration
- parameters to DHCP clients.
-
- o "DDNS"
-
- An abbreviation for "Dynamic DNS", which refers to the capabil-
- ity to update a DNS server's name (actually resource record)
-
-
-
-Droms, et. al. Expires September 2003 [Page 6]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- database using an on-the-wire protocol defined in [RFC 2136].
-
- o "DNS"
-
- An abbreviation for "Domain Name System", a scheme where a cen-
- tral name repository is used to map names to IP addresses and IP
- addresses to names.
-
- o "failover endpoint"
-
- The failover protocol allows for there to be a unique failover
- endpoint per partner per role per relationship (where role is
- primary or secondary and the relationship is defined by the
- relationship-name option). This failover endpoint can take
- actions and hold unique states. Typically, there is a one fail-
- over endpoint per partner, although there may be more.
-
- o "FQDN"
-
- An FQDN is a "fully qualified domain name". A fully qualified
- domain name generally is a host name with at least one zone
- name, for example "www.dhcp.org" is a fully qualified domain
- name.
-
- o "lazy update"
-
- Lazy update refers to the requirement placed on a server imple-
- menting a failover protocol to update its failover partner when-
- ever the binding database changes. A failover protocol which
- didn't support lazy update would require the failover partner
- update to be complete before a DHCP server could respond to a
- DHCP client request with a DHCPACK. A failover protocol which
- does support lazy update places no such restriction on the
- update of the failover partner server, and so a server can allo-
- cate an IP address or extend a lease on an IP address and then
- update its failover partner as time permits. A failover proto-
- col which supports lazy update not only removes the requirement
- to update the failover partner prior to responding to a DHCP
- client with a DHCPACK, but also allows gathering up batches of
- updates from one failover server to its partner.
-
- o "MCLT"
-
- The MCLT refers to maximum client lead time. This time is con-
- figured on the primary server and transmitted from the primary
- to the secondary server in the CONNECT message. It is the max-
- imum amount of time that one server can extend a lease for a
- client's binding beyond the time known by the partner server.
-
-
-
-Droms, et. al. Expires September 2003 [Page 7]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- See section 5.2.1 for details.
-
- o "partner"
-
- A "partner", for the purposes of this document, refers to a
- failover server, typically the other failover server. In many
- (if not most) cases, the failover protocol is symmetric with
- respect to the primary or secondary nature of the servers, and
- so it is often appropriate to discuss "updating the partner
- server", since it could be a primary server updating a secondary
- server or a secondary server updating a primary server.
-
- o "Primary server" or "Primary"
-
- A DHCP server configured to provide primary service to a set of
- DHCP clients for a particular set of subnet address pools.
-
- o "RR"
-
- "RR" is an abbreviation for "resource record". All records in
- the DNS are resource records. The resource records of most
- relevance to this document are the "A" resource record, which
- maps a DNS name to a particular IP address, the "PTR" resource
- record, which allows a "reverse map", from the IP address back
- to a DNS name, and the "KEY" resource record, which is used in
- ways defined in [FQDN] to tag a DNS name with the identity of
- the DHCP client with which it is associated.
-
- o "Secondary server" or "Secondary"
-
- A DHCP server configured to act as backup to a primary server
- for a particular set of subnet address pools.
-
- o "stable storage"
-
- Every DHCP server is assumed to have some form of what is called
- "stable storage". Stable storage is used to hold information
- concerning IP address bindings (among other things) so that this
- information is not lost in the event of a server failure which
- requires restart of the server.
-
- o "state"
-
- In this document, the term "state" refers exclusively to the
- state of a failover endpoint, for example: NORMAL,
- COMMUNICATIONS-INTERRUPTED, PARTNER-DOWN. It is not used to
- refer to any attributes of an IP address or a binding of an IP
- address. See "binding-status".
-
-
-
-Droms, et. al. Expires September 2003 [Page 8]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- o "subnet address pool"
-
- A subnet address pool is the set of IP addresses which is asso-
- ciated with a particular network number and subnet mask. In the
- simple case, there is a single network number and subnet mask
- and a set of IP addresses. In the more complex case (sometimes
- called "secondary subnets", sometimes "superscopes"), several
- (apparently unrelated) network number and subnet mask combina-
- tions with their associated IP addresses may all be configured
- together into one subnet address pool.
-
-
-3. Background and External Requirements
-
- This section highlights key aspects of the DHCP protocol on which the
- failover protocol depends. It also discusses the requirements that
- the failover protocol places on other aspects of the network infras-
- tructure, and some general issues surrounding server failure detec-
- tion. Some failure scenarios that provide particular challenges to a
- failover protocol are discussed. Finally, the challenges inherent in
- using a TCP connection as a means to detect failure of a partner
- server are elaborated.
-
-3.1. Key aspects of the DHCP protocol
-
- The failover protocol is designed to augment the DHCP protocol as
- described in RFC 2131 [RFC 2131]. There are several key aspects of
- the DHCP protocol which are required by the failover protocol in
- order to successfully meet its design goals.
-
-3.1.1. Broadcast behavior
-
- There are two aspects of the broadcast behavior of the DHCP protocol
- which are key to making the failover protocol operate successfully.
- The first is simply that the DHCP protocol requires a DHCP client to
- broadcast all DHCPDISCOVER and DHCPREQUEST/INIT-REBOOT messages.
- Because of this requirement, a DHCP client who was communicating with
- one server will automatically be able to communicate with another
- server if one is available.
-
- The second aspect of broadcast behavior is similar to the first, but
- involves the distinction between a DHCPREQUEST/RENEW and
- DHCPREQUEST/REBINDING. A DHCPREQUEST/RENEW is the message that a
- DHCP client uses to extend its lease. It is unicast to the DHCP
- server from which it acquired the lease. However, the DHCP protocol
- (in a farsighted move), was explicitly designed so that in the event
- that a DHCP client cannot contact the server from which it received a
- lease on an IP address using a DHCPREQUEST/RENEW, the client is
-
-
-
-Droms, et. al. Expires September 2003 [Page 9]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- required to broadcast its renewal using a DHCPREQUEST/REBINDING to
- any available DHCP server. Since all DHCP clients were required to
- implement this algorithm, the failover protocol can have a different
- server from the one that initially granted a lease be the server to
- renew a lease. Thus, one server can take over for another with no
- interruption in the service as experienced by the DHCP client or its
- associated applications software.
-
-3.1.2. Client responsibility
-
- In the DHCP protocol the DHCP clients are entrusted with a consider-
- able responsibility. In particular, after they are granted a lease
- on an IP address, they are enjoined to only use that IP address while
- their lease is valid. Every DHCP client is expected to stop using an
- IP address if the expiration time on the lease has passed and if it
- cannot get an extension on the lease for that IP address from some
- DHCP server. Thus, the correct behavior of every DHCP client in this
- regard is required to ensure the integrity of the DHCP service. On
- the other hand, incorrect behavior by a client in this area will tend
- to adversely affect at most one other DHCP client.
-
- Furthermore, any DHCP client which sends in a DHCPREQUEST/RENEW or
- DHCPREQUEST/REBINDING to a DHCP server (either unicast for a RENEW or
- broadcast for a REBINDING) MUST still have time to run on the lease
- for that IP address. The DHCP server sends the DHCPACK back unicast
- to the IP address from which the RENEW or REBINDING originated.
-
- Given the existing responsibility placed on the client to only use an
- IP address when the lease is valid, and to only send in a RENEW or
- REBINDING if the lease is valid, the failover protocol relies on DHCP
- clients to perform responsibly and will, in the absence of conflict-
- ing information, believe a DHCP client that is attempting to RENEW or
- REBIND a lease on an IP address is the legitimate owner of that IP
- address.
-
- If clients do not follow these rules, it is possible for an address
- to be in use by more than one client. For a single server, this hap-
- pens because the server has leased the expired address to another
- client and the original client is also attempting to use the address.
- The server would NAK the renewal request. This is made slightly worse
- in the failover protocol if the two servers are unable to communicate
- with each other and one server leases an available address to a new
- client while the other server receives a renewal from a different
- client. In this case, both servers lease the same address to dif-
- ferent clients for the MCLT time.
-
- One troublesome issue is that of the DHCP client responsibility when
- sending in DHCPREQUEST/INIT-REBOOT requests. While the original DHCP
-
-
-
-Droms, et. al. Expires September 2003 [Page 10]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- RFC was written to require a DHCP client to have time left to run on
- the lease for an IP address if the client is sending an INIT-REBOOT
- request, it was sufficiently unclear that some client vendors didn't
- realize this until recently. Since the INIT-REBOOT request was sent
- with the IP address in the dhcp-requested-address option and not in
- the ciaddr (for perfectly good reasons), the similarity to the RENEW
- and REBINDING case was lost on many people.
-
- At present, the failover protocol does not assume that a client send-
- ing in an INIT-REBOOT request necessarily has a valid lease on the IP
- address appearing in the dhcp-requested-address option in the INIT-
- REBOOT request.
-
- The implications of this are as follows: Assume that there is a DHCP
- client that gets a lease from one server while that server is unable
- to communicate with its failover partner. Then, assume that after
- that client reboots it is able only to communicate with the other
- failover server. If the failover servers have not been able to com-
- municate with each other during this process, then the DHCP client
- will get a new IP address instead of being able to continue to use
- its existing IP address. This will affect no applications on the DHCP
- client, since it is rebooting. However, it will use up an additional
- IP address in this marginal case.
-
-3.1.3. Stable storage update before DHCPACK
-
- The DHCP protocol allocates resources, and in order to operate
- correctly it requires that a DHCP server update some form of stable
- storage prior to sending a DHCPACK to a DHCP client in order to grant
- that client a lease on an IP address.
-
- One of the goals of the failover protocol is that it not add signifi-
- cant additional time to this already time consuming requirement to
- update stable storage prior to a DHCPACK. In particular, adding a
- requirement to communicate with another server prior to sending a
- DHCPACK would greatly simplify the failover protocol, but it would
- unacceptably limit the potential scalability of any DHCP server which
- employed the failover protocol.
-
-3.2. BOOTP relay agent implementation
-
- Many DHCP clients are not resident on the same network segment as a
- DHCP server. In order to support this form of network architecture,
- most contemporary routers implement something known as a BOOTP Relay
- Agent. This capability inside of a router listens for all broadcasts
- at the DHCP port, port 67, and will relay any broadcasts that it
- receives on to a DHCP server. The IP address of the DHCP server must
- have been previously configured into the router. As part of the
-
-
-
-Droms, et. al. Expires September 2003 [Page 11]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- relay process, the relay agent will place the address of the inter-
- face on which it received the broadcast into the giaddr field of the
- DHCP packet.
-
- Since the failover protocol requires two DHCP servers to receive any
- broadcast DHCP messages, in order to work with DHCP clients which are
- not local to the DHCP server, the BOOTP relay agent on the router
- closest to the DHCP client must be configured to point at more than
- one DHCP server.
-
- Most BOOTP relay agent implementations allow this duplication of
- packets.
-
- If this is not possible, an administrator might be able to configure
- the relay agent with a subnet broadcast address, but in this case the
- primary and secondary DHCP servers in a failover pair must both
- reside on the same subnet.
-
-3.3. What does it mean if a server can't communicate with its partner?
-
- In any protocol designed to allow one server to take over some
- responsibilities from a partner server in the event of "failure" of
- that partner server, there is an inherent difficulty in determining
- when that partner server has failed.
-
- In fact, it is fundamentally impossible for one server to distinguish
- a network communications failure from the outright failure of the
- server to which it is trying to communicate. In the case where each
- server is handing out resources (in this case IP addresses) to a
- client community, mistaking an inability to communicate with a
- partner server for failure of that partner server could easily cause
- both servers to be handing out the same IP addresses to different
- clients.
-
- One way that this is sometimes handled is for there to be more than
- two servers. In the case of an odd number of servers, the servers
- that can still communicate with a majority of other servers will con-
- sider themselves operational, and any server which can't communicate
- to a majority of other servers must immediately cease operations.
-
- While this technique works in some domains, having the only server to
- which a DHCP client can communicate voluntarily shut itself down
- seems like something worth avoiding.
-
- The failover protocol will operate correctly while both servers are
- unable to communicate, whether they are both running or not. At some
- point there may be resource contention, and if one of the servers is
- actually down, then the operator can inform the operational server
-
-
-
-Droms, et. al. Expires September 2003 [Page 12]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- and the operational server will be able to use all of the failed
- server's resources.
-
- The protocol also allows detection of an orderly shutdown of a parti-
- cipating server.
-
-3.4. Challenging scenarios for a Failover protocol
-
- There exist two failure scenarios which provide particular challenges
- to the correctness guarantees of a failover protocol.
-
-3.4.1. Primary Server crash before "lazy" update:
-
- In the case where the primary server sends a DHCPACK to a client for
- a newly allocated IP address and then crashes prior to sending the
- corresponding update to the secondary server, the secondary server
- will have no record of the IP address allocation. When the secondary
- server takes over, it may well try to allocate that IP address to a
- different client. In the case where the first client to receive the
- IP address is not on the net at the time (yet while there was still
- time to run on its lease), an ICMP echo (i.e., ping) will not prevent
- the secondary server from allocating that IP address to a different
- client.
-
- The failover protocol deals with this situation by having the primary
- and secondary servers allocate addresses for new clients from dis-
- joint address pools. See section 5.5 for details.
-
- A more likely (in that DHCPREQUEST/RENEWs are presumably more common
- than DHCPDISCOVERs) and more subtle version of this problem is where
- the primary server crashes after extending a client's lease time, and
- before updating the secondary with a new time using a lazy update.
- After the secondary takes over, if the client is not connected to the
- network the secondary will believe the client's lease has expired
- when, in fact, it has not. In this case as well, the IP address
- might be reallocated to a different client while the first client is
- still using it.
-
- This scenario is handled by the failover protocol through control of
- the lease time and the use of the maximum client lead time (MCLT).
- See section 5.2.1 for details.
-
-3.4.2. Network partition where DHCP servers can't communicate but each
-can talk to clients:
-
- Several conditions are required for this situation to occur. First,
- due to a network failure, the primary and secondary servers cannot
- communicate. As well, some of the DHCP clients must be able to
-
-
-
-Droms, et. al. Expires September 2003 [Page 13]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- communicate with the primary server, and some of the clients must now
- only be able to communicate with the secondary server. When this
- condition occurs, both primary and secondary servers could attempt to
- allocate IP addresses for new clients from the same pool of available
- addresses. At some point, then, two clients will end up being allo-
- cated the same IP address. This will cause problems when the network
- failure that created this situation is corrected.
-
- The failover protocol deals with this situation by having the primary
- and secondary servers allocate addresses for new clients from dis-
- joint address pools. See section 5.5 for details.
-
-3.5. Using TCP to detect partner server failure
-
- There are several characteristics of TCP that are important to the
- functioning of the failover protocol, which uses one TCP connection
- for both bulk data transfer as well as to assess communications
- integrity with the other server. Reliable and ordered message
- delivery are chief among these important characteristics.
-
- It would be nice to use the capabilities built in to TCP to allow it
- to determine if communications integrity exists to the failover
- partner but this strategy contains some problems which require
- analysis. There exist three fundamental cases for an open TCP con-
- nection that must be examined.
-
- 1. When no data is being sent on a TCP connection, the TCP layer
- also does not exchange any signaling messages to assure that
- the peer is still up.
-
- 2. When data is queued to be sent, and the receiver has not
- blocked the sending of additional data, then messages are
- flowing across the TCP connection containing the applications
- data.
-
- 3. When data is queued to be sent, and the receiver has blocked
- the transmission of additional data, then persist messages are
- flowing from the receiver to the sender to ensure that the
- sender doesn't miss the receiver opening the window for
- further transmissions.
-
- The first case can be turned into the second case by sending
- application-level keep-alive messages periodically when there is no
- other data queued to be sent. Note TCP keep-alive messages might be
- used as well, but they present additional problems.
-
- Thus, we can ensure that the TCP connection has messages flowing
- periodically across the connection fairly easily. The question
-
-
-
-Droms, et. al. Expires September 2003 [Page 14]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- remains as to what TCP will do if the other end of the connection
- fails to respond (either because of network partition or because the
- receiving server crashes). TCP will attempt to retransmit a message
- with an exponential backoff, and will eventually timeout that
- retransmission. However, the length of that timeout cannot, in gen-
- eral, be set on a per-connection basis, and is frequently as long as
- nine minutes, though in some cases it may be as short as two minutes.
- On some systems it can be set system-wide, while on other systems it
- cannot be changed at all.
-
- A value for this timeout that would be appropriate for the failover
- protocol, say less than 1 minute, could have unpleasant side-effects
- on other applications running on the same server, assuming that it
- could be changed at all on the host operating system.
-
- Nine minutes is a long time for the DHCP service to be unavailable to
- any new clients that were being served by the server which has
- crashed, when there is another server running that could respond to
- them as soon as it determines that its partner is not operational.
-
- The conclusion drawn from this analysis is that TCP provides very
- useful support for the failover protocol in the areas of reliable and
- ordered message delivery, but cannot by itself be relied upon to
- detect partner server failure in a fashion acceptable to the needs of
- the failover protocol. Additional failover protocol capabilities
- have been created to support timely detection of partner server
- failure. See section 8.3 for details on this mechanism.
-
-4. Design Goals
-
- This section lists the design goals and the limitations of the fail-
- over protocol.
-
-4.1. Design goals for this protocol
-
- The following is a list of goals that are met by this protocol. They
- are listed in priority order.
-
- 1. Implementations of this protocol must work with existing DHCP
- client implementations based on the DHCP protocol [RFC 2131].
-
- 2. Implementations of the protocol must work with existing BOOTP
- relay agent implementations.
-
- 3. The protocol must provide failover redundancy between servers
- that are not located on the same subnet.
-
- 4. Provide for continued service to DHCP clients through an
-
-
-
-Droms, et. al. Expires September 2003 [Page 15]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- automated mechanism in the event of failure of the primary
- server.
-
- 5. Avoid binding an IP address to a client while that binding is
- currently valid for another client. In other words, do not
- allocate the same IP address to two clients.
-
- 6. Minimize any need for manual administrative intervention.
-
- 7. Introduce no additional delays in server response time as a
- result of the network communications required to implement the
- failover protocol, i.e., don't require communications with the
- partner between the receipt of a DHCPREQUEST and the
- corresponding DHCPACK.
-
- 8. Share IP address ranges between primary and secondary servers;
- i.e., impose no requirement that the pool of available
- addresses be manually or permanently divided between servers.
-
- 9. Continue to meet the goals and objectives of this protocol in
- the event of server failure or network partition.
-
- 10. Provide graceful reintegration of full protocol service after
- server failure or network partition.
-
- 11. Allow for one computer to act as a secondary server for multi-
- ple primary servers. The protocol must allow failover primary
- and secondary configuration choices to be made at a granular-
- ity smaller than "all of the subnets served by a single
- server", though individual implementations may not choose to
- allow such flexibility.
-
- 12. Ensure that an existing client can keep its existing IP
- address binding if it can communicate with either the primary
- or secondary DHCP server implementing this protocol - not just
- whichever server that originally offered it the binding.
-
- 13. Ensure that a new client can get an IP address from some
- server. Ensure that in the face of partition, where servers
- continue to run but cannot communicate with each other, the
- above goals and requirements may be met. In addition, when
- the partition condition is removed, allow graceful automatic
- re-integration without requiring human intervention.
-
- 14. If either primary or secondary server loses all of the infor-
- mation that it has stored in stable storage, ensure that it be
- able to refresh its stable storage from the other server.
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 16]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- 15. Support load balancing between the primary and secondary
- servers, and allow configuration of the percentage of the
- client population served by each with a moderately fine granu-
- larity.
-
-
-4.2. Limitations of this protocol
-
- The following are explicit limitations of this protocol.
-
- 1. This protocol provides only one level of redundancy through a
- single secondary server for each primary server.
-
- 2. A subset of the address pool is reserved for secondary server
- use. In order to handle the failure case where both servers
- are able to communicate with DHCP clients, but unable to com-
- municate with each other, a subset of the IP address pool must
- be set aside as a private address pool for the secondary
- server. The secondary can use these to service newly arrived
- DHCP clients during such a period. The required size of this
- private pool is based only on the arrival rate of new DHCP
- clients and the length of expected downtime, and is not influ-
- enced in any way by the total number of DHCP clients supported
- by the server pair.
-
- The failover protocol can be used in a mode where both the
- primary and secondary servers can share the load between them
- when both are operating. In this load balancing mode, the
- addresses allocated by the primary server to the secondary
- server are not unused, but are used instead to service the
- portion of the client base to which the secondary server is
- required to respond. See section 5.3 for more information on
- load balancing.
-
- 3. The primary and secondary servers do not respond to client
- requests at all while recovering from a failure that could
- have resulted in duplicate IP assignments. (When synchroniz-
- ing in POTENTIAL-CONFLICT state).
-
-
-5. Protocol Overview
-
- This section will discuss the failover protocol at a relatively high
- level of detail. In the event that a description in this section
- conflicts (or appears to conflict due to the overview nature of this
- section) with information in later sections of this draft, the infor-
- mation in the later sections should be considered authoritative.
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 17]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-5.1. Messages and States
-
- This protocol is centered around the message exchange used by one
- server to update the other server of binding database changes result-
- ing from DHCP client activity:
-
- o Communication of binding database changes
-
- The binding update (BNDUPD) message is used to send the binding
- database changes to the partner server, and the partner server
- responds with a binding acknowledgement (BNDACK) message when it
- has successfully committed those changes to its own stable
- storage.
-
- All of the other messages involve ancillary issues:
-
- o Management of available IP addresses
-
- The pool request (POOLREQ) message is used by the secondary
- server to request an allocation of IP addresses from the primary
- server. The pool response (POOLRESP) message is used by the
- primary server to inform the secondary server how many IP
- addresses were allocated to the secondary server as the result
- of the pool request.
-
- o Synchronization of the binding databases between the servers
- after they've been out of communications
-
- The update request (UPDREQ) message is used by one server to
- request that its partner send it all binding database informa-
- tion that it has not already seen. The update request all
- (UPDREQALL) message is used by one server to request that all
- binding database information be sent in order to recover from a
- total loss of its binding database by the requesting server.
- The update done (UPDDONE) message is used by the responding
- server to indicate that all requested updates have been sent the
- responding server and acked by the requesting server.
-
- o Connection establishment
-
- The connect (CONNECT) message is used by the primary server to
- establish a high level connection with the other server, and to
- transmit several important configuration data items between the
- servers. The connect acknowledgement message (CONNECTACK) is
- used by the secondary server to respond to a CONNECT message
- from the primary server. The disconnect (DISCONNECT) message is
- used by either server when closing a connection.
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 18]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- o Server synchronization
-
- The state change (STATE) message is used by either server to
- inform the other server of a change of failover state.
-
- o Connection integrity management
-
- The contact (CONTACT) message is used by either server to ensure
- that the other server continues to see the connection as opera-
- tional. It MUST be transmitted periodically over every esta-
- blished connection if other message traffic is not flowing, and
- it MAY be sent at any time.
-
-5.1.1. Failover endpoints
-
- The proper operation of the failover protocol requires more than the
- transmission of messages between one server and the other. Each end-
- point might seem to be a single DHCP server, but in fact there are
- many situations where additional flexibility in configuration is use-
- ful.
-
- For instance, there might be several servers which are each primary
- for a distinct set of address pools, and one server which is secon-
- dary for all of those address pools. The situation with the pri-
- maries is straightforward, but the secondary will need to maintain a
- separate failover state, partner state, and communications up/down
- status for each of the separate primary servers for which it is act-
- ing as a secondary.
-
- The failover protocol is SHOULD be configured with one failover rela-
- tionship between each pair of failover servers. In this case there is
- one failover endpoint for that relationship on each partner. This
- failover relationship MUST have a unique name, which is communicated
- using the relationship-name option in the CONNECT and CONNECTACK mes-
- sages.
-
- There is typically little need for addtional relationships between
- any two servers but there MAY be more than one failover relationship
- between two servers -- however each MUST have a unique relationship
- name (stored in the relationship-name option).
-
- Any failover endpoint can take actions and hold unique states.
-
- Thus, in the case where there are two primary servers A and B each
- backed up by a single common secondary server C, there is one fail-
- over endpoint on each of A and B, and two different failover end-
- points on C. The two different failover endpoints on C each have
- unique states, unique relationship names, and independent TCP
-
-
-
-Droms, et. al. Expires September 2003 [Page 19]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- connections.
-
- This document frequently describes the behavior of the protocol in
- terms of primary and secondary servers, not primary and secondary
- failover endpoints. However, it is important to remember that every
- 'server' described in this document is in reality a failover endpoint
- that resides in a particular process, and that many failover end-
- points may reside in the same server process.
-
- It is not the case that there is a unique failover endpoint for each
- subnet address pool that participates in a failover relationship. On
- one server, there is (typically) one failover endpoint per partner,
- regardless of how many subnet address pools are managed by that com-
- bination of partner and role. Conversely, on a particular server,
- any given subnet address pool will be associated with exactly one
- failover endpoint.
-
- When a connection is received from the partner, the unique failover
- endpoint to which the message is directed is determined solely by the
- IP address of the partner, the relationship-name, and the role of the
- receiving server. See section 8.2.
-
-5.2. Fundamental guarantees
-
- There a several fundamental restrictions this protocol places on what
- one server can do in the absence of knowledge of the other server.
- Operating within these restrictions allows certain guarantees to be
- made to the partner server, and these are key to the correct opera-
- tion of the protocol.
-
-5.2.1. Control of lease time
-
- The key problem with lazy update is that when a server fails after
- updating a client with a particular lease time and before updating
- its partner, the partner will believe that a lease has expired even
- though the client still retains a valid lease on that IP address.
-
- In order to handle this problem, a period of time known as the "Max-
- imum Client Lead Time" (MCLT) is defined and must be known to both
- the primary and secondary servers. Proper use of this time interval
- places an upper bound on the difference allowed between the lease
- time provided to a DHCP client by a server and the lease time known
- by that server's partner. However, the MCLT is typically much less
- than the lease time that a server has been configured to offer a
- client, and so some strategy must exist to allow a server to offer
- the configured lease time to a client. During a lazy update the
- updating server typically updates its partner with a potential
- expiration time which is longer than the lease time previously given
-
-
-
-Droms, et. al. Expires September 2003 [Page 20]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- to the client and which is longer than the lease time that the server
- has been configured to give a client. This allows that server to
- give a longer lease time to the client the next time the client
- renews its lease, since the time that it will give to the client will
- not exceed the MCLT beyond the potential expiration time acknowledged
- by its partner.
-
- The PARTNER-DOWN state exists so that a server can be sure that its
- partner is, indeed, down. Correct operation while in that state
- requires (generally) that the server wait the MCLT after anything
- that happened prior to its transition into PARTNER-DOWN state (or,
- more accurately, when the other server went down if that is known).
- Thus, the server MUST wait the MCLT after the partner server went
- down before allocating any of the partner's addresses which were
- available for allocation. In the event the partner was not in com-
- munication prior to going down, it might have allocated one or more
- of its FREE addresses to a DHCP client and been unable to inform the
- server entering PARTNER-DOWN prior to going down itself. By waiting
- the MCLT after the time the partner went down, the server in
- PARTNER-DOWN state ensures that any clients which have a lease on one
- of the partner's FREE addresses will either time out or contact the
- server in PARTNER-DOWN by the time that period ends.
-
- In addition, once a server has made a transition to PARTNER-DOWN
- state, it MUST NOT reallocate an IP address from one client to
- another client until the longer of the following two times:
-
- o The MCLT after the time the partner server went down (see
- above).
-
- o An additional MCLT interval after the lease by the original
- client expires. (Actually, until the maximum client lead time
- after what it believes to be the lease expiration time of the
- client.)
-
- Some optimizations exist for this restriction, in that it only
- applies to leases that were issued BEFORE entering PARTNER-DOWN. Once
- a server has entered PARTNER-DOWN and it leases out an address, it
- need not wait this time as long as it has never communicated with the
- partner since the lease was given out.
-
- The fundamental relationship on which much of the correctness of this
- protocol depends is that the lease expiration time known to a DHCP
- client MUST NOT be more than the maximum client lead time greater
- than the potential expiration time known to a server's partner.
-
- The remainder of this section makes the above fundamental relation-
- ship more explicit.
-
-
-
-Droms, et. al. Expires September 2003 [Page 21]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- This protocol requires a DHCP server to deal with several different
- lease intervals and places specific restrictions on their relation-
- ships. The purpose of these restrictions is to allow the other server
- in the pair to be able to make certain assumptions in the absence of
- an ability to communicate between servers.
-
- The different lease times are:
-
- o desired lease interval
-
- The desired lease interval is the lease interval that a DHCP server
- would like to give to a DHCP client in the absence of any restric-
- tions imposed by the Failover protocol. Its determination is out-
- side of the scope of this protocol. Typically this is the result of
- external configuration of a DHCP server.
-
- o actual lease interval
-
- The actual lease internal is the lease interval that a DHCP server
- gives out to a DHCP client in the dhcp-lease-time option of a
- DHCPACK packet. It may be shorter than the desired client lease
- interval (as explained below).
-
- o potential lease interval
-
- The potential lease interval is the lease expiration interval the
- local server tells to its partner in the potential-expiration-time
- option of a BNDUPD message.
-
- o acknowledged potential lease interval
-
- The acknowledged potential lease interval is the potential lease
- interval the partner server has most recently acknowledged in the
- potential-expiration-time option of a BNDACK message.
-
- The key restriction (and guarantee) that any server makes with
- respect to lease intervals is that the actual client lease interval
- never exceeds the acknowledged potential lease interval (if any) by
- more than a fixed amount. This fixed amount is called the "Maximum
- Client Lead Time" (MCLT).
-
- The MCLT MAY be configurable on the primary server, but for correct
- server operation it MUST be the same and known to both the primary
- and secondary servers. The secondary server determines the MCLT from
- the MCLT option sent from the primary server to the secondary server
- in the CONNECT message.
-
- A server MUST record in its stable storage both the actual lease
-
-
-
-Droms, et. al. Expires September 2003 [Page 22]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- interval and the most recently acknowledged potential lease interval
- for each IP address binding. It is assumed that the desired client
- lease interval can be determined through techniques outside of the
- scope of this protocol. See section 7.1.5 for more details concern-
- ing the times that the server MUST record in its stable storage and
- the way that they interact with the lease time that may be offered to
- a DHCP client.
-
- Again, the fundamental relationship among these times which MUST be
- maintained is:
-
- actual lease interval <
- ( acknowledged potential lease interval + MCLT )
-
-
- Figure 5.2.1-1 illustrates an initial lease to a client using the
- rules discussed in the example which follows it. Note that this is
- only one example -- as long as the fundamental relationship is
- preserved, the actual times used could be quite different.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 23]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-
- DHCP Primary Secondary
- time Client Server Server
-
- | (time in intervals) | (absolute time) |
- | | |
- | >-DHCPDISCOVER-> | |
- | <---DHCPOFFER-< | |
- | lease-time=MCLT | |
- | | |
- | >-DHCPREQUEST-> | |
- | (selecting) | |
- | | |
- t | <--------DHCPACK-< | |
- | lease-time=MCLT | |
- | | >-BNDUPD--> |
- | | lease-expiration=t+MCLT
- | | potential-expiration=t+(MCLT/2)+X
- | | |
- | | <-BNDACK-< |
- | | potential-expiration=t+(MCLT/2)+X
- ... ... ...
- | | |
- t+MCLT/2 | >-DHCPREQUEST-> | |
- | (renew) | |
- | | |
- t1 | <--------DHCPACK-< | |
- | lease-time=X | |
- | | >-BNDUPD--> |
- | | lease-expiration=t1+X
- | | potential-expiration=t1+(X/2)+X
- | | |
- | | <-BNDACK-< |
- | | potential-expiration=t1+(X/2)+X
- ... ... ...
-
- Figure 5.2.1-1: Lazy Update Message Traffic
- X = Desired Lease Interval
- Assumes renewal interval = lease interval / 2
-
-
- DISCUSSION:
-
- This protocol mandates only that the above fundamental relation-
- ship concerning lease intervals is preserved.
-
- In the interests of clarity, however, let's examine a specific
- example. The MCLT in this case is 1 hour. The desired lease
-
-
-
-Droms, et. al. Expires September 2003 [Page 24]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- interval is 3 days, and its renewal time is half the lease inter-
- val.
-
- The rules for this example are:
-
- o What to tell the client:
-
- Take the remainder of the acknowledged potential lease interval.
- If this is a new lease, then this value will be zero. If this
- remainder plus the MCLT is greater than the desired lease inter-
- val, give the client the desired lease interval else give the
- client the remainder plus the MCLT.
-
- o What to tell the failover partner server:
-
- Take the renewal interval (typically half of the actual client
- lease interval), add to it the desired lease interval, and add
- it to the current time to yield the value that goes into the
- potential-expiration-time option.
-
- Also tell the failover partner the actual lease interval by
- adding it to the current time to yield the value that goes into
- the lease-expiration option.
-
- In operation this might work as follows:
-
- When a server makes an offer for a new lease on an IP address to a
- DHCP client, it determines the desired lease interval (in this
- case, 3 days). It then examines the acknowledged potential lease
- interval (which in this case is zero) and determines the remainder
- of the time left to run, which is also zero. To this it adds the
- MCLT. Since the actual lease interval cannot be allowed to exceed
- the remainder of the current acknowledged potential lease interval
- plus the MCLT, the offer made to the client is for the remainder
- of the current acknowledged potential lease interval (i.e., zero)
- plus the MCLT. Thus, the actual lease interval is 1 hour.
-
- Once the server has performed the DHCPACK to the DHCP client, it
- will update the secondary server with the lease information. How-
- ever, the desired potential lease interval will be composed of one
- half of the current actual lease interval added to the desired
- lease interval. Thus, the secondary server is updated with a
- BNDUPD with a lease interval of 3 days + 1/2 hour specified in the
- potential-expiration-time option.
-
- When the primary server receives a BNDACK to its update of the
- secondary server's (partner's) potential lease interval, it
- records that as the acknowledged potential lease interval. A
-
-
-
-Droms, et. al. Expires September 2003 [Page 25]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- server MUST NOT send a BNDACK in response to a BNDUPD message
- until it is sure that the information in the BNDUPD message
- resides in its stable storage. Thus, the primary server in this
- case can be sure that the secondary server has recorded the poten-
- tial lease interval in its stable storage when the primary server
- receives a BNDACK message from the secondary server.
-
- When the DHCP client attempts to renew at T1 (approximately one
- half an hour from the start of the lease), the primary server
- again determines the desired lease interval, which is still 3
- days. It then compares this with the remaining acknowledged
- potential lease interval (3 days + 1/2 hour) and adjusts for the
- time passed since the secondary was last updated (1/2 hour). Thus
- the time remaining of the acknowledged potential lease interval is
- 3 days. Adding the MCLT to this yields 3 days plus 1 hour, which
- is more than the desired lease interval of 3 days. So the client
- is renewed for the desired lease interval -- 3 days.
-
- When the primary DHCP server updates the secondary DHCP server
- after the DHCP client's renewal ACK is complete, it will calculate
- the desired potential lease interval as the T1 fraction of the
- actual client lease interval (1/2 of 3 days this time = 1.5 days).
- To this it will add the desired client lease interval of 3 days,
- yielding a total desired partner server lease interval of 4.5
- days. In this way, the primary attempts to have the secondary
- always "lead" the client in its understanding of the client's
- lease interval so as to be able to always offer the client the
- desired client lease interval.
-
- Once the initial actual client lease interval of the MCLT is past,
- the protocol operates effectively like the DHCP protocol does
- today in its behavior concerning lease intervals. However, the
- guarantee that the actual client lease interval will never exceed
- the remaining acknowledged partner server lease interval by more
- than the MCLT allows full recovery from a variety of failures.
-
-5.2.2. Controlled re-allocation of IP addresses
-
- When in PARTNER-DOWN state there is a waiting period after which an
- IP address can be re-allocated to another client. For IP addresses
- which are available when the server enters PARTNER-DOWN state, the
- period is the MCLT from entry into PARTNER-DOWN state. For IP
- addresses which are not available when the server enters PARTNER-DOWN
- state, the period is the MCLT after the IP address becomes available.
- See section 9.4.2 for more details.
-
- In any other state, a server cannot reallocate an address from one
- client to another without first notifying its partner (through a
-
-
-
-Droms, et. al. Expires September 2003 [Page 26]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- BNDUPD message) and receiving acknowledgement (through a BNDACK mes-
- sage) that its partner is aware that that first client is not using
- the address.
-
- This could be modeled in the following way. Though this specific
- implementation is in no way required, it may serve to better illus-
- trate the concept.
-
- An "available" IP address on a server may be allocated to any client.
- An IP address which was leased to a client and which expired or was
- released by that client would take on a new state, EXPIRED or
- RELEASED respectively. The partner server would then be notified
- that this IP address was EXPIRED or RELEASED through a BNDUPD. When
- the sending server received the BNDACK for that IP address showing it
- was FREE, it would move the IP address from EXPIRED or RELEASED to
- FREE, and it would be available for allocation by the primary server
- to any clients.
-
- A server MAY reallocate an IP address in the EXPIRED or RELEASED
- state to the same client with no restrictions provided it has not
- sent a BNDUPD message to its partner. This situation would exist if
- the lease expired or was released after the transition into PARTNER-
- DOWN state, for instance.
-
-
-5.3. Load balancing
-
- In order to implement load balancing between a primary and secondary
- server pair, each server must respond to DHCPDISCOVER requests from
- some clients and not from other clients. In order to do this suc-
- cessfully, each server must be able to determine immediately upon
- receipt of a DHCP client request whether it is to service this
- request or to ignore it in order to allow the other server to service
- the request.
-
- In addition, it should be possible to configure the percentage of
- clients which will be serviced by either the primary or secondary
- server. This configuration should be more or less continuous, from
- all clients serviced by the primary through an even split with half
- serviced by each, to all clients serviced by the secondary.
-
- The technique chosen to support these goals is described in [RFC
- 3074].
-
- A bitmap-style Hash Bucket Assignment (as described in [RFC 3074]) is
- used to determine which DHCP clients can be processed. There are two
- potential HBA's in a failover server -- a server HBA and a failover
- HBA. The way that a server acquires a server HBA is outside of the
-
-
-
-Droms, et. al. Expires September 2003 [Page 27]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- scope of the failover protocol, but both servers in a failover pair
- MUST have the same server HBA. The failover HBA (which specifies the
- clients that the secondary is supposed to process) is sent by the
- primary server to the secondary server whenever a connection is esta-
- blished, using the hash-bucket-assignment option defined in section
- 12.11.
-
- When using the server HBA (if any) and the failover HBA (if any), to
- decide whether to process a DHCP request, the server HBA always
- applies in every failover state, and the failover HBA (which MUST be
- a subset of the server HBA) is used by the secondary server to decide
- which packets to process when in NORMAL state.
-
-5.4. IP address allocations between servers
-
- The failover protocol allows a DHCP server which implements it to
- operate correctly in spite of the uncertainty over whether its
- partner has failed or whether the communications link to its partner
- has failed. This is made possible in part by the existence of
- separate address pools on each server for allocation to newly arrived
- DHCP clients.
-
- Thus, each server has its own pool of available IP addresses. Note
- that an IP address is not "owned" by a particular server throughout
- its entire lifetime. Only an IP address which is available is
- "owned" by a particular server -- once it has been leased to a DHCP
- client, it is not owned by either failover partner. When it finally
- becomes available again, it will be owned initially by the primary
- server, and it may or may not be allocated to the secondary server by
- the primary server.
-
- So, the flow of IP address ownership is as follows: initially an IP
- address is owned by the primary server. It may be allocated to the
- secondary server if it is available, and then it is owned by the
- secondary server. Either server can allocate available IP addresses
- which they own to DHCP clients, in which case they cease to own them.
- When the DHCP client releases the address or the lease on it expires,
- it will again become available and will be owned by the primary.
-
- An IP address will not become owned by the server which allocated it
- initially when it is released or the lease expires because, in gen-
- eral, that server will have had to replenish its pool of available
- addresses well in advance of any likely lease expirations. Thus,
- having a particular IP address cycle back to the secondary might well
- put the secondary more out of balance with respect to the primary
- instead of enhancing the balance of available addresses between them.
-
- These address pools are used when in COMMUNICATIONS-INTERRUPTED state
-
-
-
-Droms, et. al. Expires September 2003 [Page 28]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- and while waiting for the MCLT expiration in PARTNER-DOWN state. In
- addition, when using load balancing, these pools are used when in
- NORMAL state as well.
-
- This allocation and maintenance of these address pools is an area of
- some sensitivity, since the goal is to maintain a more or less con-
- stant ratio of available addresses between the two servers.
-
- The initial allocation when the servers first integrate is triggered
- by the POOLREQ message from the secondary to the primary. This is
- followed by the POOLRESP message where the primary tells the secon-
- dary how many IP addresses it allocated to the secondary. Then, the
- primary sends the allocated IP addresses to the secondary via BNDUPD
- messages. l The POOLREQ/POOLRESP message is a trigger to the primary
- to perform a scan of its database and to ensure that the secondary
- has enough IP addresses (based on some configured ratio).
-
- The actual IP addresses are sent to the secondary using the BNDUPD
- message with a state of BACKUP, which indicates the IP address is now
- available for allocation by the secondary. Once the message is sent,
- the primary MUST NOT use these addresses for allocation to DHCP
- clients.
-
- The POOLREQ/POOLRESP message exchange initiated by the secondary is
- valid at any time, and the primary server SHOULD, whenever it
- receives the POOLREQ message, scan its database of address pools and
- determine if the secondary needs more IP addresses from any of the IP
- address pools.
-
- However, in order to support a reasonably dynamic balance of the IP
- addresses between the failover partners, the primary server needs to
- do additional work to ensure that the secondary server has as many IP
- addresses as it needs (but that it doesn't have *more* than it needs
- either).
-
- The primary server SHOULD examine the balance of available addresses
- between the primary and secondary for a particular address pool when-
- ever the number of available addresses for either the primary or
- secondary changes. The primary server SHOULD adjust the available
- address balance as required to ensure the configured address balance,
- excepting that the primary server SHOULD employ some threshold
- mechanism to such a balance adjustment in order to minimize the over-
- head of maintaining this balance.
-
- An example of a threshold approach is: do not attempt to re-balance
- the available pools on the primary and secondary until the out of
- balance value exceeds a configured value.
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 29]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- The primary server can, at any time, send an available IP address to
- the secondary using a BNDUPD with the state BACKUP. The primary
- server can attempt to take an available IP address away from the
- secondary by sending a BNDUPD with the state FREE. If the secondary
- accepts the BNDUPD, then it is now available to the PRIMARY and not
- available to the secondary. Of course, the secondary MUST reject
- that BNDUPD if it has already used that IP address for a DHCP client.
-
- Whenever the primary server examines the possible available IP
- addresses which it could send to the secondary server, the primary
- server SHOULD take into account whether load balancing is in use, and
- it SHOULD attempt to send to the secondary any IP addresses whose
- most recent client would be processed by the secondary under the
- current load balancing regime in use. Likewise, when removing avail-
- able IP addresses from the secondary server when load balancing is in
- use, the primary server SHOULD first remove those IP addresses whose
- most recent client would be processed by the primary server under the
- current load balancing regime in use.
-
-5.5. Operating in NORMAL state
-
- When in NORMAL state, each server services DHCPDISCOVER's and all
- other DHCP requests other than DHCPREQUEST/RENEWAL or
- DHCPREQUEST/REBINDING from the client set defined by the load balanc-
- ing algorithm [RFC 3074]. Each server services DHCPREQUEST/RENEWAL
- or DHCPDISCOVER/REBINDING requests from any client.
-
- In general, whenever the binding database is changed in stable
- storage (other than a change resulting from receiving a BNDUPD from
- the failover partner), then a BNDUPD message is sent with the con-
- tents of that change to the partner server. The partner server then
- writes the information about that binding in its bindings database in
- stable storage and replies with a BNDACK message.
-
- The binding database in a DHCP server would normally be changed as a
- result of DHCP protocol activity with a DHCP client (e.g., granting
- a lease to a DHCP client through the familiar
- DISCOVER/OFFER/REQUEST/ACK cycle or extending a lease due to a
- renewal from a DHCP client) or possibly (on some servers) because a
- lease has expired or undergone another state change that must be
- recorded in the DHCP binding database. These are the state changes
- that would be communicated to the partner server using a BNDUPD mes-
- sage. Of course, receipt of a BNDUPD message itself will normally
- cause an update of the binding database for all of the IP addresses
- contained in the BNDUPD, and a binding database change such as this
- MUST NOT trigger a corresponding BNDUPD message to the partner.
-
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 30]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-5.6. Operating in COMMUNICATIONS-INTERRUPTED state
-
- When operating in COMMUNICATIONS-INTERRUPTED state, each server is
- operating independently, but does not assume that its partner is not
- operating. The partner server might be operating and simply unable
- to communicate with this server, or might not be operating.
-
- Each server responds to the full range of DHCP client messages that
- it receives (subject to server load balancing [RFC 3074]), but in
- such a way that graceful reintegration is always possible when its
- partner comes back into contact with it.
-
-5.7. Operating in PARTNER-DOWN state
-
- When operating in PARTNER-DOWN state, a server assumes that its
- partner is not currently operating, but does make allowances for the
- possibility that that server was operating in the past, though possi-
- bly out of communications with this server. It responds to all DHCP
- client requests in PARTNER-DOWN state (subject to server load balanc-
- ing [RFC 3074]).
-
-5.8. Operating in RECOVER state
-
- A server operating in RECOVER state assumes that it is reintegrating
- with a server that has been operating in PARTNER-DOWN state, and that
- it needs to update its bindings database before it services DHCP
- client requests.
-
- A server may also operate in RECOVER state in order to fully recover
- its bindings database from its partner server.
-
-5.9. Operating in STARTUP state
-
- A server operating in STARTUP state assumes that failover is opera-
- tional, and it spends a short time whenever it comes up attempting to
- contact the partner. During this short time, the server is unrespon-
- sive to DHCP client requests. This period exists in order to give a
- server a chance to determine that its partner has changed state since
- it was last in communications, and to react to that changed state (if
- any) prior to responding to DHCP client requests.
-
- The startup period SHOULD be conditioned on the length of time the
- server has been down (if that can be determined). If the server has
- been down less than the MCLT then it can wait only a few (say 5 or
- 10) seconds. If it has been down a longer time (such that the
- partner may well have moved to PARTNER-DOWN state), a considerably
- longer startup period of 30 to 60 seconds may be warranted, since the
- consequences of running while the partner is in PARTNER-DOWN state
-
-
-
-Droms, et. al. Expires September 2003 [Page 31]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- are unpleasant.
-
- The period of time a server remains in STARTUP state SHOULD be long
- enough to ensure that it will connect to the other server if that
- server is available for connections.
-
-5.10. Time synchronization between servers
-
- The failover protocol is designed to operate between two servers
- which have time values which differ by an arbitrarily large amount.
- A particular implementation MAY choose to only support servers whose
- time values differ by an arbitrarily small amount.
-
- Note that if an implementation that requires time synchronization
- between servers encounters a case where the time is not synchronized
- to its satisfaction between two servers, then this failure will prob-
- ably prevent the two servers from reaching communications OK status.
- In this occurs, and if both servers continue to operate and deal with
- clients, potentially troublesome things can happen. For instance, if
- there is a safe period configured on either server, then it will
- eventually go into PARTNER-DOWN state, but in this case the partner
- will not be down. This will almost certainly create problems. Thus,
- some method to prevent this sort of situation SHOULD exist in imple-
- mentations that can be configured to require time synchronization.
-
- In any event, whether large or only small differences in time values
- are supported, every message that is sent MUST include the time and
- every packet that is received MUST be tagged with a time value as
- soon as possible after receipt. This time value is used along with
- the time value that is sent in every message between the failover
- partners to develop a delta time between the servers. This delta
- time is used during the connection process to establish a baseline
- delta time between the servers, and upon receipt of each message, the
- delta time for that message is used to refine the delta time for the
- server pair.
-
- While the algorithm for this refinement of delta time is not speci-
- fied as part of this protocol, a server SHOULD allow the delta time
- value for a pair of failover servers to be periodically updated to
- account for time drift. In addition, the delta time value between
- servers SHOULD be smoothed in some fashion, so that transient network
- delays will not cause it to vary wildly.
-
- A server SHOULD recognize a drastic change in the delta time value as
- an event to be signaled to a network administrator, as well as reset-
- ting the time delta between the failover partners.
-
- The specific definitions of a minor or drastic change in delta time
-
-
-
-Droms, et. al. Expires September 2003 [Page 32]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- as well as the algorithm used to smooth minor changes into the run-
- ning delta time are implementation issues and are not further
- addressed in this document.
-
-5.11. IP address binding-status
-
- In most DHCP servers an IP address can take on several different
- binding-status values, sometimes also called states. While no two
- DHCP servers probably have exactly the same possible binding-status
- values, the DHCP RFC enforces some commonality among the general
- semantics of the binding-status values used by various DHCP server
- implementations.
-
- In order to transmit binding database updates between one server and
- another using the failover protocol, some common denominator
- binding-status values must be defined. It is not expected that these
- binding-status-values correspond with any actual implementation of
- the DHCP protocol in a DHCP server, but rather that the binding-
- status values defined in this document should be a common denominator
- of those in use by many DHCP server implementations. It is a goal of
- this protocol that any DHCP server can map the various IP address
- binding-status values that it uses internally into these failover IP
- address binding-status values on transmission of binding database
- updates to its partner, and likewise that it can map any failover IP
- address binding-status values it received in a binding update into
- its internal IP address binding-status values.
-
- The IP address binding-status values defined for the failover proto-
- col are listed below. Unless otherwise noted below, there MAY be
- client information associated with each of these binding-status
- values.
-
- o ACTIVE -- Lease is assigned to a client. Client identification
- MUST appear.
-
- o EXPIRED -- indicates that a client's binding on an IP address
- has expired. When the partner server ACK's the BNDUPD of an
- EXPIRED IP address, the server sets its internal state to FREE.
- It is then available for allocation to any client of the primary
- server. It may be allocated to the same client on the server
- where the lease expired if a BNDUPD containing the EXPIRED state
- has not yet been sent to the partner (e.g., in the event that
- the servers are not in communication). Client identification
- SHOULD appear.
-
- o RELEASED -- indicates that a DHCP client sent in a DHCPRELEASE
- message. When the partner server ACK's the BNDUPD of an
- RELEASED IP address, the server sets its internal state to FREE,
-
-
-
-Droms, et. al. Expires September 2003 [Page 33]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- and it is available for allocation by the primary server to any
- DHCP client. It may be allocated to the same client if a BNDUPD
- has not yet been sent to the partner. Client identification
- SHOULD appear.
-
- o FREE -- is used when a DHCP server needs to communicate that an
- IP address is unused by any DHCP client, but it was not just
- released, expired, or reset by a network administrator. When
- the partner server ACK's the BNDUPD of a FREE IP address, the
- server sets its internal state such that it is available for
- allocation by the primary DHCP server to any DHCP client. (Note
- that in PARTNER-DOWN state, after waiting the MCLT, the IP
- address MAY be allocated to a DHCP client by the secondary
- server.)
-
- Note that when an IP address that was allocated by the secondary
- reverts to the FREE state, it must (like any other IP address)
- be assigned to the secondary through the POOLREQ/BNDUPD process
- before the secondary can reallocate it.
-
- Client identification MAY appear.
-
- o ABANDONED -- indicates that an IP address is considered unusable
- by the DHCP subsystem. An IP address for which a valid PING
- response was received SHOULD be set to ABANDONED. An IP address
- for which a DHCPDECLINE was received should be set to ABANDONED.
- Client identification MUST NOT appear.
-
- o RESET -- indicates that this IP address was made available by
- operator command. This is a distinct state so that the reason
- that the IP address became FREE can be determined. Client iden-
- tification MAY appear.
-
- o BACKUP -- indicates that this IP address can be allocated by the
- secondary server to a DHCP client at any time. When the MCLT has
- passed after its time of entry into PARTNER-DOWN state, the IP
- address may be allocated by the primary to any DHCP client.
- Client identification MAY appear.
-
- These binding-status values are communicated from one failover
- partner to another using the binding-status option, see section 12.3
- for details of this option. Unless otherwise noted above there MAY
- be client information associated with each of these binding-status
- values.
-
- An IP address will move between these binding-status values using the
- following state transition diagram:
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 34]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-
-
- DHCP client DECLINE or
- server detected problem
- from any state
- |
- V
- +----------+ +--+------+
- External >---->| RESET | (3) |ABANDONED|
- command | +<--------+ |
- +----------+ +---------+
- |
- Comm w/Parter(1)
- V
- +---------+ Comm(1) +----------+ Comm(1) +---------+
- | EXPIRED |--------->| FREE |<----------| RELEASED|
- | | w/Parter | | w/Partner | |
- +---------+ +----------+ +---------+
- ^ ^ | | +-----------+ ^
- | | | | | |
- | Exp. grace IP | IP addr alloc. IP addr |
- | period ends address to sec.(2) reserved |
- | | leased V | |
- | | by | +----------+ | |
- | | primary | BACKUP |<---+ |
- | wait for | | | |
- | grace period | +----------+ |
- | | | | |
- | | | IP addr leased by |
- | Expired grace | secondary |
- | period exists V V |
- | | +----------+ |
- | | Lease on | ACTIVE | DHCPRELEASE |
- +-----+-IP addr---| |------------------+
- expires +----------+
-
-
- Figure 5.11-1: Transitions between binding-status values.
-
- (1) This transition MAY also occur if the server is in
- PARTNER-DOWN state and the MCLT has passed since the entry
- in the RELEASED, EXPIRED, or RESET states.
-
- (2) This transition MAY occur if the server is the secondary
- and the MCLT has passed since its entry into PARTNER-DOWN state.
-
- (3) This transition MAY occur due to an implementation specific
- handling of ABANDONED IP addresses.
-
-
-
-Droms, et. al. Expires September 2003 [Page 35]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-
-
-
- Again, note that a DHCP server implementing the failover protocol
- does not have to implement either this state machine or use these
- particular binding-status values in its normal operation of allocat-
- ing IP addresses to DHCP clients. It only needs to map its internal
- binding-status-values onto these "standard" binding-status values,
- and map these "standard" binding-status values back into its internal
- binding-status values. For example, a server which implements a
- grace period for a IP address binding SHOULD simply wait to update
- its partner server until the grace period on that binding has run
- out.
-
- The process of setting an IP address to FREE deserves some detailed
- discussion. When an IP address is moved to the EXPIRED,RELEASED, or
- RESET binding-status on a server, it will send a BNDUPD with the
- binding-status of EXPIRED, RELEASED, or RESET to its partner. If its
- partner agrees that is acceptable (see sections 7.1.2 and 7.1.3 con-
- cerning why a server might not accept a BNDUPD) it will return a
- BNDACK with no reject-reason, signifying that it accepted the update.
- As part of the BNDUPD processing, the server returning the BNDACK
- will set the binding-status of the IP address to FREE, and upon
- receipt of the BNDACK the server which sent the BNDUPD will set the
- binding-status of the IP address to FREE. Thus, the EXPIRED,
- RELEASED, or RESET binding-status is something of a transitory state.
- This process is encoded in the transition diagram above by "Comm
- w/Partner".
-
-5.12. DNS dynamic update considerations
-
- DHCP servers (and clients) can use DNS Dynamic Updates as described
- in [RFC 2136] to maintain DNS name-mappings as they maintain DHCP
- leases. Many different administrative models for DHCP-DNS integra-
- tion are possible. Descriptions of several of these models, and
- guidelines that DHCP servers and clients should follow in carrying
- them out, are laid out in [FQDN]. The nature of the DHCP failover
- protocol introduces some issues concerning dynamic DNS updates that
- are not part of non-failover DHCP environments. This section
- describes these issues, and defines the information which failover
- partners should exchange and the protocol which they should follow in
- order to ensure consistent behavior. The presence of this section
- should not be interpreted as requiring that implementations of the
- DHCP failover protocol must also support DDNS updates. The purpose
- of this discussion is to clarify the areas where the DHCP failover
- and DHCP-DDNS protocols intersect for the benefit of implementations
- which support both protocols, not to introduce a new requirement into
- the DHCP failover protocol. Thus, a DHCP server which implements the
-
-
-
-Droms, et. al. Expires September 2003 [Page 36]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- failover protocol MAY also support dynamic DNS updates, but if it
- does support dynamic DNS updates it SHOULD utilize the techniques
- described here in order to correctly distribute them between the
- failover partners. See [FQDN], [DNSRES], and [DHCID] for details of
- how DHCP servers update DNS.
-
- From the standpoint of the failover protocol, there is no reason why
- a server which is utilizing the DDNS protocol to update a DNS server
- should not be a partner with a server which is not utilizing the DDNS
- protocol to update a DNS server. However, a server which is not able
- to support DDNS or is not configured to support DDNS SHOULD output a
- warning message when it receives BNDUPD messages which indicate that
- its failover partner is configured to support the DDNS protocol to
- update a DNS server. An implementation MAY consider this an error
- and refuse to operate, or it MAY choose to operate anyway, having
- warned the user of the problem in some way.
-
-5.12.1. Relationship between failover and dynamic DNS update
-
- The failover protocol describes the conditions under which each fail-
- over server may renew a lease to its current DHCP client, and
- describes the conditions under which it may grant a lease to a new
- DHCP client. An analogous set of conditions determines when a fail-
- over server should initiate a DDNS update, and when it should attempt
- to remove records from the DNS. The failover protocol's conditions
- are based on the desired external behavior: avoiding duplicate
- address assignments; allowing clients to continue using leases which
- they obtained from one failover partner even if they can only commun-
- icate with the other partner; allowing the backup DHCP server to
- grant new leases even if it is unable to communicate with the primary
- server. The desired external DDNS behavior for DHCP failover servers
- is:
-
- 1. Allow timely DDNS updates from the server which grants a
- client a lease. Recognize that there is often a DDNS update
- lifecycle which parallels the DHCP lease lifecycle. This is
- likely to include the addition of records when the lease is
- granted, and the removal of DNS records when the lease is sub-
- sequently made available for allocation to a different client.
-
- 2. Communicate enough information between the two failover
- servers to allow one to complete the DDNS update 'lifecycle'
- even if the other server originally granted the lease.
-
- 3. Avoid redundant or overlapping DDNS updates, where both fail-
- over servers are attempting to perform DDNS updates for the
- same lease-client binding. Avoid situations where one partner
- is attempting to add RRs related to a lease binding while the
-
-
-
-Droms, et. al. Expires September 2003 [Page 37]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- other partner is attempting to remove RRs related to the same
- lease binding.
-
-5.12.2. Use of the DDNS option
-
- In order for either server to be able to complete a DDNS update, or
- to remove DNS records which were added by its partner, both servers
- need to know the FQDN associated with the lease-client binding. The
- FQDN associated with the client's A RR and PTR RR SHOULD be communi-
- cated from the server which adds records into the DNS to its partner.
- The initiating server SHOULD use the DDNS option in the BNDUPD mes-
- sages to inform the partner server of the status of any DDNS updates
- associated with a lease binding. Failover servers MAY choose not to
- include the DDNS option in BNDUPD messages if there has been no
- change in the status of any DDNS update related to the lease binding.
- The partner server receiving BNDUPD messages containing the DDNS
- option SHOULD compare the status flags and the FQDN contained in the
- option data with the current DDNS information it has associated with
- the lease binding, and update its notion of the DDNS status accord-
- ingly.
-
- The initiating server MAY send a BNDUPD to its partner before the
- DDNS update has been successfully completed. If it does so, it SHOULD
- leave the 'C' bit in the Flags field clear, to indicate to the
- partner that the DDNS update may not be complete. When the DDNS
- update has been successfully acknowledged by the DNS server, the ini-
- tiating DHCP server SHOULD include the DDNS option in its next BNDUPD
- message about the binding, so that the partner server will be able to
- record the final status of the DDNS update. The initiating server
- SHOULD set the 'C' bit in the DDNS option if the DDNS update was suc-
- cessfully accepted by the DNS server.
-
- Some implementations will choose to send a BNDUPD without waiting for
- the DDNS update to complete, and then will send a second BNDUPD once
- the DDNS update is complete. Other implementations will delay sending
- the partner a BNDUPD until the DDNS update has been acknowledged by
- the DNS server, or until some time-limit has elapsed, in order to
- avoid sending a second BNDUPD.
-
- The Domain Name field in the DDNS option contains the FQDN that will
- be associated with the A RR (if the server is performing an A RR
- update for the client) and the PTR RR. This FQDN may be composed in
- any of several ways, depending on server configuration and the infor-
- mation provided by the client in its DHCP messages. The client may
- supply a hostname which it would like the server to use in forming
- the FQDN, or it may supply the entire FQDN. The server may be config-
- ured to attempt to use the information the client supplies, it may be
- configured with an FQDN to use for the client, or it may be
-
-
-
-Droms, et. al. Expires September 2003 [Page 38]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- configured to synthesize an FQDN. The responsive server SHOULD
- include the FQDN that it will be using in DDNS updates it initiates
- when it sends the DDNS option.
-
- Since the responsive server may not have completed the DDNS update at
- the time it sends the first BNDUPD about the lease binding, there may
- be cases where the FQDN in later BNDUPD messages does not match the
- FQDN included in earlier messages. For example, the responsive
- server may be configured to handle situations where two or more DHCP
- client FQDNs are identical by modifying the most-specific label in
- the FQDNs of some of the clients in an attempt to generate unique
- FQDNs for them (a process sometimes called "disambiguation"). Alter-
- natively, at sites which use some or all of the information which
- clients supply to form the FQDN, it's possible that a client's confi-
- guration may be changed so that it begins to supply new data. The
- responsive server may react by removing the DNS records which it ori-
- ginally added for the client, and replacing them with records that
- refer to the client's new FQDN. In such cases, the responsive server
- SHOULD include the actual FQDN that was used in subsequent DDNS
- options. The responsive server SHOULD include relevant client-option
- data in the client-request-options option in its BNDUPD messages.
- This information may be necessary in order to allow the non-
- responsive partner to detect client configuration changes that change
- the hostname or FQDN data which the client includes in its DHCP
- requests.
-
-5.12.3. Adding RRs to the DNS
-
- A failover server which is going to perform DDNS updates SHOULD ini-
- tiate the DDNS update when it grants a new lease to a client. The
- non-responsive partner SHOULD NOT initiate a DDNS update when it
- receives the BNDUPD after the lease has been granted. The failover
- protocol ensures that only one of the partners will grant a lease to
- any individual client, so it follows that this requirement will
- prevent both partners from initiating updates simultaneously. The
- server initiating the update SHOULD follow the protocol in [FQDN].
- The server may be configured to perform an A RR update on behalf of
- its clients, or not. Ordinarily, a failover server will not initiate
- DDNS updates when it renews leases. In two cases, however, a failover
- server MAY initiate a DDNS update when it renews a lease to its
- existing client:
-
- 1. When the lease was granted before the server was configured to
- perform DDNS updates, the server MAY be configured to perform
- updates when it next renews existing leases. Since both
- servers are responsive to renewals in NORMAL state, it is not
- enough to simply require the non-responsive server to avoid a
- DNS update in this case. The server which would be responsive
-
-
-
-Droms, et. al. Expires September 2003 [Page 39]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- to a DHCPDISCOVER from this client (even though the current
- request is a DHCPREQUEST/RENEW) is the server which should
- initiate the DDNS update.
-
- 2. If a server is in PARTNER-DOWN state, it can conclude that its
- partner is no longer attempting to perform an update for the
- existing client. If the remaining server has not recorded that
- an update for the binding has been successfully completed, the
- server MAY initiate a DDNS update. It MAY initiate this
- update immediately upon entry to PARTNER-DOWN state, it may
- perform this in the background, or it MAY initiate this update
- upon next hearing from the DHCP client.
-
-5.12.4. Deleting RRs from the DNS
-
- The failover server which makes an IP address FREE SHOULD initiate
- any DDNS deletes, if it has recorded that DNS records were added on
- behalf of the client.
-
- A server not in PARTNER-DOWN state "makes an IP address FREE" when it
- initiates a BNDUPD with a binding-status of FREE, EXPIRED, or
- RELEASED. Its partner confirms this status by acking that BNDUPD,
- and upon receipt of the ACK the server has "made the IP address
- FREE". Conversely, a server in PARTNER-DOWN state "makes an IP
- address FREE" when it sets the binding-status to FREE, since in
- PARTNER-DOWN state no communications is required with the partner.
-
- It is at this point that it should initiate the DDNS operations to
- delete RRs from the DDNS. Its partner SHOULD NOT initiate DDNS
- deletes for DNS records related to the lease binding as part of send-
- ing the BNDACK message. The partner MAY have issued BNDUPD messages
- with a binding-status of FREE, EXPIRED, or RELEASED previously, but
- the other server will have NAKed these BNDUPD messages.
-
- The failover protocol ensures that only one of the two partner
- servers will be able to make a lease FREE. The server making the
- lease FREE may be doing so while it is in NORMAL communication with
- its partner, or it may be in PARTNER-DOWN state. If a server is in
- PARTNER-DOWN state, it may be performing DDNS deletes for RRs which
- its partner added originally. This allows a single remaining partner
- server to assume responsibility for all of the DDNS activity which
- the two servers were undertaking.
-
- Another implication of this approach is that no DDNS RR deletes will
- be performed while either server is in COMMUNICATIONS-INTERRUPTED
- state, since no IP addresses are moved into the FREE state during
- that period.
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 40]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-5.13. Reservations and failover
-
- Some DHCP servers support a capability to offer specific pre-
- configured IP addresses to DHCP clients. These are real DHCP
- clients, they do the entire DHCP protocol, but these servers always
- offer the client a specific pre-configured IP address -- and they
- offer that IP address to no other clients. Such a capability has
- several names, but it is sometimes called a "reservation", in that
- the IP address is reserved for a particular DHCP client.
-
- In a situation where there are two DHCP servers serving the same sub-
- net without using failover, the two DHCP server's need to have dis-
- joint IP address pools, but identical reservations for the DHCP
- clients.
-
- In a failover context, both servers need to be configured with the
- proper reservations in an identical manner, but if we stop there
- problems can occur around the edge conditions where reservations are
- made for an IP address that has already been leased to a different
- client. Different servers handle this conflict in different ways,
- but the goal of the failover protocol is to allow correct operation
- with any server's approach to the normal processing of the DHCP pro-
- tocol.
-
- The general solution with regards to reservations is as follows.
- Whenever a reserved IP address becomes FREE (i.e., when first config-
- ured or whenever a client frees it or it expires or is reset), the
- primary server MUST show that IP address as FREE (and thus available
- for its own allocation) and it MUST send it to the secondary server
- with the R bit set in the IP-flags option and the binding-status
- BACKUP.
-
- Note that this implies that a reserved IP address goes through the
- normal state changes from FREE to ACTIVE (and possibly back to FREE).
- The failover protocol supports this approach to reservations, i.e.,
- where the IP address undergoes the normal state changes of any IP
- address, but it can only be offered to the client for which it is
- reserved. Other approaches to the support of reservations exist in
- some DHCP server implementations (e.g., where the IP address is
- apparently leased to a particular client forever, without any expira-
- tion). The goal is for the failover protocol to support any of the
- usual approaches to reservations, both those that allow an IP address
- to go through different states when reserved, and those that don't.
-
- From the above, it follows that a reservation soley on the secondary
- will not necessarily allow the secondary to offer that address to
- client to whom it is reserved. The reservation must also appear on
- the primary as well for the secondary to be able to offer the IP
-
-
-
-Droms, et. al. Expires September 2003 [Page 41]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- address to the client to which is is reserved.
-
- When the reservation on an IP address is cancelled, if the IP address
- is currently FREE and the server is the primary, or BACKUP and the
- server is the secondary, the server MUST send a BNDUPD to the other
- server with the binding-status FREE and the R bit clear.
-
-5.14. Dynamic BOOTP and failover
-
- Some DHCP servers support a capability to offer IP addresses to BOOTP
- clients without having a particular address previously allocated for
- those clients. This capability is often called something like
- "dynamic BOOTP". It is discussed briefly in RFC 1534 [RFC 1534].
-
- This capability has a negative interaction with the fundamental ele-
- ments of the failover protocol, in that an address handed out to a
- BOOTP device has no term (or effectively no term, in that usually
- they are considered leases for "forever"). There is no opportunity
- to hand out a lease which is only the MCLT long when first hearing
- from a BOOTP device, because they may only interact once with the
- DHCP server and they have no notion of a lease expiration time. Thus
- the entire concept of the MCLT and waiting the MCLT after entering
- PARTNER-DOWN state is defeated when dealing with BOOTP devices.
-
- With some restrictions, however, dynamic BOOTP devices can be sup-
- ported in a server on a subnet where failover is supported. The only
- restriction (and it is not small) is that on any portion of the sub-
- net (in any address pool) where dynamic BOOTP devices can be allo-
- cated IP addresses, a DHCP server MUST NOT ever use any of the IP
- addresses which were previously available for allocation by its fail-
- over partner. Thus, the addresses allocated by the primary to the
- secondary for allocation that might have been allocated to BOOTP dev-
- ices MUST NOT ever be used by the primary server even if it is in
- PARTNER-DOWN state and has waited the MCLT after entering that state.
- Conversely, addresses available for allocation by the primary MUST
- NOT be used by the secondary even it is in PARTNER-DOWN state. The
- reason for this is because one of those IP address could have been
- allocated by the secondary server to a BOOTP device, and the primary
- server would have no way of ever knowing that happened.
-
- Whenever a server sends BNDUPD message to its partner, if the client
- associated with the IP address is a BOOTP client, then the server
- MUST set the B bit in the IP-flags option.
-
- There is a very slight possibility that a BOOTP client could get an
- IP address on each server of a failover pair. When these two servers
- eventually attempt to resolve this conflict, they SHOULD agree to
- disagree, since it is not possible to know which IP address the BOOTP
-
-
-
-Droms, et. al. Expires September 2003 [Page 42]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- client will actually use -- indeed, it could use both. Operator
- intervention will, in general, be required to rectify this situation.
- Fortunately, it is extremely unlikely to ever actually occur.
-
-5.15. Guidelines for selecting MCLT
-
- There is no one correct value for the MCLT. There is an explicit
- tradeoff between various factors in selecting an MCLT value.
-
-5.15.1. Short MCLT
-
- A short MCLT value will mean that after entering PARTNER-DOWN state,
- a server will only have to wait a short time before it can start
- allocating its partner's IP addresses to DHCP clients. Furthermore,
- it will only have to wait a short time after the expiration of a
- lease on an IP address before it can reallocate that IP address to
- another DHCP client.
-
- However the downside of a short MCLT value is that the initial lease
- interval that will be offered to every new DHCP client will be short,
- which will cause increased traffic as those clients will need to send
- in their first renew in a half of a short MCLT time. In addition,
- the lease extensions that a server in COMMUNICATIONS-INTERRUPTED
- state can give will be only the MCLT after the server has been in
- COMMUNICATIONS-INTERRUPTED for around the desired client lease
- period. If a server stays in COMMUNICATIONS-INTERRUPTED for that
- long, then the leases it hands out will be short and that will
- increase the load on that server, possibly causing difficulty.
-
-5.15.2. Long MCLT
-
- A long MCLT value will mean that the initial lease period will be
- longer and the time that a server in COMMUNICATIONS-INTERRUPTED state
- will be able to extend leases (after it has been in COMMUNICATIONS-
- INTERRUPTED state for around the desired client lease period) will be
- longer.
-
- However, a server entering PARTNER-DOWN state will have to wait the
- longer MCLT before being able to allocate its partner's IP addresses
- to new DHCP clients. This may mean that additional IP addresses are
- required in order to cover this time period. Further, the server in
- PARTNER-DOWN will have to wait the longer MCLT from every lease
- expiration before it can reallocate an IP address to a different DHCP
- client.
-
-5.16. What is sent in response to an UPDREQ or UPDREQALL message?
-
- In section 7.3, the UPDREQ message is defined, and it says that the
-
-
-
-Droms, et. al. Expires September 2003 [Page 43]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- receiving server sends to the requesting server "all of the binding
- database information that it has not already seen". In section
- 7.4.2, the UPDREQALL message is defined, and it says that the receiv-
- ing server sends to the requesting server "all binding database
- information".
-
- Both of these statements need further elaboration.
-
- First, for the UPDREQ message, the information to be sent in BNDUPD
- messages concerns "all of the binding database information it has not
- already seen". Since every BNDUPD is acked by the receiving server,
- the sending server need only keep track of which IP addresses have
- binding database changes not yet seen by the partner, and when they
- are finally acked by the partner it can record that. Thus, at any
- time, it knows which IP addresses have unacked binding database
- information. This is less simple when, across reconfigurations of
- the servers, an IP address can change the failover partner to which
- it is associated. In that case, it is important to reset the indica-
- tion that the partner has seen this binding information. See section
- 5.17, below, for a more complete discussion of this issue.
-
- Second, in the event that a failover server's binding database infor-
- mation is restored from a backup, it will be partially out of date.
- In this case, its partner's indication of which binding database
- information the restored server has seen will be also be out of date.
-
- The solution to this problem is for a server which is connecting with
- its partner to check the partner's last communicated time, and if it
- is very much ahead of its own last communicated time, go to into
- RECOVER state and transmit an UPDREQALL to allow it to refresh its
- state. See section 9.3.2, step 5. If the partner's last communi-
- cated time is very much behind its own record of when it last commun-
- icated with the partner, then it SHOULD invalidate its information on
- which binding database information the partner server knows, so that
- it will send all of its relevant binding database information to the
- partner.
-
- Third, in the event that a server receives a UPDREQALL message, what
- constitutes "all binding database information"? At first glance this
- would seem to be information on every configured IP address in the
- server. While this would be technically correct, it may impose a
- serious and unacceptable performance penalty on servers which have
- millions of configured IP addresses. What can be done to lessen the
- data that must be sent for an UPDREQALL?
-
- When sending "all binding database information", if the sending
- server sends only information concerning IP addresses which have been
- at some time associated with clients, it will send enough information
-
-
-
-Droms, et. al. Expires September 2003 [Page 44]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- to satisfy the needs of the failover protocol. It need not send
- information on any IP addresses that have never been used, since
- presumably they will be initialized as available to the primary
- server (i.e. FREE) on any server employing failover.
-
-5.17. How do you determine that your partner is "up to date" for
-specific binding?
-
- Throughout this document, one server is assumed to know for each IP
- address binding whether or not its partner is "up to date" for that
- binding. There are some subtle issues involved in recording this "up
- to date" information about a specific binding.
-
- In a steady state world, it would suffice to have a single bit in the
- binding database to represent the information about whether the
- partner was or was not up to date.
-
- In a more complex environment a configuration change affecting a par-
- ticular IP address may change the failover endpoint with which it is
- associated, and if this should happen, any "up to date" bit which is
- written into the bindings database will be accurate for only the pre-
- vious failover endpoint, but not the current failover endpoint. If
- failover is disabled and then re-enabled (and the "up to date" bits,
- if used, are not cleared) problems can also occur.
-
- A server MUST have be able to relate the "up to date" condition to a
- particular failover endpoint and even a particular instantiation of
- that failover endpoint. The techniques to do this are implementation
- dependent.
-
- In addition, section 7.4 requires that a server be able to remember
- that an UPDREQALL message has been received and to treat every UPDREQ
- message as an UPDREQALL message until the first UPDDONE message is
- sent. One way to do this is to clear all of the "up to date" indica-
- tions for an entire failover endpoint upon receipt of an UPDREQALL
- message, thereby ensuring that every active binding will be sent to
- the partner whether through the completion of this UPDREQALL or
- through processing of a subsequent UPDREQ message. This is actually
- better than remembering that an UPDREQALL was received and turning
- every UPDREQ into an UPDREQALL, since any information sent in an
- incomplete UPDREQALL (or subsequent UPDREQ messages turned into "all"
- messages) will be remembered and not re-sent.
-
-6. Common Message Format
-
- This section discusses the common message format that all failover
- messages have in common, including the message header format as well
- as the common option format. See section 12 for the the definitions
-
-
-
-Droms, et. al. Expires September 2003 [Page 45]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- of the specific options used in the failover protocol.
-
-6.1. Message header format
-
- The options contained in the payload data section of the failover
- message all use a two byte option number and two byte length format.
-
- All failover protocol messages are sent over the TCP connection
- between failover endpoints and encoded using a message format
- specific to the failover protocol.
-
- There exists a common message format for all failover messages, which
- utilizes the options in a way similar to the DHCP protocol. For each
- message type, some options are required and some are optional. In
- addition, when a message is received any options that are not under-
- stood by the receiving server MUST be ignored.
-
- All of the fields in the fixed portion of the message MUST be filled
- with correct data in every message sent.
-
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | message length (2) | msg type (1) |payload off (1)|
- +---------------+---------------+---------------+---------------+
- | time (4) |
- +---------------------------------------------------------------+
- | xid (4) |
- +---------------------------------------------------------------+
- | 0 or more additional header bytes (variable) |
- +---------------------------------------------------------------+
- | payload data (variable) |
- | |
- | formatted as DHCP-style options |
- | using a two byte option code and two byte length |
- | See section 6.2 for details. |
- +---------------------------------------------------------------+
-
-
-
- message length - 2 bytes, network byte order
-
- This is the length of the message in bytes. It includes the two byte
- message length itself. The maximum length is 2048 bytes. The
- minimum length is 12.
-
-
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 46]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- msg type - 1 byte
-
- The message type field is used to distinguish between messages.
-
- The following message types are defined:
-
- Value Message Type
- ----- ------------
- 0 reserved not used
- 1 POOLREQ request allocation of addresses
- 2 POOLRESP respond with allocation count
- 3 BNDUPD update partner with binding info
- 4 BNDACK acknowledge receipt of binding update
- 5 CONNECT establish connection with the secondary
- 6 CONNECTACK respond to attempt to establish connection with partner
- 7 UPDREQALL request full transfer of binding info
- 8 UPDDONE ack send and ack of req'd binding info
- 9 UPDREQ request transfer of un-acked binding info
- 10 STATE inform partner of current state or state change
- 11 CONTACT probe communications integrity with partner
- 12 DISCONNECT close a connection
-
-
- New message types should be defined in one of two ranges, 0-127 or
- 129-255. The range of 0-127 is used for messages that MUST be sup-
- ported by every server, and if a server receives a message in the
- range of 0-127 that it doesn't understand, it MUST close the TCP con-
- nection. The range of 128-255 is used for messages which MAY be sup-
- ported but are not required, and if a server receives a message in
- this range that it does not understand it SHOULD ignore the message.
-
- payload offset - 1 byte
-
- The byte offset of the Payload Data, from the beginning of the
- failover message header. The value for the current protocol version
- (version 1) is 8.
-
- time - 4 bytes, network byte order
-
- The absolute time in GMT when the message was transmitted,
- represented as seconds elapsed since Jan 1, 1970 (i.e., similar to
- the ANSI C time_t time value representation). While the ANSI C
- time_t value is signed, the value used in this specification is
- unsigned.
-
- A server SHOULD set this time as close to the actual transmission of
- the message as possible.
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 47]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- xid - 4 bytes, network byte order
-
- This is the transaction id of the failover message. The sender of a
- failover protocol message is responsible for setting this number, and
- the receiver of the message copies the number over into any response
- message, treating it as opaque data. The sender MUST ensure that
- every message sent from a particular failover endpoint over the
- associated TCP connection has a unique transaction id.
-
- For failover messages that have no corresponding response message,
- the XID value is meaningless, but MUST be supplied. The XID value is
- used solely by the receiver of a response message to determine the
- corresponding request message.
-
- Request messages where the XID is used in the corresponding response
- messages are: POOLREQ, BNDUPD, CONNECT, UPDREQALL, and UPDREQ. The
- corresponding response messages are POOLRESP, BNDACK, CONNECTACK,
- UPDDONE, and UPDDONE, respectively.
-
- As requests/responses don't survive connection reestablishment, XIDs
- only need to be unique during a specific connection.
-
-
- payload data - variable length
-
- The options are placed after the header, after skipping payload
- offset bytes from beginning of the message. The payload data options
- are not preceded by a "cookie" value.
-
- The payload data is formatted as DHCP style options using two byte
- option codes and two byte option lengths. The option codes are in a
- namespace which is unique to the failover protocol.
-
- The maximum length of the payload data in octets is 2048 less the
- size of the header, i.e., the maximum message length is 2048 octets.
-
-6.2. Common option format
-
- The options contained in the payload data section of the failover
- message all use a two byte option number and two byte length format.
-
- The option numbers are drawn from an option number space unique to
- the failover protocol. All of the message types share a common
- option number space and common options definitions, though not all
- options are required or meaningful for every message.
-
- In contrast to the options which appear in DHCP client and server
- messages, the options in failover message are ordered. That is, for
-
-
-
-Droms, et. al. Expires September 2003 [Page 48]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- some messages the order in which the options appear in the payload
- data area is significant. The messages for which option ordering is
- significant explicitly describe the ordering requirements. If no
- ordering requirements are mentioned, then the order is not signifi-
- cant for that message.
-
- For all options which refer to time, they all use an absolute time in
- GMT. Time synchronization has already been achieved between the
- source and the target server using the CONNECT message and is updated
- and refined using the time in every packet.
-
- The time value is an unsigned 32 bit integer in network byte order
- giving the number of seconds since 00:00 UTC, 1st January 1970. This
- can be converted to an NTP timestamp by adding decimal 2208988800.
- This time format will not wrap until the year 2106. Until sometime
- in 2038, it is equal to the ANSI C time_t value (which is a signed 32
- bit value and will overflow into a negative number in 2038).
-
- Options should appear once only in each message (except for BNDUPD
- and BNDACK messages where bulking is used, see section 6.3 for
- details.) An option that appears twice is not concatenated, but
- treated as an error.
-
- Specific option values are described in section 12.
-
- See section 13 for how to define additional options.
-
-6.3. Batching multiple binding update transactions in one BNDUPD mes-
-sage
-
- Implementations of this protocol MAY send multiple binding update
- transactions in one BNDUPD message, where a binding update transac-
- tion is defined as the set of options which are associated with the
- update of a single IP address. All implementations of this protocol
- MUST be prepared to receive BNDUPD messages which contain multiple
- binding update transactions and respond correctly to them, including
- replying with a BNDACK message which contains status for the multiple
- binding update transactions contained in the BNDUPD message.
-
- In the discussion of sending and receiving BNDUPD messages in section
- 7.1 and BNDACK messages in section 7.2, each BNDUPD message and
- BNDACK message is assumed to contain a single binding update transac-
- tion in order to reduce the complexity of the discussions in section
- 7.
-
- Multiple binding update transactions MAY be batched together in one
- BNDUPD protocol message with the data sets for the individual tran-
- sactions delimited by the assigned-IP-address option, which MUST
-
-
-
-Droms, et. al. Expires September 2003 [Page 49]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- appear first in the option set for each transaction. Ordering of
- options between the assigned-IP-address options is not significant.
- This is illustrated in the following schematic representation:
-
-
- Non-IP Address/Non-client specific options first
- assigned-IP-address option for the first IP address
- Options pertaining to first address, including at least the
- binding-status option and others as required.
- assigned-IP-address option for the second IP address
- Options pertaining to second address, including at least the
- binding-status option and others as required.
- ...
- Trailing options (message digest).
-
-
- There MUST be a one-to-one correspondence between BNDUPD and BNDACK
- messages, and every BNDACK message MUST contain status for all of the
- binding update transactions in the corresponding BNDUPD message.
-
- The BNDACK message corresponding to a BNDUPD message MUST contain
- assigned-IP-address options for all of the binding update transac-
- tions in the BNDUPD message. Thus, every BNDACK message contains
- exactly the same assigned-IP-address options as does its correspond-
- ing BNDUPD message. The order of the assigned-IP-address options
- MAY, however, be different. Here is a schematic representation of a
- BNDACK:
-
-
- Non-IP Address/Non-client specific options first
- assigned-IP-address option for the first IP address
- If rejected, reject-reason option and message option.
- assigned-IP-address option for the second IP address
- If rejected, reject-reason option and message option.
- ...
- Trailing options (message digest).
-
-
- In case the server chooses to reject some or all of the IP address
- binding information in a BNDUPD message in a BNDACK reply, the BNDACK
- message MUST contain a reject-reason option following every failed
- assigned-IP-address option in order to indicate that the binding
- update transaction for that IP address was not accepted and why. As
- with a BNDACK message containing a single binding update transaction,
- an assigned-IP-address option without any associated reject-reason
- option indicates a successful binding update transaction.
-
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 50]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-7. Protocol Messages
-
- This section contains the detailed definition of the protocol mes-
- sages, including the information to include when sending the message,
- as well as the actions to take upon receiving the message. The mes-
- sage type for each message appears as [n] in the heading for the mes-
- sage (see section 6.1).
-
-7.1. BNDUPD message [3]
-
- The binding update (BNDUPD) message is used to send the binding data-
- base changes (known as binding update transactions) to the partner
- server, and the partner server responds with a binding acknowledge-
- ment (BNDACK) message when it has successfully committed those
- changes to its own stable storage.
-
- The rest of the failover protocol exists to determine whether the
- partner server is able to communicate or not, and to enable the
- partners to exchange BNDUPD/BNDACK messages in order to keep their
- binding databases in stable storage synchronized.
-
- The rest of this section is written as though every BNDUPD message
- contains only a single binding update transaction in order to reduce
- the complexity of the discussion. See section 6.3 for information on
- how to create and process BNDUPD and BNDACK messages which contain
- multiple binding update transactions. Note that while a server MAY
- generate BNDUPD messages with multiple binding update transactions,
- every server MUST be able to process a BNDUPD message which contains
- multiple binding update transactions and generate the corresponding
- BNDACK messages with status for multiple binding update transactions.
-
- The following table summarizes the various options for the BNDUPD
- message.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 51]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-
-
- binding-status BACKUP
- RESET
- ABANDONED
- Option ACTIVE EXPIRED RELEASED FREE
- ------ ------ ------- -------- ----
- assigned-IP-address (3) MUST MUST MUST MUST
- IP-flags MUST(4) MUST(4) MUST(4) MUST(4)
- binding-status MUST MUST MUST MUST
- client-identifier MAY MAY MAY MAY(2)
- client-hardware-address MUST MUST MUST MAY(2)
- lease-expiration-time MUST MUST NOT MUST NOT MUST NOT
- potential-expiration-time MUST MUST NOT MUST NOT MUST NOT
- start-time-of-state SHOULD SHOULD SHOULD SHOULD
- client-last-trans.-time MUST SHOULD MUST MAY
- DDNS(1) SHOULD SHOULD SHOULD SHOULD
- client-request-options SHOULD SHOULD NOT SHOULD SHOULD NOT
- client-reply-options SHOULD SHOULD NOT SHOULD NOT SHOULD NOT
-
- (1) MUST if server is performing dynamic DNS for this IP address, else
- MUST NOT.
- (2) MUST NOT if binding-status is ABANDONED.
- (3) assigned-IP-address MUST be the first option for an IP address
- (4) IP-flags option MUST appear if any flags are non-zero, else it
- MAY appear.
-
- Table 7.1-1: Options used in a BNDUPD message
-
-
-7.1.1. Sending the BNDUPD message
-
- A BNDUPD message SHOULD be generated whenever any binding changes. A
- change might be in the binding-status, the lease-expiration-time, or
- even just the last-transaction-time. In general, any time a DHCP
- server writes its stable storage, a BNDUPD message SHOULD be gen-
- erated. This will often be the result of the processing of a DHCP
- client request, but it might also be the result of a successful
- dynamic DNS update operation. Stable storage updates due to BNDUPD
- or BNDACK messages SHOULD NOT result in additional BNDUPD messages.
-
- BNDUPD (and BNDACK) messages refer to the binding-status of the IP
- address, and this protocol defines a series of binding-statuses, dis-
- cussed in more detail below. Some servers may not support all of
- these binding-statuses, and so in those cases they will not be sent.
- Upon receipt of a BNDUPD message which contains an unsupported
- binding-status, a reasonable interpretation should be made (see sec-
- tion 5.10).
-
-
-
-Droms, et. al. Expires September 2003 [Page 52]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- All BNDUPD messages MUST contain the IP address of the binding update
- transaction in the assigned-IP-address option.
-
- All binding update transactions MUST contain an IP-flags option if
- the value of any of the flags would be non-zero. The IP-flags option
- MAY be omitted if all of the flags that it contains are zero. The
- IP-flags option contains a flag which indicates if the IP address is
- currently reserved on the server sending the BNDUPD message. It also
- contains a flag which indicates that the lease is associated with a
- client that used the BOOTP protocol (as opposed to the DHCP protocol)
- to interact with the DHCP server.
-
- All binding update transactions contain a binding-status option, and
- it will have one of the values found in section 5.11. Client infor-
- mation consists of client-hardware-address and possibly a client-
- identifier, and is explained in more detail later in this section.
- The following table indicates whether client information should or
- should not appear with each binding-status in a binding update tran-
- saction:
-
-
- binding-status includes client information
- ------------------------------------------------
- ACTIVE MUST
- EXPIRED SHOULD
- RELEASED SHOULD
- FREE MAY
- ABANDONED MUST NOT
- RESET MAY
- BACKUP MAY
-
- Table 7.1.1-1: Client information required by various
- binding-status values.
-
-
- The ACTIVE binding-status requires some options to indicate the
- length of the binding:
-
-
- o lease-expiration-time
-
- The lease-expiration-time option MUST appear, and be set to the
- expiration time most recently ACKed to the DHCP client. Note
- that the time ACKed to a DHCP client is a lease duration in
- seconds, while the lease-expiration-time option in a BNDUPD mes-
- sage is an absolute time value.
-
- o potential-expiration-time
-
-
-
-Droms, et. al. Expires September 2003 [Page 53]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- The potential-expiration-time option MUST appear, and be set to
- a value beyond that of the lease-expiration time. This is the
- value that is ACKed by the BNDACK message. A server sending a
- BNDUPD message MUST be able to recover the potential-
- expiration-time sent in every BNDUPD, not just those that
- receive a corresponding BNDACK, in order to be able to protect
- against possible duplicate allocation of IP addresses after
- transitioning to PARTNER-DOWN state. See section 5.2.1 for
- details as to why the potential-expiration-time exists and
- guidelines for how to decide on the value.
-
- The following option information applies to all BNDUPD messages,
- regardless of the value of the binding-status, unless otherwise
- noted.
-
- o Identifying the client
-
- For many of the binding-status values a client MUST appear while
- for others a client MAY appear, and for some a client MUST NOT
- appear.
-
- A client is identified in a BNDUPD message by at least one and pos-
- sibly two options. The client-hardware-address option MUST appear
- any time that a client appears in a BNDUPD message, and contains
- the hardware type and chaddr information from the DHCP request
- packet. A failover client-identifier option MUST appear any time
- that a client appears in a BNDUPD message if and only if that
- client used a DHCP client-identifier option when communicating with
- the DHCP server. See section 12.5 and 12.4 for details of how to
- construct these two options from a DHCP request packet.
-
- o start-time-of-state
-
- The start-time-of-state SHOULD appear. It is set to the time at
- which this IP address first took on the state that corresponds to
- the current value of binding-status.
-
- o last-transaction-time
-
- The last-transaction-time value SHOULD appear. This is the time at
- which this DHCP server last received a packet from the DHCP client
- referenced by the client-identifier or client-hardware-address that
- was associated with the IP address referenced by the assigned-IP-
- address.
-
- o DDNS
-
- If the DHCP server is performing dynamic DNS operations on behalf
-
-
-
-Droms, et. al. Expires September 2003 [Page 54]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- of the DHCP client represented by the client-identifier or client-
- hardware-address, then it should include a DDNS option containing
- the domain name and status of any dynamic DNS operations enabled.
-
- o client-request-options
-
- If the BNDUPD was triggered by a request from a DHCP client (typi-
- cally those with binding-status of ACTIVE and RELEASED), then the
- server SHOULD include options of interest to a failover partner
- from the client's request packet in the client-request-options for
- transmission to its partner (see section 12.8).
-
- A server sending a BNDUPD SHOULD remember the "interesting" options
- or the information that would appear in an "interesting" option for
- transmission at a time when the BNDUPD is not closely associated
- with a DHCP client request.
-
- A server SHOULD send the following "interesting" options. It MAY
- send any DHCP client options. As new options are defined, the RFC
- defining these options SHOULD include information that they are
- "interesting to failover servers" if they should be sent as part of
- a BNDUPD.
-
-
- option option
- number name
- -----------------------------------------
-
- 12 host-name
- 81 client-FQDN [FQDN]
- 82 relay-agent-information [RFC 3046]
- 77 user-class [RFC 3004]
- 60 vendor-class-identifier
- 118 subnet-selection [RFC 3011]
-
- Table 7.1.1-2: Options which SHOULD be sent in
- the client-request-options option in a BNDUPD message.
-
-
- o client-reply-options
-
- If the BNDUPD was triggered by a request from a DHCP client (typi-
- cally those with binding-status of ACTIVE and RELEASED), then the
- server SHOULD include options of interest to a failover partner
- from the server's DHCP reply packet in the client-reply-options for
- transmission to its partner (see section 12.7).
-
- A server sending a BNDUPD SHOULD remember the "interesting" options
-
-
-
-Droms, et. al. Expires September 2003 [Page 55]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- or the information that would appear in an "interesting" option for
- transmission at a time when the BNDUPD is not closely associated
- with a DHCP client request.
-
- A server SHOULD send the following "interesting" options. It MAY
- send any DHCP client options. As new options are defined, the RFC
- defining these options SHOULD include information that they are
- "interesting to failover servers" if they should be sent as part of
- a BNDUPD.
-
-
- option option
- number name
- -----------------------------------------
-
- 58 renewal-time
- 59 rebinding-time
-
- Table 7.1.1-3: Options which SHOULD be sent in
- the client-reply-options option in a BNDUPD message.
-
-
- The BNDUPD message SHOULD be sent as soon as possible from the time
- that the DHCP client received a response and the lease bindings data-
- base is written on stable storage.
-
-7.1.2. Receiving the BNDUPD message
-
- When a server receives a BNDUPD message, it needs to decide how to
- process the binding update transaction it contains and whether that
- transaction represents a conflict of any sort. The conflict resolu-
- tion process MUST be used on the receipt of every BNDUPD message, not
- just those that are received while in POTENTIAL-CONFLICT state, in
- order to increase the robustness of the protocol.
-
- There are three sorts of conflicts:
-
- o Two clients, one IP address conflict
-
- This is the duplicate IP address allocation conflict. There are
- two different clients each allocated the same address. See sec-
- tion 7.1.3 for how to resolve this conflict.
-
- o Two IP addresses, one client conflict
-
- This conflict exists when a client on one server is associated
- with a one IP address, and on the other server with a different
- IP address in the same or a related subnet. This does not refer
-
-
-
-Droms, et. al. Expires September 2003 [Page 56]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- to the case where a single client has addresses in multiple dif-
- ferent subnets or administrative domains, but rather the case
- where on the same subnet the client has as lease on one IP
- address in one server and on a different IP address on the other
- server.
-
- This conflict may or may not be a problem for a given DHCP
- server implementation. In the event that a DHCP server requires
- that a DHCP client have only one outstanding lease for an IP
- address on one subnet, this conflict should be resolved by
- accepting the lease information which has the latest client-
- last-transaction-time.
-
- o binding-status conflict
-
- This is normal conflict, where one server is updating the other
- with newer information. See section 7.1.3 for details of how to
- resolve these conflicts.
-
-7.1.3. Deciding whether to accept the binding update transaction in a
-BNDUPD message
-
- When analyzing a BNDUPD message from a partner server, if there is
- insufficient information in the BNDUPD to process it, then reject the
- BNDUPD with reject-reason 3: "Missing binding information".
-
- If the IP address in the BNDUPD is not an IP address associated with
- the failover endpoint which received the BNDUPD message, then reject
- it with reject-reason 1: "Illegal IP address (not part of any address
- pool)".
-
- IP addresses undergo binding status changes for several reasons,
- including receipt and processing of DHCP client requests, administra-
- tive inputs and receipt of BNDUPD messages. Every DHCP server needs
- to respond to DHCP client requests and administrative inputs with
- changes to its internal record of the binding-status of an IP
- address, and this response is not in the scope of the failover proto-
- col. However, the receipt of BNDUPD messages implies at least a pos-
- sible change of the binding-status for an IP address, and must be
- discussed here. See section 7.1.2 for general actions to take upon
- receipt of a BNDUPD message.
-
- Every BNDUPD message SHOULD contain a client-last-transaction-time
- option, which MUST, if it appears, be the time that the server last
- interacted with the DHCP client. It MUST NOT be, for instance, the
- time that the lease on an IP address expired. If there has been no
- interaction with the DHCP client in question (or there is no DHCP
- client presently associated with this IP address), then there will be
-
-
-
-Droms, et. al. Expires September 2003 [Page 57]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- no client-last-transaction-time option in the BNDUPD message.
-
- The list in Figure 7.1.3-1 is indexed by the binding-status that a
- server receives in a BNDUPD message. In many cases, the binding-
- status of an IP address within the receiving server's data storage
- will have an affect upon the checks performed prior to accepting the
- new binding-status in a BNDUPD message.
-
- In Figure 7.1.3-1, to "accept" a BNDUPD means to update the server's
- bindings database with the information contained in the BNDUPD and
- once that update is complete, send a BNDACK message corresponding to
- the BNDUPD message. To "reject" a BNDUPD means to respond to the
- BNDUPD with a BNDACK with a reject-reason option included.
-
- When interpreting the information in the following table (Figure
- 7.1.3-1), for those rules that are listed with "time" -- if a BNDUPD
- doesn't have a client-last-transaction-time value, then it MUST NOT
- be considered later than the client-last-transaction-time in the
- receiving server's binding. If the BNDUPD contains a client-last-
- transaction-time value and the receiving server's binding does not,
- then the client-last-transaction-time value in the BNDUPD MUST be
- considered later than the server's.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 58]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-
- binding-status in received BNDUPD
- binding-status
- in receiving FREE RESET
- server ACTIVE EXPIRED RELEASED BACKUP ABANDONED
-
- ACTIVE accept(5) time(2) time(1) time(2) accept
- EXPIRED time(1) accept accept accept accept
- RELEASED time(1) time(1) accept accept accept
- FREE/BACKUP accept accept accept accept accept
- RESET time(3) accept accept accept accept
- ABANDONED reject(4) reject(4) reject(4) reject(4) accept
-
- time(1): If the client-last-transaction-time in the BNDUPD
- is later than the client-last-transaction-time in the
- receiving server's binding, accept it, else reject it.
-
- time(2): If the current time is later than the receiving
- servers' lease-expiration-time, accept it, else reject it.
-
- time(3): If the client-last-transaction-time in the BNDUPD
- is later than the start-time-of-state in the receiving server's
- binding, accept it, else reject it.
-
- (1,2,3): If rejecting, use reject reason 15: "Outdated binding
- information".
-
- (4): Use reject reason 16: "Less critical binding information".
-
- (5): If the clients in a BNDUPD message and in a receiving
- server's binding differ, then if the receiving server is a
- secondary accept it, else reject it with a reject reason of 2:
- "Fatal conflict exists: address in use by other client".
-
- Figure 7.1.3-1: Accepting BNDUPD messages
-
-
-
- If the IP address in the BNDUPD message has the R flag set in the
- IP-flags option, indicating it is a reserved IP address, and if the
- binding-status in the BNDUPD is BACKUP, then if the receiving server
- does not show the IP address as reserved, the receiving server SHOULD
- reject the BNDUPD using reject reason 19: "IP not reserved on this
- server".
-
-7.1.4. Accepting the BNDUPD message
-
- When accepting a BNDUPD message, the information contained in the
-
-
-
-Droms, et. al. Expires September 2003 [Page 59]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- client-request-options and client-reply-options SHOULD be examined
- for any information of interest to this server. For instance, a
- server which wished to detect changes in client specified host names
- might want to examine and save information from the host-name or
- client-FQDN options. Servers which expect to utilize information
- from the relay-agent-information option SHOULD store this informa-
- tion.
-
-7.1.5. Time values related to the BNDUPD message
-
- There are four time values that MAY be sent in a BNDUPD message.
-
- o lease-expiration-time
-
- The time that the server gave to the client, i.e., the time that
- the server believes that the client's lease will expire.
-
- o potential-expiration-time
-
- The time that the server wants to be sure its partner waits
- (added to the MCLT) before assuming that this lease has expired.
- Typically some time beyond the desired client lease time.
-
- o client-last-transaction-time
-
- The time that the client last interacted with this server.
-
- o start-time-of-state
-
- The time at which the binding first went into the current state.
-
- As discussed in section 5.2, each server knows what its partner has
- ACKed with regard to potential-expiration time. In addition, each
- server needs to remember what it has told its partner as the
- potential-expiration-time. Moreover, each server must remember what
- it has acked to the *other* server as the most recent potential-
- expiration-time from that server.
-
- Remember that each server sends a potential-expiration-time and
- receives an ACK for that as well as receiving a potential-
- expiration-time and needing to remember what it has acked for that.
-
- While they don't have to be named in any particular way, the times
- that a server needs to remember for every IP address in order to
- implement the failover protocol are:
-
- o lease-expiration-time
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 60]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- The time that a server gave to the DHCP client. A DHCP server
- needs to remember this time already, just to be a DHCP server.
- A server SHOULD update this time with the lease-expiration time
- received from a partner in a BNDUPD if the received lease-
- expiration time is later than the lease-expiration time recorded
- for this binding.
-
- o sent-potential-expiration-time
-
- The latest time sent to the partner for a potential-expiration-
- time.
-
- o acked-potential-expiration-time
-
- The latest time that the partner has acked for a potential
- expiration time. Typically the same as sent-potential-
- expiration-time if there is not a BNDUPD outstanding.
-
- o received-potential-expiration-time
-
- The latest time that this server has ever received as a
- potential-expiration-time from its partner in a BNDUPD that this
- server ACKed.
-
- So, a server has to remember two additional times concerning BNDUPD
- messages that it has initiated, and one additional time concerning
- BNDUPD message that it has received. How are these times used?
-
- First, let's look at the time that a DHCP server can offer to a DHCP
- client. A server can offer to a DHCP client a time that is no longer
- than the MCLT beyond the max( received-potential-expiration-time,
- acked-potential-expiration-time). One might think that the server
- should be able to offer only the MCLT beyond the acked-potential-
- expiration-time, and while that is certainly simple and easy to
- understand, it has negative consequences in actual operation.
-
- To illustrate this, in the simple case where the primary updates the
- secondary for a while and then fails, if the secondary can then renew
- the client for only the MCLT beyond the acked-potential-expiration-
- time, then the secondary will only be able to renew the client for
- the MCLT, because the secondary has never sent a BNDUPD packet to the
- primary concerning this IP address and client, and so its acked-
- potential-expiration-time is zero.
-
- However, since the secondary is allowed to renew the client with the
- MCLT beyond the max( received-potential-expiration-time, acked-
- potential-expiration-time), then the secondary can usually renew the
- client for the full lease period, at least for the first renew it
-
-
-
-Droms, et. al. Expires September 2003 [Page 61]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- sees from the client, since the received-potential-expiration-time is
- generally longer than the client's desired lease interval. The
- difference in renew times could make a big difference in server load
- on the secondary in this case.
-
- What are the consequences of allowing a server to offer a DHCP client
- a lease term of the MCLT beyond the max( received-potential-
- expiration-time, acked-potential-expiration-time)? The consequences
- appear whenever a server enters PARTNER-DOWN state, and affect how
- long that server has to wait before reallocating expired leases.
- With this approach, when a server goes into PARTNER-DOWN state, it
- must wait the MCLT beyond the max( lease-expiration-time, sent-
- potential-expiration-time, acked-potential-expiration-time,
- received-potential-expiration-time ) for each IP address before it
- can reallocate that IP address to another DHCP client. One might
- normally think that it needed to wait only the MCLT beyond the max(
- lease-expiration-time, received-potential-expiration-time ), i.e.,
- beyond what it has told the client and what it has explicitly acked
- to the other server. But with the optimization discussed above --
- where either server can offer the DHCP client a lease term of the
- MCLT beyond the max( received-potential-expiration-time, acked-
- potential-expiration-time), then the additional times sent-
- potential-expiration-time and acked-potential-expiration-time must be
- added into the expression, since the partner could have used those
- times as part of its own lease time calculation.
-
- Thus this optimization may require a longer waiting time when enter-
- ing PARTNER-DOWN state, but will generally allow servers to operate
- considerably more effectively when running in COMMUNICATIONS-
- INTERRUPTED state.
-
-7.2. BNDACK message [4]
-
- A server sends a binding acknowledgement (BNDACK) message when it has
- processed a BNDUPD message and after it has successfully committed to
- stable storage any binding database changes made as a result of pro-
- cessing the BNDUPD message. A BNDACK message is used to both accept
- or reject a BNDUPD message. A BNDACK message which contains a
- reject-reason option is a rejection of the corresponding BNDUPD mes-
- sage.
-
- In order to reduce the complexity of the discussion, the rest of this
- section is written as though every BNDUPD message contains only a
- single binding update transaction and thus every corresponding BNDACK
- message would also contain reply information about only a single
- binding update transaction. See section 6.3 for information on how
- to create and process BNDUPD and BNDACK messages which contain multi-
- ple binding update transactions.
-
-
-
-Droms, et. al. Expires September 2003 [Page 62]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- Note that while a server MAY generate BNDUPD messages with multiple
- binding update transactions, every server MUST be able to process a
- BNDUPD message which contains multiple binding update transactions
- and generate the corresponding BNDACK messages with status for multi-
- ple binding update transactions. If a server does not ever create
- BNDUPD messages which contain multiple binding update transactions,
- then it does not need to be able to process a received BNDACK message
- with multiple binding update transactions. However, all servers MUST
- be able to create BNDACK messages which deal with multiple binding
- update transactions received in a BNDUPD message.
-
- Every BNDUPD message that is received by a server MUST be responded
- to with a corresponding BNDACK message. The receiving server SHOULD
- respond quickly to every BNDUPD message but it MAY choose to respond
- preferentially to DHCP client requests instead of BNDUPD messages,
- since there is no absolute time period within which a BNDACK must be
- sent in response to a BNDUPD message, while DHCP clients frequently
- have strict time constraints.
-
- A BNDACK message can only be sent in response to a BNDUPD message
- using the same TCP connection from which the BNDUPD message was
- received, since the XID's in BNDUPD messages are guaranteed unique
- only during the life of a single TCP connection. When a connection
- to a partner server goes down, a server with unprocessed BNDUPD mes-
- sages MAY simply drop all of those messages, since it can be sure
- that the partner will resend them when they are next in communica-
- tions (albeit with a different XID), or it MAY instead choose to pro-
- cess those BNDUPD messages, but it MUST NOT send any BNDACK messages
- in response.
-
- The following table summarizes the options for the BNDACK message.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 63]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-
-
- Option accept reject
- ------ ------ ------
- assigned-IP-address (1) MUST MUST
- IP-flags SHOULD NOT SHOULD NOT
- binding-status SHOULD NOT SHOULD NOT
- client-identifier SHOULD NOT SHOULD NOT
- client-hardware-address SHOULD NOT SHOULD NOT
- reject-reason SHOULD NOT MUST
- message SHOULD NOT SHOULD
- lease-expiration-time SHOULD NOT SHOULD NOT
- potential-expiration-time SHOULD NOT SHOULD NOT
- start-time-of-state SHOULD NOT SHOULD NOT
- client-last-trans.-time SHOULD NOT SHOULD NOT
- DDNS(1) SHOULD NOT SHOULD NOT
-
- (1) assigned-IP-address MUST be the first option for an IP address
-
- Table 7.2-1: Options used in a BNDACK message
-
-
-7.2.1. Sending the BNDACK message
-
- The BNDACK message MUST contain the same xid as the corresponding
- BNDUPD message.
-
- The assigned-IP-address option from the BNDUPD message MUST be
- included in the BNDACK message. Any additional options from the
- BNDUPD message SHOULD NOT appear in the BNDACK message. Note that
- any information sent in options (e.g, a later lease-expiration time)
- in the BNDACK message MUST NOT be assumed to necessarily be recorded
- in the stable storage of the server who receives the BNDACK message
- because there is no corresponding ACK of the BNDACK message. Any
- information that SHOULD be recorded in the partner server's stable
- storage MUST be transmitted in a subsequent BNDUPD.
-
- If the server is accepting the BNDUPD, the BNDACK message includes
- only the assigned-IP-address option. If the server is rejecting the
- BNDUPD, the additional option reject-reason MUST appear in the BNDACK
- message, and the message option SHOULD appear in this case containing
- a human-readable error message describing in some detail the reason
- for the rejection of the BNDUPD message.
-
- If the server rejects the BNDUPD message with a BNDACK and a reject-
- reason option, it may be because the server believes that it has
- binding information that the other server should know. A server
- which is rejecting a BNDUPD may initiate a BNDUPD of its own in order
-
-
-
-Droms, et. al. Expires September 2003 [Page 64]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- to update its partner with what it believes is better binding infor-
- mation, but it MUST ensure through some means that it will not end up
- in a situation where each server is sending BNDUPD messages as fast
- as possible because they can't agree on which server has better bind-
- ing data. Placing a considerable delay on the initiation of a BNDUPD
- message after sending a BNDACK with a reject-reason would be one way
- to ensure this situation doesn't occur.
-
-7.2.2. Receiving the BNDACK message
-
- When a server receives a BNDACK message, if it doesn't contain a
- reject-reason option that means that the BNDUPD message was accepted,
- and the server which sent the BNDUPD SHOULD update its stable storage
- with the potential-expiration-time value sent in the BNDUPD message.
-
- If the BNDACK message contains a reject-reason option, that means
- that the BNDUPD was rejected. There SHOULD be a message option in
- the BNDACK giving a text reason for the rejection, and the server
- SHOULD log the message in some way. The server MUST NOT immediately
- try to resend the BNDUPD message as there is no reason to believe the
- partner won't reject it a second time. However a server MAY choose
- to send another BNDUPD at some future time, for instance when the
- server next processes an update request from its partner.
-
-7.3. UPDREQ message [9]
-
- The update request (UPDREQ) message is used by one server to request
- that its partner send it all of the binding database information that
- it has not already seen. Since each server is required to keep
- track at all times of the binding information the other server has
- ACKed, one server can request transmission of all un-ACKed binding
- database information held by the other server by using the UPDREQ
- message.
-
- The UPDREQ message is used whenever the sending server cannot proceed
- before it has processed all previously un-ACKed binding update infor-
- mation, since the UPDREQ message should yield a corresponding UPDDONE
- message. The UPDDONE message is not sent until the server that sent
- the UPDREQ message has responded to all of the BNDUPD messages gen-
- erated by the UPDREQ message with BNDACK messages (they may either be
- accepted or rejected by the BNDACK messages, but they MUST have been
- responded to). Thus, the sender of the UPDREQ message can be sure
- upon receipt of an UPDDONE message that it has received and committed
- to stable storage all outstanding binding database updates.
-
- See section 9, Failover Endpoint States, for the details of when the
- UPDREQ message is sent.
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 65]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-7.3.1. Sending the UPDREQ message
-
- The UPDREQ message has no message specific options.
-
-7.3.2. Receiving the UPDREQ message
-
- A server receiving an UPDREQ message MUST send all binding database
- changes that have not yet been ACKed by the sending server. These
- changes are sent as undistinguished BNDUPD messages.
-
- However, the server which received and is processing the UPDREQ mes-
- sage MUST track the BNDACK messages that correspond to the BNDUPD
- messages triggered by the UPDREQ message and, when they are all
- received, the server MUST send an UPDDONE message.
-
- The server processing the UPDREQ message and sending BNDUPD messages
- to its partner SHOULD only track the BNDUPD and BNDACK message pairs
- for unACKed binding database changes that were present upon the
- receipt of the UPDREQ message. A server which has received an UPDREQ
- message SHOULD send BNDUPD messages for binding database changes that
- occur after receipt of the UPDREQ message, but it SHOULD NOT include
- those additional BNDUPD messages and their corresponding BNDACK mes-
- sages in the accounting necessary to consider the UPDREQ complete and
- subsequently send the UPDDONE message. If some additional binding
- database changes end up becoming part of the set of BNDUPD messages
- considered as part of the UPDREQ (due to whatever algorithm the
- server uses to scan its bindings database for unacked changes) it
- will probably not cause any difficulty, but a server MUST NOT attempt
- to include all such later BNDUPD messages in the accounting for the
- UPDREQ in order to be able to transmit an UPDDONE message.
-
- When queuing up the BNDUPD messages for transmission to the sender of
- the UPDREQ message, the server processing the UPDREQ message MUST
- honor the value returned in the max-unacked-bndupd option in the CON-
- NECT or CONNECTACK message that set up the connection with the send-
- ing server. It MUST NOT send more BNDUPD messages without receiving
- corresponding BNDACKs than the value returned in max-unacked-bndupd.
- (See section 8 for more details.)
-
-7.4. UPDREQALL message [7]
-
- The update request all (UPDREQALL) message is used by one server to
- request that its partner send it all of the binding database informa-
- tion. This message is used to allow one server to recover from a
- failure of stable storage and to restore its binding database in its
- entirety from the other server.
-
- A server which sends an UPDREQALL message cannot proceed until all of
-
-
-
-Droms, et. al. Expires September 2003 [Page 66]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- its binding update information is restored, and it knows that all of
- that information is restored when an UPDDONE message is received.
-
- See section 9, Protocol state transitions, for the details of when
- the UPDREQALL message is sent.
-
- The UPDREQALL message has no message specific options.
-
-7.4.1. Sending the UPDREQALL message
-
- The UPDREQALL is sent.
-
-7.4.2. Receiving the UPDREQALL message
-
- A server receiving an UPDREQALL message MUST send all binding data-
- base information to the sending server. See section 5.16 for details
- of what might actually comprise "all binding database information".
-
- A server receiving an UPDREQALL message MUST remember that such a
- message has been received, ensure that all binding information extant
- at that point is sent to the partner prior to any UPDDONE message
- being sent to that partner. One way to do this is to remember the
- receipt of an UPDREQALL message and to and treat every subsequent
- UPDREQ message as an UPDREQALL message until it sends the first
- UPDDONE message after receipt of the UPDREQALL message. This
- requirement exists because communications may fail and become re-
- established between the two servers, and the specific conditions
- which provoked the UPDREQALL message may not longer exist even though
- the UPDREQALL message may not yet have completed. See section 5.17
- for information on a more efficient way to meet the above require-
- ment.
-
- These changes are sent as undistinguished BNDUPD messages. Otherwise
- the processing is the same as for the UPDREQ message. See section
- 7.3.2 for details.
-
-7.5. UPDDONE message [8]
-
- The update done (UPDDONE) message is used by a server receiving an
- UPDREQ or UPDREQALL message to signify that it has sent all of the
- BNDUPD messages requested by the UPDREQ or UPDREQALL request and that
- it has received a BNDACK for each of those messages.
-
- While a BNDACK message MUST have been received for each BNDUPD mes-
- sage prior to the transmission of the UPDDONE message, this doesn't
- necessarily mean that all of the BNDUPD messages were accepted, only
- that all of them were responded to with a BNDACK message. Thus, a
- NAK (comprised of a BNDACK message containing a reject-reason option)
-
-
-
-Droms, et. al. Expires September 2003 [Page 67]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- could be used to reject a BNDUPD, but for the purposes of the UPDDONE
- message, such NAK would count as a response to the associated BNDUPD
- message, and would not block the eventual transmission of the UPDDONE
- message.
-
- The xid in an UPDDONE message MUST be identical to the xid in the
- UPDREQ or UPDREQALL message that initiated the update process.
-
- The UPDDONE message has no message specific options.
-
-7.5.1. Sending the UPDDONE message
-
- The UPDDONE message SHOULD be sent as soon as the last BNDACK message
- corresponding to a BNDUPD message requested by the UPDREQ or
- UPDREQALL is received from the server which sent the UPDREQ or
- UPDREQALL. The XID of the UPDDONE message MUST be the same as the
- XID of the corresponding UPDREQ or UPDREQALL message.
-
-7.5.2. Receiving the UPDDONE message
-
- A server receiving the UPDDONE message knows that all of the informa-
- tion that it requested by sending an UPDREQ or UPDREQALL message has
- now been sent and that it has recorded this information in its stable
- storage. It typically uses the receipt of an UPDDONE message to move
- to a different failover state. See sections 9.5.2 and 9.8.3 for
- details.
-
-7.6. POOLREQ message [1]
-
- The pool request (POOLREQ) message is used by the secondary server to
- request an allocation of IP addresses from the primary server. It
- MUST be sent by a secondary server to a primary server to request IP
- address allocation by the primary. The IP addresses allocated are
- transmitted using normal BNDUPD messages from the primary to the
- secondary.
-
- The POOLREQ message SHOULD be sent from the secondary to the primary
- whenever the secondary makes a transition into NORMAL state. It
- SHOULD periodically be resent in order that any change in the number
- of available IP addresses on the primary be reflected in the pool on
- the secondary. The period may be influenced by the secondary
- server's leasing activity.
-
- The POOLREQ message has no message specific options.
-
-7.6.1. Sending the POOLREQ message
-
- The POOLREQ message is sent.
-
-
-
-Droms, et. al. Expires September 2003 [Page 68]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-7.6.2. Receiving the POOLREQ message
-
- When a primary server receives a POOLREQ message it SHOULD examine
- the binding database and determine how many IP addresses the secon-
- dary server should have, and set these IP addresses to BACKUP state.
- It SHOULD then send BNDUPD messages concerning all of these IP
- addresses to the secondary server.
-
- Servers frequently have several kinds of IP addresses available on a
- particular network segment. The failover protocol assumes that both
- primary and secondary servers are configured in such a way that each
- knows the type and number of IP addresses on every network segment
- participating in the failover protocol. The primary server is
- responsible for allocating the secondary server the correct propor-
- tion of available IP addresses of each kind, and the secondary server
- is responsible for being configured in such a way that it can tell
- the kind of every IP address based solely on the IP address itself.
-
- A primary server MUST keep track of how many IP addresses were allo-
- cated as a result of processing the POOLREQ message, and send that
- number in the POOLRESP message.
-
- A primary server MAY choose to defer processing a POOLREQ message
- until a more convenient time to process it, but it should not depend
- on the secondary server to resend the POOLREQ message in that case.
-
- If a secondary server receives a POOLREQ message it SHOULD report an
- error.
-
-7.7. POOLRESP message [2]
-
- A primary server sends a POOLRESP message to a secondary server after
- the allocation process for available addresses to the secondary
- server is complete. Typically this message will precede some of the
- BNDUPD messages that the primary uses to send the actual allocated IP
- addresses to the secondary.
-
- The xid in the POOLRESP message MUST be identical to the xid in the
- POOLREQ message for which this POOLRESP is a response.
-
-
-7.7.1. Sending the POOLRESP message
-
- The POOLRESP message MUST contain the same xid as the corresponding
- POOLREQ message.
-
- Only one option MUST appear in a POOLREQ message:
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 69]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- o addresses-transferred
-
- The number of addresses allocated to the secondary server by the
- primary server as a result of a POOLREQ is contained in the
- addresses-transferred option in a POOLRESP message. Note this
- is the number of addresses that are transferred to the secondary
- in the primary's binding database as a result of the correspond-
- ing POOLREQ message, and that it may be some time before they
- can all be transmitted to the secondary server through the use
- of BNDUPD messages.
-
-7.7.2. Receiving the POOLRESP message
-
- When a secondary server receives a POOLRESP message, it SHOULD send
- another POOLREQ message if the value of the addresses-transferred
- option is non-zero.
-
- Typically, no other action is taken on the reception of a POOLRESP
- message.
-
-7.8. CONNECT message [5]
-
- The connect message is used to establish an applications level con-
- nection over a newly created TCP connection. It gives the source
- information for the connection and critical configuration informa-
- tion. It MUST be sent only by the primary server. Either server can
- initiate a TCP connection, but the CONNECT message is only sent by
- the primary server.
-
- The CONNECT message MUST be the first message sent down a newly esta-
- blished connection, and it MUST be sent only by the primary server.
-
- The following table summarizes the options that are associated with
- the CONNECT message:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 70]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-
-
- Option
- ------
- relationship-name MUST
- max-unacked-bndupd MUST
- receive-timer MUST
- vendor-class-identifier MUST
- protocol-version MUST
- TLS-request MUST (1)
- MCLT MUST
- hash-bucket-assignment MUST
-
- (1) MUST NOT if CONNECT is being sent over a TLS connection
-
- Table 7.8-1: Options used in a CONNECT message
-
-
-7.8.1. Sending the CONNECT message
-
- The CONNECT message MUST be the first message sent by the primary
- server after the establishment of a new TCP connection with a secon-
- dary server participating in the failover protocol.
-
- The xid of the CONNECT message is not related to any previous xid
- sequence, but initiates the sequence for this connection.
-
- The name of the failover relationship MUST be placed in the
- relationship-name option. This information is placed in an option
- inside of the message in order to allow the identity of the sender to
- be covered by a shared secret.
-
- The number of BNDUPD messages the primary server can accept without
- blocking the TCP connection MUST be placed in the max-unacked-bndupd
- option. This MUST be a number equal to or greater than 1, SHOULD be
- a number greater than 10, and SHOULD be a number less than 100.
-
- The length of the receive timer (tReceive, see section 8.3) MUST be
- placed in the receive-timer option.
-
- The MCLT MUST be placed in the MCLT option.
-
- The hash-bucket-assignment option MUST be included in the CONNECT
- message. In the event that load balancing is not configured for this
- server, the hash-bucket-assignment option will indicate that. The
- value of the hash-bucket-assignment option is determined from the
- specific buckets that the primary server has determined that the
- secondary server MUST service as part of the load-balancing
-
-
-
-Droms, et. al. Expires September 2003 [Page 71]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- algorithm. The way in which the primary server determines this
- information is outside the scope of this protocol definition. The
- primary server SHOULD be configured with a percentage of clients that
- the secondary server will be instructed to service, and the primary
- server SHOULD use the algorithm in [RFC 3074] to generate a Hash
- Bucket Assignment which it sends to the secondary server.
-
- The vendor class identifier MUST be placed in the vendor-class-
- identifier option.
-
- The protocol-version option MUST be included in every CONNECT mes-
- sage. The current value of the protocol version is 1.
-
- The TLS-request option MUST be sent and contains the desired TLS con-
- nection request as well as information concerning whether TLS is sup-
- ported. If this CONNECT message is being sent over a already
- created TLS connection, the TLS-request MUST NOT appear.
-
-7.8.2. Receiving the CONNECT message
-
- When a server established a TCP connection on a failover port, if it
- is a PRIMARY server it should send a CONNECT message, and if it is a
- secondary server it should wait for a CONNECT message before sending
- any messages. To avoid denial of service attacks, a secondary should
- only wait for a CONNECT message on a new connection for a limited
- amount of time and close the connection if none is received during
- that time.
-
- When a secondary server receives a CONNECT message it should:
-
- 1. Record the time at which the message was received.
-
- 2. Examine the protocol-version option, and decide if this server
- is capable of interoperating with another server running that
- protocol version. If not, send the CONNECTACK message with
- the reject reason 14: "Protocol version mismatch". The server
- MUST include its protocol-version in the CONNECTACK message.
-
- 3. Examine the TLS-request option. Figure out the TLS-reply
- value based on the capabilities and configuration of this
- server. If the result for the TLS-reply value is a 1 and the
- connection is accepted, indicating use of TLS, then immedi-
- ately send the CONNECTACK message and go into TLS negotiation.
- If the TLS-reply value implies rejection of the connection,
- then immediately send the CONNECTACK message with the TLS-
- reply value and the appropriate reject-reason option value.
- In all other cases, save the TLS-reply option information for
- the eventual CONNECTACK message.
-
-
-
-Droms, et. al. Expires September 2003 [Page 72]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- The possibilities for TLS-request and TLS-reply are:
-
- CONNECT CONNECTACK
- TLS TLS
- request reply
- Reject
- t1 t1 Reason Comments
- -- -- ------ --------
- 0 0 no TLS used
- 0 1 11 primary won't use TLS, secondary requires TLS
- 1 0 primary desires TLS, secondary doesn't
- 1 1 primary desires TLS, secondary will use TLS
- 2 0 9, 10 primary requires TLS and secondary won't
- 2 1 primary requires TLS and secondary will use TLS
-
-
-
- 4. Check to see if there is a message-digest option in the CON-
- NECT message. If there was, and the server does not support
- message-digests, then reject the connection with reject reason
- 12: "Message digest not supported" in the CONNECTACK. If the
- server does support message-digests, then check this message
- for validity based on the message-digest, and reject it if the
- digest indicates the message was altered with reject reason
- 20: "Message digest failed to compare".
-
- 5. Determine if the sender (from the relationship-name option)
- and the implicit role of the sender (i.e., primary) represents
- a server with which the receiver was configured to engage in
- failover activity. This is performed after any TLS or message
- digest processing so that it occurs after a secure connection
- is created, to ensure that there is no tampering with the
- relationship name of the partner. In the absence of any other
- security capability (i.e., when TLS or a message digest is not
- used), the server MAY wish to be configured with the IP
- address of the partner and check the source-ip of the CONNECT
- message against that IP address as a weak form of security.
-
- If not, then the receiving server should reject the CONNECT
- request by sending a CONNECTACK message with a reject-reason
- value of: 8, invalid failover partner.
-
- If it is, then the receiving failover endpoint should be
- determined.
-
- 6. Decide if the time delta between the sending of the message,
- in the time field, and the receipt of the message, recorded in
- step 1 above, is acceptable. A server MAY require an
-
-
-
-Droms, et. al. Expires September 2003 [Page 73]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- arbitrarily small delta in time values in order to set up a
- failover connection with another server. See section 5.10 for
- information on time synchronization.
-
- If the delta between the time values is too great, the server
- should reject the CONNECT request by sending a CONNECTACK mes-
- sage with a reject-reason of 4, time mismatch too great.
-
- If the time mismatch is not considered too great then the
- receiving server MUST record the delta between the servers.
- The receiving server MUST use this delta to correct all of the
- absolute times received from the other server in all time-
- valued options. Note that servers can participate in failover
- with arbitrarily great time mismatches, as long as it is more
- or less constant.
-
- 7. Examine the MCLT option in the CONNECT request and use the
- value of the MCLT as the MCLT for this failover endpoint.
-
- The secondary server SHOULD be able to operate with any MCLT
- sent by the primary, but if it cannot, then it should send a
- CONNECTACK with a reject-reason of 5, MCLT mismatch. In the
- event that the MCLT from the primary does not match that con-
- figured on the secondary, and the secondary will run with the
- primary's value, then the secondary MUST save the MCLT in
- secondary storage since it will need it even if it cannot con-
- tact the primary. The secondary MUST NOT use a different MCLT
- value than it received from the primary even if it cannot con-
- tact the primary.
-
- 8. The server MUST store hash-bucket-assignment option for use
- during processing during NORMAL state. If this hash bucket
- assignment conflicts with the secondary server's configured
- hash bucket assignment for use in other than NORMAL state, the
- secondary server should send a CONNECTACK with a reject reason
- of 19, Hash bucket assignment conflict.
-
- 9. The receiving server MAY use the vendor-class-identifier to do
- vendor specific processing.
-
-7.9. CONNECTACK message [6]
-
- The CONNECTACK message is sent to accept or reject a CONNECT message.
- It is sent by the secondary server which received a CONNECT message.
-
- Attempting immediately to reconnect after either receiving a CONNEC-
- TACK with a reject-reason or after sending a CONNECTACK with a
- reject-reason could yield unwanted looping behavior, since the reason
-
-
-
-Droms, et. al. Expires September 2003 [Page 74]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- that the connection was rejected may well not have changed since the
- last attempt. A simple suggested solution is to wait a minute or two
- after sending or receiving a CONNECTACK message with a reject-reason
- before attempting to reestablish communication.
-
- The following table summarizes the options associated with the CON-
- NECTACK message:
-
-
- Option accept reject
- ------
- relationship-name MUST MUST
- max-unacked-bndupd MUST MUST NOT
- receive-timer MUST MUST NOT
- vendor-class-identifier MUST MUST NOT
- protocol-version MUST MUST
- TLS-reply (1) (2)
- reject-reason MUST NOT MUST
- message MUST NOT SHOULD
- MCLT MUST NOT MUST NOT
- hash-bucket-assignment MUST NOT MUST NOT
-
- (1) MUST NOT if sending CONNECTACK after TLS negotiation, MUST
- if TLS-request in CONNECT, else MUST NOT.
- (2) MUST if TLS-request in CONNECT message, else MUST NOT.
-
- Table 7.9-1: Options used in a CONNECTACK message
-
-
-7.9.1. Sending the CONNECTACK message
-
- The xid of the CONNECTACK message MUST be that of the corresponding
- CONNECT message.
-
- The name of the relationship MUST be placed in the relationship-name
- option. This information is placed in an option inside of the mes-
- sage in order to allow the identity of the sender to be covered by a
- shared secret.
-
- The protocol-version option MUST be included in every CONNECTACK mes-
- sage. The current value of the protocol version is 1.
-
- If the connection has been rejected, the reject-reason option MUST be
- placed in the CONNECTACK message with an appropriate reason, and a
- message option SHOULD be included with a human-readable error message
- describing the reason for the rejection in some detail. If the
- reject-reason option appears, then the remaining options listed below
- do not appear. The sending server should close the connection after
-
-
-
-Droms, et. al. Expires September 2003 [Page 75]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- sending the CONNECTACK if the connection was rejected.
-
- The results of the TLS negotiation MUST be placed in the TLS-reply
- option. If this CONNECTACK message is being sent over an already TLS
- secured connection, then there MUST NOT be a TLS-reply option.
-
- If there was a message-digest option in the CONNECT message, then
- there MUST be a message-digest in the CONNECTACK message and any sub-
- sequent messages if the CONNECTACK does not contain a reject-reason.
-
- The number of BNDUPD messages the server can accept without blocking
- the TCP connection MUST be placed in the max-unacked-bndupd option.
- This SHOULD be a number greater than 10, and SHOULD be a number less
- than 100.
-
- The length of the receive timer (tReceive, see section 8.3) MUST be
- placed in the receive-timer option.
-
- The vendor class identifier MUST be placed in the vendor-class-
- identifier option.
-
- After a connection is created (either by sending a CONNECTACK message
- to the first CONNECT message, or sending a CONNECTACK message to a
- CONNECT message received over a TLS connection), the server MUST send
- a STATE message.
-
- After a connection is created, the server MUST start two timers for
- the connection: tSend and tReceive. The tSend timer SHOULD be
- approximately 33 percent of the time in the receiver-timer option in
- the corresponding CONNECT message. The tReceive timer SHOULD be the
- time sent in the receiver-timer option in the CONNECTACK message.
-
- The tReceive timer is reset whenever a message is received from this
- TCP connection. If it ever expires, the TCP connection is dropped
- and communications with this partner is considered not ok. The
- reject reason 17: "No traffic within sufficient time" is placed in
- the DISCONNECT message sent prior to dropping the TCP connection.
-
- The tSend timer is reset whenever a message is sent over this connec-
- tion. When it expires, a CONTACT message MUST be sent.
-
-7.9.2. Receiving the CONNECTACK message
-
- If a CONNECTACK message is received with a different XID from the one
- in the CONNECT that was sent, it SHOULD be ignored. To avoid denial
- of service attacks, a primary should only wait for a CONNECTACK mes-
- sage on a new connection for a limited amount of time and close the
- connection if none is received during that time.
-
-
-
-Droms, et. al. Expires September 2003 [Page 76]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- When a CONNECTACK message is received, the following actions should
- be taken:
-
- 1. Record the time the message was received.
-
- 2. Check to see if the xid on the CONNECTACK matches an outstand-
- ing CONNECT message on this TCP connection.
-
- 3. Check to see if there is a reject-reason option in the CONNEC-
- TACK message. If not, continue with step 3. If there is a
- reject-reason option, the server SHOULD report the error code.
- If a message option appears a server SHOULD display the string
- from the message option in a user visible way. The server
- MUST close the connection if a reject-reason option appears.
-
- 4. Check the value of the TLS-reply option (if any, which there
- won't be if this CONNECT is taking place utilizing TLS), and
- if it was 1, then skip processing of the rest of the CONNEC-
- TACK message, and immediately enter into TLS connection setup.
-
- This step occurs prior to steps 5 and 6 in order to allow
- creation of a secure connection (if required) prior to pro-
- cessing the protocol version and IP address information.
-
- 5. Examine the value of the protocol-version option. If this
- server is able to establish connections with another server
- running this protocol version, then continue, else close the
- connection.
-
- 6. Decide if the time delta between the sending of the message,
- in the time field, and the receipt of the message, recorded in
- step 1 above, is acceptable. A server MAY require an arbi-
- trarily small delta in time values in order to set up a fail-
- over connection with another server.
-
- If the delta between the time values is too great, the server
- should drop the TCP connection (see section 7.12).
-
- If the time mismatch is not considered too great then the
- receiving server MUST record the delta between the servers.
- The receiving server MUST use this delta to correct all of the
- absolute times received from the other server in all time-
- valued options. Note that the failover protocol is con-
- structed so that two servers can be failover partners with
- arbitrarily great time mismatches.
-
- 7. The receiving server MAY use the vendor-class-identifier to do
- vendor specific processing.
-
-
-
-Droms, et. al. Expires September 2003 [Page 77]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- 8. After accepting a CONNECTACK message, the server MUST send a
- STATE message.
-
- After receiving a CONNECTACK message, the server MUST start
- two timers for the connection: tSend and tReceive. The tSend
- timer SHOULD be approximately 20 percent of the time in the
- receiver-timer option in the corresponding CONNECTACK message.
- The tReceive timer SHOULD be set to the time sent in the
- receiver-timer option in the CONNECT message.
-
- The tReceive timer is reset whenever a message is received
- from this TCP connection. If it ever expires, the TCP connec-
- tion is dropped and communications with this partner is con-
- sidered not ok. The reject reason 17: "No traffic within suf-
- ficient time" is placed in the DISCONNECT message sent prior
- to dropping the TCP connection.
-
- The tSend timer is reset whenever a message is sent over this
- connection. When it expires, a CONTACT message MUST be sent.
-
-7.10. STATE message [10]
-
- The state (STATE) message is used to communicate the current failover
- state to the partner server.
-
- The STATE message MUST be sent after sending a CONNECTACK message
- that didn't contain a reject-reason option, and MUST be sent after
- receiving a CONNECTACK message without a reject-reason option.
-
- A STATE message MUST be sent whenever the failover endpoint changes
- its failover state and a connection exists to the partner.
-
- The STATE message requires no response from the failover partner.
-
- The following table shows the options that MUST appear in a STATE
- message:
-
-
- Option
- ------
- sending-state MUST
- server-flags MUST
- start-time-of-state MUST
-
- Table 7.10-1: Options used in a STATE message
-
-
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 78]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-7.10.1. Sending the STATE message
-
- The current failover state is placed in the server-state option and
- the current state of the STARTUP flag is placed in the server-flags
- option.
-
- The message is sent with a unique xid.
-
- A server SHOULD only send the STATE message either when the connec-
- tion is created (i.e, after sending or receiving a CONNECTACK message
- with no reject-reason option), or when there is a change from the
- values sent in a previous STATE message.
-
-7.10.2. Receiving the STATE message
-
- Every STATE message SHOULD indicate a change in state or a change in
- the flags.
-
- When a STATE message is received, any state transitions specified in
- section 9 are taken.
-
- No response to a STATE message is required.
-
-7.11. CONTACT message [11]
-
- The contact (CONTACT) message is sent to verify communications
- integrity with a failover partner. The CONTACT message is sent when
- no messages have been sent to the failover partner for a specified
- period of time. This is determined by the tSend timer expiring (see
- section 8.3).
-
- The CONTACT message has no message specific options.
-
-7.11.1. Sending the CONTACT message
-
- The CONTACT message is sent.
-
-7.11.2. Receiving the CONTACT message
-
- When a CONTACT message is received, the tReceive timer is reset (as
- it is with any message that is received).
-
- A server SHOULD use the time in the time field and the time the mes-
- sage was received to refine the delta time calculations between the
- servers.
-
-
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 79]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-7.12. DISCONNECT message [12]
-
- The DISCONNECT is the last message sent over a connection before
- dropping an established connection (note that an established connec-
- tion is one where a CONNECTACK has been sent without a reject rea-
- son).
-
- After sending or receiving a DISCONNECT message, a server needs to
- have some mechanism to prevent an error loop. Simply reconnecting to
- the partner immediately is not the best option, especially after
- several consecutive attempts.
-
- A simple suggested solution is to wait a minute or two after sending
- or receiving a DISCONNECT before attempting to reestablish communica-
- tion.
-
- The DISCONNECT message MUST be the last message sent down a connec-
- tion before it is closed.
-
- The following table summarizes the options that are associated with
- the DISCONNECT message:
-
-
- Option
- ------
- reject-reason MUST
- message SHOULD
-
- Table 7.12-1: Options used in a DISCONNECT message
-
-
-
-7.12.1. Sending the DISCONNECT message
-
- The DISCONNECT message MUST be the last message sent by the a server
- which is dropping a TCP connection.
-
- The xid of the DISCONNECT message must be unique.
-
- The reject-reason option MUST appear giving a reason why the connec-
- tion was dropped. A message option SHOULD appear giving a human
- readable error message with possibly more details.
-
-7.12.2. Receiving the DISCONNECT message
-
- When a server receives a DISCONNECT message it should log the message
- if there was one and possibly raise an alarm of some sort if the
- reject reason was one that was sufficiently serious.
-
-
-
-Droms, et. al. Expires September 2003 [Page 80]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-8. Connection Management
-
- Servers participating in the failover protocol communicate over TCP
- connections. These TCP connections are used both to transmit bind-
- ing information from one server to another as well as to allow each
- server to determine whether communications is possible with the other
- server.
-
- Central to the operation of the failover protocol is a notion of
- "communications okay" or "communications failed". Failover state
- transitions are taken in many cases when the status of communications
- with the partner changes, and the existence or non-existence of a TCP
- connections between failover endpoints is used to determine if com-
- munications is "okay" or "failed".
-
- A single TCP connection exists which connects two failover endpoints.
-
-8.1. Connection granularity
-
- There exists one TCP connection between each set of failover end-
- points. See section 5.1.1 for an explanation of failover endpoints.
-
- Typically there is one failover endpoint for each end of a failover
- relationship between two servers, and only a single relationship
- between any two servers. Given the integration of loadbalancing into
- the failover protocol, there is little value in having more than one
- failover relationship between two servers, though the protocol will
- support multiple relationships between two servers.
-
- Each failover relationship MUST have a unique relationship-name, and
- the relationship-name option is used to communicate this name in the
- CONNECT and CONNECTACK messages.
-
-8.2. Creating the TCP connection
-
- All failover TCP connections are initiated over port 647. Every
- server implementing the failover protocol MUST listen on port 647.
-
- Every server implementing the failover protocol SHOULD attempt to
- connect to all of its partners periodically, where the period is
- implementation dependent and SHOULD be configurable. In the event
- that a connection has been rejected by a CONNECTACK message with a
- reject-reason option contained in it or a DISCONNECT message, a
- server SHOULD reduce the frequency with which it attempts to connect
- to that server but it SHOULD continue to attempt to connect periodi-
- cally.
-
- When a connection attempt succeeds, if the server generating the
-
-
-
-Droms, et. al. Expires September 2003 [Page 81]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- connection attempt is a primary server for that relationship, then it
- MUST send a CONNECT message down the connection. If it is not a pri-
- mary server for the relationship, then it MUST just drop the connec-
- tion and wait for the primary server to connect to it.
-
- When a connection attempt is received on port 647, the only informa-
- tion that the receiving server has is the IP address of the partner
- initiating a connection. It also knows whether it has the primary
- role for any failover relationships with the connecting server. If
- it has any relationships for which it is a primary server, it should
- initiate a connection of its own to port 647 of the partner server,
- one for each primary relationship it has with that server.
-
- If it has any relationships with the connecting server for which it
- is a seconary server, it should just await the CONNECT message to
- determine which relationship this connection is to serve.
-
- If it has no secondary relationships with the connecting server, it
- SHOULD drop the connection.
-
- To summarize -- a primary server MUST use a connection that it has
- initiated in order to send a CONNECT message. Every server that is a
- secondary server in a relationship attempts to create a connection to
- the server which is primary in the relationship, but that connection
- is only used to stimulate the primary server into recognizing that
- the secondary server is ready for operation. The reason behind this
- is that the secondary server has no way to communicate to the primary
- server which relationship a connection is designed to serve.
-
- A server which has multiple secondary relationships with a primary
- server SHOULD only send one stimulus connection attempt to the pri-
- mary server.
-
- Once a connection is established, the primary server MUST send a CON-
- NECT message across the connection. A secondary server MUST wait for
- the CONNECT message from a primary server. If the secondary server
- doesn't receive a CONNECT message from the primary server in an ins-
- tallation dependent amount of time, it MAY drop the connection and
- send another stimulus connection attempt to the primary server.
-
- Every CONNECT message includes a TLS-request option, and if the CON-
- NECTACK message does not reject the CONNECT message and the TLS-reply
- option says TLS MUST be used, then the servers will immediately enter
- into TLS negotiation.
-
- Once TLS negotiation is complete, the primary server MUST resend the
- CONNECT message on the newly secured TLS connection and then wait for
- the CONNECTACK message in response. The TLS-request and TLS-reply
-
-
-
-Droms, et. al. Expires September 2003 [Page 82]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- options MUST NOT appear in either this second CONNECT or its associ-
- ated CONNECTACK message as they had in the first messages.
-
- The second message sent over a new connection (either a bare TCP con-
- nection or a connection utilizing TLS) is a STATE message. Upon the
- receipt of this message, the receiver can consider communications up.
-
- It is entirely possible that two servers will attempt to make connec-
- tions to each other essentially simultaneously, and in this case the
- secondary server will be waiting for a CONNECT message on each con-
- nection. The primary server MUST send a CONNECT message over one
- connection and it MUST close the other connection.
-
- A secondary server MUST NOT respond to the closing of a TCP connec-
- tion with a blind attempt to reconnect -- there may be another TCP
- connection to the same failover partner already in use.
-
-8.3. Using the TCP connection for determining communications status
-
- The TCP connection is used to determine the communications status of
- the other server, i.e., communications-ok, or communications-
- interrupted.
-
- Three things must happen for a server to consider that communications
- are ok with respect to another server:
-
-
- 1. A TCP connection must be established to the other server.
-
- 2. A CONNECT message must be received and a CONNECTACK message
- sent in response. The CONNECT message is used to determine
- the identify of the failover endpoint of the other end of the
- TCP connection -- without it, the failover endpoint cannot be
- uniquely determined. Without knowledge of the failover end-
- point, then the entity with which communications is ok is
- undetermined.
-
- 3. A STATE message must be received from the other server over
- the connection. This STATE message initializes important
- information necessary to the operation of the state machine
- the governs the behavior of this failover endpoint.
-
- There are two ways that a server can determine that communications
- has failed:
-
-
- 1. The TCP connection can go down, yielding an error when
- attempting to send or receive a message. This will happen at
-
-
-
-Droms, et. al. Expires September 2003 [Page 83]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- least as often as the period of the tSend timer.
-
- 2. The tReceive timer can expire.
-
- In either of these cases, communications is considered interrupted.
-
- If the tReceive timer expires, the connection MUST be dropped. The
- reject reason 17: "No traffic within sufficient time" is placed in
- the DISCONNECT message sent prior to dropping the TCP connection.
-
- Several difficulties arise when trying to use one TCP connection for
- both bulk data transfer as well as to sense the communications status
- of the other server. One aspect of the problem stems from the dif-
- ferent requirements of both uses. The bulk data transfer is of
- course critically important to the protocol, but the speed with which
- it is processed is not terribly significant. It might well be
- minutes before a BNDUPD message is processed, and while not optimal,
- such an occasional delay doesn't compromise the correctness of the
- protocol. However, the speed with which one server detects the other
- server is up (or, more importantly, down) is more highly constrained.
- Generally one server should be able to detect that the other server
- is not communicating within a minute or less.
-
- These differing time constraints makes it difficult to use the same
- TCP connection for data transfer as well as to sense communications
- integrity. See section 3.5 for additional details on TCP.
-
- The solution to this problem is to require that some message be
- received by each end of the connection within a limited time or that
- the connection will be considered down. If no messages have been
- sent recently, then a CONTACT message is sent.
-
- In the case where there is no data queued to be sent, this is not a
- problem, but in the case where there is data queued to be sent to the
- partner, then the CONTACT message will not actually be transmitted
- until the queued data is sent. Section 3.5 explains why waiting for
- TCP to determine that the connection is down is not acceptable, and
- leads to a requirement that the receiving server never block the
- sending server from sending CONTACT messages.
-
- In order to meet this requirement, each server tells the other server
- the number of outstanding BNDUPD messages that it will accept. The
- receiving server is required to always be able to accept that many
- BNDUPD messages off of the connection's input queue even if it cannot
- process them immediately, and to accept all other messages immedi-
- ately.
-
- Thus, the sending server's TCP is never blocked from sending a
-
-
-
-Droms, et. al. Expires September 2003 [Page 84]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- message except for very short periods, less than a few seconds unless
- the network connection itself has problems. In this case, if the
- CONTACT messages don't make it to the partner then the partner will
- close the connection.
-
- DISCUSSION:
-
- When implementing this capability, one needs to be careful when
- sending any message on the TCP connection as TCP can easily block
- the server if the local TCP send buffers are full. This can't be
- prevented because if the receiver is not reachable (via the net-
- work), the sending TCP can't send and thus it will be unable to
- empty the local TCP send buffers. So, all send operations either
- need to assume they may block for some time or non-blocking sends
- must be used carefully.
-
-8.4. Using the TCP connection for binding data
-
- Binding data, in the form of BNDUPD messages and BNDACK messages to
- respond to them, are sent across the TCP connection.
-
- In order to support timely detection of any failure in the partner
- server, the TCP connection MUST NOT block for more than a very short
- time, on the order of a few seconds. Therefore, a server that is
- sending BNDUPD messages MUST send only a restricted number before
- receiving BNDACK messages about previous messages sent.
-
- The number of outstanding BNDUPD messages that each server will
- accept without causing TCP to block transmission of additional data
- (i.e, CONTACT messages) is sent by each server in the CONNECT and
- CONNECTACK messages in the max-unacked-bndupd option.
-
-8.5. Using the TCP connection for control messages
-
- The TCP connection is used for control messages: POOLREQ, UPDREQ,
- STATE, CONTACT, UPDREQALL and the corresponding reply messages: POOL-
- RESP, UPDDONE. A server MUST immediately accept all of these mes-
- sages from the TCP connection. A server MUST immediately accept any
- BNDACK which is received as well.
-
-8.6. Losing the TCP connection
-
- When the TCP connection is lost, then communications is not ok with
- the other server. A server which has lost communications SHOULD
- immediately attempt to reconnect to the other server, and should
- retry these connection attempts periodically.
-
- An acknowledgement message (BNDACK, POOLRESP, UPDDONE) message can
-
-
-
-Droms, et. al. Expires September 2003 [Page 85]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- only be sent in response to a request message (BNDUPD, POOLREQ,
- UPDREQ, UPDREQALL) on the same TCP connection from which the request
- was received, in part since the XID's in the request messages are
- guaranteed unique only during the life of a single TCP connection.
-
- When a connection to a partner server goes down, a server with unpro-
- cessed request messages MAY simply drop all of those messages, since
- it can be sure that the partner will resend them when they are next
- in communications. A server with unprocessed BNDUPD messages when a
- TCP connection goes down MAY instead choose to process those BNDUPD
- messages, but it MUST NOT send any BNDACK messages in response (again
- because of the issues surrounding XID uniqueness).
-
- When the TCP connection is closed explicitly, the DISCONNECT message
- with a reject-reason option (and, ideally, a message option) MUST be
- sent over the TCP connection.
-
-9. Failover Endpoint States
-
- This section discusses the various states that a failover endpoint
- may take, and the server actions required when entering the state,
- operating in the state, and leaving the state, as well as the events
- that cause transitions out of the state into another state.
-
- The state transition diagram in Figure 9.2-1 is relevant for this
- section. This is the common state transition diagram for both servers
- in a failover pair. In the event that the textual description of a
- state differs from the state transition diagram, the textual descrip-
- tion is to be considered authoritative.
-
-9.1. Server Initialization
-
- When a server starts it starts out in STARTUP state. See section 9.3
- below for details.
-
-9.2. Server State Transitions
-
- Whenever a server makes a transition into a new state, it MUST record
- the state and the time at which it entered that state in stable
- storage. If communications is "ok", it MUST also send a STATE mes-
- sage to its failover partner.
-
- Figure 9.2-1 is the diagram of the server state transitions. The
- remainder of this section contains information important to the
- understanding of that diagram.
-
- The server stays in the current state until all of the actions speci-
- fied on the state transition are complete. If communications fails
-
-
-
-Droms, et. al. Expires September 2003 [Page 86]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- during one of the actions, the server simply stays in the current
- state and attempts a transition whenever the conditions for a transi-
- tion are later fulfilled.
-
- In the state transition diagram below, the "+" or "-" in the upper
- right corner of each state is a notation about whether communication
- is ongoing with the other server.
-
- The legend "responsive", "balanced", or "unresponsive" in each state
- indicates whether the server is responsive to all DHCP client
- requests, running in load balanced mode, or totally unresponsive in
- the respective state. The terms "responsive" and "unresponsive" have
- the obvious meanings, while "balanced" means that a DHCP server may
- respond to all DHCPREQUEST messages that are RENEWAL or REBINDING,
- and to all other messages from clients for which the load balancing
- algorithm indicates that it MUST respond to. See sections 5.3 and
- 9.8.2 for details on load balancing.
-
- Note that in situations where a server does not respond to a DHCP
- client message, it MUST NOT remember any of the information from that
- message.
-
- In the state transition diagram below, when communication is reesta-
- blished between the two servers, each must record the state of the
- partner when communication was restored. State transitions on one
- server in some cases imply state transitions on the partner server,
- so a record of the current state of the partner server must be kept
- by each server.
-
- If the state of the partner changes while communicating a server
- moves through the communications-failed transition and into whatever
- state results. It then immediately moves through whatever state
- transition is appropriate given the current state of the partner
- server. A server performing this operation SHOULD NOT close the TCP
- connection to its partner.
-
- DISCUSSION:
-
- The point of this technique is simplicity, both in explanation of
- the protocol and in its implementation. The alternative to this
- technique of memory of partner state and automatic state transi-
- tion on change of partner state is to have every state in the fol-
- lowing diagram have a state transition for every possible state of
- the partner. With the approach adopted, only the states in which
- communications are reestablished require a state transition for
- each possible partner state.
-
- The current state of a server MUST be recorded in stable storage and
-
-
-
-Droms, et. al. Expires September 2003 [Page 87]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- thus be available to the server after a server restart.
-
- A transition into SHUTDOWN or PAUSED state is not represented in the
- following figure, since other than sending that state to its partner,
- the remaining actions involved look just like the server halting in
- its otherwise current state, which then becomes the previous state
- upon server restart.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 88]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-
- +---------------+ V +--------------+
- | RECOVER -|+| | | STARTUP - |
- |(unresponsive) | +->+(unresponsive)|
- +------+--------+ +--------------+
- +-Comm. OK +-----------------+
- | Other State: | PARTNER DOWN - +<----------------------+
- | RESOLUTION-INTER. | (responsive) | ^
- All POTENTIAL- +----+------------+ |
- Others CONFLICT------------ | --------+ |
- | CONFLICT-DONE Comm. OK | +--------------+ |
- UPDREQ or Other State: | +--+ RESOLUTION - | |
- UPDREQALL | | | | | INTERRUPTED | |
- Rcv UPDDONE RECOVER All | | | (responsive) | |
- | +---------------+ | Others | | +------------+-+ |
- +->+RECOVER-WAIT +-| RECOVER | | | ^ | |
- |(unresponsive) | WAIT or | | Comm. | Ext. |
- +-----------+---+ DONE | | OK Comm. Cmd----->+
- Comm.---+ Wait MCLT | V V V Failed |
- Changed | V +---+ +---+-----+--+-+ | |
- | +---+----------++ | | POTENTIAL + +-------+ |
- | |RECOVER-DONE +-| Wait | CONFLICT +------+ |
- +->+(unresponsive) | for |(unresponsive)| Primary |
- +------+--------+ Other +>+----+--------++ resolve Comm. |
- Comm. OK State: | | ^ conflict Changed |
- +---Other State:-+ RECOVER | Secondary | V V | |
- | | | DONE | resolve | ++----------+---++ |
- | All Others: POTENT. | | conflict | |CONFLICT-DONE-|+| |
- | Wait for CONFLICT- | ----+ see (9.10) | | (responsive) | |
- | Other State: V V | +------+---------+ |
- | NORMAL or RECOVER ++------------+---+ Other State: NORMAL |
- | | DONE | NORMAL + +<--------------+ |
- | +--+----------+-->+ (balanced) +-------External Command--->+
- | ^ ^ +--------+--------+ or Other State: |
- | | | | | SHUTDOWN |
- | Wait for Comm. OK Comm. Failed or | |
- | Other Other Other State: PAUSED | External
- | State: State: | | Command
- | RECOVER-DONE NORMAL Start Safe Comm. OK or
- | | COMM. INT. Period Timer Other State: Safe
- | Comm. OK. | V All Others Period
- | Other State: | +---------+--------+ | expiration
- | RECOVER +--+ COMMUNICATIONS - +----+ |
- | +-------------+ INTERRUPTED | |
- RECOVER | (responsive) +-------------------------->+
- RECOVER-WAIT--------->+------------------+
- Figure 9.2-1: Server state diagram.
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 89]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-
-9.3. STARTUP state
-
- The STARTUP state affords an opportunity for a server to probe its
- partner server, before starting to service DHCP clients.
-
- DISCUSSION:
-
- Without the STARTUP state, a server would likely start in a state
- derived from its previously stored state (held in stable storage),
- if any. However, this may be inconsistent with the current state
- of the partner. The STARTUP state affords the opportunity for a
- server to potentially learn the partner's state and determine if
- that state is consistent with its derived starting state or
- whether some significant state change has occurred at the partner
- that forces the server to start in another state. This is
- especially critical if significant time has elapsed while the
- server was down.
-
-
-9.3.1. Operation while in STARTUP state
-
- Whenever a server is in STARTUP state, it MUST be unresponsive to
- DHCP client requests, and so the time spent in the STARTUP state is
- necessarily short, typically on the order of a few seconds to a few
- tens of seconds. The exact time spent in the STARTUP state is imple-
- mentation dependent, and the primary and secondary server are not
- required to spend the same amount of time in the STARTUP state. See
- section 5.9 for some guidelines on the time to spend in STARTUP
- state.
-
- Whenever a STATE message is sent to the partner while in STARTUP
- state the STARTUP bit MUST be set in the server-flags option and the
- previously recorded failover state MUST be placed in the server-state
- option.
-
-
-9.3.2. Transition out of STARTUP state
-
- Each server starts out in startup state every time it initializes
- itself, and performs the following algorithm as part of its initiali-
- zation:
-
- 1. Is there any record in stable storage of a previous failover
- state? If yes, set previous-state to the last recorded state
- in stable storage, and continue with step 2.
-
- Is there any configuration information that indicates that
-
-
-
-Droms, et. al. Expires September 2003 [Page 90]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- this server was previously running but lost its stable
- storage? Such information must typically come from some
- administrative intervention, since it is difficult for a
- server to distinguish first startup from a startup after it
- has lost its stable storage. If yes, then set the previous-
- state to RECOVER, and set the time-of-failure to whatever time
- was configured, and go on to step 2. This time-of-failure
- will be used in the transition out of the RECOVER-WAIT state
- into the RECOVER-DONE state, below.
-
- If there is no record of any previous failover state in stable
- storage for this server, then set the previous-state to
- RECOVER and set the time-of-failure to a time before the
- maximum-client-lead-time before now. If using standard Posix
- times, 0 would typically do quite well. This will allow two
- servers which already have lease information to synchronize
- themselves prior to operating.
-
- Note that neither server is responsive to DHCP client requests
- while in the RECOVER state. If both servers can communicate,
- however, they will come out of the RECOVER state and progress
- through RECOVER-WAIT to RECOVER-DONE and thence to NORMAL or
- COMMUNICATIONS-INTERRUPTED state quickly. If both have state,
- then they will exchange information. If only one has state,
- then the one that does not will complete its update of its
- partner quickly (since it has nothing to send).
-
- In some cases, an existing server will be commissioned as a
- failover server and brought back into operation where its
- partner is not yet available. In this case, the newly commis-
- sioned failover server will not operate until its partner
- comes online -- but it has operational responsibilities as a
- DHCP server nonetheless. To properly handle this situation, a
- server SHOULD be configurable in such a way as to move
- directly into PARTNER-DOWN state after the startup period
- expires if it has been unable to contact its partner during
- the startup period.
-
- 2. If the previous state is one where communications was "OK",
- then set the previous state to the state that is the result of
- the communications failed state transition in Figure 9.2-1 (if
- such transition is shown -- some states don't have a communi-
- cations failed state transition, since they allow both commun-
- ications OK and failed).
-
- 3. Start the STARTUP state timer. The time that a server remains
- in the STARTUP state (absent any communications with its
- partner) is implementation dependent and SHOULD be
-
-
-
-Droms, et. al. Expires September 2003 [Page 91]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- configurable. It SHOULD be long enough for a TCP connection
- to be created to a heavily loaded partner across a slow net-
- work.
-
- 4. Attempt to create a TCP connection to the failover partner.
- See section 8.2.
-
- 5. Wait for "communications okay", i.e., the process discussed in
- section 8.2 "Creating the TCP Connection", to complete,
- including the receipt of a STATE message from the partner.
-
- When and if communications become "okay", clear the STARTUP
- flag, and set the current state to the previous-state.
-
- If the partner is in PARTNER-DOWN state, and if the time at
- which it entered PARTNER-DOWN state (as received in the
- start-time-of-state option in the STATE message) is later than
- the last recorded time of operation of this server, then set
- the current state to RECOVER. If the time at which it entered
- PARTNER-DOWN state is earlier than the last recorded time of
- operation of this server, then set the current state to
- POTENTIAL-CONFLICT.
-
- Then, transition to the current state and take the "communica-
- tions okay" state transition based on the current state of
- this server and the partner.
-
- 6. If the startup time expires, take an implementation dependent
- action: The server MAY go to the previous-state, or the
- server MAY wait.
-
- Reasons to go to previous-state and begin processing:
-
- If the current server is the only operational server, then if
- it waits, there will be no operational DHCP servers. This
- situation could occur very easily where one server fails and
- then the other crashes and reboots. If the rebooting server
- doesn't start processing DHCP client requests without first
- being in communication with the other server, then the level
- of DHCP redundancy is not particularly high. This is an
- appropriate approach if the possibility of partition is low,
- or if the safe period expiration time is well beyond the time
- at which an operator would notice and react to a partition
- situation. It is also quite appropriate if the safe period
- will never expire.
-
- Reasons to wait:
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 92]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- If the current server has been down for longer than the
- maximum-client-lead-time, and it is partitioned from the other
- server, then when it returns it will attempt to use its own
- available addresses to allocate to new DHCP clients, and the
- other server may well be in PARTNER-DOWN state and may have
- already allocated some of those available addresses to DHCP
- clients. In cases where the possibility of partition is high,
- and the safe period expiration time is less than the likely
- operator reaction time, this is a good approach to use.
-
-9.4. PARTNER-DOWN state
-
- PARTNER-DOWN state is a state either server can enter. When in this
- state, the server does not assume that the other server could still
- be operating and servicing a different set of clients, but instead
- assumes that it is the only server operating. If one server is in
- PARTNER-DOWN state, the other server MUST NOT be operating.
-
-
-9.4.1. Upon entry to PARTNER-DOWN state
-
- No special actions are required when entering PARTNER-DOWN state.
-
- The server should continue to attempt to connect to the partner
- periodically.
-
-
-9.4.2. Operation while in PARTNER-DOWN state
-
- A server in PARTNER-DOWN state MUST respond to DHCP client requests.
- It will allow renewal of all outstanding leases on IP addresses, and
- will allocate IP addresses from its own pool, and after a fixed
- period of time (the MCLT interval) has elapsed from entry into
- PARTNER-DOWN state, it will allocate IP addresses from the set of all
- available IP addresses.
-
- Once a server has entered NORMAL state, the PARTNER-DOWN state is
- entered only on command of an external agency (typically an adminis-
- trator of some sort) or after the expiration of an externally config-
- ured minimum safe-time after the beginning of COMMUNICATIONS-
- INTERRUPTED state.
-
- Any IP address tagged as available for allocation by the other server
- (at entry to PARTNER-DOWN state) MUST NOT be allocated to a new
- client until the maximum-client-lead-time beyond the entry into
- PARTNER-DOWN state has elapsed.
-
- A server in PARTNER-DOWN state MUST NOT allocate an IP address to a
-
-
-
-Droms, et. al. Expires September 2003 [Page 93]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- DHCP client different from that to which it was allocated at the
- entrance to PARTNER-DOWN state until the maximum-client-lead-time
- beyond the maximum of the following times: client expiration time,
- most recently transmitted potential-expiration-time, most recently
- received ack of potential-expiration-time from the partner, and most
- recently acked potential-expiration-time to the partner. See section
- 7.1.5 for details. If this time would be earlier than the current
- time plus the maximum-client-lead-time, then the time the server
- entered PARTNER-DOWN state plus the maximum-client-lead-time is used.
-
- Two options exist for lease times given out while in PARTNER-DOWN
- state, with different ramifications flowing from each.
-
- If the server wishes the Failover protocol to protect it from loss of
- stable storage in PARTNER-DOWN state, then it should ensure that the
- MCLT based lease time restrictions in section 5.1 are maintained,
- even in PARTNER-DOWN state.
-
- If the server wishes to forego the protection of the Failover proto-
- col in the event of loss of stable storage, then it need recognize no
- restrictions on actual client lease times while in PARTNER-DOWN
- state.
-
- A server in PARTNER-DOWN state MUST continue to attempt to establish
- communications and synchronization with its partner.
-
-9.4.3. Transitions out of PARTNER-DOWN state
-
- When a server in PARTNER-DOWN state succeeds in establishing a con-
- nection to its partner, its actions are conditional on the state and
- flags received in the STATE message from the other server as part of
- the process of establishing the connection.
-
- If the STARTUP bit is set in the server-flags option of a received
- STATE message, a server in PARTNER-DOWN state MUST NOT take any state
- transitions based on reestablishing communications. Essentially, if a
- server is in PARTNER-DOWN state, it ignores all STATE messages from
- its partner that have the STARTUP bit set in the server-flags option
- of the STATE message.
-
- If the STARTUP bit is not set in the server-flags option of a STATE
- message received from its partner, then a server in PARTNER-DOWN
- state takes the following actions based on the value of the server-
- state option in the received STATE message (either immediately after
- establishing communications or at any time later when a new state is
- received):
-
- o partner in NORMAL, COMMUNICATIONS-INTERRUPTED, PARTNER-DOWN,
-
-
-
-Droms, et. al. Expires September 2003 [Page 94]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- POTENTIAL-CONFLICT, RESOLUTION-INTERRUPTED, or CONFLICT-DONE
- state
-
- transition to POTENTIAL-CONFLICT state
-
- o partner in RECOVER, RECOVER-WAIT, SHUTDOWN, PAUSED state
-
- stay in PARTNER-DOWN state
-
- o partner in RECOVER-DONE state
-
- transition into NORMAL state
-
-9.5. RECOVER state
-
- This state indicates that the server has no information in its stable
- storage or that it is re-integrating with a server in PARTNER-DOWN
- state after it has been down. A server in this state MUST attempt to
- refresh its stable storage from the other server.
-
-9.5.1. Operation in RECOVER state
-
- A server in RECOVER MUST NOT respond to DHCP client requests.
-
- A server in RECOVER state will attempt to reestablish communications
- with the other server.
-
-9.5.2. Transitions out of RECOVER state
-
- If the other server is in POTENTIAL-CONFLICT, RESOLUTION-INTERRUPTED,
- or CONFLICT-DONE state when communications are reestablished, then
- the server in RECOVER state will move to POTENTIAL-CONFLICT state
- itself.
-
- If the other server is in any other state, then the server in RECOVER
- state will request an update of missing binding information by send-
- ing an UPDREQ message. If the server has been instructed (through
- configuration or other external agency) that it has lost its stable
- storage, or if it has deduced that from the fact that it has no
- record of ever having talked to its partner, while its partner does
- have a record of communicating with it, it MUST send an UPDREQALL
- message, otherwise it MUST send an UPDREQ message. See Figure
- 9.5.2-1.
-
- It will wait for an UPDDONE message, and upon receipt of that message
- it will transition to RECOVER-WAIT state.
-
- If communications fails during the reception of the results of the
-
-
-
-Droms, et. al. Expires September 2003 [Page 95]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- UPDREQ or UPDREQALL message, the server will remain in RECOVER state,
- and will re-issue the UPDREQ or UPDREQALL when communications are
- re-established. (See section 5.17).
-
- If an UPDDONE message isn't received within an implementation depen-
- dent amount of time, and no BNDUPD messages are being received, the
- connection SHOULD be dropped.
-
-
-
-
- A B
- Server Server
-
- | |
- RECOVER PARTNER-DOWN
- | |
- | >--UPDREQ--------------------> |
- | |
- | <---------------------BNDUPD--< |
- | >--BNDACK--------------------> |
- ... ...
- | |
- | <---------------------BNDUPD--< |
- | >--BNDACK--------------------> |
- | |
- | <--------------------UPDDONE--< |
- | |
- RECOVER-WAIT |
- | |
- | >--STATE-(RECOVER-WAIT)------> |
- | |
- | |
- Wait MCLT from last known |
- time of failover operation |
- | |
- RECOVER-DONE |
- | |
- | >--STATE-(RECOVER-DONE)------> |
- | NORMAL
- | <-------------(NORMAL)-STATE--< |
- NORMAL |
- | >---- State-(NORMAL)--------------->
- | |
- | |
-
- Figure 9.5.2-1: Transition out of RECOVER state
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 96]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-
-If, at any time while a server is in RECOVER state communications fails,
-the server will stay in RECOVER state. When communications are
-restored, it will restart the process of transitioning out of RECOVER
-state.
-
-9.6. RECOVER-WAIT state
-
- This state indicates that the server has done an UPDREQ or UPDREQALL
- and has received the UPDDONE message indicating that it has received
- all outstanding binding update information. In the RECOVER-WAIT
- state the server will wait for the MCLT in order to ensure that any
- processing that this server might have done prior to losing its
- stable storage will not cause future difficulties.
-
-9.6.1. Operation in RECOVER-WAIT state
-
- A server in RECOVER-WAIT MUST NOT respond to DHCP client requests.
-
-9.6.2. Transitions out of RECOVER-WAIT state
-
- Upon entry to RECOVER-WAIT state the server MUST start a timer whose
- expiration is set to a time equal to the time the server went down
- (if known) or the time the server started (if the down-time is
- unknown) plus the maximum-client-lead-time. When this timer goes
- off, the server will transition into RECOVER-DONE state.
-
- This is to allow any IP addresses that were allocated by this server
- prior to loss of its client binding information in stable storage to
- contact the other server or to time out.
-
- If this is the first time this server has run failover -- as
- determined by the information received from the partner, not
- necessarily only as determined by this server's stable storage (as
- that may have been lost), then the waiting time discussed above may
- be skipped, and the server may transition immediately to RECOVER-DONE
- state.
-
- See Figure 9.5.2-1.
-
- DISCUSSION:
-
- The actual requirement on this wait period in RECOVER is that it
- start not before the recovering server went down, not necessarily
- when it came back up. If the time when the recovering server
- failed is known, it could be communicated to the recovering server
- (perhaps through actions of the network administrator), and the
- wait period could be reduced to the maximum-client-lead-time less
-
-
-
-Droms, et. al. Expires September 2003 [Page 97]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- the difference between the current time and the time the server
- failed. In this way, the waiting period could be minimized.
- Various heuristics could be used to estimate this time, for
- example if the recovering server periodically updates stable
- storage with a time stamp, the wait period could be calculated to
- start at the time of the last update of stable storage plus the
- time required for the next update (which never occurred). This
- estimate is later than the server went down, but probably not too
- much later.
-
- If the server has never before run failover, then there is no need
- to wait in this state -- but, again, to determine if this server
- has run failover it is vital that the information provided by the
- partner be utilized, since the stable storage of this server may
- have been lost.
-
- If communications fails while a server is in RECOVER-WAIT state, it
- has no effect on the operation of this state. The server SHOULD
- continue to operate its timer, and the timer goes off during the
- period where communications with the other server have failed, then
- the server SHOULD transition to RECOVER-DONE state. This is rare --
- failover state transitions are not usually made while communications
- are interrupted, but in this case there is no reason to inhibit the
- timer. A server MAY state in RECOVER-WAIT state even after expiry of
- the timer and transition to RECOVER-DONE state upon re-establishing
- communications with the partner if desired. The key point here is to
- allow the timer to continue to operate, not whether or not the state
- transition is made before or after communications are re-established.
-
-
-9.7. RECOVER-DONE state
-
- This state exists to allow an interlocked transition for one server
- from RECOVER state and another server from PARTNER-DOWN or
- COMMUNICATIONS-INTERRUPTED state into NORMAL state.
-
-9.7.1. Operation in RECOVER-DONE state
-
- A server in RECOVER-DONE state MUST respond only to
- DHCPREQUEST/RENEWAL and DHCPREQUEST/REBINDING DHCP messages.
-
-9.7.2. Transitions out of RECOVER-DONE state
-
- When a server in RECOVER-DONE state determines that its partner
- server has entered NORMAL or RECOVER-DONE state, then it will transi-
- tion into NORMAL state.
-
- If communications fails while in RECOVER-DONE state, a server will
-
-
-
-Droms, et. al. Expires September 2003 [Page 98]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- stay in RECOVER-DONE state.
-
-
- 9.8. NORMAL state
-
- NORMAL state is the state used by a server when it is communicating
- with the other server, and any required resynchronization has been
- performed. While some bindings database synchronization is performed
- in NORMAL state, potential conflicts are resolved prior to entry into
- NORMAL state as is binding database data loss.
-
-
-9.8.1. Upon entry to NORMAL state
-
- When entering NORMAL state, a server will send to the other server
- all currently unacknowledged binding updates as BNDUPD messages.
-
- When the above process is complete, if the server entering NORMAL
- state is a secondary server, then it will request IP addresses for
- allocation using the POOLREQ message.
-
-
-9.8.2. Processing DHCP client requests and load balancing
-
- In NORMAL state, a server MUST process every DHCPREQUEST/RENEWAL or
- DHCPREQUEST/REBINDING request it receives. And, it processes other
- requests only for those clients as dictated by the load balancing
- algorithm specified in [RFC 3074].
-
- As discussed in section 5.3, each server will take the client-
- identifier from each DHCP client request (or the client-hardware-
- address, i.e., the chaddr if no client-identifier is present in the
- request) and use it as the 'Request ID' specified in [RFC 3074].
- After applying the algorithm specified in [RFC 3074] and comparing
- the result with the hash bucket assignment (performed during connect
- processing between failover servers), each failover server will be
- able to unambiguously determine if it should process the DHCP client
- request.
-
-9.8.3. Operation in NORMAL state
-
- When in NORMAL state, for every DHCP client request that it
- processes, as determined by the algorithm described in section 9.8.2,
- above, a server will operate in the following manner:
-
- o Lease time calculations
-
- As discussed in section 5.2.1, "Control of lease time", the
-
-
-
-Droms, et. al. Expires September 2003 [Page 99]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- lease interval given to a DHCP client can never be more than the
- MCLT greater than the most recently received potential-
- expiration-time from the failover partner or the current time,
- whichever is later.
-
- As long as a server adheres to this constraint, the specifics of
- the lease interval that it gives to a DHCP client or the value
- of the potential-expiration-time sent to its failover partner
- are implementation dependent. One possible approach is dis-
- cussed in section 5.2.1, but that particular approach is in no
- way required by this protocol.
-
- See section 7.1.5 for details concerning the storage of time
- associated with IP addresses and how to use these times when
- calculating lease times for DHCP clients.
-
- o Lazy update of partner server
-
- After an DHCPACK of a IP address binding, the server servicing a
- DHCP client request attempts to update its partner with the new
- binding information. The lease time used in the update of the
- secondary MUST be at least that given to the DHCP client in the
- DHCPACK, and the potential-expiration-time MUST be at least the
- lease time, and SHOULD be considerably longer.
-
- o Reallocation of IP addresses between clients
-
- Whenever a client binding is released or expires, a BNDUPD mes-
- sage must be sent to the partner, setting the binding state to
- RELEASED or EXPIRED. However, until a BNDACK is received for
- this message, the IP address cannot be allocated to another
- client. It cannot be allocated to the same client again if a
- BNDUPD was sent, otherwise it can. See section 5.2.2.
-
- In normal state, each server receives binding updates from its
- partner server in BNDUPD messages. It records these in its client
- binding database in stable storage and then sends a corresponding
- BNDACK message to its partner server. It MUST ensure that the infor-
- mation is recorded in stable storage prior to sending the BNDACK mes-
- sage back to its partner.
-
-
-9.8.4. Transitions out of NORMAL state
-
- If an external command is received by a server in NORMAL state
- informing it that its partner is down, then transition into PARTNER-
- DOWN state. Generally, this would be an unusual situation, where
- some external agency knew the partner server was down. Using the
-
-
-
-Droms, et. al. Expires September 2003 [Page 100]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- command in this case would be appropriate if the polling interval and
- timeout were long.
-
- If a server in NORMAL state fails to receive acks to messages sent to
- its partner for an implementation dependent period of time, it MAY
- move into COMMUNICATIONS-INTERRUPTED state. This situation might
- occur if the partner server was capable of maintaining the TCP con-
- nection between the server and also capable of sending a CONTACT mes-
- sage every tSend seconds, but was (for some reason) incapable of pro-
- cessing BNDUPD messages.
-
- If the communications is determined to not be "ok" (as defined in
- section 8), then transition into COMMUNICATIONS-INTERRUPTED state.
-
- If a server in NORMAL state receives any messages from its partner
- where the partner has changed state from that expected by the server
- in NORMAL state, then the server should transition into
- COMMUNICATIONS-INTERRUPTED state and take the appropriate state tran-
- sition from there. For example, it would be expected for the partner
- to transition from POTENTIAL-CONFLICT into NORMAL state, but not for
- the partner to transition from NORMAL into POTENTIAL-CONFLICT state.
-
- If a server in NORMAL state receives any messages from its partner
- where the PARTNER has changed into PAUSED state, the server should
- transition into COMMUNICATIONS-INTERRUPTED state. If a server in
- NORMAL state receives any messages from its partner where the PARTNER
- has changed into SHUTDOWN state, the server should transition into
- PARTNER-DOWN state.
-
-9.9. COMMUNICATIONS-INTERRUPTED State
-
- A server goes into COMMUNICATIONS-INTERRUPTED state whenever it is
- unable to communicate with the other server. Primary and secondary
- servers cycle automatically (without administrative intervention)
- between NORMAL and COMMUNICATIONS-INTERRUPTED state as the network
- connection between them fails and recovers, or as the partner server
- cycles between operational and non-operational. No duplicate IP
- address allocation can occur while the servers cycle between these
- states.
-
-
-9.9.1. Upon entry to COMMUNICATIONS-INTERRUPTED state
-
- When a server enters COMMUNICATIONS-INTERRUPTED state, if it has been
- configured to support an automatic transition out of COMMUNICATIONS-
- INTERRUPTED state and into PARTNER-DOWN state (i.e., a "safe period"
- has been configured, see section 10), then a timer MUST be started
- for the length of the configured safe period.
-
-
-
-Droms, et. al. Expires September 2003 [Page 101]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- A server transitioning into the COMMUNICATIONS-INTERRUPTED state from
- the NORMAL state SHOULD raise some alarm condition to alert adminis-
- trative staff to a potential problem in the DHCP subsystem.
-
-
-9.9.2. Operation in COMMUNICATIONS-INTERRUPTED State
-
- In this state a server MUST respond to all DHCP client requests, and
- the algorithm for load balancing described in section 5.3 MUST NOT be
- used. When allocating new IP addresses, each server allocates from
- its own IP address pool, where the primary MUST allocate only FREE IP
- addresses, and the secondary MUST allocate only BACKUP IP addresses.
- When responding to renewal requests, each server will allow continued
- renewal of a DHCP client's current lease on an IP address irrespec-
- tive of whether that lease was given out by the receiving server or
- not, although the renewal period MUST NOT exceed the maximum client
- lead time (MCLT) beyond the latest of: 1) the potential-expiration-
- time already acknowledged by the other server, or 2) the lease-
- expiration-time, or 3) the potential-expiration-time received from
- the partner server.
-
- However, since the server cannot communicate with its partner in this
- state, the acknowledged-potential-expiration time will not be updated
- in any new bindings. This is likely to eventually cause the actual-
- client-lease-times to be the current time plus the maximum-client-
- lead-time (unless this is greater than the desired-client-lease-
- time).
-
- The server should continue to try to establish a connection with its
- partner.
-
-
-9.9.3. Transition out of COMMUNICATIONS-INTERRUPTED State
-
- If the safe period timer expires while a server is in the
- COMMUNICATIONS-INTERRUPTED state, it will transition immediately into
- PARTNER-DOWN state.
-
- If an external command is received by a server in COMMUNICATIONS-
- INTERRUPTED state informing it that its partner is down, it will
- transition immediately into PARTNER-DOWN state.
-
- If communications is restored with the other server, then the server
- in COMMUNICATIONS-INTERRUPTED state will transition into another
- state based on the state of the partner:
-
- o partner in NORMAL or COMMUNICATIONS-INTERRUPTED
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 102]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- The partner SHOULD NOT be in NORMAL state here, since upon res-
- toration of communications it MUST have created a new TCP con-
- nection which would have forced it into COMMUNICATIONS-
- INTERRUPTED state. Still, we should account for every state
- just in case.
-
- Transition into the NORMAL state.
-
- o partner in RECOVER
-
- Stay in COMMUNICATIONS-INTERRUPTED state.
-
- o partner in RECOVER-DONE
-
- Transition into NORMAL state.
-
- o partner in PARTNER-DOWN, POTENTIAL-CONFLICT, CONFLICT-DONE, or
- RESOLUTION-INTERRUPTED
-
- Transition into POTENTIAL-CONFLICT state.
-
- o partner in PAUSED
-
- Stay in COMMUNICATIONS-INTERRUPTED state.
-
- o partner in SHUTDOWN
-
- Transition into PARTNER-DOWN state.
-
- The following figure illustrates the transition from NORMAL to
- COMMUNICATIONS-INTERRUPTED state and then back to NORMAL state again.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 103]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-
- Primary Secondary
- Server Server
-
- NORMAL NORMAL
- | >--CONTACT-------------------> |
- | <--------------------CONTACT--< |
- | [TCP connection broken] |
- COMMUNICATIONS : COMMUNICATIONS
- INTERRUPTED : INTERRUPTED
- | [attempt new TCP connection] |
- | [connection succeeds] |
- | |
- | >--CONNECT-------------------> |
- | <-----------------CONNECTACK--< |
- | NORMAL
- | <-------------------STATE-----< |
- NORMAL |
- | >--STATE---------------------> |
- |
- | >--BNDUPD--------------------> |
- | <---------------------BNDACK--< |
- | |
- | <---------------------BNDUPD--< |
- | >------BNDACK----------------> |
- ... ...
- | |
- | <--------------------POOLREQ--< |
- | >--POOLRESP-(2)--------------> |
- | |
- | >--BNDUPD-(#1)---------------> |
- | <---------------------BNDACK--< |
- | |
- | <--------------------POOLREQ--< |
- | >--POOLRESP-(0)--------------> |
- | |
- | >--BNDUPD-(#2)---------------> |
- | <---------------------BNDACK--< |
- | |
-
- Figure 9.9.3-1: Transition from NORMAL to COMMUNICATIONS-
- INTERRUPTED and back (example with 2
- addresses allocated to secondary)
-
-
-
-
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 104]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-
-9.10. POTENTIAL-CONFLICT state
-
- This state indicates that the two servers are attempting to re-
- integrate with each other, but at least one of them was running in a
- state that did not guarantee automatic reintegration would be
- possible. In POTENTIAL-CONFLICT state the servers may determine that
- the same IP address has been offered and accepted by two different
- DHCP clients.
-
- It is a goal of this protocol to minimize the possibility that
- POTENTIAL-CONFLICT state is ever entered.
-
-9.10.1. Upon entry to POTENTIAL-CONFLICT state
-
- When a primary server enters POTENTIAL-CONFLICT state it should
- request that the secondary send it all updates of which it is
- currently unaware by sending an UPDREQ message to the secondary
- server.
-
- A secondary server entering POTENTIAL-CONFLICT state will wait for
- the primary to send it an UPDREQ message.
-
-9.10.2. Operation in POTENTIAL-CONFLICT state
-
- Any server in POTENTIAL-CONFLICT state MUST NOT process any incoming
- DHCP requests.
-
-
-9.10.3. Transitions out of POTENTIAL-CONFLICT state
-
- If communications fails with the partner while in POTENTIAL-CONFLICT
- state, then the server will transition to RESOLUTION-INTERRUPTED
- state.
-
- Whenever either server receives an UPDDONE message from its partner
- while in POTENTIAL-CONFLICT state, it MUST transition to a new state.
- The primary MUST transition to CONFLICT-DONE state, and the secondary
- MUST transition to NORMAL state. This will cause the primary server
- to leave POTENTIAL-CONFLICT state prior to the secondary, since the
- primary sends an UPDREQ message and receives an UPDDONE before the
- secondary sends an UPDREQ message and receives its UPDDONE message.
-
- When a secondary server receives an indication that the primary
- server has made a transition from POTENTIAL-CONFLICT to CONFLICT-DONE
- state, it SHOULD send an UPDREQ message to the primary server.
-
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 105]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-
-
- Primary Secondary
- Server Server
-
- | |
- POTENTIAL-CONFLICT POTENTIAL-CONFLICT
- | |
- | >--UPDREQ--------------------> |
- | |
- | <---------------------BNDUPD--< |
- | >--BNDACK--------------------> |
- ... ...
- | |
- | <---------------------BNDUPD--< |
- | >--BNDACK--------------------> |
- | |
- | <--------------------UPDDONE--< |
- CONFLICT-DONE |
- | >--STATE--(CONFLICT-DONE)----> |
- | <---------------------UPDREQ--< |
- | |
- | >--BNDUPD--------------------> |
- | <---------------------BNDACK--< |
- ... ...
- | >--BNDUPD--------------------> |
- | <---------------------BNDACK--< |
- | |
- | >--UPDDONE-------------------> |
- | NORMAL
- | <------------STATE--(NORMAL)--< |
- NORMAL |
- | >--STATE--(NORMAL)-----------> |
- | |
- | <--------------------POOLREQ--< |
- | >------POOLRESP-(n)----------> |
- | addresses |
-
- Figure 9.10.3-1: Transition out of POTENTIAL-CONFLICT
-
-
-
-
-
-
-
-
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 106]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-
-9.11. RESOLUTION-INTERRUPTED state
-
- This state indicates that the two servers were attempting to re-
- integrate with each other in POTENTIAL-CONFLICT state, but
- communications failed prior to completion of re-integration.
-
- If the servers remained in POTENTIAL-CONFLICT while communications
- was interrupted, neither server would be responsive to DHCP client
- requests, and if one server had crashed, then there might be no
- server able to process DHCP requests.
-
-9.11.1. Upon entry to RESOLUTION-INTERRUPTED state
-
- When a server enters RESOLUTION-INTERRUPTED state it SHOULD raise an
- alarm condition to alert administrative staff of a problem in the
- DHCP subsystem.
-
-9.11.2. Operation in RESOLUTION-INTERRUPTED state
-
- In this state a server MUST respond to all DHCP client requests, and
- any load balancing (described in section 5.3) MUST NOT be used. When
- allocating new IP addresses, each server SHOULD allocate from its own
- IP address pool (if that can be determined), where the primary SHOULD
- allocate only FREE IP addresses, and the secondary SHOULD allocate
- only BACKUP IP addresses. When responding to renewal requests, each
- server will allow continued renewal of a DHCP client's current lease
- on an IP address irrespective of whether that lease was given out by
- the receiving server or not, although the renewal period MUST not
- exceed the maximum client lead time (MCLT) beyond the latest of: 1)
- the potential-expiration-time already acknowledged by the other
- server or 2) the lease-expiration-time or 3) `potential-expiration-
- time received from the partner server.
-
- However, since the server cannot communicate with its partner in this
- state, the acknowledged-potential-expiration time will not be updated
- in any new bindings.
-
-
-9.11.3. Transitions out of RESOLUTION-INTERRUPTED state
-
- If an external command is received by a server in RESOLUTION-
- INTERRUPTED state informing it that its partner is down, it will
- transition immediately into PARTNER-DOWN state.
-
- If communications is restored with the other server, then the server
- in RESOLUTION-INTERRUPTED state will transition into POTENTIAL-
- CONFLICT state.
-
-
-
-Droms, et. al. Expires September 2003 [Page 107]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-
-9.12. CONFLICT-DONE state
-
- This state indicates that during the process where the two servers
- are attempting to re-integrate with each other, the primary server
- has received all of the updates from the secondary server. It make a
- transition into CONFLICT-DONE state in order that it may be totally
- responsive to the client load, as opposed to NORMAL state where it
- would be in a "balanced" responsive state, running the load balancing
- algorithm.
-
-9.12.1. Upon entry to CONFLICT-DONE state
-
- A secondary server should never enter CONFLICT-DONE state.
-
-9.12.2. Operation in CONFLICT-DONE state
-
- A primary server in CONFLICT-DONE state is fully responsive to all
- DHCP clients (similar to the situation in COMMUNICATIONS-INTERRUPTED
- state).
-
- If communications fails, remain in CONFLICT-DONE state. If communi-
- cations becomes OK, remain in CONFLICT-DONE state until the condi-
- tions for transition out become satisfied.
-
-
-9.12.3. Transitions out of CONFLICT-DONE state
-
- If communications fails with the partner while in CONFLICT-DONE
- state, then the server will remain in CONFLICT-DONE state.
-
- When a primary server determines that the secondary server has made a
- transition into NORMAL state, the primary server will also transition
- into NORMAL state.
-
-9.13. PAUSED state
-
- This state exists to allow one server to inform another that it will
- be out of service for what is predicted to be a relatively short
- time, and to allow the other server to transition to COMMUNICATIONS-
- INTERRUPTED state immediately and to begin servicing all DHCP clients
- with no interruption in service to new DHCP clients.
-
- A server which is aware that it is shutting down temporarily SHOULD
- send a STATE message with the server-state option containing PAUSED
- state and close the TCP connection.
-
- While a server may or may not transition internally into PAUSED
-
-
-
-Droms, et. al. Expires September 2003 [Page 108]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- state, the 'previous' state determined when it is restarted MUST be
- the state the server was in prior to receiving the command to shut-
- down and restart and which precedes its entry into the PAUSED state.
- See section 9.3.2 concerning the use of the previous state upon
- server restart.
-
-9.13.1. Upon entry to PAUSED state
-
- When entering PAUSED state, the server MUST store the previous state
- in stable storage, and use that state as the previous state when it
- is restarted.
-
-9.13.2. Transitions out of PAUSED state
-
- A server makes a transition out of PAUSED state by being restarted.
- At that time, the previous state MUST be the state the server was in
- prior to entering the PAUSED state.
-
-
-9.14. SHUTDOWN state
-
- This state exists to allow one server to inform another that it will
- be out of service for what is predicted to be a relatively long time,
- and to allow the other server to transition immediately to PARTNER-
- DOWN state, and take over completely for the server going down.
-
-9.14.1. Upon entry to SHUTDOWN state
-
- When entering SHUTDOWN state, the server MUST record the previous
- state in stable storage for use when the server is restarted. It
- also MUST record the current time as the last time operational.
-
- A server which is aware that it is shutting down SHOULD send a STATE
- message with the server-state field containing SHUTDOWN.
-
-9.14.2. Operation in SHUTDOWN state
-
- A server in SHUTDOWN state MUST NOT respond to any DHCP client input.
-
- If a server receives any message indicating that the partner has
- moved to PARTNER-DOWN state while it is in SHUTDOWN state then it
- MUST record RECOVER state as the previous state to be used when it is
- restarted.
-
- A server SHOULD wait for a few seconds after informing the partner of
- entry into SHUTDOWN state (if communications are okay) to determine
- if the partner entered PARTNER-DOWN state.
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 109]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-9.14.3. Transitions out of SHUTDOWN state
-
- A server makes a transition out of SHUTDOWN state by being restarted.
-
-10. Safe Period
-
- Due to the restrictions imposed on each server while in
- COMMUNICATIONS-INTERRUPTED state, long-term operation in this state
- is not feasible for either server. One reason that these states
- exist at all, is to allow the servers to easily survive transient
- network communications failures of a few minutes to a few days
- (although the actual time periods will depend a great deal on the
- DHCP activity of the network in terms of arrival and departure of
- DHCP clients on the network).
-
- Eventually, when the servers are unable to communicate, they will
- have to move into a state where they no longer can re-integrate
- without some possibility of a duplicate IP address allocation. There
- are two ways that they can move into this state (known as PARTNER-
- DOWN).
-
- They can either be informed by external command that, indeed, the
- partner server is down. In this case, there is no difficulty in mov-
- ing into the PARTNER-DOWN state since it is an accurate reflection of
- reality and the protocol has been designed to operate correctly (even
- during reintegration) as long as, when in PARTNER-DOWN state the
- partner is, indeed, down.
-
- The more difficult scenario is when the servers are running unat-
- tended for extended periods, and in this case an option is provided
- to configure something called a "safe-period" into each server. This
- OPTIONAL safe-period is the period after which either the primary or
- secondary server will automatically transition to PARTNER-DOWN from
- COMMUNICATIONS-INTERRUPTED state. If this transition is completed
- and the partner is not down, then the possibility of duplicate IP
- address allocations will exist.
-
- The goal of the "safe-period" is to allow network operations staff
- some time to react to a server moving into COMMUNICATIONS-INTERRUPTED
- state. During the safe-period the only requirement is that the net-
- work operations staff determine if both servers are still running --
- and if they are, to either fix the network communications failure
- between them, or to take one of the servers down before the expira-
- tion of the safe-period.
-
- The length of the safe-period is installation dependent, and depends
- in large part on the number of unallocated IP addresses within the
- subnet address pool and the expected frequency of arrival of
-
-
-
-Droms, et. al. Expires September 2003 [Page 110]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- previously unknown DHCP clients requiring IP addresses. Many
- environments should be able to support safe-periods of several days.
-
- During this safe period, either server will allow renewals from any
- existing client. The only limitation concerns the need for IP
- addresses for the DHCP server to hand out to new DHCP clients and the
- need to re-allocate IP addresses to different DHCP clients.
-
- The number of "extra" IP addresses required is equal to the expected
- total number of new DHCP clients encountered during the safe period.
- This is dependent only on the arrival rate of new DHCP clients, not
- the total number of outstanding leases on IP addresses.
-
- In the unlikely event that a relatively short safe period of an hour
- is all that can be used (given a dearth of IP addresses or a very
- high arrival rate of new DHCP clients), even that can provide sub-
- stantial benefits in allowing the DHCP subsystem to ride through
- minor problems that could occur and be fixed within that hour. In
- these cases, no possibility of duplicate IP address allocation
- exists, and re-integration after the failure is solved will be
- automatic and require no operator intervention.
-
-11. Security
-
- The Failover protocol communicates DHCP lease activity and this data
- is generally easily discovered via other means, such as by pinging
- addresses and doing DNS lookups. Therefore, the need to encrypt the
- data over the wire is likely not great (though some sites may feel
- differently).
-
- However, it is very desirable to assure the integrity of failover
- partners and to thus ensure proper operation of the servers. For
- example, denial of service attacks are possible by the communication
- of invalid state information to one or both servers.
-
- Therefore, the Failover protocol MUST be capable of being secured by
- using a simple shared secret message digest which covers each mes-
- sage. This provides authentication of the servers, but does not pro-
- vide encryption of the data exchange.
-
- The Failover protocol MAY also be secured by using TLS [RFC 2246]
- (Transport Layer Security) if encryption of the data exchange is
- desired. The use of the shared secret or TLS will not protect
- against TCP or IP layer attacks (such as someone sending fake TCP RST
- segments). IPsec [RFC 2401] SHOULD be used to protect against most
- (if not all) of these kinds of attacks.
-
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 111]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-11.1. Simple shared secret
-
- Messages between the failover partners can be authenticated through
- the use of a shared secret, which is never sent over the network and
- must be known by each server. How each server is told about this
- shared secret and secures its storage of the shared secret is outside
- the scope of this document. If a server is configured with a shared
- secret for a partner, it MUST send the message-digest option in ALL
- messages to that partner and it MUST treat any messages received from
- that partner without a message-digest option as failing authentica-
- tion and reject them with reject reason 21: "Missing message digest".
- Note that the message digest option MUST be the first option in the
- message.
-
- If a server is not configured with a shared secret for a partner, it
- MUST NOT send the message-digest option in any message to that
- partner and it MUST treat any messages received from that partner
- with a message-digest option as failing authentication with reject
- reason 13: "Message digest not configured".
-
- The shared secret is used to calculate a 16 octet message-digest
- which is sent in every failover message in the message-digest option.
- See section 12.16. The message-digest contains a one-way 16 octet
- HMAC-MD5 [RFC 2104] hash calculated over a stream of octets consist-
- ing of the entire message concatenated with the shared secret.
-
- For calculation, the message includes the message-digest option with
- the message-digest data zeroed (16-octets of zero). Once the calcula-
- tion is complete, these 16 octets of zero are replaced by the 16-
- octet HMAC-MD5 hash and the message is sent.
-
- For verification, the 16-octet message-digest is saved and replaced
- with 16-octets of zero and calculated per above. The resulting HMAC-
- MD5 hash is compared to the received hash and if they match, the mes-
- sage is assumed authenticated.
-
- A failover partner that fails to authenticate a received message or
- receives a message without a message-digest option when configured
- with a shared secret MUST close the connection immediately and take
- steps to notify operators.
-
- Every time a CONNECT message is received, the time at which that mes-
- sage was sent by the partner (i.e., the time that actually appears in
- the message itself) MUST be saved. If a CONNECT message is ever
- received containing that time or containing a time before that time,
- it MUST be rejected.
-
- The XID (see section 6.1) of every message received at a failover
-
-
-
-Droms, et. al. Expires September 2003 [Page 112]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- endpoint MUST be greater than that of the previous message received
- on that failover endpoint or the message just received MUST be
- rejected.
-
- A server MAY operate with arbitrary time skew between servers (see
- section 5.10), but when using a shared secret administrators MAY wish
- to configure a maximum allowable time skew between a failover server
- and its partner(s). Servers SHOULD allow an administrator to config-
- ure a maximum allowable time skew between two failover partners.
-
-11.2. TLS
-
- TLS, Transport Layer Security, as specified in [RFC 2246] MAY be
- used. The use of TLS would be similar to the way it is used with
- SMTP [RFC 2487] and IMAP/POP3/ACAP [RFC 2595].
-
- To request the use of TLS, the primary MUST send the TLS-request
- option as part of the CONNECT message. The secondary receiving the
- TLS-request option MUST respond with a TLS-reply option indicating
- its acceptance or rejection of the TLS-request in the CONNECT mes-
- sage."
-
- If the CONNECTACK message contained a TLS-reply of 1 , then both
- servers immediately begin TLS negotiation.
-
- Upon completion of this negotiation, the primary server sends another
- CONNECT message without any TLS-request option, and must wait for a
- corresponding CONNECTACK.
-
- Implementation of the TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA [RFC 2246]
- cipher suite is REQUIRED in Failover servers supporting TLS. This is
- important as it assures that any two compliant implementations can be
- configured to interoperate.
-
-12. Failover Options
-
- This section lists all of the options that are currently defined to
- be used with the failover protocol. See section 6.2 for details con-
- cerning time values.
-
-
-
-
-
-
-
-
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 113]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-
-12.1. addresses-transferred
-
- A 32 bit unsigned long in network byte order. Reports the number of
- addresses transferred by the primary to the secondary server
- (addresses to be used for the secondary server's private address
- pool).
-
- Code Len Number of Addresses
- +-----+-----+-----+-----+----+-----+-----+-----+
- | 0 | 1 | 0 | 4 | n1 | n2 | n3 | n4 |
- +-----+-----+-----+-----+----+-----+-----+-----+
-
-
-12.2. assigned-IP-address
-
- The DHCP managed IP address to which this message refers.
-
- Code Len Address
- +-----+-----+-----+-----+----+-----+-----+-----+
- | 0 | 2 | 0 | 4 | a1 | a2 | a3 | a4 |
- +-----+-----+-----+-----+----+-----+-----+-----+
-
-
-12.3. binding-status
-
- This option is used to convey the current state of a binding.
-
- Code Len Type
- +-----+-----+-----+-----+-----+
- | 0 | 3 | 0 | 1 | 1-7 |
- +-----+-----+-----+-----+-----+
-
- Legal values for this option are:
-
- Value Binding Status
- ----- ------------------------------------------------
- 1 FREE Lease is currently available to the primary
- 2 ACTIVE Lease is assigned to a client
- 3 EXPIRED Lease has expired
- 4 RELEASED Lease has been released by client
- 5 ABANDONED A server, or client flagged address as unusable
- 6 RESET Lease was freed by some external agent
- 7 BACKUP Lease belongs to secondary's private address pool
-
-
-
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 114]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-
-12.4. client-identifier
-
- This is the client-identifier for the client associated with a
- binding. The client-identifier data is subject to the same
- conventions as DHCP option 81 [RFC 2132].
-
- Code Len Client Identifier
- +-----+-----+-----+-----+----+-----+---
- | 0 | 4 | 0 | n | i1 | i2 | ...
- +-----+-----+-----+-----+----+-----+--
-
-
-12.5. client-hardware-address
-
- This is the hardware address for the client associated with a
- binding. Byte t1 (type) MUST be set to the proper ARP hardware
- address code, as defined in the ARP section of RFC 1700 (it MUST NOT
- be zero!)
-
- Code Len htype chaddr
- +-----+-----+-----+-----+----+-----+-----+---
- | 0 | 5 | 0 | n | t1 | c1 | c2 | ...
- +-----+-----+-----+-----+----+-----+-----+---
-
-
-12.6. client-last-transaction-time
-
- The time at which this server last received a DHCP request from a
- particular client expressed as an absolute time (see section 6.2).
-
-
- Code Len client last transaction time
- +-----+-----+-----+-----+----+-----+-----+-----+
- | 0 | 6 | 0 | 4 | t1 | t2 | t3 | t4 |
- +-----+-----+-----+-----+----+-----+-----+-----+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 115]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-
-12.7. client-reply-options
-
- This option contains options from a DHCP server's reply to a DHCP
- client request. It is sent in a BNDUPD message. The first 4 bytes
- of the option contain the "magic number" of the option area from
- which the DHCP reply options were taken and serves to define the
- format of the rest of the sub-options contained in this option.
- After the magic number, the options included are in the normal
- options format appropriate for that magic number.
-
- A server SHOULD NOT include all of the options in a DHCP server's
- reply to a client's request in this option, but rather a server
- SHOULD include only those options which are of likely interest to its
- partner server. See section 7.1 for details.
-
- Code Len Magic Number Embedded options
- +-----+-----+-----+-----+----+----+----+----+----+----+--
- | 0 | 7 | 0 | n | m1 | m2 | m3 | m4 | b1 | b2 | ...
- +-----+-----+-----+-----+----+----+----+----+----+----+--
-
-
-12.8. client-request-options
-
- This option contains options from a DHCP client's request. It is
- sent in a BNDUPD message. The first 4 bytes of the option contain
- the "magic number" of the option area from which the DHCP client's
- request options were taken and serves to define the format of the
- rest of the sub-options contained in this option. After the magic
- number, the options included are in the normal options format
- appropriate for that magic number.
-
- A server SHOULD NOT include all of the options in a DHCP client
- request in this option, but rather a server SHOULD include only those
- options which are of likely interest to its partner server. See
- section 7.1 for details.
-
- Code Len Magic Number Embedded options
- +-----+-----+-----+-----+----+----+----+----+----+----+--
- | 0 | 8 | 0 | n | m1 | m2 | m3 | m4 | b1 | b2 | ...
- +-----+-----+-----+-----+----+----+----+----+----+----+--
-
-
-
-
-
-
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 116]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-
-12.9. DDNS
-
- If an implementation supports Dynamic DNS updates, this option is
- used to communicate the status of the DDNS update associated with a
- particular lease binding. The Flags field conveys the types of DNS
- RRs that are to be updated by the DHCP server, and the status of the
- DDNS update. The Domain Name field conveys the DNS FQDN that the
- DHCP server is using to refer to the client, in DNS encoding as
- specified in [RFC 1035].
-
- Code Len Flags Domain Name
- +-----+-----+-----+-----+-----+------+------+-----+------
- | 0 | 9 | 0 | n | flags | d1 | d2 | ...
- +-----+-----+-----+-----+-----+------+------+-----+------
-
- The Flags field is a 16-bit field; several bit positions are
- specified here.
-
- 1 1 1 1 1 1
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- |C|A|D|P| MBZ |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
- The bits (numbered from the least-significant bit in network
- byte-order) are used as follows:
-
- 0 (C): name to address (such as A RR) update successfully completed
- 1 (A): Server is controlling A RR on behalf of the client
- 2 (D): address to name (such as PTR RR) update successfully completed (Done)
- 3 (P): Server is controlling PTR RR on behalf of the client
- 4-15 : Must be zero
-
- All of the unspecified bit positions SHOULD be set to 0 by servers
- sending the Failover-DDNS option, and they MUST be ignored by servers
- receiving the option.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 117]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-
-12.10. delayed-service-parameter
-
- The delayed-service-parameter is an optional load balancing tuning
- parameter, defined in [RFC 3074]. If it is used, it MUST be sent in
- the same message as the hash-bucket-assignment option (see section
- 12.11).
-
- Format :
-
-
- Code Len Seconds
- +-----+-----+-----+-----+----+
- | 0 | 10 | 0 | 1 | S |
- +-----+-----+-----+-----+----+
-
- S is a one byte value, 1..255.
-
-
-12.11. hash-bucket-assignment
-
- A set of load balancing hash values for the secondary server. A one
- bit in the hash buckets indicates that the secondary is to service
- that set of clients. See section 5.3 for more information on how
- this option is used. This option is only sent from the primary to
- the secondary.
-
- The format and usage of the data in this option is defined in [RFC
- 3074].
-
- Code Len Hash Buckets
- +-----+-----+-----+-----+-----+-----+-----+-----+
- | 0 | 11 | 0 | 32 | b1 | b2 | ... | b32 |
- +-----+-----+-----+-----+-----+-----+-----+-----+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 118]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-
-12.12. IP-flags
-
- This option is used to convey the current flags of the assigned-IP-
- address option preceding it.
-
- Code Len IP Flags
- +-----+-----+-----+-----+-----+-----+
- | 0 | 12 | 0 | 1 | f1 | f2 |
- +-----+-----+-----+-----+-----+-----+
-
- The IP-flags field is a 16-bit field; two bit positions are
- specified here.
-
- 1 1 1 1 1 1
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- |R|B| MBZ |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
- The bits (numbered from the least-significant bit in network
- byte-order) are used as follows:
-
- 0 (R): RESERVED (this bit allocated and in use and named "RESERVED")
- Bit 0 MUST be set to 1 whenever the IP address in the preceding
- assigned-IP-address option is reserved on the server sending the
- packet.
- 1 (B): BOOTP
- Bit 1 MUST be set to 1 whenever the IP address in the preceding
- assigned-IP-address option is a an IP address which has been
- allocated due to an interaction with a BOOTP client (as opposed
- to a DHCP client).
- 2-15 : Must be zero
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 119]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-
-12.13. lease-expiration-time
-
- The lease expiration time is the lease interval that a DHCP server
- has ACKed to a DHCP client added to the time at which that ACK was
- transmitted -- expressed as an absolute time (see section 6.2).
-
-
- Code Len Time
- +-----+-----+-----+-----+----+-----+-----+-----+
- | 0 | 13 | 0 | 4 | t1 | t2 | t3 | t4 |
- +-----+-----+-----+-----+----+-----+-----+-----+
-
-
-12.14. max-unacked-bndupd
-
- The maximum number of BNDUPD message that this server is prepared to
- accept over the TCP connection without causing the TCP connection to
- block. A 32 bit unsigned integer value, in network byte order.
-
-
- Code Len Maximum Unacked BNDUPD
- +-----+-----+-----+-----+----+-----+-----+-----+
- | 0 | 14 | 0 | 4 | n1 | n2 | n3 | n4 |
- +-----+-----+-----+-----+----+-----+-----+-----+
-
-
-12.15. MCLT
-
- Maximum Client Lead Time, an interval, in seconds. A 32 bit unsigned
- integer value, in network byte order.
-
- Code Len Time
- +-----+-----+-----+-----+----+-----+-----+-----+
- | 0 | 15 | 0 | 4 | t1 | t2 | t3 | t4 |
- +-----+-----+-----+-----+----+-----+-----+-----+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 120]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-
-12.16. message
-
- This option is used to supply a human readable message text. It may
- be used in association with the Reject Reason Code to provide a human
- readable error message for the reject.
-
-
- Code Len Text
- +-----+-----+-----+-----+------+-----+--
- | 0 | 16 | 0 | n | c1 | c2 | ...
- +-----+-----+-----+-----+------+-----+--
-
-
-12.17. message-digest
-
- The message digest for this message.
-
- This option consists of a variable number of bytes which contain the
- message digest of the message prior to the inclusion of this option.
-
- When this option appears in a message, it MUST appear as the first
- option in the message. It MUST appear in every message if message
- digests are required. The Type MUST be configurable (once additional
- types are defined). When additional types are defined, they MUST be
- specified as either optional (MAY be supported) or required (MUST be
- supported). See the section on IANA considerations for more details.
-
- Code Len Type Message Digest
- +-----+-----+-----+-----+-----+-----+-----+--
- | 0 | 17 | 0 | n | t | d1 | d2 | ...
- +-----+-----+-----+-----+-----+-----+-----+--
-
-
- Type: 0 Not Allowed
- 1 HMAC-MD5
- 2-255 Not Allowed
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 121]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-
-12.18. potential-expiration-time
-
- The potential expiration time is the time that one server tells
- another server that it may wish to grant in a lease to a DHCP client.
- It is an absolute time. See section 6.2.
-
-
- Code Len Time
- +-----+-----+-----+-----+----+-----+-----+-----+
- | 0 | 18 | 0 | 4 | t1 | t2 | t3 | t4 |
- +-----+-----+-----+-----+----+-----+-----+-----+
-
-
-12.19. receive-timer
-
- The number of seconds (an interval) within which the server must
- receive a message from its partner, or it will assume that
- communications with the partner is not ok. An unsigned 32 bit
- integer in network byte order.
-
- Code Len Receive Timer
- +-----+-----+-----+-----+----+-----+-----+-----+
- | 0 | 19 | 0 | 4 | s1 | s2 | s3 | s4 |
- +-----+-----+-----+-----+----+-----+-----+-----+
-
-
-12.20. protocol-version
-
- The protocol version being used by the server. It is only sent in the
- CONNECT and CONNECTACK messages. The current value for the version
- is 1.
-
- Code Len Version
- +-----+-----+-----+-----+-----+
- | 0 | 20 | 0 | 1 | 1 |
- +-----+-----+-----+-----+-----+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 122]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-
-12.21. reject-reason
-
- This option is used to selectively reject binding updates. It MAY be
- used in a BNDACK message or a CONNECTACK message, always associated
- with an assigned-IP-address option, which contains the IP address of
- the update being rejected.
-
- Code Len Reason Code
- +-----+-----+-----+-----+-----+
- | 0 | 21 | 0 | 1 | R1 |
- +-----+-----+-----+-----+-----+
-
- Reason codes (section where referenced in parentheses):
-
- 0 Reserved
- 1 Illegal IP address (not part of any address pool). (7.1.3)
- 2 Fatal conflict exists: address in use by other client. (7.1.3)
- 3 Missing binding information. (7.1.3)
- 4 Connection rejected, time mismatch too great. (7.8.2)
- 5 Connection rejected, invalid MCLT. (7.8.2)
- 6 Connection rejected, unknown reason. (not specifically referenced)
- 7 Connection rejected, duplicate connection. (unused)
- 8 Connection rejected, invalid failover partner. (7.8.2)
- 9 TLS not supported. (7.8.2)
- 10 TLS supported but not configured. (7.8.2)
- 11 TLS required but not supported by partner. (7.8.2)
- 12 Message digest not supported. (11.1)
- 13 Message digest not configured. (11.1)
- 14 Protocol version mismatch. (7.8.2)
- 15 Outdated binding information. (7.1.3)
- 16 Less critical binding information. (7.1.3)
- 17 No traffic within sufficient time. (8.6)
- 18 Hash bucket assignment conflict. (7.8.2)
- 19 IP not reserved on this server. (7.1.3)
- 20 Message digest failed to compare. (7.8.2)
- 21 Missing message digest. (7.1.3)
- 22-253, reserved.
- 254 Unknown: Error occurred but does not match any reason code.
- 255 Reserved for code expansion.
-
-
-
-
-
-
-
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 123]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-
-12.22. relationship-name
-
- A string which is a unique identifier for the failover relationship.
-
- Code Len Relationship Name
- +-----+-----+-----+-----+----+-----+---
- | 0 | 22 | 0 | n | c1 | c2 | ...
- +-----+-----+-----+-----+----+-----+---
-
-
-12.23. server-flags
-
- This option is used to convey the current flags of the failover
- endpoint in the sending server.
-
- Code Len Server Flags
- +-----+-----+-----+-----+-------+
- | 0 | 23 | 0 | 1 | flags |
- +-----+-----+-----+-----+-------+
-
- The flags field is an 8-bit field; one bit position is
- specified here.
-
-
- 0 1 2 3 4 5 6 7
- +-+-+-+-+-+-+-+-+
- |S| MBZ |
- +-+-+-+-+-+-+-+-+
-
- The bits (numbered from the least-significant bit in network
- byte-order) are used as follows:
-
- 0 (S): STARTUP,
- Bit 0 MUST be set to 1 whenever the server is in STARTUP state,
- and set to 0 otherwise. (Note that when in STARTUP state, the
- state transmitted in the server-state option is usually the last
- recorded state from stable storage, but see section 9.3 for
- details.)
- 1-7 : Must be zero
-
-
-
-
-
-
-
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 124]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-
-12.24. server-state
-
- This option is used to convey the current state of the failover
- endpoint in the sending server.
-
- Code Len Server State
- +-----+-----+-----+-----+-----+
- | 0 | 24 | 0 | 1 | 1-9 |
- +-----+-----+-----+-----+-----+
-
- Legal values for this option are:
-
- Value Server State
- ----- -------------------------------------------------------------
- 0 reserved
- 1 STARTUP Startup state (1)
- 2 NORMAL Normal state
- 3 COMMUNICATIONS-INTERRUPTED Communication interrupted (safe)
- 4 PARTNER-DOWN Partner down (unsafe mode)
- 5 POTENTIAL-CONFLICT Synchronizing
- 6 RECOVER Recovering bindings from partner
- 7 PAUSED Shutting down for a short period.
- 8 SHUTDOWN Shutting down for an extended
- period.
- 9 RECOVER-DONE Interlock state prior to NORMAL
- 10 RESOLUTION-INTERRUPTED Comm. failed during resolution
- 11 CONFLICT-DONE Primary has resolved its conflicts
-
- (1) The STARTUP state is never sent to the partner server, it is
- indicated by the STARTUP bit in the server-flags options (see section
- 12.22).
-
-
-12.25. start-time-of-state
-
- This option is used for different states in different messages. In a
- BNDUPD message it represents the start time of the state of the lease
- in the BNDUPD message. In a STATE message, it represents the start
- time of the partner server's failover state. In all cases it is an
- absolute time.
-
-
- Code Len Start Time of State
- +-----+-----+-----+-----+----+-----+-----+-----+
- | 0 | 25 | 0 | 4 | t1 | t2 | t3 | t4 |
- +-----+-----+-----+-----+----+-----+-----+-----+
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 125]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-
-12.26. TLS-reply
-
- This option contains information relating to TLS security
- negotiation. It is sent in a CONNECTACK message
-
- A t1 value of 0 indicates no TLS operation, a value of 1 indicates
- that TLS operation is required.
-
- Code Len TLS
- +-----+-----+-----+-----+-----+
- | 0 | 26 | 0 | 1 | t1 |
- +-----+-----+-----+-----+-----+
-
-
-12.27. TLS-request
-
- This option contains information relating to TLS security
- negotiation. It is sent in a CONNECT message.
-
- The t1 byte is the TLS request from the primary server. A value of 0
- indicates no TLS operation (to communicate the secondary server MUST
- NOT require TLS), a value of 1 indicates that TLS operation is
- desired but not required (to communicate, the secondary server MAY
- utilize TLS), and a value of 2 indicates that TLS operation is
- required (to communicate the secondary server MUST utilize TLS) to
- establish communications with the primary server.
-
- Code Len TLS
- +-----+-----+-----+-----+-----+
- | 0 | 27 | 0 | 1 | t1 |
- +-----+-----+-----+-----+-----+
-
-
-12.28. vendor-class-identifier
-
- A string which identifies the vendor of the failover protocol
- implementation.
-
- Code Len vendor class string
- +-----+-----+-----+-----+----+-----+---
- | 0 | 28 | 0 | n | c1 | c2 | ...
- +-----+-----+-----+-----+----+-----+---
-
-
-
-
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 126]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-
-12.29. vendor-specific-options
-
- This option is used to convey options specific to a particular
- vendor's implementation. The vendor class identifier is used to
- specify which option space the embedded options are drawn from.
- Every message that uses vendor specific options MUST have a vendor-
- class-identifier option in it.
-
- It functions similarly to the vendor class identifier and vendor
- specific options in the DHCP protocol.
-
- This option contains other options in the same two byte code, two
- byte length format. If this option appears in a message without a
- corresponding vendor class identifier, it MUST be ignored.
-
- Code Len Embedded options
- +-----+-----+-----+-----+----+-----+---
- | 0 | 29 | 0 | n | c1 | c2 | ...
- +-----+-----+-----+-----+----+-----+---
-
-
-
-
-13. IANA Considerations
-
- This document defines several number spaces (failover options, fail-
- over message types, message digest types, and failover reject reason
- codes). For all of these number spaces, certain values are defined in
- this specification. New values may only be defined by IETF Con-
- sensus, as described in [RFC 2434]. Basically, this means that they
- are defined by RFCs approved by the IESG.
-
-
-14. Acknowledgments
-
- Ralph Droms started it all, by sketching out an initial interserver
- draft that embodied ideas from several past IETF meetings. In that
- draft, he acknowledged contributions by Jeff Mogul, Greg Minshall,
- Rob Stevens, Walt Wimer, Ted Lemon, and the DHC working group.
-
- Kim Kinnear and Bob Cole each extended that draft, separately and
- then together, until they created an interserver draft that supported
- any number of servers. The complexity of that approach was just too
- great, and that draft wasn't greeted with enthusiasm by many, includ-
- ing its authors.
-
- It did however lead to a much simpler approach embodied in the first
-
-
-
-Droms, et. al. Expires September 2003 [Page 127]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- Failover draft by Greg Rabil, Mike Dooley, Arun Kapur and Ralph
- Droms. This draft posited only two servers -- a primary and a secon-
- dary.
-
- Kim Kinnear then wrote the Safe Failover draft to layer on top of the
- Failover Draft and increase its robustness in the face of certain
- rare network failures.
-
- At the spring 1998 IETF meeting in LA, the DHC working group said
- that they wanted a merged Failover and Safe Failover draft. Steve
- Gonczi and Bernie Volz stepped up and produced the raw material for
- such a merged draft, along with a new message format designed around
- DHCP options and other extensions and clarifications. Kim Kinnear
- edited their work into draft format and made other changes in time
- for the Summer Chicago IETF meeting.
-
- Many people have reviewed the various earlier drafts that went into
- this result. At American Internet, ideas were contributed by Brad
- Parker. At Cisco Systems Paul Fox and Ellen Garvey contributed to
- the design of the protocol.
-
- During the summer and fall of 1998, two groups worked on separate
- implementations of the UDP failover draft. Bernie Volz and Steve
- Gonczi constituted one group, and Kim Kinnear, Mark Stapp and Paul
- Fox made up the other. These two groups worked together to produce
- considerable changes and simplifications of the protocol during that
- period, and Steve Gonczi and Kim Kinnear edited those changes into
- -03 draft in time for submission to the December 1998 Orlando IETF
- meeting.
-
- In February of 1999 Kim Kinnear and Mark Stapp hosted a meeting of
- people interested in the failover draft. During that meeting a gen-
- eral agreement was reached to recast the failover protocol to use TCP
- instead of UDP. In addition, the group together brainstormed a work-
- able load-balancing technique. Kim Kinnear rewrote the entire draft
- to include the changes made at that meeting as well as to restructure
- the draft along guidelines suggested by Thomas Narten. The result
- was the -04 draft, submitted prior to the Oslo IETF meeting.
-
- The initial idea for a hash-based load balancing approach was offered
- by Ted Lemon, and the determination of an algorithm and its integra-
- tion into the draft was done by Steve Gonczi. The security section
- was spearheaded by Bernie Volz. Both contributed considerably to the
- ideas and text in the rest of the draft with several reviews.
-
- In early October of 1999, three conference calls were held to discuss
- the -04 draft. The -05 includes changes as a result of those calls,
- perhaps the largest of which was to remove the load balancing
-
-
-
-Droms, et. al. Expires September 2003 [Page 128]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- approach into a separate draft. Thanks to all of the many people
- who participated in the conference calls. Changes were made because
- of contributions by: Ted Lemon, David Erdmann, Richard Jones, Rob
- Stevens, Thomas Narten, Diana Lane, and Andre Kostur.
-
- Another conference call was held in mid-January of 2000, and the -06
- draft was produced to tighten up the the -05 draft both technically
- as well as editorially.
-
- The -07 draft was edited by Kim Kinnear and was based in part on
- reviews by Richard Jones, Bernie Volz, and Steve Gonczi. It embodies
- several technical updates as well as numerous editorial revisions
- that enhanced both correctness as well as clarity.
-
- The -08 draft was edited by Kim Kinnear and was based on the results
- of two conference calls held in October and November of 2000. It
- includes the correct second port number, a new state to synchronize
- conflict resolution with load balancing, a generally accepted
- approach to secondary pool allocation, and many other updates based
- on both operational as well as implementation experience.
-
- The -09 draft was edited by Kim Kinnear based on discussions held at
- the Minneapolis IETF in December of 2000, as well as issues raised by
- Ted Lemon based on implementation and deployment. The specific
- changes were mailed to the dhcp-v4 list.
-
- The -10 draft differed from the -09 draft in that figure 9.8.3-1 was
- correctly relabeled figure 9.10.3-1, and it was updated to include
- the CONFLICT-DONE message. One of the authors affiliations was also
- updated.
-
- This, the -11 draft differs only slightly from the -10 draft in
- correcting another author affiliation.
-
- These most recent changes have not been widely circulated among the
- other authors prior to submission to the IETF.
-
- Glenn Waters of Nortel Networks contributed ideas and enthusiasm to
- make a Failover protocol that was both "safe" and "lazy".
-
-
-15. References
-
-
- [DHCID] Stapp, M., Lemon, T., Gustafsson, A., "draft-ietf-dnsext-
- dhcid-rr-02.txt", March, 2001.
-
- [DNSRES] Stapp, M., "draft-ietf-dhc-dns-resolution-01.txt", March,
-
-
-
-Droms, et. al. Expires September 2003 [Page 129]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- 2001.
-
- [FQDN] Rekhter, Y., Stapp, M., "draft-ietf-dhc-fqdn-option-01.txt",
- March, 2001.
-
- [RFC 1035] Mockapetris, P., "Domain Names - Implementation and
- Specification", November, 1987.
-
- [RFC 1534] Droms, R., "Interoperation between DHCP and BOOTP", RFC
- 1534, October 1993.
-
- [RFC 2104] Krawczyk, H., Bellare, M., and Canetti, R., "HMAC: Keyed
- Hashing for Message Authentication", RFC 2104, IBM T.J. Watson
- Research Center, University of California at San Diego, February
- 1997.
-
- [RFC 2119] Bradner, S. "Key words for use in RFCs to Indicate
- Requirement Levels", RFC 2119.
-
- [RFC 2131] Droms, R., "Dynamic Host Configuration Protocol", RFC
- 2131, March 1997.
-
- [RFC 2132] Alexander, S., Droms, R., "DHCP Options and BOOTP Vendor
- Extensions", Internet RFC 2132, March 1997.
-
- [RFC 2136] P. Vixie, S. Thomson, Y. Rekhter, J. Bound, "Dynamic
- Updates in the Domain Name System (DNS UPDATE)", RFC 2136, April
- 1997
-
- [RFC 2139] Rigney, C., "Radius Accounting", RFC 2139, Livingston
- Enterprises, April 1997.
-
- [RFC 2246] Dierks, T., "The TLS Protocol, Version 1.0", RFC 2246,
- January 1999.
-
- [RFC 2401] Kent, S., Atkinson, R., "Security Architecture for the
- Internet Protocol", RFC 2401, November 1998.
-
- [RFC 2434] Alvestrand, H. and T. Narten, "Guidelines for Writing an
- IANA Considerations Section in RFCs", BCP 26, RFC 2434, October
- 1998.
-
- [RFC 2487] Hoffman, P., "SMTP Service Extension for Secure SMTP over
- TLS", RFC 2487, January 1999.
-
- [RFC 2595] Newman, C., "Using TLS with IMAP, POP3, and ACAP", RFC
- 2595, June 1999.
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 130]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- [RFC 3004] Stump, G., Droms, R., Gu, Y., Vyaghrapuri, R., Demirtjis,
- A., Privat, J. "The User Class Option for DHCP", November 2000.
-
- [RFC 3011] Waters, G., "The IPv4 Subnet Selection Option for DHCP",
- November 2000.
-
- [RFC 3046] Patrick, M., "DHCP Relay Agent Information Option", RFC
- 3046, January 2001.
-
- [RFC 3074] Volz, B., Gonczi, S., Lemon, T., Stevens, R., "DHC Load-
- balancing Algorithm", February, 2001.
-
-16. Author's information
-
- Ralph Droms
- Kim Kinnear
- Mark Stapp
- Cisco Systems
- 250 Apollo Drive
- Chelmsford, MA 01824
-
- Phone: (978) 497-0000
-
- EMail: rdroms@cisco.com
- kkinnear@cisco.com
- mjs@cisco.com
-
-
-
- Bernie Volz
- Ericsson
- 959 Concord St.
- Framingham, MA 01701
-
- Phone: (508) 875-3162
-
- EMail: bernie.volz@ericsson.com
-
-
- Steve Gonczi
- Relicore, Inc.
- One Wall Street
- Burlington, MA 01803
-
- Phone: (781) 229-1122
-
- Email: steve@relicore.com
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 131]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
- Greg Rabil
- Lucent Technologies
- 400 Lapp Road
- Malvern, PA 19355
-
- Phone: (800) 208-2747
-
- EMail: grabil@lucent.com
-
-
-
-
- Michael Dooley
- Diamond IP Technologies
- One E Uwchlan Ave, Suite 112
- Exton, PA 19341
-
- EMail: mdooley@diamondip.com
-
-
-
-
- Arun Kapur
- K5 Networks
- 2 Toll House Lane
- Colts Neck, NJ 07722
-
- Phone: (732) 817-9475
-
-17. Full Copyright Statement
-
-Copyright (C) The Internet Society (2003). All Rights Reserved.
-
-This document and translations of it may be copied and furnished to oth-
-ers, and derivative works that comment on or otherwise explain it or
-assist in its implementation may be prepared, copied, published and dis-
-tributed, in whole or in part, without restriction of any kind, provided
-that the above copyright notice and this paragraph are included on all
-such copies and derivative works. However, this document itself may not
-be modified in any way, such as by removing the copyright notice or
-references to the Internet Society or other Internet organizations,
-except as needed for the purpose of developing Internet standards in
-which case the procedures for copyrights defined in the Internet Stan-
-dards process must be followed, or as required to translate it into
-languages other than English.
-
-The limited permissions granted above are perpetual and will not be
-revoked by the Internet Society or its successors or assigns.
-
-
-
-Droms, et. al. Expires September 2003 [Page 132]
-
-Internet Draft DHCP Failover Protocol March 2003
-
-
-This document and the information contained herein is provided on an "AS
-IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK
-FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT
-LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT
-INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FIT-
-NESS FOR A PARTICULAR PURPOSE.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Droms, et. al. Expires September 2003 [Page 133]
- \ No newline at end of file
diff --git a/doc/rfc1542.txt b/doc/rfc1542.txt
deleted file mode 100644
index cc03e669..00000000
--- a/doc/rfc1542.txt
+++ /dev/null
@@ -1,1291 +0,0 @@
-
-
-
-
-
-
-Network Working Group W. Wimer
-Request for Comments: 1542 Carnegie Mellon University
-Updates: 951 October 1993
-Obsoletes: 1532
-Category: Standards Track
-
-
- Clarifications and Extensions for the Bootstrap Protocol
-
-Status of this Memo
-
- This RFC specifies an Internet standards track protocol for the
- Internet community, and requests discussion and suggestions for
- improvements. Please refer to the current edition of the "Internet
- Official Protocol Standards" for the standardization state and status
- of this protocol. Distribution of this memo is unlimited.
-
-Abstract
-
- Some aspects of the BOOTP protocol were rather loosely defined in its
- original specification. In particular, only a general description
- was provided for the behavior of "BOOTP relay agents" (originally
- called BOOTP forwarding agents"). The client behavior description
- also suffered in certain ways. This memo attempts to clarify and
- strengthen the specification in these areas. Due to some errors
- introduced into RFC 1532 in the editorial process, this memo is
- reissued as RFC 1542.
-
- In addition, new issues have arisen since the original specification
- was written. This memo also attempts to address some of these.
-
-Table of Contents
-
- 1. Introduction................................................. 2
- 1.1 Requirements................................................ 3
- 1.2 Terminology................................................. 3
- 1.3 Data Transmission Order..................................... 4
- 2. General Issues............................................... 5
- 2.1 General BOOTP Processing.................................... 5
- 2.2 Definition of the 'flags' Field............................. 5
- 2.3 Bit Ordering of Hardware Addresses.......................... 7
- 2.4 BOOTP Over IEEE 802.5 Token Ring Networks................... 8
- 3. BOOTP Client Behavior........................................ 9
- 3.1 Client use of the 'flags' field............................. 9
- 3.1.1 The BROADCAST flag........................................ 9
- 3.1.2 The remainder of the 'flags' field........................ 9
- 3.2 Definition of the 'secs' field.............................. 10
- 3.3 Use of the 'ciaddr' and 'yiaddr' fields..................... 10
-
-
-
-Wimer [Page 1]
-
-RFC 1542 Clarifications and Extensions for BOOTP October 1993
-
-
- 3.4 Interpretation of the 'giaddr' field........................ 11
- 3.5 Vendor information "magic cookie"........................... 12
- 4. BOOTP Relay Agents........................................... 13
- 4.1 General BOOTP Processing for Relay Agents................... 14
- 4.1.1 BOOTREQUEST Messages...................................... 14
- 4.1.2 BOOTREPLY Messages........................................ 17
- 5. BOOTP Server Behavior........................................ 18
- 5.1 Reception of BOOTREQUEST Messages........................... 18
- 5.2 Use of the 'secs' field..................................... 19
- 5.3 Use of the 'ciaddr' field................................... 19
- 5.4 Strategy for Delivery of BOOTREPLY Messages................. 20
- Acknowledgements................................................ 21
- References...................................................... 22
- Security Considerations......................................... 23
- Author's Address................................................ 23
-
-1. Introduction
-
- The Bootstrap Protocol (BOOTP) is a UDP/IP-based protocol which
- allows a booting host to configure itself dynamically and without
- user supervision. BOOTP provides a means to notify a host of its
- assigned IP address, the IP address of a boot server host, and the
- name of a file to be loaded into memory and executed [1]. Other
- configuration information such as the local subnet mask, the local
- time offset, the addresses of default routers, and the addresses of
- various Internet servers can also be communicated to a host using
- BOOTP [2].
-
- Unfortunately, the original BOOTP specification [1] left some issues
- of the protocol open to question. The exact behavior of BOOTP relay
- agents formerly called "BOOTP forwarding agents") was not clearly
- specified. Some parts of the overall protocol specification actually
- conflict, while other parts have been subject to misinterpretation,
- indicating that clarification is needed. This memo addresses these
- problems.
-
- Since the introduction of BOOTP, the IEEE 802.5 Token Ring Network
- has been developed which presents a unique problem for BOOTP's
- particular message-transfer paradigm. This memo also suggests a
- solution for this problem.
-
- NOTE: Unless otherwise specified in this document or a later
- document, the information and requirements specified througout this
- document also apply to extensions to BOOTP such as the Dynamic Host
- Configuration Protocol (DHCP) [3].
-
-
-
-
-
-
-Wimer [Page 2]
-
-RFC 1542 Clarifications and Extensions for BOOTP October 1993
-
-
-1.1 Requirements
-
- In this memo, the words that are used to define the significance of
- particular requirements are capitalized. These words are:
-
- o "MUST"
-
- This word or the adjective "REQUIRED" means that the item
- is an absolute requirement of the specification.
-
- o "MUST NOT"
-
- This phrase means that the item is an absolute prohibition
- of the specification.
-
- o "SHOULD"
-
- This word or the adjective "RECOMMENDED" means that there
- may exist valid reasons in particular circumstances to
- ignore this item, but the full implications should be
- understood and the case carefully weighed before choosing a
- different course.
-
- o "SHOULD NOT"
-
- This phrase means that there may exist valid reasons in
- particular circumstances when the listed behavior is
- acceptable or even useful, but the full implications should
- be understood and the case carefully weighed before
- implementing any behavior described with this label.
-
- o "MAY"
-
- This word or the adjective "OPTIONAL" means that this item
- is truly optional. One vendor may choose to include the
- item because a particular marketplace requires it or
- because it enhances the product, for example; another
- vendor may omit the same item.
-
-1.2 Terminology
-
- This memo uses the following terms:
-
- BOOTREQUEST
-
- A BOOTREQUEST message is a BOOTP message sent from a BOOTP
- client to a BOOTP server, requesting configuration information.
-
-
-
-
-Wimer [Page 3]
-
-RFC 1542 Clarifications and Extensions for BOOTP October 1993
-
-
- BOOTREPLY
-
- A BOOTREPLY message is a BOOTP message sent from a BOOTP server
- to a BOOTP client, providing configuration information.
-
- Silently discard
-
- This memo specifies several cases where a BOOTP entity is to
- "silently discard" a received BOOTP message. This means that
- the entity is to discard the message without further
- processing, and that the entity will not send any ICMP error
- message as a result. However, for diagnosis of problems, the
- entity SHOULD provide the capability of logging the error,
- including the contents of the silently-discarded message, and
- SHOULD record the event in a statistics counter.
-
-1.3 Data Transmission Order
-
- The order of transmission of the header and data described in this
- document is resolved to the octet level. Whenever a diagram shows a
- group of octets, the order of transmission of those octets is the
- normal order in which they are read in English. For example, in the
- following diagram, the octets are transmitted in the order they are
- numbered.
-
- 0 1
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | 1 | 2 |
- +-------------------------------+
- | 3 | 4 |
- +-------------------------------+
- | 5 | 6 |
- +-------------------------------+
-
- Whenever an octet represents a numeric quantity, the leftmost bit in
- the diagram is the high order or most significant bit. That is, the
- bit labeled 0 is the most significant bit. For example, the
- following diagram represents the value 170 (decimal).
-
- 0 1 2 3 4 5 6 7
- +-+-+-+-+-+-+-+-+
- |1 0 1 0 1 0 1 0|
- +---------------+
-
- Similarly, whenever a multi-octet field represents a numeric quantity
- the leftmost bit of the whole field is the most significant bit.
- When a multi-octet quantity is transmitted the most significant octet
-
-
-
-Wimer [Page 4]
-
-RFC 1542 Clarifications and Extensions for BOOTP October 1993
-
-
- is transmitted first.
-
-2. General Issues
-
- This section covers issues of general relevance to all BOOTP entities
- (clients, servers, and relay agents).
-
-2.1 General BOOTP Processing
-
- The following consistency checks SHOULD be performed on BOOTP
- messages:
-
- o The IP Total Length and UDP Length must be large enough to
- contain the minimal BOOTP header of 300 octets (in the UDP
- data field) specified in [1].
-
- NOTE: Future extensions to the BOOTP protocol may increase the size
- of BOOTP messages. Therefore, BOOTP messages which, according to the
- IP Total Length and UDP Length fields, are larger than the minimum
- size specified by [1] MUST also be accepted.
-
- o The 'op' (opcode) field of the message must contain either the
- code for a BOOTREQUEST (1) or the code for a BOOTREPLY (2).
-
- BOOTP messages not meeting these consistency checks MUST be silently
- discarded.
-
-2.2 Definition of the 'flags' Field
-
- The standard BOOTP message format defined in [1] includes a two-octet
- field located between the 'secs' field and the 'ciaddr' field. This
- field is merely designated as "unused" and its contents left
- unspecified, although Section 7.1 of [1] does offer the following
- suggestion:
-
- "Before setting up the packet for the first time, it is a good
- idea to clear the entire packet buffer to all zeros; this will
- place all fields in their default state."
-
- This memo hereby designates this two-octet field as the 'flags'
- field.
-
- This memo hereby defines the most significant bit of the 'flags'
- field as the BROADCAST (B) flag. The semantics of this flag are
- discussed in Sections 3.1.1 and 4.1.2 of this memo.
-
- The remaining bits of the 'flags' field are reserved for future
- use. They MUST be set to zero by clients and ignored by servers
-
-
-
-Wimer [Page 5]
-
-RFC 1542 Clarifications and Extensions for BOOTP October 1993
-
-
- and relay agents.
-
- The 'flags' field, then, appears as follows:
-
- 0 1
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- |B| MBZ |
- +-+-----------------------------+
-
- where:
-
- B BROADCAST flag (discussed in Sections 3.1.1 and 4.1.2)
-
- MBZ MUST BE ZERO (reserved for future use)
-
- The format of a BOOTP message is shown below. The numbers in
- parentheses indicate the size of each field in octets.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Wimer [Page 6]
-
-RFC 1542 Clarifications and Extensions for BOOTP October 1993
-
-
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | op (1) | htype (1) | hlen (1) | hops (1) |
- +---------------+---------------+---------------+---------------+
- | xid (4) |
- +-------------------------------+-------------------------------+
- | secs (2) | flags (2) |
- +-------------------------------+-------------------------------+
- | ciaddr (4) |
- +---------------------------------------------------------------+
- | yiaddr (4) |
- +---------------------------------------------------------------+
- | siaddr (4) |
- +---------------------------------------------------------------+
- | giaddr (4) |
- +---------------------------------------------------------------+
- | |
- | chaddr (16) |
- | |
- | |
- +---------------------------------------------------------------+
- | |
- | sname (64) |
- +---------------------------------------------------------------+
- | |
- | file (128) |
- +---------------------------------------------------------------+
- | |
- | vend (64) |
- +---------------------------------------------------------------+
-
-2.3 Bit Ordering of Hardware Addresses
-
- The bit ordering used for link-level hardware addresses in the
- 'chaddr' field SHOULD be the same as the ordering used for the ARP
- protocol [4] on the client's link-level network (assuming ARP is
- defined for that network).
-
- The 'chaddr' field MUST be preserved as it was specified by the BOOTP
- client. A relay agent MUST NOT reverse the bit ordering of the
- 'chaddr' field even if it happens to be relaying a BOOTREQUEST
- between two networks which use different bit orderings.
-
- DISCUSSION:
-
- One of the primary reasons the 'chaddr' field exists is to
- enable BOOTP servers and relay agents to communicate directly
-
-
-
-Wimer [Page 7]
-
-RFC 1542 Clarifications and Extensions for BOOTP October 1993
-
-
- with clients without the use of broadcasts. In practice, the
- contents of the 'chaddr' field is often used to create an ARP-
- cache entry in exactly the same way the normal ARP protocol
- would have. Clearly, interoperability can only be achieved if
- a consistent interpretation of the 'chaddr' field is used.
-
- As a practical example, this means that the bit ordering used
- for the 'chaddr' field by a BOOTP client on an IEEE 802.5 Token
- Ring network is the opposite of the bit ordering used by a
- BOOTP client on a DIX ethernet network.
-
-2.4 BOOTP Over IEEE 802.5 Token Ring Networks
-
- Special consideration of the client/server and client/relay agent
- interactions must be given to IEEE 802.5 networks because of non-
- transparent bridging.
-
- The client SHOULD send its broadcast BOOTREQUEST with an All Routes
- Explorer RIF. This will enable servers/relay agents to cache the
- return route if they choose to do so. For those server/relay agents
- which cannot cache the return route (because they are stateless, for
- example), the BOOTREPLY message SHOULD be sent to the client's
- hardware address, as taken from the BOOTP message, with a Spanning
- Tree Rooted RIF. The actual bridge route will be recorded by the
- client and server/relay agent by normal ARP processing code.
-
- DISCUSSION:
-
- In the simplest case, an unbridged, single ring network, the
- broadcast behavior of the BOOTP protocol is identical to that
- of Ethernet networks. However, a BOOTP client cannot know, a
- priori, that an 802.5 network is not bridged. In fact, the
- likelihood is that the server, or relay agent, will not know
- either.
-
- Of the four possible scenerios, only two are interesting: where
- the assumption is that the 802.5 network is not bridged and it
- is, and the assumption that the network is bridged and it is
- not. In the former case, the Routing Information Field (RIF)
- will not be used; therefore, if the server/relay agent are on
- another segment of the ring, the client cannot reach it. In
- the latter case, the RIF field will be used, resulting in a few
- extraneous bytes on the ring. It is obvious that an almost
- immeasurable inefficiency is to be preferred over a complete
- failure to communicate.
-
-
-
-
-
-
-Wimer [Page 8]
-
-RFC 1542 Clarifications and Extensions for BOOTP October 1993
-
-
- Given that the assumption is that RIF fields will be needed, it
- is necesary to determine the optimum method for the client to
- reach the server/relay agent, and the optimum method for the
- response to be returned.
-
-3. BOOTP Client Behavior
-
- This section clarifies various issues regarding BOOTP client
- behavior.
-
-3.1 Client use of the 'flags' field
-
-3.1.1 The BROADCAST flag
-
- Normally, BOOTP servers and relay agents attempt to deliver BOOTREPLY
- messages directly to a client using unicast delivery. The IP
- destination address (in the IP header) is set to the BOOTP 'yiaddr'
- address and the link-layer destination address is set to the BOOTP
- 'chaddr' address. Unfortunately, some client implementations are
- unable to receive such unicast IP datagrams until they know their own
- IP address (thus we have a "chicken and egg" issue). Often, however,
- they can receive broadcast IP datagrams (those with a valid IP
- broadcast address as the IP destination and the link-layer broadcast
- address as the link-layer destination).
-
- If a client falls into this category, it SHOULD set (to 1) the
- newly-defined BROADCAST flag in the 'flags' field of BOOTREPLY
- messages it generates. This will provide a hint to BOOTP servers and
- relay agents that they should attempt to broadcast their BOOTREPLY
- messages to the client.
-
- If a client does not have this limitation (i.e., it is perfectly able
- to receive unicast BOOTREPLY messages), it SHOULD NOT set the
- BROADCAST flag (i.e., it SHOULD clear the BROADCAST flag to 0).
-
- DISCUSSION:
-
- This addition to the protocol is a workaround for old host
- implementations. Such implementations SHOULD be modified so
- that they may receive unicast BOOTREPLY messages, thus making
- use of this workaround unnecessary. In general, the use of
- this mechanism is discouraged.
-
-3.1.2 The remainder of the 'flags' field
-
- The remaining bits of the 'flags' field are reserved for future use.
- A client MUST set these bits to zero in all BOOTREQUEST messages it
- generates. A client MUST ignore these bits in all BOOTREPLY messages
-
-
-
-Wimer [Page 9]
-
-RFC 1542 Clarifications and Extensions for BOOTP October 1993
-
-
- it receives.
-
-3.2 Definition of the 'secs' field
-
- The 'secs' field of a BOOTREQUEST message SHOULD represent the
- elapsed time, in seconds, since the client sent its first BOOTREQUEST
- message. Note that this implies that the 'secs' field of the first
- BOOTREQUEST message SHOULD be set to zero.
-
- Clients SHOULD NOT set the 'secs' field to a value which is constant
- for all BOOTREQUEST messages.
-
- DISCUSSION:
-
- The original definition of the 'secs' field was vague. It was
- not clear whether it represented the time since the first
- BOOTREQUEST message was sent or some other time period such as
- the time since the client machine was powered-up. This has
- limited its usefulness as a policy control mechanism for BOOTP
- servers and relay agents. Furthermore, certain client
- implementations have been known to simply set this field to a
- constant value or use incorrect byte-ordering. Incorrect
- byte-ordering usually makes it appear as if a client has been
- waiting much longer than it really has, so a relay agent will
- relay the BOOTREQUEST sooner than desired (usually
- immediately). These implementation errors have further
- undermined the usefulness of the 'secs' field. These incorrect
- implementations SHOULD be corrected.
-
-3.3 Use of the 'ciaddr' and 'yiaddr' fields
-
- If a BOOTP client does not know what IP address it should be using,
- the client SHOULD set the 'ciaddr' field to 0.0.0.0. If the client
- has the ability to remember the last IP address it was assigned, or
- it has been preconfigured with an IP address via some alternate
- mechanism, the client MAY fill the 'ciaddr' field with that IP
- address. If the client does place a non-zero IP address in the
- 'ciaddr' field, the client MUST be prepared to accept incoming
- unicast datagrams addressed to that IP address and also answer ARP
- requests for that IP address (if ARP is used on that network).
-
- The BOOTP server is free to assign a different IP address (in the
- 'yiaddr' field) than the client expressed in 'ciaddr'. The client
- SHOULD adopt the IP address specified in 'yiaddr' and begin using it
- as soon as possible.
-
-
-
-
-
-
-Wimer [Page 10]
-
-RFC 1542 Clarifications and Extensions for BOOTP October 1993
-
-
- DISCUSSION:
-
- There are various interpretations about the purpose of the
- 'ciaddr' field and, unfortunately, no agreement on a single
- correct interpretation. One interpretation is that if a client
- is willing to accept whatever IP address the BOOTP server
- assigns to it, the client should always place 0.0.0.0 in the
- 'ciaddr' field, regardless of whether it knows its previously-
- assigned address. Conversely, if the client wishes to assert
- that it must have a particular IP address (e.g., the IP address
- was hand-configured by the host administrator and BOOTP is only
- being used to obtain a boot file and/or information from the
- 'vend' field), the client will then fill the 'ciaddr' field
- with the desired IP address and ignore the IP address assigned
- by the BOOTP server as indicated in the 'yiaddr' field. An
- alternate interpretation holds that the client always fills the
- 'ciaddr' field with its most recently-assigned IP address (if
- known) even if that address may be incorrect. Such a client
- will still accept and use the address assigned by the BOOTP
- server as indicated in the 'yiaddr' field. The motivation for
- this interpretation is to aid the server in identifying the
- client and/or in delivering the BOOTREPLY to the client. Yet a
- third (mis)interpretation allows the client to use 'ciaddr' to
- express the client's desired IP address, even if the client has
- never used that address before or is not currently using that
- address.
-
- The last interpretation is incorrect as it may prevent the
- BOOTREPLY from reaching the client. The server will usually
- unicast the reply to the address given in 'ciaddr' but the
- client may not be listening on that address yet, or the client
- may be connected to an incorrect subnet such that normal IP
- routing (correctly) routes the reply to a different subnet.
-
- The second interpretation also suffers from the "incorrect
- subnet" problem.
-
- The first interpretation seems to be the safest and most likely
- to promote interoperability.
-
-3.4 Interpretation of the 'giaddr' field
-
- The 'giaddr' field is rather poorly named. It exists to facilitate
- the transfer of BOOTREQUEST messages from a client, through BOOTP
- relay agents, to servers on different networks than the client.
- Similarly, it facilitates the delivery of BOOTREPLY messages from the
- servers, through BOOTP relay agents, back to the client. In no case
- does it represent a general IP router to be used by the client. A
-
-
-
-Wimer [Page 11]
-
-RFC 1542 Clarifications and Extensions for BOOTP October 1993
-
-
- BOOTP client MUST set the 'giaddr' field to zero (0.0.0.0) in all
- BOOTREQUEST messages it generates.
-
- A BOOTP client MUST NOT interpret the 'giaddr' field of a BOOTREPLY
- message to be the IP address of an IP router. A BOOTP client SHOULD
- completely ignore the contents of the 'giaddr' field in BOOTREPLY
- messages.
-
- DISCUSSION:
-
- The semantics of the 'giaddr' field were poorly defined.
- Section 7.5 of [1] states:
-
- "If 'giaddr' (gateway address) is nonzero, then the packets
- should be forwarded there first, in order to get to the
- server."
-
- In that sentence, "get to" refers to communication from the client to
- the server subsequent to the BOOTP exchange, such as a TFTP session.
- Unfortunately, the 'giaddr' field may contain the address of a BOOTP
- relay agent that is not itself an IP router (according to [1],
- Section 8, fifth paragraph), in which case, it will be useless as a
- first-hop for TFTP packets sent to the server (since, by definition,
- non-routers don't forward datagrams at the IP layer).
-
- Although now prohibited by Section 4.1.1 of this memo, the 'giaddr'
- field might contain a broadcast address according to Section 8, sixth
- paragraph of [1]. Not only would such an address be useless as a
- router address, it might also cause the client to ARP for the
- broadcast address (since, if the client didn't receive a subnet mask
- in the BOOTREPLY message, it would be unable to recognize a subnet
- broadcast address). This is clearly undesirable.
-
- To reach a non-local server, clients can obtain a first-hop router
- address from the "Gateway" subfield of the "Vendor Information
- Extensions" [2] (if present), or via the ICMP router discovery
- protocol [5] or other similar mechanism.
-
-3.5 Vendor information "magic cookie"
-
- It is RECOMMENDED that a BOOTP client always fill the first four
- octets of the 'vend' (vendor information) field of a BOOTREQUEST with
- a four-octet identifier called a "magic cookie." A BOOTP client
- SHOULD do this even if it has no special information to communicate
- to the BOOTP server using the 'vend' field. This aids the BOOTP
- server in determining what vendor information format it should use in
- its BOOTREPLY messages.
-
-
-
-
-Wimer [Page 12]
-
-RFC 1542 Clarifications and Extensions for BOOTP October 1993
-
-
- If a special vendor-specific magic cookie is not being used, a BOOTP
- client SHOULD use the dotted decimal value 99.130.83.99 as specified
- in [2]. In this case, if the client has no information to
- communicate to the server, the octet immediately following the magic
- cookie SHOULD be set to the "End" tag (255) and the remaining octets
- of the 'vend' field SHOULD be set to zero.
-
- DISCUSSION:
-
- Sometimes different operating systems or networking packages
- are run on the same machine at different times (or even at the
- same time!). Since the hardware address placed in the 'chaddr'
- field will likely be the same, BOOTREQUESTs from completely
- different BOOTP clients on the same machine will likely be
- difficult for a BOOTP server to differentiate. If the client
- includes a magic cookie in its BOOTREQUESTs, the server will at
- least know what format the client expects and can understand in
- corresponding BOOTREPLY messages.
-
-4. BOOTP Relay Agents
-
- In many cases, BOOTP clients and their associated BOOTP
- server(s) do not reside on the same IP network or subnet. In
- such cases, some kind of third-party agent is required to
- transfer BOOTP messages between clients and servers. Such an
- agent was originally referred to as a "BOOTP forwarding agent."
- However, in order to avoid confusion with the IP forwarding
- function of an IP router, the name "BOOTP relay agent" is
- hereby adopted instead.
-
- DISCUSSION:
-
- A BOOTP relay agent performs a task which is distinct from an
- IP router's normal IP forwarding function. While a router
- normally switches IP datagrams between networks more-or-less
- transparently, a BOOTP relay agent may more properly be thought
- to receive BOOTP messages as a final destination and then
- generate new BOOTP messages as a result. It is incorrect for a
- relay agent implementation to simply forward a BOOTP message
- "straight through like a regular packet."
-
- This relay-agent functionality is most conveniently located in
- the routers which interconnect the clients and servers, but may
- alternatively be located in a host which is directly connected
- to the client subnet.
-
- Any Internet host or router which provides BOOTP relay-agent
- capability MUST conform to the specifications in this memo.
-
-
-
-Wimer [Page 13]
-
-RFC 1542 Clarifications and Extensions for BOOTP October 1993
-
-
-4.1 General BOOTP Processing for Relay Agents
-
- All locally delivered UDP messages whose UDP destination port number
- is BOOTPS (67) are considered for special processing by the host or
- router's logical BOOTP relay agent.
-
- In the case of a host, locally delivered datagrams are simply all
- datagrams normally received by that host, i.e., broadcast and
- multicast datagrams as well as unicast datagrams addressed to IP
- addresses of that host.
-
- In the case of a router, locally delivered datagrams are broadcast
- and multicast datagrams as well as unicast datagrams addressed to IP
- addresses of that router. These are datagrams for which the router
- should be considered an end destination as opposed to an intermediate
- switching node. Thus a unicast datagram with an IP destination not
- matching any of the router's IP addresses is not considered for
- processing by the router's logical BOOTP relay agent.
-
- Hosts and routers are usually required to silently discard incoming
- datagrams containing illegal IP source addresses. This is generally
- known as "Martian address filtering." One of these illegal addresses
- is 0.0.0.0 (or actually anything on network 0). However, hosts or
- routers which support a BOOTP relay agent MUST accept for local
- delivery to the relay agent BOOTREQUEST messages whose IP source
- address is 0.0.0.0. BOOTREQUEST messages from legal IP source
- addresses MUST also be accepted.
-
- A relay agent MUST silently discard any received UDP messages whose
- UDP destination port number is BOOTPC (68).
-
- DISCUSSION:
-
- There should be no need for a relay agent to process messages
- addressed to the BOOTPC port. Careful reading of the original
- BOOTP specification [1] will show this. Nevertheless, some
- relay agent implementations incorrectly relay such messages.
-
- The consistency checks specified in Section 2.1 SHOULD be performed
- by the relay agent. BOOTP messages not meeting these consistency
- checks MUST be silently discarded.
-
-4.1.1 BOOTREQUEST Messages
-
- Some configuration mechanism MUST exist to enable or disable the
- relaying of BOOTREQUEST messages. Relaying MUST be disabled by
- default.
-
-
-
-
-Wimer [Page 14]
-
-RFC 1542 Clarifications and Extensions for BOOTP October 1993
-
-
- When the BOOTP relay agent receives a BOOTREQUEST message, it MAY use
- the value of the 'secs' (seconds since client began booting) field of
- the request as a factor in deciding whether to relay the request. If
- such a policy mechanism is implemented, its threshold SHOULD be
- configurable.
-
- DISCUSSION:
-
- To date, this feature of the BOOTP protocol has not necessarily
- been shown to be useful. See Section 3.2 for a discussion.
-
- The relay agent MUST silently discard BOOTREQUEST messages whose
- 'hops' field exceeds the value 16. A configuration option SHOULD be
- provided to set this threshold to a smaller value if desired by the
- network manager. The default setting for a configurable threshold
- SHOULD be 4.
-
- If the relay agent does decide to relay the request, it MUST examine
- the 'giaddr' ("gateway" IP address) field. If this field is zero,
- the relay agent MUST fill this field with the IP address of the
- interface on which the request was received. If the interface has
- more than one IP address logically associated with it, the relay
- agent SHOULD choose one IP address associated with that interface and
- use it consistently for all BOOTP messages it relays. If the
- 'giaddr' field contains some non-zero value, the 'giaddr' field MUST
- NOT be modified. The relay agent MUST NOT, under any circumstances,
- fill the 'giaddr' field with a broadcast address as is suggested in
- [1] (Section 8, sixth paragraph).
-
- The value of the 'hops' field MUST be incremented.
-
- All other BOOTP fields MUST be preserved intact.
-
- At this point, the request is relayed to its new destination (or
- destinations). This destination MUST be configurable. Further, this
- destination configuration SHOULD be independent of the destination
- configuration for any other so-called "broadcast forwarders" (e.g.,
- for the UDP-based TFTP, DNS, Time, etc. protocols).
-
- DISCUSSION:
-
- The network manager may wish the relaying destination to be an
- IP unicast, multicast, broadcast, or some combination. A
- configurable list of destination IP addresses provides good
- flexibility. More flexible configuration schemes are
- encouraged. For example, it may be desirable to send to the
- limited broadcast address (255.255.255.255) on specific
- physical interfaces. However, if the BOOTREQUEST message was
-
-
-
-Wimer [Page 15]
-
-RFC 1542 Clarifications and Extensions for BOOTP October 1993
-
-
- received as a broadcast, the relay agent MUST NOT rebroadcast
- the BOOTREQUEST on the physical interface from whence it came.
-
- A relay agent MUST use the same destination (or set of
- destinations) for all BOOTREQUEST messages it relays from a
- given client.
-
- DISCUSSION:
-
- At least one known relay agent implementation uses a round-
- robin scheme to provide load balancing across multiple BOOTP
- servers. Each time it receives a new BOOTREQUEST message, it
- relays the message to the next BOOTP server in a list of
- servers. Thus, with this relay agent, multiple consecutive
- BOOTREQUEST messages from a given client will be delivered to
- different servers.
-
- Unfortunately, this well-intentioned scheme reacts badly with
- DHCP [3] and perhaps other variations of the BOOTP protocol
- which depend on multiple exchanges of BOOTREQUEST and BOOTREPLY
- messages between clients and servers. Therefore, all
- BOOTREQUEST messages from a given client MUST be relayed to the
- same destination (or set of destinations).
-
- One way to meet this requirement while providing some load-
- balancing benefit is to hash the client's link-layer address
- (or some other reliable client-identifying information) and use
- the resulting hash value to select the appropriate relay
- destination (or set of destinations). The simplest solution,
- of course, is to not use a load-balancing scheme and just relay
- ALL received BOOTREQUEST messages to the same destination (or
- set of destinations).
-
- When transmitting the request to its next destination, the
- relay agent may set the IP Time-To-Live field to either the
- default value for new datagrams originated by the relay agent,
- or to the TTL of the original BOOTREQUEST decremented by (at
- least) one.
-
- DISCUSSION:
-
- As an extra precaution against BOOTREQUEST loops, it is
- preferable to use the decremented TTL from the original
- BOOTREQUEST. Unfortunately, this may be difficult to do in
- some implementations.
-
- If the BOOTREQUEST has a UDP checksum (i.e., the UDP checksum
- is non-zero), the checksum must be recalculated before
-
-
-
-Wimer [Page 16]
-
-RFC 1542 Clarifications and Extensions for BOOTP October 1993
-
-
- transmitting the request.
-
-4.1.2 BOOTREPLY Messages
-
- BOOTP relay agents relay BOOTREPLY messages only to BOOTP clients.
- It is the responsibility of BOOTP servers to send BOOTREPLY messages
- directly to the relay agent identified in the 'giaddr' field.
- Therefore, a relay agent may assume that all BOOTREPLY messages it
- receives are intended for BOOTP clients on its directly-connected
- networks.
-
- When a relay agent receives a BOOTREPLY message, it should examine
- the BOOTP 'giaddr', 'yiaddr', 'chaddr', 'htype', and 'hlen' fields.
- These fields should provide adequate information for the relay agent
- to deliver the BOOTREPLY message to the client.
-
- The 'giaddr' field can be used to identify the logical interface from
- which the reply must be sent (i.e., the host or router interface
- connected to the same network as the BOOTP client). If the content
- of the 'giaddr' field does not match one of the relay agent's
- directly-connected logical interfaces, the BOOTREPLY messsage MUST be
- silently discarded.
-
- The 'htype', 'hlen', and 'chaddr' fields supply the link-layer
- hardware type, hardware address length, and hardware address of the
- client as defined in the ARP protocol [4] and the Assigned Numbers
- document [6]. The 'yiaddr' field is the IP address of the client, as
- assigned by the BOOTP server.
-
- The relay agent SHOULD examine the newly-defined BROADCAST flag (see
- Sections 2.2 and 3.1.1 for more information). If this flag is set to
- 1, the reply SHOULD be sent as an IP broadcast using the IP limited
- broadcast address 255.255.255.255 as the IP destination address and
- the link-layer broadcast address as the link-layer destination
- address. If the BROADCAST flag is cleared (0), the reply SHOULD be
- sent as an IP unicast to the IP address specified by the 'yiaddr'
- field and the link-layer address specified in the 'chaddr' field. If
- unicasting is not possible, the reply MAY be sent as a broadcast, in
- which case it SHOULD be sent to the link-layer broadcast address
- using the IP limited broadcast address 255.255.255.255 as the IP
- destination address.
-
- DISCUSSION:
-
- The addition of the BROADCAST flag to the protocol is a
- workaround to help promote interoperability with certain client
- implementations.
-
-
-
-
-Wimer [Page 17]
-
-RFC 1542 Clarifications and Extensions for BOOTP October 1993
-
-
- Note that since the 'flags' field was previously defined in [1]
- simply as an "unused" field, it is possible that old client or
- server implementations may accidentally and unknowingly set the
- new BROADCAST flag. It is actually expected that such
- implementations will be rare (most implementations seem to
- zero-out this field), but interactions with such
- implementations must nevertheless be considered. If an old
- client or server does set the BROADCAST flag to 1 incorrectly,
- conforming relay agents will generate broadcast BOOTREPLY
- messages to the corresponding client. The BOOTREPLY messages
- should still properly reach the client, at the cost of one
- (otherwise unnecessary) additional broadcast. This, however,
- is no worse than a server or relay agent which always
- broadcasts its BOOTREPLY messages.
-
- Older client or server implementations which accidentally set
- the BROADCAST flag SHOULD be corrected to properly comply with
- this newer specification.
-
- All BOOTP fields MUST be preserved intact. The relay agent
- MUST NOT modify any BOOTP field of the BOOTREPLY message when
- relaying it to the client.
-
- The reply MUST have its UDP destination port set to BOOTPC
- (68).
-
- If the BOOTREPLY has a UDP checksum (i.e., the UDP checksum is
- non-zero), the checksum must be recalculated before
- transmitting the reply.
-
-5. BOOTP Server Behavior
-
- This section provides clarifications on the behavior of BOOTP
- servers.
-
-5.1 Reception of BOOTREQUEST Messages
-
- All received UDP messages whose UDP destination port number is BOOTPS
- (67) are considered for processing by the BOOTP server.
-
- Hosts and routers are usually required to silently discard incoming
- datagrams containing illegal IP source addresses. This is generally
- known as "Martian address filtering." One of these illegal addresses
- is 0.0.0.0 (or actually anything on network 0). However, hosts or
- routers which support a BOOTP server MUST accept for local delivery
- to the server BOOTREQUEST messages whose IP source address is
- 0.0.0.0. BOOTREQUEST messages from legal IP source addresses MUST
- also be accepted.
-
-
-
-Wimer [Page 18]
-
-RFC 1542 Clarifications and Extensions for BOOTP October 1993
-
-
- A BOOTP server MUST silently discard any received UDP messages whose
- UDP destination port number is BOOTPC (68).
-
- DISCUSSION:
-
- There should be no need for a BOOTP server to process messages
- addressed to the BOOTPC port. Careful reading of the original
- BOOTP specification [1] will show this.
-
- The consistency checks specified in Section 2.1 SHOULD be
- performed by the BOOTP server. BOOTP messages not meeting
- these consistency checks MUST be silently discarded.
-
-5.2 Use of the 'secs' field
-
- When the BOOTP server receives a BOOTREQUEST message, it MAY use the
- value of the 'secs' (seconds since client began booting) field of the
- request as a factor in deciding whether and/or how to reply to the
- request.
-
- DISCUSSION:
-
- To date, this feature of the BOOTP protocol has not necessarily
- been shown to be useful. See Section 3.2 for a discussion.
-
-5.3 Use of the 'ciaddr' field
-
- There have been various client interpretations of the 'ciaddr' field
- for which Section 3.3 should be consulted. A BOOTP server SHOULD be
- prepared to deal with these varying interpretations. In general, the
- 'ciaddr' field SHOULD NOT be trusted as a sole key in identifying a
- client; the contents of the 'ciaddr', 'chaddr', 'htype', and 'hlen'
- fields, and probably other information (perhaps in the 'file' and
- 'vend' fields) SHOULD all be considered together in deciding how to
- respond to a given client.
-
- BOOTP servers SHOULD preserve the contents of the 'ciaddr' field in
- BOOTREPLY messages; the contents of 'ciaddr' in a BOOTREPLY message
- SHOULD exactly match the contents of 'ciaddr' in the corresponding
- BOOTREQUEST message.
-
- DISCUSSION:
-
- It has been suggested that a client may wish to use the
- contents of 'ciaddr' to further verify that a particular
- BOOTREPLY message was indeed intended for it.
-
-
-
-
-
-Wimer [Page 19]
-
-RFC 1542 Clarifications and Extensions for BOOTP October 1993
-
-
-5.4 Strategy for Delivery of BOOTREPLY Messages
-
- Once the BOOTP server has created an appropriate BOOTREPLY message,
- that BOOTREPLY message must be properly delivered to the client.
-
- The server SHOULD first check the 'ciaddr' field. If the 'ciaddr'
- field is non-zero, the BOOTREPLY message SHOULD be sent as an IP
- unicast to the IP address identified in the 'ciaddr' field. The UDP
- destination port MUST be set to BOOTPC (68). However, the server
- MUST be aware of the problems identified in Section 3.3. The server
- MAY choose to ignore the 'ciaddr' field and act as if the 'ciaddr'
- field contains 0.0.0.0 (and thus continue with the rest of the
- delivery algorithm below).
-
- The server SHOULD next check the 'giaddr' field. If this field is
- non-zero, the server SHOULD send the BOOTREPLY as an IP unicast to
- the IP address identified in the 'giaddr' field. The UDP destination
- port MUST be set to BOOTPS (67). This action will deliver the
- BOOTREPLY message directly to the BOOTP relay agent closest to the
- client; the relay agent will then perform the final delivery to the
- client. If the BOOTP server has prior knowledge that a particular
- client cannot receive unicast BOOTREPLY messages (e.g., the network
- manager has explicitly configured the server with such knowledge),
- the server MAY set the newly-defined BROADCAST flag to indicate that
- relay agents SHOULD broadcast the BOOTREPLY message to the client.
- Otherwise, the server MUST preserve the state of the BROADCAST flag
- so that the relay agent can correctly act upon it.
-
- If the 'giaddr' field is set to 0.0.0.0, then the client resides on
- one of the same networks as the BOOTP server. The server SHOULD
- examine the newly-defined BROADCAST flag (see Sections 2.2, 3.1.1 and
- 4.1.2 for more information). If this flag is set to 1 or the server
- has prior knowledge that the client is unable to receive unicast
- BOOTREPLY messages, the reply SHOULD be sent as an IP broadcast using
- the IP limited broadcast address 255.255.255.255 as the IP
- destination address and the link-layer broadcast address as the
- link-layer destination address. If the BROADCAST flag is cleared
- (0), the reply SHOULD be sent as an IP unicast to the IP address
- specified by the 'yiaddr' field and the link-layer address specified
- in the 'chaddr' field. If unicasting is not possible, the reply MAY
- be sent as a broadcast in which case it SHOULD be sent to the link-
- layer broadcast address using the IP limited broadcast address
- 255.255.255.255 as the IP destination address. In any case, the UDP
- destination port MUST be set to BOOTPC (68).
-
-
-
-
-
-
-
-Wimer [Page 20]
-
-RFC 1542 Clarifications and Extensions for BOOTP October 1993
-
-
- DISCUSSION:
-
- The addition of the BROADCAST flag to the protocol is a
- workaround to help promote interoperability with certain client
- implementations.
-
- The following table summarizes server delivery decisions for
- BOOTREPLY messages based upon information in BOOTREQUEST
- messages:
-
- BOOTREQUEST fields BOOTREPLY values for UDP, IP, link-layer
- +-----------------------+-----------------------------------------+
- | 'ciaddr' 'giaddr' B | UDP dest IP destination link dest |
- +-----------------------+-----------------------------------------+
- | non-zero X X | BOOTPC (68) 'ciaddr' normal |
- | 0.0.0.0 non-zero X | BOOTPS (67) 'giaddr' normal |
- | 0.0.0.0 0.0.0.0 0 | BOOTPC (68) 'yiaddr' 'chaddr' |
- | 0.0.0.0 0.0.0.0 1 | BOOTPC (68) 255.255.255.255 broadcast |
- +-----------------------+-----------------------------------------+
-
- B = BROADCAST flag
-
- X = Don't care
-
- normal = determine from the given IP destination using normal
- IP routing mechanisms and/or ARP as for any other
- normal datagram
-
-Acknowledgements
-
- The author would like to thank Gary Malkin for his contribution of
- the "BOOTP over IEEE 802.5 Token Ring Networks" section, and Steve
- Deering for his observations on the problems associated with the
- 'giaddr' field.
-
- Ralph Droms and the many members of the IETF Dynamic Host
- Configuration and Router Requirements working groups provided ideas
- for this memo as well as encouragement to write it.
-
- Philip Almquist and David Piscitello offered many helpful suggestions
- for improving the clarity, accuracy, and organization of this memo.
- These contributions are graciously acknowledged.
-
-
-
-
-
-
-
-
-
-Wimer [Page 21]
-
-RFC 1542 Clarifications and Extensions for BOOTP October 1993
-
-
-References
-
- [1] Croft, B., and J. Gilmore, "Bootstrap Protocol (BOOTP)", RFC 951,
- Stanford University and Sun Microsystems, September 1985.
-
- [2] Reynolds, J., "BOOTP Vendor Information Extensions", RFC 1497,
- USC/Information Sciences Institute, August 1993. This RFC is
- occasionally reissued with a new number. Please be sure to
- consult the latest version.
-
- [3] Droms, R., "Dynamic Host Configuration Protocol", RFC 1541,
- Bucknell University, October 1993.
-
- [4] Plummer, D., "An Ethernet Address Resolution Protocol", STD 37,
- RFC 826, MIT, November 1982.
-
- [5] Deering, S., "ICMP Router Discovery Messages", RFC 1256, Xerox
- PARC, September 1991.
-
- [6] Reynolds, J., and J. Postel, "Assigned Numbers", STD 2, RFC 1340,
- USC/Information Sciences Institute, July, 1992. This RFC is
- periodically reissued with a new number. Please be sure to
- consult the latest version.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Wimer [Page 22]
-
-RFC 1542 Clarifications and Extensions for BOOTP October 1993
-
-
-Security Considerations
-
- There are many factors which make BOOTP in its current form quite
- insecure. BOOTP is built directly upon UDP and IP which are as yet
- inherently insecure themselves. Furthermore, BOOTP is generally
- intended to make maintenance of remote and/or diskless hosts easier.
- While perhaps not impossible, configuring such hosts with passwords or
- keys may be difficult and inconvenient. This makes it difficult to
- provide any form of reasonable authentication between servers and
- clients.
-
- Unauthorized BOOTP servers may easily be set up. Such servers can
- then send false and potentially disruptive information to clients such
- as incorrect or duplicate IP addresses, incorrect routing information
- (including spoof routers, etc.), incorrect domain nameserver addresses
- (such as spoof nameservers), and so on. Clearly, once this "seed"
- mis-information is planted, an attacker can further compromise the
- affected systems.
-
- Unauthorized BOOTP relay agents may present some of the same problems
- as unauthorized BOOTP servers.
-
- Malicious BOOTP clients could masquerade as legitimate clients and
- retrieve information intended for those legitimate clients. Where
- dynamic allocation of resources is used, a malicious client could
- claim all resources for itself, thereby denying resources to
- legitimate clients.
-
-Author's Address
-
- Walt Wimer
- Network Development
- Carnegie Mellon University
- 5000 Forbes Avenue
- Pittsburgh, PA 15213-3890
-
- Phone: (412) 268-6252
- EMail: Walter.Wimer@CMU.EDU
-
-
-
-
-
-
-
-
-
-
-
-
-
-Wimer [Page 23]
- \ No newline at end of file
diff --git a/doc/rfc2131.txt b/doc/rfc2131.txt
deleted file mode 100644
index f45d9b86..00000000
--- a/doc/rfc2131.txt
+++ /dev/null
@@ -1,2523 +0,0 @@
-
-
-
-
-
-
-Network Working Group R. Droms
-Request for Comments: 2131 Bucknell University
-Obsoletes: 1541 March 1997
-Category: Standards Track
-
- Dynamic Host Configuration Protocol
-
-Status of this memo
-
- This document specifies an Internet standards track protocol for the
- Internet community, and requests discussion and suggestions for
- improvements. Please refer to the current edition of the "Internet
- Official Protocol Standards" (STD 1) for the standardization state
- and status of this protocol. Distribution of this memo is unlimited.
-
-Abstract
-
- The Dynamic Host Configuration Protocol (DHCP) provides a framework
- for passing configuration information to hosts on a TCPIP network.
- DHCP is based on the Bootstrap Protocol (BOOTP) [7], adding the
- capability of automatic allocation of reusable network addresses and
- additional configuration options [19]. DHCP captures the behavior of
- BOOTP relay agents [7, 21], and DHCP participants can interoperate
- with BOOTP participants [9].
-
-Table of Contents
-
- 1. Introduction. . . . . . . . . . . . . . . . . . . . . . . . . 2
- 1.1 Changes to RFC1541. . . . . . . . . . . . . . . . . . . . . . 3
- 1.2 Related Work. . . . . . . . . . . . . . . . . . . . . . . . . 4
- 1.3 Problem definition and issues . . . . . . . . . . . . . . . . 4
- 1.4 Requirements. . . . . . . . . . . . . . . . . . . . . . . . . 5
- 1.5 Terminology . . . . . . . . . . . . . . . . . . . . . . . . . 6
- 1.6 Design goals. . . . . . . . . . . . . . . . . . . . . . . . . 6
- 2. Protocol Summary. . . . . . . . . . . . . . . . . . . . . . . 8
- 2.1 Configuration parameters repository . . . . . . . . . . . . . 11
- 2.2 Dynamic allocation of network addresses . . . . . . . . . . . 12
- 3. The Client-Server Protocol. . . . . . . . . . . . . . . . . . 13
- 3.1 Client-server interaction - allocating a network address. . . 13
- 3.2 Client-server interaction - reusing a previously allocated
- network address . . . . . . . . . . . . . . . . . . . . . . . 17
- 3.3 Interpretation and representation of time values. . . . . . . 20
- 3.4 Obtaining parameters with externally configured network
- address . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
- 3.5 Client parameters in DHCP . . . . . . . . . . . . . . . . . . 21
- 3.6 Use of DHCP in clients with multiple interfaces . . . . . . . 22
- 3.7 When clients should use DHCP. . . . . . . . . . . . . . . . . 22
- 4. Specification of the DHCP client-server protocol. . . . . . . 22
-
-
-
-Droms Standards Track [Page 1]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
- 4.1 Constructing and sending DHCP messages. . . . . . . . . . . . 22
- 4.2 DHCP server administrative controls . . . . . . . . . . . . . 25
- 4.3 DHCP server behavior. . . . . . . . . . . . . . . . . . . . . 26
- 4.4 DHCP client behavior. . . . . . . . . . . . . . . . . . . . . 34
- 5. Acknowledgments. . . . . . . . . . . . . . . . . . . . . . . .42
- 6. References . . . . . . . . . . . . . . . . . . . . . . . . . .42
- 7. Security Considerations. . . . . . . . . . . . . . . . . . . .43
- 8. Author's Address . . . . . . . . . . . . . . . . . . . . . . .44
- A. Host Configuration Parameters . . . . . . . . . . . . . . . .45
-List of Figures
- 1. Format of a DHCP message . . . . . . . . . . . . . . . . . . . 9
- 2. Format of the 'flags' field. . . . . . . . . . . . . . . . . . 11
- 3. Timeline diagram of messages exchanged between DHCP client and
- servers when allocating a new network address. . . . . . . . . 15
- 4. Timeline diagram of messages exchanged between DHCP client and
- servers when reusing a previously allocated network address. . 18
- 5. State-transition diagram for DHCP clients. . . . . . . . . . . 34
-List of Tables
- 1. Description of fields in a DHCP message. . . . . . . . . . . . 10
- 2. DHCP messages. . . . . . . . . . . . . . . . . . . . . . . . . 14
- 3. Fields and options used by DHCP servers. . . . . . . . . . . . 28
- 4. Client messages from various states. . . . . . . . . . . . . . 33
- 5. Fields and options used by DHCP clients. . . . . . . . . . . . 37
-
-1. Introduction
-
- The Dynamic Host Configuration Protocol (DHCP) provides configuration
- parameters to Internet hosts. DHCP consists of two components: a
- protocol for delivering host-specific configuration parameters from a
- DHCP server to a host and a mechanism for allocation of network
- addresses to hosts.
-
- DHCP is built on a client-server model, where designated DHCP server
- hosts allocate network addresses and deliver configuration parameters
- to dynamically configured hosts. Throughout the remainder of this
- document, the term "server" refers to a host providing initialization
- parameters through DHCP, and the term "client" refers to a host
- requesting initialization parameters from a DHCP server.
-
- A host should not act as a DHCP server unless explicitly configured
- to do so by a system administrator. The diversity of hardware and
- protocol implementations in the Internet would preclude reliable
- operation if random hosts were allowed to respond to DHCP requests.
- For example, IP requires the setting of many parameters within the
- protocol implementation software. Because IP can be used on many
- dissimilar kinds of network hardware, values for those parameters
- cannot be guessed or assumed to have correct defaults. Also,
- distributed address allocation schemes depend on a polling/defense
-
-
-
-Droms Standards Track [Page 2]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
- mechanism for discovery of addresses that are already in use. IP
- hosts may not always be able to defend their network addresses, so
- that such a distributed address allocation scheme cannot be
- guaranteed to avoid allocation of duplicate network addresses.
-
- DHCP supports three mechanisms for IP address allocation. In
- "automatic allocation", DHCP assigns a permanent IP address to a
- client. In "dynamic allocation", DHCP assigns an IP address to a
- client for a limited period of time (or until the client explicitly
- relinquishes the address). In "manual allocation", a client's IP
- address is assigned by the network administrator, and DHCP is used
- simply to convey the assigned address to the client. A particular
- network will use one or more of these mechanisms, depending on the
- policies of the network administrator.
-
- Dynamic allocation is the only one of the three mechanisms that
- allows automatic reuse of an address that is no longer needed by the
- client to which it was assigned. Thus, dynamic allocation is
- particularly useful for assigning an address to a client that will be
- connected to the network only temporarily or for sharing a limited
- pool of IP addresses among a group of clients that do not need
- permanent IP addresses. Dynamic allocation may also be a good choice
- for assigning an IP address to a new client being permanently
- connected to a network where IP addresses are sufficiently scarce
- that it is important to reclaim them when old clients are retired.
- Manual allocation allows DHCP to be used to eliminate the error-prone
- process of manually configuring hosts with IP addresses in
- environments where (for whatever reasons) it is desirable to manage
- IP address assignment outside of the DHCP mechanisms.
-
- The format of DHCP messages is based on the format of BOOTP messages,
- to capture the BOOTP relay agent behavior described as part of the
- BOOTP specification [7, 21] and to allow interoperability of existing
- BOOTP clients with DHCP servers. Using BOOTP relay agents eliminates
- the necessity of having a DHCP server on each physical network
- segment.
-
-1.1 Changes to RFC 1541
-
- This document updates the DHCP protocol specification that appears in
- RFC1541. A new DHCP message type, DHCPINFORM, has been added; see
- section 3.4, 4.3 and 4.4 for details. The classing mechanism for
- identifying DHCP clients to DHCP servers has been extended to include
- "vendor" classes as defined in sections 4.2 and 4.3. The minimum
- lease time restriction has been removed. Finally, many editorial
- changes have been made to clarify the text as a result of experience
- gained in DHCP interoperability tests.
-
-
-
-
-Droms Standards Track [Page 3]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
-1.2 Related Work
-
- There are several Internet protocols and related mechanisms that
- address some parts of the dynamic host configuration problem. The
- Reverse Address Resolution Protocol (RARP) [10] (through the
- extensions defined in the Dynamic RARP (DRARP) [5]) explicitly
- addresses the problem of network address discovery, and includes an
- automatic IP address assignment mechanism. The Trivial File Transfer
- Protocol (TFTP) [20] provides for transport of a boot image from a
- boot server. The Internet Control Message Protocol (ICMP) [16]
- provides for informing hosts of additional routers via "ICMP
- redirect" messages. ICMP also can provide subnet mask information
- through the "ICMP mask request" message and other information through
- the (obsolete) "ICMP information request" message. Hosts can locate
- routers through the ICMP router discovery mechanism [8].
-
- BOOTP is a transport mechanism for a collection of configuration
- information. BOOTP is also extensible, and official extensions [17]
- have been defined for several configuration parameters. Morgan has
- proposed extensions to BOOTP for dynamic IP address assignment [15].
- The Network Information Protocol (NIP), used by the Athena project at
- MIT, is a distributed mechanism for dynamic IP address assignment
- [19]. The Resource Location Protocol RLP [1] provides for location
- of higher level services. Sun Microsystems diskless workstations use
- a boot procedure that employs RARP, TFTP and an RPC mechanism called
- "bootparams" to deliver configuration information and operating
- system code to diskless hosts. (Sun Microsystems, Sun Workstation
- and SunOS are trademarks of Sun Microsystems, Inc.) Some Sun
- networks also use DRARP and an auto-installation mechanism to
- automate the configuration of new hosts in an existing network.
-
- In other related work, the path minimum transmission unit (MTU)
- discovery algorithm can determine the MTU of an arbitrary internet
- path [14]. The Address Resolution Protocol (ARP) has been proposed
- as a transport protocol for resource location and selection [6].
- Finally, the Host Requirements RFCs [3, 4] mention specific
- requirements for host reconfiguration and suggest a scenario for
- initial configuration of diskless hosts.
-
-1.3 Problem definition and issues
-
- DHCP is designed to supply DHCP clients with the configuration
- parameters defined in the Host Requirements RFCs. After obtaining
- parameters via DHCP, a DHCP client should be able to exchange packets
- with any other host in the Internet. The TCP/IP stack parameters
- supplied by DHCP are listed in Appendix A.
-
-
-
-
-
-Droms Standards Track [Page 4]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
- Not all of these parameters are required for a newly initialized
- client. A client and server may negotiate for the transmission of
- only those parameters required by the client or specific to a
- particular subnet.
-
- DHCP allows but does not require the configuration of client
- parameters not directly related to the IP protocol. DHCP also does
- not address registration of newly configured clients with the Domain
- Name System (DNS) [12, 13].
-
- DHCP is not intended for use in configuring routers.
-
-1.4 Requirements
-
- Throughout this document, the words that are used to define the
- significance of particular requirements are capitalized. These words
- are:
-
- o "MUST"
-
- This word or the adjective "REQUIRED" means that the
- item is an absolute requirement of this specification.
-
- o "MUST NOT"
-
- This phrase means that the item is an absolute prohibition
- of this specification.
-
- o "SHOULD"
-
- This word or the adjective "RECOMMENDED" means that there
- may exist valid reasons in particular circumstances to ignore
- this item, but the full implications should be understood and
- the case carefully weighed before choosing a different course.
-
- o "SHOULD NOT"
-
- This phrase means that there may exist valid reasons in
- particular circumstances when the listed behavior is acceptable
- or even useful, but the full implications should be understood
- and the case carefully weighed before implementing any behavior
- described with this label.
-
-
-
-
-
-
-
-
-
-Droms Standards Track [Page 5]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
- o "MAY"
-
- This word or the adjective "OPTIONAL" means that this item is
- truly optional. One vendor may choose to include the item
- because a particular marketplace requires it or because it
- enhances the product, for example; another vendor may omit the
- same item.
-
-1.5 Terminology
-
- This document uses the following terms:
-
- o "DHCP client"
-
- A DHCP client is an Internet host using DHCP to obtain
- configuration parameters such as a network address.
-
- o "DHCP server"
-
- A DHCP server is an Internet host that returns configuration
- parameters to DHCP clients.
-
- o "BOOTP relay agent"
-
- A BOOTP relay agent or relay agent is an Internet host or router
- that passes DHCP messages between DHCP clients and DHCP servers.
- DHCP is designed to use the same relay agent behavior as specified
- in the BOOTP protocol specification.
-
- o "binding"
-
- A binding is a collection of configuration parameters, including
- at least an IP address, associated with or "bound to" a DHCP
- client. Bindings are managed by DHCP servers.
-
-1.6 Design goals
-
- The following list gives general design goals for DHCP.
-
- o DHCP should be a mechanism rather than a policy. DHCP must
- allow local system administrators control over configuration
- parameters where desired; e.g., local system administrators
- should be able to enforce local policies concerning allocation
- and access to local resources where desired.
-
-
-
-
-
-
-
-Droms Standards Track [Page 6]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
- o Clients should require no manual configuration. Each client
- should be able to discover appropriate local configuration
- parameters without user intervention and incorporate those
- parameters into its own configuration.
-
- o Networks should require no manual configuration for individual
- clients. Under normal circumstances, the network manager
- should not have to enter any per-client configuration
- parameters.
-
- o DHCP should not require a server on each subnet. To allow for
- scale and economy, DHCP must work across routers or through the
- intervention of BOOTP relay agents.
-
- o A DHCP client must be prepared to receive multiple responses
- to a request for configuration parameters. Some installations
- may include multiple, overlapping DHCP servers to enhance
- reliability and increase performance.
-
- o DHCP must coexist with statically configured, non-participating
- hosts and with existing network protocol implementations.
-
- o DHCP must interoperate with the BOOTP relay agent behavior as
- described by RFC 951 and by RFC 1542 [21].
-
- o DHCP must provide service to existing BOOTP clients.
-
- The following list gives design goals specific to the transmission of
- the network layer parameters. DHCP must:
-
- o Guarantee that any specific network address will not be in
- use by more than one DHCP client at a time,
-
- o Retain DHCP client configuration across DHCP client reboot. A
- DHCP client should, whenever possible, be assigned the same
- configuration parameters (e.g., network address) in response
- to each request,
-
- o Retain DHCP client configuration across server reboots, and,
- whenever possible, a DHCP client should be assigned the same
- configuration parameters despite restarts of the DHCP mechanism,
-
- o Allow automated assignment of configuration parameters to new
- clients to avoid hand configuration for new clients,
-
- o Support fixed or permanent allocation of configuration
- parameters to specific clients.
-
-
-
-
-Droms Standards Track [Page 7]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
-2. Protocol Summary
-
- From the client's point of view, DHCP is an extension of the BOOTP
- mechanism. This behavior allows existing BOOTP clients to
- interoperate with DHCP servers without requiring any change to the
- clients' initialization software. RFC 1542 [2] details the
- interactions between BOOTP and DHCP clients and servers [9]. There
- are some new, optional transactions that optimize the interaction
- between DHCP clients and servers that are described in sections 3 and
- 4.
-
- Figure 1 gives the format of a DHCP message and table 1 describes
- each of the fields in the DHCP message. The numbers in parentheses
- indicate the size of each field in octets. The names for the fields
- given in the figure will be used throughout this document to refer to
- the fields in DHCP messages.
-
- There are two primary differences between DHCP and BOOTP. First,
- DHCP defines mechanisms through which clients can be assigned a
- network address for a finite lease, allowing for serial reassignment
- of network addresses to different clients. Second, DHCP provides the
- mechanism for a client to acquire all of the IP configuration
- parameters that it needs in order to operate.
-
- DHCP introduces a small change in terminology intended to clarify the
- meaning of one of the fields. What was the "vendor extensions" field
- in BOOTP has been re-named the "options" field in DHCP. Similarly,
- the tagged data items that were used inside the BOOTP "vendor
- extensions" field, which were formerly referred to as "vendor
- extensions," are now termed simply "options."
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Droms Standards Track [Page 8]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | op (1) | htype (1) | hlen (1) | hops (1) |
- +---------------+---------------+---------------+---------------+
- | xid (4) |
- +-------------------------------+-------------------------------+
- | secs (2) | flags (2) |
- +-------------------------------+-------------------------------+
- | ciaddr (4) |
- +---------------------------------------------------------------+
- | yiaddr (4) |
- +---------------------------------------------------------------+
- | siaddr (4) |
- +---------------------------------------------------------------+
- | giaddr (4) |
- +---------------------------------------------------------------+
- | |
- | chaddr (16) |
- | |
- | |
- +---------------------------------------------------------------+
- | |
- | sname (64) |
- +---------------------------------------------------------------+
- | |
- | file (128) |
- +---------------------------------------------------------------+
- | |
- | options (variable) |
- +---------------------------------------------------------------+
-
- Figure 1: Format of a DHCP message
-
- DHCP defines a new 'client identifier' option that is used to pass an
- explicit client identifier to a DHCP server. This change eliminates
- the overloading of the 'chaddr' field in BOOTP messages, where
- 'chaddr' is used both as a hardware address for transmission of BOOTP
- reply messages and as a client identifier. The 'client identifier'
- is an opaque key, not to be interpreted by the server; for example,
- the 'client identifier' may contain a hardware address, identical to
- the contents of the 'chaddr' field, or it may contain another type of
- identifier, such as a DNS name. The 'client identifier' chosen by a
- DHCP client MUST be unique to that client within the subnet to which
- the client is attached. If the client uses a 'client identifier' in
- one message, it MUST use that same identifier in all subsequent
- messages, to ensure that all servers correctly identify the client.
-
-
-
-
-Droms Standards Track [Page 9]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
- DHCP clarifies the interpretation of the 'siaddr' field as the
- address of the server to use in the next step of the client's
- bootstrap process. A DHCP server may return its own address in the
- 'siaddr' field, if the server is prepared to supply the next
- bootstrap service (e.g., delivery of an operating system executable
- image). A DHCP server always returns its own address in the 'server
- identifier' option.
-
- FIELD OCTETS DESCRIPTION
- ----- ------ -----------
-
- op 1 Message op code / message type.
- 1 = BOOTREQUEST, 2 = BOOTREPLY
- htype 1 Hardware address type, see ARP section in "Assigned
- Numbers" RFC; e.g., '1' = 10mb ethernet.
- hlen 1 Hardware address length (e.g. '6' for 10mb
- ethernet).
- hops 1 Client sets to zero, optionally used by relay agents
- when booting via a relay agent.
- xid 4 Transaction ID, a random number chosen by the
- client, used by the client and server to associate
- messages and responses between a client and a
- server.
- secs 2 Filled in by client, seconds elapsed since client
- began address acquisition or renewal process.
- flags 2 Flags (see figure 2).
- ciaddr 4 Client IP address; only filled in if client is in
- BOUND, RENEW or REBINDING state and can respond
- to ARP requests.
- yiaddr 4 'your' (client) IP address.
- siaddr 4 IP address of next server to use in bootstrap;
- returned in DHCPOFFER, DHCPACK by server.
- giaddr 4 Relay agent IP address, used in booting via a
- relay agent.
- chaddr 16 Client hardware address.
- sname 64 Optional server host name, null terminated string.
- file 128 Boot file name, null terminated string; "generic"
- name or null in DHCPDISCOVER, fully qualified
- directory-path name in DHCPOFFER.
- options var Optional parameters field. See the options
- documents for a list of defined options.
-
- Table 1: Description of fields in a DHCP message
-
- The 'options' field is now variable length. A DHCP client must be
- prepared to receive DHCP messages with an 'options' field of at least
- length 312 octets. This requirement implies that a DHCP client must
- be prepared to receive a message of up to 576 octets, the minimum IP
-
-
-
-Droms Standards Track [Page 10]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
- datagram size an IP host must be prepared to accept [3]. DHCP
- clients may negotiate the use of larger DHCP messages through the
- 'maximum DHCP message size' option. The options field may be further
- extended into the 'file' and 'sname' fields.
-
- In the case of a client using DHCP for initial configuration (before
- the client's TCP/IP software has been completely configured), DHCP
- requires creative use of the client's TCP/IP software and liberal
- interpretation of RFC 1122. The TCP/IP software SHOULD accept and
- forward to the IP layer any IP packets delivered to the client's
- hardware address before the IP address is configured; DHCP servers
- and BOOTP relay agents may not be able to deliver DHCP messages to
- clients that cannot accept hardware unicast datagrams before the
- TCP/IP software is configured.
-
- To work around some clients that cannot accept IP unicast datagrams
- before the TCP/IP software is configured as discussed in the previous
- paragraph, DHCP uses the 'flags' field [21]. The leftmost bit is
- defined as the BROADCAST (B) flag. The semantics of this flag are
- discussed in section 4.1 of this document. The remaining bits of the
- flags field are reserved for future use. They MUST be set to zero by
- clients and ignored by servers and relay agents. Figure 2 gives the
- format of the 'flags' field.
-
- 1 1 1 1 1 1
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- |B| MBZ |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
- B: BROADCAST flag
-
- MBZ: MUST BE ZERO (reserved for future use)
-
- Figure 2: Format of the 'flags' field
-
-2.1 Configuration parameters repository
-
- The first service provided by DHCP is to provide persistent storage
- of network parameters for network clients. The model of DHCP
- persistent storage is that the DHCP service stores a key-value entry
- for each client, where the key is some unique identifier (for
- example, an IP subnet number and a unique identifier within the
- subnet) and the value contains the configuration parameters for the
- client.
-
- For example, the key might be the pair (IP-subnet-number, hardware-
- address) (note that the "hardware-address" should be typed by the
-
-
-
-Droms Standards Track [Page 11]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
- type of hardware to accommodate possible duplication of hardware
- addresses resulting from bit-ordering problems in a mixed-media,
- bridged network) allowing for serial or concurrent reuse of a
- hardware address on different subnets, and for hardware addresses
- that may not be globally unique. Alternately, the key might be the
- pair (IP-subnet-number, hostname), allowing the server to assign
- parameters intelligently to a DHCP client that has been moved to a
- different subnet or has changed hardware addresses (perhaps because
- the network interface failed and was replaced). The protocol defines
- that the key will be (IP-subnet-number, hardware-address) unless the
- client explicitly supplies an identifier using the 'client
- identifier' option. A client can query the DHCP service to
- retrieve its configuration parameters. The client interface to the
- configuration parameters repository consists of protocol messages to
- request configuration parameters and responses from the server
- carrying the configuration parameters.
-
-2.2 Dynamic allocation of network addresses
-
- The second service provided by DHCP is the allocation of temporary or
- permanent network (IP) addresses to clients. The basic mechanism for
- the dynamic allocation of network addresses is simple: a client
- requests the use of an address for some period of time. The
- allocation mechanism (the collection of DHCP servers) guarantees not
- to reallocate that address within the requested time and attempts to
- return the same network address each time the client requests an
- address. In this document, the period over which a network address
- is allocated to a client is referred to as a "lease" [11]. The
- client may extend its lease with subsequent requests. The client may
- issue a message to release the address back to the server when the
- client no longer needs the address. The client may ask for a
- permanent assignment by asking for an infinite lease. Even when
- assigning "permanent" addresses, a server may choose to give out
- lengthy but non-infinite leases to allow detection of the fact that
- the client has been retired.
-
- In some environments it will be necessary to reassign network
- addresses due to exhaustion of available addresses. In such
- environments, the allocation mechanism will reuse addresses whose
- lease has expired. The server should use whatever information is
- available in the configuration information repository to choose an
- address to reuse. For example, the server may choose the least
- recently assigned address. As a consistency check, the allocating
- server SHOULD probe the reused address before allocating the address,
- e.g., with an ICMP echo request, and the client SHOULD probe the
- newly received address, e.g., with ARP.
-
-
-
-
-
-Droms Standards Track [Page 12]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
-3. The Client-Server Protocol
-
- DHCP uses the BOOTP message format defined in RFC 951 and given in
- table 1 and figure 1. The 'op' field of each DHCP message sent from
- a client to a server contains BOOTREQUEST. BOOTREPLY is used in the
- 'op' field of each DHCP message sent from a server to a client.
-
- The first four octets of the 'options' field of the DHCP message
- contain the (decimal) values 99, 130, 83 and 99, respectively (this
- is the same magic cookie as is defined in RFC 1497 [17]). The
- remainder of the 'options' field consists of a list of tagged
- parameters that are called "options". All of the "vendor extensions"
- listed in RFC 1497 are also DHCP options. RFC 1533 gives the
- complete set of options defined for use with DHCP.
-
- Several options have been defined so far. One particular option -
- the "DHCP message type" option - must be included in every DHCP
- message. This option defines the "type" of the DHCP message.
- Additional options may be allowed, required, or not allowed,
- depending on the DHCP message type.
-
- Throughout this document, DHCP messages that include a 'DHCP message
- type' option will be referred to by the type of the message; e.g., a
- DHCP message with 'DHCP message type' option type 1 will be referred
- to as a "DHCPDISCOVER" message.
-
-3.1 Client-server interaction - allocating a network address
-
- The following summary of the protocol exchanges between clients and
- servers refers to the DHCP messages described in table 2. The
- timeline diagram in figure 3 shows the timing relationships in a
- typical client-server interaction. If the client already knows its
- address, some steps may be omitted; this abbreviated interaction is
- described in section 3.2.
-
- 1. The client broadcasts a DHCPDISCOVER message on its local physical
- subnet. The DHCPDISCOVER message MAY include options that suggest
- values for the network address and lease duration. BOOTP relay
- agents may pass the message on to DHCP servers not on the same
- physical subnet.
-
- 2. Each server may respond with a DHCPOFFER message that includes an
- available network address in the 'yiaddr' field (and other
- configuration parameters in DHCP options). Servers need not
- reserve the offered network address, although the protocol will
- work more efficiently if the server avoids allocating the offered
- network address to another client. When allocating a new address,
- servers SHOULD check that the offered network address is not
-
-
-
-Droms Standards Track [Page 13]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
- already in use; e.g., the server may probe the offered address
- with an ICMP Echo Request. Servers SHOULD be implemented so that
- network administrators MAY choose to disable probes of newly
- allocated addresses. The server transmits the DHCPOFFER message
- to the client, using the BOOTP relay agent if necessary.
-
- Message Use
- ------- ---
-
- DHCPDISCOVER - Client broadcast to locate available servers.
-
- DHCPOFFER - Server to client in response to DHCPDISCOVER with
- offer of configuration parameters.
-
- DHCPREQUEST - Client message to servers either (a) requesting
- offered parameters from one server and implicitly
- declining offers from all others, (b) confirming
- correctness of previously allocated address after,
- e.g., system reboot, or (c) extending the lease on a
- particular network address.
-
- DHCPACK - Server to client with configuration parameters,
- including committed network address.
-
- DHCPNAK - Server to client indicating client's notion of network
- address is incorrect (e.g., client has moved to new
- subnet) or client's lease as expired
-
- DHCPDECLINE - Client to server indicating network address is already
- in use.
-
- DHCPRELEASE - Client to server relinquishing network address and
- cancelling remaining lease.
-
- DHCPINFORM - Client to server, asking only for local configuration
- parameters; client already has externally configured
- network address.
-
- Table 2: DHCP messages
-
-
-
-
-
-
-
-
-
-
-
-
-Droms Standards Track [Page 14]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
- Server Client Server
- (not selected) (selected)
-
- v v v
- | | |
- | Begins initialization |
- | | |
- | _____________/|\____________ |
- |/DHCPDISCOVER | DHCPDISCOVER \|
- | | |
- Determines | Determines
- configuration | configuration
- | | |
- |\ | ____________/ |
- | \________ | /DHCPOFFER |
- | DHCPOFFER\ |/ |
- | \ | |
- | Collects replies |
- | \| |
- | Selects configuration |
- | | |
- | _____________/|\____________ |
- |/ DHCPREQUEST | DHCPREQUEST\ |
- | | |
- | | Commits configuration
- | | |
- | | _____________/|
- | |/ DHCPACK |
- | | |
- | Initialization complete |
- | | |
- . . .
- . . .
- | | |
- | Graceful shutdown |
- | | |
- | |\ ____________ |
- | | DHCPRELEASE \|
- | | |
- | | Discards lease
- | | |
- v v v
- Figure 3: Timeline diagram of messages exchanged between DHCP
- client and servers when allocating a new network address
-
-
-
-
-
-
-
-Droms Standards Track [Page 15]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
- 3. The client receives one or more DHCPOFFER messages from one or more
- servers. The client may choose to wait for multiple responses.
- The client chooses one server from which to request configuration
- parameters, based on the configuration parameters offered in the
- DHCPOFFER messages. The client broadcasts a DHCPREQUEST message
- that MUST include the 'server identifier' option to indicate which
- server it has selected, and that MAY include other options
- specifying desired configuration values. The 'requested IP
- address' option MUST be set to the value of 'yiaddr' in the
- DHCPOFFER message from the server. This DHCPREQUEST message is
- broadcast and relayed through DHCP/BOOTP relay agents. To help
- ensure that any BOOTP relay agents forward the DHCPREQUEST message
- to the same set of DHCP servers that received the original
- DHCPDISCOVER message, the DHCPREQUEST message MUST use the same
- value in the DHCP message header's 'secs' field and be sent to the
- same IP broadcast address as the original DHCPDISCOVER message.
- The client times out and retransmits the DHCPDISCOVER message if
- the client receives no DHCPOFFER messages.
-
- 4. The servers receive the DHCPREQUEST broadcast from the client.
- Those servers not selected by the DHCPREQUEST message use the
- message as notification that the client has declined that server's
- offer. The server selected in the DHCPREQUEST message commits the
- binding for the client to persistent storage and responds with a
- DHCPACK message containing the configuration parameters for the
- requesting client. The combination of 'client identifier' or
- 'chaddr' and assigned network address constitute a unique
- identifier for the client's lease and are used by both the client
- and server to identify a lease referred to in any DHCP messages.
- Any configuration parameters in the DHCPACK message SHOULD NOT
- conflict with those in the earlier DHCPOFFER message to which the
- client is responding. The server SHOULD NOT check the offered
- network address at this point. The 'yiaddr' field in the DHCPACK
- messages is filled in with the selected network address.
-
- If the selected server is unable to satisfy the DHCPREQUEST message
- (e.g., the requested network address has been allocated), the
- server SHOULD respond with a DHCPNAK message.
-
- A server MAY choose to mark addresses offered to clients in
- DHCPOFFER messages as unavailable. The server SHOULD mark an
- address offered to a client in a DHCPOFFER message as available if
- the server receives no DHCPREQUEST message from that client.
-
- 5. The client receives the DHCPACK message with configuration
- parameters. The client SHOULD perform a final check on the
- parameters (e.g., ARP for allocated network address), and notes the
- duration of the lease specified in the DHCPACK message. At this
-
-
-
-Droms Standards Track [Page 16]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
- point, the client is configured. If the client detects that the
- address is already in use (e.g., through the use of ARP), the
- client MUST send a DHCPDECLINE message to the server and restarts
- the configuration process. The client SHOULD wait a minimum of ten
- seconds before restarting the configuration process to avoid
- excessive network traffic in case of looping.
-
- If the client receives a DHCPNAK message, the client restarts the
- configuration process.
-
- The client times out and retransmits the DHCPREQUEST message if the
- client receives neither a DHCPACK or a DHCPNAK message. The client
- retransmits the DHCPREQUEST according to the retransmission
- algorithm in section 4.1. The client should choose to retransmit
- the DHCPREQUEST enough times to give adequate probability of
- contacting the server without causing the client (and the user of
- that client) to wait overly long before giving up; e.g., a client
- retransmitting as described in section 4.1 might retransmit the
- DHCPREQUEST message four times, for a total delay of 60 seconds,
- before restarting the initialization procedure. If the client
- receives neither a DHCPACK or a DHCPNAK message after employing the
- retransmission algorithm, the client reverts to INIT state and
- restarts the initialization process. The client SHOULD notify the
- user that the initialization process has failed and is restarting.
-
- 6. The client may choose to relinquish its lease on a network address
- by sending a DHCPRELEASE message to the server. The client
- identifies the lease to be released with its 'client identifier',
- or 'chaddr' and network address in the DHCPRELEASE message. If the
- client used a 'client identifier' when it obtained the lease, it
- MUST use the same 'client identifier' in the DHCPRELEASE message.
-
-3.2 Client-server interaction - reusing a previously allocated network
- address
-
- If a client remembers and wishes to reuse a previously allocated
- network address, a client may choose to omit some of the steps
- described in the previous section. The timeline diagram in figure 4
- shows the timing relationships in a typical client-server interaction
- for a client reusing a previously allocated network address.
-
-
-
-
-
-
-
-
-
-
-
-Droms Standards Track [Page 17]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
- 1. The client broadcasts a DHCPREQUEST message on its local subnet.
- The message includes the client's network address in the
- 'requested IP address' option. As the client has not received its
- network address, it MUST NOT fill in the 'ciaddr' field. BOOTP
- relay agents pass the message on to DHCP servers not on the same
- subnet. If the client used a 'client identifier' to obtain its
- address, the client MUST use the same 'client identifier' in the
- DHCPREQUEST message.
-
- 2. Servers with knowledge of the client's configuration parameters
- respond with a DHCPACK message to the client. Servers SHOULD NOT
- check that the client's network address is already in use; the
- client may respond to ICMP Echo Request messages at this point.
-
- Server Client Server
-
- v v v
- | | |
- | Begins |
- | initialization |
- | | |
- | /|\ |
- | _________ __/ | \__________ |
- | /DHCPREQU EST | DHCPREQUEST\ |
- |/ | \|
- | | |
- Locates | Locates
- configuration | configuration
- | | |
- |\ | /|
- | \ | ___________/ |
- | \ | / DHCPACK |
- | \ _______ |/ |
- | DHCPACK\ | |
- | Initialization |
- | complete |
- | \| |
- | | |
- | (Subsequent |
- | DHCPACKS |
- | ignored) |
- | | |
- | | |
- v v v
-
- Figure 4: Timeline diagram of messages exchanged between DHCP
- client and servers when reusing a previously allocated
- network address
-
-
-
-Droms Standards Track [Page 18]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
- If the client's request is invalid (e.g., the client has moved
- to a new subnet), servers SHOULD respond with a DHCPNAK message to
- the client. Servers SHOULD NOT respond if their information is not
- guaranteed to be accurate. For example, a server that identifies a
- request for an expired binding that is owned by another server SHOULD
- NOT respond with a DHCPNAK unless the servers are using an explicit
- mechanism to maintain coherency among the servers.
-
- If 'giaddr' is 0x0 in the DHCPREQUEST message, the client is on
- the same subnet as the server. The server MUST
- broadcast the DHCPNAK message to the 0xffffffff broadcast address
- because the client may not have a correct network address or subnet
- mask, and the client may not be answering ARP requests.
- Otherwise, the server MUST send the DHCPNAK message to the IP
- address of the BOOTP relay agent, as recorded in 'giaddr'. The
- relay agent will, in turn, forward the message directly to the
- client's hardware address, so that the DHCPNAK can be delivered even
- if the client has moved to a new network.
-
- 3. The client receives the DHCPACK message with configuration
- parameters. The client performs a final check on the parameters
- (as in section 3.1), and notes the duration of the lease specified
- in the DHCPACK message. The specific lease is implicitly identified
- by the 'client identifier' or 'chaddr' and the network address. At
- this point, the client is configured.
-
- If the client detects that the IP address in the DHCPACK message
- is already in use, the client MUST send a DHCPDECLINE message to the
- server and restarts the configuration process by requesting a
- new network address. This action corresponds to the client
- moving to the INIT state in the DHCP state diagram, which is
- described in section 4.4.
-
- If the client receives a DHCPNAK message, it cannot reuse its
- remembered network address. It must instead request a new
- address by restarting the configuration process, this time
- using the (non-abbreviated) procedure described in section
- 3.1. This action also corresponds to the client moving to
- the INIT state in the DHCP state diagram.
-
- The client times out and retransmits the DHCPREQUEST message if
- the client receives neither a DHCPACK nor a DHCPNAK message. The
- client retransmits the DHCPREQUEST according to the retransmission
- algorithm in section 4.1. The client should choose to retransmit
- the DHCPREQUEST enough times to give adequate probability of
- contacting the server without causing the client (and the user of
- that client) to wait overly long before giving up; e.g., a client
- retransmitting as described in section 4.1 might retransmit the
-
-
-
-Droms Standards Track [Page 19]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
- DHCPREQUEST message four times, for a total delay of 60 seconds,
- before restarting the initialization procedure. If the client
- receives neither a DHCPACK or a DHCPNAK message after employing
- the retransmission algorithm, the client MAY choose to use the
- previously allocated network address and configuration parameters
- for the remainder of the unexpired lease. This corresponds to
- moving to BOUND state in the client state transition diagram shown
- in figure 5.
-
- 4. The client may choose to relinquish its lease on a network
- address by sending a DHCPRELEASE message to the server. The
- client identifies the lease to be released with its
- 'client identifier', or 'chaddr' and network address in the
- DHCPRELEASE message.
-
- Note that in this case, where the client retains its network
- address locally, the client will not normally relinquish its
- lease during a graceful shutdown. Only in the case where the
- client explicitly needs to relinquish its lease, e.g., the client
- is about to be moved to a different subnet, will the client send
- a DHCPRELEASE message.
-
-3.3 Interpretation and representation of time values
-
- A client acquires a lease for a network address for a fixed period of
- time (which may be infinite). Throughout the protocol, times are to
- be represented in units of seconds. The time value of 0xffffffff is
- reserved to represent "infinity".
-
- As clients and servers may not have synchronized clocks, times are
- represented in DHCP messages as relative times, to be interpreted
- with respect to the client's local clock. Representing relative
- times in units of seconds in an unsigned 32 bit word gives a range of
- relative times from 0 to approximately 100 years, which is sufficient
- for the relative times to be measured using DHCP.
-
- The algorithm for lease duration interpretation given in the previous
- paragraph assumes that client and server clocks are stable relative
- to each other. If there is drift between the two clocks, the server
- may consider the lease expired before the client does. To
- compensate, the server may return a shorter lease duration to the
- client than the server commits to its local database of client
- information.
-
-3.4 Obtaining parameters with externally configured network address
-
- If a client has obtained a network address through some other means
- (e.g., manual configuration), it may use a DHCPINFORM request message
-
-
-
-Droms Standards Track [Page 20]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
- to obtain other local configuration parameters. Servers receiving a
- DHCPINFORM message construct a DHCPACK message with any local
- configuration parameters appropriate for the client without:
- allocating a new address, checking for an existing binding, filling
- in 'yiaddr' or including lease time parameters. The servers SHOULD
- unicast the DHCPACK reply to the address given in the 'ciaddr' field
- of the DHCPINFORM message.
-
- The server SHOULD check the network address in a DHCPINFORM message
- for consistency, but MUST NOT check for an existing lease. The
- server forms a DHCPACK message containing the configuration
- parameters for the requesting client and sends the DHCPACK message
- directly to the client.
-
-3.5 Client parameters in DHCP
-
- Not all clients require initialization of all parameters listed in
- Appendix A. Two techniques are used to reduce the number of
- parameters transmitted from the server to the client. First, most of
- the parameters have defaults defined in the Host Requirements RFCs;
- if the client receives no parameters from the server that override
- the defaults, a client uses those default values. Second, in its
- initial DHCPDISCOVER or DHCPREQUEST message, a client may provide the
- server with a list of specific parameters the client is interested
- in. If the client includes a list of parameters in a DHCPDISCOVER
- message, it MUST include that list in any subsequent DHCPREQUEST
- messages.
-
- The client SHOULD include the 'maximum DHCP message size' option to
- let the server know how large the server may make its DHCP messages.
- The parameters returned to a client may still exceed the space
- allocated to options in a DHCP message. In this case, two additional
- options flags (which must appear in the 'options' field of the
- message) indicate that the 'file' and 'sname' fields are to be used
- for options.
-
- The client can inform the server which configuration parameters the
- client is interested in by including the 'parameter request list'
- option. The data portion of this option explicitly lists the options
- requested by tag number.
-
- In addition, the client may suggest values for the network address
- and lease time in the DHCPDISCOVER message. The client may include
- the 'requested IP address' option to suggest that a particular IP
- address be assigned, and may include the 'IP address lease time'
- option to suggest the lease time it would like. Other options
- representing "hints" at configuration parameters are allowed in a
- DHCPDISCOVER or DHCPREQUEST message. However, additional options may
-
-
-
-Droms Standards Track [Page 21]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
- be ignored by servers, and multiple servers may, therefore, not
- return identical values for some options. The 'requested IP address'
- option is to be filled in only in a DHCPREQUEST message when the
- client is verifying network parameters obtained previously. The
- client fills in the 'ciaddr' field only when correctly configured
- with an IP address in BOUND, RENEWING or REBINDING state.
-
- If a server receives a DHCPREQUEST message with an invalid 'requested
- IP address', the server SHOULD respond to the client with a DHCPNAK
- message and may choose to report the problem to the system
- administrator. The server may include an error message in the
- 'message' option.
-
-3.6 Use of DHCP in clients with multiple interfaces
-
- A client with multiple network interfaces must use DHCP through each
- interface independently to obtain configuration information
- parameters for those separate interfaces.
-
-3.7 When clients should use DHCP
-
- A client SHOULD use DHCP to reacquire or verify its IP address and
- network parameters whenever the local network parameters may have
- changed; e.g., at system boot time or after a disconnection from the
- local network, as the local network configuration may change without
- the client's or user's knowledge.
-
- If a client has knowledge of a previous network address and is unable
- to contact a local DHCP server, the client may continue to use the
- previous network address until the lease for that address expires.
- If the lease expires before the client can contact a DHCP server, the
- client must immediately discontinue use of the previous network
- address and may inform local users of the problem.
-
-4. Specification of the DHCP client-server protocol
-
- In this section, we assume that a DHCP server has a block of network
- addresses from which it can satisfy requests for new addresses. Each
- server also maintains a database of allocated addresses and leases in
- local permanent storage.
-
-4.1 Constructing and sending DHCP messages
-
- DHCP clients and servers both construct DHCP messages by filling in
- fields in the fixed format section of the message and appending
- tagged data items in the variable length option area. The options
- area includes first a four-octet 'magic cookie' (which was described
- in section 3), followed by the options. The last option must always
-
-
-
-Droms Standards Track [Page 22]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
- be the 'end' option.
-
- DHCP uses UDP as its transport protocol. DHCP messages from a client
- to a server are sent to the 'DHCP server' port (67), and DHCP
- messages from a server to a client are sent to the 'DHCP client' port
- (68). A server with multiple network address (e.g., a multi-homed
- host) MAY use any of its network addresses in outgoing DHCP messages.
-
- The 'server identifier' field is used both to identify a DHCP server
- in a DHCP message and as a destination address from clients to
- servers. A server with multiple network addresses MUST be prepared
- to to accept any of its network addresses as identifying that server
- in a DHCP message. To accommodate potentially incomplete network
- connectivity, a server MUST choose an address as a 'server
- identifier' that, to the best of the server's knowledge, is reachable
- from the client. For example, if the DHCP server and the DHCP client
- are connected to the same subnet (i.e., the 'giaddr' field in the
- message from the client is zero), the server SHOULD select the IP
- address the server is using for communication on that subnet as the
- 'server identifier'. If the server is using multiple IP addresses on
- that subnet, any such address may be used. If the server has
- received a message through a DHCP relay agent, the server SHOULD
- choose an address from the interface on which the message was
- recieved as the 'server identifier' (unless the server has other,
- better information on which to make its choice). DHCP clients MUST
- use the IP address provided in the 'server identifier' option for any
- unicast requests to the DHCP server.
-
- DHCP messages broadcast by a client prior to that client obtaining
- its IP address must have the source address field in the IP header
- set to 0.
-
- If the 'giaddr' field in a DHCP message from a client is non-zero,
- the server sends any return messages to the 'DHCP server' port on the
- BOOTP relay agent whose address appears in 'giaddr'. If the 'giaddr'
- field is zero and the 'ciaddr' field is nonzero, then the server
- unicasts DHCPOFFER and DHCPACK messages to the address in 'ciaddr'.
- If 'giaddr' is zero and 'ciaddr' is zero, and the broadcast bit is
- set, then the server broadcasts DHCPOFFER and DHCPACK messages to
- 0xffffffff. If the broadcast bit is not set and 'giaddr' is zero and
- 'ciaddr' is zero, then the server unicasts DHCPOFFER and DHCPACK
- messages to the client's hardware address and 'yiaddr' address. In
- all cases, when 'giaddr' is zero, the server broadcasts any DHCPNAK
- messages to 0xffffffff.
-
- If the options in a DHCP message extend into the 'sname' and 'file'
- fields, the 'option overload' option MUST appear in the 'options'
- field, with value 1, 2 or 3, as specified in RFC 1533. If the
-
-
-
-Droms Standards Track [Page 23]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
- 'option overload' option is present in the 'options' field, the
- options in the 'options' field MUST be terminated by an 'end' option,
- and MAY contain one or more 'pad' options to fill the options field.
- The options in the 'sname' and 'file' fields (if in use as indicated
- by the 'options overload' option) MUST begin with the first octet of
- the field, MUST be terminated by an 'end' option, and MUST be
- followed by 'pad' options to fill the remainder of the field. Any
- individual option in the 'options', 'sname' and 'file' fields MUST be
- entirely contained in that field. The options in the 'options' field
- MUST be interpreted first, so that any 'option overload' options may
- be interpreted. The 'file' field MUST be interpreted next (if the
- 'option overload' option indicates that the 'file' field contains
- DHCP options), followed by the 'sname' field.
-
- The values to be passed in an 'option' tag may be too long to fit in
- the 255 octets available to a single option (e.g., a list of routers
- in a 'router' option [21]). Options may appear only once, unless
- otherwise specified in the options document. The client concatenates
- the values of multiple instances of the same option into a single
- parameter list for configuration.
-
- DHCP clients are responsible for all message retransmission. The
- client MUST adopt a retransmission strategy that incorporates a
- randomized exponential backoff algorithm to determine the delay
- between retransmissions. The delay between retransmissions SHOULD be
- chosen to allow sufficient time for replies from the server to be
- delivered based on the characteristics of the internetwork between
- the client and the server. For example, in a 10Mb/sec Ethernet
- internetwork, the delay before the first retransmission SHOULD be 4
- seconds randomized by the value of a uniform random number chosen
- from the range -1 to +1. Clients with clocks that provide resolution
- granularity of less than one second may choose a non-integer
- randomization value. The delay before the next retransmission SHOULD
- be 8 seconds randomized by the value of a uniform number chosen from
- the range -1 to +1. The retransmission delay SHOULD be doubled with
- subsequent retransmissions up to a maximum of 64 seconds. The client
- MAY provide an indication of retransmission attempts to the user as
- an indication of the progress of the configuration process.
-
- The 'xid' field is used by the client to match incoming DHCP messages
- with pending requests. A DHCP client MUST choose 'xid's in such a
- way as to minimize the chance of using an 'xid' identical to one used
- by another client. For example, a client may choose a different,
- random initial 'xid' each time the client is rebooted, and
- subsequently use sequential 'xid's until the next reboot. Selecting
- a new 'xid' for each retransmission is an implementation decision. A
- client may choose to reuse the same 'xid' or select a new 'xid' for
- each retransmitted message.
-
-
-
-Droms Standards Track [Page 24]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
- Normally, DHCP servers and BOOTP relay agents attempt to deliver
- DHCPOFFER, DHCPACK and DHCPNAK messages directly to the client using
- uicast delivery. The IP destination address (in the IP header) is
- set to the DHCP 'yiaddr' address and the link-layer destination
- address is set to the DHCP 'chaddr' address. Unfortunately, some
- client implementations are unable to receive such unicast IP
- datagrams until the implementation has been configured with a valid
- IP address (leading to a deadlock in which the client's IP address
- cannot be delivered until the client has been configured with an IP
- address).
-
- A client that cannot receive unicast IP datagrams until its protocol
- software has been configured with an IP address SHOULD set the
- BROADCAST bit in the 'flags' field to 1 in any DHCPDISCOVER or
- DHCPREQUEST messages that client sends. The BROADCAST bit will
- provide a hint to the DHCP server and BOOTP relay agent to broadcast
- any messages to the client on the client's subnet. A client that can
- receive unicast IP datagrams before its protocol software has been
- configured SHOULD clear the BROADCAST bit to 0. The BOOTP
- clarifications document discusses the ramifications of the use of the
- BROADCAST bit [21].
-
- A server or relay agent sending or relaying a DHCP message directly
- to a DHCP client (i.e., not to a relay agent specified in the
- 'giaddr' field) SHOULD examine the BROADCAST bit in the 'flags'
- field. If this bit is set to 1, the DHCP message SHOULD be sent as
- an IP broadcast using an IP broadcast address (preferably 0xffffffff)
- as the IP destination address and the link-layer broadcast address as
- the link-layer destination address. If the BROADCAST bit is cleared
- to 0, the message SHOULD be sent as an IP unicast to the IP address
- specified in the 'yiaddr' field and the link-layer address specified
- in the 'chaddr' field. If unicasting is not possible, the message
- MAY be sent as an IP broadcast using an IP broadcast address
- (preferably 0xffffffff) as the IP destination address and the link-
- layer broadcast address as the link-layer destination address.
-
-4.2 DHCP server administrative controls
-
- DHCP servers are not required to respond to every DHCPDISCOVER and
- DHCPREQUEST message they receive. For example, a network
- administrator, to retain stringent control over the clients attached
- to the network, may choose to configure DHCP servers to respond only
- to clients that have been previously registered through some external
- mechanism. The DHCP specification describes only the interactions
- between clients and servers when the clients and servers choose to
- interact; it is beyond the scope of the DHCP specification to
- describe all of the administrative controls that system
- administrators might want to use. Specific DHCP server
-
-
-
-Droms Standards Track [Page 25]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
- implementations may incorporate any controls or policies desired by a
- network administrator.
-
- In some environments, a DHCP server will have to consider the values
- of the vendor class options included in DHCPDISCOVER or DHCPREQUEST
- messages when determining the correct parameters for a particular
- client.
-
- A DHCP server needs to use some unique identifier to associate a
- client with its lease. The client MAY choose to explicitly provide
- the identifier through the 'client identifier' option. If the client
- supplies a 'client identifier', the client MUST use the same 'client
- identifier' in all subsequent messages, and the server MUST use that
- identifier to identify the client. If the client does not provide a
- 'client identifier' option, the server MUST use the contents of the
- 'chaddr' field to identify the client. It is crucial for a DHCP
- client to use an identifier unique within the subnet to which the
- client is attached in the 'client identifier' option. Use of
- 'chaddr' as the client's unique identifier may cause unexpected
- results, as that identifier may be associated with a hardware
- interface that could be moved to a new client. Some sites may choose
- to use a manufacturer's serial number as the 'client identifier', to
- avoid unexpected changes in a clients network address due to transfer
- of hardware interfaces among computers. Sites may also choose to use
- a DNS name as the 'client identifier', causing address leases to be
- associated with the DNS name rather than a specific hardware box.
-
- DHCP clients are free to use any strategy in selecting a DHCP server
- among those from which the client receives a DHCPOFFER message. The
- client implementation of DHCP SHOULD provide a mechanism for the user
- to select directly the 'vendor class identifier' values.
-
-4.3 DHCP server behavior
-
- A DHCP server processes incoming DHCP messages from a client based on
- the current state of the binding for that client. A DHCP server can
- receive the following messages from a client:
-
- o DHCPDISCOVER
-
- o DHCPREQUEST
-
- o DHCPDECLINE
-
- o DHCPRELEASE
-
- o DHCPINFORM
-
-
-
-
-Droms Standards Track [Page 26]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
- Table 3 gives the use of the fields and options in a DHCP message by
- a server. The remainder of this section describes the action of the
- DHCP server for each possible incoming message.
-
-4.3.1 DHCPDISCOVER message
-
- When a server receives a DHCPDISCOVER message from a client, the
- server chooses a network address for the requesting client. If no
- address is available, the server may choose to report the problem to
- the system administrator. If an address is available, the new address
- SHOULD be chosen as follows:
-
- o The client's current address as recorded in the client's current
- binding, ELSE
-
- o The client's previous address as recorded in the client's (now
- expired or released) binding, if that address is in the server's
- pool of available addresses and not already allocated, ELSE
-
- o The address requested in the 'Requested IP Address' option, if that
- address is valid and not already allocated, ELSE
-
- o A new address allocated from the server's pool of available
- addresses; the address is selected based on the subnet from which
- the message was received (if 'giaddr' is 0) or on the address of
- the relay agent that forwarded the message ('giaddr' when not 0).
-
- As described in section 4.2, a server MAY, for administrative
- reasons, assign an address other than the one requested, or may
- refuse to allocate an address to a particular client even though free
- addresses are available.
-
- Note that, in some network architectures (e.g., internets with more
- than one IP subnet assigned to a physical network segment), it may be
- the case that the DHCP client should be assigned an address from a
- different subnet than the address recorded in 'giaddr'. Thus, DHCP
- does not require that the client be assigned as address from the
- subnet in 'giaddr'. A server is free to choose some other subnet,
- and it is beyond the scope of the DHCP specification to describe ways
- in which the assigned IP address might be chosen.
-
- While not required for correct operation of DHCP, the server SHOULD
- NOT reuse the selected network address before the client responds to
- the server's DHCPOFFER message. The server may choose to record the
- address as offered to the client.
-
- The server must also choose an expiration time for the lease, as
- follows:
-
-
-
-Droms Standards Track [Page 27]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
- o IF the client has not requested a specific lease in the
- DHCPDISCOVER message and the client already has an assigned network
- address, the server returns the lease expiration time previously
- assigned to that address (note that the client must explicitly
- request a specific lease to extend the expiration time on a
- previously assigned address), ELSE
-
- o IF the client has not requested a specific lease in the
- DHCPDISCOVER message and the client does not have an assigned
- network address, the server assigns a locally configured default
- lease time, ELSE
-
- o IF the client has requested a specific lease in the DHCPDISCOVER
- message (regardless of whether the client has an assigned network
- address), the server may choose either to return the requested
- lease (if the lease is acceptable to local policy) or select
- another lease.
-
-Field DHCPOFFER DHCPACK DHCPNAK
------ --------- ------- -------
-'op' BOOTREPLY BOOTREPLY BOOTREPLY
-'htype' (From "Assigned Numbers" RFC)
-'hlen' (Hardware address length in octets)
-'hops' 0 0 0
-'xid' 'xid' from client 'xid' from client 'xid' from client
- DHCPDISCOVER DHCPREQUEST DHCPREQUEST
- message message message
-'secs' 0 0 0
-'ciaddr' 0 'ciaddr' from 0
- DHCPREQUEST or 0
-'yiaddr' IP address offered IP address 0
- to client assigned to client
-'siaddr' IP address of next IP address of next 0
- bootstrap server bootstrap server
-'flags' 'flags' from 'flags' from 'flags' from
- client DHCPDISCOVER client DHCPREQUEST client DHCPREQUEST
- message message message
-'giaddr' 'giaddr' from 'giaddr' from 'giaddr' from
- client DHCPDISCOVER client DHCPREQUEST client DHCPREQUEST
- message message message
-'chaddr' 'chaddr' from 'chaddr' from 'chaddr' from
- client DHCPDISCOVER client DHCPREQUEST client DHCPREQUEST
- message message message
-'sname' Server host name Server host name (unused)
- or options or options
-'file' Client boot file Client boot file (unused)
- name or options name or options
-'options' options options
-
-
-
-Droms Standards Track [Page 28]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
-Option DHCPOFFER DHCPACK DHCPNAK
------- --------- ------- -------
-Requested IP address MUST NOT MUST NOT MUST NOT
-IP address lease time MUST MUST (DHCPREQUEST) MUST NOT
- MUST NOT (DHCPINFORM)
-Use 'file'/'sname' fields MAY MAY MUST NOT
-DHCP message type DHCPOFFER DHCPACK DHCPNAK
-Parameter request list MUST NOT MUST NOT MUST NOT
-Message SHOULD SHOULD SHOULD
-Client identifier MUST NOT MUST NOT MAY
-Vendor class identifier MAY MAY MAY
-Server identifier MUST MUST MUST
-Maximum message size MUST NOT MUST NOT MUST NOT
-All others MAY MAY MUST NOT
-
- Table 3: Fields and options used by DHCP servers
-
- Once the network address and lease have been determined, the server
- constructs a DHCPOFFER message with the offered configuration
- parameters. It is important for all DHCP servers to return the same
- parameters (with the possible exception of a newly allocated network
- address) to ensure predictable client behavior regardless of which
- server the client selects. The configuration parameters MUST be
- selected by applying the following rules in the order given below.
- The network administrator is responsible for configuring multiple
- DHCP servers to ensure uniform responses from those servers. The
- server MUST return to the client:
-
- o The client's network address, as determined by the rules given
- earlier in this section,
-
- o The expiration time for the client's lease, as determined by the
- rules given earlier in this section,
-
- o Parameters requested by the client, according to the following
- rules:
-
- -- IF the server has been explicitly configured with a default
- value for the parameter, the server MUST include that value
- in an appropriate option in the 'option' field, ELSE
-
- -- IF the server recognizes the parameter as a parameter
- defined in the Host Requirements Document, the server MUST
- include the default value for that parameter as given in the
- Host Requirements Document in an appropriate option in the
- 'option' field, ELSE
-
- -- The server MUST NOT return a value for that parameter,
-
-
-
-Droms Standards Track [Page 29]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
- The server MUST supply as many of the requested parameters as
- possible and MUST omit any parameters it cannot provide. The
- server MUST include each requested parameter only once unless
- explicitly allowed in the DHCP Options and BOOTP Vendor
- Extensions document.
-
- o Any parameters from the existing binding that differ from the Host
- Requirements Document defaults,
-
- o Any parameters specific to this client (as identified by
- the contents of 'chaddr' or 'client identifier' in the DHCPDISCOVER
- or DHCPREQUEST message), e.g., as configured by the network
- administrator,
-
- o Any parameters specific to this client's class (as identified
- by the contents of the 'vendor class identifier'
- option in the DHCPDISCOVER or DHCPREQUEST message),
- e.g., as configured by the network administrator; the parameters
- MUST be identified by an exact match between the client's vendor
- class identifiers and the client's classes identified in the
- server,
-
- o Parameters with non-default values on the client's subnet.
-
- The server MAY choose to return the 'vendor class identifier' used to
- determine the parameters in the DHCPOFFER message to assist the
- client in selecting which DHCPOFFER to accept. The server inserts
- the 'xid' field from the DHCPDISCOVER message into the 'xid' field of
- the DHCPOFFER message and sends the DHCPOFFER message to the
- requesting client.
-
-4.3.2 DHCPREQUEST message
-
- A DHCPREQUEST message may come from a client responding to a
- DHCPOFFER message from a server, from a client verifying a previously
- allocated IP address or from a client extending the lease on a
- network address. If the DHCPREQUEST message contains a 'server
- identifier' option, the message is in response to a DHCPOFFER
- message. Otherwise, the message is a request to verify or extend an
- existing lease. If the client uses a 'client identifier' in a
- DHCPREQUEST message, it MUST use that same 'client identifier' in all
- subsequent messages. If the client included a list of requested
- parameters in a DHCPDISCOVER message, it MUST include that list in
- all subsequent messages.
-
-
-
-
-
-
-
-Droms Standards Track [Page 30]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
- Any configuration parameters in the DHCPACK message SHOULD NOT
- conflict with those in the earlier DHCPOFFER message to which the
- client is responding. The client SHOULD use the parameters in the
- DHCPACK message for configuration.
-
- Clients send DHCPREQUEST messages as follows:
-
- o DHCPREQUEST generated during SELECTING state:
-
- Client inserts the address of the selected server in 'server
- identifier', 'ciaddr' MUST be zero, 'requested IP address' MUST be
- filled in with the yiaddr value from the chosen DHCPOFFER.
-
- Note that the client may choose to collect several DHCPOFFER
- messages and select the "best" offer. The client indicates its
- selection by identifying the offering server in the DHCPREQUEST
- message. If the client receives no acceptable offers, the client
- may choose to try another DHCPDISCOVER message. Therefore, the
- servers may not receive a specific DHCPREQUEST from which they can
- decide whether or not the client has accepted the offer. Because
- the servers have not committed any network address assignments on
- the basis of a DHCPOFFER, servers are free to reuse offered
- network addresses in response to subsequent requests. As an
- implementation detail, servers SHOULD NOT reuse offered addresses
- and may use an implementation-specific timeout mechanism to decide
- when to reuse an offered address.
-
- o DHCPREQUEST generated during INIT-REBOOT state:
-
- 'server identifier' MUST NOT be filled in, 'requested IP address'
- option MUST be filled in with client's notion of its previously
- assigned address. 'ciaddr' MUST be zero. The client is seeking to
- verify a previously allocated, cached configuration. Server SHOULD
- send a DHCPNAK message to the client if the 'requested IP address'
- is incorrect, or is on the wrong network.
-
- Determining whether a client in the INIT-REBOOT state is on the
- correct network is done by examining the contents of 'giaddr', the
- 'requested IP address' option, and a database lookup. If the DHCP
- server detects that the client is on the wrong net (i.e., the
- result of applying the local subnet mask or remote subnet mask (if
- 'giaddr' is not zero) to 'requested IP address' option value
- doesn't match reality), then the server SHOULD send a DHCPNAK
- message to the client.
-
-
-
-
-
-
-
-Droms Standards Track [Page 31]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
- If the network is correct, then the DHCP server should check if
- the client's notion of its IP address is correct. If not, then the
- server SHOULD send a DHCPNAK message to the client. If the DHCP
- server has no record of this client, then it MUST remain silent,
- and MAY output a warning to the network administrator. This
- behavior is necessary for peaceful coexistence of non-
- communicating DHCP servers on the same wire.
-
- If 'giaddr' is 0x0 in the DHCPREQUEST message, the client is on
- the same subnet as the server. The server MUST broadcast the
- DHCPNAK message to the 0xffffffff broadcast address because the
- client may not have a correct network address or subnet mask, and
- the client may not be answering ARP requests.
-
- If 'giaddr' is set in the DHCPREQUEST message, the client is on a
- different subnet. The server MUST set the broadcast bit in the
- DHCPNAK, so that the relay agent will broadcast the DHCPNAK to the
- client, because the client may not have a correct network address
- or subnet mask, and the client may not be answering ARP requests.
-
- o DHCPREQUEST generated during RENEWING state:
-
- 'server identifier' MUST NOT be filled in, 'requested IP address'
- option MUST NOT be filled in, 'ciaddr' MUST be filled in with
- client's IP address. In this situation, the client is completely
- configured, and is trying to extend its lease. This message will
- be unicast, so no relay agents will be involved in its
- transmission. Because 'giaddr' is therefore not filled in, the
- DHCP server will trust the value in 'ciaddr', and use it when
- replying to the client.
-
- A client MAY choose to renew or extend its lease prior to T1. The
- server may choose not to extend the lease (as a policy decision by
- the network administrator), but should return a DHCPACK message
- regardless.
-
- o DHCPREQUEST generated during REBINDING state:
-
- 'server identifier' MUST NOT be filled in, 'requested IP address'
- option MUST NOT be filled in, 'ciaddr' MUST be filled in with
- client's IP address. In this situation, the client is completely
- configured, and is trying to extend its lease. This message MUST
- be broadcast to the 0xffffffff IP broadcast address. The DHCP
- server SHOULD check 'ciaddr' for correctness before replying to
- the DHCPREQUEST.
-
-
-
-
-
-
-Droms Standards Track [Page 32]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
- The DHCPREQUEST from a REBINDING client is intended to accommodate
- sites that have multiple DHCP servers and a mechanism for
- maintaining consistency among leases managed by multiple servers.
- A DHCP server MAY extend a client's lease only if it has local
- administrative authority to do so.
-
-4.3.3 DHCPDECLINE message
-
- If the server receives a DHCPDECLINE message, the client has
- discovered through some other means that the suggested network
- address is already in use. The server MUST mark the network address
- as not available and SHOULD notify the local system administrator of
- a possible configuration problem.
-
-4.3.4 DHCPRELEASE message
-
- Upon receipt of a DHCPRELEASE message, the server marks the network
- address as not allocated. The server SHOULD retain a record of the
- client's initialization parameters for possible reuse in response to
- subsequent requests from the client.
-
-4.3.5 DHCPINFORM message
-
- The server responds to a DHCPINFORM message by sending a DHCPACK
- message directly to the address given in the 'ciaddr' field of the
- DHCPINFORM message. The server MUST NOT send a lease expiration time
- to the client and SHOULD NOT fill in 'yiaddr'. The server includes
- other parameters in the DHCPACK message as defined in section 4.3.1.
-
-4.3.6 Client messages
-
- Table 4 details the differences between messages from clients in
- various states.
-
- ---------------------------------------------------------------------
- | |INIT-REBOOT |SELECTING |RENEWING |REBINDING |
- ---------------------------------------------------------------------
- |broad/unicast |broadcast |broadcast |unicast |broadcast |
- |server-ip |MUST NOT |MUST |MUST NOT |MUST NOT |
- |requested-ip |MUST |MUST |MUST NOT |MUST NOT |
- |ciaddr |zero |zero |IP address |IP address|
- ---------------------------------------------------------------------
-
- Table 4: Client messages from different states
-
-
-
-
-
-
-
-Droms Standards Track [Page 33]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
-4.4 DHCP client behavior
-
- Figure 5 gives a state-transition diagram for a DHCP client. A
- client can receive the following messages from a server:
-
- o DHCPOFFER
-
- o DHCPACK
-
- o DHCPNAK
-
- The DHCPINFORM message is not shown in figure 5. A client simply
- sends the DHCPINFORM and waits for DHCPACK messages. Once the client
- has selected its parameters, it has completed the configuration
- process.
-
- Table 5 gives the use of the fields and options in a DHCP message by
- a client. The remainder of this section describes the action of the
- DHCP client for each possible incoming message. The description in
- the following section corresponds to the full configuration procedure
- previously described in section 3.1, and the text in the subsequent
- section corresponds to the abbreviated configuration procedure
- described in section 3.2.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Droms Standards Track [Page 34]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
- -------- -------
-| | +-------------------------->| |<-------------------+
-| INIT- | | +-------------------->| INIT | |
-| REBOOT |DHCPNAK/ +---------->| |<---+ |
-| |Restart| | ------- | |
- -------- | DHCPNAK/ | | |
- | Discard offer | -/Send DHCPDISCOVER |
--/Send DHCPREQUEST | | |
- | | | DHCPACK v | |
- ----------- | (not accept.)/ ----------- | |
-| | | Send DHCPDECLINE | | |
-| REBOOTING | | | | SELECTING |<----+ |
-| | | / | | |DHCPOFFER/ |
- ----------- | / ----------- | |Collect |
- | | / | | | replies |
-DHCPACK/ | / +----------------+ +-------+ |
-Record lease, set| | v Select offer/ |
-timers T1, T2 ------------ send DHCPREQUEST | |
- | +----->| | DHCPNAK, Lease expired/ |
- | | | REQUESTING | Halt network |
- DHCPOFFER/ | | | |
- Discard ------------ | |
- | | | | ----------- |
- | +--------+ DHCPACK/ | | |
- | Record lease, set -----| REBINDING | |
- | timers T1, T2 / | | |
- | | DHCPACK/ ----------- |
- | v Record lease, set ^ |
- +----------------> ------- /timers T1,T2 | |
- +----->| |<---+ | |
- | | BOUND |<---+ | |
- DHCPOFFER, DHCPACK, | | | T2 expires/ DHCPNAK/
- DHCPNAK/Discard ------- | Broadcast Halt network
- | | | | DHCPREQUEST |
- +-------+ | DHCPACK/ | |
- T1 expires/ Record lease, set | |
- Send DHCPREQUEST timers T1, T2 | |
- to leasing server | | |
- | ---------- | |
- | | |------------+ |
- +->| RENEWING | |
- | |----------------------------+
- ----------
- Figure 5: State-transition diagram for DHCP clients
-
-
-
-
-
-
-
-Droms Standards Track [Page 35]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
-4.4.1 Initialization and allocation of network address
-
- The client begins in INIT state and forms a DHCPDISCOVER message.
- The client SHOULD wait a random time between one and ten seconds to
- desynchronize the use of DHCP at startup. The client sets 'ciaddr'
- to 0x00000000. The client MAY request specific parameters by
- including the 'parameter request list' option. The client MAY
- suggest a network address and/or lease time by including the
- 'requested IP address' and 'IP address lease time' options. The
- client MUST include its hardware address in the 'chaddr' field, if
- necessary for delivery of DHCP reply messages. The client MAY
- include a different unique identifier in the 'client identifier'
- option, as discussed in section 4.2. If the client included a list
- of requested parameters in a DHCPDISCOVER message, it MUST include
- that list in all subsequent messages.
-
- The client generates and records a random transaction identifier and
- inserts that identifier into the 'xid' field. The client records its
- own local time for later use in computing the lease expiration. The
- client then broadcasts the DHCPDISCOVER on the local hardware
- broadcast address to the 0xffffffff IP broadcast address and 'DHCP
- server' UDP port.
-
- If the 'xid' of an arriving DHCPOFFER message does not match the
- 'xid' of the most recent DHCPDISCOVER message, the DHCPOFFER message
- must be silently discarded. Any arriving DHCPACK messages must be
- silently discarded.
-
- The client collects DHCPOFFER messages over a period of time, selects
- one DHCPOFFER message from the (possibly many) incoming DHCPOFFER
- messages (e.g., the first DHCPOFFER message or the DHCPOFFER message
- from the previously used server) and extracts the server address from
- the 'server identifier' option in the DHCPOFFER message. The time
- over which the client collects messages and the mechanism used to
- select one DHCPOFFER are implementation dependent.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Droms Standards Track [Page 36]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
-Field DHCPDISCOVER DHCPREQUEST DHCPDECLINE,
- DHCPINFORM DHCPRELEASE
------ ------------ ----------- -----------
-'op' BOOTREQUEST BOOTREQUEST BOOTREQUEST
-'htype' (From "Assigned Numbers" RFC)
-'hlen' (Hardware address length in octets)
-'hops' 0 0 0
-'xid' selected by client 'xid' from server selected by
- DHCPOFFER message client
-'secs' 0 or seconds since 0 or seconds since 0
- DHCP process started DHCP process started
-'flags' Set 'BROADCAST' Set 'BROADCAST' 0
- flag if client flag if client
- requires broadcast requires broadcast
- reply reply
-'ciaddr' 0 (DHCPDISCOVER) 0 or client's 0 (DHCPDECLINE)
- client's network address client's network
- network address (BOUND/RENEW/REBIND) address
- (DHCPINFORM) (DHCPRELEASE)
-'yiaddr' 0 0 0
-'siaddr' 0 0 0
-'giaddr' 0 0 0
-'chaddr' client's hardware client's hardware client's hardware
- address address address
-'sname' options, if options, if (unused)
- indicated in indicated in
- 'sname/file' 'sname/file'
- option; otherwise option; otherwise
- unused unused
-'file' options, if options, if (unused)
- indicated in indicated in
- 'sname/file' 'sname/file'
- option; otherwise option; otherwise
- unused unused
-'options' options options (unused)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Droms Standards Track [Page 37]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
-Option DHCPDISCOVER DHCPREQUEST DHCPDECLINE,
- DHCPINFORM DHCPRELEASE
------- ------------ ----------- -----------
-Requested IP address MAY MUST (in MUST
- (DISCOVER) SELECTING or (DHCPDECLINE),
- MUST NOT INIT-REBOOT) MUST NOT
- (INFORM) MUST NOT (in (DHCPRELEASE)
- BOUND or
- RENEWING)
-IP address lease time MAY MAY MUST NOT
- (DISCOVER)
- MUST NOT
- (INFORM)
-Use 'file'/'sname' fields MAY MAY MAY
-DHCP message type DHCPDISCOVER/ DHCPREQUEST DHCPDECLINE/
- DHCPINFORM DHCPRELEASE
-Client identifier MAY MAY MAY
-Vendor class identifier MAY MAY MUST NOT
-Server identifier MUST NOT MUST (after MUST
- SELECTING)
- MUST NOT (after
- INIT-REBOOT,
- BOUND, RENEWING
- or REBINDING)
-Parameter request list MAY MAY MUST NOT
-Maximum message size MAY MAY MUST NOT
-Message SHOULD NOT SHOULD NOT SHOULD
-Site-specific MAY MAY MUST NOT
-All others MAY MAY MUST NOT
-
- Table 5: Fields and options used by DHCP clients
-
- If the parameters are acceptable, the client records the address of
- the server that supplied the parameters from the 'server identifier'
- field and sends that address in the 'server identifier' field of a
- DHCPREQUEST broadcast message. Once the DHCPACK message from the
- server arrives, the client is initialized and moves to BOUND state.
- The DHCPREQUEST message contains the same 'xid' as the DHCPOFFER
- message. The client records the lease expiration time as the sum of
- the time at which the original request was sent and the duration of
- the lease from the DHCPACK message. The client SHOULD perform a
- check on the suggested address to ensure that the address is not
- already in use. For example, if the client is on a network that
- supports ARP, the client may issue an ARP request for the suggested
- request. When broadcasting an ARP request for the suggested address,
- the client must fill in its own hardware address as the sender's
- hardware address, and 0 as the sender's IP address, to avoid
- confusing ARP caches in other hosts on the same subnet. If the
-
-
-
-Droms Standards Track [Page 38]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
- network address appears to be in use, the client MUST send a
- DHCPDECLINE message to the server. The client SHOULD broadcast an ARP
- reply to announce the client's new IP address and clear any outdated
- ARP cache entries in hosts on the client's subnet.
-
-4.4.2 Initialization with known network address
-
- The client begins in INIT-REBOOT state and sends a DHCPREQUEST
- message. The client MUST insert its known network address as a
- 'requested IP address' option in the DHCPREQUEST message. The client
- may request specific configuration parameters by including the
- 'parameter request list' option. The client generates and records a
- random transaction identifier and inserts that identifier into the
- 'xid' field. The client records its own local time for later use in
- computing the lease expiration. The client MUST NOT include a
- 'server identifier' in the DHCPREQUEST message. The client then
- broadcasts the DHCPREQUEST on the local hardware broadcast address to
- the 'DHCP server' UDP port.
-
- Once a DHCPACK message with an 'xid' field matching that in the
- client's DHCPREQUEST message arrives from any server, the client is
- initialized and moves to BOUND state. The client records the lease
- expiration time as the sum of the time at which the DHCPREQUEST
- message was sent and the duration of the lease from the DHCPACK
- message.
-
-4.4.3 Initialization with an externally assigned network address
-
- The client sends a DHCPINFORM message. The client may request
- specific configuration parameters by including the 'parameter request
- list' option. The client generates and records a random transaction
- identifier and inserts that identifier into the 'xid' field. The
- client places its own network address in the 'ciaddr' field. The
- client SHOULD NOT request lease time parameters.
-
- The client then unicasts the DHCPINFORM to the DHCP server if it
- knows the server's address, otherwise it broadcasts the message to
- the limited (all 1s) broadcast address. DHCPINFORM messages MUST be
- directed to the 'DHCP server' UDP port.
-
- Once a DHCPACK message with an 'xid' field matching that in the
- client's DHCPINFORM message arrives from any server, the client is
- initialized.
-
- If the client does not receive a DHCPACK within a reasonable period
- of time (60 seconds or 4 tries if using timeout suggested in section
- 4.1), then it SHOULD display a message informing the user of the
- problem, and then SHOULD begin network processing using suitable
-
-
-
-Droms Standards Track [Page 39]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
- defaults as per Appendix A.
-
-4.4.4 Use of broadcast and unicast
-
- The DHCP client broadcasts DHCPDISCOVER, DHCPREQUEST and DHCPINFORM
- messages, unless the client knows the address of a DHCP server. The
- client unicasts DHCPRELEASE messages to the server. Because the
- client is declining the use of the IP address supplied by the server,
- the client broadcasts DHCPDECLINE messages.
-
- When the DHCP client knows the address of a DHCP server, in either
- INIT or REBOOTING state, the client may use that address in the
- DHCPDISCOVER or DHCPREQUEST rather than the IP broadcast address.
- The client may also use unicast to send DHCPINFORM messages to a
- known DHCP server. If the client receives no response to DHCP
- messages sent to the IP address of a known DHCP server, the DHCP
- client reverts to using the IP broadcast address.
-
-4.4.5 Reacquisition and expiration
-
- The client maintains two times, T1 and T2, that specify the times at
- which the client tries to extend its lease on its network address.
- T1 is the time at which the client enters the RENEWING state and
- attempts to contact the server that originally issued the client's
- network address. T2 is the time at which the client enters the
- REBINDING state and attempts to contact any server. T1 MUST be
- earlier than T2, which, in turn, MUST be earlier than the time at
- which the client's lease will expire.
-
- To avoid the need for synchronized clocks, T1 and T2 are expressed in
- options as relative times [2].
-
- At time T1 the client moves to RENEWING state and sends (via unicast)
- a DHCPREQUEST message to the server to extend its lease. The client
- sets the 'ciaddr' field in the DHCPREQUEST to its current network
- address. The client records the local time at which the DHCPREQUEST
- message is sent for computation of the lease expiration time. The
- client MUST NOT include a 'server identifier' in the DHCPREQUEST
- message.
-
- Any DHCPACK messages that arrive with an 'xid' that does not match
- the 'xid' of the client's DHCPREQUEST message are silently discarded.
- When the client receives a DHCPACK from the server, the client
- computes the lease expiration time as the sum of the time at which
- the client sent the DHCPREQUEST message and the duration of the lease
- in the DHCPACK message. The client has successfully reacquired its
- network address, returns to BOUND state and may continue network
- processing.
-
-
-
-Droms Standards Track [Page 40]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
- If no DHCPACK arrives before time T2, the client moves to REBINDING
- state and sends (via broadcast) a DHCPREQUEST message to extend its
- lease. The client sets the 'ciaddr' field in the DHCPREQUEST to its
- current network address. The client MUST NOT include a 'server
- identifier' in the DHCPREQUEST message.
-
- Times T1 and T2 are configurable by the server through options. T1
- defaults to (0.5 * duration_of_lease). T2 defaults to (0.875 *
- duration_of_lease). Times T1 and T2 SHOULD be chosen with some
- random "fuzz" around a fixed value, to avoid synchronization of
- client reacquisition.
-
- A client MAY choose to renew or extend its lease prior to T1. The
- server MAY choose to extend the client's lease according to policy
- set by the network administrator. The server SHOULD return T1 and
- T2, and their values SHOULD be adjusted from their original values to
- take account of the time remaining on the lease.
-
- In both RENEWING and REBINDING states, if the client receives no
- response to its DHCPREQUEST message, the client SHOULD wait one-half
- of the remaining time until T2 (in RENEWING state) and one-half of
- the remaining lease time (in REBINDING state), down to a minimum of
- 60 seconds, before retransmitting the DHCPREQUEST message.
-
- If the lease expires before the client receives a DHCPACK, the client
- moves to INIT state, MUST immediately stop any other network
- processing and requests network initialization parameters as if the
- client were uninitialized. If the client then receives a DHCPACK
- allocating that client its previous network address, the client
- SHOULD continue network processing. If the client is given a new
- network address, it MUST NOT continue using the previous network
- address and SHOULD notify the local users of the problem.
-
-4.4.6 DHCPRELEASE
-
- If the client no longer requires use of its assigned network address
- (e.g., the client is gracefully shut down), the client sends a
- DHCPRELEASE message to the server. Note that the correct operation
- of DHCP does not depend on the transmission of DHCPRELEASE messages.
-
-
-
-
-
-
-
-
-
-
-
-
-Droms Standards Track [Page 41]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
-5. Acknowledgments
-
- The author thanks the many (and too numerous to mention!) members of
- the DHC WG for their tireless and ongoing efforts in the development
- of DHCP and this document.
-
- The efforts of J Allard, Mike Carney, Dave Lapp, Fred Lien and John
- Mendonca in organizing DHCP interoperability testing sessions are
- gratefully acknowledged.
-
- The development of this document was supported in part by grants from
- the Corporation for National Research Initiatives (CNRI), Bucknell
- University and Sun Microsystems.
-
-6. References
-
- [1] Acetta, M., "Resource Location Protocol", RFC 887, CMU, December
- 1983.
-
- [2] Alexander, S., and R. Droms, "DHCP Options and BOOTP Vendor
- Extensions", RFC 1533, Lachman Technology, Inc., Bucknell
- University, October 1993.
-
- [3] Braden, R., Editor, "Requirements for Internet Hosts --
- Communication Layers", STD 3, RFC 1122, USC/Information Sciences
- Institute, October 1989.
-
- [4] Braden, R., Editor, "Requirements for Internet Hosts --
- Application and Support, STD 3, RFC 1123, USC/Information
- Sciences Institute, October 1989.
-
- [5] Brownell, D, "Dynamic Reverse Address Resolution Protocol
- (DRARP)", Work in Progress.
-
- [6] Comer, D., and R. Droms, "Uniform Access to Internet Directory
- Services", Proc. of ACM SIGCOMM '90 (Special issue of Computer
- Communications Review), 20(4):50--59, 1990.
-
- [7] Croft, B., and J. Gilmore, "Bootstrap Protocol (BOOTP)", RFC 951,
- Stanford and SUN Microsystems, September 1985.
-
- [8] Deering, S., "ICMP Router Discovery Messages", RFC 1256, Xerox
- PARC, September 1991.
-
- [9] Droms, D., "Interoperation between DHCP and BOOTP", RFC 1534,
- Bucknell University, October 1993.
-
-
-
-
-
-Droms Standards Track [Page 42]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
- [10] Finlayson, R., Mann, T., Mogul, J., and M. Theimer, "A Reverse
- Address Resolution Protocol", RFC 903, Stanford, June 1984.
-
- [11] Gray C., and D. Cheriton, "Leases: An Efficient Fault-Tolerant
- Mechanism for Distributed File Cache Consistency", In Proc. of
- the Twelfth ACM Symposium on Operating Systems Design, 1989.
-
- [12] Mockapetris, P., "Domain Names -- Concepts and Facilities", STD
- 13, RFC 1034, USC/Information Sciences Institute, November 1987.
-
- [13] Mockapetris, P., "Domain Names -- Implementation and
- Specification", STD 13, RFC 1035, USC/Information Sciences
- Institute, November 1987.
-
- [14] Mogul J., and S. Deering, "Path MTU Discovery", RFC 1191,
- November 1990.
-
- [15] Morgan, R., "Dynamic IP Address Assignment for Ethernet Attached
- Hosts", Work in Progress.
-
- [16] Postel, J., "Internet Control Message Protocol", STD 5, RFC 792,
- USC/Information Sciences Institute, September 1981.
-
- [17] Reynolds, J., "BOOTP Vendor Information Extensions", RFC 1497,
- USC/Information Sciences Institute, August 1993.
-
- [18] Reynolds, J., and J. Postel, "Assigned Numbers", STD 2, RFC 1700,
- USC/Information Sciences Institute, October 1994.
-
- [19] Jeffrey Schiller and Mark Rosenstein. A Protocol for the Dynamic
- Assignment of IP Addresses for use on an Ethernet. (Available
- from the Athena Project, MIT), 1989.
-
- [20] Sollins, K., "The TFTP Protocol (Revision 2)", RFC 783, NIC,
- June 1981.
-
- [21] Wimer, W., "Clarifications and Extensions for the Bootstrap
- Protocol", RFC 1542, Carnegie Mellon University, October 1993.
-
-7. Security Considerations
-
- DHCP is built directly on UDP and IP which are as yet inherently
- insecure. Furthermore, DHCP is generally intended to make
- maintenance of remote and/or diskless hosts easier. While perhaps
- not impossible, configuring such hosts with passwords or keys may be
- difficult and inconvenient. Therefore, DHCP in its current form is
- quite insecure.
-
-
-
-
-Droms Standards Track [Page 43]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
- Unauthorized DHCP servers may be easily set up. Such servers can
- then send false and potentially disruptive information to clients
- such as incorrect or duplicate IP addresses, incorrect routing
- information (including spoof routers, etc.), incorrect domain
- nameserver addresses (such as spoof nameservers), and so on.
- Clearly, once this seed information is in place, an attacker can
- further compromise affected systems.
-
- Malicious DHCP clients could masquerade as legitimate clients and
- retrieve information intended for those legitimate clients. Where
- dynamic allocation of resources is used, a malicious client could
- claim all resources for itself, thereby denying resources to
- legitimate clients.
-
-8. Author's Address
-
- Ralph Droms
- Computer Science Department
- 323 Dana Engineering
- Bucknell University
- Lewisburg, PA 17837
-
- Phone: (717) 524-1145
- EMail: droms@bucknell.edu
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Droms Standards Track [Page 44]
-
-RFC 2131 Dynamic Host Configuration Protocol March 1997
-
-
-A. Host Configuration Parameters
-
- IP-layer_parameters,_per_host:_
-
- Be a router on/off HRC 3.1
- Non-local source routing on/off HRC 3.3.5
- Policy filters for
- non-local source routing (list) HRC 3.3.5
- Maximum reassembly size integer HRC 3.3.2
- Default TTL integer HRC 3.2.1.7
- PMTU aging timeout integer MTU 6.6
- MTU plateau table (list) MTU 7
- IP-layer_parameters,_per_interface:_
- IP address (address) HRC 3.3.1.6
- Subnet mask (address mask) HRC 3.3.1.6
- MTU integer HRC 3.3.3
- All-subnets-MTU on/off HRC 3.3.3
- Broadcast address flavor 0x00000000/0xffffffff HRC 3.3.6
- Perform mask discovery on/off HRC 3.2.2.9
- Be a mask supplier on/off HRC 3.2.2.9
- Perform router discovery on/off RD 5.1
- Router solicitation address (address) RD 5.1
- Default routers, list of:
- router address (address) HRC 3.3.1.6
- preference level integer HRC 3.3.1.6
- Static routes, list of:
- destination (host/subnet/net) HRC 3.3.1.2
- destination mask (address mask) HRC 3.3.1.2
- type-of-service integer HRC 3.3.1.2
- first-hop router (address) HRC 3.3.1.2
- ignore redirects on/off HRC 3.3.1.2
- PMTU integer MTU 6.6
- perform PMTU discovery on/off MTU 6.6
-
- Link-layer_parameters,_per_interface:_
- Trailers on/off HRC 2.3.1
- ARP cache timeout integer HRC 2.3.2.1
- Ethernet encapsulation (RFC 894/RFC 1042) HRC 2.3.3
-
- TCP_parameters,_per_host:_
- TTL integer HRC 4.2.2.19
- Keep-alive interval integer HRC 4.2.3.6
- Keep-alive data size 0/1 HRC 4.2.3.6
-
-Key:
-
- MTU = Path MTU Discovery (RFC 1191, Proposed Standard)
- RD = Router Discovery (RFC 1256, Proposed Standard)
-
-
-
-Droms Standards Track [Page 45]
-
diff --git a/doc/rfc2132.txt b/doc/rfc2132.txt
deleted file mode 100644
index e9c4f4b3..00000000
--- a/doc/rfc2132.txt
+++ /dev/null
@@ -1,1907 +0,0 @@
-
-
-
-
-
-
-Network Working Group S. Alexander
-Request for Comments: 2132 Silicon Graphics, Inc.
-Obsoletes: 1533 R. Droms
-Category: Standards Track Bucknell University
- March 1997
-
- DHCP Options and BOOTP Vendor Extensions
-
-Status of this memo
-
- This document specifies an Internet standards track protocol for the
- Internet community, and requests discussion and suggestions for
- improvements. Please refer to the current edition of the "Internet
- Official Protocol Standards" (STD 1) for the standardization state
- and status of this protocol. Distribution of this memo is unlimited.
-
-Abstract
-
- The Dynamic Host Configuration Protocol (DHCP) [1] provides a
- framework for passing configuration information to hosts on a TCP/IP
- network. Configuration parameters and other control information are
- carried in tagged data items that are stored in the 'options' field
- of the DHCP message. The data items themselves are also called
- "options."
-
- This document specifies the current set of DHCP options. Future
- options will be specified in separate RFCs. The current list of
- valid options is also available in ftp://ftp.isi.edu/in-
- notes/iana/assignments [22].
-
- All of the vendor information extensions defined in RFC 1497 [2] may
- be used as DHCP options. The definitions given in RFC 1497 are
- included in this document, which supersedes RFC 1497. All of the
- DHCP options defined in this document, except for those specific to
- DHCP as defined in section 9, may be used as BOOTP vendor information
- extensions.
-
-Table of Contents
-
- 1. Introduction .............................................. 2
- 2. BOOTP Extension/DHCP Option Field Format .................. 4
- 3. RFC 1497 Vendor Extensions ................................ 5
- 4. IP Layer Parameters per Host .............................. 11
- 5. IP Layer Parameters per Interface ........................ 13
- 6. Link Layer Parameters per Interface ....................... 16
- 7. TCP Parameters ............................................ 17
- 8. Application and Service Parameters ........................ 18
- 9. DHCP Extensions ........................................... 25
-
-
-
-Alexander & Droms Standards Track [Page 1]
-
-RFC 2132 DHCP Options and BOOTP Vendor Extensions March 1997
-
-
- 10. Defining new extensions ................................... 31
- 11. Acknowledgements .......................................... 31
- 12. References ................................................ 32
- 13. Security Considerations ................................... 33
- 14. Authors' Addresses ........................................ 34
-
-1. Introduction
-
- This document specifies options for use with both the Dynamic Host
- Configuration Protocol and the Bootstrap Protocol.
-
- The full description of DHCP packet formats may be found in the DHCP
- specification document [1], and the full description of BOOTP packet
- formats may be found in the BOOTP specification document [3]. This
- document defines the format of information in the last field of DHCP
- packets ('options') and of BOOTP packets ('vend'). The remainder of
- this section defines a generalized use of this area for giving
- information useful to a wide class of machines, operating systems and
- configurations. Sites with a single DHCP or BOOTP server that is
- shared among heterogeneous clients may choose to define other, site-
- specific formats for the use of the 'options' field.
-
- Section 2 of this memo describes the formats of DHCP options and
- BOOTP vendor extensions. Section 3 describes options defined in
- previous documents for use with BOOTP (all may also be used with
- DHCP). Sections 4-8 define new options intended for use with both
- DHCP and BOOTP. Section 9 defines options used only in DHCP.
-
- References further describing most of the options defined in sections
- 2-6 can be found in section 12. The use of the options defined in
- section 9 is described in the DHCP specification [1].
-
- Information on registering new options is contained in section 10.
-
- This document updates the definition of DHCP/BOOTP options that
- appears in RFC1533. The classing mechanism has been extended to
- include vendor classes as described in section 8.4 and 9.13. The new
- procedure for defining new DHCP/BOOTP options in described in section
- 10. Several new options, including NIS+ domain and servers, Mobile
- IP home agent, SMTP server, TFTP server and Bootfile server, have
- been added. Text giving definitions used throughout the document has
- been added in section 1.1. Text emphasizing the need for uniqueness
- of client-identifiers has been added to section 9.14.
-
-
-
-
-
-
-
-
-Alexander & Droms Standards Track [Page 2]
-
-RFC 2132 DHCP Options and BOOTP Vendor Extensions March 1997
-
-
-1.1 Requirements
-
- Throughout this document, the words that are used to define the
- significance of particular requirements are capitalized. These words
- are:
-
- o "MUST"
-
- This word or the adjective "REQUIRED" means that the item is an
- absolute requirement of this specification.
-
- o "MUST NOT"
-
- This phrase means that the item is an absolute prohibition of
- this specification.
-
- o "SHOULD"
-
- This word or the adjective "RECOMMENDED" means that there may
- exist valid reasons in particular circumstances to ignore this
- item, but the full implications should be understood and the case
- carefully weighed before choosing a different course.
-
- o "SHOULD NOT"
-
- This phrase means that there may exist valid reasons in
- particular circumstances when the listed behavior is acceptable
- or even useful, but the full implications should be understood
- and the case carefully weighed before implementing any behavior
- described with this label.
-
- o "MAY"
-
- This word or the adjective "OPTIONAL" means that this item is
- truly optional. One vendor may choose to include the item
- because a particular marketplace requires it or because it
- enhances the product, for example; another vendor may omit the
- same item.
-
-1.2 Terminology
-
- This document uses the following terms:
-
- o "DHCP client"
-
- A DHCP client or "client" is an Internet host using DHCP to
- obtain configuration parameters such as a network address.
-
-
-
-
-Alexander & Droms Standards Track [Page 3]
-
-RFC 2132 DHCP Options and BOOTP Vendor Extensions March 1997
-
-
- o "DHCP server"
-
- A DHCP server of "server"is an Internet host that returns
- configuration parameters to DHCP clients.
-
- o "binding"
-
- A binding is a collection of configuration parameters, including
- at least an IP address, associated with or "bound to" a DHCP
- client. Bindings are managed by DHCP servers.
-
-2. BOOTP Extension/DHCP Option Field Format
-
-
- DHCP options have the same format as the BOOTP 'vendor extensions'
- defined in RFC 1497 [2]. Options may be fixed length or variable
- length. All options begin with a tag octet, which uniquely
- identifies the option. Fixed-length options without data consist of
- only a tag octet. Only options 0 and 255 are fixed length. All
- other options are variable-length with a length octet following the
- tag octet. The value of the length octet does not include the two
- octets specifying the tag and length. The length octet is followed
- by "length" octets of data. Options containing NVT ASCII data SHOULD
- NOT include a trailing NULL; however, the receiver of such options
- MUST be prepared to delete trailing nulls if they exist. The
- receiver MUST NOT require that a trailing null be included in the
- data. In the case of some variable-length options the length field
- is a constant but must still be specified.
-
- Any options defined subsequent to this document MUST contain a length
- octet even if the length is fixed or zero.
-
- All multi-octet quantities are in network byte-order.
-
- When used with BOOTP, the first four octets of the vendor information
- field have been assigned to the "magic cookie" (as suggested in RFC
- 951). This field identifies the mode in which the succeeding data is
- to be interpreted. The value of the magic cookie is the 4 octet
- dotted decimal 99.130.83.99 (or hexadecimal number 63.82.53.63) in
- network byte order.
-
- All of the "vendor extensions" defined in RFC 1497 are also DHCP
- options.
-
- Option codes 128 to 254 (decimal) are reserved for site-specific
- options.
-
-
-
-
-
-Alexander & Droms Standards Track [Page 4]
-
-RFC 2132 DHCP Options and BOOTP Vendor Extensions March 1997
-
-
- Except for the options in section 9, all options may be used with
- either DHCP or BOOTP.
-
- Many of these options have their default values specified in other
- documents. In particular, RFC 1122 [4] specifies default values for
- most IP and TCP configuration parameters.
-
- Many options supply one or more 32-bit IP address. Use of IP
- addresses rather than fully-qualified Domain Names (FQDNs) may make
- future renumbering of IP hosts more difficult. Use of these
- addresses is discouraged at sites that may require renumbering.
-
-3. RFC 1497 Vendor Extensions
-
- This section lists the vendor extensions as defined in RFC 1497.
- They are defined here for completeness.
-
-3.1. Pad Option
-
- The pad option can be used to cause subsequent fields to align on
- word boundaries.
-
- The code for the pad option is 0, and its length is 1 octet.
-
- Code
- +-----+
- | 0 |
- +-----+
-
-3.2. End Option
-
- The end option marks the end of valid information in the vendor
- field. Subsequent octets should be filled with pad options.
-
- The code for the end option is 255, and its length is 1 octet.
-
- Code
- +-----+
- | 255 |
- +-----+
-
-3.3. Subnet Mask
-
- The subnet mask option specifies the client's subnet mask as per RFC
- 950 [5].
-
- If both the subnet mask and the router option are specified in a DHCP
- reply, the subnet mask option MUST be first.
-
-
-
-Alexander & Droms Standards Track [Page 5]
-
-RFC 2132 DHCP Options and BOOTP Vendor Extensions March 1997
-
-
- The code for the subnet mask option is 1, and its length is 4 octets.
-
- Code Len Subnet Mask
- +-----+-----+-----+-----+-----+-----+
- | 1 | 4 | m1 | m2 | m3 | m4 |
- +-----+-----+-----+-----+-----+-----+
-
-3.4. Time Offset
-
- The time offset field specifies the offset of the client's subnet in
- seconds from Coordinated Universal Time (UTC). The offset is
- expressed as a two's complement 32-bit integer. A positive offset
- indicates a location east of the zero meridian and a negative offset
- indicates a location west of the zero meridian.
-
- The code for the time offset option is 2, and its length is 4 octets.
-
- Code Len Time Offset
- +-----+-----+-----+-----+-----+-----+
- | 2 | 4 | n1 | n2 | n3 | n4 |
- +-----+-----+-----+-----+-----+-----+
-
-3.5. Router Option
-
- The router option specifies a list of IP addresses for routers on the
- client's subnet. Routers SHOULD be listed in order of preference.
-
- The code for the router option is 3. The minimum length for the
- router option is 4 octets, and the length MUST always be a multiple
- of 4.
-
- Code Len Address 1 Address 2
- +-----+-----+-----+-----+-----+-----+-----+-----+--
- | 3 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
- +-----+-----+-----+-----+-----+-----+-----+-----+--
-
-3.6. Time Server Option
-
- The time server option specifies a list of RFC 868 [6] time servers
- available to the client. Servers SHOULD be listed in order of
- preference.
-
- The code for the time server option is 4. The minimum length for
- this option is 4 octets, and the length MUST always be a multiple of
- 4.
-
-
-
-
-
-
-Alexander & Droms Standards Track [Page 6]
-
-RFC 2132 DHCP Options and BOOTP Vendor Extensions March 1997
-
-
- Code Len Address 1 Address 2
- +-----+-----+-----+-----+-----+-----+-----+-----+--
- | 4 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
- +-----+-----+-----+-----+-----+-----+-----+-----+--
-
-3.7. Name Server Option
-
- The name server option specifies a list of IEN 116 [7] name servers
- available to the client. Servers SHOULD be listed in order of
- preference.
-
- The code for the name server option is 5. The minimum length for
- this option is 4 octets, and the length MUST always be a multiple of
- 4.
-
- Code Len Address 1 Address 2
- +-----+-----+-----+-----+-----+-----+-----+-----+--
- | 5 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
- +-----+-----+-----+-----+-----+-----+-----+-----+--
-
-3.8. Domain Name Server Option
-
- The domain name server option specifies a list of Domain Name System
- (STD 13, RFC 1035 [8]) name servers available to the client. Servers
- SHOULD be listed in order of preference.
-
- The code for the domain name server option is 6. The minimum length
- for this option is 4 octets, and the length MUST always be a multiple
- of 4.
-
- Code Len Address 1 Address 2
- +-----+-----+-----+-----+-----+-----+-----+-----+--
- | 6 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
- +-----+-----+-----+-----+-----+-----+-----+-----+--
-
-3.9. Log Server Option
-
- 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.
-
- The code for the log server option is 7. The minimum length for this
- option is 4 octets, and the length MUST always be a multiple of 4.
-
- Code Len Address 1 Address 2
- +-----+-----+-----+-----+-----+-----+-----+-----+--
- | 7 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
- +-----+-----+-----+-----+-----+-----+-----+-----+--
-
-
-
-Alexander & Droms Standards Track [Page 7]
-
-RFC 2132 DHCP Options and BOOTP Vendor Extensions March 1997
-
-
-3.10. Cookie Server Option
-
- The cookie server option specifies a list of RFC 865 [9] cookie
- servers available to the client. Servers SHOULD be listed in order
- of preference.
-
- The code for the log server option is 8. The minimum length for this
- option is 4 octets, and the length MUST always be a multiple of 4.
-
- Code Len Address 1 Address 2
- +-----+-----+-----+-----+-----+-----+-----+-----+--
- | 8 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
- +-----+-----+-----+-----+-----+-----+-----+-----+--
-
-3.11. LPR Server Option
-
- The LPR server option specifies a list of RFC 1179 [10] line printer
- servers available to the client. Servers SHOULD be listed in order
- of preference.
-
- The code for the LPR server option is 9. The minimum length for this
- option is 4 octets, and the length MUST always be a multiple of 4.
-
- Code Len Address 1 Address 2
- +-----+-----+-----+-----+-----+-----+-----+-----+--
- | 9 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
- +-----+-----+-----+-----+-----+-----+-----+-----+--
-
-3.12. Impress Server Option
-
- The Impress server option specifies a list of Imagen Impress servers
- available to the client. Servers SHOULD be listed in order of
- preference.
-
- The code for the Impress server option is 10. The minimum length for
- this option is 4 octets, and the length MUST always be a multiple of
- 4.
-
- Code Len Address 1 Address 2
- +-----+-----+-----+-----+-----+-----+-----+-----+--
- | 10 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
- +-----+-----+-----+-----+-----+-----+-----+-----+--
-
-3.13. Resource Location Server Option
-
- This option specifies a list of RFC 887 [11] Resource Location
- servers available to the client. Servers SHOULD be listed in order
- of preference.
-
-
-
-Alexander & Droms Standards Track [Page 8]
-
-RFC 2132 DHCP Options and BOOTP Vendor Extensions March 1997
-
-
- The code for this option is 11. The minimum length for this option
- is 4 octets, and the length MUST always be a multiple of 4.
-
- Code Len Address 1 Address 2
- +-----+-----+-----+-----+-----+-----+-----+-----+--
- | 11 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
- +-----+-----+-----+-----+-----+-----+-----+-----+--
-
-3.14. Host Name Option
-
- This option specifies the name of the client. The name may or may
- not be qualified with the local domain name (see section 3.17 for the
- preferred way to retrieve the domain name). See RFC 1035 for
- character set restrictions.
-
- The code for this option is 12, and its minimum length is 1.
-
- Code Len Host Name
- +-----+-----+-----+-----+-----+-----+-----+-----+--
- | 12 | n | h1 | h2 | h3 | h4 | h5 | h6 | ...
- +-----+-----+-----+-----+-----+-----+-----+-----+--
-
-3.15. Boot File Size Option
-
- This option specifies the length in 512-octet blocks of the default
- boot image for the client. The file length is specified as an
- unsigned 16-bit integer.
-
- The code for this option is 13, and its length is 2.
-
- Code Len File Size
- +-----+-----+-----+-----+
- | 13 | 2 | l1 | l2 |
- +-----+-----+-----+-----+
-
-3.16. Merit Dump File
-
- 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.
-
- The code for this option is 14. Its minimum length is 1.
-
- Code Len Dump File Pathname
- +-----+-----+-----+-----+-----+-----+---
- | 14 | n | n1 | n2 | n3 | n4 | ...
- +-----+-----+-----+-----+-----+-----+---
-
-
-
-Alexander & Droms Standards Track [Page 9]
-
-RFC 2132 DHCP Options and BOOTP Vendor Extensions March 1997
-
-
-3.17. Domain Name
-
- This option specifies the domain name that client should use when
- resolving hostnames via the Domain Name System.
-
- The code for this option is 15. Its minimum length is 1.
-
- Code Len Domain Name
- +-----+-----+-----+-----+-----+-----+--
- | 15 | n | d1 | d2 | d3 | d4 | ...
- +-----+-----+-----+-----+-----+-----+--
-
-3.18. Swap Server
-
- This specifies the IP address of the client's swap server.
-
- The code for this option is 16 and its length is 4.
-
- Code Len Swap Server Address
- +-----+-----+-----+-----+-----+-----+
- | 16 | n | a1 | a2 | a3 | a4 |
- +-----+-----+-----+-----+-----+-----+
-
-3.19. Root Path
-
- 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.
-
- The code for this option is 17. Its minimum length is 1.
-
- Code Len Root Disk Pathname
- +-----+-----+-----+-----+-----+-----+---
- | 17 | n | n1 | n2 | n3 | n4 | ...
- +-----+-----+-----+-----+-----+-----+---
-
-3.20. Extensions Path
-
- A string to specify a file, retrievable via TFTP, which contains
- information which can be interpreted in the same way as the 64-octet
- vendor-extension field within the BOOTP response, with the following
- exceptions:
-
- - the length of the file is unconstrained;
- - all references to Tag 18 (i.e., instances of the
- BOOTP Extensions Path field) within the file are
- ignored.
-
-
-
-
-Alexander & Droms Standards Track [Page 10]
-
-RFC 2132 DHCP Options and BOOTP Vendor Extensions March 1997
-
-
- The code for this option is 18. Its minimum length is 1.
-
- Code Len Extensions Pathname
- +-----+-----+-----+-----+-----+-----+---
- | 18 | n | n1 | n2 | n3 | n4 | ...
- +-----+-----+-----+-----+-----+-----+---
-
-4. IP Layer Parameters per Host
-
- This section details the options that affect the operation of the IP
- layer on a per-host basis.
-
-4.1. IP Forwarding Enable/Disable Option
-
- This option specifies whether the client should configure its IP
- layer for packet forwarding. A value of 0 means disable IP
- forwarding, and a value of 1 means enable IP forwarding.
-
- The code for this option is 19, and its length is 1.
-
- Code Len Value
- +-----+-----+-----+
- | 19 | 1 | 0/1 |
- +-----+-----+-----+
-
-4.2. Non-Local Source Routing Enable/Disable Option
-
- 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 0 means disallow forwarding of such datagrams, and a value of 1
- means allow forwarding.
-
- The code for this option is 20, and its length is 1.
-
- Code Len Value
- +-----+-----+-----+
- | 20 | 1 | 0/1 |
- +-----+-----+-----+
-
-4.3. Policy Filter Option
-
- 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.
-
- Any source routed datagram whose next-hop address does not match one
- of the filters should be discarded by the client.
-
-
-
-Alexander & Droms Standards Track [Page 11]
-
-RFC 2132 DHCP Options and BOOTP Vendor Extensions March 1997
-
-
- See [4] for further information.
-
- The code for this option is 21. The minimum length of this option is
- 8, and the length MUST be a multiple of 8.
-
- Code Len Address 1 Mask 1
- +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
- | 21 | n | a1 | a2 | a3 | a4 | m1 | m2 | m3 | m4 |
- +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
- Address 2 Mask 2
- +-----+-----+-----+-----+-----+-----+-----+-----+---
- | a1 | a2 | a3 | a4 | m1 | m2 | m3 | m4 | ...
- +-----+-----+-----+-----+-----+-----+-----+-----+---
-
-4.4. Maximum Datagram Reassembly Size
-
- This option specifies the maximum size datagram that the client
- should be prepared to reassemble. The size is specified as a 16-bit
- unsigned integer. The minimum value legal value is 576.
-
- The code for this option is 22, and its length is 2.
-
- Code Len Size
- +-----+-----+-----+-----+
- | 22 | 2 | s1 | s2 |
- +-----+-----+-----+-----+
-
-4.5. Default IP Time-to-live
-
- This option specifies the default time-to-live that the client should
- use on outgoing datagrams. The TTL is specified as an octet with a
- value between 1 and 255.
-
- The code for this option is 23, and its length is 1.
-
- Code Len TTL
- +-----+-----+-----+
- | 23 | 1 | ttl |
- +-----+-----+-----+
-
-4.6. Path MTU Aging Timeout Option
-
- This option specifies the timeout (in seconds) to use when aging Path
- MTU values discovered by the mechanism defined in RFC 1191 [12]. The
- timeout is specified as a 32-bit unsigned integer.
-
- The code for this option is 24, and its length is 4.
-
-
-
-
-Alexander & Droms Standards Track [Page 12]
-
-RFC 2132 DHCP Options and BOOTP Vendor Extensions March 1997
-
-
- Code Len Timeout
- +-----+-----+-----+-----+-----+-----+
- | 24 | 4 | t1 | t2 | t3 | t4 |
- +-----+-----+-----+-----+-----+-----+
-
-4.7. Path MTU Plateau Table Option
-
- 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.
-
- The code for this option is 25. Its minimum length is 2, and the
- length MUST be a multiple of 2.
-
- Code Len Size 1 Size 2
- +-----+-----+-----+-----+-----+-----+---
- | 25 | n | s1 | s2 | s1 | s2 | ...
- +-----+-----+-----+-----+-----+-----+---
-
-5. IP Layer Parameters per Interface
-
- This section details the options that affect the operation of the IP
- layer on a per-interface basis. It is expected that a client can
- issue multiple requests, one per interface, in order to configure
- interfaces with their specific parameters.
-
-5.1. Interface MTU Option
-
- This option specifies the MTU to use on this interface. The MTU is
- specified as a 16-bit unsigned integer. The minimum legal value for
- the MTU is 68.
-
- The code for this option is 26, and its length is 2.
-
- Code Len MTU
- +-----+-----+-----+-----+
- | 26 | 2 | m1 | m2 |
- +-----+-----+-----+-----+
-
-5.2. All Subnets are Local Option
-
- 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 1 indicates that all subnets share
- the same MTU. A value of 0 means that the client should assume that
- some subnets of the directly connected network may have smaller MTUs.
-
-
-
-Alexander & Droms Standards Track [Page 13]
-
-RFC 2132 DHCP Options and BOOTP Vendor Extensions March 1997
-
-
- The code for this option is 27, and its length is 1.
-
- Code Len Value
- +-----+-----+-----+
- | 27 | 1 | 0/1 |
- +-----+-----+-----+
-
-5.3. Broadcast Address Option
-
- 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 [4].
-
- The code for this option is 28, and its length is 4.
-
- Code Len Broadcast Address
- +-----+-----+-----+-----+-----+-----+
- | 28 | 4 | b1 | b2 | b3 | b4 |
- +-----+-----+-----+-----+-----+-----+
-
-5.4. Perform Mask Discovery Option
-
- This option specifies whether or not the client should perform subnet
- mask discovery using ICMP. A value of 0 indicates that the client
- should not perform mask discovery. A value of 1 means that the
- client should perform mask discovery.
-
- The code for this option is 29, and its length is 1.
-
- Code Len Value
- +-----+-----+-----+
- | 29 | 1 | 0/1 |
- +-----+-----+-----+
-
-5.5. Mask Supplier Option
-
- This option specifies whether or not the client should respond to
- subnet mask requests using ICMP. A value of 0 indicates that the
- client should not respond. A value of 1 means that the client should
- respond.
-
- The code for this option is 30, and its length is 1.
-
- Code Len Value
- +-----+-----+-----+
- | 30 | 1 | 0/1 |
- +-----+-----+-----+
-
-
-
-
-Alexander & Droms Standards Track [Page 14]
-
-RFC 2132 DHCP Options and BOOTP Vendor Extensions March 1997
-
-
-5.6. Perform Router Discovery Option
-
- This option specifies whether or not the client should solicit
- routers using the Router Discovery mechanism defined in RFC 1256
- [13]. A value of 0 indicates that the client should not perform
- router discovery. A value of 1 means that the client should perform
- router discovery.
-
- The code for this option is 31, and its length is 1.
-
- Code Len Value
- +-----+-----+-----+
- | 31 | 1 | 0/1 |
- +-----+-----+-----+
-
-5.7. Router Solicitation Address Option
-
- This option specifies the address to which the client should transmit
- router solicitation requests.
-
- The code for this option is 32, and its length is 4.
-
- Code Len Address
- +-----+-----+-----+-----+-----+-----+
- | 32 | 4 | a1 | a2 | a3 | a4 |
- +-----+-----+-----+-----+-----+-----+
-
-5.8. Static Route Option
-
- 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.
-
- 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.
-
- The default route (0.0.0.0) is an illegal destination for a static
- route. See section 3.5 for information about the router option.
-
- The code for this option is 33. The minimum length of this option is
- 8, and the length MUST be a multiple of 8.
-
-
-
-
-
-
-
-
-Alexander & Droms Standards Track [Page 15]
-
-RFC 2132 DHCP Options and BOOTP Vendor Extensions March 1997
-
-
- Code Len Destination 1 Router 1
- +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
- | 33 | n | d1 | d2 | d3 | d4 | r1 | r2 | r3 | r4 |
- +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
- Destination 2 Router 2
- +-----+-----+-----+-----+-----+-----+-----+-----+---
- | d1 | d2 | d3 | d4 | r1 | r2 | r3 | r4 | ...
- +-----+-----+-----+-----+-----+-----+-----+-----+---
-
-6. Link Layer Parameters per Interface
-
- This section lists the options that affect the operation of the data
- link layer on a per-interface basis.
-
-6.1. Trailer Encapsulation Option
-
- 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 0 indicates that the client should not attempt to use trailers. A
- value of 1 means that the client should attempt to use trailers.
-
- The code for this option is 34, and its length is 1.
-
- Code Len Value
- +-----+-----+-----+
- | 34 | 1 | 0/1 |
- +-----+-----+-----+
-
-6.2. ARP Cache Timeout Option
-
- This option specifies the timeout in seconds for ARP cache entries.
- The time is specified as a 32-bit unsigned integer.
-
- The code for this option is 35, and its length is 4.
-
- Code Len Time
- +-----+-----+-----+-----+-----+-----+
- | 35 | 4 | t1 | t2 | t3 | t4 |
- +-----+-----+-----+-----+-----+-----+
-
-6.3. Ethernet Encapsulation Option
-
- This option specifies whether or not the client should use Ethernet
- Version 2 (RFC 894 [15]) or IEEE 802.3 (RFC 1042 [16]) encapsulation
- if the interface is an Ethernet. A value of 0 indicates that the
- client should use RFC 894 encapsulation. A value of 1 means that the
- client should use RFC 1042 encapsulation.
-
-
-
-
-Alexander & Droms Standards Track [Page 16]
-
-RFC 2132 DHCP Options and BOOTP Vendor Extensions March 1997
-
-
- The code for this option is 36, and its length is 1.
-
- Code Len Value
- +-----+-----+-----+
- | 36 | 1 | 0/1 |
- +-----+-----+-----+
-
-7. TCP Parameters
-
- This section lists the options that affect the operation of the TCP
- layer on a per-interface basis.
-
-7.1. TCP Default TTL Option
-
- This option specifies the default TTL that the client should use when
- sending TCP segments. The value is represented as an 8-bit unsigned
- integer. The minimum value is 1.
-
- The code for this option is 37, and its length is 1.
-
- Code Len TTL
- +-----+-----+-----+
- | 37 | 1 | n |
- +-----+-----+-----+
-
-7.2. TCP Keepalive Interval Option
-
- 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.
-
- The code for this option is 38, and its length is 4.
-
- Code Len Time
- +-----+-----+-----+-----+-----+-----+
- | 38 | 4 | t1 | t2 | t3 | t4 |
- +-----+-----+-----+-----+-----+-----+
-
-7.3. TCP Keepalive Garbage Option
-
- This option specifies the whether or not the client should send TCP
- keepalive messages with a octet of garbage for compatibility with
- older implementations. A value of 0 indicates that a garbage octet
- should not be sent. A value of 1 indicates that a garbage octet
- should be sent.
-
-
-
-
-Alexander & Droms Standards Track [Page 17]
-
-RFC 2132 DHCP Options and BOOTP Vendor Extensions March 1997
-
-
- The code for this option is 39, and its length is 1.
-
- Code Len Value
- +-----+-----+-----+
- | 39 | 1 | 0/1 |
- +-----+-----+-----+
-
-8. Application and Service Parameters
-
- This section details some miscellaneous options used to configure
- miscellaneous applications and services.
-
-8.1. Network Information Service Domain Option
-
- This option specifies the name of the client's NIS [17] domain. The
- domain is formatted as a character string consisting of characters
- from the NVT ASCII character set.
-
- The code for this option is 40. Its minimum length is 1.
-
- Code Len NIS Domain Name
- +-----+-----+-----+-----+-----+-----+---
- | 40 | n | n1 | n2 | n3 | n4 | ...
- +-----+-----+-----+-----+-----+-----+---
-
-8.2. Network Information Servers Option
-
- This option specifies a list of IP addresses indicating NIS servers
- available to the client. Servers SHOULD be listed in order of
- preference.
-
- The code for this option is 41. Its minimum length is 4, and the
- length MUST be a multiple of 4.
-
- Code Len Address 1 Address 2
- +-----+-----+-----+-----+-----+-----+-----+-----+--
- | 41 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
- +-----+-----+-----+-----+-----+-----+-----+-----+--
-
-8.3. Network Time Protocol Servers Option
-
- This option specifies a list of IP addresses indicating NTP [18]
- servers available to the client. Servers SHOULD be listed in order
- of preference.
-
- The code for this option is 42. Its minimum length is 4, and the
- length MUST be a multiple of 4.
-
-
-
-
-Alexander & Droms Standards Track [Page 18]
-
-RFC 2132 DHCP Options and BOOTP Vendor Extensions March 1997
-
-
- Code Len Address 1 Address 2
- +-----+-----+-----+-----+-----+-----+-----+-----+--
- | 42 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
- +-----+-----+-----+-----+-----+-----+-----+-----+--
-
-8.4. Vendor Specific Information
-
- This option is used by clients and servers to exchange vendor-
- specific information. The information is an opaque object of n
- octets, presumably interpreted by vendor-specific code on the clients
- and servers. The definition of this information is vendor specific.
- The vendor is indicated in the vendor class identifier option.
- Servers not equipped to interpret the vendor-specific information
- sent by a client MUST ignore it (although it may be reported).
- Clients which do not receive desired vendor-specific information
- SHOULD make an attempt to operate without it, although they may do so
- (and announce they are doing so) in a degraded mode.
-
- If a vendor potentially encodes more than one item of information in
- this option, then the vendor SHOULD encode the option using
- "Encapsulated vendor-specific options" as described below:
-
- The Encapsulated vendor-specific options field SHOULD be encoded as a
- sequence of code/length/value fields of identical syntax to the DHCP
- options field with the following exceptions:
-
- 1) There SHOULD NOT be a "magic cookie" field in the encapsulated
- vendor-specific extensions field.
-
- 2) Codes other than 0 or 255 MAY be redefined by the vendor within
- the encapsulated vendor-specific extensions field, but SHOULD
- conform to the tag-length-value syntax defined in section 2.
-
- 3) Code 255 (END), if present, signifies the end of the
- encapsulated vendor extensions, not the end of the vendor
- extensions field. If no code 255 is present, then the end of
- the enclosing vendor-specific information field is taken as the
- end of the encapsulated vendor-specific extensions field.
-
- The code for this option is 43 and its minimum length is 1.
-
- Code Len Vendor-specific information
- +-----+-----+-----+-----+---
- | 43 | n | i1 | i2 | ...
- +-----+-----+-----+-----+---
-
-
-
-
-
-
-Alexander & Droms Standards Track [Page 19]
-
-RFC 2132 DHCP Options and BOOTP Vendor Extensions March 1997
-
-
- When encapsulated vendor-specific extensions are used, the
- information bytes 1-n have the following format:
-
- Code Len Data item Code Len Data item Code
- +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
- | T1 | n | d1 | d2 | ... | T2 | n | D1 | D2 | ... | ... |
- +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
-
-8.5. NetBIOS over TCP/IP Name Server Option
-
- The NetBIOS name server (NBNS) option specifies a list of RFC
- 1001/1002 [19] [20] NBNS name servers listed in order of preference.
-
- The code for this option is 44. The minimum length of the option is
- 4 octets, and the length must always be a multiple of 4.
-
- Code Len Address 1 Address 2
- +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+----
- | 44 | n | a1 | a2 | a3 | a4 | b1 | b2 | b3 | b4 | ...
- +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+----
-
-8.6. NetBIOS over TCP/IP Datagram Distribution Server Option
-
- The NetBIOS datagram distribution server (NBDD) option specifies a
- list of RFC 1001/1002 NBDD servers listed in order of preference. The
- code for this option is 45. The minimum length of the option is 4
- octets, and the length must always be a multiple of 4.
-
- Code Len Address 1 Address 2
- +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+----
- | 45 | n | a1 | a2 | a3 | a4 | b1 | b2 | b3 | b4 | ...
- +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+----
-
-8.7. NetBIOS over TCP/IP Node Type Option
-
- 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
- as follows:
-
- Value Node Type
- ----- ---------
- 0x1 B-node
- 0x2 P-node
- 0x4 M-node
- 0x8 H-node
-
-
-
-
-
-Alexander & Droms Standards Track [Page 20]
-
-RFC 2132 DHCP Options and BOOTP Vendor Extensions March 1997
-
-
- In the above chart, the notation '0x' indicates a number in base-16
- (hexadecimal).
-
- The code for this option is 46. The length of this option is always
- 1.
-
- Code Len Node Type
- +-----+-----+-----------+
- | 46 | 1 | see above |
- +-----+-----+-----------+
-
-8.8. NetBIOS over TCP/IP Scope Option
-
- The NetBIOS scope option specifies the NetBIOS over TCP/IP scope
- parameter for the client as specified in RFC 1001/1002. See [19],
- [20], and [8] for character-set restrictions.
-
- The code for this option is 47. The minimum length of this option is
- 1.
-
- Code Len NetBIOS Scope
- +-----+-----+-----+-----+-----+-----+----
- | 47 | n | s1 | s2 | s3 | s4 | ...
- +-----+-----+-----+-----+-----+-----+----
-
-8.9. X Window System Font Server Option
-
- This option specifies a list of X Window System [21] Font servers
- available to the client. Servers SHOULD be listed in order of
- preference.
-
- The code for this option is 48. The minimum length of this option is
- 4 octets, and the length MUST be a multiple of 4.
-
- Code Len Address 1 Address 2
- +-----+-----+-----+-----+-----+-----+-----+-----+---
- | 48 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
- +-----+-----+-----+-----+-----+-----+-----+-----+---
-
-8.10. X Window System Display Manager Option
-
- This option specifies a list of IP addresses 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.
-
-
-
-
-
-Alexander & Droms Standards Track [Page 21]
-
-RFC 2132 DHCP Options and BOOTP Vendor Extensions March 1997
-
-
- The code for the this option is 49. The minimum length of this option
- is 4, and the length MUST be a multiple of 4.
-
- Code Len Address 1 Address 2
-
- +-----+-----+-----+-----+-----+-----+-----+-----+---
- | 49 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
- +-----+-----+-----+-----+-----+-----+-----+-----+---
-
-8.11. Network Information Service+ Domain Option
-
- This option specifies the name of the client's NIS+ [17] domain. The
- domain is formatted as a character string consisting of characters
- from the NVT ASCII character set.
-
- The code for this option is 64. Its minimum length is 1.
-
- Code Len NIS Client Domain Name
- +-----+-----+-----+-----+-----+-----+---
- | 64 | n | n1 | n2 | n3 | n4 | ...
- +-----+-----+-----+-----+-----+-----+---
-
-8.12. Network Information Service+ Servers Option
-
- This option specifies a list of IP addresses indicating NIS+ servers
- available to the client. Servers SHOULD be listed in order of
- preference.
-
- The code for this option is 65. Its minimum length is 4, and the
- length MUST be a multiple of 4.
-
- Code Len Address 1 Address 2
- +-----+-----+-----+-----+-----+-----+-----+-----+--
- | 65 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
- +-----+-----+-----+-----+-----+-----+-----+-----+--
-
-8.13. Mobile IP Home Agent option
-
- 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.
-
- The code for this option is 68. Its minimum length is 0 (indicating
- no home agents are available) and the length MUST be a multiple of 4.
- It is expected that the usual length will be four octets, containing
- a single home agent's address.
-
-
-
-
-
-Alexander & Droms Standards Track [Page 22]
-
-RFC 2132 DHCP Options and BOOTP Vendor Extensions March 1997
-
-
- Code Len Home Agent Addresses (zero or more)
- +-----+-----+-----+-----+-----+-----+--
- | 68 | n | a1 | a2 | a3 | a4 | ...
- +-----+-----+-----+-----+-----+-----+--
-
-8.14. Simple Mail Transport Protocol (SMTP) Server Option
-
- The SMTP server option specifies a list of SMTP servers available to
- the client. Servers SHOULD be listed in order of preference.
-
- The code for the SMTP server option is 69. The minimum length for
- this option is 4 octets, and the length MUST always be a multiple of
- 4.
-
- Code Len Address 1 Address 2
- +-----+-----+-----+-----+-----+-----+-----+-----+--
- | 69 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
- +-----+-----+-----+-----+-----+-----+-----+-----+--
-
-8.15. Post Office Protocol (POP3) Server Option
-
- The POP3 server option specifies a list of POP3 available to the
- client. Servers SHOULD be listed in order of preference.
-
- The code for the POP3 server option is 70. The minimum length for
- this option is 4 octets, and the length MUST always be a multiple of
- 4.
-
- Code Len Address 1 Address 2
- +-----+-----+-----+-----+-----+-----+-----+-----+--
- | 70 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
- +-----+-----+-----+-----+-----+-----+-----+-----+--
-
-8.16. Network News Transport Protocol (NNTP) Server Option
-
- The NNTP server option specifies a list of NNTP available to the
- client. Servers SHOULD be listed in order of preference.
-
- The code for the NNTP server option is 71. The minimum length for
- this option is 4 octets, and the length MUST always be a multiple of
- 4.
-
- Code Len Address 1 Address 2
- +-----+-----+-----+-----+-----+-----+-----+-----+--
- | 71 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
- +-----+-----+-----+-----+-----+-----+-----+-----+--
-
-
-
-
-
-Alexander & Droms Standards Track [Page 23]
-
-RFC 2132 DHCP Options and BOOTP Vendor Extensions March 1997
-
-
-8.17. Default World Wide Web (WWW) Server Option
-
- The WWW server option specifies a list of WWW available to the
- client. Servers SHOULD be listed in order of preference.
-
- The code for the WWW server option is 72. The minimum length for
- this option is 4 octets, and the length MUST always be a multiple of
- 4.
-
- Code Len Address 1 Address 2
- +-----+-----+-----+-----+-----+-----+-----+-----+--
- | 72 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
- +-----+-----+-----+-----+-----+-----+-----+-----+--
-
-8.18. Default Finger Server Option
-
- The Finger server option specifies a list of Finger available to the
- client. Servers SHOULD be listed in order of preference.
-
- The code for the Finger server option is 73. The minimum length for
- this option is 4 octets, and the length MUST always be a multiple of
- 4.
-
- Code Len Address 1 Address 2
- +-----+-----+-----+-----+-----+-----+-----+-----+--
- | 73 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
- +-----+-----+-----+-----+-----+-----+-----+-----+--
-
-8.19. Default Internet Relay Chat (IRC) Server Option
-
- The IRC server option specifies a list of IRC available to the
- client. Servers SHOULD be listed in order of preference.
-
- The code for the IRC server option is 74. The minimum length for
- this option is 4 octets, and the length MUST always be a multiple of
- 4.
-
- Code Len Address 1 Address 2
- +-----+-----+-----+-----+-----+-----+-----+-----+--
- | 74 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
- +-----+-----+-----+-----+-----+-----+-----+-----+--
-
-8.20. StreetTalk Server Option
-
- The StreetTalk server option specifies a list of StreetTalk servers
- available to the client. Servers SHOULD be listed in order of
- preference.
-
-
-
-
-Alexander & Droms Standards Track [Page 24]
-
-RFC 2132 DHCP Options and BOOTP Vendor Extensions March 1997
-
-
- The code for the StreetTalk server option is 75. The minimum length
- for this option is 4 octets, and the length MUST always be a multiple
- of 4.
-
- Code Len Address 1 Address 2
- +-----+-----+-----+-----+-----+-----+-----+-----+--
- | 75 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
- +-----+-----+-----+-----+-----+-----+-----+-----+--
-
-8.21. StreetTalk Directory Assistance (STDA) Server Option
-
- 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.
-
- The code for the StreetTalk Directory Assistance server option is 76.
- The minimum length for this option is 4 octets, and the length MUST
- always be a multiple of 4.
-
- Code Len Address 1 Address 2
- +-----+-----+-----+-----+-----+-----+-----+-----+--
- | 76 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
- +-----+-----+-----+-----+-----+-----+-----+-----+--
-
-9. DHCP Extensions
-
- This section details the options that are specific to DHCP.
-
-9.1. Requested IP Address
-
- This option is used in a client request (DHCPDISCOVER) to allow the
- client to request that a particular IP address be assigned.
-
- The code for this option is 50, and its length is 4.
-
- Code Len Address
- +-----+-----+-----+-----+-----+-----+
- | 50 | 4 | a1 | a2 | a3 | a4 |
- +-----+-----+-----+-----+-----+-----+
-
-9.2. IP Address Lease Time
-
- 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.
-
-
-
-
-
-Alexander & Droms Standards Track [Page 25]
-
-RFC 2132 DHCP Options and BOOTP Vendor Extensions March 1997
-
-
- The time is in units of seconds, and is specified as a 32-bit
- unsigned integer.
-
- The code for this option is 51, and its length is 4.
-
- Code Len Lease Time
- +-----+-----+-----+-----+-----+-----+
- | 51 | 4 | t1 | t2 | t3 | t4 |
- +-----+-----+-----+-----+-----+-----+
-
-9.3. Option Overload
-
- 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.
-
- If this option is present, the client interprets the specified
- additional fields after it concludes interpretation of the standard
- option fields.
-
- The code for this option is 52, and its length is 1. Legal values
- for this option are:
-
- Value Meaning
- ----- --------
- 1 the 'file' field is used to hold options
- 2 the 'sname' field is used to hold options
- 3 both fields are used to hold options
-
- Code Len Value
- +-----+-----+-----+
- | 52 | 1 |1/2/3|
- +-----+-----+-----+
-
-9.4 TFTP server name
-
- This option is used to identify a TFTP server when the 'sname' field
- in the DHCP header has been used for DHCP options.
-
- The code for this option is 66, and its minimum length is 1.
-
- Code Len TFTP server
- +-----+-----+-----+-----+-----+---
- | 66 | n | c1 | c2 | c3 | ...
- +-----+-----+-----+-----+-----+---
-
-
-
-
-
-Alexander & Droms Standards Track [Page 26]
-
-RFC 2132 DHCP Options and BOOTP Vendor Extensions March 1997
-
-
-9.5 Bootfile name
-
- This option is used to identify a bootfile when the 'file' field in
- the DHCP header has been used for DHCP options.
-
- The code for this option is 67, and its minimum length is 1.
-
- Code Len Bootfile name
- +-----+-----+-----+-----+-----+---
- | 67 | n | c1 | c2 | c3 | ...
- +-----+-----+-----+-----+-----+---
-
-9.6. DHCP Message Type
-
- This option is used to convey the type of the DHCP message. The code
- for this option is 53, and its length is 1. Legal values for this
- option are:
-
- Value Message Type
- ----- ------------
- 1 DHCPDISCOVER
- 2 DHCPOFFER
- 3 DHCPREQUEST
- 4 DHCPDECLINE
- 5 DHCPACK
- 6 DHCPNAK
- 7 DHCPRELEASE
- 8 DHCPINFORM
-
- Code Len Type
- +-----+-----+-----+
- | 53 | 1 | 1-9 |
- +-----+-----+-----+
-
-9.7. Server Identifier
-
- 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.
-
- The identifier is the IP address of the selected server.
-
- The code for this option is 54, and its length is 4.
-
-
-
-Alexander & Droms Standards Track [Page 27]
-
-RFC 2132 DHCP Options and BOOTP Vendor Extensions March 1997
-
-
- Code Len Address
- +-----+-----+-----+-----+-----+-----+
- | 54 | 4 | a1 | a2 | a3 | a4 |
- +-----+-----+-----+-----+-----+-----+
-
-9.8. Parameter Request List
-
- This option is used by a DHCP client to request values for specified
- configuration parameters. The list of requested parameters is
- specified as n octets, where each octet is a valid DHCP option code
- as defined in this document.
-
- The client MAY list the options in order of preference. The DHCP
- server is not required to return the options in the requested order,
- but MUST try to insert the requested options in the order requested
- by the client.
-
- The code for this option is 55. Its minimum length is 1.
-
- Code Len Option Codes
- +-----+-----+-----+-----+---
- | 55 | n | c1 | c2 | ...
- +-----+-----+-----+-----+---
-
-9.9. Message
-
- 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 the why the
- client declined the offered parameters. The message consists of n
- octets of NVT ASCII text, which the client may display on an
- available output device.
-
- The code for this option is 56 and its minimum length is 1.
-
- Code Len Text
- +-----+-----+-----+-----+---
- | 56 | n | c1 | c2 | ...
- +-----+-----+-----+-----+---
-
-9.10. Maximum DHCP Message Size
-
- This option specifies the maximum length DHCP message that it is
- willing to accept. The length is specified as an unsigned 16-bit
- integer. A client may use the maximum DHCP message size option in
- DHCPDISCOVER or DHCPREQUEST messages, but should not use the option
- in DHCPDECLINE messages.
-
-
-
-
-Alexander & Droms Standards Track [Page 28]
-
-RFC 2132 DHCP Options and BOOTP Vendor Extensions March 1997
-
-
- The code for this option is 57, and its length is 2. The minimum
- legal value is 576 octets.
-
- Code Len Length
- +-----+-----+-----+-----+
- | 57 | 2 | l1 | l2 |
- +-----+-----+-----+-----+
-
-9.11. Renewal (T1) Time Value
-
- This option specifies the time interval from address assignment until
- the client transitions to the RENEWING state.
-
- The value is in units of seconds, and is specified as a 32-bit
- unsigned integer.
-
- The code for this option is 58, and its length is 4.
-
- Code Len T1 Interval
- +-----+-----+-----+-----+-----+-----+
- | 58 | 4 | t1 | t2 | t3 | t4 |
- +-----+-----+-----+-----+-----+-----+
-
-9.12. Rebinding (T2) Time Value
-
- This option specifies the time interval from address assignment until
- the client transitions to the REBINDING state.
-
- The value is in units of seconds, and is specified as a 32-bit
- unsigned integer.
-
- The code for this option is 59, and its length is 4.
-
- Code Len T2 Interval
- +-----+-----+-----+-----+-----+-----+
- | 59 | 4 | t1 | t2 | t3 | t4 |
- +-----+-----+-----+-----+-----+-----+
-
-9.13. Vendor class identifier
-
- This option is used by DHCP clients to optionally identify the vendor
- type and configuration of a DHCP client. The information is a string
- of n octets, interpreted by servers. Vendors may choose to define
- specific vendor class identifiers to convey particular configuration
- or other identification information about a client. For example, the
- identifier may encode the client's hardware configuration. Servers
- not equipped to interpret the class-specific information sent by a
- client MUST ignore it (although it may be reported). Servers that
-
-
-
-Alexander & Droms Standards Track [Page 29]
-
-RFC 2132 DHCP Options and BOOTP Vendor Extensions March 1997
-
-
- respond SHOULD only use option 43 to return the vendor-specific
- information to the client.
-
- The code for this option is 60, and its minimum length is 1.
-
- Code Len Vendor class Identifier
- +-----+-----+-----+-----+---
- | 60 | n | i1 | i2 | ...
- +-----+-----+-----+-----+---
-
-9.14. Client-identifier
-
- This option is used by DHCP clients to specify their unique
- identifier. DHCP servers use this value to index their database of
- address bindings. This value is expected to be unique for all
- clients in an administrative domain.
-
- Identifiers SHOULD be treated as opaque objects by DHCP servers.
-
- The client identifier MAY consist of type-value pairs similar to the
- 'htype'/'chaddr' fields defined in [3]. For instance, it MAY consist
- of a hardware type and hardware address. In this case the type field
- SHOULD be one of the ARP hardware types defined in STD2 [22]. A
- hardware type of 0 (zero) should be used when the value field
- contains an identifier other than a hardware address (e.g. a fully
- qualified domain name).
-
- For correct identification of clients, each client's client-
- identifier MUST be unique among the client-identifiers used on the
- subnet to which the client is attached. Vendors and system
- administrators are responsible for choosing client-identifiers that
- meet this requirement for uniqueness.
-
- The code for this option is 61, and its minimum length is 2.
-
- Code Len Type Client-Identifier
- +-----+-----+-----+-----+-----+---
- | 61 | n | t1 | i1 | i2 | ...
- +-----+-----+-----+-----+-----+---
-
-
-
-
-
-
-
-
-
-
-
-
-Alexander & Droms Standards Track [Page 30]
-
-RFC 2132 DHCP Options and BOOTP Vendor Extensions March 1997
-
-
-10. Defining new extensions
-
- The author of a new DHCP option will follow these steps to obtain
- acceptance of the option as a part of the DHCP Internet Standard:
-
- 1. The author devises the new option.
- 2. The author requests a number for the new option from IANA by
- contacting:
- Internet Assigned Numbers Authority (IANA)
- USC/Information Sciences Institute
- 4676 Admiralty Way
- Marina del Rey, California 90292-6695
-
- or by email as: iana@iana.org
-
- 3. The author documents the new option, using the newly obtained
- option number, as an Internet Draft.
- 4. The author submits the Internet Draft for review through the IETF
- standards process as defined in "Internet Official Protocol
- Standards" (STD 1). The new option will be submitted for eventual
- acceptance as an Internet Standard.
- 5. The new option progresses through the IETF standards process; the
- new option will be reviewed by the Dynamic Host Configuration
- Working Group (if that group still exists), or as an Internet
- Draft not submitted by an IETF working group.
- 6. If the new option fails to gain acceptance as an Internet
- Standard, the assigned option number will be returned to IANA for
- reassignment.
-
- This procedure for defining new extensions will ensure that:
-
- * allocation of new option numbers is coordinated from a single
- authority,
- * new options are reviewed for technical correctness and
- appropriateness, and
- * documentation for new options is complete and published.
-
-11. Acknowledgements
-
- The author thanks the many (and too numerous to mention!) members of
- the DHC WG for their tireless and ongoing efforts in the development
- of DHCP and this document.
-
- The efforts of J Allard, Mike Carney, Dave Lapp, Fred Lien and John
- Mendonca in organizing DHCP interoperability testing sessions are
- gratefully acknowledged.
-
-
-
-
-
-Alexander & Droms Standards Track [Page 31]
-
-RFC 2132 DHCP Options and BOOTP Vendor Extensions March 1997
-
-
- The development of this document was supported in part by grants from
- the Corporation for National Research Initiatives (CNRI), Bucknell
- University and Sun Microsystems.
-
-12. References
-
- [1] Droms, R., "Dynamic Host Configuration Protocol", RFC 2131,
- Bucknell University, March 1997.
-
- [2] Reynolds, J., "BOOTP Vendor Information Extensions", RFC 1497,
- USC/Information Sciences Institute, August 1993.
-
- [3] Croft, W., and J. Gilmore, "Bootstrap Protocol", RFC 951,
- Stanford University and Sun Microsystems, September 1985.
-
- [4] Braden, R., Editor, "Requirements for Internet Hosts -
- Communication Layers", STD 3, RFC 1122, USC/Information Sciences
- Institute, October 1989.
-
- [5] Mogul, J., and J. Postel, "Internet Standard Subnetting
- Procedure", STD 5, RFC 950, USC/Information Sciences Institute,
- August 1985.
-
- [6] Postel, J., and K. Harrenstien, "Time Protocol", STD 26, RFC
- 868, USC/Information Sciences Institute, SRI, May 1983.
-
- [7] Postel, J., "Name Server", IEN 116, USC/Information Sciences
- Institute, August 1979.
-
- [8] Mockapetris, P., "Domain Names - Implementation and
- Specification", STD 13, RFC 1035, USC/Information Sciences
- Institute, November 1987.
-
- [9] Postel, J., "Quote of the Day Protocol", STD 23, RFC 865,
- USC/Information Sciences Institute, May 1983.
-
- [10] McLaughlin, L., "Line Printer Daemon Protocol", RFC 1179, The
- Wollongong Group, August 1990.
-
- [11] Accetta, M., "Resource Location Protocol", RFC 887, CMU,
- December 1983.
-
- [12] Mogul, J. and S. Deering, "Path MTU Discovery", RFC 1191,
- DECWRL, Stanford University, November 1990.
-
- [13] Deering, S., "ICMP Router Discovery Messages", RFC 1256,
- Xerox PARC, September 1991.
-
-
-
-
-Alexander & Droms Standards Track [Page 32]
-
-RFC 2132 DHCP Options and BOOTP Vendor Extensions March 1997
-
-
- [14] Leffler, S. and M. Karels, "Trailer Encapsulations", RFC 893,
- U. C. Berkeley, April 1984.
-
- [15] Hornig, C., "Standard for the Transmission of IP Datagrams over
- Ethernet Networks", RFC 894, Symbolics, April 1984.
-
- [16] Postel, J. and J. Reynolds, "Standard for the Transmission of
- IP Datagrams Over IEEE 802 Networks", RFC 1042, USC/Information
- Sciences Institute, February 1988.
-
- [17] Sun Microsystems, "System and Network Administration", March
- 1990.
-
- [18] Mills, D., "Internet Time Synchronization: The Network Time
- Protocol", RFC 1305, UDEL, March 1992.
-
- [19] NetBIOS Working Group, "Protocol Standard for a NetBIOS Service
- on a TCP/UDP transport: Concepts and Methods", STD 19, RFC 1001,
- March 1987.
-
- [20] NetBIOS Working Group, "Protocol Standard for a NetBIOS Service
- on a TCP/UDP transport: Detailed Specifications", STD 19, RFC
- 1002, March 1987.
-
- [21] Scheifler, R., "FYI On the X Window System", FYI 6, RFC 1198,
- MIT Laboratory for Computer Science, January 1991.
-
- [22] Reynolds, J., and J. Postel, "Assigned Numbers", STD 2, RFC 1700,
- USC/Information Sciences Institute, July 1992.
-
-13. Security Considerations
-
- Security issues are not discussed in this memo.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Alexander & Droms Standards Track [Page 33]
-
-RFC 2132 DHCP Options and BOOTP Vendor Extensions March 1997
-
-
-14. Authors' Addresses
-
- Steve Alexander
- Silicon Graphics, Inc.
- 2011 N. Shoreline Boulevard
- Mailstop 510
- Mountain View, CA 94043-1389
-
- Phone: (415) 933-6172
- EMail: sca@engr.sgi.com
-
-
- Ralph Droms
- Bucknell University
- Lewisburg, PA 17837
-
- Phone: (717) 524-1145
- EMail: droms@bucknell.edu
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Alexander & Droms Standards Track [Page 34]
-
diff --git a/doc/rfc2485.txt b/doc/rfc2485.txt
deleted file mode 100644
index 752b03c5..00000000
--- a/doc/rfc2485.txt
+++ /dev/null
@@ -1,227 +0,0 @@
-
-
-
-
-
-
-Network Working Group S. Drach
-Request for Comments: 2485 Sun Microsystems
-Category: Standards Track January 1999
-
-
-
- DHCP Option for The Open Group's User Authentication Protocol
-
-Status of this Memo
-
- This document specifies an Internet standards track protocol for the
- Internet community, and requests discussion and suggestions for
- improvements. Please refer to the current edition of the "Internet
- Official Protocol Standards" (STD 1) for the standardization state
- and status of this protocol. Distribution of this memo is unlimited.
-
-Copyright Notice
-
- Copyright (C) The Internet Society (1999). All Rights Reserved.
-
-Abstract
-
- This document defines a DHCP [1] option that contains a list of
- pointers to User Authentication Protocol servers that provide user
- authentication services for clients that conform to The Open Group
- Network Computing Client Technical Standard [2].
-
-Introduction
-
- The Open Group Network Computing Client Technical Standard, a product
- of The Open Group's Network Computing Working Group (NCWG), defines a
- network computing client user authentication facility named the User
- Authentication Protocol (UAP).
-
- UAP provides two levels of authentication, basic and secure. Basic
- authentication uses the Basic Authentication mechanism defined in the
- HTTP 1.1 [3] specification. Secure authentication is simply basic
- authentication encapsulated in an SSLv3 [4] session.
-
- In both cases, a UAP client needs to obtain the IP address and port
- of the UAP service. Additional path information may be required,
- depending on the implementation of the service. A URL [5] is an
- excellent mechanism for encapsulation of this information since many
- UAP servers will be implemented as components within legacy HTTP/SSL
- servers.
-
-
-
-
-
-
-Drach Standards Track [Page 1]
-
-RFC 2485 DCHP Option for the Open Group's UAP January 1999
-
-
- Most UAP clients have no local state and are configured when booted
- through DHCP. No existing DHCP option [6] has a data field that
- contains a URL. Option 72 contains a list of IP addresses for WWW
- servers, but it is not adequate since a port and/or path can not be
- specified. Hence there is a need for an option that contains a list
- of URLs.
-
-User Authentication Protocol Option
-
- 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.
-
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Code | Length | URL list
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
- Code 98
-
- Length The length of the data field (i.e., URL list) in
- bytes.
-
- URL list A list of one or more URLs separated by the ASCII
- space character (0x20).
-
-References
-
- [1] Droms, R., "Dynamic Host Configuration Protocol", RFC 2131,
- March 1997.
-
- [2] Technical Standard: Network Computing Client, The Open Group,
- Document Number C801, October 1998.
-
- [3] Fielding, R., Gettys, J., Mogul, J., Frystyk, H., and T.
- Berners-Lee, "Hypertext Transfer Protocol -- HTTP/1.1", RFC
- 2068, January 1997.
-
- [4] Freier, A., Karlton, P., and P. Kocher, "The SSL Protocol,
- Version 3.0", Netscape Communications Corp., November 1996.
- Standards Information Base, The Open Group,
- http://www.db.opengroup.org/sib.htm#SSL_3.
-
-
-
-Drach Standards Track [Page 2]
-
-RFC 2485 DCHP Option for the Open Group's UAP January 1999
-
-
- [5] Berners-Lee, T., Masinter, L., and M. McCahill, "Uniform
- Resource Locators (URL)", RFC 1738, December 1994.
-
- [6] Alexander, S. and R. Droms, "DHCP Options and BOOTP Vendor
- Extensions", RFC 2132, March 1997.
-
-Security Considerations
-
- DHCP currently provides no authentication or security mechanisms.
- Potential exposures to attack are discussed in section 7 of the DHCP
- protocol specification.
-
- The User Authentication Protocol does not have a means to detect
- whether or not the client is communicating with a rogue
- authentication service that the client contacted because it received
- a forged or otherwise compromised UAP option from a DHCP service
- whose security was compromised. Even secure authentication does not
- provide relief from this type of attack. This security exposure is
- mitigated by the environmental assumptions documented in the Network
- Computing Client Technical Standard.
-
-Author's Address
-
- Steve Drach
- Sun Microsystems, Inc.
- 901 San Antonio Road
- Palo Alto, CA 94303
-
- Phone: (650) 960-1300
- EMail: drach@sun.com
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Drach Standards Track [Page 3]
-
-RFC 2485 DCHP Option for the Open Group's UAP January 1999
-
-
-Full Copyright Statement
-
- Copyright (C) The Internet Society (1999). All Rights Reserved.
-
- This document and translations of it may be copied and furnished to
- others, and derivative works that comment on or otherwise explain it
- or assist in its implementation may be prepared, copied, published
- and distributed, in whole or in part, without restriction of any
- kind, provided that the above copyright notice and this paragraph are
- included on all such copies and derivative works. However, this
- document itself may not be modified in any way, such as by removing
- the copyright notice or references to the Internet Society or other
- Internet organizations, except as needed for the purpose of
- developing Internet standards in which case the procedures for
- copyrights defined in the Internet Standards process must be
- followed, or as required to translate it into languages other than
- English.
-
- The limited permissions granted above are perpetual and will not be
- revoked by the Internet Society or its successors or assigns.
-
- This document and the information contained herein is provided on an
- "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
- TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
- BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
- HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
- MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Drach Standards Track [Page 4]
-
diff --git a/doc/rfc2489.txt b/doc/rfc2489.txt
deleted file mode 100644
index 42e066ec..00000000
--- a/doc/rfc2489.txt
+++ /dev/null
@@ -1,283 +0,0 @@
-
-
-
-
-
-
-Network Working Group R. Droms
-Request for Comments: 2489 Bucknell University
-BCP: 29 January 1999
-Category: Best Current Practice
-
-
- Procedure for Defining New DHCP Options
-
-Status of this Memo
-
- This document specifies an Internet Best Current Practices for the
- Internet Community, and requests discussion and suggestions for
- improvements. Distribution of this memo is unlimited.
-
-Copyright Notice
-
- Copyright (C) The Internet Society (1999). All Rights Reserved.
-
-Abstract
-
- The Dynamic Host Configuration Protocol (DHCP) provides a framework
- for passing configuration information to hosts on a TCP/IP network.
- Configuration parameters and other control information are carried in
- tagged data items that are stored in the 'options' field of the DHCP
- message. The data items themselves are also called "options."
-
- New DHCP options may be defined after the publication of the DHCP
- specification to accommodate requirements for conveyance of new
- configuration parameters. This document describes the procedure for
- defining new DHCP options.
-
-1. Introduction
-
- The Dynamic Host Configuration Protocol (DHCP) [1] provides a
- framework for passing configuration information to hosts on a TCP/IP
- network. Configuration parameters and other control information are
- carried in tagged data items that are stored in the 'options' field
- of the DHCP message. The data items themselves are also called
- "options." [2]
-
- This document describes the procedure for defining new DHCP options.
- The procedure will guarantee that:
-
- * allocation of new option numbers is coordinated from a single
- authority,
- * new options are reviewed for technical correctness and
- appropriateness, and
- * documentation for new options is complete and published.
-
-
-
-Droms Best Current Practice [Page 1]
-
-RFC 2489 Defining New DCHP Options January 1999
-
-
- As indicated in "Guidelines for Writing an IANA Considerations
- Section in RFCs" (see references), IANA acts as a central authority
- for assignment of numbers such as DHCP option codes. The new
- procedure outlined in this document will provide guidance to IANA in
- the assignment of new option codes.
-
-2. Overview and background
-
- The procedure described in this document modifies and clarifies the
- procedure for defining new options in RFC 2131 [2]. The primary
- modification is to the time at which a new DHCP option is assigned an
- option number. In the procedure described in this document, the
- option number is not assigned until specification for the option is
- about to be published as an RFC.
-
- Since the publication of RFC 2132, the option number space for
- publically defined DHCP options (1-127) has almost been exhausted.
- Many of the defined option numbers have not been followed up with
- Internet Drafts submitted to the DHC WG. There has been a lack of
- specific guidance to IANA from the DHC WG as to the assignment of
- DHCP option numbers
-
- The procedure as specified in RFC 2132 does not clearly state that
- new options are to be reviewed individually for technical
- correctness, appropriateness and complete documentation. RFC 2132
- also does not require that new options are to be submitted to the
- IESG for review, and that the author of the option specification is
- responsible for bringing new options to the attention of the IESG.
- Finally, RFC 2132 does not make clear that newly defined options are
- not to be incorporated into products, included in other
- specifications or otherwise used until the specification for the
- option is published as an RFC.
-
- In the future, new DHCP option codes will be assigned by IETF
- consensus. New DHCP options will be documented in RFCs approved by
- the IESG, and the codes for those options will be assigned at the
- time the relevant RFCs are published. Typically, the IESG will seek
- input on prospective assignments from appropriate sources (e.g., a
- relevant Working Group if one exists). Groups of related options may
- be combined into a single specification and reviewed as a set by the
- IESG. Prior to assignment of an option code, it is not appropriate
- to incorporate new options into products, include the specification
- in other documents or otherwise make use of the new options.
-
- The DHCP option number space (1-254) is split into two parts. The
- site-specific options (128-254) are defined as "Private Use" and
- require no review by the DHC WG. The public options (1-127) are
-
-
-
-
-Droms Best Current Practice [Page 2]
-
-RFC 2489 Defining New DCHP Options January 1999
-
-
- defined as "Specification Required" and new options must be reviewed
- prior to assignment of an option number by IANA. The details of the
- review process are given in the following section of this document.
-
-3. Procedure
-
- The author of a new DHCP option will follow these steps to obtain
- approval for the option and publication of the specification of the
- option as an RFC:
-
- 1. The author devises the new option.
-
- 2. The author documents the new option, leaving the option code as
- "To Be Determined" (TBD), as an Internet Draft.
-
- The requirement that the new option be documented as an Internet
- Draft is a matter of expediency. In theory, the new option could
- be documented on the back of an envelope for submission; as a
- practical matter, the specification will eventually become an
- Internet Draft as part of the review process.
-
- 3. The author submits the Internet Draft for review by the IESG.
- Preferably, the author will submit the Internet Draft to the DHC
- Working Group, but the author may choose to submit the Internet
- Draft directly to the IESG.
-
- Note that simply publishing the new option as an Internet Draft
- does not automatically bring the option to the attention of the
- IESG. The author of the new option must explicitly forward a
- request for action on the new option to the DHC WG or the IESG.
-
- 4. The specification of the new option is reviewed by the IESG. The
- specification is reviewed by the DHC WG (if it exists) or by the
- IETF. If the option is accepted for inclusion in the DHCP
- specification, the specification of the option is published as an
- RFC. It may be published as either a standards-track or a non-
- standards-track RFC.
-
- 5. At the time of publication as an RFC, IANA assigns a DHCP option
- number to the new option.
-
-4. References
-
- [1] Droms, R., "Dynamic Host Configuration Protocol", RFC 2131,
- March 1997.
-
- [2] Alexander, S. and R. Droms, "DHCP Options and BOOTP Vendor
- Extensions", RFC 2132, March 1997.
-
-
-
-Droms Best Current Practice [Page 3]
-
-RFC 2489 Defining New DCHP Options January 1999
-
-
- [3] Droms, R. and K. Fong, "NetWare/IP Domain Name and Information",
- RFC 2142, November 1997.
-
- [4] Narten, T. and H. Alvestrand, "Guidelines for Writing an IANA
- Considerations Section in RFCs", BCP 26, RFC 2434, October 1998.
-
-5. Security Considerations
-
- Information that creates or updates an option number assignment needs
- to be authenticated.
-
- An analysis of security issues is required for all newly defined DHCP
- options. The description of security issues in the specification of
- new options must be as accurate as possible. The specification for a
- new option may reference the "Security Considerations" section in the
- DHCP specification [1]; e.g. (from "NetWare/IP Domain Name and
- Information" [3]):
-
- DHCP currently provides no authentication or security mechanisms.
- Potential exposures to attack are discussed in section 7 of the
- DHCP protocol specification [RFC 2131].
-
-6. IANA Considerations
-
- RFC 2132 provided guidance to the IANA on the procedure it should
- follow when assigning option numbers for new DHCP options. This
- document updates and replaces those instructions. In particular,
- IANA is requested to assign DHCP option numbers only for options that
- have been approved for publication as RFCs; i.e., documents that have
- been approved through "IETF consensus" as defined in RFC 2434 [4].
-
-7. Author's Address
-
- Ralph Droms
- Computer Science Department
- 323 Dana Engineering
- Bucknell University
- Lewisburg, PA 17837
-
- Phone: (717) 524-1145
- EMail: droms@bucknell.edu
-
-
-
-
-
-
-
-
-
-
-Droms Best Current Practice [Page 4]
-
-RFC 2489 Defining New DCHP Options January 1999
-
-
-8. Full Copyright Statement
-
- Copyright (C) The Internet Society (1999). All Rights Reserved.
-
- This document and translations of it may be copied and furnished to
- others, and derivative works that comment on or otherwise explain it
- or assist in its implementation may be prepared, copied, published
- and distributed, in whole or in part, without restriction of any
- kind, provided that the above copyright notice and this paragraph are
- included on all such copies and derivative works. However, this
- document itself may not be modified in any way, such as by removing
- the copyright notice or references to the Internet Society or other
- Internet organizations, except as needed for the purpose of
- developing Internet standards in which case the procedures for
- copyrights defined in the Internet Standards process must be
- followed, or as required to translate it into languages other than
- English.
-
- The limited permissions granted above are perpetual and will not be
- revoked by the Internet Society or its successors or assigns.
-
- This document and the information contained herein is provided on an
- "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
- TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
- BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
- HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
- MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Droms Best Current Practice [Page 5]
-
diff --git a/doc/rfc951.txt b/doc/rfc951.txt
deleted file mode 100644
index 86cd69e6..00000000
--- a/doc/rfc951.txt
+++ /dev/null
@@ -1,684 +0,0 @@
-
-
-Network Working Group Bill Croft (Stanford University)
-Request for Comments: 951 John Gilmore (Sun Microsystems)
- September 1985
-
- BOOTSTRAP PROTOCOL (BOOTP)
-
-
-1. Status of this Memo
-
- This RFC suggests a proposed protocol for the ARPA-Internet
- community, and requests discussion and suggestions for improvements.
- Distribution of this memo is unlimited.
-
-2. Overview
-
- This RFC describes an IP/UDP bootstrap protocol (BOOTP) which allows
- a diskless client machine to discover its own IP address, the address
- of a server host, and the name of a file to be loaded into memory and
- executed. The bootstrap operation can be thought of as consisting of
- TWO PHASES. This RFC describes the first phase, which could be
- labeled 'address determination and bootfile selection'. After this
- address and filename information is obtained, control passes to the
- second phase of the bootstrap where a file transfer occurs. The file
- transfer will typically use the TFTP protocol [9], since it is
- intended that both phases reside in PROM on the client. However
- BOOTP could also work with other protocols such as SFTP [3] or
- FTP [6].
-
- We suggest that the client's PROM software provide a way to do a
- complete bootstrap without 'user' interaction. This is the type of
- boot that would occur during an unattended power-up. A mechanism
- should be provided for the user to manually supply the necessary
- address and filename information to bypass the BOOTP protocol and
- enter the file transfer phase directly. If non-volatile storage is
- available, we suggest keeping default settings there and bypassing
- the BOOTP protocol unless these settings cause the file transfer
- phase to fail. If the cached information fails, the bootstrap should
- fall back to phase 1 and use BOOTP.
-
- Here is a brief outline of the protocol:
-
- 1. A single packet exchange is performed. Timeouts are used to
- retransmit until a reply is received. The same packet field
- layout is used in both directions. Fixed length fields of maximum
- reasonable length are used to simplify structure definition and
- parsing.
-
- 2. An 'opcode' field exists with two values. The client
- broadcasts a 'bootrequest' packet. The server then answers with a
- 'bootreply' packet. The bootrequest contains the client's
- hardware address and its IP address, if known.
-
-
-Croft & Gilmore [Page 1]
-
-
-
-RFC 951 September 1985
-Bootstrap Protocol
-
-
- 3. The request can optionally contain the name of the server the
- client wishes to respond. This is so the client can force the
- boot to occur from a specific host (e.g. if multiple versions of
- the same bootfile exist or if the server is in a far distant
- net/domain). The client does not have to deal with name / domain
- services; instead this function is pushed off to the BOOTP server.
-
- 4. The request can optionally contain the 'generic' filename to be
- booted. For example 'unix' or 'ethertip'. When the server sends
- the bootreply, it replaces this field with the fully qualified
- path name of the appropriate boot file. In determining this name,
- the server may consult his own database correlating the client's
- address and filename request, with a particular boot file
- customized for that client. If the bootrequest filename is a null
- string, then the server returns a filename field indicating the
- 'default' file to be loaded for that client.
-
- 5. In the case of clients who do not know their IP addresses, the
- server must also have a database relating hardware address to IP
- address. This client IP address is then placed into a field in
- the bootreply.
-
- 6. Certain network topologies (such as Stanford's) may be such
- that a given physical cable does not have a TFTP server directly
- attached to it (e.g. all the gateways and hosts on a certain cable
- may be diskless). With the cooperation of neighboring gateways,
- BOOTP can allow clients to boot off of servers several hops away,
- through these gateways. See the section 'Booting Through
- Gateways' below. This part of the protocol requires no special
- action on the part of the client. Implementation is optional and
- requires a small amount of additional code in gateways and
- servers.
-
-3. Packet Format
-
- All numbers shown are decimal, unless indicated otherwise. The BOOTP
- packet is enclosed in a standard IP [8] UDP [7] datagram. For
- simplicity it is assumed that the BOOTP packet is never fragmented.
- Any numeric fields shown are packed in 'standard network byte order',
- i.e. high order bits are sent first.
-
- In the IP header of a bootrequest, the client fills in its own IP
- source address if known, otherwise zero. When the server address is
- unknown, the IP destination address will be the 'broadcast address'
- 255.255.255.255. This address means 'broadcast on the local cable,
- (I don't know my net number)' [4].
-
-
-
-Croft & Gilmore [Page 2]
-
-
-
-RFC 951 September 1985
-Bootstrap Protocol
-
-
- The UDP header contains source and destination port numbers. The
- BOOTP protocol uses two reserved port numbers, 'BOOTP client' (68)
- and 'BOOTP server' (67). The client sends requests using 'BOOTP
- server' as the destination port; this is usually a broadcast. The
- server sends replies using 'BOOTP client' as the destination port;
- depending on the kernel or driver facilities in the server, this may
- or may not be a broadcast (this is explained further in the section
- titled 'Chicken/Egg issues' below). The reason TWO reserved ports
- are used, is to avoid 'waking up' and scheduling the BOOTP server
- daemons, when a bootreply must be broadcast to a client. Since the
- server and other hosts won't be listening on the 'BOOTP client' port,
- any such incoming broadcasts will be filtered out at the kernel
- level. We could not simply allow the client to pick a 'random' port
- number for the UDP source port field; since the server reply may be
- broadcast, a randomly chosen port number could confuse other hosts
- that happened to be listening on that port.
-
- The UDP length field is set to the length of the UDP plus BOOTP
- portions of the packet. The UDP checksum field can be set to zero by
- the client (or server) if desired, to avoid this extra overhead in a
- PROM implementation. In the 'Packet Processing' section below the
- phrase '[UDP checksum.]' is used whenever the checksum might be
- verified/computed.
-
- FIELD BYTES DESCRIPTION
- ----- ----- -----------
-
- op 1 packet op code / message type.
- 1 = BOOTREQUEST, 2 = BOOTREPLY
-
- htype 1 hardware address type,
- see ARP section in "Assigned Numbers" RFC.
- '1' = 10mb ethernet
-
- hlen 1 hardware address length
- (eg '6' for 10mb ethernet).
-
- hops 1 client sets to zero,
- optionally used by gateways
- in cross-gateway booting.
-
- xid 4 transaction ID, a random number,
- used to match this boot request with the
- responses it generates.
-
- secs 2 filled in by client, seconds elapsed since
- client started trying to boot.
-
-
-Croft & Gilmore [Page 3]
-
-
-
-RFC 951 September 1985
-Bootstrap Protocol
-
-
- -- 2 unused
-
- ciaddr 4 client IP address;
- filled in by client in bootrequest if known.
-
- yiaddr 4 'your' (client) IP address;
- filled by server if client doesn't
- know its own address (ciaddr was 0).
-
- siaddr 4 server IP address;
- returned in bootreply by server.
-
- giaddr 4 gateway IP address,
- used in optional cross-gateway booting.
-
- chaddr 16 client hardware address,
- filled in by client.
-
- sname 64 optional server host name,
- null terminated string.
-
- file 128 boot file name, null terminated string;
- 'generic' name or null in bootrequest,
- fully qualified directory-path
- name in bootreply.
-
- vend 64 optional vendor-specific area,
- e.g. could be hardware type/serial on request,
- or 'capability' / remote file system handle
- on reply. This info may be set aside for use
- by a third phase bootstrap or kernel.
-
-4. Chicken / Egg Issues
-
- How can the server send an IP datagram to the client, if the client
- doesnt know its own IP address (yet)? Whenever a bootreply is being
- sent, the transmitting machine performs the following operations:
-
- 1. If the client knows its own IP address ('ciaddr' field is
- nonzero), then the IP can be sent 'as normal', since the client
- will respond to ARPs [5].
-
- 2. If the client does not yet know its IP address (ciaddr zero),
- then the client cannot respond to ARPs sent by the transmitter of
- the bootreply. There are two options:
-
- a. If the transmitter has the necessary kernel or driver hooks
-
-
-Croft & Gilmore [Page 4]
-
-
-
-RFC 951 September 1985
-Bootstrap Protocol
-
-
- to 'manually' construct an ARP address cache entry, then it can
- fill in an entry using the 'chaddr' and 'yiaddr' fields. Of
- course, this entry should have a timeout on it, just like any
- other entry made by the normal ARP code itself. The
- transmitter of the bootreply can then simply send the bootreply
- to the client's IP address. UNIX (4.2 BSD) has this
- capability.
-
- b. If the transmitter lacks these kernel hooks, it can simply
- send the bootreply to the IP broadcast address on the
- appropriate interface. This is only one additional broadcast
- over the previous case.
-
-5. Client Use of ARP
-
- The client PROM must contain a simple implementation of ARP, e.g. the
- address cache could be just one entry in size. This will allow a
- second-phase-only boot (TFTP) to be performed when the client knows
- the IP addresses and bootfile name.
-
- Any time the client is expecting to receive a TFTP or BOOTP reply, it
- should be prepared to answer an ARP request for its own IP to
- hardware address mapping (if known).
-
- Since the bootreply will contain (in the hardware encapsulation) the
- hardware source address of the server/gateway, the client MAY be able
- to avoid sending an ARP request for the server/gateway IP address to
- be used in the following TFTP phase. However this should be treated
- only as a special case, since it is desirable to still allow a
- second-phase-only boot as described above.
-
-6. Comparison to RARP
-
- An earlier protocol, Reverse Address Resolution Protocol (RARP) [1]
- was proposed to allow a client to determine its IP address, given
- that it knew its hardware address. However RARP had the disadvantage
- that it was a hardware link level protocol (not IP/UDP based). This
- means that RARP could only be implemented on hosts containing special
- kernel or driver modifications to access these 'raw' packets. Since
- there are many network kernels existent now, with each source
- maintained by different organizations, a boot protocol that does not
- require kernel modifications is a decided advantage.
-
- BOOTP provides this hardware to IP address lookup function, in
- addition to the other useful features described in the sections
- above.
-
-
-
-Croft & Gilmore [Page 5]
-
-
-
-RFC 951 September 1985
-Bootstrap Protocol
-
-
-7. Packet Processing
-
- 7.1. Client Transmission
-
- Before setting up the packet for the first time, it is a good idea
- to clear the entire packet buffer to all zeros; this will place
- all fields in their default state. The client then creates a
- packet with the following fields.
-
- The IP destination address is set to 255.255.255.255. (the
- broadcast address) or to the server's IP address (if known). The
- IP source address and 'ciaddr' are set to the client's IP address
- if known, else 0. The UDP header is set with the proper length;
- source port = 'BOOTP client' port destination port = 'BOOTP
- server' port.
-
- 'op' is set to '1', BOOTREQUEST. 'htype' is set to the hardware
- address type as assigned in the ARP section of the "Assigned
- Numbers" RFC. 'hlen' is set to the length of the hardware address,
- e.g. '6' for 10mb ethernet.
-
- 'xid' is set to a 'random' transaction id. 'secs' is set to the
- number of seconds that have elapsed since the client has started
- booting. This will let the servers know how long a client has
- been trying. As the number gets larger, certain servers may feel
- more 'sympathetic' towards a client they don't normally service.
- If a client lacks a suitable clock, it could construct a rough
- estimate using a loop timer. Or it could choose to simply send
- this field as always a fixed value, say 100 seconds.
-
- If the client knows its IP address, 'ciaddr' (and the IP source
- address) are set to this value. 'chaddr' is filled in with the
- client's hardware address.
-
- If the client wishes to restrict booting to a particular server
- name, it may place a null-terminated string in 'sname'. The name
- used should be any of the allowable names or nicknames of the
- desired host.
-
- The client has several options for filling the 'file' name field.
- If left null, the meaning is 'I want to boot the default file for
- my machine'. A null file name can also mean 'I am only interested
- in finding out client/server/gateway IP addresses, I dont care
- about file names'.
-
- The field can also be a 'generic' name such as 'unix' or
-
-
-
-Croft & Gilmore [Page 6]
-
-
-
-RFC 951 September 1985
-Bootstrap Protocol
-
-
- 'gateway'; this means 'boot the named program configured for my
- machine'. Finally the field can be a fully directory qualified
- path name.
-
- The 'vend' field can be filled in by the client with
- vendor-specific strings or structures. For example the machine
- hardware type or serial number may be placed here. However the
- operation of the BOOTP server should not DEPEND on this
- information existing.
-
- If the 'vend' field is used, it is recommended that a 4 byte
- 'magic number' be the first item within 'vend'. This lets a
- server determine what kind of information it is seeing in this
- field. Numbers can be assigned by the usual 'magic number'
- process --you pick one and it's magic. A different magic number
- could be used for bootreply's than bootrequest's to allow the
- client to take special action with the reply information.
-
- [UDP checksum.]
-
- 7.2. Client Retransmission Strategy
-
- If no reply is received for a certain length of time, the client
- should retransmit the request. The time interval must be chosen
- carefully so as not to flood the network. Consider the case of a
- cable containing 100 machines that are just coming up after a
- power failure. Simply retransmitting the request every four
- seconds will inundate the net.
-
- As a possible strategy, you might consider backing off
- exponentially, similar to the way ethernet backs off on a
- collision. So for example if the first packet is at time 0:00,
- the second would be at :04, then :08, then :16, then :32, then
- :64. You should also randomize each time; this would be done
- similar to the ethernet specification by starting with a mask and
- 'and'ing that with with a random number to get the first backoff.
- On each succeeding backoff, the mask is increased in length by one
- bit. This doubles the average delay on each backoff.
-
- After the 'average' backoff reaches about 60 seconds, it should be
- increased no further, but still randomized.
-
- Before each retransmission, the client should update the 'secs'
- field. [UDP checksum.]
-
-
-
-
-
-Croft & Gilmore [Page 7]
-
-
-
-RFC 951 September 1985
-Bootstrap Protocol
-
-
- 7.3. Server Receives BOOTREQUEST
-
- [UDP checksum.] If the UDP destination port does not match the
- 'BOOTP server' port, discard the packet.
-
- If the server name field (sname) is null (no particular server
- specified), or sname is specified and matches our name or
- nickname, then continue with packet processing.
-
- If the sname field is specified, but does not match 'us', then
- there are several options:
-
- 1. You may choose to simply discard this packet.
-
- 2. If a name lookup on sname shows it to be on this same cable,
- discard the packet.
-
- 3. If sname is on a different net, you may choose to forward
- the packet to that address. If so, check the 'giaddr' (gateway
- address) field. If 'giaddr' is zero, fill it in with my
- address or the address of a gateway that can be used to get to
- that net. Then forward the packet.
-
- If the client IP address (ciaddr) is zero, then the client does
- not know its own IP address. Attempt to lookup the client
- hardware address (chaddr, hlen, htype) in our database. If no
- match is found, discard the packet. Otherwise we now have an IP
- address for this client; fill it into the 'yiaddr' (your IP
- address) field.
-
- We now check the boot file name field (file). The field will be
- null if the client is not interested in filenames, or wants the
- default bootfile. If the field is non-null, it is used as a
- lookup key in a database, along with the client's IP address. If
- there is a default file or generic file (possibly indexed by the
- client address) or a fully-specified path name that matches, then
- replace the 'file' field with the fully-specified path name of the
- selected boot file. If the field is non-null and no match was
- found, then the client is asking for a file we dont have; discard
- the packet, perhaps some other BOOTP server will have it.
-
- The 'vend' vendor-specific data field should now be checked and if
- a recognized type of data is provided, client-specific actions
- should be taken, and a response placed in the 'vend' data field of
- the reply packet. For example, a workstation client could provide
-
-
-
-
-Croft & Gilmore [Page 8]
-
-
-
-RFC 951 September 1985
-Bootstrap Protocol
-
-
- an authentication key and receive from the server a capability for
- remote file access, or a set of configuration options, which can
- be passed to the operating system that will shortly be booted in.
-
- Place my (server) IP address in the 'siaddr' field. Set the 'op'
- field to BOOTREPLY. The UDP destination port is set to 'BOOTP
- client'. If the client address 'ciaddr' is nonzero, send the
- packet there; else if the gateway address 'giaddr' is nonzero, set
- the UDP destination port to 'BOOTP server' and send the packet to
- 'giaddr'; else the client is on one of our cables but it doesnt
- know its own IP address yet --use a method described in the 'Egg'
- section above to send it to the client. If 'Egg' is used and we
- have multiple interfaces on this host, use the 'yiaddr' (your IP
- address) field to figure out which net (cable/interface) to send
- the packet to. [UDP checksum.]
-
- 7.4. Server/Gateway Receives BOOTREPLY
-
- [UDP checksum.] If 'yiaddr' (your [the client's] IP address)
- refers to one of our cables, use one of the 'Egg' methods above to
- forward it to the client. Be sure to send it to the 'BOOTP
- client' UDP destination port.
-
- 7.5. Client Reception
-
- Don't forget to process ARP requests for my own IP address (if I
- know it). [UDP checksum.] The client should discard incoming
- packets that: are not IP/UDPs addressed to the boot port; are not
- BOOTREPLYs; do not match my IP address (if I know it) or my
- hardware address; do not match my transaction id. Otherwise we
- have received a successful reply. 'yiaddr' will contain my IP
- address, if I didnt know it before. 'file' is the name of the
- file name to TFTP 'read request'. The server address is in
- 'siaddr'. If 'giaddr' (gateway address) is nonzero, then the
- packets should be forwarded there first, in order to get to the
- server.
-
-8. Booting Through Gateways
-
- This part of the protocol is optional and requires some additional
- code in cooperating gateways and servers, but it allows cross-gateway
- booting. This is mainly useful when gateways are diskless machines.
- Gateways containing disks (e.g. a UNIX machine acting as a gateway),
- might as well run their own BOOTP/TFTP servers.
-
- Gateways listening to broadcast BOOTREQUESTs may decide to forward or
- rebroadcast these requests 'when appropriate'. For example, the
-
-
-Croft & Gilmore [Page 9]
-
-
-
-RFC 951 September 1985
-Bootstrap Protocol
-
-
- gateway could have, as part of his configuration tables, a list of
- other networks or hosts to receive a copy of any broadcast
- BOOTREQUESTs. Even though a 'hops' field exists, it is a poor idea
- to simply globally rebroadcast the requests, since broadcast loops
- will almost certainly occur.
-
- The forwarding could begin immediately, or wait until the 'secs'
- (seconds client has been trying) field passes a certain threshold.
-
- If a gateway does decide to forward the request, it should look at
- the 'giaddr' (gateway IP address) field. If zero, it should plug its
- own IP address (on the receiving cable) into this field. It may also
- use the 'hops' field to optionally control how far the packet is
- reforwarded. Hops should be incremented on each forwarding. For
- example, if hops passes '3', the packet should probably be discarded.
- [UDP checksum.]
-
- Here we have recommended placing this special forwarding function in
- the gateways. But that does not have to be the case. As long as
- some 'BOOTP forwarding agent' exists on the net with the booting
- client, the agent can do the forwarding when appropriate. Thus this
- service may or may not be co-located with the gateway.
-
- In the case of a forwarding agent not located in the gateway, the
- agent could save himself some work by plugging the broadcast address
- of the interface receiving the bootrequest into the 'giaddr' field.
- Thus the reply would get forwarded using normal gateways, not
- involving the forwarding agent. Of course the disadvantage here is
- that you lose the ability to use the 'Egg' non-broadcast method of
- sending the reply, causing extra overhead for every host on the
- client cable.
-
-9. Sample BOOTP Server Database
-
- As a suggestion, we show a sample text file database that the BOOTP
- server program might use. The database has two sections, delimited
- by a line containing an percent in column 1. The first section
- contains a 'default directory' and mappings from generic names to
- directory/pathnames. The first generic name in this section is the
- 'default file' you get when the bootrequest contains a null 'file'
- string.
-
- The second section maps hardware addresstype/address into an
- ipaddress. Optionally you can also overide the default generic name
- by supplying a ipaddress specific genericname. A 'suffix' item is
- also an option; if supplied, any generic names specified by the
- client will be accessed by first appending 'suffix' to the 'pathname'
-
-
-Croft & Gilmore [Page 10]
-
-
-
-RFC 951 September 1985
-Bootstrap Protocol
-
-
- appropriate to that generic name. If that file is not found, then
- the plain 'pathname' will be tried. This 'suffix' option allows a
- whole set of custom generics to be setup without a lot of effort.
- Below is shown the general format; fields are delimited by one or
- more spaces or tabs; trailing empty fields may be omitted; blank
- lines and lines beginning with '#' are ignored.
-
- # comment line
-
- homedirectory
- genericname1 pathname1
- genericname2 pathname2
- ...
-
- % end of generic names, start of address mappings
-
- hostname1 hardwaretype hardwareaddr1 ipaddr1 genericname suffix
- hostname2 hardwaretype hardwareaddr2 ipaddr2 genericname suffix
- ...
-
- Here is a specific example. Note the 'hardwaretype' number is the
- same as that shown in the ARP section of the 'Assigned Numbers' RFC.
- The 'hardwaretype' and 'ipaddr' numbers are in decimal;
- 'hardwareaddr' is in hex.
-
- # last updated by smith
-
- /usr/boot
- vmunix vmunix
- tip ethertip
- watch /usr/diag/etherwatch
- gate gate.
-
- % end of generic names, start of address mappings
-
- hamilton 1 02.60.8c.06.34.98 36.19.0.5
- burr 1 02.60.8c.34.11.78 36.44.0.12
- 101-gateway 1 02.60.8c.23.ab.35 36.44.0.32 gate 101
- mjh-gateway 1 02.60.8c.12.32.bc 36.42.0.64 gate mjh
- welch-tipa 1 02.60.8c.22.65.32 36.47.0.14 tip
- welch-tipb 1 02.60.8c.12.15.c8 36.46.0.12 tip
-
- In the example above, if 'mjh-gateway' does a default boot, it will
- get the file '/usr/boot/gate.mjh'.
-
-
-
-
-
-Croft & Gilmore [Page 11]
-
-
-
-RFC 951 September 1985
-Bootstrap Protocol
-
-
-10. Acknowledgements
-
- Ross Finlayson (et. al.) produced two earlier RFC's discussing TFTP
- bootstraping [2] using RARP [1].
-
- We would also like to acknowledge the previous work and comments of
- Noel Chiappa, Bob Lyon, Jeff Mogul, Mark Lewis, and David Plummer.
-
-REFERENCES
-
- 1. Ross Finlayson, Timothy Mann, Jeffrey Mogul, Marvin Theimer. A
- Reverse Address Resolution Protocol. RFC 903, NIC, June, 1984.
-
- 2. Ross Finlayson. Bootstrap Loading using TFTP. RFC 906, NIC,
- June, 1984.
-
- 3. Mark Lottor. Simple File Transfer Protocol. RFC 913, NIC,
- September, 1984.
-
- 4. Jeffrey Mogul. Broadcasting Internet Packets. RFC 919, NIC,
- October, 1984.
-
- 5. David Plummer. An Ethernet Address Resolution Protocol. RFC
- 826, NIC, September, 1982.
-
- 6. Jon Postel. File Transfer Protocol. RFC 765, NIC, June, 1980.
-
- 7. Jon Postel. User Datagram Protocol. RFC 768, NIC, August, 1980.
-
- 8. Jon Postel. Internet Protocol. RFC 791, NIC, September, 1981.
-
- 9. K. R. Sollins, Noel Chiappa. The TFTP Protocol. RFC 783, NIC,
- June, 1981.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Croft & Gilmore [Page 12]
-
diff --git a/includes/cf/aix.h b/includes/cf/aix.h
index 202e8572..6bf5cf50 100644
--- a/includes/cf/aix.h
+++ b/includes/cf/aix.h
@@ -67,6 +67,9 @@ extern int h_errno;
#ifndef _PATH_DHCLIENT_PID
#define _PATH_DHCLIENT_PID "/etc/dhclient.pid"
#endif
+#ifndef _PATH_DHCLIENT6_PID
+#define _PATH_DHCLIENT6_PID "/etc/dhclient6.pid"
+#endif
#ifndef _PATH_DHCRELAY_PID
#define _PATH_DHCRELAY_PID "/etc/dhcrelay.pid"
#endif
@@ -77,7 +80,7 @@ extern int h_errno;
#define va_dcl
/* The vsnprint function definition in /usr/include/ appears to use this
- * as a key for wether or not it should be declared. Seems reasoanble for
+ * as a key for whether or not it should be declared. Seems reasoanble for
* us to use the same key.
*/
#if (_XOPEN_SOURCE != 500)
diff --git a/includes/cf/alphaosf.h b/includes/cf/alphaosf.h
index 625e4a92..99702544 100644
--- a/includes/cf/alphaosf.h
+++ b/includes/cf/alphaosf.h
@@ -83,6 +83,9 @@ typedef unsigned long u_int64_t;
#ifndef _PATH_DHCLIENT_PID
#define _PATH_DHCLIENT_PID "/var/run/dhclient.pid"
#endif
+#ifndef _PATH_DHCLIENT6_PID
+#define _PATH_DHCLIENT6_PID "/var/run/dhclient6.pid"
+#endif
#define EOL '\n'
#define VOIDPTR void *
diff --git a/includes/cf/bsdos.h b/includes/cf/bsdos.h
index 9f9234b5..615c06eb 100644
--- a/includes/cf/bsdos.h
+++ b/includes/cf/bsdos.h
@@ -66,9 +66,15 @@ extern int h_errno;
#ifndef _PATH_DHCLIENT_PID
#define _PATH_DHCLIENT_PID "/var/run/dhclient.pid"
#endif
+#ifndef _PATH_DHCLIENT6_PID
+#define _PATH_DHCLIENT6_PID "/var/run/dhclient6.pid"
+#endif
#ifndef _PATH_DHCLIENT_DB
#define _PATH_DHCLIENT_DB "/var/db/dhclient.leases"
#endif
+#ifndef _PATH_DHCLIENT6_DB
+#define _PATH_DHCLIENT6_DB "/var/db/dhclient6.leases"
+#endif
#define EOL '\n'
#define VOIDPTR void *
diff --git a/includes/cf/cygwin32.h b/includes/cf/cygwin32.h
index 812180ae..43f9bdaa 100644
--- a/includes/cf/cygwin32.h
+++ b/includes/cf/cygwin32.h
@@ -84,9 +84,15 @@
#ifndef _PATH_DHCLIENT_PID
#define _PATH_DHCLIENT_PID "//e/etc/dhclient.pid"
#endif
+#ifndef _PATH_DHCLIENT6_PID
+#define _PATH_DHCLIENT6_PID "//e/etc/dhclient6.pid"
+#endif
#ifndef _PATH_DHCLIENT_DB
#define _PATH_DHCLIENT_DB "//e/etc/dhclient.leases"
#endif
+#ifndef _PATH_DHCLIENT6_DB
+#define _PATH_DHCLIENT6_DB "//e/etc/dhclient6.leases"
+#endif
#ifndef _PATH_DHCLIENT_CONF
#define _PATH_DHCLIENT_CONF "//e/etc/dhclient.conf"
#endif
diff --git a/includes/cf/freebsd.h b/includes/cf/freebsd.h
index 75804de7..0bfadaa3 100644
--- a/includes/cf/freebsd.h
+++ b/includes/cf/freebsd.h
@@ -73,9 +73,15 @@ extern int h_errno;
#ifndef _PATH_DHCLIENT_PID
#define _PATH_DHCLIENT_PID "/var/run/dhclient.pid"
#endif
+#ifndef _PATH_DHCLIENT6_PID
+#define _PATH_DHCLIENT6_PID "/var/run/dhclient6.pid"
+#endif
#ifndef _PATH_DHCLIENT_DB
#define _PATH_DHCLIENT_DB "/var/db/dhclient.leases"
#endif
+#ifndef _PATH_DHCLIENT6_DB
+#define _PATH_DHCLIENT6_DB "/var/db/dhclient6.leases"
+#endif
#define EOL '\n'
#define VOIDPTR void *
diff --git a/includes/cf/hpux.h b/includes/cf/hpux.h
index bd3cf1f9..dc4b147c 100644
--- a/includes/cf/hpux.h
+++ b/includes/cf/hpux.h
@@ -65,6 +65,9 @@ extern int h_errno;
#ifndef _PATH_DHCLIENT_PID
#define _PATH_DHCLIENT_PID "/etc/dhclient.pid"
#endif
+#ifndef _PATH_DHCLIENT6_PID
+#define _PATH_DHCLIENT6_PID "/etc/dhclient6.pid"
+#endif
#ifndef _PATH_DHCRELAY_PID
#define _PATH_DHCRELAY_PID "/etc/dhcrelay.pid"
#endif
diff --git a/includes/cf/irix.h b/includes/cf/irix.h
index d03a9177..3e957da0 100644
--- a/includes/cf/irix.h
+++ b/includes/cf/irix.h
@@ -58,6 +58,9 @@ extern int h_errno;
#ifndef _PATH_DHCLIENT_PID
#define _PATH_DHCLIENT_PID "/etc/dhclient.pid"
#endif
+#ifndef _PATH_DHCLIENT6_PID
+#define _PATH_DHCLIENT6_PID "/etc/dhclient6.pid"
+#endif
#ifndef _PATH_DHCRELAY_PID
#define _PATH_DHCRELAY_PID "/etc/dhcrelay.pid"
#endif
diff --git a/includes/cf/linux.h b/includes/cf/linux.h
index ad2c9790..c0b527a0 100644
--- a/includes/cf/linux.h
+++ b/includes/cf/linux.h
@@ -89,6 +89,9 @@ extern int h_errno;
#ifndef _PATH_DHCLIENT_DB
#define _PATH_DHCLIENT_DB "/var/state/dhcp/dhclient.leases"
#endif
+#ifndef _PATH_DHCLIENT6_DB
+#define _PATH_DHCLIENT6_DB "/var/state/dhcp/dhclient6.leases"
+#endif
/* Varargs stuff... */
#include <stdarg.h>
diff --git a/includes/cf/netbsd.h b/includes/cf/netbsd.h
index fcd95577..97cfa09a 100644
--- a/includes/cf/netbsd.h
+++ b/includes/cf/netbsd.h
@@ -69,9 +69,15 @@ extern int h_errno;
#ifndef _PATH_DHCLIENT_PID
#define _PATH_DHCLIENT_PID "/var/run/dhclient.pid"
#endif
+#ifndef _PATH_DHCLIENT6_PID
+#define _PATH_DHCLIENT6_PID "/var/run/dhclient6.pid"
+#endif
#ifndef _PATH_DHCLIENT_DB
#define _PATH_DHCLIENT_DB "/var/db/dhclient.leases"
#endif
+#ifndef _PATH_DHCLIENT6_DB
+#define _PATH_DHCLIENT6_DB "/var/db/dhclient6.leases"
+#endif
#define EOL '\n'
#define VOIDPTR void *
diff --git a/includes/cf/nextstep.h b/includes/cf/nextstep.h
index 38517230..e33e0b87 100644
--- a/includes/cf/nextstep.h
+++ b/includes/cf/nextstep.h
@@ -79,6 +79,10 @@ extern int h_errno;
# define _PATH_DHCLIENT_PID "/etc/dhclient.pid"
#endif
+#if !defined (_PATH_DHCLIENT6_PID)
+# define _PATH_DHCLIENT6_PID "/etc/dhclient6.pid"
+#endif
+
#if !defined (_PATH_DHCRELAY_PID)
# define _PATH_DHCRELAY_PID "/etc/dhcrelay.pid"
#endif
diff --git a/includes/cf/openbsd.h b/includes/cf/openbsd.h
index dc29bf3b..7f094950 100644
--- a/includes/cf/openbsd.h
+++ b/includes/cf/openbsd.h
@@ -69,9 +69,15 @@ extern int h_errno;
#ifndef _PATH_DHCLIENT_PID
#define _PATH_DHCLIENT_PID "/var/run/dhclient.pid"
#endif
+#ifndef _PATH_DHCLIENT6_PID
+#define _PATH_DHCLIENT6_PID "/var/run/dhclient6.pid"
+#endif
#ifndef _PATH_DHCLIENT_DB
#define _PATH_DHCLIENT_DB "/var/db/dhclient.leases"
#endif
+#ifndef _PATH_DHCLIENT6_DB
+#define _PATH_DHCLIENT6_DB "/var/db/dhclient6.leases"
+#endif
#define EOL '\n'
#define VOIDPTR void *
diff --git a/includes/cf/qnx.h b/includes/cf/qnx.h
index 5bd6673e..bdfd7359 100644
--- a/includes/cf/qnx.h
+++ b/includes/cf/qnx.h
@@ -69,6 +69,9 @@ extern int h_errno;
#ifndef _PATH_DHCLIENT_PID
#define _PATH_DHCLIENT_PID "/etc/dhclient.pid"
#endif
+#ifndef _PATH_DHCLIENT6_PID
+#define _PATH_DHCLIENT6_PID "/etc/dhclient6.pid"
+#endif
#ifndef _PATH_DHCRELAY_PID
#define _PATH_DHCRELAY_PID "/etc/dhcrelay.pid"
#endif
diff --git a/includes/cf/rhapsody.h b/includes/cf/rhapsody.h
index d99a7c67..0535ac32 100644
--- a/includes/cf/rhapsody.h
+++ b/includes/cf/rhapsody.h
@@ -69,9 +69,15 @@ extern int h_errno;
#ifndef _PATH_DHCLIENT_PID
#define _PATH_DHCLIENT_PID "/var/run/dhclient.pid"
#endif
+#ifndef _PATH_DHCLIENT6_PID
+#define _PATH_DHCLIENT6_PID "/var/run/dhclient6.pid"
+#endif
#ifndef _PATH_DHCLIENT_DB
#define _PATH_DHCLIENT_DB "/var/db/dhclient.leases"
#endif
+#ifndef _PATH_DHCLIENT6_DB
+#define _PATH_DHCLIENT6_DB "/var/db/dhclient6.leases"
+#endif
#define EOL '\n'
#define VOIDPTR void *
diff --git a/includes/cf/sco.h b/includes/cf/sco.h
index ce844e48..1d5418a0 100644
--- a/includes/cf/sco.h
+++ b/includes/cf/sco.h
@@ -83,6 +83,9 @@ extern int h_errno;
#ifndef _PATH_DHCLIENT_PID
#define _PATH_DHCLIENT_PID "/etc/dhclient.pid"
#endif
+#ifndef _PATH_DHCLIENT6_PID
+#define _PATH_DHCLIENT6_PID "/etc/dhclient6.pid"
+#endif
#ifndef _PATH_DHCRELAY_PID
#define _PATH_DHCRELAY_PID "/etc/dhcrelay.pid"
#endif
@@ -92,6 +95,9 @@ extern int h_errno;
#ifndef _PATH_DHCLIENT_DB
#define _PATH_DHCLIENT_DB "/etc/dhclient.leases"
#endif
+#ifndef _PATH_DHCLIENT6_DB
+#define _PATH_DHCLIENT6_DB "/etc/dhclient6.leases"
+#endif
#if !defined (INADDR_LOOPBACK)
diff --git a/includes/cf/sunos4.h b/includes/cf/sunos4.h
index f6fa5578..a0001ead 100644
--- a/includes/cf/sunos4.h
+++ b/includes/cf/sunos4.h
@@ -102,6 +102,9 @@ extern int h_errno;
#ifndef _PATH_DHCLIENT_PID
#define _PATH_DHCLIENT_PID "/etc/dhclient.pid"
#endif
+#ifndef _PATH_DHCLIENT6_PID
+#define _PATH_DHCLIENT6_PID "/etc/dhclient6.pid"
+#endif
#ifndef _PATH_DHCRELAY_PID
#define _PATH_DHCRELAY_PID "/etc/dhcrelay.pid"
#endif
diff --git a/includes/cf/sunos5-5.h b/includes/cf/sunos5-5.h
index e9789d60..9c103048 100644
--- a/includes/cf/sunos5-5.h
+++ b/includes/cf/sunos5-5.h
@@ -38,6 +38,7 @@
typedef uint8_t u_int8_t;
typedef uint16_t u_int16_t;
typedef uint32_t u_int32_t;
+typedef uint64_t u_int64_t;
#else /* Older SunOS has no idea what these things mean. */
typedef int8_t char
typedef int16_t short
@@ -95,6 +96,9 @@ extern int h_errno;
#ifndef _PATH_DHCLIENT_PID
#define _PATH_DHCLIENT_PID "/etc/dhclient.pid"
#endif
+#ifndef _PATH_DHCLIENT6_PID
+#define _PATH_DHCLIENT6_PID "/etc/dhclient6.pid"
+#endif
#ifndef _PATH_DHCRELAY_PID
#define _PATH_DHCRELAY_PID "/etc/dhcrelay.pid"
#endif
diff --git a/includes/cf/ultrix.h b/includes/cf/ultrix.h
index 176d4f48..6d09f39d 100644
--- a/includes/cf/ultrix.h
+++ b/includes/cf/ultrix.h
@@ -55,6 +55,9 @@ extern int h_errno;
#ifndef _PATH_DHCLIENT_PID
#define _PATH_DHCLIENT_PID "/etc/dhclient.pid"
#endif
+#ifndef _PATH_DHCLIENT6_PID
+#define _PATH_DHCLIENT6_PID "/etc/dhclient6.pid"
+#endif
#ifndef _PATH_DHCRELAY_PID
#define _PATH_DHCRELAY_PID "/etc/dhcrelay.pid"
#endif
diff --git a/includes/dhcp.h b/includes/dhcp.h
index 1be7b04f..9636043b 100644
--- a/includes/dhcp.h
+++ b/includes/dhcp.h
@@ -3,7 +3,7 @@
Protocol structures... */
/*
- * Copyright (c) 2004-2005 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004-2006 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
@@ -30,6 +30,9 @@
* To learn more about Vixie Enterprises, see ``http://www.vix.com''.
*/
+#ifndef DHCP_H
+#define DHCP_H
+
#define DHCP_UDP_OVERHEAD (20 + /* IP header */ \
8) /* UDP header */
#define DHCP_SNAME_LEN 64
@@ -197,3 +200,6 @@ struct dhcp_packet {
/* Enterprise Suboptions: */
#define VENDOR_ISC_SUBOPTIONS 2495
+
+#endif /* DHCP_H */
+
diff --git a/includes/dhcp6.h b/includes/dhcp6.h
new file mode 100644
index 00000000..dafee7fa
--- /dev/null
+++ b/includes/dhcp6.h
@@ -0,0 +1,160 @@
+/* dhcp6.h
+
+ DHCPv6 Protocol structures... */
+
+/*
+ * Copyright (c) 2006 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
+ * <info@isc.org>
+ * http://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 /* lifetime */
+#define D6O_BCMCS_SERVER_D 33 /* RFC4280 */
+#define D6O_BCMCS_SERVER_A 34 /* RFC4280 */
+/* 35 is unassigned */
+#define D6O_GEOCONF_CIVIC 36 /* geopriv-dhcp-civil */
+#define D6O_REMOTE_ID 37 /* dhcpv6-remoteid */
+#define D6O_SUBSCRIBER_ID 38 /* RFC4580 */
+#define D6O_CLIENT_FQDN 39 /* dhcpv6-fqdn */
+
+/*
+ * Status Codes, from RFC 3315 section 24.4.
+ */
+#define STATUS_Success 0
+#define STATUS_UnspecFail 1
+#define STATUS_NoAddrsAvail 2
+#define STATUS_NoBinding 3
+#define STATUS_NotOnLink 4
+#define STATUS_UseMulticast 5
+
+/*
+ * 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
+
+extern 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
+
+/*
+ * 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)
+ */
+
+#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
+
+/*
+ * 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[0];
+};
+
+/*
+ * 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[0];
+};
+
diff --git a/includes/dhcpd.h b/includes/dhcpd.h
index b621eb95..4277df95 100644
--- a/includes/dhcpd.h
+++ b/includes/dhcpd.h
@@ -32,6 +32,9 @@
* ``http://www.nominum.com''.
*/
+/* XXX: HACK just until the code is autoconfiscated */
+#define DHCPv6
+
#ifndef __CYGWIN32__
#include <sys/types.h>
#include <netinet/in.h>
@@ -73,10 +76,12 @@ typedef struct hash_table host_hash_t;
typedef struct hash_table class_hash_t;
#include "dhcp.h"
+#include "dhcp6.h"
#include "statement.h"
#include "tree.h"
#include "inet.h"
#include "dhctoken.h"
+#include "heap.h"
#include <isc-dhcp/result.h>
#include <omapip/omapip_p.h>
@@ -140,6 +145,10 @@ typedef struct hash_table class_hash_t;
# 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
@@ -318,6 +327,20 @@ struct packet {
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;
+
int options_valid;
int client_port;
struct iaddr client_addr;
@@ -353,6 +376,12 @@ struct packet {
* 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;
};
/* A network interface's MAC address. */
@@ -499,6 +528,11 @@ struct lease_state {
#define DISCOVER_RELAY 3
#define DISCOVER_REQUESTED 4
+/* DDNS_UPDATE_STYLE enumerations. */
+#define DDNS_UPDATE_STYLE_NONE 0
+#define DDNS_UPDATE_STYLE_AD_HOC 1
+#define DDNS_UPDATE_STYLE_INTERIM 2
+
/* Server option names. */
#define SV_DEFAULT_LEASE_TIME 1
@@ -553,6 +587,7 @@ struct lease_state {
#define SV_ADAPTIVE_LEASE_TIME_THRESHOLD 50
#define SV_DO_REVERSE_UPDATES 51
#define SV_FQDN_REPLY 52
+#define SV_PREFER_LIFETIME 53
#if !defined (DEFAULT_PING_TIMEOUT)
# define DEFAULT_PING_TIMEOUT 1
@@ -661,6 +696,11 @@ struct host_decl {
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 group *group;
struct group_object *named_group;
@@ -715,6 +755,9 @@ struct shared_network {
struct subnet *subnets;
struct interface_info *interface;
struct pool *pools;
+ struct ipv6_pool **ipv6_pools; /* NULL-terminated array */
+ int last_ipv6_pool; /* offset of last IPv6 pool
+ used to issue a lease */
struct group *group;
#if defined (FAILOVER_PROTOCOL)
dhcp_failover_state_t *failover_peer;
@@ -730,7 +773,7 @@ struct subnet {
struct iaddr interface_address;
struct iaddr net;
struct iaddr netmask;
-
+ int prefix_len; /* XXX: currently for IPv6 only */
struct group *group;
};
@@ -804,6 +847,48 @@ struct client_lease {
struct option_state *options; /* Options supplied with lease. */
};
+/* DHCPv6 lease structures */
+struct dhc6_addr {
+ struct dhc6_addr *next;
+ struct iaddr address;
+
+ /* Address state flags. */
+ #define DHC6_ADDR_DEPREFFED 0x01
+ #define DHC6_ADDR_EXPIRED 0x02
+ 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];
+
+ 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;
+
+ 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,
@@ -879,35 +964,77 @@ struct client_config {
};
/* 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. */
+
+
+ /* 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. */
- enum dhcp_state state; /* Current state for this interface. */
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? */
- int dns_update_timeout; /* Last timeout set for DNS update. */
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. */
- struct client_config *config; /* Client configuration. */
- struct string_list *env; /* Client script environment. */
- int envc; /* Number of entries in environment. */
+ /* 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;
+
+ TIME start_time;
+ u_int16_t elapsed;
+ int txcount;
+
+ /* See RFC3315 section 14. */
+ TIME RT;
+ TIME IRT;
+ TIME MRC;
+ TIME MRT;
+ TIME MRD;
+ TIME next_MRD;
+
+ /* 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 *);
+};
- struct option_state *sent_options; /* Options we sent. */
+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. */
@@ -918,7 +1045,17 @@ struct interface_info {
struct shared_network *shared_network;
/* Networks connected to this interface. */
struct hardware hw_address; /* Its physical address. */
- struct in_addr primary_address; /* Primary interface 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. */
@@ -939,6 +1076,9 @@ struct interface_info {
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
@@ -1082,10 +1222,18 @@ typedef unsigned char option_mask [16];
#define _PATH_DHCLIENT_PID "/var/run/dhclient.pid"
#endif
+#ifndef _PATH_DHCLIENT6_PID
+#define _PATH_DHCLIENT6_PID "/var/run/dhclient6.pid"
+#endif
+
#ifndef _PATH_DHCLIENT_DB
#define _PATH_DHCLIENT_DB "/etc/dhclient.leases"
#endif
+#ifndef _PATH_DHCLIENT6_DB
+#define _PATH_DHCLIENT6_DB "/etc/dhclient6.leases"
+#endif
+
#ifndef _PATH_RESOLV_CONF
#define _PATH_RESOLV_CONF "/etc/resolv.conf"
#endif
@@ -1101,6 +1249,47 @@ typedef unsigned char option_mask [16];
#define MAX_TIME 0x7fffffff
#define MIN_TIME 0
+typedef struct hash_table ia_na_hash_t;
+typedef struct hash_table iaaddr_hash_t;
+
+struct iaaddr {
+ int refcnt; /* reference count */
+ struct in6_addr addr; /* IPv6 address */
+ binding_state_t state; /* state */
+ struct binding_scope *scope; /* "set var = value;" */
+ time_t valid_lifetime_end_time; /* time address expires */
+ struct ia_na *ia_na; /* IA for this address */
+ struct ipv6_pool *ipv6_pool; /* pool for this address */
+ int heap_index; /* index into heap, or -1
+ (internal use only) */
+};
+
+struct ia_na {
+ int refcnt; /* reference count */
+ struct data_string iaid_duid; /* from the client */
+ int num_iaaddr; /* number of IAADDR for this IA_NA */
+ int max_iaaddr; /* space available for IAADDR */
+ struct iaaddr **iaaddr; /* pointers to the various IAADDRs */
+};
+
+extern ia_na_hash_t *ia_active;
+
+struct ipv6_pool {
+ int refcnt; /* reference count */
+ struct in6_addr start_addr; /* first IPv6 address */
+ int bits; /* number of bits, CIDR style */
+ iaaddr_hash_t *addrs; /* non-free IAADDR */
+ int num_active; /* count of active IAADDR */
+ isc_heap_t *active_timeouts; /* timeouts for active leases */
+ isc_heap_t *inactive_timeouts; /* timeouts for expired or
+ released leases */
+ struct shared_network *shared_network; /* shared_network for
+ this pool */
+};
+
+extern struct ipv6_pool **pools;
+extern int num_pools;
+
/* External definitions... */
HASH_FUNCTIONS_DECL (group, const char *, struct group_object, group_hash_t)
@@ -1134,12 +1323,52 @@ int cons_options PROTO ((struct packet *, struct dhcp_packet *, struct lease *,
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);
+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 PROTO ((int *, unsigned char *, unsigned, struct packet *,
struct lease *, struct client_state *,
struct option_state *,
struct option_state *, struct binding_scope **,
unsigned *, int, unsigned, unsigned,
int, const char *));
+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 PROTO ((struct option *, const unsigned char *,
@@ -1158,9 +1387,14 @@ struct option_cache *lookup_option PROTO ((struct universe *,
struct option_cache *lookup_hashed_option PROTO ((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);
+void build_server_oro(struct data_string *, struct option_state *,
+ const char *, int);
void save_option PROTO ((struct universe *,
struct option_state *, struct option_cache *));
void save_hashed_option PROTO ((struct universe *,
@@ -1279,6 +1513,14 @@ struct option_cache *lookup_linked_option (struct universe *,
void do_packet PROTO ((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 add_option(struct option_state *options,
+ unsigned int option_num,
+ void *data,
+ unsigned int data_len);
int add_option(struct option_state *options,
unsigned int option_num,
@@ -1296,9 +1538,9 @@ extern const char *path_dhcpd_pid;
extern int dhcp_max_agent_option_packet_length;
-int main PROTO ((int, char **, char **));
-void postconf_initialization (int);
-void postdb_startup (void);
+int main(int, char **);
+void postconf_initialization(int);
+void postdb_startup(void);
void cleanup PROTO ((void));
void lease_pinged PROTO ((struct iaddr, u_int8_t *, int));
void lease_ping_timeout PROTO ((void *));
@@ -1344,16 +1586,23 @@ int parse_class_declaration PROTO ((struct class **, struct parse *,
void parse_shared_net_declaration PROTO ((struct parse *, struct group *));
void parse_subnet_declaration PROTO ((struct parse *,
struct shared_network *));
+void parse_subnet6_declaration PROTO ((struct parse *,
+ struct shared_network *));
void parse_group_declaration PROTO ((struct parse *, struct group *));
-int parse_fixed_addr_param PROTO ((struct option_cache **, struct parse *));
+int parse_fixed_addr_param PROTO ((struct option_cache **,
+ struct parse *, enum dhcp_token));
int parse_lease_declaration PROTO ((struct lease **, struct parse *));
void parse_address_range PROTO ((struct parse *, struct group *, int,
struct pool *, struct lease **));
+void parse_address_range6(struct parse *cfile, struct group *group);
+void parse_ia_na_declaration(struct parse *);
+void parse_server_duid(struct parse *cfile);
+void parse_server_duid_conf(struct parse *cfile);
/* ddns.c */
-int ddns_updates PROTO ((struct packet *, struct lease *, struct lease *,
- struct lease_state *));
-int ddns_removals PROTO ((struct lease *));
+int ddns_updates(struct packet *, struct lease *, struct lease *,
+ struct iaaddr *, struct iaaddr *, struct option_state *);
+int ddns_removals(struct lease *, struct iaaddr *);
/* parse.c */
void add_enumeration (struct enumeration *);
@@ -1419,7 +1668,7 @@ int parse_allow_deny PROTO ((struct option_cache **, struct parse *, int));
int parse_auth_key PROTO ((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);
+struct expression *parse_domain_list(struct parse *cfile, int);
/* tree.c */
@@ -1528,6 +1777,7 @@ int bind_ds_value (struct binding_scope **,
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, ...);
/* dhcp.c */
extern int outstanding_pings;
@@ -1563,6 +1813,18 @@ void get_server_source_address(struct in_addr *from,
struct option_state *options,
struct packet *packet);
+void get_server_source_address(struct in_addr *from,
+ struct option_state *options,
+ struct packet *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);
+isc_result_t generate_new_server_duid(void);
+void dhcpv6(struct packet *);
+
/* bootp.c */
void bootp PROTO ((struct packet *));
@@ -1730,10 +1992,12 @@ void print_dns_status (int, ns_updque *);
#endif
const char *print_time(TIME);
+void get_hw_addr(const char *name, struct hardware *hw);
+
/* socket.c */
#if defined (USE_SOCKET_SEND) || defined (USE_SOCKET_RECEIVE) \
|| defined (USE_SOCKET_FALLBACK)
-int if_register_socket PROTO ((struct interface_info *));
+int if_register_socket(struct interface_info *, int, int);
#endif
#if defined (USE_SOCKET_FALLBACK) && !defined (USE_SOCKET_SEND)
@@ -1743,6 +2007,9 @@ ssize_t send_fallback PROTO ((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
@@ -1753,6 +2020,9 @@ ssize_t send_packet PROTO ((struct interface_info *,
struct packet *, struct dhcp_packet *, size_t,
struct in_addr,
struct sockaddr_in *, struct hardware *));
+ssize_t send_packet6(struct interface_info *, struct packet *,
+ struct dhcp_packet *, size_t, struct in6_addr,
+ struct sockaddr_in6 *, struct hardware *);
#endif
#ifdef USE_SOCKET_RECEIVE
void if_reinitialize_receive PROTO ((struct interface_info *));
@@ -1761,6 +2031,8 @@ void if_deregister_receive PROTO ((struct interface_info *));
ssize_t receive_packet PROTO ((struct interface_info *,
unsigned char *, size_t,
struct sockaddr_in *, struct hardware *));
+ssize_t receive_packet6(struct interface_info *, unsigned char *, size_t,
+ struct sockaddr_in *, struct in6_addr *);
#endif
#if defined (USE_SOCKET_FALLBACK)
@@ -1907,7 +2179,9 @@ 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 u_int16_t local_port;
extern u_int16_t remote_port;
@@ -1920,6 +2194,9 @@ extern void (*bootp_packet_handler) PROTO ((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)
@@ -1930,8 +2207,8 @@ trace_type_t *outpacket_trace;
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 PROTO ((int));
+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 PROTO ((omapi_object_t *));
void reinitialize_interfaces PROTO ((void));
@@ -1940,7 +2217,8 @@ void reinitialize_interfaces PROTO ((void));
void set_time(TIME);
struct timeval *process_outstanding_timeouts (struct timeval *);
void dispatch PROTO ((void));
-isc_result_t got_one PROTO ((omapi_object_t *));
+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 *,
@@ -1970,8 +2248,10 @@ OMAPI_OBJECT_ALLOC_DECL (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];
@@ -2006,8 +2286,17 @@ struct iaddr broadcast_addr PROTO ((struct iaddr, struct iaddr));
u_int32_t host_addr PROTO ((struct iaddr, struct iaddr));
int addr_eq PROTO ((struct iaddr, struct iaddr));
int addr_match(struct iaddr *, struct iaddrmatch *);
-char *piaddr PROTO ((struct iaddr));
-char *piaddrmask (struct iaddr, struct iaddr, const char *, int);
+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_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 PROTO ((struct iaddr));
+char *piaddrmask(struct iaddr *, struct iaddr *);
+char *piaddrcidr(const struct iaddr *, unsigned int);
/* dhclient.c */
extern const char *path_dhclient_conf;
@@ -2015,6 +2304,7 @@ 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 struct client_config top_level_config;
@@ -2054,6 +2344,9 @@ void write_lease_option (struct option_cache *, struct packet *,
struct binding_scope **, struct universe *, void *);
int write_client_lease PROTO ((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 PROTO ((struct client_state *, const char *,
@@ -2077,8 +2370,23 @@ void do_release PROTO ((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, int, int);
+isc_result_t client_dns_update(struct client_state *client, int, int,
+ struct iaddr *);
+
+void dhcpv4_client_assignments(void);
+void dhcpv6_client_assignments(void);
+
+/* dhc6.c */
+void dhc6_lease_destroy(struct dhc6_lease *lease, char *file, int line);
+void start_init6(struct client_state *client);
+void start_confirm6(struct client_state *client);
+void start_selecting6(struct client_state *client);
+isc_result_t write_client6_lease(struct client_state *client,
+ struct dhc6_lease *lease,
+ int rewrite, int sync);
/* db.c */
int write_lease PROTO ((struct lease *));
@@ -2096,6 +2404,7 @@ int commit_leases PROTO ((void));
void db_startup PROTO ((int));
int new_lease_file PROTO ((void));
int group_writer (struct group_object *);
+int write_ia_na(const struct ia_na *);
/* packet.c */
u_int32_t checksum PROTO ((unsigned char *, unsigned, u_int32_t));
@@ -2226,11 +2535,11 @@ void forget_zone (struct dns_zone **);
void repudiate_zone (struct dns_zone **);
void cache_found_zone (ns_class, char *, struct in_addr *, int);
int get_dhcid (struct data_string *, int, const u_int8_t *, unsigned);
-isc_result_t ddns_update_a(struct data_string *, struct iaddr,
- struct data_string *, unsigned long, unsigned,
- unsigned);
-isc_result_t ddns_remove_a (struct data_string *,
- struct iaddr, 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 *);
#endif /* NSUPDATE */
/* resolv.c */
@@ -2591,11 +2900,14 @@ isc_result_t enter_class PROTO ((struct class *, int, int));
isc_result_t delete_class PROTO ((struct class *, int));
isc_result_t enter_host PROTO ((struct host_decl *, int, int));
isc_result_t delete_host PROTO ((struct host_decl *, int));
+void change_host_uid(struct host_decl *host, const char *data, int len);
int find_hosts_by_haddr PROTO ((struct host_decl **, int,
const unsigned char *, unsigned,
const char *, int));
int find_hosts_by_uid PROTO ((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 PROTO ((struct subnet **, struct host_decl **,
struct iaddr *, struct shared_network *));
void new_address_range PROTO ((struct parse *, struct iaddr, struct iaddr,
@@ -2605,12 +2917,12 @@ 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 PROTO ((struct subnet **, struct shared_network *,
struct iaddr, const char *, int));
-int find_subnet (struct subnet **, struct iaddr, const char *, int);
+int find_subnet(struct subnet **, struct iaddr, const char *, int);
void enter_shared_network PROTO ((struct shared_network *));
void new_shared_network_interface PROTO ((struct parse *,
struct shared_network *,
const char *));
-int subnet_inner_than PROTO ((struct subnet *, struct subnet *, int));
+int subnet_inner_than(const struct subnet *, const struct subnet *, int);
void enter_subnet PROTO ((struct subnet *));
void enter_lease PROTO ((struct lease *));
int supersede_lease PROTO ((struct lease *, struct lease *, int, int, int));
@@ -2631,6 +2943,7 @@ void uid_hash_delete PROTO ((struct lease *));
void hw_hash_add PROTO ((struct lease *));
void hw_hash_delete PROTO ((struct lease *));
int write_leases PROTO ((void));
+int write_leases6(void);
int lease_enqueue (struct lease *);
isc_result_t lease_instantiate(const void *, unsigned, void *);
void expire_all_pools PROTO ((void));
@@ -2812,3 +3125,71 @@ OMAPI_OBJECT_ALLOC_DECL (dhcp_failover_link, dhcp_failover_link_t,
#endif /* FAILOVER_PROTOCOL */
const char *binding_state_print (enum failover_state);
+
+
+/* mdb6.c */
+HASH_FUNCTIONS_DECL(ia_na, unsigned char *, struct ia_na, ia_na_hash_t);
+HASH_FUNCTIONS_DECL(iaaddr, struct in6_addr *, struct iaaddr, iaaddr_hash_t);
+
+isc_result_t iaaddr_allocate(struct iaaddr **iaaddr,
+ const char *file, int line);
+isc_result_t iaaddr_reference(struct iaaddr **iaaddr, struct iaaddr *src,
+ const char *file, int line);
+isc_result_t iaaddr_dereference(struct iaaddr **iaaddr,
+ const char *file, int line);
+
+isc_result_t ia_na_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_na_allocate(struct ia_na **ia_na, u_int32_t iaid,
+ const char *duid, unsigned int duid_len,
+ const char *file, int line);
+isc_result_t ia_na_reference(struct ia_na **ia_na, struct ia_na *src,
+ const char *file, int line);
+isc_result_t ia_na_dereference(struct ia_na **ia_na,
+ const char *file, int line);
+isc_result_t ia_na_add_iaaddr(struct ia_na *ia_na, struct iaaddr *iaaddr,
+ const char *file, int line);
+void ia_na_remove_iaaddr(struct ia_na *ia_na, struct iaaddr *iaaddr,
+ const char *file, int line);
+
+isc_result_t ipv6_pool_allocate(struct ipv6_pool **pool,
+ const struct in6_addr *start_addr, int bits,
+ 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 activate_lease6(struct ipv6_pool *pool,
+ struct iaaddr **addr,
+ unsigned int *attempts,
+ const struct data_string *uid,
+ time_t valid_lifetime_end_time);
+isc_result_t add_lease6(struct ipv6_pool *pool,
+ struct iaaddr *addr,
+ time_t valid_lifetime_end_time);
+isc_result_t renew_lease6(struct ipv6_pool *pool, struct iaaddr *addr);
+isc_result_t expire_lease6(struct iaaddr **addr,
+ struct ipv6_pool *pool, time_t now);
+isc_result_t release_lease6(struct ipv6_pool *pool, struct iaaddr *addr);
+isc_result_t decline_lease6(struct ipv6_pool *pool, struct iaaddr *addr);
+isc_boolean_t lease6_exists(const struct ipv6_pool *pool,
+ const struct in6_addr *addr);
+isc_result_t mark_address_unavailble(struct ipv6_pool *pool,
+ const struct in6_addr *addr);
+
+isc_result_t add_ipv6_pool(struct ipv6_pool *pool);
+isc_result_t find_ipv6_pool(struct ipv6_pool **pool,
+ const struct in6_addr *addr);
+isc_boolean_t ipv6_addr_in_pool(const struct in6_addr *addr,
+ const struct ipv6_pool *pool);
+
+void expire_leases(time_t now);
+isc_result_t renew_leases(struct ia_na *ia_na);
+isc_result_t release_leases(struct ia_na *ia_na);
+isc_result_t decline_leases(struct ia_na *ia_na);
+
+void mark_hosts_unavailable(void);
+void mark_interfaces_unavailable(void);
+
diff --git a/includes/dhctoken.h b/includes/dhctoken.h
index 89df27b0..24e07438 100644
--- a/includes/dhctoken.h
+++ b/includes/dhctoken.h
@@ -325,7 +325,23 @@ enum dhcp_token {
MIN_BALANCE = 629,
DOMAIN_LIST = 630,
LEASEQUERY = 631,
- EXECUTE = 632
+ EXECUTE = 632,
+ IP6_ADDRESS = 633,
+ FIXED_ADDR6 = 634,
+ COMPRESSED = 635,
+ SUBNET6 = 636,
+ HOST_IDENTIFIER = 637,
+ IA_NA = 638,
+ IAADDR = 639,
+ LEASE6 = 640,
+ PREFERRED_LIFE = 641,
+ MAX_LIFE = 642,
+ DEFAULT_DUID = 643,
+ SERVER_DUID = 644,
+ LLT = 645,
+ EN = 646,
+ LL = 647,
+ RANGE6 = 648
};
#define is_identifier(x) ((x) >= FIRST_TOKEN && \
diff --git a/includes/heap.h b/includes/heap.h
new file mode 100644
index 00000000..788372c4
--- /dev/null
+++ b/includes/heap.h
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1997-2001 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.
+ */
+
+/* $Id: heap.h,v 1.2 2007/05/08 23:05:21 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 */
diff --git a/includes/inet.h b/includes/inet.h
index ddc2d9ea..2c61b20c 100644
--- a/includes/inet.h
+++ b/includes/inet.h
@@ -67,3 +67,19 @@ 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;
+};
+
diff --git a/includes/tree.h b/includes/tree.h
index a13f67a2..c25bf4e1 100644
--- a/includes/tree.h
+++ b/includes/tree.h
@@ -51,6 +51,7 @@ struct enumeration_value {
struct enumeration {
struct enumeration *next;
const char *name;
+ unsigned width;
struct enumeration_value *values;
};
@@ -334,6 +335,9 @@ struct universe {
option_code_hash_t *code_hash;
struct option *enc_opt;
int index;
+
+ /* Flags should probably become condensed. */
+ int concat_duplicates;
};
struct option {
diff --git a/includes/version.h b/includes/version.h
index 3d35424d..790855d4 100644
--- a/includes/version.h
+++ b/includes/version.h
@@ -1,3 +1,3 @@
/* Current version of ISC DHCP Distribution. */
-#define DHCP_VERSION "V3.?-HEAD"
+#define DHCP_VERSION "V4.0.0-2007041300"
diff --git a/minires/res_mkupdate.c b/minires/res_mkupdate.c
index c7395863..70a554f8 100644
--- a/minires/res_mkupdate.c
+++ b/minires/res_mkupdate.c
@@ -27,7 +27,7 @@
*/
#if !defined(lint) && !defined(SABER)
-static const char rcsid[] = "$Id: res_mkupdate.c,v 1.8 2005/03/17 20:15:19 dhankins Exp $";
+static const char rcsid[] = "$Id: res_mkupdate.c,v 1.9 2007/05/08 23:05:21 dhankins Exp $";
#endif /* not lint */
#include <sys/types.h>
@@ -102,6 +102,7 @@ res_nmkupdate(res_state statp,
int n, i, soanum, multiline;
ns_updrec *rrecp;
struct in_addr ina;
+ struct in6_addr in6a;
char buf2[MAXDNAME];
u_char buf3[MAXDNAME];
int section, numrrs = 0, counts[ns_s_max];
@@ -236,6 +237,16 @@ res_nmkupdate(res_state statp,
ShrinkBuffer(INT32SZ);
PUTLONG(n1, cp);
break;
+ case T_AAAA:
+ if (!getword_str(buf2, sizeof buf2, &startp, endp))
+ return (-1);
+ if (!inet_pton(AF_INET6, buf2, &in6a))
+ return (-1);
+ n = sizeof(struct in6_addr);
+ memcpy(cp, &in6a, n);
+ cp += n;
+ ShrinkBuffer(n);
+ break;
case T_CNAME:
case T_MB:
case T_MG:
diff --git a/omapip/hash.c b/omapip/hash.c
index ace7ef22..88b80982 100644
--- a/omapip/hash.c
+++ b/omapip/hash.c
@@ -34,7 +34,7 @@
#ifndef lint
static char copyright[] =
-"$Id: hash.c,v 1.12 2006/10/27 22:54:12 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
+"$Id: hash.c,v 1.13 2007/05/08 23:05:21 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
#endif /* not lint */
#include <omapip/omapip_p.h>
diff --git a/relay/dhcrelay.8 b/relay/dhcrelay.8
index 238b16da..fc5cbc61 100644
--- a/relay/dhcrelay.8
+++ b/relay/dhcrelay.8
@@ -27,7 +27,7 @@
.\" see ``http://www.isc.org/isc''. To learn more about Vixie
.\" Enterprises, see ``http://www.vix.com''.
.\"
-.\" $Id: dhcrelay.8,v 1.12 2006/08/09 14:57:48 dhankins Exp $
+.\" $Id: dhcrelay.8,v 1.13 2007/05/08 23:05:21 dhankins Exp $
.\"
.TH dhcrelay 8
.SH NAME
diff --git a/relay/dhcrelay.c b/relay/dhcrelay.c
index 8465b03b..b2b1dcbe 100644
--- a/relay/dhcrelay.c
+++ b/relay/dhcrelay.c
@@ -34,7 +34,7 @@
#ifndef lint
static char ocopyright[] =
-"$Id: dhcrelay.c,v 1.59 2006/08/09 14:57:48 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
+"$Id: dhcrelay.c,v 1.60 2007/05/08 23:05:21 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
@@ -103,10 +103,8 @@ static char arr [] = "All rights reserved.";
static char message [] = "Internet Systems Consortium DHCP Relay Agent";
static char url [] = "For info, please visit http://www.isc.org/sw/dhcp/";
-int main (argc, argv, envp)
- int argc;
- char **argv, **envp;
-{
+int
+main(int argc, char **argv) {
int fd;
int i;
struct servent *ent;
@@ -350,9 +348,16 @@ void relay (ip, packet, length, from_port, from, hfrom)
in the packet. */
if (packet -> giaddr.s_addr) {
for (out = interfaces; out; out = out -> next) {
- if (!memcmp (&out -> primary_address,
- &packet -> giaddr,
- sizeof packet -> giaddr))
+ 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 {
@@ -399,10 +404,8 @@ void relay (ip, packet, length, from_port, from, hfrom)
return;
}
- if (send_packet (out,
- (struct packet *)0,
- packet, length, out -> primary_address,
- &to, htop) < 0) {
+ 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",
@@ -422,8 +425,8 @@ void relay (ip, packet, length, from_port, from, hfrom)
/* Add relay agent options if indicated. If something goes wrong,
drop the packet. */
- if (!(length = add_relay_agent_options (ip, packet, length,
- ip -> primary_address)))
+ 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
@@ -432,7 +435,7 @@ void relay (ip, packet, length, from_port, from, hfrom)
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 -> primary_address;
+ packet->giaddr = ip->addresses[0];
if (packet -> hops < max_hop_count)
packet -> hops = packet -> hops + 1;
else
@@ -441,11 +444,10 @@ void relay (ip, packet, length, from_port, from, hfrom)
/* 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),
- (struct packet *)0,
- packet, length, ip -> primary_address,
- &sp -> to, (struct hardware *)0) < 0) {
+ 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",
@@ -494,6 +496,11 @@ void dhcp (packet)
{
}
+void
+dhcpv6(struct packet *packet) {
+ /* XXX: should we warn or something here? */
+}
+
int find_subnet (struct subnet **sp,
struct iaddr addr, const char *file, int line)
{
diff --git a/server/Makefile.dist b/server/Makefile.dist
index 4ead8f0a..ef5f31d9 100644
--- a/server/Makefile.dist
+++ b/server/Makefile.dist
@@ -25,10 +25,12 @@
CATMANPAGES = dhcpd.cat8 dhcpd.conf.cat5 dhcpd.leases.cat5
SEDMANPAGES = dhcpd.man8 dhcpd.conf.man5 dhcpd.leases.man5
SRCS = 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
+ omapi.c mdb.c stables.c salloc.c ddns.c dhcpleasequery.c dhcpv6.c \
+ mdb6.c
OBJS = dhcpd.o dhcp.o bootp.o confpars.o db.o class.o failover.o \
- omapi.o mdb.o stables.o salloc.o ddns.o dhcpleasequery.o
-PROG = dhcpd
+ omapi.o mdb.o stables.o salloc.o ddns.o dhcpleasequery.o dhcpv6.o \
+ mdb6.o
+PROG = dhcpd testmdb6
MAN = dhcpd.8 dhcpd.conf.5 dhcpd.leases.5
INCLUDES = -I$(TOP) $(BINDINC) -I$(TOP)/includes
@@ -108,4 +110,17 @@ dhcpd.leases.man5: dhcpd.leases.5
dhcpd: $(OBJS) $(COBJ) $(DHCPLIB)
$(CC) $(LFLAGS) -o dhcpd $(OBJS) $(DHCPLIB) $(LIBS)
+testmdb6.o: mdb6.c
+ $(CC) -c -DUNIT_TEST -o testmdb6.o $(INCLUDES) $(PREDEFINES) mdb6.c
+
+testdhcpd.o: dhcpd.c
+ $(CC) -c -DUNIT_TEST -o testdhcpd.o $(INCLUDES) $(PREDEFINES) dhcpd.c
+
+TOBJS = dhcp.o bootp.o confpars.o db.o class.o failover.o \
+ omapi.o mdb.o stables.o salloc.o ddns.o dhcpleasequery.o dhcpv6.o
+
+testmdb6: testmdb6.o testdhcpd.o $(TOBJS) $(COBJ) $(DHCPLIB)
+ $(CC) $(LFLAGS) -o testmdb6 testmdb6.o testdhcpd.o \
+ $(TOBJS) $(DHCPLIB) $(LIBS)
+
# Dependencies (semi-automatically-generated)
diff --git a/server/bootp.c b/server/bootp.c
index 6976f6d1..6219c0f3 100644
--- a/server/bootp.c
+++ b/server/bootp.c
@@ -34,7 +34,7 @@
#ifndef lint
static char copyright[] =
-"$Id: bootp.c,v 1.76 2006/08/09 14:57:48 dhankins Exp $ Copyright (c) 2004-2005 Internet Systems Consortium. All rights reserved.\n";
+"$Id: bootp.c,v 1.77 2007/05/08 23:05:21 dhankins Exp $ Copyright (c) 2004-2005 Internet Systems Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
@@ -272,11 +272,12 @@ void bootp (packet)
memcpy (&raw.siaddr, d1.data, 4);
data_string_forget (&d1, MDL);
} else {
- if (lease -> subnet -> shared_network -> interface)
- raw.siaddr = (lease -> subnet -> shared_network ->
- interface -> primary_address);
- else
- raw.siaddr = packet -> interface -> primary_address;
+ 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;
@@ -326,7 +327,8 @@ void bootp (packet)
hto.hlen = packet -> raw -> hlen + 1;
memcpy (&hto.hbuf [1], packet -> raw -> chaddr, packet -> raw -> hlen);
- from = packet -> interface -> primary_address;
+ if (packet->interface->address_count)
+ from = packet->interface->addresses[0];
/* Report what we're doing... */
log_info ("%s", msgbuf);
diff --git a/server/confpars.c b/server/confpars.c
index 53968f19..61b696b6 100644
--- a/server/confpars.c
+++ b/server/confpars.c
@@ -34,7 +34,7 @@
#ifndef lint
static char copyright[] =
-"$Id: confpars.c,v 1.162 2007/05/03 21:24:38 each Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
+"$Id: confpars.c,v 1.163 2007/05/08 23:05:21 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
@@ -267,6 +267,8 @@ isc_result_t lease_file_subparse (struct parse *cfile)
} else
parse_warn (cfile,
"possibly corrupt lease file");
+ } else if (token == IA_NA) {
+ parse_ia_na_declaration(cfile);
} else if (token == CLASS) {
parse_class_declaration(0, cfile, root_group,
CLASS_TYPE_CLASS);
@@ -282,6 +284,8 @@ isc_result_t lease_file_subparse (struct parse *cfile)
parse_failover_state_declaration
(cfile, (dhcp_failover_state_t *)0);
#endif
+ } else if (token == SERVER_DUID) {
+ parse_server_duid(cfile);
} else {
log_error ("Corrupt lease file - possible data loss!");
skip_to_semi (cfile);
@@ -410,6 +414,7 @@ int parse_statement (cfile, group, type, host_decl, declaration)
return 1;
case SUBNET:
+ case SUBNET6:
next_token (&val, (unsigned *)0, cfile);
if (type == HOST_DECL || type == SUBNET_DECL ||
type == CLASS_DECL) {
@@ -420,9 +425,14 @@ int parse_statement (cfile, group, type, host_decl, declaration)
}
/* If we're in a subnet declaration, just do the parse. */
- if (group -> shared_network) {
- parse_subnet_declaration (cfile,
- group -> shared_network);
+ if (group->shared_network) {
+ if (token == SUBNET) {
+ parse_subnet_declaration(cfile,
+ group->shared_network);
+ } else {
+ parse_subnet6_declaration(cfile,
+ group->shared_network);
+ }
break;
}
@@ -439,26 +449,40 @@ int parse_statement (cfile, group, type, host_decl, declaration)
shared_network_reference (&share -> group -> shared_network,
share, MDL);
- parse_subnet_declaration (cfile, share);
+ 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);
+ if (share->subnets) {
+ interface_reference(&share->interface,
+ share->subnets->interface,
+ MDL);
/* Make the shared network name from network number. */
- n = piaddrmask (share -> subnets -> net,
- share -> subnets -> netmask, MDL);
- share -> name = n;
+ 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);
+ share->group->authoritative =
+ share->subnets->group->authoritative;
+ enter_shared_network(share);
}
- shared_network_dereference (&share, MDL);
+ shared_network_dereference(&share, MDL);
return 1;
case VENDOR_CLASS:
@@ -525,23 +549,24 @@ int parse_statement (cfile, group, type, host_decl, declaration)
break;
case FIXED_ADDR:
- next_token (&val, (unsigned *)0, cfile);
- cache = (struct option_cache *)0;
- if (parse_fixed_addr_param (&cache, cfile)) {
+ case FIXED_ADDR6:
+ next_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%s",
- " declaration per host.");
+ 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;
+ host_decl->fixed_addr = cache;
}
} else {
- parse_warn (cfile,
- "fixed-address parameter not %s",
- "allowed here.");
- option_cache_dereference (&cache, MDL);
+ parse_warn(cfile,
+ "fixed-address parameter not "
+ "allowed here.");
+ option_cache_dereference(&cache, MDL);
}
}
break;
@@ -571,6 +596,17 @@ int parse_statement (cfile, group, type, host_decl, declaration)
(struct lease **)0);
return declaration;
+ case RANGE6:
+ next_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);
+ return declaration;
+
case TOKEN_NOT:
token = next_token (&val, (unsigned *)0, cfile);
token = next_token (&val, (unsigned *)0, cfile);
@@ -678,6 +714,10 @@ int parse_statement (cfile, group, type, host_decl, declaration)
skip_to_semi (cfile);
#endif
break;
+
+ case SERVER_DUID:
+ parse_server_duid_conf(cfile);
+ break;
default:
et = (struct executable_statement *)0;
@@ -1685,6 +1725,10 @@ void parse_host_declaration (cfile, group)
int dynamicp = 0;
int deleted = 0;
isc_result_t status;
+ int known;
+ struct option *option;
+ struct expression *expr;
+ char *tmp_format;
name = parse_host_name (cfile);
if (!name) {
@@ -1817,6 +1861,78 @@ void parse_host_declaration (cfile, group)
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;
+ }
+ next_token(&val, NULL, cfile);
+ token = next_token(&val, NULL, cfile);
+ if (token != OPTION) {
+ parse_warn(cfile,
+ "host-identifer must be an option");
+ 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;
+ }
+
+ /* XXX: we're always using "lookups" */
+ expr = NULL;
+ tmp_format = option->format;
+ /*
+ * XXX: using parse_option_token() is not ideal here,
+ * as it does not handle things like arrays and
+ * such.
+ */
+ if (!parse_option_token(&expr, cfile, (const char **)
+ &tmp_format, NULL, 1, 1)) {
+ skip_to_rbrace(cfile, 1);
+ option_dereference(&option, MDL);
+ break;
+ }
+
+ /* I think this is guaranteed, but a check
+ won't hurt. -Shane */
+ if (expr->op != expr_const_data) {
+ parse_warn(cfile,
+ "options for host-identifier "
+ "must have a constant value");
+ skip_to_rbrace(cfile, 1);
+ expression_dereference(&expr, MDL);
+ 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);
@@ -2321,6 +2437,76 @@ void parse_shared_net_declaration (cfile, group)
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) {
+ token = next_token(&val, NULL, cfile);
+ break;
+ } else if (token == END_OF_FILE) {
+ token = next_token(&val, NULL, cfile);
+ parse_warn (cfile, "unexpected end of file");
+ break;
+ } else if (token == INTERFACE) {
+ token = next_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 */
@@ -2334,8 +2520,6 @@ void parse_subnet_declaration (cfile, share)
struct iaddr iaddr;
unsigned char addr [4];
unsigned len = sizeof addr;
- int declaration = 0;
- struct interface_info *ip;
isc_result_t status;
subnet = (struct subnet *)0;
@@ -2387,65 +2571,91 @@ void parse_subnet_declaration (cfile, share)
return;
}
- enter_subnet (subnet);
+ common_subnet_parsing(cfile, share, subnet);
+}
- if (!parse_lbrace (cfile)) {
- subnet_dereference (&subnet, MDL);
+/* subnet6-declaration :==
+ net / bits RBRACE parameters declarations LBRACE */
+
+void
+parse_subnet6_declaration(struct parse *cfile, struct shared_network *share) {
+ 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;
+ struct ipv6_pool *pool;
+
+ 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 (!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;
}
- do {
- token = peek_token (&val, (unsigned *)0, cfile);
- if (token == RBRACE) {
- token = next_token (&val, (unsigned *)0, cfile);
- break;
- } else if (token == END_OF_FILE) {
- token = next_token (&val, (unsigned *)0, cfile);
- parse_warn (cfile, "unexpected end of file");
- break;
- } else if (token == INTERFACE) {
- token = next_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, subnet -> group,
- SUBNET_DECL,
- (struct host_decl *)0,
- declaration);
- } while (1);
+ token = next_token(&val, NULL, cfile);
+ if (token != SLASH) {
+ parse_warn(cfile, "Expecting a '/'.");
+ skip_to_semi(cfile);
+ return;
+ }
- /* Add the subnet to the list of subnets in this shared net. */
- if (!share -> subnets)
- subnet_reference (&share -> subnets, subnet, MDL);
- else {
- u = (struct subnet *)0;
- 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;
- }
- u = t;
- }
- subnet_reference (&t -> next_sibling, subnet, MDL);
+ 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;
+ }
+
+ /*
+ * 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;
}
- subnet_dereference (&subnet, MDL);
}
/* group-declaration :== RBRACE parameters declarations LBRACE */
@@ -2543,47 +2753,59 @@ void parse_group_declaration (cfile, group)
ip-addrs-or-hostnames :== ip-addr-or-hostname
| ip-addrs-or-hostnames ip-addr-or-hostname */
-int parse_fixed_addr_param (oc, cfile)
- struct option_cache **oc;
- struct parse *cfile;
-{
+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 = (struct expression *)0;
+ struct expression *expr = NULL;
struct expression *tmp, *new;
int status;
do {
- tmp = (struct expression *)0;
- if (parse_ip_addr_or_hostname (&tmp, cfile, 1)) {
- if (expr) {
- new = (struct expression *)0;
- status = make_concat (&new, expr, tmp);
- expression_dereference (&expr, MDL);
- expression_dereference (&tmp, MDL);
- if (!status)
+ 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
+ } else {
expr = tmp;
+ }
} else {
- if (expr)
+ if (expr != NULL) {
expression_dereference (&expr, MDL);
+ }
return 0;
}
- token = peek_token (&val, (unsigned *)0, cfile);
- if (token == COMMA)
- token = next_token (&val, (unsigned *)0, cfile);
+ token = peek_token(&val, NULL, cfile);
+ if (token == COMMA) {
+ token = next_token(&val, NULL, cfile);
+ }
} while (token == COMMA);
- if (!parse_semi (cfile)) {
- if (expr)
+ if (!parse_semi(cfile)) {
+ if (expr) {
expression_dereference (&expr, MDL);
+ }
return 0;
}
- status = option_cache (oc, (struct data_string *)0, expr,
- (struct option *)0, MDL);
- expression_dereference (&expr, MDL);
+
+ status = option_cache(oc, NULL, expr, NULL, MDL);
+ expression_dereference(&expr, MDL);
return status;
}
@@ -3353,6 +3575,160 @@ void parse_address_range (cfile, group, type, inpool, lpchain)
pool_dereference (&pool, MDL);
}
+static void
+add_ipv6_pool_to_shared_network(struct shared_network *share,
+ struct iaddr *lo_addr,
+ int bits) {
+ 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, &tmp_in6_addr,
+ bits, 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 our pool to our shared_network.
+ */
+ pool->shared_network = NULL;
+ shared_network_reference(&pool->shared_network, share, MDL);
+
+ /*
+ * Increase our array size for ipv6_pools in the shared_network.
+ */
+ if (share->ipv6_pools == NULL) {
+ num_pools = 0;
+ } else {
+ num_pools = 0;
+ while (share->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, share->ipv6_pools,
+ sizeof(struct ipv6_pool *) * num_pools);
+ }
+ if (share->ipv6_pools != NULL) {
+ dfree(share->ipv6_pools, MDL);
+ }
+ share->ipv6_pools = tmp;
+
+ /*
+ * Record this pool in our array of pools for this shared network.
+ */
+ ipv6_pool_reference(&share->ipv6_pools[num_pools], pool, MDL);
+ share->ipv6_pools[num_pools+1] = NULL;
+}
+
+/* address-range6-declaration :== ip-address6 ip-address6 SEMI
+ | ip-address6 SLASH number SEMI */
+
+void
+parse_address_range6(struct parse *cfile, struct group *group) {
+ struct iaddr lo, hi;
+ int bits;
+ enum dhcp_token token;
+ const char *val;
+ struct shared_network *share;
+ struct iaddrcidrnetlist *nets;
+ struct iaddrcidrnetlist *p;
+
+ /*
+ * We'll use the shared_network from our group.
+ */
+ share = group->shared_network;
+ if (share == NULL) {
+ share = group->subnet->shared_network;
+ }
+
+ /*
+ * Read starting address.
+ */
+ if (!parse_ip6_addr(cfile, &lo)) {
+ return;
+ }
+
+ /*
+ * See if we we're using range or CIDR notation.
+ */
+ token = peek_token(&val, NULL, cfile);
+ if (token == SLASH) {
+ /*
+ * '/' means CIDR notation, so read the bits we want.
+ */
+ next_token(NULL, NULL, cfile);
+ token = next_token(&val, NULL, cfile);
+ if (token != NUMBER) {
+ parse_warn(cfile, "expecting number");
+ skip_to_semi(cfile);
+ return;
+ }
+ bits = atoi(val);
+ if ((bits < 0) || (bits > 128)) {
+ parse_warn(cfile, "networks have 0 to 128 bits");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ add_ipv6_pool_to_shared_network(share, &lo, bits);
+
+ } else {
+ /*
+ * No '/', so we are looking for the end address of
+ * the IPv6 pool.
+ */
+ if (!parse_ip6_addr(cfile, &hi)) {
+ 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");
+ }
+
+ for (p=nets; p != NULL; p=p->next) {
+ add_ipv6_pool_to_shared_network(share,
+ &p->cidrnet.lo_addr,
+ p->cidrnet.bits);
+ }
+
+ free_iaddrcidrnetlist(&nets);
+
+ }
+
+ token = next_token(NULL, NULL, cfile);
+ if (token != SEMI) {
+ parse_warn(cfile, "semicolon expected.");
+ skip_to_semi(cfile);
+ return;
+ }
+}
+
/* allow-deny-keyword :== BOOTP
| BOOTING
| DYNAMIC_BOOTP
@@ -3424,3 +3800,469 @@ int parse_allow_deny (oc, cfile, flag)
return status;
}
+void
+parse_ia_na_declaration(struct parse *cfile) {
+ enum dhcp_token token;
+ struct ia_na *ia_na;
+ const char *val;
+ int len;
+ u_int32_t iaid;
+ struct iaddr iaddr;
+ binding_state_t state;
+ TIME end_time;
+ struct iaaddr *iaaddr;
+ struct ipv6_pool *pool;
+ char addr_buf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
+ struct data_string uid;
+
+ token = next_token(&val, &len, cfile);
+ if (token != STRING) {
+ parse_warn(cfile, "corrupt lease file; "
+ "expecting an iaid+ia_na string");
+ skip_to_semi(cfile);
+ return;
+ }
+ if (len < 5) {
+ parse_warn(cfile, "corrupt lease file; "
+ "iaid+ia_na string too short");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ memcpy(&iaid, val, 4);
+ ia_na = NULL;
+ if (ia_na_allocate(&ia_na, iaid, val+4, len-4, MDL) != ISC_R_SUCCESS) {
+ log_fatal("Out of memory.");
+ }
+
+ 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 != 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;
+ end_time = -1;
+ for (;;) {
+ token = next_token(&val, NULL, cfile);
+ if (token == RBRACE) break;
+
+ if (token == 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.");
+ }
+
+ } else if (token == ENDS) {
+ end_time = parse_date(cfile);
+ } else {
+ parse_warn(cfile, "corrupt lease file; "
+ "expecting binding or ends, "
+ "got '%s'", val);
+ skip_to_semi(cfile);
+ return;
+ }
+ }
+
+ 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 (iaaddr_allocate(&iaaddr, MDL) != ISC_R_SUCCESS) {
+ log_fatal("Out of memory.");
+ }
+ memcpy(&iaaddr->addr, iaddr.iabuf, sizeof(iaaddr->addr));
+ iaaddr->state = state;
+ iaaddr->valid_lifetime_end_time = end_time;
+
+ /* add to our various structures */
+ ia_na_add_iaaddr(ia_na, iaaddr, MDL);
+ pool = NULL;
+ if (find_ipv6_pool(&pool, &iaaddr->addr) != ISC_R_SUCCESS) {
+ inet_ntop(AF_INET6, &iaaddr->addr,
+ addr_buf, sizeof(addr_buf));
+ parse_warn(cfile, "no pool found for address %s",
+ addr_buf);
+ return;
+ }
+ add_lease6(pool, iaaddr, end_time);
+ switch (state) {
+ case FTS_ABANDONED:
+ decline_lease6(pool, iaaddr);
+ break;
+ case FTS_EXPIRED:
+ decline_lease6(pool, iaaddr);
+ iaaddr->state = FTS_EXPIRED;
+ break;
+ case FTS_RELEASED:
+ decline_lease6(pool, iaaddr);
+ iaaddr->state = FTS_RELEASED;
+ break;
+ }
+ ipv6_pool_dereference(&pool, MDL);
+ iaaddr_dereference(&iaaddr, MDL);
+ }
+
+ ia_na_hash_add(ia_active, (char *)ia_na->iaid_duid.data,
+ ia_na->iaid_duid.len, ia_na, MDL);
+}
+
+/*
+ * 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";
+ */
+void
+parse_server_duid(struct parse *cfile) {
+ enum dhcp_token token;
+ const char *val;
+ int len;
+ struct data_string duid;
+
+ token = next_token(&val, &len, cfile);
+ if (token != STRING) {
+ parse_warn(cfile, "corrupt lease file; expecting a DUID");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ memset(&duid, 0, sizeof(duid));
+ duid.len = len;
+ if (!buffer_allocate(&duid.buffer, duid.len, MDL)) {
+ log_fatal("Out of memory storing DUID");
+ }
+ duid.data = (char *)duid.buffer->data;
+ memcpy(duid.buffer->data, val, len);
+
+ set_server_duid(&duid);
+
+ data_string_forget(&duid, MDL);
+
+ token = next_token(&val, &len, cfile);
+ if (token != SEMI) {
+ parse_warn(cfile, "corrupt lease file; expecting a semicolon");
+ skip_to_semi(cfile);
+ return;
+ }
+}
+
+/*
+ * 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-identifer-1234";
+ */
+void
+parse_server_duid_conf(struct parse *cfile) {
+ enum dhcp_token token;
+ const char *val;
+ 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.
+ */
+ token = next_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 = (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 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 = (char *)duid.buffer->data;
+ putUShort(duid.buffer->data, DUID_LL);
+ putULong(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 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 = (char *)duid.buffer->data;
+ putUShort(duid.buffer->data, DUID_LLT);
+ putULong(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 = (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);
+ }
+}
diff --git a/server/db.c b/server/db.c
index 2ea9c866..b2c70364 100644
--- a/server/db.c
+++ b/server/db.c
@@ -34,7 +34,7 @@
#ifndef lint
static char copyright[] =
-"$Id: db.c,v 1.75 2006/10/27 22:54:12 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
+"$Id: db.c,v 1.76 2007/05/08 23:05:21 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
@@ -475,6 +475,137 @@ int write_group (group)
return !errors;
}
+/*
+ * Write an IA_NA and the options it has.
+ */
+int
+write_ia_na(const struct ia_na *ia_na) {
+ struct iaaddr *iaaddr;
+ 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;
+
+ /*
+ * 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()) {
+ return 0;
+ }
+ }
+
+ if (counting) {
+ ++count;
+ }
+
+
+ s = quotify_buf(ia_na->iaid_duid.data, ia_na->iaid_duid.len, MDL);
+ if (s == NULL) {
+ goto error_exit;
+ }
+ fprintf_ret = fprintf(db_file, "ia-na \"%s\" {\n", s);
+ dfree(s, MDL);
+ if (fprintf_ret < 0) {
+ goto error_exit;
+ }
+ for (i=0; i<ia_na->num_iaaddr; i++) {
+ iaaddr = ia_na->iaaddr[i];
+
+ inet_ntop(AF_INET6, &iaaddr->addr, addr_buf, sizeof(addr_buf));
+ if (fprintf(db_file, " iaaddr %s {\n", addr_buf) < 0) {
+ goto error_exit;
+ }
+ if ((iaaddr->state <= 0) || (iaaddr->state > FTS_LAST)) {
+ log_fatal("Unknown ia_na state %d at %s:%d",
+ iaaddr->state, MDL);
+ }
+ binding_state = binding_state_names[iaaddr->state-1];
+ if (fprintf(db_file, " binding state %s;\n",
+ binding_state) < 0) {
+ goto error_exit;
+ }
+ tval = print_time(iaaddr->valid_lifetime_end_time);
+ if (tval == NULL) {
+ goto error_exit;
+ }
+ if (fprintf(db_file, " ends %s", tval) < 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_na: unable to write ia-na");
+ lease_file_is_corrupt = 1;
+ return 0;
+}
+
+/*
+ * 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()) {
+ 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 = quotify_buf(server_duid.data, server_duid.len, 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;
+}
+
#if defined (FAILOVER_PROTOCOL)
int write_failover_state (dhcp_failover_state_t *state)
{
diff --git a/server/ddns.c b/server/ddns.c
index 3e833d63..300f5431 100644
--- a/server/ddns.c
+++ b/server/ddns.c
@@ -34,7 +34,7 @@
#ifndef lint
static char copyright[] =
-"$Id: ddns.c,v 1.26 2007/04/27 22:48:00 each Exp $ Copyright (c) 2004-2007 Internet Systems Consortium. All rights reserved.\n";
+"$Id: ddns.c,v 1.27 2007/05/08 23:05:21 dhankins Exp $ Copyright (c) 2004-2005 Internet Systems Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
@@ -213,9 +213,13 @@ static isc_result_t ddns_remove_ptr (struct data_string *ddns_rev_name)
}
-int ddns_updates (struct packet *packet,
- struct lease *lease, struct lease *old,
- struct lease_state *state)
+/* 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 iaaddr *lease6, struct iaaddr *old6,
+ struct option_state *options)
{
unsigned long ddns_ttl = DEFAULT_DDNS_TTL;
struct data_string ddns_hostname;
@@ -224,6 +228,8 @@ int ddns_updates (struct packet *packet,
struct data_string ddns_fwd_name;
struct data_string ddns_rev_name;
struct data_string ddns_dhcid;
+ struct binding_scope **scope;
+ struct iaddr addr;
unsigned len;
struct data_string d1;
struct option_cache *oc;
@@ -234,13 +240,21 @@ int ddns_updates (struct packet *packet,
int server_updates_ptr = 1;
struct buffer *bp = (struct buffer *)0;
int ignorep = 0, client_ignorep = 0;
+ int rev_name_len;
+ int i;
if (ddns_update_style != 2)
return 0;
- /* Can only cope with IPv4 addrs at the moment. */
- if (lease -> ip_addr . len != 4)
- return 0;
+ if (lease != NULL) {
+ scope = &(lease->scope);
+ addr = lease->ip_addr;
+ } else if (lease6 != NULL) {
+ scope = &(lease6->scope);
+ memcpy(addr.iabuf, lease6->addr.s6_addr, 16);
+ addr.len = 16;
+ } else
+ log_fatal("Impossible condition at %s:%d.", MDL);
memset(&d1, 0, sizeof(d1));
memset (&ddns_hostname, 0, sizeof (ddns_hostname));
@@ -252,21 +266,19 @@ int ddns_updates (struct packet *packet,
/* 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, state->options,
+ if (!(oc = lookup_option(&server_universe, options,
SV_CLIENT_UPDATES)) ||
evaluate_boolean_option_cache(&client_ignorep, packet, lease, NULL,
- packet->options, state->options,
- &lease->scope, oc, MDL)) {
+ 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,
- (struct client_state *)0,
- packet -> options,
- state -> options,
- &lease -> scope, oc, MDL))
+ 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. */
@@ -275,11 +287,9 @@ int ddns_updates (struct packet *packet,
goto noclient;
if (!(oc = lookup_option (&fqdn_universe, packet -> options,
FQDN_FQDN)) ||
- !evaluate_option_cache (&ddns_fwd_name, packet, lease,
- (struct client_state *)0,
- packet -> options,
- state -> options,
- &lease -> scope, oc, MDL))
+ !evaluate_option_cache(&ddns_fwd_name, packet, lease,
+ NULL, packet->options,
+ options, scope, oc, MDL))
goto noclient;
server_updates_a = 0;
goto client_updates;
@@ -288,52 +298,43 @@ int ddns_updates (struct packet *packet,
/* 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, state -> options,
+ if ((oc = lookup_option (&server_universe, options,
SV_DO_FORWARD_UPDATES)) &&
- !evaluate_boolean_option_cache (&ignorep, packet, lease,
- (struct client_state *)0,
- packet -> options,
- state -> options,
- &lease -> scope, oc, MDL)) {
+ !evaluate_boolean_option_cache(&ignorep, packet, lease,
+ NULL, packet->options,
+ options, scope, oc, MDL)) {
return 0;
}
/* 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. */
- if (lease -> flags & STATIC_LEASE) {
- if (!(oc = lookup_option (&server_universe, state -> options,
- SV_UPDATE_STATIC_LEASES)) ||
- !evaluate_boolean_option_cache (&ignorep, packet, lease,
- (struct client_state *)0,
- packet -> options,
- state -> options,
- &lease -> scope, oc, MDL))
+ /* 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))
return 0;
}
/*
* Compute the name for the A record.
*/
- oc = lookup_option (&server_universe, state -> options,
- SV_DDNS_HOST_NAME);
+ oc = lookup_option(&server_universe, options, SV_DDNS_HOST_NAME);
if (oc)
- s1 = evaluate_option_cache (&ddns_hostname, packet, lease,
- (struct client_state *)0,
- packet -> options,
- state -> options,
- &lease -> scope, oc, MDL);
+ s1 = evaluate_option_cache(&ddns_hostname, packet, lease,
+ NULL, packet->options,
+ options, scope, oc, MDL);
else
s1 = 0;
- oc = lookup_option (&server_universe, state -> options,
- SV_DDNS_DOMAIN_NAME);
+ oc = lookup_option(&server_universe, options, SV_DDNS_DOMAIN_NAME);
if (oc)
- s2 = evaluate_option_cache (&ddns_domainname, packet, lease,
- (struct client_state *)0,
- packet -> options,
- state -> options,
- &lease -> scope, oc, MDL);
+ s2 = evaluate_option_cache(&ddns_domainname, packet, lease,
+ NULL, packet->options,
+ options, scope, oc, MDL);
else
s2 = 0;
@@ -360,37 +361,34 @@ int ddns_updates (struct packet *packet,
client_updates:
/* See if there's a name already stored on the lease. */
- if (find_bound_string (&old_ddns_fwd_name,
- lease -> scope, "ddns-fwd-name")) {
+ 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, try to delete
the old A record. */
- if (!ddns_removals (lease))
+ if (!ddns_removals(lease, lease6))
goto out;
/* If the delete succeeded, go install the new
record. */
goto in;
}
- /* See if there's a DHCID on the lease. */
- if (!find_bound_string (&ddns_dhcid,
- lease -> scope, "ddns-txt")) {
+ /* See if there's a DHCID on the lease, and if not
+ * then potentially look for 'on events' for ad-hoc ddns.
+ */
+ if (!find_bound_string(&ddns_dhcid, *scope, "ddns-txt") &&
+ (old != NULL)) {
/* If there's no DHCID, the update was probably
done with the old-style ad-hoc DDNS updates.
So if the expiry and release events look like
they're the same, run them. This should delete
the old DDNS data. */
if (old -> on_expiry == old -> on_release) {
- execute_statements ((struct binding_value **)0,
- (struct packet *)0, lease,
- (struct client_state *)0,
- (struct option_state *)0,
- (struct option_state *)0,
- &lease -> scope,
- old -> on_expiry);
+ execute_statements(NULL, NULL, lease, NULL,
+ NULL, NULL, scope,
+ old->on_expiry);
if (old -> on_expiry)
executable_statement_dereference
(&old -> on_expiry, MDL);
@@ -406,13 +404,11 @@ int ddns_updates (struct packet *packet,
/* 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, state -> options,
- SV_UPDATE_OPTIMIZATION)) ||
- evaluate_boolean_option_cache (&ignorep, packet, lease,
- (struct client_state *)0,
- packet -> options,
- state -> options,
- &lease -> scope, oc, MDL)) {
+ 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;
}
@@ -420,22 +416,18 @@ int ddns_updates (struct packet *packet,
* 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, lease->scope,
+ } 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,
- state -> options,
- SV_UPDATE_OPTIMIZATION)) ||
- evaluate_boolean_option_cache (&ignorep, packet, lease,
- (struct client_state *)0,
- packet -> options,
- state -> options,
- &lease -> scope, oc,
- MDL))) {
+ (!(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;
}
}
@@ -455,13 +447,10 @@ int ddns_updates (struct packet *packet,
* Compute the RR TTL.
*/
ddns_ttl = DEFAULT_DDNS_TTL;
- if ((oc = lookup_option (&server_universe, state -> options,
- SV_DDNS_TTL))) {
- if (evaluate_option_cache (&d1, packet, lease,
- (struct client_state *)0,
- packet -> options,
- state -> options,
- &lease -> scope, oc, MDL)) {
+ 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);
@@ -470,51 +459,76 @@ int ddns_updates (struct packet *packet,
/* CC: see if we are configured NOT to do reverse ptr updates
*/
- if ((oc = lookup_option (&server_universe, state -> options,
- SV_DO_REVERSE_UPDATES)) &&
- !evaluate_boolean_option_cache (&ignorep, packet, lease,
- (struct client_state *)0,
- packet -> options,
- state -> options,
- &lease -> scope, oc, MDL)) {
+ 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)) {
server_updates_ptr = 0;
}
/*
* Compute the reverse IP name.
*/
- oc = lookup_option (&server_universe, state -> options,
- SV_DDNS_REV_DOMAIN_NAME);
+
+ /*
+ * Figure out the length of the part of the name that depends
+ * on the address.
+ */
+ if (addr.len == 4) {
+ char buf[1];
+ /* XXX: WOW this is gross. */
+ rev_name_len = snprintf(buf, sizeof(buf), "%u.%u.%u.%u.",
+ addr.iabuf[3] & 0xff,
+ addr.iabuf[2] & 0xff,
+ addr.iabuf[1] & 0xff,
+ addr.iabuf[0] & 0xff);
+ } else if (addr.len == 16) {
+ /*
+ * IPv6 reverse names are always the same length, with
+ * 32 hex characters separated by dots.
+ */
+ rev_name_len = 64;
+ } else
+ log_fatal("invalid address length %d", addr.len);
+
+ oc = lookup_option(&server_universe, options, SV_DDNS_REV_DOMAIN_NAME);
if (oc)
- s1 = evaluate_option_cache (&d1, packet, lease,
- (struct client_state *)0,
- packet -> options,
- state -> options,
- &lease -> scope, oc, MDL);
+ s1 = evaluate_option_cache(&d1, packet, lease, NULL,
+ packet->options, options,
+ scope, oc, MDL);
else
s1 = 0;
- if (s1 && (d1.len > 238)) {
+ if (s1 && ((d1.len + rev_name_len) > 255)) {
log_error ("ddns_update: Calculated rev domain name too long.");
s1 = 0;
data_string_forget (&d1, MDL);
}
if (oc && s1) {
- /* Buffer length:
- XXX.XXX.XXX.XXX.<ddns-rev-domain-name>\0 */
buffer_allocate (&ddns_rev_name.buffer,
- d1.len + 17, MDL);
+ d1.len + rev_name_len + 1, MDL);
if (ddns_rev_name.buffer) {
- ddns_rev_name.data = ddns_rev_name.buffer -> data;
-
- /* %Audit% Cannot exceed 17 bytes. %2004.06.17,Safe% */
- sprintf ((char *)ddns_rev_name.buffer -> data,
- "%u.%u.%u.%u.",
- lease -> ip_addr . iabuf[3] & 0xff,
- lease -> ip_addr . iabuf[2] & 0xff,
- lease -> ip_addr . iabuf[1] & 0xff,
- lease -> ip_addr . iabuf[0] & 0xff);
+ ddns_rev_name.data = ddns_rev_name.buffer->data;
+
+ if (addr.len == 4) {
+ sprintf((char *)ddns_rev_name.buffer->data,
+ "%u.%u.%u.%u.",
+ addr.iabuf[3] & 0xff,
+ addr.iabuf[2] & 0xff,
+ addr.iabuf[1] & 0xff,
+ addr.iabuf[0] & 0xff);
+ } else if (addr.len == 16) {
+ char *p = (char *)&ddns_rev_name.buffer->data;
+ unsigned char *a = addr.iabuf + 15;
+ for (i=0; i<16; i++) {
+ sprintf(p, "%x.%x.",
+ (*a & 0xF), ((*a >> 4) & 0xF));
+ p += 4;
+ a -= 1;
+ }
+ }
ddns_rev_name.len =
strlen ((const char *)ddns_rev_name.data);
@@ -522,7 +536,7 @@ int ddns_updates (struct packet *packet,
ddns_rev_name.buffer -> data [ddns_rev_name.len] ='\0';
ddns_rev_name.terminated = 1;
}
-
+
data_string_forget (&d1, MDL);
}
@@ -530,14 +544,23 @@ int ddns_updates (struct packet *packet,
* If we are updating the A record, compute the DHCID value.
*/
if (server_updates_a) {
- if (lease -> uid && lease -> uid_len)
+ memset (&ddns_dhcid, 0, sizeof ddns_dhcid);
+ if (lease6 != NULL)
+ result = get_dhcid(&ddns_dhcid, 2,
+ lease6->ia_na->iaid_duid.data,
+ lease6->ia_na->iaid_duid.len);
+ else if ((lease != NULL) && (lease->uid != NULL) &&
+ (lease->uid_len != 0))
result = get_dhcid (&ddns_dhcid,
DHO_DHCP_CLIENT_IDENTIFIER,
lease -> uid, lease -> uid_len);
- else
+ else if (lease != NULL)
result = get_dhcid (&ddns_dhcid, 0,
lease -> hardware_addr.hbuf,
lease -> hardware_addr.hlen);
+ else
+ log_fatal("Impossible condition at %s:%d.", MDL);
+
if (!result)
goto badfqdn;
}
@@ -558,19 +581,18 @@ int ddns_updates (struct packet *packet,
if (ddns_fwd_name.len && ddns_dhcid.len) {
unsigned conflict;
- oc = lookup_option(&server_universe, state->options,
+ oc = lookup_option(&server_universe, options,
SV_DDNS_CONFLICT_DETECT);
if (!oc ||
evaluate_boolean_option_cache(&ignorep, packet, lease,
NULL, packet->options,
- state->options,
- &lease->scope, oc, MDL))
+ options, scope, oc, MDL))
conflict = 1;
else
conflict = 0;
- rcode1 = ddns_update_a (&ddns_fwd_name, lease -> ip_addr,
- &ddns_dhcid, ddns_ttl, 0, conflict);
+ rcode1 = ddns_update_fwd(&ddns_fwd_name, addr, &ddns_dhcid,
+ ddns_ttl, 0, conflict);
}
if (rcode1 == ISC_R_SUCCESS && server_updates_ptr) {
@@ -582,18 +604,15 @@ int ddns_updates (struct packet *packet,
if (rcode1 == ISC_R_SUCCESS &&
(server_updates_a || rcode2 == ISC_R_SUCCESS)) {
- bind_ds_value (&lease -> scope,
- (server_updates_a
- ? "ddns-fwd-name" : "ddns-client-fqdn"),
- &ddns_fwd_name);
+ bind_ds_value(scope, server_updates_a ? "ddns-fwd-name"
+ : "ddns-client-fqdn",
+ &ddns_fwd_name);
if (server_updates_a)
- bind_ds_value (&lease -> scope, "ddns-txt",
- &ddns_dhcid);
+ bind_ds_value(scope, "ddns-txt", &ddns_dhcid);
}
if (rcode2 == ISC_R_SUCCESS && server_updates_ptr) {
- bind_ds_value (&lease -> scope, "ddns-rev-name",
- &ddns_rev_name);
+ bind_ds_value(scope, "ddns-rev-name", &ddns_rev_name);
}
noerror:
@@ -603,13 +622,10 @@ int ddns_updates (struct packet *packet,
* (WinXP clients allegedly misbehave if the option is present,
* refusing to handle PTR updates themselves).
*/
- if ((oc = lookup_option (&server_universe, state->options,
- SV_FQDN_REPLY)) &&
- !evaluate_boolean_option_cache (&ignorep, packet, lease,
- (struct client_state *)0,
- packet->options,
- state->options,
- &lease->scope, oc, MDL)) {
+ 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
@@ -624,26 +640,26 @@ int ddns_updates (struct packet *packet,
(oc = lookup_option(&fqdn_universe, packet->options,
FQDN_SERVER_UPDATE)) &&
!evaluate_boolean_option_cache(&ignorep, packet, lease, NULL,
- packet->options, state->options,
- &lease->scope, oc, MDL)) {
+ 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, state->options,
- &global_scope, oc, MDL)) {
+ 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, state->options,
+ 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, state->options,
+ if (!save_option_buffer(&fqdn_universe, options,
bp, &bp->data[1], 1,
FQDN_NO_CLIENT_UPDATE, 0))
goto badfqdn;
@@ -651,30 +667,30 @@ int ddns_updates (struct packet *packet,
/* 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,
- state->options,
- &lease->scope, oc,
- MDL))
+ 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, state->options,
+ 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, state->options,
+ 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, state->options,
+ if (!save_option_buffer(&fqdn_universe, options,
bp, &bp->data[4], 1,
FQDN_RCODE2, 0))
goto badfqdn;
@@ -688,7 +704,7 @@ int ddns_updates (struct packet *packet,
* transmitted if the client elects that encoding.
*/
memcpy(&bp->data[5], d1.data, d1.len);
- if (!save_option_buffer(&fqdn_universe, state->options,
+ if (!save_option_buffer(&fqdn_universe, options,
bp, &bp->data[5], 1,
FQDN_FQDN, 0))
goto badfqdn;
@@ -705,43 +721,42 @@ int ddns_updates (struct packet *packet,
FQDN_ENCODED)) &&
buffer_allocate(&bp, ddns_fwd_name.len + 5, MDL)) {
bp -> data [0] = server_updates_a;
- if (!save_option_buffer (&fqdn_universe, state -> options,
- bp, &bp -> data [0], 1,
- FQDN_SERVER_UPDATE, 0))
+ 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, state -> options,
- bp, &bp -> data [1], 1,
+ 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,
- state->options,
- &lease->scope, oc, MDL))
+ options, scope, oc, MDL))
bp -> data [2] = 1;
else
bp -> data [2] = 0;
- if (!save_option_buffer (&fqdn_universe, state -> options,
- bp, &bp -> data [2], 1,
- FQDN_ENCODED, 0))
+ if (!save_option_buffer(&fqdn_universe, options,
+ bp, &bp->data [2], 1,
+ FQDN_ENCODED, 0))
goto badfqdn;
bp -> data [3] = isc_rcode_to_ns (rcode1);
- if (!save_option_buffer (&fqdn_universe, state -> options,
- bp, &bp -> data [3], 1,
- FQDN_RCODE1, 0))
+ if (!save_option_buffer(&fqdn_universe, options,
+ bp, &bp->data [3], 1,
+ FQDN_RCODE1, 0))
goto badfqdn;
bp -> data [4] = isc_rcode_to_ns (rcode2);
- if (!save_option_buffer (&fqdn_universe, state -> options,
- bp, &bp -> data [4], 1,
- FQDN_RCODE2, 0))
+ 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, state -> options,
- bp, &bp -> data [5],
+ if (!save_option_buffer(&fqdn_universe, options,
+ bp, &bp->data [5],
ddns_fwd_name.len,
FQDN_FQDN, 0))
goto badfqdn;
@@ -766,18 +781,32 @@ int ddns_updates (struct packet *packet,
return result;
}
-int ddns_removals (struct lease *lease)
+/* Remove relevant entries from DNS. */
+int
+ddns_removals(struct lease *lease, struct iaaddr *lease6)
{
struct data_string ddns_fwd_name;
struct data_string ddns_rev_name;
struct data_string ddns_dhcid;
isc_result_t rcode;
struct binding *binding;
+ struct binding_scope **scope;
+ struct iaddr addr;
int result = 0;
int client_updated = 0;
+ if (lease != NULL) {
+ scope = &(lease->scope);
+ addr = lease->ip_addr;
+ } else if (lease6 != NULL) {
+ scope = &(lease6->scope);
+ memcpy(addr.iabuf, lease6->addr.s6_addr, 16);
+ addr.len = 16;
+ } else
+ return 0;
+
/* No scope implies that DDNS has not been performed for this lease. */
- if (!lease -> scope)
+ if (*scope == NULL)
return 0;
if (ddns_update_style != 2)
@@ -802,21 +831,19 @@ int ddns_removals (struct lease *lease)
/* We need the fwd name whether we are deleting both records or just
the PTR record, so if it's not there, we can't proceed. */
- if (!find_bound_string (&ddns_fwd_name,
- lease -> scope, "ddns-fwd-name")) {
+ if (!find_bound_string(&ddns_fwd_name, *scope, "ddns-fwd-name")) {
/* If there's no ddns-fwd-name, look for the client fqdn,
in case the client did the update. */
- if (!find_bound_string (&ddns_fwd_name,
- lease -> scope, "ddns-client-fqdn"))
- goto try_rev;
- client_updated = 1;
+ if (find_bound_string(&ddns_fwd_name, *scope,
+ "ddns-client-fqdn"))
+ client_updated = 1;
goto try_rev;
}
/* If the ddns-txt binding isn't there, this isn't an interim
or rfc3??? record, so we can't delete the A record using
this mechanism, but we can delete the PTR record. */
- if (!find_bound_string (&ddns_dhcid, lease -> scope, "ddns-txt")) {
+ if (!find_bound_string (&ddns_dhcid, *scope, "ddns-txt")) {
result = 1;
goto try_rev;
}
@@ -825,28 +852,26 @@ int ddns_removals (struct lease *lease)
* Perform removals.
*/
if (ddns_fwd_name.len)
- rcode = ddns_remove_a (&ddns_fwd_name,
- lease -> ip_addr, &ddns_dhcid);
+ rcode = ddns_remove_fwd(&ddns_fwd_name, addr, &ddns_dhcid);
else
rcode = ISC_R_SUCCESS;
if (rcode == ISC_R_SUCCESS) {
result = 1;
- unset (lease -> scope, "ddns-fwd-name");
- unset (lease -> scope, "ddns-txt");
+ unset(*scope, "ddns-fwd-name");
+ unset(*scope, "ddns-txt");
try_rev:
- if (find_bound_string (&ddns_rev_name,
- lease -> scope, "ddns-rev-name")) {
+ if (find_bound_string(&ddns_rev_name, *scope,
+ "ddns-rev-name")) {
if (ddns_remove_ptr(&ddns_rev_name) == NOERROR) {
- unset (lease -> scope, "ddns-rev-name");
+ unset(*scope, "ddns-rev-name");
if (client_updated)
- unset (lease -> scope,
- "ddns-client-fqdn");
+ unset(*scope, "ddns-client-fqdn");
/* XXX this is to compensate for a bug in
XXX 3.0rc8, and should be removed before
XXX 3.0pl1. */
else if (!ddns_fwd_name.len)
- unset (lease -> scope, "ddns-text");
+ unset(*scope, "ddns-text");
} else
result = 0;
}
diff --git a/server/dhcp.c b/server/dhcp.c
index bf256773..84fe33bb 100644
--- a/server/dhcp.c
+++ b/server/dhcp.c
@@ -34,7 +34,7 @@
#ifndef lint
static char copyright[] =
-"$Id: dhcp.c,v 1.216 2007/04/26 20:06:25 dhankins Exp $ Copyright (c) 2004-2007 Internet Systems Consortium. All rights reserved.\n";
+"$Id: dhcp.c,v 1.217 2007/05/08 23:05:22 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
@@ -302,7 +302,7 @@ void dhcpdiscover (packet, ms_nulltp)
/* If the lease is ours to 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
- * wether or not to offer later on.
+ * whether or not to offer later on.
*/
if (lease->binding_state == FTS_ACTIVE ||
lease_mine_to_reallocate(lease)) {
@@ -1132,7 +1132,7 @@ void dhcpinform (packet, ms_nulltp)
}
options -> site_universe = u -> index;
- options -> site_code_min = 224; /* XXX */
+ options -> site_code_min = 224; /* From RFC3942 */
data_string_forget (&d1, MDL);
} else {
options -> site_universe = dhcp_universe.index;
@@ -1357,7 +1357,8 @@ void nak_lease (packet, cip)
option_state_dereference (&options, MDL);
/* memset (&raw.ciaddr, 0, sizeof raw.ciaddr);*/
- raw.siaddr = packet -> interface -> primary_address;
+ if (packet->interface->address_count)
+ raw.siaddr = packet->interface->addresses[0];
raw.giaddr = packet -> raw -> giaddr;
memcpy (raw.chaddr, packet -> raw -> chaddr, sizeof raw.chaddr);
raw.hlen = packet -> raw -> hlen;
@@ -2120,7 +2121,7 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
}
/* Update potential expiry. Allow for the desired
- * lease time plus one half the actual (wether
+ * 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
@@ -2342,7 +2343,7 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
packet -> options,
state -> options,
&lt -> scope, oc, MDL))) {
- ddns_updates (packet, lt, lease, state);
+ ddns_updates(packet, lt, lease, NULL, NULL, state->options);
}
#endif /* NSUPDATE */
@@ -2527,9 +2528,13 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
}
} else {
/* XXXSK: should we use get_server_source_address() here? */
- state -> from.len = sizeof state -> ip -> primary_address;
- memcpy (state -> from.iabuf, &state -> ip -> primary_address,
- state -> from.len);
+ 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. */
@@ -2679,7 +2684,7 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
}
state -> options -> site_universe = u -> index;
- state -> options -> site_code_min = 224; /* XXX */
+ state -> options -> site_code_min = 224; /* From RFC3942 */
data_string_forget (&d1, MDL);
} else {
state -> options -> site_code_min = 0;
@@ -3952,18 +3957,22 @@ get_server_source_address(struct in_addr *from,
data_string_forget(&d, MDL);
}
- if (option_cache_allocate(&oc, MDL)) {
- a = &packet->interface->primary_address;
- if (make_const_data(&oc->expression,
- (char *)a, sizeof(*a),
- 0, 0, MDL)) {
- option_code_hash_lookup(&oc->option,
- dhcp_universe.code_hash,
- &option_num, 0, MDL);
- save_option(&dhcp_universe, options, oc);
+ if (packet->interface->address_count > 0) {
+ if (option_cache_allocate(&oc, MDL)) {
+ a = &packet->interface->addresses[0];
+ if (make_const_data(&oc->expression,
+ (char *)a, sizeof(*a),
+ 0, 0, MDL)) {
+ option_code_hash_lookup(&oc->option,
+ dhcp_universe.code_hash,
+ &option_num, 0, MDL);
+ save_option(&dhcp_universe, options, oc);
+ }
+ option_cache_dereference(&oc, MDL);
}
- option_cache_dereference(&oc, MDL);
+ *from = packet->interface->addresses[0];
+ } else {
+ memset(from, 0, sizeof(*from));
}
- *from = packet->interface->primary_address;
}
diff --git a/server/dhcpd.8 b/server/dhcpd.8
index 670a1a09..3df19922 100644
--- a/server/dhcpd.8
+++ b/server/dhcpd.8
@@ -28,7 +28,7 @@
.\" see ``http://www.vix.com''. To learn more about Nominum, Inc., see
.\" ``http://www.nominum.com''.
.\"
-.\" $Id: dhcpd.8,v 1.25 2007/04/19 21:35:11 dhankins Exp $
+.\" $Id: dhcpd.8,v 1.26 2007/05/08 23:05:22 dhankins Exp $
.\"
.TH dhcpd 8
.SH NAME
@@ -54,6 +54,11 @@ dhcpd - Dynamic Host Configuration Protocol Server
.B -T
]
[
+.B -4
+|
+.B -6
+]
+[
.B -cf
.I config-file
]
@@ -175,6 +180,13 @@ 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
+The server either operates as a DHCPv6 server or a DHCP server, but
+not both at the same time. To run as a DHCPv6 server, use the
+.B -6
+flag. To run as a DHCP server, use the
+.B -4
+flag. If neither is used, the default is to run as a DHCPv6 server.
+.PP
If dhcpd should listen on a port other than the standard (port 67),
the
.B -p
diff --git a/server/dhcpd.c b/server/dhcpd.c
index 6eb66cdf..6de4d9f2 100644
--- a/server/dhcpd.c
+++ b/server/dhcpd.c
@@ -34,7 +34,7 @@
#ifndef lint
static char ocopyright[] =
-"$Id: dhcpd.c,v 1.121 2006/07/17 15:21:45 dhankins Exp $ Copyright 2004-2006 Internet Systems Consortium.";
+"$Id: dhcpd.c,v 1.122 2007/05/08 23:05:22 dhankins Exp $ Copyright 2004-2006 Internet Systems Consortium.";
#endif
static char copyright[] =
@@ -193,10 +193,9 @@ static void omapi_listener_start (void *foo)
omapi_object_dereference (&listener, MDL);
}
-int main (argc, argv, envp)
- int argc;
- char **argv, **envp;
-{
+#ifndef UNIT_TEST
+int
+main(int argc, char **argv) {
int fd;
int i, status;
struct servent *ent;
@@ -222,6 +221,7 @@ int main (argc, argv, envp)
int no_dhcpd_conf = 0;
int no_dhcpd_db = 0;
int no_dhcpd_pid = 0;
+ int local_family_set = 0;
#if defined (TRACING)
char *traceinfile = (char *)0;
char *traceoutfile = (char *)0;
@@ -324,6 +324,20 @@ int main (argc, argv, envp)
} else if (!strcmp (argv [i], "-q")) {
quiet = 1;
quiet_interface_discovery = 1;
+ } 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;
} else if (!strcmp (argv [i], "--version")) {
log_info ("isc-dhcpd-%s", DHCP_VERSION);
exit (0);
@@ -402,18 +416,39 @@ int main (argc, argv, envp)
log_debug ("binding to environment-specified port %d",
ntohs (local_port));
} else {
- ent = getservbyname ("dhcp", "udp");
- if (!ent)
- local_port = htons (67);
- else
- local_port = ent -> s_port;
+ 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
}
}
- remote_port = htons (ntohs (local_port) + 1);
+ 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 (!inet_aton (server, &limited_broadcast)) {
@@ -448,6 +483,7 @@ int main (argc, argv, envp)
/* Set up various hooks. */
dhcp_interface_setup_hook = dhcpd_interface_setup_hook;
bootp_packet_handler = do_packet;
+ dhcpv6_packet_handler = do_packet6;
#if defined (NSUPDATE)
/* Set up the standard name service updater routine. */
@@ -491,6 +527,13 @@ int main (argc, argv, envp)
}
#endif
+#ifdef DHCPv6
+ /* set up DHCPv6 hash */
+ if (!ia_na_new_hash(&ia_active, DEFAULT_HASH_SIZE, MDL)) {
+ log_fatal("Out of memory creating hash for active IA.");
+ }
+#endif /* DHCPv6 */
+
/* Read the dhcpd.conf file... */
if (readconf () != ISC_R_SUCCESS)
log_fatal ("Configuration file errors encountered -- exiting");
@@ -510,7 +553,17 @@ int main (argc, argv, envp)
exit (0);
/* Discover all the network interfaces and initialize them. */
- discover_interfaces (DISCOVER_SERVER);
+ discover_interfaces(DISCOVER_SERVER);
+
+#ifdef DHCPv6
+ /*
+ * Remove addresses from our pools that we should not issue
+ * to clients.
+ */
+ mark_hosts_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
@@ -531,6 +584,21 @@ int main (argc, argv, envp)
#endif
postdb_startup ();
+ /*
+ * Set server DHCPv6 identifier.
+ * See dhcpv6.c for discussion of setting DUID.
+ */
+ if (set_server_duid_from_option() == ISC_R_SUCCESS) {
+ write_server_duid();
+ } else {
+ if (!server_duid_isset()) {
+ if (generate_new_server_duid() != ISC_R_SUCCESS) {
+ log_fatal("Unable to set server identifier.");
+ }
+ write_server_duid();
+ }
+ }
+
#ifndef DEBUG
if (daemon) {
/* First part of becoming a daemon... */
@@ -623,6 +691,7 @@ int main (argc, argv, envp)
/* Not reached */
return 0;
}
+#endif /* !UNIT_TEST */
void postconf_initialization (int quiet)
{
diff --git a/server/dhcpd.conf.5 b/server/dhcpd.conf.5
index 66a9bac6..28a06bd7 100644
--- a/server/dhcpd.conf.5
+++ b/server/dhcpd.conf.5
@@ -28,7 +28,7 @@
.\" see ``http://www.vix.com''. To learn more about Nominum, Inc., see
.\" ``http://www.nominum.com''.
.\"
-.\" $Id: dhcpd.conf.5,v 1.84 2007/04/24 20:49:51 dhankins Exp $
+.\" $Id: dhcpd.conf.5,v 1.85 2007/05/08 23:05:22 dhankins Exp $
.\"
.TH dhcpd.conf 5
.SH NAME
@@ -1474,6 +1474,27 @@ 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 identifer, specified as ip6-address/bits.
+.PP
.B The
.I range
.B statement
@@ -1492,6 +1513,25 @@ 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
+.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
+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
+.B The
.I host
.B statement
.PP
@@ -1549,14 +1589,20 @@ 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. 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.
+used to match a host declaration, or the \fIhost-identifer 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
@@ -1983,7 +2029,8 @@ statement
.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.
+time. This is used for both DHCPv4 and DHCPv6 leases (it is also known
+as the "valid lifetime" in DHCPv6).
.RE
.PP
The
@@ -2090,6 +2137,18 @@ 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 get-lease-hostnames
statement
.RS 0.25i
@@ -2131,6 +2190,24 @@ 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
+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.
+.RE
+.PP
The
.I infinite-is-reserved
statement
@@ -2392,6 +2469,24 @@ 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 the renew+rebind timers, or 3/4 the
+default lease time if none were specified.
+.RE
+.PP
+The
.I remote-port
statement
.RS 0.25i
@@ -2439,6 +2534,29 @@ to using the server-identifier statement.
.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
+The default server-duid type is LLT.
+.RE
+.PP
+The
.I server-name
statement
.RS 0.25i
diff --git a/server/dhcpv6.c b/server/dhcpv6.c
new file mode 100644
index 00000000..fca44c56
--- /dev/null
+++ b/server/dhcpv6.c
@@ -0,0 +1,3010 @@
+/*
+ * Copyright (C) 2006-2007 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.
+ */
+
+#include "dhcpd.h"
+
+/*
+ * 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)
+ */
+
+/*
+ * Prototypes local to this file.
+ */
+static void build_dhcpv6_reply(struct data_string *, struct packet *);
+
+/*
+ * 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
+
+/*
+ * 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);
+
+ 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 ISC_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 ISC_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, 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));
+ 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;
+
+ /* INSIST((duid.data != NULL) && (duid.len > 0)); */
+
+ ret_val = 0;
+ memset(server_id, 0, sizeof(*server_id));
+
+ /*
+ * Required by RFC 3315, section 15.
+ */
+ if (packet->unicast) {
+ log_debug("Discarding %s from %s; "
+ "IA_NA option present",
+ dhcpv6_type_names[packet->dhcpv6_msg_type],
+ piaddr(packet->client_addr));
+ 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",
+ dhcpv6_type_names[packet->dhcpv6_msg_type],
+ piaddr(packet->client_addr));
+ 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",
+ 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(server_id, packet, NULL, NULL,
+ packet->options, NULL,
+ &global_scope, oc, MDL)) {
+ log_error("Error processing %s from %s; "
+ "unable to evaluate Server Identifier",
+ dhcpv6_type_names[packet->dhcpv6_msg_type],
+ piaddr(packet->client_addr));
+ 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)",
+ 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));
+ 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,
+ 0
+};
+static const int required_opts_solicit[] = {
+ D6O_CLIENTID,
+ D6O_SERVERID,
+ D6O_IA_NA,
+ D6O_IA_TA,
+ D6O_RAPID_COMMIT,
+ D6O_STATUS_CODE,
+ D6O_VENDOR_OPTS,
+ D6O_RECONF_ACCEPT,
+ 0
+};
+static const int required_opts_IA_NA[] = {
+ D6O_IAADDR,
+ D6O_STATUS_CODE,
+ D6O_VENDOR_OPTS,
+ 0
+};
+static const int required_opts_STATUS_CODE[] = {
+ D6O_STATUS_CODE,
+ 0
+};
+
+/*
+ * Creates an option state and data string, based on the packet contents,
+ * and the specific option defined in the option cache.
+ */
+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) {
+
+ /*
+ * 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 < 12) {
+ 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+12,
+ enc_opt_data->len-12,
+ &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, (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;
+}
+
+/*
+ * 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;
+ struct data_string server_oro;
+ char *server_id_data;
+ int server_id_len;
+
+ reply->msg_type = DHCPV6_REPLY;
+
+ /*
+ * Use the client's transaction identifier for the 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("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);
+
+ /*
+ * 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 = (char *)server_duid.data;
+ server_id_len = server_duid.len;
+ } else {
+ server_id_data = (char *)server_id->data;
+ server_id_len = server_id->len;
+ }
+ if (!save_option_buffer(&dhcpv6_universe, *opt_state,
+ NULL, 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, "", 0, D6O_RECONF_ACCEPT, 0)) {
+ log_error("start_reply: "
+ "error saving RECONF_ACCEPT option.");
+ option_state_dereference(opt_state, MDL);
+ return 0;
+ }
+ }
+
+ /*
+ * Set the ORO for the main packet.
+ */
+ build_server_oro(&server_oro, *opt_state, MDL);
+ if (!save_option_buffer(&dhcpv6_universe, *opt_state,
+ server_oro.buffer, (char *)server_oro.data,
+ server_oro.len, D6O_ORO, 0)) {
+ log_error("start_reply: error saving server ORO.");
+ data_string_forget(&server_oro, MDL);
+ option_state_dereference(opt_state, MDL);
+ return 0;
+ }
+ data_string_forget(&server_oro, MDL);
+
+ 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 iaaddr **addr,
+ struct ipv6_pool *pool,
+ const struct data_string *requested_addr) {
+ struct in6_addr tmp_addr;
+ isc_result_t result;
+ struct iaddrmatch match;
+
+ if (requested_addr->len < sizeof(tmp_addr)) {
+ return ISC_R_INVALIDARG;
+ }
+ memcpy(&tmp_addr, requested_addr->data, sizeof(tmp_addr));
+ if (IN6_IS_ADDR_UNSPECIFIED(&tmp_addr)) {
+ return ISC_R_FAILURE;
+ }
+
+ if (!ipv6_addr_in_pool(&tmp_addr, pool)) {
+ return ISC_R_FAILURE;
+ }
+
+ if (lease6_exists(pool, &tmp_addr)) {
+ return ISC_R_ADDRINUSE;
+ }
+
+ result = iaaddr_allocate(addr, MDL);
+ if (result != ISC_R_SUCCESS) {
+ return result;
+ }
+ (*addr)->addr = tmp_addr;
+
+ result = add_lease6(pool, *addr, 0);
+ if (result != ISC_R_SUCCESS) {
+ iaaddr_dereference(addr, MDL);
+ }
+ return result;
+}
+
+/*
+ * Get an IPv6 address for the client.
+ *
+ * addr is the result (should be a pointer to NULL on entry)
+ * packet is the information about the packet from the client
+ * requested_iaaddr is a hint from the client
+ * client_id is the DUID for the client
+ */
+static isc_result_t
+pick_v6_address(struct iaaddr **addr,
+ struct ipv6_pool **pool,
+ struct packet *packet,
+ const struct data_string *requested_iaaddr,
+ const struct data_string *client_id) {
+ const struct packet *chk_packet;
+ const struct in6_addr *link_addr;
+ const struct in6_addr *first_link_addr;
+ struct iaddr tmp_addr;
+ struct subnet *subnet;
+ struct shared_network *shared_network;
+ struct ipv6_pool *p;
+ int i;
+ int start_pool;
+ int attempts;
+
+ /*
+ * First, find the link address where the packet from the client
+ * first appeared.
+ */
+ 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;
+ }
+ chk_packet = chk_packet->dhcpv6_container_packet;
+ }
+
+ /*
+ * If there is a 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;
+ }
+ shared_network = NULL;
+ shared_network_reference(&shared_network,
+ 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 {
+ shared_network = NULL;
+ shared_network_reference(&shared_network,
+ packet->interface->shared_network,
+ MDL);
+ }
+
+ /*
+ * No pools, we're done.
+ */
+ if (shared_network->ipv6_pools == NULL) {
+ shared_network_dereference(&shared_network, MDL);
+ return ISC_R_NORESOURCES;
+ }
+
+ /*
+ * If the client requested an address, try each subnet to see
+ * if the address is available in that subnet. We will try to
+ * respect the client's request if possible.
+ */
+ if ((requested_iaaddr != NULL) && (requested_iaaddr->len > 0)) {
+ for (i=0; shared_network->ipv6_pools[i] != NULL; i++) {
+ p = shared_network->ipv6_pools[i];
+ if (try_client_v6_address(addr, p, requested_iaaddr)
+ == ISC_R_SUCCESS) {
+ ipv6_pool_reference(pool, p, MDL);
+ shared_network_dereference(&shared_network,
+ MDL);
+ return ISC_R_SUCCESS;
+ }
+ }
+ }
+
+ /*
+ * Otherwise try to get a lease from the first subnet possible.
+ *
+ * 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.
+ */
+ start_pool = shared_network->last_ipv6_pool;
+ i = start_pool;
+ do {
+
+ p = shared_network->ipv6_pools[i];
+ if (activate_lease6(p, addr, &attempts,
+ client_id, 0) == ISC_R_SUCCESS) {
+ ipv6_pool_reference(pool, p, MDL);
+
+ /*
+ * Record the pool used (or next one if there
+ * was a collision).
+ */
+ if (attempts > 1) {
+ i++;
+ if (shared_network->ipv6_pools[i] == NULL) {
+ i = 0;
+ }
+ }
+ shared_network->last_ipv6_pool = i;
+
+ shared_network_dereference(&shared_network, MDL);
+ return ISC_R_SUCCESS;
+ }
+
+ i++;
+ if (shared_network->ipv6_pools[i] == NULL) {
+ i = 0;
+ }
+ } while (i != start_pool);
+
+ /*
+ * If we failed to pick an IPv6 address from any of the subnets.
+ * Presumably that means we have no addresses for the client.
+ */
+ shared_network_dereference(&shared_network, MDL);
+ return ISC_R_NORESOURCES;
+}
+
+/* TODO: IA_TA */
+/* TODO: look at client hints for lease times */
+/* XXX: need to add IA_NA to our ORO? */
+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) {
+ struct option_cache *oc;
+ struct data_string packet_oro;
+ struct host_decl *packet_host;
+ int matched_packet_host;
+ struct option_cache *ia;
+ int rapid_commit;
+ char reply_data[65536];
+ struct dhcpv6_packet *reply = (struct dhcpv6_packet *)reply_data;
+ int reply_ofs = (int)((char *)reply->options - (char *)reply);
+ struct option_state *opt_state;
+ struct host_decl *host;
+ struct option_state *host_opt_state;
+ /* 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;
+ u_int32_t preferred_lifetime;
+ u_int32_t valid_lifetime;
+ struct data_string iaaddr;
+ struct data_string fixed_addr;
+ struct data_string d;
+ u_int16_t len;
+ u_int32_t t1, t2;
+ struct host_decl *save_host;
+ char zeros[24];
+ struct ipv6_pool *pool;
+ struct iaaddr *lease;
+ struct group *group;
+ u_int32_t iaid;
+ struct ia_na *ia_na;
+ struct ia_na *existing_ia_na;
+ int i;
+
+ /*
+ * Initialize to empty values, in case we have to exit early.
+ */
+ opt_state = NULL;
+ memset(&packet_oro, 0, sizeof(packet_oro));
+ memset(&cli_enc_opt_data, 0, sizeof(cli_enc_opt_data));
+ cli_enc_opt_state = NULL;
+ host_opt_state = NULL;
+ memset(&fixed_addr, 0, sizeof(fixed_addr));
+ memset(&iaaddr, 0, sizeof(iaaddr));
+ ia_na = NULL;
+ lease = NULL;
+
+ /*
+ * Set up reply.
+ */
+ if (!start_reply(packet, client_id, NULL, &opt_state, reply)) {
+ goto exit;
+ }
+
+ /*
+ * 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;
+ }
+ }
+
+ /*
+ * 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) {
+ if (!save_option_buffer(&dhcpv6_universe,
+ opt_state, NULL, "", 0,
+ D6O_RAPID_COMMIT, 0)) {
+ log_error("start_reply: error saving "
+ "RAPID_COMMIT option.");
+ goto exit;
+ }
+
+ reply->msg_type = DHCPV6_REPLY;
+ }
+ }
+
+ }
+
+
+
+ /*
+ * Find the host record that matches from the packet, if any.
+ */
+ packet_host = NULL;
+ if (!find_hosts_by_option(&packet_host, packet, packet->options, MDL)) {
+ packet_host = NULL;
+ /*
+ * If we don't have a match from the packet contents,
+ * see if we can match by UID.
+ */
+ if (!find_hosts_by_uid(&packet_host,
+ client_id->data, client_id->len, MDL)) {
+ packet_host = NULL;
+ }
+ }
+ matched_packet_host = 0;
+
+ /*
+ * 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_solicit, &packet_oro);
+
+ /*
+ * Now loop across each IA_NA that we have.
+ */
+ ia = lookup_option(&dhcpv6_universe, packet->options, D6O_IA_NA);
+ while (ia != NULL) {
+ /*
+ * T1 and T2, set to 0 means the client can choose.
+ *
+ * We will adjust this based on preferred and valid
+ * times if we have an address.
+ */
+ t1 = 0;
+ t2 = 0;
+
+ if (!get_encapsulated_IA_state(&cli_enc_opt_state,
+ &cli_enc_opt_data,
+ packet, ia)) {
+ goto exit;
+ }
+
+ /*
+ * Create an IA_NA structure.
+ */
+ iaid = getULong(cli_enc_opt_data.data);
+ ia_na = NULL;
+ if (ia_na_allocate(&ia_na, iaid, client_id->data,
+ client_id->len, MDL) != ISC_R_SUCCESS) {
+ log_fatal("lease_to_client: no memory for ia_na.");
+ }
+
+ /*
+ * Create state for this IA_NA.
+ */
+ host_opt_state = NULL;
+ if (!option_state_allocate(&host_opt_state, MDL)) {
+ log_error("lease_to_client: out of memory "
+ "allocating option_state.");
+ goto exit;
+ }
+
+ /*
+ * See if this NA has an address. If so, we use it
+ * when trying to find a matching host record.
+ */
+ memset(&iaaddr, 0, sizeof(iaaddr));
+ oc = lookup_option(&dhcpv6_universe, cli_enc_opt_state,
+ D6O_IAADDR);
+ if (oc != NULL) {
+ /*
+ * TODO: Check that the address is on one of
+ * the networks that we have, and issue
+ * NotOnLink if not (for Solicit/Request),
+ * or remember to set the address lifetime
+ * to 0 (for Renew/Rebind).
+ */
+ if (!evaluate_option_cache(&iaaddr, packet,
+ NULL, NULL,
+ packet->options,
+ NULL, &global_scope,
+ oc, MDL)) {
+ log_error("lease_to_client: "
+ "error evaluating IAADDR.");
+ goto exit;
+ }
+ /*
+ * Clients may choose to send :: as an address,
+ * with the idea to give hints about
+ * preferred-lifetime or valid-lifetime.
+ *
+ * We ignore this.
+ */
+ memset(zeros, 0, sizeof(zeros));
+ if ((iaaddr.len >= 24) &&
+ !memcmp(iaaddr.data, zeros, 16)) {
+ data_string_forget(&iaaddr, MDL);
+ }
+ }
+
+ /*
+ * Now we need to figure out which host record matches
+ * this IA_NA.
+ *
+ * We first try matching the encapsulated option state.
+ * If nothing is there, then we will use the host entry
+ * matched from the non-encapsulated option state, if
+ * there was one.
+ *
+ * We will only use this host entry for one of the
+ * IA_NA in the packet, to avoid having the same address
+ * on multiple interfaces.
+ */
+ host = NULL;
+ if (!find_hosts_by_option(&host, packet,
+ cli_enc_opt_state, MDL)) {
+ if ((packet_host != NULL) && !matched_packet_host) {
+ matched_packet_host = 1;
+ host = packet_host;
+ } else {
+ host = NULL;
+ }
+ }
+
+ if (iaaddr.len == 0) {
+ /*
+ * Client did not specify the IAADDR to use.
+ *
+ * If we are renewing, this is a problem. Set
+ * to no host, and this will cause the appropriate
+ * response to be sent.
+ *
+ * Otherwise, we simply find the first matching host.
+ */
+ if (packet->dhcpv6_msg_type == DHCPV6_RENEW) {
+ host = NULL;
+ } else {
+ while ((host != NULL) &&
+ (host->fixed_addr == NULL)) {
+ host = host->n_ipaddr;
+ }
+ }
+ } else {
+ /*
+ * Client wanted a specific IAADDR, so we will
+ * only accept a host entry that matches.
+ */
+ save_host = host;
+ for (; host != NULL; host = host->n_ipaddr) {
+ if (host->fixed_addr == NULL) {
+ continue;
+ }
+ if (!evaluate_option_cache(&fixed_addr, NULL,
+ NULL, NULL, NULL,
+ NULL, &global_scope,
+ host->fixed_addr,
+ MDL)) {
+ log_error("lease_to_client: 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);
+ }
+ /*
+ * If we got a Solicit and don't have a matching
+ * host record, have gotten a bad hint.
+ * Pick a host record.
+ */
+ if ((host == NULL) &&
+ (packet->dhcpv6_msg_type == DHCPV6_SOLICIT)) {
+ host = save_host;
+ while ((host != NULL) &&
+ (host->fixed_addr == NULL)) {
+ host = host->n_ipaddr;
+ }
+ }
+ }
+
+ /*
+ * At this point, if we don't have a host entry,
+ * try to find the appropriate pool for this IA.
+ */
+ group = NULL;
+ lease = NULL;
+ if (host != NULL) {
+ group = host->group;
+ } else if (num_pools > 0) {
+ struct iaaddr *tmp;
+ struct in6_addr *in6_addr;
+
+ /*
+ * Find existing IA_NA.
+ */
+ existing_ia_na = NULL;
+ if (ia_na_hash_lookup(&existing_ia_na, ia_active,
+ (char *)ia_na->iaid_duid.data,
+ ia_na->iaid_duid.len, MDL) == 0) {
+ existing_ia_na = NULL;
+ }
+
+ /*
+ * If there are no addresses, we'll ignore this IA_NA.
+ */
+ if ((existing_ia_na != NULL) &&
+ (existing_ia_na->num_iaaddr == 0)) {
+ existing_ia_na = NULL;
+ }
+
+ /*
+ * If we have this IA_NA, then use that.
+ */
+ if ((iaaddr.len == 0) && (existing_ia_na != NULL)) {
+ /*
+ * If the client doesn't ask for a specific
+ * address, we're cool.
+ */
+ tmp = existing_ia_na->iaaddr[0];
+ pool = NULL;
+ ipv6_pool_reference(&pool, tmp->ipv6_pool, MDL);
+ iaaddr_reference(&lease, tmp, MDL);
+ } else if (existing_ia_na != NULL) {
+ /*
+ * Make sure this address is in the IA_NA.
+ */
+ pool = NULL;
+ for (i=0; i<existing_ia_na->num_iaaddr; i++) {
+ tmp = existing_ia_na->iaaddr[i];
+ in6_addr = &tmp->addr;
+ if (memcmp(in6_addr,
+ iaaddr.data, 16) == 0) {
+ ipv6_pool_reference(&pool,
+ tmp->ipv6_pool, MDL);
+ iaaddr_reference(&lease,
+ tmp, MDL);
+ break;
+ }
+ }
+
+ /*
+ * If we didn't find a pool it means that the
+ * client sent an address we don't know about
+ * and we'll consider it a non-match.
+ */
+ if (pool == NULL) {
+ existing_ia_na = NULL;
+ }
+ }
+
+ /*
+ * If we don't have a matching IA_NA for this
+ * client, then we need to get a new address.
+ */
+ if (existing_ia_na == NULL) {
+ pool = NULL;
+ pick_v6_address(&lease, &pool, packet,
+ &iaaddr, client_id);
+ }
+
+ /*
+ * If we got an address, get our group information
+ * from the pool used.
+ */
+ if (pool != NULL) {
+ group = pool->shared_network->group;
+ }
+ }
+
+ /*
+ * Get the lease time from the group.
+ */
+ if (group != NULL) {
+ /*
+ * Execute statements for the host's group.
+ */
+ execute_statements_in_scope(NULL, packet, NULL, NULL,
+ packet->options,
+ host_opt_state,
+ &global_scope,
+ group, root_group);
+ /*
+ * Get our lease time. Note that "preferred lifetime"
+ * and "valid lifetime" are defined in RFC 2462.
+ */
+ oc = lookup_option(&server_universe, opt_state,
+ SV_DEFAULT_LEASE_TIME);
+ valid_lifetime = DEFAULT_DEFAULT_LEASE_TIME;
+ if (oc != NULL) {
+ memset(&d, 0, sizeof(d));
+ if (!evaluate_option_cache(&d, packet, NULL,
+ NULL,
+ packet->options,
+ opt_state,
+ &global_scope,
+ oc, MDL)) {
+ log_error("lease_to_client: error "
+ "getting lease time, "
+ "using default.");
+ } else {
+ /* INSIST(d.len == 4); */
+ valid_lifetime = getULong(d.data);
+ data_string_forget(&d, MDL);
+ }
+ }
+
+ /*
+ * T1: RENEW time.
+ * T2: REBIND time.
+ * preferred: 'deprecate' address.
+ * valid: address expires.
+ *
+ * Values are required for valid and preferred
+ * lifetimes. T1 and T2, if zero, will allow
+ * the client to select their own behaviour.
+ */
+ t1 = t2 = 0;
+ /* XXX: This is more than a little weird. */
+ oc = lookup_option(&dhcp_universe, opt_state,
+ DHO_DHCP_RENEWAL_TIME);
+ if (oc != NULL) {
+ memset(&d, 0, sizeof(d));
+ if (!evaluate_option_cache(&d, packet, NULL,
+ NULL,
+ packet->options,
+ opt_state,
+ &global_scope,
+ oc, MDL)) {
+ /* XXX: I think there are already log
+ * lines by this point.
+ */
+ log_error("lease_to_client: error "
+ "evaluating renew time, "
+ "defaulting to 0");
+ } else {
+ t1 = getULong(d.data);
+ data_string_forget(&d, MDL);
+ }
+ }
+
+ oc = lookup_option(&dhcp_universe, opt_state,
+ DHO_DHCP_REBINDING_TIME);
+ if (oc != NULL) {
+ memset(&d, 0, sizeof(d));
+ if (!evaluate_option_cache(&d, packet, NULL,
+ NULL,
+ packet->options,
+ opt_state,
+ &global_scope,
+ oc, MDL)) {
+ /* XXX: I think there are already log
+ * lines by this point.
+ */
+ log_error("lease_to_client: error "
+ "evaluating rebinding "
+ "time, defaulting to 0");
+ } else {
+ t2 = getULong(d.data);
+ data_string_forget(&d, MDL);
+ }
+ }
+
+ preferred_lifetime = t1 + t2;
+
+ if (preferred_lifetime == 0 ||
+ preferred_lifetime >= valid_lifetime)
+ preferred_lifetime = (valid_lifetime / 2) +
+ (valid_lifetime / 4);
+
+ oc = lookup_option(&server_universe, opt_state,
+ SV_PREFER_LIFETIME);
+ if (oc != NULL) {
+ memset(&d, 0, sizeof(d));
+ if (!evaluate_option_cache(&d, packet, NULL,
+ NULL,
+ packet->options,
+ opt_state,
+ &global_scope,
+ oc, MDL)) {
+ /* XXX: I think there are already log
+ * lines by this point.
+ */
+ log_error("lease_to_client: error "
+ "evaluating preferred "
+ "lifetime, defaulting to "
+ "%d", preferred_lifetime);
+ } else {
+ preferred_lifetime = getULong(d.data);
+ data_string_forget(&d, MDL);
+ }
+ }
+ }
+
+ /*
+ * Shift the lease to the right place, based on our timeout.
+ */
+ if (lease != NULL) {
+ lease->valid_lifetime_end_time = valid_lifetime +
+ cur_time;
+ renew_lease6(pool, lease);
+ }
+
+ /*
+ * Get the address.
+ */
+ memset(&fixed_addr, 0, sizeof(fixed_addr));
+ if (host != NULL) {
+ if (!evaluate_option_cache(&fixed_addr, NULL, NULL,
+ NULL, NULL, NULL,
+ &global_scope,
+ host->fixed_addr, MDL)) {
+ log_error("lease_to_client: error "
+ "evaluating host address.");
+ goto exit;
+ }
+ if (fixed_addr.len != 16) {
+ log_error("lease_to_client: invalid address "
+ "length (%d)", fixed_addr.len);
+ goto exit;
+ }
+ } else if (lease != NULL) {
+ fixed_addr.len = 16;
+ if (!buffer_allocate(&fixed_addr.buffer, 16, MDL)) {
+ log_fatal("lease_to_client: no memory for "
+ "address information.");
+ }
+ fixed_addr.data = fixed_addr.buffer->data;
+ memcpy(fixed_addr.buffer->data, &lease->addr, 16);
+ }
+
+ if (fixed_addr.len == 16) {
+ struct iaaddr *store_iaaddr;
+
+ /*
+ * Store the address.
+ *
+ * XXX: This should allow multiple addresses, rather
+ * than only a single one!
+ */
+ memset(&d, 0, sizeof(d));
+ d.len = 24; /* From RFC 3315, section 22.6 */
+ if (!buffer_allocate(&d.buffer, d.len, MDL)) {
+ log_fatal("lease_to_client: no memory for "
+ "address information.");
+ }
+ d.data = d.buffer->data;
+ memcpy(d.buffer->data, fixed_addr.data, 16);
+ putULong(d.buffer->data+16, preferred_lifetime);
+ putULong(d.buffer->data+20, valid_lifetime);
+ data_string_forget(&fixed_addr, MDL);
+ if (!save_option_buffer(&dhcpv6_universe,
+ host_opt_state,
+ d.buffer,
+ d.buffer->data,
+ d.len, D6O_IAADDR, 0)) {
+ log_error("lease_to_client: error saving "
+ "IAADDR.");
+ data_string_forget(&d, MDL);
+ goto exit;
+ }
+ data_string_forget(&d, MDL);
+ }
+
+ if ((host != NULL) || (lease != NULL)) {
+
+ /*
+ * Remember the client identifier so we can look
+ * it up later.
+ */
+ if (host != NULL) {
+ change_host_uid(host, client_id->data,
+ client_id->len);
+ }
+ /*
+ * Otherwise save the IA_NA, for the same reason.
+ */
+ else if (packet->dhcpv6_msg_type != DHCPV6_SOLICIT) {
+ ia_na_hash_delete(ia_active,
+ (char *)ia_na->iaid_duid.data,
+ ia_na->iaid_duid.len, MDL);
+ /*
+ * ia_na_add_iaaddr() will reference the
+ * lease, so we need to dereference the
+ * previous reference to the lease if one
+ * exists.
+ */
+ if (lease->ia_na != NULL) {
+ ia_na_dereference(&lease->ia_na, MDL);
+ }
+ if (ia_na_add_iaaddr(ia_na, lease,
+ MDL) != ISC_R_SUCCESS) {
+ log_fatal("lease_to_client: out of "
+ "memory adding IAADDR");
+ }
+ ia_na_hash_add(ia_active,
+ (char *)ia_na->iaid_duid.data,
+ ia_na->iaid_duid.len,
+ ia_na, MDL);
+ write_ia_na(ia_na);
+
+ /* If this constitutes a binding, and we
+ * are performing ddns updates, then give
+ * ddns_updates() a chance to do its mojo.
+ */
+ if (((packet->dhcpv6_msg_type
+ == DHCPV6_REQUEST) ||
+ (packet->dhcpv6_msg_type
+ == DHCPV6_RENEW) ||
+ (packet->dhcpv6_msg_type
+ == DHCPV6_REBIND)) &&
+ (((oc = lookup_option(&server_universe,
+ opt_state,
+ SV_DDNS_UPDATES))
+ == NULL) ||
+ evaluate_boolean_option_cache(NULL,
+ packet, NULL, NULL,
+ packet->options, opt_state,
+ &lease->scope, oc, MDL))) {
+ ddns_updates(packet, NULL, NULL,
+ lease, /* XXX */ NULL,
+ opt_state);
+ }
+ }
+
+ } else {
+
+ /*
+ * We send slightly different errors, depending on
+ * whether the client is asking for a new address, or
+ * attempting to renew an address it thinks it has.
+ */
+ if (packet->dhcpv6_msg_type == DHCPV6_REBIND) {
+ if (iaaddr.len >= 24) {
+ /*
+ * If the we have a client address,
+ * tell the client it is invalid.
+ */
+ memset(&d, 0, sizeof(d));
+ d.len = 24;
+ if (!buffer_allocate(&d.buffer,
+ d.len, MDL)) {
+ log_fatal("lease_to_client: "
+ "no memory for "
+ "address "
+ "information.");
+ }
+ d.data = d.buffer->data;
+ memcpy(d.buffer->data, iaaddr.data, 16);
+ putULong(d.buffer->data+16, 0);
+ putULong(d.buffer->data+20, 0);
+ if (!save_option_buffer(
+ &dhcpv6_universe,
+ host_opt_state,
+ d.buffer,
+ d.buffer->data,
+ d.len,
+ D6O_IAADDR, 0)) {
+ log_error("lease_to_client: "
+ "error saving "
+ "IAADDR.");
+ data_string_forget(&d, MDL);
+ goto exit;
+ }
+ data_string_forget(&d, MDL);
+ } else {
+ /*
+ * Otherwise, this is an error.
+ *
+ * XXX: The other possibility is to
+ * not say anything for this
+ * IA, which might be more
+ * correct. RFC 3315 does not
+ * address this case.
+ */
+ if (!set_status_code(STATUS_UnspecFail,
+ "Rebind requested without "
+ "including an addresses.",
+ host_opt_state)) {
+ goto exit;
+ }
+ }
+ } else if (packet->dhcpv6_msg_type == DHCPV6_RENEW) {
+ if (!set_status_code(STATUS_NoBinding,
+ "Address not bound "
+ "to this interface.",
+ host_opt_state)) {
+ goto exit;
+ }
+ } else if ((iaaddr.len >= 24) &&
+ (packet->dhcpv6_msg_type == DHCPV6_REQUEST)){
+ if (!set_status_code(STATUS_NotOnLink,
+ "Address not for "
+ "use on this link.",
+ host_opt_state)) {
+ goto exit;
+ }
+ } else {
+ if (!set_status_code(STATUS_NoAddrsAvail,
+ "No addresses available "
+ "for this interface.",
+ host_opt_state)) {
+ goto exit;
+ }
+ }
+
+ }
+
+ /*
+ * Insure we have enough space
+ */
+ if (sizeof(reply_data) < (reply_ofs + 16)) {
+ log_error("lease_to_client: "
+ "out of space for reply packet.");
+ goto exit;
+ }
+
+ /*
+ * Store the encapsulated option data for this IA_NA into
+ * our reply packet.
+ */
+ len = store_options6(reply_data+reply_ofs+16,
+ sizeof(reply_data)-reply_ofs-16,
+ host_opt_state, packet,
+ required_opts_IA_NA, NULL);
+
+ /*
+ * Store the non-encapsulated option data for this IA_NA
+ * into our reply packet. Defined in RFC 3315, section 22.4.
+ */
+ /* option number */
+ putShort(reply_data+reply_ofs, D6O_IA_NA);
+ /* option length */
+ putUShort(reply_data+reply_ofs+2, len + 12);
+ /* IA_NA, copied from the client */
+ memcpy(reply_data+reply_ofs+4, cli_enc_opt_data.data, 4);
+ /* T1 and T2, set previously */
+ putULong(reply_data+reply_ofs+8, t1);
+ putULong(reply_data+reply_ofs+12, t2);
+
+ /*
+ * Get ready for next IA_NA.
+ */
+ reply_ofs += (len + 16);
+
+ /*
+ * Bit of cleanup.
+ */
+ if (lease != NULL) {
+ iaaddr_dereference(&lease, MDL);
+ }
+ ia_na_dereference(&ia_na, MDL);
+ if (iaaddr.data != NULL) {
+ data_string_forget(&iaaddr, MDL);
+ }
+ option_state_dereference(&host_opt_state, MDL);
+ option_state_dereference(&cli_enc_opt_state, MDL);
+ data_string_forget(&cli_enc_opt_data, MDL);
+ ia = ia->next;
+ }
+
+ /*
+ * 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) {
+ iaaddr_dereference(&lease, MDL);
+ }
+ if (ia_na != NULL) {
+ ia_na_dereference(&ia_na, MDL);
+ }
+ if (iaaddr.buffer != NULL) {
+ data_string_forget(&iaaddr, MDL);
+ }
+ if (fixed_addr.buffer != NULL) {
+ data_string_forget(&fixed_addr, MDL);
+ }
+ if (host_opt_state != NULL) {
+ option_state_dereference(&host_opt_state, 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 (packet_oro.buffer != NULL) {
+ data_string_forget(&packet_oro, MDL);
+ }
+ if (opt_state != NULL) {
+ option_state_dereference(&opt_state, MDL);
+ }
+}
+
+/*
+ * 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.
+ */
+
+/* TODO: discard unicast messages */
+static void
+dhcpv6_solicit(struct data_string *reply_ret, struct packet *packet) {
+ struct data_string client_id;
+ struct option_cache *oc;
+
+ /*
+ * 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.
+ */
+
+/* TODO: discard unicast messages, unless we set unicast option */
+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;
+ }
+
+ /*
+ * 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);
+}
+
+/*
+ * When a client thinks it might be on a new link, it sends a
+ * Confirm message.
+ */
+
+/* TODO: discard unicast messages, unless we set unicast option */
+static void
+dhcpv6_confirm(struct data_string *reply_ret, struct packet *packet) {
+ struct data_string client_id;
+ struct option_state *opt_state;
+ char reply_data[65536];
+ struct dhcpv6_packet *reply = (struct dhcpv6_packet *)reply_data;
+ int reply_ofs = (int)((char *)reply->options - (char *)reply);
+ 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 data_string iaaddr;
+ struct host_decl *host;
+ int num_ia;
+ int num_addr;
+ struct data_string fixed_addr;
+ struct data_string packet_oro;
+
+ /*
+ * Validate the message.
+ */
+ if (!valid_client_msg(packet, &client_id)) {
+ return;
+ }
+
+ /*
+ * Bit of variable initialization.
+ */
+ memset(&iaaddr, 0, sizeof(iaaddr));
+ num_ia = 0;
+ num_addr = 0;
+ memset(&fixed_addr, 0, sizeof(fixed_addr));
+ memset(&packet_oro, 0, sizeof(packet_oro));
+
+ /*
+ * Set up reply.
+ */
+ if (!start_reply(packet, &client_id, NULL, &opt_state, reply)) {
+ goto exit;
+ }
+
+ /*
+ * Search each IA to make sure we know about it.
+ */
+ ia = lookup_option(&dhcpv6_universe, packet->options, D6O_IA_NA);
+ for (; ia != NULL; ia = ia->next) {
+ num_ia++;
+
+ if (!get_encapsulated_IA_state(&cli_enc_opt_state,
+ &cli_enc_opt_data,
+ packet, ia)) {
+ goto exit;
+ }
+
+ memset(&iaaddr, 0, sizeof(iaaddr));
+ oc = lookup_option(&dhcpv6_universe, cli_enc_opt_state,
+ D6O_IAADDR);
+ if (oc == NULL) {
+ /*
+ * No address with this IA, check next.
+ */
+ continue;
+ }
+
+ if (!evaluate_option_cache(&iaaddr, packet,
+ NULL, NULL,
+ packet->options,
+ NULL, &global_scope,
+ oc, MDL)) {
+ log_error("dhcpv6_confirm: "
+ "error evaluating IAADDR.");
+ goto exit;
+ }
+
+ host = NULL;
+ if (!find_hosts_by_uid(&host,
+ client_id.data, client_id.len, MDL)) {
+ /*
+ * RFC 3315, section 18.2.2 implies that when the
+ * server doesn't know about the client it should
+ * not send a reply.
+ */
+ goto exit;
+ }
+
+ /*
+ * Find the matching host entry, if any.
+ */
+ for (; host != NULL; host = host->n_ipaddr) {
+ if (host->fixed_addr == NULL) {
+ continue;
+ }
+ if (!evaluate_option_cache(&fixed_addr, NULL,
+ NULL, NULL, NULL,
+ NULL, &global_scope,
+ host->fixed_addr,
+ MDL)) {
+ log_error("dhcpv6_confirm: 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);
+ }
+
+ /*
+ * No matching entry found, so this is a bad address.
+ */
+ if (host != NULL) {
+ num_addr++;
+ }
+
+ }
+
+ /*
+ * If we have no addresses from the client, we don't send a reply.
+ */
+ if (num_addr == 0) {
+ goto exit;
+ }
+
+ /*
+ * Set our status.
+ */
+ if (num_addr < num_ia) {
+ 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:
+ if (iaaddr.buffer != NULL) {
+ data_string_forget(&iaaddr, MDL);
+ }
+ if (fixed_addr.buffer != NULL) {
+ data_string_forget(&fixed_addr, MDL);
+ }
+ data_string_forget(&client_id, MDL);
+}
+
+/*
+ * Renew is when a client wants to extend its lease, at time T1.
+ *
+ * We handle this the same as if the client wants a new lease, except
+ * for the error code of when addresses don't match.
+ */
+
+/* TODO: discard unicast messages, unless we set unicast option */
+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;
+ }
+
+ /*
+ * 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.
+ */
+
+/* TODO: discard unicast messages, unless we set unicast option */
+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 iaaddr *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);
+ write_ia_na(lease->ia_na);
+ }
+}
+
+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(reply_data+(*reply_ofs), D6O_IA_NA);
+ /* option length */
+ putUShort(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(reply_data+(*reply_ofs)+8, 0);
+ putULong(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,
+ 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 option_state *host_opt_state;
+ char tmp_addr[INET6_ADDRSTRLEN];
+ int len;
+ struct data_string iaaddr;
+ struct data_string fixed_addr;
+ int iaaddr_is_found;
+ char reply_data[65536];
+ struct dhcpv6_packet *reply = (struct dhcpv6_packet *)reply_data;
+ int reply_ofs = (int)((char *)reply->options - (char *)reply);
+ char status_msg[32];
+ struct iaaddr *lease;
+ struct ia_na *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));
+ host_opt_state = NULL;
+ lease = NULL;
+
+ /*
+ * Find the host record that matches from the packet, if any.
+ */
+ packet_host = NULL;
+ if (!find_hosts_by_uid(&packet_host,
+ client_id->data, client_id->len, MDL)) {
+ packet_host = NULL;
+ /*
+ * Note: In general, we don't expect a client to provide
+ * enough information to match by option for these
+ * types of messages, but if we don't have a UID
+ * match we can check anyway.
+ */
+ if (!find_hosts_by_option(&packet_host,
+ packet, packet->options, MDL)) {
+ packet_host = NULL;
+ }
+ }
+
+ /*
+ * 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);
+
+ /*
+ * 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, (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) {
+ iaaddr_is_found = 0;
+
+ if (!get_encapsulated_IA_state(&cli_enc_opt_state,
+ &cli_enc_opt_data,
+ packet, ia)) {
+ 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.
+ *
+ * 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 >= 24)) {
+ /*
+ * Find existing IA_NA.
+ */
+ if (ia_na_make_key(&key, iaid, 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_na_hash_lookup(&existing_ia_na, ia_active,
+ (char *)key.data, key.len, MDL)) {
+ /*
+ * Make sure this address is in the IA_NA.
+ */
+ for (i=0; i<existing_ia_na->num_iaaddr; i++) {
+ struct iaaddr *tmp;
+ struct in6_addr *in6_addr;
+
+ tmp = existing_ia_na->iaaddr[i];
+ in6_addr = &tmp->addr;
+ if (memcmp(in6_addr,
+ iaaddr.data, 16) == 0) {
+ iaaddr_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) {
+ iaaddr_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) {
+ iaaddr_dereference(&lease, MDL);
+ }
+ if (host_opt_state != NULL) {
+ option_state_dereference(&host_opt_state, 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 occurrance.
+ *
+ * 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: discard unicast messages, unless we set unicast option */
+/* 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;
+ }
+
+ /*
+ * 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);
+}
+
+static void
+ia_na_match_release(const struct data_string *client_id,
+ const struct data_string *iaaddr,
+ struct iaaddr *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);
+ write_ia_na(lease->ia_na);
+ }
+}
+
+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_match_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_match_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(reply_data+(*reply_ofs), D6O_IA_NA);
+ /* option length */
+ putUShort(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(reply_data+(*reply_ofs)+8, 0);
+ putULong(reply_data+(*reply_ofs)+12, 0);
+
+ /*
+ * Get ready for next IA_NA.
+ */
+ *reply_ofs += (len + 16);
+
+exit:
+ option_state_dereference(&host_opt_state, MDL);
+}
+
+/*
+ * Release means a client is done with the addresses.
+ */
+
+/* TODO: discard unicast messages, unless we set unicast option */
+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;
+ }
+
+ /*
+ * 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);
+
+ 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.
+ */
+
+/* TODO: discard unicast messages, unless we set unicast option */
+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_NA or IA_TA that would cause us to
+ * allocate addresses to the client.
+ */
+ lease_to_client(reply, packet, &client_id, &server_id);
+
+ /*
+ * 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 */
+static void
+dhcpv6_relay_forw(struct data_string *reply_ret, struct packet *packet) {
+ struct dhcpv6_relay_packet reply;
+ 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 interface_id;
+
+ /*
+ * Intialize variables for early exit.
+ */
+ memset(&enc_opt_data, 0, sizeof(enc_opt_data));
+ enc_packet = NULL;
+ memset(&enc_reply, 0, sizeof(enc_reply));
+ memset(&interface_id, 0, sizeof(interface_id));
+
+ /*
+ * 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;
+ }
+
+ 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("dhcpv6_forw_relay: error evaluating "
+ "relayed message.");
+ goto exit;
+ }
+
+ if (!packet6_len_okay(enc_opt_data.data, enc_opt_data.len)) {
+ 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)) {
+ log_error("dhcpv6_forw_relay: "
+ "no memory for encapsulated packet.");
+ goto exit;
+ }
+
+ if (!option_state_allocate(&enc_packet->options, MDL)) {
+ 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;
+ enc_packet->dhcpv6_container_packet = packet;
+
+ msg_type = enc_opt_data.data[0];
+ if ((msg_type == DHCPV6_RELAY_FORW) ||
+ (msg_type == DHCPV6_RELAY_REPL)) {
+ 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-sizeof(*relay),
+ &dhcpv6_universe)) {
+ /* no logging here, as parse_option_buffer() logs all
+ cases where it fails */
+ goto exit;
+ }
+ } else {
+ 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-sizeof(*msg),
+ &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;
+ }
+
+ /*
+ * Append the interface-id if present
+ */
+ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_INTERFACE_ID);
+ if (oc != NULL) {
+ memset(&interface_id, 0, sizeof(interface_id));
+ if (!evaluate_option_cache(&interface_id, NULL, NULL, NULL,
+ NULL, NULL, &global_scope,
+ oc, MDL)) {
+ log_error("dhcpv6_forw_relay: error evaluating "
+ "Interface ID.");
+ goto exit;
+ }
+ }
+
+ /*
+ * Packet header stuff all comes from the forward message.
+ */
+ 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));
+
+ /*
+ * Copy our encapsulated stuff for caller.
+ */
+ reply_ret->len = sizeof(reply) + 4 + enc_reply.len;
+ if (interface_id.data != NULL) {
+ reply_ret->len += 4 + interface_id.len;
+ }
+ /*
+ * XXX: We should not allow this to happen, perhaps by letting
+ * build_dhcp_reply() know our space remaining.
+ */
+ if (reply_ret->len >= 65536) {
+ log_error("dhcpv6_forw_relay: RELAY-REPL too big (%d bytes)",
+ reply_ret->len);
+ goto exit;
+ }
+ 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, sizeof(reply));
+ putShort(reply_ret->buffer->data+sizeof(reply), D6O_RELAY_MSG);
+ putShort(reply_ret->buffer->data+sizeof(reply)+2, enc_reply.len);
+ memcpy(reply_ret->buffer->data+sizeof(reply)+4,
+ enc_reply.data, enc_reply.len);
+ if (interface_id.data != NULL) {
+ putShort(reply_ret->buffer->data+sizeof(reply)+4+enc_reply.len,
+ D6O_INTERFACE_ID);
+ putShort(reply_ret->buffer->data+sizeof(reply)+6+enc_reply.len,
+ interface_id.len);
+ memcpy(reply_ret->buffer->data+sizeof(reply)+8+enc_reply.len,
+ interface_id.data, interface_id.len);
+ }
+
+exit:
+ if (interface_id.data != NULL) {
+ data_string_forget(&interface_id, 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);
+ }
+}
+
+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));
+ switch (packet->dhcpv6_msg_type) {
+ case DHCPV6_SOLICIT:
+ dhcpv6_solicit(reply, packet);
+ break;
+ case DHCPV6_ADVERTISE:
+ dhcpv6_discard(packet);
+ break;
+ case DHCPV6_REQUEST:
+ dhcpv6_request(reply, packet);
+ break;
+ case DHCPV6_CONFIRM:
+ dhcpv6_confirm(reply, packet);
+ break;
+ case DHCPV6_RENEW:
+ dhcpv6_renew(reply, packet);
+ break;
+ case DHCPV6_REBIND:
+ dhcpv6_rebind(reply, packet);
+ break;
+ case DHCPV6_REPLY:
+ dhcpv6_discard(packet);
+ break;
+ case DHCPV6_RELEASE:
+ dhcpv6_release(reply, packet);
+ break;
+ case DHCPV6_DECLINE:
+ dhcpv6_decline(reply, packet);
+ break;
+ case DHCPV6_RECONFIGURE:
+ dhcpv6_discard(packet);
+ break;
+ case DHCPV6_INFORMATION_REQUEST:
+ dhcpv6_information_request(reply, packet);
+ break;
+ case DHCPV6_RELAY_FORW:
+ dhcpv6_relay_forw(reply, packet);
+ break;
+ case DHCPV6_RELAY_REPL:
+ 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;
+ struct option_cache *oc;
+ struct data_string tmp_ds;
+
+ 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 {
+ 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;
+ }
+/* For testing, we reply to the sending port, so we don't need a root client */
+ to_addr.sin6_port = packet->client_port;
+ 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);
+ }
+}
+
diff --git a/server/mdb.c b/server/mdb.c
index 910c72dc..536407dd 100644
--- a/server/mdb.c
+++ b/server/mdb.c
@@ -34,7 +34,7 @@
#ifndef lint
static char copyright[] =
-"$Id: mdb.c,v 1.88 2007/05/04 21:46:50 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
+"$Id: mdb.c,v 1.89 2007/05/08 23:05:22 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
@@ -49,6 +49,25 @@ 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.
+ */
+typedef struct host_id_info {
+ struct option *option;
+ host_hash_t *values_hash;
+ 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;
@@ -129,6 +148,79 @@ static int find_uid_statement (struct executable_statement *esp,
return 0;
}
+
+static host_id_info_t *
+find_host_id_info(unsigned int option_code) {
+ host_id_info_t *p;
+
+ for (p=host_id_info; p != NULL; p = p->next) {
+ if (p->option->code == option_code) {
+ 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) {
+ struct host_decl *old_entry;
+
+ /* 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;
@@ -137,6 +229,7 @@ isc_result_t enter_host (hd, dynamicp, 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))
@@ -276,6 +369,63 @@ isc_result_t enter_host (hd, dynamicp, commit)
}
}
+
+ /*
+ * 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);
+ 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-identifer "
+ "option hash.");
+ }
+ 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;
@@ -409,6 +559,11 @@ isc_result_t delete_host (hd, commit)
}
}
+ 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
@@ -470,6 +625,43 @@ int find_hosts_by_uid (struct host_decl **hp,
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;
+
+ for (p = host_id_info; p != NULL; p = p->next) {
+ oc = lookup_option(p->option->universe,
+ opt_state, p->option->code);
+ if (oc != NULL) {
+ memset(&data, 0, sizeof(data));
+ if (!evaluate_option_cache(&data, packet, NULL, NULL,
+ opt_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
@@ -688,29 +880,29 @@ int find_grouped_subnet (struct subnet **sp,
return 0;
}
-int subnet_inner_than (subnet, scan, warnp)
- struct subnet *subnet, *scan;
- int warnp;
-{
- if (addr_eq (subnet_number (subnet -> net, scan -> netmask),
- scan -> net) ||
- addr_eq (subnet_number (scan -> net, subnet -> netmask),
- subnet -> net)) {
- char n1buf [16];
+/* 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 (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 < 32; i++)
- if (subnet -> netmask.iabuf [3 - (i >> 3)]
+ for (i = 0; i < 128; i++)
+ if (subnet->netmask.iabuf[3 - (i >> 3)]
& (1 << (i & 7)))
break;
- for (j = 0; j < 32; j++)
- if (scan -> netmask.iabuf [3 - (j >> 3)] &
+ for (j = 0; j < 128; j++)
+ if (scan->netmask.iabuf[3 - (j >> 3)] &
(1 << (j & 7)))
break;
- strcpy (n1buf, piaddr (subnet -> net));
- if (warnp)
- log_error ("%ssubnet %s/%d overlaps subnet %s/%d",
- "Warning: ", n1buf, 32 - i,
- piaddr (scan -> net), 32 - j);
+ 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;
}
@@ -1053,7 +1245,7 @@ int supersede_lease (comp, lease, commit, propogate, pimmediate)
just_move_it:
#if defined (FAILOVER_PROTOCOL)
/* Atsfp should be cleared upon any state change that implies
- * propogation wether supersede_lease was given a copy lease
+ * propogation whether supersede_lease was given a copy lease
* structure or not (often from the pool_timer()).
*/
if (propogate)
@@ -1234,7 +1426,7 @@ void make_binding_state_transition (struct lease *lease)
lease -> binding_state == FTS_ACTIVE &&
lease -> next_binding_state != FTS_RELEASED))) {
#if defined (NSUPDATE)
- ddns_removals (lease);
+ ddns_removals(lease, NULL);
#endif
if (lease -> on_expiry) {
execute_statements ((struct binding_value **)0,
@@ -1285,7 +1477,7 @@ void make_binding_state_transition (struct lease *lease)
lease -> binding_state == FTS_ACTIVE &&
lease -> next_binding_state == FTS_RELEASED))) {
#if defined (NSUPDATE)
- ddns_removals (lease);
+ ddns_removals(lease, NULL);
#endif
if (lease -> on_release) {
execute_statements ((struct binding_value **)0,
@@ -1452,7 +1644,7 @@ void release_lease (lease, packet)
/* If there are statements to execute when the lease is
released, execute them. */
#if defined (NSUPDATE)
- ddns_removals (lease);
+ ddns_removals(lease, NULL);
#endif
if (lease -> on_release) {
execute_statements ((struct binding_value **)0,
@@ -1513,7 +1705,7 @@ void abandon_lease (lease, message)
{
struct lease *lt = (struct lease *)0;
#if defined (NSUPDATE)
- ddns_removals (lease);
+ ddns_removals(lease, NULL);
#endif
if (!lease_copy (&lt, lease, MDL))
@@ -1545,7 +1737,7 @@ void dissociate_lease (lease)
{
struct lease *lt = (struct lease *)0;
#if defined (NSUPDATE)
- ddns_removals (lease);
+ ddns_removals(lease, NULL);
#endif
if (!lease_copy (&lt, lease, MDL))
@@ -2102,6 +2294,9 @@ int write_leases ()
}
}
log_info ("Wrote %d leases to leases file.", num_written);
+ if (!write_leases6()) {
+ return 0;
+ }
if (!commit_leases ())
return 0;
return 1;
@@ -2470,6 +2665,15 @@ void free_everything ()
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);
diff --git a/server/mdb6.c b/server/mdb6.c
new file mode 100644
index 00000000..058122bc
--- /dev/null
+++ b/server/mdb6.c
@@ -0,0 +1,1737 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
+/* TODO: assert() */
+/* TODO: simplify functions, as pool is now in iaaddr */
+
+#include <time.h>
+#include <netinet/in.h>
+
+#include "isc-dhcp/result.h"
+
+#include <stdarg.h>
+#include "omapip/omapip.h"
+#include "dhcpd.h"
+#include "omapip/hash.h"
+#include "dst/md5.h"
+
+HASH_FUNCTIONS(ia_na, unsigned char *, struct ia_na, ia_na_hash_t,
+ ia_na_reference, ia_na_dereference, do_string_hash);
+
+ia_na_hash_t *ia_active;
+
+HASH_FUNCTIONS(iaaddr, struct in6_addr *, struct iaaddr, iaaddr_hash_t,
+ ia_na_reference, ia_na_dereference, do_string_hash);
+
+struct ipv6_pool **pools;
+int num_pools;
+
+/*
+ * Create a new IAADDR structure.
+ *
+ * - iaaddr must be a pointer to a (struct iaaddr *) pointer previously
+ * initialized to NULL
+ */
+isc_result_t
+iaaddr_allocate(struct iaaddr **iaaddr, const char *file, int line) {
+ struct iaaddr *tmp;
+
+ if (iaaddr == NULL) {
+ log_error("%s(%d): NULL pointer reference", file, line);
+ return ISC_R_INVALIDARG;
+ }
+ if (*iaaddr != NULL) {
+ log_error("%s(%d): non-NULL pointer", file, line);
+ return ISC_R_INVALIDARG;
+ }
+
+ tmp = dmalloc(sizeof(*tmp), file, line);
+ if (tmp == NULL) {
+ return ISC_R_NOMEMORY;
+ }
+
+ tmp->refcnt = 1;
+ tmp->state = FTS_FREE;
+ tmp->heap_index = -1;
+
+ *iaaddr = tmp;
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Reference an IAADDR structure.
+ *
+ * - iaaddr must be a pointer to a (struct iaaddr *) pointer previously
+ * initialized to NULL
+ */
+isc_result_t
+iaaddr_reference(struct iaaddr **iaaddr, struct iaaddr *src,
+ const char *file, int line) {
+ if (iaaddr == NULL) {
+ log_error("%s(%d): NULL pointer reference", file, line);
+ return ISC_R_INVALIDARG;
+ }
+ if (*iaaddr != NULL) {
+ log_error("%s(%d): non-NULL pointer", file, line);
+ return ISC_R_INVALIDARG;
+ }
+ if (src == NULL) {
+ log_error("%s(%d): NULL pointer reference", file, line);
+ return ISC_R_INVALIDARG;
+ }
+ *iaaddr = src;
+ src->refcnt++;
+ return ISC_R_SUCCESS;
+}
+
+
+/*
+ * Dereference an IAADDR structure.
+ *
+ * If it is the last reference, then the memory for the
+ * structure is freed.
+ */
+isc_result_t
+iaaddr_dereference(struct iaaddr **iaaddr, const char *file, int line) {
+ struct iaaddr *tmp;
+
+ if ((iaaddr == NULL) || (*iaaddr == NULL)) {
+ log_error("%s(%d): NULL pointer", file, line);
+ return ISC_R_INVALIDARG;
+ }
+
+ tmp = *iaaddr;
+ *iaaddr = 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_na != NULL) {
+ ia_na_dereference(&(tmp->ia_na), 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);
+ }
+ dfree(tmp, file, line);
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Make the key that we use for IA_NA.
+ */
+isc_result_t
+ia_na_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_NA structure.
+ *
+ * - ia_na must be a pointer to a (struct ia_na *) 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_na_allocate(struct ia_na **ia_na, u_int32_t iaid,
+ const char *duid, unsigned int duid_len,
+ const char *file, int line) {
+ struct ia_na *tmp;
+
+ if (ia_na == NULL) {
+ log_error("%s(%d): NULL pointer reference", file, line);
+ return ISC_R_INVALIDARG;
+ }
+ if (*ia_na != NULL) {
+ log_error("%s(%d): non-NULL pointer", file, line);
+ return ISC_R_INVALIDARG;
+ }
+
+ tmp = dmalloc(sizeof(*tmp), file, line);
+ if (tmp == NULL) {
+ return ISC_R_NOMEMORY;
+ }
+
+ if (ia_na_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_na = tmp;
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Reference an IA_NA structure.
+ *
+ * - ia_na must be a pointer to a (struct ia_na *) pointer previously
+ * initialized to NULL
+ */
+isc_result_t
+ia_na_reference(struct ia_na **ia_na, struct ia_na *src,
+ const char *file, int line) {
+ if (ia_na == NULL) {
+ log_error("%s(%d): NULL pointer reference", file, line);
+ return ISC_R_INVALIDARG;
+ }
+ if (*ia_na != NULL) {
+ log_error("%s(%d): non-NULL pointer", file, line);
+ return ISC_R_INVALIDARG;
+ }
+ if (src == NULL) {
+ log_error("%s(%d): NULL pointer reference", file, line);
+ return ISC_R_INVALIDARG;
+ }
+ *ia_na = src;
+ src->refcnt++;
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Dereference an IA_NA structure.
+ *
+ * If it is the last reference, then the memory for the
+ * structure is freed.
+ */
+isc_result_t
+ia_na_dereference(struct ia_na **ia_na, const char *file, int line) {
+ struct ia_na *tmp;
+ int i;
+
+ if ((ia_na == NULL) || (*ia_na == NULL)) {
+ log_error("%s(%d): NULL pointer", file, line);
+ return ISC_R_INVALIDARG;
+ }
+
+ tmp = *ia_na;
+ *ia_na = NULL;
+
+ tmp->refcnt--;
+ if (tmp->refcnt < 0) {
+ log_error("%s(%d): negative refcnt", file, line);
+ tmp->refcnt = 0;
+ }
+ if (tmp->refcnt == 0) {
+ if (tmp->iaaddr != NULL) {
+ for (i=0; i<tmp->num_iaaddr; i++) {
+ iaaddr_dereference(&(tmp->iaaddr[i]),
+ file, line);
+ }
+ dfree(tmp->iaaddr, file, line);
+ }
+ data_string_forget(&(tmp->iaid_duid), file, line);
+ dfree(tmp, file, line);
+ }
+ return ISC_R_SUCCESS;
+}
+
+
+/*
+ * Add an IAADDR entry to an IA_NA structure.
+ */
+isc_result_t
+ia_na_add_iaaddr(struct ia_na *ia_na, struct iaaddr *iaaddr,
+ const char *file, int line) {
+ int max;
+ struct iaaddr **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 we might expect on an
+ * interface.
+ */
+ if (ia_na->max_iaaddr <= ia_na->num_iaaddr) {
+ max = ia_na->max_iaaddr + 4;
+ new = dmalloc(max * sizeof(struct iaaddr *), file, line);
+ if (new == NULL) {
+ return ISC_R_NOMEMORY;
+ }
+ memcpy(new, ia_na->iaaddr,
+ ia_na->num_iaaddr * sizeof(struct iaaddr *));
+ ia_na->iaaddr = new;
+ ia_na->max_iaaddr = max;
+ }
+
+ iaaddr_reference(&(ia_na->iaaddr[ia_na->num_iaaddr]), iaaddr,
+ file, line);
+ ia_na->num_iaaddr++;
+ ia_na_reference(&iaaddr->ia_na, ia_na, file, line);
+
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Remove an IAADDR entry to an IA_NA structure.
+ *
+ * Note: if an IAADDR appears more than once, then only ONE will be removed.
+ */
+void
+ia_na_remove_iaaddr(struct ia_na *ia_na, struct iaaddr *iaaddr,
+ const char *file, int line) {
+ int i, j;
+
+ for (i=0; i<ia_na->num_iaaddr; i++) {
+ if (ia_na->iaaddr[i] == iaaddr) {
+ /* remove this IAADDR */
+ iaaddr_dereference(&(ia_na->iaaddr[i]), file, line);
+ /* move remaining IAADDR pointers down one */
+ for (j=i+1; j < ia_na->num_iaaddr; j++) {
+ ia_na->iaaddr[j-1] = ia_na->iaaddr[j];
+ }
+ /* decrease our total count */
+ ia_na->num_iaaddr--;
+ return;
+ }
+ }
+ log_error("%s(%d): IAADDR not in IA_NA", file, line);
+}
+
+/*
+ * 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 iaaddr *ia = (struct iaaddr *)a;
+ struct iaaddr *ib = (struct iaaddr *)b;
+
+ return difftime(ia->valid_lifetime_end_time,
+ ib->valid_lifetime_end_time) < 0;
+}
+
+/*
+ * Helper function for lease heaps.
+ * Callback when an address's position in the heap changes.
+ */
+static void
+lease_address_index_changed(void *iaaddr, unsigned int new_heap_index) {
+ ((struct iaaddr *)iaaddr)-> heap_index = new_heap_index;
+}
+
+
+/*
+ * Create a new IPv6 lease pool structure.
+ *
+ * - pool must be a pointer to a (struct ipv6_pool *) pointer previously
+ * initialized to NULL
+ */
+isc_result_t
+ipv6_pool_allocate(struct ipv6_pool **pool,
+ const struct in6_addr *start_addr, int bits,
+ const char *file, int line) {
+ struct ipv6_pool *tmp;
+
+ if (pool == NULL) {
+ log_error("%s(%d): NULL pointer reference", file, line);
+ return ISC_R_INVALIDARG;
+ }
+ if (*pool != NULL) {
+ log_error("%s(%d): non-NULL pointer", file, line);
+ return ISC_R_INVALIDARG;
+ }
+
+ tmp = dmalloc(sizeof(*tmp), file, line);
+ if (tmp == NULL) {
+ return ISC_R_NOMEMORY;
+ }
+
+ tmp->refcnt = 1;
+ tmp->start_addr = *start_addr;
+ tmp->bits = bits;
+ if (!iaaddr_new_hash(&tmp->addrs, DEFAULT_HASH_SIZE, file, line)) {
+ dfree(tmp, file, line);
+ return ISC_R_NOMEMORY;
+ }
+ if (isc_heap_create(lease_older, lease_address_index_changed,
+ 0, &(tmp->active_timeouts)) != ISC_R_SUCCESS) {
+ iaaddr_free_hash_table(&(tmp->addrs), file, line);
+ dfree(tmp, file, line);
+ return ISC_R_NOMEMORY;
+ }
+ if (isc_heap_create(lease_older, lease_address_index_changed,
+ 0, &(tmp->inactive_timeouts)) != ISC_R_SUCCESS) {
+ isc_heap_destroy(&(tmp->active_timeouts));
+ iaaddr_free_hash_table(&(tmp->addrs), file, line);
+ dfree(tmp, file, line);
+ return ISC_R_NOMEMORY;
+ }
+
+ *pool = tmp;
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Reference an IPv6 pool structure.
+ *
+ * - pool must be a pointer to a (struct pool *) pointer previously
+ * initialized to NULL
+ */
+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 ISC_R_INVALIDARG;
+ }
+ if (*pool != NULL) {
+ log_error("%s(%d): non-NULL pointer", file, line);
+ return ISC_R_INVALIDARG;
+ }
+ if (src == NULL) {
+ log_error("%s(%d): NULL pointer reference", file, line);
+ return ISC_R_INVALIDARG;
+ }
+ *pool = src;
+ src->refcnt++;
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Note: Each IAADDR in a pool is referenced by the pool. This is needed
+ * to prevent the IAADDR 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 iaaddr *iaaddr = (struct iaaddr *)value;
+
+ iaaddr_dereference(&iaaddr, 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 iaaddr *iaaddr = (struct iaaddr *)value;
+
+ iaaddr_dereference(&iaaddr, MDL);
+}
+
+
+/*
+ * Dereference an IPv6 pool structure.
+ *
+ * If it is the last reference, then the memory for the
+ * structure is freed.
+ */
+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 ISC_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) {
+ iaaddr_hash_foreach(tmp->addrs, dereference_hash_entry);
+ iaaddr_free_hash_table(&(tmp->addrs), 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
+create_address(struct in6_addr *addr,
+ const struct in6_addr *net_start_addr, int net_bits,
+ const struct data_string *input) {
+ MD5_CTX 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.
+ */
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, input->data, input->len);
+ MD5_Final((unsigned char *)addr, &ctx);
+
+ /*
+ * Copy the 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;
+ }
+}
+
+/*
+ * Create a lease for the given address and client duid.
+ *
+ * - pool must be a pointer to a (struct 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
+activate_lease6(struct ipv6_pool *pool, struct iaaddr **addr,
+ unsigned int *attempts,
+ const struct data_string *uid, time_t valid_lifetime_end_time) {
+ struct data_string ds;
+ struct in6_addr tmp;
+ struct iaaddr *test_iaaddr;
+ struct data_string new_ds;
+ struct iaaddr *iaaddr;
+ isc_result_t iaaddr_allocate_result;
+ isc_result_t insert_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) > 100) {
+ data_string_forget(&ds, MDL);
+ return ISC_R_NORESOURCES;
+ }
+
+ /*
+ * Create an address
+ */
+ create_address(&tmp, &pool->start_addr, pool->bits, &ds);
+
+ /*
+ * If this address is not in use, we're happy with it
+ */
+ test_iaaddr = NULL;
+ if (iaaddr_hash_lookup(&test_iaaddr, pool->addrs,
+ &tmp, sizeof(tmp), MDL) == 0) {
+ break;
+ }
+ iaaddr_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((char *)new_ds.data, ds.data, ds.len);
+ memcpy((char *)new_ds.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;
+ iaaddr_allocate_result = iaaddr_allocate(&iaaddr, MDL);
+ if (iaaddr_allocate_result != ISC_R_SUCCESS) {
+ return iaaddr_allocate_result;
+ }
+ memcpy(&iaaddr->addr, &tmp, sizeof(iaaddr->addr));
+
+ iaaddr_reference(addr, iaaddr, MDL);
+ return add_lease6(pool, iaaddr, valid_lifetime_end_time);
+}
+
+/*
+ * 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 iaaddr *iaaddr,
+ time_t valid_lifetime_end_time) {
+ isc_result_t insert_result;
+
+ iaaddr->state = FTS_ACTIVE;
+ iaaddr->valid_lifetime_end_time = valid_lifetime_end_time;
+ ipv6_pool_reference(&iaaddr->ipv6_pool, pool, MDL);
+
+ /*
+ * Add IAADDR to our structures.
+ */
+ iaaddr_hash_add(pool->addrs, &iaaddr->addr,
+ sizeof(iaaddr->addr), iaaddr, MDL);
+ insert_result = isc_heap_insert(pool->active_timeouts, iaaddr);
+ if (insert_result != ISC_R_SUCCESS) {
+ iaaddr_hash_delete(pool->addrs, &iaaddr->addr,
+ sizeof(iaaddr->addr), MDL);
+ iaaddr_dereference(&iaaddr, MDL);
+ return insert_result;
+ }
+
+ /*
+ * And we're done.
+ */
+ pool->num_active++;
+ return ISC_R_SUCCESS;
+}
+
+isc_boolean_t
+lease6_exists(const struct ipv6_pool *pool, const struct in6_addr *addr) {
+ struct iaaddr *test_iaaddr;
+
+ test_iaaddr = NULL;
+ if (iaaddr_hash_lookup(&test_iaaddr, pool->addrs,
+ (void *)addr, sizeof(*addr), MDL)) {
+ iaaddr_dereference(&test_iaaddr, MDL);
+ return ISC_TRUE;
+ } else {
+ return ISC_FALSE;
+ }
+}
+
+/*
+ * Renew an lease in the pool.
+ *
+ * To do this, first set the new valid_lifetime_end_time for the address,
+ * and then invoke renew_lease() on the address.
+ *
+ * WARNING: lease times must only be extended, never reduced!!!
+ *
+ * We return a isc_result_t so this function can be called the same
+ * as release or decline.
+ */
+isc_result_t
+renew_lease6(struct ipv6_pool *pool, struct iaaddr *addr) {
+ isc_heap_decreased(pool->active_timeouts, addr->heap_index);
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Expire the oldest lease if it's lifetime_end_time is
+ * older than the given time.
+ *
+ * - iaaddr must be a pointer to a (struct iaaddr *) pointer previously
+ * initialized to NULL
+ *
+ * On return iaaddr 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 iaaddr **addr, struct ipv6_pool *pool, time_t now) {
+ struct iaaddr *tmp;
+ isc_result_t insert_result;
+
+ if (addr == NULL) {
+ log_error("%s(%d): NULL pointer reference", MDL);
+ return ISC_R_INVALIDARG;
+ }
+ if (*addr != NULL) {
+ log_error("%s(%d): non-NULL pointer", MDL);
+ return ISC_R_INVALIDARG;
+ }
+
+ if (pool->num_active > 0) {
+ tmp = (struct iaaddr *)isc_heap_element(pool->active_timeouts,
+ 1);
+ if (now > tmp->valid_lifetime_end_time) {
+ insert_result = isc_heap_insert(pool->inactive_timeouts,
+ tmp);
+ if (insert_result != ISC_R_SUCCESS) {
+ return insert_result;
+ }
+ iaaddr_hash_delete(pool->addrs,
+ &tmp->addr, sizeof(tmp->addr), MDL);
+ isc_heap_delete(pool->active_timeouts, 1);
+ tmp->state = FTS_EXPIRED;
+ iaaddr_reference(addr, tmp, MDL);
+ pool->num_active--;
+ }
+ }
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Put the lease on our inactive pool, with the specified state.
+ */
+static isc_result_t
+move_lease_to_inactive(struct ipv6_pool *pool, struct iaaddr *addr,
+ binding_state_t state) {
+ isc_result_t insert_result;
+ int old_heap_index;
+
+ old_heap_index = addr->heap_index;
+ insert_result = isc_heap_insert(pool->inactive_timeouts, addr);
+ if (insert_result == ISC_R_SUCCESS) {
+ iaaddr_hash_delete(pool->addrs,
+ &addr->addr, sizeof(addr->addr), MDL);
+ isc_heap_delete(pool->active_timeouts, old_heap_index);
+ addr->state = state;
+ pool->num_active--;
+ }
+ return insert_result;
+}
+
+/*
+ * 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 iaaddr *addr) {
+ addr->state = FTS_ABANDONED;
+ addr->valid_lifetime_end_time = MAX_TIME;
+ isc_heap_decreased(pool->active_timeouts, addr->heap_index);
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Put the returned lease on our inactive pool.
+ */
+isc_result_t
+release_lease6(struct ipv6_pool *pool, struct iaaddr *addr) {
+ return move_lease_to_inactive(pool, addr, FTS_RELEASED);
+}
+
+/*
+ * Mark an IPv6 address as unavailable from a pool.
+ *
+ * This is used for host entries and the addresses of the server itself.
+ */
+isc_result_t
+mark_address_unavailable(struct ipv6_pool *pool, const struct in6_addr *addr) {
+ struct iaaddr *dummy_iaaddr;
+ isc_result_t result;
+
+ dummy_iaaddr = NULL;
+ result = iaaddr_allocate(&dummy_iaaddr, MDL);
+ if (result == ISC_R_SUCCESS) {
+ dummy_iaaddr->addr = *addr;
+ iaaddr_hash_add(pool->addrs, &dummy_iaaddr->addr,
+ sizeof(*addr), dummy_iaaddr, 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;
+}
+
+
+/*
+ * Remove all leases that have expired from the active pool.
+ */
+void
+expire_leases(time_t now) {
+ struct ipv6_pool *pool;
+ int i;
+ struct iaaddr *addr;
+
+ for (i=0; i<num_pools; i++) {
+ pool = pools[i];
+ for (;;) {
+ addr = NULL;
+ if (expire_lease6(&addr, pool, now) != ISC_R_SUCCESS) {
+ break;
+ }
+ if (addr == NULL) {
+ break;
+ }
+ /* Look to see if there were ddns updates, and if
+ * so, drop them.
+ *
+ * DH: Do we want to do this on a special 'depref'
+ * timer rather than expiration timer?
+ */
+ ddns_removals(NULL, addr);
+ iaaddr_dereference(&addr, MDL);
+ }
+ }
+}
+
+/*
+ * Given an address and the length of the network mask, return
+ * only the network portion.
+ *
+ * Examples:
+ *
+ * "fe80::216:6fff:fe49:7d9b", length 64 = "fe80::"
+ * "2001:888:1936:2:216:6fff:fe49:7d9b", length 48 = "2001:888:1936::"
+ */
+static void
+ipv6_network_portion(struct in6_addr *result,
+ const struct in6_addr *addr, int bits) {
+ unsigned char *addrp;
+ int mask_bits;
+ int bytes;
+ int extra_bits;
+ int i;
+
+ static const unsigned char bitmasks[] = {
+ 0x00, 0xFE, 0xFC, 0xF8,
+ 0xF0, 0xE0, 0xC0, 0x80,
+ };
+
+ /*
+ * Sanity check our bits. ;)
+ */
+ if ((bits < 0) || (bits > 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; i<bytes; i++) {
+ *addrp = 0;
+ addrp--;
+ }
+ if (extra_bits) {
+ *addrp &= bitmasks[extra_bits];
+ }
+}
+
+/*
+ * Determine if the given address is in the pool.
+ */
+isc_boolean_t
+ipv6_addr_in_pool(const struct in6_addr *addr, const struct ipv6_pool *pool) {
+ struct in6_addr tmp;
+
+ ipv6_network_portion(&tmp, addr, pool->bits);
+ 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, const struct in6_addr *addr) {
+ int i;
+
+ if (pool == NULL) {
+ log_error("%s(%d): NULL pointer reference", MDL);
+ return ISC_R_INVALIDARG;
+ }
+ if (*pool != NULL) {
+ log_error("%s(%d): non-NULL pointer", MDL);
+ return ISC_R_INVALIDARG;
+ }
+
+ for (i=0; i<num_pools; i++) {
+ if (ipv6_addr_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_na *ia_na,
+ isc_result_t (*change_func)(struct ipv6_pool *, struct iaaddr*)) {
+ 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; i<ia_na->num_iaaddr; i++) {
+ pool = NULL;
+ addr = &ia_na->iaaddr[i]->addr;
+ if (find_ipv6_pool(&pool, addr) == ISC_R_SUCCESS) {
+ renew_retval = change_func(pool, ia_na->iaaddr[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_NA from all pools.
+ *
+ * The new valid_lifetime_end_time should be updated for the addresses.
+ *
+ * WARNING: lease times must only be extended, never reduced!!!
+ */
+isc_result_t
+renew_leases(struct ia_na *ia_na) {
+ return change_leases(ia_na, renew_lease6);
+}
+
+/*
+ * Release all leases in an IA_NA from all pools.
+ */
+isc_result_t
+release_leases(struct ia_na *ia_na) {
+ return change_leases(ia_na, release_lease6);
+}
+
+/*
+ * Decline all leases in an IA_NA from all pools.
+ */
+isc_result_t
+decline_leases(struct ia_na *ia_na) {
+ return change_leases(ia_na, decline_lease6);
+}
+
+/*
+ * Helper function to output leases.
+ */
+static int write_error;
+
+static isc_result_t
+write_ia_na_leases(const void *name, unsigned len, void *value) {
+ struct ia_na *ia_na = (struct ia_na *)value;
+
+ if (!write_error) {
+ if (!write_ia_na(ia_na)) {
+ write_error = 1;
+ }
+ }
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Write all DHCPv6 information.
+ */
+int
+write_leases6(void) {
+ write_error = 0;
+ write_server_duid();
+ iaaddr_hash_foreach(ia_active, write_ia_na_leases);
+ if (write_error) {
+ return 0;
+ }
+ return 1;
+}
+
+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, &addr) == ISC_R_SUCCESS) {
+ mark_address_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);
+}
+
+void
+mark_interfaces_unavailable(void) {
+ struct interface_info *ip;
+ int i;
+ struct ipv6_pool *p;
+
+ ip = interfaces;
+ while (ip != NULL) {
+ for (i=0; i<ip->v6address_count; i++) {
+ p = NULL;
+ if (find_ipv6_pool(&p, &ip->v6addresses[i])
+ == ISC_R_SUCCESS) {
+ mark_address_unavailable(p,
+ &ip->v6addresses[i]);
+ ipv6_pool_dereference(&p, MDL);
+ }
+ }
+ ip = ip->next;
+ }
+}
+
+
+#ifdef UNIT_TEST
+#include <stdlib.h>
+
+int
+main(int argc, char *argv[]) {
+ struct iaaddr *iaaddr;
+ struct iaaddr *iaaddr_copy;
+ u_int32_t iaid;
+ struct ia_na *ia_na;
+ struct ia_na *ia_na_copy;
+ int i;
+ 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 iaaddr *expired_iaaddr;
+ unsigned int attempts;
+
+ /*
+ * Test 0: Basic iaaddr manipulation.
+ */
+ iaaddr = NULL;
+ if (iaaddr_allocate(&iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iaaddr_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+ if (iaaddr->state != FTS_FREE) {
+ printf("ERROR: bad state %s:%d\n", MDL);
+ return 1;
+ }
+ if (iaaddr->heap_index != -1) {
+ printf("ERROR: bad heap_index %s:%d\n", MDL);
+ return 1;
+ }
+ iaaddr_copy = NULL;
+ if (iaaddr_reference(&iaaddr_copy, iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iaaddr_reference() %s:%d\n", MDL);
+ return 1;
+ }
+ if (iaaddr_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iaaddr_reference() %s:%d\n", MDL);
+ return 1;
+ }
+ if (iaaddr_dereference(&iaaddr_copy, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iaaddr_reference() %s:%d\n", MDL);
+ return 1;
+ }
+
+ /*
+ * Test 1: Error iaaddr manipulation.
+ */
+ /* bogus allocate arguments */
+ if (iaaddr_allocate(NULL, MDL) != ISC_R_INVALIDARG) {
+ printf("ERROR: iaaddr_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+ iaaddr = (struct iaaddr *)1;
+ if (iaaddr_allocate(&iaaddr, MDL) != ISC_R_INVALIDARG) {
+ printf("ERROR: iaaddr_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+
+ /* bogus reference arguments */
+ iaaddr = NULL;
+ if (iaaddr_allocate(&iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iaaddr_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+ if (iaaddr_reference(NULL, iaaddr, MDL) != ISC_R_INVALIDARG) {
+ printf("ERROR: iaaddr_reference() %s:%d\n", MDL);
+ return 1;
+ }
+ iaaddr_copy = (struct iaaddr *)1;
+ if (iaaddr_reference(&iaaddr_copy, iaaddr, MDL) != ISC_R_INVALIDARG) {
+ printf("ERROR: iaaddr_reference() %s:%d\n", MDL);
+ return 1;
+ }
+ iaaddr_copy = NULL;
+ if (iaaddr_reference(&iaaddr_copy, NULL, MDL) != ISC_R_INVALIDARG) {
+ printf("ERROR: iaaddr_reference() %s:%d\n", MDL);
+ return 1;
+ }
+ if (iaaddr_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iaaddr_reference() %s:%d\n", MDL);
+ return 1;
+ }
+
+ /* bogus dereference arguments */
+ if (iaaddr_dereference(NULL, MDL) != ISC_R_INVALIDARG) {
+ printf("ERROR: iaaddr_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+ iaaddr = NULL;
+ if (iaaddr_dereference(&iaaddr, MDL) != ISC_R_INVALIDARG) {
+ printf("ERROR: iaaddr_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+
+ /*
+ * Test 2: Basic ia_na manipulation.
+ */
+ iaid = 666;
+ ia_na = NULL;
+ if (ia_na_allocate(&ia_na, iaid, "TestDUID", 8, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ia_na_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+ if (memcmp(ia_na->iaid_duid.data, &iaid, sizeof(iaid)) != 0) {
+ printf("ERROR: bad IAID_DUID %s:%d\n", MDL);
+ return 1;
+ }
+ if (memcmp(ia_na->iaid_duid.data+sizeof(iaid), "TestDUID", 8) != 0) {
+ printf("ERROR: bad IAID_DUID %s:%d\n", MDL);
+ return 1;
+ }
+ if (ia_na->num_iaaddr != 0) {
+ printf("ERROR: bad num_iaaddr %s:%d\n", MDL);
+ return 1;
+ }
+ ia_na_copy = NULL;
+ if (ia_na_reference(&ia_na_copy, ia_na, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ia_na_reference() %s:%d\n", MDL);
+ return 1;
+ }
+ iaaddr = NULL;
+ if (iaaddr_allocate(&iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iaaddr_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+ if (ia_na_add_iaaddr(ia_na, iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ia_na_add_iaaddr() %s:%d\n", MDL);
+ return 1;
+ }
+ ia_na_remove_iaaddr(ia_na, iaaddr, MDL);
+ if (iaaddr_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iaaddr_reference() %s:%d\n", MDL);
+ return 1;
+ }
+ if (ia_na_dereference(&ia_na, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ia_na_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+ if (ia_na_dereference(&ia_na_copy, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ia_na_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+
+ /*
+ * Test 3: lots of iaaddr in our ia_na
+ */
+
+ /* lots of iaaddr that we delete */
+ iaid = 666;
+ ia_na = NULL;
+ if (ia_na_allocate(&ia_na, iaid, "TestDUID", 8, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ia_na_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+ for (i=0; i<100; i++) {
+ iaaddr = NULL;
+ if (iaaddr_allocate(&iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iaaddr_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+ if (ia_na_add_iaaddr(ia_na, iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ia_na_add_iaaddr() %s:%d\n", MDL);
+ return 1;
+ }
+ if (iaaddr_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iaaddr_reference() %s:%d\n", MDL);
+ return 1;
+ }
+ }
+ for (i=0; i<100; i++) {
+ iaaddr = ia_na->iaaddr[random() % ia_na->num_iaaddr];
+ ia_na_remove_iaaddr(ia_na, iaaddr, MDL);
+ }
+ if (ia_na_dereference(&ia_na, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ia_na_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+
+ /* lots of iaaddr, let dereference cleanup */
+ iaid = 666;
+ ia_na = NULL;
+ if (ia_na_allocate(&ia_na, iaid, "TestDUID", 8, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ia_na_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+ for (i=0; i<100; i++) {
+ iaaddr = NULL;
+ if (iaaddr_allocate(&iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iaaddr_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+ if (ia_na_add_iaaddr(ia_na, iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ia_na_add_iaaddr() %s:%d\n", MDL);
+ return 1;
+ }
+ if (iaaddr_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iaaddr_reference() %s:%d\n", MDL);
+ return 1;
+ }
+ }
+ if (ia_na_dereference(&ia_na, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ia_na_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+
+ /*
+ * Test 4: Errors in ia_na.
+ */
+ /* bogus allocate arguments */
+ if (ia_na_allocate(NULL, 123, "", 0, MDL) != ISC_R_INVALIDARG) {
+ printf("ERROR: ia_na_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+ ia_na = (struct ia_na *)1;
+ if (ia_na_allocate(&ia_na, 456, "", 0, MDL) != ISC_R_INVALIDARG) {
+ printf("ERROR: ia_na_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+
+ /* bogus reference arguments */
+ iaid = 666;
+ ia_na = NULL;
+ if (ia_na_allocate(&ia_na, iaid, "TestDUID", 8, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ia_na_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+ if (ia_na_reference(NULL, ia_na, MDL) != ISC_R_INVALIDARG) {
+ printf("ERROR: ia_na_reference() %s:%d\n", MDL);
+ return 1;
+ }
+ ia_na_copy = (struct ia_na *)1;
+ if (ia_na_reference(&ia_na_copy, ia_na, MDL) != ISC_R_INVALIDARG) {
+ printf("ERROR: ia_na_reference() %s:%d\n", MDL);
+ return 1;
+ }
+ ia_na_copy = NULL;
+ if (ia_na_reference(&ia_na_copy, NULL, MDL) != ISC_R_INVALIDARG) {
+ printf("ERROR: ia_na_reference() %s:%d\n", MDL);
+ return 1;
+ }
+ if (ia_na_dereference(&ia_na, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ia_na_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+
+ /* bogus dereference arguments */
+ if (ia_na_dereference(NULL, MDL) != ISC_R_INVALIDARG) {
+ printf("ERROR: ia_na_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+
+ /* bogus remove */
+ iaid = 666;
+ ia_na = NULL;
+ if (ia_na_allocate(&ia_na, iaid, "TestDUID", 8, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ia_na_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+ ia_na_remove_iaaddr(ia_na, NULL, MDL);
+ if (ia_na_dereference(&ia_na, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ia_na_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+
+ /*
+ * Test 5: Basic ipv6_pool manipulation.
+ */
+
+ /* allocate, reference */
+ inet_pton(AF_INET6, "1:2:3:4::", &addr);
+ pool = NULL;
+ if (ipv6_pool_allocate(&pool, &addr, 64, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ipv6_pool_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+ if (pool->num_active != 0) {
+ printf("ERROR: bad num_active %s:%d\n", MDL);
+ return 1;
+ }
+ if (pool->bits != 64) {
+ printf("ERROR: bad bits %s:%d\n", MDL);
+ return 1;
+ }
+ 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) {
+ printf("ERROR: bad start_addr %s:%d\n", MDL);
+ return 1;
+ }
+ pool_copy = NULL;
+ if (ipv6_pool_reference(&pool_copy, pool, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ipv6_pool_reference() %s:%d\n", MDL);
+ return 1;
+ }
+
+ /* activate_lease6, renew_lease6, expire_lease6 */
+ uid = "client0";
+ memset(&ds, 0, sizeof(ds));
+ ds.len = strlen(uid);
+ if (!buffer_allocate(&ds.buffer, ds.len, MDL)) {
+ printf("Out of memory\n");
+ return 1;
+ }
+ ds.data = ds.buffer->data;
+ memcpy((char *)ds.data, uid, ds.len);
+ if (activate_lease6(pool, &iaaddr,
+ &attempts, &ds, 1) != ISC_R_SUCCESS) {
+ printf("ERROR: activate_lease6() %s:%d\n", MDL);
+ return 1;
+ }
+ if (pool->num_active != 1) {
+ printf("ERROR: bad num_active %s:%d\n", MDL);
+ return 1;
+ }
+ if (renew_lease6(pool, iaaddr) != ISC_R_SUCCESS) {
+ printf("ERROR: renew_lease6() %s:%d\n", MDL);
+ return 1;
+ }
+ if (pool->num_active != 1) {
+ printf("ERROR: bad num_active %s:%d\n", MDL);
+ return 1;
+ }
+ expired_iaaddr = NULL;
+ if (expire_lease6(&expired_iaaddr, pool, 0) != ISC_R_SUCCESS) {
+ printf("ERROR: expire_lease6() %s:%d\n", MDL);
+ return 1;
+ }
+ if (expired_iaaddr != NULL) {
+ printf("ERROR: should not have expired a lease %s:%d\n", MDL);
+ return 1;
+ }
+ if (pool->num_active != 1) {
+ printf("ERROR: bad num_active %s:%d\n", MDL);
+ return 1;
+ }
+ if (expire_lease6(&expired_iaaddr, pool, 1000) != ISC_R_SUCCESS) {
+ printf("ERROR: expire_lease6() %s:%d\n", MDL);
+ return 1;
+ }
+ if (expired_iaaddr == NULL) {
+ printf("ERROR: should have expired a lease %s:%d\n", MDL);
+ return 1;
+ }
+ if (iaaddr_dereference(&expired_iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iaaddr_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+ if (pool->num_active != 0) {
+ printf("ERROR: bad num_active %s:%d\n", MDL);
+ return 1;
+ }
+ if (iaaddr_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iaaddr_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+
+ /* release_lease6, decline_lease6 */
+ if (activate_lease6(pool, &iaaddr, &attempts,
+ &ds, 1) != ISC_R_SUCCESS) {
+ printf("ERROR: activate_lease6() %s:%d\n", MDL);
+ return 1;
+ }
+ if (pool->num_active != 1) {
+ printf("ERROR: bad num_active %s:%d\n", MDL);
+ return 1;
+ }
+ if (release_lease6(pool, iaaddr) != ISC_R_SUCCESS) {
+ printf("ERROR: decline_lease6() %s:%d\n", MDL);
+ return 1;
+ }
+ if (pool->num_active != 0) {
+ printf("ERROR: bad num_active %s:%d\n", MDL);
+ return 1;
+ }
+ if (iaaddr_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iaaddr_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+ if (activate_lease6(pool, &iaaddr, &attempts,
+ &ds, 1) != ISC_R_SUCCESS) {
+ printf("ERROR: activate_lease6() %s:%d\n", MDL);
+ return 1;
+ }
+ if (pool->num_active != 1) {
+ printf("ERROR: bad num_active %s:%d\n", MDL);
+ return 1;
+ }
+ if (decline_lease6(pool, iaaddr) != ISC_R_SUCCESS) {
+ printf("ERROR: decline_lease6() %s:%d\n", MDL);
+ return 1;
+ }
+ if (pool->num_active != 1) {
+ printf("ERROR: bad num_active %s:%d\n", MDL);
+ return 1;
+ }
+ if (iaaddr_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iaaddr_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+
+ /* dereference */
+ if (ipv6_pool_dereference(&pool, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ipv6_pool_reference() %s:%d\n", MDL);
+ return 1;
+ }
+ if (ipv6_pool_dereference(&pool_copy, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ipv6_pool_reference() %s:%d\n", MDL);
+ return 1;
+ }
+
+ /*
+ * Test 6: Error ipv6_pool manipulation
+ */
+ if (ipv6_pool_allocate(NULL, &addr, 64, MDL) != ISC_R_INVALIDARG) {
+ printf("ERROR: ipv6_pool_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+ pool = (struct ipv6_pool *)1;
+ if (ipv6_pool_allocate(&pool, &addr, 64, MDL) != ISC_R_INVALIDARG) {
+ printf("ERROR: ipv6_pool_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+ if (ipv6_pool_reference(NULL, pool, MDL) != ISC_R_INVALIDARG) {
+ printf("ERROR: ipv6_pool_reference() %s:%d\n", MDL);
+ return 1;
+ }
+ pool_copy = (struct ipv6_pool *)1;
+ if (ipv6_pool_reference(&pool_copy, pool, MDL) != ISC_R_INVALIDARG) {
+ printf("ERROR: ipv6_pool_reference() %s:%d\n", MDL);
+ return 1;
+ }
+ pool_copy = NULL;
+ if (ipv6_pool_reference(&pool_copy, NULL, MDL) != ISC_R_INVALIDARG) {
+ printf("ERROR: ipv6_pool_reference() %s:%d\n", MDL);
+ return 1;
+ }
+ if (ipv6_pool_dereference(NULL, MDL) != ISC_R_INVALIDARG) {
+ printf("ERROR: ipv6_pool_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+ if (ipv6_pool_dereference(&pool_copy, MDL) != ISC_R_INVALIDARG) {
+ printf("ERROR: ipv6_pool_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+
+ /*
+ * Test 7: order of expiration
+ */
+ pool = NULL;
+ if (ipv6_pool_allocate(&pool, &addr, 64, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ipv6_pool_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+ for (i=10; i<100; i+=10) {
+ if (activate_lease6(pool, &iaaddr, &attempts,
+ &ds, i) != ISC_R_SUCCESS) {
+ printf("ERROR: activate_lease6() %s:%d\n", MDL);
+ return 1;
+ }
+ if (iaaddr_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iaaddr_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+ if (pool->num_active != (i / 10)) {
+ printf("ERROR: bad num_active %s:%d\n", MDL);
+ return 1;
+ }
+ }
+ if (pool->num_active != 9) {
+ printf("ERROR: bad num_active %s:%d\n", MDL);
+ return 1;
+ }
+ for (i=10; i<100; i+=10) {
+ if (expire_lease6(&expired_iaaddr,
+ pool, 1000) != ISC_R_SUCCESS) {
+ printf("ERROR: expire_lease6() %s:%d\n", MDL);
+ return 1;
+ }
+ if (expired_iaaddr == NULL) {
+ printf("ERROR: should have expired a lease %s:%d\n",
+ MDL);
+ return 1;
+ }
+ if (pool->num_active != (9 - (i / 10))) {
+ printf("ERROR: bad num_active %s:%d\n", MDL);
+ return 1;
+ }
+ if (expired_iaaddr->valid_lifetime_end_time != i) {
+ printf("ERROR: bad valid_lifetime_end_time %s:%d\n",
+ MDL);
+ return 1;
+ }
+ if (iaaddr_dereference(&expired_iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iaaddr_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+ }
+ if (pool->num_active != 0) {
+ printf("ERROR: bad num_active %s:%d\n", MDL);
+ return 1;
+ }
+ expired_iaaddr = NULL;
+ if (expire_lease6(&expired_iaaddr, pool, 1000) != ISC_R_SUCCESS) {
+ printf("ERROR: expire_lease6() %s:%d\n", MDL);
+ return 1;
+ }
+ if (ipv6_pool_dereference(&pool, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ipv6_pool_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+
+ /*
+ * Test 8: small pool
+ */
+ pool = NULL;
+ if (ipv6_pool_allocate(&pool, &addr, 127, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ipv6_pool_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+ if (activate_lease6(pool, &iaaddr, &attempts,
+ &ds, 42) != ISC_R_SUCCESS) {
+ printf("ERROR: activate_lease6() %s:%d\n", MDL);
+ return 1;
+ }
+ if (iaaddr_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iaaddr_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+ if (activate_lease6(pool, &iaaddr, &attempts,
+ &ds, 11) != ISC_R_SUCCESS) {
+ printf("ERROR: activate_lease6() %s:%d\n", MDL);
+ return 1;
+ }
+ if (iaaddr_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iaaddr_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+ if (activate_lease6(pool, &iaaddr, &attempts,
+ &ds, 11) != ISC_R_NORESOURCES) {
+ printf("ERROR: activate_lease6() %s:%d\n", MDL);
+ return 1;
+ }
+ if (ipv6_pool_dereference(&pool, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ipv6_pool_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+
+ /*
+ * Test 9: functions across all pools
+ */
+ pool = NULL;
+ if (ipv6_pool_allocate(&pool, &addr, 64, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ipv6_pool_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+ if (add_ipv6_pool(pool) != ISC_R_SUCCESS) {
+ printf("ERROR: add_ipv6_pool() %s:%d\n", MDL);
+ return 1;
+ }
+ if (ipv6_pool_dereference(&pool, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ipv6_pool_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+ pool = NULL;
+ if (find_ipv6_pool(&pool, &addr) != ISC_R_SUCCESS) {
+ printf("ERROR: find_ipv6_pool() %s:%d\n", MDL);
+ return 1;
+ }
+ if (ipv6_pool_dereference(&pool, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ipv6_pool_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+ inet_pton(AF_INET6, "1:2:3:4:ffff:ffff:ffff:ffff", &addr);
+ pool = NULL;
+ if (find_ipv6_pool(&pool, &addr) != ISC_R_SUCCESS) {
+ printf("ERROR: find_ipv6_pool() %s:%d\n", MDL);
+ return 1;
+ }
+ if (ipv6_pool_dereference(&pool, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ipv6_pool_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+ inet_pton(AF_INET6, "1:2:3:5::", &addr);
+ pool = NULL;
+ if (find_ipv6_pool(&pool, &addr) != ISC_R_NOTFOUND) {
+ printf("ERROR: find_ipv6_pool() %s:%d\n", MDL);
+ return 1;
+ }
+ inet_pton(AF_INET6, "1:2:3:3:ffff:ffff:ffff:ffff", &addr);
+ pool = NULL;
+ if (find_ipv6_pool(&pool, &addr) != ISC_R_NOTFOUND) {
+ printf("ERROR: find_ipv6_pool() %s:%d\n", MDL);
+ return 1;
+ }
+
+/* iaid = 666;
+ ia_na = NULL;
+ if (ia_na_allocate(&ia_na, iaid, "TestDUID", 8, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ia_na_allocate() %s:%d\n", MDL);
+ return 1;
+ }*/
+
+ printf("SUCCESS: all tests passed (ignore any warning messages)\n");
+ return 0;
+}
+#endif
diff --git a/server/stables.c b/server/stables.c
index 3d68e180..2ac63421 100644
--- a/server/stables.c
+++ b/server/stables.c
@@ -34,7 +34,7 @@
#ifndef lint
static char copyright[] =
-"$Id: stables.c,v 1.37 2007/04/27 22:48:00 each Exp $ Copyright (c) 2004-2007 Internet Systems Consortium. All rights reserved.\n";
+"$Id: stables.c,v 1.38 2007/05/08 23:05:22 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
@@ -238,6 +238,7 @@ static struct option server_options[] = {
{ "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 },
{ NULL, NULL, NULL, 0, 0 }
};
@@ -250,7 +251,7 @@ struct enumeration_value ddns_styles_values [] = {
struct enumeration ddns_styles = {
(struct enumeration *)0,
- "ddns-styles",
+ "ddns-styles", 1,
ddns_styles_values
};
@@ -320,7 +321,7 @@ struct enumeration_value syslog_values [] = {
struct enumeration syslog_enum = {
(struct enumeration *)0,
- "syslog-facilities",
+ "syslog-facilities", 1,
syslog_values
};
@@ -331,6 +332,7 @@ void initialize_server_option_spaces()
/* 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;
@@ -372,6 +374,7 @@ void initialize_server_option_spaces()
/* 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;
diff --git a/tests/DHCPv6/000-badmsgtype.pl b/tests/DHCPv6/000-badmsgtype.pl
new file mode 100644
index 00000000..c6dc202b
--- /dev/null
+++ b/tests/DHCPv6/000-badmsgtype.pl
@@ -0,0 +1,144 @@
+#! /usr/bin/perl -w
+
+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";
+
diff --git a/tests/DHCPv6/010-solicit-noclientid.pl b/tests/DHCPv6/010-solicit-noclientid.pl
new file mode 100644
index 00000000..89f4bc47
--- /dev/null
+++ b/tests/DHCPv6/010-solicit-noclientid.pl
@@ -0,0 +1,192 @@
+#! /usr/bin/perl -w
+
+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";
+
diff --git a/tests/DHCPv6/011-solicit-serverid.pl b/tests/DHCPv6/011-solicit-serverid.pl
new file mode 100644
index 00000000..f77fd42e
--- /dev/null
+++ b/tests/DHCPv6/011-solicit-serverid.pl
@@ -0,0 +1,195 @@
+#! /usr/bin/perl -w
+
+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";
+
diff --git a/tests/DHCPv6/020-advertise-mcast.pl b/tests/DHCPv6/020-advertise-mcast.pl
new file mode 100644
index 00000000..b1ee8e6f
--- /dev/null
+++ b/tests/DHCPv6/020-advertise-mcast.pl
@@ -0,0 +1,144 @@
+#! /usr/bin/perl -w
+
+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";
+
diff --git a/tests/DHCPv6/030-request-noclientid.pl b/tests/DHCPv6/030-request-noclientid.pl
new file mode 100644
index 00000000..e7460426
--- /dev/null
+++ b/tests/DHCPv6/030-request-noclientid.pl
@@ -0,0 +1,192 @@
+#! /usr/bin/perl -w
+
+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";
+
diff --git a/tests/DHCPv6/031-request-noserverid.pl b/tests/DHCPv6/031-request-noserverid.pl
new file mode 100644
index 00000000..0a2a94b2
--- /dev/null
+++ b/tests/DHCPv6/031-request-noserverid.pl
@@ -0,0 +1,192 @@
+#! /usr/bin/perl -w
+
+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";
+
diff --git a/tests/DHCPv6/032-request-badduid.pl b/tests/DHCPv6/032-request-badduid.pl
new file mode 100644
index 00000000..bb61952e
--- /dev/null
+++ b/tests/DHCPv6/032-request-badduid.pl
@@ -0,0 +1,196 @@
+#! /usr/bin/perl -w
+
+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";
+
diff --git a/tests/DHCPv6/110-information-request-ia_na.pl b/tests/DHCPv6/110-information-request-ia_na.pl
new file mode 100644
index 00000000..03a44ecd
--- /dev/null
+++ b/tests/DHCPv6/110-information-request-ia_na.pl
@@ -0,0 +1,192 @@
+#! /usr/bin/perl -w
+
+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";
+
diff --git a/tests/DHCPv6/111-information-request-ia_ta.pl b/tests/DHCPv6/111-information-request-ia_ta.pl
new file mode 100644
index 00000000..aa9eba28
--- /dev/null
+++ b/tests/DHCPv6/111-information-request-ia_ta.pl
@@ -0,0 +1,192 @@
+#! /usr/bin/perl -w
+
+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";
+
diff --git a/tests/DHCPv6/112-badduid.pl b/tests/DHCPv6/112-badduid.pl
new file mode 100644
index 00000000..41ccdeb3
--- /dev/null
+++ b/tests/DHCPv6/112-badduid.pl
@@ -0,0 +1,188 @@
+#! /usr/bin/perl -w
+
+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";
+
diff --git a/tests/DHCPv6/210-solicit-nohost.pl b/tests/DHCPv6/210-solicit-nohost.pl
new file mode 100644
index 00000000..5ca80250
--- /dev/null
+++ b/tests/DHCPv6/210-solicit-nohost.pl
@@ -0,0 +1,192 @@
+#! /usr/bin/perl -w
+
+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";
+
diff --git a/tests/DHCPv6/211-solicit-opt-in-na.pl b/tests/DHCPv6/211-solicit-opt-in-na.pl
new file mode 100644
index 00000000..2dfbbcc9
--- /dev/null
+++ b/tests/DHCPv6/211-solicit-opt-in-na.pl
@@ -0,0 +1,198 @@
+#! /usr/bin/perl -w
+
+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";
+
diff --git a/tests/DHCPv6/212-solicit-opt-in-na-norapidcommit.pl b/tests/DHCPv6/212-solicit-opt-in-na-norapidcommit.pl
new file mode 100644
index 00000000..f7517eb5
--- /dev/null
+++ b/tests/DHCPv6/212-solicit-opt-in-na-norapidcommit.pl
@@ -0,0 +1,198 @@
+#! /usr/bin/perl -w
+
+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";
+
diff --git a/tests/DHCPv6/280-release-nohost.pl b/tests/DHCPv6/280-release-nohost.pl
new file mode 100644
index 00000000..2b84481d
--- /dev/null
+++ b/tests/DHCPv6/280-release-nohost.pl
@@ -0,0 +1,155 @@
+#! /usr/bin/perl -w
+
+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";
+
diff --git a/tests/DHCPv6/281-release-bad-address.pl b/tests/DHCPv6/281-release-bad-address.pl
new file mode 100644
index 00000000..635e701e
--- /dev/null
+++ b/tests/DHCPv6/281-release-bad-address.pl
@@ -0,0 +1,167 @@
+#! /usr/bin/perl -w
+
+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";
+
diff --git a/tests/DHCPv6/282-release-no-address.pl b/tests/DHCPv6/282-release-no-address.pl
new file mode 100644
index 00000000..26f06b7d
--- /dev/null
+++ b/tests/DHCPv6/282-release-no-address.pl
@@ -0,0 +1,163 @@
+#! /usr/bin/perl -w
+
+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";
+
diff --git a/tests/DHCPv6/283-release.pl b/tests/DHCPv6/283-release.pl
new file mode 100644
index 00000000..8d82a30c
--- /dev/null
+++ b/tests/DHCPv6/283-release.pl
@@ -0,0 +1,169 @@
+#! /usr/bin/perl -w
+
+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";
+
diff --git a/tests/DHCPv6/290-decline-nohost.pl b/tests/DHCPv6/290-decline-nohost.pl
new file mode 100644
index 00000000..4d8e8893
--- /dev/null
+++ b/tests/DHCPv6/290-decline-nohost.pl
@@ -0,0 +1,155 @@
+#! /usr/bin/perl -w
+
+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";
+
diff --git a/tests/DHCPv6/291-decline-bad-address.pl b/tests/DHCPv6/291-decline-bad-address.pl
new file mode 100644
index 00000000..1ebd2992
--- /dev/null
+++ b/tests/DHCPv6/291-decline-bad-address.pl
@@ -0,0 +1,167 @@
+#! /usr/bin/perl -w
+
+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";
+
diff --git a/tests/DHCPv6/292-decline-no-address.pl b/tests/DHCPv6/292-decline-no-address.pl
new file mode 100644
index 00000000..f9ed7177
--- /dev/null
+++ b/tests/DHCPv6/292-decline-no-address.pl
@@ -0,0 +1,163 @@
+#! /usr/bin/perl -w
+
+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";
+
diff --git a/tests/DHCPv6/293-decline.pl b/tests/DHCPv6/293-decline.pl
new file mode 100644
index 00000000..bb7d06e4
--- /dev/null
+++ b/tests/DHCPv6/293-decline.pl
@@ -0,0 +1,169 @@
+#! /usr/bin/perl -w
+
+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";
+
diff --git a/tests/DHCPv6/README b/tests/DHCPv6/README
new file mode 100644
index 00000000..c559de24
--- /dev/null
+++ b/tests/DHCPv6/README
@@ -0,0 +1,62 @@
+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 -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.
+
diff --git a/tests/DHCPv6/dhcp_client.pm b/tests/DHCPv6/dhcp_client.pm
new file mode 100644
index 00000000..85ff0cc3
--- /dev/null
+++ b/tests/DHCPv6/dhcp_client.pm
@@ -0,0 +1,435 @@
+#! /usr/bin/perl -w
+
+
+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<length($data); $i++) {
+ printf ':%02X', ord(substr($data, $i, 1));
+ }
+ }
+ print "\n";
+ } elsif ($num == $dhcp_client::OPT_IA_NA) {
+ printf "${indent} IAID: 0x\%08X\n",
+ unpack("N", substr($data, 0, 4));
+ printf "${indent} T1: \%d\n", unpack("N", substr($data, 4, 4));
+ printf "${indent} T2: \%d\n", unpack("N", substr($data, 8, 4));
+ if (length($data) > 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;
+}
+
diff --git a/tests/DHCPv6/stubcli-opt-in-na.pl b/tests/DHCPv6/stubcli-opt-in-na.pl
new file mode 100644
index 00000000..f400031b
--- /dev/null
+++ b/tests/DHCPv6/stubcli-opt-in-na.pl
@@ -0,0 +1,197 @@
+#! /usr/bin/perl -w
+
+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";
+
diff --git a/tests/DHCPv6/stubcli.pl b/tests/DHCPv6/stubcli.pl
new file mode 100644
index 00000000..5ca80250
--- /dev/null
+++ b/tests/DHCPv6/stubcli.pl
@@ -0,0 +1,192 @@
+#! /usr/bin/perl -w
+
+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";
+
diff --git a/tests/DHCPv6/test-a.conf b/tests/DHCPv6/test-a.conf
new file mode 100644
index 00000000..9fe42364
--- /dev/null
+++ b/tests/DHCPv6/test-a.conf
@@ -0,0 +1,67 @@
+#
+# 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 {
+}
+
+
diff --git a/tests/DHCPv6/test-b.conf b/tests/DHCPv6/test-b.conf
new file mode 100644
index 00000000..4b673806
--- /dev/null
+++ b/tests/DHCPv6/test-b.conf
@@ -0,0 +1,60 @@
+#
+# 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;
+}
+
+