diff options
-rw-r--r-- | usr.sbin/dhcrelay/Makefile | 11 | ||||
-rw-r--r-- | usr.sbin/dhcrelay/bpf.c | 321 | ||||
-rw-r--r-- | usr.sbin/dhcrelay/dhcp.h | 168 | ||||
-rw-r--r-- | usr.sbin/dhcrelay/dhcpd.h | 290 | ||||
-rw-r--r-- | usr.sbin/dhcrelay/dhcrelay.8 | 157 | ||||
-rw-r--r-- | usr.sbin/dhcrelay/dhcrelay.c | 402 | ||||
-rw-r--r-- | usr.sbin/dhcrelay/dispatch.c | 496 | ||||
-rw-r--r-- | usr.sbin/dhcrelay/errwarn.c | 199 | ||||
-rw-r--r-- | usr.sbin/dhcrelay/packet.c | 255 | ||||
-rw-r--r-- | usr.sbin/dhcrelay/tree.h | 107 |
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; +}; |