summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHenning Brauer <henning@cvs.openbsd.org>2004-04-12 21:10:29 +0000
committerHenning Brauer <henning@cvs.openbsd.org>2004-04-12 21:10:29 +0000
commitb1339c0bdd504605ee687bffd8ccb87626831648 (patch)
tree4709b4fedb5590f9690b040e19aae526e5190d16
parent08ca88c4716478f9cb87ab3cd44fe45745a98cf0 (diff)
initial cut at split out dhcrelay, whacked from usr.sbin/dhcp/ and sbin/dhclient
-rw-r--r--usr.sbin/dhcrelay/Makefile11
-rw-r--r--usr.sbin/dhcrelay/bpf.c321
-rw-r--r--usr.sbin/dhcrelay/dhcp.h168
-rw-r--r--usr.sbin/dhcrelay/dhcpd.h290
-rw-r--r--usr.sbin/dhcrelay/dhcrelay.8157
-rw-r--r--usr.sbin/dhcrelay/dhcrelay.c402
-rw-r--r--usr.sbin/dhcrelay/dispatch.c496
-rw-r--r--usr.sbin/dhcrelay/errwarn.c199
-rw-r--r--usr.sbin/dhcrelay/packet.c255
-rw-r--r--usr.sbin/dhcrelay/tree.h107
10 files changed, 2406 insertions, 0 deletions
diff --git a/usr.sbin/dhcrelay/Makefile b/usr.sbin/dhcrelay/Makefile
new file mode 100644
index 00000000000..3b17dd22241
--- /dev/null
+++ b/usr.sbin/dhcrelay/Makefile
@@ -0,0 +1,11 @@
+# $OpenBSD: Makefile,v 1.1 2004/04/12 21:10:28 henning Exp $
+
+.include <bsd.own.mk>
+
+SRCS= dhcrelay.c dispatch.c errwarn.c bpf.c packet.c alloc.c
+PROG= dhcrelay
+MAN= dhcrelay.8
+
+CFLAGS+=-Wall
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/dhcrelay/bpf.c b/usr.sbin/dhcrelay/bpf.c
new file mode 100644
index 00000000000..657aad79af7
--- /dev/null
+++ b/usr.sbin/dhcrelay/bpf.c
@@ -0,0 +1,321 @@
+/* $OpenBSD: bpf.c,v 1.1 2004/04/12 21:10:28 henning Exp $ */
+
+/* BPF socket interface code, originally contributed by Archie Cobbs. */
+
+/*
+ * Copyright (c) 1995, 1996, 1998, 1999
+ * The Internet Software Consortium. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include "dhcpd.h"
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+
+#include <net/bpf.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <netinet/if_ether.h>
+
+#define BPF_FORMAT "/dev/bpf%d"
+
+/*
+ * Called by get_interface_list for each interface that's discovered.
+ * Opens a packet filter for each interface and adds it to the select
+ * mask.
+ */
+int
+if_register_bpf(struct interface_info *info)
+{
+ char filename[50];
+ int sock, b;
+
+ /* Open a BPF device */
+ for (b = 0; 1; b++) {
+ snprintf(filename, sizeof(filename), BPF_FORMAT, b);
+ sock = open(filename, O_RDWR, 0);
+ if (sock < 0) {
+ if (errno == EBUSY)
+ continue;
+ else
+ error("Can't find free bpf: %m");
+ } else
+ break;
+ }
+
+ /* Set the BPF device to point at this interface. */
+ if (ioctl(sock, BIOCSETIF, info->ifp) < 0)
+ error("Can't attach interface %s to bpf device %s: %m",
+ info->name, filename);
+
+ return (sock);
+}
+
+void
+if_register_send(struct interface_info *info)
+{
+ /*
+ * If we're using the bpf API for sending and receiving, we
+ * don't need to register this interface twice.
+ */
+ info->wfdesc = info->rfdesc;
+}
+
+/*
+ * Packet filter program...
+ *
+ * XXX: Changes to the filter program may require changes to the
+ * constant offsets used in if_register_send to patch the BPF program!
+ */
+struct bpf_insn dhcp_bpf_filter[] = {
+ /* Make sure this is an IP packet... */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 8),
+
+ /* Make sure it's a UDP packet... */
+ BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 6),
+
+ /* Make sure this isn't a fragment... */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20),
+ BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 4, 0),
+
+ /* Get the IP header length... */
+ BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14),
+
+ /* Make sure it's to the right port... */
+ BPF_STMT(BPF_LD + BPF_H + BPF_IND, 16),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 67, 0, 1), /* patch */
+
+ /* If we passed all the tests, ask for the whole packet. */
+ BPF_STMT(BPF_RET+BPF_K, (u_int)-1),
+
+ /* Otherwise, drop it. */
+ BPF_STMT(BPF_RET+BPF_K, 0),
+};
+
+int dhcp_bpf_filter_len = sizeof(dhcp_bpf_filter) / sizeof(struct bpf_insn);
+
+void
+if_register_receive(struct interface_info *info)
+{
+ struct bpf_version v;
+ struct bpf_program p;
+ int flag = 1, sz;
+
+ /* Open a BPF device and hang it on this interface... */
+ info->rfdesc = if_register_bpf(info);
+
+ /* Make sure the BPF version is in range... */
+ if (ioctl(info->rfdesc, BIOCVERSION, &v) < 0)
+ error("Can't get BPF version: %m");
+
+ if (v.bv_major != BPF_MAJOR_VERSION ||
+ v.bv_minor < BPF_MINOR_VERSION)
+ error("Kernel BPF version out of range - recompile dhcpd!");
+
+ /*
+ * Set immediate mode so that reads return as soon as a packet
+ * comes in, rather than waiting for the input buffer to fill
+ * with packets.
+ */
+ if (ioctl(info->rfdesc, BIOCIMMEDIATE, &flag) < 0)
+ error("Can't set immediate mode on bpf device: %m");
+
+ /* Get the required BPF buffer length from the kernel. */
+ if (ioctl(info->rfdesc, BIOCGBLEN, &sz) < 0)
+ error("Can't get bpf buffer length: %m");
+ info->rbuf_max = sz;
+ info->rbuf = malloc(info->rbuf_max);
+ if (!info->rbuf)
+ error("Can't allocate %lu bytes for bpf input buffer.",
+ (unsigned long)info->rbuf_max);
+ info->rbuf_offset = 0;
+ info->rbuf_len = 0;
+
+ /* Set up the bpf filter program structure. */
+ p.bf_len = dhcp_bpf_filter_len;
+ p.bf_insns = dhcp_bpf_filter;
+
+ /* Patch the server port into the BPF program...
+ *
+ * XXX: changes to filter program may require changes to the
+ * insn number(s) used below!
+ */
+ dhcp_bpf_filter[8].k = LOCAL_PORT;
+
+ if (ioctl(info->rfdesc, BIOCSETF, &p) < 0)
+ error("Can't install packet filter program: %m");
+}
+
+ssize_t
+send_packet(struct interface_info *interface, struct packet *packet,
+ struct dhcp_packet *raw, size_t len, struct in_addr from,
+ struct sockaddr_in *to, struct hardware *hto)
+{
+ unsigned char buf[256];
+ struct iovec iov[2];
+ int result, bufp = 0;
+
+ /* Assemble the headers... */
+ assemble_hw_header(interface, buf, &bufp, hto);
+ assemble_udp_ip_header(interface, buf, &bufp, from.s_addr,
+ to->sin_addr.s_addr, to->sin_port, (unsigned char *)raw, len);
+
+ /* Fire it off */
+ iov[0].iov_base = (char *)buf;
+ iov[0].iov_len = bufp;
+ iov[1].iov_base = (char *)raw;
+ iov[1].iov_len = len;
+
+ result = writev(interface->wfdesc, iov, 2);
+ if (result < 0)
+ warn("send_packet: %m");
+ return (result);
+}
+
+ssize_t
+receive_packet(struct interface_info *interface, unsigned char *buf,
+ size_t len, struct sockaddr_in *from, struct hardware *hfrom)
+{
+ int length = 0, offset = 0;
+ struct bpf_hdr hdr;
+
+ /*
+ * All this complexity is because BPF doesn't guarantee that
+ * only one packet will be returned at a time. We're getting
+ * what we deserve, though - this is a terrible abuse of the BPF
+ * interface. Sigh.
+ */
+
+ /* Process packets until we get one we can return or until we've
+ * done a read and gotten nothing we can return...
+ */
+ do {
+ /* If the buffer is empty, fill it. */
+ if (interface->rbuf_offset == interface->rbuf_len) {
+ length = read(interface->rfdesc, interface->rbuf,
+ interface->rbuf_max);
+ if (length <= 0)
+ return (length);
+ interface->rbuf_offset = 0;
+ interface->rbuf_len = length;
+ }
+
+ /*
+ * If there isn't room for a whole bpf header, something
+ * went wrong, but we'll ignore it and hope it goes
+ * away... XXX
+ */
+ if (interface->rbuf_len - interface->rbuf_offset <
+ sizeof(hdr)) {
+ interface->rbuf_offset = interface->rbuf_len;
+ continue;
+ }
+
+ /* Copy out a bpf header... */
+ memcpy(&hdr, &interface->rbuf[interface->rbuf_offset],
+ sizeof(hdr));
+
+ /*
+ * If the bpf header plus data doesn't fit in what's
+ * left of the buffer, stick head in sand yet again...
+ */
+ if (interface->rbuf_offset + hdr.bh_hdrlen + hdr.bh_caplen >
+ interface->rbuf_len) {
+ interface->rbuf_offset = interface->rbuf_len;
+ continue;
+ }
+
+ /*
+ * If the captured data wasn't the whole packet, or if
+ * the packet won't fit in the input buffer, all we can
+ * do is drop it.
+ */
+ if (hdr.bh_caplen != hdr.bh_datalen) {
+ interface->rbuf_offset += hdr.bh_hdrlen = hdr.bh_caplen;
+ continue;
+ }
+
+ /* Skip over the BPF header... */
+ interface->rbuf_offset += hdr.bh_hdrlen;
+
+ /* Decode the physical header... */
+ offset = decode_hw_header(interface,
+ interface->rbuf, interface->rbuf_offset, hfrom);
+
+ /*
+ * If a physical layer checksum failed (dunno of any
+ * physical layer that supports this, but WTH), skip
+ * this packet.
+ */
+ if (offset < 0) {
+ interface->rbuf_offset += hdr.bh_caplen;
+ continue;
+ }
+ interface->rbuf_offset += offset;
+ hdr.bh_caplen -= offset;
+
+ /* Decode the IP and UDP headers... */
+ offset = decode_udp_ip_header(interface, interface->rbuf,
+ interface->rbuf_offset, from, NULL, hdr.bh_caplen);
+
+ /* If the IP or UDP checksum was bad, skip the packet... */
+ if (offset < 0) {
+ interface->rbuf_offset += hdr.bh_caplen;
+ continue;
+ }
+ interface->rbuf_offset += offset;
+ hdr.bh_caplen -= offset;
+
+ /*
+ * If there's not enough room to stash the packet data,
+ * we have to skip it (this shouldn't happen in real
+ * life, though).
+ */
+ if (hdr.bh_caplen > len) {
+ interface->rbuf_offset += hdr.bh_caplen;
+ continue;
+ }
+
+ /* Copy out the data in the packet... */
+ memcpy(buf, interface->rbuf + interface->rbuf_offset,
+ hdr.bh_caplen);
+ interface->rbuf_offset += hdr.bh_caplen;
+ return (hdr.bh_caplen);
+ } while (!length);
+ return (0);
+}
diff --git a/usr.sbin/dhcrelay/dhcp.h b/usr.sbin/dhcrelay/dhcp.h
new file mode 100644
index 00000000000..2b6278dc8e0
--- /dev/null
+++ b/usr.sbin/dhcrelay/dhcp.h
@@ -0,0 +1,168 @@
+/* $OpenBSD: dhcp.h,v 1.1 2004/04/12 21:10:28 henning 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 <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#define DHCP_UDP_OVERHEAD (14 + /* Ethernet header */ \
+ 20 + /* IP header */ \
+ 8) /* UDP header */
+#define DHCP_SNAME_LEN 64
+#define DHCP_FILE_LEN 128
+#define DHCP_FIXED_NON_UDP 236
+#define DHCP_FIXED_LEN (DHCP_FIXED_NON_UDP + DHCP_UDP_OVERHEAD)
+ /* Everything but options. */
+#define DHCP_MTU_MAX 1500
+#define DHCP_OPTION_LEN (DHCP_MTU_MAX - DHCP_FIXED_LEN)
+
+#define BOOTP_MIN_LEN 300
+#define DHCP_MIN_LEN 548
+
+struct dhcp_packet {
+ u_int8_t op; /* Message opcode/type */
+ u_int8_t htype; /* Hardware addr type (see net/if_types.h) */
+ u_int8_t hlen; /* Hardware addr length */
+ u_int8_t hops; /* Number of relay agent hops from client */
+ u_int32_t xid; /* Transaction ID */
+ u_int16_t secs; /* Seconds since client started looking */
+ u_int16_t flags; /* Flag bits */
+ struct in_addr ciaddr; /* Client IP address (if already in use) */
+ struct in_addr yiaddr; /* Client IP address */
+ struct in_addr siaddr; /* IP address of next server to talk to */
+ struct in_addr giaddr; /* DHCP relay agent IP address */
+ unsigned char chaddr[16]; /* Client hardware address */
+ char sname[DHCP_SNAME_LEN]; /* Server name */
+ char file[DHCP_FILE_LEN]; /* Boot filename */
+ unsigned char options[DHCP_OPTION_LEN];
+ /* Optional parameters
+ (actual length dependent on MTU). */
+};
+
+/* BOOTP (rfc951) message types */
+#define BOOTREQUEST 1
+#define BOOTREPLY 2
+
+/* Possible values for flags field... */
+#define BOOTP_BROADCAST 32768L
+
+/* Possible values for hardware type (htype) field... */
+#define HTYPE_ETHER 1 /* Ethernet 10Mbps */
+#define HTYPE_IEEE802 6 /* IEEE 802.2 Token Ring... */
+#define HTYPE_FDDI 8 /* FDDI... */
+
+/* Magic cookie validating dhcp options field (and bootp vendor
+ extensions field). */
+#define DHCP_OPTIONS_COOKIE "\143\202\123\143"
+
+/* DHCP Option codes: */
+
+#define DHO_PAD 0
+#define DHO_SUBNET_MASK 1
+#define DHO_TIME_OFFSET 2
+#define DHO_ROUTERS 3
+#define DHO_TIME_SERVERS 4
+#define DHO_NAME_SERVERS 5
+#define DHO_DOMAIN_NAME_SERVERS 6
+#define DHO_LOG_SERVERS 7
+#define DHO_COOKIE_SERVERS 8
+#define DHO_LPR_SERVERS 9
+#define DHO_IMPRESS_SERVERS 10
+#define DHO_RESOURCE_LOCATION_SERVERS 11
+#define DHO_HOST_NAME 12
+#define DHO_BOOT_SIZE 13
+#define DHO_MERIT_DUMP 14
+#define DHO_DOMAIN_NAME 15
+#define DHO_SWAP_SERVER 16
+#define DHO_ROOT_PATH 17
+#define DHO_EXTENSIONS_PATH 18
+#define DHO_IP_FORWARDING 19
+#define DHO_NON_LOCAL_SOURCE_ROUTING 20
+#define DHO_POLICY_FILTER 21
+#define DHO_MAX_DGRAM_REASSEMBLY 22
+#define DHO_DEFAULT_IP_TTL 23
+#define DHO_PATH_MTU_AGING_TIMEOUT 24
+#define DHO_PATH_MTU_PLATEAU_TABLE 25
+#define DHO_INTERFACE_MTU 26
+#define DHO_ALL_SUBNETS_LOCAL 27
+#define DHO_BROADCAST_ADDRESS 28
+#define DHO_PERFORM_MASK_DISCOVERY 29
+#define DHO_MASK_SUPPLIER 30
+#define DHO_ROUTER_DISCOVERY 31
+#define DHO_ROUTER_SOLICITATION_ADDRESS 32
+#define DHO_STATIC_ROUTES 33
+#define DHO_TRAILER_ENCAPSULATION 34
+#define DHO_ARP_CACHE_TIMEOUT 35
+#define DHO_IEEE802_3_ENCAPSULATION 36
+#define DHO_DEFAULT_TCP_TTL 37
+#define DHO_TCP_KEEPALIVE_INTERVAL 38
+#define DHO_TCP_KEEPALIVE_GARBAGE 39
+#define DHO_NIS_DOMAIN 40
+#define DHO_NIS_SERVERS 41
+#define DHO_NTP_SERVERS 42
+#define DHO_VENDOR_ENCAPSULATED_OPTIONS 43
+#define DHO_NETBIOS_NAME_SERVERS 44
+#define DHO_NETBIOS_DD_SERVER 45
+#define DHO_NETBIOS_NODE_TYPE 46
+#define DHO_NETBIOS_SCOPE 47
+#define DHO_FONT_SERVERS 48
+#define DHO_X_DISPLAY_MANAGER 49
+#define DHO_DHCP_REQUESTED_ADDRESS 50
+#define DHO_DHCP_LEASE_TIME 51
+#define DHO_DHCP_OPTION_OVERLOAD 52
+#define DHO_DHCP_MESSAGE_TYPE 53
+#define DHO_DHCP_SERVER_IDENTIFIER 54
+#define DHO_DHCP_PARAMETER_REQUEST_LIST 55
+#define DHO_DHCP_MESSAGE 56
+#define DHO_DHCP_MAX_MESSAGE_SIZE 57
+#define DHO_DHCP_RENEWAL_TIME 58
+#define DHO_DHCP_REBINDING_TIME 59
+#define DHO_DHCP_CLASS_IDENTIFIER 60
+#define DHO_DHCP_CLIENT_IDENTIFIER 61
+#define DHO_DHCP_USER_CLASS_ID 77
+#define DHO_END 255
+
+/* DHCP message types. */
+#define DHCPDISCOVER 1
+#define DHCPOFFER 2
+#define DHCPREQUEST 3
+#define DHCPDECLINE 4
+#define DHCPACK 5
+#define DHCPNAK 6
+#define DHCPRELEASE 7
+#define DHCPINFORM 8
diff --git a/usr.sbin/dhcrelay/dhcpd.h b/usr.sbin/dhcrelay/dhcpd.h
new file mode 100644
index 00000000000..6b305814d71
--- /dev/null
+++ b/usr.sbin/dhcrelay/dhcpd.h
@@ -0,0 +1,290 @@
+/* $OpenBSD: dhcpd.h,v 1.1 2004/04/12 21:10:28 henning Exp $ */
+
+/*
+ * Copyright (c) 2004 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 1995, 1996, 1997, 1998, 1999
+ * The Internet Software Consortium. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include <sys/types.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <netdb.h>
+#include <paths.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+
+#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];
+ int got_requested_address;
+};
+
+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 DHCPD_LOG_FACILITY LOG_DAEMON
+
+#define MAX_TIME 0x7fffffff
+#define MIN_TIME 0
+
+/* External definitions... */
+
+/* errwarn.c */
+extern int warnings_occurred;
+void error(char *, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
+int warn(char *, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
+int note(char *, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
+int debug(char *, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
+
+/* 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 packet *, 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 *);
+
+/* packet.c */
+void assemble_hw_header(struct interface_info *, unsigned char *,
+ int *, struct hardware *);
+void assemble_udp_ip_header(struct interface_info *, unsigned char *,
+ int *, u_int32_t, u_int32_t, unsigned int, unsigned char *, int);
+ssize_t decode_hw_header(struct interface_info *, unsigned char *,
+ int, struct hardware *);
+ssize_t decode_udp_ip_header(struct interface_info *, unsigned char *,
+ int, struct sockaddr_in *, unsigned char *, int);
+
+
+/* crap */
+extern time_t cur_time;
+extern int log_priority;
+extern int log_perror;
diff --git a/usr.sbin/dhcrelay/dhcrelay.8 b/usr.sbin/dhcrelay/dhcrelay.8
new file mode 100644
index 00000000000..cc814ff5bfd
--- /dev/null
+++ b/usr.sbin/dhcrelay/dhcrelay.8
@@ -0,0 +1,157 @@
+.\" $OpenBSD: dhcrelay.8,v 1.1 2004/04/12 21:10:28 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 <mellon@fugue.com> in cooperation with Vixie
+.\" Enterprises. To learn more about the Internet Software Consortium,
+.\" see ``http://www.isc.org/isc''. To learn more about Vixie
+.\" Enterprises, see ``http://www.vix.com''.
+.\"
+.Dd January 1, 1997
+.Dt DHCRELAY 8
+.Os
+.Sh NAME
+.Nm dhcrelay
+.Nd Dynamic Host Configuration Protocol Relay Agent
+.Sh SYNOPSIS
+.Nm dhcrelay
+.Op Fl p Ar port
+.Op Fl d
+.Op Fl q
+.Xo
+.Oo Fl i Ar if0 Oo Ar ...
+.Fl i Ar ifN Oc Oc
+.Xc
+.Ar server0 Op Ar ... serverN
+.Sh DESCRIPTION
+The Internet Software Consortium DHCP Relay Agent,
+.Nm dhcrelay ,
+provides a means for relaying DHCP and BOOTP requests from a subnet to which
+no DHCP server is directly connected to one or more DHCP servers on other
+subnets.
+.Sh OPERATION
+The DHCP Relay Agent listens for DHCP requests on all interfaces
+attached to a host, unless one or more interfaces are specified on the
+command line with the
+.Fl i
+flag.
+.Pp
+When a query is received,
+.Nm
+forwards it to the list of DHCP servers specified on the command line.
+When a reply is received, it is broadcast or unicast on the network from
+whence the original request came.
+.Pp
+It is possible to specify a set of interfaces on which
+.Nm
+will listen, so that if
+.Nm
+is connected through one interface to a network on which there is no DHCP
+server, but is connected on another interface to a network on which there
+is a DHCP server, it will not relay DHCP and BOOTP requests from the network
+on which the server exists to that server.
+This is an imperfect solution.
+.Sh COMMAND LINE
+The names of the network interfaces that
+.Nm
+should attempt to configure may be specified on the command line using the
+.Fl i
+option.
+If no interface names are specified on the command line,
+.Nm
+will identify all network interfaces, eliminating non-broadcast interfaces
+if possible, and attempt to configure each interface.
+.Pp
+If
+.Nm
+should listen and transmit on a port other than the standard (port 67), the
+.Fl p
+flag may used.
+It should be followed by the UDP port number that
+.Nm
+should use.
+This is mostly useful for debugging purposes.
+If the
+.Fl p
+flag is specified, the relay agent will transmit responses to clients
+at a port number that is one greater than the one specified \- i.e., if
+you specify
+.Fl p
+67, then the relay agent will listen on port 67 and transmit to port 68.
+Transmissions to servers will be sent to the same port number
+that is specified with the
+.Fl p
+flag.
+.Pp
+.Nm
+will normally run in the foreground until it has configured
+an interface, and then will revert to running in the background.
+To force
+.Nm
+to always run as a foreground process, the
+.Fl d
+flag should be specified.
+This is useful when running
+.Nm
+under a debugger, or when running it out of inittab on System V systems.
+.Pp
+.Nm
+will normally print its network configuration on startup.
+This can be annoying in a system startup script \- to disable this
+behaviour, specify the
+.Fl q
+flag.
+.Pp
+The name of at least one DHCP server to which DHCP and BOOTP requests
+should be relayed must be specified on the command line.
+.Sh SEE ALSO
+.Xr dhclient 8 ,
+.Xr dhcpd 8
+.Pp
+RFC 2132, RFC 2131.
+.Sh AUTHORS
+.Nm
+has been written for the Internet Software Consortium by
+.An Ted Lemon Aq mellon@fugue.com
+in cooperation with Vixie Enterprises.
+To learn more about the Internet Software Consortium, see
+.Pa http://www.vix.com/isc .
+To learn more about Vixie Enterprises, see
+.Pa http://www.vix.com .
+.Sh BUGS
+Relayed DHCP traffic could actually safely be protected by IPsec, but
+like
+.Xr dhcpd 8
+and
+.Xr dhclient 8 ,
+.Nm
+will bypass IPsec for all its traffic.
diff --git a/usr.sbin/dhcrelay/dhcrelay.c b/usr.sbin/dhcrelay/dhcrelay.c
new file mode 100644
index 00000000000..6cc899cc72a
--- /dev/null
+++ b/usr.sbin/dhcrelay/dhcrelay.c
@@ -0,0 +1,402 @@
+/* dhcrelay.c
+
+ DHCP/BOOTP Relay Agent. */
+
+/*
+ * Copyright (c) 1997, 1998, 1999 The Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include "dhcpd.h"
+
+void usage(char *);
+void relay(struct interface_info *, struct dhcp_packet *, int,
+ unsigned int, struct iaddr, struct hardware *);
+char *print_hw_addr(int, int, unsigned char *);
+
+time_t cur_time;
+time_t default_lease_time = 43200; /* 12 hours... */
+time_t max_lease_time = 86400; /* 24 hours... */
+struct tree_cache *global_options [256];
+
+int log_perror = 1;
+
+/* Needed to prevent linking against conflex.c. */
+#if 0
+int lexline;
+int lexchar;
+char *token_line;
+char *tlname;
+#endif
+
+char *path_dhcrelay_pid = "/var/run/dhcrelay.pid";
+
+u_int16_t local_port;
+u_int16_t remote_port;
+int log_priority;
+struct interface_info *interfaces = NULL;
+
+struct server_list {
+ struct server_list *next;
+ struct sockaddr_in to;
+} *servers;
+
+static char copyright [] =
+"Copyright 1997, 1998, 1999 The Internet Software Consortium.";
+static char arr [] = "All rights reserved.";
+static char message [] = "Internet Software Consortium DHCP Relay Agent";
+static char contrib [] = "Please contribute if you find this software useful.";
+static char url [] = "For info, please visit http://www.isc.org/dhcp-contrib.html";
+
+int main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int i;
+ struct servent *ent;
+ struct server_list *sp = (struct server_list *)0;
+ int no_daemon = 0;
+ int quiet = 0;
+ char *s;
+
+ s = strrchr (argv [0], '/');
+ if (!s)
+ s = argv [0];
+ else
+ s++;
+
+ /* Initially, log errors to stderr as well as to syslogd. */
+ openlog (s, LOG_NDELAY, DHCPD_LOG_FACILITY);
+
+ setlogmask (LOG_UPTO (LOG_INFO));
+
+ for (i = 1; i < argc; i++) {
+ if (!strcmp (argv [i], "-p")) {
+ if (++i == argc)
+ usage (s);
+ local_port = htons (atoi (argv [i]));
+ debug ("binding to user-specified port %d",
+ ntohs (local_port));
+ } else if (!strcmp (argv [i], "-pf")) {
+ if (++i == argc)
+ usage (s);
+ path_dhcrelay_pid = argv [i];
+ } else if (!strcmp (argv [i], "-d")) {
+ no_daemon = 1;
+ } else if (!strcmp (argv [i], "-i")) {
+ if (++i == argc || interfaces != NULL) {
+ usage (s);
+ }
+ if ((interfaces = calloc(1,
+ sizeof(struct interface_info))) == NULL)
+ error("calloc");
+ strlcpy(interfaces -> name, argv [i],
+ sizeof(interfaces->name));
+ } else if (!strcmp (argv [i], "-q")) {
+ quiet = 1;
+ } else if (argv [i][0] == '-') {
+ usage (s);
+ } else {
+ struct hostent *he;
+ struct in_addr ia, *iap = (struct in_addr *)0;
+ if (inet_aton (argv [i], &ia)) {
+ iap = &ia;
+ } else {
+ he = gethostbyname (argv [i]);
+ if (!he) {
+ warn ("%s: host unknown", argv [i]);
+ } else {
+ iap = ((struct in_addr *)
+ he -> h_addr_list [0]);
+ }
+ }
+ if (iap) {
+ sp = calloc(1, sizeof *sp);
+ if (!sp)
+ error ("no memory for server.\n");
+ sp -> next = servers;
+ servers = sp;
+ memcpy (&sp -> to.sin_addr,
+ iap, sizeof *iap);
+ }
+ }
+ }
+
+ if (!quiet)
+ log_perror = 0;
+
+ if (interfaces == NULL)
+ error("no interface given");
+
+ /* Default to the DHCP/BOOTP port. */
+ if (!local_port) {
+ ent = getservbyname ("dhcps", "udp");
+ if (!ent)
+ local_port = htons (67);
+ else
+ local_port = ent -> s_port;
+ endservent ();
+ }
+ remote_port = htons (ntohs (local_port) + 1);
+
+ /* We need at least one server. */
+ if (!sp) {
+ usage (s);
+ }
+
+ /* Set up the server sockaddrs. */
+ for (sp = servers; sp; sp = sp -> next) {
+ sp -> to.sin_port = local_port;
+ sp -> to.sin_family = AF_INET;
+ sp -> to.sin_len = sizeof sp -> to;
+ }
+
+ /* Get the current time... */
+ time(&cur_time);
+
+ /* Discover all the network interfaces. */
+ discover_interfaces(interfaces);
+
+ /* Set up the bootp packet handler... */
+ bootp_packet_handler = relay;
+
+ /* Become a daemon... */
+ if (!no_daemon) {
+ int pid;
+ FILE *pf;
+ int pfdesc;
+
+ log_perror = 0;
+
+ if ((pid = fork()) < 0)
+ error ("can't fork daemon: %m");
+ else if (pid)
+ exit (0);
+
+ pfdesc = open (path_dhcrelay_pid,
+ O_CREAT | O_TRUNC | O_WRONLY, 0644);
+
+ if (pfdesc < 0) {
+ warn ("Can't create %s: %m", path_dhcrelay_pid);
+ } else {
+ pf = fdopen (pfdesc, "w");
+ if (!pf)
+ warn ("Can't fdopen %s: %m",
+ path_dhcrelay_pid);
+ else {
+ fprintf (pf, "%ld\n", (long)getpid ());
+ fclose (pf);
+ }
+ }
+
+ close (0);
+ close (1);
+ close (2);
+ pid = setsid ();
+ }
+
+ /* Start dispatching packets and timeouts... */
+ dispatch ();
+
+ /*NOTREACHED*/
+ return 0;
+}
+
+void relay (ip, packet, length, from_port, from, hfrom)
+ struct interface_info *ip;
+ struct dhcp_packet *packet;
+ int length;
+ unsigned int from_port;
+ struct iaddr from;
+ struct hardware *hfrom;
+{
+ struct server_list *sp;
+ struct sockaddr_in to;
+ struct interface_info *out;
+ struct hardware hto;
+
+ if (packet -> hlen > sizeof packet -> chaddr) {
+ note ("Discarding packet with invalid hlen.");
+ return;
+ }
+
+ /* If it's a bootreply, forward it to the client. */
+ if (packet -> op == BOOTREPLY) {
+ memset(&to, 0, sizeof(to));
+ if (!(packet -> flags & htons (BOOTP_BROADCAST))) {
+ to.sin_addr = packet -> yiaddr;
+ to.sin_port = remote_port;
+ } else {
+ to.sin_addr.s_addr = htonl (INADDR_BROADCAST);
+ to.sin_port = remote_port;
+ }
+ to.sin_family = AF_INET;
+ to.sin_len = sizeof to;
+
+ /* Set up the hardware destination address. */
+ hto.hlen = packet -> hlen;
+ if (hto.hlen > sizeof hto.haddr)
+ hto.hlen = sizeof hto.haddr;
+ memcpy (hto.haddr, packet -> chaddr, hto.hlen);
+ hto.htype = packet -> htype;
+
+ /* Find the interface that corresponds to the giaddr
+ in the packet. */
+ for (out = interfaces; out; out = out -> next) {
+ if (!memcmp (&out -> primary_address,
+ &packet -> giaddr,
+ sizeof packet -> giaddr))
+ break;
+ }
+ if (!out) {
+ warn ("packet to bogus giaddr %s.",
+ inet_ntoa (packet -> giaddr));
+ return;
+ }
+
+ if (send_packet (out,
+ (struct packet *)0,
+ packet, length, out -> primary_address,
+ &to, &hto) != -1)
+ debug ("forwarded BOOTREPLY for %s to %s",
+ print_hw_addr (packet -> htype, packet -> hlen,
+ packet -> chaddr),
+ inet_ntoa (to.sin_addr));
+
+ return;
+ }
+
+ /* If giaddr is set on a BOOTREQUEST, ignore it - it's already
+ been gatewayed. */
+ if (packet -> giaddr.s_addr) {
+ note ("ignoring BOOTREQUEST with giaddr of %s\n",
+ inet_ntoa (packet -> giaddr));
+ return;
+ }
+
+ /* Set the giaddr so the server can figure out what net it's
+ from and so that we can later forward the response to the
+ correct net. */
+ packet -> giaddr = ip -> primary_address;
+
+ /* Otherwise, it's a BOOTREQUEST, so forward it to all the
+ servers. */
+ for (sp = servers; sp; sp = sp -> next) {
+ if (send_packet (interfaces,
+ (struct packet *)0,
+ packet, length, ip -> primary_address,
+ &sp -> to, (struct hardware *)0) != -1) {
+ debug ("forwarded BOOTREQUEST for %s to %s",
+ print_hw_addr (packet -> htype, packet -> hlen,
+ packet -> chaddr),
+ inet_ntoa (sp -> to.sin_addr));
+ }
+ }
+
+}
+
+void usage (appname)
+ char *appname;
+{
+ note (message);
+ note (copyright);
+ note (arr);
+ note ("%s", "");
+ note (contrib);
+ note (url);
+ note ("%s", "");
+
+ warn ("Usage: %s [-q] [-d] [-i if0] [...-i ifN] [-p <port>]", appname);
+ error (" [-pf pidfilename] [server1 [... serverN]]");
+}
+
+void cleanup ()
+{
+}
+
+int write_lease (lease)
+ struct lease *lease;
+{
+ return 1;
+}
+
+int commit_leases ()
+{
+ return 1;
+}
+
+void bootp (packet)
+ struct packet *packet;
+{
+}
+
+void dhcp (packet)
+ struct packet *packet;
+{
+}
+
+char *print_hw_addr (htype, hlen, data)
+ int htype;
+ int hlen;
+ unsigned char *data;
+{
+ static char habuf [49];
+ char *s;
+ int i;
+
+ if (htype == 0 || hlen == 0) {
+ goto bad;
+ } else {
+ int slen = sizeof(habuf);
+ s = habuf;
+ for (i = 0; i < hlen; i++) {
+ int j;
+ j = snprintf (s, slen, "%02x", data [i]);
+ if (j <= 0)
+ goto bad;
+
+ s += strlen (s);
+ slen -= (strlen(s) + 1);
+ *s++ = ':';
+ }
+ *--s = 0;
+ }
+ return habuf;
+ bad:
+ strlcpy (habuf, "<null>", sizeof habuf);
+ return habuf;
+
+}
diff --git a/usr.sbin/dhcrelay/dispatch.c b/usr.sbin/dhcrelay/dispatch.c
new file mode 100644
index 00000000000..a38850842ad
--- /dev/null
+++ b/usr.sbin/dhcrelay/dispatch.c
@@ -0,0 +1,496 @@
+/* $OpenBSD: dispatch.c,v 1.1 2004/04/12 21:10:28 henning Exp $ */
+
+/*
+ * Copyright 2004 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 1995, 1996, 1997, 1998, 1999
+ * The Internet Software Consortium. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include "dhcpd.h"
+
+#include <sys/ioctl.h>
+
+#include <net/if_media.h>
+#include <ifaddrs.h>
+#include <poll.h>
+
+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 sockaddr_in foo;
+ struct ifaddrs *ifap, *ifa;
+ 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;
+
+ nfds = 0;
+ 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 indefinetely.
+ */
+ howlong = timeouts->when - cur_time;
+ if (howlong > INT_MAX / 1000)
+ howlong = INT_MAX / 1000;
+ to_msec = howlong * 1000;
+ } else
+ to_msec = -1;
+
+ /* Set up the descriptors to be polled. */
+ i = 0;
+
+ for (l = protocols; l; l = l->next) {
+ struct interface_info *ip = l->local;
+ if (ip && (l->handler != got_one || !ip->dead)) {
+ fds[i].fd = l->fd;
+ fds[i].events = POLLIN;
+ fds[i].revents = 0;
+ i++;
+ }
+ }
+
+ if (i == 0)
+ error("No live interfaces to poll on - exiting.");
+
+ /* Wait for a packet or a timeout... XXX */
+ count = poll(fds, nfds, to_msec);
+
+ /* Not likely to be transitory... */
+ if (count == -1) {
+ if (errno == EAGAIN || errno == EINTR) {
+ 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)) {
+ 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;
+ size_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) {
+ warn("receive_packet failed on %s: %s", ip->name,
+ strerror(errno));
+ ip->errors++;
+ if ((!interface_status(ip)) ||
+ (ip->noifmedia && ip->errors > 20)) {
+ /* our interface has gone away. */
+ warn("Interface %s no longer appears valid.",
+ ip->name);
+ ip->dead = 1;
+ interfaces_invalidated = 1;
+ close(l->fd);
+ remove_protocol(l);
+ free(ip);
+ }
+ return;
+ }
+ if (result == 0)
+ return;
+
+ if (bootp_packet_handler) {
+ ifrom.len = 4;
+ memcpy(ifrom.iabuf, &from.sin_addr, ifrom.len);
+
+ (*bootp_packet_handler)(ip, &u.packet, result,
+ from.sin_port, ifrom, &hfrom);
+ }
+}
+
+int
+interface_status(struct interface_info *ifinfo)
+{
+ char *ifname = ifinfo->name;
+ int ifsock = ifinfo->rfdesc;
+ struct ifreq ifr;
+ struct ifmediareq ifmr;
+
+ /* get interface flags */
+ memset(&ifr, 0, sizeof(ifr));
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(ifsock, SIOCGIFFLAGS, &ifr) < 0) {
+ syslog(LOG_ERR, "ioctl(SIOCGIFFLAGS) on %s: %m", ifname);
+ goto inactive;
+ }
+ /*
+ * if one of UP and RUNNING flags is dropped,
+ * the interface is not active.
+ */
+ if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
+ goto inactive;
+ }
+ /* Next, check carrier on the interface, if possible */
+ if (ifinfo->noifmedia)
+ goto active;
+ memset(&ifmr, 0, sizeof(ifmr));
+ strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
+ if (ioctl(ifsock, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) {
+ if (errno != EINVAL) {
+ syslog(LOG_DEBUG, "ioctl(SIOCGIFMEDIA) on %s: %m",
+ ifname);
+
+ ifinfo->noifmedia = 1;
+ goto active;
+ }
+ /*
+ * EINVAL (or ENOTTY) simply means that the interface
+ * does not support the SIOCGIFMEDIA ioctl. We regard it alive.
+ */
+ ifinfo->noifmedia = 1;
+ goto active;
+ }
+ if (ifmr.ifm_status & IFM_AVALID) {
+ switch (ifmr.ifm_active & IFM_NMASK) {
+ case IFM_ETHER:
+ if (ifmr.ifm_status & IFM_ACTIVE)
+ goto active;
+ else
+ goto inactive;
+ break;
+ default:
+ goto inactive;
+ }
+ }
+inactive:
+ return (0);
+active:
+ return (1);
+}
+
+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/usr.sbin/dhcrelay/errwarn.c b/usr.sbin/dhcrelay/errwarn.c
new file mode 100644
index 00000000000..081896720b5
--- /dev/null
+++ b/usr.sbin/dhcrelay/errwarn.c
@@ -0,0 +1,199 @@
+/* $OpenBSD: errwarn.c,v 1.1 2004/04/12 21:10:28 henning Exp $ */
+
+/* Errors and warnings... */
+
+/*
+ * Copyright (c) 1996 The Internet Software Consortium.
+ * All Rights Reserved.
+ * Copyright (c) 1995 RadioMail Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of RadioMail Corporation, the Internet Software
+ * Consortium nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY RADIOMAIL CORPORATION, THE INTERNET
+ * SOFTWARE CONSORTIUM AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL RADIOMAIL CORPORATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This software was written for RadioMail Corporation by Ted Lemon
+ * under a contract with Vixie Enterprises. Further modifications have
+ * been made for the Internet Software Consortium under a contract
+ * with Vixie Laboratories.
+ */
+
+#include <errno.h>
+
+#include "dhcpd.h"
+
+static void do_percentm(char *obuf, size_t size, char *ibuf);
+
+static char mbuf[1024];
+static char fbuf[1024];
+
+int warnings_occurred;
+
+/*
+ * Log an error message, then exit.
+ */
+void
+error(char *fmt, ...)
+{
+ va_list list;
+
+ do_percentm(fbuf, sizeof(fbuf), fmt);
+
+ va_start(list, fmt);
+ vsnprintf(mbuf, sizeof(mbuf), fbuf, list);
+ va_end(list);
+
+#ifndef DEBUG
+ syslog(log_priority | LOG_ERR, "%s", mbuf);
+#endif
+
+ /* Also log it to stderr? */
+ if (log_perror) {
+ write(2, mbuf, strlen(mbuf));
+ write(2, "\n", 1);
+ }
+
+ syslog(LOG_CRIT, "exiting.");
+ if (log_perror) {
+ fprintf(stderr, "exiting.\n");
+ fflush(stderr);
+ }
+ exit(1);
+}
+
+/*
+ * Log a warning message...
+ */
+int
+warn(char *fmt, ...)
+{
+ va_list list;
+
+ do_percentm(fbuf, sizeof(fbuf), fmt);
+
+ va_start(list, fmt);
+ vsnprintf(mbuf, sizeof(mbuf), fbuf, list);
+ va_end(list);
+
+#ifndef DEBUG
+ syslog(log_priority | LOG_ERR, "%s", mbuf);
+#endif
+
+ if (log_perror) {
+ write(2, mbuf, strlen(mbuf));
+ write(2, "\n", 1);
+ }
+
+ return (0);
+}
+
+/*
+ * Log a note...
+ */
+int
+note(char *fmt, ...)
+{
+ va_list list;
+
+ do_percentm(fbuf, sizeof(fbuf), fmt);
+
+ va_start(list, fmt);
+ vsnprintf(mbuf, sizeof(mbuf), fbuf, list);
+ va_end(list);
+
+#ifndef DEBUG
+ syslog(log_priority | LOG_INFO, "%s", mbuf);
+#endif
+
+ if (log_perror) {
+ write(2, mbuf, strlen(mbuf));
+ write(2, "\n", 1);
+ }
+
+ return (0);
+}
+
+/*
+ * Log a debug message...
+ */
+int
+debug(char *fmt, ...)
+{
+ va_list list;
+
+ do_percentm(fbuf, sizeof(fbuf), fmt);
+
+ va_start(list, fmt);
+ vsnprintf(mbuf, sizeof(mbuf), fbuf, list);
+ va_end(list);
+
+#ifndef DEBUG
+ syslog(log_priority | LOG_DEBUG, "%s", mbuf);
+#endif
+
+ if (log_perror) {
+ write(2, mbuf, strlen(mbuf));
+ write(2, "\n", 1);
+ }
+
+ return (0);
+}
+
+/*
+ * Find %m in the input string and substitute an error message string.
+ */
+static void
+do_percentm(char *obuf, size_t size, char *ibuf)
+{
+ char ch;
+ char *s = ibuf;
+ char *t = obuf;
+ size_t prlen;
+ size_t fmt_left;
+ int saved_errno = errno;
+
+ /*
+ * We wouldn't need this mess if printf handled %m, or if
+ * strerror() had been invented before syslog().
+ */
+ for (fmt_left = size; (ch = *s); ++s) {
+ if (ch == '%' && s[1] == 'm') {
+ ++s;
+ prlen = snprintf(t, fmt_left, "%s",
+ strerror(saved_errno));
+ if (prlen >= fmt_left)
+ prlen = fmt_left - 1;
+ t += prlen;
+ fmt_left -= prlen;
+ } else {
+ if (fmt_left > 1) {
+ *t++ = ch;
+ fmt_left--;
+ }
+ }
+ }
+ *t = '\0';
+}
+
diff --git a/usr.sbin/dhcrelay/packet.c b/usr.sbin/dhcrelay/packet.c
new file mode 100644
index 00000000000..925b3afe36a
--- /dev/null
+++ b/usr.sbin/dhcrelay/packet.c
@@ -0,0 +1,255 @@
+/* $OpenBSD: packet.c,v 1.1 2004/04/12 21:10:28 henning Exp $ */
+
+/* Packet assembly code, originally contributed by Archie Cobbs. */
+
+/*
+ * Copyright (c) 1995, 1996, 1999 The Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include "dhcpd.h"
+
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <netinet/if_ether.h>
+
+#define ETHER_HEADER_SIZE (ETHER_ADDR_LEN * 2 + sizeof(u_int16_t))
+
+u_int32_t checksum(unsigned char *, unsigned, u_int32_t);
+u_int32_t wrapsum(u_int32_t);
+
+void assemble_ethernet_header(struct interface_info *, unsigned char *,
+ int *, struct hardware *);
+ssize_t decode_ethernet_header(struct interface_info *, unsigned char *,
+ int bufix, struct hardware *);
+
+u_int32_t
+checksum(unsigned char *buf, unsigned nbytes, u_int32_t sum)
+{
+ int i;
+
+ /* Checksum all the pairs of bytes first... */
+ for (i = 0; i < (nbytes & ~1U); i += 2) {
+ sum += (u_int16_t)ntohs(*((u_int16_t *)(buf + i)));
+ if (sum > 0xFFFF)
+ sum -= 0xFFFF;
+ }
+
+ /*
+ * If there's a single byte left over, checksum it, too.
+ * Network byte order is big-endian, so the remaining byte is
+ * the high byte.
+ */
+ if (i < nbytes) {
+ sum += buf[i] << 8;
+ if (sum > 0xFFFF)
+ sum -= 0xFFFF;
+ }
+
+ return (sum);
+}
+
+u_int32_t
+wrapsum(u_int32_t sum)
+{
+ sum = ~sum & 0xFFFF;
+ return (htons(sum));
+}
+
+void
+assemble_hw_header(struct interface_info *interface, unsigned char *buf,
+ int *bufix, struct hardware *to)
+{
+ struct ether_header eh;
+
+ if (to != NULL && to->hlen == 6) /* XXX */
+ memcpy(eh.ether_dhost, to->haddr, sizeof(eh.ether_dhost));
+ else
+ memset(eh.ether_dhost, 0xff, sizeof(eh.ether_dhost));
+ if (interface->hw_address.hlen == sizeof(eh.ether_shost))
+ memcpy(eh.ether_shost, interface->hw_address.haddr,
+ sizeof(eh.ether_shost));
+ else
+ memset(eh.ether_shost, 0x00, sizeof(eh.ether_shost));
+
+ eh.ether_type = htons(ETHERTYPE_IP);
+
+ memcpy(&buf[*bufix], &eh, ETHER_HEADER_SIZE);
+ *bufix += ETHER_HEADER_SIZE;
+}
+
+void
+assemble_udp_ip_header(struct interface_info *interface, unsigned char *buf,
+ int *bufix, u_int32_t from, u_int32_t to, unsigned int port,
+ unsigned char *data, int len)
+{
+ struct ip ip;
+ struct udphdr udp;
+
+ ip.ip_v = 4;
+ ip.ip_hl = 5;
+ ip.ip_tos = IPTOS_LOWDELAY;
+ ip.ip_len = htons(sizeof(ip) + sizeof(udp) + len);
+ ip.ip_id = 0;
+ ip.ip_off = 0;
+ ip.ip_ttl = 16;
+ ip.ip_p = IPPROTO_UDP;
+ ip.ip_sum = 0;
+ ip.ip_src.s_addr = from;
+ ip.ip_dst.s_addr = to;
+
+ ip.ip_sum = wrapsum(checksum((unsigned char *)&ip, sizeof(ip), 0));
+ memcpy(&buf[*bufix], &ip, sizeof(ip));
+ *bufix += sizeof(ip);
+
+ udp.uh_sport = htons(LOCAL_PORT); /* XXX */
+ udp.uh_dport = port; /* XXX */
+ udp.uh_ulen = htons(sizeof(udp) + len);
+ memset(&udp.uh_sum, 0, sizeof(udp.uh_sum));
+
+ udp.uh_sum = wrapsum(checksum((unsigned char *)&udp, sizeof(udp),
+ checksum(data, len, checksum((unsigned char *)&ip.ip_src,
+ 2 * sizeof(ip.ip_src),
+ IPPROTO_UDP + (u_int32_t)ntohs(udp.uh_ulen)))));
+
+ memcpy(&buf[*bufix], &udp, sizeof(udp));
+ *bufix += sizeof(udp);
+}
+
+ssize_t
+decode_hw_header(struct interface_info *interface, unsigned char *buf,
+ int bufix, struct hardware *from)
+{
+ struct ether_header eh;
+
+ memcpy(&eh, buf + bufix, ETHER_HEADER_SIZE);
+
+ memcpy(from->haddr, eh.ether_shost, sizeof(eh.ether_shost));
+ from->htype = ARPHRD_ETHER;
+ from->hlen = sizeof(eh.ether_shost);
+
+ return (sizeof(eh));
+}
+
+ssize_t
+decode_udp_ip_header(struct interface_info *interface, unsigned char *buf,
+ int bufix, struct sockaddr_in *from, unsigned char *data, int buflen)
+{
+ struct ip *ip;
+ struct udphdr *udp;
+ u_int32_t ip_len = (buf[bufix] & 0xf) << 2;
+ u_int32_t sum, usum;
+ static int ip_packets_seen;
+ static int ip_packets_bad_checksum;
+ static int udp_packets_seen;
+ static int udp_packets_bad_checksum;
+ static int udp_packets_length_checked;
+ static int udp_packets_length_overflow;
+ int len = 0;
+
+ ip = (struct ip *)(buf + bufix);
+ udp = (struct udphdr *)(buf + bufix + ip_len);
+
+ /* Check the IP header checksum - it should be zero. */
+ ip_packets_seen++;
+ if (wrapsum(checksum(buf + bufix, ip_len, 0)) != 0) {
+ ip_packets_bad_checksum++;
+ if (ip_packets_seen > 4 &&
+ (ip_packets_seen / ip_packets_bad_checksum) < 2) {
+ note("%d bad IP checksums seen in %d packets",
+ ip_packets_bad_checksum, ip_packets_seen);
+ ip_packets_seen = ip_packets_bad_checksum = 0;
+ }
+ return (-1);
+ }
+
+ if (ntohs(ip->ip_len) != buflen)
+ debug("ip length %d disagrees with bytes received %d.",
+ ntohs(ip->ip_len), buflen);
+
+ memcpy(&from->sin_addr, &ip->ip_src, 4);
+
+ /*
+ * Compute UDP checksums, including the ``pseudo-header'', the
+ * UDP header and the data. If the UDP checksum field is zero,
+ * we're not supposed to do a checksum.
+ */
+ if (!data) {
+ data = buf + bufix + ip_len + sizeof(*udp);
+ len = ntohs(udp->uh_ulen) - sizeof(*udp);
+ udp_packets_length_checked++;
+ if (len + data > buf + bufix + buflen) {
+ udp_packets_length_overflow++;
+ if (udp_packets_length_checked > 4 &&
+ (udp_packets_length_checked /
+ udp_packets_length_overflow) < 2) {
+ note("%d udp packets in %d too long - dropped",
+ udp_packets_length_overflow,
+ udp_packets_length_checked);
+ udp_packets_length_overflow =
+ udp_packets_length_checked = 0;
+ }
+ return (-1);
+ }
+ if (len + data != buf + bufix + buflen)
+ debug("accepting packet with data after udp payload.");
+ }
+
+ usum = udp->uh_sum;
+ udp->uh_sum = 0;
+
+ sum = wrapsum(checksum((unsigned char *)udp, sizeof(*udp),
+ checksum(data, len, checksum((unsigned char *)&ip->ip_src,
+ 2 * sizeof(ip->ip_src),
+ IPPROTO_UDP + (u_int32_t)ntohs(udp->uh_ulen)))));
+
+ udp_packets_seen++;
+ if (usum && usum != sum) {
+ udp_packets_bad_checksum++;
+ if (udp_packets_seen > 4 &&
+ (udp_packets_seen / udp_packets_bad_checksum) < 2) {
+ note("%d bad udp checksums in %d packets",
+ udp_packets_bad_checksum, udp_packets_seen);
+ udp_packets_seen = udp_packets_bad_checksum = 0;
+ }
+ return (-1);
+ }
+
+ memcpy(&from->sin_port, &udp->uh_sport, sizeof(udp->uh_sport));
+
+ return (ip_len + sizeof(*udp));
+}
diff --git a/usr.sbin/dhcrelay/tree.h b/usr.sbin/dhcrelay/tree.h
new file mode 100644
index 00000000000..a17c8888870
--- /dev/null
+++ b/usr.sbin/dhcrelay/tree.h
@@ -0,0 +1,107 @@
+/* $OpenBSD: tree.h,v 1.1 2004/04/12 21:10:28 henning 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 <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+/* A pair of pointers, suitable for making a linked list. */
+typedef struct _pair {
+ caddr_t car;
+ struct _pair *cdr;
+} *pair;
+
+/* Tree node types... */
+#define TREE_CONCAT 1
+#define TREE_HOST_LOOKUP 2
+#define TREE_CONST 3
+#define TREE_LIMIT 4
+
+/* Tree structure for deferred evaluation of changing values. */
+struct tree {
+ int op;
+ union {
+ struct concat {
+ struct tree *left;
+ struct tree *right;
+ } concat;
+ struct host_lookup {
+ struct dns_host_entry *host;
+ } host_lookup;
+ struct const_val {
+ unsigned char *data;
+ int len;
+ } const_val;
+ struct limit {
+ struct tree *tree;
+ int limit;
+ } limit;
+ } data;
+};
+
+/* DNS host entry structure... */
+struct dns_host_entry {
+ char *hostname;
+ unsigned char *data;
+ int data_len;
+ int buf_len;
+ time_t timeout;
+};
+
+struct tree_cache {
+ unsigned char *value;
+ int len;
+ int buf_size;
+ time_t timeout;
+ struct tree *tree;
+ int flags;
+#define TC_AWAITING_RESOLUTION 1
+#define TC_TEMPORARY 2
+};
+
+struct universe {
+ char *name;
+ struct hash_table *hash;
+ struct option *options[256];
+};
+
+struct option {
+ char *name;
+ char *format;
+ struct universe *universe;
+ unsigned char code;
+};