summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--usr.sbin/dhcpd/Makefile13
-rw-r--r--usr.sbin/dhcpd/alloc.c344
-rw-r--r--usr.sbin/dhcpd/bootp.c369
-rw-r--r--usr.sbin/dhcpd/bpf.c321
-rw-r--r--usr.sbin/dhcpd/cdefs.h57
-rw-r--r--usr.sbin/dhcpd/conflex.c536
-rw-r--r--usr.sbin/dhcpd/confpars.c1372
-rw-r--r--usr.sbin/dhcpd/convert.c113
-rw-r--r--usr.sbin/dhcpd/db.c255
-rw-r--r--usr.sbin/dhcpd/dhcp.c1732
-rw-r--r--usr.sbin/dhcpd/dhcp.h168
-rw-r--r--usr.sbin/dhcpd/dhcpd.8399
-rw-r--r--usr.sbin/dhcpd/dhcpd.c370
-rw-r--r--usr.sbin/dhcpd/dhcpd.cat8257
-rw-r--r--usr.sbin/dhcpd/dhcpd.conf90
-rw-r--r--usr.sbin/dhcpd/dhcpd.conf.5937
-rw-r--r--usr.sbin/dhcpd/dhcpd.conf.cat5618
-rw-r--r--usr.sbin/dhcpd/dhcpd.h1110
-rw-r--r--usr.sbin/dhcpd/dhcpd.leases.5222
-rw-r--r--usr.sbin/dhcpd/dhcpd.leases.cat5134
-rw-r--r--usr.sbin/dhcpd/dhctoken.h136
-rw-r--r--usr.sbin/dhcpd/dispatch.c668
-rw-r--r--usr.sbin/dhcpd/errwarn.c234
-rw-r--r--usr.sbin/dhcpd/hash.c159
-rw-r--r--usr.sbin/dhcpd/hash.h56
-rw-r--r--usr.sbin/dhcpd/icmp.c170
-rw-r--r--usr.sbin/dhcpd/inet.c184
-rw-r--r--usr.sbin/dhcpd/inet.h52
-rw-r--r--usr.sbin/dhcpd/memory.c942
-rw-r--r--usr.sbin/dhcpd/options.c742
-rw-r--r--usr.sbin/dhcpd/osdep.h221
-rw-r--r--usr.sbin/dhcpd/packet.c255
-rw-r--r--usr.sbin/dhcpd/parse.c642
-rw-r--r--usr.sbin/dhcpd/print.c198
-rw-r--r--usr.sbin/dhcpd/site.h100
-rw-r--r--usr.sbin/dhcpd/sysconf.h52
-rw-r--r--usr.sbin/dhcpd/tables.c687
-rw-r--r--usr.sbin/dhcpd/tree.c402
-rw-r--r--usr.sbin/dhcpd/tree.h107
-rw-r--r--usr.sbin/dhcpd/version.h3
40 files changed, 15427 insertions, 0 deletions
diff --git a/usr.sbin/dhcpd/Makefile b/usr.sbin/dhcpd/Makefile
new file mode 100644
index 00000000000..38d616991fe
--- /dev/null
+++ b/usr.sbin/dhcpd/Makefile
@@ -0,0 +1,13 @@
+# $OpenBSD: Makefile,v 1.1 2004/04/13 23:41:48 henning Exp $
+
+.include <bsd.own.mk>
+
+SRCS= bootp.c confpars.c db.c dhcp.c dhcpd.c bpf.c packet.c errwarn.c \
+ dispatch.c print.c memory.c options.c inet.c conflex.c parse.c \
+ alloc.c tables.c tree.c hash.c convert.c icmp.c
+PROG= dhcpd
+MAN= dhcpd.8 dhcpd.conf.5 dhcpd.leases.5
+
+CFLAGS+=-Wall
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/dhcpd/alloc.c b/usr.sbin/dhcpd/alloc.c
new file mode 100644
index 00000000000..ad976a33d25
--- /dev/null
+++ b/usr.sbin/dhcpd/alloc.c
@@ -0,0 +1,344 @@
+/* alloc.c
+
+ Memory allocation... */
+
+/*
+ * Copyright (c) 1995, 1996, 1998 The Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include "dhcpd.h"
+
+struct dhcp_packet *dhcp_free_list;
+struct packet *packet_free_list;
+
+void * dmalloc(size, name)
+ int size;
+ char *name;
+{
+ void *foo = calloc(size, sizeof(char));
+ if (!foo)
+ warn ("No memory for %s.", name);
+ return foo;
+}
+
+void dfree(ptr, name)
+ void *ptr;
+ char *name;
+{
+ if (!ptr) {
+ warn ("dfree %s: free on null pointer.", name);
+ return;
+ }
+ free (ptr);
+}
+
+struct packet *new_packet(name)
+ char *name;
+{
+ struct packet *rval;
+ rval = (struct packet *)dmalloc(sizeof(struct packet), name);
+ return rval;
+}
+
+struct dhcp_packet *new_dhcp_packet(name)
+ char *name;
+{
+ struct dhcp_packet *rval;
+ rval = (struct dhcp_packet *)dmalloc(sizeof(struct dhcp_packet),
+ name);
+ return rval;
+}
+
+struct tree *new_tree(name)
+ char *name;
+{
+ struct tree *rval = dmalloc(sizeof(struct tree), name);
+ return rval;
+}
+
+struct string_list *new_string_list(size, name)
+ size_t size;
+ char * name;
+{
+ struct string_list *rval;
+
+ rval =dmalloc(sizeof(struct string_list) + size, name);
+ if (rval != NULL)
+ rval->string = ((char *)rval) + sizeof(struct string_list);
+ return rval;
+}
+
+struct tree_cache *free_tree_caches;
+
+struct tree_cache *new_tree_cache(name)
+ char *name;
+{
+ struct tree_cache *rval;
+
+ if (free_tree_caches) {
+ rval = free_tree_caches;
+ free_tree_caches =
+ (struct tree_cache *)(rval->value);
+ } else {
+ rval = dmalloc(sizeof(struct tree_cache), name);
+ if (!rval)
+ error("unable to allocate tree cache for %s.", name);
+ }
+ return rval;
+}
+
+struct hash_table *new_hash_table(count, name)
+ int count;
+ char *name;
+{
+ struct hash_table *rval;
+ rval = dmalloc(sizeof (struct hash_table)
+ - (DEFAULT_HASH_SIZE * sizeof(struct hash_bucket *))
+ + (count * sizeof(struct hash_bucket *)), name);
+ if (rval == NULL)
+ return NULL;
+ rval->hash_count = count;
+ return rval;
+}
+
+struct hash_bucket *new_hash_bucket(name)
+ char *name;
+{
+ struct hash_bucket *rval = dmalloc(sizeof(struct hash_bucket), name);
+ return rval;
+}
+
+struct lease *new_leases(n, name)
+ int n;
+ char *name;
+{
+ struct lease *rval = dmalloc(n * sizeof(struct lease), name);
+ return rval;
+}
+
+struct lease *new_lease(name)
+ char *name;
+{
+ struct lease *rval = dmalloc(sizeof(struct lease), name);
+ return rval;
+}
+
+struct subnet *new_subnet(name)
+ char *name;
+{
+ struct subnet *rval = dmalloc(sizeof(struct subnet), name);
+ return rval;
+}
+
+struct class *new_class(name)
+ char *name;
+{
+ struct class *rval = dmalloc(sizeof(struct class), name);
+ return rval;
+}
+
+struct shared_network *new_shared_network(name)
+ char *name;
+{
+ struct shared_network *rval =
+ dmalloc (sizeof(struct shared_network), name);
+ return rval;
+}
+
+struct group *new_group(name)
+ char *name;
+{
+ struct group *rval =
+ dmalloc(sizeof(struct group), name);
+ return rval;
+}
+
+struct protocol *new_protocol(name)
+ char *name;
+{
+ struct protocol *rval = dmalloc(sizeof(struct protocol), name);
+ return rval;
+}
+
+struct lease_state *free_lease_states;
+
+struct lease_state *new_lease_state (name)
+ char *name;
+{
+ struct lease_state *rval;
+
+ if (free_lease_states) {
+ rval = free_lease_states;
+ free_lease_states =
+ (struct lease_state *)(free_lease_states->next);
+ } else {
+ rval = dmalloc (sizeof (struct lease_state), name);
+ }
+ return rval;
+}
+
+struct domain_search_list *new_domain_search_list (name)
+ char *name;
+{
+ struct domain_search_list *rval =
+ dmalloc (sizeof (struct domain_search_list), name);
+ return rval;
+}
+
+struct name_server *new_name_server (name)
+ char *name;
+{
+ struct name_server *rval =
+ dmalloc (sizeof (struct name_server), name);
+ return rval;
+}
+
+void free_name_server (ptr, name)
+ struct name_server *ptr;
+ char *name;
+{
+ dfree (ptr, name);
+}
+
+void free_domain_search_list (ptr, name)
+ struct domain_search_list *ptr;
+ char *name;
+{
+ dfree (ptr, name);
+}
+
+void free_lease_state (ptr, name)
+ struct lease_state *ptr;
+ char *name;
+{
+ if (ptr->prl)
+ dfree (ptr->prl, name);
+ ptr->next = free_lease_states;
+ free_lease_states = ptr;
+}
+
+void free_protocol (ptr, name)
+ struct protocol *ptr;
+ char *name;
+{
+ dfree (ptr, name);
+}
+
+void free_group (ptr, name)
+ struct group *ptr;
+ char *name;
+{
+ dfree (ptr, name);
+}
+
+void free_shared_network (ptr, name)
+ struct shared_network *ptr;
+ char *name;
+{
+ dfree (ptr, name);
+}
+
+void free_class (ptr, name)
+ struct class *ptr;
+ char *name;
+{
+ dfree (ptr, name);
+}
+
+void free_subnet (ptr, name)
+ struct subnet *ptr;
+ char *name;
+{
+ dfree (ptr, name);
+}
+
+void free_lease (ptr, name)
+ struct lease *ptr;
+ char *name;
+{
+ dfree (ptr, name);
+}
+
+void free_hash_bucket (ptr, name)
+ struct hash_bucket *ptr;
+ char *name;
+{
+ dfree (ptr, name);
+}
+
+void free_hash_table (ptr, name)
+ struct hash_table *ptr;
+ char *name;
+{
+ dfree (ptr, name);
+}
+
+void free_tree_cache (ptr, name)
+ struct tree_cache *ptr;
+ char *name;
+{
+ ptr->value = (unsigned char *)free_tree_caches;
+ free_tree_caches = ptr;
+}
+
+void free_packet (ptr, name)
+ struct packet *ptr;
+ char *name;
+{
+ dfree (ptr, name);
+}
+
+void free_dhcp_packet (ptr, name)
+ struct dhcp_packet *ptr;
+ char *name;
+{
+ dfree (ptr, name);
+}
+
+void free_tree (ptr, name)
+ struct tree *ptr;
+ char *name;
+{
+ dfree (ptr, name);
+}
+
+void free_string_list (ptr, name)
+ struct string_list *ptr;
+ char *name;
+{
+ dfree (ptr, name);
+}
diff --git a/usr.sbin/dhcpd/bootp.c b/usr.sbin/dhcpd/bootp.c
new file mode 100644
index 00000000000..902d2f4f5ef
--- /dev/null
+++ b/usr.sbin/dhcpd/bootp.c
@@ -0,0 +1,369 @@
+/* bootp.c
+
+ BOOTP Protocol support. */
+
+/*
+ * Copyright (c) 1995, 1996, 1998, 1999 The Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include "dhcpd.h"
+
+void bootp (packet)
+ struct packet *packet;
+{
+ int result;
+ struct host_decl *hp;
+ struct host_decl *host = (struct host_decl *)0;
+ struct packet outgoing;
+ struct dhcp_packet raw;
+ struct sockaddr_in to;
+ struct in_addr from;
+ struct hardware hto;
+ struct tree_cache *options [256];
+ struct subnet *subnet;
+ struct lease *lease;
+ struct iaddr ip_address;
+ int i;
+
+ if (packet -> raw -> op != BOOTREQUEST)
+ return;
+
+ note ("BOOTREQUEST from %s via %s%s",
+ print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr),
+ packet -> raw -> giaddr.s_addr
+ ? inet_ntoa (packet -> raw -> giaddr)
+ : packet -> interface -> name,
+ packet -> options_valid ? "" : " (non-rfc1048)");
+
+
+
+ if (!locate_network (packet))
+ return;
+
+ hp = find_hosts_by_haddr (packet -> raw -> htype,
+ packet -> raw -> chaddr,
+ packet -> raw -> hlen);
+
+ lease = find_lease (packet, packet -> shared_network, 0);
+
+ /* Find an IP address in the host_decl that matches the
+ specified network. */
+ if (hp)
+ subnet = find_host_for_network (&hp, &ip_address,
+ packet -> shared_network);
+ else
+ subnet = (struct subnet *)0;
+
+ if (!subnet) {
+ /* We didn't find an applicable host declaration.
+ Just in case we may be able to dynamically assign
+ an address, see if there's a host declaration
+ that doesn't have an ip address associated with it. */
+ if (hp) {
+ for (; hp; hp = hp -> n_ipaddr) {
+ if (!hp -> fixed_addr) {
+ host = hp;
+ break;
+ }
+ }
+ }
+
+ if (host && (!host -> group -> allow_booting)) {
+ note ("Ignoring excluded BOOTP client %s",
+ host -> name
+ ? host -> name
+ : print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr));
+ return;
+ }
+
+ if (host && (!host -> group -> allow_bootp)) {
+ note ("Ignoring BOOTP request from client %s",
+ host -> name
+ ? host -> name
+ : print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr));
+ return;
+ }
+
+ /* If we've been told not to boot unknown clients,
+ and we didn't find any host record for this client,
+ ignore it. */
+ if (!host && !(packet -> shared_network ->
+ group -> boot_unknown_clients)) {
+ note ("Ignoring unknown BOOTP client %s via %s",
+ print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr),
+ packet -> raw -> giaddr.s_addr
+ ? inet_ntoa (packet -> raw -> giaddr)
+ : packet -> interface -> name);
+ return;
+ }
+
+ /* If we've been told not to boot with bootp on this
+ network, ignore it. */
+ if (!host &&
+ !(packet -> shared_network -> group -> allow_bootp)) {
+ note ("Ignoring BOOTP request from client %s via %s",
+ print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr),
+ packet -> raw -> giaddr.s_addr
+ ? inet_ntoa (packet -> raw -> giaddr)
+ : packet -> interface -> name);
+ return;
+ }
+
+ /* If the packet is from a host we don't know and there
+ are no dynamic bootp addresses on the network it came
+ in on, drop it on the floor. */
+ if (!(packet -> shared_network -> group -> dynamic_bootp)) {
+ lose:
+ note ("No applicable record for BOOTP host %s via %s",
+ print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr),
+ packet -> raw -> giaddr.s_addr
+ ? inet_ntoa (packet -> raw -> giaddr)
+ : packet -> interface -> name);
+ return;
+ }
+
+ /* If a lease has already been assigned to this client
+ and it's still okay to use dynamic bootp on
+ that lease, reassign it. */
+ if (lease) {
+ /* If this lease can be used for dynamic bootp,
+ do so. */
+ if ((lease -> flags & DYNAMIC_BOOTP_OK)) {
+
+ /* If it's not a DYNAMIC_BOOTP lease,
+ release it before reassigning it
+ so that we don't get a lease
+ conflict. */
+ if (!(lease -> flags & BOOTP_LEASE))
+ release_lease (lease);
+
+ lease -> host = host;
+ ack_lease (packet, lease, 0, 0);
+ return;
+ }
+
+ /* If dynamic BOOTP is no longer allowed for
+ this lease, set it free. */
+ release_lease (lease);
+ }
+
+ /* If there are dynamic bootp addresses that might be
+ available, try to snag one. */
+ for (lease = packet -> shared_network -> last_lease;
+ lease && lease -> ends <= cur_time;
+ lease = lease -> prev) {
+ if ((lease -> flags & DYNAMIC_BOOTP_OK)) {
+ lease -> host = host;
+ ack_lease (packet, lease, 0, 0);
+ return;
+ }
+ }
+ goto lose;
+ }
+
+ /* Make sure we're allowed to boot this client. */
+ if (hp && (!hp -> group -> allow_booting)) {
+ note ("Ignoring excluded BOOTP client %s",
+ hp -> name);
+ return;
+ }
+
+ /* Make sure we're allowed to boot this client with bootp. */
+ if (hp && (!hp -> group -> allow_bootp)) {
+ note ("Ignoring BOOTP request from client %s",
+ hp -> name);
+ return;
+ }
+
+ /* Set up the outgoing packet... */
+ memset (&outgoing, 0, sizeof outgoing);
+ memset (&raw, 0, sizeof raw);
+ outgoing.raw = &raw;
+
+ /* If we didn't get a known vendor magic number on the way in,
+ just copy the input options to the output. */
+ if (!packet -> options_valid &&
+ !subnet -> group -> always_reply_rfc1048 &&
+ (!hp || !hp -> group -> always_reply_rfc1048)) {
+ memcpy (outgoing.raw -> options,
+ packet -> raw -> options, DHCP_OPTION_LEN);
+ outgoing.packet_length = BOOTP_MIN_LEN;
+ } else {
+ struct tree_cache netmask_tree; /* -- RBF */
+
+ /* Come up with a list of options that we want to send
+ to this client. Start with the per-subnet options,
+ and then override those with client-specific
+ options. */
+
+ memcpy (options, subnet -> group -> options, sizeof options);
+
+ for (i = 0; i < 256; i++) {
+ if (hp -> group -> options [i])
+ options [i] = hp -> group -> options [i];
+ }
+
+ /* Use the subnet mask from the subnet declaration if no other
+ mask has been provided. */
+ if (!options [DHO_SUBNET_MASK]) {
+ options [DHO_SUBNET_MASK] = &netmask_tree;
+ netmask_tree.flags = TC_TEMPORARY;
+ netmask_tree.value = lease -> subnet -> netmask.iabuf;
+ netmask_tree.len = lease -> subnet -> netmask.len;
+ netmask_tree.buf_size = lease -> subnet -> netmask.len;
+ netmask_tree.timeout = 0xFFFFFFFF;
+ netmask_tree.tree = (struct tree *)0;
+ }
+
+ /* Pack the options into the buffer. Unlike DHCP, we
+ can't pack options into the filename and server
+ name buffers. */
+
+ outgoing.packet_length =
+ cons_options (packet, outgoing.raw,
+ 0, options, 0, 0, 1, (u_int8_t *)0, 0);
+ if (outgoing.packet_length < BOOTP_MIN_LEN)
+ outgoing.packet_length = BOOTP_MIN_LEN;
+ }
+
+ /* Take the fields that we care about... */
+ raw.op = BOOTREPLY;
+ raw.htype = packet -> raw -> htype;
+ raw.hlen = packet -> raw -> hlen;
+ memcpy (raw.chaddr, packet -> raw -> chaddr, sizeof raw.chaddr);
+ raw.hops = packet -> raw -> hops;
+ raw.xid = packet -> raw -> xid;
+ raw.secs = packet -> raw -> secs;
+ raw.flags = packet -> raw -> flags;
+ raw.ciaddr = packet -> raw -> ciaddr;
+ memcpy (&raw.yiaddr, ip_address.iabuf, sizeof raw.yiaddr);
+
+ /* Figure out the address of the next server. */
+ if (hp && hp -> group -> next_server.len)
+ memcpy (&raw.siaddr, hp -> group -> next_server.iabuf, 4);
+ else if (subnet -> group -> next_server.len)
+ memcpy (&raw.siaddr, subnet -> group -> next_server.iabuf, 4);
+ else if (subnet -> interface_address.len)
+ memcpy (&raw.siaddr, subnet -> interface_address.iabuf, 4);
+ else
+ raw.siaddr = packet -> interface -> primary_address;
+
+ raw.giaddr = packet -> raw -> giaddr;
+ if (hp -> group -> server_name)
+ strncpy (raw.sname, hp -> group -> server_name,
+ (sizeof raw.sname));
+ else if (subnet -> group -> server_name)
+ strncpy (raw.sname, subnet -> group -> server_name,
+ (sizeof raw.sname));
+
+ if (hp -> group -> filename)
+ strncpy (raw.file, hp -> group -> filename,
+ (sizeof raw.file));
+ else if (subnet -> group -> filename)
+ strncpy (raw.file, subnet -> group -> filename,
+ (sizeof raw.file));
+ else
+ memcpy (raw.file, packet -> raw -> file, sizeof raw.file);
+
+ /* Set up the hardware destination address... */
+ hto.htype = packet -> raw -> htype;
+ hto.hlen = packet -> raw -> hlen;
+ memcpy (hto.haddr, packet -> raw -> chaddr, hto.hlen);
+
+ from = packet -> interface -> primary_address;
+
+ /* Report what we're doing... */
+ note ("BOOTREPLY for %s to %s (%s) via %s",
+ piaddr (ip_address), hp -> name,
+ print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr),
+ packet -> raw -> giaddr.s_addr
+ ? inet_ntoa (packet -> raw -> giaddr)
+ : packet -> interface -> name);
+
+ /* Set up the parts of the address that are in common. */
+ memset (&to, 0, sizeof to);
+ to.sin_family = AF_INET;
+#ifdef HAVE_SA_LEN
+ to.sin_len = sizeof to;
+#endif
+
+ /* If this was gatewayed, send it back to the gateway... */
+ if (raw.giaddr.s_addr) {
+ to.sin_addr = raw.giaddr;
+ to.sin_port = local_port;
+
+ if (fallback_interface) {
+ result = send_packet (fallback_interface,
+ (struct packet *)0,
+ &raw, outgoing.packet_length,
+ from, &to, &hto);
+ return;
+ }
+
+ /* If it comes from a client that already knows its address
+ and is not requesting a broadcast response, and we can
+ unicast to a client without using the ARP protocol, sent it
+ directly to that client. */
+ } else if (!(raw.flags & htons (BOOTP_BROADCAST))) {
+ to.sin_addr = raw.yiaddr;
+ to.sin_port = remote_port;
+
+ /* Otherwise, broadcast it on the local network. */
+ } else {
+ to.sin_addr.s_addr = INADDR_BROADCAST;
+ to.sin_port = remote_port; /* XXX */
+ }
+
+ errno = 0;
+ result = send_packet (packet -> interface,
+ packet, &raw, outgoing.packet_length,
+ from, &to, &hto);
+}
diff --git a/usr.sbin/dhcpd/bpf.c b/usr.sbin/dhcpd/bpf.c
new file mode 100644
index 00000000000..b996cf27fc0
--- /dev/null
+++ b/usr.sbin/dhcpd/bpf.c
@@ -0,0 +1,321 @@
+/* $OpenBSD: bpf.c,v 1.1 2004/04/13 23:41:48 henning Exp $ */
+
+/* BPF socket interface code, originally contributed by Archie Cobbs. */
+
+/*
+ * Copyright (c) 1995, 1996, 1998, 1999
+ * The Internet Software Consortium. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include "dhcpd.h"
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+
+#include <net/bpf.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <netinet/if_ether.h>
+
+#define BPF_FORMAT "/dev/bpf%d"
+
+/*
+ * Called by get_interface_list for each interface that's discovered.
+ * Opens a packet filter for each interface and adds it to the select
+ * mask.
+ */
+int
+if_register_bpf(struct interface_info *info)
+{
+ char filename[50];
+ int sock, b;
+
+ /* Open a BPF device */
+ for (b = 0; 1; b++) {
+ snprintf(filename, sizeof(filename), BPF_FORMAT, b);
+ sock = open(filename, O_RDWR, 0);
+ if (sock < 0) {
+ if (errno == EBUSY)
+ continue;
+ else
+ error("Can't find free bpf: %m");
+ } else
+ break;
+ }
+
+ /* Set the BPF device to point at this interface. */
+ if (ioctl(sock, BIOCSETIF, info->ifp) < 0)
+ error("Can't attach interface %s to bpf device %s: %m",
+ info->name, filename);
+
+ return (sock);
+}
+
+void
+if_register_send(struct interface_info *info)
+{
+ /*
+ * If we're using the bpf API for sending and receiving, we
+ * don't need to register this interface twice.
+ */
+ info->wfdesc = info->rfdesc;
+}
+
+/*
+ * Packet filter program...
+ *
+ * XXX: Changes to the filter program may require changes to the
+ * constant offsets used in if_register_send to patch the BPF program!
+ */
+struct bpf_insn dhcp_bpf_filter[] = {
+ /* Make sure this is an IP packet... */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 8),
+
+ /* Make sure it's a UDP packet... */
+ BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 6),
+
+ /* Make sure this isn't a fragment... */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20),
+ BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 4, 0),
+
+ /* Get the IP header length... */
+ BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14),
+
+ /* Make sure it's to the right port... */
+ BPF_STMT(BPF_LD + BPF_H + BPF_IND, 16),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 67, 0, 1), /* patch */
+
+ /* If we passed all the tests, ask for the whole packet. */
+ BPF_STMT(BPF_RET+BPF_K, (u_int)-1),
+
+ /* Otherwise, drop it. */
+ BPF_STMT(BPF_RET+BPF_K, 0),
+};
+
+int dhcp_bpf_filter_len = sizeof(dhcp_bpf_filter) / sizeof(struct bpf_insn);
+
+void
+if_register_receive(struct interface_info *info)
+{
+ struct bpf_version v;
+ struct bpf_program p;
+ int flag = 1, sz;
+
+ /* Open a BPF device and hang it on this interface... */
+ info->rfdesc = if_register_bpf(info);
+
+ /* Make sure the BPF version is in range... */
+ if (ioctl(info->rfdesc, BIOCVERSION, &v) < 0)
+ error("Can't get BPF version: %m");
+
+ if (v.bv_major != BPF_MAJOR_VERSION ||
+ v.bv_minor < BPF_MINOR_VERSION)
+ error("Kernel BPF version out of range - recompile dhcpd!");
+
+ /*
+ * Set immediate mode so that reads return as soon as a packet
+ * comes in, rather than waiting for the input buffer to fill
+ * with packets.
+ */
+ if (ioctl(info->rfdesc, BIOCIMMEDIATE, &flag) < 0)
+ error("Can't set immediate mode on bpf device: %m");
+
+ /* Get the required BPF buffer length from the kernel. */
+ if (ioctl(info->rfdesc, BIOCGBLEN, &sz) < 0)
+ error("Can't get bpf buffer length: %m");
+ info->rbuf_max = sz;
+ info->rbuf = malloc(info->rbuf_max);
+ if (!info->rbuf)
+ error("Can't allocate %lu bytes for bpf input buffer.",
+ (unsigned long)info->rbuf_max);
+ info->rbuf_offset = 0;
+ info->rbuf_len = 0;
+
+ /* Set up the bpf filter program structure. */
+ p.bf_len = dhcp_bpf_filter_len;
+ p.bf_insns = dhcp_bpf_filter;
+
+ /* Patch the server port into the BPF program...
+ *
+ * XXX: changes to filter program may require changes to the
+ * insn number(s) used below!
+ */
+ dhcp_bpf_filter[8].k = LOCAL_PORT;
+
+ if (ioctl(info->rfdesc, BIOCSETF, &p) < 0)
+ error("Can't install packet filter program: %m");
+}
+
+ssize_t
+send_packet(struct interface_info *interface, struct packet *packet,
+ struct dhcp_packet *raw, size_t len, struct in_addr from,
+ struct sockaddr_in *to, struct hardware *hto)
+{
+ unsigned char buf[256];
+ struct iovec iov[2];
+ int result, bufp = 0;
+
+ /* Assemble the headers... */
+ assemble_hw_header(interface, buf, &bufp, hto);
+ assemble_udp_ip_header(interface, buf, &bufp, from.s_addr,
+ to->sin_addr.s_addr, to->sin_port, (unsigned char *)raw, len);
+
+ /* Fire it off */
+ iov[0].iov_base = (char *)buf;
+ iov[0].iov_len = bufp;
+ iov[1].iov_base = (char *)raw;
+ iov[1].iov_len = len;
+
+ result = writev(interface->wfdesc, iov, 2);
+ if (result < 0)
+ warn("send_packet: %m");
+ return (result);
+}
+
+ssize_t
+receive_packet(struct interface_info *interface, unsigned char *buf,
+ size_t len, struct sockaddr_in *from, struct hardware *hfrom)
+{
+ int length = 0, offset = 0;
+ struct bpf_hdr hdr;
+
+ /*
+ * All this complexity is because BPF doesn't guarantee that
+ * only one packet will be returned at a time. We're getting
+ * what we deserve, though - this is a terrible abuse of the BPF
+ * interface. Sigh.
+ */
+
+ /* Process packets until we get one we can return or until we've
+ * done a read and gotten nothing we can return...
+ */
+ do {
+ /* If the buffer is empty, fill it. */
+ if (interface->rbuf_offset == interface->rbuf_len) {
+ length = read(interface->rfdesc, interface->rbuf,
+ interface->rbuf_max);
+ if (length <= 0)
+ return (length);
+ interface->rbuf_offset = 0;
+ interface->rbuf_len = length;
+ }
+
+ /*
+ * If there isn't room for a whole bpf header, something
+ * went wrong, but we'll ignore it and hope it goes
+ * away... XXX
+ */
+ if (interface->rbuf_len - interface->rbuf_offset <
+ sizeof(hdr)) {
+ interface->rbuf_offset = interface->rbuf_len;
+ continue;
+ }
+
+ /* Copy out a bpf header... */
+ memcpy(&hdr, &interface->rbuf[interface->rbuf_offset],
+ sizeof(hdr));
+
+ /*
+ * If the bpf header plus data doesn't fit in what's
+ * left of the buffer, stick head in sand yet again...
+ */
+ if (interface->rbuf_offset + hdr.bh_hdrlen + hdr.bh_caplen >
+ interface->rbuf_len) {
+ interface->rbuf_offset = interface->rbuf_len;
+ continue;
+ }
+
+ /*
+ * If the captured data wasn't the whole packet, or if
+ * the packet won't fit in the input buffer, all we can
+ * do is drop it.
+ */
+ if (hdr.bh_caplen != hdr.bh_datalen) {
+ interface->rbuf_offset += hdr.bh_hdrlen = hdr.bh_caplen;
+ continue;
+ }
+
+ /* Skip over the BPF header... */
+ interface->rbuf_offset += hdr.bh_hdrlen;
+
+ /* Decode the physical header... */
+ offset = decode_hw_header(interface,
+ interface->rbuf, interface->rbuf_offset, hfrom);
+
+ /*
+ * If a physical layer checksum failed (dunno of any
+ * physical layer that supports this, but WTH), skip
+ * this packet.
+ */
+ if (offset < 0) {
+ interface->rbuf_offset += hdr.bh_caplen;
+ continue;
+ }
+ interface->rbuf_offset += offset;
+ hdr.bh_caplen -= offset;
+
+ /* Decode the IP and UDP headers... */
+ offset = decode_udp_ip_header(interface, interface->rbuf,
+ interface->rbuf_offset, from, NULL, hdr.bh_caplen);
+
+ /* If the IP or UDP checksum was bad, skip the packet... */
+ if (offset < 0) {
+ interface->rbuf_offset += hdr.bh_caplen;
+ continue;
+ }
+ interface->rbuf_offset += offset;
+ hdr.bh_caplen -= offset;
+
+ /*
+ * If there's not enough room to stash the packet data,
+ * we have to skip it (this shouldn't happen in real
+ * life, though).
+ */
+ if (hdr.bh_caplen > len) {
+ interface->rbuf_offset += hdr.bh_caplen;
+ continue;
+ }
+
+ /* Copy out the data in the packet... */
+ memcpy(buf, interface->rbuf + interface->rbuf_offset,
+ hdr.bh_caplen);
+ interface->rbuf_offset += hdr.bh_caplen;
+ return (hdr.bh_caplen);
+ } while (!length);
+ return (0);
+}
diff --git a/usr.sbin/dhcpd/cdefs.h b/usr.sbin/dhcpd/cdefs.h
new file mode 100644
index 00000000000..2bc67a5251a
--- /dev/null
+++ b/usr.sbin/dhcpd/cdefs.h
@@ -0,0 +1,57 @@
+/* cdefs.h
+
+ Standard C definitions... */
+
+/*
+ * Copyright (c) 1996 The Internet Software Consortium.
+ * All Rights Reserved.
+ * Copyright (c) 1995 RadioMail Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of RadioMail Corporation, the Internet Software
+ * Consortium nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY RADIOMAIL CORPORATION, THE INTERNET
+ * SOFTWARE CONSORTIUM AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL RADIOMAIL CORPORATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This software was written for RadioMail Corporation by Ted Lemon
+ * under a contract with Vixie Enterprises. Further modifications have
+ * been made for the Internet Software Consortium under a contract
+ * with Vixie Laboratories.
+ */
+
+#if (defined (__GNUC__) || defined (__STDC__)) && !defined (BROKEN_ANSI)
+#define PROTO(x) x
+#define KandR(x)
+#define ANSI_DECL(x) x
+#if defined (__GNUC__)
+#define INLINE inline
+#else
+#define INLINE
+#endif /* __GNUC__ */
+#else
+#define PROTO(x) ()
+#define KandR(x) x
+#define ANSI_DECL(x)
+#define INLINE
+#endif /* __GNUC__ || __STDC__ */
diff --git a/usr.sbin/dhcpd/conflex.c b/usr.sbin/dhcpd/conflex.c
new file mode 100644
index 00000000000..58136a2bc1a
--- /dev/null
+++ b/usr.sbin/dhcpd/conflex.c
@@ -0,0 +1,536 @@
+/* conflex.c
+
+ Lexical scanner for dhcpd config file... */
+
+/*
+ * Copyright (c) 1995, 1996, 1997 The Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include "dhcpd.h"
+#include "dhctoken.h"
+#include <ctype.h>
+
+int lexline;
+int lexchar;
+char *token_line;
+char *prev_line;
+char *cur_line;
+char *tlname;
+int eol_token;
+
+static char line1 [81];
+static char line2 [81];
+static int lpos;
+static int line;
+static int tlpos;
+static int tline;
+static int token;
+static int ugflag;
+static char *tval;
+static char tokbuf [1500];
+
+#ifdef OLD_LEXER
+char comments [4096];
+int comment_index;
+#endif
+
+
+static int get_char PROTO ((FILE *));
+static int get_token PROTO ((FILE *));
+static void skip_to_eol PROTO ((FILE *));
+static int read_string PROTO ((FILE *));
+static int read_number PROTO ((int, FILE *));
+static int read_num_or_name PROTO ((int, FILE *));
+static int intern PROTO ((char *, int));
+
+void new_parse (name)
+ char *name;
+{
+ tlname = name;
+ lpos = line = 1;
+ cur_line = line1;
+ prev_line = line2;
+ token_line = cur_line;
+ cur_line [0] = prev_line [0] = 0;
+ warnings_occurred = 0;
+}
+
+static int get_char (cfile)
+ FILE *cfile;
+{
+ int c = getc (cfile);
+ if (!ugflag) {
+ if (c == EOL) {
+ if (cur_line == line1) {
+ cur_line = line2;
+ prev_line = line1;
+ } else {
+ cur_line = line2;
+ prev_line = line1;
+ }
+ line++;
+ lpos = 1;
+ cur_line [0] = 0;
+ } else if (c != EOF) {
+ if (lpos <= 81) {
+ cur_line [lpos - 1] = c;
+ cur_line [lpos] = 0;
+ }
+ lpos++;
+ }
+ } else
+ ugflag = 0;
+ return c;
+}
+
+static int get_token (cfile)
+ FILE *cfile;
+{
+ int c;
+ int ttok;
+ static char tb [2];
+ int l, p, u;
+
+ do {
+ l = line;
+ p = lpos;
+ u = ugflag;
+
+ c = get_char (cfile);
+
+ if (!(c == '\n' && eol_token) && isascii (c) && isspace (c))
+ continue;
+ if (c == '#') {
+ skip_to_eol (cfile);
+ continue;
+ }
+ if (c == '"') {
+ lexline = l;
+ lexchar = p;
+ ttok = read_string (cfile);
+ break;
+ }
+ if ((isascii (c) && isdigit (c)) || c == '-') {
+ lexline = l;
+ lexchar = p;
+ ttok = read_number (c, cfile);
+ break;
+ } else if (isascii (c) && isalpha (c)) {
+ lexline = l;
+ lexchar = p;
+ ttok = read_num_or_name (c, cfile);
+ break;
+ } else {
+ lexline = l;
+ lexchar = p;
+ tb [0] = c;
+ tb [1] = 0;
+ tval = tb;
+ ttok = c;
+ break;
+ }
+ } while (1);
+ return ttok;
+}
+
+int next_token (rval, cfile)
+ char **rval;
+ FILE *cfile;
+{
+ int rv;
+
+ if (token) {
+ if (lexline != tline)
+ token_line = cur_line;
+ lexchar = tlpos;
+ lexline = tline;
+ rv = token;
+ token = 0;
+ } else {
+ rv = get_token (cfile);
+ token_line = cur_line;
+ }
+ if (rval)
+ *rval = tval;
+#ifdef DEBUG_TOKENS
+ fprintf (stderr, "%s:%d ", tval, rv);
+#endif
+ return rv;
+}
+
+int peek_token (rval, cfile)
+ char **rval;
+ FILE *cfile;
+{
+ int x;
+
+ if (!token) {
+ tlpos = lexchar;
+ tline = lexline;
+ token = get_token (cfile);
+ if (lexline != tline)
+ token_line = prev_line;
+ x = lexchar; lexchar = tlpos; tlpos = x;
+ x = lexline; lexline = tline; tline = x;
+ }
+ if (rval)
+ *rval = tval;
+#ifdef DEBUG_TOKENS
+ fprintf (stderr, "(%s:%d) ", tval, token);
+#endif
+ return token;
+}
+
+static void skip_to_eol (cfile)
+ FILE *cfile;
+{
+ int c;
+ do {
+ c = get_char (cfile);
+ if (c == EOF)
+ return;
+ if (c == EOL)
+ return;
+ } while (1);
+}
+
+static int read_string (cfile)
+ FILE *cfile;
+{
+ int i;
+ int bs = 0;
+ int c;
+
+ for (i = 0; i < sizeof tokbuf; i++) {
+ c = get_char (cfile);
+ if (c == EOF) {
+ parse_warn ("eof in string constant");
+ break;
+ }
+ if (bs) {
+ bs = 0;
+ tokbuf [i] = c;
+ } else if (c == '\\')
+ bs = 1;
+ else if (c == '"')
+ break;
+ else
+ tokbuf [i] = c;
+ }
+ /* Normally, I'd feel guilty about this, but we're talking about
+ strings that'll fit in a DHCP packet here... */
+ if (i == sizeof tokbuf) {
+ parse_warn ("string constant larger than internal buffer");
+ --i;
+ }
+ tokbuf [i] = 0;
+ tval = tokbuf;
+ return STRING;
+}
+
+static int read_number (c, cfile)
+ int c;
+ FILE *cfile;
+{
+ int seenx = 0;
+ int i = 0;
+ int token = NUMBER;
+
+ tokbuf [i++] = c;
+ for (; i < sizeof tokbuf; i++) {
+ c = get_char (cfile);
+ if (!seenx && c == 'x') {
+ seenx = 1;
+ } else if (!isascii (c) || !isxdigit (c)) {
+ ungetc (c, cfile);
+ ugflag = 1;
+ break;
+ }
+ tokbuf [i] = c;
+ }
+ if (i == sizeof tokbuf) {
+ parse_warn ("numeric token larger than internal buffer");
+ --i;
+ }
+ tokbuf [i] = 0;
+ tval = tokbuf;
+ return token;
+}
+
+static int read_num_or_name (c, cfile)
+ int c;
+ FILE *cfile;
+{
+ int i = 0;
+ int rv = NUMBER_OR_NAME;
+ tokbuf [i++] = c;
+ for (; i < sizeof tokbuf; i++) {
+ c = get_char (cfile);
+ if (!isascii (c) ||
+ (c != '-' && c != '_' && !isalnum (c))) {
+ ungetc (c, cfile);
+ ugflag = 1;
+ break;
+ }
+ if (!isxdigit (c))
+ rv = NAME;
+ tokbuf [i] = c;
+ }
+ if (i == sizeof tokbuf) {
+ parse_warn ("token larger than internal buffer");
+ --i;
+ }
+ tokbuf [i] = 0;
+ tval = tokbuf;
+ return intern (tval, rv);
+}
+
+static int intern (atom, dfv)
+ char *atom;
+ int dfv;
+{
+ if (!isascii (atom [0]))
+ return dfv;
+
+ switch (tolower (atom [0])) {
+ case 'a':
+ if (!strcasecmp (atom + 1, "lways-reply-rfc1048"))
+ return ALWAYS_REPLY_RFC1048;
+ if (!strcasecmp (atom + 1, "ppend"))
+ return APPEND;
+ if (!strcasecmp (atom + 1, "llow"))
+ return ALLOW;
+ if (!strcasecmp (atom + 1, "lias"))
+ return ALIAS;
+ if (!strcasecmp (atom + 1, "bandoned"))
+ return ABANDONED;
+ if (!strcasecmp (atom + 1, "uthoritative"))
+ return AUTHORITATIVE;
+ break;
+ case 'b':
+ if (!strcasecmp (atom + 1, "ackoff-cutoff"))
+ return BACKOFF_CUTOFF;
+ if (!strcasecmp (atom + 1, "ootp"))
+ return BOOTP;
+ if (!strcasecmp (atom + 1, "ooting"))
+ return BOOTING;
+ if (!strcasecmp (atom + 1, "oot-unknown-clients"))
+ return BOOT_UNKNOWN_CLIENTS;
+ case 'c':
+ if (!strcasecmp (atom + 1, "lass"))
+ return CLASS;
+ if (!strcasecmp (atom + 1, "iaddr"))
+ return CIADDR;
+ if (!strcasecmp (atom + 1, "lient-identifier"))
+ return CLIENT_IDENTIFIER;
+ if (!strcasecmp (atom + 1, "lient-hostname"))
+ return CLIENT_HOSTNAME;
+ break;
+ case 'd':
+ if (!strcasecmp (atom + 1, "omain"))
+ return DOMAIN;
+ if (!strcasecmp (atom + 1, "eny"))
+ return DENY;
+ if (!strncasecmp (atom + 1, "efault", 6)) {
+ if (!atom [7])
+ return DEFAULT;
+ if (!strcasecmp (atom + 7, "-lease-time"))
+ return DEFAULT_LEASE_TIME;
+ break;
+ }
+ if (!strncasecmp (atom + 1, "ynamic-bootp", 12)) {
+ if (!atom [13])
+ return DYNAMIC_BOOTP;
+ if (!strcasecmp (atom + 13, "-lease-cutoff"))
+ return DYNAMIC_BOOTP_LEASE_CUTOFF;
+ if (!strcasecmp (atom + 13, "-lease-length"))
+ return DYNAMIC_BOOTP_LEASE_LENGTH;
+ break;
+ }
+ break;
+ case 'e':
+ if (!strcasecmp (atom + 1, "thernet"))
+ return ETHERNET;
+ if (!strcasecmp (atom + 1, "nds"))
+ return ENDS;
+ if (!strcasecmp (atom + 1, "xpire"))
+ return EXPIRE;
+ break;
+ case 'f':
+ if (!strcasecmp (atom + 1, "ilename"))
+ return FILENAME;
+ if (!strcasecmp (atom + 1, "ixed-address"))
+ return FIXED_ADDR;
+ if (!strcasecmp (atom + 1, "ddi"))
+ return FDDI;
+ break;
+ case 'g':
+ if (!strcasecmp (atom + 1, "iaddr"))
+ return GIADDR;
+ if (!strcasecmp (atom + 1, "roup"))
+ return GROUP;
+ if (!strcasecmp (atom + 1, "et-lease-hostnames"))
+ return GET_LEASE_HOSTNAMES;
+ break;
+ case 'h':
+ if (!strcasecmp (atom + 1, "ost"))
+ return HOST;
+ if (!strcasecmp (atom + 1, "ardware"))
+ return HARDWARE;
+ if (!strcasecmp (atom + 1, "ostname"))
+ return HOSTNAME;
+ break;
+ case 'i':
+ if (!strcasecmp (atom + 1, "nitial-interval"))
+ return INITIAL_INTERVAL;
+ if (!strcasecmp (atom + 1, "nterface"))
+ return INTERFACE;
+ break;
+ case 'l':
+ if (!strcasecmp (atom + 1, "ease"))
+ return LEASE;
+ break;
+ case 'm':
+ if (!strcasecmp (atom + 1, "ax-lease-time"))
+ return MAX_LEASE_TIME;
+ if (!strncasecmp (atom + 1, "edi", 3)) {
+ if (!strcasecmp (atom + 4, "a"))
+ return MEDIA;
+ if (!strcasecmp (atom + 4, "um"))
+ return MEDIUM;
+ break;
+ }
+ break;
+ case 'n':
+ if (!strcasecmp (atom + 1, "ameserver"))
+ return NAMESERVER;
+ if (!strcasecmp (atom + 1, "etmask"))
+ return NETMASK;
+ if (!strcasecmp (atom + 1, "ext-server"))
+ return NEXT_SERVER;
+ if (!strcasecmp (atom + 1, "ot"))
+ return TOKEN_NOT;
+ break;
+ case 'o':
+ if (!strcasecmp (atom + 1, "ption"))
+ return OPTION;
+ if (!strcasecmp (atom + 1, "ne-lease-per-client"))
+ return ONE_LEASE_PER_CLIENT;
+ break;
+ case 'p':
+ if (!strcasecmp (atom + 1, "repend"))
+ return PREPEND;
+ if (!strcasecmp (atom + 1, "acket"))
+ return PACKET;
+ break;
+ case 'r':
+ if (!strcasecmp (atom + 1, "ange"))
+ return RANGE;
+ if (!strcasecmp (atom + 1, "equest"))
+ return REQUEST;
+ if (!strcasecmp (atom + 1, "equire"))
+ return REQUIRE;
+ if (!strcasecmp (atom + 1, "etry"))
+ return RETRY;
+ if (!strcasecmp (atom + 1, "enew"))
+ return RENEW;
+ if (!strcasecmp (atom + 1, "ebind"))
+ return REBIND;
+ if (!strcasecmp (atom + 1, "eboot"))
+ return REBOOT;
+ if (!strcasecmp (atom + 1, "eject"))
+ return REJECT;
+ break;
+ case 's':
+ if (!strcasecmp (atom + 1, "earch"))
+ return SEARCH;
+ if (!strcasecmp (atom + 1, "tarts"))
+ return STARTS;
+ if (!strcasecmp (atom + 1, "iaddr"))
+ return SIADDR;
+ if (!strcasecmp (atom + 1, "ubnet"))
+ return SUBNET;
+ if (!strcasecmp (atom + 1, "hared-network"))
+ return SHARED_NETWORK;
+ if (!strcasecmp (atom + 1, "erver-name"))
+ return SERVER_NAME;
+ if (!strcasecmp (atom + 1, "erver-identifier"))
+ return SERVER_IDENTIFIER;
+ if (!strcasecmp (atom + 1, "elect-timeout"))
+ return SELECT_TIMEOUT;
+ if (!strcasecmp (atom + 1, "end"))
+ return SEND;
+ if (!strcasecmp (atom + 1, "cript"))
+ return SCRIPT;
+ if (!strcasecmp (atom + 1, "upersede"))
+ return SUPERSEDE;
+ break;
+ case 't':
+ if (!strcasecmp (atom + 1, "imestamp"))
+ return TIMESTAMP;
+ if (!strcasecmp (atom + 1, "imeout"))
+ return TIMEOUT;
+ if (!strcasecmp (atom + 1, "oken-ring"))
+ return TOKEN_RING;
+ break;
+ case 'u':
+ if (!strncasecmp (atom + 1, "se", 2)) {
+ if (!strcasecmp (atom + 3, "r-class"))
+ return USER_CLASS;
+ if (!strcasecmp (atom + 3, "-host-decl-names"))
+ return USE_HOST_DECL_NAMES;
+ if (!strcasecmp (atom + 3,
+ "-lease-addr-for-default-route"))
+ return USE_LEASE_ADDR_FOR_DEFAULT_ROUTE;
+ break;
+ }
+ if (!strcasecmp (atom + 1, "id"))
+ return UID;
+ if (!strcasecmp (atom + 1, "nknown-clients"))
+ return UNKNOWN_CLIENTS;
+ break;
+ case 'v':
+ if (!strcasecmp (atom + 1, "endor-class"))
+ return VENDOR_CLASS;
+ break;
+ case 'y':
+ if (!strcasecmp (atom + 1, "iaddr"))
+ return YIADDR;
+ break;
+ }
+ return dfv;
+}
diff --git a/usr.sbin/dhcpd/confpars.c b/usr.sbin/dhcpd/confpars.c
new file mode 100644
index 00000000000..fec0c61d274
--- /dev/null
+++ b/usr.sbin/dhcpd/confpars.c
@@ -0,0 +1,1372 @@
+/* confpars.c
+
+ Parser for dhcpd config file... */
+
+/*
+ * Copyright (c) 1995, 1996, 1997 The Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include "dhcpd.h"
+#include "dhctoken.h"
+
+static TIME parsed_time;
+
+/* conf-file :== parameters declarations EOF
+ parameters :== <nil> | parameter | parameters parameter
+ declarations :== <nil> | declaration | declarations declaration */
+
+int readconf ()
+{
+ FILE *cfile;
+ char *val;
+ int token;
+ int declaration = 0;
+
+ new_parse (path_dhcpd_conf);
+
+ /* Set up the initial dhcp option universe. */
+ initialize_universes ();
+
+ /* Set up the global defaults... */
+ root_group.default_lease_time = 43200; /* 12 hours. */
+ root_group.max_lease_time = 86400; /* 24 hours. */
+ root_group.bootp_lease_cutoff = MAX_TIME;
+ root_group.boot_unknown_clients = 1;
+ root_group.allow_bootp = 1;
+ root_group.allow_booting = 1;
+ root_group.authoritative = 1;
+
+ if ((cfile = fopen (path_dhcpd_conf, "r")) == NULL) {
+ error ("Can't open %s: %m", path_dhcpd_conf);
+ }
+
+ do {
+ token = peek_token (&val, cfile);
+ if (token == EOF)
+ break;
+ declaration = parse_statement (cfile, &root_group,
+ ROOT_GROUP,
+ (struct host_decl *)0,
+ declaration);
+ } while (1);
+ token = next_token (&val, cfile); /* Clear the peek buffer */
+ fclose(cfile);
+
+ return !warnings_occurred;
+}
+
+/* lease-file :== lease-declarations EOF
+ lease-statments :== <nil>
+ | lease-declaration
+ | lease-declarations lease-declaration */
+
+void read_leases ()
+{
+ FILE *cfile;
+ char *val;
+ int token;
+
+ new_parse (path_dhcpd_db);
+
+ /* Open the lease file. If we can't open it, fail. The reason
+ for this is that although on initial startup, the absence of
+ a lease file is perfectly benign, if dhcpd has been running
+ and this file is absent, it means that dhcpd tried and failed
+ to rewrite the lease database. If we proceed and the
+ problem which caused the rewrite to fail has been fixed, but no
+ human has corrected the database problem, then we are left
+ thinking that no leases have been assigned to anybody, which
+ could create severe network chaos. */
+ if ((cfile = fopen (path_dhcpd_db, "r")) == NULL) {
+ warn ("Can't open lease database %s: %m -- %s",
+ path_dhcpd_db,
+ "check for failed database rewrite attempt!");
+ warn ("Please read the dhcpd.leases manual page if you.");
+ error ("don't know what to do about this."); }
+
+ do {
+ token = next_token (&val, cfile);
+ if (token == EOF)
+ break;
+ if (token != LEASE) {
+ warn ("Corrupt lease file - possible data loss!");
+ skip_to_semi (cfile);
+ } else {
+ struct lease *lease;
+ lease = parse_lease_declaration (cfile);
+ if (lease)
+ enter_lease (lease);
+ else
+ parse_warn ("possibly corrupt lease file");
+ }
+
+ } while (1);
+ fclose(cfile);
+}
+
+/* statement :== parameter | declaration
+
+ parameter :== timestamp
+ | DEFAULT_LEASE_TIME lease_time
+ | MAX_LEASE_TIME lease_time
+ | DYNAMIC_BOOTP_LEASE_CUTOFF date
+ | DYNAMIC_BOOTP_LEASE_LENGTH lease_time
+ | BOOT_UNKNOWN_CLIENTS boolean
+ | ONE_LEASE_PER_CLIENT boolean
+ | GET_LEASE_HOSTNAMES boolean
+ | USE_HOST_DECL_NAME boolean
+ | NEXT_SERVER ip-addr-or-hostname SEMI
+ | option_parameter
+ | SERVER-IDENTIFIER ip-addr-or-hostname SEMI
+ | FILENAME string-parameter
+ | SERVER_NAME string-parameter
+ | hardware-parameter
+ | fixed-address-parameter
+ | ALLOW allow-deny-keyword
+ | DENY allow-deny-keyword
+ | USE_LEASE_ADDR_FOR_DEFAULT_ROUTE boolean
+
+ declaration :== host-declaration
+ | group-declaration
+ | shared-network-declaration
+ | subnet-declaration
+ | VENDOR_CLASS class-declaration
+ | USER_CLASS class-declaration
+ | RANGE address-range-declaration */
+
+int parse_statement (cfile, group, type, host_decl, declaration)
+ FILE *cfile;
+ struct group *group;
+ int type;
+ struct host_decl *host_decl;
+ int declaration;
+{
+ int token;
+ char *val;
+ struct shared_network *share;
+ char *t, *n;
+ struct tree *tree;
+ struct tree_cache *cache;
+ struct hardware hardware;
+
+ switch (next_token (&val, cfile)) {
+ case HOST:
+ if (type != HOST_DECL)
+ parse_host_declaration (cfile, group);
+ else {
+ parse_warn ("host declarations not allowed here.");
+ skip_to_semi (cfile);
+ }
+ return 1;
+
+ case GROUP:
+ if (type != HOST_DECL)
+ parse_group_declaration (cfile, group);
+ else {
+ parse_warn ("host declarations not allowed here.");
+ skip_to_semi (cfile);
+ }
+ return 1;
+
+ case TIMESTAMP:
+ parsed_time = parse_timestamp (cfile);
+ break;
+
+ case SHARED_NETWORK:
+ if (type == SHARED_NET_DECL ||
+ type == HOST_DECL ||
+ type == SUBNET_DECL) {
+ parse_warn ("shared-network parameters not %s.",
+ "allowed here");
+ skip_to_semi (cfile);
+ break;
+ }
+
+ parse_shared_net_declaration (cfile, group);
+ return 1;
+
+ case SUBNET:
+ if (type == HOST_DECL || type == SUBNET_DECL) {
+ parse_warn ("subnet declarations not allowed here.");
+ skip_to_semi (cfile);
+ return 1;
+ }
+
+ /* If we're in a subnet declaration, just do the parse. */
+ if (group -> shared_network) {
+ parse_subnet_declaration (cfile,
+ group -> shared_network);
+ break;
+ }
+
+ /* Otherwise, cons up a fake shared network structure
+ and populate it with the lone subnet... */
+
+ share = new_shared_network ("parse_statement");
+ if (!share)
+ error ("No memory for shared subnet");
+ share -> group = clone_group (group, "parse_statement:subnet");
+ share -> group -> shared_network = share;
+
+ parse_subnet_declaration (cfile, share);
+
+ /* share -> subnets is the subnet we just parsed. */
+ if (share -> subnets) {
+ share -> interface =
+ share -> subnets -> interface;
+
+ /* Make the shared network name from network number. */
+ n = piaddr (share -> subnets -> net);
+ t = malloc (strlen (n) + 1);
+ if (!t)
+ error ("no memory for subnet name");
+ strlcpy (t, n, (strlen(n) + 1));
+ share -> name = t;
+
+ /* 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);
+ }
+ return 1;
+
+ case VENDOR_CLASS:
+ parse_class_declaration (cfile, group, 0);
+ return 1;
+
+ case USER_CLASS:
+ parse_class_declaration (cfile, group, 1);
+ return 1;
+
+ case DEFAULT_LEASE_TIME:
+ parse_lease_time (cfile, &group -> default_lease_time);
+ break;
+
+ case MAX_LEASE_TIME:
+ parse_lease_time (cfile, &group -> max_lease_time);
+ break;
+
+ case DYNAMIC_BOOTP_LEASE_CUTOFF:
+ group -> bootp_lease_cutoff = parse_date (cfile);
+ break;
+
+ case DYNAMIC_BOOTP_LEASE_LENGTH:
+ parse_lease_time (cfile, &group -> bootp_lease_length);
+ break;
+
+ case BOOT_UNKNOWN_CLIENTS:
+ if (type == HOST_DECL)
+ parse_warn ("boot-unknown-clients not allowed here.");
+ group -> boot_unknown_clients = parse_boolean (cfile);
+ break;
+
+ case ONE_LEASE_PER_CLIENT:
+ if (type == HOST_DECL)
+ parse_warn ("one-lease-per-client not allowed here.");
+ group -> one_lease_per_client = parse_boolean (cfile);
+ break;
+
+ case GET_LEASE_HOSTNAMES:
+ if (type == HOST_DECL)
+ parse_warn ("get-lease-hostnames not allowed here.");
+ group -> get_lease_hostnames = parse_boolean (cfile);
+ break;
+
+ case ALWAYS_REPLY_RFC1048:
+ group -> always_reply_rfc1048 = parse_boolean (cfile);
+ break;
+
+ case USE_HOST_DECL_NAMES:
+ if (type == HOST_DECL)
+ parse_warn ("use-host-decl-names not allowed here.");
+ group -> use_host_decl_names = parse_boolean (cfile);
+ break;
+
+ case USE_LEASE_ADDR_FOR_DEFAULT_ROUTE:
+ group -> use_lease_addr_for_default_route =
+ parse_boolean (cfile);
+ break;
+
+ case TOKEN_NOT:
+ token = next_token (&val, cfile);
+ switch (token) {
+ case AUTHORITATIVE:
+ if (type == HOST_DECL)
+ parse_warn ("authority makes no sense here.");
+ group -> authoritative = 0;
+ parse_semi (cfile);
+ break;
+ default:
+ parse_warn ("expecting assertion");
+ skip_to_semi (cfile);
+ break;
+ }
+ break;
+
+ case AUTHORITATIVE:
+ if (type == HOST_DECL)
+ parse_warn ("authority makes no sense here.");
+ group -> authoritative = 1;
+ parse_semi (cfile);
+ break;
+
+ case NEXT_SERVER:
+ tree = parse_ip_addr_or_hostname (cfile, 0);
+ if (!tree)
+ break;
+ cache = tree_cache (tree);
+ if (!tree_evaluate (cache))
+ error ("next-server is not known");
+ group -> next_server.len = 4;
+ memcpy (group -> next_server.iabuf,
+ cache -> value, group -> next_server.len);
+ parse_semi (cfile);
+ break;
+
+ case OPTION:
+ parse_option_param (cfile, group);
+ break;
+
+ case SERVER_IDENTIFIER:
+ tree = parse_ip_addr_or_hostname (cfile, 0);
+ if (!tree)
+ return declaration;
+ group -> options [DHO_DHCP_SERVER_IDENTIFIER] =
+ tree_cache (tree);
+ token = next_token (&val, cfile);
+ break;
+
+ case FILENAME:
+ group -> filename = parse_string (cfile);
+ break;
+
+ case SERVER_NAME:
+ group -> server_name = parse_string (cfile);
+ break;
+
+ case HARDWARE:
+ parse_hardware_param (cfile, &hardware);
+ if (host_decl)
+ host_decl -> interface = hardware;
+ else
+ parse_warn ("hardware address parameter %s",
+ "not allowed here.");
+ break;
+
+ case FIXED_ADDR:
+ cache = parse_fixed_addr_param (cfile);
+ if (host_decl)
+ host_decl -> fixed_addr = cache;
+ else
+ parse_warn ("fixed-address parameter not %s",
+ "allowed here.");
+ break;
+
+ case RANGE:
+ if (type != SUBNET_DECL || !group -> subnet) {
+ parse_warn ("range declaration not allowed here.");
+ skip_to_semi (cfile);
+ return declaration;
+ }
+ parse_address_range (cfile, group -> subnet);
+ return declaration;
+
+ case ALLOW:
+ parse_allow_deny (cfile, group, 1);
+ break;
+
+ case DENY:
+ parse_allow_deny (cfile, group, 0);
+ break;
+
+ default:
+ if (declaration)
+ parse_warn ("expecting a declaration.");
+ else
+ parse_warn ("expecting a parameter or declaration.");
+ skip_to_semi (cfile);
+ return declaration;
+ }
+
+ if (declaration) {
+ parse_warn ("parameters not allowed after first declaration.");
+ return 1;
+ }
+
+ return 0;
+}
+
+/* allow-deny-keyword :== BOOTP
+ | BOOTING
+ | DYNAMIC_BOOTP
+ | UNKNOWN_CLIENTS */
+
+void parse_allow_deny (cfile, group, flag)
+ FILE *cfile;
+ struct group *group;
+ int flag;
+{
+ int token;
+ char *val;
+
+ token = next_token (&val, cfile);
+ switch (token) {
+ case BOOTP:
+ group -> allow_bootp = flag;
+ break;
+
+ case BOOTING:
+ group -> allow_booting = flag;
+ break;
+
+ case DYNAMIC_BOOTP:
+ group -> dynamic_bootp = flag;
+ break;
+
+ case UNKNOWN_CLIENTS:
+ group -> boot_unknown_clients = flag;
+ break;
+
+ default:
+ parse_warn ("expecting allow/deny key");
+ skip_to_semi (cfile);
+ return;
+ }
+ parse_semi (cfile);
+}
+
+/* boolean :== ON SEMI | OFF SEMI | TRUE SEMI | FALSE SEMI */
+
+int parse_boolean (cfile)
+ FILE *cfile;
+{
+ int token;
+ char *val;
+ int rv;
+
+ token = next_token (&val, cfile);
+ if (!strcasecmp (val, "true")
+ || !strcasecmp (val, "on"))
+ rv = 1;
+ else if (!strcasecmp (val, "false")
+ || !strcasecmp (val, "off"))
+ rv = 0;
+ else {
+ parse_warn ("boolean value (true/false/on/off) expected");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ parse_semi (cfile);
+ return rv;
+}
+
+/* Expect a left brace; if there isn't one, skip over the rest of the
+ statement and return zero; otherwise, return 1. */
+
+int parse_lbrace (cfile)
+ FILE *cfile;
+{
+ int token;
+ char *val;
+
+ token = next_token (&val, cfile);
+ if (token != LBRACE) {
+ parse_warn ("expecting left brace.");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ return 1;
+}
+
+
+/* host-declaration :== hostname RBRACE parameters declarations LBRACE */
+
+void parse_host_declaration (cfile, group)
+ FILE *cfile;
+ struct group *group;
+{
+ char *val;
+ int token;
+ struct host_decl *host;
+ char *name = parse_host_name (cfile);
+ int declaration = 0;
+
+ if (!name)
+ return;
+
+ host = (struct host_decl *)dmalloc (sizeof (struct host_decl),
+ "parse_host_declaration");
+ if (!host)
+ error ("can't allocate host decl struct %s.", name);
+
+ host -> name = name;
+ host -> group = clone_group (group, "parse_host_declaration");
+
+ if (!parse_lbrace (cfile))
+ return;
+
+ do {
+ token = peek_token (&val, cfile);
+ if (token == RBRACE) {
+ token = next_token (&val, cfile);
+ break;
+ }
+ if (token == EOF) {
+ token = next_token (&val, cfile);
+ parse_warn ("unexpected end of file");
+ break;
+ }
+ declaration = parse_statement (cfile, host -> group,
+ HOST_DECL, host,
+ declaration);
+ } while (1);
+
+ if (!host -> group -> options [DHO_HOST_NAME] &&
+ host -> group -> use_host_decl_names) {
+ host -> group -> options [DHO_HOST_NAME] =
+ new_tree_cache ("parse_host_declaration");
+ if (!host -> group -> options [DHO_HOST_NAME])
+ error ("can't allocate a tree cache for hostname.");
+ host -> group -> options [DHO_HOST_NAME] -> len =
+ strlen (name);
+ host -> group -> options [DHO_HOST_NAME] -> value =
+ (unsigned char *)name;
+ host -> group -> options [DHO_HOST_NAME] -> buf_size =
+ host -> group -> options [DHO_HOST_NAME] -> len;
+ host -> group -> options [DHO_HOST_NAME] -> timeout =
+ 0xFFFFFFFF;
+ host -> group -> options [DHO_HOST_NAME] -> tree =
+ (struct tree *)0;
+ }
+
+ enter_host (host);
+}
+
+/* class-declaration :== STRING LBRACE parameters declarations RBRACE
+*/
+
+void parse_class_declaration (cfile, group, type)
+ FILE *cfile;
+ struct group *group;
+ int type;
+{
+ char *val;
+ int token;
+ struct class *class;
+ int declaration = 0;
+
+ token = next_token (&val, cfile);
+ if (token != STRING) {
+ parse_warn ("Expecting class name");
+ skip_to_semi (cfile);
+ return;
+ }
+
+ class = add_class (type, val);
+ if (!class)
+ error ("No memory for class %s.", val);
+ class -> group = clone_group (group, "parse_class_declaration");
+
+ if (!parse_lbrace (cfile))
+ return;
+
+ do {
+ token = peek_token (&val, cfile);
+ if (token == RBRACE) {
+ token = next_token (&val, cfile);
+ break;
+ } else if (token == EOF) {
+ token = next_token (&val, cfile);
+ parse_warn ("unexpected end of file");
+ break;
+ } else {
+ declaration = parse_statement (cfile, class -> group,
+ CLASS_DECL,
+ (struct host_decl *)0,
+ declaration);
+ }
+ } while (1);
+}
+
+/* shared-network-declaration :==
+ hostname LBRACE declarations parameters RBRACE */
+
+void parse_shared_net_declaration (cfile, group)
+ FILE *cfile;
+ struct group *group;
+{
+ char *val;
+ int token;
+ struct shared_network *share;
+ char *name;
+ int declaration = 0;
+
+ share = new_shared_network ("parse_shared_net_declaration");
+ if (!share)
+ error ("No memory for shared subnet");
+ share -> leases = (struct lease *)0;
+ share -> last_lease = (struct lease *)0;
+ share -> insertion_point = (struct lease *)0;
+ share -> next = (struct shared_network *)0;
+ share -> interface = (struct interface_info *)0;
+ share -> group = clone_group (group, "parse_shared_net_declaration");
+ share -> group -> shared_network = share;
+
+ /* Get the name of the shared network... */
+ token = peek_token (&val, cfile);
+ if (token == STRING) {
+ token = next_token (&val, cfile);
+
+ if (val [0] == 0) {
+ parse_warn ("zero-length shared network name");
+ val = "<no-name-given>";
+ }
+ name = malloc (strlen (val) + 1);
+ if (!name)
+ error ("no memory for shared network name");
+ strlcpy (name, val, strlen(val) + 1);
+ } else {
+ name = parse_host_name (cfile);
+ if (!name)
+ return;
+ }
+ share -> name = name;
+
+ if (!parse_lbrace (cfile))
+ return;
+
+ do {
+ token = peek_token (&val, cfile);
+ if (token == RBRACE) {
+ token = next_token (&val, cfile);
+ if (!share -> subnets) {
+ parse_warn ("empty shared-network decl");
+ return;
+ }
+ enter_shared_network (share);
+ return;
+ } else if (token == EOF) {
+ token = next_token (&val, cfile);
+ parse_warn ("unexpected end of file");
+ break;
+ }
+
+ declaration = parse_statement (cfile, share -> group,
+ SHARED_NET_DECL,
+ (struct host_decl *)0,
+ declaration);
+ } while (1);
+}
+
+/* subnet-declaration :==
+ net NETMASK netmask RBRACE parameters declarations LBRACE */
+
+void parse_subnet_declaration (cfile, share)
+ FILE *cfile;
+ struct shared_network *share;
+{
+ char *val;
+ int token;
+ struct subnet *subnet, *t, *u;
+ struct iaddr iaddr;
+ unsigned char addr [4];
+ int len = sizeof addr;
+ int declaration = 0;
+
+ subnet = new_subnet ("parse_subnet_declaration");
+ if (!subnet)
+ error ("No memory for new subnet");
+ subnet -> shared_network = share;
+ subnet -> group = clone_group (share -> group,
+ "parse_subnet_declaration");
+ subnet -> group -> subnet = subnet;
+
+ /* Get the network number... */
+ if (!parse_numeric_aggregate (cfile, addr, &len, DOT, 10, 8))
+ return;
+ memcpy (iaddr.iabuf, addr, len);
+ iaddr.len = len;
+ subnet -> net = iaddr;
+
+ token = next_token (&val, cfile);
+ if (token != NETMASK) {
+ parse_warn ("Expecting netmask");
+ skip_to_semi (cfile);
+ return;
+ }
+
+ /* Get the netmask... */
+ if (!parse_numeric_aggregate (cfile, addr, &len, DOT, 10, 8))
+ return;
+ memcpy (iaddr.iabuf, addr, len);
+ iaddr.len = len;
+ subnet -> netmask = iaddr;
+
+ enter_subnet (subnet);
+
+ if (!parse_lbrace (cfile))
+ return;
+
+ do {
+ token = peek_token (&val, cfile);
+ if (token == RBRACE) {
+ token = next_token (&val, cfile);
+ break;
+ } else if (token == EOF) {
+ token = next_token (&val, cfile);
+ parse_warn ("unexpected end of file");
+ break;
+ }
+ declaration = parse_statement (cfile, subnet -> group,
+ SUBNET_DECL,
+ (struct host_decl *)0,
+ declaration);
+ } while (1);
+
+ /* If this subnet supports dynamic bootp, flag it so in the
+ shared_network containing it. */
+ if (subnet -> group -> dynamic_bootp)
+ share -> group -> dynamic_bootp = 1;
+ if (subnet -> group -> one_lease_per_client)
+ share -> group -> one_lease_per_client = 1;
+
+ /* Add the subnet to the list of subnets in this shared net. */
+ if (!share -> subnets)
+ share -> subnets = subnet;
+ else {
+ u = (struct subnet *)0;
+ for (t = share -> subnets; t; t = t -> next_sibling) {
+ if (subnet_inner_than (subnet, t, 0)) {
+ if (u)
+ u -> next_sibling = subnet;
+ else
+ share -> subnets = subnet;
+ subnet -> next_sibling = t;
+ return;
+ }
+ u = t;
+ }
+ u -> next_sibling = subnet;
+ }
+}
+
+/* group-declaration :== RBRACE parameters declarations LBRACE */
+
+void parse_group_declaration (cfile, group)
+ FILE *cfile;
+ struct group *group;
+{
+ char *val;
+ int token;
+ struct group *g;
+ int declaration = 0;
+
+ g = clone_group (group, "parse_group_declaration");
+
+ if (!parse_lbrace (cfile))
+ return;
+
+ do {
+ token = peek_token (&val, cfile);
+ if (token == RBRACE) {
+ token = next_token (&val, cfile);
+ break;
+ } else if (token == EOF) {
+ token = next_token (&val, cfile);
+ parse_warn ("unexpected end of file");
+ break;
+ }
+ declaration = parse_statement (cfile, g, GROUP_DECL,
+ (struct host_decl *)0,
+ declaration);
+ } while (1);
+}
+
+/* ip-addr-or-hostname :== ip-address | hostname
+ ip-address :== NUMBER DOT NUMBER DOT NUMBER DOT NUMBER
+
+ Parse an ip address or a hostname. If uniform is zero, put in
+ a TREE_LIMIT node to catch hostnames that evaluate to more than
+ one IP address. */
+
+struct tree *parse_ip_addr_or_hostname (cfile, uniform)
+ FILE *cfile;
+ int uniform;
+{
+ char *val;
+ int token;
+ unsigned char addr [4];
+ int len = sizeof addr;
+ char *name;
+ struct tree *rv;
+
+ token = peek_token (&val, cfile);
+ if (is_identifier (token)) {
+ name = parse_host_name (cfile);
+ if (!name)
+ return (struct tree *)0;
+ rv = tree_host_lookup (name);
+ if (!uniform)
+ rv = tree_limit (rv, 4);
+ } else if (token == NUMBER) {
+ if (!parse_numeric_aggregate (cfile, addr, &len, DOT, 10, 8))
+ return (struct tree *)0;
+ rv = tree_const (addr, len);
+ } else {
+ if (token != RBRACE && token != LBRACE)
+ token = next_token (&val, cfile);
+ parse_warn ("%s (%d): expecting IP address or hostname",
+ val, token);
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ return (struct tree *)0;
+ }
+
+ return rv;
+}
+
+
+/* fixed-addr-parameter :== ip-addrs-or-hostnames SEMI
+ ip-addrs-or-hostnames :== ip-addr-or-hostname
+ | ip-addrs-or-hostnames ip-addr-or-hostname */
+
+struct tree_cache *parse_fixed_addr_param (cfile)
+ FILE *cfile;
+{
+ char *val;
+ int token;
+ struct tree *tree = (struct tree *)0;
+ struct tree *tmp;
+
+ do {
+ tmp = parse_ip_addr_or_hostname (cfile, 0);
+ if (tree)
+ tree = tree_concat (tree, tmp);
+ else
+ tree = tmp;
+ token = peek_token (&val, cfile);
+ if (token == COMMA)
+ token = next_token (&val, cfile);
+ } while (token == COMMA);
+
+ if (!parse_semi (cfile))
+ return (struct tree_cache *)0;
+ return tree_cache (tree);
+}
+
+/* option_parameter :== identifier DOT identifier <syntax> SEMI
+ | identifier <syntax> SEMI
+
+ Option syntax is handled specially through format strings, so it
+ would be painful to come up with BNF for it. However, it always
+ starts as above and ends in a SEMI. */
+
+void parse_option_param (cfile, group)
+ FILE *cfile;
+ struct group *group;
+{
+ char *val;
+ int token;
+ unsigned char buf [4];
+ char *vendor;
+ char *fmt;
+ struct universe *universe;
+ struct option *option;
+ struct tree *tree = (struct tree *)0;
+ struct tree *t;
+
+ token = next_token (&val, cfile);
+ if (!is_identifier (token)) {
+ parse_warn ("expecting identifier after option keyword.");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ return;
+ }
+ vendor = malloc (strlen (val) + 1);
+ if (!vendor)
+ error ("no memory for vendor token.");
+ strlcpy (vendor, val, strlen(val) + 1);
+ token = peek_token (&val, cfile);
+ if (token == DOT) {
+ /* Go ahead and take the DOT token... */
+ token = next_token (&val, cfile);
+
+ /* The next token should be an identifier... */
+ token = next_token (&val, cfile);
+ if (!is_identifier (token)) {
+ parse_warn ("expecting identifier after '.'");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ return;
+ }
+
+ /* Look up the option name hash table for the specified
+ vendor. */
+ universe = ((struct universe *)
+ hash_lookup (&universe_hash,
+ (unsigned char *)vendor, 0));
+ /* If it's not there, we can't parse the rest of the
+ declaration. */
+ if (!universe) {
+ parse_warn ("no vendor named %s.", vendor);
+ skip_to_semi (cfile);
+ return;
+ }
+ } else {
+ /* Use the default hash table, which contains all the
+ standard dhcp option names. */
+ val = vendor;
+ universe = &dhcp_universe;
+ }
+
+ /* Look up the actual option info... */
+ option = (struct option *)hash_lookup (universe -> hash,
+ (unsigned char *)val, 0);
+
+ /* If we didn't get an option structure, it's an undefined option. */
+ if (!option) {
+ if (val == vendor)
+ parse_warn ("no option named %s", val);
+ else
+ parse_warn ("no option named %s for vendor %s",
+ val, vendor);
+ skip_to_semi (cfile);
+ return;
+ }
+
+ /* Free the initial identifier token. */
+ free (vendor);
+
+ /* Parse the option data... */
+ do {
+ /* Set a flag if this is an array of a simple type (i.e.,
+ not an array of pairs of IP addresses, or something
+ like that. */
+ int uniform = option -> format [1] == 'A';
+
+ for (fmt = option -> format; *fmt; fmt++) {
+ if (*fmt == 'A')
+ break;
+ switch (*fmt) {
+ case 'X':
+ token = peek_token (&val, cfile);
+ if (token == NUMBER_OR_NAME ||
+ token == NUMBER) {
+ do {
+ token = next_token
+ (&val, cfile);
+ if (token != NUMBER
+ && token != NUMBER_OR_NAME)
+ goto need_number;
+ convert_num (buf, val, 16, 8);
+ tree = tree_concat
+ (tree,
+ tree_const (buf, 1));
+ token = peek_token
+ (&val, cfile);
+ if (token == COLON)
+ token = next_token
+ (&val, cfile);
+ } while (token == COLON);
+ } else if (token == STRING) {
+ token = next_token (&val, cfile);
+ tree = tree_concat
+ (tree,
+ tree_const ((unsigned char *)
+ val,
+ strlen (val)));
+ } else {
+ parse_warn ("expecting string %s.",
+ "or hexadecimal data");
+ skip_to_semi (cfile);
+ return;
+ }
+ break;
+
+ case 't': /* Text string... */
+ token = next_token (&val, cfile);
+ if (token != STRING
+ && !is_identifier (token)) {
+ parse_warn ("expecting string.");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ return;
+ }
+ tree = tree_concat
+ (tree,
+ tree_const ((unsigned char *)val,
+ strlen (val)));
+ break;
+
+ case 'I': /* IP address or hostname. */
+ t = parse_ip_addr_or_hostname (cfile, uniform);
+ if (!t)
+ return;
+ tree = tree_concat (tree, t);
+ break;
+
+ case 'L': /* Unsigned 32-bit integer... */
+ case 'l': /* Signed 32-bit integer... */
+ token = next_token (&val, cfile);
+ if (token != NUMBER) {
+ need_number:
+ parse_warn ("expecting number.");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ return;
+ }
+ convert_num (buf, val, 0, 32);
+ tree = tree_concat (tree, tree_const (buf, 4));
+ break;
+ case 's': /* Signed 16-bit integer. */
+ case 'S': /* Unsigned 16-bit integer. */
+ token = next_token (&val, cfile);
+ if (token != NUMBER)
+ goto need_number;
+ convert_num (buf, val, 0, 16);
+ tree = tree_concat (tree, tree_const (buf, 2));
+ break;
+ case 'b': /* Signed 8-bit integer. */
+ case 'B': /* Unsigned 8-bit integer. */
+ token = next_token (&val, cfile);
+ if (token != NUMBER)
+ goto need_number;
+ convert_num (buf, val, 0, 8);
+ tree = tree_concat (tree, tree_const (buf, 1));
+ break;
+ case 'f': /* Boolean flag. */
+ token = next_token (&val, cfile);
+ if (!is_identifier (token)) {
+ parse_warn ("expecting identifier.");
+ bad_flag:
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ return;
+ }
+ if (!strcasecmp (val, "true")
+ || !strcasecmp (val, "on"))
+ buf [0] = 1;
+ else if (!strcasecmp (val, "false")
+ || !strcasecmp (val, "off"))
+ buf [0] = 0;
+ else {
+ parse_warn ("expecting boolean.");
+ goto bad_flag;
+ }
+ tree = tree_concat (tree, tree_const (buf, 1));
+ break;
+ default:
+ warn ("Bad format %c in parse_option_param.",
+ *fmt);
+ skip_to_semi (cfile);
+ return;
+ }
+ }
+ if (*fmt == 'A') {
+ token = peek_token (&val, cfile);
+ if (token == COMMA) {
+ token = next_token (&val, cfile);
+ continue;
+ }
+ break;
+ }
+ } while (*fmt == 'A');
+
+ token = next_token (&val, cfile);
+ if (token != SEMI) {
+ parse_warn ("semicolon expected.");
+ skip_to_semi (cfile);
+ return;
+ }
+ group -> options [option -> code] = tree_cache (tree);
+}
+
+/* timestamp :== date
+
+ Timestamps are actually not used in dhcpd.conf, which is a static file,
+ but rather in the database file and the journal file. (Okay, actually
+ they're not even used there yet). */
+
+TIME parse_timestamp (cfile)
+ FILE *cfile;
+{
+ TIME rv;
+
+ rv = parse_date (cfile);
+ return rv;
+}
+
+/* lease_declaration :== LEASE ip_address LBRACE lease_parameters RBRACE
+
+ lease_parameters :== <nil>
+ | lease_parameter
+ | lease_parameters lease_parameter
+
+ lease_parameter :== STARTS date
+ | ENDS date
+ | TIMESTAMP date
+ | HARDWARE hardware-parameter
+ | UID hex_numbers SEMI
+ | HOSTNAME hostname SEMI
+ | CLIENT_HOSTNAME hostname SEMI
+ | CLASS identifier SEMI
+ | DYNAMIC_BOOTP SEMI */
+
+struct lease *parse_lease_declaration (cfile)
+ FILE *cfile;
+{
+ char *val;
+ int token;
+ unsigned char addr [4];
+ int len = sizeof addr;
+ int seenmask = 0;
+ int seenbit;
+ char tbuf [32];
+ static struct lease lease;
+
+ /* Zap the lease structure... */
+ memset (&lease, 0, sizeof lease);
+
+ /* Get the address for which the lease has been issued. */
+ if (!parse_numeric_aggregate (cfile, addr, &len, DOT, 10, 8))
+ return (struct lease *)0;
+ memcpy (lease.ip_addr.iabuf, addr, len);
+ lease.ip_addr.len = len;
+
+ if (!parse_lbrace (cfile))
+ return (struct lease *)0;
+
+ do {
+ token = next_token (&val, cfile);
+ if (token == RBRACE)
+ break;
+ else if (token == EOF) {
+ parse_warn ("unexpected end of file");
+ break;
+ }
+ strlcpy (tbuf, val, sizeof tbuf);
+
+ /* Parse any of the times associated with the lease. */
+ if (token == STARTS || token == ENDS || token == TIMESTAMP) {
+ TIME t;
+ t = parse_date (cfile);
+ switch (token) {
+ case STARTS:
+ seenbit = 1;
+ lease.starts = t;
+ break;
+
+ case ENDS:
+ seenbit = 2;
+ lease.ends = t;
+ break;
+
+ case TIMESTAMP:
+ seenbit = 4;
+ lease.timestamp = t;
+ break;
+
+ default:
+ /*NOTREACHED*/
+ seenbit = 0;
+ break;
+ }
+ } else {
+ switch (token) {
+ /* Colon-separated hexadecimal octets... */
+ case UID:
+ seenbit = 8;
+ token = peek_token (&val, cfile);
+ if (token == STRING) {
+ token = next_token (&val, cfile);
+ lease.uid_len = strlen (val);
+ lease.uid = (unsigned char *)
+ malloc (lease.uid_len);
+ if (!lease.uid) {
+ warn ("no space for uid");
+ return (struct lease *)0;
+ }
+ memcpy (lease.uid, val, lease.uid_len);
+ parse_semi (cfile);
+ } else {
+ lease.uid_len = 0;
+ lease.uid = parse_numeric_aggregate
+ (cfile, (unsigned char *)0,
+ &lease.uid_len, ':', 16, 8);
+ if (!lease.uid) {
+ warn ("no space for uid");
+ return (struct lease *)0;
+ }
+ if (lease.uid_len == 0) {
+ lease.uid = (unsigned char *)0;
+ parse_warn ("zero-length uid");
+ seenbit = 0;
+ break;
+ }
+ }
+ if (!lease.uid) {
+ error ("No memory for lease uid");
+ }
+ break;
+
+ case CLASS:
+ seenbit = 32;
+ token = next_token (&val, cfile);
+ if (!is_identifier (token)) {
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ return (struct lease *)0;
+ }
+ /* for now, we aren't using this. */
+ break;
+
+ case HARDWARE:
+ seenbit = 64;
+ parse_hardware_param (cfile,
+ &lease.hardware_addr);
+ break;
+
+ case DYNAMIC_BOOTP:
+ seenbit = 128;
+ lease.flags |= BOOTP_LEASE;
+ break;
+
+ case ABANDONED:
+ seenbit = 256;
+ lease.flags |= ABANDONED_LEASE;
+ break;
+
+ case HOSTNAME:
+ seenbit = 512;
+ token = peek_token (&val, cfile);
+ if (token == STRING)
+ lease.hostname = parse_string (cfile);
+ else
+ lease.hostname =
+ parse_host_name (cfile);
+ if (!lease.hostname) {
+ seenbit = 0;
+ return (struct lease *)0;
+ }
+ break;
+
+ case CLIENT_HOSTNAME:
+ seenbit = 1024;
+ token = peek_token (&val, cfile);
+ if (token == STRING)
+ lease.client_hostname =
+ parse_string (cfile);
+ else
+ lease.client_hostname =
+ parse_host_name (cfile);
+ break;
+
+ default:
+ skip_to_semi (cfile);
+ seenbit = 0;
+ return (struct lease *)0;
+ }
+
+ if (token != HARDWARE && token != STRING) {
+ token = next_token (&val, cfile);
+ if (token != SEMI) {
+ parse_warn ("semicolon expected.");
+ skip_to_semi (cfile);
+ return (struct lease *)0;
+ }
+ }
+ }
+ if (seenmask & seenbit) {
+ parse_warn ("Too many %s parameters in lease %s\n",
+ tbuf, piaddr (lease.ip_addr));
+ } else
+ seenmask |= seenbit;
+
+ } while (1);
+ return &lease;
+}
+
+/* address-range-declaration :== ip-address ip-address SEMI
+ | DYNAMIC_BOOTP ip-address ip-address SEMI */
+
+void parse_address_range (cfile, subnet)
+ FILE *cfile;
+ struct subnet *subnet;
+{
+ struct iaddr low, high;
+ unsigned char addr [4];
+ int len = sizeof addr;
+ int token;
+ char *val;
+ int dynamic = 0;
+
+ if ((token = peek_token (&val, cfile)) == DYNAMIC_BOOTP) {
+ token = next_token (&val, cfile);
+ subnet -> group -> dynamic_bootp = dynamic = 1;
+ }
+
+ /* Get the bottom address in the range... */
+ if (!parse_numeric_aggregate (cfile, addr, &len, DOT, 10, 8))
+ return;
+ memcpy (low.iabuf, addr, len);
+ low.len = len;
+
+ /* Only one address? */
+ token = peek_token (&val, cfile);
+ if (token == SEMI)
+ high = low;
+ else {
+ /* Get the top address in the range... */
+ if (!parse_numeric_aggregate (cfile, addr, &len, DOT, 10, 8))
+ return;
+ memcpy (high.iabuf, addr, len);
+ high.len = len;
+ }
+
+ token = next_token (&val, cfile);
+ if (token != SEMI) {
+ parse_warn ("semicolon expected.");
+ skip_to_semi (cfile);
+ return;
+ }
+
+ /* Create the new address range... */
+ new_address_range (low, high, subnet, dynamic);
+}
+
+
diff --git a/usr.sbin/dhcpd/convert.c b/usr.sbin/dhcpd/convert.c
new file mode 100644
index 00000000000..12afe87a40a
--- /dev/null
+++ b/usr.sbin/dhcpd/convert.c
@@ -0,0 +1,113 @@
+/* convert.c
+
+ Safe copying of option values into and out of the option buffer, which
+ can't be assumed to be aligned. */
+
+/*
+ * Copyright (c) 1995, 1996 The Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include "dhcpd.h"
+
+u_int32_t getULong (buf)
+ unsigned char *buf;
+{
+ u_int32_t ibuf;
+
+ memcpy (&ibuf, buf, sizeof (ibuf));
+ return ntohl (ibuf);
+}
+
+int32_t getLong (buf)
+ unsigned char *buf;
+{
+ int32_t ibuf;
+
+ memcpy (&ibuf, buf, sizeof (ibuf));
+ return ntohl (ibuf);
+}
+
+u_int16_t getUShort (buf)
+ unsigned char *buf;
+{
+ u_int16_t ibuf;
+
+ memcpy (&ibuf, buf, sizeof (ibuf));
+ return ntohs (ibuf);
+}
+
+int16_t getShort (buf)
+ unsigned char *buf;
+{
+ int16_t ibuf;
+
+ memcpy (&ibuf, buf, sizeof (ibuf));
+ return ntohs (ibuf);
+}
+
+void putULong (obuf, val)
+ unsigned char *obuf;
+ u_int32_t val;
+{
+ u_int32_t tmp = htonl (val);
+ memcpy (obuf, &tmp, sizeof tmp);
+}
+
+void putLong (obuf, val)
+ unsigned char *obuf;
+ int32_t val;
+{
+ int32_t tmp = htonl (val);
+ memcpy (obuf, &tmp, sizeof tmp);
+}
+
+void putUShort (obuf, val)
+ unsigned char *obuf;
+ unsigned int val;
+{
+ u_int16_t tmp = htons (val);
+ memcpy (obuf, &tmp, sizeof tmp);
+}
+
+void putShort (obuf, val)
+ unsigned char *obuf;
+ int val;
+{
+ int16_t tmp = htons (val);
+ memcpy (obuf, &tmp, sizeof tmp);
+}
+
diff --git a/usr.sbin/dhcpd/db.c b/usr.sbin/dhcpd/db.c
new file mode 100644
index 00000000000..ae39a2ec24e
--- /dev/null
+++ b/usr.sbin/dhcpd/db.c
@@ -0,0 +1,255 @@
+/* db.c
+
+ Persistent database management routines for DHCPD... */
+
+/*
+ * Copyright (c) 1995, 1996 The Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include "dhcpd.h"
+
+FILE *db_file;
+
+static int counting = 0;
+static int count = 0;
+TIME write_time;
+
+/* Write the specified lease to the current lease database file. */
+
+int write_lease (lease)
+ struct lease *lease;
+{
+ struct tm *t;
+ char tbuf [64];
+ int errors = 0;
+ int i;
+
+ if (counting)
+ ++count;
+ errno = 0;
+ fprintf (db_file, "lease %s {\n", piaddr (lease -> ip_addr));
+ if (errno) {
+ ++errors;
+ }
+
+ t = gmtime (&lease -> starts);
+ snprintf (tbuf, sizeof tbuf, "%d %d/%02d/%02d %02d:%02d:%02d;",
+ t -> tm_wday, t -> tm_year + 1900,
+ t -> tm_mon + 1, t -> tm_mday,
+ t -> tm_hour, t -> tm_min, t -> tm_sec);
+ errno = 0;
+ fprintf (db_file, "\tstarts %s\n", tbuf);
+ if (errno) {
+ ++errors;
+ }
+
+ t = gmtime (&lease -> ends);
+ snprintf (tbuf, sizeof tbuf,"%d %d/%02d/%02d %02d:%02d:%02d;",
+ t -> tm_wday, t -> tm_year + 1900,
+ t -> tm_mon + 1, t -> tm_mday,
+ t -> tm_hour, t -> tm_min, t -> tm_sec);
+ errno = 0;
+ fprintf (db_file, "\tends %s", tbuf);
+ if (errno) {
+ ++errors;
+ }
+
+ if (lease -> hardware_addr.hlen) {
+ errno = 0;
+ fprintf (db_file, "\n\thardware %s %s;",
+ hardware_types [lease -> hardware_addr.htype],
+ print_hw_addr (lease -> hardware_addr.htype,
+ lease -> hardware_addr.hlen,
+ lease -> hardware_addr.haddr));
+ if (errno) {
+ ++errors;
+ }
+ }
+ if (lease -> uid_len) {
+ int i;
+ errno = 0;
+ fprintf (db_file, "\n\tuid %2.2x", lease -> uid [0]);
+ if (errno) {
+ ++errors;
+ }
+ for (i = 1; i < lease -> uid_len; i++) {
+ errno = 0;
+ fprintf (db_file, ":%2.2x", lease -> uid [i]);
+ if (errno) {
+ ++errors;
+ }
+ }
+ putc (';', db_file);
+ }
+ if (lease -> flags & BOOTP_LEASE) {
+ errno = 0;
+ fprintf (db_file, "\n\tdynamic-bootp;");
+ if (errno) {
+ ++errors;
+ }
+ }
+ if (lease -> flags & ABANDONED_LEASE) {
+ errno = 0;
+ fprintf (db_file, "\n\tabandoned;");
+ if (errno) {
+ ++errors;
+ }
+ }
+ if (lease -> client_hostname) {
+ for (i = 0; lease -> client_hostname [i]; i++)
+ if (lease -> client_hostname [i] < 33 ||
+ lease -> client_hostname [i] > 126)
+ goto bad_client_hostname;
+ errno = 0;
+ fprintf (db_file, "\n\tclient-hostname \"%s\";",
+ lease -> client_hostname);
+ if (errno) {
+ ++errors;
+ }
+ }
+ bad_client_hostname:
+ if (lease -> hostname) {
+ for (i = 0; lease -> hostname [i]; i++)
+ if (lease -> hostname [i] < 33 ||
+ lease -> hostname [i] > 126)
+ goto bad_hostname;
+ errno = 0;
+ errno = 0;
+ fprintf (db_file, "\n\thostname \"%s\";",
+ lease -> hostname);
+ if (errno) {
+ ++errors;
+ }
+ }
+ bad_hostname:
+ errno = 0;
+ fputs ("\n}\n", db_file);
+ if (errno) {
+ ++errors;
+ }
+ if (errors)
+ note ("write_lease: unable to write lease %s",
+ piaddr (lease -> ip_addr));
+ return !errors;
+}
+
+/* Commit any leases that have been written out... */
+
+int commit_leases ()
+{
+ /* Commit any outstanding writes to the lease database file.
+ We need to do this even if we're rewriting the file below,
+ just in case the rewrite fails. */
+ if (fflush (db_file) == EOF) {
+ note ("commit_leases: unable to commit: %m");
+ return 0;
+ }
+ if (fsync (fileno (db_file)) == -1) {
+ note ("commit_leases: unable to commit: %m");
+ return 0;
+ }
+
+ /* If we've written more than a thousand leases or if
+ we haven't rewritten the lease database in over an
+ hour, rewrite it now. */
+ if (count > 1000 || (count && cur_time - write_time > 3600)) {
+ count = 0;
+ write_time = cur_time;
+ new_lease_file ();
+ }
+ return 1;
+}
+
+void db_startup ()
+{
+ /* Read in the existing lease file... */
+ read_leases ();
+
+ GET_TIME (&write_time);
+ new_lease_file ();
+}
+
+void new_lease_file ()
+{
+ char newfname [MAXPATHLEN];
+ char backfname [MAXPATHLEN];
+ TIME t;
+ int db_fd;
+
+ /* If we already have an open database, close it. */
+ if (db_file) {
+ fclose (db_file);
+ }
+
+ /* Make a temporary lease file... */
+ GET_TIME (&t);
+ snprintf (newfname, sizeof newfname,"%s.%d", path_dhcpd_db, (int)t);
+ db_fd = open (newfname, O_WRONLY | O_TRUNC | O_CREAT, 0664);
+ if (db_fd == -1) {
+ error ("Can't create new lease file: %m");
+ }
+ if ((db_file = fdopen (db_fd, "w")) == NULL) {
+ error ("Can't fdopen new lease file!");
+ }
+
+ /* Write an introduction so people don't complain about time
+ being off. */
+ fprintf (db_file, "# All times in this file are in UTC (GMT), not %s",
+ "your local timezone.\n");
+ fprintf (db_file, "# The format of this file is documented in the %s",
+ "dhcpd.leases(5) manual page.\n\n");
+
+ /* Write out all the leases that we know of... */
+ counting = 0;
+ write_leases ();
+
+ /* Get the old database out of the way... */
+ snprintf (backfname, sizeof backfname, "%s~", path_dhcpd_db);
+ if (unlink (backfname) == -1 && errno != ENOENT)
+ error ("Can't remove old lease database backup %s: %m",
+ backfname);
+ if (link (path_dhcpd_db, backfname) == -1)
+ error ("Can't backup lease database %s to %s: %m",
+ path_dhcpd_db, backfname);
+
+ /* Move in the new file... */
+ if (rename (newfname, path_dhcpd_db) == -1)
+ error ("Can't install new lease database %s to %s: %m",
+ newfname, path_dhcpd_db);
+
+ counting = 1;
+}
diff --git a/usr.sbin/dhcpd/dhcp.c b/usr.sbin/dhcpd/dhcp.c
new file mode 100644
index 00000000000..eca34e64d60
--- /dev/null
+++ b/usr.sbin/dhcpd/dhcp.c
@@ -0,0 +1,1732 @@
+/* dhcp.c
+
+ DHCP Protocol engine. */
+
+/*
+ * Copyright (c) 1995, 1996, 1997, 1998, 1999
+ * The Internet Software Consortium. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include "dhcpd.h"
+
+int outstanding_pings;
+
+static char dhcp_message [256];
+
+void dhcp (packet)
+ struct packet *packet;
+{
+ if (!locate_network (packet) && packet -> packet_type != DHCPREQUEST)
+ return;
+
+ switch (packet -> packet_type) {
+ case DHCPDISCOVER:
+ dhcpdiscover (packet);
+ break;
+
+ case DHCPREQUEST:
+ dhcprequest (packet);
+ break;
+
+ case DHCPRELEASE:
+ dhcprelease (packet);
+ break;
+
+ case DHCPDECLINE:
+ dhcpdecline (packet);
+ break;
+
+ case DHCPINFORM:
+ dhcpinform (packet);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void dhcpdiscover (packet)
+ struct packet *packet;
+{
+ struct lease *lease = find_lease (packet, packet -> shared_network, 0);
+ struct host_decl *hp;
+
+ note ("DHCPDISCOVER from %s via %s",
+ print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr),
+ packet -> raw -> giaddr.s_addr
+ ? inet_ntoa (packet -> raw -> giaddr)
+ : packet -> interface -> name);
+
+ /* Sourceless packets don't make sense here. */
+ if (!packet -> shared_network) {
+ note ("Packet from unknown subnet: %s",
+ inet_ntoa (packet -> raw -> giaddr));
+ return;
+ }
+
+ /* If we didn't find a lease, try to allocate one... */
+ if (!lease) {
+ lease = packet -> shared_network -> last_lease;
+
+ /* If there are no leases in that subnet that have
+ expired, we have nothing to offer this client. */
+ if (!lease || lease -> ends > cur_time) {
+ note ("no free leases on subnet %s",
+ packet -> shared_network -> name);
+ return;
+ }
+
+ /* If we find an abandoned lease, take it, but print a
+ warning message, so that if it continues to lose,
+ the administrator will eventually investigate. */
+ if ((lease -> flags & ABANDONED_LEASE)) {
+ struct lease *lp;
+
+ /* See if we can find an unabandoned lease first. */
+ for (lp = lease; lp; lp = lp -> prev) {
+ if (lp -> ends > cur_time)
+ break;
+ if (!(lp -> flags & ABANDONED_LEASE)) {
+ lease = lp;
+ break;
+ }
+ }
+
+ /* If we can't find an unabandoned lease, reclaim the
+ abandoned lease. */
+ if ((lease -> flags & ABANDONED_LEASE)) {
+ warn ("Reclaiming abandoned IP address %s.",
+ piaddr (lease -> ip_addr));
+ lease -> flags &= ~ABANDONED_LEASE;
+ }
+ }
+
+ /* Try to find a host_decl that matches the client
+ identifier or hardware address on the packet, and
+ has no fixed IP address. If there is one, hang
+ it off the lease so that its option definitions
+ can be used. */
+ if (((packet -> options [DHO_DHCP_CLIENT_IDENTIFIER].len
+ != 0) &&
+ ((hp = find_hosts_by_uid
+ (packet -> options [DHO_DHCP_CLIENT_IDENTIFIER].data,
+ packet -> options [DHO_DHCP_CLIENT_IDENTIFIER].len))
+ != (struct host_decl *)0)) ||
+ ((hp = find_hosts_by_haddr (packet -> raw -> htype,
+ packet -> raw -> chaddr,
+ packet -> raw -> hlen))
+ != (struct host_decl *)0)) {
+ for (; hp; hp = hp -> n_ipaddr) {
+ if (!hp -> fixed_addr) {
+ lease -> host = hp;
+ break;
+ }
+ }
+ } else {
+ lease -> host = (struct host_decl *)0;
+ }
+ }
+
+ /* If this subnet won't boot unknown clients, ignore the
+ request. */
+ if (!lease -> host &&
+ !lease -> subnet -> group -> boot_unknown_clients) {
+ note ("Ignoring unknown client %s",
+ print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr));
+ } else if (lease -> host &&
+ !lease -> host -> group -> allow_booting) {
+ note ("Declining to boot client %s",
+ lease -> host -> name
+ ? lease -> host -> name
+ : print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr));
+ } else
+ ack_lease (packet, lease, DHCPOFFER, cur_time + 120);
+}
+
+void dhcprequest (packet)
+ struct packet *packet;
+{
+ struct lease *lease;
+ struct iaddr cip;
+ struct subnet *subnet;
+ int ours = 0;
+
+ if (packet -> options [DHO_DHCP_REQUESTED_ADDRESS].len) {
+ cip.len = 4;
+ memcpy (cip.iabuf,
+ packet -> options [DHO_DHCP_REQUESTED_ADDRESS].data,
+ 4);
+ } else {
+ cip.len = 4;
+ memcpy (cip.iabuf, &packet -> raw -> ciaddr.s_addr, 4);
+ }
+ subnet = find_subnet (cip);
+
+ /* Find the lease that matches the address requested by the
+ client. */
+
+ if (subnet)
+ lease = find_lease (packet, subnet -> shared_network, &ours);
+ else
+ lease = (struct lease *)0;
+
+ note ("DHCPREQUEST for %s from %s via %s",
+ piaddr (cip),
+ print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr),
+ packet -> raw -> giaddr.s_addr
+ ? inet_ntoa (packet -> raw -> giaddr)
+ : packet -> interface -> name);
+
+ /* If a client on a given network REQUESTs a lease on an
+ address on a different network, NAK it. If the Requested
+ Address option was used, the protocol says that it must
+ have been broadcast, so we can trust the source network
+ information.
+
+ If ciaddr was specified and Requested Address was not, then
+ we really only know for sure what network a packet came from
+ if it came through a BOOTP gateway - if it came through an
+ IP router, we'll just have to assume that it's cool.
+
+ If we don't think we know where the packet came from, it
+ came through a gateway from an unknown network, so it's not
+ from a RENEWING client. If we recognize the network it
+ *thinks* it's on, we can NAK it even though we don't
+ recognize the network it's *actually* on; otherwise we just
+ have to ignore it.
+
+ We don't currently try to take advantage of access to the
+ raw packet, because it's not available on all platforms.
+ So a packet that was unicast to us through a router from a
+ RENEWING client is going to look exactly like a packet that
+ was broadcast to us from an INIT-REBOOT client.
+
+ Since we can't tell the difference between these two kinds
+ of packets, if the packet appears to have come in off the
+ local wire, we have to treat it as if it's a RENEWING
+ client. This means that we can't NAK a RENEWING client on
+ the local wire that has a bogus address. The good news is
+ that we won't ACK it either, so it should revert to INIT
+ state and send us a DHCPDISCOVER, which we *can* work with.
+
+ Because we can't detect that a RENEWING client is on the
+ wrong wire, it's going to sit there trying to renew until
+ it gets to the REBIND state, when we *can* NAK it because
+ the packet will get to us through a BOOTP gateway. We
+ shouldn't actually see DHCPREQUEST packets from RENEWING
+ clients on the wrong wire anyway, since their idea of their
+ local router will be wrong. In any case, the protocol
+ doesn't really allow us to NAK a DHCPREQUEST from a
+ RENEWING client, so we can punt on this issue. */
+
+ if (!packet -> shared_network ||
+ (packet -> raw -> ciaddr.s_addr &&
+ packet -> raw -> giaddr.s_addr) ||
+ (packet -> options [DHO_DHCP_REQUESTED_ADDRESS].len &&
+ !packet -> raw -> ciaddr.s_addr)) {
+
+ /* If we don't know where it came from but we do know
+ where it claims to have come from, it didn't come
+ from there. Fry it. */
+ if (!packet -> shared_network) {
+ if (subnet &&
+ subnet -> shared_network -> group -> authoritative)
+ {
+ nak_lease (packet, &cip);
+ return;
+ }
+ /* Otherwise, ignore it. */
+ return;
+ }
+
+ /* If we do know where it came from and it asked for an
+ address that is not on that shared network, nak it. */
+ subnet = find_grouped_subnet (packet -> shared_network, cip);
+ if (!subnet) {
+ if (packet -> shared_network -> group -> authoritative)
+ nak_lease (packet, &cip);
+ return;
+ }
+ }
+
+ /* If we found a lease for the client but it's not the one the
+ client asked for, don't send it - some other server probably
+ made the cut. */
+ if (lease && !addr_eq (lease -> ip_addr, cip)) {
+ /* If we found the address the client asked for, but
+ it wasn't what got picked, the lease belongs to us,
+ so we should NAK it. */
+ if (ours)
+ nak_lease (packet, &cip);
+ return;
+ }
+
+ /* If the address the client asked for is ours, but it wasn't
+ available for the client, NAK it. */
+ if (!lease && ours) {
+ nak_lease (packet, &cip);
+ return;
+ }
+
+ /* If we're not allowed to serve this client anymore, don't. */
+ if (lease &&
+ !lease -> host &&
+ !lease -> subnet -> group -> boot_unknown_clients) {
+ note ("Ignoring unknown client %s",
+ print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr));
+ return;
+ } else if (lease && lease -> host &&
+ !lease -> host -> group -> allow_booting) {
+ note ("Declining to renew client %s",
+ lease -> host -> name
+ ? lease -> host -> name
+ : print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr));
+ return;
+ }
+
+ /* If we own the lease that the client is asking for,
+ and it's already been assigned to the client, ack it. */
+ if (lease &&
+ ((lease -> uid_len && lease -> uid_len ==
+ packet -> options [DHO_DHCP_CLIENT_IDENTIFIER].len &&
+ !memcmp (packet -> options
+ [DHO_DHCP_CLIENT_IDENTIFIER].data,
+ lease -> uid, lease -> uid_len)) ||
+ (lease -> hardware_addr.hlen == packet -> raw -> hlen &&
+ lease -> hardware_addr.htype == packet -> raw -> htype &&
+ !memcmp (lease -> hardware_addr.haddr,
+ packet -> raw -> chaddr,
+ packet -> raw -> hlen)))) {
+ ack_lease (packet, lease, DHCPACK, 0);
+ return;
+ }
+
+ /* At this point, the client has requested a lease, and it's
+ available, but it wasn't assigned to the client, which
+ means that the client probably hasn't gone through the
+ DHCPDISCOVER part of the protocol. We are within our
+ rights to send a DHCPNAK. We can also send a DHCPACK.
+ The thing we probably should not do is to remain silent.
+ For now, we'll just assign the lease to the client anyway. */
+ if (lease)
+ ack_lease (packet, lease, DHCPACK, 0);
+}
+
+void dhcprelease (packet)
+ struct packet *packet;
+{
+ struct lease *lease;
+ struct iaddr cip;
+ int i;
+
+ /* DHCPRELEASE must not specify address in requested-address
+ option, but old protocol specs weren't explicit about this,
+ so let it go. */
+ if (packet -> options [DHO_DHCP_REQUESTED_ADDRESS].len) {
+ note ("DHCPRELEASE from %s specified requested-address.",
+ print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr));
+ }
+
+ i = DHO_DHCP_CLIENT_IDENTIFIER;
+ if (packet -> options [i].len) {
+ lease = find_lease_by_uid (packet -> options [i].data,
+ packet -> options [i].len);
+
+ /* See if we can find a lease that matches the IP address
+ the client is claiming. */
+ for (; lease; lease = lease -> n_uid) {
+ if (!memcmp (&packet -> raw -> ciaddr,
+ lease -> ip_addr.iabuf, 4)) {
+ break;
+ }
+ }
+ } else {
+ /* The client is supposed to pass a valid client-identifier,
+ but the spec on this has changed historically, so try the
+ IP address in ciaddr if the client-identifier fails. */
+ cip.len = 4;
+ memcpy (cip.iabuf, &packet -> raw -> ciaddr, 4);
+ lease = find_lease_by_ip_addr (cip);
+ }
+
+ note ("DHCPRELEASE of %s from %s via %s (%sfound)",
+ inet_ntoa (packet -> raw -> ciaddr),
+ print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr),
+ packet -> raw -> giaddr.s_addr
+ ? inet_ntoa (packet -> raw -> giaddr)
+ : packet -> interface -> name,
+ lease ? "" : "not ");
+
+ /* If we found a lease, release it. */
+ if (lease && lease -> ends > cur_time) {
+ /* first, we ping this lease to see if it's still
+ * there. if it is, we don't release it.
+ * this avoids the problem of spoofed releases
+ * being used to liberate addresses from the
+ * server.
+ */
+ if (! lease->releasing) {
+ note ("DHCPRELEASE of %s from %s via %s (found)",
+ inet_ntoa (packet -> raw -> ciaddr),
+ print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr),
+ packet -> raw -> giaddr.s_addr
+ ? inet_ntoa (packet -> raw -> giaddr)
+ : packet -> interface -> name);
+
+ lease->releasing = 1;
+ add_timeout (cur_time + 1, lease_ping_timeout, lease);
+ icmp_echorequest (&(lease -> ip_addr));
+ ++outstanding_pings;
+ }
+ else {
+ note ("DHCPRELEASE of %s from %s via %s ignored (release already pending)",
+ inet_ntoa (packet -> raw -> ciaddr),
+ print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr),
+ packet -> raw -> giaddr.s_addr
+ ? inet_ntoa (packet -> raw -> giaddr)
+ : packet -> interface -> name);
+ }
+ }
+ else {
+ note ("DHCPRELEASE of %s from %s via %s for nonexistent lease",
+ inet_ntoa (packet -> raw -> ciaddr),
+ print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr),
+ packet -> raw -> giaddr.s_addr
+ ? inet_ntoa (packet -> raw -> giaddr)
+ : packet -> interface -> name);
+ }
+}
+
+void dhcpdecline (packet)
+ struct packet *packet;
+{
+ struct lease *lease;
+ struct iaddr cip;
+
+ /* DHCPDECLINE must specify address. */
+ if (!packet -> options [DHO_DHCP_REQUESTED_ADDRESS].len) {
+ return;
+ }
+
+ cip.len = 4;
+ memcpy (cip.iabuf,
+ packet -> options [DHO_DHCP_REQUESTED_ADDRESS].data, 4);
+ lease = find_lease_by_ip_addr (cip);
+
+ note ("DHCPDECLINE on %s from %s via %s",
+ piaddr (cip),
+ print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr),
+ packet -> raw -> giaddr.s_addr
+ ? inet_ntoa (packet -> raw -> giaddr)
+ : packet -> interface -> name);
+
+ /* If we found a lease, mark it as unusable and complain. */
+ if (lease) {
+ abandon_lease (lease, "declined.");
+ }
+}
+
+void dhcpinform (packet)
+ struct packet *packet;
+{
+ note ("DHCPINFORM from %s",
+ inet_ntoa (packet -> raw -> ciaddr));
+}
+
+void nak_lease (packet, cip)
+ struct packet *packet;
+ struct iaddr *cip;
+{
+ struct sockaddr_in to;
+ struct in_addr from;
+ int result;
+ struct dhcp_packet raw;
+ unsigned char nak = DHCPNAK;
+ struct packet outgoing;
+ struct hardware hto;
+ int i;
+
+ struct tree_cache *options [256];
+ struct tree_cache dhcpnak_tree;
+ struct tree_cache dhcpmsg_tree;
+
+ memset (options, 0, sizeof options);
+ memset (&outgoing, 0, sizeof outgoing);
+ memset (&raw, 0, sizeof raw);
+ outgoing.raw = &raw;
+
+ /* Set DHCP_MESSAGE_TYPE to DHCPNAK */
+ options [DHO_DHCP_MESSAGE_TYPE] = &dhcpnak_tree;
+ options [DHO_DHCP_MESSAGE_TYPE] -> value = &nak;
+ options [DHO_DHCP_MESSAGE_TYPE] -> len = sizeof nak;
+ options [DHO_DHCP_MESSAGE_TYPE] -> buf_size = sizeof nak;
+ options [DHO_DHCP_MESSAGE_TYPE] -> timeout = 0xFFFFFFFF;
+ options [DHO_DHCP_MESSAGE_TYPE] -> tree = (struct tree *)0;
+
+ /* Set DHCP_MESSAGE to whatever the message is */
+ options [DHO_DHCP_MESSAGE] = &dhcpmsg_tree;
+ options [DHO_DHCP_MESSAGE] -> value = (unsigned char *)dhcp_message;
+ options [DHO_DHCP_MESSAGE] -> len =
+ options [DHO_DHCP_MESSAGE] -> buf_size = strlen (dhcp_message);
+ options [DHO_DHCP_MESSAGE] -> timeout = 0xFFFFFFFF;
+ options [DHO_DHCP_MESSAGE] -> tree = (struct tree *)0;
+
+ /* Do not use the client's requested parameter list. */
+ i = DHO_DHCP_PARAMETER_REQUEST_LIST;
+ if (packet -> options [i].data) {
+ packet -> options [i].len = 0;
+ dfree (packet -> options [i].data, "nak_lease");
+ packet -> options [i].data = (unsigned char *)0;
+ }
+
+ /* Set up the option buffer... */
+ outgoing.packet_length =
+ cons_options (packet, outgoing.raw, 0, options, 0, 0, 0,
+ (u_int8_t *)0, 0);
+
+/* memset (&raw.ciaddr, 0, sizeof raw.ciaddr);*/
+ raw.siaddr = packet -> interface -> primary_address;
+ raw.giaddr = packet -> raw -> giaddr;
+ memcpy (raw.chaddr, packet -> raw -> chaddr, sizeof raw.chaddr);
+ raw.hlen = packet -> raw -> hlen;
+ raw.htype = packet -> raw -> htype;
+
+ raw.xid = packet -> raw -> xid;
+ raw.secs = packet -> raw -> secs;
+ raw.flags = packet -> raw -> flags | htons (BOOTP_BROADCAST);
+ raw.hops = packet -> raw -> hops;
+ raw.op = BOOTREPLY;
+
+ /* Report what we're sending... */
+ note ("DHCPNAK on %s to %s via %s",
+ piaddr (*cip),
+ print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr),
+ packet -> raw -> giaddr.s_addr
+ ? inet_ntoa (packet -> raw -> giaddr)
+ : packet -> interface -> name);
+
+
+
+#ifdef DEBUG_PACKET
+ dump_packet (packet);
+ dump_raw ((unsigned char *)packet -> raw, packet -> packet_length);
+ dump_packet (&outgoing);
+ dump_raw ((unsigned char *)&raw, outgoing.packet_length);
+#endif
+
+ hto.htype = packet -> raw -> htype;
+ hto.hlen = packet -> raw -> hlen;
+ memcpy (hto.haddr, packet -> raw -> chaddr, hto.hlen);
+
+ /* Set up the common stuff... */
+ memset (&to, 0, sizeof to);
+ to.sin_family = AF_INET;
+ to.sin_len = sizeof to;
+
+ from = packet -> interface -> primary_address;
+
+ /* Make sure that the packet is at least as big as a BOOTP packet. */
+ if (outgoing.packet_length < BOOTP_MIN_LEN)
+ outgoing.packet_length = BOOTP_MIN_LEN;
+
+ /* If this was gatewayed, send it back to the gateway.
+ Otherwise, broadcast it on the local network. */
+ if (raw.giaddr.s_addr) {
+ to.sin_addr = raw.giaddr;
+ to.sin_port = local_port;
+
+ if (fallback_interface) {
+ result = send_packet (fallback_interface,
+ packet, &raw,
+ outgoing.packet_length,
+ from, &to, &hto);
+ if (result == -1)
+ warn ("send_fallback: %m");
+ return;
+ }
+ } else {
+ to.sin_addr.s_addr = htonl (INADDR_BROADCAST);
+ to.sin_port = remote_port;
+ }
+
+ errno = 0;
+ result = send_packet (packet -> interface,
+ packet, &raw, outgoing.packet_length,
+ from, &to, (struct hardware *)0);
+}
+
+void ack_lease (packet, lease, offer, when)
+ struct packet *packet;
+ struct lease *lease;
+ unsigned int offer;
+ TIME when;
+{
+ struct lease lt;
+ struct lease_state *state;
+ TIME lease_time;
+ TIME offered_lease_time;
+ TIME max_lease_time;
+ TIME default_lease_time;
+ int ulafdr;
+
+ struct class *vendor_class, *user_class;
+ int i;
+
+ /* If we're already acking this lease, don't do it again. */
+ if (lease -> state) {
+ note ("already acking lease %s", piaddr (lease -> ip_addr));
+ return;
+ }
+
+ if (packet -> options [DHO_DHCP_CLASS_IDENTIFIER].len) {
+ vendor_class =
+ find_class (0,
+ packet ->
+ options [DHO_DHCP_CLASS_IDENTIFIER].data,
+ packet ->
+ options [DHO_DHCP_CLASS_IDENTIFIER].len);
+ } else {
+ vendor_class = (struct class *)0;
+ }
+
+ if (packet -> options [DHO_DHCP_USER_CLASS_ID].len) {
+ user_class =
+ find_class (1,
+ packet ->
+ options [DHO_DHCP_USER_CLASS_ID].data,
+ packet ->
+ options [DHO_DHCP_USER_CLASS_ID].len);
+ } else {
+ user_class = (struct class *)0;
+ }
+
+ /*
+ * If there is not a specific host entry, and either the
+ * vendor class or user class (if they exist) deny booting,
+ * then bug out.
+ */
+ if (!lease -> host) {
+ if (vendor_class && !vendor_class -> group -> allow_booting) {
+ debug ("Booting denied by vendor class");
+ return;
+ }
+
+ if (user_class && !user_class -> group -> allow_booting) {
+ debug ("Booting denied by user class");
+ return;
+ }
+ }
+
+ /* Allocate a lease state structure... */
+ state = new_lease_state ("ack_lease");
+ if (!state)
+ error ("unable to allocate lease state!");
+ memset (state, 0, sizeof *state);
+ state -> got_requested_address = packet -> got_requested_address;
+ state -> shared_network = packet -> interface -> shared_network;
+
+ /* Remember if we got a server identifier option. */
+ if (packet -> options [DHO_DHCP_SERVER_IDENTIFIER].len)
+ state -> got_server_identifier = 1;
+
+ /* Replace the old lease hostname with the new one, if it's changed. */
+ if (packet -> options [DHO_HOST_NAME].len &&
+ lease -> client_hostname &&
+ (strlen (lease -> client_hostname) ==
+ packet -> options [DHO_HOST_NAME].len) &&
+ !memcmp (lease -> client_hostname,
+ packet -> options [DHO_HOST_NAME].data,
+ packet -> options [DHO_HOST_NAME].len)) {
+ } else if (packet -> options [DHO_HOST_NAME].len) {
+ if (lease -> client_hostname)
+ free (lease -> client_hostname);
+ lease -> client_hostname =
+ malloc (packet -> options [DHO_HOST_NAME].len + 1);
+ if (!lease -> client_hostname)
+ error ("no memory for client hostname.\n");
+ memcpy (lease -> client_hostname,
+ packet -> options [DHO_HOST_NAME].data,
+ packet -> options [DHO_HOST_NAME].len);
+ lease -> client_hostname
+ [packet -> options [DHO_HOST_NAME].len] = 0;
+ } else if (lease -> client_hostname) {
+ free (lease -> client_hostname);
+ lease -> client_hostname = 0;
+ }
+
+ /* Choose a filename; first from the host_decl, if any, then from
+ the user class, then from the vendor class. */
+ if (lease -> host && lease -> host -> group -> filename)
+ strlcpy (state -> filename, lease -> host -> group -> filename,
+ sizeof state -> filename);
+ else if (user_class && user_class -> group -> filename)
+ strlcpy (state -> filename, user_class -> group -> filename,
+ sizeof state -> filename);
+ else if (vendor_class && vendor_class -> group -> filename)
+ strlcpy (state -> filename, vendor_class -> group -> filename,
+ sizeof state -> filename);
+ else if (packet -> raw -> file [0])
+ strlcpy (state -> filename, packet -> raw -> file,
+ sizeof state -> filename);
+ else if (lease -> subnet -> group -> filename)
+ strlcpy (state -> filename,
+ lease -> subnet -> group -> filename,
+ sizeof state -> filename);
+ else
+ strlcpy (state -> filename, "", sizeof state -> filename);
+
+ /* Choose a server name as above. */
+ if (lease -> host && lease -> host -> group -> server_name)
+ state -> server_name = lease -> host -> group -> server_name;
+ else if (user_class && user_class -> group -> server_name)
+ state -> server_name = user_class -> group -> server_name;
+ else if (vendor_class && vendor_class -> group -> server_name)
+ state -> server_name = vendor_class -> group -> server_name;
+ else if (lease -> subnet -> group -> server_name)
+ state -> server_name =
+ lease -> subnet -> group -> server_name;
+ else state -> server_name = (char *)0;
+
+ /* At this point, we have a lease that we can offer the client.
+ Now we construct a lease structure that contains what we want,
+ and call supersede_lease to do the right thing with it. */
+
+ memset (&lt, 0, sizeof lt);
+
+ /* Use the ip address of the lease that we finally found in
+ the database. */
+ lt.ip_addr = lease -> ip_addr;
+
+ /* Start now. */
+ lt.starts = cur_time;
+
+ /* Figure out maximum lease time. */
+ if (lease -> host &&
+ lease -> host -> group -> max_lease_time)
+ max_lease_time = lease -> host -> group -> max_lease_time;
+ else
+ max_lease_time = lease -> subnet -> group -> max_lease_time;
+
+ /* Figure out default lease time. */
+ if (lease -> host
+ && lease -> host -> group -> default_lease_time)
+ default_lease_time =
+ lease -> host -> group -> default_lease_time;
+ else
+ default_lease_time =
+ lease -> subnet -> group -> default_lease_time;
+
+ /* Figure out how long a lease to assign. If this is a
+ dynamic BOOTP lease, its duration must be infinite. */
+ if (offer) {
+ if (packet -> options [DHO_DHCP_LEASE_TIME].len == 4) {
+ lease_time = getULong
+ (packet -> options [DHO_DHCP_LEASE_TIME].data);
+
+ /* Don't let the client ask for a longer lease than
+ is supported for this subnet or host. */
+ if (lease_time > max_lease_time)
+ lease_time = max_lease_time;
+ } else
+ lease_time = default_lease_time;
+
+ state -> offered_expiry = cur_time + lease_time;
+ if (when)
+ lt.ends = when;
+ else
+ lt.ends = state -> offered_expiry;
+ } else {
+ if (lease -> host &&
+ lease -> host -> group -> bootp_lease_length)
+ lt.ends = (cur_time +
+ lease -> host ->
+ group -> bootp_lease_length);
+ else if (lease -> subnet -> group -> bootp_lease_length)
+ lt.ends = (cur_time +
+ lease -> subnet ->
+ group -> bootp_lease_length);
+ else if (lease -> host &&
+ lease -> host -> group -> bootp_lease_cutoff)
+ lt.ends = lease -> host -> group -> bootp_lease_cutoff;
+ else
+ lt.ends = (lease -> subnet ->
+ group -> bootp_lease_cutoff);
+ state -> offered_expiry = lt.ends;
+ lt.flags = BOOTP_LEASE;
+ }
+
+ /* Record the uid, if given... */
+ i = DHO_DHCP_CLIENT_IDENTIFIER;
+ if (packet -> options [i].len) {
+ if (packet -> options [i].len <= sizeof lt.uid_buf) {
+ memcpy (lt.uid_buf, packet -> options [i].data,
+ packet -> options [i].len);
+ lt.uid = lt.uid_buf;
+ lt.uid_max = sizeof lt.uid_buf;
+ lt.uid_len = packet -> options [i].len;
+ } else {
+ lt.uid_max = lt.uid_len = packet -> options [i].len;
+ lt.uid = (unsigned char *)malloc (lt.uid_max);
+ if (!lt.uid)
+ error ("can't allocate memory for large uid.");
+ memcpy (lt.uid,
+ packet -> options [i].data, lt.uid_len);
+ }
+ }
+
+ lt.host = lease -> host;
+ lt.subnet = lease -> subnet;
+ lt.shared_network = lease -> shared_network;
+
+ /* Don't call supersede_lease on a mocked-up lease. */
+ if (lease -> flags & STATIC_LEASE) {
+ /* Copy the hardware address into the static lease
+ structure. */
+ lease -> hardware_addr.hlen = packet -> raw -> hlen;
+ lease -> hardware_addr.htype = packet -> raw -> htype;
+ memcpy (lease -> hardware_addr.haddr, packet -> raw -> chaddr,
+ sizeof packet -> raw -> chaddr); /* XXX */
+ } else {
+ /* Record the hardware address, if given... */
+ lt.hardware_addr.hlen = packet -> raw -> hlen;
+ lt.hardware_addr.htype = packet -> raw -> htype;
+ memcpy (lt.hardware_addr.haddr, packet -> raw -> chaddr,
+ sizeof packet -> raw -> chaddr);
+
+ /* Install the new information about this lease in the
+ database. If this is a DHCPACK or a dynamic BOOTREPLY
+ and we can't write the lease, don't ACK it (or BOOTREPLY
+ it) either. */
+
+ if (!(supersede_lease (lease, &lt, !offer || offer == DHCPACK)
+ || (offer && offer != DHCPACK)))
+ return;
+ }
+
+ /* Remember the interface on which the packet arrived. */
+ state -> ip = packet -> interface;
+
+ /* Set a flag if this client is a lame Microsoft client that NUL
+ terminates string options and expects us to do likewise. */
+ if (packet -> options [DHO_HOST_NAME].data &&
+ packet -> options [DHO_HOST_NAME].data
+ [packet -> options [DHO_HOST_NAME].len - 1] == '\0')
+ lease -> flags |= MS_NULL_TERMINATION;
+ else
+ lease -> flags &= ~MS_NULL_TERMINATION;
+
+ /* Remember the giaddr, xid, secs, flags and hops. */
+ state -> giaddr = packet -> raw -> giaddr;
+ state -> ciaddr = packet -> raw -> ciaddr;
+ state -> xid = packet -> raw -> xid;
+ state -> secs = packet -> raw -> secs;
+ state -> bootp_flags = packet -> raw -> flags;
+ state -> hops = packet -> raw -> hops;
+ state -> offer = offer;
+
+ /* Figure out what options to send to the client: */
+
+ /* Start out with the subnet options... */
+ memcpy (state -> options,
+ lease -> subnet -> group -> options,
+ sizeof state -> options);
+
+ /* Vendor and user classes are only supported for DHCP clients. */
+ if (state -> offer) {
+ /* If we have a vendor class, install those options,
+ superseding any subnet options. */
+ if (vendor_class) {
+ for (i = 0; i < 256; i++)
+ if (vendor_class -> group -> options [i])
+ state -> options [i] =
+ (vendor_class -> group ->
+ options [i]);
+ }
+
+ /* If we have a user class, install those options,
+ superseding any subnet and vendor class options. */
+ if (user_class) {
+ for (i = 0; i < 256; i++)
+ if (user_class -> group -> options [i])
+ state -> options [i] =
+ (user_class -> group ->
+ options [i]);
+ }
+
+ }
+
+ /* If we have a host_decl structure, install the associated
+ options, superseding anything that's in the way. */
+ if (lease -> host) {
+ for (i = 0; i < 256; i++)
+ if (lease -> host -> group -> options [i])
+ state -> options [i] = (lease -> host ->
+ group -> options [i]);
+ }
+
+ /* Get the Maximum Message Size option from the packet, if one
+ was sent. */
+ i = DHO_DHCP_MAX_MESSAGE_SIZE;
+ if (packet -> options [i].data &&
+ (packet -> options [i].len == sizeof (u_int16_t)))
+ state -> max_message_size =
+ getUShort (packet -> options [i].data);
+ /* Otherwise, if a maximum message size was specified, use that. */
+ else if (state -> options [i] && state -> options [i] -> value)
+ state -> max_message_size =
+ getUShort (state -> options [i] -> value);
+
+ /* Save the parameter request list if there is one. */
+ i = DHO_DHCP_PARAMETER_REQUEST_LIST;
+ if (packet -> options [i].data) {
+ state -> prl = dmalloc (packet -> options [i].len,
+ "ack_lease: prl");
+ if (!state -> prl)
+ warn ("no memory for parameter request list");
+ else {
+ memcpy (state -> prl,
+ packet -> options [i].data,
+ packet -> options [i].len);
+ state -> prl_len = packet -> options [i].len;
+ }
+ }
+
+ /* If we didn't get a hostname from an option somewhere, see if
+ we can get one from the lease. */
+ i = DHO_HOST_NAME;
+ if (!state -> options [i] && lease -> hostname) {
+ state -> options [i] = new_tree_cache ("hostname");
+ state -> options [i] -> flags = TC_TEMPORARY;
+ state -> options [i] -> value =
+ (unsigned char *)lease -> hostname;
+ state -> options [i] -> len = strlen (lease -> hostname);
+ state -> options [i] -> buf_size = state -> options [i] -> len;
+ state -> options [i] -> timeout = 0xFFFFFFFF;
+ state -> options [i] -> tree = (struct tree *)0;
+ }
+
+ /* Now, if appropriate, put in DHCP-specific options that
+ override those. */
+ if (state -> offer) {
+ i = DHO_DHCP_MESSAGE_TYPE;
+ state -> options [i] = new_tree_cache ("message-type");
+ state -> options [i] -> flags = TC_TEMPORARY;
+ state -> options [i] -> value = &state -> offer;
+ state -> options [i] -> len = sizeof state -> offer;
+ state -> options [i] -> buf_size = sizeof state -> offer;
+ state -> options [i] -> timeout = 0xFFFFFFFF;
+ state -> options [i] -> tree = (struct tree *)0;
+
+ i = DHO_DHCP_SERVER_IDENTIFIER;
+ if (!state -> options [i]) {
+ use_primary:
+ state -> options [i] = new_tree_cache ("server-id");
+ state -> options [i] -> value =
+ (unsigned char *)&state ->
+ ip -> primary_address;
+ state -> options [i] -> len =
+ sizeof state -> ip -> primary_address;
+ state -> options [i] -> buf_size
+ = state -> options [i] -> len;
+ state -> options [i] -> timeout = 0xFFFFFFFF;
+ state -> options [i] -> tree = (struct tree *)0;
+ state -> from.len =
+ sizeof state -> ip -> primary_address;
+ memcpy (state -> from.iabuf,
+ &state -> ip -> primary_address,
+ state -> from.len);
+ } else {
+ /* Find the value of the server identifier... */
+ if (!tree_evaluate (state -> options [i]))
+ goto use_primary;
+ if (!state -> options [i] -> value ||
+ (state -> options [i] -> len >
+ sizeof state -> from.iabuf))
+ goto use_primary;
+
+ state -> from.len = state -> options [i] -> len;
+ memcpy (state -> from.iabuf,
+ state -> options [i] -> value,
+ state -> from.len);
+ }
+
+ /* Sanity check the lease time. */
+ if ((state -> offered_expiry - cur_time) < 15)
+ offered_lease_time = default_lease_time;
+ else if (state -> offered_expiry - cur_time > max_lease_time)
+ offered_lease_time = max_lease_time;
+ else
+ offered_lease_time =
+ state -> offered_expiry - cur_time;
+
+ putULong ((unsigned char *)&state -> expiry,
+ offered_lease_time);
+ i = DHO_DHCP_LEASE_TIME;
+ state -> options [i] = new_tree_cache ("lease-expiry");
+ state -> options [i] -> flags = TC_TEMPORARY;
+ state -> options [i] -> value =
+ (unsigned char *)&state -> expiry;
+ state -> options [i] -> len = sizeof state -> expiry;
+ state -> options [i] -> buf_size = sizeof state -> expiry;
+ state -> options [i] -> timeout = 0xFFFFFFFF;
+ state -> options [i] -> tree = (struct tree *)0;
+
+ /* Renewal time is lease time * 0.5. */
+ offered_lease_time /= 2;
+ putULong ((unsigned char *)&state -> renewal,
+ offered_lease_time);
+ i = DHO_DHCP_RENEWAL_TIME;
+ state -> options [i] = new_tree_cache ("renewal-time");
+ state -> options [i] -> flags = TC_TEMPORARY;
+ state -> options [i] -> value =
+ (unsigned char *)&state -> renewal;
+ state -> options [i] -> len = sizeof state -> renewal;
+ state -> options [i] -> buf_size = sizeof state -> renewal;
+ state -> options [i] -> timeout = 0xFFFFFFFF;
+ state -> options [i] -> tree = (struct tree *)0;
+
+
+ /* Rebinding time is lease time * 0.875. */
+ offered_lease_time += (offered_lease_time / 2
+ + offered_lease_time / 4);
+ putULong ((unsigned char *)&state -> rebind,
+ offered_lease_time);
+ i = DHO_DHCP_REBINDING_TIME;
+ state -> options [i] = new_tree_cache ("rebind-time");
+ state -> options [i] -> flags = TC_TEMPORARY;
+ state -> options [i] -> value =
+ (unsigned char *)&state -> rebind;
+ state -> options [i] -> len = sizeof state -> rebind;
+ state -> options [i] -> buf_size = sizeof state -> rebind;
+ state -> options [i] -> timeout = 0xFFFFFFFF;
+ state -> options [i] -> tree = (struct tree *)0;
+
+ /* If we used the vendor class the client specified, we
+ have to return it. */
+ if (vendor_class) {
+ i = DHO_DHCP_CLASS_IDENTIFIER;
+ state -> options [i] =
+ new_tree_cache ("class-identifier");
+ state -> options [i] -> flags = TC_TEMPORARY;
+ state -> options [i] -> value =
+ (unsigned char *)vendor_class -> name;
+ state -> options [i] -> len =
+ strlen (vendor_class -> name);
+ state -> options [i] -> buf_size =
+ state -> options [i] -> len;
+ state -> options [i] -> timeout = 0xFFFFFFFF;
+ state -> options [i] -> tree = (struct tree *)0;
+ }
+
+ /* If we used the user class the client specified, we
+ have to return it. */
+ if (user_class) {
+ i = DHO_DHCP_USER_CLASS_ID;
+ state -> options [i] = new_tree_cache ("user-class");
+ state -> options [i] -> flags = TC_TEMPORARY;
+ state -> options [i] -> value =
+ (unsigned char *)user_class -> name;
+ state -> options [i] -> len =
+ strlen (user_class -> name);
+ state -> options [i] -> buf_size =
+ state -> options [i] -> len;
+ state -> options [i] -> timeout = 0xFFFFFFFF;
+ state -> options [i] -> tree = (struct tree *)0;
+ }
+ }
+
+ /* Use the subnet mask from the subnet declaration if no other
+ mask has been provided. */
+ i = DHO_SUBNET_MASK;
+ if (!state -> options [i]) {
+ state -> options [i] = new_tree_cache ("subnet-mask");
+ state -> options [i] -> flags = TC_TEMPORARY;
+ state -> options [i] -> value =
+ lease -> subnet -> netmask.iabuf;
+ state -> options [i] -> len = lease -> subnet -> netmask.len;
+ state -> options [i] -> buf_size =
+ lease -> subnet -> netmask.len;
+ state -> options [i] -> timeout = 0xFFFFFFFF;
+ state -> options [i] -> tree = (struct tree *)0;
+ }
+
+ /* If so directed, use the leased IP address as the router address.
+ This supposedly makes Win95 machines ARP for all IP addresses,
+ so if the local router does proxy arp, you win. */
+
+ ulafdr = 0;
+ if (lease -> host) {
+ if (lease -> host -> group -> use_lease_addr_for_default_route)
+ ulafdr = 1;
+ } else if (user_class) {
+ if (user_class -> group -> use_lease_addr_for_default_route)
+ ulafdr = 1;
+ } else if (vendor_class) {
+ if (vendor_class -> group -> use_lease_addr_for_default_route)
+ ulafdr = 1;
+ } else if (lease -> subnet -> group ->
+ use_lease_addr_for_default_route)
+ ulafdr = 1;
+ else
+ ulafdr = 0;
+
+ i = DHO_ROUTERS;
+ if (ulafdr && !state -> options [i]) {
+ state -> options [i] = new_tree_cache ("routers");
+ state -> options [i] -> flags = TC_TEMPORARY;
+ state -> options [i] -> value =
+ lease -> ip_addr.iabuf;
+ state -> options [i] -> len =
+ lease -> ip_addr.len;
+ state -> options [i] -> buf_size =
+ lease -> ip_addr.len;
+ state -> options [i] -> timeout = 0xFFFFFFFF;
+ state -> options [i] -> tree = (struct tree *)0;
+ }
+
+#ifdef DEBUG_PACKET
+ dump_packet (packet);
+ dump_raw ((unsigned char *)packet -> raw, packet -> packet_length);
+#endif
+
+ lease -> state = state;
+
+ /* If this is a DHCPOFFER, ping the lease address before actually
+ sending the offer. */
+ if (offer == DHCPOFFER && !(lease -> flags & STATIC_LEASE) &&
+ cur_time - lease -> timestamp > 60) {
+ lease -> timestamp = cur_time;
+ icmp_echorequest (&lease -> ip_addr);
+ add_timeout (cur_time + 1, lease_ping_timeout, lease);
+ ++outstanding_pings;
+ } else {
+ lease -> timestamp = cur_time;
+ dhcp_reply (lease);
+ }
+}
+
+void dhcp_reply (lease)
+ struct lease *lease;
+{
+ int bufs = 0;
+ int packet_length;
+ struct dhcp_packet raw;
+ struct sockaddr_in to;
+ struct in_addr from;
+ struct hardware hto;
+ int result;
+ int i;
+ struct lease_state *state = lease -> state;
+ int nulltp, bootpp;
+ u_int8_t *prl;
+ int prl_len;
+
+ if (!state)
+ error ("dhcp_reply was supplied lease with no state!");
+
+ /* Compose a response for the client... */
+ memset (&raw, 0, sizeof raw);
+
+ /* Copy in the filename if given; otherwise, flag the filename
+ buffer as available for options. */
+ if (state -> filename [0])
+ strlcpy (raw.file, state -> filename, sizeof raw.file);
+ else
+ bufs |= 1;
+
+ /* Copy in the server name if given; otherwise, flag the
+ server_name buffer as available for options. */
+ if (state -> server_name)
+ strlcpy (raw.sname, state -> server_name, sizeof raw.sname);
+ else
+ bufs |= 2; /* XXX */
+
+ memcpy (raw.chaddr, lease -> hardware_addr.haddr, sizeof raw.chaddr);
+ raw.hlen = lease -> hardware_addr.hlen;
+ raw.htype = lease -> hardware_addr.htype;
+
+ /* See if this is a Microsoft client that NUL-terminates its
+ strings and expects us to do likewise... */
+ if (lease -> flags & MS_NULL_TERMINATION)
+ nulltp = 1;
+ else
+ nulltp = 0;
+
+ /* See if this is a bootp client... */
+ if (state -> offer)
+ bootpp = 0;
+ else
+ bootpp = 1;
+
+ if (state -> options [DHO_DHCP_PARAMETER_REQUEST_LIST] &&
+ state -> options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> value) {
+ prl = state -> options
+ [DHO_DHCP_PARAMETER_REQUEST_LIST] -> value;
+ prl_len = state -> options
+ [DHO_DHCP_PARAMETER_REQUEST_LIST] -> len;
+ } else if (state -> prl) {
+ prl = state -> prl;
+ prl_len = state -> prl_len;
+ } else {
+ prl = (u_int8_t *)0;
+ prl_len = 0;
+ }
+
+ /* Insert such options as will fit into the buffer. */
+ packet_length = cons_options ((struct packet *)0, &raw,
+ state -> max_message_size,
+ state -> options,
+ bufs, nulltp, bootpp, prl, prl_len);
+
+ /* Having done the cons_options(), we can release the tree_cache
+ entries. */
+ for (i = 0; i < 256; i++) {
+ if (state -> options [i] &&
+ state -> options [i] -> flags & TC_TEMPORARY)
+ free_tree_cache (state -> options [i], "dhcp_reply");
+ }
+
+ memcpy (&raw.ciaddr, &state -> ciaddr, sizeof raw.ciaddr);
+ memcpy (&raw.yiaddr, lease -> ip_addr.iabuf, 4);
+
+ /* Figure out the address of the next server. */
+ if (lease -> host && lease -> host -> group -> next_server.len)
+ memcpy (&raw.siaddr,
+ lease -> host -> group -> next_server.iabuf, 4);
+ else if (lease -> subnet -> group -> next_server.len)
+ memcpy (&raw.siaddr,
+ lease -> subnet -> group -> next_server.iabuf, 4);
+ else if (lease -> subnet -> interface_address.len)
+ memcpy (&raw.siaddr,
+ lease -> subnet -> interface_address.iabuf, 4);
+ else
+ raw.siaddr = state -> ip -> primary_address;
+
+ raw.giaddr = state -> giaddr;
+
+ raw.xid = state -> xid;
+ raw.secs = state -> secs;
+ raw.flags = state -> bootp_flags;
+ raw.hops = state -> hops;
+ raw.op = BOOTREPLY;
+
+ /* Say what we're doing... */
+ note ("%s on %s to %s via %s",
+ (state -> offer
+ ? (state -> offer == DHCPACK ? "DHCPACK" : "DHCPOFFER")
+ : "BOOTREPLY"),
+ piaddr (lease -> ip_addr),
+ print_hw_addr (lease -> hardware_addr.htype,
+ lease -> hardware_addr.hlen,
+ lease -> hardware_addr.haddr),
+ state -> giaddr.s_addr
+ ? inet_ntoa (state -> giaddr)
+ : state -> ip -> name);
+
+ /* Set up the hardware address... */
+ hto.htype = lease -> hardware_addr.htype;
+ hto.hlen = lease -> hardware_addr.hlen;
+ memcpy (hto.haddr, lease -> hardware_addr.haddr, hto.hlen);
+
+ memset (&to, 0, sizeof to);
+ to.sin_family = AF_INET;
+#ifdef HAVE_SA_LEN
+ to.sin_len = sizeof to;
+#endif
+
+#ifdef DEBUG_PACKET
+ dump_raw ((unsigned char *)&raw, packet_length);
+#endif
+
+ /* Make sure outgoing packets are at least as big
+ as a BOOTP packet. */
+ if (packet_length < BOOTP_MIN_LEN)
+ packet_length = BOOTP_MIN_LEN;
+
+ /* If this was gatewayed, send it back to the gateway... */
+ if (raw.giaddr.s_addr) {
+ to.sin_addr = raw.giaddr;
+ to.sin_port = local_port;
+
+ if (fallback_interface) {
+ result = send_packet (fallback_interface,
+ (struct packet *)0,
+ &raw, packet_length,
+ raw.siaddr,
+ &to, (struct hardware *)0);
+
+ free_lease_state (state, "dhcp_reply fallback 1");
+ lease -> state = (struct lease_state *)0;
+ return;
+ }
+
+ /* If the client is RENEWING, unicast to the client using the
+ regular IP stack. Some clients, particularly those that
+ follow RFC1541, are buggy, and send both ciaddr and
+ server-identifier. We deal with this situation by assuming
+ that if we got both dhcp-server-identifier and ciaddr, and
+ giaddr was not set, then the client is on the local
+ network, and we can therefore unicast or broadcast to it
+ successfully. A client in REQUESTING state on another
+ network that's making this mistake will have set giaddr,
+ and will therefore get a relayed response from the above
+ code. */
+ } else if (raw.ciaddr.s_addr &&
+ !((state -> got_server_identifier ||
+ (raw.flags & htons (BOOTP_BROADCAST))) &&
+ /* XXX This won't work if giaddr isn't zero, but it is: */
+ (state -> shared_network == lease -> shared_network)) &&
+ state -> offer == DHCPACK) {
+ to.sin_addr = raw.ciaddr;
+ to.sin_port = remote_port;
+
+ if (fallback_interface) {
+ result = send_packet (fallback_interface,
+ (struct packet *)0,
+ &raw, packet_length,
+ raw.siaddr, &to,
+ (struct hardware *)0);
+ free_lease_state (state,
+ "dhcp_reply fallback 2");
+ lease -> state = (struct lease_state *)0;
+ return;
+ }
+
+ /* If it comes from a client that already knows its address
+ and is not requesting a broadcast response, and we can
+ unicast to a client without using the ARP protocol, sent it
+ directly to that client. */
+ } else if (!(raw.flags & htons (BOOTP_BROADCAST))) {
+ to.sin_addr = raw.yiaddr;
+ to.sin_port = remote_port;
+
+ /* Otherwise, broadcast it on the local network. */
+ } else {
+ to.sin_addr.s_addr = htonl (INADDR_BROADCAST);
+ to.sin_port = remote_port;
+ }
+
+ memcpy (&from, state -> from.iabuf, sizeof from);
+
+ result = send_packet (state -> ip,
+ (struct packet *)0, &raw, packet_length,
+ from, &to, &hto);
+
+ free_lease_state (state, "dhcp_reply");
+ lease -> state = (struct lease_state *)0;
+}
+
+struct lease *find_lease (packet, share, ours)
+ struct packet *packet;
+ struct shared_network *share;
+ int *ours;
+{
+ struct lease *uid_lease, *ip_lease, *hw_lease;
+ struct lease *lease = (struct lease *)0;
+ struct iaddr cip;
+ struct host_decl *hp, *host = (struct host_decl *)0;
+ struct lease *fixed_lease;
+
+ /* Figure out what IP address the client is requesting, if any. */
+ if (packet -> options [DHO_DHCP_REQUESTED_ADDRESS].len &&
+ packet -> options [DHO_DHCP_REQUESTED_ADDRESS].len == 4) {
+ packet -> got_requested_address = 1;
+ cip.len = 4;
+ memcpy (cip.iabuf,
+ packet -> options [DHO_DHCP_REQUESTED_ADDRESS].data,
+ cip.len);
+ } else if (packet -> raw -> ciaddr.s_addr) {
+ cip.len = 4;
+ memcpy (cip.iabuf, &packet -> raw -> ciaddr, 4);
+ } else
+ cip.len = 0;
+
+ /* Try to find a host or lease that's been assigned to the
+ specified unique client identifier. */
+ if (packet -> options [DHO_DHCP_CLIENT_IDENTIFIER].len) {
+ /* First, try to find a fixed host entry for the specified
+ client identifier... */
+ hp = find_hosts_by_uid (packet -> options
+ [DHO_DHCP_CLIENT_IDENTIFIER].data,
+ packet -> options
+ [DHO_DHCP_CLIENT_IDENTIFIER].len);
+ if (hp) {
+ host = hp;
+ fixed_lease = mockup_lease (packet, share, hp);
+ uid_lease = (struct lease *)0;
+ } else {
+ uid_lease = find_lease_by_uid
+ (packet -> options
+ [DHO_DHCP_CLIENT_IDENTIFIER].data,
+ packet -> options
+ [DHO_DHCP_CLIENT_IDENTIFIER].len);
+ /* Find the lease matching this uid that's on the
+ network the packet came from (if any). */
+ for (; uid_lease; uid_lease = uid_lease -> n_uid)
+ if (uid_lease -> shared_network == share)
+ break;
+ fixed_lease = (struct lease *)0;
+ if (uid_lease &&
+ (uid_lease -> flags & ABANDONED_LEASE))
+ uid_lease = (struct lease *)0;
+ }
+ } else {
+ uid_lease = (struct lease *)0;
+ fixed_lease = (struct lease *)0;
+ }
+
+ /* If we didn't find a fixed lease using the uid, try doing
+ it with the hardware address... */
+ if (!fixed_lease) {
+ hp = find_hosts_by_haddr (packet -> raw -> htype,
+ packet -> raw -> chaddr,
+ packet -> raw -> hlen);
+ if (hp) {
+ host = hp; /* Save it for later. */
+ fixed_lease = mockup_lease (packet, share, hp);
+ }
+ }
+
+ /* If fixed_lease is present but does not match the requested
+ IP address, and this is a DHCPREQUEST, then we can't return
+ any other lease, so we might as well return now. */
+ if (packet -> packet_type == DHCPREQUEST && fixed_lease &&
+ (fixed_lease -> ip_addr.len != cip.len ||
+ memcmp (fixed_lease -> ip_addr.iabuf,
+ cip.iabuf, cip.len))) {
+ if (ours)
+ *ours = 1;
+ strlcpy (dhcp_message, "requested address is incorrect",
+ sizeof (dhcp_message));
+ return (struct lease *)0;
+ }
+
+ /* Try to find a lease that's been attached to the client's
+ hardware address... */
+ hw_lease = find_lease_by_hw_addr (packet -> raw -> chaddr,
+ packet -> raw -> hlen);
+ /* Find the lease that's on the network the packet came from
+ (if any). */
+ for (; hw_lease; hw_lease = hw_lease -> n_hw) {
+ if (hw_lease -> shared_network == share) {
+ if ((hw_lease -> flags & ABANDONED_LEASE))
+ continue;
+ if (packet -> packet_type)
+ break;
+ if (hw_lease -> flags &
+ (BOOTP_LEASE | DYNAMIC_BOOTP_OK))
+ break;
+ }
+ }
+
+ /* Try to find a lease that's been allocated to the client's
+ IP address. */
+ if (cip.len)
+ ip_lease = find_lease_by_ip_addr (cip);
+ else
+ ip_lease = (struct lease *)0;
+
+ /* If ip_lease is valid at this point, set ours to one, so that
+ even if we choose a different lease, we know that the address
+ the client was requesting was ours, and thus we can NAK it. */
+ if (ip_lease && ours)
+ *ours = 1;
+
+ /* If the requested IP address isn't on the network the packet
+ came from, don't use it. Allow abandoned leases to be matched
+ here - if the client is requesting it, there's a decent chance
+ that it's because the lease database got trashed and a client
+ that thought it had this lease answered an ARP or PING, causing the
+ lease to be abandoned. If so, this request probably came from
+ that client. */
+ if (ip_lease && (ip_lease -> shared_network != share)) {
+ ip_lease = (struct lease *)0;
+ strlcpy (dhcp_message, "requested address on bad subnet",
+ sizeof(dhcp_message));
+ }
+
+ /* Toss ip_lease if it hasn't yet expired and isn't owned by the
+ client. */
+ if (ip_lease &&
+ ip_lease -> ends >= cur_time &&
+ ip_lease != uid_lease) {
+ int i = DHO_DHCP_CLIENT_IDENTIFIER;
+ /* Make sure that ip_lease actually belongs to the client,
+ and toss it if not. */
+ if ((ip_lease -> uid_len &&
+ packet -> options [i].data &&
+ ip_lease -> uid_len == packet -> options [i].len &&
+ !memcmp (packet -> options [i].data,
+ ip_lease -> uid, ip_lease -> uid_len)) ||
+ (!ip_lease -> uid_len &&
+ (ip_lease -> hardware_addr.htype ==
+ packet -> raw -> htype) &&
+ ip_lease -> hardware_addr.hlen == packet -> raw -> hlen &&
+ !memcmp (ip_lease -> hardware_addr.haddr,
+ packet -> raw -> chaddr,
+ ip_lease -> hardware_addr.hlen))) {
+ if (uid_lease) {
+ if (uid_lease -> ends > cur_time) {
+ warn ("client %s has duplicate leases on %s",
+ print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr),
+ ip_lease -> shared_network -> name);
+
+ if (uid_lease &&
+ !packet -> raw -> ciaddr.s_addr)
+ release_lease (uid_lease);
+ }
+ uid_lease = ip_lease;
+ }
+ } else {
+ strlcpy (dhcp_message,
+ "requested address is not available",
+ sizeof(dhcp_message));
+ ip_lease = (struct lease *)0;
+ }
+
+ /* If we get to here and fixed_lease is not null, that means
+ that there are both a dynamic lease and a fixed-address
+ declaration for the same IP address. */
+ if (packet -> packet_type == DHCPREQUEST && fixed_lease) {
+ fixed_lease = (struct lease *)0;
+ db_conflict:
+ warn ("Both dynamic and static leases present for %s.",
+ piaddr (cip));
+ warn ("Either remove host declaration %s or remove %s",
+ (fixed_lease && fixed_lease -> host
+ ? (fixed_lease -> host -> name
+ ? fixed_lease -> host -> name : piaddr (cip))
+ : piaddr (cip)),
+ piaddr (cip));
+ warn ("from the dynamic address pool for %s",
+ share -> name);
+ if (fixed_lease)
+ ip_lease = (struct lease *)0;
+ strlcpy (dhcp_message,
+ "database conflict - call for help!",
+ sizeof(dhcp_message));
+ }
+ }
+
+ /* If we get to here with both fixed_lease and ip_lease not
+ null, then we have a configuration file bug. */
+ if (packet -> packet_type == DHCPREQUEST && fixed_lease && ip_lease)
+ goto db_conflict;
+
+ /* Toss hw_lease if it hasn't yet expired and the uid doesn't
+ match, except that if the hardware address matches and the
+ client is now doing dynamic BOOTP (and thus hasn't provided
+ a uid) we let the client get away with it. */
+ if (hw_lease &&
+ hw_lease -> ends >= cur_time &&
+ hw_lease -> uid &&
+ packet -> options [DHO_DHCP_CLIENT_IDENTIFIER].len &&
+ hw_lease != uid_lease)
+ hw_lease = (struct lease *)0;
+
+ /* Toss extra pointers to the same lease... */
+ if (hw_lease == uid_lease)
+ hw_lease = (struct lease *)0;
+ if (ip_lease == hw_lease)
+ hw_lease = (struct lease *)0;
+ if (ip_lease == uid_lease)
+ uid_lease = (struct lease *)0;
+
+ /* If we've already eliminated the lease, it wasn't there to
+ begin with. If we have come up with a matching lease,
+ set the message to bad network in case we have to throw it out. */
+ if (!ip_lease) {
+ strlcpy (dhcp_message, "requested address not available",
+ sizeof(dhcp_message));
+ }
+
+ /* Now eliminate leases that are on the wrong network... */
+ if (ip_lease &&
+ (share != ip_lease -> shared_network)) {
+ if (packet -> packet_type == DHCPREQUEST)
+ release_lease (ip_lease);
+ ip_lease = (struct lease *)0;
+ }
+ if (uid_lease &&
+ (share != uid_lease -> shared_network)) {
+ if (packet -> packet_type == DHCPREQUEST)
+ release_lease (uid_lease);
+ uid_lease = (struct lease *)0;
+ }
+ if (hw_lease &&
+ (share != hw_lease -> shared_network)) {
+ if (packet -> packet_type == DHCPREQUEST)
+ release_lease (hw_lease);
+ hw_lease = (struct lease *)0;
+ }
+
+ /* If this is a DHCPREQUEST, make sure the lease we're going to return
+ matches the requested IP address. If it doesn't, don't return a
+ lease at all. */
+ if (packet -> packet_type == DHCPREQUEST && !ip_lease && !fixed_lease)
+ return (struct lease *)0;
+
+ /* At this point, if fixed_lease is nonzero, we can assign it to
+ this client. */
+ if (fixed_lease) {
+ lease = fixed_lease;
+ }
+
+ /* If we got a lease that matched the ip address and don't have
+ a better offer, use that; otherwise, release it. */
+ if (ip_lease) {
+ if (lease) {
+ if (packet -> packet_type == DHCPREQUEST)
+ release_lease (ip_lease);
+ } else {
+ lease = ip_lease;
+ lease -> host = (struct host_decl *)0;
+ }
+ }
+
+ /* If we got a lease that matched the client identifier, we may want
+ to use it, but if we already have a lease we like, we must free
+ the lease that matched the client identifier. */
+ if (uid_lease) {
+ if (lease) {
+ if (packet -> packet_type == DHCPREQUEST)
+ release_lease (uid_lease);
+ } else {
+ lease = uid_lease;
+ lease -> host = (struct host_decl *)0;
+ }
+ }
+
+ /* The lease that matched the hardware address is treated likewise. */
+ if (hw_lease) {
+ if (lease) {
+ if (packet -> packet_type == DHCPREQUEST)
+ release_lease (hw_lease);
+ } else {
+ lease = hw_lease;
+ lease -> host = (struct host_decl *)0;
+ }
+ }
+
+ /* If we found a host_decl but no matching address, try to
+ find a host_decl that has no address, and if there is one,
+ hang it off the lease so that we can use the supplied
+ options. */
+ if (lease && host && !lease -> host) {
+ for (; host; host = host -> n_ipaddr) {
+ if (!host -> fixed_addr) {
+ lease -> host = host;
+ break;
+ }
+ }
+ }
+
+ /* If we find an abandoned lease, take it, but print a
+ warning message, so that if it continues to lose,
+ the administrator will eventually investigate. */
+ if (lease && (lease -> flags & ABANDONED_LEASE)) {
+ if (packet -> packet_type == DHCPREQUEST) {
+ warn ("Reclaiming REQUESTed abandoned IP address %s.",
+ piaddr (lease -> ip_addr));
+ lease -> flags &= ~ABANDONED_LEASE;
+ } else
+ lease = (struct lease *)0;
+ }
+
+ return lease;
+}
+
+/* Search the provided host_decl structure list for an address that's on
+ the specified shared network. If one is found, mock up and return a
+ lease structure for it; otherwise return the null pointer. */
+
+struct lease *mockup_lease (packet, share, hp)
+ struct packet *packet;
+ struct shared_network *share;
+ struct host_decl *hp;
+{
+ static struct lease mock;
+
+ mock.subnet = find_host_for_network (&hp, &mock.ip_addr, share);
+ if (!mock.subnet)
+ return (struct lease *)0;
+ mock.next = mock.prev = (struct lease *)0;
+ mock.shared_network = mock.subnet -> shared_network;
+ mock.host = hp;
+
+ if (hp -> group -> options [DHO_DHCP_CLIENT_IDENTIFIER]) {
+ mock.uid = hp -> group ->
+ options [DHO_DHCP_CLIENT_IDENTIFIER] -> value;
+ mock.uid_len = hp -> group ->
+ options [DHO_DHCP_CLIENT_IDENTIFIER] -> len;
+ } else {
+ mock.uid = (unsigned char *)0;
+ mock.uid_len = 0;
+ }
+
+ mock.hardware_addr = hp -> interface;
+ mock.starts = mock.timestamp = mock.ends = MIN_TIME;
+ mock.flags = STATIC_LEASE;
+ return &mock;
+}
diff --git a/usr.sbin/dhcpd/dhcp.h b/usr.sbin/dhcpd/dhcp.h
new file mode 100644
index 00000000000..b96ec3d096d
--- /dev/null
+++ b/usr.sbin/dhcpd/dhcp.h
@@ -0,0 +1,168 @@
+/* dhcp.h
+
+ Protocol structures... */
+
+/*
+ * Copyright (c) 1995, 1996 The Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#define DHCP_UDP_OVERHEAD (14 + /* Ethernet header */ \
+ 20 + /* IP header */ \
+ 8) /* UDP header */
+#define DHCP_SNAME_LEN 64
+#define DHCP_FILE_LEN 128
+#define DHCP_FIXED_NON_UDP 236
+#define DHCP_FIXED_LEN (DHCP_FIXED_NON_UDP + DHCP_UDP_OVERHEAD)
+ /* Everything but options. */
+#define DHCP_MTU_MAX 1500
+#define DHCP_OPTION_LEN (DHCP_MTU_MAX - DHCP_FIXED_LEN)
+
+#define BOOTP_MIN_LEN 300
+#define DHCP_MIN_LEN 548
+
+struct dhcp_packet {
+ u_int8_t op; /* Message opcode/type */
+ u_int8_t htype; /* Hardware addr type (see net/if_types.h) */
+ u_int8_t hlen; /* Hardware addr length */
+ u_int8_t hops; /* Number of relay agent hops from client */
+ u_int32_t xid; /* Transaction ID */
+ u_int16_t secs; /* Seconds since client started looking */
+ u_int16_t flags; /* Flag bits */
+ struct in_addr ciaddr; /* Client IP address (if already in use) */
+ struct in_addr yiaddr; /* Client IP address */
+ struct in_addr siaddr; /* IP address of next server to talk to */
+ struct in_addr giaddr; /* DHCP relay agent IP address */
+ unsigned char chaddr [16]; /* Client hardware address */
+ char sname [DHCP_SNAME_LEN]; /* Server name */
+ char file [DHCP_FILE_LEN]; /* Boot filename */
+ unsigned char options [DHCP_OPTION_LEN];
+ /* Optional parameters
+ (actual length dependent on MTU). */
+};
+
+/* BOOTP (rfc951) message types */
+#define BOOTREQUEST 1
+#define BOOTREPLY 2
+
+/* Possible values for flags field... */
+#define BOOTP_BROADCAST 32768L
+
+/* Possible values for hardware type (htype) field... */
+#define HTYPE_ETHER 1 /* Ethernet 10Mbps */
+#define HTYPE_IEEE802 6 /* IEEE 802.2 Token Ring... */
+#define HTYPE_FDDI 8 /* FDDI... */
+
+/* Magic cookie validating dhcp options field (and bootp vendor
+ extensions field). */
+#define DHCP_OPTIONS_COOKIE "\143\202\123\143"
+
+/* DHCP Option codes: */
+
+#define DHO_PAD 0
+#define DHO_SUBNET_MASK 1
+#define DHO_TIME_OFFSET 2
+#define DHO_ROUTERS 3
+#define DHO_TIME_SERVERS 4
+#define DHO_NAME_SERVERS 5
+#define DHO_DOMAIN_NAME_SERVERS 6
+#define DHO_LOG_SERVERS 7
+#define DHO_COOKIE_SERVERS 8
+#define DHO_LPR_SERVERS 9
+#define DHO_IMPRESS_SERVERS 10
+#define DHO_RESOURCE_LOCATION_SERVERS 11
+#define DHO_HOST_NAME 12
+#define DHO_BOOT_SIZE 13
+#define DHO_MERIT_DUMP 14
+#define DHO_DOMAIN_NAME 15
+#define DHO_SWAP_SERVER 16
+#define DHO_ROOT_PATH 17
+#define DHO_EXTENSIONS_PATH 18
+#define DHO_IP_FORWARDING 19
+#define DHO_NON_LOCAL_SOURCE_ROUTING 20
+#define DHO_POLICY_FILTER 21
+#define DHO_MAX_DGRAM_REASSEMBLY 22
+#define DHO_DEFAULT_IP_TTL 23
+#define DHO_PATH_MTU_AGING_TIMEOUT 24
+#define DHO_PATH_MTU_PLATEAU_TABLE 25
+#define DHO_INTERFACE_MTU 26
+#define DHO_ALL_SUBNETS_LOCAL 27
+#define DHO_BROADCAST_ADDRESS 28
+#define DHO_PERFORM_MASK_DISCOVERY 29
+#define DHO_MASK_SUPPLIER 30
+#define DHO_ROUTER_DISCOVERY 31
+#define DHO_ROUTER_SOLICITATION_ADDRESS 32
+#define DHO_STATIC_ROUTES 33
+#define DHO_TRAILER_ENCAPSULATION 34
+#define DHO_ARP_CACHE_TIMEOUT 35
+#define DHO_IEEE802_3_ENCAPSULATION 36
+#define DHO_DEFAULT_TCP_TTL 37
+#define DHO_TCP_KEEPALIVE_INTERVAL 38
+#define DHO_TCP_KEEPALIVE_GARBAGE 39
+#define DHO_NIS_DOMAIN 40
+#define DHO_NIS_SERVERS 41
+#define DHO_NTP_SERVERS 42
+#define DHO_VENDOR_ENCAPSULATED_OPTIONS 43
+#define DHO_NETBIOS_NAME_SERVERS 44
+#define DHO_NETBIOS_DD_SERVER 45
+#define DHO_NETBIOS_NODE_TYPE 46
+#define DHO_NETBIOS_SCOPE 47
+#define DHO_FONT_SERVERS 48
+#define DHO_X_DISPLAY_MANAGER 49
+#define DHO_DHCP_REQUESTED_ADDRESS 50
+#define DHO_DHCP_LEASE_TIME 51
+#define DHO_DHCP_OPTION_OVERLOAD 52
+#define DHO_DHCP_MESSAGE_TYPE 53
+#define DHO_DHCP_SERVER_IDENTIFIER 54
+#define DHO_DHCP_PARAMETER_REQUEST_LIST 55
+#define DHO_DHCP_MESSAGE 56
+#define DHO_DHCP_MAX_MESSAGE_SIZE 57
+#define DHO_DHCP_RENEWAL_TIME 58
+#define DHO_DHCP_REBINDING_TIME 59
+#define DHO_DHCP_CLASS_IDENTIFIER 60
+#define DHO_DHCP_CLIENT_IDENTIFIER 61
+#define DHO_DHCP_USER_CLASS_ID 77
+#define DHO_END 255
+
+/* DHCP message types. */
+#define DHCPDISCOVER 1
+#define DHCPOFFER 2
+#define DHCPREQUEST 3
+#define DHCPDECLINE 4
+#define DHCPACK 5
+#define DHCPNAK 6
+#define DHCPRELEASE 7
+#define DHCPINFORM 8
diff --git a/usr.sbin/dhcpd/dhcpd.8 b/usr.sbin/dhcpd/dhcpd.8
new file mode 100644
index 00000000000..80c648c0391
--- /dev/null
+++ b/usr.sbin/dhcpd/dhcpd.8
@@ -0,0 +1,399 @@
+.\" $OpenBSD: dhcpd.8,v 1.1 2004/04/13 23:41:48 henning Exp $
+.\"
+.\" Copyright (c) 1995, 1996 The Internet Software Consortium.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of The Internet Software Consortium nor the names
+.\" of its contributors may be used to endorse or promote products derived
+.\" from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+.\" CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+.\" INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+.\" DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+.\" CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+.\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+.\" ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+.\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+.\" OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" This software has been written for the Internet Software Consortium
+.\" by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+.\" Enterprises. To learn more about the Internet Software Consortium,
+.\" see ``http://www.isc.org/''. To learn more about Vixie
+.\" Enterprises, see ``http://www.vix.com''.
+.\"
+.Dd January 1, 1995
+.Dt DHCPD 8
+.Os
+.Sh NAME
+.Nm dhcpd
+.Nd Dynamic Host Configuration Protocol Server
+.Sh SYNOPSIS
+.Nm dhcpd
+.Op Fl p Ar port
+.Op Fl f
+.Op Fl d
+.Op Fl q
+.Op Fl cf Ar config-file
+.Op Fl lf Ar lease-file
+.Bk -words
+.Op Ar if0 Op Ar ... ifN
+.Ek
+.Sh DESCRIPTION
+The Internet Software Consortium DHCP Server,
+.Nm dhcpd ,
+implements the Dynamic Host Configuration Protocol (DHCP) and the
+Internet Bootstrap Protocol (BOOTP).
+DHCP allows hosts on a TCP/IP network to request and be assigned IP addresses,
+and also to discover information about the network to which they are attached.
+BOOTP provides similar functionality, with certain restrictions.
+.Sh OPERATION
+The DHCP protocol allows a host which is unknown to the network
+administrator to be automatically assigned a new IP address out of a
+pool of IP addresses for its network.
+In order for this to work, the network administrator allocates address pools
+in each subnet and enters them into the
+.Xr dhcpd.conf 5
+file.
+.Pp
+On startup,
+.Nm
+reads the
+.Pa dhcpd.conf
+file and stores a list of available addresses on each subnet in memory.
+When a client requests an address using the DHCP protocol,
+.Nm
+allocates an address for it.
+Each client is assigned a lease, which expires after an amount of time
+chosen by the administrator (by default, one day).
+Before leases expire, the clients to which leases are assigned are expected
+to renew them in order to continue to use the addresses.
+Once a lease has expired, the client to which that lease was assigned is no
+longer permitted to use the leased IP address.
+.Pp
+In order to keep track of leases across system reboots and server restarts,
+.Nm
+keeps a list of leases it has assigned in the
+.Xr dhcpd.leases 5
+file.
+Before
+.Nm
+grants a lease to a host, it records the lease in this file and makes sure
+that the contents of the file are flushed to disk.
+This ensures that even in the event of a system crash,
+.Nm
+will not forget about a lease that it has assigned.
+On startup, after reading the
+.Pa dhcpd.conf
+file,
+.Nm
+reads the
+.Pa dhcpd.leases
+file to refresh its memory about what leases have been assigned.
+.Pp
+New leases are appended to the end of the
+.Pa dhcpd.leases
+file.
+In order to prevent the file from becoming arbitrarily large,
+from time to time
+.Nm
+creates a new
+.Pa dhcpd.leases
+file from its in-core lease database.
+Once this file has been written to disk, the old file is renamed
+.Pa dhcpd.leases~ ,
+and the new file is renamed
+.Pa dhcpd.leases .
+If the system crashes in the middle of this process, whichever
+.Pa dhcpd.leases
+file remains will contain all the lease information, so there is no need for
+a special crash recovery process.
+.Pp
+BOOTP support is also provided by this server.
+Unlike DHCP, the BOOTP protocol does not provide a protocol for recovering
+dynamically-assigned addresses once they are no longer needed.
+It is still possible to dynamically assign addresses to BOOTP clients, but
+some administrative process for reclaiming addresses is required.
+By default, leases are granted to BOOTP clients in perpetuity, although
+the network administrator may set an earlier cutoff date or a shorter
+lease length for BOOTP leases if that makes sense.
+.Pp
+BOOTP clients may also be served in the old standard way, which is
+simply to provide a declaration in the
+.Pa dhcpd.conf
+file for each BOOTP client, permanently assigning an address to each client.
+.Pp
+Whenever changes are made to the
+.Pa dhcpd.conf
+file,
+.Nm
+must be restarted.
+To restart
+.Nm dhcpd ,
+send a SIGTERM (signal 15) to the process ID contained in
+.Pa /var/run/dhcpd.pid ,
+and then re-invoke
+.Nm dhcpd .
+Because the DHCP server database is not as lightweight as a BOOTP database,
+.Nm
+does not automatically restart itself when it sees a change to the
+.Pa dhcpd.conf
+file.
+.Pp
+DHCP traffic always bypasses IPsec.
+Otherwise there could be situations when a server has an IPsec SA for the
+client and sends replies over that,
+which a newly booted client would not be able to grasp.
+.Sh COMMAND LINE
+The names of the network interfaces on which
+.Nm
+should listen for broadcasts may be specified on the command line.
+This should be done on systems where
+.Nm
+is unable to identify non-broadcast interfaces,
+but should not be required on other systems.
+If no interface names are specified on the command line,
+.Nm
+will identify all network interfaces which are up, eliminating non-broadcast
+interfaces if possible, and listen for DHCP broadcasts on each interface.
+.Pp
+If
+.Nm
+should listen on a port other than the standard (port 67), the
+.Fl p
+flag may used.
+It should be followed by the UDP port number on which
+.Nm
+should listen.
+This is mostly useful for debugging purposes.
+If the
+.Fl p
+flag is specified, the server will transmit responses to clients at a
+port number that is one greater than the one specified \- i.e., if you
+specify
+.Fl p
+67, then the server will listen on port 67 and transmit to port 68.
+Datagrams that must go through relay agents are sent to the port
+number specified with the
+.Fl p
+flag.
+If you wish to use alternate port numbers, you must configure
+any relay agents you are using to use the same alternate port numbers.
+.Pp
+To run
+.Nm
+as a foreground process, rather than allowing it to run as a daemon in the
+background, the
+.Fl f
+flag should be specified.
+This is useful when running
+.Nm
+under a debugger, or when running it out of inittab on System V systems.
+.Pp
+To have
+.Nm
+log to
+.Ar stderr ,
+the
+.Fl d
+flag should be specified.
+This can be useful for debugging, and also at sites where a complete log of
+all dhcp activity must be kept, but
+.Xr syslogd 8
+is not reliable or otherwise cannot be used.
+Normally,
+.Nm
+will log all output using the
+.Xr syslog 3
+function with the log facility set to LOG_DAEMON.
+.Pp
+.Nm
+can be made to use an alternate configuration file with the
+.Fl cf
+flag, or an alternate lease file with the
+.Fl lf
+flag.
+Because of the importance of using the same lease database at all times when
+running
+.Nm
+in production, these options should be used
+.Em only
+for testing lease files or database files in a non-production environment.
+.Pp
+To avoid printing out the entire copyright message on start-up, the
+.Fl q
+flag should be specified.
+.Sh CONFIGURATION
+The syntax of the
+.Xr dhcpd.conf 5
+file is discussed separately.
+This section should be used as an overview of the configuration process,
+and the
+.Xr dhcpd.conf 5
+documentation should be consulted for detailed reference information.
+.Bl -tag -width 3n
+.It Subnets
+.Nm
+needs to know the subnet numbers and netmasks of all subnets for
+which it will be providing service.
+In addition, in order to dynamically allocate addresses, it must be assigned
+one or more ranges of addresses on each subnet which it can in turn assign
+to client hosts as they boot.
+Thus, a very simple configuration providing DHCP support might look like this:
+.Bd -literal -offset indent
+subnet 239.252.197.0 netmask 255.255.255.0 {
+ range 239.252.197.10 239.252.197.250;
+}
+.Ed
+.Pp
+Multiple address ranges may be specified like this:
+.Bd -literal -offset indent
+subnet 239.252.197.0 netmask 255.255.255.0 {
+ range 239.252.197.10 239.252.197.107;
+ range 239.252.197.113 239.252.197.250;
+}
+.Ed
+.Pp
+If a subnet will only be provided with BOOTP service and no dynamic
+address assignment, the range clause can be left out entirely, but the
+subnet statement must appear.
+.It Lease Lengths
+DHCP leases can be assigned almost any length from zero seconds to infinity.
+What lease length makes sense for any given subnet, or for any given
+installation, will vary depending on the kinds of hosts being served.
+.Pp
+For example, in an office environment where systems are added from
+time to time and removed from time to time, but move relatively
+infrequently, it might make sense to allow lease times of a month of more.
+In a final test environment on a manufacturing floor, it may make more sense
+to assign a maximum lease length of 30 minutes \- enough time to go through a
+simple test procedure on a network appliance before packaging it up for
+delivery.
+.Pp
+It is possible to specify two lease lengths: the default length that
+will be assigned if a client doesn't ask for any particular lease
+length, and a maximum lease length.
+These are specified as clauses to the subnet command:
+.Bd -literal -offset indent
+subnet 239.252.197.0 netmask 255.255.255.0 {
+ range 239.252.197.10 239.252.197.107;
+ default-lease-time 600;
+ max-lease-time 7200;
+}
+.Ed
+.Pp
+This particular subnet declaration specifies a default lease time of
+600 seconds (ten minutes), and a maximum lease time of 7200 seconds
+(two hours).
+Other common values would be 86400 (one day), 604800 (one week)
+and 2592000 (30 days).
+.Pp
+Each subnet need not have the same lease \- in the case of an office
+environment and a manufacturing environment served by the same DHCP
+server, it might make sense to have widely disparate values for
+default and maximum lease times on each subnet.
+.It BOOTP Support
+Each BOOTP client must be explicitly declared in the
+.Xr dhcpd.conf 5
+file.
+A very basic client declaration will specify the client network interface's
+hardware address and the IP address to assign to that client.
+If the client needs to be able to load a boot file from the server,
+that file's name must be specified.
+A simple BOOTP client declaration might look like this:
+.Bd -literal -offset indent
+host haagen {
+ hardware ethernet 08:00:2b:4c:59:23;
+ fixed-address 239.252.197.9;
+ filename "/tftpboot/haagen.boot";
+}
+.Ed
+.It Options
+DHCP (and also BOOTP with Vendor Extensions) provides a mechanism
+whereby the server can provide the client with information about how
+to configure its network interface (e.g., subnet mask), and also how
+the client can access various network services (e.g., DNS, IP routers,
+and so on).
+.Pp
+These options can be specified on a per-subnet basis, and, for BOOTP
+clients, also on a per-client basis.
+In the event that a BOOTP client declaration specifies options that are
+also specified in its subnet declaration, the options specified in the
+client declaration take precedence.
+A reasonably complete DHCP configuration might look something like this:
+.Bd -literal -offset indent
+subnet 239.252.197.0 netmask 255.255.255.0 {
+ range 239.252.197.10 239.252.197.250;
+ default-lease-time 600 max-lease-time 7200;
+ option subnet-mask 255.255.255.0;
+ option broadcast-address 239.252.197.255;
+ option routers 239.252.197.1;
+ option domain-name-servers 239.252.197.2, 239.252.197.3;
+ option domain-name "isc.org";
+}
+.Ed
+.Pp
+A BOOTP host on that subnet that needs to be in a different domain and
+use a different name server might be declared as follows:
+.Bd -literal -offset indent
+host haagen {
+ hardware ethernet 08:00:2b:4c:59:23;
+ fixed-address 239.252.197.9;
+ filename "/tftpboot/haagen.boot";
+ option domain-name-servers 192.5.5.1;
+ option domain-name "vix.com";
+}
+.Ed
+.El
+.Pp
+A more complete description of the
+.Pa dhcpd.conf
+file syntax is provided in
+.Xr dhcpd.conf 5 .
+.Sh FILES
+.Bl -tag -width "/var/db/dhcpd.leases~ " -compact
+.It /etc/dhcpd.conf
+DHCPD configuration file.
+.It /var/db/dhcpd.leases
+Current DHCPD lease file.
+.It /var/db/dhcpd.leases~
+Backup DHCPD lease file.
+.It /var/run/dhcpd.pid
+DHCPD PID.
+.El
+.Sh SEE ALSO
+.Xr dhcpd.conf 5 ,
+.Xr dhcpd.leases 5 ,
+.Xr dhclient 8 ,
+.Xr dhcp 8 ,
+.Xr dhcrelay 8 ,
+.Xr pxeboot 8
+.Sh AUTHORS
+.Nm
+was written by
+.An Ted Lemon Aq mellon@vix.com
+under a contract with Vixie Labs.
+Funding for this project was provided by the Internet Software Corporation.
+Information about the Internet Software Consortium can be found at
+.Pa http://www.isc.org/ .
+.Sh BUGS
+We realize that it would be nice if one could send a SIGHUP to the server
+and have it reload the database.
+This is not technically impossible, but it would require a great deal of work,
+our resources are extremely limited, and they can be better spent elsewhere.
+So please don't complain about this on the mailing list unless you're prepared
+to fund a project to implement this feature, or prepared to do it yourself.
diff --git a/usr.sbin/dhcpd/dhcpd.c b/usr.sbin/dhcpd/dhcpd.c
new file mode 100644
index 00000000000..eff62fd6047
--- /dev/null
+++ b/usr.sbin/dhcpd/dhcpd.c
@@ -0,0 +1,370 @@
+/* dhcpd.c
+
+ DHCP Server Daemon. */
+
+/*
+ * Copyright (c) 1995, 1996, 1997, 1998, 1999
+ * The Internet Software Consortium. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+
+static char copyright[] =
+"Copyright 1995, 1996, 1997, 1998, 1999 The Internet Software Consortium.";
+static char arr [] = "All rights reserved.";
+static char message [] = "Internet Software Consortium DHCP Server";
+static char contrib [] = "Please contribute if you find this software useful.";
+static char url [] = "For info, please visit http://www.isc.org/dhcp-contrib.html";
+
+#include "dhcpd.h"
+#include "version.h"
+
+static void usage PROTO ((char *));
+
+TIME cur_time;
+struct group root_group;
+
+struct iaddr server_identifier;
+int server_identifier_matched;
+
+u_int16_t local_port;
+u_int16_t remote_port;
+
+int log_priority;
+#ifdef DEBUG
+int log_perror = -1;
+#else
+int log_perror = 1;
+#endif
+
+char *path_dhcpd_conf = _PATH_DHCPD_CONF;
+char *path_dhcpd_db = _PATH_DHCPD_DB;
+char *path_dhcpd_pid = _PATH_DHCPD_PID;
+
+int main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int i, status;
+ struct servent *ent;
+ char *s, *appname;
+ int cftest = 0;
+#ifndef DEBUG
+ int pidfilewritten = 0;
+ int pid;
+ char pbuf [20];
+ int daemon = 1;
+#endif
+ int quiet = 0;
+
+ appname = strrchr (argv [0], '/');
+ if (!appname)
+ appname = argv [0];
+ else
+ appname++;
+
+ /* Initially, log errors to stderr as well as to syslogd. */
+ openlog (appname, LOG_NDELAY, DHCPD_LOG_FACILITY);
+ setlogmask (LOG_UPTO (LOG_INFO));
+
+ for (i = 1; i < argc; i++) {
+ if (!strcmp (argv [i], "-p")) {
+ if (++i == argc)
+ usage (appname);
+ for (s = argv [i]; *s; s++)
+ if (!isdigit (*s))
+ error ("%s: not a valid UDP port",
+ argv [i]);
+ status = atoi (argv [i]);
+ if (status < 1 || status > 65535)
+ error ("%s: not a valid UDP port",
+ argv [i]);
+ local_port = htons (status);
+ debug ("binding to user-specified port %d",
+ ntohs (local_port));
+ } else if (!strcmp (argv [i], "-f")) {
+#ifndef DEBUG
+ daemon = 0;
+#endif
+ } else if (!strcmp (argv [i], "-d")) {
+#ifndef DEBUG
+ daemon = 0;
+#endif
+ log_perror = -1;
+ } else if (!strcmp (argv [i], "-cf")) {
+ if (++i == argc)
+ usage (appname);
+ path_dhcpd_conf = argv [i];
+ } else if (!strcmp (argv [i], "-pf")) {
+ if (++i == argc)
+ usage (appname);
+ path_dhcpd_pid = argv [i];
+ } else if (!strcmp (argv [i], "-lf")) {
+ if (++i == argc)
+ usage (appname);
+ path_dhcpd_db = argv [i];
+ } else if (!strcmp (argv [i], "-t")) {
+ /* test configurations only */
+#ifndef DEBUG
+ daemon = 0;
+#endif
+ cftest = 1;
+ log_perror = -1;
+ } else if (!strcmp (argv [i], "-q")) {
+ quiet = 1;
+ quiet_interface_discovery = 1;
+ } else if (argv [i][0] == '-') {
+ usage (appname);
+ } else {
+ struct interface_info *tmp =
+ ((struct interface_info *)
+ dmalloc (sizeof *tmp, "get_interface_list"));
+ if (!tmp)
+ error ("Insufficient memory to %s %s",
+ "record interface", argv [i]);
+ memset (tmp, 0, sizeof *tmp);
+ strlcpy (tmp->name, argv [i], sizeof(tmp->name));
+ tmp->next = interfaces;
+ tmp->flags = INTERFACE_REQUESTED;
+ interfaces = tmp;
+ }
+ }
+
+ if (!quiet) {
+ note ("%s %s", message, DHCP_VERSION);
+ note ("%s", copyright);
+ note ("%s", arr);
+ note ("%s", "");
+ note ("%s", contrib);
+ note ("%s", url);
+ note ("%s", "");
+ } else
+ log_perror = 0;
+
+ /* Default to the DHCP/BOOTP port. */
+ if (!local_port)
+ {
+ ent = getservbyname ("dhcp", "udp");
+ if (!ent)
+ local_port = htons (67);
+ else
+ local_port = ent -> s_port;
+#ifndef __CYGWIN32__ /* XXX */
+ endservent ();
+#endif
+ }
+
+ remote_port = htons (ntohs (local_port) + 1);
+
+ /* Get the current time... */
+ GET_TIME (&cur_time);
+
+ /* Read the dhcpd.conf file... */
+ if (!readconf ())
+ error ("Configuration file errors encountered -- exiting");
+
+ /* test option should cause an early exit */
+ if (cftest)
+ exit(0);
+
+ /* Start up the database... */
+ db_startup ();
+
+ /* Discover all the network interfaces and initialize them. */
+ discover_interfaces (DISCOVER_SERVER);
+
+ /* Initialize icmp support... */
+ icmp_startup (1, lease_pinged);
+
+#ifndef DEBUG
+ if (daemon) {
+ /* First part of becoming a daemon... */
+ if ((pid = fork ()) == -1)
+ error ("Can't fork daemon: %m");
+ else if (pid)
+ exit (0);
+ }
+
+ /* Read previous pid file. */
+ if ((i = open (path_dhcpd_pid, O_RDONLY)) != -1) {
+ status = read (i, pbuf, (sizeof pbuf) - 1);
+ close (i);
+ pbuf [status] = 0;
+ pid = atoi (pbuf);
+
+ /* If the previous server process is not still running,
+ write a new pid file immediately. */
+ if (pid && (pid == getpid () || kill (pid, 0) == -1)) {
+ unlink (path_dhcpd_pid);
+ if ((i = open (path_dhcpd_pid,
+ O_WRONLY | O_CREAT, 0640)) != -1) {
+ snprintf (pbuf, sizeof(pbuf), "%d\n",
+ (int)getpid ());
+ write (i, pbuf, strlen (pbuf));
+ close (i);
+ pidfilewritten = 1;
+ }
+ } else
+ error ("There's already a DHCP server running.\n");
+ }
+
+ /* If we were requested to log to stdout on the command line,
+ keep doing so; otherwise, stop. */
+ if (log_perror == -1)
+ log_perror = 1;
+ else
+ log_perror = 0;
+
+ if (daemon) {
+ /* Become session leader and get pid... */
+ close (0);
+ close (1);
+ close (2);
+ pid = setsid ();
+ }
+
+ /* If we didn't write the pid file earlier because we found a
+ process running the logged pid, but we made it to here,
+ meaning nothing is listening on the bootp port, then write
+ the pid file out - what's in it now is bogus anyway. */
+ if (!pidfilewritten) {
+ unlink (path_dhcpd_pid);
+ if ((i = open (path_dhcpd_pid,
+ O_WRONLY | O_CREAT, 0640)) != -1) {
+ snprintf (pbuf, sizeof(pbuf), "%d\n",
+ (int)getpid ());
+ write (i, pbuf, strlen (pbuf));
+ close (i);
+ pidfilewritten = 1;
+ }
+ }
+#endif /* !DEBUG */
+
+ /* Set up the bootp packet handler... */
+ bootp_packet_handler = do_packet;
+
+ /* Receive packets and dispatch them... */
+ dispatch ();
+
+ /* Not reached */
+ return 0;
+}
+
+/* Print usage message. */
+
+static void usage (appname)
+ char *appname;
+{
+ note ("%s", message);
+ note ("%s", copyright);
+ note ("%s", arr);
+ note ("%s", "");
+ note ("%s", contrib);
+ note ("%s", url);
+ note ("%s", "");
+
+ warn ("Usage: %s [-p <UDP port #>] [-d] [-f] [-cf config-file]",
+ appname);
+ error(" [-lf lease-file] [-pf pidfile] [if0 [...ifN]]");
+}
+
+void cleanup ()
+{
+}
+
+void lease_pinged (from, packet, length)
+ struct iaddr from;
+ u_int8_t *packet;
+ int length;
+{
+ struct lease *lp;
+
+ /* Don't try to look up a pinged lease if we aren't trying to
+ ping one - otherwise somebody could easily make us churn by
+ just forging repeated ICMP EchoReply packets for us to look
+ up. */
+ if (!outstanding_pings)
+ return;
+
+ lp = find_lease_by_ip_addr (from);
+
+ if (!lp) {
+ note ("unexpected ICMP Echo Reply from %s", piaddr (from));
+ return;
+ }
+
+ if (!lp -> state && ! lp -> releasing) {
+ warn ("ICMP Echo Reply for %s arrived late or is spurious.",
+ piaddr (from));
+ return;
+ }
+
+ /* At this point it looks like we pinged a lease and got a
+ * response, which shouldn't have happened.
+ * if it did it's either one of two two cases:
+ * 1 - we pinged this lease before offering it and
+ * something answered, so we abandon it.
+ * 2 - we pinged this lease before releaseing it
+ * and something answered, so we don't release it.
+ */
+ if (lp -> releasing) {
+ warn ("IP address %s answers a ping after sending a release",
+ piaddr (lp -> ip_addr));
+ warn ("Possible release spoof - Not releasing address %s",
+ piaddr (lp -> ip_addr));
+ lp -> releasing = 0;
+ }
+ else {
+ free_lease_state (lp -> state, "lease_pinged");
+ lp -> state = (struct lease_state *)0;
+ abandon_lease (lp, "pinged before offer");
+ }
+ cancel_timeout (lease_ping_timeout, lp);
+ --outstanding_pings;
+}
+
+void lease_ping_timeout (vlp)
+ void *vlp;
+{
+ struct lease *lp = vlp;
+
+ --outstanding_pings;
+ if (lp->releasing) {
+ lp->releasing = 0;
+ release_lease(lp);
+ }
+ else
+ dhcp_reply (lp);
+}
diff --git a/usr.sbin/dhcpd/dhcpd.cat8 b/usr.sbin/dhcpd/dhcpd.cat8
new file mode 100644
index 00000000000..dda679a1c70
--- /dev/null
+++ b/usr.sbin/dhcpd/dhcpd.cat8
@@ -0,0 +1,257 @@
+DHCPD(8) OpenBSD System Manager's Manual DHCPD(8)
+
+NNAAMMEE
+ ddhhccppdd - Dynamic Host Configuration Protocol Server
+
+SSYYNNOOPPSSIISS
+ ddhhccppdd [--pp _p_o_r_t] [--ff] [--dd] [--qq] [--ccff _c_o_n_f_i_g_-_f_i_l_e] [--llff _l_e_a_s_e_-_f_i_l_e]
+ [_i_f_0 [_._._. _i_f_N]]
+
+DDEESSCCRRIIPPTTIIOONN
+ The Internet Software Consortium DHCP Server, ddhhccppdd, implements the Dy-
+ namic Host Configuration Protocol (DHCP) and the Internet Bootstrap Pro-
+ tocol (BOOTP). DHCP allows hosts on a TCP/IP network to request and be
+ assigned IP addresses, and also to discover information about the network
+ to which they are attached. BOOTP provides similar functionality, with
+ certain restrictions.
+
+OOPPEERRAATTIIOONN
+ The DHCP protocol allows a host which is unknown to the network adminis-
+ trator to be automatically assigned a new IP address out of a pool of IP
+ addresses for its network. In order for this to work, the network admin-
+ istrator allocates address pools in each subnet and enters them into the
+ dhcpd.conf(5) file.
+
+ On startup, ddhhccppdd reads the _d_h_c_p_d_._c_o_n_f file and stores a list of avail-
+ able addresses on each subnet in memory. When a client requests an ad-
+ dress using the DHCP protocol, ddhhccppdd allocates an address for it. Each
+ client is assigned a lease, which expires after an amount of time chosen
+ by the administrator (by default, one day). Before leases expire, the
+ clients to which leases are assigned are expected to renew them in order
+ to continue to use the addresses. Once a lease has expired, the client
+ to which that lease was assigned is no longer permitted to use the leased
+ IP address.
+
+ In order to keep track of leases across system reboots and server
+ restarts, ddhhccppdd keeps a list of leases it has assigned in the
+ dhcpd.leases(5) file. Before ddhhccppdd grants a lease to a host, it records
+ the lease in this file and makes sure that the contents of the file are
+ flushed to disk. This ensures that even in the event of a system crash,
+ ddhhccppdd will not forget about a lease that it has assigned. On startup,
+ after reading the _d_h_c_p_d_._c_o_n_f file, ddhhccppdd reads the _d_h_c_p_d_._l_e_a_s_e_s file to
+ refresh its memory about what leases have been assigned.
+
+ New leases are appended to the end of the _d_h_c_p_d_._l_e_a_s_e_s file. In order to
+ prevent the file from becoming arbitrarily large, from time to time ddhhccppdd
+ creates a new _d_h_c_p_d_._l_e_a_s_e_s file from its in-core lease database. Once
+ this file has been written to disk, the old file is renamed
+ _d_h_c_p_d_._l_e_a_s_e_s_~, and the new file is renamed _d_h_c_p_d_._l_e_a_s_e_s. If the system
+ crashes in the middle of this process, whichever _d_h_c_p_d_._l_e_a_s_e_s file re-
+ mains will contain all the lease information, so there is no need for a
+ special crash recovery process.
+
+ BOOTP support is also provided by this server. Unlike DHCP, the BOOTP
+ protocol does not provide a protocol for recovering dynamically-assigned
+ addresses once they are no longer needed. It is still possible to dynam-
+ ically assign addresses to BOOTP clients, but some administrative process
+ for reclaiming addresses is required. By default, leases are granted to
+ BOOTP clients in perpetuity, although the network administrator may set
+ an earlier cutoff date or a shorter lease length for BOOTP leases if that
+ makes sense.
+
+ BOOTP clients may also be served in the old standard way, which is simply
+ to provide a declaration in the _d_h_c_p_d_._c_o_n_f file for each BOOTP client,
+ permanently assigning an address to each client.
+
+ Whenever changes are made to the _d_h_c_p_d_._c_o_n_f file, ddhhccppdd must be restart-
+ ed. To restart ddhhccppdd, send a SIGTERM (signal 15) to the process ID con-
+ tained in _/_v_a_r_/_r_u_n_/_d_h_c_p_d_._p_i_d, and then re-invoke ddhhccppdd. Because the DHCP
+ server database is not as lightweight as a BOOTP database, ddhhccppdd does not
+ automatically restart itself when it sees a change to the _d_h_c_p_d_._c_o_n_f
+ file.
+
+ DHCP traffic always bypasses IPsec. Otherwise there could be situations
+ when a server has an IPsec SA for the client and sends replies over that,
+ which a newly booted client would not be able to grasp.
+
+CCOOMMMMAANNDD LLIINNEE
+ The names of the network interfaces on which ddhhccppdd should listen for
+ broadcasts may be specified on the command line. This should be done on
+ systems where ddhhccppdd is unable to identify non-broadcast interfaces, but
+ should not be required on other systems. If no interface names are spec-
+ ified on the command line, ddhhccppdd will identify all network interfaces
+ which are up, eliminating non-broadcast interfaces if possible, and lis-
+ ten for DHCP broadcasts on each interface.
+
+ If ddhhccppdd should listen on a port other than the standard (port 67), the
+ --pp flag may used. It should be followed by the UDP port number on which
+ ddhhccppdd should listen. This is mostly useful for debugging purposes. If
+ the --pp flag is specified, the server will transmit responses to clients
+ at a port number that is one greater than the one specified - i.e., if
+ you specify --pp 67, then the server will listen on port 67 and transmit to
+ port 68. Datagrams that must go through relay agents are sent to the
+ port number specified with the --pp flag. If you wish to use alternate
+ port numbers, you must configure any relay agents you are using to use
+ the same alternate port numbers.
+
+ To run ddhhccppdd as a foreground process, rather than allowing it to run as a
+ daemon in the background, the --ff flag should be specified. This is use-
+ ful when running ddhhccppdd under a debugger, or when running it out of init-
+ tab on System V systems.
+
+ To have ddhhccppdd log to _s_t_d_e_r_r, the --dd flag should be specified. This can
+ be useful for debugging, and also at sites where a complete log of all
+ dhcp activity must be kept, but syslogd(8) is not reliable or otherwise
+ cannot be used. Normally, ddhhccppdd will log all output using the syslog(3)
+ function with the log facility set to LOG_DAEMON.
+
+ ddhhccppdd can be made to use an alternate configuration file with the --ccff
+ flag, or an alternate lease file with the --llff flag. Because of the im-
+ portance of using the same lease database at all times when running ddhhccppdd
+ in production, these options should be used _o_n_l_y for testing lease files
+ or database files in a non-production environment.
+
+ To avoid printing out the entire copyright message on start-up, the --qq
+ flag should be specified.
+
+CCOONNFFIIGGUURRAATTIIOONN
+ The syntax of the dhcpd.conf(5) file is discussed separately. This sec-
+ tion should be used as an overview of the configuration process, and the
+ dhcpd.conf(5) documentation should be consulted for detailed reference
+ information.
+
+ Subnets
+ ddhhccppdd needs to know the subnet numbers and netmasks of all subnets
+ for which it will be providing service. In addition, in order to
+ dynamically allocate addresses, it must be assigned one or more
+ ranges of addresses on each subnet which it can in turn assign to
+ client hosts as they boot. Thus, a very simple configuration pro-
+ viding DHCP support might look like this:
+
+ subnet 239.252.197.0 netmask 255.255.255.0 {
+ range 239.252.197.10 239.252.197.250;
+ }
+
+ Multiple address ranges may be specified like this:
+
+ subnet 239.252.197.0 netmask 255.255.255.0 {
+ range 239.252.197.10 239.252.197.107;
+ range 239.252.197.113 239.252.197.250;
+ }
+
+ If a subnet will only be provided with BOOTP service and no dynamic
+ address assignment, the range clause can be left out entirely, but
+ the subnet statement must appear.
+
+ Lease Lengths
+ DHCP leases can be assigned almost any length from zero seconds to
+ infinity. What lease length makes sense for any given subnet, or
+ for any given installation, will vary depending on the kinds of
+ hosts being served.
+
+ For example, in an office environment where systems are added from
+ time to time and removed from time to time, but move relatively in-
+ frequently, it might make sense to allow lease times of a month of
+ more. In a final test environment on a manufacturing floor, it may
+ make more sense to assign a maximum lease length of 30 minutes -
+ enough time to go through a simple test procedure on a network ap-
+ pliance before packaging it up for delivery.
+
+ It is possible to specify two lease lengths: the default length that
+ will be assigned if a client doesn't ask for any particular lease
+ length, and a maximum lease length. These are specified as clauses
+ to the subnet command:
+
+ subnet 239.252.197.0 netmask 255.255.255.0 {
+ range 239.252.197.10 239.252.197.107;
+ default-lease-time 600;
+ max-lease-time 7200;
+ }
+
+ This particular subnet declaration specifies a default lease time of
+ 600 seconds (ten minutes), and a maximum lease time of 7200 seconds
+ (two hours). Other common values would be 86400 (one day), 604800
+ (one week) and 2592000 (30 days).
+
+ Each subnet need not have the same lease - in the case of an office
+ environment and a manufacturing environment served by the same DHCP
+ server, it might make sense to have widely disparate values for de-
+ fault and maximum lease times on each subnet.
+
+ BOOTP Support
+ Each BOOTP client must be explicitly declared in the dhcpd.conf(5)
+ file. A very basic client declaration will specify the client net-
+ work interface's hardware address and the IP address to assign to
+ that client. If the client needs to be able to load a boot file
+ from the server, that file's name must be specified. A simple BOOTP
+ client declaration might look like this:
+
+ host haagen {
+ hardware ethernet 08:00:2b:4c:59:23;
+ fixed-address 239.252.197.9;
+ filename "/tftpboot/haagen.boot";
+ }
+
+ Options
+ DHCP (and also BOOTP with Vendor Extensions) provides a mechanism
+ whereby the server can provide the client with information about how
+ to configure its network interface (e.g., subnet mask), and also how
+ the client can access various network services (e.g., DNS, IP
+ routers, and so on).
+
+ These options can be specified on a per-subnet basis, and, for BOOTP
+ clients, also on a per-client basis. In the event that a BOOTP
+ client declaration specifies options that are also specified in its
+ subnet declaration, the options specified in the client declaration
+ take precedence. A reasonably complete DHCP configuration might
+ look something like this:
+
+ subnet 239.252.197.0 netmask 255.255.255.0 {
+ range 239.252.197.10 239.252.197.250;
+ default-lease-time 600 max-lease-time 7200;
+ option subnet-mask 255.255.255.0;
+ option broadcast-address 239.252.197.255;
+ option routers 239.252.197.1;
+ option domain-name-servers 239.252.197.2, 239.252.197.3;
+ option domain-name "isc.org";
+ }
+
+ A BOOTP host on that subnet that needs to be in a different domain
+ and use a different name server might be declared as follows:
+
+ host haagen {
+ hardware ethernet 08:00:2b:4c:59:23;
+ fixed-address 239.252.197.9;
+ filename "/tftpboot/haagen.boot";
+ option domain-name-servers 192.5.5.1;
+ option domain-name "vix.com";
+ }
+
+ A more complete description of the _d_h_c_p_d_._c_o_n_f file syntax is provided in
+ dhcpd.conf(5).
+
+FFIILLEESS
+ /etc/dhcpd.conf DHCPD configuration file.
+ /var/db/dhcpd.leases Current DHCPD lease file.
+ /var/db/dhcpd.leases~ Backup DHCPD lease file.
+ /var/run/dhcpd.pid DHCPD PID.
+
+SSEEEE AALLSSOO
+ dhcpd.conf(5), dhcpd.leases(5), dhclient(8), dhcp(8), dhcrelay(8),
+ pxeboot(8)
+
+AAUUTTHHOORRSS
+ ddhhccppdd was written by Ted Lemon <mellon@vix.com> under a contract with
+ Vixie Labs. Funding for this project was provided by the Internet Soft-
+ ware Corporation. Information about the Internet Software Consortium can
+ be found at _h_t_t_p_:_/_/_w_w_w_._i_s_c_._o_r_g_/.
+
+BBUUGGSS
+ We realize that it would be nice if one could send a SIGHUP to the server
+ and have it reload the database. This is not technically impossible, but
+ it would require a great deal of work, our resources are extremely limit-
+ ed, and they can be better spent elsewhere. So please don't complain
+ about this on the mailing list unless you're prepared to fund a project
+ to implement this feature, or prepared to do it yourself.
+
+OpenBSD 3.5 January 1, 1995 4
diff --git a/usr.sbin/dhcpd/dhcpd.conf b/usr.sbin/dhcpd/dhcpd.conf
new file mode 100644
index 00000000000..d58d54d6eaf
--- /dev/null
+++ b/usr.sbin/dhcpd/dhcpd.conf
@@ -0,0 +1,90 @@
+# dhcpd.conf
+#
+# Sample configuration file for ISC dhcpd
+#
+
+# option definitions common to all supported networks...
+option domain-name "fugue.com";
+option domain-name-servers toccata.fugue.com;
+
+option subnet-mask 255.255.255.224;
+default-lease-time 600;
+max-lease-time 7200;
+
+subnet 204.254.239.0 netmask 255.255.255.224 {
+ range 204.254.239.10 204.254.239.20;
+ option broadcast-address 204.254.239.31;
+ option routers prelude.fugue.com;
+}
+
+# The other subnet that shares this physical network
+subnet 204.254.239.32 netmask 255.255.255.224 {
+ range dynamic-bootp 204.254.239.10 204.254.239.20;
+ option broadcast-address 204.254.239.31;
+ option routers snarg.fugue.com;
+}
+
+subnet 192.5.5.0 netmask 255.255.255.224 {
+ range 192.5.5.26 192.5.5.30;
+ option name-servers bb.home.vix.com, gw.home.vix.com;
+ option domain-name "vix.com";
+ option routers 192.5.5.1;
+ option subnet-mask 255.255.255.224;
+ option broadcast-address 192.5.5.31;
+ default-lease-time 600;
+ max-lease-time 7200;
+}
+
+# Hosts which require special configuration options can be listed in
+# host statements. If no address is specified, the address will be
+# allocated dynamically (if possible), but the host-specific information
+# will still come from the host declaration.
+
+host passacaglia {
+ hardware ethernet 0:0:c0:5d:bd:95;
+ filename "vmunix.passacaglia";
+ server-name "toccata.fugue.com";
+}
+
+# Fixed IP addresses can also be specified for hosts. These addresses
+# should not also be listed as being available for dynamic assignment.
+# Hosts for which fixed IP addresses have been specified can boot using
+# BOOTP or DHCP. Hosts for which no fixed address is specified can only
+# be booted with DHCP, unless there is an address range on the subnet
+# to which a BOOTP client is connected which has the dynamic-bootp flag
+# set.
+host fantasia {
+ hardware ethernet 08:00:07:26:c0:a5;
+ fixed-address fantasia.fugue.com;
+}
+
+# If a DHCP or BOOTP client is mobile and might be connected to a variety
+# of networks, more than one fixed address for that host can be specified.
+# Hosts can have fixed addresses on some networks, but receive dynamically
+# allocated address on other subnets; in order to support this, a host
+# declaration for that client must be given which does not have a fixed
+# address. If a client should get different parameters depending on
+# what subnet it boots on, host declarations for each such network should
+# be given. Finally, if a domain name is given for a host's fixed address
+# and that domain name evaluates to more than one address, the address
+# corresponding to the network to which the client is attached, if any,
+# will be assigned.
+host confusia {
+ hardware ethernet 02:03:04:05:06:07;
+ fixed-address confusia-1.fugue.com, confusia-2.fugue.com;
+ filename "vmunix.confusia";
+ server-name "toccata.fugue.com";
+}
+
+host confusia {
+ hardware ethernet 02:03:04:05:06:07;
+ fixed-address confusia-3.fugue.com;
+ filename "vmunix.confusia";
+ server-name "snarg.fugue.com";
+}
+
+host confusia {
+ hardware ethernet 02:03:04:05:06:07;
+ filename "vmunix.confusia";
+ server-name "bb.home.vix.com";
+}
diff --git a/usr.sbin/dhcpd/dhcpd.conf.5 b/usr.sbin/dhcpd/dhcpd.conf.5
new file mode 100644
index 00000000000..8bccb3249ad
--- /dev/null
+++ b/usr.sbin/dhcpd/dhcpd.conf.5
@@ -0,0 +1,937 @@
+.\" $OpenBSD: dhcpd.conf.5,v 1.1 2004/04/13 23:41:48 henning Exp $
+.\"
+.\" Copyright (c) 1995, 1996, 1997, 1998, 1998, 1999
+.\" The Internet Software Consortium. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of The Internet Software Consortium nor the names
+.\" of its contributors may be used to endorse or promote products derived
+.\" from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+.\" CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+.\" INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+.\" DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+.\" CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+.\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+.\" ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+.\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+.\" OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" This software has been written for the Internet Software Consortium
+.\" by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+.\" Enterprises. To learn more about the Internet Software Consortium,
+.\" see ``http://www.isc.org/isc''. To learn more about Vixie
+.\" Enterprises, see ``http://www.vix.com''.
+.\"
+.Dd January 1, 1995
+.Dt DHCPD.CONF 5
+.Os
+.Sh NAME
+.Nm dhcpd.conf
+.Nd dhcpd configuration file
+.Sh DESCRIPTION
+The
+.Nm
+file contains configuration information for
+.Xr dhcpd 8 ,
+the Internet Software Consortium DHCP Server.
+.Pp
+The
+.Nm
+file is a free-form ASCII text file.
+It is parsed by the recursive-descent parser built into
+.Xr dhcpd 8 .
+The file may contain extra tabs and newlines for formatting purposes.
+Keywords in the file are case-insensitive.
+Comments may be placed anywhere within the file (except within quotes).
+Comments begin with the
+.Sq #
+character and end at the end of the line.
+.Pp
+The file essentially consists of a list of statements.
+Statements fall into two broad categories \- parameters and declarations.
+.Pp
+Parameter statements say how to do something (e.g., how long a
+lease to offer), whether to do something (e.g., should
+.Xr dhcpd 8
+provide addresses to unknown clients), or what parameters to provide to the
+client (e.g., use gateway 220.177.244.7).
+.Pp
+Declarations are used to describe the topology of the
+network, to describe clients on the network, to provide addresses that
+can be assigned to clients, or to apply a group of parameters to a
+group of declarations.
+In any group of parameters and declarations, all parameters must be specified
+before any declarations which depend on those parameters may be specified.
+.Pp
+Declarations about network topology include the
+.Ic shared-network
+and the
+.Ic subnet
+declarations.
+If clients on a subnet are to be assigned addresses dynamically, a
+.Ic range
+declaration must appear within the
+.Ic subnet
+declaration.
+For clients with statically assigned addresses, or for installations where
+only known clients will be served, each such client must have a
+.Ic host
+declaration.
+If parameters are to be applied to a group of declarations which are not
+related strictly on a per-subnet basis, the
+.Ic group
+declaration can be used.
+.Pp
+For every subnet which will be served, and for every subnet
+to which the dhcp server is connected, there must be one
+.Ic subnet
+declaration, which tells
+.Xr dhcpd 8
+how to recognize that an address is on that subnet.
+A
+.Ic subnet
+declaration is required for each subnet even if no addresses will be
+dynamically allocated on that subnet.
+.Pp
+Some installations have physical networks on which more than one IP
+subnet operates.
+For example, if there is a site-wide requirement that 8-bit subnet masks
+be used, but a department with a single physical ethernet network expands
+to the point where it has more than 254 nodes, it may be necessary to run
+two 8-bit subnets on the same ethernet until such time as a new physical
+network can be added.
+In this case, the
+.Ic subnet
+declarations for these two networks may be enclosed in a
+.Ic shared-network
+declaration.
+.Pp
+Some sites may have departments which have clients on more than one
+subnet, but it may be desirable to offer those clients a uniform set
+of parameters which are different than what would be offered to
+clients from other departments on the same subnet.
+For clients which will be declared explicitly with
+.Ic host
+declarations, these declarations can be enclosed in a
+.Ic group
+declaration along with the parameters which are common to that department.
+For clients whose addresses will be dynamically assigned, there is currently no
+way to group parameter assignments other than by network topology.
+.Pp
+When a client is to be booted, its boot parameters are determined by
+first consulting that client's
+.Ic host
+declaration (if any), then consulting the
+.Ic group
+declaration (if any) which enclosed that
+.Ic host
+declaration, then consulting the
+.Ic subnet
+declaration for the subnet on which the client is booting, then consulting the
+.Ic shared-network
+declaration (if any) containing that subnet, and finally consulting the
+top-level parameters which may be specified outside of any declaration.
+.Pp
+When
+.Xr dhcpd 8
+tries to find a
+.Ic host
+declaration for a client, it first looks for a
+.Ic host
+declaration which has a
+.Ar fixed-address
+parameter which matches the subnet or shared network on which the client
+is booting.
+If it doesn't find any such entry, it then tries to find an entry which has no
+.Ar fixed-address
+parameter.
+If no such entry is found, then
+.Xr dhcpd 8
+acts as if there is no entry in the
+.Nm
+file for that client, even if there is an entry for that client on a
+different subnet or shared network.
+.Sh EXAMPLES
+A typical
+.Nm
+file will look something like this:
+.Pp
+Example 1
+.Bd -unfilled -offset indent
+.Ar global parameters...
+
+shared-network ISC-BIGGIE {
+.Ar \ \&\ \&shared-network-specific parameters...
+ subnet 204.254.239.0 netmask 255.255.255.224 {
+.Ar \ \&\ \&\ \&\ \&subnet-specific parameters...
+ range 204.254.239.10 204.254.239.30;
+ }
+ subnet 204.254.239.32 netmask 255.255.255.224 {
+.Ar \ \&\ \&\ \&\ \&subnet-specific parameters...
+ range 204.254.239.42 204.254.239.62;
+ }
+}
+
+subnet 204.254.239.64 netmask 255.255.255.224 {
+.Ar \ \&\ \&subnet-specific parameters...
+ range 204.254.239.74 204.254.239.94;
+}
+
+group {
+.Ar \ \&\ \&group-specific parameters...
+ host zappo.test.isc.org {
+.Ar \ \&\ \&\ \&\ \&host-specific parameters...
+ }
+ host beppo.test.isc.org {
+.Ar \ \&\ \&\ \&\ \&host-specific parameters...
+ }
+ host harpo.test.isc.org {
+.Ar \ \&\ \&\ \&\ \&host-specific parameters...
+ }
+}
+.Ed
+.Pp
+Notice that at the beginning of the file, there's a place
+for global parameters.
+These might be things like the organization's domain name,
+the addresses of the name servers
+(if they are common to the entire organization), and so on.
+So, for example:
+.Pp
+Example 2
+.Bd -literal -offset indent
+option domain-name \&"isc.org\&";
+option domain-name-servers ns1.isc.org, ns2.isc.org;
+.Ed
+.Pp
+As you can see in Example 2, it's legal to specify host addresses in
+parameters as domain names rather than as numeric IP addresses.
+If a given hostname resolves to more than one IP address (for example, if
+that host has two ethernet interfaces), both addresses are supplied to
+the client.
+.Pp
+In Example 1, you can see that both the shared-network statement and
+the subnet statements can have parameters.
+Let us say that the shared network ISC-BIGGIE supports an entire department \-
+perhaps the accounting department.
+If accounting has its own domain, then a shared-network-specific parameter
+might be:
+.Pp
+.Dl option domain-name \&"accounting.isc.org\&";
+.Pp
+All subnet declarations appearing in the shared-network declaration
+would then have the domain-name option set to
+.Dq accounting.isc.org
+instead of just
+.Dq isc.org .
+.Pp
+The most obvious reason for having subnet-specific parameters as
+shown in Example 1 is that each subnet, of necessity, has its own router.
+So for the first subnet, for example, there should be something like:
+.Pp
+.Dl option routers 204.254.239.1;
+.Pp
+Note that the address here is specified numerically.
+This is not required \- if you have a different domain name for each
+interface on your router, it's perfectly legitimate to use the domain name
+for that interface instead of the numeric address.
+However, in many cases there may be only one domain name for all of a router's
+IP addresses, and it would not be appropriate to use that name here.
+.Pp
+In Example 1 there is also a
+.Ic group
+statement, which provides common parameters for a set of three hosts \- zappo,
+beppo and harpo.
+As you can see, these hosts are all in the test.isc.org domain, so it
+might make sense for a group-specific parameter to override the domain
+name supplied to these hosts:
+.Pp
+.Dl option domain-name \&"test.isc.org\&";
+.Pp
+Also, given the domain they're in, these are probably test machines.
+If we wanted to test the DHCP leasing mechanism, we might set the
+lease timeout somewhat shorter than the default:
+.Bd -literal -offset indent
+max-lease-time 120;
+default-lease-time 120;
+.Ed
+.Pp
+You may have noticed that while some parameters start with the
+.Ic option
+keyword, some do not.
+Parameters starting with the
+.Ic option
+keyword correspond to actual DHCP options, while parameters that do not start
+with the option keyword either control the behaviour of the DHCP server
+(e.g., how long a lease
+.Xr dhcpd 8
+will give out), or specify client parameters that are not optional in the
+DHCP protocol (for example, server-name and filename).
+.Pp
+In Example 1, each host had
+.Ar host-specific parameters .
+These could include such things as the
+.Ic hostname
+option, the name of a file to upload (the
+.Ar filename
+parameter) and the address of the server from which to upload the file (the
+.Ar next-server
+parameter).
+In general, any parameter can appear anywhere that parameters are allowed,
+and will be applied according to the scope in which the parameter appears.
+.Pp
+Imagine that you have a site with a lot of NCD X-Terminals.
+These terminals come in a variety of models, and you want to specify the
+boot files for each model.
+One way to do this would be to have host declarations for each server
+and group them by model:
+.Bd -literal -offset indent
+group {
+ filename "Xncd19r";
+ next-server ncd-booter;
+
+ host ncd1 { hardware ethernet 0:c0:c3:49:2b:57; }
+ host ncd4 { hardware ethernet 0:c0:c3:80:fc:32; }
+ host ncd8 { hardware ethernet 0:c0:c3:22:46:81; }
+}
+
+group {
+ filename "Xncd19c";
+ next-server ncd-booter;
+
+ host ncd2 { hardware ethernet 0:c0:c3:88:2d:81; }
+ host ncd3 { hardware ethernet 0:c0:c3:00:14:11; }
+}
+
+group {
+ filename "XncdHMX";
+ next-server ncd-booter;
+
+ host ncd5 { hardware ethernet 0:c0:c3:11:90:23; }
+ host ncd6 { hardware ethernet 0:c0:c3:91:a7:8; }
+ host ncd7 { hardware ethernet 0:c0:c3:cc:a:8f; }
+}
+.Ed
+.Sh REFERENCE: DECLARATIONS
+The
+.Ic shared-network
+statement
+.Bd -unfilled -offset indent
+.Ic shared-network Ar name No {
+.Pf \ \&\ \& Op Ar parameters
+.Pf \ \&\ \& Op Ar declarations
+}
+.Ed
+.Pp
+The
+.Ic shared-network
+statement is used to inform the DHCP server that some IP subnets actually
+share the same physical network.
+Any subnets in a shared network should be declared within a
+.Ic shared-network
+statement.
+Parameters specified in the
+.Ic shared-network
+statement will be used when booting clients on those subnets unless
+parameters provided at the subnet or host level override them.
+If any subnet in a shared network has addresses available for dynamic
+allocation, those addresses are collected into a common pool for that
+shared network and assigned to clients as needed.
+There is no way to distinguish on which subnet of a shared network a
+client should boot.
+.Pp
+.Ar name
+should be the name of the shared network.
+This name is used when printing debugging messages, so it should be
+descriptive for the shared network.
+The name may have the syntax of a valid domain name
+(although it will never be used as such), or it may be any arbitrary
+name, enclosed in quotes.
+.Pp
+The
+.Ic subnet
+statement
+.Bd -unfilled -offset indent
+.Ic subnet Ar subnet-number Ic netmask Ar netmask No {
+.Pf \ \&\ \& Op Ar parameters
+.Pf \ \&\ \& Op Ar declarations
+}
+.Ed
+.Pp
+The
+.Ic subnet
+statement is used to provide
+.Xr dhcpd 8
+with enough information to tell whether or not an IP address is on that subnet.
+It may also be used to provide subnet-specific parameters and to
+specify what addresses may be dynamically allocated to clients booting
+on that subnet.
+Such addresses are specified using the
+.Ic range
+declaration.
+.Pp
+The
+.Ar subnet-number
+should be an IP address or domain name which resolves to the subnet
+number of the subnet being described.
+The
+.Ar netmask
+should be an IP address or domain name which resolves to the subnet mask
+of the subnet being described.
+The subnet number, together with the netmask, are sufficient to determine
+whether any given IP address is on the specified subnet.
+.Pp
+Although a netmask must be given with every subnet declaration, it is
+recommended that if there is any variance in subnet masks at a site, a
+subnet-mask option statement be used in each subnet declaration to set
+the desired subnet mask, since any subnet-mask option statement will
+override the subnet mask declared in the subnet statement.
+.Pp
+The
+.Ic range
+statement
+.Pp
+.Xo
+.Ic range Op Ic dynamic-bootp
+.Ar low-address Oo Ar high-address Oc ;
+.Xc
+.Pp
+For any subnet on which addresses will be assigned dynamically, there
+must be at least one
+.Ic range
+statement.
+The range statement gives the lowest and highest IP addresses in a range.
+All IP addresses in the range should be in the subnet in which the
+.Ic range
+statement is declared.
+The
+.Ic dynamic-bootp
+flag may be specified if addresses in the specified range may be dynamically
+assigned to BOOTP clients as well as DHCP clients.
+When specifying a single address,
+.Ar high-address
+can be omitted.
+.Pp
+The
+.Ic host
+statement
+.Bd -unfilled -offset indent
+.Ic host Ar hostname No {
+.Pf \ \&\ \& Op Ar parameters
+.Pf \ \&\ \& Op Ar declarations
+}
+.Ed
+.Pp
+There must be at least one
+.Ic host
+statement for every BOOTP client that is to be served.
+.Ic host
+statements may also be specified for DHCP clients, although this is
+not required unless booting is only enabled for known hosts.
+.Pp
+If it is desirable to be able to boot a DHCP or BOOTP
+client on more than one subnet with fixed addresses, more than one
+address may be specified in the
+.Ar fixed-address
+parameter, or more than one
+.Ic host
+statement may be specified.
+.Pp
+If client-specific boot parameters must change based on the network
+to which the client is attached, then multiple
+.Ic host
+statements should be used.
+.Pp
+If a client is to be booted using a fixed address if it's
+possible, but should be allocated a dynamic address otherwise, then a
+.Ic host
+statement must be specified without a
+.Ar fixed-address
+clause.
+.Ar hostname
+should be a name identifying the host.
+If a
+.Ar hostname
+option is not specified for the host,
+.Ar hostname
+is used.
+.Pp
+.Ic host
+declarations are matched to actual DHCP or BOOTP clients by matching the
+.Ic dhcp-client-identifier
+option specified in the
+.Ic host
+declaration to the one supplied by the client, or, if the
+.Ic host
+declaration or the client does not provide a
+.Ic dhcp-client-identifier
+option, by matching the
+.Ar hardware
+parameter in the
+.Ic host
+declaration to the network hardware address supplied by the client.
+BOOTP clients do not normally provide a
+.Ar dhcp-client-identifier ,
+so the hardware address must be used for all clients that may boot using
+the BOOTP protocol.
+.Pp
+The
+.Ic group
+statement
+.Bd -unfilled -offset indent
+.Ic group No {
+.Pf \ \&\ \& Op Ar parameters
+.Pf \ \&\ \& Op Ar declarations
+}
+.Ed
+.Pp
+The
+.Ic group
+statement is used simply to apply one or more parameters to a group of
+declarations.
+It can be used to group hosts, shared networks, subnets, or even other groups.
+.Sh REFERENCE: ALLOW and DENY
+The
+.Ic allow
+and
+.Ic deny
+statements can be used to control the behaviour of
+.Xr dhcpd 8
+to various sorts of requests.
+.Pp
+The
+.Ar unknown-clients
+keyword
+.Bd -literal -offset indent
+allow unknown-clients;
+deny unknown-clients;
+.Ed
+.Pp
+The
+.Ar unknown-clients
+flag is used to tell
+.Xr dhcpd 8
+whether or not to dynamically assign addresses to unknown clients.
+Dynamic address assignment to unknown clients is allowed by default.
+.Pp
+The
+.Ar bootp
+keyword
+.Bd -literal -offset indent
+allow bootp;
+deny bootp;
+.Ed
+.Pp
+The
+.Ar bootp
+flag is used to tell
+.Xr dhcpd 8
+whether or not to respond to bootp queries.
+Bootp queries are allowed by default.
+.Pp
+The
+.Ar booting
+keyword
+.Bd -literal -offset indent
+allow booting;
+deny booting;
+.Ed
+.Pp
+The
+.Ar booting
+flag is used to tell
+.Xr dhcpd 8
+whether or not to respond to queries from a particular client.
+This keyword only has meaning when it appears in a host declaration.
+By default, booting is allowed, but if it is disabled for a particular client,
+then that client will not be able to get an address from the DHCP server.
+.Sh REFERENCE: PARAMETERS
+The
+.Ic default-lease-time
+statement
+.Pp
+.D1 Ic default-lease-time Ar time ;
+.Pp
+.Ar 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.
+.Pp
+The
+.Ic max-lease-time
+statement
+.Pp
+.D1 Ic max-lease-time Ar time ;
+.Pp
+.Ar time
+should be the maximum length in seconds that will be assigned to a
+lease if the client requesting the lease asks for a specific expiration time.
+.Pp
+The
+.Ic hardware
+statement
+.Pp
+.D1 Ic hardware Ar hardware-type hardware-address ;
+.Pp
+In order for a BOOTP client to be recognized, its network hardware
+address must be declared using a
+.Ic hardware
+clause in the
+.Ic host
+statement.
+.Ar hardware-type
+must be the name of a physical hardware interface type.
+Currently, only the
+.Ar ethernet
+and
+.Ar token-ring
+types are recognized, although support for an
+.Ar fddi
+hardware type (and others) would also be desirable.
+The
+.Ar hardware-address
+should be a set of hexadecimal octets (numbers from 0 through ff)
+separated by colons.
+The
+.Ic hardware
+statement may also be used for DHCP clients.
+.Pp
+The
+.Ic filename
+statement
+.Pp
+.D1 Ic filename Ar \&"filename\&" ;
+.Pp
+The
+.Ic filename
+statement can be used to specify the name of the initial boot file which
+is to be loaded by a client.
+The
+.Ar filename
+should be a filename recognizable to whatever file transfer protocol
+the client can be expected to use to load the file.
+.Pp
+The
+.Ic server-name
+statement
+.Pp
+.D1 Ic server-name Ar \&"name\&" ;
+.Pp
+The
+.Ic server-name
+statement can be used to inform the client of the name of the server
+from which it is booting.
+.Ar name
+should be the name that will be provided to the client.
+.Pp
+The
+.Ic next-server
+statement
+.Pp
+.D1 Ic next-server Ar server-name ;
+.Pp
+The
+.Ic next-server
+statement is used to specify the host address of
+the server from which the initial boot file (specified in the
+.Ic filename
+statement) is to be loaded.
+.Ar server-name
+should be a numeric IP address or a domain name.
+If no
+.Ic next-server
+parameter applies to a given client, the DHCP server's IP address is used.
+.Pp
+The
+.Ic fixed-address
+statement
+.Pp
+.Xo
+.Ic \ \&fixed-address Ar address
+.Op , Ar address ... ;
+.Xc
+.Pp
+The
+.Ic fixed-address
+statement is used to assign one or more fixed IP addresses to a client.
+It should only appear in a
+.Ic host
+declaration.
+If more than one address is supplied, then when the client boots, it will be
+assigned the address which corresponds to the network on which it is booting.
+If none of the addresses in the
+.Ic fixed-address
+statement are on the network on which the client is booting, that client will
+not match the
+.Ic host
+declaration containing that
+.Ic fixed-address
+statement.
+Each
+.Ar address
+should be either an IP address or a domain name which resolves to one
+or more IP addresses.
+.Pp
+The
+.Ic dynamic-bootp-lease-cutoff
+statement
+.Pp
+.D1 Ic dynamic-bootp-lease-cutoff Ar date ;
+.Pp
+The
+.Ic dynamic-bootp-lease-cutoff
+statement sets the ending time for all leases assigned dynamically to
+BOOTP clients.
+Because BOOTP clients do not have any way of renewing leases,
+and don't know that their leases could expire, by default
+.Xr dhcpd 8
+assigns infinite leases to all BOOTP clients.
+However, it may make sense in some situations to set a cutoff date for all
+BOOTP leases \- for example, the end of a school term,
+or the time at night when a facility is closed and all
+machines are required to be powered off.
+.Pp
+.Ar date
+should be the date on which all assigned BOOTP leases will end.
+The date is specified in the form:
+.Pp
+.Dl W YYYY/MM/DD HH:MM:SS
+.Pp
+W is the day of the week expressed as a number from zero (Sunday)
+to six (Saturday).
+YYYY is the year, including the century.
+MM is the month expressed as a number from 1 to 12.
+DD is the day of the month, counting from 1.
+HH is the hour, from zero to 23.
+MM is the minute and SS is the second.
+The time is always in Coordinated Universal Time (UTC), not local time.
+.Pp
+The
+.Ic dynamic-bootp-lease-length
+statement
+.Pp
+.D1 Ic dynamic-bootp-lease-length Ar length ;
+.Pp
+The
+.Ic dynamic-bootp-lease-length
+statement is used to set the length of leases dynamically assigned to
+BOOTP clients.
+At some sites, it may be possible to assume that a lease is no longer in
+use if its holder has not used BOOTP or DHCP to get its address within
+a certain time period.
+The period is specified in
+.Ar length
+as a number of seconds.
+If a client reboots using BOOTP during the timeout period, the lease
+duration is reset to
+.Ar length ,
+so a BOOTP client that boots frequently enough will never lose its lease.
+Needless to say, this parameter should be adjusted with extreme caution.
+.Pp
+The
+.Ic get-lease-hostnames
+statement
+.Pp
+.D1 Ic get-lease-hostnames Ar flag ;
+.Pp
+The
+.Ic get-lease-hostnames
+statement is used to tell
+.Xr dhcpd 8
+whether or not to look up the domain name corresponding to the IP address of
+each address in the lease pool and use that address for the DHCP
+.Ic hostname
+option.
+If
+.Ar flag
+is true, then this lookup is done for all addresses in the current scope.
+By default, or if
+.Ar flag
+is false, no lookups are done.
+.Pp
+The
+.Ic use-host-decl-names
+statement
+.Pp
+.D1 Ic use-host-decl-names Ar flag ;
+.Pp
+If the
+.Ic use-host-decl-names
+parameter is true in a given scope, then for every host declaration within
+that scope, the name provided for the host declaration will be supplied to
+the client as its hostname.
+So, for example,
+.Bd -literal -offset indent
+group {
+ use-host-decl-names on;
+
+ host joe {
+ hardware ethernet 08:00:2b:4c:29:32;
+ fixed-address joe.fugue.com;
+ }
+}
+.Ed
+.Pp
+is equivalent to
+.Bd -literal -offset indent
+ host joe {
+hardware ethernet 08:00:2b:4c:29:32;
+fixed-address joe.fugue.com;
+ option host-name "joe";
+ }
+.Ed
+.Pp
+An
+.Ic option host-name
+statement within a host declaration will override the use of the name
+in the host declaration.
+.Pp
+The
+.Ic authoritative
+statement
+.Pp
+.D1 Ic authoritative ;
+.Pp
+.D1 Ic not authoritative ;
+.Pp
+The DHCP server will normally assume that the configuration
+information about a given network segment is known to be correct and
+is authoritative.
+So if a client requests an IP address on a given network segment that the
+server knows is not valid for that segment, the server will respond with a
+DHCPNAK message, causing the client to forget its IP address and try to get
+a new one.
+.Pp
+If a DHCP server is being configured by somebody who is not the
+network administrator and who therefore does not wish to assert this
+level of authority, then the statement
+.Dq not authoritative
+should be written in the appropriate scope in the configuration file.
+.Pp
+Usually, writing
+.Em not authoritative;
+at the top level of the file should be sufficient.
+However, if a DHCP server is to be set up so that it is aware of some
+networks for which it is authoritative and some networks for which it is not,
+it may be more appropriate to declare authority on a per-network-segment basis.
+.Pp
+Note that the most specific scope for which the concept of authority
+makes any sense is the physical network segment \- either a
+shared-network statement or a subnet statement that is not contained
+within a shared-network statement.
+It is not meaningful to specify that the server is authoritative for some
+subnets within a shared network, but not authoritative for others,
+nor is it meaningful to specify that the server is authoritative for some
+host declarations and not others.
+.Pp
+The
+.Ic use-lease-addr-for-default-route
+statement
+.Pp
+.D1 Ic use-lease-addr-for-default-route Ar flag ;
+.Pp
+If the
+.Ic use-lease-addr-for-default-route
+parameter is true in a given scope, then instead of sending the value
+specified in the routers option (or sending no value at all),
+the IP address of the lease being assigned is sent to the client.
+This supposedly causes Win95 machines to ARP for all IP addresses,
+which can be helpful if your router is configured for proxy ARP.
+.Pp
+If
+.Ic use-lease-addr-for-default-route
+is enabled and an option routers statement are both in scope,
+the routers option will be preferred.
+The rationale for this is that in situations where you want to use
+this feature, you probably want it enabled for a whole bunch of
+Windows 95 machines, and you want to override it for a few other machines.
+Unfortunately, if the opposite happens to be true for your
+site, you are probably better off not trying to use this flag.
+.Pp
+The
+.Ic always-reply-rfc1048
+statement
+.Pp
+.D1 Ic always-reply-rfc1048 Ar flag ;
+.Pp
+Some BOOTP clients expect RFC 1048-style responses, but do not follow
+RFC 1048 when sending their requests.
+You can tell that a client is having this problem if it is not getting
+the options you have configured for it and if you see in the server log
+the message
+.Dq (non-rfc1048)
+printed with each BOOTREQUEST that is logged.
+.Pp
+If you want to send RFC 1048 options to such a client, you can set the
+.Ic always-reply-rfc1048
+option in that client's host declaration, and the DHCP server will
+respond with an RFC 1048-style vendor options field.
+This flag can be set in any scope, and will affect all clients covered
+by that scope.
+.Pp
+The
+.Ic server-identifier
+statement
+.Pp
+.D1 Ic server-identifier Ar hostname ;
+.Pp
+The
+.Ic server-identifier
+statement can be used to define the value that is sent in the
+DHCP Server Identifier option for a given scope.
+The value specified
+.Em must
+be an IP address for the DHCP server, and must be reachable by all
+clients served by a particular scope.
+.Pp
+The use of the server-identifier statement is not recommended \- the only
+reason to use it is to force a value other than the default value to be
+sent on occasions where the default value would be incorrect.
+The default value is the first IP address associated with the physical
+network interface on which the request arrived.
+.Pp
+The usual case where the
+.Ic server-identifier
+statement needs to be sent is when a physical interface has more than one
+IP address, and the one being sent by default isn't appropriate for some
+or all clients served by that interface.
+Another common case is when an alias is defined for the purpose of
+having a consistent IP address for the DHCP server, and it is desired
+that the clients use this IP address when contacting the server.
+.Pp
+Supplying a value for the
+.Ic dhcp-server-identifier
+option is equivalent to using the
+.Ic server-identifier
+statement.
+.Sh REFERENCE: OPTION STATEMENTS
+DHCP option statements are documented in the
+.Xr dhcp-options 5
+manual page.
+.Sh SEE ALSO
+.Xr dhcp-options 5 ,
+.Xr dhcpd.leases 5 ,
+.Xr dhcpd 8
+.Pp
+RFC 2132, RFC 2131.
+.Sh AUTHORS
+.Xr dhcpd 8
+was written by
+.An Ted Lemon Aq mellon@vix.com
+under a contract with Vixie Labs.
+Funding for this project was provided by the Internet Software Corporation.
+Information about the Internet Software Consortium can be found at
+.Pa http://www.isc.org/isc .
diff --git a/usr.sbin/dhcpd/dhcpd.conf.cat5 b/usr.sbin/dhcpd/dhcpd.conf.cat5
new file mode 100644
index 00000000000..f601871c586
--- /dev/null
+++ b/usr.sbin/dhcpd/dhcpd.conf.cat5
@@ -0,0 +1,618 @@
+DHCPD.CONF(5) OpenBSD Programmer's Manual DHCPD.CONF(5)
+
+NNAAMMEE
+ ddhhccppdd..ccoonnff - dhcpd configuration file
+
+DDEESSCCRRIIPPTTIIOONN
+ The ddhhccppdd..ccoonnff file contains configuration information for dhcpd(8), the
+ Internet Software Consortium DHCP Server.
+
+ The ddhhccppdd..ccoonnff file is a free-form ASCII text file. It is parsed by the
+ recursive-descent parser built into dhcpd(8). The file may contain extra
+ tabs and newlines for formatting purposes. Keywords in the file are
+ case-insensitive. Comments may be placed anywhere within the file (ex-
+ cept within quotes). Comments begin with the `#' character and end at
+ the end of the line.
+
+ The file essentially consists of a list of statements. Statements fall
+ into two broad categories - parameters and declarations.
+
+ Parameter statements say how to do something (e.g., how long a lease to
+ offer), whether to do something (e.g., should dhcpd(8) provide addresses
+ to unknown clients), or what parameters to provide to the client (e.g.,
+ use gateway 220.177.244.7).
+
+ Declarations are used to describe the topology of the network, to de-
+ scribe clients on the network, to provide addresses that can be assigned
+ to clients, or to apply a group of parameters to a group of declarations.
+ In any group of parameters and declarations, all parameters must be spec-
+ ified before any declarations which depend on those parameters may be
+ specified.
+
+ Declarations about network topology include the sshhaarreedd--nneettwwoorrkk and the
+ ssuubbnneett declarations. If clients on a subnet are to be assigned addresses
+ dynamically, a rraannggee declaration must appear within the ssuubbnneett declara-
+ tion. For clients with statically assigned addresses, or for installa-
+ tions where only known clients will be served, each such client must have
+ a hhoosstt declaration. If parameters are to be applied to a group of decla-
+ rations which are not related strictly on a per-subnet basis, the ggrroouupp
+ declaration can be used.
+
+ For every subnet which will be served, and for every subnet to which the
+ dhcp server is connected, there must be one ssuubbnneett declaration, which
+ tells dhcpd(8) how to recognize that an address is on that subnet. A
+ ssuubbnneett declaration is required for each subnet even if no addresses will
+ be dynamically allocated on that subnet.
+
+ Some installations have physical networks on which more than one IP sub-
+ net operates. For example, if there is a site-wide requirement that
+ 8-bit subnet masks be used, but a department with a single physical eth-
+ ernet network expands to the point where it has more than 254 nodes, it
+ may be necessary to run two 8-bit subnets on the same ethernet until such
+ time as a new physical network can be added. In this case, the ssuubbnneett
+ declarations for these two networks may be enclosed in a sshhaarreedd--nneettwwoorrkk
+ declaration.
+
+ Some sites may have departments which have clients on more than one sub-
+ net, but it may be desirable to offer those clients a uniform set of pa-
+ rameters which are different than what would be offered to clients from
+ other departments on the same subnet. For clients which will be declared
+ explicitly with hhoosstt declarations, these declarations can be enclosed in
+ a ggrroouupp declaration along with the parameters which are common to that
+ department. For clients whose addresses will be dynamically assigned,
+ there is currently no way to group parameter assignments other than by
+ network topology.
+
+ When a client is to be booted, its boot parameters are determined by
+ first consulting that client's hhoosstt declaration (if any), then consulting
+ the ggrroouupp declaration (if any) which enclosed that hhoosstt declaration, then
+ consulting the ssuubbnneett declaration for the subnet on which the client is
+ booting, then consulting the sshhaarreedd--nneettwwoorrkk declaration (if any) contain-
+ ing that subnet, and finally consulting the top-level parameters which
+ may be specified outside of any declaration.
+
+ When dhcpd(8) tries to find a hhoosstt declaration for a client, it first
+ looks for a hhoosstt declaration which has a _f_i_x_e_d_-_a_d_d_r_e_s_s parameter which
+ matches the subnet or shared network on which the client is booting. If
+ it doesn't find any such entry, it then tries to find an entry which has
+ no _f_i_x_e_d_-_a_d_d_r_e_s_s parameter. If no such entry is found, then dhcpd(8)
+ acts as if there is no entry in the ddhhccppdd..ccoonnff file for that client, even
+ if there is an entry for that client on a different subnet or shared net-
+ work.
+
+EEXXAAMMPPLLEESS
+ A typical ddhhccppdd..ccoonnff file will look something like this:
+
+ Example 1
+
+ _g_l_o_b_a_l _p_a_r_a_m_e_t_e_r_s_._._.
+
+ shared-network ISC-BIGGIE {
+ _s_h_a_r_e_d_-_n_e_t_w_o_r_k_-_s_p_e_c_i_f_i_c _p_a_r_a_m_e_t_e_r_s_._._.
+ subnet 204.254.239.0 netmask 255.255.255.224 {
+ _s_u_b_n_e_t_-_s_p_e_c_i_f_i_c _p_a_r_a_m_e_t_e_r_s_._._.
+ range 204.254.239.10 204.254.239.30;
+ }
+ subnet 204.254.239.32 netmask 255.255.255.224 {
+ _s_u_b_n_e_t_-_s_p_e_c_i_f_i_c _p_a_r_a_m_e_t_e_r_s_._._.
+ range 204.254.239.42 204.254.239.62;
+ }
+ }
+
+ subnet 204.254.239.64 netmask 255.255.255.224 {
+ _s_u_b_n_e_t_-_s_p_e_c_i_f_i_c _p_a_r_a_m_e_t_e_r_s_._._.
+ range 204.254.239.74 204.254.239.94;
+ }
+
+ group {
+ _g_r_o_u_p_-_s_p_e_c_i_f_i_c _p_a_r_a_m_e_t_e_r_s_._._.
+ host zappo.test.isc.org {
+ _h_o_s_t_-_s_p_e_c_i_f_i_c _p_a_r_a_m_e_t_e_r_s_._._.
+ }
+ host beppo.test.isc.org {
+ _h_o_s_t_-_s_p_e_c_i_f_i_c _p_a_r_a_m_e_t_e_r_s_._._.
+ }
+ host harpo.test.isc.org {
+ _h_o_s_t_-_s_p_e_c_i_f_i_c _p_a_r_a_m_e_t_e_r_s_._._.
+ }
+ }
+
+ Notice that at the beginning of the file, there's a place for global pa-
+ rameters. These might be things like the organization's domain name, the
+ addresses of the name servers (if they are common to the entire organiza-
+ tion), and so on. So, for example:
+
+ Example 2
+
+ option domain-name "isc.org";
+ option domain-name-servers ns1.isc.org, ns2.isc.org;
+
+ As you can see in Example 2, it's legal to specify host addresses in pa-
+ rameters as domain names rather than as numeric IP addresses. If a given
+ hostname resolves to more than one IP address (for example, if that host
+ has two ethernet interfaces), both addresses are supplied to the client.
+
+ In Example 1, you can see that both the shared-network statement and the
+ subnet statements can have parameters. Let us say that the shared net-
+ work ISC-BIGGIE supports an entire department - perhaps the accounting
+ department. If accounting has its own domain, then a shared-network-spe-
+ cific parameter might be:
+
+ option domain-name "accounting.isc.org";
+
+ All subnet declarations appearing in the shared-network declaration would
+ then have the domain-name option set to ``accounting.isc.org'' instead of
+ just ``isc.org''.
+
+ The most obvious reason for having subnet-specific parameters as shown in
+ Example 1 is that each subnet, of necessity, has its own router. So for
+ the first subnet, for example, there should be something like:
+
+ option routers 204.254.239.1;
+
+ Note that the address here is specified numerically. This is not re-
+ quired - if you have a different domain name for each interface on your
+ router, it's perfectly legitimate to use the domain name for that inter-
+ face instead of the numeric address. However, in many cases there may be
+ only one domain name for all of a router's IP addresses, and it would not
+ be appropriate to use that name here.
+
+ In Example 1 there is also a ggrroouupp statement, which provides common pa-
+ rameters for a set of three hosts - zappo, beppo and harpo. As you can
+ see, these hosts are all in the test.isc.org domain, so it might make
+ sense for a group-specific parameter to override the domain name supplied
+ to these hosts:
+
+ option domain-name "test.isc.org";
+
+ Also, given the domain they're in, these are probably test machines. If
+ we wanted to test the DHCP leasing mechanism, we might set the lease
+ timeout somewhat shorter than the default:
+
+ max-lease-time 120;
+ default-lease-time 120;
+
+ You may have noticed that while some parameters start with the ooppttiioonn
+ keyword, some do not. Parameters starting with the ooppttiioonn keyword corre-
+ spond to actual DHCP options, while parameters that do not start with the
+ option keyword either control the behaviour of the DHCP server (e.g., how
+ long a lease dhcpd(8) will give out), or specify client parameters that
+ are not optional in the DHCP protocol (for example, server-name and file-
+ name).
+
+ In Example 1, each host had _h_o_s_t_-_s_p_e_c_i_f_i_c _p_a_r_a_m_e_t_e_r_s. These could in-
+ clude such things as the hhoossttnnaammee option, the name of a file to upload
+ (the _f_i_l_e_n_a_m_e parameter) and the address of the server from which to up-
+ load the file (the _n_e_x_t_-_s_e_r_v_e_r parameter). In general, any parameter can
+ appear anywhere that parameters are allowed, and will be applied accord-
+ ing to the scope in which the parameter appears.
+
+ Imagine that you have a site with a lot of NCD X-Terminals. These termi-
+ nals come in a variety of models, and you want to specify the boot files
+ for each model. One way to do this would be to have host declarations
+ for each server and group them by model:
+
+ group {
+ filename "Xncd19r";
+ next-server ncd-booter;
+
+ host ncd1 { hardware ethernet 0:c0:c3:49:2b:57; }
+ host ncd4 { hardware ethernet 0:c0:c3:80:fc:32; }
+ host ncd8 { hardware ethernet 0:c0:c3:22:46:81; }
+ }
+
+ group {
+ filename "Xncd19c";
+ next-server ncd-booter;
+
+ host ncd2 { hardware ethernet 0:c0:c3:88:2d:81; }
+ host ncd3 { hardware ethernet 0:c0:c3:00:14:11; }
+ }
+
+ group {
+ filename "XncdHMX";
+ next-server ncd-booter;
+
+ host ncd5 { hardware ethernet 0:c0:c3:11:90:23; }
+ host ncd6 { hardware ethernet 0:c0:c3:91:a7:8; }
+ host ncd7 { hardware ethernet 0:c0:c3:cc:a:8f; }
+ }
+
+RREEFFEERREENNCCEE:: DDEECCLLAARRAATTIIOONNSS
+ The sshhaarreedd--nneettwwoorrkk statement
+
+ sshhaarreedd--nneettwwoorrkk _n_a_m_e {
+ [_p_a_r_a_m_e_t_e_r_s]
+ [_d_e_c_l_a_r_a_t_i_o_n_s]
+ }
+
+ The sshhaarreedd--nneettwwoorrkk statement is used to inform the DHCP server that some
+ IP subnets actually share the same physical network. Any subnets in a
+ shared network should be declared within a sshhaarreedd--nneettwwoorrkk statement. Pa-
+ rameters specified in the sshhaarreedd--nneettwwoorrkk statement will be used when
+ booting clients on those subnets unless parameters provided at the subnet
+ or host level override them. If any subnet in a shared network has ad-
+ dresses available for dynamic allocation, those addresses are collected
+ into a common pool for that shared network and assigned to clients as
+ needed. There is no way to distinguish on which subnet of a shared net-
+ work a client should boot.
+
+ _n_a_m_e should be the name of the shared network. This name is used when
+ printing debugging messages, so it should be descriptive for the shared
+ network. The name may have the syntax of a valid domain name (although
+ it will never be used as such), or it may be any arbitrary name, enclosed
+ in quotes.
+
+ The ssuubbnneett statement
+
+ ssuubbnneett _s_u_b_n_e_t_-_n_u_m_b_e_r nneettmmaasskk _n_e_t_m_a_s_k {
+ [_p_a_r_a_m_e_t_e_r_s]
+ [_d_e_c_l_a_r_a_t_i_o_n_s]
+ }
+
+ The ssuubbnneett statement is used to provide dhcpd(8) with enough information
+ to tell whether or not an IP address is on that subnet. It may also be
+ used to provide subnet-specific parameters and to specify what addresses
+ may be dynamically allocated to clients booting on that subnet. Such ad-
+ dresses are specified using the rraannggee declaration.
+
+ The _s_u_b_n_e_t_-_n_u_m_b_e_r should be an IP address or domain name which resolves
+ to the subnet number of the subnet being described. The _n_e_t_m_a_s_k should
+ be an IP address or domain name which resolves to the subnet mask of the
+ subnet being described. The subnet number, together with the netmask,
+ are sufficient to determine whether any given IP address is on the speci-
+ fied subnet.
+
+ Although a netmask must be given with every subnet declaration, it is
+ recommended that if there is any variance in subnet masks at a site, a
+ subnet-mask option statement be used in each subnet declaration to set
+ the desired subnet mask, since any subnet-mask option statement will
+ override the subnet mask declared in the subnet statement.
+
+ The rraannggee statement
+
+ rraannggee [ddyynnaammiicc--bboooottpp] _l_o_w_-_a_d_d_r_e_s_s [_h_i_g_h_-_a_d_d_r_e_s_s];
+
+ For any subnet on which addresses will be assigned dynamically, there
+ must be at least one rraannggee statement. The range statement gives the low-
+ est and highest IP addresses in a range. All IP addresses in the range
+ should be in the subnet in which the rraannggee statement is declared. The
+ ddyynnaammiicc--bboooottpp flag may be specified if addresses in the specified range
+ may be dynamically assigned to BOOTP clients as well as DHCP clients.
+ When specifying a single address, _h_i_g_h_-_a_d_d_r_e_s_s can be omitted.
+
+ The hhoosstt statement
+
+ hhoosstt _h_o_s_t_n_a_m_e {
+ [_p_a_r_a_m_e_t_e_r_s]
+ [_d_e_c_l_a_r_a_t_i_o_n_s]
+ }
+
+ There must be at least one hhoosstt statement for every BOOTP client that is
+ to be served. hhoosstt statements may also be specified for DHCP clients,
+ although this is not required unless booting is only enabled for known
+ hosts.
+
+ If it is desirable to be able to boot a DHCP or BOOTP client on more than
+ one subnet with fixed addresses, more than one address may be specified
+ in the _f_i_x_e_d_-_a_d_d_r_e_s_s parameter, or more than one hhoosstt statement may be
+ specified.
+
+ If client-specific boot parameters must change based on the network to
+ which the client is attached, then multiple hhoosstt statements should be
+ used.
+
+ If a client is to be booted using a fixed address if it's possible, but
+ should be allocated a dynamic address otherwise, then a hhoosstt statement
+ must be specified without a _f_i_x_e_d_-_a_d_d_r_e_s_s clause. _h_o_s_t_n_a_m_e should be a
+ name identifying the host. If a _h_o_s_t_n_a_m_e option is not specified for the
+ host, _h_o_s_t_n_a_m_e is used.
+
+ hhoosstt declarations are matched to actual DHCP or BOOTP clients by matching
+ the ddhhccpp--cclliieenntt--iiddeennttiiffiieerr option specified in the hhoosstt declaration to
+ the one supplied by the client, or, if the hhoosstt declaration or the client
+ does not provide a ddhhccpp--cclliieenntt--iiddeennttiiffiieerr option, by matching the
+ _h_a_r_d_w_a_r_e parameter in the hhoosstt declaration to the network hardware ad-
+ dress supplied by the client. BOOTP clients do not normally provide a
+ _d_h_c_p_-_c_l_i_e_n_t_-_i_d_e_n_t_i_f_i_e_r, so the hardware address must be used for all
+ clients that may boot using the BOOTP protocol.
+
+ The ggrroouupp statement
+
+ ggrroouupp {
+ [_p_a_r_a_m_e_t_e_r_s]
+ [_d_e_c_l_a_r_a_t_i_o_n_s]
+ }
+
+ The ggrroouupp statement is used simply to apply one or more parameters to a
+ group of declarations. It can be used to group hosts, shared networks,
+ subnets, or even other groups.
+
+RREEFFEERREENNCCEE:: AALLLLOOWW aanndd DDEENNYY
+ The aallllooww and ddeennyy statements can be used to control the behaviour of
+ dhcpd(8) to various sorts of requests.
+
+ The _u_n_k_n_o_w_n_-_c_l_i_e_n_t_s keyword
+
+ allow unknown-clients;
+ deny unknown-clients;
+
+ The _u_n_k_n_o_w_n_-_c_l_i_e_n_t_s flag is used to tell dhcpd(8) whether or not to dy-
+ namically assign addresses to unknown clients. Dynamic address assign-
+ ment to unknown clients is allowed by default.
+
+ The _b_o_o_t_p keyword
+
+ allow bootp;
+ deny bootp;
+
+ The _b_o_o_t_p flag is used to tell dhcpd(8) whether or not to respond to
+ bootp queries. Bootp queries are allowed by default.
+
+ The _b_o_o_t_i_n_g keyword
+
+ allow booting;
+ deny booting;
+
+ The _b_o_o_t_i_n_g flag is used to tell dhcpd(8) whether or not to respond to
+ queries from a particular client. This keyword only has meaning when it
+ appears in a host declaration. By default, booting is allowed, but if it
+ is disabled for a particular client, then that client will not be able to
+ get an address from the DHCP server.
+
+RREEFFEERREENNCCEE:: PPAARRAAMMEETTEERRSS
+ The ddeeffaauulltt--lleeaassee--ttiimmee statement
+
+ ddeeffaauulltt--lleeaassee--ttiimmee _t_i_m_e;
+
+ _t_i_m_e 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.
+
+ The mmaaxx--lleeaassee--ttiimmee statement
+
+ mmaaxx--lleeaassee--ttiimmee _t_i_m_e;
+
+ _t_i_m_e should be the maximum length in seconds that will be assigned to a
+ lease if the client requesting the lease asks for a specific expiration
+ time.
+
+ The hhaarrddwwaarree statement
+
+ hhaarrddwwaarree _h_a_r_d_w_a_r_e_-_t_y_p_e _h_a_r_d_w_a_r_e_-_a_d_d_r_e_s_s;
+
+ In order for a BOOTP client to be recognized, its network hardware ad-
+ dress must be declared using a hhaarrddwwaarree clause in the hhoosstt statement.
+ _h_a_r_d_w_a_r_e_-_t_y_p_e must be the name of a physical hardware interface type.
+ Currently, only the _e_t_h_e_r_n_e_t and _t_o_k_e_n_-_r_i_n_g types are recognized, al-
+ though support for an _f_d_d_i hardware type (and others) would also be de-
+ sirable. The _h_a_r_d_w_a_r_e_-_a_d_d_r_e_s_s should be a set of hexadecimal octets
+ (numbers from 0 through ff) separated by colons. The hhaarrddwwaarree statement
+ may also be used for DHCP clients.
+
+ The ffiilleennaammee statement
+
+ ffiilleennaammee _"_f_i_l_e_n_a_m_e_";
+
+ The ffiilleennaammee statement can be used to specify the name of the initial
+ boot file which is to be loaded by a client. The _f_i_l_e_n_a_m_e should be a
+ filename recognizable to whatever file transfer protocol the client can
+ be expected to use to load the file.
+
+ The sseerrvveerr--nnaammee statement
+
+ sseerrvveerr--nnaammee _"_n_a_m_e_";
+
+ The sseerrvveerr--nnaammee statement can be used to inform the client of the name of
+ the server from which it is booting. _n_a_m_e should be the name that will
+ be provided to the client.
+
+ The nneexxtt--sseerrvveerr statement
+
+ nneexxtt--sseerrvveerr _s_e_r_v_e_r_-_n_a_m_e;
+
+ The nneexxtt--sseerrvveerr statement is used to specify the host address of the
+ server from which the initial boot file (specified in the ffiilleennaammee state-
+ ment) is to be loaded. _s_e_r_v_e_r_-_n_a_m_e should be a numeric IP address or a
+ domain name. If no nneexxtt--sseerrvveerr parameter applies to a given client, the
+ DHCP server's IP address is used.
+
+ The ffiixxeedd--aaddddrreessss statement
+
+ ffiixxeedd--aaddddrreessss _a_d_d_r_e_s_s [, _a_d_d_r_e_s_s _._._.];
+
+ The ffiixxeedd--aaddddrreessss statement is used to assign one or more fixed IP ad-
+ dresses to a client. It should only appear in a hhoosstt declaration. If
+ more than one address is supplied, then when the client boots, it will be
+ assigned the address which corresponds to the network on which it is
+ booting. If none of the addresses in the ffiixxeedd--aaddddrreessss statement are on
+ the network on which the client is booting, that client will not match
+ the hhoosstt declaration containing that ffiixxeedd--aaddddrreessss statement. Each
+ _a_d_d_r_e_s_s should be either an IP address or a domain name which resolves to
+ one or more IP addresses.
+
+ The ddyynnaammiicc--bboooottpp--lleeaassee--ccuuttooffff statement
+
+ ddyynnaammiicc--bboooottpp--lleeaassee--ccuuttooffff _d_a_t_e;
+
+ The ddyynnaammiicc--bboooottpp--lleeaassee--ccuuttooffff statement sets the ending time for all
+ leases assigned dynamically to BOOTP clients. Because BOOTP clients do
+ not have any way of renewing leases, and don't know that their leases
+ could expire, by default dhcpd(8) assigns infinite leases to all BOOTP
+ clients. However, it may make sense in some situations to set a cutoff
+ date for all BOOTP leases - for example, the end of a school term, or the
+ time at night when a facility is closed and all machines are required to
+ be powered off.
+
+ _d_a_t_e should be the date on which all assigned BOOTP leases will end. The
+ date is specified in the form:
+
+ W YYYY/MM/DD HH:MM:SS
+
+ W is the day of the week expressed as a number from zero (Sunday) to six
+ (Saturday). YYYY is the year, including the century. MM is the month
+ expressed as a number from 1 to 12. DD is the day of the month, counting
+ from 1. HH is the hour, from zero to 23. MM is the minute and SS is the
+ second. The time is always in Coordinated Universal Time (UTC), not lo-
+ cal time.
+
+ The ddyynnaammiicc--bboooottpp--lleeaassee--lleennggtthh statement
+
+ ddyynnaammiicc--bboooottpp--lleeaassee--lleennggtthh _l_e_n_g_t_h;
+
+ The ddyynnaammiicc--bboooottpp--lleeaassee--lleennggtthh statement is used to set the length of
+ leases dynamically assigned to BOOTP clients. At some sites, it may be
+ possible to assume that a lease is no longer in use if its holder has not
+ used BOOTP or DHCP to get its address within a certain time period. The
+ period is specified in _l_e_n_g_t_h as a number of seconds. If a client re-
+ boots using BOOTP during the timeout period, the lease duration is reset
+ to _l_e_n_g_t_h, so a BOOTP client that boots frequently enough will never lose
+ its lease. Needless to say, this parameter should be adjusted with ex-
+ treme caution.
+
+ The ggeett--lleeaassee--hhoossttnnaammeess statement
+
+ ggeett--lleeaassee--hhoossttnnaammeess _f_l_a_g;
+
+ The ggeett--lleeaassee--hhoossttnnaammeess statement is used to tell dhcpd(8) whether or not
+ to look up the domain name corresponding to the IP address of each ad-
+ dress in the lease pool and use that address for the DHCP hhoossttnnaammee op-
+ tion. If _f_l_a_g is true, then this lookup is done for all addresses in the
+ current scope. By default, or if _f_l_a_g is false, no lookups are done.
+
+ The uussee--hhoosstt--ddeeccll--nnaammeess statement
+
+ uussee--hhoosstt--ddeeccll--nnaammeess _f_l_a_g;
+
+ If the uussee--hhoosstt--ddeeccll--nnaammeess parameter is true in a given scope, then for
+ every host declaration within that scope, the name provided for the host
+ declaration will be supplied to the client as its hostname. So, for ex-
+ ample,
+
+ group {
+ use-host-decl-names on;
+
+ host joe {
+ hardware ethernet 08:00:2b:4c:29:32;
+ fixed-address joe.fugue.com;
+ }
+ }
+
+ is equivalent to
+
+ host joe {
+ hardware ethernet 08:00:2b:4c:29:32;
+ fixed-address joe.fugue.com;
+ option host-name "joe";
+ }
+
+ An ooppttiioonn hhoosstt--nnaammee statement within a host declaration will override the
+ use of the name in the host declaration.
+
+ The aauutthhoorriittaattiivvee statement
+
+ aauutthhoorriittaattiivvee;
+
+ nnoott aauutthhoorriittaattiivvee;
+
+ The DHCP server will normally assume that the configuration information
+ about a given network segment is known to be correct and is authorita-
+ tive. So if a client requests an IP address on a given network segment
+ that the server knows is not valid for that segment, the server will re-
+ spond with a DHCPNAK message, causing the client to forget its IP address
+ and try to get a new one.
+
+ If a DHCP server is being configured by somebody who is not the network
+ administrator and who therefore does not wish to assert this level of au-
+ thority, then the statement ``not authoritative'' should be written in
+ the appropriate scope in the configuration file.
+
+ Usually, writing _n_o_t _a_u_t_h_o_r_i_t_a_t_i_v_e_; at the top level of the file should
+ be sufficient. However, if a DHCP server is to be set up so that it is
+ aware of some networks for which it is authoritative and some networks
+ for which it is not, it may be more appropriate to declare authority on a
+ per-network-segment basis.
+
+ Note that the most specific scope for which the concept of authority
+ makes any sense is the physical network segment - either a shared-network
+ statement or a subnet statement that is not contained within a shared-
+ network statement. It is not meaningful to specify that the server is
+ authoritative for some subnets within a shared network, but not authori-
+ tative for others, nor is it meaningful to specify that the server is au-
+ thoritative for some host declarations and not others.
+
+ The uussee--lleeaassee--aaddddrr--ffoorr--ddeeffaauulltt--rroouuttee statement
+
+ uussee--lleeaassee--aaddddrr--ffoorr--ddeeffaauulltt--rroouuttee _f_l_a_g;
+
+ If the uussee--lleeaassee--aaddddrr--ffoorr--ddeeffaauulltt--rroouuttee parameter is true in a given
+ scope, then instead of sending the value specified in the routers option
+ (or sending no value at all), the IP address of the lease being assigned
+ is sent to the client. This supposedly causes Win95 machines to ARP for
+ all IP addresses, which can be helpful if your router is configured for
+ proxy ARP.
+
+ If uussee--lleeaassee--aaddddrr--ffoorr--ddeeffaauulltt--rroouuttee is enabled and an option routers
+ statement are both in scope, the routers option will be preferred. The
+ rationale for this is that in situations where you want to use this fea-
+ ture, you probably want it enabled for a whole bunch of Windows 95 ma-
+ chines, and you want to override it for a few other machines. Unfortu-
+ nately, if the opposite happens to be true for your site, you are proba-
+ bly better off not trying to use this flag.
+
+ The aallwwaayyss--rreeppllyy--rrffcc11004488 statement
+
+ aallwwaayyss--rreeppllyy--rrffcc11004488 _f_l_a_g;
+
+ Some BOOTP clients expect RFC 1048-style responses, but do not follow RFC
+ 1048 when sending their requests. You can tell that a client is having
+ this problem if it is not getting the options you have configured for it
+ and if you see in the server log the message ``(non-rfc1048)'' printed
+ with each BOOTREQUEST that is logged.
+
+ If you want to send RFC 1048 options to such a client, you can set the
+ aallwwaayyss--rreeppllyy--rrffcc11004488 option in that client's host declaration, and the
+ DHCP server will respond with an RFC 1048-style vendor options field.
+ This flag can be set in any scope, and will affect all clients covered by
+ that scope.
+
+ The sseerrvveerr--iiddeennttiiffiieerr statement
+
+ sseerrvveerr--iiddeennttiiffiieerr _h_o_s_t_n_a_m_e;
+
+ The sseerrvveerr--iiddeennttiiffiieerr statement can be used to define the value that is
+ sent in the DHCP Server Identifier option for a given scope. The value
+ specified _m_u_s_t be an IP address for the DHCP server, and must be reach-
+ able by all clients served by a particular scope.
+
+ The use of the server-identifier statement is not recommended - the only
+ reason to use it is to force a value other than the default value to be
+ sent on occasions where the default value would be incorrect. The de-
+ fault value is the first IP address associated with the physical network
+ interface on which the request arrived.
+
+ The usual case where the sseerrvveerr--iiddeennttiiffiieerr statement needs to be sent is
+ when a physical interface has more than one IP address, and the one being
+ sent by default isn't appropriate for some or all clients served by that
+ interface. Another common case is when an alias is defined for the pur-
+ pose of having a consistent IP address for the DHCP server, and it is de-
+ sired that the clients use this IP address when contacting the server.
+
+ Supplying a value for the ddhhccpp--sseerrvveerr--iiddeennttiiffiieerr option is equivalent to
+ using the sseerrvveerr--iiddeennttiiffiieerr statement.
+
+RREEFFEERREENNCCEE:: OOPPTTIIOONN SSTTAATTEEMMEENNTTSS
+ DHCP option statements are documented in the dhcp-options(5) manual page.
+
+SSEEEE AALLSSOO
+ dhcp-options(5), dhcpd.leases(5), dhcpd(8)
+
+ RFC 2132, RFC 2131.
+
+AAUUTTHHOORRSS
+ dhcpd(8) was written by Ted Lemon <mellon@vix.com> under a contract with
+ Vixie Labs. Funding for this project was provided by the Internet Soft-
+ ware Corporation. Information about the Internet Software Consortium can
+ be found at _h_t_t_p_:_/_/_w_w_w_._i_s_c_._o_r_g_/_i_s_c.
+
+OpenBSD 3.5 January 1, 1995 10
diff --git a/usr.sbin/dhcpd/dhcpd.h b/usr.sbin/dhcpd/dhcpd.h
new file mode 100644
index 00000000000..d97848fb82f
--- /dev/null
+++ b/usr.sbin/dhcpd/dhcpd.h
@@ -0,0 +1,1110 @@
+/* dhcpd.h
+
+ Definitions for dhcpd... */
+
+/*
+ * Copyright (c) 1995, 1996, 1997, 1998, 1999
+ * The Internet Software Consortium. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <time.h>
+
+#include <syslog.h>
+#include <sys/types.h>
+#include <string.h>
+#include <paths.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <setjmp.h>
+#include <limits.h>
+
+#include <sys/wait.h>
+#include <signal.h>
+
+extern int h_errno;
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+#include <sys/sockio.h>
+
+#define ifr_netmask ifr_addr
+
+/* Varargs stuff... */
+#include <stdarg.h>
+#define VA_DOTDOTDOT ...
+#define va_dcl
+#define VA_start(list, last) va_start (list, last)
+
+#ifndef _PATH_DHCPD_PID
+#define _PATH_DHCPD_PID "/var/run/dhcpd.pid"
+#endif
+#ifndef _PATH_DHCPD_DB
+#define _PATH_DHCPD_DB "/var/db/dhcpd.leases"
+#endif
+#ifndef _PATH_DHCLIENT_PID
+#define _PATH_DHCLIENT_PID "/var/run/dhclient.pid"
+#endif
+#ifndef _PATH_DHCLIENT_DB
+#define _PATH_DHCLIENT_DB "/var/db/dhclient.leases"
+#endif
+
+#define EOL '\n'
+#define VOIDPTR void *
+
+/* Time stuff... */
+#include <sys/time.h>
+#define TIME time_t
+#define GET_TIME(x) time ((x))
+
+#define HAVE_SA_LEN
+#define HAVE_MKSTEMP
+
+#if defined (USE_DEFAULT_NETWORK)
+# define USE_BPF
+#endif
+
+#if defined(__alpha__) || (defined(__sparc64__) && defined(__arch64__))
+#define PTRSIZE_64BIT
+#endif
+
+#include "cdefs.h"
+#include "osdep.h"
+#include "dhcp.h"
+#include "tree.h"
+#include "hash.h"
+#include "inet.h"
+#include "sysconf.h"
+
+#define LOCAL_PORT 68
+
+struct option_data {
+ int len;
+ u_int8_t *data;
+};
+
+struct string_list {
+ struct string_list *next;
+ char *string;
+};
+
+/* A name server, from /etc/resolv.conf. */
+struct name_server {
+ struct name_server *next;
+ struct sockaddr_in addr;
+ TIME rcdate;
+};
+
+/* A domain search list element. */
+struct domain_search_list {
+ struct domain_search_list *next;
+ char *domain;
+ TIME rcdate;
+};
+
+/* A dhcp packet and the pointers to its option values. */
+struct packet {
+ struct dhcp_packet *raw;
+ int packet_length;
+ int packet_type;
+ int options_valid;
+ int client_port;
+ struct iaddr client_addr;
+ struct interface_info *interface; /* Interface on which packet
+ was received. */
+ struct hardware *haddr; /* Physical link address
+ of local sender (maybe gateway). */
+ struct shared_network *shared_network;
+ struct option_data options [256];
+ int got_requested_address; /* True if client sent the
+ dhcp-requested-address option. */
+};
+
+struct hardware {
+ u_int8_t htype;
+ u_int8_t hlen;
+ u_int8_t haddr [16];
+};
+
+/* A dhcp lease declaration structure. */
+struct lease {
+ struct lease *next;
+ struct lease *prev;
+ struct lease *n_uid, *n_hw;
+ struct lease *waitq_next;
+
+ struct iaddr ip_addr;
+ TIME starts, ends, timestamp;
+ unsigned char *uid;
+ int uid_len;
+ int uid_max;
+ unsigned char uid_buf [32];
+ char *hostname;
+ char *client_hostname;
+ struct host_decl *host;
+ struct subnet *subnet;
+ struct shared_network *shared_network;
+ struct hardware hardware_addr;
+
+ int flags;
+# define STATIC_LEASE 1
+# define BOOTP_LEASE 2
+# define DYNAMIC_BOOTP_OK 4
+# define PERSISTENT_FLAGS (DYNAMIC_BOOTP_OK)
+# define EPHEMERAL_FLAGS (BOOTP_LEASE)
+# define MS_NULL_TERMINATION 8
+# define ABANDONED_LEASE 16
+
+ struct lease_state *state;
+ u_int8_t releasing;
+};
+
+struct lease_state {
+ struct lease_state *next;
+
+ struct interface_info *ip;
+
+ TIME offered_expiry;
+
+ struct tree_cache *options [256];
+ u_int32_t expiry, renewal, rebind;
+ char filename [DHCP_FILE_LEN];
+ char *server_name;
+
+ struct iaddr from;
+
+ int max_message_size;
+ u_int8_t *prl;
+ int prl_len;
+ int got_requested_address; /* True if client sent the
+ dhcp-requested-address option. */
+ int got_server_identifier; /* True if client sent the
+ dhcp-server-identifier option. */
+ struct shared_network *shared_network; /* Shared network of interface
+ on which request arrived. */
+
+ u_int32_t xid;
+ u_int16_t secs;
+ u_int16_t bootp_flags;
+ struct in_addr ciaddr;
+ struct in_addr giaddr;
+ u_int8_t hops;
+ u_int8_t offer;
+};
+
+#define ROOT_GROUP 0
+#define HOST_DECL 1
+#define SHARED_NET_DECL 2
+#define SUBNET_DECL 3
+#define CLASS_DECL 4
+#define GROUP_DECL 5
+
+/* Possible modes in which discover_interfaces can run. */
+
+#define DISCOVER_RUNNING 0
+#define DISCOVER_SERVER 1
+#define DISCOVER_UNCONFIGURED 2
+#define DISCOVER_RELAY 3
+#define DISCOVER_REQUESTED 4
+
+/* Group of declarations that share common parameters. */
+struct group {
+ struct group *next;
+
+ struct subnet *subnet;
+ struct shared_network *shared_network;
+
+ TIME default_lease_time;
+ TIME max_lease_time;
+ TIME bootp_lease_cutoff;
+ TIME bootp_lease_length;
+
+ char *filename;
+ char *server_name;
+ struct iaddr next_server;
+
+ int boot_unknown_clients;
+ int dynamic_bootp;
+ int allow_bootp;
+ int allow_booting;
+ int one_lease_per_client;
+ int get_lease_hostnames;
+ int use_host_decl_names;
+ int use_lease_addr_for_default_route;
+ int authoritative;
+ int always_reply_rfc1048;
+
+ struct tree_cache *options [256];
+};
+
+/* A dhcp host declaration structure. */
+struct host_decl {
+ struct host_decl *n_ipaddr;
+ char *name;
+ struct hardware interface;
+ struct tree_cache *fixed_addr;
+ struct group *group;
+};
+
+struct shared_network {
+ struct shared_network *next;
+ char *name;
+ struct subnet *subnets;
+ struct interface_info *interface;
+ struct lease *leases;
+ struct lease *insertion_point;
+ struct lease *last_lease;
+
+ struct group *group;
+};
+
+struct subnet {
+ struct subnet *next_subnet;
+ struct subnet *next_sibling;
+ struct shared_network *shared_network;
+ struct interface_info *interface;
+ struct iaddr interface_address;
+ struct iaddr net;
+ struct iaddr netmask;
+
+ struct group *group;
+};
+
+struct class {
+ char *name;
+
+ struct group *group;
+};
+
+/* DHCP client lease structure... */
+struct client_lease {
+ struct client_lease *next; /* Next lease in list. */
+ TIME expiry, renewal, rebind; /* Lease timeouts. */
+ struct iaddr address; /* Address being leased. */
+ char *server_name; /* Name of boot server. */
+ char *filename; /* Name of file we're supposed to boot. */
+ struct string_list *medium; /* Network medium. */
+
+ unsigned int is_static : 1; /* If set, lease is from config file. */
+ unsigned int is_bootp: 1; /* If set, lease was aquired with BOOTP. */
+
+ struct option_data options [256]; /* Options supplied with lease. */
+};
+
+/* Possible states in which the client can be. */
+enum dhcp_state {
+ S_REBOOTING,
+ S_INIT,
+ S_SELECTING,
+ S_REQUESTING,
+ S_BOUND,
+ S_RENEWING,
+ S_REBINDING
+};
+
+/* Configuration information from the config file... */
+struct client_config {
+ struct option_data defaults [256]; /* Default values for options. */
+ enum {
+ ACTION_DEFAULT, /* Use server value if present,
+ otherwise default. */
+ ACTION_SUPERSEDE, /* Always use default. */
+ ACTION_PREPEND, /* Prepend default to server. */
+ ACTION_APPEND /* Append default to server. */
+ } default_actions [256];
+
+ struct option_data send_options [256]; /* Send these to server. */
+ u_int8_t required_options [256]; /* Options server must supply. */
+ u_int8_t requested_options [256]; /* Options to request from server. */
+ int requested_option_count; /* Number of requested options. */
+ TIME timeout; /* Start to panic if we don't get a
+ lease in this time period when
+ SELECTING. */
+ TIME initial_interval; /* All exponential backoff intervals
+ start here. */
+ TIME retry_interval; /* If the protocol failed to produce
+ an address before the timeout,
+ try the protocol again after this
+ many seconds. */
+ TIME select_interval; /* Wait this many seconds from the
+ first DHCPDISCOVER before
+ picking an offered lease. */
+ TIME reboot_timeout; /* When in INIT-REBOOT, wait this
+ long before giving up and going
+ to INIT. */
+ TIME backoff_cutoff; /* When doing exponential backoff,
+ never back off to an interval
+ longer than this amount. */
+ struct string_list *media; /* Possible network media values. */
+ char *script_name; /* Name of config script. */
+ enum { IGNORE, ACCEPT, PREFER } bootp_policy;
+ /* Ignore, accept or prefer BOOTP
+ responses. */
+ struct string_list *medium; /* Current network medium. */
+
+ struct iaddrlist *reject_list; /* Servers to reject. */
+};
+
+/* Per-interface state used in the dhcp client... */
+struct client_state {
+ 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? */
+ struct string_list *medium; /* Last media type tried. */
+
+ struct dhcp_packet packet; /* Outgoing DHCP packet. */
+ int packet_length; /* Actual length of generated packet. */
+
+ struct iaddr requested_address; /* Address we would like to get. */
+
+ struct client_config *config; /* Information from config file. */
+
+ char **scriptEnv; /* Client script env */
+ int scriptEnvsize; /* size of the env table */
+
+ struct string_list *env; /* Client script environment. */
+ int envc; /* Number of entries in environment. */
+};
+
+/* Information about each network interface. */
+
+struct interface_info {
+ struct interface_info *next; /* Next interface in list... */
+ struct shared_network *shared_network;
+ /* Networks connected to this interface. */
+ struct hardware hw_address; /* Its physical address. */
+ struct in_addr primary_address; /* Primary interface address. */
+ char name [IFNAMSIZ]; /* Its name... */
+ int rfdesc; /* Its read file descriptor. */
+ int wfdesc; /* Its write file descriptor, if
+ different. */
+ unsigned char *rbuf; /* Read buffer, if required. */
+ size_t rbuf_max; /* Size of read buffer. */
+ size_t rbuf_offset; /* Current offset into buffer. */
+ size_t rbuf_len; /* Length of data in buffer. */
+
+ struct ifreq *ifp; /* Pointer to ifreq struct. */
+ u_int32_t flags; /* Control flags... */
+#define INTERFACE_REQUESTED 1
+#define INTERFACE_AUTOMATIC 2
+
+ /* Only used by DHCP client code. */
+ struct client_state *client;
+ int noifmedia;
+ int errors;
+ int dead;
+ u_int16_t index;
+};
+
+struct hardware_link {
+ struct hardware_link *next;
+ char name [IFNAMSIZ];
+ struct hardware address;
+};
+
+struct timeout {
+ struct timeout *next;
+ TIME when;
+ void (*func) PROTO ((void *));
+ void *what;
+};
+
+struct protocol {
+ struct protocol *next;
+ int fd;
+ void (*handler) PROTO ((struct protocol *));
+ void *local;
+};
+
+/* Bitmask of dhcp option codes. */
+typedef unsigned char option_mask [16];
+
+/* DHCP Option mask manipulation macros... */
+#define OPTION_ZERO(mask) (memset (mask, 0, 16))
+#define OPTION_SET(mask, bit) (mask [bit >> 8] |= (1 << (bit & 7)))
+#define OPTION_CLR(mask, bit) (mask [bit >> 8] &= ~(1 << (bit & 7)))
+#define OPTION_ISSET(mask, bit) (mask [bit >> 8] & (1 << (bit & 7)))
+#define OPTION_ISCLR(mask, bit) (!OPTION_ISSET (mask, bit))
+
+/* An option occupies its length plus two header bytes (code and
+ length) for every 255 bytes that must be stored. */
+#define OPTION_SPACE(x) ((x) + 2 * ((x) / 255 + 1))
+
+/* Default path to dhcpd config file. */
+#ifdef DEBUG
+#undef _PATH_DHCPD_CONF
+#define _PATH_DHCPD_CONF "dhcpd.conf"
+#undef _PATH_DHCPD_DB
+#define _PATH_DHCPD_DB "dhcpd.leases"
+#else
+#ifndef _PATH_DHCPD_CONF
+#define _PATH_DHCPD_CONF "/etc/dhcpd.conf"
+#endif
+
+#ifndef _PATH_DHCPD_DB
+#define _PATH_DHCPD_DB "/etc/dhcpd.leases"
+#endif
+
+#ifndef _PATH_DHCPD_PID
+#define _PATH_DHCPD_PID "/var/run/dhcpd.pid"
+#endif
+#endif
+
+#ifndef _PATH_DHCLIENT_CONF
+#define _PATH_DHCLIENT_CONF "/etc/dhclient.conf"
+#endif
+
+#ifndef _PATH_DHCLIENT_PID
+#define _PATH_DHCLIENT_PID "/var/run/dhclient.pid"
+#endif
+
+#ifndef _PATH_DHCLIENT_DB
+#define _PATH_DHCLIENT_DB "/etc/dhclient.leases"
+#endif
+
+#ifndef _PATH_RESOLV_CONF
+#define _PATH_RESOLV_CONF "/etc/resolv.conf"
+#endif
+
+#ifndef _PATH_DHCRELAY_PID
+#define _PATH_DHCRELAY_PID "/var/run/dhcrelay.pid"
+#endif
+
+#ifndef DHCPD_LOG_FACILITY
+#define DHCPD_LOG_FACILITY LOG_DAEMON
+#endif
+
+#define MAX_TIME 0x7fffffff
+#define MIN_TIME 0
+
+/* External definitions... */
+
+/* options.c */
+
+void parse_options PROTO ((struct packet *));
+void parse_option_buffer PROTO ((struct packet *, unsigned char *, int));
+int cons_options PROTO ((struct packet *, struct dhcp_packet *, int,
+ struct tree_cache **, int, int, int,
+ u_int8_t *, int));
+int store_options PROTO ((unsigned char *, int, struct tree_cache **,
+ unsigned char *, int, int, int, int));
+char *pretty_print_option PROTO ((unsigned int,
+ unsigned char *, int, int, int));
+void do_packet PROTO ((struct interface_info *,
+ struct dhcp_packet *, int,
+ unsigned int, struct iaddr, struct hardware *));
+
+/* errwarn.c */
+extern int warnings_occurred;
+void error (char *, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
+int warn (char *, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
+int note (char *, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
+int debug (char *, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
+int parse_warn (char *, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
+
+/* dhcpd.c */
+extern TIME cur_time;
+extern struct group root_group;
+
+extern u_int16_t local_port;
+extern u_int16_t remote_port;
+extern int log_priority;
+extern int log_perror;
+
+extern char *path_dhcpd_conf;
+extern char *path_dhcpd_db;
+extern char *path_dhcpd_pid;
+
+int main PROTO ((int, char **));
+void cleanup PROTO ((void));
+void lease_pinged PROTO ((struct iaddr, u_int8_t *, int));
+void lease_ping_timeout PROTO ((void *));
+
+/* conflex.c */
+extern int lexline, lexchar;
+extern char *token_line, *tlname;
+extern char comments [4096];
+extern int comment_index;
+extern int eol_token;
+void new_parse PROTO ((char *));
+int next_token PROTO ((char **, FILE *));
+int peek_token PROTO ((char **, FILE *));
+
+/* confpars.c */
+int readconf PROTO ((void));
+void read_leases PROTO ((void));
+int parse_statement PROTO ((FILE *,
+ struct group *, int, struct host_decl *, int));
+void parse_allow_deny PROTO ((FILE *, struct group *, int));
+void skip_to_semi PROTO ((FILE *));
+int parse_boolean PROTO ((FILE *));
+int parse_semi PROTO ((FILE *));
+int parse_lbrace PROTO ((FILE *));
+void parse_host_declaration PROTO ((FILE *, struct group *));
+char *parse_host_name PROTO ((FILE *));
+void parse_class_declaration PROTO ((FILE *, struct group *, int));
+void parse_lease_time PROTO ((FILE *, TIME *));
+void parse_shared_net_declaration PROTO ((FILE *, struct group *));
+void parse_subnet_declaration PROTO ((FILE *, struct shared_network *));
+void parse_group_declaration PROTO ((FILE *, struct group *));
+void parse_hardware_param PROTO ((FILE *, struct hardware *));
+char *parse_string PROTO ((FILE *));
+struct tree *parse_ip_addr_or_hostname PROTO ((FILE *, int));
+struct tree_cache *parse_fixed_addr_param PROTO ((FILE *));
+void parse_option_param PROTO ((FILE *, struct group *));
+TIME parse_timestamp PROTO ((FILE *));
+struct lease *parse_lease_declaration PROTO ((FILE *));
+void parse_address_range PROTO ((FILE *, struct subnet *));
+TIME parse_date PROTO ((FILE *));
+unsigned char *parse_numeric_aggregate PROTO ((FILE *,
+ unsigned char *, int *,
+ int, int, int));
+void convert_num PROTO ((unsigned char *, char *, int, int));
+
+/* tree.c */
+pair cons PROTO ((caddr_t, pair));
+struct tree_cache *tree_cache PROTO ((struct tree *));
+struct tree *tree_host_lookup PROTO ((char *));
+struct dns_host_entry *enter_dns_host PROTO ((char *));
+struct tree *tree_const PROTO ((unsigned char *, int));
+struct tree *tree_concat PROTO ((struct tree *, struct tree *));
+struct tree *tree_limit PROTO ((struct tree *, int));
+int tree_evaluate PROTO ((struct tree_cache *));
+
+/* dhcp.c */
+extern int outstanding_pings;
+
+void dhcp PROTO ((struct packet *));
+void dhcpdiscover PROTO ((struct packet *));
+void dhcprequest PROTO ((struct packet *));
+void dhcprelease PROTO ((struct packet *));
+void dhcpdecline PROTO ((struct packet *));
+void dhcpinform PROTO ((struct packet *));
+void nak_lease PROTO ((struct packet *, struct iaddr *cip));
+void ack_lease PROTO ((struct packet *, struct lease *, unsigned int, TIME));
+void dhcp_reply PROTO ((struct lease *));
+struct lease *find_lease PROTO ((struct packet *,
+ struct shared_network *, int *));
+struct lease *mockup_lease PROTO ((struct packet *,
+ struct shared_network *,
+ struct host_decl *));
+
+/* bootp.c */
+void bootp PROTO ((struct packet *));
+
+/* memory.c */
+void enter_host PROTO ((struct host_decl *));
+struct host_decl *find_hosts_by_haddr PROTO ((int, unsigned char *, int));
+struct host_decl *find_hosts_by_uid PROTO ((unsigned char *, int));
+struct subnet *find_host_for_network PROTO ((struct host_decl **,
+ struct iaddr *,
+ struct shared_network *));
+void new_address_range PROTO ((struct iaddr, struct iaddr,
+ struct subnet *, int));
+extern struct subnet *find_grouped_subnet PROTO ((struct shared_network *,
+ struct iaddr));
+extern struct subnet *find_subnet PROTO ((struct iaddr));
+void enter_shared_network PROTO ((struct shared_network *));
+int subnet_inner_than PROTO ((struct subnet *, struct subnet *, int));
+void enter_subnet PROTO ((struct subnet *));
+void enter_lease PROTO ((struct lease *));
+int supersede_lease PROTO ((struct lease *, struct lease *, int));
+void release_lease PROTO ((struct lease *));
+void abandon_lease PROTO ((struct lease *, char *));
+struct lease *find_lease_by_uid PROTO ((unsigned char *, int));
+struct lease *find_lease_by_hw_addr PROTO ((unsigned char *, int));
+struct lease *find_lease_by_ip_addr PROTO ((struct iaddr));
+void uid_hash_add PROTO ((struct lease *));
+void uid_hash_delete PROTO ((struct lease *));
+void hw_hash_add PROTO ((struct lease *));
+void hw_hash_delete PROTO ((struct lease *));
+struct class *add_class PROTO ((int, char *));
+struct class *find_class PROTO ((int, unsigned char *, int));
+struct group *clone_group PROTO ((struct group *, char *));
+void write_leases PROTO ((void));
+void dump_subnets PROTO ((void));
+
+/* alloc.c */
+VOIDPTR dmalloc PROTO ((int, char *));
+void dfree PROTO ((VOIDPTR, char *));
+struct packet *new_packet PROTO ((char *));
+struct dhcp_packet *new_dhcp_packet PROTO ((char *));
+struct tree *new_tree PROTO ((char *));
+struct tree_cache *new_tree_cache PROTO ((char *));
+struct hash_table *new_hash_table PROTO ((int, char *));
+struct hash_bucket *new_hash_bucket PROTO ((char *));
+struct lease *new_lease PROTO ((char *));
+struct lease *new_leases PROTO ((int, char *));
+struct subnet *new_subnet PROTO ((char *));
+struct class *new_class PROTO ((char *));
+struct shared_network *new_shared_network PROTO ((char *));
+struct group *new_group PROTO ((char *));
+struct protocol *new_protocol PROTO ((char *));
+struct lease_state *new_lease_state PROTO ((char *));
+struct domain_search_list *new_domain_search_list PROTO ((char *));
+struct name_server *new_name_server PROTO ((char *));
+struct string_list *new_string_list PROTO ((size_t size, char * name));
+void free_name_server PROTO ((struct name_server *, char *));
+void free_domain_search_list PROTO ((struct domain_search_list *, char *));
+void free_lease_state PROTO ((struct lease_state *, char *));
+void free_protocol PROTO ((struct protocol *, char *));
+void free_group PROTO ((struct group *, char *));
+void free_shared_network PROTO ((struct shared_network *, char *));
+void free_class PROTO ((struct class *, char *));
+void free_subnet PROTO ((struct subnet *, char *));
+void free_lease PROTO ((struct lease *, char *));
+void free_hash_bucket PROTO ((struct hash_bucket *, char *));
+void free_hash_table PROTO ((struct hash_table *, char *));
+void free_tree_cache PROTO ((struct tree_cache *, char *));
+void free_packet PROTO ((struct packet *, char *));
+void free_dhcp_packet PROTO ((struct dhcp_packet *, char *));
+void free_tree PROTO ((struct tree *, char *));
+void free_string_list PROTO ((struct string_list *, char *));
+
+/* print.c */
+char *print_hw_addr PROTO ((int, int, unsigned char *));
+void print_lease PROTO ((struct lease *));
+void dump_raw PROTO ((unsigned char *, int));
+void dump_packet PROTO ((struct packet *));
+void hash_dump PROTO ((struct hash_table *));
+
+/* socket.c */
+#if defined (USE_SOCKET_SEND) || defined (USE_SOCKET_RECEIVE) \
+ || defined (USE_SOCKET_FALLBACK)
+int if_register_socket PROTO ((struct interface_info *));
+#endif
+
+#if defined (USE_SOCKET_FALLBACK) && !defined (USE_SOCKET_SEND)
+void if_reinitialize_fallback PROTO ((struct interface_info *));
+void if_register_fallback PROTO ((struct interface_info *));
+ssize_t send_fallback PROTO ((struct interface_info *,
+ struct packet *, struct dhcp_packet *, size_t,
+ struct in_addr,
+ struct sockaddr_in *, struct hardware *));
+#endif
+
+#ifdef USE_SOCKET_SEND
+void if_reinitialize_send PROTO ((struct interface_info *));
+void if_register_send PROTO ((struct interface_info *));
+ssize_t send_packet PROTO ((struct interface_info *,
+ struct packet *, struct dhcp_packet *, size_t,
+ struct in_addr,
+ struct sockaddr_in *, struct hardware *));
+#endif
+#if defined (USE_SOCKET_FALLBACK)
+void fallback_discard PROTO ((struct protocol *));
+#endif
+#ifdef USE_SOCKET_RECEIVE
+void if_reinitialize_receive PROTO ((struct interface_info *));
+void if_register_receive PROTO ((struct interface_info *));
+ssize_t receive_packet PROTO ((struct interface_info *,
+ unsigned char *, size_t,
+ struct sockaddr_in *, struct hardware *));
+#endif
+#if defined (USE_SOCKET_SEND)
+int can_unicast_without_arp PROTO ((void));
+int can_receive_unicast_unconfigured PROTO ((struct interface_info *));
+void maybe_setup_fallback PROTO ((void));
+#endif
+
+/* bpf.c */
+#if defined (USE_BPF_SEND) || defined (USE_BPF_RECEIVE)
+int if_register_bpf PROTO ( (struct interface_info *));
+#endif
+#ifdef USE_BPF_SEND
+void if_reinitialize_send PROTO ((struct interface_info *));
+void if_register_send PROTO ((struct interface_info *));
+ssize_t send_packet PROTO ((struct interface_info *,
+ struct packet *, struct dhcp_packet *, size_t,
+ struct in_addr,
+ struct sockaddr_in *, struct hardware *));
+#endif
+#ifdef USE_BPF_RECEIVE
+void if_reinitialize_receive PROTO ((struct interface_info *));
+void if_register_receive PROTO ((struct interface_info *));
+ssize_t receive_packet PROTO ((struct interface_info *,
+ unsigned char *, size_t,
+ struct sockaddr_in *, struct hardware *));
+#endif
+#if defined (USE_BPF_SEND)
+int can_unicast_without_arp PROTO ((void));
+int can_receive_unicast_unconfigured PROTO ((struct interface_info *));
+void maybe_setup_fallback PROTO ((void));
+#endif
+
+/* lpf.c */
+#if defined (USE_LPF_SEND) || defined (USE_LPF_RECEIVE)
+int if_register_lpf PROTO ( (struct interface_info *));
+#endif
+#ifdef USE_LPF_SEND
+void if_reinitialize_send PROTO ((struct interface_info *));
+void if_register_send PROTO ((struct interface_info *));
+ssize_t send_packet PROTO ((struct interface_info *,
+ struct packet *, struct dhcp_packet *, size_t,
+ struct in_addr,
+ struct sockaddr_in *, struct hardware *));
+#endif
+#ifdef USE_LPF_RECEIVE
+void if_reinitialize_receive PROTO ((struct interface_info *));
+void if_register_receive PROTO ((struct interface_info *));
+ssize_t receive_packet PROTO ((struct interface_info *,
+ unsigned char *, size_t,
+ struct sockaddr_in *, struct hardware *));
+#endif
+#if defined (USE_LPF_SEND)
+int can_unicast_without_arp PROTO ((void));
+int can_receive_unicast_unconfigured PROTO ((struct interface_info *));
+void maybe_setup_fallback PROTO ((void));
+#endif
+
+/* nit.c */
+#if defined (USE_NIT_SEND) || defined (USE_NIT_RECEIVE)
+int if_register_nit PROTO ( (struct interface_info *));
+#endif
+
+#ifdef USE_NIT_SEND
+void if_reinitialize_send PROTO ((struct interface_info *));
+void if_register_send PROTO ((struct interface_info *));
+ssize_t send_packet PROTO ((struct interface_info *,
+ struct packet *, struct dhcp_packet *, size_t,
+ struct in_addr,
+ struct sockaddr_in *, struct hardware *));
+#endif
+#ifdef USE_NIT_RECEIVE
+void if_reinitialize_receive PROTO ((struct interface_info *));
+void if_register_receive PROTO ((struct interface_info *));
+ssize_t receive_packet PROTO ((struct interface_info *,
+ unsigned char *, size_t,
+ struct sockaddr_in *, struct hardware *));
+#endif
+#if defined (USE_NIT_SEND)
+int can_unicast_without_arp PROTO ((void));
+int can_receive_unicast_unconfigured PROTO ((struct interface_info *));
+void maybe_setup_fallback PROTO ((void));
+#endif
+
+#ifdef USE_DLPI_SEND
+void if_reinitialize_send PROTO ((struct interface_info *));
+void if_register_send PROTO ((struct interface_info *));
+ssize_t send_packet PROTO ((struct interface_info *,
+ struct packet *, struct dhcp_packet *, size_t,
+ struct in_addr,
+ struct sockaddr_in *, struct hardware *));
+#endif
+#ifdef USE_DLPI_RECEIVE
+void if_reinitialize_receive PROTO ((struct interface_info *));
+void if_register_receive PROTO ((struct interface_info *));
+ssize_t receive_packet PROTO ((struct interface_info *,
+ unsigned char *, size_t,
+ struct sockaddr_in *, struct hardware *));
+#endif
+#if defined (USE_DLPI_SEND)
+int can_unicast_without_arp PROTO ((void));
+int can_receive_unicast_unconfigured PROTO ((struct interface_info *));
+void maybe_setup_fallback PROTO ((void));
+#endif
+
+/* raw.c */
+#ifdef USE_RAW_SEND
+void if_reinitialize_send PROTO ((struct interface_info *));
+void if_register_send PROTO ((struct interface_info *));
+ssize_t send_packet PROTO ((struct interface_info *,
+ struct packet *, struct dhcp_packet *, size_t,
+ struct in_addr,
+ struct sockaddr_in *, struct hardware *));
+int can_unicast_without_arp PROTO ((void));
+int can_receive_unicast_unconfigured PROTO ((struct interface_info *));
+void maybe_setup_fallback PROTO ((void));
+#endif
+
+/* dispatch.c */
+extern struct interface_info *interfaces,
+ *dummy_interfaces, *fallback_interface;
+extern struct protocol *protocols;
+extern int quiet_interface_discovery;
+extern void (*bootp_packet_handler) PROTO ((struct interface_info *,
+ struct dhcp_packet *, int,
+ unsigned int,
+ struct iaddr, struct hardware *));
+extern struct timeout *timeouts;
+void discover_interfaces PROTO ((int));
+struct interface_info *setup_fallback PROTO ((void));
+void reinitialize_interfaces PROTO ((void));
+void dispatch PROTO ((void));
+int locate_network PROTO ((struct packet *));
+void got_one PROTO ((struct protocol *));
+void add_timeout PROTO ((TIME, void (*) PROTO ((void *)), void *));
+void cancel_timeout PROTO ((void (*) PROTO ((void *)), void *));
+void add_protocol PROTO ((char *, int,
+ void (*) PROTO ((struct protocol *)), void *));
+
+void remove_protocol PROTO ((struct protocol *));
+
+/* hash.c */
+struct hash_table *new_hash PROTO ((void));
+void add_hash PROTO ((struct hash_table *, unsigned char *,
+ int, unsigned char *));
+void delete_hash_entry PROTO ((struct hash_table *, unsigned char *, int));
+unsigned char *hash_lookup PROTO ((struct hash_table *, unsigned char *, int));
+
+/* tables.c */
+extern struct option dhcp_options [256];
+extern unsigned char dhcp_option_default_priority_list [];
+extern int sizeof_dhcp_option_default_priority_list;
+extern char *hardware_types [256];
+extern struct hash_table universe_hash;
+extern struct universe dhcp_universe;
+void initialize_universes PROTO ((void));
+
+/* convert.c */
+u_int32_t getULong PROTO ((unsigned char *));
+int32_t getLong PROTO ((unsigned char *));
+u_int16_t getUShort PROTO ((unsigned char *));
+int16_t getShort PROTO ((unsigned char *));
+void putULong PROTO ((unsigned char *, u_int32_t));
+void putLong PROTO ((unsigned char *, int32_t));
+void putUShort PROTO ((unsigned char *, unsigned int));
+void putShort PROTO ((unsigned char *, int));
+
+/* inet.c */
+struct iaddr subnet_number PROTO ((struct iaddr, struct iaddr));
+struct iaddr ip_addr PROTO ((struct iaddr, struct iaddr, u_int32_t));
+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));
+char *piaddr PROTO ((struct iaddr));
+
+/* dhclient.c */
+extern char *path_dhclient_conf;
+extern char *path_dhclient_db;
+extern char *path_dhclient_pid;
+extern int interfaces_requested;
+
+extern struct client_config top_level_config;
+
+void dhcpoffer PROTO ((struct packet *));
+void dhcpack PROTO ((struct packet *));
+void dhcpnak PROTO ((struct packet *));
+
+void send_discover PROTO ((void *));
+void send_request PROTO ((void *));
+void send_release PROTO ((void *));
+void send_decline PROTO ((void *));
+
+void state_reboot PROTO ((void *));
+void state_init PROTO ((void *));
+void state_selecting PROTO ((void *));
+void state_requesting PROTO ((void *));
+void state_bound PROTO ((void *));
+void state_panic PROTO ((void *));
+
+void bind_lease PROTO ((struct interface_info *));
+
+void make_discover PROTO ((struct interface_info *, struct client_lease *));
+void make_request PROTO ((struct interface_info *, struct client_lease *));
+void make_decline PROTO ((struct interface_info *, struct client_lease *));
+void make_release PROTO ((struct interface_info *, struct client_lease *));
+
+void free_client_lease PROTO ((struct client_lease *));
+void rewrite_client_leases PROTO ((void));
+void write_client_lease PROTO ((struct interface_info *,
+ struct client_lease *, int));
+
+void script_init PROTO ((struct interface_info *, char *,
+ struct string_list *));
+void script_write_params PROTO ((struct interface_info *,
+ char *, struct client_lease *));
+int script_go PROTO ((struct interface_info *));
+void client_envadd PROTO ((struct client_state *,
+ const char *, const char *, const char *, ...));
+void script_set_env (struct client_state *, const char *, const char *,
+ const char *);
+void script_flush_env(struct client_state *);
+int dhcp_option_ev_name (char *, size_t, struct option *);
+
+struct client_lease *packet_to_lease PROTO ((struct packet *));
+void go_daemon PROTO ((void));
+void write_client_pid_file PROTO ((void));
+void status_message PROTO ((struct sysconf_header *, void *));
+void client_location_changed PROTO ((void));
+
+/* db.c */
+int write_lease PROTO ((struct lease *));
+int commit_leases PROTO ((void));
+void db_startup PROTO ((void));
+void new_lease_file PROTO ((void));
+
+/* packet.c */
+u_int32_t checksum PROTO ((unsigned char *, unsigned, u_int32_t));
+u_int32_t wrapsum PROTO ((u_int32_t));
+void assemble_hw_header PROTO ((struct interface_info *, unsigned char *,
+ int *, struct hardware *));
+void assemble_udp_ip_header PROTO ((struct interface_info *, unsigned char *,
+ int *, u_int32_t, u_int32_t, unsigned int,
+ unsigned char *, int));
+ssize_t decode_hw_header PROTO ((struct interface_info *, unsigned char *,
+ int, struct hardware *));
+ssize_t decode_udp_ip_header PROTO ((struct interface_info *, unsigned char *,
+ int, struct sockaddr_in *,
+ unsigned char *, int));
+
+/* ethernet.c */
+void assemble_ethernet_header PROTO ((struct interface_info *, unsigned char *,
+ int *, struct hardware *));
+ssize_t decode_ethernet_header PROTO ((struct interface_info *,
+ unsigned char *,
+ int, struct hardware *));
+
+/* tr.c */
+void assemble_tr_header PROTO ((struct interface_info *, unsigned char *,
+ int *, struct hardware *));
+ssize_t decode_tr_header PROTO ((struct interface_info *,
+ unsigned char *,
+ int, struct hardware *));
+
+/* dhxpxlt.c */
+void convert_statement PROTO ((FILE *));
+void convert_host_statement PROTO ((FILE *, jrefproto));
+void convert_host_name PROTO ((FILE *, jrefproto));
+void convert_class_statement PROTO ((FILE *, jrefproto, int));
+void convert_class_decl PROTO ((FILE *, jrefproto));
+void convert_lease_time PROTO ((FILE *, jrefproto, char *));
+void convert_shared_net_statement PROTO ((FILE *, jrefproto));
+void convert_subnet_statement PROTO ((FILE *, jrefproto));
+void convert_subnet_decl PROTO ((FILE *, jrefproto));
+void convert_host_decl PROTO ((FILE *, jrefproto));
+void convert_hardware_decl PROTO ((FILE *, jrefproto));
+void convert_hardware_addr PROTO ((FILE *, jrefproto));
+void convert_filename_decl PROTO ((FILE *, jrefproto));
+void convert_servername_decl PROTO ((FILE *, jrefproto));
+void convert_ip_addr_or_hostname PROTO ((FILE *, jrefproto, int));
+void convert_fixed_addr_decl PROTO ((FILE *, jrefproto));
+void convert_option_decl PROTO ((FILE *, jrefproto));
+void convert_timestamp PROTO ((FILE *, jrefproto));
+void convert_lease_statement PROTO ((FILE *, jrefproto));
+void convert_address_range PROTO ((FILE *, jrefproto));
+void convert_date PROTO ((FILE *, jrefproto, char *));
+void convert_numeric_aggregate PROTO ((FILE *, jrefproto, int, int, int, int));
+void indent PROTO ((int));
+
+/* route.c */
+void add_route_direct PROTO ((struct interface_info *, struct in_addr));
+void add_route_net PROTO ((struct interface_info *, struct in_addr,
+ struct in_addr));
+void add_route_default_gateway PROTO ((struct interface_info *,
+ struct in_addr));
+void remove_routes PROTO ((struct in_addr));
+void remove_if_route PROTO ((struct interface_info *, struct in_addr));
+void remove_all_if_routes PROTO ((struct interface_info *));
+void set_netmask PROTO ((struct interface_info *, struct in_addr));
+void set_broadcast_addr PROTO ((struct interface_info *, struct in_addr));
+void set_ip_address PROTO ((struct interface_info *, struct in_addr));
+
+/* clparse.c */
+int read_client_conf PROTO ((void));
+void read_client_leases PROTO ((void));
+void parse_client_statement PROTO ((FILE *, struct interface_info *,
+ struct client_config *));
+int parse_X PROTO ((FILE *, u_int8_t *, int));
+int parse_option_list PROTO ((FILE *, u_int8_t *));
+void parse_interface_declaration PROTO ((FILE *, struct client_config *));
+struct interface_info *interface_or_dummy PROTO ((char *));
+void make_client_state PROTO ((struct interface_info *));
+void make_client_config PROTO ((struct interface_info *,
+ struct client_config *));
+void parse_client_lease_statement PROTO ((FILE *, int));
+void parse_client_lease_declaration PROTO ((FILE *, struct client_lease *,
+ struct interface_info **));
+struct option *parse_option_decl PROTO ((FILE *, struct option_data *));
+void parse_string_list PROTO ((FILE *, struct string_list **, int));
+int parse_ip_addr PROTO ((FILE *, struct iaddr *));
+void parse_reject_statement PROTO ((FILE *, struct client_config *));
+
+/* dhcrelay.c */
+void relay PROTO ((struct interface_info *, struct dhcp_packet *, int,
+ unsigned int, struct iaddr, struct hardware *));
+
+/* icmp.c */
+void icmp_startup PROTO ((int, void (*) PROTO ((struct iaddr,
+ u_int8_t *, int))));
+int icmp_echorequest PROTO ((struct iaddr *));
+void icmp_echoreply PROTO ((struct protocol *));
+
+/* dns.c */
+void dns_startup PROTO ((void));
+int ns_inaddr_lookup PROTO ((u_int16_t, struct iaddr));
+void dns_packet PROTO ((struct protocol *));
+
+/* resolv.c */
+extern char path_resolv_conf [];
+struct name_server *name_servers;
+struct domain_search_list *domains;
+
+void read_resolv_conf PROTO ((TIME));
+struct sockaddr_in *pick_name_server PROTO ((void));
+
+/* inet_addr.c */
+#ifdef NEED_INET_ATON
+int inet_aton PROTO ((const char *, struct in_addr *));
+#endif
+
+/* sysconf.c */
+void sysconf_startup PROTO ((void (*) (struct sysconf_header *, void *)));
+void sysconf_restart PROTO ((void *));
+void sysconf_message PROTO ((struct protocol *proto));
diff --git a/usr.sbin/dhcpd/dhcpd.leases.5 b/usr.sbin/dhcpd/dhcpd.leases.5
new file mode 100644
index 00000000000..968f2437f44
--- /dev/null
+++ b/usr.sbin/dhcpd/dhcpd.leases.5
@@ -0,0 +1,222 @@
+.\" $OpenBSD: dhcpd.leases.5,v 1.1 2004/04/13 23:41:48 henning Exp $
+.\"
+.\" Copyright (c) 1997, 1998 The Internet Software Consortium.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of The Internet Software Consortium nor the names
+.\" of its contributors may be used to endorse or promote products derived
+.\" from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+.\" CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+.\" INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+.\" DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+.\" CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+.\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+.\" ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+.\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+.\" OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" This software has been written for the Internet Software Consortium
+.\" by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+.\" Enterprises. To learn more about the Internet Software Consortium,
+.\" see ``http://www.isc.org/isc''. To learn more about Vixie
+.\" Enterprises, see ``http://www.vix.com''.
+.\"
+.Dd January 1, 1997
+.Dt DHCPD.LEASES 5
+.Os
+.Sh NAME
+.Nm dhcpd.leases
+.Nd DHCP client lease database
+.Sh DESCRIPTION
+The Internet Software Consortium DHCP Server keeps a persistent
+database of leases that it has assigned.
+This database is a free-form ASCII file containing a series of
+lease declarations.
+Every time a lease is acquired, renewed or released, its new value is
+recorded at the end of the lease file.
+So if more than one declaration appears for a given lease,
+the last one in the file is the current one.
+.Pp
+When
+.Xr dhcpd 8
+is first installed, there is no lease database.
+However, dhcpd requires that a lease database be present before it will start.
+To make the initial lease database, just create an empty file called
+.Pa /var/db/dhcpd.leases .
+.Pp
+In order to prevent the lease database from growing without bound, the
+file is rewritten from time to time.
+First, a temporary lease database is created and all known leases are
+dumped to it.
+Then, the old lease database is renamed
+.Pa /var/db/dhcpd.leases~ .
+Finally, the newly written lease database is moved into place.
+.Pp
+There is a window of vulnerability where if the dhcpd process is
+killed or the system crashes after the old lease database has been
+renamed but before the new one has been moved into place, there will
+be no
+.Pa /var/db/dhcpd.leases .
+In this case, dhcpd will refuse to start,
+and will require manual intervention.
+.Em DO NOT
+simply create a new lease file when this happens \- if you do,
+you will lose all your old bindings, and chaos will ensue.
+Instead, rename
+.Pa /var/db/dhcpd.leases~
+to
+.Pa /var/db/dhcpd.leases ,
+restoring the old, valid lease file, and then start dhcpd.
+This guarantees that a valid lease file will be restored.
+.Sh FORMAT
+Lease descriptions are stored in a format that is parsed by the same
+recursive descent parser used to read the
+.Xr dhcpd.conf 5
+and
+.Xr dhclient.conf 5
+files.
+Currently, the only declaration that is used in the
+.Nm
+file is the
+.Ic lease
+declaration.
+.Pp
+.Xo
+.Ic \ \&lease Ar ip-address No { Ar statements... No }
+.Xc
+.Pp
+Each lease declaration includes the single IP address that has been
+leased to the client.
+The statements within the braces define the duration of the lease
+and to whom it is assigned.
+.Pp
+The start and end time of a lease are recorded using the ``starts''
+and ``ends'' statements:
+.Pp
+.D1 Ic starts Ar date ;
+.D1 Ic ends Ar date ;
+.Pp
+Dates are specified as follows:
+.Pp
+.Sm off
+.Xo
+.Ar \ \&weekday\ \&year No / Ar month
+.Pf / Ar day\ \& hour No :
+.Ar minute No : Ar second
+.Xc
+.Sm on
+.Pp
+The weekday is present to make it easy for a human to tell when a
+lease expires \- it's specified as a number from zero to six, with zero
+being Sunday.
+The day of week is ignored on input.
+The year is specified with the century, so it should generally be four digits
+except for really long leases.
+The month is specified as a number starting with 1 for January.
+The day of the month is likewise specified starting with 1.
+The hour is a number from 0 to 23, the minute a number from 0 to 59,
+and the second also a number from 0 to 59.
+.Pp
+Lease times are specified in Coordinated Universal Time (UTC), not in the
+local time zone.
+.Qq Li date -u
+will show the current time in UTC.
+.Pp
+The MAC address of the network interface that was used to acquire the
+lease is recorded with the
+.Ic hardware
+statement:
+.Pp
+.D1 Ic hardware Ar hardware-type mac-address ;
+.Pp
+The MAC address is specified as a series of hexadecimal octets,
+separated by colons.
+.Pp
+If the client uses a client identifier to acquire its address, the
+client identifier is recorded using the
+.Ic uid
+statement:
+.Pp
+.D1 Ic uid Ar client-identifier ;
+.Pp
+The client identifier is recorded as a series of hexadecimal octets,
+regardless of whether the client specifies an ASCII string or uses the
+newer hardware type/MAC address format.
+.Pp
+If the client sends a hostname using the
+.Ic Client Hostname
+option, as specified in some versions of the DHCP-DNS Interaction draft, that
+hostname is recorded using the
+.Ic client-hostname
+statement.
+.Pp
+.D1 Ic client-hostname Ar \&"hostname\&" ;
+.Pp
+If the client sends its hostname using the
+.Ic Hostname
+option, as Windows 95 does, it is recorded using the
+.Ic hostname
+statement.
+.Pp
+.D1 Ic hostname Ar \&"hostname\&" ;
+.Pp
+The DHCP server may determine that a lease has been misused in some
+way, either because a client that has been assigned a lease NAKs it,
+or because the server's own attempt to see if an address is in use
+prior to reusing it reveals that the address is in fact already in
+use.
+In that case, the
+.Ic abandoned
+statement will be used to indicate that the lease should not be reassigned.
+.Pp
+.D1 Ic abandoned ;
+.Pp
+Abandoned leases are reclaimed automatically.
+When a client asks for a new address, and the server finds that there
+are no new addresses, it checks to see if there are any abandoned leases,
+and allocates the least recently abandoned lease.
+The standard mechanisms for checking for lease address conflicts are still
+followed, so if the abandoned lease's IP address is still in use,
+it will be reabandoned.
+.Pp
+If a client
+.Em requests
+an abandoned address, the server assumes that the reason the address was
+abandoned was that the lease file was corrupted, and that the client is
+the machine that responded when the lease was probed,
+causing it to be abandoned.
+In that case, the address is immediately assigned to the client.
+.Sh FILES
+.Bl -tag -width Ds -compact
+.It /var/db/dhcpd.leases
+.It /var/db/dhcpd.leases~
+.El
+.Sh SEE ALSO
+.Xr dhcp-options 5 ,
+.Xr dhcpd.conf 5 ,
+.Xr dhcpd 8
+.Pp
+RFC 2132, RFC 2131.
+.Sh AUTHORS
+.Xr dhcpd 8
+was written by
+.An Ted Lemon Aq mellon@vix.com
+under a contract with Vixie Labs.
+Funding for this project was provided by the Internet Software Corporation.
+Information about the Internet Software Consortium can be found at
+.Pa http://www.isc.org/isc .
diff --git a/usr.sbin/dhcpd/dhcpd.leases.cat5 b/usr.sbin/dhcpd/dhcpd.leases.cat5
new file mode 100644
index 00000000000..db6e5a0e80f
--- /dev/null
+++ b/usr.sbin/dhcpd/dhcpd.leases.cat5
@@ -0,0 +1,134 @@
+DHCPD.LEASES(5) OpenBSD Programmer's Manual DHCPD.LEASES(5)
+
+NNAAMMEE
+ ddhhccppdd..lleeaasseess - DHCP client lease database
+
+DDEESSCCRRIIPPTTIIOONN
+ The Internet Software Consortium DHCP Server keeps a persistent database
+ of leases that it has assigned. This database is a free-form ASCII file
+ containing a series of lease declarations. Every time a lease is ac-
+ quired, renewed or released, its new value is recorded at the end of the
+ lease file. So if more than one declaration appears for a given lease,
+ the last one in the file is the current one.
+
+ When dhcpd(8) is first installed, there is no lease database. However,
+ dhcpd requires that a lease database be present before it will start. To
+ make the initial lease database, just create an empty file called
+ _/_v_a_r_/_d_b_/_d_h_c_p_d_._l_e_a_s_e_s.
+
+ In order to prevent the lease database from growing without bound, the
+ file is rewritten from time to time. First, a temporary lease database
+ is created and all known leases are dumped to it. Then, the old lease
+ database is renamed _/_v_a_r_/_d_b_/_d_h_c_p_d_._l_e_a_s_e_s_~. Finally, the newly written
+ lease database is moved into place.
+
+ There is a window of vulnerability where if the dhcpd process is killed
+ or the system crashes after the old lease database has been renamed but
+ before the new one has been moved into place, there will be no
+ _/_v_a_r_/_d_b_/_d_h_c_p_d_._l_e_a_s_e_s. In this case, dhcpd will refuse to start, and will
+ require manual intervention. _D_O _N_O_T simply create a new lease file when
+ this happens - if you do, you will lose all your old bindings, and chaos
+ will ensue. Instead, rename _/_v_a_r_/_d_b_/_d_h_c_p_d_._l_e_a_s_e_s_~ to
+ _/_v_a_r_/_d_b_/_d_h_c_p_d_._l_e_a_s_e_s, restoring the old, valid lease file, and then start
+ dhcpd. This guarantees that a valid lease file will be restored.
+
+FFOORRMMAATT
+ Lease descriptions are stored in a format that is parsed by the same re-
+ cursive descent parser used to read the dhcpd.conf(5) and
+ dhclient.conf(5) files. Currently, the only declaration that is used in
+ the ddhhccppdd..lleeaasseess file is the lleeaassee declaration.
+
+ lleeaassee _i_p_-_a_d_d_r_e_s_s { _s_t_a_t_e_m_e_n_t_s_._._. }
+
+ Each lease declaration includes the single IP address that has been
+ leased to the client. The statements within the braces define the dura-
+ tion of the lease and to whom it is assigned.
+
+ The start and end time of a lease are recorded using the ``starts'' and
+ ``ends'' statements:
+
+ ssttaarrttss _d_a_t_e;
+ eennddss _d_a_t_e;
+
+ Dates are specified as follows:
+
+ _w_e_e_k_d_a_y _y_e_a_r/_m_o_n_t_h/_d_a_y _h_o_u_r:_m_i_n_u_t_e:_s_e_c_o_n_d
+
+ The weekday is present to make it easy for a human to tell when a lease
+ expires - it's specified as a number from zero to six, with zero being
+ Sunday. The day of week is ignored on input. The year is specified with
+ the century, so it should generally be four digits except for really long
+ leases. The month is specified as a number starting with 1 for January.
+ The day of the month is likewise specified starting with 1. The hour is
+ a number from 0 to 23, the minute a number from 0 to 59, and the second
+ also a number from 0 to 59.
+
+ Lease times are specified in Coordinated Universal Time (UTC), not in the
+ local time zone. "date -u" will show the current time in UTC.
+
+ The MAC address of the network interface that was used to acquire the
+ lease is recorded with the hhaarrddwwaarree statement:
+
+ hhaarrddwwaarree _h_a_r_d_w_a_r_e_-_t_y_p_e _m_a_c_-_a_d_d_r_e_s_s;
+
+ The MAC address is specified as a series of hexadecimal octets, separated
+ by colons.
+
+ If the client uses a client identifier to acquire its address, the client
+ identifier is recorded using the uuiidd statement:
+
+ uuiidd _c_l_i_e_n_t_-_i_d_e_n_t_i_f_i_e_r;
+
+ The client identifier is recorded as a series of hexadecimal octets, re-
+ gardless of whether the client specifies an ASCII string or uses the new-
+ er hardware type/MAC address format.
+
+ If the client sends a hostname using the CClliieenntt HHoossttnnaammee option, as spec-
+ ified in some versions of the DHCP-DNS Interaction draft, that hostname
+ is recorded using the cclliieenntt--hhoossttnnaammee statement.
+
+ cclliieenntt--hhoossttnnaammee _"_h_o_s_t_n_a_m_e_";
+
+ If the client sends its hostname using the HHoossttnnaammee option, as Windows 95
+ does, it is recorded using the hhoossttnnaammee statement.
+
+ hhoossttnnaammee _"_h_o_s_t_n_a_m_e_";
+
+ The DHCP server may determine that a lease has been misused in some way,
+ either because a client that has been assigned a lease NAKs it, or be-
+ cause the server's own attempt to see if an address is in use prior to
+ reusing it reveals that the address is in fact already in use. In that
+ case, the aabbaannddoonneedd statement will be used to indicate that the lease
+ should not be reassigned.
+
+ aabbaannddoonneedd;
+
+ Abandoned leases are reclaimed automatically. When a client asks for a
+ new address, and the server finds that there are no new addresses, it
+ checks to see if there are any abandoned leases, and allocates the least
+ recently abandoned lease. The standard mechanisms for checking for lease
+ address conflicts are still followed, so if the abandoned lease's IP ad-
+ dress is still in use, it will be reabandoned.
+
+ If a client _r_e_q_u_e_s_t_s an abandoned address, the server assumes that the
+ reason the address was abandoned was that the lease file was corrupted,
+ and that the client is the machine that responded when the lease was
+ probed, causing it to be abandoned. In that case, the address is immedi-
+ ately assigned to the client.
+
+FFIILLEESS
+ /var/db/dhcpd.leases
+ /var/db/dhcpd.leases~
+
+SSEEEE AALLSSOO
+ dhcp-options(5), dhcpd.conf(5), dhcpd(8)
+
+ RFC 2132, RFC 2131.
+
+AAUUTTHHOORRSS
+ dhcpd(8) was written by Ted Lemon <mellon@vix.com> under a contract with
+ Vixie Labs. Funding for this project was provided by the Internet Soft-
+ ware Corporation. Information about the Internet Software Consortium can
+ be found at _h_t_t_p_:_/_/_w_w_w_._i_s_c_._o_r_g_/_i_s_c.
+
+OpenBSD 3.5 January 1, 1997 2
diff --git a/usr.sbin/dhcpd/dhctoken.h b/usr.sbin/dhcpd/dhctoken.h
new file mode 100644
index 00000000000..2aeb5303af1
--- /dev/null
+++ b/usr.sbin/dhcpd/dhctoken.h
@@ -0,0 +1,136 @@
+/* dhctoken.h
+
+ Tokens for config file lexer and parser. */
+
+/*
+ * Copyright (c) 1995, 1996, 1997, 1998, 1999
+ * The Internet Software Consortium. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#define SEMI ';'
+#define DOT '.'
+#define COLON ':'
+#define COMMA ','
+#define SLASH '/'
+#define LBRACE '{'
+#define RBRACE '}'
+
+#define FIRST_TOKEN HOST
+#define HOST 256
+#define HARDWARE 257
+#define FILENAME 258
+#define FIXED_ADDR 259
+#define OPTION 260
+#define ETHERNET 261
+#define STRING 262
+#define NUMBER 263
+#define NUMBER_OR_NAME 264
+#define NAME 265
+#define TIMESTAMP 266
+#define STARTS 267
+#define ENDS 268
+#define UID 269
+#define CLASS 270
+#define LEASE 271
+#define RANGE 272
+#define PACKET 273
+#define CIADDR 274
+#define YIADDR 275
+#define SIADDR 276
+#define GIADDR 277
+#define SUBNET 278
+#define NETMASK 279
+#define DEFAULT_LEASE_TIME 280
+#define MAX_LEASE_TIME 281
+#define VENDOR_CLASS 282
+#define USER_CLASS 283
+#define SHARED_NETWORK 284
+#define SERVER_NAME 285
+#define DYNAMIC_BOOTP 286
+#define SERVER_IDENTIFIER 287
+#define DYNAMIC_BOOTP_LEASE_CUTOFF 288
+#define DYNAMIC_BOOTP_LEASE_LENGTH 289
+#define BOOT_UNKNOWN_CLIENTS 290
+#define NEXT_SERVER 291
+#define TOKEN_RING 292
+#define GROUP 293
+#define ONE_LEASE_PER_CLIENT 294
+#define GET_LEASE_HOSTNAMES 295
+#define USE_HOST_DECL_NAMES 296
+#define SEND 297
+#define CLIENT_IDENTIFIER 298
+#define REQUEST 299
+#define REQUIRE 300
+#define TIMEOUT 301
+#define RETRY 302
+#define SELECT_TIMEOUT 303
+#define SCRIPT 304
+#define INTERFACE 305
+#define RENEW 306
+#define REBIND 307
+#define EXPIRE 308
+#define UNKNOWN_CLIENTS 309
+#define ALLOW 310
+#define BOOTP 311
+#define DENY 312
+#define BOOTING 313
+#define DEFAULT 314
+#define MEDIA 315
+#define MEDIUM 316
+#define ALIAS 317
+#define REBOOT 318
+#define ABANDONED 319
+#define BACKOFF_CUTOFF 320
+#define INITIAL_INTERVAL 321
+#define NAMESERVER 322
+#define DOMAIN 323
+#define SEARCH 324
+#define SUPERSEDE 325
+#define APPEND 326
+#define PREPEND 327
+#define HOSTNAME 328
+#define CLIENT_HOSTNAME 329
+#define REJECT 330
+#define FDDI 331
+#define USE_LEASE_ADDR_FOR_DEFAULT_ROUTE 332
+#define AUTHORITATIVE 333
+#define TOKEN_NOT 334
+#define ALWAYS_REPLY_RFC1048 335
+
+#define is_identifier(x) ((x) >= FIRST_TOKEN && \
+ (x) != STRING && \
+ (x) != NUMBER && \
+ (x) != EOF)
diff --git a/usr.sbin/dhcpd/dispatch.c b/usr.sbin/dhcpd/dispatch.c
new file mode 100644
index 00000000000..89a2f626ecd
--- /dev/null
+++ b/usr.sbin/dhcpd/dispatch.c
@@ -0,0 +1,668 @@
+/* dispatch.c
+
+ Network input dispatcher... */
+
+/*
+ * Copyright (c) 1995, 1996, 1997, 1998, 1999
+ * The Internet Software Consortium. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include "dhcpd.h"
+#include <ifaddrs.h>
+#include <sys/ioctl.h>
+#include <poll.h>
+#include <net/if_media.h>
+
+
+/* Most boxes has less than 16 interfaces, so this might be a good guess. */
+#define INITIAL_IFREQ_COUNT 16
+
+struct interface_info *interfaces, *dummy_interfaces, *fallback_interface;
+struct protocol *protocols;
+struct timeout *timeouts;
+static struct timeout *free_timeouts;
+static int interfaces_invalidated;
+void (*bootp_packet_handler) PROTO ((struct interface_info *,
+ struct dhcp_packet *, int, unsigned int,
+ struct iaddr, struct hardware *));
+
+static int interface_status(struct interface_info *ifinfo);
+
+int quiet_interface_discovery;
+
+/* Use getifaddrs() 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;
+ struct interface_info *last, *next;
+ struct subnet *subnet;
+ struct shared_network *share;
+ struct sockaddr_in foo;
+ int ir;
+ struct ifreq *tif;
+ struct ifaddrs *ifap, *ifa;
+#ifdef ALIAS_NAMES_PERMUTED
+ char *s;
+#endif
+
+ if (getifaddrs(&ifap) != 0)
+ error ("getifaddrs failed");
+
+ /* If we already have a list of interfaces, and we're running as
+ a DHCP server, the interfaces were requested. */
+ if (interfaces && (state == DISCOVER_SERVER ||
+ state == DISCOVER_RELAY ||
+ state == DISCOVER_REQUESTED))
+ ir = 0;
+ else if (state == DISCOVER_UNCONFIGURED)
+ ir = INTERFACE_REQUESTED | INTERFACE_AUTOMATIC;
+ else
+ ir = INTERFACE_REQUESTED;
+
+ /* Cycle through the list of interfaces looking for IP addresses. */
+ for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
+ /* See if this is the sort of interface we want to
+ deal with. Skip loopback, point-to-point and down
+ interfaces, except don't skip down interfaces if we're
+ trying to get a list of configurable interfaces. */
+ if ((ifa->ifa_flags & IFF_LOOPBACK) ||
+ (ifa->ifa_flags & IFF_POINTOPOINT) ||
+ (!(ifa->ifa_flags & IFF_UP) &&
+ state != DISCOVER_UNCONFIGURED))
+ continue;
+
+ /* See if we've seen an interface that matches this one. */
+ for (tmp = interfaces; tmp; tmp = tmp -> next)
+ if (!strcmp (tmp -> name, ifa -> ifa_name))
+ break;
+
+ /* If there isn't already an interface by this name,
+ allocate one. */
+ if (!tmp) {
+ tmp = ((struct interface_info *)
+ dmalloc (sizeof *tmp, "discover_interfaces"));
+ if (!tmp)
+ error ("Insufficient memory to %s %s",
+ "record interface", ifa -> ifa_name);
+ strlcpy (tmp -> name, ifa -> ifa_name, sizeof(tmp->name));
+ tmp -> next = interfaces;
+ tmp -> flags = ir;
+ tmp -> noifmedia = tmp -> dead = tmp->errors = 0;
+ interfaces = tmp;
+ }
+
+ /* If we have the capability, extract link information
+ and record it in a linked list. */
+ if (ifa -> ifa_addr->sa_family == AF_LINK) {
+ struct sockaddr_dl *foo = ((struct sockaddr_dl *)
+ (ifa -> ifa_addr));
+ tmp -> index = foo->sdl_index;
+ tmp -> hw_address.hlen = foo -> sdl_alen;
+ tmp -> hw_address.htype = HTYPE_ETHER; /* XXX */
+ memcpy (tmp -> hw_address.haddr,
+ LLADDR (foo), foo -> sdl_alen);
+ } else if (ifa -> ifa_addr->sa_family == AF_INET) {
+ struct iaddr addr;
+
+ /* Get a pointer to the address... */
+ bcopy(ifa->ifa_addr, &foo, sizeof(foo));
+
+ /* We don't want the loopback interface. */
+ if (foo.sin_addr.s_addr == htonl (INADDR_LOOPBACK))
+ continue;
+
+ /* 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) {
+ int len = (IFNAMSIZ +
+ ifa -> ifa_addr->sa_len);
+ tif = (struct ifreq *)malloc (len);
+ if (!tif)
+ error ("no space to remember ifp.");
+ strlcpy(tif->ifr_name, ifa->ifa_name, IFNAMSIZ);
+ memcpy(&tif->ifr_addr, ifa->ifa_addr,
+ ifa->ifa_addr->sa_len);
+ tmp -> ifp = tif;
+ tmp -> primary_address = foo.sin_addr;
+ }
+
+ /* Grab the address... */
+ addr.len = 4;
+ memcpy (addr.iabuf, &foo.sin_addr.s_addr,
+ addr.len);
+
+ /* If there's a registered subnet for this address,
+ connect it together... */
+ if ((subnet = find_subnet (addr))) {
+ /* If this interface has multiple aliases
+ on the same subnet, ignore all but the
+ first we encounter. */
+ if (!subnet -> interface) {
+ subnet -> interface = tmp;
+ subnet -> interface_address = addr;
+ } else if (subnet -> interface != tmp) {
+ warn ("Multiple %s %s: %s %s",
+ "interfaces match the",
+ "same subnet",
+ subnet -> interface -> name,
+ tmp -> name);
+ }
+ share = subnet -> shared_network;
+ if (tmp -> shared_network &&
+ tmp -> shared_network != share) {
+ warn ("Interface %s matches %s",
+ tmp -> name,
+ "multiple shared networks");
+ } else {
+ tmp -> shared_network = share;
+ }
+
+ if (!share -> interface) {
+ share -> interface = tmp;
+ } else if (share -> interface != tmp) {
+ warn ("Multiple %s %s: %s %s",
+ "interfaces match the",
+ "same shared network",
+ share -> interface -> name,
+ tmp -> name);
+ }
+ }
+ }
+ }
+
+ /* Now cycle through all the interfaces we found, looking for
+ hardware addresses. */
+
+ /* If we're just trying to get a list of interfaces that we might
+ be able to configure, we can quit now. */
+ if (state == DISCOVER_UNCONFIGURED)
+ return;
+
+ /* Weed out the interfaces that did not have IP addresses. */
+ last = (struct interface_info *)0;
+ for (tmp = interfaces; tmp; tmp = next) {
+ next = tmp -> next;
+ if ((tmp -> flags & INTERFACE_AUTOMATIC) &&
+ state == DISCOVER_REQUESTED)
+ tmp -> flags &= ~(INTERFACE_AUTOMATIC |
+ INTERFACE_REQUESTED);
+ if (!tmp -> ifp || !(tmp -> flags & INTERFACE_REQUESTED)) {
+ if ((tmp -> flags & INTERFACE_REQUESTED) != ir)
+ error ("%s: not found", tmp -> name);
+ if (!last)
+ interfaces = interfaces -> next;
+ else
+ last -> next = tmp -> next;
+
+ /* Remember the interface in case we need to know
+ about it later. */
+ tmp -> next = dummy_interfaces;
+ dummy_interfaces = tmp;
+ continue;
+ }
+ last = tmp;
+
+ 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)) {
+ warn ("No subnet declaration for %s (%s).",
+ tmp -> name, inet_ntoa (foo.sin_addr));
+ warn ("Please write a subnet declaration in your %s",
+ "dhcpd.conf file for the");
+ error ("network segment to which interface %s %s",
+ tmp -> name, "is attached.");
+ }
+
+ /* Find subnets that don't have valid interface
+ addresses... */
+ for (subnet = (tmp -> shared_network
+ ? tmp -> shared_network -> subnets
+ : (struct subnet *)0);
+ subnet; subnet = subnet -> next_sibling) {
+ if (!subnet -> interface_address.len) {
+ /* Set the interface address for this subnet
+ to the first address we found. */
+ subnet -> interface_address.len = 4;
+ memcpy (subnet -> interface_address.iabuf,
+ &foo.sin_addr.s_addr, 4);
+ }
+ }
+
+ /* Register the interface... */
+ if_register_receive (tmp);
+ if_register_send (tmp);
+ }
+
+ /* Now register all the remaining interfaces as protocols. */
+ for (tmp = interfaces; tmp; tmp = tmp -> next) {
+ add_protocol (tmp -> name, tmp -> rfdesc, got_one, tmp);
+ }
+
+ freeifaddrs(ifap);
+}
+
+struct interface_info *setup_fallback ()
+{
+ fallback_interface =
+ ((struct interface_info *)
+ dmalloc (sizeof *fallback_interface, "discover_interfaces"));
+ if (!fallback_interface)
+ error ("Insufficient memory to record fallback interface.");
+ memset (fallback_interface, 0, sizeof *fallback_interface);
+ strlcpy (fallback_interface -> name, "fallback", IFNAMSIZ);
+ fallback_interface -> shared_network =
+ new_shared_network ("parse_statement");
+ if (!fallback_interface -> shared_network)
+ error ("No memory for shared subnet");
+ memset (fallback_interface -> shared_network, 0,
+ sizeof (struct shared_network));
+ fallback_interface -> shared_network -> name = "fallback-net";
+ return fallback_interface;
+}
+
+void reinitialize_interfaces ()
+{
+ struct interface_info *ip;
+
+ interfaces_invalidated = 1;
+}
+
+/* Wait for packets to come in using poll(). When a packet comes in,
+ call receive_packet to receive the packet and possibly strip hardware
+ addressing information from it, and then call through the
+ bootp_packet_handler hook to try to do something with it. */
+
+void dispatch ()
+{
+ struct protocol *l;
+ int nfds = 0;
+ struct pollfd *fds;
+ int count;
+ int i;
+ time_t howlong;
+ int to_msec;
+
+ nfds = 0;
+ for (l = protocols; l; l = l -> next) {
+ ++nfds;
+ }
+ fds = (struct pollfd *)malloc ((nfds) * sizeof (struct pollfd));
+ if (fds == NULL)
+ error ("Can't allocate poll structures.");
+
+ do {
+ /* Call any expired timeouts, and then if there's
+ still a timeout registered, time out the select
+ call then. */
+ another:
+ if (timeouts) {
+ struct timeout *t;
+ if (timeouts -> when <= cur_time) {
+ t = timeouts;
+ timeouts = timeouts -> next;
+ (*(t -> func)) (t -> what);
+ t -> next = free_timeouts;
+ free_timeouts = t;
+ goto another;
+ }
+ /*
+ * Figure timeout in milliseconds, and check for
+ * potential overflow, so we can cram into an int
+ * for poll, while not polling with a negative
+ * timeout and blocking indefinetely.
+ */
+
+ howlong = timeouts -> when - cur_time;
+ if (howlong > INT_MAX / 1000)
+ howlong = INT_MAX / 1000;
+ to_msec = howlong * 1000;
+ } else
+ to_msec = -1;
+
+ /* Set up the descriptors to be polled. */
+ i = 0;
+
+ for (l = protocols; l; l = l -> next) {
+ struct interface_info *ip = l -> local;
+ if (ip && (l->handler != got_one || !ip->dead)) {
+ fds [i].fd = l -> fd;
+ fds [i].events = POLLIN;
+ fds [i].revents = 0;
+ ++i;
+ }
+ }
+
+ if (i == 0)
+ error("No live interfaces to poll on - exiting.");
+
+ /* Wait for a packet or a timeout... XXX */
+ count = poll (fds, nfds, to_msec);
+
+ /* Not likely to be transitory... */
+ if (count == -1) {
+ if (errno == EAGAIN || errno == EINTR) {
+ GET_TIME (&cur_time);
+ continue;
+ }
+ else
+ error ("poll: %m");
+ }
+
+ /* Get the current time... */
+ GET_TIME (&cur_time);
+
+ i = 0;
+ for (l = protocols; l; l = l -> next) {
+ struct interface_info *ip;
+ ip = l->local;
+ if ((fds [i].revents & POLLIN)) {
+ fds [i].revents = 0;
+ if (ip && (l->handler != got_one ||
+ !ip->dead))
+ (*(l -> handler)) (l);
+ if (interfaces_invalidated)
+ break;
+ }
+ ++i;
+ }
+ interfaces_invalidated = 0;
+ } while (1);
+}
+
+
+void got_one (l)
+ struct protocol *l;
+{
+ struct sockaddr_in from;
+ struct hardware hfrom;
+ struct iaddr ifrom;
+ size_t result;
+ union {
+ unsigned char packbuf [4095]; /* Packet input buffer.
+ Must be as large as largest
+ possible MTU. */
+ struct dhcp_packet packet;
+ } u;
+ struct interface_info *ip = l -> local;
+
+ if ((result =
+ receive_packet (ip, u.packbuf, sizeof u, &from, &hfrom)) == -1) {
+ warn ("receive_packet failed on %s: %s", ip -> name,
+ strerror(errno));
+ ip->errors++;
+ if ((! interface_status(ip))
+ || (ip->noifmedia && ip->errors > 20)) {
+ /* our interface has gone away. */
+ warn("Interface %s no longer appears valid.",
+ ip->name);
+ ip->dead = 1;
+ interfaces_invalidated = 1;
+ close(l->fd);
+ remove_protocol(l);
+ free(ip);
+ }
+ return;
+ }
+ if (result == 0)
+ return;
+
+ if (bootp_packet_handler) {
+ ifrom.len = 4;
+ memcpy (ifrom.iabuf, &from.sin_addr, ifrom.len);
+
+ (*bootp_packet_handler) (ip, &u.packet, result,
+ from.sin_port, ifrom, &hfrom);
+ }
+}
+
+int
+interface_status(struct interface_info *ifinfo)
+{
+ char * ifname = ifinfo->name;
+ int ifsock = ifinfo->rfdesc;
+ struct ifreq ifr;
+ struct ifmediareq ifmr;
+
+ /* get interface flags */
+ memset(&ifr, 0, sizeof(ifr));
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(ifsock, SIOCGIFFLAGS, &ifr) < 0) {
+ syslog(LOG_ERR, "ioctl(SIOCGIFFLAGS) on %s: %m",
+ ifname);
+ goto inactive;
+ }
+ /*
+ * if one of UP and RUNNING flags is dropped,
+ * the interface is not active.
+ */
+ if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
+ goto inactive;
+ }
+ /* Next, check carrier on the interface, if possible */
+ if (ifinfo->noifmedia)
+ goto active;
+ memset(&ifmr, 0, sizeof(ifmr));
+ strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
+ if (ioctl(ifsock, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) {
+ if (errno != EINVAL) {
+ syslog(LOG_DEBUG, "ioctl(SIOCGIFMEDIA) on %s: %m",
+ ifname);
+ ifinfo->noifmedia = 1;
+ goto active;
+ }
+ /*
+ * EINVAL (or ENOTTY) simply means that the interface
+ * does not support the SIOCGIFMEDIA ioctl. We regard it alive.
+ */
+ ifinfo->noifmedia = 1;
+ goto active;
+ }
+ if (ifmr.ifm_status & IFM_AVALID) {
+ switch(ifmr.ifm_active & IFM_NMASK) {
+ case IFM_ETHER:
+ if (ifmr.ifm_status & IFM_ACTIVE)
+ goto active;
+ else
+ goto inactive;
+ break;
+ default:
+ goto inactive;
+ }
+ }
+ inactive:
+ return(0);
+ active:
+ return(1);
+}
+
+int locate_network (packet)
+ struct packet *packet;
+{
+ struct iaddr ia;
+
+ /* If this came through a gateway, find the corresponding subnet... */
+ if (packet -> raw -> giaddr.s_addr) {
+ struct subnet *subnet;
+ ia.len = 4;
+ memcpy (ia.iabuf, &packet -> raw -> giaddr, 4);
+ subnet = find_subnet (ia);
+ if (subnet)
+ packet -> shared_network = subnet -> shared_network;
+ else
+ packet -> shared_network = (struct shared_network *)0;
+ } else {
+ packet -> shared_network =
+ packet -> interface -> shared_network;
+ }
+ if (packet -> shared_network)
+ return 1;
+ return 0;
+}
+
+void add_timeout (when, where, what)
+ TIME when;
+ void (*where) PROTO ((void *));
+ void *what;
+{
+ struct timeout *t, *q;
+
+ /* See if this timeout supersedes an existing timeout. */
+ t = (struct timeout *)0;
+ for (q = timeouts; q; q = q -> next) {
+ if (q -> func == where && q -> what == what) {
+ if (t)
+ t -> next = q -> next;
+ else
+ timeouts = q -> next;
+ break;
+ }
+ t = q;
+ }
+
+ /* If we didn't supersede a timeout, allocate a timeout
+ structure now. */
+ if (!q) {
+ if (free_timeouts) {
+ q = free_timeouts;
+ free_timeouts = q -> next;
+ q -> func = where;
+ q -> what = what;
+ } else {
+ q = (struct timeout *)malloc (sizeof (struct timeout));
+ if (!q)
+ error ("Can't allocate timeout structure!");
+ q -> func = where;
+ q -> what = what;
+ }
+ }
+
+ q -> when = when;
+
+ /* Now sort this timeout into the timeout list. */
+
+ /* Beginning of list? */
+ if (!timeouts || timeouts -> when > q -> when) {
+ q -> next = timeouts;
+ timeouts = q;
+ return;
+ }
+
+ /* Middle of list? */
+ for (t = timeouts; t -> next; t = t -> next) {
+ if (t -> next -> when > q -> when) {
+ q -> next = t -> next;
+ t -> next = q;
+ return;
+ }
+ }
+
+ /* End of list. */
+ t -> next = q;
+ q -> next = (struct timeout *)0;
+}
+
+void cancel_timeout (where, what)
+ void (*where) PROTO ((void *));
+ void *what;
+{
+ struct timeout *t, *q;
+
+ /* Look for this timeout on the list, and unlink it if we find it. */
+ t = (struct timeout *)0;
+ for (q = timeouts; q; q = q -> next) {
+ if (q -> func == where && q -> what == what) {
+ if (t)
+ t -> next = q -> next;
+ else
+ timeouts = q -> next;
+ break;
+ }
+ t = q;
+ }
+
+ /* If we found the timeout, put it on the free list. */
+ if (q) {
+ q -> next = free_timeouts;
+ free_timeouts = q;
+ }
+}
+
+/* Add a protocol to the list of protocols... */
+void add_protocol (name, fd, handler, local)
+ char *name;
+ int fd;
+ void (*handler) PROTO ((struct protocol *));
+ void *local;
+{
+ struct protocol *p;
+
+ p = (struct protocol *)malloc (sizeof *p);
+ if (!p)
+ error ("can't allocate protocol struct for %s", name);
+
+ p -> fd = fd;
+ p -> handler = handler;
+ p -> local = local;
+
+ p -> next = protocols;
+ protocols = p;
+}
+
+void remove_protocol (proto)
+ struct protocol *proto;
+{
+ struct protocol *p, *next, *prev;
+
+ prev = (struct protocol *)0;
+ for (p = protocols; p; p = next) {
+ next = p -> next;
+ if (p == proto) {
+ if (prev)
+ prev -> next = p -> next;
+ else
+ protocols = p -> next;
+ free (p);
+ }
+ }
+}
diff --git a/usr.sbin/dhcpd/errwarn.c b/usr.sbin/dhcpd/errwarn.c
new file mode 100644
index 00000000000..65fe1ca05e2
--- /dev/null
+++ b/usr.sbin/dhcpd/errwarn.c
@@ -0,0 +1,234 @@
+/* $OpenBSD: errwarn.c,v 1.1 2004/04/13 23:41:48 henning Exp $ */
+
+/* Errors and warnings... */
+
+/*
+ * Copyright (c) 1996 The Internet Software Consortium.
+ * All Rights Reserved.
+ * Copyright (c) 1995 RadioMail Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of RadioMail Corporation, the Internet Software
+ * Consortium nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY RADIOMAIL CORPORATION, THE INTERNET
+ * SOFTWARE CONSORTIUM AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL RADIOMAIL CORPORATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This software was written for RadioMail Corporation by Ted Lemon
+ * under a contract with Vixie Enterprises. Further modifications have
+ * been made for the Internet Software Consortium under a contract
+ * with Vixie Laboratories.
+ */
+
+#include <errno.h>
+
+#include "dhcpd.h"
+
+static void do_percentm(char *obuf, size_t size, char *ibuf);
+
+static char mbuf[1024];
+static char fbuf[1024];
+
+int warnings_occurred;
+
+/*
+ * Log an error message, then exit.
+ */
+void
+error(char *fmt, ...)
+{
+ va_list list;
+
+ do_percentm(fbuf, sizeof(fbuf), fmt);
+
+ va_start(list, fmt);
+ vsnprintf(mbuf, sizeof(mbuf), fbuf, list);
+ va_end(list);
+
+#ifndef DEBUG
+ syslog(log_priority | LOG_ERR, "%s", mbuf);
+#endif
+
+ /* Also log it to stderr? */
+ if (log_perror) {
+ write(2, mbuf, strlen(mbuf));
+ write(2, "\n", 1);
+ }
+
+ syslog(LOG_CRIT, "exiting.");
+ if (log_perror) {
+ fprintf(stderr, "exiting.\n");
+ fflush(stderr);
+ }
+ exit(1);
+}
+
+/*
+ * Log a warning message...
+ */
+int
+warn(char *fmt, ...)
+{
+ va_list list;
+
+ do_percentm(fbuf, sizeof(fbuf), fmt);
+
+ va_start(list, fmt);
+ vsnprintf(mbuf, sizeof(mbuf), fbuf, list);
+ va_end(list);
+
+#ifndef DEBUG
+ syslog(log_priority | LOG_ERR, "%s", mbuf);
+#endif
+
+ if (log_perror) {
+ write(2, mbuf, strlen(mbuf));
+ write(2, "\n", 1);
+ }
+
+ return (0);
+}
+
+/*
+ * Log a note...
+ */
+int
+note(char *fmt, ...)
+{
+ va_list list;
+
+ do_percentm(fbuf, sizeof(fbuf), fmt);
+
+ va_start(list, fmt);
+ vsnprintf(mbuf, sizeof(mbuf), fbuf, list);
+ va_end(list);
+
+#ifndef DEBUG
+ syslog(log_priority | LOG_INFO, "%s", mbuf);
+#endif
+
+ if (log_perror) {
+ write(2, mbuf, strlen(mbuf));
+ write(2, "\n", 1);
+ }
+
+ return (0);
+}
+
+/*
+ * Log a debug message...
+ */
+int
+debug(char *fmt, ...)
+{
+ va_list list;
+
+ do_percentm(fbuf, sizeof(fbuf), fmt);
+
+ va_start(list, fmt);
+ vsnprintf(mbuf, sizeof(mbuf), fbuf, list);
+ va_end(list);
+
+#ifndef DEBUG
+ syslog(log_priority | LOG_DEBUG, "%s", mbuf);
+#endif
+
+ if (log_perror) {
+ write(2, mbuf, strlen(mbuf));
+ write(2, "\n", 1);
+ }
+
+ return (0);
+}
+
+/*
+ * Find %m in the input string and substitute an error message string.
+ */
+static void
+do_percentm(char *obuf, size_t size, char *ibuf)
+{
+ char ch;
+ char *s = ibuf;
+ char *t = obuf;
+ size_t prlen;
+ size_t fmt_left;
+ int saved_errno = errno;
+
+ /*
+ * We wouldn't need this mess if printf handled %m, or if
+ * strerror() had been invented before syslog().
+ */
+ for (fmt_left = size; (ch = *s); ++s) {
+ if (ch == '%' && s[1] == 'm') {
+ ++s;
+ prlen = snprintf(t, fmt_left, "%s",
+ strerror(saved_errno));
+ if (prlen >= fmt_left)
+ prlen = fmt_left - 1;
+ t += prlen;
+ fmt_left -= prlen;
+ } else {
+ if (fmt_left > 1) {
+ *t++ = ch;
+ fmt_left--;
+ }
+ }
+ }
+ *t = '\0';
+}
+
+int
+parse_warn(char *fmt, ...)
+{
+ va_list list;
+ static char spaces[] =
+ " "
+ " "; /* 80 spaces */
+
+ do_percentm(mbuf, sizeof(mbuf), fmt);
+ snprintf(fbuf, sizeof(fbuf), "%s line %d: %s", tlname, lexline, mbuf);
+ va_start(list, fmt);
+ vsnprintf(mbuf, sizeof(mbuf), fbuf, list);
+ va_end(list);
+
+#ifndef DEBUG
+ syslog(log_priority | LOG_ERR, "%s", mbuf);
+ syslog(log_priority | LOG_ERR, "%s", token_line);
+ if (lexline < 81)
+ syslog(log_priority | LOG_ERR,
+ "%s^", &spaces[sizeof(spaces) - lexchar]);
+#endif
+
+ if (log_perror) {
+ write(2, mbuf, strlen(mbuf));
+ write(2, "\n", 1);
+ write(2, token_line, strlen(token_line));
+ write(2, "\n", 1);
+ write(2, spaces, lexchar - 1);
+ write(2, "^\n", 2);
+ }
+
+ warnings_occurred = 1;
+
+ return (0);
+}
diff --git a/usr.sbin/dhcpd/hash.c b/usr.sbin/dhcpd/hash.c
new file mode 100644
index 00000000000..b4bba2186c9
--- /dev/null
+++ b/usr.sbin/dhcpd/hash.c
@@ -0,0 +1,159 @@
+/* hash.c
+
+ Routines for manipulating hash tables... */
+
+/*
+ * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include "dhcpd.h"
+
+static int do_hash PROTO ((unsigned char *, int, int));
+
+struct hash_table *new_hash ()
+{
+ struct hash_table *rv = new_hash_table (DEFAULT_HASH_SIZE, "new_hash");
+ if (!rv)
+ return rv;
+ memset (&rv -> buckets [0], 0,
+ DEFAULT_HASH_SIZE * sizeof (struct hash_bucket *));
+ return rv;
+}
+
+static int do_hash (name, len, size)
+ unsigned char *name;
+ int len;
+ int size;
+{
+ register int accum = 0;
+ register unsigned char *s = name;
+ int i = len;
+ while (i--) {
+ /* Add the character in... */
+ accum += *s++;
+ /* Add carry back in... */
+ while (accum > 255) {
+ accum = (accum & 255) + (accum >> 8);
+ }
+ }
+ return accum % size;
+}
+
+void add_hash (table, name, len, pointer)
+ struct hash_table *table;
+ int len;
+ unsigned char *name;
+ unsigned char *pointer;
+{
+ int hashno;
+ struct hash_bucket *bp;
+
+ if (!table)
+ return;
+ if (!len)
+ len = strlen ((char *)name);
+
+ hashno = do_hash (name, len, table -> hash_count);
+ bp = new_hash_bucket ("add_hash");
+
+ if (!bp) {
+ warn ("Can't add %s to hash table.", name);
+ return;
+ }
+ bp -> name = name;
+ bp -> value = pointer;
+ bp -> next = table -> buckets [hashno];
+ bp -> len = len;
+ table -> buckets [hashno] = bp;
+}
+
+void delete_hash_entry (table, name, len)
+ struct hash_table *table;
+ int len;
+ unsigned char *name;
+{
+ int hashno;
+ struct hash_bucket *bp, *pbp = (struct hash_bucket *)0;
+
+ if (!table)
+ return;
+ if (!len)
+ len = strlen ((char *)name);
+
+ hashno = do_hash (name, len, table -> hash_count);
+
+ /* Go through the list looking for an entry that matches;
+ if we find it, delete it. */
+ for (bp = table -> buckets [hashno]; bp; bp = bp -> next) {
+ if ((!bp -> len &&
+ !strcmp ((char *)bp -> name, (char *)name)) ||
+ (bp -> len == len &&
+ !memcmp (bp -> name, name, len))) {
+ if (pbp) {
+ pbp -> next = bp -> next;
+ } else {
+ table -> buckets [hashno] = bp -> next;
+ }
+ free_hash_bucket (bp, "delete_hash_entry");
+ break;
+ }
+ pbp = bp; /* jwg, 9/6/96 - nice catch! */
+ }
+}
+
+unsigned char *hash_lookup (table, name, len)
+ struct hash_table *table;
+ unsigned char *name;
+ int len;
+{
+ int hashno;
+ struct hash_bucket *bp;
+
+ if (!table)
+ return (unsigned char *)0;
+
+ if (!len)
+ len = strlen ((char *)name);
+
+ hashno = do_hash (name, len, table -> hash_count);
+
+ for (bp = table -> buckets [hashno]; bp; bp = bp -> next) {
+ if (len == bp -> len && !memcmp (bp -> name, name, len))
+ return bp -> value;
+ }
+ return (unsigned char *)0;
+}
diff --git a/usr.sbin/dhcpd/hash.h b/usr.sbin/dhcpd/hash.h
new file mode 100644
index 00000000000..1bebb3140f8
--- /dev/null
+++ b/usr.sbin/dhcpd/hash.h
@@ -0,0 +1,56 @@
+/* hash.h
+
+ Definitions for hashing... */
+
+/*
+ * Copyright (c) 1995, 1996 The Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#define DEFAULT_HASH_SIZE 97
+
+struct hash_bucket {
+ struct hash_bucket *next;
+ unsigned char *name;
+ int len;
+ unsigned char *value;
+};
+
+struct hash_table {
+ int hash_count;
+ struct hash_bucket *buckets [DEFAULT_HASH_SIZE];
+};
+
diff --git a/usr.sbin/dhcpd/icmp.c b/usr.sbin/dhcpd/icmp.c
new file mode 100644
index 00000000000..8a215e862e1
--- /dev/null
+++ b/usr.sbin/dhcpd/icmp.c
@@ -0,0 +1,170 @@
+/* icmp.c
+
+ ICMP Protocol engine - for sending out pings and receiving
+ responses. */
+
+/*
+ * Copyright (c) 1997, 1998 The Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include "dhcpd.h"
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+
+static int icmp_protocol_initialized;
+static int icmp_protocol_fd;
+
+/* Initialize the ICMP protocol. */
+
+void icmp_startup (routep, handler)
+ int routep;
+ void (*handler) PROTO ((struct iaddr, u_int8_t *, int));
+{
+ struct protoent *proto;
+ int protocol = 1;
+ int state;
+
+ /* Only initialize icmp once. */
+ if (icmp_protocol_initialized)
+ error ("attempted to reinitialize icmp protocol");
+ icmp_protocol_initialized = 1;
+
+ /* Get the protocol number (should be 1). */
+ proto = getprotobyname ("icmp");
+ if (proto)
+ protocol = proto -> p_proto;
+
+ /* Get a raw socket for the ICMP protocol. */
+ icmp_protocol_fd = socket (AF_INET, SOCK_RAW, protocol);
+ if (icmp_protocol_fd < 0)
+ error ("unable to create icmp socket: %m");
+
+ /* Make sure it does routing... */
+ state = 0;
+ if (setsockopt (icmp_protocol_fd, SOL_SOCKET, SO_DONTROUTE,
+ (char *)&state, sizeof state) < 0)
+ error ("Unable to disable SO_DONTROUTE on ICMP socket: %m");
+
+ add_protocol ("icmp", icmp_protocol_fd, icmp_echoreply,
+ (void *)handler);
+}
+
+int icmp_echorequest (addr)
+ struct iaddr *addr;
+{
+ struct sockaddr_in to;
+ struct icmp icmp;
+ int status;
+
+ if (!icmp_protocol_initialized)
+ error ("attempt to use ICMP protocol before initialization.");
+
+ memset(&to, 0, sizeof to);
+ to.sin_len = sizeof to;
+ to.sin_family = AF_INET;
+ to.sin_port = 0; /* unused. */
+ memcpy (&to.sin_addr, addr -> iabuf, sizeof to.sin_addr); /* XXX */
+
+ icmp.icmp_type = ICMP_ECHO;
+ icmp.icmp_code = 0;
+ icmp.icmp_cksum = 0;
+ icmp.icmp_seq = 0;
+#ifdef PTRSIZE_64BIT
+ icmp.icmp_id = (((u_int32_t)(u_int64_t)addr) ^
+ (u_int32_t)(((u_int64_t)addr) >> 32));
+#else
+ icmp.icmp_id = (u_int32_t)addr;
+#endif
+
+ icmp.icmp_cksum = wrapsum (checksum ((unsigned char *)&icmp,
+ sizeof icmp, 0));
+
+ /* Send the ICMP packet... */
+ status = sendto (icmp_protocol_fd, (char *)&icmp, sizeof icmp, 0,
+ (struct sockaddr *)&to, sizeof to);
+ if (status < 0)
+ warn ("icmp_echorequest %s: %m", inet_ntoa(to.sin_addr));
+
+ if (status != sizeof icmp)
+ return 0;
+ return 1;
+}
+
+void icmp_echoreply (protocol)
+ struct protocol *protocol;
+{
+ struct icmp *icfrom;
+ struct sockaddr_in from;
+ u_int8_t icbuf [1500];
+ int status, len;
+ socklen_t salen;
+ struct iaddr ia;
+ void (*handler) PROTO ((struct iaddr, u_int8_t *, int));
+
+ salen = sizeof from;
+ status = recvfrom (protocol -> fd, (char *)icbuf, sizeof icbuf, 0,
+ (struct sockaddr *)&from, &salen);
+ if (status < 0) {
+ warn ("icmp_echoreply: %m");
+ return;
+ }
+
+ /* Probably not for us. */
+ if (status < (sizeof (struct ip)) + (sizeof *icfrom)) {
+ return;
+ }
+
+ len = status - sizeof (struct ip);
+ icfrom = (struct icmp *)(icbuf + sizeof (struct ip));
+
+ /* Silently discard ICMP packets that aren't echoreplies. */
+ if (icfrom -> icmp_type != ICMP_ECHOREPLY) {
+ return;
+ }
+
+ /* If we were given a second-stage handler, call it. */
+ if (protocol -> local) {
+ handler = ((void (*) PROTO ((struct iaddr,
+ u_int8_t *, int)))
+ protocol -> local);
+ memcpy (ia.iabuf, &from.sin_addr, sizeof from.sin_addr);
+ ia.len = sizeof from.sin_addr;
+
+ (*handler) (ia, icbuf, len);
+ }
+}
diff --git a/usr.sbin/dhcpd/inet.c b/usr.sbin/dhcpd/inet.c
new file mode 100644
index 00000000000..16097b08185
--- /dev/null
+++ b/usr.sbin/dhcpd/inet.c
@@ -0,0 +1,184 @@
+/* inet.c
+
+ Subroutines to manipulate internet addresses in a safely portable
+ way... */
+
+/*
+ * Copyright (c) 1996 The Internet Software Consortium. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include "dhcpd.h"
+
+/* Return just the network number of an internet address... */
+
+struct iaddr subnet_number (addr, mask)
+ struct iaddr addr;
+ struct iaddr mask;
+{
+ int i;
+ struct iaddr rv;
+
+ rv.len = 0;
+
+ /* Both addresses must have the same length... */
+ if (addr.len != mask.len)
+ return rv;
+
+ rv.len = addr.len;
+ for (i = 0; i < rv.len; i++)
+ rv.iabuf [i] = addr.iabuf [i] & mask.iabuf [i];
+ return rv;
+}
+
+/* Combine a network number and a integer to produce an internet address.
+ * This won't work for subnets with more than 32 bits of host address, but
+ * maybe this isn't a problem.
+ */
+
+struct iaddr ip_addr (subnet, mask, host_address)
+ struct iaddr subnet;
+ struct iaddr mask;
+ u_int32_t host_address;
+{
+ int i, j, k;
+ u_int32_t swaddr;
+ struct iaddr rv;
+ unsigned char habuf [sizeof swaddr];
+
+ swaddr = htonl (host_address);
+ memcpy (habuf, &swaddr, sizeof swaddr);
+
+ /* Combine the subnet address and the host address. If
+ the host address is bigger than can fit in the subnet,
+ return a zero-length iaddr structure. */
+ rv = subnet;
+ j = rv.len - sizeof habuf;
+ for (i = sizeof habuf - 1; i >= 0; i--) {
+ if (mask.iabuf [i + j]) {
+ if (habuf [i] > (mask.iabuf [i + j] ^ 0xFF)) {
+ rv.len = 0;
+ return rv;
+ }
+ for (k = i - 1; k >= 0; k--) {
+ if (habuf [k]) {
+ rv.len = 0;
+ return rv;
+ }
+ }
+ rv.iabuf [i + j] |= habuf [i];
+ break;
+ } else
+ rv.iabuf [i + j] = habuf [i];
+ }
+
+ return rv;
+}
+
+/* Given a subnet number and netmask, return the address on that subnet
+ for which the host portion of the address is all ones (the standard
+ broadcast address). */
+
+struct iaddr broadcast_addr (subnet, mask)
+ struct iaddr subnet;
+ struct iaddr mask;
+{
+ int i;
+ struct iaddr rv;
+
+ if (subnet.len != mask.len) {
+ rv.len = 0;
+ return rv;
+ }
+
+ for (i = 0; i < subnet.len; i++) {
+ rv.iabuf [i] = subnet.iabuf [i] | (~mask.iabuf [i] & 255);
+ }
+ rv.len = subnet.len;
+
+ return rv;
+}
+
+u_int32_t host_addr (addr, mask)
+ struct iaddr addr;
+ struct iaddr mask;
+{
+ int i;
+ u_int32_t swaddr;
+ struct iaddr rv;
+
+ rv.len = 0;
+
+ /* Mask out the network bits... */
+ rv.len = addr.len;
+ for (i = 0; i < rv.len; i++)
+ rv.iabuf [i] = addr.iabuf [i] & ~mask.iabuf [i];
+
+ /* Copy out up to 32 bits... */
+ memcpy (&swaddr, &rv.iabuf [rv.len - sizeof swaddr], sizeof swaddr);
+
+ /* Swap it and return it. */
+ return ntohl (swaddr);
+}
+
+int addr_eq (addr1, addr2)
+ struct iaddr addr1, addr2;
+{
+ if (addr1.len != addr2.len)
+ return 0;
+ return memcmp (addr1.iabuf, addr2.iabuf, addr1.len) == 0;
+}
+
+char *piaddr (addr)
+ struct iaddr addr;
+{
+ static char pbuf [32];
+ char *s;
+ struct in_addr a;
+
+ memcpy(&a, &(addr.iabuf), sizeof(struct in_addr));
+
+ if (addr.len == 0) {
+ strlcpy (pbuf, "<null address>", sizeof(pbuf));
+ }
+ else {
+ s = inet_ntoa(a);
+ if (s != NULL)
+ strlcpy(pbuf, s, sizeof(pbuf));
+ else
+ strlcpy (pbuf, "<invalid address>", sizeof(pbuf));
+ }
+ return(pbuf);
+}
diff --git a/usr.sbin/dhcpd/inet.h b/usr.sbin/dhcpd/inet.h
new file mode 100644
index 00000000000..1cedc2331a2
--- /dev/null
+++ b/usr.sbin/dhcpd/inet.h
@@ -0,0 +1,52 @@
+/* inet.h
+
+ Portable definitions for internet addresses */
+
+/*
+ * Copyright (c) 1996 The Internet Software Consortium. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+/* An internet address of up to 128 bits. */
+
+struct iaddr {
+ int len;
+ unsigned char iabuf [16];
+};
+
+struct iaddrlist {
+ struct iaddrlist *next;
+ struct iaddr addr;
+};
diff --git a/usr.sbin/dhcpd/memory.c b/usr.sbin/dhcpd/memory.c
new file mode 100644
index 00000000000..d383320bf8a
--- /dev/null
+++ b/usr.sbin/dhcpd/memory.c
@@ -0,0 +1,942 @@
+/* memory.c
+
+ Memory-resident database... */
+
+/*
+ * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include "dhcpd.h"
+
+static struct subnet *subnets;
+static struct shared_network *shared_networks;
+static struct hash_table *host_hw_addr_hash;
+static struct hash_table *host_uid_hash;
+static struct hash_table *lease_uid_hash;
+static struct hash_table *lease_ip_addr_hash;
+static struct hash_table *lease_hw_addr_hash;
+static struct lease *dangling_leases;
+
+static struct hash_table *vendor_class_hash;
+static struct hash_table *user_class_hash;
+
+void enter_host (hd)
+ struct host_decl *hd;
+{
+ struct host_decl *hp = (struct host_decl *)0;
+ struct host_decl *np = (struct host_decl *)0;
+
+ hd -> n_ipaddr = (struct host_decl *)0;
+
+ if (hd -> interface.hlen) {
+ if (!host_hw_addr_hash)
+ host_hw_addr_hash = new_hash ();
+ else
+ hp = (struct host_decl *)
+ hash_lookup (host_hw_addr_hash,
+ hd -> interface.haddr,
+ hd -> interface.hlen);
+
+ /* If there isn't already a host decl matching this
+ address, add it to the hash table. */
+ if (!hp)
+ add_hash (host_hw_addr_hash,
+ hd -> interface.haddr, hd -> interface.hlen,
+ (unsigned char *)hd);
+ }
+
+ /* If there was already a host declaration for this hardware
+ address, add this one to the end of the list. */
+
+ if (hp) {
+ for (np = hp; np -> n_ipaddr; np = np -> n_ipaddr)
+ ;
+ np -> n_ipaddr = hd;
+ }
+
+ if (hd -> group -> options [DHO_DHCP_CLIENT_IDENTIFIER]) {
+ if (!tree_evaluate (hd -> group -> options
+ [DHO_DHCP_CLIENT_IDENTIFIER]))
+ return;
+
+ /* If there's no uid hash, make one; otherwise, see if
+ there's already an entry in the hash for this host. */
+ if (!host_uid_hash) {
+ host_uid_hash = new_hash ();
+ hp = (struct host_decl *)0;
+ } else
+ hp = (struct host_decl *) hash_lookup
+ (host_uid_hash,
+ hd -> group -> options
+ [DHO_DHCP_CLIENT_IDENTIFIER] -> value,
+ hd -> group -> options
+ [DHO_DHCP_CLIENT_IDENTIFIER] -> len);
+
+ /* If there's already a host declaration for this
+ client identifier, add this one to the end of the
+ list. Otherwise, add it to the hash table. */
+ if (hp) {
+ /* Don't link it in twice... */
+ if (!np) {
+ for (np = hp; np -> n_ipaddr;
+ np = np -> n_ipaddr)
+ ;
+ np -> n_ipaddr = hd;
+ }
+ } else {
+ add_hash (host_uid_hash,
+ hd -> group -> options
+ [DHO_DHCP_CLIENT_IDENTIFIER] -> value,
+ hd -> group -> options
+ [DHO_DHCP_CLIENT_IDENTIFIER] -> len,
+ (unsigned char *)hd);
+ }
+ }
+}
+
+struct host_decl *find_hosts_by_haddr (htype, haddr, hlen)
+ int htype;
+ unsigned char *haddr;
+ int hlen;
+{
+ struct host_decl *foo;
+
+ foo = (struct host_decl *)hash_lookup (host_hw_addr_hash,
+ haddr, hlen);
+ return foo;
+}
+
+struct host_decl *find_hosts_by_uid (data, len)
+ unsigned char *data;
+ int len;
+{
+ struct host_decl *foo;
+
+ foo = (struct host_decl *)hash_lookup (host_uid_hash, data, len);
+ return foo;
+}
+
+/* More than one host_decl can be returned by find_hosts_by_haddr or
+ find_hosts_by_uid, and each host_decl can have multiple addresses.
+ Loop through the list of hosts, and then for each host, through the
+ list of addresses, looking for an address that's in the same shared
+ network as the one specified. Store the matching address through
+ the addr pointer, update the host pointer to point at the host_decl
+ that matched, and return the subnet that matched. */
+
+struct subnet *find_host_for_network (host, addr, share)
+ struct host_decl **host;
+ struct iaddr *addr;
+ struct shared_network *share;
+{
+ int i;
+ struct subnet *subnet;
+ struct iaddr ip_address;
+ struct host_decl *hp;
+
+ for (hp = *host; hp; hp = hp -> n_ipaddr) {
+ if (!hp -> fixed_addr || !tree_evaluate (hp -> fixed_addr))
+ continue;
+ for (i = 0; i < hp -> fixed_addr -> len; i += 4) {
+ ip_address.len = 4;
+ memcpy (ip_address.iabuf,
+ hp -> fixed_addr -> value + i, 4);
+ subnet = find_grouped_subnet (share, ip_address);
+ if (subnet) {
+ *addr = ip_address;
+ *host = hp;
+ return subnet;
+ }
+ }
+ }
+ return (struct subnet *)0;
+}
+
+void new_address_range (low, high, subnet, dynamic)
+ struct iaddr low, high;
+ struct subnet *subnet;
+ int dynamic;
+{
+ struct lease *address_range, *lp, *plp;
+ struct iaddr net;
+ int min, max, i;
+ char lowbuf [16], highbuf [16], netbuf [16];
+ struct shared_network *share = subnet -> shared_network;
+ struct hostent *h;
+ struct in_addr ia;
+
+ /* All subnets should have attached shared network structures. */
+ if (!share) {
+ strlcpy (netbuf, piaddr (subnet -> net), sizeof(netbuf));
+ error ("No shared network for network %s (%s)",
+ netbuf, piaddr (subnet -> netmask));
+ }
+
+ /* Initialize the hash table if it hasn't been done yet. */
+ if (!lease_uid_hash)
+ lease_uid_hash = new_hash ();
+ if (!lease_ip_addr_hash)
+ lease_ip_addr_hash = new_hash ();
+ if (!lease_hw_addr_hash)
+ lease_hw_addr_hash = new_hash ();
+
+ /* Make sure that high and low addresses are in same subnet. */
+ net = subnet_number (low, subnet -> netmask);
+ if (!addr_eq (net, subnet_number (high, subnet -> netmask))) {
+ strlcpy (lowbuf, piaddr (low), sizeof(lowbuf));
+ strlcpy (highbuf, piaddr (high), sizeof(highbuf));
+ strlcpy (netbuf, piaddr (subnet -> netmask), sizeof(netbuf));
+ error ("Address range %s to %s, netmask %s spans %s!",
+ lowbuf, highbuf, netbuf, "multiple subnets");
+ }
+
+ /* Make sure that the addresses are on the correct subnet. */
+ if (!addr_eq (net, subnet -> net)) {
+ strlcpy (lowbuf, piaddr (low), sizeof(lowbuf));
+ strlcpy (highbuf, piaddr (high), sizeof(highbuf));
+ strlcpy (netbuf, piaddr (subnet -> netmask), sizeof(netbuf));
+ error ("Address range %s to %s not on net %s/%s!",
+ lowbuf, highbuf, piaddr (subnet -> net), netbuf);
+ }
+
+ /* Get the high and low host addresses... */
+ max = host_addr (high, subnet -> netmask);
+ min = host_addr (low, subnet -> netmask);
+
+ /* Allow range to be specified high-to-low as well as low-to-high. */
+ if (min > max) {
+ max = min;
+ min = host_addr (high, subnet -> netmask);
+ }
+
+ /* Get a lease structure for each address in the range. */
+ address_range = new_leases (max - min + 1, "new_address_range");
+ if (!address_range) {
+ strlcpy (lowbuf, piaddr (low), sizeof(lowbuf));
+ strlcpy (highbuf, piaddr (high), sizeof(highbuf));
+ error ("No memory for address range %s-%s.", lowbuf, highbuf);
+ }
+ memset (address_range, 0, (sizeof *address_range) * (max - min + 1));
+
+ /* Fill in the last lease if it hasn't been already... */
+ if (!share -> last_lease) {
+ share -> last_lease = &address_range [0];
+ }
+
+ /* Fill out the lease structures with some minimal information. */
+ for (i = 0; i < max - min + 1; i++) {
+ address_range [i].ip_addr =
+ ip_addr (subnet -> net, subnet -> netmask, i + min);
+ address_range [i].starts =
+ address_range [i].timestamp = MIN_TIME;
+ address_range [i].ends = MIN_TIME;
+ address_range [i].subnet = subnet;
+ address_range [i].shared_network = share;
+ address_range [i].flags = dynamic ? DYNAMIC_BOOTP_OK : 0;
+
+ memcpy (&ia, address_range [i].ip_addr.iabuf, 4);
+
+ if (subnet -> group -> get_lease_hostnames) {
+ h = gethostbyaddr ((char *)&ia, sizeof ia, AF_INET);
+ if (!h)
+ warn ("No hostname for %s", inet_ntoa (ia));
+ else {
+ int len = strlen(h->h_name) + 1;
+ address_range [i].hostname =
+ malloc (len);
+ if (!address_range [i].hostname)
+ error ("no memory for hostname %s.",
+ h -> h_name);
+ strlcpy (address_range [i].hostname,
+ h -> h_name, len);
+ }
+ }
+
+ /* Link this entry into the list. */
+ address_range [i].next = share -> leases;
+ address_range [i].prev = (struct lease *)0;
+ share -> leases = &address_range [i];
+ if (address_range [i].next)
+ address_range [i].next -> prev = share -> leases;
+ add_hash (lease_ip_addr_hash,
+ address_range [i].ip_addr.iabuf,
+ address_range [i].ip_addr.len,
+ (unsigned char *)&address_range [i]);
+ }
+
+ /* Find out if any dangling leases are in range... */
+ plp = (struct lease *)0;
+ for (lp = dangling_leases; lp; lp = lp -> next) {
+ struct iaddr lnet;
+ int lhost;
+
+ lnet = subnet_number (lp -> ip_addr, subnet -> netmask);
+ lhost = host_addr (lp -> ip_addr, subnet -> netmask);
+
+ /* If it's in range, fill in the real lease structure with
+ the dangling lease's values, and remove the lease from
+ the list of dangling leases. */
+ if (addr_eq (lnet, subnet -> net) &&
+ lhost >= i && lhost <= max) {
+ if (plp) {
+ plp -> next = lp -> next;
+ } else {
+ dangling_leases = lp -> next;
+ }
+ lp -> next = (struct lease *)0;
+ address_range [lhost - i].hostname = lp -> hostname;
+ address_range [lhost - i].client_hostname =
+ lp -> client_hostname;
+ supersede_lease (&address_range [lhost - i], lp, 0);
+ free_lease (lp, "new_address_range");
+ } else
+ plp = lp;
+ }
+}
+
+struct subnet *find_subnet (addr)
+ struct iaddr addr;
+{
+ struct subnet *rv;
+
+ for (rv = subnets; rv; rv = rv -> next_subnet) {
+ if (addr_eq (subnet_number (addr, rv -> netmask), rv -> net))
+ return rv;
+ }
+ return (struct subnet *)0;
+}
+
+struct subnet *find_grouped_subnet (share, addr)
+ struct shared_network *share;
+ struct iaddr addr;
+{
+ struct subnet *rv;
+
+ for (rv = share -> subnets; rv; rv = rv -> next_sibling) {
+ if (addr_eq (subnet_number (addr, rv -> netmask), rv -> net))
+ return rv;
+ }
+ return (struct subnet *)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];
+ int i, j;
+ for (i = 0; i < 32; 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)] &
+ (1 << (j & 7)))
+ break;
+ strlcpy (n1buf, piaddr (subnet -> net), sizeof(n1buf));
+ if (warnp)
+ warn ("%ssubnet %s/%d conflicts with subnet %s/%d",
+ "Warning: ", n1buf, 32 - i,
+ piaddr (scan -> net), 32 - j);
+ if (i < j)
+ return 1;
+ }
+ return 0;
+}
+
+/* Enter a new subnet into the subnet list. */
+
+void enter_subnet (subnet)
+ struct subnet *subnet;
+{
+ struct subnet *scan, *prev = (struct subnet *)0;
+
+ /* Check for duplicates... */
+ for (scan = subnets; scan; scan = scan -> next_subnet) {
+ /* When we find a conflict, make sure that the
+ subnet with the narrowest subnet mask comes
+ first. */
+ if (subnet_inner_than (subnet, scan, 1)) {
+ if (prev) {
+ prev -> next_subnet = subnet;
+ } else
+ subnets = subnet;
+ subnet -> next_subnet = scan;
+ return;
+ }
+ prev = scan;
+ }
+
+ /* XXX use the BSD radix tree code instead of a linked list. */
+ subnet -> next_subnet = subnets;
+ subnets = subnet;
+}
+
+/* Enter a new shared network into the shared network list. */
+
+void enter_shared_network (share)
+ struct shared_network *share;
+{
+ /* XXX Sort the nets into a balanced tree to make searching quicker. */
+ share -> next = shared_networks;
+ shared_networks = share;
+}
+
+/* Enter a lease into the system. This is called by the parser each
+ time it reads in a new lease. If the subnet for that lease has
+ already been read in (usually the case), just update that lease;
+ otherwise, allocate temporary storage for the lease and keep it around
+ until we're done reading in the config file. */
+
+void enter_lease (lease)
+ struct lease *lease;
+{
+ struct lease *comp = find_lease_by_ip_addr (lease -> ip_addr);
+
+ /* If we don't have a place for this lease yet, save it for
+ later. */
+ if (!comp) {
+ comp = new_lease ("enter_lease");
+ if (!comp) {
+ error ("No memory for lease %s\n",
+ piaddr (lease -> ip_addr));
+ }
+ *comp = *lease;
+ comp -> next = dangling_leases;
+ comp -> prev = (struct lease *)0;
+ dangling_leases = comp;
+ } else {
+ /* Record the hostname information in the lease. */
+ comp -> hostname = lease -> hostname;
+ comp -> client_hostname = lease -> client_hostname;
+ supersede_lease (comp, lease, 0);
+ }
+}
+
+/* Replace the data in an existing lease with the data in a new lease;
+ adjust hash tables to suit, and insertion sort the lease into the
+ list of leases by expiry time so that we can always find the oldest
+ lease. */
+
+int supersede_lease (comp, lease, commit)
+ struct lease *comp, *lease;
+ int commit;
+{
+ int enter_uid = 0;
+ int enter_hwaddr = 0;
+ struct lease *lp;
+
+ /* Static leases are not currently kept in the database... */
+ if (lease -> flags & STATIC_LEASE)
+ return 1;
+
+ /* If the existing lease hasn't expired and has a different
+ unique identifier or, if it doesn't have a unique
+ identifier, a different hardware address, then the two
+ leases are in conflict. If the existing lease has a uid
+ and the new one doesn't, but they both have the same
+ hardware address, and dynamic bootp is allowed on this
+ lease, then we allow that, in case a dynamic BOOTP lease is
+ requested *after* a DHCP lease has been assigned. */
+
+ if (!(lease -> flags & ABANDONED_LEASE) &&
+ comp -> ends > cur_time &&
+ (((comp -> uid && lease -> uid) &&
+ (comp -> uid_len != lease -> uid_len ||
+ memcmp (comp -> uid, lease -> uid, comp -> uid_len))) ||
+ (!comp -> uid &&
+ ((comp -> hardware_addr.htype !=
+ lease -> hardware_addr.htype) ||
+ (comp -> hardware_addr.hlen !=
+ lease -> hardware_addr.hlen) ||
+ memcmp (comp -> hardware_addr.haddr,
+ lease -> hardware_addr.haddr,
+ comp -> hardware_addr.hlen))))) {
+ warn ("Lease conflict at %s",
+ piaddr (comp -> ip_addr));
+ return 0;
+ } else {
+ /* If there's a Unique ID, dissociate it from the hash
+ table and free it if necessary. */
+ if (comp -> uid) {
+ uid_hash_delete (comp);
+ enter_uid = 1;
+ if (comp -> uid != &comp -> uid_buf [0]) {
+ free (comp -> uid);
+ comp -> uid_max = 0;
+ comp -> uid_len = 0;
+ }
+ comp -> uid = (unsigned char *)0;
+ } else
+ enter_uid = 1;
+
+ if (comp -> hardware_addr.htype &&
+ ((comp -> hardware_addr.hlen !=
+ lease -> hardware_addr.hlen) ||
+ (comp -> hardware_addr.htype !=
+ lease -> hardware_addr.htype) ||
+ memcmp (comp -> hardware_addr.haddr,
+ lease -> hardware_addr.haddr,
+ comp -> hardware_addr.hlen))) {
+ hw_hash_delete (comp);
+ enter_hwaddr = 1;
+ } else if (!comp -> hardware_addr.htype)
+ enter_hwaddr = 1;
+
+ /* Copy the data files, but not the linkages. */
+ comp -> starts = lease -> starts;
+ if (lease -> uid) {
+ if (lease -> uid_len < sizeof (lease -> uid_buf)) {
+ memcpy (comp -> uid_buf,
+ lease -> uid, lease -> uid_len);
+ comp -> uid = &comp -> uid_buf [0];
+ comp -> uid_max = sizeof comp -> uid_buf;
+ } else if (lease -> uid != &lease -> uid_buf [0]) {
+ comp -> uid = lease -> uid;
+ comp -> uid_max = lease -> uid_max;
+ lease -> uid = (unsigned char *)0;
+ lease -> uid_max = 0;
+ } else {
+ error ("corrupt lease uid."); /* XXX */
+ }
+ } else {
+ comp -> uid = (unsigned char *)0;
+ comp -> uid_max = 0;
+ }
+ comp -> uid_len = lease -> uid_len;
+ comp -> host = lease -> host;
+ comp -> hardware_addr = lease -> hardware_addr;
+ comp -> flags = ((lease -> flags & ~PERSISTENT_FLAGS) |
+ (comp -> flags & ~EPHEMERAL_FLAGS));
+
+ /* Record the lease in the uid hash if necessary. */
+ if (enter_uid && lease -> uid) {
+ uid_hash_add (comp);
+ }
+
+ /* Record it in the hardware address hash if necessary. */
+ if (enter_hwaddr && lease -> hardware_addr.htype) {
+ hw_hash_add (comp);
+ }
+
+ /* Remove the lease from its current place in the
+ timeout sequence. */
+ if (comp -> prev) {
+ comp -> prev -> next = comp -> next;
+ } else {
+ comp -> shared_network -> leases = comp -> next;
+ }
+ if (comp -> next) {
+ comp -> next -> prev = comp -> prev;
+ }
+ if (comp -> shared_network -> last_lease == comp) {
+ comp -> shared_network -> last_lease = comp -> prev;
+ }
+
+ /* Find the last insertion point... */
+ if (comp == comp -> shared_network -> insertion_point ||
+ !comp -> shared_network -> insertion_point) {
+ lp = comp -> shared_network -> leases;
+ } else {
+ lp = comp -> shared_network -> insertion_point;
+ }
+
+ if (!lp) {
+ /* Nothing on the list yet? Just make comp the
+ head of the list. */
+ comp -> shared_network -> leases = comp;
+ comp -> shared_network -> last_lease = comp;
+ } else if (lp -> ends > lease -> ends) {
+ /* Skip down the list until we run out of list
+ or find a place for comp. */
+ while (lp -> next && lp -> ends > lease -> ends) {
+ lp = lp -> next;
+ }
+ if (lp -> ends > lease -> ends) {
+ /* If we ran out of list, put comp
+ at the end. */
+ lp -> next = comp;
+ comp -> prev = lp;
+ comp -> next = (struct lease *)0;
+ comp -> shared_network -> last_lease = comp;
+ } else {
+ /* If we didn't, put it between lp and
+ the previous item on the list. */
+ if ((comp -> prev = lp -> prev))
+ comp -> prev -> next = comp;
+ comp -> next = lp;
+ lp -> prev = comp;
+ }
+ } else {
+ /* Skip up the list until we run out of list
+ or find a place for comp. */
+ while (lp -> prev && lp -> ends < lease -> ends) {
+ lp = lp -> prev;
+ }
+ if (lp -> ends < lease -> ends) {
+ /* If we ran out of list, put comp
+ at the beginning. */
+ lp -> prev = comp;
+ comp -> next = lp;
+ comp -> prev = (struct lease *)0;
+ comp -> shared_network -> leases = comp;
+ } else {
+ /* If we didn't, put it between lp and
+ the next item on the list. */
+ if ((comp -> next = lp -> next))
+ comp -> next -> prev = comp;
+ comp -> prev = lp;
+ lp -> next = comp;
+ }
+ }
+ comp -> shared_network -> insertion_point = comp;
+ comp -> ends = lease -> ends;
+ }
+
+ /* Return zero if we didn't commit the lease to permanent storage;
+ nonzero if we did. */
+ return commit && write_lease (comp) && commit_leases ();
+}
+
+/* Release the specified lease and re-hash it as appropriate. */
+
+void release_lease (lease)
+ struct lease *lease;
+{
+ struct lease lt;
+
+ lt = *lease;
+ if (lt.ends > cur_time) {
+ lt.ends = cur_time;
+ supersede_lease (lease, &lt, 1);
+ note ("Released lease for IP address %s",
+ piaddr (lease -> ip_addr));
+ }
+}
+
+
+/* Abandon the specified lease for the specified time. sets it's
+ particulars to zero, the end time apropriately and re-hash it as
+ appropriate. abandons permanently if abtime is 0 */
+
+void abandon_lease (lease, message)
+ struct lease *lease;
+ char *message;
+{
+ struct lease lt;
+ TIME abtime;
+
+ abtime = lease -> subnet -> group -> default_lease_time;
+ lease -> flags |= ABANDONED_LEASE;
+ lt = *lease;
+ lt.ends = cur_time + abtime;
+ warn ("Abandoning IP address %s for %d seconds: %s",
+ piaddr (lease -> ip_addr), abtime, message);
+ lt.hardware_addr.htype = 0;
+ lt.hardware_addr.hlen = 0;
+ lt.uid = (unsigned char *)0;
+ lt.uid_len = 0;
+ supersede_lease (lease, &lt, 1);
+}
+
+/* Locate the lease associated with a given IP address... */
+
+struct lease *find_lease_by_ip_addr (addr)
+ struct iaddr addr;
+{
+ struct lease *lease = (struct lease *)hash_lookup (lease_ip_addr_hash,
+ addr.iabuf,
+ addr.len);
+ return lease;
+}
+
+struct lease *find_lease_by_uid (uid, len)
+ unsigned char *uid;
+ int len;
+{
+ struct lease *lease = (struct lease *)hash_lookup (lease_uid_hash,
+ uid, len);
+ return lease;
+}
+
+struct lease *find_lease_by_hw_addr (hwaddr, hwlen)
+ unsigned char *hwaddr;
+ int hwlen;
+{
+ struct lease *lease = (struct lease *)hash_lookup (lease_hw_addr_hash,
+ hwaddr, hwlen);
+ return lease;
+}
+
+/* Add the specified lease to the uid hash. */
+
+void uid_hash_add (lease)
+ struct lease *lease;
+{
+ struct lease *head =
+ find_lease_by_uid (lease -> uid, lease -> uid_len);
+ struct lease *scan;
+
+#ifdef DEBUG
+ if (lease -> n_uid)
+ abort ();
+#endif
+
+ /* If it's not in the hash, just add it. */
+ if (!head)
+ add_hash (lease_uid_hash, lease -> uid,
+ lease -> uid_len, (unsigned char *)lease);
+ else {
+ /* Otherwise, attach it to the end of the list. */
+ for (scan = head; scan -> n_uid; scan = scan -> n_uid)
+#ifdef DEBUG
+ if (scan == lease)
+ abort ()
+#endif
+ ;
+ scan -> n_uid = lease;
+ }
+}
+
+/* Delete the specified lease from the uid hash. */
+
+void uid_hash_delete (lease)
+ struct lease *lease;
+{
+ struct lease *head =
+ find_lease_by_uid (lease -> uid, lease -> uid_len);
+ struct lease *scan;
+
+ /* If it's not in the hash, we have no work to do. */
+ if (!head) {
+ lease -> n_uid = (struct lease *)0;
+ return;
+ }
+
+ /* If the lease we're freeing is at the head of the list,
+ remove the hash table entry and add a new one with the
+ next lease on the list (if there is one). */
+ if (head == lease) {
+ delete_hash_entry (lease_uid_hash,
+ lease -> uid, lease -> uid_len);
+ if (lease -> n_uid)
+ add_hash (lease_uid_hash,
+ lease -> n_uid -> uid,
+ lease -> n_uid -> uid_len,
+ (unsigned char *)(lease -> n_uid));
+ } else {
+ /* Otherwise, look for the lease in the list of leases
+ attached to the hash table entry, and remove it if
+ we find it. */
+ for (scan = head; scan -> n_uid; scan = scan -> n_uid) {
+ if (scan -> n_uid == lease) {
+ scan -> n_uid = scan -> n_uid -> n_uid;
+ break;
+ }
+ }
+ }
+ lease -> n_uid = (struct lease *)0;
+}
+
+/* Add the specified lease to the hardware address hash. */
+
+void hw_hash_add (lease)
+ struct lease *lease;
+{
+ struct lease *head =
+ find_lease_by_hw_addr (lease -> hardware_addr.haddr,
+ lease -> hardware_addr.hlen);
+ struct lease *scan;
+
+ /* If it's not in the hash, just add it. */
+ if (!head)
+ add_hash (lease_hw_addr_hash,
+ lease -> hardware_addr.haddr,
+ lease -> hardware_addr.hlen,
+ (unsigned char *)lease);
+ else {
+ /* Otherwise, attach it to the end of the list. */
+ for (scan = head; scan -> n_hw; scan = scan -> n_hw)
+ ;
+ scan -> n_hw = lease;
+ }
+}
+
+/* Delete the specified lease from the hardware address hash. */
+
+void hw_hash_delete (lease)
+ struct lease *lease;
+{
+ struct lease *head =
+ find_lease_by_hw_addr (lease -> hardware_addr.haddr,
+ lease -> hardware_addr.hlen);
+ struct lease *scan;
+
+ /* If it's not in the hash, we have no work to do. */
+ if (!head) {
+ lease -> n_hw = (struct lease *)0;
+ return;
+ }
+
+ /* If the lease we're freeing is at the head of the list,
+ remove the hash table entry and add a new one with the
+ next lease on the list (if there is one). */
+ if (head == lease) {
+ delete_hash_entry (lease_hw_addr_hash,
+ lease -> hardware_addr.haddr,
+ lease -> hardware_addr.hlen);
+ if (lease -> n_hw)
+ add_hash (lease_hw_addr_hash,
+ lease -> n_hw -> hardware_addr.haddr,
+ lease -> n_hw -> hardware_addr.hlen,
+ (unsigned char *)(lease -> n_hw));
+ } else {
+ /* Otherwise, look for the lease in the list of leases
+ attached to the hash table entry, and remove it if
+ we find it. */
+ for (scan = head; scan -> n_hw; scan = scan -> n_hw) {
+ if (scan -> n_hw == lease) {
+ scan -> n_hw = scan -> n_hw -> n_hw;
+ break;
+ }
+ }
+ }
+ lease -> n_hw = (struct lease *)0;
+}
+
+
+struct class *add_class (type, name)
+ int type;
+ char *name;
+{
+ struct class *class = new_class ("add_class");
+ char *tname = (char *)malloc (strlen (name) + 1);
+
+ if (!vendor_class_hash)
+ vendor_class_hash = new_hash ();
+ if (!user_class_hash)
+ user_class_hash = new_hash ();
+
+ if (!tname || !class || !vendor_class_hash || !user_class_hash)
+ return (struct class *)0;
+
+ memset (class, 0, sizeof *class);
+ strlcpy (tname, name, strlen(name) + 1);
+ class -> name = tname;
+
+ if (type)
+ add_hash (user_class_hash,
+ (unsigned char *)tname, strlen (tname),
+ (unsigned char *)class);
+ else
+ add_hash (vendor_class_hash,
+ (unsigned char *)tname, strlen (tname),
+ (unsigned char *)class);
+ return class;
+}
+
+struct class *find_class (type, name, len)
+ int type;
+ unsigned char *name;
+ int len;
+{
+ struct class *class =
+ (struct class *)hash_lookup (type
+ ? user_class_hash
+ : vendor_class_hash, name, len);
+ return class;
+}
+
+struct group *clone_group (group, caller)
+ struct group *group;
+ char *caller;
+{
+ struct group *g = new_group (caller);
+ if (!g)
+ error ("%s: can't allocate new group", caller);
+ *g = *group;
+ return g;
+}
+
+/* Write all interesting leases to permanent storage. */
+
+void write_leases ()
+{
+ struct lease *l;
+ struct shared_network *s;
+
+ for (s = shared_networks; s; s = s -> next) {
+ for (l = s -> leases; l; l = l -> next) {
+ if (l -> hardware_addr.hlen ||
+ l -> uid_len ||
+ (l -> flags & ABANDONED_LEASE))
+ if (!write_lease (l))
+ error ("Can't rewrite lease database");
+ }
+ }
+ if (!commit_leases ())
+ error ("Can't commit leases to new database: %m");
+}
+
+void dump_subnets ()
+{
+ struct lease *l;
+ struct shared_network *s;
+ struct subnet *n;
+
+ note ("Subnets:");
+ for (n = subnets; n; n = n -> next_subnet) {
+ debug (" Subnet %s", piaddr (n -> net));
+ debug (" netmask %s",
+ piaddr (n -> netmask));
+ }
+ note ("Shared networks:");
+ for (s = shared_networks; s; s = s -> next) {
+ note (" %s", s -> name);
+ for (l = s -> leases; l; l = l -> next) {
+ print_lease (l);
+ }
+ if (s -> last_lease) {
+ debug (" Last Lease:");
+ print_lease (s -> last_lease);
+ }
+ }
+}
diff --git a/usr.sbin/dhcpd/options.c b/usr.sbin/dhcpd/options.c
new file mode 100644
index 00000000000..e445dad97c0
--- /dev/null
+++ b/usr.sbin/dhcpd/options.c
@@ -0,0 +1,742 @@
+/* options.c
+
+ DHCP options parsing and reassembly. */
+
+/*
+ * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#define DHCP_OPTION_DATA
+#include "dhcpd.h"
+#include <ctype.h>
+
+int bad_options = 0;
+int bad_options_max = 5;
+
+/* Parse all available options out of the specified packet. */
+
+void parse_options (packet)
+ struct packet *packet;
+{
+ /* Initially, zero all option pointers. */
+ memset (packet -> options, 0, sizeof (packet -> options));
+
+ /* If we don't see the magic cookie, there's nothing to parse. */
+ if (memcmp (packet -> raw -> options, DHCP_OPTIONS_COOKIE, 4)) {
+ packet -> options_valid = 0;
+ return;
+ }
+
+ /* Go through the options field, up to the end of the packet
+ or the End field. */
+ parse_option_buffer (packet, &packet -> raw -> options [4],
+ packet -> packet_length - DHCP_FIXED_NON_UDP - 4);
+ /* If we parsed a DHCP Option Overload option, parse more
+ options out of the buffer(s) containing them. */
+ if (packet -> options_valid
+ && packet -> options [DHO_DHCP_OPTION_OVERLOAD].data) {
+ if (packet -> options [DHO_DHCP_OPTION_OVERLOAD].data [0] & 1)
+ parse_option_buffer (packet,
+ (unsigned char *)
+ packet -> raw -> file,
+ sizeof packet -> raw -> file);
+ if (packet -> options [DHO_DHCP_OPTION_OVERLOAD].data [0] & 2)
+ parse_option_buffer (packet,
+ (unsigned char *)
+ packet -> raw -> sname,
+ sizeof packet -> raw -> sname);
+ }
+}
+
+/* Parse options out of the specified buffer, storing addresses of option
+ values in packet -> options and setting packet -> options_valid if no
+ errors are encountered. */
+
+void parse_option_buffer (packet, buffer, length)
+ struct packet *packet;
+ unsigned char *buffer;
+ int length;
+{
+ unsigned char *s, *t;
+ unsigned char *end = buffer + length;
+ int len;
+ int code;
+
+ for (s = buffer; *s != DHO_END && s < end; ) {
+ code = s [0];
+
+ /* Pad options don't have a length - just skip them. */
+ if (code == DHO_PAD) {
+ ++s;
+ continue;
+ }
+ if (s + 2 > end) {
+ len = 65536;
+ goto bogus;
+ }
+
+ /* All other fields (except end, see above) have a
+ one-byte length. */
+ len = s [1];
+
+ /* If the length is outrageous, silently skip the
+ * rest, and mark the packet bad. Unfortuntely
+ * some crappy dhcp servers always seem to give
+ * us garbage on the end of a packet. so rather than
+ * keep refusing, give up and try to take one after
+ * seeing a few without anything good.
+ */
+ if (s + len + 2 > end) {
+ bogus:
+ bad_options++;
+ warn ("option %s (%d) %s.",
+ dhcp_options [code].name, len,
+ "larger than buffer");
+ if (bad_options == bad_options_max) {
+ packet -> options_valid = 1;
+ bad_options = 0;
+ warn ("Many bogus options seen in offers.");
+ warn ("Taking this offer in spite of bogus");
+ warn ("options - hope for the best!");
+ } else {
+ warn ("rejecting bogus offer.");
+ packet -> options_valid = 0;
+ }
+ return;
+ }
+ /* If we haven't seen this option before, just make
+ space for it and copy it there. */
+ if (!packet -> options [code].data) {
+ if (!(t = ((unsigned char *)
+ dmalloc (len + 1, "parse_option_buffer"))))
+ error ("Can't allocate storage for option %s.",
+ dhcp_options [code].name);
+ /* Copy and NUL-terminate the option (in case it's an
+ ASCII string. */
+ memcpy (t, &s [2], len);
+ t [len] = 0;
+ packet -> options [code].len = len;
+ packet -> options [code].data = t;
+ } else {
+ /* If it's a repeat, concatenate it to whatever
+ we last saw. This is really only required
+ for clients, but what the heck... */
+ t = ((unsigned char *)
+ dmalloc (len + packet -> options [code].len + 1,
+ "parse_option_buffer"));
+ if (!t)
+ error ("Can't expand storage for option %s.",
+ dhcp_options [code].name);
+ memcpy (t, packet -> options [code].data,
+ packet -> options [code].len);
+ memcpy (t + packet -> options [code].len,
+ &s [2], len);
+ packet -> options [code].len += len;
+ t [packet -> options [code].len] = 0;
+ dfree (packet -> options [code].data,
+ "parse_option_buffer");
+ packet -> options [code].data = t;
+ }
+ s += len + 2;
+ }
+ packet -> options_valid = 1;
+}
+
+/* cons options into a big buffer, and then split them out into the
+ three separate buffers if needed. This allows us to cons up a set
+ of vendor options using the same routine. */
+
+int cons_options (inpacket, outpacket, mms,
+ options, overload, terminate, bootpp, prl, prl_len)
+ struct packet *inpacket;
+ struct dhcp_packet *outpacket;
+ int mms;
+ struct tree_cache **options;
+ int overload; /* Overload flags that may be set. */
+ int terminate;
+ int bootpp;
+ u_int8_t *prl;
+ int prl_len;
+{
+ unsigned char priority_list [300];
+ int priority_len;
+ unsigned char buffer [4096]; /* Really big buffer... */
+ int main_buffer_size;
+ int mainbufix, bufix;
+ int option_size;
+ int length;
+
+ /* If the client has provided a maximum DHCP message size,
+ use that; otherwise, if it's BOOTP, only 64 bytes; otherwise
+ use up to the minimum IP MTU size (576 bytes). */
+ /* XXX if a BOOTP client specifies a max message size, we will
+ honor it. */
+ if (!mms &&
+ inpacket &&
+ inpacket -> options [DHO_DHCP_MAX_MESSAGE_SIZE].data &&
+ (inpacket -> options [DHO_DHCP_MAX_MESSAGE_SIZE].len >=
+ sizeof (u_int16_t)))
+ mms = getUShort (inpacket -> options
+ [DHO_DHCP_MAX_MESSAGE_SIZE].data);
+
+ /* If the client has provided a maximum DHCP message size,
+ use that; otherwise, if it's BOOTP, only 64 bytes; otherwise
+ use up to the minimum IP MTU size (576 bytes). */
+ /* XXX if a BOOTP client specifies a max message size, we will
+ honor it. */
+ if (mms)
+ main_buffer_size = mms - DHCP_FIXED_LEN;
+ else if (bootpp)
+ main_buffer_size = 64;
+ else
+ main_buffer_size = 576 - DHCP_FIXED_LEN;
+
+ if (main_buffer_size > sizeof buffer)
+ main_buffer_size = sizeof buffer;
+
+ /* Preload the option priority list with mandatory options. */
+ priority_len = 0;
+ priority_list [priority_len++] = DHO_DHCP_MESSAGE_TYPE;
+ priority_list [priority_len++] = DHO_DHCP_SERVER_IDENTIFIER;
+ priority_list [priority_len++] = DHO_DHCP_LEASE_TIME;
+ priority_list [priority_len++] = DHO_DHCP_MESSAGE;
+
+ /* If the client has provided a list of options that it wishes
+ returned, use it to prioritize. Otherwise, prioritize
+ based on the default priority list. */
+
+ if (inpacket &&
+ inpacket -> options [DHO_DHCP_PARAMETER_REQUEST_LIST].data) {
+ int prlen = (inpacket ->
+ options [DHO_DHCP_PARAMETER_REQUEST_LIST].len);
+ if (prlen + priority_len > sizeof priority_list)
+ prlen = (sizeof priority_list) - priority_len;
+
+ memcpy (&priority_list [priority_len],
+ (inpacket -> options
+ [DHO_DHCP_PARAMETER_REQUEST_LIST].data), prlen);
+ priority_len += prlen;
+ prl = priority_list;
+ } else if (prl) {
+ if (prl_len + priority_len > sizeof priority_list)
+ prl_len = (sizeof priority_list) - priority_len;
+
+ memcpy (&priority_list [priority_len], prl, prl_len);
+ priority_len += prl_len;
+ prl = priority_list;
+ } else {
+ memcpy (&priority_list [priority_len],
+ dhcp_option_default_priority_list,
+ sizeof_dhcp_option_default_priority_list);
+ priority_len += sizeof_dhcp_option_default_priority_list;
+ }
+
+ /* Copy the options into the big buffer... */
+ option_size = store_options (buffer,
+ (main_buffer_size - 7 +
+ ((overload & 1) ? DHCP_FILE_LEN : 0) +
+ ((overload & 2) ? DHCP_SNAME_LEN : 0)),
+ options, priority_list, priority_len,
+ main_buffer_size,
+ (main_buffer_size +
+ ((overload & 1) ? DHCP_FILE_LEN : 0)),
+ terminate);
+
+ /* Put the cookie up front... */
+ memcpy (outpacket -> options, DHCP_OPTIONS_COOKIE, 4);
+ mainbufix = 4;
+
+ /* If we're going to have to overload, store the overload
+ option at the beginning. If we can, though, just store the
+ whole thing in the packet's option buffer and leave it at
+ that. */
+ if (option_size <= main_buffer_size - mainbufix) {
+ memcpy (&outpacket -> options [mainbufix],
+ buffer, option_size);
+ mainbufix += option_size;
+ if (mainbufix < main_buffer_size)
+ outpacket -> options [mainbufix++]
+ = DHO_END;
+ length = DHCP_FIXED_NON_UDP + mainbufix;
+ } else {
+ outpacket -> options [mainbufix++] =
+ DHO_DHCP_OPTION_OVERLOAD;
+ outpacket -> options [mainbufix++] = 1;
+ if (option_size > main_buffer_size - mainbufix + DHCP_FILE_LEN)
+ outpacket -> options [mainbufix++] = 3;
+ else
+ outpacket -> options [mainbufix++] = 1;
+
+ memcpy (&outpacket -> options [mainbufix],
+ buffer, main_buffer_size - mainbufix);
+ bufix = main_buffer_size - mainbufix;
+ length = DHCP_FIXED_NON_UDP + mainbufix;
+ if (overload & 1) {
+ if (option_size - bufix <= DHCP_FILE_LEN) {
+ memcpy (outpacket -> file,
+ &buffer [bufix], option_size - bufix);
+ mainbufix = option_size - bufix;
+ if (mainbufix < DHCP_FILE_LEN)
+ outpacket -> file [mainbufix++]
+ = DHO_END;
+ while (mainbufix < DHCP_FILE_LEN)
+ outpacket -> file [mainbufix++]
+ = DHO_PAD;
+ } else {
+ memcpy (outpacket -> file,
+ &buffer [bufix], DHCP_FILE_LEN);
+ bufix += DHCP_FILE_LEN;
+ }
+ }
+ if ((overload & 2) && option_size < bufix) {
+ memcpy (outpacket -> sname,
+ &buffer [bufix], option_size - bufix);
+
+ mainbufix = option_size - bufix;
+ if (mainbufix < DHCP_SNAME_LEN)
+ outpacket -> file [mainbufix++]
+ = DHO_END;
+ while (mainbufix < DHCP_SNAME_LEN)
+ outpacket -> file [mainbufix++]
+ = DHO_PAD;
+ }
+ }
+ return length;
+}
+
+/* Store all the requested options into the requested buffer. */
+
+int store_options (buffer, buflen, options, priority_list, priority_len,
+ first_cutoff, second_cutoff, terminate)
+ unsigned char *buffer;
+ int buflen;
+ struct tree_cache **options;
+ unsigned char *priority_list;
+ int priority_len;
+ int first_cutoff, second_cutoff;
+ int terminate;
+{
+ int bufix = 0;
+ int option_stored [256];
+ int i;
+ int ix;
+ int tto;
+
+ /* Zero out the stored-lengths array. */
+ memset (option_stored, 0, sizeof option_stored);
+
+ /* Copy out the options in the order that they appear in the
+ priority list... */
+ for (i = 0; i < priority_len; i++) {
+ /* Code for next option to try to store. */
+ int code = priority_list [i];
+ int optstart;
+
+ /* Number of bytes left to store (some may already
+ have been stored by a previous pass). */
+ int length;
+
+ /* If no data is available for this option, skip it. */
+ if (!options [code]) {
+ continue;
+ }
+
+ /* The client could ask for things that are mandatory,
+ in which case we should avoid storing them twice... */
+ if (option_stored [code])
+ continue;
+ option_stored [code] = 1;
+
+ /* Find the value of the option... */
+ if (!tree_evaluate (options [code])) {
+ continue;
+ }
+
+ /* We should now have a constant length for the option. */
+ length = options [code] -> len;
+
+ /* Do we add a NUL? */
+ if (terminate && dhcp_options [code].format [0] == 't') {
+ length++;
+ tto = 1;
+ } else {
+ tto = 0;
+ }
+
+ /* Try to store the option. */
+
+ /* If the option's length is more than 255, we must store it
+ in multiple hunks. Store 255-byte hunks first. However,
+ in any case, if the option data will cross a buffer
+ boundary, split it across that boundary. */
+
+ ix = 0;
+
+ optstart = bufix;
+ while (length) {
+ unsigned char incr = length > 255 ? 255 : length;
+
+ /* If this hunk of the buffer will cross a
+ boundary, only go up to the boundary in this
+ pass. */
+ if (bufix < first_cutoff &&
+ bufix + incr > first_cutoff)
+ incr = first_cutoff - bufix;
+ else if (bufix < second_cutoff &&
+ bufix + incr > second_cutoff)
+ incr = second_cutoff - bufix;
+
+ /* If this option is going to overflow the buffer,
+ skip it. */
+ if (bufix + 2 + incr > buflen) {
+ bufix = optstart;
+ break;
+ }
+
+ /* Everything looks good - copy it in! */
+ buffer [bufix] = code;
+ buffer [bufix + 1] = incr;
+ if (tto && incr == length) {
+ memcpy (buffer + bufix + 2,
+ options [code] -> value + ix,
+ incr - 1);
+ buffer [bufix + 2 + incr - 1] = 0;
+ } else {
+ memcpy (buffer + bufix + 2,
+ options [code] -> value + ix, incr);
+ }
+ length -= incr;
+ ix += incr;
+ bufix += 2 + incr;
+ }
+ }
+ return bufix;
+}
+
+/* Format the specified option so that a human can easily read it. */
+
+char *pretty_print_option (code, data, len, emit_commas, emit_quotes)
+ unsigned int code;
+ unsigned char *data;
+ int len;
+ int emit_commas;
+ int emit_quotes;
+{
+ static char optbuf [32768]; /* XXX */
+ int hunksize = 0;
+ int numhunk = -1;
+ int numelem = 0;
+ char fmtbuf [32];
+ int i, j, k;
+ char *op = optbuf;
+ int opleft = sizeof(optbuf);
+ unsigned char *dp = data;
+ struct in_addr foo;
+ char comma;
+
+
+ /* Code should be between 0 and 255. */
+ if (code > 255)
+ error ("pretty_print_option: bad code %d\n", code);
+
+ if (emit_commas)
+ comma = ',';
+ else
+ comma = ' ';
+
+ /* Figure out the size of the data. */
+ for (i = 0; dhcp_options [code].format [i]; i++) {
+ if (!numhunk) {
+ warn ("%s: Excess information in format string: %s",
+ dhcp_options [code].name,
+ &(dhcp_options [code].format [i]));
+ break;
+ }
+ numelem++;
+ fmtbuf [i] = dhcp_options [code].format [i];
+ switch (dhcp_options [code].format [i]) {
+ case 'A':
+ --numelem;
+ fmtbuf [i] = 0;
+ numhunk = 0;
+ break;
+ case 'X':
+ for (k = 0; k < len; k++) {
+ if (!isascii (data [k]) ||
+ !isprint (data [k]))
+ break;
+ }
+ if (k == len) {
+ fmtbuf [i] = 't';
+ numhunk = -2;
+ } else {
+ fmtbuf [i] = 'x';
+ hunksize++;
+ comma = ':';
+ numhunk = 0;
+ }
+ fmtbuf [i + 1] = 0;
+ break;
+ case 't':
+ fmtbuf [i] = 't';
+ fmtbuf [i + 1] = 0;
+ numhunk = -2;
+ break;
+ case 'I':
+ case 'l':
+ case 'L':
+ hunksize += 4;
+ break;
+ case 's':
+ case 'S':
+ hunksize += 2;
+ break;
+ case 'b':
+ case 'B':
+ case 'f':
+ hunksize++;
+ break;
+ case 'e':
+ break;
+ default:
+ warn ("%s: garbage in format string: %s",
+ dhcp_options [code].name,
+ &(dhcp_options [code].format [i]));
+ break;
+ }
+ }
+
+ /* Check for too few bytes... */
+ if (hunksize > len) {
+ warn ("%s: expecting at least %d bytes; got %d",
+ dhcp_options [code].name,
+ hunksize, len);
+ return "<error>";
+ }
+ /* Check for too many bytes... */
+ if (numhunk == -1 && hunksize < len)
+ warn ("%s: %d extra bytes",
+ dhcp_options [code].name,
+ len - hunksize);
+
+ /* If this is an array, compute its size. */
+ if (!numhunk)
+ numhunk = len / hunksize;
+ /* See if we got an exact number of hunks. */
+ if (numhunk > 0 && numhunk * hunksize < len)
+ warn ("%s: %d extra bytes at end of array",
+ dhcp_options [code].name,
+ len - numhunk * hunksize);
+
+ /* A one-hunk array prints the same as a single hunk. */
+ if (numhunk < 0)
+ numhunk = 1;
+
+ /* Cycle through the array (or hunk) printing the data. */
+ for (i = 0; i < numhunk; i++) {
+ for (j = 0; j < numelem; j++) {
+ int opcount;
+ switch (fmtbuf [j]) {
+ case 't':
+ if (emit_quotes) {
+ *op++ = '"';
+ opleft--;
+ }
+ for (; dp < data + len; dp++) {
+ if (!isascii (*dp) ||
+ !isprint (*dp)) {
+ if (dp + 1 != data + len ||
+ *dp != 0) {
+ snprintf(op, opleft,
+ "\\%03o", *dp);
+ op += 4;
+ opleft -= 4;
+ }
+ } else if (*dp == '"' ||
+ *dp == '\'' ||
+ *dp == '$' ||
+ *dp == '`' ||
+ *dp == '\\') {
+ *op++ = '\\';
+ *op++ = *dp;
+ opleft -= 2;
+ } else {
+ *op++ = *dp;
+ opleft--;
+ }
+ }
+ if (emit_quotes) {
+ *op++ = '"';
+ opleft--;
+ }
+
+ *op = 0;
+ break;
+ case 'I':
+ foo.s_addr = htonl(getULong (dp));
+ opcount = strlcpy(op, inet_ntoa (foo),
+ opleft);
+ if (opcount >= opleft)
+ goto toobig;
+ opleft -= opcount;
+ dp += 4;
+ break;
+ case 'l':
+ opcount = snprintf(op, opleft,"%ld",
+ (long)getLong (dp));
+ if (opcount >= opleft)
+ goto toobig;
+ opleft -= opcount;
+ dp += 4;
+ break;
+ case 'L':
+ opcount = snprintf(op, opleft, "%ld",
+ (unsigned long)getULong (dp));
+ if (opcount >= opleft)
+ goto toobig;
+ opleft -= opcount;
+ dp += 4;
+ break;
+ case 's':
+ opcount = snprintf(op, opleft, "%d",
+ getShort (dp));
+ if (opcount >= opleft)
+ goto toobig;
+ opleft -= opcount;
+ dp += 2;
+ break;
+ case 'S':
+ opcount = snprintf(op, opleft, "%d",
+ getUShort (dp));
+ if (opcount >= opleft)
+ goto toobig;
+ opleft -= opcount;
+ dp += 2;
+ break;
+ case 'b':
+ opcount = snprintf(op, opleft, "%d",
+ *(char *)dp++);
+ if (opcount >= opleft)
+ goto toobig;
+ opleft -= opcount;
+ break;
+ case 'B':
+ opcount = snprintf(op, opleft, "%d", *dp++);
+ if (opcount >= opleft)
+ goto toobig;
+ opleft -= opcount;
+ break;
+ case 'x':
+ opcount = snprintf(op, opleft, "%x", *dp++);
+ if (opcount >= opleft)
+ goto toobig;
+ opleft -= opcount;
+ break;
+ case 'f':
+ opcount = strlcpy(op,
+ *dp++ ? "true" : "false", opleft);
+ if (opcount >= opleft)
+ goto toobig;
+ opleft -= opcount;
+ break;
+ default:
+ warn ("Unexpected format code %c", fmtbuf [j]);
+ }
+ op += strlen (op);
+ opleft -= strlen(op);
+ if (opleft < 1)
+ goto toobig;
+ if (j + 1 < numelem && comma != ':') {
+ *op++ = ' ';
+ opleft--;
+ }
+ }
+ if (i + 1 < numhunk) {
+ *op++ = comma;
+ opleft--;
+ }
+ if (opleft < 1)
+ goto toobig;
+
+ }
+ return optbuf;
+ toobig:
+ warn ("dhcp option too large");
+ return "<error>";
+}
+
+void do_packet (interface, packet, len, from_port, from, hfrom)
+ struct interface_info *interface;
+ struct dhcp_packet *packet;
+ int len;
+ unsigned int from_port;
+ struct iaddr from;
+ struct hardware *hfrom;
+{
+ struct packet tp;
+ int i;
+
+ if (packet -> hlen > sizeof packet -> chaddr) {
+ note ("Discarding packet with invalid hlen.");
+ return;
+ }
+
+ memset (&tp, 0, sizeof tp);
+ tp.raw = packet;
+ tp.packet_length = len;
+ tp.client_port = from_port;
+ tp.client_addr = from;
+ tp.interface = interface;
+ tp.haddr = hfrom;
+
+ parse_options (&tp);
+ if (tp.options_valid &&
+ tp.options [DHO_DHCP_MESSAGE_TYPE].data)
+ tp.packet_type =
+ tp.options [DHO_DHCP_MESSAGE_TYPE].data [0];
+ if (tp.packet_type)
+ dhcp (&tp);
+ else
+ bootp (&tp);
+
+ /* Free the data associated with the options. */
+ for (i = 0; i < 256; i++) {
+ if (tp.options [i].len && tp.options [i].data)
+ dfree (tp.options [i].data, "do_packet");
+ }
+}
+
diff --git a/usr.sbin/dhcpd/osdep.h b/usr.sbin/dhcpd/osdep.h
new file mode 100644
index 00000000000..22826a0b1b1
--- /dev/null
+++ b/usr.sbin/dhcpd/osdep.h
@@ -0,0 +1,221 @@
+/* osdep.h
+
+ Operating system dependencies... */
+
+/*
+ * Copyright (c) 1996, 1997, 1998, 1999 The Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE INTERNET SOFTWARE CONSORTIUM OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This software was written for the Internet Software Consortium by Ted Lemon
+ * under a contract with Vixie Laboratories.
+ */
+
+#include "site.h"
+
+/* Porting::
+
+ If you add a new network API, you must add a check for it below: */
+
+#if !defined (USE_SOCKETS) && \
+ !defined (USE_SOCKET_SEND) && \
+ !defined (USE_SOCKET_RECEIVE) && \
+ !defined (USE_RAW_SOCKETS) && \
+ !defined (USE_RAW_SEND) && \
+ !defined (USE_SOCKET_RECEIVE) && \
+ !defined (USE_BPF) && \
+ !defined (USE_BPF_SEND) && \
+ !defined (USE_BPF_RECEIVE) && \
+ !defined (USE_LPF) && \
+ !defined (USE_LPF_SEND) && \
+ !defined (USE_LPF_RECEIVE) && \
+ !defined (USE_NIT) && \
+ !defined (USE_NIT_SEND) && \
+ !defined (USE_NIT_RECEIVE) && \
+ !defined (USR_DLPI_SEND) && \
+ !defined (USE_DLPI_RECEIVE)
+# define USE_DEFAULT_NETWORK
+#endif
+
+/* Porting::
+
+ If you add a new network API, and have it set up so that it can be
+ used for sending or receiving, but doesn't have to be used for both,
+ then set up an ifdef like the ones below: */
+
+#ifdef USE_SOCKETS
+# define USE_SOCKET_SEND
+# define USE_SOCKET_RECEIVE
+#endif
+
+#ifdef USE_RAW_SOCKETS
+# define USE_RAW_SEND
+# define USE_SOCKET_RECEIVE
+#endif
+
+#ifdef USE_BPF
+# define USE_BPF_SEND
+# define USE_BPF_RECEIVE
+#endif
+
+#ifdef USE_LPF
+# define USE_LPF_SEND
+# define USE_LPF_RECEIVE
+#endif
+
+#ifdef USE_NIT
+# define USE_NIT_SEND
+# define USE_NIT_RECEIVE
+#endif
+
+#ifdef USE_DLPI
+# define USE_DLPI_SEND
+# define USE_DLPI_RECEIVE
+#endif
+
+#ifdef USE_UPF
+# define USE_UPF_SEND
+# define USE_UPF_RECEIVE
+#endif
+
+/* Porting::
+
+ If you add support for sending packets directly out an interface,
+ and your support does not do ARP or routing, you must use a fallback
+ mechanism to deal with packets that need to be sent to routers.
+ Currently, all low-level packet interfaces use BSD sockets as a
+ fallback. */
+
+#if defined (USE_BPF_SEND) || defined (USE_NIT_SEND) || \
+ defined (USE_DLPI_SEND) || defined (USE_UPF_SEND) || defined (USE_LPF_SEND)
+# define USE_SOCKET_FALLBACK
+# define USE_FALLBACK
+#endif
+
+/* Porting::
+
+ If you add support for sending packets directly out an interface
+ and need to be able to assemble packets, add the USE_XXX_SEND
+ definition for your interface to the list tested below. */
+
+#if defined (USE_RAW_SEND) || defined (USE_BPF_SEND) || \
+ defined (USE_NIT_SEND) || defined (USE_UPF_SEND) || \
+ defined (USE_DLPI_SEND) || defined (USE_LPF_SEND)
+# define PACKET_ASSEMBLY
+#endif
+
+/* Porting::
+
+ If you add support for receiving packets directly from an interface
+ and need to be able to decode raw packets, add the USE_XXX_RECEIVE
+ definition for your interface to the list tested below. */
+
+#if defined (USE_RAW_RECEIVE) || defined (USE_BPF_SEND) || \
+ defined (USE_NIT_RECEIVE) || defined (USE_UPF_RECEIVE) || \
+ defined (USE_DLPI_RECEIVE) || \
+ defined (USE_LPF_SEND) || \
+ (defined (USE_SOCKET_SEND) && defined (SO_BINDTODEVICE))
+# define PACKET_DECODING
+#endif
+
+/* If we don't have a DLPI packet filter, we have to filter in userland.
+ Probably not worth doing, actually. */
+#if defined (USE_DLPI_RECEIVE) && !defined (USE_DLPI_PFMOD)
+# define USERLAND_FILTER
+#endif
+
+/* jmp_buf is assumed to be a struct unless otherwise defined in the
+ system header. */
+#ifndef jbp_decl
+# define jbp_decl(x) jmp_buf *x
+#endif
+#ifndef jref
+# define jref(x) (&(x))
+#endif
+#ifndef jdref
+# define jdref(x) (*(x))
+#endif
+#ifndef jrefproto
+# define jrefproto jmp_buf *
+#endif
+
+#ifndef BPF_FORMAT
+# define BPF_FORMAT "/dev/bpf%d"
+#endif
+
+#if defined (IFF_POINTOPOINT) && !defined (HAVE_IFF_POINTOPOINT)
+# define HAVE_IFF_POINTOPOINT
+#endif
+
+#if defined (AF_LINK) && !defined (HAVE_AF_LINK)
+# define HAVE_AF_LINK
+#endif
+
+#if defined (ARPHRD_TUNNEL) && !defined (HAVE_ARPHRD_TUNNEL)
+# define HAVE_ARPHRD_TUNNEL
+#endif
+
+#if defined (ARPHRD_LOOPBACK) && !defined (HAVE_ARPHRD_LOOPBACK)
+# define HAVE_ARPHRD_LOOPBACK
+#endif
+
+#if defined (ARPHRD_ROSE) && !defined (HAVE_ARPHRD_ROSE)
+# define HAVE_ARPHRD_ROSE
+#endif
+
+#if defined (ARPHRD_IEEE802) && !defined (HAVE_ARPHRD_IEEE802)
+# define HAVE_ARPHRD_IEEE802
+#endif
+
+#if defined (ARPHRD_FDDI) && !defined (HAVE_ARPHRD_FDDI)
+# define HAVE_ARPHRD_FDDI
+#endif
+
+#if defined (ARPHRD_AX25) && !defined (HAVE_ARPHRD_AX25)
+# define HAVE_ARPHRD_AX25
+#endif
+
+#if defined (ARPHRD_NETROM) && !defined (HAVE_ARPHRD_NETROM)
+# define HAVE_ARPHRD_NETROM
+#endif
+
+#if defined (ARPHRD_METRICOM) && !defined (HAVE_ARPHRD_METRICOM)
+# define HAVE_ARPHRD_METRICOM
+#endif
+
+#if defined (SO_BINDTODEVICE) && !defined (HAVE_SO_BINDTODEVICE)
+# define HAVE_SO_BINDTODEVICE
+#endif
+
+#if defined (SIOCGIFHWADDR) && !defined (HAVE_SIOCGIFHWADDR)
+# define HAVE_SIOCGIFHWADDR
+#endif
+
+#if defined (AF_LINK) && !defined (HAVE_AF_LINK)
+# define HAVE_AF_LINK
+#endif
diff --git a/usr.sbin/dhcpd/packet.c b/usr.sbin/dhcpd/packet.c
new file mode 100644
index 00000000000..9be6f300872
--- /dev/null
+++ b/usr.sbin/dhcpd/packet.c
@@ -0,0 +1,255 @@
+/* $OpenBSD: packet.c,v 1.1 2004/04/13 23:41:49 henning Exp $ */
+
+/* Packet assembly code, originally contributed by Archie Cobbs. */
+
+/*
+ * Copyright (c) 1995, 1996, 1999 The Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include "dhcpd.h"
+
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <netinet/if_ether.h>
+
+#define ETHER_HEADER_SIZE (ETHER_ADDR_LEN * 2 + sizeof(u_int16_t))
+
+u_int32_t checksum(unsigned char *, unsigned, u_int32_t);
+u_int32_t wrapsum(u_int32_t);
+
+void assemble_ethernet_header(struct interface_info *, unsigned char *,
+ int *, struct hardware *);
+ssize_t decode_ethernet_header(struct interface_info *, unsigned char *,
+ int bufix, struct hardware *);
+
+u_int32_t
+checksum(unsigned char *buf, unsigned nbytes, u_int32_t sum)
+{
+ int i;
+
+ /* Checksum all the pairs of bytes first... */
+ for (i = 0; i < (nbytes & ~1U); i += 2) {
+ sum += (u_int16_t)ntohs(*((u_int16_t *)(buf + i)));
+ if (sum > 0xFFFF)
+ sum -= 0xFFFF;
+ }
+
+ /*
+ * If there's a single byte left over, checksum it, too.
+ * Network byte order is big-endian, so the remaining byte is
+ * the high byte.
+ */
+ if (i < nbytes) {
+ sum += buf[i] << 8;
+ if (sum > 0xFFFF)
+ sum -= 0xFFFF;
+ }
+
+ return (sum);
+}
+
+u_int32_t
+wrapsum(u_int32_t sum)
+{
+ sum = ~sum & 0xFFFF;
+ return (htons(sum));
+}
+
+void
+assemble_hw_header(struct interface_info *interface, unsigned char *buf,
+ int *bufix, struct hardware *to)
+{
+ struct ether_header eh;
+
+ if (to != NULL && to->hlen == 6) /* XXX */
+ memcpy(eh.ether_dhost, to->haddr, sizeof(eh.ether_dhost));
+ else
+ memset(eh.ether_dhost, 0xff, sizeof(eh.ether_dhost));
+ if (interface->hw_address.hlen == sizeof(eh.ether_shost))
+ memcpy(eh.ether_shost, interface->hw_address.haddr,
+ sizeof(eh.ether_shost));
+ else
+ memset(eh.ether_shost, 0x00, sizeof(eh.ether_shost));
+
+ eh.ether_type = htons(ETHERTYPE_IP);
+
+ memcpy(&buf[*bufix], &eh, ETHER_HEADER_SIZE);
+ *bufix += ETHER_HEADER_SIZE;
+}
+
+void
+assemble_udp_ip_header(struct interface_info *interface, unsigned char *buf,
+ int *bufix, u_int32_t from, u_int32_t to, unsigned int port,
+ unsigned char *data, int len)
+{
+ struct ip ip;
+ struct udphdr udp;
+
+ ip.ip_v = 4;
+ ip.ip_hl = 5;
+ ip.ip_tos = IPTOS_LOWDELAY;
+ ip.ip_len = htons(sizeof(ip) + sizeof(udp) + len);
+ ip.ip_id = 0;
+ ip.ip_off = 0;
+ ip.ip_ttl = 16;
+ ip.ip_p = IPPROTO_UDP;
+ ip.ip_sum = 0;
+ ip.ip_src.s_addr = from;
+ ip.ip_dst.s_addr = to;
+
+ ip.ip_sum = wrapsum(checksum((unsigned char *)&ip, sizeof(ip), 0));
+ memcpy(&buf[*bufix], &ip, sizeof(ip));
+ *bufix += sizeof(ip);
+
+ udp.uh_sport = htons(LOCAL_PORT); /* XXX */
+ udp.uh_dport = port; /* XXX */
+ udp.uh_ulen = htons(sizeof(udp) + len);
+ memset(&udp.uh_sum, 0, sizeof(udp.uh_sum));
+
+ udp.uh_sum = wrapsum(checksum((unsigned char *)&udp, sizeof(udp),
+ checksum(data, len, checksum((unsigned char *)&ip.ip_src,
+ 2 * sizeof(ip.ip_src),
+ IPPROTO_UDP + (u_int32_t)ntohs(udp.uh_ulen)))));
+
+ memcpy(&buf[*bufix], &udp, sizeof(udp));
+ *bufix += sizeof(udp);
+}
+
+ssize_t
+decode_hw_header(struct interface_info *interface, unsigned char *buf,
+ int bufix, struct hardware *from)
+{
+ struct ether_header eh;
+
+ memcpy(&eh, buf + bufix, ETHER_HEADER_SIZE);
+
+ memcpy(from->haddr, eh.ether_shost, sizeof(eh.ether_shost));
+ from->htype = ARPHRD_ETHER;
+ from->hlen = sizeof(eh.ether_shost);
+
+ return (sizeof(eh));
+}
+
+ssize_t
+decode_udp_ip_header(struct interface_info *interface, unsigned char *buf,
+ int bufix, struct sockaddr_in *from, unsigned char *data, int buflen)
+{
+ struct ip *ip;
+ struct udphdr *udp;
+ u_int32_t ip_len = (buf[bufix] & 0xf) << 2;
+ u_int32_t sum, usum;
+ static int ip_packets_seen;
+ static int ip_packets_bad_checksum;
+ static int udp_packets_seen;
+ static int udp_packets_bad_checksum;
+ static int udp_packets_length_checked;
+ static int udp_packets_length_overflow;
+ int len = 0;
+
+ ip = (struct ip *)(buf + bufix);
+ udp = (struct udphdr *)(buf + bufix + ip_len);
+
+ /* Check the IP header checksum - it should be zero. */
+ ip_packets_seen++;
+ if (wrapsum(checksum(buf + bufix, ip_len, 0)) != 0) {
+ ip_packets_bad_checksum++;
+ if (ip_packets_seen > 4 &&
+ (ip_packets_seen / ip_packets_bad_checksum) < 2) {
+ note("%d bad IP checksums seen in %d packets",
+ ip_packets_bad_checksum, ip_packets_seen);
+ ip_packets_seen = ip_packets_bad_checksum = 0;
+ }
+ return (-1);
+ }
+
+ if (ntohs(ip->ip_len) != buflen)
+ debug("ip length %d disagrees with bytes received %d.",
+ ntohs(ip->ip_len), buflen);
+
+ memcpy(&from->sin_addr, &ip->ip_src, 4);
+
+ /*
+ * Compute UDP checksums, including the ``pseudo-header'', the
+ * UDP header and the data. If the UDP checksum field is zero,
+ * we're not supposed to do a checksum.
+ */
+ if (!data) {
+ data = buf + bufix + ip_len + sizeof(*udp);
+ len = ntohs(udp->uh_ulen) - sizeof(*udp);
+ udp_packets_length_checked++;
+ if (len + data > buf + bufix + buflen) {
+ udp_packets_length_overflow++;
+ if (udp_packets_length_checked > 4 &&
+ (udp_packets_length_checked /
+ udp_packets_length_overflow) < 2) {
+ note("%d udp packets in %d too long - dropped",
+ udp_packets_length_overflow,
+ udp_packets_length_checked);
+ udp_packets_length_overflow =
+ udp_packets_length_checked = 0;
+ }
+ return (-1);
+ }
+ if (len + data != buf + bufix + buflen)
+ debug("accepting packet with data after udp payload.");
+ }
+
+ usum = udp->uh_sum;
+ udp->uh_sum = 0;
+
+ sum = wrapsum(checksum((unsigned char *)udp, sizeof(*udp),
+ checksum(data, len, checksum((unsigned char *)&ip->ip_src,
+ 2 * sizeof(ip->ip_src),
+ IPPROTO_UDP + (u_int32_t)ntohs(udp->uh_ulen)))));
+
+ udp_packets_seen++;
+ if (usum && usum != sum) {
+ udp_packets_bad_checksum++;
+ if (udp_packets_seen > 4 &&
+ (udp_packets_seen / udp_packets_bad_checksum) < 2) {
+ note("%d bad udp checksums in %d packets",
+ udp_packets_bad_checksum, udp_packets_seen);
+ udp_packets_seen = udp_packets_bad_checksum = 0;
+ }
+ return (-1);
+ }
+
+ memcpy(&from->sin_port, &udp->uh_sport, sizeof(udp->uh_sport));
+
+ return (ip_len + sizeof(*udp));
+}
diff --git a/usr.sbin/dhcpd/parse.c b/usr.sbin/dhcpd/parse.c
new file mode 100644
index 00000000000..90949a9ef16
--- /dev/null
+++ b/usr.sbin/dhcpd/parse.c
@@ -0,0 +1,642 @@
+/* parse.c
+
+ Common parser code for dhcpd and dhclient. */
+
+/*
+ * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include "dhcpd.h"
+#include "dhctoken.h"
+
+/* Skip to the semicolon ending the current statement. If we encounter
+ braces, the matching closing brace terminates the statement. If we
+ encounter a right brace but haven't encountered a left brace, return
+ leaving the brace in the token buffer for the caller. If we see a
+ semicolon and haven't seen a left brace, return. This lets us skip
+ over:
+
+ statement;
+ statement foo bar { }
+ statement foo bar { statement { } }
+ statement}
+
+ ...et cetera. */
+
+void skip_to_semi (cfile)
+ FILE *cfile;
+{
+ int token;
+ char *val;
+ int brace_count = 0;
+
+ do {
+ token = peek_token (&val, cfile);
+ if (token == RBRACE) {
+ if (brace_count) {
+ token = next_token (&val, cfile);
+ if (!--brace_count)
+ return;
+ } else
+ return;
+ } else if (token == LBRACE) {
+ brace_count++;
+ } else if (token == SEMI && !brace_count) {
+ token = next_token (&val, cfile);
+ return;
+ } else if (token == EOL) {
+ /* EOL only happens when parsing /etc/resolv.conf,
+ and we treat it like a semicolon because the
+ resolv.conf file is line-oriented. */
+ token = next_token (&val, cfile);
+ return;
+ }
+ token = next_token (&val, cfile);
+ } while (token != EOF);
+}
+
+int parse_semi (cfile)
+ FILE *cfile;
+{
+ int token;
+ char *val;
+
+ token = next_token (&val, cfile);
+ if (token != SEMI) {
+ parse_warn ("semicolon expected.");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ return 1;
+}
+
+/* string-parameter :== STRING SEMI */
+
+char *parse_string (cfile)
+ FILE *cfile;
+{
+ char *val;
+ int token;
+ char *s;
+
+ token = next_token (&val, cfile);
+ if (token != STRING) {
+ parse_warn ("filename must be a string");
+ skip_to_semi (cfile);
+ return (char *)0;
+ }
+ if (!parse_semi (cfile))
+ return (char *)0;
+
+ s = (char *)malloc (strlen (val) + 1);
+ if (!s)
+ error ("no memory for string %s.", val);
+ strlcpy (s, val, strlen(val) + 1);
+
+ return s;
+}
+
+/* hostname :== identifier | hostname DOT identifier */
+
+char *parse_host_name (cfile)
+ FILE *cfile;
+{
+ char *val;
+ int token;
+ int len = 0;
+ char *s;
+ char *t;
+ pair c = (pair)0;
+
+ /* Read a dotted hostname... */
+ do {
+ /* Read a token, which should be an identifier. */
+ token = next_token (&val, cfile);
+ if (!is_identifier (token) && token != NUMBER) {
+ parse_warn ("expecting an identifier in hostname");
+ skip_to_semi (cfile);
+ return (char *)0;
+ }
+ /* Store this identifier... */
+ if (!(s = (char *)malloc (strlen (val) + 1)))
+ error ("can't allocate temp space for hostname.");
+ strlcpy (s, val, strlen(val) + 1);
+ c = cons ((caddr_t)s, c);
+ len += strlen (s) + 1;
+ /* Look for a dot; if it's there, keep going, otherwise
+ we're done. */
+ token = peek_token (&val, cfile);
+ if (token == DOT)
+ token = next_token (&val, cfile);
+ } while (token == DOT);
+
+ /* Assemble the hostname together into a string. */
+ if (!(s = (char *)malloc (len)))
+ error ("can't allocate space for hostname.");
+ t = s + len;
+ *--t = 0;
+ while (c) {
+ pair cdr = c -> cdr;
+ int l = strlen ((char *)(c -> car));
+ t -= l;
+ memcpy (t, (char *)(c -> car), l);
+ /* Free up temp space. */
+ free (c -> car);
+ free (c);
+ c = cdr;
+ if (t != s)
+ *--t = '.';
+ }
+ return s;
+}
+
+int parse_ip_addr (cfile, addr)
+ FILE *cfile;
+ struct iaddr *addr;
+{
+ addr -> len = 4;
+ if (parse_numeric_aggregate (cfile, addr -> iabuf,
+ &addr -> len, DOT, 10, 8))
+ return 1;
+ return 0;
+}
+
+/* hardware-parameter :== HARDWARE ETHERNET csns SEMI
+ csns :== NUMBER | csns COLON NUMBER */
+
+void parse_hardware_param (cfile, hardware)
+ FILE *cfile;
+ struct hardware *hardware;
+{
+ char *val;
+ int token;
+ int hlen;
+ unsigned char *t;
+
+ token = next_token (&val, cfile);
+ switch (token) {
+ case ETHERNET:
+ hardware -> htype = HTYPE_ETHER;
+ break;
+ case TOKEN_RING:
+ hardware -> htype = HTYPE_IEEE802;
+ break;
+ case FDDI:
+ hardware -> htype = HTYPE_FDDI;
+ break;
+ default:
+ parse_warn ("expecting a network hardware type");
+ skip_to_semi (cfile);
+ return;
+ }
+
+ /* Parse the hardware address information. Technically,
+ it would make a lot of sense to restrict the length of the
+ data we'll accept here to the length of a particular hardware
+ address type. Unfortunately, there are some broken clients
+ out there that put bogus data in the chaddr buffer, and we accept
+ that data in the lease file rather than simply failing on such
+ clients. Yuck. */
+ hlen = 0;
+ t = parse_numeric_aggregate (cfile, (unsigned char *)0, &hlen,
+ COLON, 16, 8);
+ if (!t)
+ return;
+ if (hlen > sizeof hardware -> haddr) {
+ free (t);
+ parse_warn ("hardware address too long");
+ } else {
+ hardware -> hlen = hlen;
+ memcpy ((unsigned char *)&hardware -> haddr [0],
+ t, hardware -> hlen);
+ if (hlen < sizeof hardware -> haddr)
+ memset (&hardware -> haddr [hlen], 0,
+ (sizeof hardware -> haddr) - hlen);
+ free (t);
+ }
+
+ token = next_token (&val, cfile);
+ if (token != SEMI) {
+ parse_warn ("expecting semicolon.");
+ skip_to_semi (cfile);
+ }
+}
+
+/* lease-time :== NUMBER SEMI */
+
+void parse_lease_time (cfile, timep)
+ FILE *cfile;
+ TIME *timep;
+{
+ char *val;
+ int token;
+
+ token = next_token (&val, cfile);
+ if (token != NUMBER) {
+ parse_warn ("Expecting numeric lease time");
+ skip_to_semi (cfile);
+ return;
+ }
+ convert_num ((unsigned char *)timep, val, 10, 32);
+ /* Unswap the number - convert_num returns stuff in NBO. */
+ *timep = ntohl (*timep); /* XXX */
+
+ parse_semi (cfile);
+}
+
+/* No BNF for numeric aggregates - that's defined by the caller. What
+ this function does is to parse a sequence of numbers separated by
+ the token specified in separator. If max is zero, any number of
+ numbers will be parsed; otherwise, exactly max numbers are
+ expected. Base and size tell us how to internalize the numbers
+ once they've been tokenized. */
+
+unsigned char *parse_numeric_aggregate (cfile, buf,
+ max, separator, base, size)
+ FILE *cfile;
+ unsigned char *buf;
+ int *max;
+ int separator;
+ int base;
+ int size;
+{
+ char *val;
+ int token;
+ unsigned char *bufp = buf, *s = NULL;
+ char *t;
+ int count = 0;
+ pair c = (pair)0;
+
+ if (!bufp && *max) {
+ bufp = (unsigned char *)malloc (*max * size / 8);
+ if (!bufp)
+ error ("can't allocate space for numeric aggregate");
+ } else
+ s = bufp;
+
+ do {
+ if (count) {
+ token = peek_token (&val, cfile);
+ if (token != separator) {
+ if (!*max)
+ break;
+ if (token != RBRACE && token != LBRACE)
+ token = next_token (&val, cfile);
+ parse_warn ("too few numbers.");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ return (unsigned char *)0;
+ }
+ token = next_token (&val, cfile);
+ }
+ token = next_token (&val, cfile);
+
+ if (token == EOF) {
+ parse_warn ("unexpected end of file");
+ break;
+ }
+
+ /* Allow NUMBER_OR_NAME if base is 16. */
+ if (token != NUMBER &&
+ (base != 16 || token != NUMBER_OR_NAME)) {
+ parse_warn ("expecting numeric value.");
+ skip_to_semi (cfile);
+ return (unsigned char *)0;
+ }
+ /* If we can, convert the number now; otherwise, build
+ a linked list of all the numbers. */
+ if (s) {
+ convert_num (s, val, base, size);
+ s += size / 8;
+ } else {
+ t = (char *)malloc (strlen (val) + 1);
+ if (!t)
+ error ("no temp space for number.");
+ strlcpy (t, val, strlen(val)+1);
+ c = cons (t, c);
+ }
+ } while (++count != *max);
+
+ /* If we had to cons up a list, convert it now. */
+ if (c) {
+ bufp = (unsigned char *)malloc (count * size / 8);
+ if (!bufp)
+ error ("can't allocate space for numeric aggregate.");
+ s = bufp + count - size / 8;
+ *max = count;
+ }
+ while (c) {
+ pair cdr = c -> cdr;
+ convert_num (s, (char *)(c -> car), base, size);
+ s -= size / 8;
+ /* Free up temp space. */
+ free (c -> car);
+ free (c);
+ c = cdr;
+ }
+ return bufp;
+}
+
+void convert_num (buf, str, base, size)
+ unsigned char *buf;
+ char *str;
+ int base;
+ int size;
+{
+ char *ptr = str;
+ int negative = 0;
+ u_int32_t val = 0;
+ int tval;
+ int max;
+
+ if (*ptr == '-') {
+ negative = 1;
+ ++ptr;
+ }
+
+ /* If base wasn't specified, figure it out from the data. */
+ if (!base) {
+ if (ptr [0] == '0') {
+ if (ptr [1] == 'x') {
+ base = 16;
+ ptr += 2;
+ } else if (isascii (ptr [1]) && isdigit (ptr [1])) {
+ base = 8;
+ ptr += 1;
+ } else {
+ base = 10;
+ }
+ } else {
+ base = 10;
+ }
+ }
+
+ do {
+ tval = *ptr++;
+ /* XXX assumes ASCII... */
+ if (tval >= 'a')
+ tval = tval - 'a' + 10;
+ else if (tval >= 'A')
+ tval = tval - 'A' + 10;
+ else if (tval >= '0')
+ tval -= '0';
+ else {
+ warn ("Bogus number: %s.", str);
+ break;
+ }
+ if (tval >= base) {
+ warn ("Bogus number: %s: digit %d not in base %d",
+ str, tval, base);
+ break;
+ }
+ val = val * base + tval;
+ } while (*ptr);
+
+ if (negative)
+ max = (1 << (size - 1));
+ else
+ max = (1 << (size - 1)) + ((1 << (size - 1)) - 1);
+ if (val > max) {
+ switch (base) {
+ case 8:
+ warn ("value %s%o exceeds max (%d) for precision.",
+ negative ? "-" : "", val, max);
+ break;
+ case 16:
+ warn ("value %s%x exceeds max (%d) for precision.",
+ negative ? "-" : "", val, max);
+ break;
+ default:
+ warn ("value %s%u exceeds max (%d) for precision.",
+ negative ? "-" : "", val, max);
+ break;
+ }
+ }
+
+ if (negative) {
+ switch (size) {
+ case 8:
+ *buf = -(unsigned long)val;
+ break;
+ case 16:
+ putShort (buf, -(unsigned long)val);
+ break;
+ case 32:
+ putLong (buf, -(unsigned long)val);
+ break;
+ default:
+ warn ("Unexpected integer size: %d", size);
+ break;
+ }
+ } else {
+ switch (size) {
+ case 8:
+ *buf = (u_int8_t)val;
+ break;
+ case 16:
+ putUShort (buf, (u_int16_t)val);
+ break;
+ case 32:
+ putULong (buf, val);
+ break;
+ default:
+ warn ("Unexpected integer size: %d", size);
+ break;
+ }
+ }
+}
+
+/* date :== NUMBER NUMBER SLASH NUMBER SLASH NUMBER
+ NUMBER COLON NUMBER COLON NUMBER SEMI
+
+ Dates are always in GMT; first number is day of week; next is
+ year/month/day; next is hours:minutes:seconds on a 24-hour
+ clock. */
+
+TIME parse_date (cfile)
+ FILE *cfile;
+{
+ struct tm tm;
+ int guess;
+ char *val;
+ int token;
+ static int months [11] = { 31, 59, 90, 120, 151, 181,
+ 212, 243, 273, 304, 334 };
+
+ /* Day of week... */
+ token = next_token (&val, cfile);
+ if (token != NUMBER) {
+ parse_warn ("numeric day of week expected.");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ return (TIME)0;
+ }
+ tm.tm_wday = atoi (val);
+
+ /* Year... */
+ token = next_token (&val, cfile);
+ if (token != NUMBER) {
+ parse_warn ("numeric year expected.");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ return (TIME)0;
+ }
+ tm.tm_year = atoi (val);
+ if (tm.tm_year > 1900)
+ tm.tm_year -= 1900;
+
+ /* Slash separating year from month... */
+ token = next_token (&val, cfile);
+ if (token != SLASH) {
+ parse_warn ("expected slash separating year from month.");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ return (TIME)0;
+ }
+
+ /* Month... */
+ token = next_token (&val, cfile);
+ if (token != NUMBER) {
+ parse_warn ("numeric month expected.");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ return (TIME)0;
+ }
+ tm.tm_mon = atoi (val) - 1;
+
+ /* Slash separating month from day... */
+ token = next_token (&val, cfile);
+ if (token != SLASH) {
+ parse_warn ("expected slash separating month from day.");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ return (TIME)0;
+ }
+
+ /* Month... */
+ token = next_token (&val, cfile);
+ if (token != NUMBER) {
+ parse_warn ("numeric day of month expected.");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ return (TIME)0;
+ }
+ tm.tm_mday = atoi (val);
+
+ /* Hour... */
+ token = next_token (&val, cfile);
+ if (token != NUMBER) {
+ parse_warn ("numeric hour expected.");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ return (TIME)0;
+ }
+ tm.tm_hour = atoi (val);
+
+ /* Colon separating hour from minute... */
+ token = next_token (&val, cfile);
+ if (token != COLON) {
+ parse_warn ("expected colon separating hour from minute.");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ return (TIME)0;
+ }
+
+ /* Minute... */
+ token = next_token (&val, cfile);
+ if (token != NUMBER) {
+ parse_warn ("numeric minute expected.");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ return (TIME)0;
+ }
+ tm.tm_min = atoi (val);
+
+ /* Colon separating minute from second... */
+ token = next_token (&val, cfile);
+ if (token != COLON) {
+ parse_warn ("expected colon separating hour from minute.");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ return (TIME)0;
+ }
+
+ /* Minute... */
+ token = next_token (&val, cfile);
+ if (token != NUMBER) {
+ parse_warn ("numeric minute expected.");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ return (TIME)0;
+ }
+ tm.tm_sec = atoi (val);
+ tm.tm_isdst = 0;
+
+ /* XXX */ /* We assume that mktime does not use tm_yday. */
+ tm.tm_yday = 0;
+
+ /* Make sure the date ends in a semicolon... */
+ token = next_token (&val, cfile);
+ if (token != SEMI) {
+ parse_warn ("semicolon expected.");
+ skip_to_semi (cfile);
+ return 0;
+ }
+
+ /* Guess the time value... */
+ guess = ((((((365 * (tm.tm_year - 70) + /* Days in years since '70 */
+ (tm.tm_year - 69) / 4 + /* Leap days since '70 */
+ (tm.tm_mon /* Days in months this year */
+ ? months [tm.tm_mon - 1]
+ : 0) +
+ (tm.tm_mon > 1 && /* Leap day this year */
+ !((tm.tm_year - 72) & 3)) +
+ tm.tm_mday - 1) * 24) + /* Day of month */
+ tm.tm_hour) * 60) +
+ tm.tm_min) * 60) + tm.tm_sec;
+
+ /* This guess could be wrong because of leap seconds or other
+ weirdness we don't know about that the system does. For
+ now, we're just going to accept the guess, but at some point
+ it might be nice to do a successive approximation here to
+ get an exact value. Even if the error is small, if the
+ server is restarted frequently (and thus the lease database
+ is reread), the error could accumulate into something
+ significant. */
+
+ return guess;
+}
diff --git a/usr.sbin/dhcpd/print.c b/usr.sbin/dhcpd/print.c
new file mode 100644
index 00000000000..4a80656ab47
--- /dev/null
+++ b/usr.sbin/dhcpd/print.c
@@ -0,0 +1,198 @@
+/* print.c
+
+ Turn data structures into printable text. */
+
+/*
+ * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include "dhcpd.h"
+
+char *print_hw_addr (htype, hlen, data)
+ int htype;
+ int hlen;
+ unsigned char *data;
+{
+ static char habuf [49];
+ char *s;
+ int i;
+
+ if (htype == 0 || hlen == 0) {
+ goto bad;
+ } else {
+ int slen = sizeof(habuf);
+ s = habuf;
+ for (i = 0; i < hlen; i++) {
+ int j;
+ j = snprintf (s, slen, "%02x", data [i]);
+ if (j <= 0)
+ goto bad;
+
+ s += strlen (s);
+ slen -= (strlen(s) + 1);
+ *s++ = ':';
+ }
+ *--s = 0;
+ }
+ return habuf;
+ bad:
+ strlcpy (habuf, "<null>", sizeof habuf);
+ return habuf;
+
+}
+
+void print_lease (lease)
+ struct lease *lease;
+{
+ struct tm *t;
+ char tbuf [32];
+
+ debug (" Lease %s",
+ piaddr (lease -> ip_addr));
+
+ t = gmtime (&lease -> starts);
+ strftime (tbuf, sizeof tbuf, "%Y/%m/%d %H:%M:%S", t);
+ debug (" start %s", tbuf);
+
+ t = gmtime (&lease -> ends);
+ strftime (tbuf, sizeof tbuf, "%Y/%m/%d %H:%M:%S", t);
+ debug (" end %s", tbuf);
+
+ t = gmtime (&lease -> timestamp);
+ strftime (tbuf, sizeof tbuf, "%Y/%m/%d %H:%M:%S", t);
+ debug (" stamp %s", tbuf);
+
+ debug (" hardware addr = %s",
+ print_hw_addr (lease -> hardware_addr.htype,
+ lease -> hardware_addr.hlen,
+ lease -> hardware_addr.haddr));
+ debug (" host %s ",
+ lease -> host ? lease -> host -> name : "<none>");
+}
+
+void dump_packet (tp)
+ struct packet *tp;
+{
+ struct dhcp_packet *tdp = tp -> raw;
+
+ debug ("packet length %d", tp -> packet_length);
+ debug ("op = %d htype = %d hlen = %d hops = %d",
+ tdp -> op, tdp -> htype, tdp -> hlen, tdp -> hops);
+ debug ("xid = %x secs = %d flags = %x",
+ tdp -> xid, tdp -> secs, tdp -> flags);
+ debug ("ciaddr = %s", inet_ntoa (tdp -> ciaddr));
+ debug ("yiaddr = %s", inet_ntoa (tdp -> yiaddr));
+ debug ("siaddr = %s", inet_ntoa (tdp -> siaddr));
+ debug ("giaddr = %s", inet_ntoa (tdp -> giaddr));
+ debug ("chaddr = %02x:%02x:%02x:%02x:%02x:%02x",
+ ((unsigned char *)(tdp -> chaddr)) [0],
+ ((unsigned char *)(tdp -> chaddr)) [1],
+ ((unsigned char *)(tdp -> chaddr)) [2],
+ ((unsigned char *)(tdp -> chaddr)) [3],
+ ((unsigned char *)(tdp -> chaddr)) [4],
+ ((unsigned char *)(tdp -> chaddr)) [5]);
+ debug ("filename = %s", tdp -> file);
+ debug ("server_name = %s", tdp -> sname);
+ if (tp -> options_valid) {
+ int i;
+
+ for (i = 0; i < 256; i++) {
+ if (tp -> options [i].data)
+ debug (" %s = %s",
+ dhcp_options [i].name,
+ pretty_print_option
+ (i, tp -> options [i].data,
+ tp -> options [i].len, 1, 1));
+ }
+ }
+ debug ("%s", "");
+}
+
+void dump_raw (buf, len)
+ unsigned char *buf;
+ int len;
+{
+ int i, j;
+ char lbuf [80];
+ int llen = sizeof(lbuf);
+ int lbix = 0;
+
+ lbuf [0] = 0;
+
+ for (i = 0; i < len; i++) {
+ if ((i & 15) == 0) {
+ if (lbix)
+ note (lbuf);
+ j = snprintf (lbuf, llen, "%03x:", i);
+ if (j >= llen)
+ return;
+ lbix+=j;
+ llen-=j;
+ } else if ((i & 7) == 0) {
+ lbuf [lbix++] = ' ';
+ len--;
+ }
+ j = snprintf (&lbuf [lbix], llen, " %02x", buf [i]);
+ if (j >= llen)
+ return;
+ lbix += j;
+ llen -= j;
+ }
+ note (lbuf);
+}
+
+void hash_dump (table)
+ struct hash_table *table;
+{
+ int i;
+ struct hash_bucket *bp;
+
+ if (!table)
+ return;
+
+ for (i = 0; i < table -> hash_count; i++) {
+ if (!table -> buckets [i])
+ continue;
+ note ("hash bucket %d:", i);
+ for (bp = table -> buckets [i]; bp; bp = bp -> next) {
+ if (bp -> len)
+ dump_raw (bp -> name, bp -> len);
+ else
+ note ((char *)bp -> name);
+ }
+ }
+}
diff --git a/usr.sbin/dhcpd/site.h b/usr.sbin/dhcpd/site.h
new file mode 100644
index 00000000000..30fdb703005
--- /dev/null
+++ b/usr.sbin/dhcpd/site.h
@@ -0,0 +1,100 @@
+/* Site-specific definitions.
+
+ For supported systems, you shouldn't need to make any changes here.
+ However, you may want to, in order to deal with site-specific
+ differences. */
+
+/* Add any site-specific definitions and inclusions here... */
+
+/* #include <site-foo-bar.h> */
+/* #define SITE_FOOBAR */
+
+/* Define this if you don't want dhcpd to run as a daemon and do want
+ to see all its output printed to stdout instead of being logged via
+ syslog(). This also makes dhcpd use the dhcpd.conf in its working
+ directory and write the dhcpd.leases file there. */
+
+/* #define DEBUG */
+
+/* Define this to see what the parser is parsing. You probably don't
+ want to see this. */
+
+/* #define DEBUG_TOKENS */
+
+/* Define this to see dumps of incoming and outgoing packets. This
+ slows things down quite a bit... */
+
+/* #define DEBUG_PACKET */
+
+/* Define this if you want to see dumps of tree evaluations. The most
+ common reason for doing this is to watch what happens with DNS name
+ lookups. */
+
+/* #define DEBUG_EVAL */
+
+/* Define this if you want the dhcpd.pid file to go somewhere other than
+ the default (which varies from system to system, but is usually either
+ /etc or /var/run. */
+
+/* #define _PATH_DHCPD_PID "/var/run/dhcpd.pid" */
+
+/* Define this if you want the dhcpd.leases file (the dynamic lease database)
+ to go somewhere other than the default location, which is normally
+ /etc/dhcpd.leases. */
+
+/* #define _PATH_DHCPD_DB "/etc/dhcpd.leases" */
+
+/* Define this if you want the dhcpd.conf file to go somewhere other than
+ the default location. By default, it goes in /etc/dhcpd.conf. */
+
+/* #define _PATH_DHCPD_CONF "/etc/dhcpd.conf" */
+
+/* Network API definitions. You do not need to choose one of these - if
+ you don't choose, one will be chosen for you in your system's config
+ header. DON'T MESS WITH THIS UNLESS YOU KNOW WHAT YOU'RE DOING!!! */
+
+/* Define this to use the standard BSD socket API.
+
+ On many systems, the BSD socket API does not provide the ability to
+ send packets to the 255.255.255.255 broadcast address, which can
+ prevent some clients (e.g., Win95) from seeing replies. This is
+ not a problem on Solaris.
+
+ In addition, the BSD socket API will not work when more than one
+ network interface is configured on the server.
+
+ However, the BSD socket API is about as efficient as you can get, so if
+ the aforementioned problems do not matter to you, or if no other
+ API is supported for your system, you may want to go with it. */
+
+/* #define USE_SOCKETS */
+
+/* Define this to use the Sun Streams NIT API.
+
+ The Sun Streams NIT API is only supported on SunOS 4.x releases. */
+
+/* #define USE_NIT */
+
+/* Define this to use the Berkeley Packet Filter API.
+
+ The BPF API is available on all 4.4-BSD derivatives, including
+ NetBSD, FreeBSD and BSDI's BSD/OS. It's also available on
+ DEC Alpha OSF/1 in a compatibility mode supported by the Alpha OSF/1
+ packetfilter interface. */
+
+/* #define USE_BPF */
+
+/* Define this to use the raw socket API.
+
+ The raw socket API is provided on many BSD derivatives, and provides
+ a way to send out raw IP packets. It is only supported for sending
+ packets - packets must be received with the regular socket API.
+ This code is experimental - I've never gotten it to actually transmit
+ a packet to the 255.255.255.255 broadcast address - so use it at your
+ own risk. */
+
+/* #define USE_RAW_SOCKETS */
+
+/* Define this to change the logging facility used by dhcpd. */
+
+/* #define DHCPD_LOG_FACILITY LOG_DAEMON */
diff --git a/usr.sbin/dhcpd/sysconf.h b/usr.sbin/dhcpd/sysconf.h
new file mode 100644
index 00000000000..5feb4c75c70
--- /dev/null
+++ b/usr.sbin/dhcpd/sysconf.h
@@ -0,0 +1,52 @@
+/* systat.h
+
+ Definitions for systat protocol... */
+
+/*
+ * Copyright (c) 1997 The Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#define SYSCONF_SOCKET "/var/run/sysconf"
+
+struct sysconf_header {
+ u_int32_t type; /* Type of status message... */
+ u_int32_t length; /* Length of message. */
+};
+
+/* Message types... */
+#define NETWORK_LOCATION_CHANGED 1
+
diff --git a/usr.sbin/dhcpd/tables.c b/usr.sbin/dhcpd/tables.c
new file mode 100644
index 00000000000..d336516c01c
--- /dev/null
+++ b/usr.sbin/dhcpd/tables.c
@@ -0,0 +1,687 @@
+/* tables.c
+
+ Tables of information... */
+
+/*
+ * Copyright (c) 1995, 1996 The Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include "dhcpd.h"
+
+/* DHCP Option names, formats and codes, from RFC1533.
+
+ Format codes:
+
+ e - end of data
+ I - IP address
+ l - 32-bit signed integer
+ L - 32-bit unsigned integer
+ s - 16-bit signed integer
+ S - 16-bit unsigned integer
+ b - 8-bit signed integer
+ B - 8-bit unsigned integer
+ t - ASCII text
+ f - flag (true or false)
+ A - array of whatever precedes (e.g., IA means array of IP addresses)
+*/
+
+struct universe dhcp_universe;
+struct option dhcp_options [256] = {
+ { "pad", "", &dhcp_universe, 0 },
+ { "subnet-mask", "I", &dhcp_universe, 1 },
+ { "time-offset", "l", &dhcp_universe, 2 },
+ { "routers", "IA", &dhcp_universe, 3 },
+ { "time-servers", "IA", &dhcp_universe, 4 },
+ { "ien116-name-servers", "IA", &dhcp_universe, 5 },
+ { "domain-name-servers", "IA", &dhcp_universe, 6 },
+ { "log-servers", "IA", &dhcp_universe, 7 },
+ { "cookie-servers", "IA", &dhcp_universe, 8 },
+ { "lpr-servers", "IA", &dhcp_universe, 9 },
+ { "impress-servers", "IA", &dhcp_universe, 10 },
+ { "resource-location-servers", "IA", &dhcp_universe, 11 },
+ { "host-name", "X", &dhcp_universe, 12 },
+ { "boot-size", "S", &dhcp_universe, 13 },
+ { "merit-dump", "t", &dhcp_universe, 14 },
+ { "domain-name", "t", &dhcp_universe, 15 },
+ { "swap-server", "I", &dhcp_universe, 16 },
+ { "root-path", "t", &dhcp_universe, 17 },
+ { "extensions-path", "t", &dhcp_universe, 18 },
+ { "ip-forwarding", "f", &dhcp_universe, 19 },
+ { "non-local-source-routing", "f", &dhcp_universe, 20 },
+ { "policy-filter", "IIA", &dhcp_universe, 21 },
+ { "max-dgram-reassembly", "S", &dhcp_universe, 22 },
+ { "default-ip-ttl", "B", &dhcp_universe, 23 },
+ { "path-mtu-aging-timeout", "L", &dhcp_universe, 24 },
+ { "path-mtu-plateau-table", "SA", &dhcp_universe, 25 },
+ { "interface-mtu", "S", &dhcp_universe, 26 },
+ { "all-subnets-local", "f", &dhcp_universe, 27 },
+ { "broadcast-address", "I", &dhcp_universe, 28 },
+ { "perform-mask-discovery", "f", &dhcp_universe, 29 },
+ { "mask-supplier", "f", &dhcp_universe, 30 },
+ { "router-discovery", "f", &dhcp_universe, 31 },
+ { "router-solicitation-address", "I", &dhcp_universe, 32 },
+ { "static-routes", "IIA", &dhcp_universe, 33 },
+ { "trailer-encapsulation", "f", &dhcp_universe, 34 },
+ { "arp-cache-timeout", "L", &dhcp_universe, 35 },
+ { "ieee802-3-encapsulation", "f", &dhcp_universe, 36 },
+ { "default-tcp-ttl", "B", &dhcp_universe, 37 },
+ { "tcp-keepalive-interval", "L", &dhcp_universe, 38 },
+ { "tcp-keepalive-garbage", "f", &dhcp_universe, 39 },
+ { "nis-domain", "t", &dhcp_universe, 40 },
+ { "nis-servers", "IA", &dhcp_universe, 41 },
+ { "ntp-servers", "IA", &dhcp_universe, 42 },
+ { "vendor-encapsulated-options", "X", &dhcp_universe, 43 },
+ { "netbios-name-servers", "IA", &dhcp_universe, 44 },
+ { "netbios-dd-server", "IA", &dhcp_universe, 45 },
+ { "netbios-node-type", "B", &dhcp_universe, 46 },
+ { "netbios-scope", "t", &dhcp_universe, 47 },
+ { "font-servers", "IA", &dhcp_universe, 48 },
+ { "x-display-manager", "IA", &dhcp_universe, 49 },
+ { "dhcp-requested-address", "I", &dhcp_universe, 50 },
+ { "dhcp-lease-time", "L", &dhcp_universe, 51 },
+ { "dhcp-option-overload", "B", &dhcp_universe, 52 },
+ { "dhcp-message-type", "B", &dhcp_universe, 53 },
+ { "dhcp-server-identifier", "I", &dhcp_universe, 54 },
+ { "dhcp-parameter-request-list", "BA", &dhcp_universe, 55 },
+ { "dhcp-message", "t", &dhcp_universe, 56 },
+ { "dhcp-max-message-size", "S", &dhcp_universe, 57 },
+ { "dhcp-renewal-time", "L", &dhcp_universe, 58 },
+ { "dhcp-rebinding-time", "L", &dhcp_universe, 59 },
+ { "dhcp-class-identifier", "t", &dhcp_universe, 60 },
+ { "dhcp-client-identifier", "X", &dhcp_universe, 61 },
+ { "option-62", "X", &dhcp_universe, 62 },
+ { "option-63", "X", &dhcp_universe, 63 },
+ { "nisplus-domain", "t", &dhcp_universe, 64 },
+ { "nisplus-servers", "IA", &dhcp_universe, 65 },
+ { "tftp-server-name", "t", &dhcp_universe, 66 },
+ { "bootfile-name", "t", &dhcp_universe, 67 },
+ { "mobile-ip-home-agent", "IA", &dhcp_universe, 68 },
+ { "smtp-server", "IA", &dhcp_universe, 69 },
+ { "pop-server", "IA", &dhcp_universe, 70 },
+ { "nntp-server", "IA", &dhcp_universe, 71 },
+ { "www-server", "IA", &dhcp_universe, 72 },
+ { "finger-server", "IA", &dhcp_universe, 73 },
+ { "irc-server", "IA", &dhcp_universe, 74 },
+ { "streettalk-server", "IA", &dhcp_universe, 75 },
+ { "streettalk-directory-assistance-server", "IA", &dhcp_universe, 76 },
+ { "user-class", "t", &dhcp_universe, 77 },
+ { "option-78", "X", &dhcp_universe, 78 },
+ { "option-79", "X", &dhcp_universe, 79 },
+ { "option-80", "X", &dhcp_universe, 80 },
+ { "option-81", "X", &dhcp_universe, 81 },
+ { "option-82", "X", &dhcp_universe, 82 },
+ { "option-83", "X", &dhcp_universe, 83 },
+ { "option-84", "X", &dhcp_universe, 84 },
+ { "nds-servers", "IA", &dhcp_universe, 85 },
+ { "nds-tree-name", "X", &dhcp_universe, 86 },
+ { "nds-context", "X", &dhcp_universe, 87 },
+ { "option-88", "X", &dhcp_universe, 88 },
+ { "option-89", "X", &dhcp_universe, 89 },
+ { "option-90", "X", &dhcp_universe, 90 },
+ { "option-91", "X", &dhcp_universe, 91 },
+ { "option-92", "X", &dhcp_universe, 92 },
+ { "option-93", "X", &dhcp_universe, 93 },
+ { "option-94", "X", &dhcp_universe, 94 },
+ { "option-95", "X", &dhcp_universe, 95 },
+ { "option-96", "X", &dhcp_universe, 96 },
+ { "option-97", "X", &dhcp_universe, 97 },
+ { "option-98", "X", &dhcp_universe, 98 },
+ { "option-99", "X", &dhcp_universe, 99 },
+ { "option-100", "X", &dhcp_universe, 100 },
+ { "option-101", "X", &dhcp_universe, 101 },
+ { "option-102", "X", &dhcp_universe, 102 },
+ { "option-103", "X", &dhcp_universe, 103 },
+ { "option-104", "X", &dhcp_universe, 104 },
+ { "option-105", "X", &dhcp_universe, 105 },
+ { "option-106", "X", &dhcp_universe, 106 },
+ { "option-107", "X", &dhcp_universe, 107 },
+ { "option-108", "X", &dhcp_universe, 108 },
+ { "option-109", "X", &dhcp_universe, 109 },
+ { "option-110", "X", &dhcp_universe, 110 },
+ { "option-111", "X", &dhcp_universe, 111 },
+ { "option-112", "X", &dhcp_universe, 112 },
+ { "option-113", "X", &dhcp_universe, 113 },
+ { "option-114", "X", &dhcp_universe, 114 },
+ { "option-115", "X", &dhcp_universe, 115 },
+ { "option-116", "X", &dhcp_universe, 116 },
+ { "option-117", "X", &dhcp_universe, 117 },
+ { "option-118", "X", &dhcp_universe, 118 },
+ { "option-119", "X", &dhcp_universe, 119 },
+ { "option-120", "X", &dhcp_universe, 120 },
+ { "option-121", "X", &dhcp_universe, 121 },
+ { "option-122", "X", &dhcp_universe, 122 },
+ { "option-123", "X", &dhcp_universe, 123 },
+ { "option-124", "X", &dhcp_universe, 124 },
+ { "option-125", "X", &dhcp_universe, 125 },
+ { "option-126", "X", &dhcp_universe, 126 },
+ { "option-127", "X", &dhcp_universe, 127 },
+ { "option-128", "X", &dhcp_universe, 128 },
+ { "option-129", "X", &dhcp_universe, 129 },
+ { "option-130", "X", &dhcp_universe, 130 },
+ { "option-131", "X", &dhcp_universe, 131 },
+ { "option-132", "X", &dhcp_universe, 132 },
+ { "option-133", "X", &dhcp_universe, 133 },
+ { "option-134", "X", &dhcp_universe, 134 },
+ { "option-135", "X", &dhcp_universe, 135 },
+ { "option-136", "X", &dhcp_universe, 136 },
+ { "option-137", "X", &dhcp_universe, 137 },
+ { "option-138", "X", &dhcp_universe, 138 },
+ { "option-139", "X", &dhcp_universe, 139 },
+ { "option-140", "X", &dhcp_universe, 140 },
+ { "option-141", "X", &dhcp_universe, 141 },
+ { "option-142", "X", &dhcp_universe, 142 },
+ { "option-143", "X", &dhcp_universe, 143 },
+ { "option-144", "X", &dhcp_universe, 144 },
+ { "option-145", "X", &dhcp_universe, 145 },
+ { "option-146", "X", &dhcp_universe, 146 },
+ { "option-147", "X", &dhcp_universe, 147 },
+ { "option-148", "X", &dhcp_universe, 148 },
+ { "option-149", "X", &dhcp_universe, 149 },
+ { "option-150", "X", &dhcp_universe, 150 },
+ { "option-151", "X", &dhcp_universe, 151 },
+ { "option-152", "X", &dhcp_universe, 152 },
+ { "option-153", "X", &dhcp_universe, 153 },
+ { "option-154", "X", &dhcp_universe, 154 },
+ { "option-155", "X", &dhcp_universe, 155 },
+ { "option-156", "X", &dhcp_universe, 156 },
+ { "option-157", "X", &dhcp_universe, 157 },
+ { "option-158", "X", &dhcp_universe, 158 },
+ { "option-159", "X", &dhcp_universe, 159 },
+ { "option-160", "X", &dhcp_universe, 160 },
+ { "option-161", "X", &dhcp_universe, 161 },
+ { "option-162", "X", &dhcp_universe, 162 },
+ { "option-163", "X", &dhcp_universe, 163 },
+ { "option-164", "X", &dhcp_universe, 164 },
+ { "option-165", "X", &dhcp_universe, 165 },
+ { "option-166", "X", &dhcp_universe, 166 },
+ { "option-167", "X", &dhcp_universe, 167 },
+ { "option-168", "X", &dhcp_universe, 168 },
+ { "option-169", "X", &dhcp_universe, 169 },
+ { "option-170", "X", &dhcp_universe, 170 },
+ { "option-171", "X", &dhcp_universe, 171 },
+ { "option-172", "X", &dhcp_universe, 172 },
+ { "option-173", "X", &dhcp_universe, 173 },
+ { "option-174", "X", &dhcp_universe, 174 },
+ { "option-175", "X", &dhcp_universe, 175 },
+ { "option-176", "X", &dhcp_universe, 176 },
+ { "option-177", "X", &dhcp_universe, 177 },
+ { "option-178", "X", &dhcp_universe, 178 },
+ { "option-179", "X", &dhcp_universe, 179 },
+ { "option-180", "X", &dhcp_universe, 180 },
+ { "option-181", "X", &dhcp_universe, 181 },
+ { "option-182", "X", &dhcp_universe, 182 },
+ { "option-183", "X", &dhcp_universe, 183 },
+ { "option-184", "X", &dhcp_universe, 184 },
+ { "option-185", "X", &dhcp_universe, 185 },
+ { "option-186", "X", &dhcp_universe, 186 },
+ { "option-187", "X", &dhcp_universe, 187 },
+ { "option-188", "X", &dhcp_universe, 188 },
+ { "option-189", "X", &dhcp_universe, 189 },
+ { "option-190", "X", &dhcp_universe, 190 },
+ { "option-191", "X", &dhcp_universe, 191 },
+ { "option-192", "X", &dhcp_universe, 192 },
+ { "option-193", "X", &dhcp_universe, 193 },
+ { "option-194", "X", &dhcp_universe, 194 },
+ { "option-195", "X", &dhcp_universe, 195 },
+ { "option-196", "X", &dhcp_universe, 196 },
+ { "option-197", "X", &dhcp_universe, 197 },
+ { "option-198", "X", &dhcp_universe, 198 },
+ { "option-199", "X", &dhcp_universe, 199 },
+ { "option-200", "X", &dhcp_universe, 200 },
+ { "option-201", "X", &dhcp_universe, 201 },
+ { "option-202", "X", &dhcp_universe, 202 },
+ { "option-203", "X", &dhcp_universe, 203 },
+ { "option-204", "X", &dhcp_universe, 204 },
+ { "option-205", "X", &dhcp_universe, 205 },
+ { "option-206", "X", &dhcp_universe, 206 },
+ { "option-207", "X", &dhcp_universe, 207 },
+ { "option-208", "X", &dhcp_universe, 208 },
+ { "option-209", "X", &dhcp_universe, 209 },
+ { "option-210", "X", &dhcp_universe, 210 },
+ { "option-211", "X", &dhcp_universe, 211 },
+ { "option-212", "X", &dhcp_universe, 212 },
+ { "option-213", "X", &dhcp_universe, 213 },
+ { "option-214", "X", &dhcp_universe, 214 },
+ { "option-215", "X", &dhcp_universe, 215 },
+ { "option-216", "X", &dhcp_universe, 216 },
+ { "option-217", "X", &dhcp_universe, 217 },
+ { "option-218", "X", &dhcp_universe, 218 },
+ { "option-219", "X", &dhcp_universe, 219 },
+ { "option-220", "X", &dhcp_universe, 220 },
+ { "option-221", "X", &dhcp_universe, 221 },
+ { "option-222", "X", &dhcp_universe, 222 },
+ { "option-223", "X", &dhcp_universe, 223 },
+ { "option-224", "X", &dhcp_universe, 224 },
+ { "option-225", "X", &dhcp_universe, 225 },
+ { "option-226", "X", &dhcp_universe, 226 },
+ { "option-227", "X", &dhcp_universe, 227 },
+ { "option-228", "X", &dhcp_universe, 228 },
+ { "option-229", "X", &dhcp_universe, 229 },
+ { "option-230", "X", &dhcp_universe, 230 },
+ { "option-231", "X", &dhcp_universe, 231 },
+ { "option-232", "X", &dhcp_universe, 232 },
+ { "option-233", "X", &dhcp_universe, 233 },
+ { "option-234", "X", &dhcp_universe, 234 },
+ { "option-235", "X", &dhcp_universe, 235 },
+ { "option-236", "X", &dhcp_universe, 236 },
+ { "option-237", "X", &dhcp_universe, 237 },
+ { "option-238", "X", &dhcp_universe, 238 },
+ { "option-239", "X", &dhcp_universe, 239 },
+ { "option-240", "X", &dhcp_universe, 240 },
+ { "option-241", "X", &dhcp_universe, 241 },
+ { "option-242", "X", &dhcp_universe, 242 },
+ { "option-243", "X", &dhcp_universe, 243 },
+ { "option-244", "X", &dhcp_universe, 244 },
+ { "option-245", "X", &dhcp_universe, 245 },
+ { "option-246", "X", &dhcp_universe, 246 },
+ { "option-247", "X", &dhcp_universe, 247 },
+ { "option-248", "X", &dhcp_universe, 248 },
+ { "option-249", "X", &dhcp_universe, 249 },
+ { "option-250", "X", &dhcp_universe, 250 },
+ { "option-251", "X", &dhcp_universe, 251 },
+ { "option-252", "X", &dhcp_universe, 252 },
+ { "option-253", "X", &dhcp_universe, 253 },
+ { "option-254", "X", &dhcp_universe, 254 },
+ { "option-end", "e", &dhcp_universe, 255 },
+};
+
+/* Default dhcp option priority list (this is ad hoc and should not be
+ mistaken for a carefully crafted and optimized list). */
+unsigned char dhcp_option_default_priority_list [] = {
+ DHO_DHCP_REQUESTED_ADDRESS,
+ DHO_DHCP_OPTION_OVERLOAD,
+ DHO_DHCP_MAX_MESSAGE_SIZE,
+ DHO_DHCP_RENEWAL_TIME,
+ DHO_DHCP_REBINDING_TIME,
+ DHO_DHCP_CLASS_IDENTIFIER,
+ DHO_DHCP_CLIENT_IDENTIFIER,
+ DHO_SUBNET_MASK,
+ DHO_TIME_OFFSET,
+ DHO_ROUTERS,
+ DHO_TIME_SERVERS,
+ DHO_NAME_SERVERS,
+ DHO_DOMAIN_NAME_SERVERS,
+ DHO_HOST_NAME,
+ DHO_LOG_SERVERS,
+ DHO_COOKIE_SERVERS,
+ DHO_LPR_SERVERS,
+ DHO_IMPRESS_SERVERS,
+ DHO_RESOURCE_LOCATION_SERVERS,
+ DHO_HOST_NAME,
+ DHO_BOOT_SIZE,
+ DHO_MERIT_DUMP,
+ DHO_DOMAIN_NAME,
+ DHO_SWAP_SERVER,
+ DHO_ROOT_PATH,
+ DHO_EXTENSIONS_PATH,
+ DHO_IP_FORWARDING,
+ DHO_NON_LOCAL_SOURCE_ROUTING,
+ DHO_POLICY_FILTER,
+ DHO_MAX_DGRAM_REASSEMBLY,
+ DHO_DEFAULT_IP_TTL,
+ DHO_PATH_MTU_AGING_TIMEOUT,
+ DHO_PATH_MTU_PLATEAU_TABLE,
+ DHO_INTERFACE_MTU,
+ DHO_ALL_SUBNETS_LOCAL,
+ DHO_BROADCAST_ADDRESS,
+ DHO_PERFORM_MASK_DISCOVERY,
+ DHO_MASK_SUPPLIER,
+ DHO_ROUTER_DISCOVERY,
+ DHO_ROUTER_SOLICITATION_ADDRESS,
+ DHO_STATIC_ROUTES,
+ DHO_TRAILER_ENCAPSULATION,
+ DHO_ARP_CACHE_TIMEOUT,
+ DHO_IEEE802_3_ENCAPSULATION,
+ DHO_DEFAULT_TCP_TTL,
+ DHO_TCP_KEEPALIVE_INTERVAL,
+ DHO_TCP_KEEPALIVE_GARBAGE,
+ DHO_NIS_DOMAIN,
+ DHO_NIS_SERVERS,
+ DHO_NTP_SERVERS,
+ DHO_VENDOR_ENCAPSULATED_OPTIONS,
+ DHO_NETBIOS_NAME_SERVERS,
+ DHO_NETBIOS_DD_SERVER,
+ DHO_NETBIOS_NODE_TYPE,
+ DHO_NETBIOS_SCOPE,
+ DHO_FONT_SERVERS,
+ DHO_X_DISPLAY_MANAGER,
+ DHO_DHCP_PARAMETER_REQUEST_LIST,
+
+ /* Presently-undefined options... */
+ 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
+ 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92,
+ 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106,
+ 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118,
+ 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130,
+ 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142,
+ 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
+ 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166,
+ 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178,
+ 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190,
+ 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202,
+ 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214,
+ 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226,
+ 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238,
+ 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250,
+ 251, 252, 253, 254,
+};
+
+int sizeof_dhcp_option_default_priority_list =
+ sizeof dhcp_option_default_priority_list;
+
+
+char *hardware_types [] = {
+ "unknown-0",
+ "ethernet",
+ "unknown-2",
+ "unknown-3",
+ "unknown-4",
+ "unknown-5",
+ "token-ring",
+ "unknown-7",
+ "fddi",
+ "unknown-9",
+ "unknown-10",
+ "unknown-11",
+ "unknown-12",
+ "unknown-13",
+ "unknown-14",
+ "unknown-15",
+ "unknown-16",
+ "unknown-17",
+ "unknown-18",
+ "unknown-19",
+ "unknown-20",
+ "unknown-21",
+ "unknown-22",
+ "unknown-23",
+ "unknown-24",
+ "unknown-25",
+ "unknown-26",
+ "unknown-27",
+ "unknown-28",
+ "unknown-29",
+ "unknown-30",
+ "unknown-31",
+ "unknown-32",
+ "unknown-33",
+ "unknown-34",
+ "unknown-35",
+ "unknown-36",
+ "unknown-37",
+ "unknown-38",
+ "unknown-39",
+ "unknown-40",
+ "unknown-41",
+ "unknown-42",
+ "unknown-43",
+ "unknown-44",
+ "unknown-45",
+ "unknown-46",
+ "unknown-47",
+ "unknown-48",
+ "unknown-49",
+ "unknown-50",
+ "unknown-51",
+ "unknown-52",
+ "unknown-53",
+ "unknown-54",
+ "unknown-55",
+ "unknown-56",
+ "unknown-57",
+ "unknown-58",
+ "unknown-59",
+ "unknown-60",
+ "unknown-61",
+ "unknown-62",
+ "unknown-63",
+ "unknown-64",
+ "unknown-65",
+ "unknown-66",
+ "unknown-67",
+ "unknown-68",
+ "unknown-69",
+ "unknown-70",
+ "unknown-71",
+ "unknown-72",
+ "unknown-73",
+ "unknown-74",
+ "unknown-75",
+ "unknown-76",
+ "unknown-77",
+ "unknown-78",
+ "unknown-79",
+ "unknown-80",
+ "unknown-81",
+ "unknown-82",
+ "unknown-83",
+ "unknown-84",
+ "unknown-85",
+ "unknown-86",
+ "unknown-87",
+ "unknown-88",
+ "unknown-89",
+ "unknown-90",
+ "unknown-91",
+ "unknown-92",
+ "unknown-93",
+ "unknown-94",
+ "unknown-95",
+ "unknown-96",
+ "unknown-97",
+ "unknown-98",
+ "unknown-99",
+ "unknown-100",
+ "unknown-101",
+ "unknown-102",
+ "unknown-103",
+ "unknown-104",
+ "unknown-105",
+ "unknown-106",
+ "unknown-107",
+ "unknown-108",
+ "unknown-109",
+ "unknown-110",
+ "unknown-111",
+ "unknown-112",
+ "unknown-113",
+ "unknown-114",
+ "unknown-115",
+ "unknown-116",
+ "unknown-117",
+ "unknown-118",
+ "unknown-119",
+ "unknown-120",
+ "unknown-121",
+ "unknown-122",
+ "unknown-123",
+ "unknown-124",
+ "unknown-125",
+ "unknown-126",
+ "unknown-127",
+ "unknown-128",
+ "unknown-129",
+ "unknown-130",
+ "unknown-131",
+ "unknown-132",
+ "unknown-133",
+ "unknown-134",
+ "unknown-135",
+ "unknown-136",
+ "unknown-137",
+ "unknown-138",
+ "unknown-139",
+ "unknown-140",
+ "unknown-141",
+ "unknown-142",
+ "unknown-143",
+ "unknown-144",
+ "unknown-145",
+ "unknown-146",
+ "unknown-147",
+ "unknown-148",
+ "unknown-149",
+ "unknown-150",
+ "unknown-151",
+ "unknown-152",
+ "unknown-153",
+ "unknown-154",
+ "unknown-155",
+ "unknown-156",
+ "unknown-157",
+ "unknown-158",
+ "unknown-159",
+ "unknown-160",
+ "unknown-161",
+ "unknown-162",
+ "unknown-163",
+ "unknown-164",
+ "unknown-165",
+ "unknown-166",
+ "unknown-167",
+ "unknown-168",
+ "unknown-169",
+ "unknown-170",
+ "unknown-171",
+ "unknown-172",
+ "unknown-173",
+ "unknown-174",
+ "unknown-175",
+ "unknown-176",
+ "unknown-177",
+ "unknown-178",
+ "unknown-179",
+ "unknown-180",
+ "unknown-181",
+ "unknown-182",
+ "unknown-183",
+ "unknown-184",
+ "unknown-185",
+ "unknown-186",
+ "unknown-187",
+ "unknown-188",
+ "unknown-189",
+ "unknown-190",
+ "unknown-191",
+ "unknown-192",
+ "unknown-193",
+ "unknown-194",
+ "unknown-195",
+ "unknown-196",
+ "unknown-197",
+ "unknown-198",
+ "unknown-199",
+ "unknown-200",
+ "unknown-201",
+ "unknown-202",
+ "unknown-203",
+ "unknown-204",
+ "unknown-205",
+ "unknown-206",
+ "unknown-207",
+ "unknown-208",
+ "unknown-209",
+ "unknown-210",
+ "unknown-211",
+ "unknown-212",
+ "unknown-213",
+ "unknown-214",
+ "unknown-215",
+ "unknown-216",
+ "unknown-217",
+ "unknown-218",
+ "unknown-219",
+ "unknown-220",
+ "unknown-221",
+ "unknown-222",
+ "unknown-223",
+ "unknown-224",
+ "unknown-225",
+ "unknown-226",
+ "unknown-227",
+ "unknown-228",
+ "unknown-229",
+ "unknown-230",
+ "unknown-231",
+ "unknown-232",
+ "unknown-233",
+ "unknown-234",
+ "unknown-235",
+ "unknown-236",
+ "unknown-237",
+ "unknown-238",
+ "unknown-239",
+ "unknown-240",
+ "unknown-241",
+ "unknown-242",
+ "unknown-243",
+ "unknown-244",
+ "unknown-245",
+ "unknown-246",
+ "unknown-247",
+ "unknown-248",
+ "unknown-249",
+ "unknown-250",
+ "unknown-251",
+ "unknown-252",
+ "unknown-253",
+ "unknown-254",
+ "unknown-255" };
+
+
+
+struct hash_table universe_hash;
+
+void initialize_universes()
+{
+ int i;
+
+ dhcp_universe.name = "dhcp";
+ dhcp_universe.hash = new_hash ();
+ if (!dhcp_universe.hash)
+ error ("Can't allocate dhcp option hash table.");
+ for (i = 0; i < 256; i++) {
+ dhcp_universe.options [i] = &dhcp_options [i];
+ add_hash (dhcp_universe.hash,
+ (unsigned char *)dhcp_options [i].name, 0,
+ (unsigned char *)&dhcp_options [i]);
+ }
+ universe_hash.hash_count = DEFAULT_HASH_SIZE;
+ add_hash (&universe_hash,
+ (unsigned char *)dhcp_universe.name, 0,
+ (unsigned char *)&dhcp_universe);
+}
diff --git a/usr.sbin/dhcpd/tree.c b/usr.sbin/dhcpd/tree.c
new file mode 100644
index 00000000000..e8912ae63fe
--- /dev/null
+++ b/usr.sbin/dhcpd/tree.c
@@ -0,0 +1,402 @@
+/* tree.c
+
+ Routines for manipulating parse trees... */
+
+/*
+ * Copyright (c) 1995, 1996, 1997 The Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include "dhcpd.h"
+
+static TIME tree_evaluate_recurse PROTO ((int *, unsigned char **, int *,
+ struct tree *));
+static TIME do_host_lookup PROTO ((int *, unsigned char **, int *,
+ struct dns_host_entry *));
+static void do_data_copy PROTO ((int *, unsigned char **, int *,
+ unsigned char *, int));
+
+pair cons (car, cdr)
+ caddr_t car;
+ pair cdr;
+{
+ pair foo = (pair)dmalloc (sizeof *foo, "cons");
+ if (!foo)
+ error ("no memory for cons.");
+ foo -> car = car;
+ foo -> cdr = cdr;
+ return foo;
+}
+
+struct tree_cache *tree_cache (tree)
+ struct tree *tree;
+{
+ struct tree_cache *tc;
+
+ tc = new_tree_cache ("tree_cache");
+ if (!tc)
+ return 0;
+ tc -> value = (unsigned char *)0;
+ tc -> len = tc -> buf_size = 0;
+ tc -> timeout = 0;
+ tc -> tree = tree;
+ return tc;
+}
+
+struct tree *tree_host_lookup (name)
+ char *name;
+{
+ struct tree *nt;
+ nt = new_tree ("tree_host_lookup");
+ if (!nt)
+ error ("No memory for host lookup tree node.");
+ nt -> op = TREE_HOST_LOOKUP;
+ nt -> data.host_lookup.host = enter_dns_host (name);
+ return nt;
+}
+
+struct dns_host_entry *enter_dns_host (name)
+ char *name;
+{
+ struct dns_host_entry *dh;
+ int len = strlen (name) + 1;
+
+ if (!(dh = (struct dns_host_entry *)dmalloc
+ (sizeof (struct dns_host_entry), "enter_dns_host"))
+ || !(dh -> hostname = dmalloc (len, "enter_dns_host")))
+ error ("Can't allocate space for new host.");
+ strlcpy (dh -> hostname, name, len);
+ dh -> data = (unsigned char *)0;
+ dh -> data_len = 0;
+ dh -> buf_len = 0;
+ dh -> timeout = 0;
+ return dh;
+}
+
+struct tree *tree_const (data, len)
+ unsigned char *data;
+ int len;
+{
+ struct tree *nt;
+ if (!(nt = new_tree ("tree_const"))
+ || !(nt -> data.const_val.data =
+ (unsigned char *)dmalloc (len, "tree_const")))
+ error ("No memory for constant data tree node.");
+ nt -> op = TREE_CONST;
+ memcpy (nt -> data.const_val.data, data, len);
+ nt -> data.const_val.len = len;
+ return nt;
+}
+
+struct tree *tree_concat (left, right)
+ struct tree *left, *right;
+{
+ struct tree *nt;
+
+ /* If we're concatenating a null tree to a non-null tree, just
+ return the non-null tree; if both trees are null, return
+ a null tree. */
+ if (!left)
+ return right;
+ if (!right)
+ return left;
+
+ /* If both trees are constant, combine them. */
+ if (left -> op == TREE_CONST && right -> op == TREE_CONST) {
+ unsigned char *buf = dmalloc (left -> data.const_val.len
+ + right -> data.const_val.len,
+ "tree_concat");
+ if (!buf)
+ error ("No memory to concatenate constants.");
+ memcpy (buf, left -> data.const_val.data,
+ left -> data.const_val.len);
+ memcpy (buf + left -> data.const_val.len,
+ right -> data.const_val.data,
+ right -> data.const_val.len);
+ dfree (left -> data.const_val.data, "tree_concat");
+ dfree (right -> data.const_val.data, "tree_concat");
+ left -> data.const_val.data = buf;
+ left -> data.const_val.len += right -> data.const_val.len;
+ free_tree (right, "tree_concat");
+ return left;
+ }
+
+ /* Otherwise, allocate a new node to concatenate the two. */
+ if (!(nt = new_tree ("tree_concat")))
+ error ("No memory for data tree concatenation node.");
+ nt -> op = TREE_CONCAT;
+ nt -> data.concat.left = left;
+ nt -> data.concat.right = right;
+ return nt;
+}
+
+struct tree *tree_limit (tree, limit)
+ struct tree *tree;
+ int limit;
+{
+ struct tree *rv;
+
+ /* If the tree we're limiting is constant, limit it now. */
+ if (tree -> op == TREE_CONST) {
+ if (tree -> data.const_val.len > limit)
+ tree -> data.const_val.len = limit;
+ return tree;
+ }
+
+ /* Otherwise, put in a node which enforces the limit on evaluation. */
+ rv = new_tree ("tree_limit");
+ if (!rv)
+ return (struct tree *)0;
+ rv -> op = TREE_LIMIT;
+ rv -> data.limit.tree = tree;
+ rv -> data.limit.limit = limit;
+ return rv;
+}
+
+int tree_evaluate (tree_cache)
+ struct tree_cache *tree_cache;
+{
+ unsigned char *bp = tree_cache -> value;
+ int bc = tree_cache -> buf_size;
+ int bufix = 0;
+
+ /* If there's no tree associated with this cache, it evaluates
+ to a constant and that was detected at startup. */
+ if (!tree_cache -> tree)
+ return 1;
+
+ /* Try to evaluate the tree without allocating more memory... */
+ tree_cache -> timeout = tree_evaluate_recurse (&bufix, &bp, &bc,
+ tree_cache -> tree);
+
+ /* No additional allocation needed? */
+ if (bufix <= bc) {
+ tree_cache -> len = bufix;
+ return 1;
+ }
+
+ /* If we can't allocate more memory, return with what we
+ have (maybe nothing). */
+ if (!(bp = (unsigned char *)dmalloc (bufix, "tree_evaluate")))
+ return 0;
+
+ /* Record the change in conditions... */
+ bc = bufix;
+ bufix = 0;
+
+ /* Note that the size of the result shouldn't change on the
+ second call to tree_evaluate_recurse, since we haven't
+ changed the ``current'' time. */
+ tree_evaluate_recurse (&bufix, &bp, &bc, tree_cache -> tree);
+
+ /* Free the old buffer if needed, then store the new buffer
+ location and size and return. */
+ if (tree_cache -> value)
+ dfree (tree_cache -> value, "tree_evaluate");
+ tree_cache -> value = bp;
+ tree_cache -> len = bufix;
+ tree_cache -> buf_size = bc;
+ return 1;
+}
+
+static TIME tree_evaluate_recurse (bufix, bufp, bufcount, tree)
+ int *bufix;
+ unsigned char **bufp;
+ int *bufcount;
+ struct tree *tree;
+{
+ int limit;
+ TIME t1, t2;
+
+ switch (tree -> op) {
+ case TREE_CONCAT:
+ t1 = tree_evaluate_recurse (bufix, bufp, bufcount,
+ tree -> data.concat.left);
+ t2 = tree_evaluate_recurse (bufix, bufp, bufcount,
+ tree -> data.concat.right);
+ if (t1 > t2)
+ return t2;
+ return t1;
+
+ case TREE_HOST_LOOKUP:
+ return do_host_lookup (bufix, bufp, bufcount,
+ tree -> data.host_lookup.host);
+
+ case TREE_CONST:
+ do_data_copy (bufix, bufp, bufcount,
+ tree -> data.const_val.data,
+ tree -> data.const_val.len);
+ t1 = MAX_TIME;
+ return t1;
+
+ case TREE_LIMIT:
+ limit = *bufix + tree -> data.limit.limit;
+ t1 = tree_evaluate_recurse (bufix, bufp, bufcount,
+ tree -> data.limit.tree);
+ *bufix = limit;
+ return t1;
+
+ default:
+ warn ("Bad node id in tree: %d.", tree -> op);
+ t1 = MAX_TIME;
+ return t1;
+ }
+}
+
+static TIME do_host_lookup (bufix, bufp, bufcount, dns)
+ int *bufix;
+ unsigned char **bufp;
+ int *bufcount;
+ struct dns_host_entry *dns;
+{
+ struct hostent *h;
+ int i;
+ int new_len;
+
+#ifdef DEBUG_EVAL
+ debug ("time: now = %d dns = %d %d diff = %d",
+ cur_time, dns -> timeout, cur_time - dns -> timeout);
+#endif
+
+ /* If the record hasn't timed out, just copy the data and return. */
+ if (cur_time <= dns -> timeout) {
+#ifdef DEBUG_EVAL
+ debug ("easy copy: %x %d %x",
+ dns -> data, dns -> data_len,
+ dns -> data ? *(int *)(dns -> data) : 0);
+#endif
+ do_data_copy (bufix, bufp, bufcount,
+ dns -> data, dns -> data_len);
+ return dns -> timeout;
+ }
+#ifdef DEBUG_EVAL
+ debug ("Looking up %s", dns -> hostname);
+#endif
+
+ /* Otherwise, look it up... */
+ h = gethostbyname (dns -> hostname);
+ if (h == NULL) {
+ switch (h_errno) {
+ case HOST_NOT_FOUND:
+ warn ("%s: host unknown.", dns -> hostname);
+ break;
+ case TRY_AGAIN:
+ warn ("%s: temporary name server failure",
+ dns -> hostname);
+ break;
+ case NO_RECOVERY:
+ warn ("%s: name server failed", dns -> hostname);
+ break;
+ case NO_DATA:
+ warn ("%s: no A record associated with address",
+ dns -> hostname);
+ }
+ /* Okay to try again after a minute. */
+ return cur_time + 60;
+ }
+
+#ifdef DEBUG_EVAL
+ debug ("Lookup succeeded; first address is %x",
+ h -> h_addr_list [0]);
+#endif
+
+ /* Count the number of addresses we got... */
+ for (i = 0; h -> h_addr_list [i]; i++)
+ ;
+
+ /* Do we need to allocate more memory? */
+ new_len = i * h -> h_length;
+ if (dns -> buf_len < i) {
+ unsigned char *buf =
+ (unsigned char *)dmalloc (new_len, "do_host_lookup");
+ /* If we didn't get more memory, use what we have. */
+ if (!buf) {
+ new_len = dns -> buf_len;
+ if (!dns -> buf_len) {
+ dns -> timeout = cur_time + 60;
+ return dns -> timeout;
+ }
+ } else {
+ if (dns -> data)
+ dfree (dns -> data, "do_host_lookup");
+ dns -> data = buf;
+ dns -> buf_len = new_len;
+ }
+ }
+
+ /* Addresses are conveniently stored one to the buffer, so we
+ have to copy them out one at a time... :'( */
+ for (i = 0; i < new_len / h -> h_length; i++) {
+ memcpy (dns -> data + h -> h_length * i,
+ h -> h_addr_list [i], h -> h_length);
+ }
+#ifdef DEBUG_EVAL
+ debug ("dns -> data: %x h -> h_addr_list [0]: %x",
+ *(int *)(dns -> data), h -> h_addr_list [0]);
+#endif
+ dns -> data_len = new_len;
+
+ /* Set the timeout for an hour from now.
+ XXX This should really use the time on the DNS reply. */
+ dns -> timeout = cur_time + 3600;
+
+#ifdef DEBUG_EVAL
+ debug ("hard copy: %x %d %x",
+ dns -> data, dns -> data_len, *(int *)(dns -> data));
+#endif
+ do_data_copy (bufix, bufp, bufcount, dns -> data, dns -> data_len);
+ return dns -> timeout;
+}
+
+static void do_data_copy (bufix, bufp, bufcount, data, len)
+ int *bufix;
+ unsigned char **bufp;
+ int *bufcount;
+ unsigned char *data;
+ int len;
+{
+ int space = *bufcount - *bufix;
+
+ /* If there's more space than we need, use only what we need. */
+ if (space > len)
+ space = len;
+
+ /* Copy as much data as will fit, then increment the buffer index
+ by the amount we actually had to copy, which could be more. */
+ if (space > 0)
+ memcpy (*bufp + *bufix, data, space);
+ *bufix += len;
+}
diff --git a/usr.sbin/dhcpd/tree.h b/usr.sbin/dhcpd/tree.h
new file mode 100644
index 00000000000..c2df41f52b1
--- /dev/null
+++ b/usr.sbin/dhcpd/tree.h
@@ -0,0 +1,107 @@
+/* tree.h
+
+ Definitions for address trees... */
+
+/*
+ * Copyright (c) 1995 The Internet Software Consortium. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+/* A pair of pointers, suitable for making a linked list. */
+typedef struct _pair {
+ caddr_t car;
+ struct _pair *cdr;
+} *pair;
+
+/* Tree node types... */
+#define TREE_CONCAT 1
+#define TREE_HOST_LOOKUP 2
+#define TREE_CONST 3
+#define TREE_LIMIT 4
+
+/* Tree structure for deferred evaluation of changing values. */
+struct tree {
+ int op;
+ union {
+ struct concat {
+ struct tree *left;
+ struct tree *right;
+ } concat;
+ struct host_lookup {
+ struct dns_host_entry *host;
+ } host_lookup;
+ struct const_val {
+ unsigned char *data;
+ int len;
+ } const_val;
+ struct limit {
+ struct tree *tree;
+ int limit;
+ } limit;
+ } data;
+};
+
+/* DNS host entry structure... */
+struct dns_host_entry {
+ char *hostname;
+ unsigned char *data;
+ int data_len;
+ int buf_len;
+ TIME timeout;
+};
+
+struct tree_cache {
+ unsigned char *value;
+ int len;
+ int buf_size;
+ TIME timeout;
+ struct tree *tree;
+ int flags;
+#define TC_AWAITING_RESOLUTION 1
+#define TC_TEMPORARY 2
+};
+
+struct universe {
+ char *name;
+ struct hash_table *hash;
+ struct option *options [256];
+};
+
+struct option {
+ char *name;
+ char *format;
+ struct universe *universe;
+ unsigned char code;
+};
diff --git a/usr.sbin/dhcpd/version.h b/usr.sbin/dhcpd/version.h
new file mode 100644
index 00000000000..2897e2e2a72
--- /dev/null
+++ b/usr.sbin/dhcpd/version.h
@@ -0,0 +1,3 @@
+/* Current version of ISC DHCP Distribution. */
+
+#define DHCP_VERSION "2.0pl5-OpenBSD"