From 47c0859616bfefdd59ce6bbd8dd46882cef4527a Mon Sep 17 00:00:00 2001 From: Brooks Davis Date: Tue, 7 Jun 2005 04:05:09 +0000 Subject: [PATCH] Import the OpenBSD dhclient as shipped with OpenBSD-3.7 (the tag OPENBSD_3_7). --- sbin/dhclient/Makefile | 58 +- sbin/dhclient/alloc.c | 76 + sbin/dhclient/bpf.c | 374 +++++ sbin/dhclient/clparse.c | 938 ++++++++++++ sbin/dhclient/conflex.c | 525 +++++++ sbin/dhclient/convert.c | 114 ++ sbin/dhclient/dhclient-script | 227 +++ sbin/dhclient/dhclient-script.8 | 246 ++++ sbin/dhclient/dhclient.8 | 181 +++ sbin/dhclient/dhclient.c | 2373 +++++++++++++++++++++++++++++++ sbin/dhclient/dhclient.conf | 36 + sbin/dhclient/dhclient.conf.5 | 541 +++++++ sbin/dhclient/dhclient.leases.5 | 87 ++ sbin/dhclient/dhcp-options.5 | 590 ++++++++ sbin/dhclient/dhcp.h | 168 +++ sbin/dhclient/dhcpd.h | 437 ++++++ sbin/dhclient/dhctoken.h | 136 ++ sbin/dhclient/dispatch.c | 495 +++++++ sbin/dhclient/errwarn.c | 234 +++ sbin/dhclient/hash.c | 119 ++ sbin/dhclient/inet.c | 118 ++ sbin/dhclient/options.c | 717 ++++++++++ sbin/dhclient/packet.c | 253 ++++ sbin/dhclient/parse.c | 577 ++++++++ sbin/dhclient/privsep.c | 235 +++ sbin/dhclient/privsep.h | 47 + sbin/dhclient/tables.c | 430 ++++++ sbin/dhclient/tree.c | 56 + sbin/dhclient/tree.h | 66 + 29 files changed, 10434 insertions(+), 20 deletions(-) create mode 100644 sbin/dhclient/alloc.c create mode 100644 sbin/dhclient/bpf.c create mode 100644 sbin/dhclient/clparse.c create mode 100644 sbin/dhclient/conflex.c create mode 100644 sbin/dhclient/convert.c create mode 100644 sbin/dhclient/dhclient-script create mode 100644 sbin/dhclient/dhclient-script.8 create mode 100644 sbin/dhclient/dhclient.8 create mode 100644 sbin/dhclient/dhclient.c create mode 100644 sbin/dhclient/dhclient.conf create mode 100644 sbin/dhclient/dhclient.conf.5 create mode 100644 sbin/dhclient/dhclient.leases.5 create mode 100644 sbin/dhclient/dhcp-options.5 create mode 100644 sbin/dhclient/dhcp.h create mode 100644 sbin/dhclient/dhcpd.h create mode 100644 sbin/dhclient/dhctoken.h create mode 100644 sbin/dhclient/dispatch.c create mode 100644 sbin/dhclient/errwarn.c create mode 100644 sbin/dhclient/hash.c create mode 100644 sbin/dhclient/inet.c create mode 100644 sbin/dhclient/options.c create mode 100644 sbin/dhclient/packet.c create mode 100644 sbin/dhclient/parse.c create mode 100644 sbin/dhclient/privsep.c create mode 100644 sbin/dhclient/privsep.h create mode 100644 sbin/dhclient/tables.c create mode 100644 sbin/dhclient/tree.c create mode 100644 sbin/dhclient/tree.h diff --git a/sbin/dhclient/Makefile b/sbin/dhclient/Makefile index 3148ea8f0bc5..594c9aff1b4e 100644 --- a/sbin/dhclient/Makefile +++ b/sbin/dhclient/Makefile @@ -1,30 +1,48 @@ -# ex:ts=8 +# $OpenBSD: Makefile,v 1.9 2004/05/04 12:52:05 henning Exp $ # -# @(#)Makefile 1.0 (obrien) 2/6/99 -# $Id$ +# Copyright (c) 1996, 1997 The Internet Software Consortium. +# All rights reserved. # -# Copyright (c) 1999 by David O'Brien -# This file is under a "FreeBSD" copyright. See /usr/src/sys/sys/copyright.h -# for the terms of the copyright. +# 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. # -.PATH: ${.CURDIR}/../../contrib/isc-dhcp/client ${.CURDIR}/../../contrib/isc-dhcp/common +.include -PROG= dhclient -MAN5= dhclient.conf.5 dhclient.leases.5 dhcp-options.5 -MAN8= dhclient.8 dhclient-script.8 +SRCS= dhclient.c clparse.c alloc.c dispatch.c hash.c bpf.c options.c \ + tree.c conflex.c errwarn.c inet.c packet.c convert.c tables.c \ + parse.c privsep.c -SRCS= dhclient.c clparse.c -SRCS+= raw.c parse.c nit.c icmp.c dispatch.c conflex.c upf.c bpf.c \ - socket.c packet.c memory.c print.c options.c inet.c convert.c \ - tree.c tables.c hash.c alloc.c errwarn.c inet_addr.c +PROG= dhclient +MAN= dhclient.8 dhclient.conf.5 dhclient.leases.5 dhclient-script.8 -CFLAGS+= -I${.CURDIR}/../../contrib/isc-dhcp/includes \ - -I${.CURDIR}/../../contrib/isc-dhcp +CFLAGS+=-Wall -afterinstall: - ${INSTALL} ${COPY} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ - ${.CURDIR}/../../contrib/isc-dhcp/client/scripts/freebsd \ - ${BINDIR}/dhclient-script +beforeinstall: + ${INSTALL} ${INSTALL_COPY} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ + ${.CURDIR}/dhclient-script ${DESTDIR}/sbin/dhclient-script .include diff --git a/sbin/dhclient/alloc.c b/sbin/dhclient/alloc.c new file mode 100644 index 000000000000..cabb76d42989 --- /dev/null +++ b/sbin/dhclient/alloc.c @@ -0,0 +1,76 @@ +/* $OpenBSD: alloc.c,v 1.9 2004/05/04 20:28:40 deraadt Exp $ */ + +/* 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 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 string_list * +new_string_list(size_t size) +{ + struct string_list *rval; + + rval = calloc(1, sizeof(struct string_list) + size); + if (rval != NULL) + rval->string = ((char *)rval) + sizeof(struct string_list); + return (rval); +} + +struct hash_table * +new_hash_table(int count) +{ + struct hash_table *rval; + + rval = calloc(1, sizeof(struct hash_table) - + (DEFAULT_HASH_SIZE * sizeof(struct hash_bucket *)) + + (count * sizeof(struct hash_bucket *))); + if (rval == NULL) + return (NULL); + rval->hash_count = count; + return (rval); +} + +struct hash_bucket * +new_hash_bucket(void) +{ + struct hash_bucket *rval = calloc(1, sizeof(struct hash_bucket)); + + return (rval); +} diff --git a/sbin/dhclient/bpf.c b/sbin/dhclient/bpf.c new file mode 100644 index 000000000000..131a88f743d2 --- /dev/null +++ b/sbin/dhclient/bpf.c @@ -0,0 +1,374 @@ +/* $OpenBSD: bpf.c,v 1.13 2004/05/05 14:28:58 deraadt 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 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 +#include + +#include +#include +#include +#include +#include + +#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); + +/* + * Packet write filter program: + * 'ip and udp and src port bootps and dst port (bootps or bootpc)' + */ +struct bpf_insn dhcp_bpf_wfilter[] = { + BPF_STMT(BPF_LD + BPF_B + BPF_IND, 14), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (IPVERSION << 4) + 5, 0, 12), + + /* Make sure this is an IP packet... */ + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 10), + + /* Make sure it's a UDP packet... */ + BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 8), + + /* Make sure this isn't a fragment... */ + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20), + BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 6, 0), /* patched */ + + /* Get the IP header length... */ + BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14), + + /* Make sure it's from the right port... */ + BPF_STMT(BPF_LD + BPF_H + BPF_IND, 14), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 68, 0, 3), + + /* Make sure it is to the right ports ... */ + BPF_STMT(BPF_LD + BPF_H + BPF_IND, 16), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 67, 0, 1), + + /* 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_wfilter_len = sizeof(dhcp_bpf_wfilter) / 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"); + + /* Set up the bpf write filter program structure. */ + p.bf_len = dhcp_bpf_wfilter_len; + p.bf_insns = dhcp_bpf_wfilter; + + if (dhcp_bpf_wfilter[7].k == 0x1fff) + dhcp_bpf_wfilter[7].k = htons(IP_MF|IP_OFFMASK); + + if (ioctl(info->rfdesc, BIOCSETWF, &p) < 0) + error("Can't install write filter program: %m"); + + if (ioctl(info->rfdesc, BIOCLOCK, NULL) < 0) + error("Cannot lock bpf"); +} + +ssize_t +send_packet(struct interface_info *interface, 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(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) + warning("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->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->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/sbin/dhclient/clparse.c b/sbin/dhclient/clparse.c new file mode 100644 index 000000000000..2777902c7767 --- /dev/null +++ b/sbin/dhclient/clparse.c @@ -0,0 +1,938 @@ +/* $OpenBSD: clparse.c,v 1.18 2004/09/15 18:15:18 henning Exp $ */ + +/* Parser for dhclient config and lease files... */ + +/* + * 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 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" + +struct client_config top_level_config; +struct interface_info *dummy_interfaces; +extern struct interface_info *ifi; + +char client_script_name[] = "/sbin/dhclient-script"; + +/* + * client-conf-file :== client-declarations EOF + * client-declarations :== + * | client-declaration + * | client-declarations client-declaration + */ +int +read_client_conf(void) +{ + FILE *cfile; + char *val; + int token; + struct client_config *config; + + new_parse(path_dhclient_conf); + + /* Set up the initial dhcp option universe. */ + initialize_universes(); + + /* Initialize the top level client configuration. */ + memset(&top_level_config, 0, sizeof(top_level_config)); + + /* Set some defaults... */ + top_level_config.timeout = 60; + top_level_config.select_interval = 0; + top_level_config.reboot_timeout = 10; + top_level_config.retry_interval = 300; + top_level_config.backoff_cutoff = 15; + top_level_config.initial_interval = 3; + top_level_config.bootp_policy = ACCEPT; + top_level_config.script_name = client_script_name; + top_level_config.requested_options + [top_level_config.requested_option_count++] = DHO_SUBNET_MASK; + top_level_config.requested_options + [top_level_config.requested_option_count++] = DHO_BROADCAST_ADDRESS; + top_level_config.requested_options + [top_level_config.requested_option_count++] = DHO_TIME_OFFSET; + top_level_config.requested_options + [top_level_config.requested_option_count++] = DHO_ROUTERS; + top_level_config.requested_options + [top_level_config.requested_option_count++] = DHO_DOMAIN_NAME; + top_level_config.requested_options + [top_level_config.requested_option_count++] = + DHO_DOMAIN_NAME_SERVERS; + top_level_config.requested_options + [top_level_config.requested_option_count++] = DHO_HOST_NAME; + + if ((cfile = fopen(path_dhclient_conf, "r")) != NULL) { + do { + token = peek_token(&val, cfile); + if (token == EOF) + break; + parse_client_statement(cfile, NULL, &top_level_config); + } while (1); + token = next_token(&val, cfile); /* Clear the peek buffer */ + fclose(cfile); + } + + /* + * Set up state and config structures for clients that don't + * have per-interface configuration declarations. + */ + config = NULL; + if (!ifi->client) { + ifi->client = malloc(sizeof(struct client_state)); + if (!ifi->client) + error("no memory for client state."); + memset(ifi->client, 0, sizeof(*(ifi->client))); + } + if (!ifi->client->config) { + if (!config) { + config = malloc(sizeof(struct client_config)); + if (!config) + error("no memory for client config."); + memcpy(config, &top_level_config, + sizeof(top_level_config)); + } + ifi->client->config = config; + } + + return (!warnings_occurred); +} + +/* + * lease-file :== client-lease-statements EOF + * client-lease-statements :== + * | client-lease-statements LEASE client-lease-statement + */ +void +read_client_leases(void) +{ + FILE *cfile; + char *val; + int token; + + new_parse(path_dhclient_db); + + /* Open the lease file. If we can't open it, just return - + we can safely trust the server to remember our state. */ + if ((cfile = fopen(path_dhclient_db, "r")) == NULL) + return; + do { + token = next_token(&val, cfile); + if (token == EOF) + break; + if (token != LEASE) { + warning("Corrupt lease file - possible data loss!"); + skip_to_semi(cfile); + break; + } else + parse_client_lease_statement(cfile, 0); + + } while (1); + fclose(cfile); +} + +/* + * client-declaration :== + * SEND option-decl | + * DEFAULT option-decl | + * SUPERSEDE option-decl | + * PREPEND option-decl | + * APPEND option-decl | + * hardware-declaration | + * REQUEST option-list | + * REQUIRE option-list | + * TIMEOUT number | + * RETRY number | + * REBOOT number | + * SELECT_TIMEOUT number | + * SCRIPT string | + * interface-declaration | + * LEASE client-lease-statement | + * ALIAS client-lease-statement + */ +void +parse_client_statement(FILE *cfile, struct interface_info *ip, + struct client_config *config) +{ + int token; + char *val; + struct option *option; + + switch (next_token(&val, cfile)) { + case SEND: + parse_option_decl(cfile, &config->send_options[0]); + return; + case DEFAULT: + option = parse_option_decl(cfile, &config->defaults[0]); + if (option) + config->default_actions[option->code] = ACTION_DEFAULT; + return; + case SUPERSEDE: + option = parse_option_decl(cfile, &config->defaults[0]); + if (option) + config->default_actions[option->code] = + ACTION_SUPERSEDE; + return; + case APPEND: + option = parse_option_decl(cfile, &config->defaults[0]); + if (option) + config->default_actions[option->code] = ACTION_APPEND; + return; + case PREPEND: + option = parse_option_decl(cfile, &config->defaults[0]); + if (option) + config->default_actions[option->code] = ACTION_PREPEND; + return; + case MEDIA: + parse_string_list(cfile, &config->media, 1); + return; + case HARDWARE: + if (ip) + parse_hardware_param(cfile, &ip->hw_address); + else { + parse_warn("hardware address parameter %s", + "not allowed here."); + skip_to_semi(cfile); + } + return; + case REQUEST: + config->requested_option_count = + parse_option_list(cfile, config->requested_options); + return; + case REQUIRE: + memset(config->required_options, 0, + sizeof(config->required_options)); + parse_option_list(cfile, config->required_options); + return; + case TIMEOUT: + parse_lease_time(cfile, &config->timeout); + return; + case RETRY: + parse_lease_time(cfile, &config->retry_interval); + return; + case SELECT_TIMEOUT: + parse_lease_time(cfile, &config->select_interval); + return; + case REBOOT: + parse_lease_time(cfile, &config->reboot_timeout); + return; + case BACKOFF_CUTOFF: + parse_lease_time(cfile, &config->backoff_cutoff); + return; + case INITIAL_INTERVAL: + parse_lease_time(cfile, &config->initial_interval); + return; + case SCRIPT: + config->script_name = parse_string(cfile); + return; + case INTERFACE: + if (ip) + parse_warn("nested interface declaration."); + parse_interface_declaration(cfile, config); + return; + case LEASE: + parse_client_lease_statement(cfile, 1); + return; + case ALIAS: + parse_client_lease_statement(cfile, 2); + return; + case REJECT: + parse_reject_statement(cfile, config); + return; + default: + parse_warn("expecting a statement."); + skip_to_semi(cfile); + break; + } + token = next_token(&val, cfile); + if (token != SEMI) { + parse_warn("semicolon expected."); + skip_to_semi(cfile); + } +} + +int +parse_X(FILE *cfile, u_int8_t *buf, int max) +{ + int token; + char *val; + int len; + + token = peek_token(&val, cfile); + if (token == NUMBER_OR_NAME || token == NUMBER) { + len = 0; + do { + token = next_token(&val, cfile); + if (token != NUMBER && token != NUMBER_OR_NAME) { + parse_warn("expecting hexadecimal constant."); + skip_to_semi(cfile); + return (0); + } + convert_num(&buf[len], val, 16, 8); + if (len++ > max) { + parse_warn("hexadecimal constant too long."); + skip_to_semi(cfile); + return (0); + } + token = peek_token(&val, cfile); + if (token == COLON) + token = next_token(&val, cfile); + } while (token == COLON); + val = (char *)buf; + } else if (token == STRING) { + token = next_token(&val, cfile); + len = strlen(val); + if (len + 1 > max) { + parse_warn("string constant too long."); + skip_to_semi(cfile); + return (0); + } + memcpy(buf, val, len + 1); + } else { + parse_warn("expecting string or hexadecimal data"); + skip_to_semi(cfile); + return (0); + } + return (len); +} + +/* + * option-list :== option_name | + * option_list COMMA option_name + */ +int +parse_option_list(FILE *cfile, u_int8_t *list) +{ + int ix, i; + int token; + char *val; + + ix = 0; + do { + token = next_token(&val, cfile); + if (!is_identifier(token)) { + parse_warn("expected option name."); + skip_to_semi(cfile); + return (0); + } + for (i = 0; i < 256; i++) + if (!strcasecmp(dhcp_options[i].name, val)) + break; + + if (i == 256) { + parse_warn("%s: unexpected option name.", val); + skip_to_semi(cfile); + return (0); + } + list[ix++] = i; + if (ix == 256) { + parse_warn("%s: too many options.", val); + skip_to_semi(cfile); + return (0); + } + token = next_token(&val, cfile); + } while (token == COMMA); + if (token != SEMI) { + parse_warn("expecting semicolon."); + skip_to_semi(cfile); + return (0); + } + return (ix); +} + +/* + * interface-declaration :== + * INTERFACE string LBRACE client-declarations RBRACE + */ +void +parse_interface_declaration(FILE *cfile, struct client_config *outer_config) +{ + int token; + char *val; + struct interface_info *ip; + + token = next_token(&val, cfile); + if (token != STRING) { + parse_warn("expecting interface name (in quotes)."); + skip_to_semi(cfile); + return; + } + + ip = interface_or_dummy(val); + + if (!ip->client) + make_client_state(ip); + + if (!ip->client->config) + make_client_config(ip, outer_config); + + token = next_token(&val, cfile); + if (token != LBRACE) { + parse_warn("expecting left brace."); + skip_to_semi(cfile); + return; + } + + do { + token = peek_token(&val, cfile); + if (token == EOF) { + parse_warn("unterminated interface declaration."); + return; + } + if (token == RBRACE) + break; + parse_client_statement(cfile, ip, ip->client->config); + } while (1); + token = next_token(&val, cfile); +} + +struct interface_info * +interface_or_dummy(char *name) +{ + struct interface_info *ip; + + /* Find the interface (if any) that matches the name. */ + if (!strcmp(ifi->name, name)) + return (ifi); + + /* If it's not a real interface, see if it's on the dummy list. */ + for (ip = dummy_interfaces; ip; ip = ip->next) + if (!strcmp(ip->name, name)) + return (ip); + + /* + * If we didn't find an interface, make a dummy interface as a + * placeholder. + */ + ip = malloc(sizeof(*ip)); + if (!ip) + error("Insufficient memory to record interface %s", name); + memset(ip, 0, sizeof(*ip)); + strlcpy(ip->name, name, IFNAMSIZ); + ip->next = dummy_interfaces; + dummy_interfaces = ip; + return (ip); +} + +void +make_client_state(struct interface_info *ip) +{ + ip->client = malloc(sizeof(*(ip->client))); + if (!ip->client) + error("no memory for state on %s", ip->name); + memset(ip->client, 0, sizeof(*(ip->client))); +} + +void +make_client_config(struct interface_info *ip, struct client_config *config) +{ + ip->client->config = malloc(sizeof(struct client_config)); + if (!ip->client->config) + error("no memory for config for %s", ip->name); + memset(ip->client->config, 0, sizeof(*(ip->client->config))); + memcpy(ip->client->config, config, sizeof(*config)); +} + +/* + * client-lease-statement :== + * RBRACE client-lease-declarations LBRACE + * + * client-lease-declarations :== + * | + * client-lease-declaration | + * client-lease-declarations client-lease-declaration + */ +void +parse_client_lease_statement(FILE *cfile, int is_static) +{ + struct client_lease *lease, *lp, *pl; + struct interface_info *ip; + int token; + char *val; + + token = next_token(&val, cfile); + if (token != LBRACE) { + parse_warn("expecting left brace."); + skip_to_semi(cfile); + return; + } + + lease = malloc(sizeof(struct client_lease)); + if (!lease) + error("no memory for lease."); + memset(lease, 0, sizeof(*lease)); + lease->is_static = is_static; + + ip = NULL; + + do { + token = peek_token(&val, cfile); + if (token == EOF) { + parse_warn("unterminated lease declaration."); + return; + } + if (token == RBRACE) + break; + parse_client_lease_declaration(cfile, lease, &ip); + } while (1); + token = next_token(&val, cfile); + + /* If the lease declaration didn't include an interface + * declaration that we recognized, it's of no use to us. + */ + if (!ip) { + free_client_lease(lease); + return; + } + + /* Make sure there's a client state structure... */ + if (!ip->client) + make_client_state(ip); + + /* If this is an alias lease, it doesn't need to be sorted in. */ + if (is_static == 2) { + ip->client->alias = lease; + return; + } + + /* + * The new lease may supersede a lease that's not the active + * lease but is still on the lease list, so scan the lease list + * looking for a lease with the same address, and if we find it, + * toss it. + */ + pl = NULL; + for (lp = ip->client->leases; lp; lp = lp->next) { + if (lp->address.len == lease->address.len && + !memcmp(lp->address.iabuf, lease->address.iabuf, + lease->address.len)) { + if (pl) + pl->next = lp->next; + else + ip->client->leases = lp->next; + free_client_lease(lp); + break; + } + } + + /* + * If this is a preloaded lease, just put it on the list of + * recorded leases - don't make it the active lease. + */ + if (is_static) { + lease->next = ip->client->leases; + ip->client->leases = lease; + return; + } + + /* + * The last lease in the lease file on a particular interface is + * the active lease for that interface. Of course, we don't + * know what the last lease in the file is until we've parsed + * the whole file, so at this point, we assume that the lease we + * just parsed is the active lease for its interface. If + * there's already an active lease for the interface, and this + * lease is for the same ip address, then we just toss the old + * active lease and replace it with this one. If this lease is + * for a different address, then if the old active lease has + * expired, we dump it; if not, we put it on the list of leases + * for this interface which are still valid but no longer + * active. + */ + if (ip->client->active) { + if (ip->client->active->expiry < cur_time) + free_client_lease(ip->client->active); + else if (ip->client->active->address.len == + lease->address.len && + !memcmp(ip->client->active->address.iabuf, + lease->address.iabuf, lease->address.len)) + free_client_lease(ip->client->active); + else { + ip->client->active->next = ip->client->leases; + ip->client->leases = ip->client->active; + } + } + ip->client->active = lease; + + /* Phew. */ +} + +/* + * client-lease-declaration :== + * BOOTP | + * INTERFACE string | + * FIXED_ADDR ip_address | + * FILENAME string | + * SERVER_NAME string | + * OPTION option-decl | + * RENEW time-decl | + * REBIND time-decl | + * EXPIRE time-decl + */ +void +parse_client_lease_declaration(FILE *cfile, struct client_lease *lease, + struct interface_info **ipp) +{ + int token; + char *val; + struct interface_info *ip; + + switch (next_token(&val, cfile)) { + case BOOTP: + lease->is_bootp = 1; + break; + case INTERFACE: + token = next_token(&val, cfile); + if (token != STRING) { + parse_warn("expecting interface name (in quotes)."); + skip_to_semi(cfile); + break; + } + ip = interface_or_dummy(val); + *ipp = ip; + break; + case FIXED_ADDR: + if (!parse_ip_addr(cfile, &lease->address)) + return; + break; + case MEDIUM: + parse_string_list(cfile, &lease->medium, 0); + return; + case FILENAME: + lease->filename = parse_string(cfile); + return; + case SERVER_NAME: + lease->server_name = parse_string(cfile); + return; + case RENEW: + lease->renewal = parse_date(cfile); + return; + case REBIND: + lease->rebind = parse_date(cfile); + return; + case EXPIRE: + lease->expiry = parse_date(cfile); + return; + case OPTION: + parse_option_decl(cfile, lease->options); + return; + default: + parse_warn("expecting lease declaration."); + skip_to_semi(cfile); + break; + } + token = next_token(&val, cfile); + if (token != SEMI) { + parse_warn("expecting semicolon."); + skip_to_semi(cfile); + } +} + +struct option * +parse_option_decl(FILE *cfile, struct option_data *options) +{ + char *val; + int token; + u_int8_t buf[4]; + u_int8_t hunkbuf[1024]; + int hunkix = 0; + char *vendor; + char *fmt; + struct universe *universe; + struct option *option; + struct iaddr ip_addr; + u_int8_t *dp; + int len; + int nul_term = 0; + + token = next_token(&val, cfile); + if (!is_identifier(token)) { + parse_warn("expecting identifier after option keyword."); + if (token != SEMI) + skip_to_semi(cfile); + return (NULL); + } + if ((vendor = strdup(val)) == NULL) + error("no memory for vendor information."); + + 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 (NULL); + } + + /* 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 (NULL); + } + } 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 (NULL); + } + + /* Free the initial identifier token. */ + free(vendor); + + /* Parse the option data... */ + do { + for (fmt = option->format; *fmt; fmt++) { + if (*fmt == 'A') + break; + switch (*fmt) { + case 'X': + len = parse_X(cfile, &hunkbuf[hunkix], + sizeof(hunkbuf) - hunkix); + hunkix += len; + break; + case 't': /* Text string... */ + token = next_token(&val, cfile); + if (token != STRING) { + parse_warn("expecting string."); + skip_to_semi(cfile); + return (NULL); + } + len = strlen(val); + if (hunkix + len + 1 > sizeof(hunkbuf)) { + parse_warn("option data buffer %s", + "overflow"); + skip_to_semi(cfile); + return (NULL); + } + memcpy(&hunkbuf[hunkix], val, len + 1); + nul_term = 1; + hunkix += len; + break; + case 'I': /* IP address. */ + if (!parse_ip_addr(cfile, &ip_addr)) + return (NULL); + len = ip_addr.len; + dp = ip_addr.iabuf; +alloc: + if (hunkix + len > sizeof(hunkbuf)) { + parse_warn("option data buffer " + "overflow"); + skip_to_semi(cfile); + return (NULL); + } + memcpy(&hunkbuf[hunkix], dp, len); + hunkix += len; + 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 (NULL); + } + convert_num(buf, val, 0, 32); + len = 4; + dp = buf; + goto alloc; + case 's': /* Signed 16-bit integer. */ + case 'S': /* Unsigned 16-bit integer. */ + token = next_token(&val, cfile); + if (token != NUMBER) + goto need_number; + convert_num(buf, val, 0, 16); + len = 2; + dp = buf; + goto alloc; + case 'b': /* Signed 8-bit integer. */ + case 'B': /* Unsigned 8-bit integer. */ + token = next_token(&val, cfile); + if (token != NUMBER) + goto need_number; + convert_num(buf, val, 0, 8); + len = 1; + dp = buf; + goto alloc; + 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 (NULL); + } + 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; + } + len = 1; + dp = buf; + goto alloc; + default: + warning("Bad format %c in parse_option_param.", + *fmt); + skip_to_semi(cfile); + return (NULL); + } + } + token = next_token(&val, cfile); + } while (*fmt == 'A' && token == COMMA); + + if (token != SEMI) { + parse_warn("semicolon expected."); + skip_to_semi(cfile); + return (NULL); + } + + options[option->code].data = malloc(hunkix + nul_term); + if (!options[option->code].data) + error("out of memory allocating option data."); + memcpy(options[option->code].data, hunkbuf, hunkix + nul_term); + options[option->code].len = hunkix; + return (option); +} + +void +parse_string_list(FILE *cfile, struct string_list **lp, int multiple) +{ + int token; + char *val; + struct string_list *cur, *tmp; + + /* Find the last medium in the media list. */ + if (*lp) + for (cur = *lp; cur->next; cur = cur->next) + ; /* nothing */ + else + cur = NULL; + + do { + token = next_token(&val, cfile); + if (token != STRING) { + parse_warn("Expecting media options."); + skip_to_semi(cfile); + return; + } + + tmp = new_string_list(strlen(val) + 1); + if (tmp == NULL) + error("no memory for string list entry."); + strlcpy(tmp->string, val, strlen(val) + 1); + tmp->next = NULL; + + /* Store this medium at the end of the media list. */ + if (cur) + cur->next = tmp; + else + *lp = tmp; + cur = tmp; + + token = next_token(&val, cfile); + } while (multiple && token == COMMA); + + if (token != SEMI) { + parse_warn("expecting semicolon."); + skip_to_semi(cfile); + } +} + +void +parse_reject_statement(FILE *cfile, struct client_config *config) +{ + int token; + char *val; + struct iaddr addr; + struct iaddrlist *list; + + do { + if (!parse_ip_addr(cfile, &addr)) { + parse_warn("expecting IP address."); + skip_to_semi(cfile); + return; + } + + list = malloc(sizeof(struct iaddrlist)); + if (!list) + error("no memory for reject list!"); + + list->addr = addr; + list->next = config->reject_list; + config->reject_list = list; + + token = next_token(&val, cfile); + } while (token == COMMA); + + if (token != SEMI) { + parse_warn("expecting semicolon."); + skip_to_semi(cfile); + } +} diff --git a/sbin/dhclient/conflex.c b/sbin/dhclient/conflex.c new file mode 100644 index 000000000000..ee96bfeb9ca0 --- /dev/null +++ b/sbin/dhclient/conflex.c @@ -0,0 +1,525 @@ +/* $OpenBSD: conflex.c,v 1.7 2004/09/15 19:02:38 deraadt Exp $ */ + +/* 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 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 + +#include "dhcpd.h" +#include "dhctoken.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]; + +static int get_char(FILE *); +static int get_token(FILE *); +static void skip_to_eol(FILE *); +static int read_string(FILE *); +static int read_number(int, FILE *); +static int read_num_or_name(int, FILE *); +static int intern(char *, int); + +void +new_parse(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(FILE *cfile) +{ + int c = getc(cfile); + if (!ugflag) { + if (c == '\n') { + 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(FILE *cfile) +{ + int c, ttok; + static char tb[2]; + int l, p; + + do { + l = line; + p = lpos; + + 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(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; + + return (rv); +} + +int +peek_token(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; + + return (token); +} + +static void +skip_to_eol(FILE *cfile) +{ + int c; + + do { + c = get_char(cfile); + if (c == EOF) + return; + if (c == '\n') + return; + } while (1); +} + +static int +read_string(FILE *cfile) +{ + int i, c, bs = 0; + + 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(int c, FILE *cfile) +{ + int seenx = 0, i = 0, 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(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(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/sbin/dhclient/convert.c b/sbin/dhclient/convert.c new file mode 100644 index 000000000000..0626dc4b3a8e --- /dev/null +++ b/sbin/dhclient/convert.c @@ -0,0 +1,114 @@ +/* $OpenBSD: convert.c,v 1.5 2004/02/07 11:35:59 henning Exp $ */ + +/* + * 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 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(unsigned char *buf) +{ + u_int32_t ibuf; + + memcpy(&ibuf, buf, sizeof(ibuf)); + return (ntohl(ibuf)); +} + +int32_t +getLong(unsigned char *(buf)) +{ + int32_t ibuf; + + memcpy(&ibuf, buf, sizeof(ibuf)); + return (ntohl(ibuf)); +} + +u_int16_t +getUShort(unsigned char *buf) +{ + u_int16_t ibuf; + + memcpy(&ibuf, buf, sizeof(ibuf)); + return (ntohs(ibuf)); +} + +int16_t +getShort(unsigned char *buf) +{ + int16_t ibuf; + + memcpy(&ibuf, buf, sizeof(ibuf)); + return (ntohs(ibuf)); +} + +void +putULong(unsigned char *obuf, u_int32_t val) +{ + u_int32_t tmp = htonl(val); + + memcpy(obuf, &tmp, sizeof(tmp)); +} + +void +putLong(unsigned char *obuf, int32_t val) +{ + int32_t tmp = htonl(val); + + memcpy(obuf, &tmp, sizeof(tmp)); +} + +void +putUShort(unsigned char *obuf, unsigned int val) +{ + u_int16_t tmp = htons(val); + + memcpy(obuf, &tmp, sizeof(tmp)); +} + +void +putShort(unsigned char *obuf, int val) +{ + int16_t tmp = htons(val); + + memcpy(obuf, &tmp, sizeof(tmp)); +} diff --git a/sbin/dhclient/dhclient-script b/sbin/dhclient/dhclient-script new file mode 100644 index 000000000000..3a6ac1877f0c --- /dev/null +++ b/sbin/dhclient/dhclient-script @@ -0,0 +1,227 @@ +#!/bin/sh +# +# $OpenBSD: dhclient-script,v 1.6 2004/05/06 18:22:41 claudio Exp $ +# +# Copyright (c) 2003 Kenneth R Westerback +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# + +# +# Helper functions that implement common actions. +# + +delete_old_address() { + if [ -n "$old_ip_address" ]; then + ifconfig $interface inet -alias $old_ip_address $medium + route delete "$old_ip_address" 127.0.0.1 >/dev/null 2>&1 + fi +} + +add_new_address() { + ifconfig $interface \ + inet $new_ip_address \ + netmask $new_subnet_mask \ + broadcast $new_broadcast_address \ + $medium + + # XXX Original TIMEOUT code did not do this unless $new_routers was set? + route add $new_ip_address 127.0.0.1 >/dev/null 2>&1 +} + +delete_old_alias() { + if [ -n "$alias_ip_address" ]; then + ifconfig $interface inet -alias $alias_ip_address > /dev/null 2>&1 + route delete $alias_ip_address 127.0.0.1 > /dev/null 2>&1 + fi +} + +add_new_alias() { + if [ -n "$alias_ip_address" ]; then + ifconfig $interface inet alias $alias_ip_address netmask \ + $alias_subnet_mask + route add $alias_ip_address 127.0.0.1 + fi +} + +delete_old_routes() { + # Delete existing default route. We only allow one, so no need to + # process $old_routers list. + route delete default >/dev/null 2>&1 + + if [ -n "$old_static_routes" ]; then + set $old_static_routes + while [ $# -gt 1 ]; do + route delete "$1" "$2" + shift; shift + done + fi + + arp -dan +} + +add_new_routes() { + route delete default >/dev/null 2>&1 + for router in $new_routers; do + if [ "$new_ip_address" = "$router" ]; then + route add default -iface $router >/dev/null 2>&1 + else + route add default $router >/dev/null 2>&1 + fi + # 2nd and subsequent default routers error out, so explicitly + # stop processing the list after the first one. + break + done + + if [ -n "$new_static_routes" ]; then + set $new_static_routes + while [ $# -gt 1 ]; do + route add $1 $2 + shift; shift + done + fi +} + +add_new_resolv_conf() { + # XXX Old code did not create/update resolv.conf unless both + # $new_domain_name and $new_domain_name_servers were provided. PR + # #3135 reported some ISP's only provide $new_domain_name_servers and + # thus broke the script. This code creates the resolv.conf if either + # are provided. + + rm -f /etc/resolv.conf.std + + if [ -n "$new_domain_name" ]; then + echo "search $new_domain_name" >>/etc/resolv.conf.std + fi + + if [ -n "$new_domain_name_servers" ]; then + for nameserver in $new_domain_name_servers; do + echo "nameserver $nameserver" >>/etc/resolv.conf.std + done + fi + + if [ -f /etc/resolv.conf.std ]; then + if [ -f /etc/resolv.conf.tail ]; then + cat /etc/resolv.conf.tail >>/etc/resolv.conf.std + fi + + # In case (e.g. during OpenBSD installs) /etc/resolv.conf + # is a symbolic link, take care to preserve the link and write + # the new data in the correct location. + + if [ -f /etc/resolv.conf ]; then + cat /etc/resolv.conf > /etc/resolv.conf.save + fi + cat /etc/resolv.conf.std > /etc/resolv.conf + rm -f /etc/resolv.conf.std + + # Try to ensure correct ownership and permissions. + chown -RL root:wheel /etc/resolv.conf + chmod -RL 644 /etc/resolv.conf + + return 0 + fi + + return 1 +} + +# +# Start of active code. +# + +if [ -n "$new_network_number" ]; then + echo "New Network Number: $new_network_number" +fi + +if [ -n "$new_broadcast_address" ]; then + echo "New Broadcast Address: $new_broadcast_address" +fi + +case $reason in +MEDIUM) + ifconfig $interface $medium + ifconfig $interface inet -alias 0.0.0.0 $medium >/dev/null 2>&1 + sleep 1 + ;; + +PREINIT) + delete_old_alias + ifconfig $interface inet 0.0.0.0 netmask 0.0.0.0 broadcast 255.255.255.255 up + ;; + +ARPCHECK|ARPSEND) + ;; + +BOUND|RENEW|REBIND|REBOOT) + if [ -n "$old_ip_address" ]; then + if [ "$old_ip_address" != "$alias_ip_address" ]; then + delete_old_alias + fi + if [ "$old_ip_address" != "$new_ip_address" ]; then + delete_old_address + delete_old_routes + fi + fi + if [ "$reason" = BOUND ] || \ + [ "$reason" = REBOOT ] || \ + [ -z "$old_ip_address" ] || \ + [ "$old_ip_address" != "$new_ip_address" ]; then + add_new_address + add_new_routes + fi + if [ "$new_ip_address" != "$alias_ip_address" ]; then + add_new_alias + fi + add_new_resolv_conf + ;; + +EXPIRE|FAIL) + delete_old_alias + if [ -n "$old_ip_address" ]; then + delete_old_address + delete_old_routes + fi + # XXX Why add alias we just deleted above? + add_new_alias + if [ -f /etc/resolv.conf.save ]; then + cat /etc/resolv.conf.save > /etc/resolv.conf + fi + ;; + +TIMEOUT) + delete_old_alias + add_new_address + sleep 1 + if [ -n "$new_routers" ]; then + set "$new_routers" + if ping -q -c 1 -w 1 "$1"; then + if [ "$new_ip_address" != "$alias_ip_address" ]; then + add_new_alias + fi + add_new_routes + if add_new_resolv_conf; then + exit 0 + fi + fi + fi + ifconfig $interface inet -alias $new_ip_address $medium + # XXX Why not a delete_old_address as before all other invocations of + # delete_old_routes? + delete_old_routes + exit 1 + ;; +esac + +exit 0 diff --git a/sbin/dhclient/dhclient-script.8 b/sbin/dhclient/dhclient-script.8 new file mode 100644 index 000000000000..57a2abdc54ef --- /dev/null +++ b/sbin/dhclient/dhclient-script.8 @@ -0,0 +1,246 @@ +.\" $OpenBSD: dhclient-script.8,v 1.2 2004/04/09 18:30:15 jmc Exp $ +.\" +.\" 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 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 DHCLIENT-SCRIPT 8 +.Os +.Sh NAME +.Nm dhclient-script +.Nd DHCP client network configuration script +.Sh DESCRIPTION +The DHCP client network configuration script is invoked from time to +time by +.Xr dhclient 8 . +This script is used by the DHCP client to set each interface's initial +configuration prior to requesting an address, to test the address once it +has been offered, and to set the interface's final configuration once a +lease has been acquired. +If no lease is acquired, the script is used to test predefined leases, if +any, and also called once if no valid lease can be identified. +.Pp +.\" No standard client script exists for some operating systems, even though +.\" the actual client may work, so a pioneering user may well need to create +.\" a new script or modify an existing one. +In general, customizations specific to a particular computer should be done +in the +.Pa /etc/dhclient.conf +file. +.Sh OPERATION +When +.Xr dhclient 8 +needs to invoke the client configuration script, it sets up a number of +environment variables and runs +.Nm dhclient-script . +In all cases, +.Va $reason +is set to the name of the reason why the script has been invoked. +The following reasons are currently defined: +MEDIUM, PREINIT, ARPCHECK, ARPSEND, BOUND, RENEW, REBIND, REBOOT, +EXPIRE, FAIL and TIMEOUT. +.Bl -tag -width "ARPCHECK" +.It MEDIUM +The DHCP client is requesting that an interface's media type be set. +The interface name is passed in +.Va $interface , +and the media type is passed in +.Va $medium . +.It PREINIT +The DHCP client is requesting that an interface be configured as +required in order to send packets prior to receiving an actual address. +.\" For clients which use the BSD socket library, +This means configuring the interface with an IP address of 0.0.0.0 +and a broadcast address of 255.255.255.255. +.\" For other clients, it may be possible to simply configure the interface up +.\" without actually giving it an IP address at all. +The interface name is passed in +.Va $interface , +and the media type in +.Va $medium . +.Pp +If an IP alias has been declared in +.Xr dhclient.conf 5 , +its address will be passed in +.Va $alias_ip_address , +and that IP alias should be deleted from the interface, +along with any routes to it. +.It ARPSEND +The DHCP client is requesting that an address that has been offered to +it be checked to see if somebody else is using it, by sending an ARP +request for that address. +It's not clear how to implement this, so no examples exist yet. +The IP address to check is passed in +.Va $new_ip_address , +and the interface name is passed in +.Va $interface . +.It ARPCHECK +The DHCP client wants to know if a response to the ARP request sent +using ARPSEND has been received. +If one has, the script should exit with a nonzero status, indicating that +the offered address has already been requested and should be declined. +.Va $new_ip_address +and +.Va $interface +are set as with ARPSEND. +.It BOUND +The DHCP client has done an initial binding to a new address. +The new IP address is passed in +.Va $new_ip_address , +and the interface name is passed in +.Va $interface . +The media type is passed in +.Va $medium . +Any options acquired from the server are passed using the option name +described in +.Xr dhcp-options 5 , +except that dashes +.Pq Sq - +are replaced by underscores +.Pq Sq _ +in order to make valid shell variables, and the variable names start with new_. +So for example, the new subnet mask would be passed in +.Va $new_subnet_mask . +.Pp +When a binding has been completed, a lot of network parameters are +likely to need to be set up. +A new +.Pa /etc/resolv.conf +needs to be created, using the values of +.Va $new_domain_name +and +.Va $new_domain_name_servers +(which may list more than one server, separated by spaces). +A default route should be set using +.Va $new_routers , +and static routes may need to be set up using +.Va $new_static_routes . +.Pp +If an IP alias has been declared, it must be set up here. +The alias IP address will be written as +.Va $alias_ip_address , +and other DHCP options that are set for the alias (e.g., subnet mask) +will be passed in variables named as described previously except starting with +$alias_ instead of $new_. +Care should be taken that the alias IP address not be used if it is identical +to the bound IP address +.Pq Va $new_ip_address , +since the other alias parameters may be incorrect in this case. +.It RENEW +When a binding has been renewed, the script is called as in BOUND, +except that in addition to all the variables starting with $new_, +there is another set of variables starting with $old_. +Persistent settings that may have changed need to be deleted \- for example, +if a local route to the bound address is being configured, the old local +route should be deleted. +If the default route has changed, the old default route should be deleted. +If the static routes have changed, the old ones should be deleted. +Otherwise, processing can be done as with BOUND. +.It REBIND +The DHCP client has rebound to a new DHCP server. +This can be handled as with RENEW, except that if the IP address has changed, +the ARP table should be cleared. +.It REBOOT +The DHCP client has successfully reacquired its old address after a reboot. +This can be processed as with BOUND. +.It EXPIRE +The DHCP client has failed to renew its lease or acquire a new one, +and the lease has expired. +The IP address must be relinquished, and all related parameters should be +deleted, as in RENEW and REBIND. +.It FAIL +The DHCP client has been unable to contact any DHCP servers, and any +leases that have been tested have not proved to be valid. +The parameters from the last lease tested should be deconfigured. +This can be handled in the same way as EXPIRE. +.It TIMEOUT +The DHCP client has been unable to contact any DHCP servers. +However, an old lease has been identified, and its parameters have +been passed in as with BOUND. +The client configuration script should test these parameters and, +if it has reason to believe they are valid, should exit with a value of zero. +If not, it should exit with a nonzero value. +.El +.Pp +The usual way to test a lease is to set up the network as with REBIND +(since this may be called to test more than one lease) and then ping +the first router defined in +.Va $routers . +If a response is received, the lease must be valid for the network to +which the interface is currently connected. +It would be more complete to try to ping all of the routers listed in +.Va $new_routers , +as well as those listed in +.Va $new_static_routes , +but current scripts do not do this. +.\" .Sh FILES +.\" Each operating system should generally have its own script file, +.\" although the script files for similar operating systems may be similar +.\" or even identical. +.\" The script files included in the Internet Software Consortium DHCP +.\" distribution appear in the distribution tree under client/scripts, +.\" and bear the names of the operating systems on which they are intended +.\" to work. +.Sh SEE ALSO +.Xr dhclient.conf 5 , +.Xr dhclient.leases 5 , +.Xr dhclient 8 , +.Xr dhcpd 8 , +.Xr dhcrelay 8 +.Sh AUTHORS +The original version of +.Nm +was written for the Internet Software Consortium by +.An Ted Lemon Aq mellon@fugue.com +in cooperation with Vixie Enterprises. +.Pp +The +.Ox +implementation of +.Nm +was written by +.An Kenneth R. Westerback Aq krw@openbsd.org . +.Sh BUGS +If more than one interface is being used, there's no obvious way to +avoid clashes between server-supplied configuration parameters \- for +example, the stock dhclient-script rewrites +.Pa /etc/resolv.conf . +If more than one interface is being configured, +.Pa /etc/resolv.conf +will be repeatedly initialized to the values provided by one server, and then +the other. +Assuming the information provided by both servers is valid, this shouldn't +cause any real problems, but it could be confusing. diff --git a/sbin/dhclient/dhclient.8 b/sbin/dhclient/dhclient.8 new file mode 100644 index 000000000000..a12952f48d8c --- /dev/null +++ b/sbin/dhclient/dhclient.8 @@ -0,0 +1,181 @@ +.\" $OpenBSD: dhclient.8,v 1.3 2004/04/09 18:30:15 jmc Exp $ +.\" +.\" 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 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 April 7, 2004 +.Dt DHCLIENT 8 +.Os +.Sh NAME +.Nm dhclient +.Nd Dynamic Host Configuration Protocol (DHCP) Client +.Sh SYNOPSIS +.Nm +.Op Fl dqu +.Op Fl c Ar file +.Op Fl l Ar file +.Ar interface +.Sh DESCRIPTION +The +.Nm +utility provides a means for configuring network interfaces using DHCP, BOOTP, +or if these protocols fail, by statically assigning an address. +.Pp +The name of the network interface that +.Nm +should attempt to +configure must be specified on the command line. +.Pp +The options are as follows: +.Bl -tag -width "-p port" +.It Fl c Ar file +Specify an alternate location, +.Ar file , +for the configuration file. +.It Fl d +Forces +.Nm +to always run as a foreground process. +By default, +.Nm +runs in the foreground until it has configured the interface, and then +will revert to running in the background. +.It Fl l Ar file +Specify an alternate location, +.Ar file , +for the leases file. +.It Fl q +Forces +.Nm +to be less verbose on startup. +.It Fl u +Forces +.Nm +to reject leases with unknown options in them. +The default behaviour is to accept such lease offers. +.El +.Pp +The DHCP protocol allows a host to contact a central server which +maintains a list of IP addresses which may be assigned on one or more +subnets. +A DHCP client may request an address from this pool, and +then use it on a temporary basis for communication on the network. +The DHCP protocol also provides a mechanism whereby a client can learn +important details about the network to which it is attached, such as +the location of a default router, the location of a name server, and +so on. +.Pp +On startup, +.Nm +reads +.Pa /etc/dhclient.conf +for configuration instructions. +It then gets a list of all the +network interfaces that are configured in the current system. +It then attempts to configure each interface with DHCP. +.Pp +In order to keep track of leases across system reboots and server +restarts, +.Nm +keeps a list of leases it has been assigned in the +.Pa /var/db/dhclient.leases.IFNAME +file. +.Qq IFNAME +represents the network interface of the DHCP client +.Pq e.g. em0 , +one for each interface. +On startup, after reading the +.Xr dhclient.conf 5 +file, +.Nm +reads the leases file to refresh its memory about what leases it has been +assigned. +.Pp +Old leases are kept around in case the DHCP server is unavailable when +.Nm +is first invoked (generally during the initial system boot +process). +In that event, old leases from the +.Pa dhclient.leases.IFNAME +file which have not yet expired are tested, and if they are determined to +be valid, they are used until either they expire or the DHCP server +becomes available. +.Pp +A mobile host which may sometimes need to access a network on which no +DHCP server exists may be preloaded with a lease for a fixed +address on that network. +When all attempts to contact a DHCP server have failed, +.Nm +will try to validate the static lease, and if it +succeeds, it will use that lease until it is restarted. +.Pp +A mobile host may also travel to some networks on which DHCP is not +available but BOOTP is. +In that case, it may be advantageous to +arrange with the network administrator for an entry on the BOOTP +database, so that the host can boot quickly on that network rather +than cycling through the list of old leases. +.Sh NOTES +You must have the Berkeley Packet Filter (BPF) configured in your kernel. +.Nm +requires at least one +.Pa /dev/bpf* +file for each broadcast network interface that is attached to your system. +See +.Xr bpf 4 +for more information. +.Sh FILES +.Bl -tag -width /var/db/dhclient.leases.IFNAME~ -compact +.It Pa /etc/dhclient.conf +DHCP client configuration file +.It Pa /var/db/dhclient.leases.IFNAME +database of acquired leases +.El +.Sh SEE ALSO +.Xr dhclient.conf 5 , +.Xr dhclient.leases 5 , +.Xr dhclient-script 8 , +.Xr dhcp 8 , +.Xr dhcpd 8 , +.Xr dhcrelay 8 +.Sh AUTHORS +.Nm +was written by +.An Ted Lemon Aq mellon@fugue.com +and +.An Elliot Poger Aq elliot@poger.com . +.Pp +The current implementation was reworked by +.An Henning Brauer Aq henning@openbsd.org . diff --git a/sbin/dhclient/dhclient.c b/sbin/dhclient/dhclient.c new file mode 100644 index 000000000000..ef353c43724d --- /dev/null +++ b/sbin/dhclient/dhclient.c @@ -0,0 +1,2373 @@ +/* $OpenBSD: dhclient.c,v 1.63 2005/02/06 17:10:13 krw Exp $ */ + +/* + * Copyright 2004 Henning Brauer + * 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 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''. + * + * This client was substantially modified and enhanced by Elliot Poger + * for use on Linux while he was working on the MosquitoNet project at + * Stanford. + * + * The current version owes much to Elliot's Linux enhancements, but + * was substantially reorganized and partially rewritten by Ted Lemon + * so as to use the same networking framework that the Internet Software + * Consortium DHCP server uses. Much system-specific configuration code + * was moved into a shell script so that as support for more operating + * systems is added, it will not be necessary to port and maintain + * system-specific configuration code to these operating systems - instead, + * the shell script can invoke the native tools to accomplish the same + * purpose. + */ + +#include "dhcpd.h" +#include "privsep.h" + +#define PERIOD 0x2e +#define hyphenchar(c) ((c) == 0x2d) +#define bslashchar(c) ((c) == 0x5c) +#define periodchar(c) ((c) == PERIOD) +#define asterchar(c) ((c) == 0x2a) +#define alphachar(c) (((c) >= 0x41 && (c) <= 0x5a) || \ + ((c) >= 0x61 && (c) <= 0x7a)) +#define digitchar(c) ((c) >= 0x30 && (c) <= 0x39) + +#define borderchar(c) (alphachar(c) || digitchar(c)) +#define middlechar(c) (borderchar(c) || hyphenchar(c)) +#define domainchar(c) ((c) > 0x20 && (c) < 0x7f) + +#define CLIENT_PATH "PATH=/usr/bin:/usr/sbin:/bin:/sbin" + +time_t cur_time; +time_t default_lease_time = 43200; /* 12 hours... */ + +char *path_dhclient_conf = _PATH_DHCLIENT_CONF; +char *path_dhclient_db = NULL; + +int log_perror = 1; +int privfd; +int nullfd = -1; + +struct iaddr iaddr_broadcast = { 4, { 255, 255, 255, 255 } }; +struct in_addr inaddr_any; +struct sockaddr_in sockaddr_broadcast; + +/* + * ASSERT_STATE() does nothing now; it used to be + * assert (state_is == state_shouldbe). + */ +#define ASSERT_STATE(state_is, state_shouldbe) {} + +#define TIME_MAX 2147483647 + +int log_priority; +int no_daemon; +int unknown_ok = 1; +int routefd; + +struct interface_info *ifi; + +int findproto(char *, int); +struct sockaddr *get_ifa(char *, int); +void routehandler(struct protocol *); +void usage(void); +int check_option(struct client_lease *l, int option); +int ipv4addrs(char * buf); +int res_hnok(const char *dn); +char *option_as_string(unsigned int code, unsigned char *data, int len); +int fork_privchld(int, int); + +#define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) +#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) + +time_t scripttime; + +int +findproto(char *cp, int n) +{ + struct sockaddr *sa; + int i; + + if (n == 0) + return -1; + for (i = 1; i; i <<= 1) { + if (i & n) { + sa = (struct sockaddr *)cp; + switch (i) { + case RTA_IFA: + case RTA_DST: + case RTA_GATEWAY: + case RTA_NETMASK: + if (sa->sa_family == AF_INET) + return AF_INET; + if (sa->sa_family == AF_INET6) + return AF_INET6; + break; + case RTA_IFP: + break; + } + ADVANCE(cp, sa); + } + } + return (-1); +} + +struct sockaddr * +get_ifa(char *cp, int n) +{ + struct sockaddr *sa; + int i; + + if (n == 0) + return (NULL); + for (i = 1; i; i <<= 1) + if (i & n) { + sa = (struct sockaddr *)cp; + if (i == RTA_IFA) + return (sa); + ADVANCE(cp, sa); + } + + return (NULL); +} +struct iaddr defaddr = { 4 }; + +/* ARGSUSED */ +void +routehandler(struct protocol *p) +{ + char msg[2048]; + struct rt_msghdr *rtm; + struct if_msghdr *ifm; + struct ifa_msghdr *ifam; + struct if_announcemsghdr *ifan; + struct client_lease *l; + time_t t = time(NULL); + struct sockaddr *sa; + struct iaddr a; + ssize_t n; + + n = read(routefd, &msg, sizeof(msg)); + rtm = (struct rt_msghdr *)msg; + if (n < sizeof(rtm->rtm_msglen) || n < rtm->rtm_msglen || + rtm->rtm_version != RTM_VERSION) + return; + + switch (rtm->rtm_type) { + case RTM_NEWADDR: + ifam = (struct ifa_msghdr *)rtm; + if (ifam->ifam_index != ifi->index) + break; + if (findproto((char *)(ifam + 1), ifam->ifam_addrs) != AF_INET) + break; + if (ifi == NULL) + goto die; + sa = get_ifa((char *)(ifam + 1), ifam->ifam_addrs); + if (sa == NULL) + goto die; + + if ((a.len = sizeof(struct in_addr)) > sizeof(a.iabuf)) + error("king bula sez: len mismatch"); + memcpy(a.iabuf, &((struct sockaddr_in *)sa)->sin_addr, a.len); + if (addr_eq(a, defaddr)) + break; + + for (l = ifi->client->active; l != NULL; l = l->next) + if (addr_eq(a, l->address)) + break; + + if (l != NULL) /* new addr is the one we set */ + break; + + goto die; + case RTM_DELADDR: + ifam = (struct ifa_msghdr *)rtm; + if (ifam->ifam_index != ifi->index) + break; + if (findproto((char *)(ifam + 1), ifam->ifam_addrs) != AF_INET) + break; + if (scripttime == 0 || t < scripttime + 10) + break; + goto die; + case RTM_IFINFO: + ifm = (struct if_msghdr *)rtm; + if (ifm->ifm_index != ifi->index) + break; + if ((rtm->rtm_flags & RTF_UP) == 0) + goto die; + break; + case RTM_IFANNOUNCE: + ifan = (struct if_announcemsghdr *)rtm; + if (ifan->ifan_what == IFAN_DEPARTURE && + ifan->ifan_index == ifi->index) + goto die; + break; + default: + break; + } + return; + +die: + script_init("FAIL", NULL); + if (ifi->client->alias) + script_write_params("alias_", ifi->client->alias); + script_go(); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + extern char *__progname; + int ch, fd, quiet = 0, i = 0; + int pipe_fd[2]; + struct passwd *pw; + + /* Initially, log errors to stderr as well as to syslogd. */ + openlog(__progname, LOG_PID | LOG_NDELAY, DHCPD_LOG_FACILITY); + setlogmask(LOG_UPTO(LOG_INFO)); + + while ((ch = getopt(argc, argv, "c:dl:qu")) != -1) + switch (ch) { + case 'c': + path_dhclient_conf = optarg; + break; + case 'd': + no_daemon = 1; + break; + case 'l': + path_dhclient_db = optarg; + break; + case 'q': + quiet = 1; + break; + case 'u': + unknown_ok = 0; + break; + default: + usage(); + } + + argc -= optind; + argv += optind; + + if (argc != 1) + usage(); + + if ((ifi = calloc(1, sizeof(struct interface_info))) == NULL) + error("calloc"); + if (strlcpy(ifi->name, argv[0], IFNAMSIZ) >= IFNAMSIZ) + error("Interface name too long"); + if (path_dhclient_db == NULL && asprintf(&path_dhclient_db, "%s.%s", + _PATH_DHCLIENT_DB, ifi->name) == -1) + error("asprintf"); + + if (quiet) + log_perror = 0; + + tzset(); + time(&cur_time); + + memset(&sockaddr_broadcast, 0, sizeof(sockaddr_broadcast)); + sockaddr_broadcast.sin_family = AF_INET; + sockaddr_broadcast.sin_port = htons(REMOTE_PORT); + sockaddr_broadcast.sin_addr.s_addr = INADDR_BROADCAST; + sockaddr_broadcast.sin_len = sizeof(sockaddr_broadcast); + inaddr_any.s_addr = INADDR_ANY; + + read_client_conf(); + + if (!interface_link_status(ifi->name)) { + fprintf(stderr, "%s: no link ...", ifi->name); + fflush(stderr); + sleep(1); + while (!interface_link_status(ifi->name)) { + fprintf(stderr, "."); + fflush(stderr); + if (++i > 10) { + fprintf(stderr, " giving up\n"); + exit(1); + } + sleep(1); + } + fprintf(stderr, " got link\n"); + } + + if ((nullfd = open(_PATH_DEVNULL, O_RDWR, 0)) == -1) + error("cannot open %s: %m", _PATH_DEVNULL); + + if ((pw = getpwnam("_dhcp")) == NULL) { + warning("no such user: _dhcp, falling back to \"nobody\""); + if ((pw = getpwnam("nobody")) == NULL) + error("no such user: nobody"); + } + + if (pipe(pipe_fd) == -1) + error("pipe"); + + fork_privchld(pipe_fd[0], pipe_fd[1]); + + close(pipe_fd[0]); + privfd = pipe_fd[1]; + + if ((fd = open(path_dhclient_db, O_RDONLY|O_EXLOCK|O_CREAT, 0)) == -1) + error("can't open and lock %s: %m", path_dhclient_db); + read_client_leases(); + rewrite_client_leases(); + close(fd); + + priv_script_init("PREINIT", NULL); + if (ifi->client->alias) + priv_script_write_params("alias_", ifi->client->alias); + priv_script_go(); + + if ((routefd = socket(PF_ROUTE, SOCK_RAW, 0)) != -1) + add_protocol("AF_ROUTE", routefd, routehandler, ifi); + + /* set up the interface */ + discover_interfaces(ifi); + + if (chroot(_PATH_VAREMPTY) == -1) + error("chroot"); + if (chdir("/") == -1) + error("chdir(\"/\")"); + + if (setgroups(1, &pw->pw_gid) || + setegid(pw->pw_gid) || setgid(pw->pw_gid) || + seteuid(pw->pw_uid) || setuid(pw->pw_uid)) + error("can't drop privileges: %m"); + + endpwent(); + + setproctitle("%s", ifi->name); + + ifi->client->state = S_INIT; + state_reboot(ifi); + + bootp_packet_handler = do_packet; + + dispatch(); + + /* not reached */ + return (0); +} + +void +usage(void) +{ + extern char *__progname; + + fprintf(stderr, "usage: %s [-dqu] ", __progname); + fprintf(stderr, "[-c conffile] [-l leasefile] interface\n"); + exit(1); +} + +/* + * Individual States: + * + * Each routine is called from the dhclient_state_machine() in one of + * these conditions: + * -> entering INIT state + * -> recvpacket_flag == 0: timeout in this state + * -> otherwise: received a packet in this state + * + * Return conditions as handled by dhclient_state_machine(): + * Returns 1, sendpacket_flag = 1: send packet, reset timer. + * Returns 1, sendpacket_flag = 0: just reset the timer (wait for a milestone). + * Returns 0: finish the nap which was interrupted for no good reason. + * + * Several per-interface variables are used to keep track of the process: + * active_lease: the lease that is being used on the interface + * (null pointer if not configured yet). + * offered_leases: leases corresponding to DHCPOFFER messages that have + * been sent to us by DHCP servers. + * acked_leases: leases corresponding to DHCPACK messages that have been + * sent to us by DHCP servers. + * sendpacket: DHCP packet we're trying to send. + * destination: IP address to send sendpacket to + * In addition, there are several relevant per-lease variables. + * T1_expiry, T2_expiry, lease_expiry: lease milestones + * In the active lease, these control the process of renewing the lease; + * In leases on the acked_leases list, this simply determines when we + * can no longer legitimately use the lease. + */ + +void +state_reboot(void *ipp) +{ + struct interface_info *ip = ipp; + + /* If we don't remember an active lease, go straight to INIT. */ + if (!ip->client->active || ip->client->active->is_bootp) { + state_init(ip); + return; + } + + /* We are in the rebooting state. */ + ip->client->state = S_REBOOTING; + + /* make_request doesn't initialize xid because it normally comes + from the DHCPDISCOVER, but we haven't sent a DHCPDISCOVER, + so pick an xid now. */ + ip->client->xid = arc4random(); + + /* Make a DHCPREQUEST packet, and set appropriate per-interface + flags. */ + make_request(ip, ip->client->active); + ip->client->destination = iaddr_broadcast; + ip->client->first_sending = cur_time; + ip->client->interval = ip->client->config->initial_interval; + + /* Zap the medium list... */ + ip->client->medium = NULL; + + /* Send out the first DHCPREQUEST packet. */ + send_request(ip); +} + +/* + * Called when a lease has completely expired and we've + * been unable to renew it. + */ +void +state_init(void *ipp) +{ + struct interface_info *ip = ipp; + + ASSERT_STATE(state, S_INIT); + + /* Make a DHCPDISCOVER packet, and set appropriate per-interface + flags. */ + make_discover(ip, ip->client->active); + ip->client->xid = ip->client->packet.xid; + ip->client->destination = iaddr_broadcast; + ip->client->state = S_SELECTING; + ip->client->first_sending = cur_time; + ip->client->interval = ip->client->config->initial_interval; + + /* Add an immediate timeout to cause the first DHCPDISCOVER packet + to go out. */ + send_discover(ip); +} + +/* + * state_selecting is called when one or more DHCPOFFER packets + * have been received and a configurable period of time has passed. + */ +void +state_selecting(void *ipp) +{ + struct interface_info *ip = ipp; + struct client_lease *lp, *next, *picked; + + ASSERT_STATE(state, S_SELECTING); + + /* Cancel state_selecting and send_discover timeouts, since either + one could have got us here. */ + cancel_timeout(state_selecting, ip); + cancel_timeout(send_discover, ip); + + /* We have received one or more DHCPOFFER packets. Currently, + the only criterion by which we judge leases is whether or + not we get a response when we arp for them. */ + picked = NULL; + for (lp = ip->client->offered_leases; lp; lp = next) { + next = lp->next; + + /* Check to see if we got an ARPREPLY for the address + in this particular lease. */ + if (!picked) { + script_init("ARPCHECK", lp->medium); + script_write_params("check_", lp); + + /* If the ARPCHECK code detects another + machine using the offered address, it exits + nonzero. We need to send a DHCPDECLINE and + toss the lease. */ + if (script_go()) { + make_decline(ip, lp); + send_decline(ip); + goto freeit; + } + picked = lp; + picked->next = NULL; + } else { +freeit: + free_client_lease(lp); + } + } + ip->client->offered_leases = NULL; + + /* If we just tossed all the leases we were offered, go back + to square one. */ + if (!picked) { + ip->client->state = S_INIT; + state_init(ip); + return; + } + + /* If it was a BOOTREPLY, we can just take the address right now. */ + if (!picked->options[DHO_DHCP_MESSAGE_TYPE].len) { + ip->client->new = picked; + + /* Make up some lease expiry times + XXX these should be configurable. */ + ip->client->new->expiry = cur_time + 12000; + ip->client->new->renewal += cur_time + 8000; + ip->client->new->rebind += cur_time + 10000; + + ip->client->state = S_REQUESTING; + + /* Bind to the address we received. */ + bind_lease(ip); + return; + } + + /* Go to the REQUESTING state. */ + ip->client->destination = iaddr_broadcast; + ip->client->state = S_REQUESTING; + ip->client->first_sending = cur_time; + ip->client->interval = ip->client->config->initial_interval; + + /* Make a DHCPREQUEST packet from the lease we picked. */ + make_request(ip, picked); + ip->client->xid = ip->client->packet.xid; + + /* Toss the lease we picked - we'll get it back in a DHCPACK. */ + free_client_lease(picked); + + /* Add an immediate timeout to send the first DHCPREQUEST packet. */ + send_request(ip); +} + +/* state_requesting is called when we receive a DHCPACK message after + having sent out one or more DHCPREQUEST packets. */ + +void +dhcpack(struct packet *packet) +{ + struct interface_info *ip = packet->interface; + struct client_lease *lease; + + /* If we're not receptive to an offer right now, or if the offer + has an unrecognizable transaction id, then just drop it. */ + if (packet->interface->client->xid != packet->raw->xid || + (packet->interface->hw_address.hlen != packet->raw->hlen) || + (memcmp(packet->interface->hw_address.haddr, + packet->raw->chaddr, packet->raw->hlen))) + return; + + if (ip->client->state != S_REBOOTING && + ip->client->state != S_REQUESTING && + ip->client->state != S_RENEWING && + ip->client->state != S_REBINDING) + return; + + note("DHCPACK from %s", piaddr(packet->client_addr)); + + lease = packet_to_lease(packet); + if (!lease) { + note("packet_to_lease failed."); + return; + } + + ip->client->new = lease; + + /* Stop resending DHCPREQUEST. */ + cancel_timeout(send_request, ip); + + /* Figure out the lease time. */ + if (ip->client->new->options[DHO_DHCP_LEASE_TIME].data) + ip->client->new->expiry = getULong( + ip->client->new->options[DHO_DHCP_LEASE_TIME].data); + else + ip->client->new->expiry = default_lease_time; + /* A number that looks negative here is really just very large, + because the lease expiry offset is unsigned. */ + if (ip->client->new->expiry < 0) + ip->client->new->expiry = TIME_MAX; + /* XXX should be fixed by resetting the client state */ + if (ip->client->new->expiry < 60) + ip->client->new->expiry = 60; + + /* Take the server-provided renewal time if there is one; + otherwise figure it out according to the spec. */ + if (ip->client->new->options[DHO_DHCP_RENEWAL_TIME].len) + ip->client->new->renewal = getULong( + ip->client->new->options[DHO_DHCP_RENEWAL_TIME].data); + else + ip->client->new->renewal = ip->client->new->expiry / 2; + + /* Same deal with the rebind time. */ + if (ip->client->new->options[DHO_DHCP_REBINDING_TIME].len) + ip->client->new->rebind = getULong( + ip->client->new->options[DHO_DHCP_REBINDING_TIME].data); + else + ip->client->new->rebind = ip->client->new->renewal + + ip->client->new->renewal / 2 + ip->client->new->renewal / 4; + + ip->client->new->expiry += cur_time; + /* Lease lengths can never be negative. */ + if (ip->client->new->expiry < cur_time) + ip->client->new->expiry = TIME_MAX; + ip->client->new->renewal += cur_time; + if (ip->client->new->renewal < cur_time) + ip->client->new->renewal = TIME_MAX; + ip->client->new->rebind += cur_time; + if (ip->client->new->rebind < cur_time) + ip->client->new->rebind = TIME_MAX; + + bind_lease(ip); +} + +void +bind_lease(struct interface_info *ip) +{ + /* Remember the medium. */ + ip->client->new->medium = ip->client->medium; + + /* Write out the new lease. */ + write_client_lease(ip, ip->client->new, 0); + + /* Run the client script with the new parameters. */ + script_init((ip->client->state == S_REQUESTING ? "BOUND" : + (ip->client->state == S_RENEWING ? "RENEW" : + (ip->client->state == S_REBOOTING ? "REBOOT" : "REBIND"))), + ip->client->new->medium); + if (ip->client->active && ip->client->state != S_REBOOTING) + script_write_params("old_", ip->client->active); + script_write_params("new_", ip->client->new); + if (ip->client->alias) + script_write_params("alias_", ip->client->alias); + script_go(); + + /* Replace the old active lease with the new one. */ + if (ip->client->active) + free_client_lease(ip->client->active); + ip->client->active = ip->client->new; + ip->client->new = NULL; + + /* Set up a timeout to start the renewal process. */ + add_timeout(ip->client->active->renewal, state_bound, ip); + + note("bound to %s -- renewal in %d seconds.", + piaddr(ip->client->active->address), + ip->client->active->renewal - cur_time); + ip->client->state = S_BOUND; + reinitialize_interfaces(); + go_daemon(); +} + +/* + * state_bound is called when we've successfully bound to a particular + * lease, but the renewal time on that lease has expired. We are + * expected to unicast a DHCPREQUEST to the server that gave us our + * original lease. + */ +void +state_bound(void *ipp) +{ + struct interface_info *ip = ipp; + + ASSERT_STATE(state, S_BOUND); + + /* T1 has expired. */ + make_request(ip, ip->client->active); + ip->client->xid = ip->client->packet.xid; + + if (ip->client->active->options[DHO_DHCP_SERVER_IDENTIFIER].len == 4) { + memcpy(ip->client->destination.iabuf, ip->client->active-> + options[DHO_DHCP_SERVER_IDENTIFIER].data, 4); + ip->client->destination.len = 4; + } else + ip->client->destination = iaddr_broadcast; + + ip->client->first_sending = cur_time; + ip->client->interval = ip->client->config->initial_interval; + ip->client->state = S_RENEWING; + + /* Send the first packet immediately. */ + send_request(ip); +} + +void +bootp(struct packet *packet) +{ + struct iaddrlist *ap; + + if (packet->raw->op != BOOTREPLY) + return; + + /* If there's a reject list, make sure this packet's sender isn't + on it. */ + for (ap = packet->interface->client->config->reject_list; + ap; ap = ap->next) { + if (addr_eq(packet->client_addr, ap->addr)) { + note("BOOTREPLY from %s rejected.", piaddr(ap->addr)); + return; + } + } + dhcpoffer(packet); +} + +void +dhcp(struct packet *packet) +{ + struct iaddrlist *ap; + void (*handler)(struct packet *); + char *type; + + switch (packet->packet_type) { + case DHCPOFFER: + handler = dhcpoffer; + type = "DHCPOFFER"; + break; + case DHCPNAK: + handler = dhcpnak; + type = "DHCPNACK"; + break; + case DHCPACK: + handler = dhcpack; + type = "DHCPACK"; + break; + default: + return; + } + + /* If there's a reject list, make sure this packet's sender isn't + on it. */ + for (ap = packet->interface->client->config->reject_list; + ap; ap = ap->next) { + if (addr_eq(packet->client_addr, ap->addr)) { + note("%s from %s rejected.", type, piaddr(ap->addr)); + return; + } + } + (*handler)(packet); +} + +void +dhcpoffer(struct packet *packet) +{ + struct interface_info *ip = packet->interface; + struct client_lease *lease, *lp; + int i; + int arp_timeout_needed, stop_selecting; + char *name = packet->options[DHO_DHCP_MESSAGE_TYPE].len ? + "DHCPOFFER" : "BOOTREPLY"; + + /* If we're not receptive to an offer right now, or if the offer + has an unrecognizable transaction id, then just drop it. */ + if (ip->client->state != S_SELECTING || + packet->interface->client->xid != packet->raw->xid || + (packet->interface->hw_address.hlen != packet->raw->hlen) || + (memcmp(packet->interface->hw_address.haddr, + packet->raw->chaddr, packet->raw->hlen))) + return; + + note("%s from %s", name, piaddr(packet->client_addr)); + + + /* If this lease doesn't supply the minimum required parameters, + blow it off. */ + for (i = 0; ip->client->config->required_options[i]; i++) { + if (!packet->options[ip->client->config-> + required_options[i]].len) { + note("%s isn't satisfactory.", name); + return; + } + } + + /* If we've already seen this lease, don't record it again. */ + for (lease = ip->client->offered_leases; + lease; lease = lease->next) { + if (lease->address.len == sizeof(packet->raw->yiaddr) && + !memcmp(lease->address.iabuf, + &packet->raw->yiaddr, lease->address.len)) { + debug("%s already seen.", name); + return; + } + } + + lease = packet_to_lease(packet); + if (!lease) { + note("packet_to_lease failed."); + return; + } + + /* If this lease was acquired through a BOOTREPLY, record that + fact. */ + if (!packet->options[DHO_DHCP_MESSAGE_TYPE].len) + lease->is_bootp = 1; + + /* Record the medium under which this lease was offered. */ + lease->medium = ip->client->medium; + + /* Send out an ARP Request for the offered IP address. */ + script_init("ARPSEND", lease->medium); + script_write_params("check_", lease); + /* If the script can't send an ARP request without waiting, + we'll be waiting when we do the ARPCHECK, so don't wait now. */ + if (script_go()) + arp_timeout_needed = 0; + else + arp_timeout_needed = 2; + + /* Figure out when we're supposed to stop selecting. */ + stop_selecting = + ip->client->first_sending + ip->client->config->select_interval; + + /* If this is the lease we asked for, put it at the head of the + list, and don't mess with the arp request timeout. */ + if (lease->address.len == ip->client->requested_address.len && + !memcmp(lease->address.iabuf, + ip->client->requested_address.iabuf, + ip->client->requested_address.len)) { + lease->next = ip->client->offered_leases; + ip->client->offered_leases = lease; + } else { + /* If we already have an offer, and arping for this + offer would take us past the selection timeout, + then don't extend the timeout - just hope for the + best. */ + if (ip->client->offered_leases && + (cur_time + arp_timeout_needed) > stop_selecting) + arp_timeout_needed = 0; + + /* Put the lease at the end of the list. */ + lease->next = NULL; + if (!ip->client->offered_leases) + ip->client->offered_leases = lease; + else { + for (lp = ip->client->offered_leases; lp->next; + lp = lp->next) + ; /* nothing */ + lp->next = lease; + } + } + + /* If we're supposed to stop selecting before we've had time + to wait for the ARPREPLY, add some delay to wait for + the ARPREPLY. */ + if (stop_selecting - cur_time < arp_timeout_needed) + stop_selecting = cur_time + arp_timeout_needed; + + /* If the selecting interval has expired, go immediately to + state_selecting(). Otherwise, time out into + state_selecting at the select interval. */ + if (stop_selecting <= 0) + state_selecting(ip); + else { + add_timeout(stop_selecting, state_selecting, ip); + cancel_timeout(send_discover, ip); + } +} + +/* Allocate a client_lease structure and initialize it from the parameters + in the specified packet. */ + +struct client_lease * +packet_to_lease(struct packet *packet) +{ + struct client_lease *lease; + int i; + + lease = malloc(sizeof(struct client_lease)); + + if (!lease) { + warning("dhcpoffer: no memory to record lease."); + return (NULL); + } + + memset(lease, 0, sizeof(*lease)); + + /* Copy the lease options. */ + for (i = 0; i < 256; i++) { + if (packet->options[i].len) { + lease->options[i].data = + malloc(packet->options[i].len + 1); + if (!lease->options[i].data) { + warning("dhcpoffer: no memory for option %d", i); + free_client_lease(lease); + return (NULL); + } else { + memcpy(lease->options[i].data, + packet->options[i].data, + packet->options[i].len); + lease->options[i].len = + packet->options[i].len; + lease->options[i].data[lease->options[i].len] = + 0; + } + if (!check_option(lease,i)) { + /* ignore a bogus lease offer */ + warning("Invalid lease option - ignoring offer"); + free_client_lease(lease); + return (NULL); + } + } + } + + lease->address.len = sizeof(packet->raw->yiaddr); + memcpy(lease->address.iabuf, &packet->raw->yiaddr, lease->address.len); + + /* If the server name was filled out, copy it. */ + if ((!packet->options[DHO_DHCP_OPTION_OVERLOAD].len || + !(packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2)) && + packet->raw->sname[0]) { + lease->server_name = malloc(DHCP_SNAME_LEN + 1); + if (!lease->server_name) { + warning("dhcpoffer: no memory for server name."); + free_client_lease(lease); + return (NULL); + } + memcpy(lease->server_name, packet->raw->sname, DHCP_SNAME_LEN); + lease->server_name[DHCP_SNAME_LEN]='\0'; + if (!res_hnok(lease->server_name) ) { + warning("Bogus server name %s", lease->server_name ); + free_client_lease(lease); + return (NULL); + } + + } + + /* Ditto for the filename. */ + if ((!packet->options[DHO_DHCP_OPTION_OVERLOAD].len || + !(packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1)) && + packet->raw->file[0]) { + /* Don't count on the NUL terminator. */ + lease->filename = malloc(DHCP_FILE_LEN + 1); + if (!lease->filename) { + warning("dhcpoffer: no memory for filename."); + free_client_lease(lease); + return (NULL); + } + memcpy(lease->filename, packet->raw->file, DHCP_FILE_LEN); + lease->filename[DHCP_FILE_LEN]='\0'; + } + return lease; +} + +void +dhcpnak(struct packet *packet) +{ + struct interface_info *ip = packet->interface; + + /* If we're not receptive to an offer right now, or if the offer + has an unrecognizable transaction id, then just drop it. */ + if (packet->interface->client->xid != packet->raw->xid || + (packet->interface->hw_address.hlen != packet->raw->hlen) || + (memcmp(packet->interface->hw_address.haddr, + packet->raw->chaddr, packet->raw->hlen))) + return; + + if (ip->client->state != S_REBOOTING && + ip->client->state != S_REQUESTING && + ip->client->state != S_RENEWING && + ip->client->state != S_REBINDING) + return; + + note("DHCPNAK from %s", piaddr(packet->client_addr)); + + if (!ip->client->active) { + note("DHCPNAK with no active lease.\n"); + return; + } + + free_client_lease(ip->client->active); + ip->client->active = NULL; + + /* Stop sending DHCPREQUEST packets... */ + cancel_timeout(send_request, ip); + + ip->client->state = S_INIT; + state_init(ip); +} + +/* Send out a DHCPDISCOVER packet, and set a timeout to send out another + one after the right interval has expired. If we don't get an offer by + the time we reach the panic interval, call the panic function. */ + +void +send_discover(void *ipp) +{ + struct interface_info *ip = ipp; + int interval, increase = 1; + + /* Figure out how long it's been since we started transmitting. */ + interval = cur_time - ip->client->first_sending; + + /* If we're past the panic timeout, call the script and tell it + we haven't found anything for this interface yet. */ + if (interval > ip->client->config->timeout) { + state_panic(ip); + return; + } + + /* If we're selecting media, try the whole list before doing + the exponential backoff, but if we've already received an + offer, stop looping, because we obviously have it right. */ + if (!ip->client->offered_leases && + ip->client->config->media) { + int fail = 0; +again: + if (ip->client->medium) { + ip->client->medium = ip->client->medium->next; + increase = 0; + } + if (!ip->client->medium) { + if (fail) + error("No valid media types for %s!", ip->name); + ip->client->medium = ip->client->config->media; + increase = 1; + } + + note("Trying medium \"%s\" %d", ip->client->medium->string, + increase); + script_init("MEDIUM", ip->client->medium); + if (script_go()) + goto again; + } + + /* + * If we're supposed to increase the interval, do so. If it's + * currently zero (i.e., we haven't sent any packets yet), set + * it to one; otherwise, add to it a random number between zero + * and two times itself. On average, this means that it will + * double with every transmission. + */ + if (increase) { + if (!ip->client->interval) + ip->client->interval = + ip->client->config->initial_interval; + else { + ip->client->interval += (arc4random() >> 2) % + (2 * ip->client->interval); + } + + /* Don't backoff past cutoff. */ + if (ip->client->interval > + ip->client->config->backoff_cutoff) + ip->client->interval = + ((ip->client->config->backoff_cutoff / 2) + + ((arc4random() >> 2) % + ip->client->config->backoff_cutoff)); + } else if (!ip->client->interval) + ip->client->interval = + ip->client->config->initial_interval; + + /* If the backoff would take us to the panic timeout, just use that + as the interval. */ + if (cur_time + ip->client->interval > + ip->client->first_sending + ip->client->config->timeout) + ip->client->interval = + (ip->client->first_sending + + ip->client->config->timeout) - cur_time + 1; + + /* Record the number of seconds since we started sending. */ + if (interval < 65536) + ip->client->packet.secs = htons(interval); + else + ip->client->packet.secs = htons(65535); + ip->client->secs = ip->client->packet.secs; + + note("DHCPDISCOVER on %s to %s port %d interval %d", + ip->name, inet_ntoa(sockaddr_broadcast.sin_addr), + ntohs(sockaddr_broadcast.sin_port), ip->client->interval); + + /* Send out a packet. */ + (void)send_packet(ip, &ip->client->packet, ip->client->packet_length, + inaddr_any, &sockaddr_broadcast, NULL); + + add_timeout(cur_time + ip->client->interval, send_discover, ip); +} + +/* + * state_panic gets called if we haven't received any offers in a preset + * amount of time. When this happens, we try to use existing leases + * that haven't yet expired, and failing that, we call the client script + * and hope it can do something. + */ +void +state_panic(void *ipp) +{ + struct interface_info *ip = ipp; + struct client_lease *loop = ip->client->active; + struct client_lease *lp; + + note("No DHCPOFFERS received."); + + /* We may not have an active lease, but we may have some + predefined leases that we can try. */ + if (!ip->client->active && ip->client->leases) + goto activate_next; + + /* Run through the list of leases and see if one can be used. */ + while (ip->client->active) { + if (ip->client->active->expiry > cur_time) { + note("Trying recorded lease %s", + piaddr(ip->client->active->address)); + /* Run the client script with the existing + parameters. */ + script_init("TIMEOUT", + ip->client->active->medium); + script_write_params("new_", ip->client->active); + if (ip->client->alias) + script_write_params("alias_", + ip->client->alias); + + /* If the old lease is still good and doesn't + yet need renewal, go into BOUND state and + timeout at the renewal time. */ + if (!script_go()) { + if (cur_time < + ip->client->active->renewal) { + ip->client->state = S_BOUND; + note("bound: renewal in %d seconds.", + ip->client->active->renewal - + cur_time); + add_timeout( + ip->client->active->renewal, + state_bound, ip); + } else { + ip->client->state = S_BOUND; + note("bound: immediate renewal."); + state_bound(ip); + } + reinitialize_interfaces(); + go_daemon(); + return; + } + } + + /* If there are no other leases, give up. */ + if (!ip->client->leases) { + ip->client->leases = ip->client->active; + ip->client->active = NULL; + break; + } + +activate_next: + /* Otherwise, put the active lease at the end of the + lease list, and try another lease.. */ + for (lp = ip->client->leases; lp->next; lp = lp->next) + ; + lp->next = ip->client->active; + if (lp->next) + lp->next->next = NULL; + ip->client->active = ip->client->leases; + ip->client->leases = ip->client->leases->next; + + /* If we already tried this lease, we've exhausted the + set of leases, so we might as well give up for + now. */ + if (ip->client->active == loop) + break; + else if (!loop) + loop = ip->client->active; + } + + /* No leases were available, or what was available didn't work, so + tell the shell script that we failed to allocate an address, + and try again later. */ + note("No working leases in persistent database - sleeping.\n"); + script_init("FAIL", NULL); + if (ip->client->alias) + script_write_params("alias_", ip->client->alias); + script_go(); + ip->client->state = S_INIT; + add_timeout(cur_time + ip->client->config->retry_interval, state_init, + ip); + go_daemon(); +} + +void +send_request(void *ipp) +{ + struct interface_info *ip = ipp; + struct sockaddr_in destination; + struct in_addr from; + int interval; + + /* Figure out how long it's been since we started transmitting. */ + interval = cur_time - ip->client->first_sending; + + /* If we're in the INIT-REBOOT or REQUESTING state and we're + past the reboot timeout, go to INIT and see if we can + DISCOVER an address... */ + /* XXX In the INIT-REBOOT state, if we don't get an ACK, it + means either that we're on a network with no DHCP server, + or that our server is down. In the latter case, assuming + that there is a backup DHCP server, DHCPDISCOVER will get + us a new address, but we could also have successfully + reused our old address. In the former case, we're hosed + anyway. This is not a win-prone situation. */ + if ((ip->client->state == S_REBOOTING || + ip->client->state == S_REQUESTING) && + interval > ip->client->config->reboot_timeout) { +cancel: + ip->client->state = S_INIT; + cancel_timeout(send_request, ip); + state_init(ip); + return; + } + + /* If we're in the reboot state, make sure the media is set up + correctly. */ + if (ip->client->state == S_REBOOTING && + !ip->client->medium && + ip->client->active->medium ) { + script_init("MEDIUM", ip->client->active->medium); + + /* If the medium we chose won't fly, go to INIT state. */ + if (script_go()) + goto cancel; + + /* Record the medium. */ + ip->client->medium = ip->client->active->medium; + } + + /* If the lease has expired, relinquish the address and go back + to the INIT state. */ + if (ip->client->state != S_REQUESTING && + cur_time > ip->client->active->expiry) { + /* Run the client script with the new parameters. */ + script_init("EXPIRE", NULL); + script_write_params("old_", ip->client->active); + if (ip->client->alias) + script_write_params("alias_", ip->client->alias); + script_go(); + + /* Now do a preinit on the interface so that we can + discover a new address. */ + script_init("PREINIT", NULL); + if (ip->client->alias) + script_write_params("alias_", ip->client->alias); + script_go(); + + ip->client->state = S_INIT; + state_init(ip); + return; + } + + /* Do the exponential backoff... */ + if (!ip->client->interval) + ip->client->interval = ip->client->config->initial_interval; + else + ip->client->interval += ((arc4random() >> 2) % + (2 * ip->client->interval)); + + /* Don't backoff past cutoff. */ + if (ip->client->interval > + ip->client->config->backoff_cutoff) + ip->client->interval = + ((ip->client->config->backoff_cutoff / 2) + + ((arc4random() >> 2) % ip->client->interval)); + + /* If the backoff would take us to the expiry time, just set the + timeout to the expiry time. */ + if (ip->client->state != S_REQUESTING && + cur_time + ip->client->interval > + ip->client->active->expiry) + ip->client->interval = + ip->client->active->expiry - cur_time + 1; + + /* If the lease T2 time has elapsed, or if we're not yet bound, + broadcast the DHCPREQUEST rather than unicasting. */ + memset(&destination, 0, sizeof(destination)); + if (ip->client->state == S_REQUESTING || + ip->client->state == S_REBOOTING || + cur_time > ip->client->active->rebind) + destination.sin_addr.s_addr = INADDR_BROADCAST; + else + memcpy(&destination.sin_addr.s_addr, + ip->client->destination.iabuf, + sizeof(destination.sin_addr.s_addr)); + destination.sin_port = htons(REMOTE_PORT); + destination.sin_family = AF_INET; + destination.sin_len = sizeof(destination); + + if (ip->client->state != S_REQUESTING) + memcpy(&from, ip->client->active->address.iabuf, + sizeof(from)); + else + from.s_addr = INADDR_ANY; + + /* Record the number of seconds since we started sending. */ + if (ip->client->state == S_REQUESTING) + ip->client->packet.secs = ip->client->secs; + else { + if (interval < 65536) + ip->client->packet.secs = htons(interval); + else + ip->client->packet.secs = htons(65535); + } + + note("DHCPREQUEST on %s to %s port %d", ip->name, + inet_ntoa(destination.sin_addr), ntohs(destination.sin_port)); + + /* Send out a packet. */ + (void) send_packet(ip, &ip->client->packet, ip->client->packet_length, + from, &destination, NULL); + + add_timeout(cur_time + ip->client->interval, send_request, ip); +} + +void +send_decline(void *ipp) +{ + struct interface_info *ip = ipp; + + note("DHCPDECLINE on %s to %s port %d", ip->name, + inet_ntoa(sockaddr_broadcast.sin_addr), + ntohs(sockaddr_broadcast.sin_port)); + + /* Send out a packet. */ + (void) send_packet(ip, &ip->client->packet, ip->client->packet_length, + inaddr_any, &sockaddr_broadcast, NULL); +} + +void +make_discover(struct interface_info *ip, struct client_lease *lease) +{ + unsigned char discover = DHCPDISCOVER; + struct tree_cache *options[256]; + struct tree_cache option_elements[256]; + int i; + + memset(option_elements, 0, sizeof(option_elements)); + memset(options, 0, sizeof(options)); + memset(&ip->client->packet, 0, sizeof(ip->client->packet)); + + /* Set DHCP_MESSAGE_TYPE to DHCPDISCOVER */ + i = DHO_DHCP_MESSAGE_TYPE; + options[i] = &option_elements[i]; + options[i]->value = &discover; + options[i]->len = sizeof(discover); + options[i]->buf_size = sizeof(discover); + options[i]->timeout = 0xFFFFFFFF; + + /* Request the options we want */ + i = DHO_DHCP_PARAMETER_REQUEST_LIST; + options[i] = &option_elements[i]; + options[i]->value = ip->client->config->requested_options; + options[i]->len = ip->client->config->requested_option_count; + options[i]->buf_size = + ip->client->config->requested_option_count; + options[i]->timeout = 0xFFFFFFFF; + + /* If we had an address, try to get it again. */ + if (lease) { + ip->client->requested_address = lease->address; + i = DHO_DHCP_REQUESTED_ADDRESS; + options[i] = &option_elements[i]; + options[i]->value = lease->address.iabuf; + options[i]->len = lease->address.len; + options[i]->buf_size = lease->address.len; + options[i]->timeout = 0xFFFFFFFF; + } else + ip->client->requested_address.len = 0; + + /* Send any options requested in the config file. */ + for (i = 0; i < 256; i++) + if (!options[i] && + ip->client->config->send_options[i].data) { + options[i] = &option_elements[i]; + options[i]->value = + ip->client->config->send_options[i].data; + options[i]->len = + ip->client->config->send_options[i].len; + options[i]->buf_size = + ip->client->config->send_options[i].len; + options[i]->timeout = 0xFFFFFFFF; + } + + /* Set up the option buffer... */ + ip->client->packet_length = cons_options(NULL, &ip->client->packet, 0, + options, 0, 0, 0, NULL, 0); + if (ip->client->packet_length < BOOTP_MIN_LEN) + ip->client->packet_length = BOOTP_MIN_LEN; + + ip->client->packet.op = BOOTREQUEST; + ip->client->packet.htype = ip->hw_address.htype; + ip->client->packet.hlen = ip->hw_address.hlen; + ip->client->packet.hops = 0; + ip->client->packet.xid = arc4random(); + ip->client->packet.secs = 0; /* filled in by send_discover. */ + ip->client->packet.flags = 0; + + memset(&(ip->client->packet.ciaddr), + 0, sizeof(ip->client->packet.ciaddr)); + memset(&(ip->client->packet.yiaddr), + 0, sizeof(ip->client->packet.yiaddr)); + memset(&(ip->client->packet.siaddr), + 0, sizeof(ip->client->packet.siaddr)); + memset(&(ip->client->packet.giaddr), + 0, sizeof(ip->client->packet.giaddr)); + memcpy(ip->client->packet.chaddr, + ip->hw_address.haddr, ip->hw_address.hlen); +} + + +void +make_request(struct interface_info *ip, struct client_lease * lease) +{ + unsigned char request = DHCPREQUEST; + struct tree_cache *options[256]; + struct tree_cache option_elements[256]; + int i; + + memset(options, 0, sizeof(options)); + memset(&ip->client->packet, 0, sizeof(ip->client->packet)); + + /* Set DHCP_MESSAGE_TYPE to DHCPREQUEST */ + i = DHO_DHCP_MESSAGE_TYPE; + options[i] = &option_elements[i]; + options[i]->value = &request; + options[i]->len = sizeof(request); + options[i]->buf_size = sizeof(request); + options[i]->timeout = 0xFFFFFFFF; + + /* Request the options we want */ + i = DHO_DHCP_PARAMETER_REQUEST_LIST; + options[i] = &option_elements[i]; + options[i]->value = ip->client->config->requested_options; + options[i]->len = ip->client->config->requested_option_count; + options[i]->buf_size = + ip->client->config->requested_option_count; + options[i]->timeout = 0xFFFFFFFF; + + /* If we are requesting an address that hasn't yet been assigned + to us, use the DHCP Requested Address option. */ + if (ip->client->state == S_REQUESTING) { + /* Send back the server identifier... */ + i = DHO_DHCP_SERVER_IDENTIFIER; + options[i] = &option_elements[i]; + options[i]->value = lease->options[i].data; + options[i]->len = lease->options[i].len; + options[i]->buf_size = lease->options[i].len; + options[i]->timeout = 0xFFFFFFFF; + } + if (ip->client->state == S_REQUESTING || + ip->client->state == S_REBOOTING) { + ip->client->requested_address = lease->address; + i = DHO_DHCP_REQUESTED_ADDRESS; + options[i] = &option_elements[i]; + options[i]->value = lease->address.iabuf; + options[i]->len = lease->address.len; + options[i]->buf_size = lease->address.len; + options[i]->timeout = 0xFFFFFFFF; + } else + ip->client->requested_address.len = 0; + + /* Send any options requested in the config file. */ + for (i = 0; i < 256; i++) + if (!options[i] && + ip->client->config->send_options[i].data) { + options[i] = &option_elements[i]; + options[i]->value = + ip->client->config->send_options[i].data; + options[i]->len = + ip->client->config->send_options[i].len; + options[i]->buf_size = + ip->client->config->send_options[i].len; + options[i]->timeout = 0xFFFFFFFF; + } + + /* Set up the option buffer... */ + ip->client->packet_length = cons_options(NULL, &ip->client->packet, 0, + options, 0, 0, 0, NULL, 0); + if (ip->client->packet_length < BOOTP_MIN_LEN) + ip->client->packet_length = BOOTP_MIN_LEN; + + ip->client->packet.op = BOOTREQUEST; + ip->client->packet.htype = ip->hw_address.htype; + ip->client->packet.hlen = ip->hw_address.hlen; + ip->client->packet.hops = 0; + ip->client->packet.xid = ip->client->xid; + ip->client->packet.secs = 0; /* Filled in by send_request. */ + + /* If we own the address we're requesting, put it in ciaddr; + otherwise set ciaddr to zero. */ + if (ip->client->state == S_BOUND || + ip->client->state == S_RENEWING || + ip->client->state == S_REBINDING) { + memcpy(&ip->client->packet.ciaddr, + lease->address.iabuf, lease->address.len); + ip->client->packet.flags = 0; + } else { + memset(&ip->client->packet.ciaddr, 0, + sizeof(ip->client->packet.ciaddr)); + ip->client->packet.flags = 0; + } + + memset(&ip->client->packet.yiaddr, 0, + sizeof(ip->client->packet.yiaddr)); + memset(&ip->client->packet.siaddr, 0, + sizeof(ip->client->packet.siaddr)); + memset(&ip->client->packet.giaddr, 0, + sizeof(ip->client->packet.giaddr)); + memcpy(ip->client->packet.chaddr, + ip->hw_address.haddr, ip->hw_address.hlen); +} + +void +make_decline(struct interface_info *ip, struct client_lease *lease) +{ + struct tree_cache *options[256], message_type_tree; + struct tree_cache requested_address_tree; + struct tree_cache server_id_tree, client_id_tree; + unsigned char decline = DHCPDECLINE; + int i; + + memset(options, 0, sizeof(options)); + memset(&ip->client->packet, 0, sizeof(ip->client->packet)); + + /* Set DHCP_MESSAGE_TYPE to DHCPDECLINE */ + i = DHO_DHCP_MESSAGE_TYPE; + options[i] = &message_type_tree; + options[i]->value = &decline; + options[i]->len = sizeof(decline); + options[i]->buf_size = sizeof(decline); + options[i]->timeout = 0xFFFFFFFF; + + /* Send back the server identifier... */ + i = DHO_DHCP_SERVER_IDENTIFIER; + options[i] = &server_id_tree; + options[i]->value = lease->options[i].data; + options[i]->len = lease->options[i].len; + options[i]->buf_size = lease->options[i].len; + options[i]->timeout = 0xFFFFFFFF; + + /* Send back the address we're declining. */ + i = DHO_DHCP_REQUESTED_ADDRESS; + options[i] = &requested_address_tree; + options[i]->value = lease->address.iabuf; + options[i]->len = lease->address.len; + options[i]->buf_size = lease->address.len; + options[i]->timeout = 0xFFFFFFFF; + + /* Send the uid if the user supplied one. */ + i = DHO_DHCP_CLIENT_IDENTIFIER; + if (ip->client->config->send_options[i].len) { + options[i] = &client_id_tree; + options[i]->value = ip->client->config->send_options[i].data; + options[i]->len = ip->client->config->send_options[i].len; + options[i]->buf_size = ip->client->config->send_options[i].len; + options[i]->timeout = 0xFFFFFFFF; + } + + + /* Set up the option buffer... */ + ip->client->packet_length = cons_options(NULL, &ip->client->packet, 0, + options, 0, 0, 0, NULL, 0); + if (ip->client->packet_length < BOOTP_MIN_LEN) + ip->client->packet_length = BOOTP_MIN_LEN; + + ip->client->packet.op = BOOTREQUEST; + ip->client->packet.htype = ip->hw_address.htype; + ip->client->packet.hlen = ip->hw_address.hlen; + ip->client->packet.hops = 0; + ip->client->packet.xid = ip->client->xid; + ip->client->packet.secs = 0; /* Filled in by send_request. */ + ip->client->packet.flags = 0; + + /* ciaddr must always be zero. */ + memset(&ip->client->packet.ciaddr, 0, + sizeof(ip->client->packet.ciaddr)); + memset(&ip->client->packet.yiaddr, 0, + sizeof(ip->client->packet.yiaddr)); + memset(&ip->client->packet.siaddr, 0, + sizeof(ip->client->packet.siaddr)); + memset(&ip->client->packet.giaddr, 0, + sizeof(ip->client->packet.giaddr)); + memcpy(ip->client->packet.chaddr, + ip->hw_address.haddr, ip->hw_address.hlen); +} + +void +free_client_lease(struct client_lease *lease) +{ + int i; + + if (lease->server_name) + free(lease->server_name); + if (lease->filename) + free(lease->filename); + for (i = 0; i < 256; i++) { + if (lease->options[i].len) + free(lease->options[i].data); + } + free(lease); +} + +FILE *leaseFile; + +void +rewrite_client_leases(void) +{ + struct client_lease *lp; + + if (!leaseFile) { + leaseFile = fopen(path_dhclient_db, "w"); + if (!leaseFile) + error("can't create %s: %m", path_dhclient_db); + } else { + fflush(leaseFile); + rewind(leaseFile); + } + + for (lp = ifi->client->leases; lp; lp = lp->next) + write_client_lease(ifi, lp, 1); + if (ifi->client->active) + write_client_lease(ifi, ifi->client->active, 1); + + fflush(leaseFile); + ftruncate(fileno(leaseFile), ftello(leaseFile)); + fsync(fileno(leaseFile)); +} + +void +write_client_lease(struct interface_info *ip, struct client_lease *lease, + int rewrite) +{ + static int leases_written; + struct tm *t; + int i; + + if (!rewrite) { + if (leases_written++ > 20) { + rewrite_client_leases(); + leases_written = 0; + } + } + + /* If the lease came from the config file, we don't need to stash + a copy in the lease database. */ + if (lease->is_static) + return; + + if (!leaseFile) { /* XXX */ + leaseFile = fopen(path_dhclient_db, "w"); + if (!leaseFile) + error("can't create %s: %m", path_dhclient_db); + } + + fprintf(leaseFile, "lease {\n"); + if (lease->is_bootp) + fprintf(leaseFile, " bootp;\n"); + fprintf(leaseFile, " interface \"%s\";\n", ip->name); + fprintf(leaseFile, " fixed-address %s;\n", piaddr(lease->address)); + if (lease->filename) + fprintf(leaseFile, " filename \"%s\";\n", lease->filename); + if (lease->server_name) + fprintf(leaseFile, " server-name \"%s\";\n", + lease->server_name); + if (lease->medium) + fprintf(leaseFile, " medium \"%s\";\n", lease->medium->string); + for (i = 0; i < 256; i++) + if (lease->options[i].len) + fprintf(leaseFile, " option %s %s;\n", + dhcp_options[i].name, + pretty_print_option(i, lease->options[i].data, + lease->options[i].len, 1, 1)); + + t = gmtime(&lease->renewal); + fprintf(leaseFile, " renew %d %d/%d/%d %02d:%02d:%02d;\n", + t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, + t->tm_hour, t->tm_min, t->tm_sec); + t = gmtime(&lease->rebind); + fprintf(leaseFile, " rebind %d %d/%d/%d %02d:%02d:%02d;\n", + t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, + t->tm_hour, t->tm_min, t->tm_sec); + t = gmtime(&lease->expiry); + fprintf(leaseFile, " expire %d %d/%d/%d %02d:%02d:%02d;\n", + t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, + t->tm_hour, t->tm_min, t->tm_sec); + fprintf(leaseFile, "}\n"); + fflush(leaseFile); +} + +void +script_init(char *reason, struct string_list *medium) +{ + size_t len, mediumlen = 0; + struct imsg_hdr hdr; + struct buf *buf; + int errs; + + if (medium != NULL && medium->string != NULL) + mediumlen = strlen(medium->string); + + hdr.code = IMSG_SCRIPT_INIT; + hdr.len = sizeof(struct imsg_hdr) + + sizeof(size_t) + mediumlen + + sizeof(size_t) + strlen(reason); + + if ((buf = buf_open(hdr.len)) == NULL) + error("buf_open: %m"); + + errs = 0; + errs += buf_add(buf, &hdr, sizeof(hdr)); + errs += buf_add(buf, &mediumlen, sizeof(mediumlen)); + if (mediumlen > 0) + errs += buf_add(buf, medium->string, mediumlen); + len = strlen(reason); + errs += buf_add(buf, &len, sizeof(len)); + errs += buf_add(buf, reason, len); + + if (errs) + error("buf_add: %m"); + + if (buf_close(privfd, buf) == -1) + error("buf_close: %m"); +} + +void +priv_script_init(char *reason, char *medium) +{ + struct interface_info *ip = ifi; + + if (ip) { + ip->client->scriptEnvsize = 100; + if (ip->client->scriptEnv == NULL) + ip->client->scriptEnv = + malloc(ip->client->scriptEnvsize * sizeof(char *)); + if (ip->client->scriptEnv == NULL) + error("script_init: no memory for environment"); + + ip->client->scriptEnv[0] = strdup(CLIENT_PATH); + if (ip->client->scriptEnv[0] == NULL) + error("script_init: no memory for environment"); + + ip->client->scriptEnv[1] = NULL; + + script_set_env(ip->client, "", "interface", ip->name); + + if (medium) + script_set_env(ip->client, "", "medium", medium); + + script_set_env(ip->client, "", "reason", reason); + } +} + +void +priv_script_write_params(char *prefix, struct client_lease *lease) +{ + struct interface_info *ip = ifi; + u_int8_t dbuf[1500]; + int i, len = 0; + char tbuf[128]; + + script_set_env(ip->client, prefix, "ip_address", + piaddr(lease->address)); + + if (lease->options[DHO_SUBNET_MASK].len && + (lease->options[DHO_SUBNET_MASK].len < + sizeof(lease->address.iabuf))) { + struct iaddr netmask, subnet, broadcast; + + memcpy(netmask.iabuf, lease->options[DHO_SUBNET_MASK].data, + lease->options[DHO_SUBNET_MASK].len); + netmask.len = lease->options[DHO_SUBNET_MASK].len; + + subnet = subnet_number(lease->address, netmask); + if (subnet.len) { + script_set_env(ip->client, prefix, "network_number", + piaddr(subnet)); + if (!lease->options[DHO_BROADCAST_ADDRESS].len) { + broadcast = broadcast_addr(subnet, netmask); + if (broadcast.len) + script_set_env(ip->client, prefix, + "broadcast_address", + piaddr(broadcast)); + } + } + } + + if (lease->filename) + script_set_env(ip->client, prefix, "filename", lease->filename); + if (lease->server_name) + script_set_env(ip->client, prefix, "server_name", + lease->server_name); + for (i = 0; i < 256; i++) { + u_int8_t *dp = NULL; + + if (ip->client->config->defaults[i].len) { + if (lease->options[i].len) { + switch ( + ip->client->config->default_actions[i]) { + case ACTION_DEFAULT: + dp = lease->options[i].data; + len = lease->options[i].len; + break; + case ACTION_SUPERSEDE: +supersede: + dp = ip->client-> + config->defaults[i].data; + len = ip->client-> + config->defaults[i].len; + break; + case ACTION_PREPEND: + len = ip->client-> + config->defaults[i].len + + lease->options[i].len; + if (len > sizeof(dbuf)) { + warning("no space to %s %s", + "prepend option", + dhcp_options[i].name); + goto supersede; + } + dp = dbuf; + memcpy(dp, + ip->client-> + config->defaults[i].data, + ip->client-> + config->defaults[i].len); + memcpy(dp + ip->client-> + config->defaults[i].len, + lease->options[i].data, + lease->options[i].len); + dp[len] = '\0'; + break; + case ACTION_APPEND: + len = ip->client-> + config->defaults[i].len + + lease->options[i].len; + if (len > sizeof(dbuf)) { + warning("no space to %s %s", + "append option", + dhcp_options[i].name); + goto supersede; + } + dp = dbuf; + memcpy(dp, + lease->options[i].data, + lease->options[i].len); + memcpy(dp + lease->options[i].len, + ip->client-> + config->defaults[i].data, + ip->client-> + config->defaults[i].len); + dp[len] = '\0'; + } + } else { + dp = ip->client-> + config->defaults[i].data; + len = ip->client-> + config->defaults[i].len; + } + } else if (lease->options[i].len) { + len = lease->options[i].len; + dp = lease->options[i].data; + } else { + len = 0; + } + if (len) { + char name[256]; + + if (dhcp_option_ev_name(name, sizeof(name), + &dhcp_options[i])) + script_set_env(ip->client, prefix, name, + pretty_print_option(i, dp, len, 0, 0)); + } + } + snprintf(tbuf, sizeof(tbuf), "%d", (int)lease->expiry); + script_set_env(ip->client, prefix, "expiry", tbuf); +} + +void +script_write_params(char *prefix, struct client_lease *lease) +{ + size_t fn_len = 0, sn_len = 0, pr_len = 0; + struct imsg_hdr hdr; + struct buf *buf; + int errs, i; + + if (lease->filename != NULL) + fn_len = strlen(lease->filename); + if (lease->server_name != NULL) + sn_len = strlen(lease->server_name); + if (prefix != NULL) + pr_len = strlen(prefix); + + hdr.code = IMSG_SCRIPT_WRITE_PARAMS; + hdr.len = sizeof(hdr) + sizeof(struct client_lease) + + sizeof(size_t) + fn_len + sizeof(size_t) + sn_len + + sizeof(size_t) + pr_len; + + for (i = 0; i < 256; i++) + hdr.len += sizeof(int) + lease->options[i].len; + + scripttime = time(NULL); + + if ((buf = buf_open(hdr.len)) == NULL) + error("buf_open: %m"); + + errs = 0; + errs += buf_add(buf, &hdr, sizeof(hdr)); + errs += buf_add(buf, lease, sizeof(struct client_lease)); + errs += buf_add(buf, &fn_len, sizeof(fn_len)); + errs += buf_add(buf, lease->filename, fn_len); + errs += buf_add(buf, &sn_len, sizeof(sn_len)); + errs += buf_add(buf, lease->server_name, sn_len); + errs += buf_add(buf, &pr_len, sizeof(pr_len)); + errs += buf_add(buf, prefix, pr_len); + + for (i = 0; i < 256; i++) { + errs += buf_add(buf, &lease->options[i].len, + sizeof(lease->options[i].len)); + errs += buf_add(buf, lease->options[i].data, + lease->options[i].len); + } + + if (errs) + error("buf_add: %m"); + + if (buf_close(privfd, buf) == -1) + error("buf_close: %m"); +} + +int +script_go(void) +{ + struct imsg_hdr hdr; + struct buf *buf; + int ret; + + scripttime = time(NULL); + + hdr.code = IMSG_SCRIPT_GO; + hdr.len = sizeof(struct imsg_hdr); + + if ((buf = buf_open(hdr.len)) == NULL) + error("buf_open: %m"); + + if (buf_add(buf, &hdr, sizeof(hdr))) + error("buf_add: %m"); + + if (buf_close(privfd, buf) == -1) + error("buf_close: %m"); + + bzero(&hdr, sizeof(hdr)); + buf_read(privfd, &hdr, sizeof(hdr)); + if (hdr.code != IMSG_SCRIPT_GO_RET) + error("unexpected msg type %u", hdr.code); + if (hdr.len != sizeof(hdr) + sizeof(int)) + error("received corrupted message"); + buf_read(privfd, &ret, sizeof(ret)); + + return (ret); +} + +int +priv_script_go(void) +{ + char *scriptName, *argv[2], **envp, *epp[3], reason[] = "REASON=NBI"; + static char client_path[] = CLIENT_PATH; + struct interface_info *ip = ifi; + int pid, wpid, wstatus; + + scripttime = time(NULL); + + if (ip) { + scriptName = ip->client->config->script_name; + envp = ip->client->scriptEnv; + } else { + scriptName = top_level_config.script_name; + epp[0] = reason; + epp[1] = client_path; + epp[2] = NULL; + envp = epp; + } + + argv[0] = scriptName; + argv[1] = NULL; + + pid = fork(); + if (pid < 0) { + error("fork: %m"); + wstatus = 0; + } else if (pid) { + do { + wpid = wait(&wstatus); + } while (wpid != pid && wpid > 0); + if (wpid < 0) { + error("wait: %m"); + wstatus = 0; + } + } else { + execve(scriptName, argv, envp); + error("execve (%s, ...): %m", scriptName); + } + + if (ip) + script_flush_env(ip->client); + + return (wstatus & 0xff); +} + +void +script_set_env(struct client_state *client, const char *prefix, + const char *name, const char *value) +{ + int i, j, namelen; + + namelen = strlen(name); + + for (i = 0; client->scriptEnv[i]; i++) + if (strncmp(client->scriptEnv[i], name, namelen) == 0 && + client->scriptEnv[i][namelen] == '=') + break; + + if (client->scriptEnv[i]) + /* Reuse the slot. */ + free(client->scriptEnv[i]); + else { + /* New variable. Expand if necessary. */ + if (i >= client->scriptEnvsize - 1) { + char **newscriptEnv; + int newscriptEnvsize = client->scriptEnvsize + 50; + + newscriptEnv = realloc(client->scriptEnv, + newscriptEnvsize); + if (newscriptEnv == NULL) { + free(client->scriptEnv); + client->scriptEnv = NULL; + client->scriptEnvsize = 0; + error("script_set_env: no memory for variable"); + } + client->scriptEnv = newscriptEnv; + client->scriptEnvsize = newscriptEnvsize; + } + /* need to set the NULL pointer at end of array beyond + the new slot. */ + client->scriptEnv[i + 1] = NULL; + } + /* Allocate space and format the variable in the appropriate slot. */ + client->scriptEnv[i] = malloc(strlen(prefix) + strlen(name) + 1 + + strlen(value) + 1); + if (client->scriptEnv[i] == NULL) + error("script_set_env: no memory for variable assignment"); + + /* No `` or $() command substitution allowed in environment values! */ + for (j=0; j < strlen(value); j++) + switch (value[j]) { + case '`': + case '$': + error("illegal character (%c) in value '%s'", value[j], + value); + /* not reached */ + } + snprintf(client->scriptEnv[i], strlen(prefix) + strlen(name) + + 1 + strlen(value) + 1, "%s%s=%s", prefix, name, value); +} + +void +script_flush_env(struct client_state *client) +{ + int i; + + for (i = 0; client->scriptEnv[i]; i++) { + free(client->scriptEnv[i]); + client->scriptEnv[i] = NULL; + } + client->scriptEnvsize = 0; +} + +int +dhcp_option_ev_name(char *buf, size_t buflen, struct option *option) +{ + int i; + + for (i = 0; option->name[i]; i++) { + if (i + 1 == buflen) + return 0; + if (option->name[i] == '-') + buf[i] = '_'; + else + buf[i] = option->name[i]; + } + + buf[i] = 0; + return 1; +} + +void +go_daemon(void) +{ + static int state = 0; + + if (no_daemon || state) + return; + + state = 1; + + /* Stop logging to stderr... */ + log_perror = 0; + + if (daemon(1, 0) == -1) + error("daemon"); + + /* we are chrooted, daemon(3) fails to open /dev/null */ + if (nullfd != -1) { + dup2(nullfd, STDIN_FILENO); + dup2(nullfd, STDOUT_FILENO); + dup2(nullfd, STDERR_FILENO); + close(nullfd); + nullfd = -1; + } +} + +int +check_option(struct client_lease *l, int option) +{ + char *opbuf; + char *sbuf; + + /* we use this, since this is what gets passed to dhclient-script */ + + opbuf = pretty_print_option(option, l->options[option].data, + l->options[option].len, 0, 0); + + sbuf = option_as_string(option, l->options[option].data, + l->options[option].len); + + switch (option) { + case DHO_SUBNET_MASK: + case DHO_TIME_SERVERS: + case DHO_NAME_SERVERS: + case DHO_ROUTERS: + case DHO_DOMAIN_NAME_SERVERS: + case DHO_LOG_SERVERS: + case DHO_COOKIE_SERVERS: + case DHO_LPR_SERVERS: + case DHO_IMPRESS_SERVERS: + case DHO_RESOURCE_LOCATION_SERVERS: + case DHO_SWAP_SERVER: + case DHO_BROADCAST_ADDRESS: + case DHO_NIS_SERVERS: + case DHO_NTP_SERVERS: + case DHO_NETBIOS_NAME_SERVERS: + case DHO_NETBIOS_DD_SERVER: + case DHO_FONT_SERVERS: + case DHO_DHCP_SERVER_IDENTIFIER: + if (!ipv4addrs(opbuf)) { + warning("Invalid IP address in option: %s", opbuf); + return (0); + } + return (1) ; + case DHO_HOST_NAME: + case DHO_DOMAIN_NAME: + case DHO_NIS_DOMAIN: + if (!res_hnok(sbuf)) { + warning("Bogus Host Name option %d: %s (%s)", option, + sbuf, opbuf); + return (0); + } + return (1); + case DHO_PAD: + case DHO_TIME_OFFSET: + case DHO_BOOT_SIZE: + case DHO_MERIT_DUMP: + case DHO_ROOT_PATH: + case DHO_EXTENSIONS_PATH: + case DHO_IP_FORWARDING: + case DHO_NON_LOCAL_SOURCE_ROUTING: + case DHO_POLICY_FILTER: + case DHO_MAX_DGRAM_REASSEMBLY: + case DHO_DEFAULT_IP_TTL: + case DHO_PATH_MTU_AGING_TIMEOUT: + case DHO_PATH_MTU_PLATEAU_TABLE: + case DHO_INTERFACE_MTU: + case DHO_ALL_SUBNETS_LOCAL: + case DHO_PERFORM_MASK_DISCOVERY: + case DHO_MASK_SUPPLIER: + case DHO_ROUTER_DISCOVERY: + case DHO_ROUTER_SOLICITATION_ADDRESS: + case DHO_STATIC_ROUTES: + case DHO_TRAILER_ENCAPSULATION: + case DHO_ARP_CACHE_TIMEOUT: + case DHO_IEEE802_3_ENCAPSULATION: + case DHO_DEFAULT_TCP_TTL: + case DHO_TCP_KEEPALIVE_INTERVAL: + case DHO_TCP_KEEPALIVE_GARBAGE: + case DHO_VENDOR_ENCAPSULATED_OPTIONS: + case DHO_NETBIOS_NODE_TYPE: + case DHO_NETBIOS_SCOPE: + case DHO_X_DISPLAY_MANAGER: + case DHO_DHCP_REQUESTED_ADDRESS: + case DHO_DHCP_LEASE_TIME: + case DHO_DHCP_OPTION_OVERLOAD: + case DHO_DHCP_MESSAGE_TYPE: + case DHO_DHCP_PARAMETER_REQUEST_LIST: + case DHO_DHCP_MESSAGE: + case DHO_DHCP_MAX_MESSAGE_SIZE: + case DHO_DHCP_RENEWAL_TIME: + case DHO_DHCP_REBINDING_TIME: + case DHO_DHCP_CLASS_IDENTIFIER: + case DHO_DHCP_CLIENT_IDENTIFIER: + case DHO_DHCP_USER_CLASS_ID: + case DHO_END: + return (1); + default: + warning("unknown dhcp option value 0x%x", option); + return (unknown_ok); + } +} + +int +res_hnok(const char *dn) +{ + int pch = PERIOD, ch = *dn++; + + while (ch != '\0') { + int nch = *dn++; + + if (periodchar(ch)) { + ; + } else if (periodchar(pch)) { + if (!borderchar(ch)) + return (0); + } else if (periodchar(nch) || nch == '\0') { + if (!borderchar(ch)) + return (0); + } else { + if (!middlechar(ch)) + return (0); + } + pch = ch, ch = nch; + } + return (1); +} + +/* Does buf consist only of dotted decimal ipv4 addrs? + * return how many if so, + * otherwise, return 0 + */ +int +ipv4addrs(char * buf) +{ + struct in_addr jnk; + int count = 0; + + while (inet_aton(buf, &jnk) == 1){ + count++; + while (periodchar(*buf) || digitchar(*buf)) + buf++; + if (*buf == '\0') + return (count); + while (*buf == ' ') + buf++; + } + return (0); +} + + +char * +option_as_string(unsigned int code, unsigned char *data, int len) +{ + static char optbuf[32768]; /* XXX */ + char *op = optbuf; + int opleft = sizeof(optbuf); + unsigned char *dp = data; + + if (code > 255) + error("option_as_string: bad code %d", code); + + 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 (opleft < 1) + goto toobig; + *op = 0; + return optbuf; +toobig: + warning("dhcp option too large"); + return ""; +} + +int +fork_privchld(int fd, int fd2) +{ + struct pollfd pfd[1]; + int nfds; + + switch (fork()) { + case -1: + error("cannot fork"); + case 0: + break; + default: + return (0); + } + + setproctitle("%s [priv]", ifi->name); + + dup2(nullfd, STDIN_FILENO); + dup2(nullfd, STDOUT_FILENO); + dup2(nullfd, STDERR_FILENO); + close(nullfd); + close(fd2); + + for (;;) { + pfd[0].fd = fd; + pfd[0].events = POLLIN; + if ((nfds = poll(pfd, 1, INFTIM)) == -1) + if (errno != EINTR) + error("poll error"); + + if (nfds == 0 || !(pfd[0].revents & POLLIN)) + continue; + + dispatch_imsg(fd); + } +} diff --git a/sbin/dhclient/dhclient.conf b/sbin/dhclient/dhclient.conf new file mode 100644 index 000000000000..147e0045a5d8 --- /dev/null +++ b/sbin/dhclient/dhclient.conf @@ -0,0 +1,36 @@ +send host-name "andare.fugue.com"; +send dhcp-client-identifier 1:0:a0:24:ab:fb:9c; +send dhcp-lease-time 3600; +supersede domain-name "fugue.com home.vix.com"; +prepend domain-name-servers 127.0.0.1; +request subnet-mask, broadcast-address, time-offset, routers, + domain-name, domain-name-servers, host-name; +require subnet-mask, domain-name-servers; +timeout 60; +retry 60; +reboot 10; +select-timeout 5; +initial-interval 2; +script "/etc/dhclient-script"; +media "-link0 -link1 -link2", "link0 link1"; +reject 192.33.137.209; + +alias { + interface "ep0"; + fixed-address 192.5.5.213; + option subnet-mask 255.255.255.255; +} + +lease { + interface "ep0"; + fixed-address 192.33.137.200; + medium "link0 link1"; + option host-name "andare.swiftmedia.com"; + option subnet-mask 255.255.255.0; + option broadcast-address 192.33.137.255; + option routers 192.33.137.250; + option domain-name-servers 127.0.0.1; + renew 2 2000/1/12 00:00:01; + rebind 2 2000/1/12 00:00:01; + expire 2 2000/1/12 00:00:01; +} diff --git a/sbin/dhclient/dhclient.conf.5 b/sbin/dhclient/dhclient.conf.5 new file mode 100644 index 000000000000..daf6f79c9537 --- /dev/null +++ b/sbin/dhclient/dhclient.conf.5 @@ -0,0 +1,541 @@ +.\" $OpenBSD: dhclient.conf.5,v 1.5 2004/11/01 23:10:18 henning Exp $ +.\" +.\" 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 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 DHCLIENT.CONF 5 +.Os +.Sh NAME +.Nm dhclient.conf +.Nd DHCP client configuration file +.Sh DESCRIPTION +The +.Nm +file contains configuration information for +.Xr dhclient 8 , +the Internet Software Consortium DHCP Client. +.Pp +The +.Nm +file is a free-form ASCII text file. +It is parsed by the recursive-descent parser built into +.Xr dhclient 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 +.Nm +file can be used to configure the behaviour of the client in a wide variety +of ways: protocol timing, information requested from the server, information +required of the server, defaults to use if the server does not provide +certain information, values with which to override information provided by +the server, or values to prepend or append to information provided by the +server. +The configuration file can also be preinitialized with addresses to +use on networks that don't have DHCP servers. +.Sh PROTOCOL TIMING +The timing behaviour of the client need not be configured by the user. +If no timing configuration is provided by the user, a fairly +reasonable timing behaviour will be used by default \- one which +results in fairly timely updates without placing an inordinate load on +the server. +.Pp +The following statements can be used to adjust the timing behaviour of +the DHCP client if required, however: +.Bl -tag -width Ds +.It Ic timeout Ar time ; +The +.Ic timeout +statement determines the amount of time that must pass between the +time that the client begins to try to determine its address and the +time that it decides that it's not going to be able to contact a server. +By default, this timeout is sixty seconds. +After the timeout has passed, if there are any static leases defined in the +configuration file, or any leases remaining in the lease database that +have not yet expired, the client will loop through these leases +attempting to validate them, and if it finds one that appears to be +valid, it will use that lease's address. +If there are no valid static leases or unexpired leases in the lease database, +the client will restart the protocol after the defined retry interval. +.It Ic retry Ar time ; +The +.Ic retry +statement determines the time that must pass after the client has +determined that there is no DHCP server present before it tries again +to contact a DHCP server. +By default, this is five minutes. +.It Ic select-timeout Ar time ; +It is possible (some might say desirable) for there to be more than +one DHCP server serving any given network. +In this case, it is possible that a client may be sent more than one offer +in response to its initial lease discovery message. +It may be that one of these offers is preferable to the other +(e.g., one offer may have the address the client previously used, +and the other may not). +.Pp +The +.Ic select-timeout +is the time after the client sends its first lease discovery request +at which it stops waiting for offers from servers, assuming that it +has received at least one such offer. +If no offers have been received by the time the +.Ic select-timeout +has expired, the client will accept the first offer that arrives. +.Pp +By default, the +.Ic select-timeout +is zero seconds \- that is, the client will take the first offer it sees. +.It Ic reboot Ar time ; +When the client is restarted, it first tries to reacquire the last +address it had. +This is called the INIT-REBOOT state. +If it is still attached to the same network it was attached to when it last +ran, this is the quickest way to get started. +The +.Ic reboot +statement sets the time that must elapse after the client first tries +to reacquire its old address before it gives up and tries to discover +a new address. +By default, the reboot timeout is ten seconds. +.It Ic backoff-cutoff Ar time ; +The client uses an exponential backoff algorithm with some randomness, +so that if many clients try to configure themselves at the same time, +they will not make their requests in lockstep. +The +.Ic backoff-cutoff +statement determines the maximum amount of time that the client is +allowed to back off. +It defaults to two minutes. +.It Ic initial-interval Ar time ; +The +.Ic initial-interval +statement sets the amount of time between the first attempt to reach a +server and the second attempt to reach a server. +Each time a message is sent, the interval between messages is incremented by +twice the current interval multiplied by a random number between zero and one. +If it is greater than the backoff-cutoff amount, it is set to that +amount. +It defaults to ten seconds. +.El +.Sh LEASE REQUIREMENTS AND REQUESTS +The DHCP protocol allows the client to request that the server send it +specific information, and not send it other information that it is not +prepared to accept. +The protocol also allows the client to reject offers from servers if they +don't contain information the client needs, or if the information provided +is not satisfactory. +.Pp +There is a variety of data contained in offers that DHCP servers send +to DHCP clients. +The data that can be specifically requested is what are called +.Em DHCP Options . +DHCP Options are defined in +.Xr dhcp-options 5 . +.Bl -tag -width Ds +.It Xo +.Ic request Op Ar option +.Oo , Ar ... option Oc ; +.Xc +The +.Ic request +statement causes the client to request that any server responding to the +client send the client its values for the specified options. +Only the option names should be specified in the request statement \- not +option parameters. +.It Xo +.Ic require Op Ar option +.Oo , Ar ... option Oc ; +.Xc +The +.Ic require +statement lists options that must be sent in order for an offer to be accepted. +Offers that do not contain all the listed options will be ignored. +.It Xo +.Ic send No { Op Ar option declaration +.Oo , Ar ... option declaration Oc } +.Xc +The +.Ic send +statement causes the client to send the specified options to the server with +the specified values. +These are full option declarations as described in +.Xr dhcp-options 5 . +Options that are always sent in the DHCP protocol should not be specified +here, except that the client can specify a +.Ar dhcp-lease-time +option other than the default requested lease time, which is two hours. +The other obvious use for this statement is to send information to the server +that will allow it to differentiate between this client and other +clients or kinds of clients. +.El +.Sh OPTION MODIFIERS +In some cases, a client may receive option data from the server which +is not really appropriate for that client, or may not receive +information that it needs, and for which a useful default value exists. +It may also receive information which is useful, but which needs to be +supplemented with local information. +To handle these needs, several option modifiers are available. +.Bl -tag -width Ds +.It Xo +.Ic default No { Op Ar option declaration +.Oo , Ar ... option declaration Oc } +.Xc +If for some set of options the client should use the value supplied by +the server, but needs to use some default value if no value was supplied +by the server, these values can be defined in the +.Ic default +statement. +.It Xo +.Ic supersede No { Op Ar option declaration +.Oo , Ar ... option declaration Oc } +.Xc +If for some set of options the client should always use its own value +rather than any value supplied by the server, these values can be defined +in the +.Ic supersede +statement. +.It Xo +.Ic prepend No { Op Ar option declaration +.Oo , Ar ... option declaration Oc } +.Xc +If for some set of options the client should use a value you supply, +and then use the values supplied by the server, if any, +these values can be defined in the +.Ic prepend +statement. +The +.Ic prepend +statement can only be used for options which allow more than one value to +be given. +This restriction is not enforced \- if violated, the results are unpredictable. +.It Xo +.Ic append No { Op Ar option declaration +.Oo , Ar ... option declaration Oc } +.Xc +If for some set of options the client should first use the values +supplied by the server, if any, and then use values you supply, these +values can be defined in the +.Ic append +statement. +The +.Ic append +statement can only be used for options which allow more than one value to +be given. +This restriction is not enforced \- if you ignore it, +the behaviour will be unpredictable. +.El +.Sh LEASE DECLARATIONS +The lease declaration: +.Pp +.Xo +.Ic \ \& lease No { Ar lease-declaration +.Oo Ar ... lease-declaration Oc } +.Xc +.Pp +The DHCP client may decide after some period of time (see +.Sx PROTOCOL TIMING ) +that it is not going to succeed in contacting a server. +At that time, it consults its own database of old leases and tests each one +that has not yet timed out by pinging the listed router for that lease to +see if that lease could work. +It is possible to define one or more +.Em fixed +leases in the client configuration file for networks where there is no DHCP +or BOOTP service, so that the client can still automatically configure its +address. +This is done with the +.Ic lease +statement. +.Pp +NOTE: the lease statement is also used in the +.Pa dhclient.leases +file in order to record leases that have been received from DHCP servers. +Some of the syntax for leases as described below is only needed in the +.Pa dhclient.leases +file. +Such syntax is documented here for completeness. +.Pp +A lease statement consists of the lease keyword, followed by a left +curly brace, followed by one or more lease declaration statements, +followed by a right curly brace. +The following lease declarations are possible: +.Bl -tag -width Ds +.It Ic bootp ; +The +.Ic bootp +statement is used to indicate that the lease was acquired using the +BOOTP protocol rather than the DHCP protocol. +It is never necessary to specify this in the client configuration file. +The client uses this syntax in its lease database file. +.It Ic interface Ar \&"string\&" ; +The +.Ic interface +lease statement is used to indicate the interface on which the lease is valid. +If set, this lease will only be tried on a particular interface. +When the client receives a lease from a server, it always records the +interface number on which it received that lease. +If predefined leases are specified in the +.Nm +file, the interface should also be specified, although this is not required. +.It Ic fixed-address Ar ip-address ; +The +.Ic fixed-address +statement is used to set the IP address of a particular lease. +This is required for all lease statements. +The IP address must be specified as a dotted quad (e.g., 12.34.56.78). +.It Ic filename Ar \&"string\&" ; +The +.Ic filename +statement specifies the name of the boot filename to use. +This is not used by the standard client configuration script, but is +included for completeness. +.It Ic server-name Ar \&"string\&" ; +The +.Ic server-name +statement specifies the name of the boot server name to use. +This is also not used by the standard client configuration script. +.It Ic option Ar option-declaration ; +The +.Ic option +statement is used to specify the value of an option supplied by the server, +or, in the case of predefined leases declared in +.Nm dhclient.conf , +the value that the user wishes the client configuration script to use if the +predefined lease is used. +.It Ic script Ar \&"script-name\&" ; +The +.Ic script +statement is used to specify the pathname of the DHCP client configuration +script. +This script is used by the DHCP client to set each interface's initial +configuration prior to requesting an address, to test the address once it +has been offered, and to set the interface's final configuration once a +lease has been acquired. +If no lease is acquired, the script is used to test predefined leases, if +any, and also called once if no valid lease can be identified. +For more information, see +.Xr dhclient.leases 5 . +.It Ic medium Ar \&"media setup\&" ; +The +.Ic medium +statement can be used on systems where network interfaces cannot +automatically determine the type of network to which they are connected. +The media setup string is a system-dependent parameter which is passed +to the DHCP client configuration script when initializing the interface. +On +.Ux +and UNIX-like systems, the argument is passed on the ifconfig command line +when configuring the interface. +.Pp +The DHCP client automatically declares this parameter if it used a +media type (see the +.Ic media +statement) when configuring the interface in order to obtain a lease. +This statement should be used in predefined leases only if the network +interface requires media type configuration. +.It Ic renew Ar date ; +.It Ic rebind Ar date ; +.It Ic expire Ar date ; +The +.Ic renew +statement defines the time at which the DHCP client should begin trying to +contact its server to renew a lease that it is using. +The +.Ic rebind +statement defines the time at which the DHCP client should begin to try to +contact +.Em any +DHCP server in order to renew its lease. +The +.Ic expire +statement defines the time at which the DHCP client must stop using a lease +if it has not been able to contact a server in order to renew it. +.El +.Pp +These declarations are automatically set in leases acquired by the +DHCP client, but must also be configured in predefined leases \- a +predefined lease whose expiry time has passed will not be used by the +DHCP client. +.Pp +Dates are specified as follows: +.Pp +.Ar \ \& +.Sm off +.Ar No / Ar No / Ar +.Ar : : +.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. +When declaring a predefined lease, it can always be specified as zero. +The year is specified with the century, so it should generally be four +digits except for really long leases. +The month is specified as a number starting with 1 for January. +The day of the month is likewise specified starting with 1. +The hour is a number between 0 and 23, +the minute a number between 0 and 59, +and the second also a number between 0 and 59. +.Sh ALIAS DECLARATIONS +.Ic alias No { Ar declarations ... No } +.Pp +Some DHCP clients running TCP/IP roaming protocols may require that in +addition to the lease they may acquire via DHCP, their interface also +be configured with a predefined IP alias so that they can have a +permanent IP address even while roaming. +The Internet Software Consortium DHCP client doesn't support roaming with +fixed addresses directly, but in order to facilitate such experimentation, +the DHCP client can be set up to configure an IP alias using the +.Ic alias +declaration. +.Pp +The +.Ic alias +declaration resembles a lease declaration, except that options other than +the subnet-mask option are ignored by the standard client configuration +script, and expiry times are ignored. +A typical alias declaration includes an interface declaration, a fixed-address +declaration for the IP alias address, and a subnet-mask option declaration. +A medium statement should never be included in an alias declaration. +.Sh OTHER DECLARATIONS +.Bl -tag -width Ds +.It Ic reject Ar ip-address ; +The +.Ic reject +statement causes the DHCP client to reject offers from servers who use +the specified address as a server identifier. +This can be used to avoid being configured by rogue or misconfigured DHCP +servers, although it should be a last resort \- better to track down +the bad DHCP server and fix it. +.It Xo +.Ic interface Ar \&"name\&" No { Ar declarations +.Ar ... No } +.Xc +A client with more than one network interface may require different +behaviour depending on which interface is being configured. +All timing parameters and declarations other than lease and alias +declarations can be enclosed in an interface declaration, and those +parameters will then be used only for the interface that matches the +specified name. +Interfaces for which there is no interface declaration will use the +parameters declared outside of any interface declaration, +or the default settings. +.It Xo +.Ic media Ar \&"media setup\&" +.Oo , Ar \&"media setup\&" , ... Oc ; +.Xc +The +.Ic media +statement defines one or more media configuration parameters which may +be tried while attempting to acquire an IP address. +The DHCP client will cycle through each media setup string on the list, +configuring the interface using that setup and attempting to boot, +and then trying the next one. +This can be used for network interfaces which aren't capable of sensing +the media type unaided \- whichever media type succeeds in getting a request +to the server and hearing the reply is probably right (no guarantees). +.Pp +The media setup is only used for the initial phase of address +acquisition (the DHCPDISCOVER and DHCPOFFER packets). +Once an address has been acquired, the DHCP client will record it in its +lease database and will record the media type used to acquire the address. +Whenever the client tries to renew the lease, it will use that same media type. +The lease must expire before the client will go back to cycling through media +types. +.El +.Sh EXAMPLES +The following configuration file is used on a laptop +which has an IP alias of 192.5.5.213, and has one interface, +ep0 (a 3Com 3C589C). +Booting intervals have been shortened somewhat from the default, because +the client is known to spend most of its time on networks with little DHCP +activity. +The laptop does roam to multiple networks. +.Bd -literal -offset indent +timeout 60; +retry 60; +reboot 10; +select-timeout 5; +initial-interval 2; +reject 192.33.137.209; + +interface "ep0" { + send host-name "andare.fugue.com"; + send dhcp-client-identifier 1:0:a0:24:ab:fb:9c; + send dhcp-lease-time 3600; + supersede domain-name "fugue.com rc.vix.com home.vix.com"; + prepend domain-name-servers 127.0.0.1; + request subnet-mask, broadcast-address, time-offset, routers, + domain-name, domain-name-servers, host-name; + require subnet-mask, domain-name-servers; + script "/etc/dhclient-script"; + media "media 10baseT/UTP", "media 10base2/BNC"; +} + +alias { + interface "ep0"; + fixed-address 192.5.5.213; + option subnet-mask 255.255.255.255; +} +.Ed +.Pp +This is a very complicated +.Nm +file \- in general, yours should be much simpler. +In many cases, it's sufficient to just create an empty +.Nm +file \- the defaults are usually fine. +.Sh SEE ALSO +.Xr dhclient.leases 5 , +.Xr dhcp-options 5 , +.Xr dhcpd.conf 5 , +.Xr dhclient 8 , +.Xr dhcpd 8 +.Pp +RFC 2132, RFC 2131. +.Sh AUTHORS +.Xr dhclient 8 +was written by +.An Ted Lemon Aq mellon@vix.com +under a contract with Vixie Labs. +.Pp +The current implementation was reworked by +.An Henning Brauer Aq henning@openbsd.org . diff --git a/sbin/dhclient/dhclient.leases.5 b/sbin/dhclient/dhclient.leases.5 new file mode 100644 index 000000000000..d5a8a2958767 --- /dev/null +++ b/sbin/dhclient/dhclient.leases.5 @@ -0,0 +1,87 @@ +.\" $OpenBSD: dhclient.leases.5,v 1.4 2004/04/15 08:59:47 jmc Exp $ +.\" +.\" 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 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 DHCLIENT.LEASES 5 +.Os +.Sh NAME +.Nm dhclient.leases +.Nd DHCP client lease database +.Sh DESCRIPTION +The Internet Software Consortium DHCP client keeps a persistent +database of leases that it has acquired that are still valid. +The database is a free-form ASCII file containing one valid declaration +per lease. +If more than one declaration appears for a given lease, +the last one in the file is used. +The file is written as a log, so this is not an unusual occurrence. +.Pp +The lease file is named +.Qq dhclient.leases.IFNAME , +where +.Qq IFNAME +represents the network interface the DHCP client acquired the lease on. +For example, if +.Xr dhclient 8 +is configured for the em0 network device, +the lease file will be named +.Qq dhclient.leases.em0 . +.Pp +The format of the lease declarations is described in +.Xr dhclient.conf 5 . +.Sh FILES +.Bl -tag -width "/var/db/dhclient.leases.IFNAME " +.It /var/db/dhclient.leases.IFNAME +Current lease file. +.El +.Sh SEE ALSO +.Xr dhclient.conf 5 , +.Xr dhcp-options 5 , +.Xr dhcpd.conf 5 , +.Xr dhclient 8 , +.Xr dhcpd 8 +.Pp +RFC 2132, RFC 2131. +.Sh AUTHORS +.Xr dhclient 8 +was written by +.An Ted Lemon Aq mellon@vix.com +under a contract with Vixie Labs. +.Pp +The current implementation was reworked by +.An Henning Brauer Aq henning@openbsd.org . diff --git a/sbin/dhclient/dhcp-options.5 b/sbin/dhclient/dhcp-options.5 new file mode 100644 index 000000000000..da3dd1c3c079 --- /dev/null +++ b/sbin/dhclient/dhcp-options.5 @@ -0,0 +1,590 @@ +.\" $OpenBSD: dhcp-options.5,v 1.5 2005/03/02 15:30:42 jmc Exp $ +.\" +.\" 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 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 DHCP-OPTIONS 5 +.Os +.Sh NAME +.Nm dhcp-options +.Nd Dynamic Host Configuration Protocol options +.Sh DESCRIPTION +The Dynamic Host Configuration protocol allows the client to receive +.Ic options +from the DHCP server describing the network configuration and various +services that are available on the network. +When configuring +.Xr dhcpd 8 +or +.Xr dhclient 8 , +options must often be declared. +The syntax for declaring options, and the names and formats of the options +that can be declared, are documented here. +.Sh REFERENCE: OPTION STATEMENTS +DHCP +.Ic option +statements always start with the +.Ic option +keyword, followed by an option name, followed by option data. +The option names and data formats are described below. +It is not necessary to exhaustively specify all DHCP options \- +only those options which are needed by clients must be specified. +.Pp +Option data comes in a variety of formats, as defined below: +.Pp +The +.Ar ip-address +data type can be entered either as an explicit IP address +(e.g., 239.254.197.10) or as a domain name (e.g., haagen.isc.org). +A domain name must resolve to a single IP address. +.Pp +The +.Ar int32 +data type specifies a signed 32-bit integer. +The +.Ar uint32 +data type specifies an unsigned 32-bit integer. +The +.Ar int16 +and +.Ar uint16 +data types specify signed and unsigned 16-bit integers. +The +.Ar int8 +and +.Ar uint8 +data types specify signed and unsigned 8-bit integers. +Unsigned 8-bit integers are also sometimes referred to as octets. +.Pp +The +.Ar string +data type specifies an +.Tn NVT +.Pq Network Virtual Terminal +.Tn ASCII +string, which must be enclosed in double quotes \- for example, +to specify a domain-name option, the syntax would be +.Pp +.Dl option domain-name \&"isc.org\&"; +.Pp +The +.Ar flag +data type specifies a boolean value. +Booleans can be either true or false +(or on or off, if that makes more sense to you). +.Pp +The +.Ar data-string +data type specifies either an +.Tn NVT ASCII +string enclosed in double quotes, or a series of octets specified in +hexadecimal, separated by colons. +For example: +.Pp +.Dl option dhcp-client-identifier \&"CLIENT-FOO\&"; +or +.Dl option dhcp-client-identifier 43:4c:49:45:54:2d:46:4f:4f; +.Pp +The documentation for the various options mentioned below is taken +from the IETF draft document on DHCP options, RFC 2132. +Options which are not listed by name may be defined by the name +.Pf option\- Ns Ar nnn , +where +.Ar nnn +is the decimal number of the option code. +These options may be followed either by a string, enclosed in quotes, or by +a series of octets, expressed as two-digit hexadecimal numbers separated +by colons. +For example: +.Bd -literal -offset indent +option option-133 "my-option-133-text"; +option option-129 1:54:c9:2b:47; +.Ed +.Pp +Because +.Xr dhcpd 8 +does not know the format of these undefined option codes, +no checking is done to ensure the correctness of the entered data. +.Pp +The standard options are: +.Ss RFC 1497 Vendor Extensions +.Bl -tag -width Ds +.It Ic option subnet-mask Ar ip-address ; +The +.Ic subnet-mask +option specifies the client's subnet mask as per RFC 950. +If no subnet-mask option is provided anywhere in scope, as a last resort +.Xr dhcpd 8 +will use the subnet mask from the subnet declaration for the network on +which an address is being assigned. +However, +.Em any +subnet-mask option declaration that is in scope for the address being +assigned will override the subnet mask specified in the subnet declaration. +.It Ic option time-offset Ar int32 ; +The +.Ic time-offset +option specifies the offset of the client's subnet in seconds from +Coordinated Universal Time (UTC). +.It Xo +.Ic option routers Ar ip-address +.Oo , Ar ip-address ... Oc ; +.Xc +The +.Ic routers +option specifies a list of IP addresses for routers on the client's subnet. +Routers should be listed in order of preference. +.It Xo +.Ic option time-servers Ar ip-address +.Oo , Ar ip-address ... Oc ; +.Xc +The +.Ic time-server +option specifies a list of RFC 868 time servers available to the client. +Servers should be listed in order of preference. +.It Xo +.Ic option ien116-name-servers Ar ip-address +.Oo , Ar ip-address ... Oc ; +.Xc +The +.Ic ien116-name-servers +option specifies a list of IEN 116 name servers available to the client. +Servers should be listed in order of preference. +.It Xo +.Ic option domain-name-servers Ar ip-address +.Oo , Ar ip-address ... Oc ; +.Xc +The +.Ic domain-name-servers +option specifies a list of Domain Name System (STD 13, RFC 1035) name servers +available to the client. +Servers should be listed in order of preference. +.It Xo +.Ic option log-servers Ar ip-address +.Oo , Ar ip-address ... Oc ; +.Xc +The +.Ic log-servers +option specifies a list of MIT-LCS UDP log servers available to the client. +Servers should be listed in order of preference. +.It Xo +.Ic option cookie-servers Ar ip-address +.Oo , Ar ip-address ... Oc ; +.Xc +The +.Ic cookie-servers +option specifies a list of RFC 865 cookie servers available to the client. +Servers should be listed in order of preference. +.It Xo +.Ic option lpr-servers Ar ip-address +.Oo , Ar ip-address ... Oc ; +.Xc +The +.Ic lpr-servers +option specifies a list of RFC 1179 line printer servers available to the +client. +Servers should be listed in order of preference. +.It Xo +.Ic option impress-servers Ar ip-address +.Oo , Ar ip-address ... Oc ; +.Xc +The +.Ic impress-servers +option specifies a list of Imagen Impress servers available to the client. +Servers should be listed in order of preference. +.It Xo +.Ic option resource-location-servers Ar ip-address +.Oo , Ar ip-address ... Oc ; +.Xc +This option specifies a list of RFC 887 Resource Location servers available +to the client. +Servers should be listed in order of preference. +.It Ic option host-name Ar string ; +This option specifies the name of the client. +The name may or may not be qualified with the local domain name +(it is preferable to use the +.Ic domain-name +option to specify the domain name). +See RFC 1035 for character set restrictions. +.It Ic option boot-size Ar uint16 ; +This option specifies the length in 512-octet blocks of the default +boot image for the client. +.It Ic option merit-dump Ar string ; +This option specifies the pathname of a file to which the client's +core image should be dumped in the event the client crashes. +The path is formatted as a character string consisting of characters from +the +.Tn NVT ASCII +character set. +.It Ic option domain-name Ar string ; +This option specifies the domain name that the client should use when +resolving hostnames via the Domain Name System. +.It Ic option swap-server Ar ip-address ; +This specifies the IP address of the client's swap server. +.It Ic option root-path Ar string ; +This option specifies the pathname that contains the client's root disk. +The path is formatted as a character string consisting of characters from +the +.Tn NVT ASCII +character set. +.El +.Ss IP Layer Parameters per Host +.Bl -tag -width Ds +.It Ic option ip-forwarding Ar flag ; +This option specifies whether the client should configure its IP layer +for packet forwarding. +A value of 0 means disable IP forwarding, and a value of 1 means enable +IP forwarding. +.It Ic option non-local-source-routing Ar flag ; +This option specifies whether the client should configure its IP +layer to allow forwarding of datagrams with non-local source routes +(see Section 3.3.5 of [4] for a discussion of this topic). +A value of 0 means disallow forwarding of such datagrams, and a value of 1 +means allow forwarding. +.It Xo +.Ic option policy-filter Ar ip-address ip-address +.Oo , Ar ip-address ip-address ... Oc ; +.Xc +This option specifies policy filters for non-local source routing. +The filters consist of a list of IP addresses and masks which specify +destination/mask pairs with which to filter incoming source routes. +.Pp +Any source-routed datagram whose next-hop address does not match one +of the filters should be discarded by the client. +.Pp +See STD 3 (RFC 1122) for further information. +.It Ic option max-dgram-reassembly Ar uint16 ; +This option specifies the maximum size datagram that the client should be +prepared to reassemble. +The minimum legal value is 576. +.It Ic option default-ip-ttl Ar uint8 ; +This option specifies the default time-to-live that the client should +use on outgoing datagrams. +.It Ic option path-mtu-aging-timeout Ar uint32 ; +This option specifies the timeout (in seconds) to use when aging Path +MTU values discovered by the mechanism defined in RFC 1191. +.It Xo +.Ic option path-mtu-plateau-table Ar uint16 +.Oo , Ar uint16 ... Oc ; +.Xc +This option specifies a table of MTU sizes to use when performing +Path MTU Discovery as defined in RFC 1191. +The table is formatted as a list of 16-bit unsigned integers, +ordered from smallest to largest. +The minimum MTU value cannot be smaller than 68. +.El +.Ss IP Layer Parameters per Interface +.Bl -tag -width Ds +.It Ic option interface-mtu Ar uint16 ; +This option specifies the MTU to use on this interface. +The minimum legal value for the MTU is 68. +.It Ic option all-subnets-local Ar flag ; +This option specifies whether or not the client may assume that all subnets +of the IP network to which the client is connected use the same MTU as the +subnet of that network to which the client is directly connected. +A value of 1 indicates that all subnets share the same MTU. +A value of 0 means that the client should assume that some subnets of the +directly connected network may have smaller MTUs. +.It Ic option broadcast-address Ar ip-address ; +This option specifies the broadcast address in use on the client's subnet. +Legal values for broadcast addresses are specified in section 3.2.1.3 of +STD 3 (RFC 1122). +.It Ic option perform-mask-discovery Ar flag ; +This option specifies whether or not the client should perform subnet mask +discovery using ICMP. +A value of 0 indicates that the client should not perform mask discovery. +A value of 1 means that the client should perform mask discovery. +.It Ic option mask-supplier Ar flag ; +This option specifies whether or not the client should respond to subnet mask +requests using ICMP. +A value of 0 indicates that the client should not respond. +A value of 1 means that the client should respond. +.It Ic option router-discovery Ar flag ; +This option specifies whether or not the client should solicit routers using +the Router Discovery mechanism defined in RFC 1256. +A value of 0 indicates that the client should not perform router discovery. +A value of 1 means that the client should perform router discovery. +.It Ic option router-solicitation-address Ar ip-address ; +This option specifies the address to which the client should transmit +router solicitation requests. +.It Xo +.Ic option static-routes Ar ip-address ip-address +.Oo , Ar ip-address ip-address ... Oc ; +.Xc +This option specifies a list of static routes that the client should +install in its routing cache. +If multiple routes to the same destination are specified, they are listed +in descending order of priority. +.Pp +The routes consist of a list of IP address pairs. +The first address is the destination address, +and the second address is the router for the destination. +.Pp +The default route (0.0.0.0) is an illegal destination for a static route. +To specify the default route, use the +.Ic routers +option. +.El +.Ss Link Layer Parameters per Interface +.Bl -tag -width Ds +.It Ic option trailer-encapsulation Ar flag ; +This option specifies whether or not the client should negotiate the +use of trailers (RFC 893 [14]) when using the ARP protocol. +A value of 0 indicates that the client should not attempt to use trailers. +A value of 1 means that the client should attempt to use trailers. +.It Ic option arp-cache-timeout Ar uint32 ; +This option specifies the timeout in seconds for ARP cache entries. +.It Ic option ieee802-3-encapsulation Ar flag ; +This option specifies whether or not the client should use Ethernet +Version 2 (RFC 894) or IEEE 802.3 (RFC 1042) encapsulation if the +interface is an Ethernet. +A value of 0 indicates that the client should use RFC 894 encapsulation. +A value of 1 means that the client should use RFC 1042 encapsulation. +.El +.Ss TCP Parameters +.Bl -tag -width Ds +.It Ic option default-tcp-ttl Ar uint8 ; +This option specifies the default TTL that the client should use when +sending TCP segments. +The minimum value is 1. +.It Ic option tcp-keepalive-interval Ar uint32 ; +This option specifies the interval (in seconds) that the client TCP +should wait before sending a keepalive message on a TCP connection. +The time is specified as a 32-bit unsigned integer. +A value of zero indicates that the client should not generate keepalive +messages on connections unless specifically requested by an application. +.It Ic option tcp-keepalive-garbage Ar flag ; +This option specifies whether or not the client should send TCP keepalive +messages with an octet of garbage for compatibility with older implementations. +A value of 0 indicates that a garbage octet should not be sent. +A value of 1 indicates that a garbage octet should be sent. +.El +.Ss Application and Service Parameters +.Bl -tag -width Ds +.It Ic option nis-domain Ar string ; +This option specifies the name of the client's NIS (Sun Network Information +Services) domain. +The domain is formatted as a character string consisting of characters +from the +.Tn NVT ASCII +character set. +.It Xo +.Ic option nis-servers Ar ip-address +.Oo , Ar ip-address ... Oc ; +.Xc +This option specifies a list of IP addresses indicating NIS servers +available to the client. +Servers should be listed in order of preference. +.It Xo +.Ic option ntp-servers Ar ip-address +.Oo , Ar ip-address ... Oc ; +.Xc +This option specifies a list of IP addresses indicating NTP (RFC 1035) +servers available to the client. +Servers should be listed in order of preference. +.It Xo +.Ic option netbios-name-servers Ar ip-address +.Oo , Ar ip-address ... Oc ; +.Xc +The NetBIOS name server (NBNS) option specifies a list of RFC 1001/1002 +NBNS name servers listed in order of preference. +NetBIOS Name Service is currently more commonly referred to as WINS. +WINS servers can be specified using the +.Ic netbios-name-servers +option. +.It Xo +.Ic option netbios-dd-server Ar ip-address +.Oo , Ar ip-address ... Oc ; +.Xc +The NetBIOS datagram distribution server (NBDD) option specifies a +list of RFC 1001/1002 NBDD servers listed in order of preference. +.It Ic option netbios-node-type Ar uint8 ; +The NetBIOS node type option allows NetBIOS over TCP/IP clients which +are configurable to be configured as described in RFC 1001/1002. +The value is specified as a single octet which identifies the client type. +.Pp +Possible node types are: +.Bl -tag -width Ds +.It 1 +B-node: Broadcast - no WINS +.It 2 +P-node: Peer - WINS only +.It 4 +M-node: Mixed - broadcast, then WINS +.It 8 +H-node: Hybrid - WINS, then broadcast +.El +.It Ic option netbios-scope Ar string ; +The NetBIOS scope option specifies the NetBIOS over TCP/IP scope +parameter for the client as specified in RFC 1001/1002. +See RFC 1001, RFC 1002, and RFC 1035 for character-set restrictions. +.It Xo +.Ic option font-servers Ar ip-address +.Oo , Ar ip-address ... Oc ; +.Xc +This option specifies a list of X Window System Font servers available +to the client. +Servers should be listed in order of preference. +.It Xo +.Ic option x-display-manager Ar ip-address +.Oo , Ar ip-address ... Oc ; +.Xc +This option specifies a list of systems that are running the X Window +System Display Manager and are available to the client. +Addresses should be listed in order of preference. +.It Ic option dhcp-client-identifier Ar data-string ; +This option can be used to specify a DHCP client identifier in a +host declaration, so that +.Xr dhcpd 8 +can find the host record by matching against the client identifier. +.It Ic option nisplus-domain Ar string ; +This option specifies the name of the client's NIS+ domain. +The domain is formatted as a character string consisting of characters +from the +.Tn NVT ASCII +character set. +.It Xo +.Ic option nisplus-servers Ar ip-address +.Oo , Ar ip-address ... Oc ; +.Xc +This option specifies a list of IP addresses indicating NIS+ servers +available to the client. +Servers should be listed in order of preference. +.It Ic option tftp-server-name Ar string ; +This option is used to identify a TFTP server and, if supported by the +client, should have the same effect as the +.Ic server-name +declaration. +BOOTP clients are unlikely to support this option. +Some DHCP clients will support it, and others actually require it. +.It Ic option bootfile-name Ar string ; +This option is used to identify a bootstrap file. +If supported by the client, it should have the same effect as the +.Ic filename +declaration. +BOOTP clients are unlikely to support this option. +Some DHCP clients will support it, and others actually require it. +.It Xo +.Ic option mobile-ip-home-agent Ar ip-address +.Oo , Ar ip-address ... Oc ; +.Xc +This option specifies a list of IP addresses indicating mobile IP +home agents available to the client. +Agents should be listed in order of preference, although normally there +will be only one such agent. +.It Xo +.Ic option smtp-server Ar ip-address +.Oo , Ar ip-address ... Oc ; +.Xc +The +.Ic smtp-server +option specifies a list of SMTP servers available to the client. +Servers should be listed in order of preference. +.It Xo +.Ic option pop-server Ar ip-address +.Oo , Ar ip-address ... Oc ; +.Xc +The +.Ic pop-server +option specifies a list of POP3 servers available to the client. +Servers should be listed in order of preference. +.It Xo +.Ic option nntp-server Ar ip-address +.Oo , Ar ip-address ... Oc ; +.Xc +The +.Ic nntp-server +option specifies a list of NNTP servers available to the client. +Servers should be listed in order of preference. +.It Xo +.Ic option www-server Ar ip-address +.Oo , Ar ip-address ... Oc ; +.Xc +The +.Ic www-server +option specifies a list of WWW servers available to the client. +Servers should be listed in order of preference. +.It Xo +.Ic option finger-server Ar ip-address +.Oo , Ar ip-address ... Oc ; +.Xc +The +.Ic finger-server +option specifies a list of +.Xr finger 1 +servers available to the client. +Servers should be listed in order of preference. +.It Xo +.Ic option irc-server Ar ip-address +.Oo , Ar ip-address ... Oc ; +.Xc +The +.Ic irc-server +option specifies a list of IRC servers available to the client. +Servers should be listed in order of preference. +.It Xo +.Ic option streettalk-server Ar ip-address +.Oo , Ar ip-address ... Oc ; +.Xc +The +.Ic streettalk-server +option specifies a list of StreetTalk servers available to the client. +Servers should be listed in order of preference. +.It Xo +.Ic option streettalk-directory-assistance-server Ar ip-address +.Oo , Ar ip-address ... Oc ; +.Xc +The StreetTalk Directory Assistance (STDA) server option specifies a +list of STDA servers available to the client. +Servers should be listed in order of preference. +.El +.Sh SEE ALSO +.Xr dhclient.conf 5 , +.Xr dhcpd.conf 5 , +.Xr dhcpd.leases 5 , +.Xr dhclient 8 , +.Xr dhcpd 8 +.Pp +RFC 2131, RFC 2132. +.Sh AUTHORS +.Xr dhcpd 8 +was written by +.An Ted Lemon Aq mellon@vix.com +under a contract with Vixie Labs. +.Pp +The current implementation was reworked by +.An Henning Brauer Aq henning@openbsd.org . diff --git a/sbin/dhclient/dhcp.h b/sbin/dhclient/dhcp.h new file mode 100644 index 000000000000..33f51224a378 --- /dev/null +++ b/sbin/dhclient/dhcp.h @@ -0,0 +1,168 @@ +/* $OpenBSD: dhcp.h,v 1.5 2004/05/04 15:49:49 deraadt Exp $ */ + +/* 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 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 */ +#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/sbin/dhclient/dhcpd.h b/sbin/dhclient/dhcpd.h new file mode 100644 index 000000000000..8097f14b1de1 --- /dev/null +++ b/sbin/dhclient/dhcpd.h @@ -0,0 +1,437 @@ +/* $OpenBSD: dhcpd.h,v 1.33 2004/05/06 22:29:15 deraadt Exp $ */ + +/* + * Copyright (c) 2004 Henning Brauer + * 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 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 + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dhcp.h" +#include "tree.h" + +#define LOCAL_PORT 68 +#define REMOTE_PORT 67 + +struct option_data { + int len; + u_int8_t *data; +}; + +struct string_list { + struct string_list *next; + char *string; +}; + +struct iaddr { + int len; + unsigned char iabuf[16]; +}; + +struct iaddrlist { + struct iaddrlist *next; + struct iaddr addr; +}; + +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; + struct hardware *haddr; + struct option_data options[256]; +}; + +struct hardware { + u_int8_t htype; + u_int8_t hlen; + u_int8_t haddr[16]; +}; + +struct client_lease { + struct client_lease *next; + time_t expiry, renewal, rebind; + struct iaddr address; + char *server_name; + char *filename; + struct string_list *medium; + unsigned int is_static : 1; + unsigned int is_bootp : 1; + struct option_data options[256]; +}; + +/* 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 +}; + +struct client_config { + struct option_data defaults[256]; + enum { + ACTION_DEFAULT, + ACTION_SUPERSEDE, + ACTION_PREPEND, + ACTION_APPEND + } default_actions[256]; + + struct option_data send_options[256]; + u_int8_t required_options[256]; + u_int8_t requested_options[256]; + int requested_option_count; + time_t timeout; + time_t initial_interval; + time_t retry_interval; + time_t select_interval; + time_t reboot_timeout; + time_t backoff_cutoff; + struct string_list *media; + char *script_name; + enum { IGNORE, ACCEPT, PREFER } + bootp_policy; + struct string_list *medium; + struct iaddrlist *reject_list; +}; + +struct client_state { + struct client_lease *active; + struct client_lease *new; + struct client_lease *offered_leases; + struct client_lease *leases; + struct client_lease *alias; + enum dhcp_state state; + struct iaddr destination; + u_int32_t xid; + u_int16_t secs; + time_t first_sending; + time_t interval; + struct string_list *medium; + struct dhcp_packet packet; + int packet_length; + struct iaddr requested_address; + struct client_config *config; + char **scriptEnv; + int scriptEnvsize; + struct string_list *env; + int envc; +}; + +struct interface_info { + struct interface_info *next; + struct hardware hw_address; + struct in_addr primary_address; + char name[IFNAMSIZ]; + int rfdesc; + int wfdesc; + unsigned char *rbuf; + size_t rbuf_max; + size_t rbuf_offset; + size_t rbuf_len; + struct ifreq *ifp; + struct client_state *client; + int noifmedia; + int errors; + int dead; + u_int16_t index; +}; + +struct timeout { + struct timeout *next; + time_t when; + void (*func)(void *); + void *what; +}; + +struct protocol { + struct protocol *next; + int fd; + void (*handler)(struct protocol *); + void *local; +}; + +#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]; +}; + +/* Default path to dhcpd config file. */ +#define _PATH_DHCLIENT_CONF "/etc/dhclient.conf" +#define _PATH_DHCLIENT_DB "/var/db/dhclient.leases" +#define DHCPD_LOG_FACILITY LOG_DAEMON + +#define MAX_TIME 0x7fffffff +#define MIN_TIME 0 + +/* External definitions... */ + +/* options.c */ +int cons_options(struct packet *, struct dhcp_packet *, int, + struct tree_cache **, int, int, int, u_int8_t *, int); +char *pretty_print_option(unsigned int, + unsigned char *, int, int, int); +void do_packet(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 warning(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))); + +/* 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(char *); +int next_token(char **, FILE *); +int peek_token(char **, FILE *); + +/* parse.c */ +void skip_to_semi(FILE *); +int parse_semi(FILE *); +char *parse_string(FILE *); +int parse_ip_addr(FILE *, struct iaddr *); +void parse_hardware_param(FILE *, struct hardware *); +void parse_lease_time(FILE *, time_t *); +unsigned char *parse_numeric_aggregate(FILE *, unsigned char *, int *, + int, int, int); +void convert_num(unsigned char *, char *, int, int); +time_t parse_date(FILE *); + +/* tree.c */ +pair cons(caddr_t, pair); + +/* alloc.c */ +struct string_list *new_string_list(size_t size); +struct hash_table *new_hash_table(int); +struct hash_bucket *new_hash_bucket(void); + +/* bpf.c */ +int if_register_bpf(struct interface_info *); +void if_register_send(struct interface_info *); +void if_register_receive(struct interface_info *); +ssize_t send_packet(struct interface_info *, struct dhcp_packet *, size_t, + struct in_addr, struct sockaddr_in *, struct hardware *); +ssize_t receive_packet(struct interface_info *, unsigned char *, size_t, + struct sockaddr_in *, struct hardware *); + +/* dispatch.c */ +extern void (*bootp_packet_handler)(struct interface_info *, + struct dhcp_packet *, int, unsigned int, struct iaddr, struct hardware *); +void discover_interfaces(struct interface_info *); +void reinitialize_interfaces(void); +void dispatch(void); +void got_one(struct protocol *); +void add_timeout(time_t, void (*)(void *), void *); +void cancel_timeout(void (*)(void *), void *); +void add_protocol(char *, int, void (*)(struct protocol *), void *); +void remove_protocol(struct protocol *); +int interface_link_status(char *); + +/* hash.c */ +struct hash_table *new_hash(void); +void add_hash(struct hash_table *, unsigned char *, int, unsigned char *); +unsigned char *hash_lookup(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 struct hash_table universe_hash; +extern struct universe dhcp_universe; +void initialize_universes(void); + +/* convert.c */ +u_int32_t getULong(unsigned char *); +int32_t getLong(unsigned char *); +u_int16_t getUShort(unsigned char *); +int16_t getShort(unsigned char *); +void putULong(unsigned char *, u_int32_t); +void putLong(unsigned char *, int32_t); +void putUShort(unsigned char *, unsigned int); +void putShort(unsigned char *, int); + +/* inet.c */ +struct iaddr subnet_number(struct iaddr, struct iaddr); +struct iaddr broadcast_addr(struct iaddr, struct iaddr); +int addr_eq(struct iaddr, struct iaddr); +char *piaddr(struct iaddr); + +/* dhclient.c */ +extern char *path_dhclient_conf; +extern char *path_dhclient_db; +extern time_t cur_time; +extern int log_priority; +extern int log_perror; + +extern struct client_config top_level_config; + +void dhcpoffer(struct packet *); +void dhcpack(struct packet *); +void dhcpnak(struct packet *); + +void send_discover(void *); +void send_request(void *); +void send_decline(void *); + +void state_reboot(void *); +void state_init(void *); +void state_selecting(void *); +void state_requesting(void *); +void state_bound(void *); +void state_panic(void *); + +void bind_lease(struct interface_info *); + +void make_discover(struct interface_info *, struct client_lease *); +void make_request(struct interface_info *, struct client_lease *); +void make_decline(struct interface_info *, struct client_lease *); + +void free_client_lease(struct client_lease *); +void rewrite_client_leases(void); +void write_client_lease(struct interface_info *, struct client_lease *, int); + +void priv_script_init(char *, char *); +void priv_script_write_params(char *, struct client_lease *); +int priv_script_go(void); + +void script_init(char *, struct string_list *); +void script_write_params(char *, struct client_lease *); +int script_go(void); +void client_envadd(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(struct packet *); +void go_daemon(void); +void client_location_changed(void); + +void bootp(struct packet *); +void dhcp(struct packet *); + +/* packet.c */ +void assemble_hw_header(struct interface_info *, unsigned char *, + int *, struct hardware *); +void assemble_udp_ip_header(unsigned char *, int *, u_int32_t, u_int32_t, + unsigned int, unsigned char *, int); +ssize_t decode_hw_header(unsigned char *, int, struct hardware *); +ssize_t decode_udp_ip_header(unsigned char *, int, struct sockaddr_in *, + unsigned char *, int); + +/* ethernet.c */ +void assemble_ethernet_header(struct interface_info *, unsigned char *, + int *, struct hardware *); +ssize_t decode_ethernet_header(struct interface_info *, unsigned char *, + int, struct hardware *); + +/* clparse.c */ +int read_client_conf(void); +void read_client_leases(void); +void parse_client_statement(FILE *, struct interface_info *, + struct client_config *); +int parse_X(FILE *, u_int8_t *, int); +int parse_option_list(FILE *, u_int8_t *); +void parse_interface_declaration(FILE *, struct client_config *); +struct interface_info *interface_or_dummy(char *); +void make_client_state(struct interface_info *); +void make_client_config(struct interface_info *, struct client_config *); +void parse_client_lease_statement(FILE *, int); +void parse_client_lease_declaration(FILE *, struct client_lease *, + struct interface_info **); +struct option *parse_option_decl(FILE *, struct option_data *); +void parse_string_list(FILE *, struct string_list **, int); +void parse_reject_statement(FILE *, struct client_config *); + +/* privsep.c */ +struct buf *buf_open(size_t); +int buf_add(struct buf *, void *, size_t); +int buf_close(int, struct buf *); +ssize_t buf_read(int, void *, size_t); +void dispatch_imsg(int); diff --git a/sbin/dhclient/dhctoken.h b/sbin/dhclient/dhctoken.h new file mode 100644 index 000000000000..7b23242fbac0 --- /dev/null +++ b/sbin/dhclient/dhctoken.h @@ -0,0 +1,136 @@ +/* $OpenBSD: dhctoken.h,v 1.2 2004/02/04 12:16:56 henning Exp $ */ + +/* 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 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/sbin/dhclient/dispatch.c b/sbin/dhclient/dispatch.c new file mode 100644 index 000000000000..a0d7e90e865a --- /dev/null +++ b/sbin/dhclient/dispatch.c @@ -0,0 +1,495 @@ +/* $OpenBSD: dispatch.c,v 1.31 2004/09/21 04:07:03 david Exp $ */ + +/* + * Copyright 2004 Henning Brauer + * 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 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 + +#include +#include +#include + +struct protocol *protocols; +struct timeout *timeouts; +static struct timeout *free_timeouts; +static int interfaces_invalidated; +void (*bootp_packet_handler)(struct interface_info *, + struct dhcp_packet *, int, unsigned int, + struct iaddr, struct hardware *); + +static int interface_status(struct interface_info *ifinfo); + +/* + * 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(struct interface_info *iface) +{ + struct ifaddrs *ifap, *ifa; + struct sockaddr_in foo; + struct ifreq *tif; + + if (getifaddrs(&ifap) != 0) + error("getifaddrs failed"); + + for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { + if ((ifa->ifa_flags & IFF_LOOPBACK) || + (ifa->ifa_flags & IFF_POINTOPOINT) || + (!(ifa->ifa_flags & IFF_UP))) + continue; + + if (strcmp(iface->name, ifa->ifa_name)) + continue; + + /* + * 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; + + iface->index = foo->sdl_index; + iface->hw_address.hlen = foo->sdl_alen; + iface->hw_address.htype = HTYPE_ETHER; /* XXX */ + memcpy(iface->hw_address.haddr, + LLADDR(foo), foo->sdl_alen); + } else if (ifa->ifa_addr->sa_family == AF_INET) { + struct iaddr addr; + + memcpy(&foo, ifa->ifa_addr, sizeof(foo)); + if (foo.sin_addr.s_addr == htonl(INADDR_LOOPBACK)) + continue; + if (!iface->ifp) { + int len = IFNAMSIZ + ifa->ifa_addr->sa_len; + if ((tif = malloc(len)) == NULL) + 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); + iface->ifp = tif; + iface->primary_address = foo.sin_addr; + } + addr.len = 4; + memcpy(addr.iabuf, &foo.sin_addr.s_addr, addr.len); + } + } + + if (!iface->ifp) + error("%s: not found", iface->name); + + /* Register the interface... */ + if_register_receive(iface); + if_register_send(iface); + add_protocol(iface->name, iface->rfdesc, got_one, iface); + freeifaddrs(ifap); +} + +void +reinitialize_interfaces(void) +{ + 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(void) +{ + int count, i, to_msec, nfds = 0; + struct protocol *l; + struct pollfd *fds; + time_t howlong; + + for (l = protocols; l; l = l->next) + nfds++; + + fds = 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 indefinitely. + */ + 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. */ + for (i = 0, 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) { + time(&cur_time); + continue; + } else + error("poll: %m"); + } + + /* Get the current time... */ + time(&cur_time); + + i = 0; + for (l = protocols; l; l = l->next) { + struct interface_info *ip; + ip = l->local; + if ((fds[i].revents & (POLLIN | POLLHUP))) { + 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(struct protocol *l) +{ + struct sockaddr_in from; + struct hardware hfrom; + struct iaddr ifrom; + ssize_t result; + union { + /* + * Packet input buffer. Must be as large as largest + * possible MTU. + */ + unsigned char packbuf[4095]; + struct dhcp_packet packet; + } u; + struct interface_info *ip = l->local; + + if ((result = receive_packet(ip, u.packbuf, sizeof(u), &from, + &hfrom)) == -1) { + warning("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. */ + warning("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); +} + +void +add_timeout(time_t when, void (*where)(void *), void *what) +{ + struct timeout *t, *q; + + /* See if this timeout supersedes an existing timeout. */ + t = NULL; + 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 = 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 = NULL; +} + +void +cancel_timeout(void (*where)(void *), void *what) +{ + struct timeout *t, *q; + + /* Look for this timeout on the list, and unlink it if we find it. */ + t = NULL; + 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(char *name, int fd, void (*handler)(struct protocol *), + void *local) +{ + struct protocol *p; + + p = 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(struct protocol *proto) +{ + struct protocol *p, *next, *prev; + + prev = NULL; + for (p = protocols; p; p = next) { + next = p->next; + if (p == proto) { + if (prev) + prev->next = p->next; + else + protocols = p->next; + free(p); + } + } +} + +int +interface_link_status(char *ifname) +{ + struct ifmediareq ifmr; + int sock; + + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) + error("Can't create socket"); + + memset(&ifmr, 0, sizeof(ifmr)); + strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name)); + if (ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifmr) == -1) { + /* EINVAL -> link state unknown. treat as active */ + if (errno != EINVAL) + syslog(LOG_DEBUG, "ioctl(SIOCGIFMEDIA) on %s: %m", + ifname); + close(sock); + return (1); + } + close(sock); + + if (ifmr.ifm_status & IFM_AVALID) { + if ((ifmr.ifm_active & IFM_NMASK) == IFM_ETHER) { + if (ifmr.ifm_status & IFM_ACTIVE) + return (1); + else + return (0); + } + } + return (1); +} diff --git a/sbin/dhclient/errwarn.c b/sbin/dhclient/errwarn.c new file mode 100644 index 000000000000..03b75478457c --- /dev/null +++ b/sbin/dhclient/errwarn.c @@ -0,0 +1,234 @@ +/* $OpenBSD: errwarn.c,v 1.7 2004/05/04 22:23:01 mickey 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 + +#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 +warning(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/sbin/dhclient/hash.c b/sbin/dhclient/hash.c new file mode 100644 index 000000000000..4f1795b24351 --- /dev/null +++ b/sbin/dhclient/hash.c @@ -0,0 +1,119 @@ +/* $OpenBSD: hash.c,v 1.9 2004/05/10 15:30:47 deraadt Exp $ */ + +/* 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 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(unsigned char *, int, int); + +struct hash_table * +new_hash(void) +{ + struct hash_table *rv = new_hash_table(DEFAULT_HASH_SIZE); + + if (!rv) + return (rv); + memset(&rv->buckets[0], 0, + DEFAULT_HASH_SIZE * sizeof(struct hash_bucket *)); + return (rv); +} + +static int +do_hash(unsigned char *name, int len, int size) +{ + unsigned char *s = name; + int accum = 0, 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(struct hash_table *table, unsigned char *name, int len, + unsigned char *pointer) +{ + struct hash_bucket *bp; + int hashno; + + if (!table) + return; + if (!len) + len = strlen((char *)name); + + hashno = do_hash(name, len, table->hash_count); + bp = new_hash_bucket(); + + if (!bp) { + warning("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; +} + +unsigned char * +hash_lookup(struct hash_table *table, unsigned char *name, int len) +{ + struct hash_bucket *bp; + int hashno; + + if (!table) + return (NULL); + + 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 (NULL); +} diff --git a/sbin/dhclient/inet.c b/sbin/dhclient/inet.c new file mode 100644 index 000000000000..a52847596e12 --- /dev/null +++ b/sbin/dhclient/inet.c @@ -0,0 +1,118 @@ +/* $OpenBSD: inet.c,v 1.7 2004/05/04 21:48:16 deraadt Exp $ */ + +/* + * 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 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(struct iaddr addr, struct iaddr mask) +{ + struct iaddr rv; + int i; + + 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); +} + +/* + * 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(struct iaddr subnet, struct iaddr mask) +{ + struct iaddr rv; + int i; + + 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); +} + +int +addr_eq(struct iaddr addr1, struct iaddr addr2) +{ + if (addr1.len != addr2.len) + return (0); + return (memcmp(addr1.iabuf, addr2.iabuf, addr1.len) == 0); +} + +char * +piaddr(struct iaddr addr) +{ + static char pbuf[32]; + struct in_addr a; + char *s; + + memcpy(&a, &(addr.iabuf), sizeof(struct in_addr)); + + if (addr.len == 0) + strlcpy(pbuf, "", sizeof(pbuf)); + else { + s = inet_ntoa(a); + if (s != NULL) + strlcpy(pbuf, s, sizeof(pbuf)); + else + strlcpy(pbuf, "", sizeof(pbuf)); + } + return (pbuf); +} diff --git a/sbin/dhclient/options.c b/sbin/dhclient/options.c new file mode 100644 index 000000000000..9c8e8fc743f1 --- /dev/null +++ b/sbin/dhclient/options.c @@ -0,0 +1,717 @@ +/* $OpenBSD: options.c,v 1.15 2004/12/26 03:17:07 deraadt Exp $ */ + +/* 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 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 + +#define DHCP_OPTION_DATA +#include "dhcpd.h" + +int bad_options = 0; +int bad_options_max = 5; + +void parse_options(struct packet *); +void parse_option_buffer(struct packet *, unsigned char *, int); +int store_options(unsigned char *, int, struct tree_cache **, + unsigned char *, int, int, int, int); + + +/* + * Parse all available options out of the specified packet. + */ +void +parse_options(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(struct packet *packet, + unsigned char *buffer, int length) +{ + unsigned char *s, *t, *end = buffer + length; + int len, 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. Unfortunately 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++; + warning("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; + warning("Many bogus options seen in offers. " + "Taking this offer in spite of bogus " + "options - hope for the best!"); + } else { + warning("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 = calloc(1, len + 1))) + 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 = calloc(1, len + packet->options[code].len + 1); + 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; + free(packet->options[code].data); + 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(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], buffer[4096]; + int priority_len, main_buffer_size, mainbufix, bufix; + int option_size, 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 (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++] = (char)DHO_END; + while (mainbufix < DHCP_FILE_LEN) + outpacket->file[mainbufix++] = (char)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++] = (char)DHO_END; + while (mainbufix < DHCP_SNAME_LEN) + outpacket->file[mainbufix++] = (char)DHO_PAD; + } + } + return (length); +} + +/* + * Store all the requested options into the requested buffer. + */ +int +store_options(unsigned char *buffer, int buflen, struct tree_cache **options, + unsigned char *priority_list, int priority_len, int first_cutoff, + int second_cutoff, int terminate) +{ + int bufix = 0, option_stored[256], i, ix, 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; + + /* 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(unsigned int code, unsigned char *data, int len, + int emit_commas, int emit_quotes) +{ + static char optbuf[32768]; /* XXX */ + int hunksize = 0, numhunk = -1, numelem = 0; + char fmtbuf[32], *op = optbuf; + int i, j, k, 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", 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) { + warning("%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: + warning("%s: garbage in format string: %s", + dhcp_options[code].name, + &(dhcp_options[code].format[i])); + break; + } + } + + /* Check for too few bytes... */ + if (hunksize > len) { + warning("%s: expecting at least %d bytes; got %d", + dhcp_options[code].name, hunksize, len); + return (""); + } + /* Check for too many bytes... */ + if (numhunk == -1 && hunksize < len) + warning("%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) + warning("%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 || opcount == -1) + goto toobig; + opleft -= opcount; + dp += 4; + break; + case 'L': + opcount = snprintf(op, opleft, "%ld", + (unsigned long)getULong(dp)); + if (opcount >= opleft || opcount == -1) + goto toobig; + opleft -= opcount; + dp += 4; + break; + case 's': + opcount = snprintf(op, opleft, "%d", + getShort(dp)); + if (opcount >= opleft || opcount == -1) + goto toobig; + opleft -= opcount; + dp += 2; + break; + case 'S': + opcount = snprintf(op, opleft, "%d", + getUShort(dp)); + if (opcount >= opleft || opcount == -1) + goto toobig; + opleft -= opcount; + dp += 2; + break; + case 'b': + opcount = snprintf(op, opleft, "%d", + *(char *)dp++); + if (opcount >= opleft || opcount == -1) + goto toobig; + opleft -= opcount; + break; + case 'B': + opcount = snprintf(op, opleft, "%d", *dp++); + if (opcount >= opleft || opcount == -1) + goto toobig; + opleft -= opcount; + break; + case 'x': + opcount = snprintf(op, opleft, "%x", *dp++); + if (opcount >= opleft || opcount == -1) + goto toobig; + opleft -= opcount; + break; + case 'f': + opcount = strlcpy(op, + *dp++ ? "true" : "false", opleft); + if (opcount >= opleft) + goto toobig; + opleft -= opcount; + break; + default: + warning("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: + warning("dhcp option too large"); + return (""); +} + +void +do_packet(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) + free(tp.options[i].data); +} diff --git a/sbin/dhclient/packet.c b/sbin/dhclient/packet.c new file mode 100644 index 000000000000..1a22044d40a1 --- /dev/null +++ b/sbin/dhclient/packet.c @@ -0,0 +1,253 @@ +/* $OpenBSD: packet.c,v 1.9 2004/05/04 18:58:50 deraadt 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 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 +#include +#include +#include + +#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(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(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(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/sbin/dhclient/parse.c b/sbin/dhclient/parse.c new file mode 100644 index 000000000000..e8870bc12ae3 --- /dev/null +++ b/sbin/dhclient/parse.c @@ -0,0 +1,577 @@ +/* $OpenBSD: parse.c,v 1.11 2004/05/05 23:07:47 deraadt Exp $ */ + +/* 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 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(FILE *cfile) +{ + int brace_count = 0, token; + char *val; + + 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 == '\n') { + /* + * 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(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(FILE *cfile) +{ + char *val, *s; + int token; + + token = next_token(&val, cfile); + if (token != STRING) { + parse_warn("filename must be a string"); + skip_to_semi(cfile); + return (NULL); + } + s = malloc(strlen(val) + 1); + if (!s) + error("no memory for string %s.", val); + strlcpy(s, val, strlen(val) + 1); + + if (!parse_semi(cfile)) + return (NULL); + return (s); +} + +int +parse_ip_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(FILE *cfile, struct hardware *hardware) +{ + unsigned char *t; + int token, hlen; + char *val; + + 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, NULL, &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(FILE *cfile, time_t *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(FILE *cfile, unsigned char *buf, int *max, + int separator, int base, int size) +{ + unsigned char *bufp = buf, *s = NULL; + int token, count = 0; + char *val, *t; + pair c = NULL; + + if (!bufp && *max) { + bufp = 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 (NULL); + } + 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 (NULL); + } + /* + * 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 = 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 = 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(unsigned char *buf, char *str, int base, int size) +{ + int negative = 0, tval, max; + u_int32_t val = 0; + char *ptr = str; + + 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 { + warning("Bogus number: %s.", str); + break; + } + if (tval >= base) { + warning("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: + warning("value %s%o exceeds max (%d) for precision.", + negative ? "-" : "", val, max); + break; + case 16: + warning("value %s%x exceeds max (%d) for precision.", + negative ? "-" : "", val, max); + break; + default: + warning("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: + warning("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: + warning("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_t +parse_date(FILE *cfile) +{ + static int months[11] = { 31, 59, 90, 120, 151, 181, + 212, 243, 273, 304, 334 }; + int guess, token; + struct tm tm; + char *val; + + /* 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 (NULL); + } + 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 (NULL); + } + 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 (NULL); + } + + /* Month... */ + token = next_token(&val, cfile); + if (token != NUMBER) { + parse_warn("numeric month expected."); + if (token != SEMI) + skip_to_semi(cfile); + return (NULL); + } + 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 (NULL); + } + + /* Month... */ + token = next_token(&val, cfile); + if (token != NUMBER) { + parse_warn("numeric day of month expected."); + if (token != SEMI) + skip_to_semi(cfile); + return (NULL); + } + 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 (NULL); + } + 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 (NULL); + } + + /* Minute... */ + token = next_token(&val, cfile); + if (token != NUMBER) { + parse_warn("numeric minute expected."); + if (token != SEMI) + skip_to_semi(cfile); + return (NULL); + } + 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 (NULL); + } + + /* Minute... */ + token = next_token(&val, cfile); + if (token != NUMBER) { + parse_warn("numeric minute expected."); + if (token != SEMI) + skip_to_semi(cfile); + return (NULL); + } + 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 (NULL); + } + + /* 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/sbin/dhclient/privsep.c b/sbin/dhclient/privsep.c new file mode 100644 index 000000000000..cf47e56ceb6e --- /dev/null +++ b/sbin/dhclient/privsep.c @@ -0,0 +1,235 @@ +/* $OpenBSD: privsep.c,v 1.7 2004/05/10 18:34:42 deraadt Exp $ */ + +/* + * Copyright (c) 2004 Henning Brauer + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE, ABUSE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "dhcpd.h" +#include "privsep.h" + +struct buf * +buf_open(size_t len) +{ + struct buf *buf; + + if ((buf = calloc(1, sizeof(struct buf))) == NULL) + return (NULL); + if ((buf->buf = malloc(len)) == NULL) { + free(buf); + return (NULL); + } + buf->size = len; + + return (buf); +} + +int +buf_add(struct buf *buf, void *data, size_t len) +{ + if (buf->wpos + len > buf->size) + return (-1); + + memcpy(buf->buf + buf->wpos, data, len); + buf->wpos += len; + return (0); +} + +int +buf_close(int sock, struct buf *buf) +{ + ssize_t n; + + do { + n = write(sock, buf->buf + buf->rpos, buf->size - buf->rpos); + if (n != -1) + buf->rpos += n; + if (n == 0) { /* connection closed */ + errno = 0; + return (-1); + } + } while (n == -1 && (errno == EAGAIN || errno == EINTR)); + + if (buf->rpos < buf->size) + error("short write: wanted %lu got %ld bytes", + (unsigned long)buf->size, (long)buf->rpos); + + free(buf->buf); + free(buf); + return (n); +} + +ssize_t +buf_read(int sock, void *buf, size_t nbytes) +{ + ssize_t n, r = 0; + char *p = buf; + + do { + n = read(sock, p, nbytes); + if (n == 0) + error("connection closed"); + if (n != -1) { + r += n; + p += n; + nbytes -= n; + } + } while (n == -1 && (errno == EINTR || errno == EAGAIN)); + + if (n == -1) + error("buf_read: %m"); + + if (r < nbytes) + error("short read: wanted %lu got %ld bytes", + (unsigned long)nbytes, (long)r); + + return (r); +} + +void +dispatch_imsg(int fd) +{ + struct imsg_hdr hdr; + char *medium, *reason, *filename, + *servername, *prefix; + size_t medium_len, reason_len, filename_len, + servername_len, prefix_len, totlen; + struct client_lease lease; + int ret, i, optlen; + struct buf *buf; + + buf_read(fd, &hdr, sizeof(hdr)); + + switch (hdr.code) { + case IMSG_SCRIPT_INIT: + if (hdr.len < sizeof(hdr) + sizeof(size_t)) + error("corrupted message received"); + buf_read(fd, &medium_len, sizeof(medium_len)); + if (hdr.len < medium_len + sizeof(size_t) + sizeof(hdr) + + sizeof(size_t) || medium_len == SIZE_T_MAX) + error("corrupted message received"); + if (medium_len > 0) { + if ((medium = calloc(1, medium_len + 1)) == NULL) + error("%m"); + buf_read(fd, medium, medium_len); + } else + medium = NULL; + + buf_read(fd, &reason_len, sizeof(reason_len)); + if (hdr.len < medium_len + reason_len + sizeof(hdr) || + reason_len == SIZE_T_MAX) + error("corrupted message received"); + if (reason_len > 0) { + if ((reason = calloc(1, reason_len + 1)) == NULL) + error("%m"); + buf_read(fd, reason, reason_len); + } else + reason = NULL; + + priv_script_init(reason, medium); + free(reason); + free(medium); + break; + case IMSG_SCRIPT_WRITE_PARAMS: + bzero(&lease, sizeof lease); + totlen = sizeof(hdr) + sizeof(lease) + sizeof(size_t); + if (hdr.len < totlen) + error("corrupted message received"); + buf_read(fd, &lease, sizeof(lease)); + + buf_read(fd, &filename_len, sizeof(filename_len)); + totlen += filename_len + sizeof(size_t); + if (hdr.len < totlen || filename_len == SIZE_T_MAX) + error("corrupted message received"); + if (filename_len > 0) { + if ((filename = calloc(1, filename_len + 1)) == NULL) + error("%m"); + buf_read(fd, filename, filename_len); + } else + filename = NULL; + + buf_read(fd, &servername_len, sizeof(servername_len)); + totlen += servername_len + sizeof(size_t); + if (hdr.len < totlen || servername_len == SIZE_T_MAX) + error("corrupted message received"); + if (servername_len > 0) { + if ((servername = + calloc(1, servername_len + 1)) == NULL) + error("%m"); + buf_read(fd, servername, servername_len); + } else + servername = NULL; + + buf_read(fd, &prefix_len, sizeof(prefix_len)); + totlen += prefix_len; + if (hdr.len < totlen || prefix_len == SIZE_T_MAX) + error("corrupted message received"); + if (prefix_len > 0) { + if ((prefix = calloc(1, prefix_len + 1)) == NULL) + error("%m"); + buf_read(fd, prefix, prefix_len); + } else + prefix = NULL; + + for (i = 0; i < 256; i++) { + totlen += sizeof(optlen); + if (hdr.len < totlen) + error("corrupted message received"); + buf_read(fd, &optlen, sizeof(optlen)); + lease.options[i].data = NULL; + lease.options[i].len = optlen; + if (optlen > 0) { + totlen += optlen; + if (hdr.len < totlen || optlen == SIZE_T_MAX) + error("corrupted message received"); + lease.options[i].data = + calloc(1, optlen + 1); + if (lease.options[i].data == NULL) + error("%m"); + buf_read(fd, lease.options[i].data, optlen); + } + } + lease.server_name = servername; + lease.filename = filename; + + priv_script_write_params(prefix, &lease); + + free(servername); + free(filename); + free(prefix); + for (i = 0; i < 256; i++) + if (lease.options[i].len > 0) + free(lease.options[i].data); + break; + case IMSG_SCRIPT_GO: + if (hdr.len != sizeof(hdr)) + error("corrupted message received"); + + ret = priv_script_go(); + + hdr.code = IMSG_SCRIPT_GO_RET; + hdr.len = sizeof(struct imsg_hdr) + sizeof(int); + if ((buf = buf_open(hdr.len)) == NULL) + error("buf_open: %m"); + if (buf_add(buf, &hdr, sizeof(hdr))) + error("buf_add: %m"); + if (buf_add(buf, &ret, sizeof(ret))) + error("buf_add: %m"); + if (buf_close(fd, buf) == -1) + error("buf_close: %m"); + break; + default: + error("received unknown message, code %d", hdr.code); + } +} diff --git a/sbin/dhclient/privsep.h b/sbin/dhclient/privsep.h new file mode 100644 index 000000000000..f30284eeefeb --- /dev/null +++ b/sbin/dhclient/privsep.h @@ -0,0 +1,47 @@ +/* $OpenBSD: privsep.h,v 1.2 2004/05/04 18:51:18 henning Exp $ */ + +/* + * Copyright (c) 2004 Henning Brauer + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE, ABUSE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +struct buf { + u_char *buf; + size_t size; + size_t wpos; + size_t rpos; +}; + +enum imsg_code { + IMSG_NONE, + IMSG_SCRIPT_INIT, + IMSG_SCRIPT_WRITE_PARAMS, + IMSG_SCRIPT_GO, + IMSG_SCRIPT_GO_RET +}; + +struct imsg_hdr { + enum imsg_code code; + size_t len; +}; + +struct buf *buf_open(size_t); +int buf_add(struct buf *, void *, size_t); +int buf_close(int, struct buf *); +ssize_t buf_read(int sock, void *, size_t); diff --git a/sbin/dhclient/tables.c b/sbin/dhclient/tables.c new file mode 100644 index 000000000000..6648756952c9 --- /dev/null +++ b/sbin/dhclient/tables.c @@ -0,0 +1,430 @@ +/* $OpenBSD: tables.c,v 1.4 2004/05/04 20:28:40 deraadt Exp $ */ + +/* 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 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); + +struct hash_table universe_hash; + +void +initialize_universes(void) +{ + 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/sbin/dhclient/tree.c b/sbin/dhclient/tree.c new file mode 100644 index 000000000000..89ac450ac5ba --- /dev/null +++ b/sbin/dhclient/tree.c @@ -0,0 +1,56 @@ +/* $OpenBSD: tree.c,v 1.13 2004/05/06 22:29:15 deraadt Exp $ */ + +/* 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 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" + +extern int h_errno; + +pair +cons(caddr_t car, pair cdr) +{ + pair foo = calloc(1, sizeof(*foo)); + if (!foo) + error("no memory for cons."); + foo->car = car; + foo->cdr = cdr; + return (foo); +} diff --git a/sbin/dhclient/tree.h b/sbin/dhclient/tree.h new file mode 100644 index 000000000000..04e08e7c820c --- /dev/null +++ b/sbin/dhclient/tree.h @@ -0,0 +1,66 @@ +/* $OpenBSD: tree.h,v 1.5 2004/05/06 22:29:15 deraadt Exp $ */ + +/* 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 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; + +struct tree_cache { + unsigned char *value; + int len; + int buf_size; + time_t timeout; +}; + +struct universe { + char *name; + struct hash_table *hash; + struct option *options[256]; +}; + +struct option { + char *name; + char *format; + struct universe *universe; + unsigned char code; +};