diff options
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 (<, 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, <, !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, <, 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, <, 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" |