diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 08:53:40 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 08:53:40 +0000 |
commit | d6583bb2a13f329cf0332ef2570eb8bb8fc0e39c (patch) | |
tree | ece253b876159b39c620e62b6c9b1174642e070e /usr.sbin/bootpd |
initial import of NetBSD tree
Diffstat (limited to 'usr.sbin/bootpd')
46 files changed, 10643 insertions, 0 deletions
diff --git a/usr.sbin/bootpd/Announce b/usr.sbin/bootpd/Announce new file mode 100644 index 00000000000..e4ae04c20d8 --- /dev/null +++ b/usr.sbin/bootpd/Announce @@ -0,0 +1,63 @@ + +This is an enhanced version of the CMU BOOTP server which was derived +from the original BOOTP server created by Bill Croft at Stanford. +This version merges most of the enhancements and bug-fixes from the +NetBSD, Columbia, and other versions. + +New features in version 2.4 include: + + Added a simple BOOTP gateway program: bootpgw + Allow host name anywhere IP address is expected. + Automatically lookup the IP address when the name of a + bootptab entry is a valid hostname. + (Dummy entries names should start with '.') + Merged changes from NetBSD and Columbia versions. + Merged changes for Solaris-2.X and SVR4 systems. + Combined bootptest into the bootp release. + Merged tag 18 support (:ef=...:) from Jason Zions. + Use :ef=extension_file_name: and make the + extension files for all clients using bootpef. + Merged HP compatibility (:ra=...:) from David R Linn. + Allows you to override the reply address. + (i.e. send the reply to a broadcast address) + Add /etc/ethers support for NetBSD. + More systems support getether (Ultrix, OSF, NetBSD) + Added RFC 1533 tags 40,41,42 + :yd=<NIS domain>:ys=<NIS server>:nt=<NTP server>: + ConvOldTab.sh to convert old (1.1) bootptab to new format. + Permits extended-length replies with more option data. + +Problems fixed in this version: + + Fixed references to free host structures. + (used to cause core dump on Solaris) + Remove change that added null terminator to string options. + (this annoyed some clients...) + Add missing symbols to dump routine, fix order. + Works (again) with no -DSYSLOGD defined. + Fixed several more NULL references in readfile. + Added proper length checks to option insertions. + Fixed bootptest IP address printing. + Cleaned-up signed/unsigned and byteorder bugs. + Added SVR4/Streams support to getif and getether + Removed extra newlines in syslog messages. + Specify facility code when calling syslog(3) + When lookup_hwa fails, assume numeric HW address. + +Systems on which I have seen this code work: + SunOS 4.X (Solaris 1.X) + SunOS 5.X (Solaris 2.X) + System V/386 Rel. 4.0 + +Systems on which others say this code works: + CDC EP/IX (1.4.3, 2.1.1) + DEC Ultrix (4.2, 4.3) + NetBSD (Current-8/94) + OSF/1 (DEC Alpha CPU) + +Please direct questions, comments, and bug reports to: + <bootp@andrew.cmu.edu> + +Gordon W. Ross Mercury Computer Systems +gwr@mc.com 199 Riverneck Road +508-256-1300 Chelmsford, MA 01824-2820 diff --git a/usr.sbin/bootpd/Changes b/usr.sbin/bootpd/Changes new file mode 100644 index 00000000000..06165486acc --- /dev/null +++ b/usr.sbin/bootpd/Changes @@ -0,0 +1,245 @@ +Changes, most recent first +Date, <email> Real Name + what... + +--> bootp-2.4.0 + +08/20/94 gwr@mc.com (Gordon W. Ross) + Fix code to build bootfile name based on combination of + client requested name and bootfile specifications. + Behave similarly with or without CHECK_FILE_ACCESS. + +07/30/94 Dirk Koeppen <dirk@incom.de> + Add "min wait" option (mw) to cause bootpd to ignore + requests from clients that have not waited long enough. + Add code to honor client requests containing the DHCP + option "Maximum Message Size" and use its value to + determine the size of the reply message. + +--> bootp-2.3.8 + +06/25/94 Christos Zoulas <christos@deshaw.com> + Add "-h" flag to override host name (affects default IP + address provided in reply messages. (Also minor bug fix) + +05/27/94 gwr@mc.com (Gordon W. Ross) + Add code to call "arp -s IPADDR HWADDR" on systems + that do not provide an SIOCSARP ioctl (i.e. NetBSD) + +--> bootp-2.3.7 + +05/05/94 Walter Wong <wcw+@CMU.EDU> + Reduce noize at debug level one, where log messages + are generated only for hosts that are recognized + and replied to by bootpd. (At request of HP folks.) + +04/30/94 gwr@mc.com (Gordon W. Ross) + Use memxxx functions unless USE_BFUNCS is defined. + Added -f <file> option to bootptest (requested file). + +04/29/94 tpaquett@ita.lgc.com (Trevor Paquette) + Remove call to haddr_conv802() in sendreply(). + The setarp should get the non-transformed address. + +04/27/94 gwr@mc.com + Improve logic for building bootfile pathname, so a path + will be put in the reply if either the client or bootpd + specifies a boot file. (Needed for NetBSD diskless boot) + +04/25/94 shamash@boxhill.com (Ari Shamash) + Fix prs_inetaddr() so it allows '_' in hostnames. + +04/16/94 gwr@mc.com (Gordon W. Ross) + Fix setarp for SVR4 (needs to use I_STR ioctl) + Thanks to several people: (all sent the same fix) + Barney Wolff <barney@databus.com>, + bear@upsys.se (Bj|rn Sj|holm), + Michael Kuschke <Michael.Kuschke@Materna.DE>, + +03/25/95 Ulrich Heuer </I=zhhi9/G=Ulrich/S=Heuer/@zhflur.ubs.ubs.ch> + Make option string lengths not include a null terminator. + The trailing null breaks some clients. + +03/15/94 "Edmund J. Sutcliffe" <ejs1@tower.york.ac.uk> + Add support for the "EX" option: Execute a program + before sending a BOOTREPLY to a client. Support for + this option is conditional on YORK_EX_OPTION. + +03/10/94 Nigel Metheringham <nigelm@ohm.york.ac.uk> + Make getether.c work on Linux. + +03/09/94 Koch@Math.Uni-Duisburg.DE (Peter Koch) + Add missing MANDIR definition to Makefile. + +03/08/94 Jeroen.Scheerder@let.ruu.nl + Fix args to report in getether code for Ultrix. + Run install individually for each program. + +--> bootp-2.3.6 +03/07/94 gwr@mc.com + Cleanup for release (run gnu indent, tab-size=4) + +02/24/94 Jeroen.Scheerder@let.ruu.nl + Allow underscore in host names - readfile.c:goodname() + Add ConvOldTab.sh - converts 1.1 bootptab to new format. + +02/20/94 gwr@mc.com (Gordon W. Ross) + Make readfile tolerant of hardware addresses that start + with a letter. (If lookup_hwa() fails, assume numeric.) + Fix whitespace skip before :vm= auto: and avoid lookup. + +02/12/94 walker@zk3.dec.com (Mary Walker) + Added support for 64-bit longs (for the DEC Alpha) + Allow ieee802 hardware address in bit-reversed oreder + +02/07/94 hl@tekla.fi (Harald Lundberg) + Fix conflict with DUMP_FILE in syslog.h on OSF1 + Use int for (struct bootp).bp_xid (for DEC Alpha) + Added Ultrix support to bootptest (getether) + +02/06/94 brezak@ch.hp.com (John Brezak) + Add man-page and install targets to Makefile.NetBSD + Add getether support for NetBSD + +02/05/94 gwr@mc.com (Gordon W. Ross) + Added tags 40,41,42 (NIS domain, NIS server, NTP server) + Add stub to getether for machines not yet supported. + +--> bootp-2.3.5 +01/29/94 gwr@mc.com (Gordon W. Ross) + Make bootpgw put a correct address in "giaddr" when + the client request came via broadcast. + +01/22/94 gwr@mc.com (Gordon W. Ross) + Fix syslog call (missing "facility" code) + Add SVR4/Streams support to getif() and getether() + Fix getif bug (matched when it should not) + Macro-ize lots of similar cases in readfile.c + +12/27/93 brezak@ch.hp.com (John Brezak) + Remove all newlines passed to syslog(3) + Add /etc/ethers support for NetBSD. + +12/18/93 gwr@mc.com (Gordon W. Ross) + Fix bootptest IP address printing. + Fix byte-order bugs in bootpgw and bootptest. + Clean-up signed/unsigned mismatches. + Back out SLIP support changes for now + (code fragment saved in ToDo). + +--> bootp-2.3.4 (beta test release) +12/12/93 gwr@mc.com (Gordon W. Ross) + Fixed several more NULL references in readfile. + Added proper length checks to option insertions. + +--> bootp-2.3.3 (beta test release) +12/09/93 gwr@mc.com (Gordon W. Ross) + Added ASSERT checks to readfile.c:fill_defaults() + +12/08/93 brezak@ch.hp.com (John Brezak) + New Makefile.NetBSD + Added setsid() and #ifdef TIOCNOTTY + (bootpd.c, bootpgw.c) + Moved #include <net/if.h> out of #ifdef SUNOS + Fixed several multiple declaration problems + +12/04/93 gwr@mc.com (Gordon W. Ross) + Re-implemented Extension File support + based on work by Jason Zions <jazz@hal.com> + Added support for Reply-Address-Override to support + HP clients (need reply sent to broadcast address) + from David R. Linn <drl@vuse.vanderbilt.edu> + +--> bootp-2.3.2 (beta test release) +11/27/93 gwr@mc.com (Gordon W. Ross) + Incorporated bootptest into the bootp release. + Added ANSI function prototypes everywhere. + +11/17/93 dpm@depend.com (David P. Maynard) + Added automatic SLIP address determination. + (This is NOT dynamic IP address assignment.) + Cleaned up some type warnings from gcc. + +11/11/93 gwr@mc.com (Gordon W. Ross) + Works (again) with no -DSYSLOGD defined. + Provide a default value for the subnet mask. + More #ifdef's for SunOS specific code (lookup_hwa) + Added a simple BOOTP gateway program: bootpgw + Reorganized for more code sharing (with bootpgw) + +--> bootp-2.3.1 (alpha test release) +11/08/93 gwr@mc.com (Gordon W. Ross) + Back-out changes to honor option structure in request + (this needs to be a per-client option). + Merged changes from NetBSD and Columbia versions. + Allow host name anywhere IP address is expected. + Add null terminators to option strings. + Add missing symbols to dump routine, dump symbols + in alphabetical order, one tag per line. + +--> bootp-2.2.D (posted as patch 2) +10/19/93 gwr@mc.com (Gordon W. Ross) + Fix references to free memory (leads to core dumps). + +--> bootp-2.2.C (posted as patch 1) +10/14/93 gwr@mc.com (Gordon W. Ross) + Fix data access alignment problems on SPARC/Solaris. + +--> bootp-2.2.B (posted to usenet) +10/11/93 gwr@mc.com (Gordon W. Ross) + Allow extended-length BOOTP packets (more vendor options) + Honor option format specified in client requests. + Added Solaris-2.X changes from db@sunbim.be (Danny Backx). + +All history before this point may be inaccurate. Please send +changes if any of the credits are incorrect. -gwr + +--> bootp-2.2+NetBSD released +08/27/93 brezak@ch.hp.com (John Brezak) + Added RFC 1396 support (tags 14-17) + +--> bootp-2.2+NetBSD (version?) +??/??/93 mckim@lerc.nasa.gov (Jim McKim) + Ported to NetBSD (see Makefile.NetBSD) + Set server host name in responses. + Check all interfaces in address match routine. + +--> bootp-2.2+FdC released +01/27/93 <fdc@watsun.cc.columbia.edu> Frank da Cruz + Added RFC 1395 information: Merit dump file, + client domain name, swap server address, root path. + +--> bootp-2.2alpha released +11/14/91 <walt+@cmu.edu> Walter L. Wimer + Add "td" to TFTP directory for "secure" (chroot) TFTP. + Add "sa" tag to set explicit server address. + Automatically determine if child of inetd. + Use RFC 1048 format when request has magic number zero. + Fixed various bugs. Give bootptab a separate man page. + +--> bootp-2.1 released +01/09/89 <walt+@cmu.edu> Walter L. Wimer + Check world read bit on TFTP boot file. + Add support for rfc1085 "bootfile size" tag. + Add generic tags. Fix byte order of rfc1048 data. + Fix various crashing bugs. + +--> bootp-2.0 released +07/15/88 <walt+@cmu.edu> Walter L. Wimer + Added vendor information to conform to RFC1048. + Adopted termcap-like file format to support above. + Added hash table lookup instead of linear search. + Other cleanups. + +--> bootp-1.3(?) released +07/24/87 <ddp@andrew.cmu.edu> Drew D. Perkins + Modified to use syslog instead of Kovar's + routines. Add debugging dumps. Many other fixups. + +--> bootp-1.2(?) released +07/30/86 David Kovar at Carnegie Mellon University + Modified to work at CMU. + +--> bootp-1.1 released +01/22/86 Bill Croft at Stanford University + Original created. diff --git a/usr.sbin/bootpd/ConvOldTab.sh b/usr.sbin/bootpd/ConvOldTab.sh new file mode 100644 index 00000000000..00683f0c049 --- /dev/null +++ b/usr.sbin/bootpd/ConvOldTab.sh @@ -0,0 +1,141 @@ +#!/bin/sh +# convert_bootptab Jeroen.Scheerder@let.ruu.nl 02/25/94 +# This script can be used to convert bootptab files in old format +# to new (termcap-like) bootptab files +# +# The old format - real entries are commented out by '###' +# +# Old-style bootp files consist of two sections. +# The first section has two entries: +# First, a line that specifies the home directory +# (where boot file paths are relative to) + +###/tftpboot + +# The next non-empty non-comment line specifies the default bootfile + +###no-file + +# End of first section - indicated by '%%' at the start of the line + +###%% + +# The remainder of this file contains one line per client +# interface with the information shown by the table headings +# below. The host name is also tried as a suffix for the +# bootfile when searching the home directory (that is, +# bootfile.host) +# +# Note that htype is always 1, indicating the hardware type Ethernet. +# Conversion therefore always yields ':ha=ether:'. +# +# host htype haddr iaddr bootfile +# + +###somehost 1 00:0b:ad:01:de:ad 128.128.128.128 dummy + +# That's all for the description of the old format. +# For the new-and-improved format, see bootptab(5). + +set -u$DX + +case $# +in 2 ) OLDTAB=$1 ; NEWTAB=$2 ;; + * ) echo "Usage: `basename $0` <Input> <Output>" + exit 1 +esac + +if [ ! -r $OLDTAB ] +then + echo "`basename $0`: $OLDTAB does not exist or is unreadable." + exit 1 +fi + +if touch $NEWTAB 2> /dev/null +then + : +else + echo "`basename $0`: cannot write to $NEWTAB." + exit 1 +fi + + +cat << END_OF_HEADER >> $NEWTAB +# /etc/bootptab: database for bootp server (/etc/bootpd) +# This file was generated automagically + +# Blank lines and lines beginning with '#' are ignored. +# +# Legend: (see bootptab.5) +# first field -- hostname (not indented) +# bf -- bootfile +# bs -- bootfile size in 512-octet blocks +# cs -- cookie servers +# df -- dump file name +# dn -- domain name +# ds -- domain name servers +# ef -- extension file +# gw -- gateways +# ha -- hardware address +# hd -- home directory for bootfiles +# hn -- host name set for client +# ht -- hardware type +# im -- impress servers +# ip -- host IP address +# lg -- log servers +# lp -- LPR servers +# ns -- IEN-116 name servers +# ra -- reply address +# rl -- resource location protocol servers +# rp -- root path +# sa -- boot server address +# sm -- subnet mask +# sw -- swap server +# tc -- template host (points to similar host entry) +# td -- TFTP directory +# to -- time offset (seconds) +# ts -- time servers +# vm -- vendor magic number +# Tn -- generic option tag n +# +# Be careful about including backslashes where they're needed. Weird (bad) +# things can happen when a backslash is omitted where one is intended. +# Also, note that generic option data must be either a string or a +# sequence of bytes where each byte is a two-digit hex value. + +# First, we define a global entry which specifies the stuff every host uses. +# (Host name lookups are relative to the domain: your.domain.name) + +END_OF_HEADER + +# Fix up HW addresses in aa:bb:cc:dd:ee:ff and aa-bb-cc-dd-ee-ff style first +# Then awk our stuff together +sed -e 's/[:-]//g' < $OLDTAB | \ +nawk 'BEGIN { PART = 0 ; FIELD=0 ; BOOTPATH="unset" ; BOOTFILE="unset" } + /^%%/ { + PART = 1 + printf ".default:\\\n\t:ht=ether:\\\n\t:hn:\\\n\t:dn=your.domain.name:\\\n\t:ds=your,dns,servers:\\\n\t:sm=255.255.0.0:\\\n\t:hd=%s:\\\n\t:rp=%s:\\\n\t:td=%s:\\\n\t:bf=%s:\\\n\t:to=auto:\n\n", BOOTPATH, BOOTPATH, BOOTPATH, BOOTFILE + next + } + /^$/ { next } + /^#/ { next } + { + if ( PART == 0 && FIELD < 2 ) + { + if ( FIELD == 0 ) BOOTPATH=$1 + if ( FIELD == 1 ) BOOTFILE=$1 + FIELD++ + } + } + { + if ( PART == 1 ) + { + HOST=$1 + HA=$3 + IP=$4 + BF=$5 + printf "%s:\\\n\t:tc=.default:\\\n\t:ha=0x%s:\\\n\t:ip=%s:\\\n\t:bf=%s:\n", HOST, HA, IP, BF + } + }' >> $NEWTAB + +exit 0 diff --git a/usr.sbin/bootpd/Installation b/usr.sbin/bootpd/Installation new file mode 100644 index 00000000000..466cabce0cd --- /dev/null +++ b/usr.sbin/bootpd/Installation @@ -0,0 +1,29 @@ + +Installation instructions for SunOS + +Compile the executable: +For SunOS 4.X: + make sunos4 +For SunOS 5.X: (Solaris) + make sunos5 + +Install the executables: + + make install + +Edit (or create) the bootptab: +(See bootptab.sample and bootptab.5 manual entry) + edit /etc/bootptab + +Edit /etc/services to add these two lines: +bootps 67/udp bootp # BOOTP Server +bootpc 68/udp # BOOTP Client + +Edit /etc/inetd.conf to add the line: +bootp dgram udp wait root /usr/etc/bootpd bootpd -i + +If you compiled report.c with LOG_LOCAL2 (defined in the Makefile) +then you may want to capture syslog messages from BOOTP by changing +your syslog.conf file. (See the sample syslog.conf file here). +Test the change with: logger -t test -p local2.info "message" + diff --git a/usr.sbin/bootpd/Makefile b/usr.sbin/bootpd/Makefile new file mode 100644 index 00000000000..ff8e27d6c04 --- /dev/null +++ b/usr.sbin/bootpd/Makefile @@ -0,0 +1,13 @@ +# bootpd/Makefile +# $Id: Makefile,v 1.1 1995/10/18 08:47:25 deraadt Exp $ + +PROG= bootpd +CFLAGS+= -DETC_ETHERS -DSYSLOG -DDEBUG -DVEND_CMU + +SRCS= bootpd.c dovend.c readfile.c hash.c dumptab.c \ + lookup.c getif.c hwaddr.c report.c tzone.c + +MAN= bootpd.8 bootptab.5 +MLINKS= bootpd.8 bootpgw.8 + +.include <bsd.prog.mk> diff --git a/usr.sbin/bootpd/Makefile.UNIX b/usr.sbin/bootpd/Makefile.UNIX new file mode 100644 index 00000000000..e333ce5d2ca --- /dev/null +++ b/usr.sbin/bootpd/Makefile.UNIX @@ -0,0 +1,184 @@ +# +# Makefile for the BOOTP programs: +# bootpd - BOOTP server daemon +# bootpef - BOOTP extension file builder +# bootpgw - BOOTP gateway daemon +# bootptest - BOOTP tester (client) +# + +# OPTion DEFinitions: +# Remove the -DVEND_CMU if you don't wish to support the "CMU vendor format" +# in addition to the RFC1048 format. Leaving out DEBUG saves little. +OPTDEFS= -DSYSLOG -DVEND_CMU -DDEBUG + +# Uncomment and edit this to choose the facility code used for syslog. +# LOG_FACILITY= "-DLOG_BOOTP=LOG_LOCAL2" + +# SYStem DEFinitions: +# Either uncomment some of the following, or do: +# "make sunos4" (or "make sunos5", etc.) +# SYSDEFS= -DSUNOS -DETC_ETHERS +# SYSDEFS= -DSVR4 +# SYSLIBS= -lsocket -lnsl + +# Uncomment this if your system does not provide streror(3) +# STRERROR=strerror.o + +# FILE DEFinitions: +# The next few lines may be uncommented and changed to alter the default +# filenames bootpd uses for its configuration and dump files. +#CONFFILE= -DCONFIG_FILE=\"/usr/etc/bootptab\" +#DUMPFILE= -DDUMPTAB_FILE=\"/usr/etc/bootpd.dump\" +#FILEDEFS= $(CONFFILE) $(DUMPFILE) + +# MORE DEFinitions (whatever you might want to add) +# One might define NDEBUG (to remove "assert()" checks). +MOREDEFS= + +INSTALL=/usr/bin/install +DESTDIR= +BINDIR=/usr/etc +MANDIR=/usr/local/man + +CFLAGS= $(OPTDEFS) $(SYSDEFS) $(FILEDEFS) $(MOREDEFS) +PROGS= bootpd bootpef bootpgw bootptest +TESTS= trylook trygetif trygetea + +all: $(PROGS) + +tests: $(TESTS) + +system: install + +install: $(PROGS) + -for f in $(PROGS) ;\ + do \ + $(INSTALL) -c -s $$f $(DESTDIR)$(BINDIR) ;\ + done + +MAN5= bootptab.5 +MAN8= bootpd.8 bootpef.8 bootptest.8 +install.man: $(MAN5) $(MAN8) + -for f in $(MAN5) ;\ + do \ + $(INSTALL) -c -m 644 $$f $(DESTDIR)$(MANDIR)/man5 ;\ + done + -for f in $(MAN8) ;\ + do \ + $(INSTALL) -c -m 644 $$f $(DESTDIR)$(MANDIR)/man8 ;\ + done + +clean: + -rm -f core *.o + -rm -f $(PROGS) $(TESTS) + +distclean: + -rm -f *.BAK *.CKP *~ .emacs* + +# +# Handy targets for individual systems: +# + +# DEC/OSF1 on the Alpha +alpha: + $(MAKE) SYSDEFS="-DETC_ETHERS -Dint32=int -D_SOCKADDR_LEN" \ + STRERROR=strerror.o + +# Control Data EP/IX 1.4.3 system, BSD 4.3 mode +epix143: + $(MAKE) CC="cc -systype bsd43" \ + SYSDEFS="-Dconst= -D_SIZE_T -DNO_UNISTD -DUSE_BFUNCS" \ + STRERROR=strerror.o + +# Control Data EP/IX 2.1.1 system, SVR4 mode +epix211: + $(MAKE) CC="cc -systype svr4" \ + SYSDEFS="-DSVR4" \ + SYSLIBS="-lsocket -lnsl" + +# Silicon Graphics IRIX (no <sys/sockio.h>, so not SVR4) +irix: + $(MAKE) SYSDEFS="-DSYSV -DIRIX" + +# SunOS 4.X +sunos4: + $(MAKE) SYSDEFS="-DSUNOS -DETC_ETHERS" \ + STRERROR=strerror.o + +# Solaris 2.X (i.e. SunOS 5.X) +sunos5: + $(MAKE) SYSDEFS="-DSVR4 -DETC_ETHERS" \ + SYSLIBS="-lsocket -lnsl" + +# UNIX System V Rel. 4 (also: IRIX 5.X, others) +svr4: + $(MAKE) SYSDEFS="-DSVR4" \ + SYSLIBS="-lsocket -lnsl" + +# +# How to build each program: +# + +OBJ_D= bootpd.o dovend.o readfile.o hash.o dumptab.o \ + lookup.o getif.o hwaddr.o tzone.o report.o $(STRERROR) +bootpd: $(OBJ_D) + $(CC) -o $@ $(OBJ_D) $(SYSLIBS) + +OBJ_EF= bootpef.o dovend.o readfile.o hash.o dumptab.o \ + lookup.o hwaddr.o tzone.o report.o $(STRERROR) +bootpef: $(OBJ_EF) + $(CC) -o $@ $(OBJ_EF) $(SYSLIBS) + +OBJ_GW= bootpgw.o getif.o hwaddr.o report.o $(STRERROR) +bootpgw: $(OBJ_GW) + $(CC) -o $@ $(OBJ_GW) $(SYSLIBS) + +OBJ_TEST= bootptest.o print-bootp.o getif.o getether.o \ + report.o $(STRERROR) +bootptest: $(OBJ_TEST) + $(CC) -o $@ $(OBJ_TEST) $(SYSLIBS) + +# This is just for testing the lookup functions. +TRYLOOK= trylook.o lookup.o report.o $(STRERROR) +trylook : $(TRYLOOK) + $(CC) -o $@ $(TRYLOOK) $(SYSLIBS) + +# This is just for testing getif. +TRYGETIF= trygetif.o getif.o report.o $(STRERROR) +trygetif : $(TRYGETIF) + $(CC) -o $@ $(TRYGETIF) $(SYSLIBS) + +# This is just for testing getether. +TRYGETEA= trygetea.o getether.o report.o $(STRERROR) +trygetea : $(TRYGETEA) + $(CC) -o $@ $(TRYGETEA) $(SYSLIBS) + +# This rule just keeps the LOG_BOOTP define localized. +report.o : report.c + $(CC) $(CFLAGS) $(LOG_FACILITY) -c $< + +# Punt SunOS -target noise +.c.o: + $(CC) $(CFLAGS) -c $< + +# +# Header file dependencies: +# + +bootpd.o : bootp.h bptypes.h hash.h hwaddr.h bootpd.h dovend.h +bootpd.o : readfile.h report.h tzone.h patchlevel.h getif.h +bootpef.o : bootp.h bptypes.h hash.h hwaddr.h bootpd.h dovend.h +bootpef.o : readfile.h report.h tzone.h patchlevel.h +bootpgw.o : bootp.h bptypes.h getif.h hwaddr.h report.h patchlevel.h +bootptest.o : bootp.h bptypes.h bootptest.h getif.h patchlevel.h +dovend.o : bootp.h bptypes.h bootpd.h hash.h hwaddr.h report.h dovend.h +dumptab.o : bootp.h bptypes.h hash.h hwaddr.h report.h patchlevel.h bootpd.h +getif.o : getif.h report.h +hash.o : hash.h +hwaddr.o : bptypes.h hwaddr.h report.h +lookup.o : bootp.h bptypes.h lookup.h report.h +print-bootp.o : bootp.h bptypes.h bootptest.h +readfile.o : bootp.h bptypes.h hash.h hwaddr.h lookup.h readfile.h +readfile.o : report.h tzone.h bootpd.h +report.o : report.h +tzone.o : bptypes.h report.h tzone.h diff --git a/usr.sbin/bootpd/Problems b/usr.sbin/bootpd/Problems new file mode 100644 index 00000000000..9478676eca4 --- /dev/null +++ b/usr.sbin/bootpd/Problems @@ -0,0 +1,47 @@ + +Common problems and ways to work around them: + +Bootpd complains that it "can not get IP addr for HOSTNAME" + + If the entry is a "dummy" (not a real host) used only for + reference by other entries, put '.' in front of the name. + + If the entry is for a real client and the IP address for + the client can not be found using gethostbyname(), specify + the IP address for the client using numeric form. + +Bootpd takes a long time to finish parsing the bootptab file: + + Excessive startup time is usually caused by waiting for + timeouts on failed DNS lookup operations. If this is the + problem, find the client names for which DNS lookup fails + and change the bootptab to specify the IP addresses for + those clients using numeric form. + + When bootptab entries do not specify an ip address, bootpd + attempts to lookup the tagname as a host name to find the + IP address. To suppress this default action, either make + the entry a "dummy" or specify its IP numeric address. + + If your DNS lookups work but are just slow, consider either + running bootpd on the same machine as the DNS server or + running a caching DNS server on the host running bootpd. + +My huge bootptab file causes startup time to be so long that clients +give up waiting for a reply. + + Truly huge bootptab files make "inetd" mode impractical. + Start bootpd in "standalone" mode when the server boots. + + Another possibility is to run one bootpd on each network + segment so each one can have a smaller bootptab. Only one + instance of bootpd may run on one server, so you would need + to use a different server for each network segment. + +My bootp clients are given responses with a boot file name that is +not a fully specified path. + + Make sure the TFTP directory or home directory tags are set: + :td=/tftpboot: (or) + :hd=/usr/boot: (for example) + diff --git a/usr.sbin/bootpd/README b/usr.sbin/bootpd/README new file mode 100644 index 00000000000..c7755b786dd --- /dev/null +++ b/usr.sbin/bootpd/README @@ -0,0 +1,133 @@ + +This is an enhanced version of the CMU BOOTP server which was derived +from the original BOOTP server created by Bill Croft at Stanford. +This version merges all the enhancements and bug-fixes from the +NetBSD, Columbia, and other versions. + +Please direct questions, comments, and bug reports to the list: + <bootp@andrew.cmu.edu> + +You can subscribe to this mailing list by sending mail to: + bootp-request@andrew.cmu.edu +(The body of the message should contain: "Add <your-address>") + +[ From the NetBSD README file: ] + +BOOTPD is a useful adjunct to the nfs diskless boot EPROM code. + +The alternatives for initiating a boot of a kernel across a network +are to use RARP protocol, or BOOTP protocol. BOOTP is more flexible; +it allows additional items of information to be returned to the +booting client; it also supports booting across gateways. + +[ From the CMU README file: ] + +Notes: +1) BOOTP was originally designed and implemented by Bill Croft at Stanford. + Much of the credit for the ideas and the code goes to him. We've added + code to support the vendor specific area of the packet as specified in + RFC1048. We've also improved the host lookup algorithm and added some + extra logging. + +2) The server now uses syslog to do logging. Specifically it uses the 4.3bsd + version. I've #ifdef'd all of these calls. If you are running 4.2 you + should compile without the -DSYSLOG switch. + +3) You must update your /etc/services file to contain the following two lines: + bootps 67/udp bootp # BOOTP Server + bootpc 68/udp # BOOTP Client + +4) Edit the bootptab. It has some explanitory comments, and there + is a manual entry describing its format (bootptab.5) + If you have any questions, just let us know. + +Construction: + [ See the file Installation which is more up-to-date. -gwr ] + + Make sure all of the files exist first. If anything is missing, + please contact either Walt Wimer or Drew Perkins by E-mail or phone. + Addresses and phone numbers are listed below. + + Type 'make'. The options at present are: -DSYSLOG which enables logging + code, -DDEBUG which enables table dumping via signals, and -DVEND_CMU + which enables the CMU extensions for CMU PC/IP. + + Edit the bootptab. The man page and the comments in the file should + explain how to go about doing so. If you have any problems, let me know. + + Type 'make install'. This should put all of the files in the right place. + + Edit your /etc/rc.local or /etc/inetd.conf file to start up bootpd upon + reboot. The following is a sample /etc/inetd.conf entry: + # BOOTP server + bootps dgram udp wait root /usr/etc/bootpd bootpd -i + +Care and feeding: + If you change the interface cards on your host or add new hosts you will + need to update /etc/bootptab. Just edit it as before. Once you write + it back out, bootpd will notice that there is a new copy and will + reread it the next time it gets a request. + + If your bootp clients don't get a response then several things might be + wrong. Most often, the entry for that host is not in the database. + Check the hardware address and then check the entry and make sure + everything is right. Other problems include the server machine crashing, + bad cables, and the like. If your network is very congested you should + try making your bootp clients send additional requests before giving up. + + +November 7, 1988 + + +Walter L. Wimer Drew D. Perkins +ww0n@andrew.cmu.edu ddp@andrew.cmu.edu +(412) 268-6252 (412) 268-8576 + +4910 Forbes Ave +Pittsburgh, PA 15213 + +[ Contents description by file: ] + +Announce* Text of release announcements +Changes Change history, reverse chronological +Installation Instructions for building and installing +Makefile* for "make" +README This file +ToDo Things not yet done +bootp.h The protocol header file +bootpd.8 Manual page for bootpd, boopgw +bootpd.c BOOTP server main module +bootpd.h header for above (and others) +bootpef.8 Manual page for bootpef +bootpef.c BOOTP extension file compiler +bootpgw.c BOOTP gateway main module +bootptab.5 A manual describing the bootptab format +bootptab.cmu A sample database file for the server +bootptab.mcs Another sample from <gwr@mc.com> +bootptest.8 Manual page for bootptest +bootptest.c BOOTP test program (fake client) +bootptest.h header for above +dovend.c Vendor Option builder (for bootpd, bootpef) +dovend.h header for above +dumptab.c Implements debugging dump for bootpd +getether.c For bootptest (not used yet) +getif.c Get network interface info. +getif.h header for above +hash.c The hash table module +hash.h header for above +hwaddr.c Hardware address support +hwaddr.h header for above +lookup.c Internet Protocol address lookup +lookup.h header for above +patchlevel.h Holds version numbers +print-bootp.c Prints BOOTP packets (taken from BSD tcpdump) +readfile.c The configuration file-reading routines +readfile.h header for above +report.c Does syslog-style messages +report.h header for above +strerror.c Library errno-to-string (for systems lacking it) +syslog.conf Sample config file for syslogd(8) +syslog.h For systems that lack syslog(3) +try*.c Test programs (for debugging) +tzone.c Get timezone offset +tzone.h header for above diff --git a/usr.sbin/bootpd/bootp.h b/usr.sbin/bootpd/bootp.h new file mode 100644 index 00000000000..b8ca2a59741 --- /dev/null +++ b/usr.sbin/bootpd/bootp.h @@ -0,0 +1,147 @@ +/************************************************************************ + Copyright 1988, 1991 by Carnegie Mellon University + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, provided +that the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of Carnegie Mellon University not be used +in advertising or publicity pertaining to distribution of the software +without specific, written prior permission. + +CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. +************************************************************************/ + +/* + * Bootstrap Protocol (BOOTP). RFC951 and RFC1395. + * + * $Id: bootp.h,v 1.1 1995/10/18 08:47:25 deraadt Exp $ + * + * + * This file specifies the "implementation-independent" BOOTP protocol + * information which is common to both client and server. + * + */ + +#include "bptypes.h" /* for int32, u_int32 */ + +#define BP_CHADDR_LEN 16 +#define BP_SNAME_LEN 64 +#define BP_FILE_LEN 128 +#define BP_VEND_LEN 64 +#define BP_MINPKTSZ 300 /* to check sizeof(struct bootp) */ + +struct bootp { + unsigned char bp_op; /* packet opcode type */ + unsigned char bp_htype; /* hardware addr type */ + unsigned char bp_hlen; /* hardware addr length */ + unsigned char bp_hops; /* gateway hops */ + u_int32 bp_xid; /* transaction ID */ + unsigned short bp_secs; /* seconds since boot began */ + unsigned short bp_flags; /* RFC1532 broadcast, etc. */ + struct in_addr bp_ciaddr; /* client IP address */ + struct in_addr bp_yiaddr; /* 'your' IP address */ + struct in_addr bp_siaddr; /* server IP address */ + struct in_addr bp_giaddr; /* gateway IP address */ + unsigned char bp_chaddr[BP_CHADDR_LEN]; /* client hardware address */ + char bp_sname[BP_SNAME_LEN]; /* server host name */ + char bp_file[BP_FILE_LEN]; /* boot file name */ + unsigned char bp_vend[BP_VEND_LEN]; /* vendor-specific area */ + /* note that bp_vend can be longer, extending to end of packet. */ +}; + +/* + * UDP port numbers, server and client. + */ +#define IPPORT_BOOTPS 67 +#define IPPORT_BOOTPC 68 + +#define BOOTREPLY 2 +#define BOOTREQUEST 1 + +/* + * Hardware types from Assigned Numbers RFC. + */ +#define HTYPE_ETHERNET 1 +#define HTYPE_EXP_ETHERNET 2 +#define HTYPE_AX25 3 +#define HTYPE_PRONET 4 +#define HTYPE_CHAOS 5 +#define HTYPE_IEEE802 6 +#define HTYPE_ARCNET 7 + +/* + * Vendor magic cookie (v_magic) for CMU + */ +#define VM_CMU "CMU" + +/* + * Vendor magic cookie (v_magic) for RFC1048 + */ +#define VM_RFC1048 { 99, 130, 83, 99 } + + + +/* + * Tag values used to specify what information is being supplied in + * the vendor (options) data area of the packet. + */ +/* RFC 1048 */ +#define TAG_END ((unsigned char) 255) +#define TAG_PAD ((unsigned char) 0) +#define TAG_SUBNET_MASK ((unsigned char) 1) +#define TAG_TIME_OFFSET ((unsigned char) 2) +#define TAG_GATEWAY ((unsigned char) 3) +#define TAG_TIME_SERVER ((unsigned char) 4) +#define TAG_NAME_SERVER ((unsigned char) 5) +#define TAG_DOMAIN_SERVER ((unsigned char) 6) +#define TAG_LOG_SERVER ((unsigned char) 7) +#define TAG_COOKIE_SERVER ((unsigned char) 8) +#define TAG_LPR_SERVER ((unsigned char) 9) +#define TAG_IMPRESS_SERVER ((unsigned char) 10) +#define TAG_RLP_SERVER ((unsigned char) 11) +#define TAG_HOST_NAME ((unsigned char) 12) +#define TAG_BOOT_SIZE ((unsigned char) 13) +/* RFC 1395 */ +#define TAG_DUMP_FILE ((unsigned char) 14) +#define TAG_DOMAIN_NAME ((unsigned char) 15) +#define TAG_SWAP_SERVER ((unsigned char) 16) +#define TAG_ROOT_PATH ((unsigned char) 17) +/* RFC 1497 */ +#define TAG_EXTEN_FILE ((unsigned char) 18) +/* RFC 1533 */ +#define TAG_NIS_DOMAIN ((unsigned char) 40) +#define TAG_NIS_SERVER ((unsigned char) 41) +#define TAG_NTP_SERVER ((unsigned char) 42) +/* DHCP maximum message size. */ +#define TAG_MAX_MSGSZ ((unsigned char) 57) + +/* XXX - Add new tags here */ + + +/* + * "vendor" data permitted for CMU bootp clients. + */ + +struct cmu_vend { + char v_magic[4]; /* magic number */ + u_int32 v_flags; /* flags/opcodes, etc. */ + struct in_addr v_smask; /* Subnet mask */ + struct in_addr v_dgate; /* Default gateway */ + struct in_addr v_dns1, v_dns2; /* Domain name servers */ + struct in_addr v_ins1, v_ins2; /* IEN-116 name servers */ + struct in_addr v_ts1, v_ts2; /* Time servers */ + int32 v_unused[6]; /* currently unused */ +}; + + +/* v_flags values */ +#define VF_SMASK 1 /* Subnet mask field contains valid data */ diff --git a/usr.sbin/bootpd/bootpd.8 b/usr.sbin/bootpd/bootpd.8 new file mode 100644 index 00000000000..d0c9853f435 --- /dev/null +++ b/usr.sbin/bootpd/bootpd.8 @@ -0,0 +1,305 @@ +.\" Copyright (c) 1988, 1989, 1991 Carnegie Mellon University +.\" +.\" $Header: /cvs/OpenBSD/src/usr.sbin/bootpd/Attic/bootpd.8,v 1.1 1995/10/18 08:47:25 deraadt Exp $ +.\" +.TH BOOTPD 8 "November 06, 1993" "Carnegie Mellon University" +.SH NAME +bootpd, bootpgw \- Internet Boot Protocol server/gateway +.SH SYNOPSIS +.B bootpd +[ +.B \-i +.B \-s +.B \-t +timeout +.B \-d +level +.B \-c +chdir\-path +] +[ +.I bootptab +[ +.I dumpfile +] ] +.br +.B bootpgw +[ +.B \-i +.B \-s +.B \-t +timeout +.B \-d +level +] server +.SH DESCRIPTION +.I Bootpd +implements an Internet Bootstrap Protocol (BOOTP) server as defined in +RFC951, RFC1532, and RFC1533. +.I Bootpgw +implements a simple BOOTP gateway which can be used to forward +requests and responses between clients on one subnet and a +BOOTP server (i.e. +.IR bootpd ) +on another subnet. While either +.I bootpd +or +.I bootpgw +will forward BOOTREPLY packets, only +.I bootpgw +will forward BOOTREQUEST packets. +.PP +One host on each network segment is normally configured to run either +.I bootpd +or +.I bootpgw +from +.I inetd +by including one of the following lines in the file +.IR /etc/inetd.conf : +.IP +bootps dgram udp wait root /etc/bootpd bootpd bootptab +.br +bootps dgram udp wait root /etc/bootpgw bootpgw server +.PP +This mode of operation is referred to as "inetd mode" and causes +.I bootpd +(or +.IR bootpgw ) +to be started only when a boot request arrives. If it does not +receive another packet within fifteen minutes of the last one +it received, it will exit to conserve system resources. The +.B \-t +option controls this timeout (see OPTIONS). +.PP +It is also possible to run +.I bootpd +(or +.IR bootpgw ) +in "standalone mode" (without +.IR inetd ) +by simply invoking it from a shell like any other regular command. +Standalone mode is particularly useful when +.I bootpd +is used with a large configuration database, where the start up +delay might otherwise prevent timely response to client requests. +(Automatic start up in standalone mode can be done by invoking +.I bootpd +from within +.IR /etc/rc.local , +for example.) +Standalone mode is less useful for +.I bootgw +which +has very little start up delay because +it does not read a configuration file. +.PP +Either program automatically detects whether it was invoked from inetd +or from a shell and automatically selects the appropriate mode. +The +.B \-s +or +.B \-i +option may be used to force standalone or inetd mode respectively +(see OPTIONS). +.SH OPTIONS +.TP +.BI \-t \ timeout +Specifies the +.I timeout +value (in minutes) that a +.I bootpd +or +.I bootpgw +process will wait for a BOOTP packet before exiting. +If no packets are recieved for +.I timeout +seconds, then the program will exit. +A timeout value of zero means "run forever". +In standalone mode, this option is forced to zero. +.TP +.BI \-d \ debug\-level +Sets the +.I debug\-level +variable that controls the amount of debugging messages generated. +For example, -d4 or -d 4 will set the debugging level to 4. +For compatibility with older versions of +.IR bootpd , +omitting the numeric parameter (i.e. just -d) will +simply increment the debug level by one. +.TP +.BI \-c \ chdir\-path +Sets the current directory used by +.I bootpd +while checking the existence and size of client boot files. This is +useful when client boot files are specified as relative pathnames, and +.I bootpd +needs to use the same current directory as the TFTP server +(typically /tftpboot). This option is not recoginzed by +.IR bootpgw . +.TP +.B \-i +Force inetd mode. This option is obsolete, but remains for +compatibility with older versions of +.IR bootpd . +.TP +.B \-s +Force standalone mode. This option is obsolete, but remains for +compatibility with older versions of +.IR bootpd . +.TP +.I bootptab +Specifies the name of the configuration file from which +.I bootpd +loads its database of known clients and client options +.RI ( bootpd +only). +.TP +.I dumpfile +Specifies the name of the file that +.I bootpd +will dump its internal database into when it receives a +SIGUSR1 signal +.RI ( bootpd +only). This option is only recognized if +.I bootpd +was compiled with the -DDEBUG flag. +.TP +.I server +Specifies the name of a BOOTP server to which +.I bootpgw +will forward all BOOTREQUEST packets it receives +.RI ( bootpgw +only). +.SH OPERATION +.PP +Both +.I bootpd +and +.I bootpgw +operate similarly in that both listen for any packets sent to the +.I bootps +port, and both simply forward any BOOTREPLY packets. +They differ in their handling of BOOTREQUEST packets. +.PP +When +.I bootpgw +is started, it determines the address of a BOOTP server +whose name is provided as a command line parameter. When +.I bootpgw +receives a BOOTREQUEST packet, it sets the "gateway address" +and "hop count" fields in the packet and forwards the packet +to the BOOTP server at the address determined earlier. +Requests are forwarded only if they indicate that +the client has been waiting for at least three seconds. +.PP +When +.I bootpd +is started it reads a configuration file, (normally +.IR /etc/bootptab ) +that initializes the internal database of known clients and client +options. This internal database is reloaded +from the configuration file when +.I bootpd +receives a hangup signal (SIGHUP) or when it discovers that the +configuration file has changed. +.PP +When +.I bootpd +receives a BOOTREQUEST packet, it +.\" checks the modification time of the +.\" configuration file and reloads the database if necessary. Then it +looks for a database entry matching the client request. +If the client is known, +.I bootpd +composes a BOOTREPLY packet using the database entry found above, +and sends the reply to the client (possibly using a gateway). +If the client is unknown, the request is discarded +(with a notice if debug > 0). +.PP +If +.I bootpd +is compiled with the -DDEBUG option, receipt of a SIGUSR1 signal causes +it to dump its internal database to the file +.I /etc/bootpd.dump +or the dumpfile specified as a command line parameter. +.PP +During initialization, both programs +determine the UDP port numbers to be used by calling +.I getservbyname +(which nomally uses +.IR /etc/services). +Two service names (and port numbers) are used: +.IP +bootps \- BOOTP Server listening port +.br +bootpc \- BOOTP Client destination port +.LP +If the port numbers cannot +be determined using +.I getservbyname +then the values default to boopts=67 and bootpc=68. +.SH FILES +.TP 20 +/etc/bootptab +Database file read by +.IR bootpd . +.TP +/etc/bootpd.dump +Debugging dump file created by +.IR bootpd . +.TP +/etc/services +Internet service numbers. +.TP +/tftpboot +Current directory typically used by the TFTP server and +.IR bootpd . + +.SH BUGS +Individual host entries must not exceed 1024 characters. + +.SH CREDITS +.PP +This distribution is currently maintained by +Walter L. Wimer <walt+@cmu.edu>. +.PP +The original BOOTP server was created by +Bill Croft at Stanford University in January 1986. +.PP +The current version of +.I bootpd +is primarily the work of David Kovar, +Drew D. Perkins, and Walter L. Wimer, +at Carnegie Mellon University. +.TP +Enhancements and bug\-fixes have been contributed by: +(in alphabetical order) +.br +Danny Backx <db@sunbim.be> +.br +John Brezak <brezak@ch.hp.com> +.br +Frank da Cruz <fdc@cc.columbia.edu> +.br +David R. Linn <drl@vuse.vanderbilt.edu> +.br +Jim McKim <mckim@lerc.nasa.gov> +.br +Gordon W. Ross <gwr@mc.com> +.br +Jason Zions <jazz@hal.com> +.SH "SEE ALSO" +.LP +bootptab(5), inetd(8), tftpd(8) +.LP +DARPA Internet Request For Comments: +.TP 10 +RFC951 +Bootstrap Protocol +.TP 10 +RFC1532 +Clarifications and Extensions for the Bootstrap Protocol +.TP 10 +RFC1533 +DHCP Options and BOOTP Vendor Extensions diff --git a/usr.sbin/bootpd/bootpd.c b/usr.sbin/bootpd/bootpd.c new file mode 100644 index 00000000000..0ab367a3d05 --- /dev/null +++ b/usr.sbin/bootpd/bootpd.c @@ -0,0 +1,1380 @@ +/************************************************************************ + Copyright 1988, 1991 by Carnegie Mellon University + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, provided +that the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of Carnegie Mellon University not be used +in advertising or publicity pertaining to distribution of the software +without specific, written prior permission. + +CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. +************************************************************************/ + +#ifndef lint +static char rcsid[] = "$Id: bootpd.c,v 1.1 1995/10/18 08:47:25 deraadt Exp $"; +#endif + +/* + * BOOTP (bootstrap protocol) server daemon. + * + * Answers BOOTP request packets from booting client machines. + * See [SRI-NIC]<RFC>RFC951.TXT for a description of the protocol. + * See [SRI-NIC]<RFC>RFC1048.TXT for vendor-information extensions. + * See RFC 1395 for option tags 14-17. + * See accompanying man page -- bootpd.8 + * + * HISTORY + * See ./Changes + * + * BUGS + * See ./ToDo + */ + + + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/file.h> +#include <sys/time.h> +#include <sys/stat.h> + +#include <net/if.h> +#include <netinet/in.h> +#include <arpa/inet.h> /* inet_ntoa */ + +#ifndef NO_UNISTD +#include <unistd.h> +#endif +#include <stdlib.h> +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <netdb.h> +#include <syslog.h> +#include <assert.h> + +#ifdef NO_SETSID +# include <fcntl.h> /* for O_RDONLY, etc */ +#endif + +#ifdef SVR4 +/* Using sigset() avoids the need to re-arm each time. */ +#define signal sigset +#endif + +#ifndef USE_BFUNCS +# include <memory.h> +/* Yes, memcpy is OK here (no overlapped copies). */ +# define bcopy(a,b,c) memcpy(b,a,c) +# define bzero(p,l) memset(p,0,l) +# define bcmp(a,b,c) memcmp(a,b,c) +#endif + +#include "bootp.h" +#include "hash.h" +#include "hwaddr.h" +#include "bootpd.h" +#include "dovend.h" +#include "getif.h" +#include "readfile.h" +#include "report.h" +#include "tzone.h" +#include "patchlevel.h" + +#ifndef CONFIG_FILE +#define CONFIG_FILE "/etc/bootptab" +#endif +#ifndef DUMPTAB_FILE +#define DUMPTAB_FILE "/tmp/bootpd.dump" +#endif + + + +/* + * Externals, forward declarations, and global variables + */ + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +extern void dumptab P((char *)); + +PRIVATE void catcher P((int)); +PRIVATE int chk_access P((char *, int32 *)); +#ifdef VEND_CMU +PRIVATE void dovend_cmu P((struct bootp *, struct host *)); +#endif +PRIVATE void dovend_rfc1048 P((struct bootp *, struct host *, int32)); +PRIVATE void handle_reply P((void)); +PRIVATE void handle_request P((void)); +PRIVATE void sendreply P((int forward, int32 dest_override)); +PRIVATE void usage P((void)); + +#undef P + +/* + * IP port numbers for client and server obtained from /etc/services + */ + +u_short bootps_port, bootpc_port; + + +/* + * Internet socket and interface config structures + */ + +struct sockaddr_in bind_addr; /* Listening */ +struct sockaddr_in recv_addr; /* Packet source */ +struct sockaddr_in send_addr; /* destination */ + + +/* + * option defaults + */ +int debug = 0; /* Debugging flag (level) */ +struct timeval actualtimeout = +{ /* fifteen minutes */ + 15 * 60L, /* tv_sec */ + 0 /* tv_usec */ +}; + +/* + * General + */ + +int s; /* Socket file descriptor */ +char *pktbuf; /* Receive packet buffer */ +int pktlen; +char *progname; +char *chdir_path; +char hostname[MAXHOSTNAMELEN]; /* System host name */ +struct in_addr my_ip_addr; + +/* Flags set by signal catcher. */ +PRIVATE int do_readtab = 0; +PRIVATE int do_dumptab = 0; + +/* + * Globals below are associated with the bootp database file (bootptab). + */ + +char *bootptab = CONFIG_FILE; +char *bootpd_dump = DUMPTAB_FILE; + + + +/* + * Initialization such as command-line processing is done and then the + * main server loop is started. + */ + +void +main(argc, argv) + int argc; + char **argv; +{ + struct timeval *timeout; + struct bootp *bp; + struct servent *servp; + struct hostent *hep; + char *stmp; + int n, ba_len, ra_len; + int nfound, readfds; + int standalone; + + progname = strrchr(argv[0], '/'); + if (progname) progname++; + else progname = argv[0]; + + /* + * Initialize logging. + */ + report_init(0); /* uses progname */ + + /* + * Log startup + */ + report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL); + + /* Debugging for compilers with struct padding. */ + assert(sizeof(struct bootp) == BP_MINPKTSZ); + + /* Get space for receiving packets and composing replies. */ + pktbuf = malloc(MAX_MSG_SIZE); + if (!pktbuf) { + report(LOG_ERR, "malloc failed"); + exit(1); + } + bp = (struct bootp *) pktbuf; + + /* + * Check to see if a socket was passed to us from inetd. + * + * Use getsockname() to determine if descriptor 0 is indeed a socket + * (and thus we are probably a child of inetd) or if it is instead + * something else and we are running standalone. + */ + s = 0; + ba_len = sizeof(bind_addr); + bzero((char *) &bind_addr, ba_len); + errno = 0; + standalone = TRUE; + if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) { + /* + * Descriptor 0 is a socket. Assume we are a child of inetd. + */ + if (bind_addr.sin_family == AF_INET) { + standalone = FALSE; + bootps_port = ntohs(bind_addr.sin_port); + } else { + /* Some other type of socket? */ + report(LOG_ERR, "getsockname: not an INET socket"); + } + } + + /* + * Set defaults that might be changed by option switches. + */ + stmp = NULL; + timeout = &actualtimeout; + + /* + * Read switches. + */ + for (argc--, argv++; argc > 0; argc--, argv++) { + if (argv[0][0] != '-') + break; + switch (argv[0][1]) { + + case 'c': /* chdir_path */ + if (argv[0][2]) { + stmp = &(argv[0][2]); + } else { + argc--; + argv++; + stmp = argv[0]; + } + if (!stmp || (stmp[0] != '/')) { + fprintf(stderr, + "bootpd: invalid chdir specification\n"); + break; + } + chdir_path = stmp; + break; + + case 'd': /* debug level */ + if (argv[0][2]) { + stmp = &(argv[0][2]); + } else if (argv[1] && argv[1][0] == '-') { + /* + * Backwards-compatible behavior: + * no parameter, so just increment the debug flag. + */ + debug++; + break; + } else { + argc--; + argv++; + stmp = argv[0]; + } + if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { + fprintf(stderr, + "%s: invalid debug level\n", progname); + break; + } + debug = n; + break; + + case 'h': /* override hostname */ + if (argv[0][2]) { + stmp = &(argv[0][2]); + } else { + argc--; + argv++; + stmp = argv[0]; + } + if (!stmp) { + fprintf(stderr, + "bootpd: missing hostname\n"); + break; + } + strncpy(hostname, stmp, sizeof(hostname)-1); + break; + + case 'i': /* inetd mode */ + standalone = FALSE; + break; + + case 's': /* standalone mode */ + standalone = TRUE; + break; + + case 't': /* timeout */ + if (argv[0][2]) { + stmp = &(argv[0][2]); + } else { + argc--; + argv++; + stmp = argv[0]; + } + if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { + fprintf(stderr, + "%s: invalid timeout specification\n", progname); + break; + } + actualtimeout.tv_sec = (int32) (60 * n); + /* + * If the actual timeout is zero, pass a NULL pointer + * to select so it blocks indefinitely, otherwise, + * point to the actual timeout value. + */ + timeout = (n > 0) ? &actualtimeout : NULL; + break; + + default: + fprintf(stderr, "%s: unknown switch: -%c\n", + progname, argv[0][1]); + usage(); + break; + + } /* switch */ + } /* for args */ + + /* + * Override default file names if specified on the command line. + */ + if (argc > 0) + bootptab = argv[0]; + + if (argc > 1) + bootpd_dump = argv[1]; + + /* + * Get my hostname and IP address. + */ + if (hostname[0] == '\0') { + if (gethostname(hostname, sizeof(hostname)) == -1) { + fprintf(stderr, "bootpd: can't get hostname\n"); + exit(1); + } + } + hep = gethostbyname(hostname); + if (!hep) { + fprintf(stderr, "Can not get my IP address\n"); + exit(1); + } + bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr)); + + if (standalone) { + /* + * Go into background and disassociate from controlling terminal. + */ + if (debug < 3) { + if (fork()) + exit(0); +#ifdef NO_SETSID + setpgrp(0,0); +#ifdef TIOCNOTTY + n = open("/dev/tty", O_RDWR); + if (n >= 0) { + ioctl(n, TIOCNOTTY, (char *) 0); + (void) close(n); + } +#endif /* TIOCNOTTY */ +#else /* SETSID */ + if (setsid() < 0) + perror("setsid"); +#endif /* SETSID */ + } /* if debug < 3 */ + + /* + * Nuke any timeout value + */ + timeout = NULL; + + } /* if standalone (1st) */ + + /* Set the cwd (i.e. to /tftpboot) */ + if (chdir_path) { + if (chdir(chdir_path) < 0) + report(LOG_ERR, "%s: chdir failed", chdir_path); + } + + /* Get the timezone. */ + tzone_init(); + + /* Allocate hash tables. */ + rdtab_init(); + + /* + * Read the bootptab file. + */ + readtab(1); /* force read */ + + if (standalone) { + + /* + * Create a socket. + */ + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + report(LOG_ERR, "socket: %s", get_network_errmsg()); + exit(1); + } + + /* + * Get server's listening port number + */ + servp = getservbyname("bootps", "udp"); + if (servp) { + bootps_port = ntohs((u_short) servp->s_port); + } else { + bootps_port = (u_short) IPPORT_BOOTPS; + report(LOG_ERR, + "udp/bootps: unknown service -- assuming port %d", + bootps_port); + } + + /* + * Bind socket to BOOTPS port. + */ + bind_addr.sin_family = AF_INET; + bind_addr.sin_addr.s_addr = INADDR_ANY; + bind_addr.sin_port = htons(bootps_port); + if (bind(s, (struct sockaddr *) &bind_addr, + sizeof(bind_addr)) < 0) + { + report(LOG_ERR, "bind: %s", get_network_errmsg()); + exit(1); + } + } /* if standalone (2nd)*/ + + /* + * Get destination port number so we can reply to client + */ + servp = getservbyname("bootpc", "udp"); + if (servp) { + bootpc_port = ntohs(servp->s_port); + } else { + report(LOG_ERR, + "udp/bootpc: unknown service -- assuming port %d", + IPPORT_BOOTPC); + bootpc_port = (u_short) IPPORT_BOOTPC; + } + + /* + * Set up signals to read or dump the table. + */ + if ((long) signal(SIGHUP, catcher) < 0) { + report(LOG_ERR, "signal: %s", get_errmsg()); + exit(1); + } + if ((long) signal(SIGUSR1, catcher) < 0) { + report(LOG_ERR, "signal: %s", get_errmsg()); + exit(1); + } + + /* + * Process incoming requests. + */ + for (;;) { + readfds = 1 << s; + nfound = select(s + 1, (fd_set *)&readfds, NULL, NULL, timeout); + if (nfound < 0) { + if (errno != EINTR) { + report(LOG_ERR, "select: %s", get_errmsg()); + } + /* + * Call readtab() or dumptab() here to avoid the + * dangers of doing I/O from a signal handler. + */ + if (do_readtab) { + do_readtab = 0; + readtab(1); /* force read */ + } + if (do_dumptab) { + do_dumptab = 0; + dumptab(bootpd_dump); + } + continue; + } + if (!(readfds & (1 << s))) { + if (debug > 1) + report(LOG_INFO, "exiting after %ld minutes of inactivity", + actualtimeout.tv_sec / 60); + exit(0); + } + ra_len = sizeof(recv_addr); + n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0, + (struct sockaddr *) &recv_addr, &ra_len); + if (n <= 0) { + continue; + } + if (debug > 1) { + report(LOG_INFO, "recvd pkt from IP addr %s", + inet_ntoa(recv_addr.sin_addr)); + } + if (n < sizeof(struct bootp)) { + if (debug) { + report(LOG_INFO, "received short packet"); + } + continue; + } + pktlen = n; + + readtab(0); /* maybe re-read bootptab */ + + switch (bp->bp_op) { + case BOOTREQUEST: + handle_request(); + break; + case BOOTREPLY: + handle_reply(); + break; + } + } +} + + + + +/* + * Print "usage" message and exit + */ + +PRIVATE void +usage() +{ + fprintf(stderr, + "usage: bootpd [-d level] [-i] [-s] [-t timeout] [configfile [dumpfile]]\n"); + fprintf(stderr, "\t -c n\tset current directory\n"); + fprintf(stderr, "\t -d n\tset debug level\n"); + fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n"); + fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n"); + fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n"); + exit(1); +} + +/* Signal catchers */ +PRIVATE void +catcher(sig) + int sig; +{ + if (sig == SIGHUP) + do_readtab = 1; + if (sig == SIGUSR1) + do_dumptab = 1; +#ifdef SYSV + /* For older "System V" derivatives with no sigset(). */ + /* XXX - Should just do it the POSIX way (sigaction). */ + signal(sig, catcher); +#endif +} + + + +/* + * Process BOOTREQUEST packet. + * + * Note: This version of the bootpd.c server never forwards + * a request to another server. That is the job of a gateway + * program such as the "bootpgw" program included here. + * + * (Also this version does not interpret the hostname field of + * the request packet; it COULD do a name->address lookup and + * forward the request there.) + */ +PRIVATE void +handle_request() +{ + struct bootp *bp = (struct bootp *) pktbuf; + struct host *hp = NULL; + struct host dummyhost; + int32 bootsize = 0; + unsigned hlen, hashcode; + int32 dest; + char realpath[1024]; + char *clntpath; + char *homedir, *bootfile; + int n; + + /* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */ + + /* + * If the servername field is set, compare it against us. + * If we're not being addressed, ignore this request. + * If the server name field is null, throw in our name. + */ + if (strlen(bp->bp_sname)) { + if (strcmp(bp->bp_sname, hostname)) { + if (debug) + report(LOG_INFO, "\ +ignoring request for server %s from client at %s address %s", + bp->bp_sname, netname(bp->bp_htype), + haddrtoa(bp->bp_chaddr, bp->bp_hlen)); + /* XXX - Is it correct to ignore such a request? -gwr */ + return; + } + } else { + strcpy(bp->bp_sname, hostname); + } + + /* Convert the request into a reply. */ + bp->bp_op = BOOTREPLY; + if (bp->bp_ciaddr.s_addr == 0) { + /* + * client doesnt know his IP address, + * search by hardware address. + */ + if (debug > 1) { + report(LOG_INFO, "request from %s address %s", + netname(bp->bp_htype), + haddrtoa(bp->bp_chaddr, bp->bp_hlen)); + } + hlen = haddrlength(bp->bp_htype); + if (hlen != bp->bp_hlen) { + report(LOG_NOTICE, "bad addr len from from %s address %s", + netname(bp->bp_htype), + haddrtoa(bp->bp_chaddr, hlen)); + } + dummyhost.htype = bp->bp_htype; + bcopy(bp->bp_chaddr, dummyhost.haddr, hlen); + hashcode = hash_HashFunction(bp->bp_chaddr, hlen); + hp = (struct host *) hash_Lookup(hwhashtable, hashcode, hwlookcmp, + &dummyhost); + if (hp == NULL && + bp->bp_htype == HTYPE_IEEE802) + { + /* Try again with address in "canonical" form. */ + haddr_conv802(bp->bp_chaddr, dummyhost.haddr, hlen); + if (debug > 1) { + report(LOG_INFO, "\ +HW addr type is IEEE 802. convert to %s and check again\n", + haddrtoa(dummyhost.haddr, bp->bp_hlen)); + } + hashcode = hash_HashFunction(dummyhost.haddr, hlen); + hp = (struct host *) hash_Lookup(hwhashtable, hashcode, + hwlookcmp, &dummyhost); + } + if (hp == NULL) { + /* + * XXX - Add dynamic IP address assignment? + */ + if (debug > 1) + report(LOG_INFO, "unknown client %s address %s", + netname(bp->bp_htype), + haddrtoa(bp->bp_chaddr, bp->bp_hlen)); + return; /* not found */ + } + (bp->bp_yiaddr).s_addr = hp->iaddr.s_addr; + + } else { + + /* + * search by IP address. + */ + if (debug > 1) { + report(LOG_INFO, "request from IP addr %s", + inet_ntoa(bp->bp_ciaddr)); + } + dummyhost.iaddr.s_addr = bp->bp_ciaddr.s_addr; + hashcode = hash_HashFunction((u_char *) &(bp->bp_ciaddr.s_addr), 4); + hp = (struct host *) hash_Lookup(iphashtable, hashcode, iplookcmp, + &dummyhost); + if (hp == NULL) { + if (debug > 1) { + report(LOG_NOTICE, "IP address not found: %s", + inet_ntoa(bp->bp_ciaddr)); + } + return; + } + } + + if (debug) { + report(LOG_INFO, "found %s (%s)", inet_ntoa(hp->iaddr), + hp->hostname->string); + } + + /* + * If there is a response delay threshold, ignore requests + * with a timestamp lower than the threshold. + */ + if (hp->flags.min_wait) { + u_int32 t = (u_int32) ntohs(bp->bp_secs); + if (t < hp->min_wait) { + if (debug > 1) + report(LOG_INFO, + "ignoring request due to timestamp (%d < %d)", + t, hp->min_wait); + return; + } + } + +#ifdef YORK_EX_OPTION + /* + * The need for the "ex" tag arose out of the need to empty + * shared networked drives on diskless PCs. This solution is + * not very clean but it does work fairly well. + * Written by Edmund J. Sutcliffe <edmund@york.ac.uk> + * + * XXX - This could compromise security if a non-trusted user + * managed to write an entry in the bootptab with :ex=trojan: + * so I would leave this turned off unless you need it. -gwr + */ + /* Run a program, passing the client name as a parameter. */ + if (hp->flags.exec_file) { + char tst[100]; + /* XXX - Check string lengths? -gwr */ + strcpy (tst, hp->exec_file->string); + strcat (tst, " "); + strcat (tst, hp->hostname->string); + strcat (tst, " &"); + if (debug) + report(LOG_INFO, "executing %s", tst); + system(tst); /* Hope this finishes soon... */ + } +#endif /* YORK_EX_OPTION */ + + /* + * If a specific TFTP server address was specified in the bootptab file, + * fill it in, otherwise zero it. + * XXX - Rather than zero it, should it be the bootpd address? -gwr + */ + (bp->bp_siaddr).s_addr = (hp->flags.bootserver) ? + hp->bootserver.s_addr : 0L; + +#ifdef STANFORD_PROM_COMPAT + /* + * Stanford bootp PROMs (for a Sun?) have no way to leave + * the boot file name field blank (because the boot file + * name is automatically generated from some index). + * As a work-around, this little hack allows those PROMs to + * specify "sunboot14" with the same effect as a NULL name. + * (The user specifies boot device 14 or some such magic.) + */ + if (strcmp(bp->bp_file, "sunboot14") == 0) + bp->bp_file[0] = '\0'; /* treat it as unspecified */ +#endif + + /* + * Fill in the client's proper bootfile. + * + * If the client specifies an absolute path, try that file with a + * ".host" suffix and then without. If the file cannot be found, no + * reply is made at all. + * + * If the client specifies a null or relative file, use the following + * table to determine the appropriate action: + * + * Homedir Bootfile Client's file + * specified? specified? specification Action + * ------------------------------------------------------------------- + * No No Null Send null filename + * No No Relative Discard request + * No Yes Null Send if absolute else null + * No Yes Relative Discard request *XXX + * Yes No Null Send null filename + * Yes No Relative Lookup with ".host" + * Yes Yes Null Send home/boot or bootfile + * Yes Yes Relative Lookup with ".host" *XXX + * + */ + + /* + * XXX - I don't like the policy of ignoring a client when the + * boot file is not accessible. The TFTP server might not be + * running on the same machine as the BOOTP server, in which + * case checking accessibility of the boot file is pointless. + * + * Therefore, file accessibility is now demanded ONLY if you + * define CHECK_FILE_ACCESS in the Makefile options. -gwr + */ + + /* + * The "real" path is as seen by the BOOTP daemon on this + * machine, while the client path is relative to the TFTP + * daemon chroot directory (i.e. /tftpboot). + */ + if (hp->flags.tftpdir) { + strcpy(realpath, hp->tftpdir->string); + clntpath = &realpath[strlen(realpath)]; + } else { + realpath[0] = '\0'; + clntpath = realpath; + } + + /* + * Determine client's requested homedir and bootfile. + */ + homedir = NULL; + bootfile = NULL; + if (bp->bp_file[0]) { + homedir = bp->bp_file; + bootfile = strrchr(homedir, '/'); + if (bootfile) { + if (homedir == bootfile) + homedir = NULL; + *bootfile++ = '\0'; + } else { + /* no "/" in the string */ + bootfile = homedir; + homedir = NULL; + } + if (debug > 2) { + report(LOG_INFO, "requested path=\"%s\" file=\"%s\"", + (homedir) ? homedir : "", + (bootfile) ? bootfile : ""); + } + } + + /* + * Specifications in bootptab override client requested values. + */ + if (hp->flags.homedir) + homedir = hp->homedir->string; + if (hp->flags.bootfile) + bootfile = hp->bootfile->string; + + /* + * Construct bootfile path. + */ + if (homedir) { + if (homedir[0] != '/') + strcat(clntpath, "/"); + strcat(clntpath, homedir); + homedir = NULL; + } + if (bootfile) { + if (bootfile[0] != '/') + strcat(clntpath, "/"); + strcat(clntpath, bootfile); + bootfile = NULL; + } + + /* + * First try to find the file with a ".host" suffix + */ + n = strlen(clntpath); + strcat(clntpath, "."); + strcat(clntpath, hp->hostname->string); + if (chk_access(realpath, &bootsize) < 0) { + clntpath[n] = 0; /* Try it without the suffix */ + if (chk_access(realpath, &bootsize) < 0) { + /* neither "file.host" nor "file" was found */ +#ifdef CHECK_FILE_ACCESS + + if (bp->bp_file[0]) { + /* + * Client wanted specific file + * and we didn't have it. + */ + report(LOG_NOTICE, + "requested file not found: \"%s\"", clntpath); + return; + } + /* + * Client didn't ask for a specific file and we couldn't + * access the default file, so just zero-out the bootfile + * field in the packet and continue processing the reply. + */ + bzero(bp->bp_file, sizeof(bp->bp_file)); + goto null_file_name; + +#else /* CHECK_FILE_ACCESS */ + + /* Complain only if boot file size was needed. */ + if (hp->flags.bootsize_auto) { + report(LOG_ERR, "can not determine size of file \"%s\"", + clntpath); + } + +#endif /* CHECK_FILE_ACCESS */ + } + } + strncpy(bp->bp_file, clntpath, BP_FILE_LEN); + if (debug > 2) + report(LOG_INFO, "bootfile=\"%s\"", clntpath); + +null_file_name: + + + /* + * Handle vendor options based on magic number. + */ + + if (debug > 1) { + report(LOG_INFO, "vendor magic field is %d.%d.%d.%d", + (int) ((bp->bp_vend)[0]), + (int) ((bp->bp_vend)[1]), + (int) ((bp->bp_vend)[2]), + (int) ((bp->bp_vend)[3])); + } + /* + * If this host isn't set for automatic vendor info then copy the + * specific cookie into the bootp packet, thus forcing a certain + * reply format. Only force reply format if user specified it. + */ + if (hp->flags.vm_cookie) { + /* Slam in the user specified magic number. */ + bcopy(hp->vm_cookie, bp->bp_vend, 4); + } + /* + * Figure out the format for the vendor-specific info. + * Note that bp->bp_vend may have been set above. + */ + if (!bcmp(bp->bp_vend, vm_rfc1048, 4)) { + /* RFC1048 conformant bootp client */ + dovend_rfc1048(bp, hp, bootsize); + if (debug > 1) { + report(LOG_INFO, "sending reply (with RFC1048 options)"); + } + } +#ifdef VEND_CMU + else if (!bcmp(bp->bp_vend, vm_cmu, 4)) { + dovend_cmu(bp, hp); + if (debug > 1) { + report(LOG_INFO, "sending reply (with CMU options)"); + } + } +#endif + else { + if (debug > 1) { + report(LOG_INFO, "sending reply (with no options)"); + } + } + + dest = (hp->flags.reply_addr) ? + hp->reply_addr.s_addr : 0L; + + /* not forwarded */ + sendreply(0, dest); +} + + +/* + * Process BOOTREPLY packet. + */ +PRIVATE void +handle_reply() +{ + if (debug) { + report(LOG_INFO, "processing boot reply"); + } + /* forwarded, no destination override */ + sendreply(1, 0); +} + + +/* + * Send a reply packet to the client. 'forward' flag is set if we are + * not the originator of this reply packet. + */ +PRIVATE void +sendreply(forward, dst_override) + int forward; + int32 dst_override; +{ + struct bootp *bp = (struct bootp *) pktbuf; + struct in_addr dst; + u_short port = bootpc_port; + unsigned char *ha; + int len; + + /* + * XXX - Should honor bp_flags "broadcast" bit here. + * Temporary workaround: use the :ra=ADDR: option to + * set the reply address to the broadcast address. + */ + + /* + * If the destination address was specified explicitly + * (i.e. the broadcast address for HP compatiblity) + * then send the response to that address. Otherwise, + * act in accordance with RFC951: + * If the client IP address is specified, use that + * else if gateway IP address is specified, use that + * else make a temporary arp cache entry for the client's + * NEW IP/hardware address and use that. + */ + if (dst_override) { + dst.s_addr = dst_override; + if (debug > 1) { + report(LOG_INFO, "reply address override: %s", + inet_ntoa(dst)); + } + } else if (bp->bp_ciaddr.s_addr) { + dst = bp->bp_ciaddr; + } else if (bp->bp_giaddr.s_addr && forward == 0) { + dst = bp->bp_giaddr; + port = bootps_port; + if (debug > 1) { + report(LOG_INFO, "sending reply to gateway %s", + inet_ntoa(dst)); + } + } else { + dst = bp->bp_yiaddr; + ha = bp->bp_chaddr; + len = bp->bp_hlen; + if (len > MAXHADDRLEN) + len = MAXHADDRLEN; + + if (debug > 1) + report(LOG_INFO, "setarp %s - %s", + inet_ntoa(dst), haddrtoa(ha, len)); + setarp(s, &dst, ha, len); + } + + if ((forward == 0) && + (bp->bp_siaddr.s_addr == 0)) + { + struct ifreq *ifr; + struct in_addr siaddr; + /* + * If we are originating this reply, we + * need to find our own interface address to + * put in the bp_siaddr field of the reply. + * If this server is multi-homed, pick the + * 'best' interface (the one on the same net + * as the client). Of course, the client may + * be on the other side of a BOOTP gateway... + */ + ifr = getif(s, &dst); + if (ifr) { + struct sockaddr_in *sip; + sip = (struct sockaddr_in *) &(ifr->ifr_addr); + siaddr = sip->sin_addr; + } else { + /* Just use my "official" IP address. */ + siaddr = my_ip_addr; + } + + /* XXX - No need to set bp_giaddr here. */ + + /* Finally, set the server address field. */ + bp->bp_siaddr = siaddr; + } + /* Set up socket address for send. */ + send_addr.sin_family = AF_INET; + send_addr.sin_port = htons(port); + send_addr.sin_addr = dst; + + /* Send reply with same size packet as request used. */ + if (sendto(s, pktbuf, pktlen, 0, + (struct sockaddr *) &send_addr, + sizeof(send_addr)) < 0) + { + report(LOG_ERR, "sendto: %s", get_network_errmsg()); + } +} /* sendreply */ + + +/* nmatch() - now in getif.c */ +/* setarp() - now in hwaddr.c */ + + +/* + * This call checks read access to a file. It returns 0 if the file given + * by "path" exists and is publically readable. A value of -1 is returned if + * access is not permitted or an error occurs. Successful calls also + * return the file size in bytes using the long pointer "filesize". + * + * The read permission bit for "other" users is checked. This bit must be + * set for tftpd(8) to allow clients to read the file. + */ + +PRIVATE int +chk_access(path, filesize) + char *path; + int32 *filesize; +{ + struct stat st; + + if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) { + *filesize = (int32) st.st_size; + return 0; + } else { + return -1; + } +} + + +/* + * Now in dumptab.c : + * dumptab() + * dump_host() + * list_ipaddresses() + */ + +#ifdef VEND_CMU + +/* + * Insert the CMU "vendor" data for the host pointed to by "hp" into the + * bootp packet pointed to by "bp". + */ + +PRIVATE void +dovend_cmu(bp, hp) + struct bootp *bp; + struct host *hp; +{ + struct cmu_vend *vendp; + struct in_addr_list *taddr; + + /* + * Initialize the entire vendor field to zeroes. + */ + bzero(bp->bp_vend, sizeof(bp->bp_vend)); + + /* + * Fill in vendor information. Subnet mask, default gateway, + * domain name server, ien name server, time server + */ + vendp = (struct cmu_vend *) bp->bp_vend; + strcpy(vendp->v_magic, (char *)vm_cmu); + if (hp->flags.subnet_mask) { + (vendp->v_smask).s_addr = hp->subnet_mask.s_addr; + (vendp->v_flags) |= VF_SMASK; + if (hp->flags.gateway) { + (vendp->v_dgate).s_addr = hp->gateway->addr->s_addr; + } + } + if (hp->flags.domain_server) { + taddr = hp->domain_server; + if (taddr->addrcount > 0) { + (vendp->v_dns1).s_addr = (taddr->addr)[0].s_addr; + if (taddr->addrcount > 1) { + (vendp->v_dns2).s_addr = (taddr->addr)[1].s_addr; + } + } + } + if (hp->flags.name_server) { + taddr = hp->name_server; + if (taddr->addrcount > 0) { + (vendp->v_ins1).s_addr = (taddr->addr)[0].s_addr; + if (taddr->addrcount > 1) { + (vendp->v_ins2).s_addr = (taddr->addr)[1].s_addr; + } + } + } + if (hp->flags.time_server) { + taddr = hp->time_server; + if (taddr->addrcount > 0) { + (vendp->v_ts1).s_addr = (taddr->addr)[0].s_addr; + if (taddr->addrcount > 1) { + (vendp->v_ts2).s_addr = (taddr->addr)[1].s_addr; + } + } + } + /* Log message now done by caller. */ +} /* dovend_cmu */ + +#endif /* VEND_CMU */ + + + +/* + * Insert the RFC1048 vendor data for the host pointed to by "hp" into the + * bootp packet pointed to by "bp". + */ +#define NEED(LEN, MSG) do \ + if (bytesleft < (LEN)) { \ + report(LOG_NOTICE, noroom, \ + hp->hostname->string, MSG); \ + return; \ + } while (0) +PRIVATE void +dovend_rfc1048(bp, hp, bootsize) + struct bootp *bp; + struct host *hp; + int32 bootsize; +{ + int bytesleft, len; + byte *vp; + char *tmpstr; + + static char noroom[] = "%s: No room for \"%s\" option"; + + vp = bp->bp_vend; + + if (hp->flags.msg_size) { + pktlen = hp->msg_size; + } else { + /* + * If the request was longer than the official length, build + * a response of that same length where the additional length + * is assumed to be part of the bp_vend (options) area. + */ + if (pktlen > sizeof(*bp)) { + if (debug > 1) + report(LOG_INFO, "request message length=%d", pktlen); + } + /* + * Check whether the request contains the option: + * Maximum DHCP Message Size (RFC1533 sec. 9.8) + * and if so, override the response length with its value. + * This request must lie within the first BP_VEND_LEN + * bytes of the option space. + */ + { + byte *p, *ep; + byte tag, len; + short msgsz = 0; + + p = vp + 4; + ep = p + BP_VEND_LEN - 4; + while (p < ep) { + tag = *p++; + /* Check for tags with no data first. */ + if (tag == TAG_PAD) + continue; + if (tag == TAG_END) + break; + /* Now scan the length byte. */ + len = *p++; + switch (tag) { + case TAG_MAX_MSGSZ: + if (len == 2) { + bcopy(p, (char*)&msgsz, 2); + msgsz = ntohs(msgsz); + } + break; + case TAG_SUBNET_MASK: + /* XXX - Should preserve this if given... */ + break; + } /* swtich */ + p += len; + } + + if (msgsz > sizeof(*bp)) { + if (debug > 1) + report(LOG_INFO, "request has DHCP msglen=%d", msgsz); + pktlen = msgsz; + } + } + } + + if (pktlen < sizeof(*bp)) { + report(LOG_ERR, "invalid response length=%d", pktlen); + pktlen = sizeof(*bp); + } + bytesleft = ((byte*)bp + pktlen) - vp; + if (pktlen > sizeof(*bp)) { + if (debug > 1) + report(LOG_INFO, "extended reply, length=%d, options=%d", + pktlen, bytesleft); + } + + /* Copy in the magic cookie */ + bcopy(vm_rfc1048, vp, 4); + vp += 4; + bytesleft -= 4; + + if (hp->flags.subnet_mask) { + /* always enough room here. */ + *vp++ = TAG_SUBNET_MASK;/* -1 byte */ + *vp++ = 4; /* -1 byte */ + insert_u_long(hp->subnet_mask.s_addr, &vp); /* -4 bytes */ + bytesleft -= 6; /* Fix real count */ + if (hp->flags.gateway) { + (void) insert_ip(TAG_GATEWAY, + hp->gateway, + &vp, &bytesleft); + } + } + if (hp->flags.bootsize) { + /* always enough room here */ + bootsize = (hp->flags.bootsize_auto) ? + ((bootsize + 511) / 512) : (hp->bootsize); /* Round up */ + *vp++ = TAG_BOOT_SIZE; + *vp++ = 2; + *vp++ = (byte) ((bootsize >> 8) & 0xFF); + *vp++ = (byte) (bootsize & 0xFF); + bytesleft -= 4; /* Tag, length, and 16 bit blocksize */ + } + /* + * This one is special: Remaining options go in the ext file. + * Only the subnet_mask, bootsize, and gateway should precede. + */ + if (hp->flags.exten_file) { + /* + * Check for room for exten_file. Add 3 to account for + * TAG_EXTEN_FILE, length, and TAG_END. + */ + len = strlen(hp->exten_file->string); + NEED((len + 3), "ef"); + *vp++ = TAG_EXTEN_FILE; + *vp++ = (byte) (len & 0xFF); + bcopy(hp->exten_file->string, vp, len); + vp += len; + *vp++ = TAG_END; + bytesleft -= len + 3; + return; /* no more options here. */ + } + /* + * The remaining options are inserted by the following + * function (which is shared with bootpef.c). + * Keep back one byte for the TAG_END. + */ + len = dovend_rfc1497(hp, vp, bytesleft - 1); + vp += len; + bytesleft -= len; + + /* There should be at least one byte left. */ + NEED(1, "(end)"); + *vp++ = TAG_END; + bytesleft--; + + /* Log message done by caller. */ + if (bytesleft > 0) { + /* + * Zero out any remaining part of the vendor area. + */ + bzero(vp, bytesleft); + } +} /* dovend_rfc1048 */ +#undef NEED + + +/* + * Now in readfile.c: + * hwlookcmp() + * iplookcmp() + */ + +/* haddrtoa() - now in hwaddr.c */ +/* + * Now in dovend.c: + * insert_ip() + * insert_generic() + * insert_u_long() + */ + +/* get_errmsg() - now in report.c */ + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/usr.sbin/bootpd/bootpd.h b/usr.sbin/bootpd/bootpd.h new file mode 100644 index 00000000000..11c4a8b41d6 --- /dev/null +++ b/usr.sbin/bootpd/bootpd.h @@ -0,0 +1,211 @@ +/************************************************************************ + Copyright 1988, 1991 by Carnegie Mellon University + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, provided +that the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of Carnegie Mellon University not be used +in advertising or publicity pertaining to distribution of the software +without specific, written prior permission. + +CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. +************************************************************************/ + + +/* + * bootpd.h -- common header file for all the modules of the bootpd program. + */ + +#include "bptypes.h" +#include "hash.h" +#include "hwaddr.h" + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef PRIVATE +#define PRIVATE static +#endif + +#ifndef SIGUSR1 +#define SIGUSR1 30 /* From 4.3 <signal.h> */ +#endif + +#define MAXSTRINGLEN 80 /* Max string length */ + +/* Local definitions: */ +#define MAX_MSG_SIZE (3*512) /* Maximum packet size */ + + +/* + * Return pointer to static string which gives full network error message. + */ +#define get_network_errmsg get_errmsg + + +/* + * Data structure used to hold an arbitrary-lengthed list of IP addresses. + * The list may be shared among multiple hosts by setting the linkcount + * appropriately. + */ + +struct in_addr_list { + unsigned int linkcount, addrcount; + struct in_addr addr[1]; /* Dynamically extended */ +}; + + +/* + * Data structures used to hold shared strings and shared binary data. + * The linkcount must be set appropriately. + */ + +struct shared_string { + unsigned int linkcount; + char string[1]; /* Dynamically extended */ +}; + +struct shared_bindata { + unsigned int linkcount, length; + byte data[1]; /* Dynamically extended */ +}; + + +/* + * Flag structure which indicates which symbols have been defined for a + * given host. This information is used to determine which data should or + * should not be reported in the bootp packet vendor info field. + */ + +struct flag { + unsigned bootfile :1, + bootserver :1, + bootsize :1, + bootsize_auto :1, + cookie_server :1, + domain_server :1, + gateway :1, + generic :1, + haddr :1, + homedir :1, + htype :1, + impress_server :1, + iaddr :1, + log_server :1, + lpr_server :1, + name_server :1, + name_switch :1, + rlp_server :1, + send_name :1, + subnet_mask :1, + tftpdir :1, + time_offset :1, + time_server :1, + dump_file :1, + domain_name :1, + swap_server :1, + root_path :1, + exten_file :1, + reply_addr :1, + nis_domain :1, + nis_server :1, + ntp_server :1, + exec_file :1, + msg_size :1, + min_wait :1, + /* XXX - Add new tags here */ + vm_cookie :1; +}; + + + +/* + * The flags structure contains TRUE flags for all the fields which + * are considered valid, regardless of whether they were explicitly + * specified or indirectly inferred from another entry. + * + * The gateway and the various server fields all point to a shared list of + * IP addresses. + * + * The hostname, home directory, and bootfile are all shared strings. + * + * The generic data field is a shared binary data structure. It is used to + * hold future RFC1048 vendor data until bootpd is updated to understand it. + * + * The vm_cookie field specifies the four-octet vendor magic cookie to use + * if it is desired to always send the same response to a given host. + * + * Hopefully, the rest is self-explanatory. + */ + +struct host { + unsigned linkcount; /* hash list inserts */ + struct flag flags; /* ALL valid fields */ + struct in_addr_list *cookie_server, + *domain_server, + *gateway, + *impress_server, + *log_server, + *lpr_server, + *name_server, + *rlp_server, + *time_server, + *nis_server, + *ntp_server; + struct shared_string *bootfile, + *hostname, + *domain_name, + *homedir, + *tftpdir, + *dump_file, + *exten_file, + *root_path, + *nis_domain, + *exec_file; + struct shared_bindata *generic; + byte vm_cookie[4], + htype, /* RFC826 says this should be 16-bits but + RFC951 only allocates 1 byte. . . */ + haddr[MAXHADDRLEN]; + int32 time_offset; + u_int32 bootsize, + msg_size, + min_wait; + struct in_addr bootserver, + iaddr, + swap_server, + reply_addr, + subnet_mask; + /* XXX - Add new tags here (or above as appropriate) */ +}; + + + +/* + * Variables shared among modules. + */ + +extern int debug; +extern char *bootptab; +extern char *progname; + +extern u_char vm_cmu[4]; +extern u_char vm_rfc1048[4]; + +extern hash_tbl *hwhashtable; +extern hash_tbl *iphashtable; +extern hash_tbl *nmhashtable; + diff --git a/usr.sbin/bootpd/bootpef.8 b/usr.sbin/bootpd/bootpef.8 new file mode 100644 index 00000000000..0f0b1fc7b43 --- /dev/null +++ b/usr.sbin/bootpd/bootpef.8 @@ -0,0 +1,52 @@ +.\" bootpef.8 +.TH BOOTPEF 8 "4 Dec 1993" "MAINTENANCE COMMANDS" +.SH NAME +bootpef \- BOOTP Extension File compiler +.SH SYNOPSIS +.LP +.B bootpef +.RI [ "-c chdir" ] +.RI [ "-d debug-level" ] +.RI [ "-f config-file" ] +.RI [ client-name " [...]]" +.SH DESCRIPTION +.B bootpef +builds the +.I Extension Path +files described by RFC 1497 (tag 18). +If any +.I client-name +arguments are specified, then +.I bootpef +compiles the extension files for only those clients. +.SH OPTIONS +.TP +.BI \-c \ chdir\-path +Sets the current directory used by +.I bootpef +while creating extension files. This is useful when the +extension file names are specified as relative pathnames, and +.I bootpef +needs to use the same current directory as the TFTP server +(typically /tftpboot). +.TP +.BI \-d \ debug\-level +Sets the +.I debug\-level +variable that controls the amount of debugging messages generated. +For example, -d4 or -d 4 will set the debugging level to 4. +.TP +.BI \-f \ config\-file +Set the name of the config file that specifies the option +data to be sent to each client. +.SH "SEE ALSO" +bootpd(8), tftpd(8) +.SH REFERENCES +.TP +RFC951 +BOOTSTRAP PROTOCOL (BOOTP) +.TP +RFC1497 +BOOTP Vendor Information Extensions + + diff --git a/usr.sbin/bootpd/bootpef.c b/usr.sbin/bootpd/bootpef.c new file mode 100644 index 00000000000..5181c09eb07 --- /dev/null +++ b/usr.sbin/bootpd/bootpef.c @@ -0,0 +1,347 @@ +/************************************************************************ + Copyright 1988, 1991 by Carnegie Mellon University + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, provided +that the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of Carnegie Mellon University not be used +in advertising or publicity pertaining to distribution of the software +without specific, written prior permission. + +CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. +************************************************************************/ + +#ifndef lint +static char rcsid[] = "$Id: bootpef.c,v 1.1 1995/10/18 08:47:26 deraadt Exp $"; +#endif + + +/* + * bootpef - BOOTP Extension File generator + * Makes an "Extension File" for each host entry that + * defines an and Extension File. (See RFC1497, tag 18.) + * + * HISTORY + * See ./Changes + * + * BUGS + * See ./ToDo + */ + + + +#ifdef __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +#include <sys/types.h> +#include <sys/time.h> + +#include <netinet/in.h> +#include <arpa/inet.h> /* inet_ntoa */ + +#ifndef NO_UNISTD +#include <unistd.h> +#endif +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <syslog.h> + +#ifndef USE_BFUNCS +#include <memory.h> +/* Yes, memcpy is OK here (no overlapped copies). */ +#define bcopy(a,b,c) memcpy(b,a,c) +#define bzero(p,l) memset(p,0,l) +#define bcmp(a,b,c) memcmp(a,b,c) +#endif + +#include "bootp.h" +#include "hash.h" +#include "hwaddr.h" +#include "bootpd.h" +#include "dovend.h" +#include "readfile.h" +#include "report.h" +#include "tzone.h" +#include "patchlevel.h" + +#define BUFFERSIZE 0x4000 + +#ifndef CONFIG_FILE +#define CONFIG_FILE "/etc/bootptab" +#endif + + + +/* + * Externals, forward declarations, and global variables + */ + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +static void dovend_rfc1048 P((struct bootp *, struct host *, int32)); +static void mktagfile P((struct host *)); +static void usage P((void)); + +#undef P + + +/* + * General + */ + +char *progname; +char *chdir_path; +int debug = 0; /* Debugging flag (level) */ +byte *buffer; + +/* + * Globals below are associated with the bootp database file (bootptab). + */ + +char *bootptab = CONFIG_FILE; + + +/* + * Print "usage" message and exit + */ +static void +usage() +{ + fprintf(stderr, + "usage: $s [ -c chdir ] [-d level] [-f configfile] [host...]\n"); + fprintf(stderr, "\t -c n\tset current directory\n"); + fprintf(stderr, "\t -d n\tset debug level\n"); + fprintf(stderr, "\t -f n\tconfig file name\n"); + exit(1); +} + + +/* + * Initialization such as command-line processing is done and then the + * main server loop is started. + */ +void +main(argc, argv) + int argc; + char **argv; +{ + struct host *hp; + char *stmp; + int n; + + progname = strrchr(argv[0], '/'); + if (progname) progname++; + else progname = argv[0]; + + /* Get work space for making tag 18 files. */ + buffer = (byte *) malloc(BUFFERSIZE); + if (!buffer) { + report(LOG_ERR, "malloc failed"); + exit(1); + } + /* + * Set defaults that might be changed by option switches. + */ + stmp = NULL; + + /* + * Read switches. + */ + for (argc--, argv++; argc > 0; argc--, argv++) { + if (argv[0][0] != '-') + break; + switch (argv[0][1]) { + + case 'c': /* chdir_path */ + if (argv[0][2]) { + stmp = &(argv[0][2]); + } else { + argc--; + argv++; + stmp = argv[0]; + } + if (!stmp || (stmp[0] != '/')) { + fprintf(stderr, + "bootpd: invalid chdir specification\n"); + break; + } + chdir_path = stmp; + break; + + case 'd': /* debug */ + if (argv[0][2]) { + stmp = &(argv[0][2]); + } else if (argv[1] && argv[1][0] == '-') { + /* + * Backwards-compatible behavior: + * no parameter, so just increment the debug flag. + */ + debug++; + break; + } else { + argc--; + argv++; + stmp = argv[0]; + } + if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { + fprintf(stderr, + "bootpd: invalid debug level\n"); + break; + } + debug = n; + break; + + case 'f': /* config file */ + if (argv[0][2]) { + stmp = &(argv[0][2]); + } else { + argc--; + argv++; + stmp = argv[0]; + } + bootptab = stmp; + break; + + default: + fprintf(stderr, "bootpd: unknown switch: -%c\n", + argv[0][1]); + usage(); + break; + } + } + + /* Get the timezone. */ + tzone_init(); + + /* Allocate hash tables. */ + rdtab_init(); + + /* + * Read the bootptab file. + */ + readtab(1); /* force read */ + + /* Set the cwd (i.e. to /tftpboot) */ + if (chdir_path) { + if (chdir(chdir_path) < 0) + report(LOG_ERR, "%s: chdir failed", chdir_path); + } + /* If there are host names on the command line, do only those. */ + if (argc > 0) { + unsigned int tlen, hashcode; + + while (argc) { + tlen = strlen(argv[0]); + hashcode = hash_HashFunction((u_char *)argv[0], tlen); + hp = (struct host *) hash_Lookup(nmhashtable, + hashcode, + nmcmp, argv[0]); + if (!hp) { + printf("%s: no matching entry\n", argv[0]); + exit(1); + } + if (!hp->flags.exten_file) { + printf("%s: no extension file\n", argv[0]); + exit(1); + } + mktagfile(hp); + argv++; + argc--; + } + exit(0); + } + /* No host names specified. Do them all. */ + hp = (struct host *) hash_FirstEntry(nmhashtable); + while (hp != NULL) { + mktagfile(hp); + hp = (struct host *) hash_NextEntry(nmhashtable); + } +} + + + +/* + * Make a "TAG 18" file for this host. + * (Insert the RFC1497 options.) + */ + +static void +mktagfile(hp) + struct host *hp; +{ + FILE *fp; + int bytesleft, len; + byte *vp; + char *tmpstr; + + if (!hp->flags.exten_file) + return; + + vp = buffer; + bytesleft = BUFFERSIZE; + bcopy(vm_rfc1048, vp, 4); /* Copy in the magic cookie */ + vp += 4; + bytesleft -= 4; + + /* + * The "extension file" options are appended by the following + * function (which is shared with bootpd.c). + */ + len = dovend_rfc1497(hp, vp, bytesleft); + vp += len; + bytesleft -= len; + + if (bytesleft < 1) { + report(LOG_ERR, "%s: too much option data", + hp->exten_file->string); + return; + } + *vp++ = TAG_END; + bytesleft--; + + /* Write the buffer to the extension file. */ + printf("Updating \"%s\"\n", hp->exten_file->string); + if ((fp = fopen(hp->exten_file->string, "w")) == NULL) { + report(LOG_ERR, "error opening \"%s\": %s", + hp->exten_file->string, get_errmsg()); + return; + } + len = vp - buffer; + if (len != fwrite(buffer, 1, len, fp)) { + report(LOG_ERR, "write failed on \"%s\" : %s", + hp->exten_file->string, get_errmsg()); + } + fclose(fp); + +} /* dovend_rfc1048 */ + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/usr.sbin/bootpd/bootpgw.c b/usr.sbin/bootpd/bootpgw.c new file mode 100644 index 00000000000..085398dc0ce --- /dev/null +++ b/usr.sbin/bootpd/bootpgw.c @@ -0,0 +1,672 @@ +/* + * bootpgw.c - BOOTP GateWay + * This program forwards BOOTP Request packets to a BOOTP server. + */ + +/************************************************************************ + Copyright 1988, 1991 by Carnegie Mellon University + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, provided +that the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of Carnegie Mellon University not be used +in advertising or publicity pertaining to distribution of the software +without specific, written prior permission. + +CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. +************************************************************************/ + +#ifndef lint +static char rcsid[] = "$Id: bootpgw.c,v 1.1 1995/10/18 08:47:26 deraadt Exp $"; +#endif + +/* + * BOOTPGW is typically used to forward BOOTP client requests from + * one subnet to a BOOTP server on a different subnet. + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/file.h> +#include <sys/time.h> +#include <sys/stat.h> + +#include <net/if.h> +#include <netinet/in.h> +#include <arpa/inet.h> /* inet_ntoa */ + +#ifndef NO_UNISTD +#include <unistd.h> +#endif +#include <stdlib.h> +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <netdb.h> +#include <syslog.h> +#include <assert.h> + +#ifdef NO_SETSID +# include <fcntl.h> /* for O_RDONLY, etc */ +#endif + +#ifndef USE_BFUNCS +# include <memory.h> +/* Yes, memcpy is OK here (no overlapped copies). */ +# define bcopy(a,b,c) memcpy(b,a,c) +# define bzero(p,l) memset(p,0,l) +# define bcmp(a,b,c) memcmp(a,b,c) +#endif + +#include "bootp.h" +#include "getif.h" +#include "hwaddr.h" +#include "report.h" +#include "patchlevel.h" + +/* Local definitions: */ +#define MAX_MSG_SIZE (3*512) /* Maximum packet size */ +#define TRUE 1 +#define FALSE 0 +#define get_network_errmsg get_errmsg + + + +/* + * Externals, forward declarations, and global variables + */ + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +static void usage P((void)); +static void handle_reply P((void)); +static void handle_request P((void)); + +#undef P + +/* + * IP port numbers for client and server obtained from /etc/services + */ + +u_short bootps_port, bootpc_port; + + +/* + * Internet socket and interface config structures + */ + +struct sockaddr_in bind_addr; /* Listening */ +struct sockaddr_in clnt_addr; /* client address */ +struct sockaddr_in serv_addr; /* server address */ + + +/* + * option defaults + */ +int debug = 0; /* Debugging flag (level) */ +struct timeval actualtimeout = +{ /* fifteen minutes */ + 15 * 60L, /* tv_sec */ + 0 /* tv_usec */ +}; +u_int maxhops = 4; /* Number of hops allowed for requests. */ +u_int minwait = 3; /* Number of seconds client must wait before + its bootrequest packets are forwarded. */ + +/* + * General + */ + +int s; /* Socket file descriptor */ +char *pktbuf; /* Receive packet buffer */ +int pktlen; +char *progname; +char *servername; + +char myhostname[64]; +struct in_addr my_ip_addr; + + + + +/* + * Initialization such as command-line processing is done and then the + * main server loop is started. + */ + +void +main(argc, argv) + int argc; + char **argv; +{ + struct timeval *timeout; + struct bootp *bp; + struct servent *servp; + struct hostent *hep; + char *stmp; + int n, ba_len, ra_len; + int nfound, readfds; + int standalone; + + progname = strrchr(argv[0], '/'); + if (progname) progname++; + else progname = argv[0]; + + /* + * Initialize logging. + */ + report_init(0); /* uses progname */ + + /* + * Log startup + */ + report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL); + + /* Debugging for compilers with struct padding. */ + assert(sizeof(struct bootp) == BP_MINPKTSZ); + + /* Get space for receiving packets and composing replies. */ + pktbuf = malloc(MAX_MSG_SIZE); + if (!pktbuf) { + report(LOG_ERR, "malloc failed"); + exit(1); + } + bp = (struct bootp *) pktbuf; + + /* + * Check to see if a socket was passed to us from inetd. + * + * Use getsockname() to determine if descriptor 0 is indeed a socket + * (and thus we are probably a child of inetd) or if it is instead + * something else and we are running standalone. + */ + s = 0; + ba_len = sizeof(bind_addr); + bzero((char *) &bind_addr, ba_len); + errno = 0; + standalone = TRUE; + if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) { + /* + * Descriptor 0 is a socket. Assume we are a child of inetd. + */ + if (bind_addr.sin_family == AF_INET) { + standalone = FALSE; + bootps_port = ntohs(bind_addr.sin_port); + } else { + /* Some other type of socket? */ + report(LOG_INFO, "getsockname: not an INET socket"); + } + } + /* + * Set defaults that might be changed by option switches. + */ + stmp = NULL; + timeout = &actualtimeout; + gethostname(myhostname, sizeof(myhostname)); + hep = gethostbyname(myhostname); + if (!hep) { + printf("Can not get my IP address\n"); + exit(1); + } + bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr)); + + /* + * Read switches. + */ + for (argc--, argv++; argc > 0; argc--, argv++) { + if (argv[0][0] != '-') + break; + switch (argv[0][1]) { + + case 'd': /* debug level */ + if (argv[0][2]) { + stmp = &(argv[0][2]); + } else if (argv[1] && argv[1][0] == '-') { + /* + * Backwards-compatible behavior: + * no parameter, so just increment the debug flag. + */ + debug++; + break; + } else { + argc--; + argv++; + stmp = argv[0]; + } + if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { + fprintf(stderr, + "%s: invalid debug level\n", progname); + break; + } + debug = n; + break; + + case 'h': /* hop count limit */ + if (argv[0][2]) { + stmp = &(argv[0][2]); + } else { + argc--; + argv++; + stmp = argv[0]; + } + if (!stmp || (sscanf(stmp, "%d", &n) != 1) || + (n < 0) || (n > 16)) + { + fprintf(stderr, + "bootpgw: invalid hop count limit\n"); + break; + } + maxhops = (u_int)n; + break; + + case 'i': /* inetd mode */ + standalone = FALSE; + break; + + case 's': /* standalone mode */ + standalone = TRUE; + break; + + case 't': /* timeout */ + if (argv[0][2]) { + stmp = &(argv[0][2]); + } else { + argc--; + argv++; + stmp = argv[0]; + } + if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { + fprintf(stderr, + "%s: invalid timeout specification\n", progname); + break; + } + actualtimeout.tv_sec = (int32) (60 * n); + /* + * If the actual timeout is zero, pass a NULL pointer + * to select so it blocks indefinitely, otherwise, + * point to the actual timeout value. + */ + timeout = (n > 0) ? &actualtimeout : NULL; + break; + + case 'w': /* wait time */ + if (argv[0][2]) { + stmp = &(argv[0][2]); + } else { + argc--; + argv++; + stmp = argv[0]; + } + if (!stmp || (sscanf(stmp, "%d", &n) != 1) || + (n < 0) || (n > 60)) + { + fprintf(stderr, + "bootpgw: invalid wait time\n"); + break; + } + minwait = (u_int)n; + break; + + default: + fprintf(stderr, "%s: unknown switch: -%c\n", + progname, argv[0][1]); + usage(); + break; + + } /* switch */ + } /* for args */ + + /* Make sure server name argument is suplied. */ + servername = argv[0]; + if (!servername) { + fprintf(stderr, "bootpgw: missing server name\n"); + usage(); + } + /* + * Get address of real bootp server. + */ + if (inet_aton(servername, &serv_addr.sin_addr) == 0) { + hep = gethostbyname(servername); + if (!hep) { + fprintf(stderr, "bootpgw: can't get addr for %s\n", servername); + exit(1); + } + memcpy(&serv_addr.sin_addr, hep->h_addr, + sizeof(serv_addr.sin_addr)); + } + + if (standalone) { + /* + * Go into background and disassociate from controlling terminal. + * XXX - This is not the POSIX way (Should use setsid). -gwr + */ + if (debug < 3) { + if (fork()) + exit(0); +#ifdef NO_SETSID + setpgrp(0,0); +#ifdef TIOCNOTTY + n = open("/dev/tty", O_RDWR); + if (n >= 0) { + ioctl(n, TIOCNOTTY, (char *) 0); + (void) close(n); + } +#endif /* TIOCNOTTY */ +#else /* SETSID */ + if (setsid() < 0) + perror("setsid"); +#endif /* SETSID */ + } /* if debug < 3 */ + /* + * Nuke any timeout value + */ + timeout = NULL; + + /* + * Here, bootpd would do: + * chdir + * tzone_init + * rdtab_init + * readtab + */ + + /* + * Create a socket. + */ + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + report(LOG_ERR, "socket: %s", get_network_errmsg()); + exit(1); + } + /* + * Get server's listening port number + */ + servp = getservbyname("bootps", "udp"); + if (servp) { + bootps_port = ntohs((u_short) servp->s_port); + } else { + bootps_port = (u_short) IPPORT_BOOTPS; + report(LOG_ERR, + "udp/bootps: unknown service -- assuming port %d", + bootps_port); + } + + /* + * Bind socket to BOOTPS port. + */ + bind_addr.sin_family = AF_INET; + bind_addr.sin_port = htons(bootps_port); + bind_addr.sin_addr.s_addr = INADDR_ANY; + if (bind(s, (struct sockaddr *) &bind_addr, + sizeof(bind_addr)) < 0) + { + report(LOG_ERR, "bind: %s", get_network_errmsg()); + exit(1); + } + } /* if standalone */ + /* + * Get destination port number so we can reply to client + */ + servp = getservbyname("bootpc", "udp"); + if (servp) { + bootpc_port = ntohs(servp->s_port); + } else { + report(LOG_ERR, + "udp/bootpc: unknown service -- assuming port %d", + IPPORT_BOOTPC); + bootpc_port = (u_short) IPPORT_BOOTPC; + } + + /* no signal catchers */ + + /* + * Process incoming requests. + */ + for (;;) { + readfds = 1 << s; + nfound = select(s + 1, (fd_set *)&readfds, NULL, NULL, timeout); + if (nfound < 0) { + if (errno != EINTR) { + report(LOG_ERR, "select: %s", get_errmsg()); + } + continue; + } + if (!(readfds & (1 << s))) { + report(LOG_INFO, "exiting after %ld minutes of inactivity", + actualtimeout.tv_sec / 60); + exit(0); + } + ra_len = sizeof(clnt_addr); + n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0, + (struct sockaddr *) &clnt_addr, &ra_len); + if (n <= 0) { + continue; + } + if (debug > 3) { + report(LOG_INFO, "recvd pkt from IP addr %s", + inet_ntoa(clnt_addr.sin_addr)); + } + if (n < sizeof(struct bootp)) { + if (debug) { + report(LOG_INFO, "received short packet"); + } + continue; + } + pktlen = n; + + switch (bp->bp_op) { + case BOOTREQUEST: + handle_request(); + break; + case BOOTREPLY: + handle_reply(); + break; + } + } +} + + + + +/* + * Print "usage" message and exit + */ + +static void +usage() +{ + fprintf(stderr, + "usage: bootpgw [-d level] [-i] [-s] [-t timeout] server\n"); + fprintf(stderr, "\t -d n\tset debug level\n"); + fprintf(stderr, "\t -h n\tset max hop count\n"); + fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n"); + fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n"); + fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n"); + fprintf(stderr, "\t -w n\tset min wait time (secs)\n"); + exit(1); +} + + + +/* + * Process BOOTREQUEST packet. + * + * Note, this just forwards the request to a real server. + */ +static void +handle_request() +{ + struct bootp *bp = (struct bootp *) pktbuf; + struct ifreq *ifr; + u_short secs, hops; + + /* XXX - SLIP init: Set bp_ciaddr = clnt_addr here? */ + + if (debug) { + report(LOG_INFO, "request from %s", + inet_ntoa(clnt_addr.sin_addr)); + } + /* Has the client been waiting long enough? */ + secs = ntohs(bp->bp_secs); + if (secs < minwait) + return; + + /* Has this packet hopped too many times? */ + hops = ntohs(bp->bp_hops); + if (++hops > maxhops) { + report(LOG_NOTICE, "request from %s reached hop limit", + inet_ntoa(clnt_addr.sin_addr)); + return; + } + bp->bp_hops = htons(hops); + + /* + * Here one might discard a request from the same subnet as the + * real server, but we can assume that the real server will send + * a reply to the client before it waits for minwait seconds. + */ + + /* If gateway address is not set, put in local interface addr. */ + if (bp->bp_giaddr.s_addr == 0) { +#if 0 /* BUG */ + struct sockaddr_in *sip; + /* + * XXX - This picks the wrong interface when the receive addr + * is the broadcast address. There is no portable way to + * find out which interface a broadcast was received on. -gwr + * (Thanks to <walker@zk3.dec.com> for finding this bug!) + */ + ifr = getif(s, &clnt_addr.sin_addr); + if (!ifr) { + report(LOG_NOTICE, "no interface for request from %s", + inet_ntoa(clnt_addr.sin_addr)); + return; + } + sip = (struct sockaddr_in *) &(ifr->ifr_addr); + bp->bp_giaddr = sip->sin_addr; +#else /* BUG */ + /* + * XXX - Just set "giaddr" to our "official" IP address. + * RFC 1532 says giaddr MUST be set to the address of the + * interface on which the request was received. Setting + * it to our "default" IP address is not strictly correct, + * but is good enough to allow the real BOOTP server to + * get the reply back here. Then, before we forward the + * reply to the client, the giaddr field is corrected. + * (In case the client uses giaddr, which it should not.) + * See handle_reply() + */ + bp->bp_giaddr = my_ip_addr; +#endif /* BUG */ + + /* + * XXX - DHCP says to insert a subnet mask option into the + * options area of the request (if vendor magic == std). + */ + } + /* Set up socket address for send. */ + serv_addr.sin_family = AF_INET; + serv_addr.sin_port = htons(bootps_port); + + /* Send reply with same size packet as request used. */ + if (sendto(s, pktbuf, pktlen, 0, + (struct sockaddr *) &serv_addr, + sizeof(serv_addr)) < 0) + { + report(LOG_ERR, "sendto: %s", get_network_errmsg()); + } +} + + + +/* + * Process BOOTREPLY packet. + */ +static void +handle_reply() +{ + struct bootp *bp = (struct bootp *) pktbuf; + struct ifreq *ifr; + struct sockaddr_in *sip; + u_char canon_haddr[MAXHADDRLEN]; + unsigned char *ha; + int len; + + if (debug) { + report(LOG_INFO, " reply for %s", + inet_ntoa(bp->bp_yiaddr)); + } + /* Make sure client is directly accessible. */ + ifr = getif(s, &(bp->bp_yiaddr)); + if (!ifr) { + report(LOG_NOTICE, "no interface for reply to %s", + inet_ntoa(bp->bp_yiaddr)); + return; + } +#if 1 /* Experimental (see BUG above) */ +/* #ifdef CATER_TO_OLD_CLIENTS ? */ + /* + * The giaddr field has been set to our "default" IP address + * which might not be on the same interface as the client. + * In case the client looks at giaddr, (which it should not) + * giaddr is now set to the address of the correct interface. + */ + sip = (struct sockaddr_in *) &(ifr->ifr_addr); + bp->bp_giaddr = sip->sin_addr; +#endif + + /* Set up socket address for send to client. */ + clnt_addr.sin_family = AF_INET; + clnt_addr.sin_addr = bp->bp_yiaddr; + clnt_addr.sin_port = htons(bootpc_port); + + /* Create an ARP cache entry for the client. */ + ha = bp->bp_chaddr; + len = bp->bp_hlen; + if (len > MAXHADDRLEN) + len = MAXHADDRLEN; + if (bp->bp_htype == HTYPE_IEEE802) { + haddr_conv802(ha, canon_haddr, len); + ha = canon_haddr; + } + if (debug > 1) + report(LOG_INFO, "setarp %s - %s", + inet_ntoa(bp->bp_yiaddr), haddrtoa(ha, len)); + setarp(s, &bp->bp_yiaddr, ha, len); + + /* Send reply with same size packet as request used. */ + if (sendto(s, pktbuf, pktlen, 0, + (struct sockaddr *) &clnt_addr, + sizeof(clnt_addr)) < 0) + { + report(LOG_ERR, "sendto: %s", get_network_errmsg()); + } +} + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/usr.sbin/bootpd/bootptab.5 b/usr.sbin/bootpd/bootptab.5 new file mode 100644 index 00000000000..a76d449e918 --- /dev/null +++ b/usr.sbin/bootpd/bootptab.5 @@ -0,0 +1,395 @@ +.\" Copyright (c) 1988, 1989, 1991 Carnegie Mellon University +.\" +.\" $Header: /cvs/OpenBSD/src/usr.sbin/bootpd/Attic/bootptab.5,v 1.1 1995/10/18 08:47:26 deraadt Exp $ +.\" +.TH BOOTPTAB 5 "October 31, 1991" "Carnegie Mellon University" +.UC 6 + +.SH NAME +bootptab \- Internet Bootstrap Protocol server database +.SH DESCRIPTION +The +.I bootptab +file is the configuration database file for +.IR bootpd , +the Internet Bootstrap Protocol server. +It's format is similar to that of +.IR termcap (5) +in which two-character case-sensitive tag symbols are used to +represent host parameters. These parameter declarations are separated by +colons (:), with a general format of: +.PP +.I " hostname:tg=value. . . :tg=value. . . :tg=value. . . ." +.PP +where +.I hostname +is the actual name of a bootp client (or a "dummy entry"), and +.I tg +is a two-character tag symbol. Dummy entries have an invalid hostname +(one with a "." as the first character) and are used to provide +default values used by other entries via the +.B tc=.dummy-entry +mechanism. Most tags must be followed by an equals-sign +and a value as above. Some may also appear in a boolean form with no +value (i.e. +.RI : tg :). +The currently recognized tags are: +.PP +.br + bf Bootfile +.br + bs Bootfile size in 512-octet blocks +.br + cs Cookie server address list +.br + df Merit dump file +.br + dn Domain name +.br + ds Domain name server address list +.br + ef Extension file +.br + gw Gateway address list +.br + ha Host hardware address +.br + hd Bootfile home directory +.br + hn Send client's hostname to client +.br + ht Host hardware type (see Assigned Numbers RFC) +.br + im Impress server address list +.br + ip Host IP address +.br + lg Log server address list +.br + lp LPR server address list +.br + ns IEN-116 name server address list +.br + nt NTP (time) Server (RFC 1129) +.br + ra Reply address override +.br + rl Resource location protocol server address list +.br + rp Root path to mount as root +.br + sa TFTP server address client should use +.br + sm Host subnet mask +.br + sw Swap server address +.br + tc Table continuation (points to similar "template" host entry) +.br + td TFTP root directory used by "secure" TFTP servers +.br + to Time offset in seconds from UTC +.br + ts Time server address list +.br + vm Vendor magic cookie selector +.br + yd YP (NIS) domain name +.br + ys YP (NIS) server address + +.PP +There is also a generic tag, +.RI T n , +where +.I n +is an RFC1084 vendor field tag number. Thus it is possible to immediately +take advantage of future extensions to RFC1084 without being forced to modify +.I bootpd +first. Generic data may be represented as either a stream of hexadecimal +numbers or as a quoted string of ASCII characters. The length of the generic +data is automatically determined and inserted into the proper field(s) of the +RFC1084-style bootp reply. +.PP +The following tags take a whitespace-separated list of IP addresses: +.BR cs , +.BR ds , +.BR gw , +.BR im , +.BR lg , +.BR lp , +.BR ns , +.BR nt , +.BR ra , +.BR rl , +and +.BR ts . +The +.BR ip , +.BR sa , +.BR sw , +.BR sm , +and +.B ys +tags each take a single IP address. +All IP addresses are specified in standard Internet "dot" notation +and may use decimal, octal, or hexadecimal numbers +(octal numbers begin with 0, hexadecimal numbers begin with '0x' or '0X'). +Any IP addresses may alternatively be specified as a hostname, causing +.I bootpd +to lookup the IP address for that host name using gethostbyname(3). +If the +.B ip +tag is not specified, +.I bootpd +will determine the IP address using the entry name as the host name. +(Dummy entries use an invalid host name to avoid automatic IP lookup.) +.PP +The +.B ht +tag specifies the hardware type code as either an unsigned decimal, octal, or +hexadecimal integer or one of the following symbolic names: +.B ethernet +or +.B ether +for 10Mb Ethernet, +.B ethernet3 +or +.B ether3 +for 3Mb experimental Ethernet, +.BR ieee802 , +.BR tr , +or +.B token-ring +for IEEE 802 networks, +.B pronet +for Proteon ProNET Token Ring, or +.BR chaos , +.BR arcnet , +or +.B ax.25 +for Chaos, ARCNET, and AX.25 Amateur Radio networks, respectively. +The +.B ha +tag takes a hardware address which may be specified as a host name +or in numeric form. Note that the numeric form +.I must +be specified in hexadecimal; optional periods and/or a leading '0x' may be +included for readability. The +.B ha +tag must be preceded by the +.B ht +tag (either explicitly or implicitly; see +.B tc +below). +If the hardware address is not specified and the type is specified +as either "ethernet" or "ieee802", then +.I bootpd +will try to determine the hardware address using ether_hton(3). +.PP +The hostname, home directory, and bootfile are ASCII strings which may be +optionally surrounded by double quotes ("). The client's request and the +values of the +.B hd +and +.B bf +symbols determine how the server fills in the bootfile field of the bootp +reply packet. +.PP +If the client provides a file name it is left as is. +Otherwise, if the +.B bf +option is specified its value is copied into the reply packet. +If the +.B hd +option is specified as well, its value is prepended to the +boot file copied into the reply packet. +The existence of the boot file is checked only if the +.BR bs =auto +option is used (to determine the boot file size). +A reply may be sent whether or not the boot file exists. +.PP +Some newer versions of +.I tftpd +provide a security feature to change their root directory using +the +.IR chroot (2) +system call. +The +.B td +tag may be used to inform +.I bootpd +of this special root directory used by +.IR tftpd . +(One may alternatively use the +.I bootpd +"-c chdir" option.) +The +.B hd +tag is actually relative to the root directory specified by the +.B td +tag. +For example, if the real absolute path to your BOOTP client bootfile is +/tftpboot/bootfiles/bootimage, and +.IR tftpd +uses /tftpboot as its "secure" directory, then specify the following in +.IR bootptab : +.PP +.br + :td=/tftpboot:hd=/bootfiles:bf=bootimage: +.PP +If your bootfiles are located directly in /tftpboot, use: +.PP +.br + :td=/tftpboot:hd=/:bf=bootimage: +.PP +The +.B sa +tag may be used to specify the IP address of the particular TFTP server +you wish the client to use. In the absence of this tag, +.I bootpd +will tell the client to perform TFTP to the same machine +.I bootpd +is running on. +.PP +The time offset +.B to +may be either a signed decimal integer specifying the client's +time zone offset in seconds from UTC, or the keyword +.B auto +which uses the server's time zone offset. Specifying the +.B to +symbol as a boolean has the same effect as specifying +.B auto +as its value. +.PP +The bootfile size +.B bs +may be either a decimal, octal, or hexadecimal integer specifying the size of +the bootfile in 512-octet blocks, or the keyword +.B auto +which causes the server to automatically calculate the bootfile size at each +request. As with the time offset, specifying the +.B bs +symbol as a boolean has the same effect as specifying +.B auto +as its value. +.PP +The vendor magic cookie selector (the +.B vm +tag) may take one of the following keywords: +.B auto +(indicating that vendor information is determined by the client's request), +.B rfc1048 +or +.B rfc1084 +(which always forces an RFC1084-style reply), or +.B cmu +(which always forces a CMU-style reply). +.PP +The +.B hn +tag is strictly a boolean tag; it does not take the usual equals-sign and +value. It's presence indicates that the hostname should be sent to RFC1084 +clients. +.I Bootpd +attempts to send the entire hostname as it is specified in the configuration +file; if this will not fit into the reply packet, the name is shortened to +just the host field (up to the first period, if present) and then tried. +In no case is an arbitrarily-truncated hostname sent (if nothing reasonable +will fit, nothing is sent). +.PP +Often, many host entries share common values for certain tags (such as name +servers, etc.). Rather than repeatedly specifying these tags, a full +specification can be listed for one host entry and shared by others via the +.B tc +(table continuation) mechanism. +Often, the template entry is a dummy host which doesn't actually exist and +never sends bootp requests. This feature is similar to the +.B tc +feature of +.IR termcap (5) +for similar terminals. Note that +.I bootpd +allows the +.B tc +tag symbol to appear anywhere in the host entry, unlike +.I termcap +which requires it to be the last tag. Information explicitly specified for a +host always overrides information implied by a +.B tc +tag symbol, regardless of its location within the entry. The +value of the +.B tc +tag may be the hostname or IP address of any host entry +previously listed in the configuration file. +.PP +Sometimes it is necessary to delete a specific tag after it has been inferred +via +.BR tc . +This can be done using the construction +.IB tag @ +which removes the effect of +.I tag +as in +.IR termcap (5). +For example, to completely undo an IEN-116 name server specification, use +":ns@:" at an appropriate place in the configuration entry. After removal +with +.BR @ , +a tag is eligible to be set again through the +.B tc +mechanism. +.PP +Blank lines and lines beginning with "#" are ignored in the configuration +file. Host entries are separated from one another by newlines; a single host +entry may be extended over multiple lines if the lines end with a backslash +(\\). It is also acceptable for lines to be longer than 80 characters. Tags +may appear in any order, with the following exceptions: the hostname must be +the very first field in an entry, and the hardware type must precede the +hardware address. +.PP +An example +.I /etc/bootptab +file follows: +.PP +.nf + # Sample bootptab file (domain=andrew.cmu.edu) + + .default:\\ + :hd=/usr/boot:bf=null:\\ + :ds=netserver, lancaster:\\ + :ns=pcs2, pcs1:\\ + :ts=pcs2, pcs1:\\ + :sm=255.255.255.0:\\ + :gw=gw.cs.cmu.edu:\\ + :hn:to=-18000: + + carnegie:ht=6:ha=7FF8100000AF:tc=.default: + baldwin:ht=1:ha=0800200159C3:tc=.default: + wylie:ht=1:ha=00DD00CADF00:tc=.default: + arnold:ht=1:ha=0800200102AD:tc=.default: + bairdford:ht=1:ha=08002B02A2F9:tc=.default: + bakerstown:ht=1:ha=08002B0287C8:tc=.default: + + # Special domain name server and option tags for next host + butlerjct:ha=08002001560D:ds=128.2.13.42:\\ + :T37=0x12345927AD3BCF:\\ + :T99="Special ASCII string":\\ + :tc=.default: + + gastonville:ht=6:ha=7FFF81000A47:tc=.default: + hahntown:ht=6:ha=7FFF81000434:tc=.default: + hickman:ht=6:ha=7FFF810001BA:tc=.default: + lowber:ht=1:ha=00DD00CAF000:tc=.default: + mtoliver:ht=1:ha=00DD00FE1600:tc=.default: + +.fi +.SH FILES +/etc/bootptab + +.SH "SEE ALSO" +.br +bootpd(8), tftpd(8), +.br +DARPA Internet Request For Comments RFC951, RFC1048, RFC1084, Assigned Numbers diff --git a/usr.sbin/bootpd/bootptab.cmu b/usr.sbin/bootpd/bootptab.cmu new file mode 100644 index 00000000000..66212d421a2 --- /dev/null +++ b/usr.sbin/bootpd/bootptab.cmu @@ -0,0 +1,124 @@ +# /etc/bootptab: database for bootp server (/etc/bootpd) +# (I've hacked on this but can't test it... -gwr) + +# Blank lines and lines beginning with '#' are ignored. +# +# Legend: (see bootptab.5) +# first field -- hostname (not indented) +# bf -- bootfile +# bs -- bootfile size in 512-octet blocks +# cs -- cookie servers +# df -- dump file name +# dn -- domain name +# ds -- domain name servers +# ef -- extension file +# gw -- gateways +# ha -- hardware address +# hd -- home directory for bootfiles +# hn -- host name set for client +# ht -- hardware type +# im -- impress servers +# ip -- host IP address +# lg -- log servers +# lp -- LPR servers +# ns -- IEN-116 name servers +# ra -- reply address +# rl -- resource location protocol servers +# rp -- root path +# sa -- boot server address +# sm -- subnet mask +# sw -- swap server +# tc -- template host (points to similar host entry) +# td -- TFTP directory +# to -- time offset (seconds) +# ts -- time servers +# vm -- vendor magic number +# Tn -- generic option tag n +# +# Be careful about including backslashes where they're needed. Weird (bad) +# things can happen when a backslash is omitted where one is intended. +# Also, note that generic option data must be either a string or a +# sequence of bytes where each byte is a two-digit hex value. + +# First, we define a global entry which specifies the stuff every host uses. +# (Host name lookups are relative to the domain: andrew.cmu.edu) +.default:\ + :hn:dn=cmu.edu:\ + :hd=/usr/boot:\ + :ds=netserver, lancaster:\ + :ns=pcs2, pcs1:\ + :ts=pcs2, pcs1:\ + :sm=255.255.0.0:\ + :gw=gw.cs.cmu.edu:\ + to=auto: + + +# Next, we can define different master entries for each subnet. . . +.subnet13 :sm=255.255.255.0:gw=128.2.13.1 :tc=.default: +.subnet19 :sm=255.255.255.0:gw=128.2.19.1 :tc=.default: +.subnet232 :sm=255.255.255.0:gw=128.2.232.1 :tc=.default: + +# +# We should be able to use as many levels of indirection as desired. Use +# your imagination. . . +# + + +# Individual entries (could also have different servers for some/all of these +# hosts, but we don't really use this feature at CMU): + +carnegie:tc=.subnet13:ht=ieee802:ha=7FF8100000AF: +baldwin:tc=.subnet19:ha=0800200159C3: +wylie:tc=.subnet232:ha=00DD00CADF00: +arnold:tc=.subnet19:ha=0800200102AD: +bairdford:tc=.subnet19:ha=08002B02A2F9: +bakerstown:tc=.subnet19:ha=08002B0287C8: +butlerjct:tc=.subnet232:ha=08002001560D: +gastonville:tc=.subnet232:ht=ieee802:ha=7FFF81000A47: +hahntown:tc=.subnet13:ht=ieee802:ha=7FFF81000434: +hickman:tc=.subnet19:ht=ieee802:ha=7FFF810001BA: +lowber:tc=.subnet13:ha=00DD00CAF000: +mtoliver:tc=.subnet19:ha=00DD00FE1600: +osborne:tc=.subnet232:ha=00DD00CAD600: +russelton:tc=.subnet232:ha=080020017FC3: +thornburg:tc=.subnet13:ha=080020012A33: + + +# Hmmm. . . Let's throw in some whitespace for readability. . . . + +andrew: tc=.subnet19:ha=00DD00C88900: +birdville: tc=.subnet19:ha=00DD00FE2D00: +coudersport: tc=.subnet13:ha=00DD00CB1E00: +bridgeville: tc=.subnet232:ha=080020011394: +franklin: tc=.subnet19:ha=08002B02A5D5: +hollidaysburg: tc=.subnet19:ht=ieee802:ha=7FFF810002C8: +honesdale: tc=.subnet19:ha=08002B02F83F: +huntingdon: tc=.subnet19:ha=08002B02E410: +indiana: tc=.subnet13:ha=08002B029BEC: +jimthorpe: tc=.subnet232:ha=08002B02FBBA: +kittanning: tc=.subnet232:ha=08002B0273FC: +lebanon: tc=.subnet232:ha=08002B037F67: +lewisburg: tc=.subnet19:ha=50005A1A0DE4: +middleburg: tc=.subnet232:ha=00DD00FE1200: +aspinwall: tc=.subnet13:ha=08002B03C163: +berlin: tc=.subnet13:ha=00DD000A4400: +norristown: tc=.subnet13:ha=08002001455B: +pottsville: tc=.subnet13:ha=00DD000A3700: +ridgway: tc=.subnet19:ha=08002B029425: +scranton: tc=.subnet232:ha=0800200113A1: +chalfont: tc=.subnet13:ha=08002001124B: +washington: tc=.subnet19:ha=00DD00656E00: +wellsboro: tc=.subnet13:ha=00DD00CB1C00: +bb1: tc=.subnet19:ha=00DD000A1F00: +adamstown: tc=.subnet13:ha=08002B02D0E6: +beta: tc=.subnet19:ha=02070100B197: +carbondale: tc=.subnet232:ha=08002B022A73: +clairton: tc=.subnet19:ha=080020010FD1: +egypt: tc=.subnet13:ha=00DD00847B00: +fairchance: tc=.subnet232:ha=00DD000AB100: +fairhope: tc=.subnet232:ha=00DD00CB0800: +galeton: tc=.subnet232:ha=08002001138C: +imperial: tc=.subnet232:ha=08002001130C: +kingston: tc=.subnet232:ha=080020011382: +knox: tc=.subnet232:ha=50005A1A0D2A: +lakecity: tc=.subnet13:ha=080020011380: diff --git a/usr.sbin/bootpd/bootptab.mcs b/usr.sbin/bootpd/bootptab.mcs new file mode 100644 index 00000000000..6fa04d1347e --- /dev/null +++ b/usr.sbin/bootpd/bootptab.mcs @@ -0,0 +1,92 @@ +# /etc/bootptab: database for bootp server (/etc/bootpd) +# Last update: gwr, Sun Dec 12 19:00:00 EDT 1993 +# Blank lines and lines beginning with '#' are ignored. +# +# Legend: (see bootptab.5) +# first field -- hostname (not indented) +# bf -- bootfile +# bs -- bootfile size in 512-octet blocks +# cs -- cookie servers +# df -- dump file name +# dn -- domain name +# ds -- domain name servers +# ef -- extension file +# gw -- gateways +# ha -- hardware address +# hd -- home directory for bootfiles +# hn -- host name set for client +# ht -- hardware type +# im -- impress servers +# ip -- host IP address +# lg -- log servers +# lp -- LPR servers +# ns -- IEN-116 name servers +# ra -- reply address +# rl -- resource location protocol servers +# rp -- root path +# sa -- boot server address +# sm -- subnet mask +# sw -- swap server +# tc -- template host (points to similar host entry) +# td -- TFTP directory +# to -- time offset (seconds) +# ts -- time servers +# vm -- vendor magic number +# Tn -- generic option tag n +# +# Be careful about including backslashes where they're needed. Weird (bad) +# things can happen when a backslash is omitted where one is intended. +# Also, note that generic option data must be either a string or a +# sequence of bytes where each byte is a two-digit hex value. + +# First, we define a global entry which specifies the stuff every host uses. + +# If you leave "td" empty, run bootpd with the "-c /tftpboot" switch +# so path names (boot files) will be interpreted relative to the same +# directory as tftpd will use when opening files. +.default:\ + :hn:dn="mc.com":\ + :td=/tftpboot:\ + :ds=merlin, jericho:\ + :to=auto: + +# Next, we can define different master entries for each subnet. . . + +.subnet16:\ + :tc=.default:\ + :sm=255.255.255.0:\ + :gw=merlin:\ + :sa=merlin: + +.subnet17:\ + :tc=.default:\ + :sm=255.255.255.0:\ + :gw=merlin-gw:\ + :sa=merlin-gw: + +# +# We should be able to use as many levels of indirection as desired. Use +# your imagination. . . +# + +# Individual entries (could also have different servers for some/all of these +# hosts, but we don't really use this feature at CMU): + +# Emulex terminal server +emulex: tc=.subnet16:ha=00.00.C9.00.42.E0:bf=P4KTL0E: + +# Lantronix eps1 +eps1: tc=.subnet16:ha=00.80.A3.04.1D.78: + +# Tadpole 885 board. +tp885: tc=.subnet17:ha=08.00.4C.00.2F.74:bf=tp885sys2.cfe: + +# MVME147 VxWorks board. +#mvme147:tc=.subnet17:ha=08.00.3e.20.da.47:bf=mv147vxw.st: + +# These are just for testing + +walnut:tc=.subnet16:ha=walnut: +banana:tc=.subnet17:ha=banana: +thor:tc=.subnet17:ha=thor: +classic:tc=.subnet16:ha=classic: diff --git a/usr.sbin/bootpd/bootptest.8 b/usr.sbin/bootpd/bootptest.8 new file mode 100644 index 00000000000..d076c8bc25b --- /dev/null +++ b/usr.sbin/bootpd/bootptest.8 @@ -0,0 +1,74 @@ +.\" bootptest.8 +.TH BOOTPTEST 8 "10 June 1993" "MAINTENANCE COMMANDS" +.SH NAME +bootptest \- send BOOTP queries and print responses +.SH SYNOPSIS +.LP +.B bootptest +[ +.B \-f +.I bootfile +] +[ +.B \-h +] +[ +.B \-m +.I magic_number +] +.I server\-name +.RI [ template-file ] +.SH DESCRIPTION +.B bootptest +sends BOOTP requests to the host specified as +.I server\-name +at one\-second intervals until either a response is received, +or until ten requests have gone unanswered. +After a response is received, +.B bootptest +will wait one more second listening for additional responses. +.SH OPTIONS +.TP +.B \-f +.I bootfile +Fill in the boot file field of the request with +.IR bootfile . +.TP +.B \-h +Use the hardware (Ethernet) address to identify the client. +By default, the IP address is copied into the request +indicating that this client already knows its IP address. +.TP +.B \-m +.I magic_number +Initialize the first word of the vendor options field with +.IR magic_number . +.LP +A +.I template-file +may be specified, in which case +.B bootptest +uses the (binary) contents of this file to initialize the +.I options +area of the request packet. +.SH CREDITS +.LP +The bootptest program is a combination of original and derived works. +The main program module (bootptest.c) is original work by +Gordon W. Ross <gwr@mc.com>. +The packet printing module (print-bootp.c) is a slightly modified +version of a file from the BSD tcpdump program. +.LP +This program includes software developed by the University of +California, Lawrence Berkeley Laboratory and its contributors. +(See the copyright notice in print-bootp.c) +.SH "SEE ALSO" +.LP +bootpd(8) +.SH REFERENCES +.TP +RFC951 +BOOTSTRAP PROTOCOL (BOOTP) +.TP +RFC1048 +BOOTP Vendor Information Extensions diff --git a/usr.sbin/bootpd/bootptest.c b/usr.sbin/bootpd/bootptest.c new file mode 100644 index 00000000000..d1cbcab92f4 --- /dev/null +++ b/usr.sbin/bootpd/bootptest.c @@ -0,0 +1,497 @@ +/* + * bootptest.c - Test out a bootp server. + * + * This simple program was put together from pieces taken from + * various places, including the CMU BOOTP client and server. + * The packet printing routine is from the Berkeley "tcpdump" + * program with some enhancements I added. The print-bootp.c + * file was shared with my copy of "tcpdump" and therefore uses + * some unusual utility routines that would normally be provided + * by various parts of the tcpdump program. Gordon W. Ross + * + * Boilerplate: + * + * This program includes software developed by the University of + * California, Lawrence Berkeley Laboratory and its contributors. + * (See the copyright notice in print-bootp.c) + * + * The remainder of this program is public domain. You may do + * whatever you like with it except claim that you wrote it. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * HISTORY: + * + * 12/02/93 Released version 1.4 (with bootp-2.3.2) + * 11/05/93 Released version 1.3 + * 10/14/93 Released version 1.2 + * 10/11/93 Released version 1.1 + * 09/28/93 Released version 1.0 + * 09/93 Original developed by Gordon W. Ross <gwr@mc.com> + */ + +char *usage = "bootptest [-h] server-name [vendor-data-template-file]"; + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/file.h> +#include <sys/time.h> +#include <sys/stat.h> + +#include <net/if.h> +#include <netinet/in.h> +#include <arpa/inet.h> /* inet_ntoa */ + +#include <stdlib.h> +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <netdb.h> +#include <assert.h> + +#include "bootp.h" +#include "bootptest.h" +#include "getif.h" +#include "patchlevel.h" + +#define LOG_ERR 1 +#define BUFLEN 1024 +#define WAITSECS 1 +#define MAXWAIT 10 + +int vflag = 1; +int tflag = 0; +int thiszone; +char *progname; +unsigned char *packetp; +unsigned char *snapend; +int snaplen; + + +/* + * IP port numbers for client and server obtained from /etc/services + */ + +u_short bootps_port, bootpc_port; + + +/* + * Internet socket and interface config structures + */ + +struct sockaddr_in sin_server; /* where to send requests */ +struct sockaddr_in sin_client; /* for bind and listen */ +struct sockaddr_in sin_from; /* Packet source */ +u_char eaddr[16]; /* Ethernet address */ + +/* + * General + */ + +int debug = 1; /* Debugging flag (level) */ +char hostname[64]; +char *sndbuf; /* Send packet buffer */ +char *rcvbuf; /* Receive packet buffer */ + +/* + * Vendor magic cookies for CMU and RFC1048 + */ + +unsigned char vm_cmu[4] = VM_CMU; +unsigned char vm_rfc1048[4] = VM_RFC1048; +short secs; /* How long client has waited */ + +char *get_errmsg(); +extern void bootp_print(); + +/* + * Initialization such as command-line processing is done, then + * the receiver loop is started. Die when interrupted. + */ + +main(argc, argv) + int argc; + char **argv; +{ + struct bootp *bp; + struct servent *sep; + struct hostent *hep; + + char *servername = NULL; + char *vendor_file = NULL; + char *bp_file = NULL; + int s; /* Socket file descriptor */ + int n, tolen, fromlen, recvcnt; + int use_hwa = 0; + int32 vend_magic; + int32 xid; + + progname = strrchr(argv[0], '/'); + if (progname) + progname++; + else + progname = argv[0]; + argc--; + argv++; + + if (debug) + printf("%s: version %s.%d\n", progname, VERSION, PATCHLEVEL); + + /* + * Verify that "struct bootp" has the correct official size. + * (Catch evil compilers that do struct padding.) + */ + assert(sizeof(struct bootp) == BP_MINPKTSZ); + + sndbuf = malloc(BUFLEN); + rcvbuf = malloc(BUFLEN); + if (!sndbuf || !rcvbuf) { + printf("malloc failed\n"); + exit(1); + } + + /* default magic number */ + bcopy(vm_rfc1048, (char*)&vend_magic, 4); + + /* Handle option switches. */ + while (argc > 0) { + if (argv[0][0] != '-') + break; + switch (argv[0][1]) { + + case 'f': /* File name to reqest. */ + if (argc < 2) + goto error; + argc--; argv++; + bp_file = *argv; + break; + + case 'h': /* Use hardware address. */ + use_hwa = 1; + break; + + case 'm': /* Magic number value. */ + if (argc < 2) + goto error; + argc--; argv++; + vend_magic = inet_addr(*argv); + break; + + error: + default: + puts(usage); + exit(1); + + } + argc--; + argv++; + } + + /* Get server name (or address) for query. */ + if (argc > 0) { + servername = *argv; + argc--; + argv++; + } + /* Get optional vendor-data-template-file. */ + if (argc > 0) { + vendor_file = *argv; + argc--; + argv++; + } + if (!servername) { + printf("missing server name.\n"); + puts(usage); + exit(1); + } + /* + * Create a socket. + */ + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("socket"); + exit(1); + } + /* + * Get server's listening port number + */ + sep = getservbyname("bootps", "udp"); + if (sep) { + bootps_port = ntohs((u_short) sep->s_port); + } else { + fprintf(stderr, "udp/bootps: unknown service -- using port %d\n", + IPPORT_BOOTPS); + bootps_port = (u_short) IPPORT_BOOTPS; + } + + /* + * Set up server socket address (for send) + */ + if (servername) { + if (inet_aton(servername, &sin_server.sin_addr) == 0) { + hep = gethostbyname(servername); + if (!hep) { + fprintf(stderr, "%s: unknown host\n", servername); + exit(1); + } + memcpy(&sin_server.sin_addr, hep->h_addr, + sizeof(sin_server.sin_addr)); + } + } else { + /* Get broadcast address */ + /* XXX - not yet */ + sin_server.sin_addr.s_addr = INADDR_ANY; + } + sin_server.sin_family = AF_INET; + sin_server.sin_port = htons(bootps_port); + + /* + * Get client's listening port number + */ + sep = getservbyname("bootpc", "udp"); + if (sep) { + bootpc_port = ntohs(sep->s_port); + } else { + fprintf(stderr, "udp/bootpc: unknown service -- using port %d\n", + IPPORT_BOOTPC); + bootpc_port = (u_short) IPPORT_BOOTPC; + } + + /* + * Set up client socket address (for listen) + */ + sin_client.sin_family = AF_INET; + sin_client.sin_port = htons(bootpc_port); + sin_client.sin_addr.s_addr = INADDR_ANY; + + /* + * Bind client socket to BOOTPC port. + */ + if (bind(s, (struct sockaddr *) &sin_client, sizeof(sin_client)) < 0) { + perror("bind BOOTPC port"); + if (errno == EACCES) + fprintf(stderr, "You need to run this as root\n"); + exit(1); + } + /* + * Build a request. + */ + bp = (struct bootp *) sndbuf; + bzero(bp, sizeof(*bp)); + bp->bp_op = BOOTREQUEST; + xid = (int32) getpid(); + bp->bp_xid = (u_int32) htonl(xid); + if (bp_file) + strncpy(bp->bp_file, bp_file, BP_FILE_LEN); + + /* + * Fill in the hardware address (or client IP address) + */ + if (use_hwa) { + struct ifreq *ifr; + + ifr = getif(s, &sin_server.sin_addr); + if (!ifr) { + printf("No interface for %s\n", servername); + exit(1); + } + if (getether(ifr->ifr_name, eaddr)) { + printf("Can not get ether addr for %s\n", ifr->ifr_name); + exit(1); + } + /* Copy Ethernet address into request packet. */ + bp->bp_htype = 1; + bp->bp_hlen = 6; + bcopy(eaddr, bp->bp_chaddr, bp->bp_hlen); + } else { + /* Fill in the client IP address. */ + gethostname(hostname, sizeof(hostname)); + hep = gethostbyname(hostname); + if (!hep) { + printf("Can not get my IP address\n"); + exit(1); + } + bcopy(hep->h_addr, &bp->bp_ciaddr, hep->h_length); + } + + /* + * Copy in the default vendor data. + */ + bcopy((char*)&vend_magic, bp->bp_vend, 4); + if (vend_magic) + bp->bp_vend[4] = TAG_END; + + /* + * Read in the "options" part of the request. + * This also determines the size of the packet. + */ + snaplen = sizeof(*bp); + if (vendor_file) { + int fd = open(vendor_file, 0); + if (fd < 0) { + perror(vendor_file); + exit(1); + } + /* Compute actual space for options. */ + n = BUFLEN - sizeof(*bp) + BP_VEND_LEN; + n = read(fd, bp->bp_vend, n); + close(fd); + if (n < 0) { + perror(vendor_file); + exit(1); + } + printf("read %d bytes of vendor template\n", n); + if (n > BP_VEND_LEN) { + printf("warning: extended options in use (len > %d)\n", + BP_VEND_LEN); + snaplen += (n - BP_VEND_LEN); + } + } + /* + * Set globals needed by print_bootp + * (called by send_request) + */ + packetp = (unsigned char *) eaddr; + snapend = (unsigned char *) sndbuf + snaplen; + + /* Send a request once per second while waiting for replies. */ + recvcnt = 0; + bp->bp_secs = secs = 0; + send_request(s); + while (1) { + struct timeval tv; + int readfds; + + tv.tv_sec = WAITSECS; + tv.tv_usec = 0L; + readfds = (1 << s); + n = select(s + 1, (fd_set *) & readfds, NULL, NULL, &tv); + if (n < 0) { + perror("select"); + break; + } + if (n == 0) { + /* + * We have not received a response in the last second. + * If we have ever received any responses, exit now. + * Otherwise, bump the "wait time" field and re-send. + */ + if (recvcnt > 0) + exit(0); + secs += WAITSECS; + if (secs > MAXWAIT) + break; + bp->bp_secs = htons(secs); + send_request(s); + continue; + } + fromlen = sizeof(sin_from); + n = recvfrom(s, rcvbuf, BUFLEN, 0, + (struct sockaddr *) &sin_from, &fromlen); + if (n <= 0) { + continue; + } + if (n < sizeof(struct bootp)) { + printf("received short packet\n"); + continue; + } + recvcnt++; + + /* Print the received packet. */ + printf("Recvd from %s", inet_ntoa(sin_from.sin_addr)); + /* set globals needed by bootp_print() */ + snaplen = n; + snapend = (unsigned char *) rcvbuf + snaplen; + bootp_print(rcvbuf, n, sin_from.sin_port, 0); + putchar('\n'); + /* + * This no longer exits immediately after receiving + * one response because it is useful to know if the + * client might get multiple responses. This code + * will now listen for one second after a response. + */ + } + fprintf(stderr, "no response from %s\n", servername); + exit(1); +} + +send_request(s) + int s; +{ + /* Print the request packet. */ + printf("Sending to %s", inet_ntoa(sin_server.sin_addr)); + bootp_print(sndbuf, snaplen, sin_from.sin_port, 0); + putchar('\n'); + + /* Send the request packet. */ + if (sendto(s, sndbuf, snaplen, 0, + (struct sockaddr *) &sin_server, + sizeof(sin_server)) < 0) + { + perror("sendto server"); + exit(1); + } +} + +/* + * Print out a filename (or other ascii string). + * Return true if truncated. + */ +int +printfn(s, ep) + register u_char *s, *ep; +{ + register u_char c; + + putchar('"'); + while (c = *s++) { + if (s > ep) { + putchar('"'); + return (1); + } + if (!isascii(c)) { + c = toascii(c); + putchar('M'); + putchar('-'); + } + if (!isprint(c)) { + c ^= 0x40; /* DEL to ?, others to alpha */ + putchar('^'); + } + putchar(c); + } + putchar('"'); + return (0); +} + +/* + * Convert an IP addr to a string. + * (like inet_ntoa, but ina is a pointer) + */ +char * +ipaddr_string(ina) + struct in_addr *ina; +{ + static char b[24]; + u_char *p; + + p = (u_char *) ina; + sprintf(b, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + return (b); +} + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/usr.sbin/bootpd/bootptest.h b/usr.sbin/bootpd/bootptest.h new file mode 100644 index 00000000000..27f78ba963b --- /dev/null +++ b/usr.sbin/bootpd/bootptest.h @@ -0,0 +1,30 @@ +/* bootptest.h */ +/* + * Hacks for sharing print-bootp.c between tcpdump and bootptest. + */ +#define ESRC(p) (p) +#define EDST(p) (p) + +#ifndef USE_BFUNCS +/* Use mem/str functions */ +/* There are no overlapped copies, so memcpy is OK. */ +#define bcopy(a,b,c) memcpy(b,a,c) +#define bzero(p,l) memset(p,0,l) +#define bcmp(a,b,c) memcmp(a,b,c) +#endif + +extern int vflag; /* verbose flag */ + +/* global pointers to beginning and end of current packet (during printing) */ +extern unsigned char *packetp; +extern unsigned char *snapend; + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +extern char *ipaddr_string P((struct in_addr *)); + +#undef P diff --git a/usr.sbin/bootpd/bptypes.h b/usr.sbin/bootpd/bptypes.h new file mode 100644 index 00000000000..9fe00a8216c --- /dev/null +++ b/usr.sbin/bootpd/bptypes.h @@ -0,0 +1,25 @@ +/* bptypes.h */ + +#ifndef BPTYPES_H +#define BPTYPES_H + +/* + * 32 bit integers are different types on various architectures + * XXX THE CORRECT WAY TO DO THIS IS: + * XXX (1) convert to _t form for all uses, + * XXX (2) define the _t's here (or somewhere) + * XXX if !defined(__BIT_TYPES_DEFINED__) + */ + +typedef int32_t int32; +typedef u_int32_t u_int32; + +/* + * Nice typedefs. . . + */ + +typedef int boolean; +typedef unsigned char byte; + + +#endif /* BPTYPES_H */ diff --git a/usr.sbin/bootpd/dovend.c b/usr.sbin/bootpd/dovend.c new file mode 100644 index 00000000000..ba6ab288b8b --- /dev/null +++ b/usr.sbin/bootpd/dovend.c @@ -0,0 +1,413 @@ +/* + * dovend.c : Inserts all but the first few vendor options. + */ + +#include <sys/types.h> + +#include <netinet/in.h> +#include <arpa/inet.h> /* inet_ntoa */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <syslog.h> + +#ifndef USE_BFUNCS +# include <memory.h> +/* Yes, memcpy is OK here (no overlapped copies). */ +# define bcopy(a,b,c) memcpy(b,a,c) +# define bzero(p,l) memset(p,0,l) +# define bcmp(a,b,c) memcmp(a,b,c) +# define index strchr +#endif + +#include "bootp.h" +#include "bootpd.h" +#include "report.h" +#include "dovend.h" + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +PRIVATE int insert_generic P((struct shared_bindata *, byte **, int *)); + +/* + * Insert the 2nd part of the options into an option buffer. + * Return amount of space used. + * + * This inserts everything EXCEPT: + * magic cookie, subnet mask, gateway, bootsize, extension file + * Those are handled separately (in bootpd.c) to allow this function + * to be shared between bootpd and bootpef. + * + * When an "extension file" is in use, the options inserted by + * this function go into the exten_file, not the bootp response. + */ + +int +dovend_rfc1497(hp, buf, len) + struct host *hp; + byte *buf; + int len; +{ + int bytesleft = len; + byte *vp = buf; + char *tmpstr; + + static char noroom[] = "%s: No room for \"%s\" option"; +#define NEED(LEN, MSG) do \ + if (bytesleft < (LEN)) { \ + report(LOG_NOTICE, noroom, \ + hp->hostname->string, MSG); \ + return (vp - buf); \ + } while (0) + + /* + * Note that the following have already been inserted: + * magic_cookie, subnet_mask, gateway, bootsize + * + * The remaining options are inserted in order of importance. + * (Of course the importance of each is a matter of opinion.) + * The option insertion order should probably be configurable. + * + * This is the order used in the NetBSD version. Can anyone + * explain why the time_offset and swap_server are first? + * Also, why is the hostname so far down the list? -gwr + */ + + if (hp->flags.time_offset) { + NEED(6, "to"); + *vp++ = TAG_TIME_OFFSET;/* -1 byte */ + *vp++ = 4; /* -1 byte */ + insert_u_long(htonl(hp->time_offset), &vp); /* -4 bytes */ + bytesleft -= 6; + } + /* + * swap server, root path, dump path + */ + if (hp->flags.swap_server) { + NEED(6, "sw"); + /* There is just one SWAP_SERVER, so it is not an iplist. */ + *vp++ = TAG_SWAP_SERVER;/* -1 byte */ + *vp++ = 4; /* -1 byte */ + insert_u_long(hp->swap_server.s_addr, &vp); /* -4 bytes */ + bytesleft -= 6; /* Fix real count */ + } + if (hp->flags.root_path) { + /* + * Check for room for root_path. Add 2 to account for + * TAG_ROOT_PATH and length. + */ + len = strlen(hp->root_path->string); + NEED((len + 2), "rp"); + *vp++ = TAG_ROOT_PATH; + *vp++ = (byte) (len & 0xFF); + bcopy(hp->root_path->string, vp, len); + vp += len; + bytesleft -= len + 2; + } + if (hp->flags.dump_file) { + /* + * Check for room for dump_file. Add 2 to account for + * TAG_DUMP_FILE and length. + */ + len = strlen(hp->dump_file->string); + NEED((len + 2), "df"); + *vp++ = TAG_DUMP_FILE; + *vp++ = (byte) (len & 0xFF); + bcopy(hp->dump_file->string, vp, len); + vp += len; + bytesleft -= len + 2; + } + /* + * DNS server and domain + */ + if (hp->flags.domain_server) { + if (insert_ip(TAG_DOMAIN_SERVER, + hp->domain_server, + &vp, &bytesleft)) + NEED(8, "ds"); + } + if (hp->flags.domain_name) { + /* + * Check for room for domain_name. Add 2 to account for + * TAG_DOMAIN_NAME and length. + */ + len = strlen(hp->domain_name->string); + NEED((len + 2), "dn"); + *vp++ = TAG_DOMAIN_NAME; + *vp++ = (byte) (len & 0xFF); + bcopy(hp->domain_name->string, vp, len); + vp += len; + bytesleft -= len + 2; + } + /* + * NIS (YP) server and domain + */ + if (hp->flags.nis_server) { + if (insert_ip(TAG_NIS_SERVER, + hp->nis_server, + &vp, &bytesleft)) + NEED(8, "ds"); + } + if (hp->flags.nis_domain) { + /* + * Check for room for nis_domain. Add 2 to account for + * TAG_NIS_DOMAIN and length. + */ + len = strlen(hp->nis_domain->string); + NEED((len + 2), "dn"); + *vp++ = TAG_NIS_DOMAIN; + *vp++ = (byte) (len & 0xFF); + bcopy(hp->nis_domain->string, vp, len); + vp += len; + bytesleft -= len + 2; + } + /* IEN 116 name server */ + if (hp->flags.name_server) { + if (insert_ip(TAG_NAME_SERVER, + hp->name_server, + &vp, &bytesleft)) + NEED(8, "ns"); + } + if (hp->flags.rlp_server) { + if (insert_ip(TAG_RLP_SERVER, + hp->rlp_server, + &vp, &bytesleft)) + NEED(8, "rl"); + } + /* Time server (RFC 868) */ + if (hp->flags.time_server) { + if (insert_ip(TAG_TIME_SERVER, + hp->time_server, + &vp, &bytesleft)) + NEED(8, "ts"); + } + /* NTP (time) Server (RFC 1129) */ + if (hp->flags.ntp_server) { + if (insert_ip(TAG_NTP_SERVER, + hp->ntp_server, + &vp, &bytesleft)) + NEED(8, "ts"); + } + /* + * I wonder: If the hostname were "promoted" into the BOOTP + * response part, might these "extension" files possibly be + * shared between several clients? + * + * Also, why not just use longer BOOTP packets with all the + * additional length used as option data. This bootpd version + * already supports that feature by replying with the same + * packet length as the client request packet. -gwr + */ + if (hp->flags.name_switch && hp->flags.send_name) { + /* + * Check for room for hostname. Add 2 to account for + * TAG_HOST_NAME and length. + */ + len = strlen(hp->hostname->string); +#if 0 + /* + * XXX - Too much magic. The user can always set the hostname + * to the short version in the bootptab file. -gwr + */ + if ((len + 2) > bytesleft) { + /* + * Not enough room for full (domain-qualified) hostname, try + * stripping it down to just the first field (host). + */ + tmpstr = hp->hostname->string; + len = 0; + while (*tmpstr && (*tmpstr != '.')) { + tmpstr++; + len++; + } + } +#endif + NEED((len + 2), "hn"); + *vp++ = TAG_HOST_NAME; + *vp++ = (byte) (len & 0xFF); + bcopy(hp->hostname->string, vp, len); + vp += len; + bytesleft -= len + 2; + } + /* + * The rest of these are less important, so they go last. + */ + if (hp->flags.lpr_server) { + if (insert_ip(TAG_LPR_SERVER, + hp->lpr_server, + &vp, &bytesleft)) + NEED(8, "lp"); + } + if (hp->flags.cookie_server) { + if (insert_ip(TAG_COOKIE_SERVER, + hp->cookie_server, + &vp, &bytesleft)) + NEED(8, "cs"); + } + if (hp->flags.log_server) { + if (insert_ip(TAG_LOG_SERVER, + hp->log_server, + &vp, &bytesleft)) + NEED(8, "lg"); + } + /* + * XXX - Add new tags here (to insert options) + */ + if (hp->flags.generic) { + if (insert_generic(hp->generic, &vp, &bytesleft)) + NEED(64, "(generic)"); + } + /* + * The end marker is inserted by the caller. + */ + return (vp - buf); +#undef NEED +} /* dovend_rfc1497 */ + + + +/* + * Insert a tag value, a length value, and a list of IP addresses into the + * memory buffer indirectly pointed to by "dest". "tag" is the RFC1048 tag + * number to use, "iplist" is a pointer to a list of IP addresses + * (struct in_addr_list), and "bytesleft" points to an integer which + * indicates the size of the "dest" buffer. + * + * Return zero if everything fits. + * + * This is used to fill the vendor-specific area of a bootp packet in + * conformance to RFC1048. + */ + +int +insert_ip(tag, iplist, dest, bytesleft) + byte tag; + struct in_addr_list *iplist; + byte **dest; + int *bytesleft; +{ + struct in_addr *addrptr; + unsigned addrcount = 1; + byte *d; + + if (iplist == NULL) + return (0); + + if (*bytesleft >= 6) { + d = *dest; /* Save pointer for later */ + **dest = tag; + (*dest) += 2; + (*bytesleft) -= 2; /* Account for tag and length */ + addrptr = iplist->addr; + addrcount = iplist->addrcount; + while ((*bytesleft >= 4) && (addrcount > 0)) { + insert_u_long(addrptr->s_addr, dest); + addrptr++; + addrcount--; + (*bytesleft) -= 4; /* Four bytes per address */ + } + d[1] = (byte) ((*dest - d - 2) & 0xFF); + } + return (addrcount); +} + + + +/* + * Insert generic data into a bootp packet. The data is assumed to already + * be in RFC1048 format. It is inserted using a first-fit algorithm which + * attempts to insert as many tags as possible. Tags and data which are + * too large to fit are skipped; any remaining tags are tried until they + * have all been exhausted. + * Return zero if everything fits. + */ + +static int +insert_generic(gendata, buff, bytesleft) + struct shared_bindata *gendata; + byte **buff; + int *bytesleft; +{ + byte *srcptr; + int length, numbytes; + int skipped = 0; + + if (gendata == NULL) + return (0); + + srcptr = gendata->data; + length = gendata->length; + while ((length > 0) && (*bytesleft > 0)) { + switch (*srcptr) { + case TAG_END: + length = 0; /* Force an exit on next iteration */ + break; + case TAG_PAD: + *(*buff)++ = *srcptr++; + (*bytesleft)--; + length--; + break; + default: + numbytes = srcptr[1] + 2; + if (*bytesleft < numbytes) + skipped += numbytes; + else { + bcopy(srcptr, *buff, numbytes); + (*buff) += numbytes; + (*bytesleft) -= numbytes; + } + srcptr += numbytes; + length -= numbytes; + break; + } + } /* while */ + return (skipped); +} + +/* + * Insert the unsigned long "value" into memory starting at the byte + * pointed to by the byte pointer (*dest). (*dest) is updated to + * point to the next available byte. + * + * Since it is desirable to internally store network addresses in network + * byte order (in struct in_addr's), this routine expects longs to be + * passed in network byte order. + * + * However, due to the nature of the main algorithm, the long must be in + * host byte order, thus necessitating the use of ntohl() first. + */ + +void +insert_u_long(value, dest) + u_int32 value; + byte **dest; +{ + byte *temp; + int n; + + value = ntohl(value); /* Must use host byte order here */ + temp = (*dest += 4); + for (n = 4; n > 0; n--) { + *--temp = (byte) (value & 0xFF); + value >>= 8; + } + /* Final result is network byte order */ +} + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/usr.sbin/bootpd/dovend.h b/usr.sbin/bootpd/dovend.h new file mode 100644 index 00000000000..b30c982d8e2 --- /dev/null +++ b/usr.sbin/bootpd/dovend.h @@ -0,0 +1,13 @@ +/* dovend.h */ + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +extern int dovend_rfc1497 P((struct host *hp, u_char *buf, int len)); +extern int insert_ip P((int, struct in_addr_list *, u_char **, int *)); +extern void insert_u_long P((u_int32, u_char **)); + +#undef P diff --git a/usr.sbin/bootpd/dumptab.c b/usr.sbin/bootpd/dumptab.c new file mode 100644 index 00000000000..5893f6c4a23 --- /dev/null +++ b/usr.sbin/bootpd/dumptab.c @@ -0,0 +1,382 @@ +/* + * dumptab.c - handles dumping the database + */ + +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/inet.h> /* inet_ntoa */ + +#include <stdio.h> +#include <stdlib.h> +#include <syslog.h> +#include <time.h> + +#ifndef USE_BFUNCS +#include <memory.h> +/* Yes, memcpy is OK here (no overlapped copies). */ +#define bcopy(a,b,c) memcpy(b,a,c) +#define bzero(p,l) memset(p,0,l) +#define bcmp(a,b,c) memcmp(a,b,c) +#endif + +#include "bootp.h" +#include "hash.h" +#include "hwaddr.h" +#include "report.h" +#include "patchlevel.h" +#include "bootpd.h" + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +static void dump_generic P((FILE *, struct shared_bindata *)); +static void dump_host P((FILE *, struct host *)); +static void list_ipaddresses P((FILE *, struct in_addr_list *)); + +#undef P + +#ifndef DEBUG +void +dumptab(filename) + char *filename; +{ + report(LOG_INFO, "No dumptab support!"); +} + +#else /* DEBUG */ + +/* + * Dump the internal memory database to bootpd_dump. + */ + +void +dumptab(filename) + char *filename; +{ + int n; + struct host *hp; + FILE *fp; + time_t t; + /* Print symbols in alphabetical order for reader's convenience. */ + static char legend[] = "#\n# Legend:\t(see bootptab.5)\n\ +#\tfirst field -- hostname (not indented)\n\ +#\tbf -- bootfile\n\ +#\tbs -- bootfile size in 512-octet blocks\n\ +#\tcs -- cookie servers\n\ +#\tdf -- dump file name\n\ +#\tdn -- domain name\n\ +#\tds -- domain name servers\n\ +#\tef -- extension file\n\ +#\tex -- exec file (YORK_EX_OPTION)\n\ +#\tgw -- gateways\n\ +#\tha -- hardware address\n\ +#\thd -- home directory for bootfiles\n\ +#\thn -- host name set for client\n\ +#\tht -- hardware type\n\ +#\tim -- impress servers\n\ +#\tip -- host IP address\n\ +#\tlg -- log servers\n\ +#\tlp -- LPR servers\n\ +#\tms -- message size\n\ +#\tmw -- min wait (secs)\n\ +#\tns -- IEN-116 name servers\n\ +#\tnt -- NTP servers (RFC 1129)\n\ +#\tra -- reply address override\n\ +#\trl -- resource location protocol servers\n\ +#\trp -- root path\n\ +#\tsa -- boot server address\n\ +#\tsm -- subnet mask\n\ +#\tsw -- swap server\n\ +#\ttc -- template host (points to similar host entry)\n\ +#\ttd -- TFTP directory\n\ +#\tto -- time offset (seconds)\n\ +#\tts -- time servers\n\ +#\tvm -- vendor magic number\n\ +#\tyd -- YP (NIS) domain\n\ +#\tys -- YP (NIS) servers\n\ +#\tTn -- generic option tag n\n\ +\n"; + + /* + * Open bootpd.dump file. + */ + if ((fp = fopen(filename, "w")) == NULL) { + report(LOG_ERR, "error opening \"%s\": %s", + filename, get_errmsg()); + exit(1); + } + t = time(NULL); + fprintf(fp, "\n# %s %s.%d\n", progname, VERSION, PATCHLEVEL); + fprintf(fp, "# %s: dump of bootp server database.\n", filename); + fprintf(fp, "# Dump taken %s", ctime(&t)); + fwrite(legend, 1, sizeof(legend) - 1, fp); + + n = 0; + for (hp = (struct host *) hash_FirstEntry(nmhashtable); hp != NULL; + hp = (struct host *) hash_NextEntry(nmhashtable)) { + dump_host(fp, hp); + fprintf(fp, "\n"); + n++; + } + fclose(fp); + + report(LOG_INFO, "dumped %d entries to \"%s\".", n, filename); +} + + + +/* + * Dump all the available information on the host pointed to by "hp". + * The output is sent to the file pointed to by "fp". + */ + +static void +dump_host(fp, hp) + FILE *fp; + struct host *hp; +{ + /* Print symbols in alphabetical order for reader's convenience. */ + if (hp) { + fprintf(fp, "%s:", (hp->hostname ? + hp->hostname->string : "?")); + if (hp->flags.bootfile) { + fprintf(fp, "\\\n\t:bf=%s:", hp->bootfile->string); + } + if (hp->flags.bootsize) { + fprintf(fp, "\\\n\t:bs="); + if (hp->flags.bootsize_auto) { + fprintf(fp, "auto:"); + } else { + fprintf(fp, "%d:", hp->bootsize); + } + } + if (hp->flags.cookie_server) { + fprintf(fp, "\\\n\t:cs="); + list_ipaddresses(fp, hp->cookie_server); + fprintf(fp, ":"); + } + if (hp->flags.dump_file) { + fprintf(fp, "\\\n\t:df=%s:", hp->dump_file->string); + } + if (hp->flags.domain_name) { + fprintf(fp, "\\\n\t:dn=%s:", hp->domain_name->string); + } + if (hp->flags.domain_server) { + fprintf(fp, "\\\n\t:ds="); + list_ipaddresses(fp, hp->domain_server); + fprintf(fp, ":"); + } + if (hp->flags.exten_file) { + fprintf(fp, "\\\n\t:ef=%s:", hp->exten_file->string); + } + if (hp->flags.exec_file) { + fprintf(fp, "\\\n\t:ex=%s:", hp->exec_file->string); + } + if (hp->flags.gateway) { + fprintf(fp, "\\\n\t:gw="); + list_ipaddresses(fp, hp->gateway); + fprintf(fp, ":"); + } + /* FdC: swap_server (see below) */ + if (hp->flags.homedir) { + fprintf(fp, "\\\n\t:hd=%s:", hp->homedir->string); + } + /* FdC: dump_file (see above) */ + /* FdC: domain_name (see above) */ + /* FdC: root_path (see below) */ + if (hp->flags.name_switch && hp->flags.send_name) { + fprintf(fp, "\\\n\t:hn:"); + } + if (hp->flags.htype) { + int hlen = haddrlength(hp->htype); + fprintf(fp, "\\\n\t:ht=%u:", (unsigned) hp->htype); + if (hp->flags.haddr) { + fprintf(fp, "ha=\"%s\":", + haddrtoa(hp->haddr, hlen)); + } + } + if (hp->flags.impress_server) { + fprintf(fp, "\\\n\t:im="); + list_ipaddresses(fp, hp->impress_server); + fprintf(fp, ":"); + } + /* NetBSD: swap_server (see below) */ + if (hp->flags.iaddr) { + fprintf(fp, "\\\n\t:ip=%s:", inet_ntoa(hp->iaddr)); + } + if (hp->flags.log_server) { + fprintf(fp, "\\\n\t:lg="); + list_ipaddresses(fp, hp->log_server); + fprintf(fp, ":"); + } + if (hp->flags.lpr_server) { + fprintf(fp, "\\\n\t:lp="); + list_ipaddresses(fp, hp->lpr_server); + fprintf(fp, ":"); + } + if (hp->flags.msg_size) { + fprintf(fp, "\\\n\t:ms=%d:", hp->msg_size); + } + if (hp->flags.min_wait) { + fprintf(fp, "\\\n\t:mw=%d:", hp->min_wait); + } + if (hp->flags.name_server) { + fprintf(fp, "\\\n\t:ns="); + list_ipaddresses(fp, hp->name_server); + fprintf(fp, ":"); + } + if (hp->flags.ntp_server) { + fprintf(fp, "\\\n\t:nt="); + list_ipaddresses(fp, hp->ntp_server); + fprintf(fp, ":"); + } + if (hp->flags.reply_addr) { + fprintf(fp, "\\\n\t:ra=%s:", inet_ntoa(hp->reply_addr)); + } + if (hp->flags.rlp_server) { + fprintf(fp, "\\\n\t:rl="); + list_ipaddresses(fp, hp->rlp_server); + fprintf(fp, ":"); + } + if (hp->flags.root_path) { + fprintf(fp, "\\\n\t:rp=%s:", hp->root_path->string); + } + if (hp->flags.bootserver) { + fprintf(fp, "\\\n\t:sa=%s:", inet_ntoa(hp->bootserver)); + } + if (hp->flags.subnet_mask) { + fprintf(fp, "\\\n\t:sm=%s:", inet_ntoa(hp->subnet_mask)); + } + if (hp->flags.swap_server) { + fprintf(fp, "\\\n\t:sw=%s:", inet_ntoa(hp->subnet_mask)); + } + if (hp->flags.tftpdir) { + fprintf(fp, "\\\n\t:td=%s:", hp->tftpdir->string); + } + /* NetBSD: rootpath (see above) */ + /* NetBSD: domainname (see above) */ + /* NetBSD: dumpfile (see above) */ + if (hp->flags.time_offset) { + fprintf(fp, "\\\n\t:to=%ld:", hp->time_offset); + } + if (hp->flags.time_server) { + fprintf(fp, "\\\n\t:ts="); + list_ipaddresses(fp, hp->time_server); + fprintf(fp, ":"); + } + if (hp->flags.vm_cookie) { + fprintf(fp, "\\\n\t:vm="); + if (!bcmp(hp->vm_cookie, vm_rfc1048, 4)) { + fprintf(fp, "rfc1048:"); + } else if (!bcmp(hp->vm_cookie, vm_cmu, 4)) { + fprintf(fp, "cmu:"); + } else { + fprintf(fp, "%d.%d.%d.%d:", + (int) ((hp->vm_cookie)[0]), + (int) ((hp->vm_cookie)[1]), + (int) ((hp->vm_cookie)[2]), + (int) ((hp->vm_cookie)[3])); + } + } + if (hp->flags.nis_domain) { + fprintf(fp, "\\\n\t:yd=%s:", + hp->nis_domain->string); + } + if (hp->flags.nis_server) { + fprintf(fp, "\\\n\t:ys="); + list_ipaddresses(fp, hp->nis_server); + fprintf(fp, ":"); + } + /* + * XXX - Add new tags here (or above, + * so they print in alphabetical order). + */ + + if (hp->flags.generic) { + dump_generic(fp, hp->generic); + } + } +} + + +static void +dump_generic(fp, generic) + FILE *fp; + struct shared_bindata *generic; +{ + u_char *bp = generic->data; + u_char *ep = bp + generic->length; + u_char tag; + int len; + + while (bp < ep) { + tag = *bp++; + if (tag == TAG_PAD) + continue; + if (tag == TAG_END) + return; + len = *bp++; + if (bp + len > ep) { + fprintf(fp, " #junk in generic! :"); + return; + } + fprintf(fp, "\\\n\t:T%d=", tag); + while (len) { + fprintf(fp, "%02X", *bp); + bp++; + len--; + if (len) + fprintf(fp, "."); + } + fprintf(fp, ":"); + } +} + + + +/* + * Dump an entire struct in_addr_list of IP addresses to the indicated file. + * + * The addresses are printed in standard ASCII "dot" notation and separated + * from one another by a single space. A single leading space is also + * printed before the first adddress. + * + * Null lists produce no output (and no error). + */ + +static void +list_ipaddresses(fp, ipptr) + FILE *fp; + struct in_addr_list *ipptr; +{ + unsigned count; + struct in_addr *addrptr; + + if (ipptr) { + count = ipptr->addrcount; + addrptr = ipptr->addr; + while (count > 0) { + fprintf(fp, "%s", inet_ntoa(*addrptr++)); + count--; + if (count) + fprintf(fp, ", "); + } + } +} + +#endif /* DEBUG */ + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/usr.sbin/bootpd/getether.c b/usr.sbin/bootpd/getether.c new file mode 100644 index 00000000000..d131b50f7f8 --- /dev/null +++ b/usr.sbin/bootpd/getether.c @@ -0,0 +1,374 @@ +/* + * getether.c : get the ethernet address of an interface + * + * All of this code is quite system-specific. As you may well + * guess, it took a good bit of detective work to figure out! + * + * If you figure out how to do this on another system, + * please let me know. <gwr@mc.com> + */ + +#include <sys/types.h> +#include <sys/socket.h> + +#include <ctype.h> +#include <syslog.h> + +#include "report.h" +#define EALEN 6 + +#if defined(ultrix) || (defined(__osf__) && defined(__alpha)) +/* + * This is really easy on Ultrix! Thanks to + * Harald Lundberg <hl@tekla.fi> for this code. + * + * The code here is not specific to the Alpha, but that was the + * only symbol we could find to identify DEC's version of OSF. + * (Perhaps we should just define DEC in the Makefile... -gwr) + */ + +#include <sys/ioctl.h> +#include <net/if.h> /* struct ifdevea */ + +getether(ifname, eap) + char *ifname, *eap; +{ + int rc = -1; + int fd; + struct ifdevea phys; + bzero(&phys, sizeof(phys)); + strcpy(phys.ifr_name, ifname); + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + report(LOG_ERR, "getether: socket(INET,DGRAM) failed"); + return -1; + } + if (ioctl(fd, SIOCRPHYSADDR, &phys) < 0) { + report(LOG_ERR, "getether: ioctl SIOCRPHYSADDR failed"); + } else { + bcopy(&phys.current_pa[0], eap, EALEN); + rc = 0; + } + close(fd); + return rc; +} + +#define GETETHER +#endif /* ultrix|osf1 */ + + +#ifdef SUNOS + +#include <sys/sockio.h> +#include <sys/time.h> /* needed by net_if.h */ +#include <net/nit_if.h> /* for NIOCBIND */ +#include <net/if.h> /* for struct ifreq */ + +getether(ifname, eap) + char *ifname; /* interface name from ifconfig structure */ + char *eap; /* Ether address (output) */ +{ + int rc = -1; + + struct ifreq ifrnit; + int nit; + + bzero((char *) &ifrnit, sizeof(ifrnit)); + strncpy(&ifrnit.ifr_name[0], ifname, IFNAMSIZ); + + nit = open("/dev/nit", 0); + if (nit < 0) { + report(LOG_ERR, "getether: open /dev/nit: %s", + get_errmsg()); + return rc; + } + do { + if (ioctl(nit, NIOCBIND, &ifrnit) < 0) { + report(LOG_ERR, "getether: NIOCBIND on nit"); + break; + } + if (ioctl(nit, SIOCGIFADDR, &ifrnit) < 0) { + report(LOG_ERR, "getether: SIOCGIFADDR on nit"); + break; + } + bcopy(&ifrnit.ifr_addr.sa_data[0], eap, EALEN); + rc = 0; + } while (0); + close(nit); + return rc; +} + +#define GETETHER +#endif /* SUNOS */ + + +#if defined(__386BSD__) || defined(__NetBSD__) +/* Thanks to John Brezak <brezak@ch.hp.com> for this code. */ +#include <sys/ioctl.h> +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_types.h> + +getether(ifname, eap) + char *ifname; /* interface name from ifconfig structure */ + char *eap; /* Ether address (output) */ +{ + int fd, rc = -1; + register int n; + struct ifreq ibuf[16], ifr; + struct ifconf ifc; + register struct ifreq *ifrp, *ifend; + + /* Fetch the interface configuration */ + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + report(LOG_ERR, "getether: socket %s: %s", ifname, get_errmsg()); + return (fd); + } + ifc.ifc_len = sizeof(ibuf); + ifc.ifc_buf = (caddr_t) ibuf; + if (ioctl(fd, SIOCGIFCONF, (char *) &ifc) < 0 || + ifc.ifc_len < sizeof(struct ifreq)) { + report(LOG_ERR, "getether: SIOCGIFCONF: %s", get_errmsg); + goto out; + } + /* Search interface configuration list for link layer address. */ + ifrp = ibuf; + ifend = (struct ifreq *) ((char *) ibuf + ifc.ifc_len); + while (ifrp < ifend) { + /* Look for interface */ + if (strcmp(ifname, ifrp->ifr_name) == 0 && + ifrp->ifr_addr.sa_family == AF_LINK && + ((struct sockaddr_dl *) &ifrp->ifr_addr)->sdl_type == IFT_ETHER) { + bcopy(LLADDR((struct sockaddr_dl *) &ifrp->ifr_addr), eap, EALEN); + rc = 0; + break; + } + /* Bump interface config pointer */ + n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name); + if (n < sizeof(*ifrp)) + n = sizeof(*ifrp); + ifrp = (struct ifreq *) ((char *) ifrp + n); + } + + out: + close(fd); + return (rc); +} + +#define GETETHER +#endif /* __NetBSD__ */ + + +#ifdef SVR4 +/* + * This is for "Streams TCP/IP" by Lachman Associates. + * They sure made this cumbersome! -gwr + */ + +#include <sys/sockio.h> +#include <sys/dlpi.h> +#include <stropts.h> +#ifndef NULL +#define NULL 0 +#endif + +getether(ifname, eap) + char *ifname; /* interface name from ifconfig structure */ + char *eap; /* Ether address (output) */ +{ + int rc = -1; + char devname[32]; + char tmpbuf[sizeof(union DL_primitives) + 16]; + struct strbuf cbuf; + int fd, flags; + union DL_primitives *dlp; + char *enaddr; + int unit = -1; /* which unit to attach */ + + sprintf(devname, "/dev/%s", ifname); + fd = open(devname, 2); + if (fd < 0) { + /* Try without the trailing digit. */ + char *p = devname + 5; + while (isalpha(*p)) + p++; + if (isdigit(*p)) { + unit = *p - '0'; + *p = '\0'; + } + fd = open(devname, 2); + if (fd < 0) { + report(LOG_ERR, "getether: open %s: %s", + devname, get_errmsg()); + return rc; + } + } +#ifdef DL_ATTACH_REQ + /* + * If this is a "Style 2" DLPI, then we must "attach" first + * to tell the driver which unit (board, port) we want. + * For now, decide this based on the device name. + * (Should do "info_req" and check dl_provider_style ...) + */ + if (unit >= 0) { + memset(tmpbuf, 0, sizeof(tmpbuf)); + dlp = (union DL_primitives *) tmpbuf; + dlp->dl_primitive = DL_ATTACH_REQ; + dlp->attach_req.dl_ppa = unit; + cbuf.buf = tmpbuf; + cbuf.len = DL_ATTACH_REQ_SIZE; + if (putmsg(fd, &cbuf, NULL, 0) < 0) { + report(LOG_ERR, "getether: attach: putmsg: %s", get_errmsg()); + goto out; + } + /* Recv the ack. */ + cbuf.buf = tmpbuf; + cbuf.maxlen = sizeof(tmpbuf); + flags = 0; + if (getmsg(fd, &cbuf, NULL, &flags) < 0) { + report(LOG_ERR, "getether: attach: getmsg: %s", get_errmsg()); + goto out; + } + /* + * Check the type, etc. + */ + if (dlp->dl_primitive == DL_ERROR_ACK) { + report(LOG_ERR, "getether: attach: dlpi_errno=%d, unix_errno=%d", + dlp->error_ack.dl_errno, + dlp->error_ack.dl_unix_errno); + goto out; + } + if (dlp->dl_primitive != DL_OK_ACK) { + report(LOG_ERR, "getether: attach: not OK or ERROR"); + goto out; + } + } /* unit >= 0 */ +#endif /* DL_ATTACH_REQ */ + + /* + * Get the Ethernet address the same way the ARP module + * does when it is pushed onto a new stream (bind). + * One should instead be able just do an dl_info_req + * but many drivers do not supply the hardware address + * in the response to dl_info_req (they MUST supply it + * for dl_bind_ack because the ARP module requires it). + */ + memset(tmpbuf, 0, sizeof(tmpbuf)); + dlp = (union DL_primitives *) tmpbuf; + dlp->dl_primitive = DL_BIND_REQ; + dlp->bind_req.dl_sap = 0x8FF; /* XXX - Unused SAP */ + cbuf.buf = tmpbuf; + cbuf.len = DL_BIND_REQ_SIZE; + if (putmsg(fd, &cbuf, NULL, 0) < 0) { + report(LOG_ERR, "getether: bind: putmsg: %s", get_errmsg()); + goto out; + } + /* Recv the ack. */ + cbuf.buf = tmpbuf; + cbuf.maxlen = sizeof(tmpbuf); + flags = 0; + if (getmsg(fd, &cbuf, NULL, &flags) < 0) { + report(LOG_ERR, "getether: bind: getmsg: %s", get_errmsg()); + goto out; + } + /* + * Check the type, etc. + */ + if (dlp->dl_primitive == DL_ERROR_ACK) { + report(LOG_ERR, "getether: bind: dlpi_errno=%d, unix_errno=%d", + dlp->error_ack.dl_errno, + dlp->error_ack.dl_unix_errno); + goto out; + } + if (dlp->dl_primitive != DL_BIND_ACK) { + report(LOG_ERR, "getether: bind: not OK or ERROR"); + goto out; + } + if (dlp->bind_ack.dl_addr_offset == 0) { + report(LOG_ERR, "getether: bind: ack has no address"); + goto out; + } + if (dlp->bind_ack.dl_addr_length < EALEN) { + report(LOG_ERR, "getether: bind: ack address truncated"); + goto out; + } + /* + * Copy the Ethernet address out of the message. + */ + enaddr = tmpbuf + dlp->bind_ack.dl_addr_offset; + memcpy(eap, enaddr, EALEN); + rc = 0; + + out: + close(fd); + return rc; +} + +#define GETETHER +#endif /* SVR4 */ + + +#ifdef linux +/* + * This is really easy on Linux! This version (for linux) + * written by Nigel Metheringham <nigelm@ohm.york.ac.uk> + * + * The code is almost identical to the Ultrix code - however + * the names are different to confuse the innocent :-) + * Most of this code was stolen from the Ultrix bit above. + */ + +#include <sys/ioctl.h> +#include <net/if.h> /* struct ifreq */ + +/* In a properly configured system this should be either sys/socketio.h + or sys/sockios.h, but on my distribution these don't line up correctly */ +#include <linux/sockios.h> /* Needed for IOCTL defs */ + +getether(ifname, eap) + char *ifname, *eap; +{ + int rc = -1; + int fd; + struct ifreq phys; + bzero(&phys, sizeof(phys)); + strcpy(phys.ifr_name, ifname); + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + report(LOG_ERR, "getether: socket(INET,DGRAM) failed"); + return -1; + } + if (ioctl(fd, SIOCGIFHWADDR, &phys) < 0) { + report(LOG_ERR, "getether: ioctl SIOCGIFHWADDR failed"); + } else { + bcopy(phys.ifr_hwaddr, eap, EALEN); + rc = 0; + } + close(fd); + return rc; +} + +#define GETETHER +#endif /* linux */ + + +/* If we don't know how on this system, just return an error. */ +#ifndef GETETHER +getether(ifname, eap) + char *ifname, *eap; +{ + return -1; +} + +#endif /* !GETETHER */ + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/usr.sbin/bootpd/getif.c b/usr.sbin/bootpd/getif.c new file mode 100644 index 00000000000..ba0a00bdd0f --- /dev/null +++ b/usr.sbin/bootpd/getif.c @@ -0,0 +1,146 @@ +/* + * getif.c : get an interface structure + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/param.h> + +#if defined(SUNOS) || defined(SVR4) +#include <sys/sockio.h> +#endif +#ifdef SVR4 +#include <sys/stropts.h> +#endif + +#include <net/if.h> /* for struct ifreq */ +#include <netinet/in.h> + +#ifndef NO_UNISTD +#include <unistd.h> +#endif +#include <syslog.h> +#include <errno.h> +#include <assert.h> + +#include "getif.h" +#include "report.h" + +#ifdef __bsdi__ +#define BSD 43 +#endif + +static struct ifreq ifreq[10]; /* Holds interface configuration */ +static struct ifconf ifconf; /* points to ifreq */ + +static int nmatch(); + +/* Return a pointer to the interface struct for the passed address. */ +struct ifreq * +getif(s, addrp) + int s; /* socket file descriptor */ + struct in_addr *addrp; /* destination address on interface */ +{ + int maxmatch; + int len, m, incr; + struct ifreq *ifrq, *ifrmax; + struct sockaddr_in *sip; + char *p; + + /* If no address was supplied, just return NULL. */ + if (!addrp) + return (struct ifreq *) 0; + + /* Get the interface config if not done already. */ + if (ifconf.ifc_len == 0) { +#ifdef SVR4 + /* + * SysVr4 returns garbage if you do this the obvious way! + * This one took a while to figure out... -gwr + */ + struct strioctl ioc; + ioc.ic_cmd = SIOCGIFCONF; + ioc.ic_timout = 0; + ioc.ic_len = sizeof(ifreq); + ioc.ic_dp = (char *) ifreq; + m = ioctl(s, I_STR, (char *) &ioc); + ifconf.ifc_len = ioc.ic_len; + ifconf.ifc_req = ifreq; +#else /* SVR4 */ + ifconf.ifc_len = sizeof(ifreq); + ifconf.ifc_req = ifreq; + m = ioctl(s, SIOCGIFCONF, (caddr_t) & ifconf); +#endif /* SVR4 */ + if ((m < 0) || (ifconf.ifc_len <= 0)) { + report(LOG_ERR, "ioctl SIOCGIFCONF"); + return (struct ifreq *) 0; + } + } + maxmatch = 7; /* this many bits or less... */ + ifrmax = (struct ifreq *) 0;/* ... is not a valid match */ + p = (char *) ifreq; + len = ifconf.ifc_len; + while (len > 0) { + ifrq = (struct ifreq *) p; + sip = (struct sockaddr_in *) &ifrq->ifr_addr; + m = nmatch(addrp, &(sip->sin_addr)); + if (m > maxmatch) { + maxmatch = m; + ifrmax = ifrq; + } + /* XXX - Could this be just #ifndef IFNAMSIZ instead? -gwr */ +#if (BSD - 0) < 43 + /* BSD not defined or earlier than 4.3 */ + incr = sizeof(*ifrq); +#else /* NetBSD */ + incr = ifrq->ifr_addr.sa_len + IFNAMSIZ; +#endif /* NetBSD */ + + p += incr; + len -= incr; + } + + return ifrmax; +} + +/* + * Return the number of leading bits matching in the + * internet addresses supplied. + */ +static int +nmatch(ca, cb) + u_char *ca, *cb; /* ptrs to IP address, network order */ +{ + u_int m = 0; /* count of matching bits */ + u_int n = 4; /* bytes left, then bitmask */ + + /* Count matching bytes. */ + while (n && (*ca == *cb)) { + ca++; + cb++; + m += 8; + n--; + } + /* Now count matching bits. */ + if (n) { + n = 0x80; + while (n && ((*ca & n) == (*cb & n))) { + m++; + n >>= 1; + } + } + return (m); +} + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/usr.sbin/bootpd/getif.h b/usr.sbin/bootpd/getif.h new file mode 100644 index 00000000000..c51dafd2095 --- /dev/null +++ b/usr.sbin/bootpd/getif.h @@ -0,0 +1,7 @@ +/* getif.h */ + +#ifdef __STDC__ +extern struct ifreq *getif(int, struct in_addr *); +#else +extern struct ifreq *getif(); +#endif diff --git a/usr.sbin/bootpd/hash.c b/usr.sbin/bootpd/hash.c new file mode 100644 index 00000000000..3ee6d8dcfc5 --- /dev/null +++ b/usr.sbin/bootpd/hash.c @@ -0,0 +1,425 @@ +/************************************************************************ + Copyright 1988, 1991 by Carnegie Mellon University + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, provided +that the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of Carnegie Mellon University not be used +in advertising or publicity pertaining to distribution of the software +without specific, written prior permission. + +CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. +************************************************************************/ + +#ifndef lint +static char rcsid[] = "$Id: hash.c,v 1.1 1995/10/18 08:47:27 deraadt Exp $"; +#endif + + +/* + * Generalized hash table ADT + * + * Provides multiple, dynamically-allocated, variable-sized hash tables on + * various data and keys. + * + * This package attempts to follow some of the coding conventions suggested + * by Bob Sidebotham and the AFS Clean Code Committee of the + * Information Technology Center at Carnegie Mellon. + */ + + +#include <sys/types.h> +#include <stdlib.h> + +#ifndef USE_BFUNCS +#include <memory.h> +/* Yes, memcpy is OK here (no overlapped copies). */ +#define bcopy(a,b,c) memcpy(b,a,c) +#define bzero(p,l) memset(p,0,l) +#define bcmp(a,b,c) memcmp(a,b,c) +#endif + +#include "hash.h" + +#define TRUE 1 +#define FALSE 0 +#ifndef NULL +#define NULL 0 +#endif + +/* + * This can be changed to make internal routines visible to debuggers, etc. + */ +#ifndef PRIVATE +#define PRIVATE static +#endif + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +PRIVATE void hashi_FreeMembers P((hash_member *, hash_freefp)); + +#undef P + + + +/* + * Hash table initialization routine. + * + * This routine creates and intializes a hash table of size "tablesize" + * entries. Successful calls return a pointer to the hash table (which must + * be passed to other hash routines to identify the hash table). Failed + * calls return NULL. + */ + +hash_tbl * +hash_Init(tablesize) + unsigned tablesize; +{ + register hash_tbl *hashtblptr; + register unsigned totalsize; + + if (tablesize > 0) { + totalsize = sizeof(hash_tbl) + + sizeof(hash_member *) * (tablesize - 1); + hashtblptr = (hash_tbl *) malloc(totalsize); + if (hashtblptr) { + bzero((char *) hashtblptr, totalsize); + hashtblptr->size = tablesize; /* Success! */ + hashtblptr->bucketnum = 0; + hashtblptr->member = (hashtblptr->table)[0]; + } + } else { + hashtblptr = NULL; /* Disallow zero-length tables */ + } + return hashtblptr; /* NULL if failure */ +} + + + +/* + * Frees an entire linked list of bucket members (used in the open + * hashing scheme). Does nothing if the passed pointer is NULL. + */ + +PRIVATE void +hashi_FreeMembers(bucketptr, free_data) + hash_member *bucketptr; + hash_freefp free_data; +{ + hash_member *nextbucket; + while (bucketptr) { + nextbucket = bucketptr->next; + (*free_data) (bucketptr->data); + free((char *) bucketptr); + bucketptr = nextbucket; + } +} + + + + +/* + * This routine re-initializes the hash table. It frees all the allocated + * memory and resets all bucket pointers to NULL. + */ + +void +hash_Reset(hashtable, free_data) + hash_tbl *hashtable; + hash_freefp free_data; +{ + hash_member **bucketptr; + unsigned i; + + bucketptr = hashtable->table; + for (i = 0; i < hashtable->size; i++) { + hashi_FreeMembers(*bucketptr, free_data); + *bucketptr++ = NULL; + } + hashtable->bucketnum = 0; + hashtable->member = (hashtable->table)[0]; +} + + + +/* + * Generic hash function to calculate a hash code from the given string. + * + * For each byte of the string, this function left-shifts the value in an + * accumulator and then adds the byte into the accumulator. The contents of + * the accumulator is returned after the entire string has been processed. + * It is assumed that this result will be used as the "hashcode" parameter in + * calls to other functions in this package. These functions automatically + * adjust the hashcode for the size of each hashtable. + * + * This algorithm probably works best when the hash table size is a prime + * number. + * + * Hopefully, this function is better than the previous one which returned + * the sum of the squares of all the bytes. I'm still open to other + * suggestions for a default hash function. The programmer is more than + * welcome to supply his/her own hash function as that is one of the design + * features of this package. + */ + +unsigned +hash_HashFunction(string, len) + unsigned char *string; + register unsigned len; +{ + register unsigned accum; + + accum = 0; + for (; len > 0; len--) { + accum <<= 1; + accum += (unsigned) (*string++ & 0xFF); + } + return accum; +} + + + +/* + * Returns TRUE if at least one entry for the given key exists; FALSE + * otherwise. + */ + +int +hash_Exists(hashtable, hashcode, compare, key) + hash_tbl *hashtable; + unsigned hashcode; + hash_cmpfp compare; + hash_datum *key; +{ + register hash_member *memberptr; + + memberptr = (hashtable->table)[hashcode % (hashtable->size)]; + while (memberptr) { + if ((*compare) (key, memberptr->data)) { + return TRUE; /* Entry does exist */ + } + memberptr = memberptr->next; + } + return FALSE; /* Entry does not exist */ +} + + + +/* + * Insert the data item "element" into the hash table using "hashcode" + * to determine the bucket number, and "compare" and "key" to determine + * its uniqueness. + * + * If the insertion is successful 0 is returned. If a matching entry + * already exists in the given bucket of the hash table, or some other error + * occurs, -1 is returned and the insertion is not done. + */ + +int +hash_Insert(hashtable, hashcode, compare, key, element) + hash_tbl *hashtable; + unsigned hashcode; + hash_cmpfp compare; + hash_datum *key, *element; +{ + hash_member *temp; + + hashcode %= hashtable->size; + if (hash_Exists(hashtable, hashcode, compare, key)) { + return -1; /* At least one entry already exists */ + } + temp = (hash_member *) malloc(sizeof(hash_member)); + if (!temp) + return -1; /* malloc failed! */ + + temp->data = element; + temp->next = (hashtable->table)[hashcode]; + (hashtable->table)[hashcode] = temp; + return 0; /* Success */ +} + + + +/* + * Delete all data elements which match the given key. If at least one + * element is found and the deletion is successful, 0 is returned. + * If no matching elements can be found in the hash table, -1 is returned. + */ + +int +hash_Delete(hashtable, hashcode, compare, key, free_data) + hash_tbl *hashtable; + unsigned hashcode; + hash_cmpfp compare; + hash_datum *key; + hash_freefp free_data; +{ + hash_member *memberptr, *tempptr; + hash_member *previous = NULL; + int retval; + + retval = -1; + hashcode %= hashtable->size; + + /* + * Delete the first member of the list if it matches. Since this moves + * the second member into the first position we have to keep doing this + * over and over until it no longer matches. + */ + memberptr = (hashtable->table)[hashcode]; + while (memberptr && (*compare) (key, memberptr->data)) { + (hashtable->table)[hashcode] = memberptr->next; + /* + * Stop hashi_FreeMembers() from deleting the whole list! + */ + memberptr->next = NULL; + hashi_FreeMembers(memberptr, free_data); + memberptr = (hashtable->table)[hashcode]; + retval = 0; + } + + /* + * Now traverse the rest of the list + */ + if (memberptr) { + previous = memberptr; + memberptr = memberptr->next; + } + while (memberptr) { + if ((*compare) (key, memberptr->data)) { + tempptr = memberptr; + previous->next = memberptr = memberptr->next; + /* + * Put the brakes on hashi_FreeMembers(). . . . + */ + tempptr->next = NULL; + hashi_FreeMembers(tempptr, free_data); + retval = 0; + } else { + previous = memberptr; + memberptr = memberptr->next; + } + } + return retval; +} + + + +/* + * Locate and return the data entry associated with the given key. + * + * If the data entry is found, a pointer to it is returned. Otherwise, + * NULL is returned. + */ + +hash_datum * +hash_Lookup(hashtable, hashcode, compare, key) + hash_tbl *hashtable; + unsigned hashcode; + hash_cmpfp compare; + hash_datum *key; +{ + hash_member *memberptr; + + memberptr = (hashtable->table)[hashcode % (hashtable->size)]; + while (memberptr) { + if ((*compare) (key, memberptr->data)) { + return (memberptr->data); + } + memberptr = memberptr->next; + } + return NULL; +} + + + +/* + * Return the next available entry in the hashtable for a linear search + */ + +hash_datum * +hash_NextEntry(hashtable) + hash_tbl *hashtable; +{ + register unsigned bucket; + register hash_member *memberptr; + + /* + * First try to pick up where we left off. + */ + memberptr = hashtable->member; + if (memberptr) { + hashtable->member = memberptr->next; /* Set up for next call */ + return memberptr->data; /* Return the data */ + } + /* + * We hit the end of a chain, so look through the array of buckets + * until we find a new chain (non-empty bucket) or run out of buckets. + */ + bucket = hashtable->bucketnum + 1; + while ((bucket < hashtable->size) && + !(memberptr = (hashtable->table)[bucket])) { + bucket++; + } + + /* + * Check to see if we ran out of buckets. + */ + if (bucket >= hashtable->size) { + /* + * Reset to top of table for next call. + */ + hashtable->bucketnum = 0; + hashtable->member = (hashtable->table)[0]; + /* + * But return end-of-table indication to the caller this time. + */ + return NULL; + } + /* + * Must have found a non-empty bucket. + */ + hashtable->bucketnum = bucket; + hashtable->member = memberptr->next; /* Set up for next call */ + return memberptr->data; /* Return the data */ +} + + + +/* + * Return the first entry in a hash table for a linear search + */ + +hash_datum * +hash_FirstEntry(hashtable) + hash_tbl *hashtable; +{ + hashtable->bucketnum = 0; + hashtable->member = (hashtable->table)[0]; + return hash_NextEntry(hashtable); +} + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/usr.sbin/bootpd/hash.h b/usr.sbin/bootpd/hash.h new file mode 100644 index 00000000000..51d0a5ebd33 --- /dev/null +++ b/usr.sbin/bootpd/hash.h @@ -0,0 +1,158 @@ +#ifndef HASH_H +#define HASH_H +/* hash.h */ +/************************************************************************ + Copyright 1988, 1991 by Carnegie Mellon University + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, provided +that the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of Carnegie Mellon University not be used +in advertising or publicity pertaining to distribution of the software +without specific, written prior permission. + +CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. +************************************************************************/ + +/* + * Generalized hash table ADT + * + * Provides multiple, dynamically-allocated, variable-sized hash tables on + * various data and keys. + * + * This package attempts to follow some of the coding conventions suggested + * by Bob Sidebotham and the AFS Clean Code Committee. + */ + + +/* + * The user must supply the following: + * + * 1. A comparison function which is declared as: + * + * int compare(data1, data2) + * hash_datum *data1, *data2; + * + * This function must compare the desired fields of data1 and + * data2 and return TRUE (1) if the data should be considered + * equivalent (i.e. have the same key value) or FALSE (0) + * otherwise. This function is called through a pointer passed to + * the various hashtable functions (thus pointers to different + * functions may be passed to effect different tests on different + * hash tables). + * + * Internally, all the functions of this package always call the + * compare function with the "key" parameter as the first parameter, + * and a full data element as the second parameter. Thus, the key + * and element arguments to functions such as hash_Lookup() may + * actually be of different types and the programmer may provide a + * compare function which compares the two different object types + * as desired. + * + * Example: + * + * int compare(key, element) + * char *key; + * struct some_complex_structure *element; + * { + * return !strcmp(key, element->name); + * } + * + * key = "John C. Doe" + * element = &some_complex_structure + * hash_Lookup(table, hashcode, compare, key); + * + * 2. A hash function yielding an unsigned integer value to be used + * as the hashcode (index into the hashtable). Thus, the user + * may hash on whatever data is desired and may use several + * different hash functions for various different hash tables. + * The actual hash table index will be the passed hashcode modulo + * the hash table size. + * + * A generalized hash function, hash_HashFunction(), is included + * with this package to make things a little easier. It is not + * guarenteed to use the best hash algorithm in existence. . . . + */ + + + +/* + * Various hash table definitions + */ + + +/* + * Define "hash_datum" as a universal data type + */ +#ifdef __STDC__ +typedef void hash_datum; +#else +typedef char hash_datum; +#endif + +typedef struct hash_memberstruct hash_member; +typedef struct hash_tblstruct hash_tbl; +typedef struct hash_tblstruct_hdr hash_tblhdr; + +struct hash_memberstruct { + hash_member *next; + hash_datum *data; +}; + +struct hash_tblstruct_hdr { + unsigned size, bucketnum; + hash_member *member; +}; + +struct hash_tblstruct { + unsigned size, bucketnum; + hash_member *member; /* Used for linear dump */ + hash_member *table[1]; /* Dynamically extended */ +}; + +/* ANSI function prototypes or empty arg list? */ +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +typedef int (*hash_cmpfp) P((hash_datum *, hash_datum *)); +typedef void (*hash_freefp) P((hash_datum *)); + +extern hash_tbl *hash_Init P((u_int tablesize)); + +extern void hash_Reset P((hash_tbl *tbl, hash_freefp)); + +extern unsigned hash_HashFunction P((u_char *str, u_int len)); + +extern int hash_Exists P((hash_tbl *, u_int code, + hash_cmpfp, hash_datum *key)); + +extern int hash_Insert P((hash_tbl *, u_int code, + hash_cmpfp, hash_datum *key, + hash_datum *element)); + +extern int hash_Delete P((hash_tbl *, u_int code, + hash_cmpfp, hash_datum *key, + hash_freefp)); + +extern hash_datum *hash_Lookup P((hash_tbl *, u_int code, + hash_cmpfp, hash_datum *key)); + +extern hash_datum *hash_FirstEntry P((hash_tbl *)); + +extern hash_datum *hash_NextEntry P((hash_tbl *)); + +#undef P + +#endif /* HASH_H */ diff --git a/usr.sbin/bootpd/hwaddr.c b/usr.sbin/bootpd/hwaddr.c new file mode 100644 index 00000000000..10a6a0518d8 --- /dev/null +++ b/usr.sbin/bootpd/hwaddr.c @@ -0,0 +1,286 @@ +/* + * hwaddr.c - routines that deal with hardware addresses. + * (i.e. Ethernet) + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/ioctl.h> + +#if defined(SUNOS) || defined(SVR4) +#include <sys/sockio.h> +#endif +#ifdef SVR4 +#include <sys/stream.h> +#include <stropts.h> +#include <fcntl.h> +#endif + +#include <net/if_arp.h> +#include <netinet/in.h> +#include <stdio.h> +#ifndef NO_UNISTD +#include <unistd.h> +#endif +#include <syslog.h> + +#ifndef USE_BFUNCS +/* Yes, memcpy is OK here (no overlapped copies). */ +#include <memory.h> +#define bcopy(a,b,c) memcpy(b,a,c) +#define bzero(p,l) memset(p,0,l) +#define bcmp(a,b,c) memcmp(a,b,c) +#endif + +#include "bptypes.h" +#include "hwaddr.h" +#include "report.h" + +extern int debug; + +/* + * Hardware address lengths (in bytes) and network name based on hardware + * type code. List in order specified by Assigned Numbers RFC; Array index + * is hardware type code. Entries marked as zero are unknown to the author + * at this time. . . . + */ + +struct hwinfo hwinfolist[] = +{ + {0, "Reserved"}, /* Type 0: Reserved (don't use this) */ + {6, "Ethernet"}, /* Type 1: 10Mb Ethernet (48 bits) */ + {1, "3Mb Ethernet"}, /* Type 2: 3Mb Ethernet (8 bits) */ + {0, "AX.25"}, /* Type 3: Amateur Radio AX.25 */ + {1, "ProNET"}, /* Type 4: Proteon ProNET Token Ring */ + {0, "Chaos"}, /* Type 5: Chaos */ + {6, "IEEE 802"}, /* Type 6: IEEE 802 Networks */ + {0, "ARCNET"} /* Type 7: ARCNET */ +}; +int hwinfocnt = sizeof(hwinfolist) / sizeof(hwinfolist[0]); + + +/* + * Setup the arp cache so that IP address 'ia' will be temporarily + * bound to hardware address 'ha' of length 'len'. + */ +void +setarp(s, ia, ha, len) + int s; /* socket fd */ + struct in_addr *ia; + u_char *ha; + int len; +{ +#ifdef SIOCSARP + struct arpreq arpreq; /* Arp request ioctl block */ + struct sockaddr_in *si; +#ifdef SVR4 + int fd; + struct strioctl iocb; +#endif /* SVR4 */ + + bzero((caddr_t) & arpreq, sizeof(arpreq)); + arpreq.arp_flags = ATF_INUSE | ATF_COM; + + /* Set up the protocol address. */ + arpreq.arp_pa.sa_family = AF_INET; + si = (struct sockaddr_in *) &arpreq.arp_pa; + si->sin_addr = *ia; + + /* Set up the hardware address. */ + bcopy(ha, arpreq.arp_ha.sa_data, len); + +#ifdef SVR4 + /* + * And now the stuff for System V Rel 4.x which does not + * appear to allow SIOCxxx ioctls on a socket descriptor. + * Thanks to several people: (all sent the same fix) + * Barney Wolff <barney@databus.com>, + * bear@upsys.se (Bj|rn Sj|holm), + * Michael Kuschke <Michael.Kuschke@Materna.DE>, + */ + if ((fd=open("/dev/arp", O_RDWR)) < 0) { + report(LOG_ERR, "open /dev/arp: %s\n", get_errmsg()); + } + iocb.ic_cmd = SIOCSARP; + iocb.ic_timout = 0; + iocb.ic_dp = (char *)&arpreq; + iocb.ic_len = sizeof(arpreq); + if (ioctl(fd, I_STR, (caddr_t)&iocb) < 0) { + report(LOG_ERR, "ioctl I_STR: %s\n", get_errmsg()); + } + close (fd); + +#else /* SVR4 */ + /* + * On SunOS, the ioctl sometimes returns ENXIO, and it + * appears to happen when the ARP cache entry you tried + * to add is already in the cache. (Sigh...) + * XXX - Should this error simply be ignored? -gwr + */ + if (ioctl(s, SIOCSARP, (caddr_t) & arpreq) < 0) { + report(LOG_ERR, "ioctl SIOCSARP: %s", get_errmsg()); + } +#endif /* SVR4 */ +#else /* SIOCSARP */ + /* + * Oh well, SIOCSARP is not defined. Just run arp(8). + * XXX - Gag! + */ + int status; + char buf[256]; + char *a; + extern char *inet_ntoa(); + + a = inet_ntoa(*ia); + sprintf(buf, "arp -d %s; arp -s %s %s temp", + a, a, haddrtoa(ha, len)); + if (debug > 2) + report(LOG_INFO, buf); + status = system(buf); + if (status) + report(LOG_ERR, "arp failed, exit code=0x%x", status); + return; +#endif /* SIOCSARP */ +} + + +/* + * Convert a hardware address to an ASCII string. + */ +char * +haddrtoa(haddr, hlen) + u_char *haddr; + int hlen; +{ + static char haddrbuf[3 * MAXHADDRLEN + 1]; + char *bufptr; + + if (hlen > MAXHADDRLEN) + hlen = MAXHADDRLEN; + + bufptr = haddrbuf; + while (hlen > 0) { + sprintf(bufptr, "%02X:", (unsigned) (*haddr++ & 0xFF)); + bufptr += 3; + hlen--; + } + bufptr[-1] = 0; + return (haddrbuf); +} + + +/* + * haddr_conv802() + * -------------- + * + * Converts a backwards address to a canonical address and a canonical address + * to a backwards address. + * + * INPUTS: + * adr_in - pointer to six byte string to convert (unsigned char *) + * addr_len - how many bytes to convert + * + * OUTPUTS: + * addr_out - The string is updated to contain the converted address. + * + * CALLER: + * many + * + * DATA: + * Uses conv802table to bit-reverse the address bytes. + */ + +static u_char conv802table[256] = +{ + /* 0x00 */ 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, + /* 0x08 */ 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, + /* 0x10 */ 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, + /* 0x18 */ 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, + /* 0x20 */ 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, + /* 0x28 */ 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, + /* 0x30 */ 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, + /* 0x38 */ 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, + /* 0x40 */ 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, + /* 0x48 */ 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, + /* 0x50 */ 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, + /* 0x58 */ 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, + /* 0x60 */ 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, + /* 0x68 */ 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, + /* 0x70 */ 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, + /* 0x78 */ 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, + /* 0x80 */ 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, + /* 0x88 */ 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, + /* 0x90 */ 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, + /* 0x98 */ 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, + /* 0xA0 */ 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, + /* 0xA8 */ 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, + /* 0xB0 */ 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, + /* 0xB8 */ 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, + /* 0xC0 */ 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, + /* 0xC8 */ 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, + /* 0xD0 */ 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, + /* 0xD8 */ 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, + /* 0xE0 */ 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, + /* 0xE8 */ 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, + /* 0xF0 */ 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, + /* 0xF8 */ 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF, +}; + +void +haddr_conv802(addr_in, addr_out, len) + register u_char *addr_in, *addr_out; + int len; +{ + u_char *lim; + + lim = addr_out + len; + while (addr_out < lim) + *addr_out++ = conv802table[*addr_in++]; +} + +#if 0 +/* + * For the record, here is a program to generate the + * bit-reverse table above. + */ +static int +bitrev(n) + int n; +{ + int i, r; + + r = 0; + for (i = 0; i < 8; i++) { + r <<= 1; + r |= (n & 1); + n >>= 1; + } + return r; +} + +main() +{ + int i; + for (i = 0; i <= 0xFF; i++) { + if ((i & 7) == 0) + printf("/* 0x%02X */", i); + printf(" 0x%02X,", bitrev(i)); + if ((i & 7) == 7) + printf("\n"); + } +} + +#endif + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/usr.sbin/bootpd/hwaddr.h b/usr.sbin/bootpd/hwaddr.h new file mode 100644 index 00000000000..dea7158be8c --- /dev/null +++ b/usr.sbin/bootpd/hwaddr.h @@ -0,0 +1,39 @@ +/* hwaddr.h */ +#ifndef HWADDR_H +#define HWADDR_H + +#define MAXHADDRLEN 8 /* Max hw address length in bytes */ + +/* + * This structure holds information about a specific network type. The + * length of the network hardware address is stored in "hlen". + * The string pointed to by "name" is the cononical name of the network. + */ +struct hwinfo { + unsigned int hlen; + char *name; +}; + +extern struct hwinfo hwinfolist[]; +extern int hwinfocnt; + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +extern void setarp P((int, struct in_addr *, u_char *, int)); +extern char *haddrtoa P((u_char *, int)); +extern void haddr_conv802 P((u_char *, u_char *, int)); + +#undef P + +/* + * Return the length in bytes of a hardware address of the given type. + * Return the canonical name of the network of the given type. + */ +#define haddrlength(type) ((hwinfolist[(int) (type)]).hlen) +#define netname(type) ((hwinfolist[(int) (type)]).name) + +#endif /* HWADDR_H */ diff --git a/usr.sbin/bootpd/lookup.c b/usr.sbin/bootpd/lookup.c new file mode 100644 index 00000000000..2a30a59b2c3 --- /dev/null +++ b/usr.sbin/bootpd/lookup.c @@ -0,0 +1,126 @@ +/* + * lookup.c - Lookup IP address, HW address, netmask + */ + +#include <sys/types.h> +#include <sys/socket.h> + +#include <net/if.h> +#include <netinet/in.h> + +#ifdef ETC_ETHERS +#include <netinet/if_ether.h> +extern int ether_hostton(); +#endif + +#include <netdb.h> +#include <syslog.h> + +#ifndef USE_BFUNCS +#include <memory.h> +/* Yes, memcpy is OK here (no overlapped copies). */ +#define bcopy(a,b,c) memcpy(b,a,c) +#endif + +#include "bootp.h" +#include "lookup.h" +#include "report.h" + +/* + * Lookup an Ethernet address and return it. + * Return NULL if addr not found. + */ +u_char * +lookup_hwa(hostname, htype) + char *hostname; + int htype; +{ + switch (htype) { + + /* XXX - How is this done on other systems? -gwr */ +#ifdef ETC_ETHERS + case HTYPE_ETHERNET: + case HTYPE_IEEE802: + { + static struct ether_addr ea; + /* This does a lookup in /etc/ethers */ + if (ether_hostton(hostname, &ea)) { + report(LOG_ERR, "no HW addr for host \"%s\"", + hostname); + return (u_char *) 0; + } + return (u_char *) & ea; + } +#endif /* ETC_ETHERS */ + + default: + report(LOG_ERR, "no lookup for HW addr type %d", htype); + } /* switch */ + + /* If the system can't do it, just return an error. */ + return (u_char *) 0; +} + + +/* + * Lookup an IP address. + * Return non-zero on failure. + */ +int +lookup_ipa(hostname, result) + char *hostname; + u_int32 *result; +{ + struct hostent *hp; + hp = gethostbyname(hostname); + if (!hp) + return -1; + bcopy(hp->h_addr, result, sizeof(*result)); + return 0; +} + + +/* + * Lookup a netmask + * Return non-zero on failure. + * + * XXX - This is OK as a default, but to really make this automatic, + * we would need to get the subnet mask from the ether interface. + * If this is wrong, specify the correct value in the bootptab. + */ +int +lookup_netmask(addr, result) + u_int32 addr; /* both in network order */ + u_int32 *result; +{ + int32 m, a; + + a = ntohl(addr); + m = 0; + + if (IN_CLASSA(a)) + m = IN_CLASSA_NET; + + if (IN_CLASSB(a)) + m = IN_CLASSB_NET; + + if (IN_CLASSC(a)) + m = IN_CLASSC_NET; + + if (!m) + return -1; + *result = htonl(m); + return 0; +} + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/usr.sbin/bootpd/lookup.h b/usr.sbin/bootpd/lookup.h new file mode 100644 index 00000000000..04805d8915c --- /dev/null +++ b/usr.sbin/bootpd/lookup.h @@ -0,0 +1,15 @@ +/* lookup.h */ + +#include "bptypes.h" /* for int32, u_int32 */ + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +extern u_char *lookup_hwa P((char *hostname, int htype)); +extern int lookup_ipa P((char *hostname, u_int32 *addr)); +extern int lookup_netmask P((u_int32 addr, u_int32 *mask)); + +#undef P diff --git a/usr.sbin/bootpd/patchlevel.h b/usr.sbin/bootpd/patchlevel.h new file mode 100644 index 00000000000..782959e3c75 --- /dev/null +++ b/usr.sbin/bootpd/patchlevel.h @@ -0,0 +1,3 @@ +/* patchlevel.h */ +#define VERSION "2.4" +#define PATCHLEVEL 1 diff --git a/usr.sbin/bootpd/print-bootp.c b/usr.sbin/bootpd/print-bootp.c new file mode 100644 index 00000000000..3e0b6cb5635 --- /dev/null +++ b/usr.sbin/bootpd/print-bootp.c @@ -0,0 +1,493 @@ +/* + * Copyright (c) 1988-1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Format and print bootp packets. + * + * This file was copied from tcpdump-2.1.1 and modified. + * There is an e-mail list for tcpdump: <tcpdump@ee.lbl.gov> + */ +#ifndef lint +static char rcsid[] = "$Id: print-bootp.c,v 1.1 1995/10/18 08:47:27 deraadt Exp $"; +/* 93/10/10 <gwr@mc.com> New data-driven option print routine. */ +#endif + +#include <stdio.h> + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <net/if.h> +#include <netinet/in.h> +#include <string.h> +#include <ctype.h> + +#include "bootp.h" +#include "bootptest.h" + +/* These decode the vendor data. */ +static void rfc1048_print(); +static void cmu_print(); +static void other_print(); +static void dump_hex(); + +/* + * Print bootp requests + */ +void +bootp_print(bp, length, sport, dport) + struct bootp *bp; + int length; + u_short sport, dport; +{ + static char tstr[] = " [|bootp]"; + static unsigned char vm_cmu[4] = VM_CMU; + static unsigned char vm_rfc1048[4] = VM_RFC1048; + u_char *ep; + int vdlen; + +#define TCHECK(var, l) if ((u_char *)&(var) > ep - l) goto trunc + + /* Note funny sized packets */ + if (length != sizeof(struct bootp)) + (void) printf(" [len=%d]", length); + + /* 'ep' points to the end of avaible data. */ + ep = (u_char *) snapend; + + switch (bp->bp_op) { + + case BOOTREQUEST: + /* Usually, a request goes from a client to a server */ + if (sport != IPPORT_BOOTPC || dport != IPPORT_BOOTPS) + printf(" (request)"); + break; + + case BOOTREPLY: + /* Usually, a reply goes from a server to a client */ + if (sport != IPPORT_BOOTPS || dport != IPPORT_BOOTPC) + printf(" (reply)"); + break; + + default: + printf(" bootp-#%d", bp->bp_op); + } + + /* The usual hardware address type is 1 (10Mb Ethernet) */ + if (bp->bp_htype != 1) + printf(" htype:%d", bp->bp_htype); + + /* The usual length for 10Mb Ethernet address is 6 bytes */ + if (bp->bp_hlen != 6) + printf(" hlen:%d", bp->bp_hlen); + + /* Client's Hardware address */ + if (bp->bp_hlen) { + register struct ether_header *eh; + register char *e; + + TCHECK(bp->bp_chaddr[0], 6); + eh = (struct ether_header *) packetp; + if (bp->bp_op == BOOTREQUEST) + e = (char *) ESRC(eh); + else if (bp->bp_op == BOOTREPLY) + e = (char *) EDST(eh); + else + e = 0; + if (e == 0 || bcmp((char *) bp->bp_chaddr, e, 6)) + dump_hex(bp->bp_chaddr, bp->bp_hlen); + } + /* Only print interesting fields */ + if (bp->bp_hops) + printf(" hops:%d", bp->bp_hops); + + if (bp->bp_xid) + printf(" xid:%d", ntohl(bp->bp_xid)); + + if (bp->bp_secs) + printf(" secs:%d", ntohs(bp->bp_secs)); + + /* Client's ip address */ + TCHECK(bp->bp_ciaddr, sizeof(bp->bp_ciaddr)); + if (bp->bp_ciaddr.s_addr) + printf(" C:%s", ipaddr_string(&bp->bp_ciaddr)); + + /* 'your' ip address (bootp client) */ + TCHECK(bp->bp_yiaddr, sizeof(bp->bp_yiaddr)); + if (bp->bp_yiaddr.s_addr) + printf(" Y:%s", ipaddr_string(&bp->bp_yiaddr)); + + /* Server's ip address */ + TCHECK(bp->bp_siaddr, sizeof(bp->bp_siaddr)); + if (bp->bp_siaddr.s_addr) + printf(" S:%s", ipaddr_string(&bp->bp_siaddr)); + + /* Gateway's ip address */ + TCHECK(bp->bp_giaddr, sizeof(bp->bp_giaddr)); + if (bp->bp_giaddr.s_addr) + printf(" G:%s", ipaddr_string(&bp->bp_giaddr)); + + TCHECK(bp->bp_sname[0], sizeof(bp->bp_sname)); + if (*bp->bp_sname) { + printf(" sname:"); + if (printfn(bp->bp_sname, ep)) { + fputs(tstr + 1, stdout); + return; + } + } + TCHECK(bp->bp_file[0], sizeof(bp->bp_file)); + if (*bp->bp_file) { + printf(" file:"); + if (printfn(bp->bp_file, ep)) { + fputs(tstr + 1, stdout); + return; + } + } + /* Don't try to decode the vendor buffer unless we're verbose */ + if (vflag <= 0) + return; + + vdlen = sizeof(bp->bp_vend); + /* Vendor data can extend to the end of the packet. */ + if (vdlen < (ep - bp->bp_vend)) + vdlen = (ep - bp->bp_vend); + + TCHECK(bp->bp_vend[0], vdlen); + printf(" vend"); + if (!bcmp(bp->bp_vend, vm_rfc1048, sizeof(u_int32))) + rfc1048_print(bp->bp_vend, vdlen); + else if (!bcmp(bp->bp_vend, vm_cmu, sizeof(u_int32))) + cmu_print(bp->bp_vend, vdlen); + else + other_print(bp->bp_vend, vdlen); + + return; + trunc: + fputs(tstr, stdout); +#undef TCHECK +} + +/* + * Option description data follows. + * These are decribed in: RFC-1048, RFC-1395, RFC-1497, RFC-1533 + * + * The first char of each option string encodes the data format: + * ?: unknown + * a: ASCII + * b: byte (8-bit) + * i: inet address + * l: int32 + * s: short (16-bit) + */ +char * +rfc1048_opts[] = { + /* Originally from RFC-1048: */ + "?PAD", /* 0: Padding - special, no data. */ + "iSM", /* 1: subnet mask (RFC950)*/ + "lTZ", /* 2: time offset, seconds from UTC */ + "iGW", /* 3: gateways (or routers) */ + "iTS", /* 4: time servers (RFC868) */ + "iINS", /* 5: IEN name servers (IEN116) */ + "iDNS", /* 6: domain name servers (RFC1035)(1034?) */ + "iLOG", /* 7: MIT log servers */ + "iCS", /* 8: cookie servers (RFC865) */ + "iLPR", /* 9: lpr server (RFC1179) */ + "iIPS", /* 10: impress servers (Imagen) */ + "iRLP", /* 11: resource location servers (RFC887) */ + "aHN", /* 12: host name (ASCII) */ + "sBFS", /* 13: boot file size (in 512 byte blocks) */ + + /* Added by RFC-1395: */ + "aDUMP", /* 14: Merit Dump File */ + "aDNAM", /* 15: Domain Name (for DNS) */ + "iSWAP", /* 16: Swap Server */ + "aROOT", /* 17: Root Path */ + + /* Added by RFC-1497: */ + "aEXTF", /* 18: Extensions Path (more options) */ + + /* Added by RFC-1533: (many, many options...) */ +#if 1 /* These might not be worth recognizing by name. */ + + /* IP Layer Parameters, per-host (RFC-1533, sect. 4) */ + "bIP-forward", /* 19: IP Forwarding flag */ + "bIP-srcroute", /* 20: IP Source Routing Enable flag */ + "iIP-filters", /* 21: IP Policy Filter (addr pairs) */ + "sIP-maxudp", /* 22: IP Max-UDP reassembly size */ + "bIP-ttlive", /* 23: IP Time to Live */ + "lIP-pmtuage", /* 24: IP Path MTU aging timeout */ + "sIP-pmtutab", /* 25: IP Path MTU plateau table */ + + /* IP parameters, per-interface (RFC-1533, sect. 5) */ + "sIP-mtu-sz", /* 26: IP MTU size */ + "bIP-mtu-sl", /* 27: IP MTU all subnets local */ + "bIP-bcast1", /* 28: IP Broadcast Addr ones flag */ + "bIP-mask-d", /* 29: IP do mask discovery */ + "bIP-mask-s", /* 30: IP do mask supplier */ + "bIP-rt-dsc", /* 31: IP do router discovery */ + "iIP-rt-sa", /* 32: IP router solicitation addr */ + "iIP-routes", /* 33: IP static routes (dst,router) */ + + /* Link Layer parameters, per-interface (RFC-1533, sect. 6) */ + "bLL-trailer", /* 34: do tralier encapsulation */ + "lLL-arp-tmo", /* 35: ARP cache timeout */ + "bLL-ether2", /* 36: Ethernet version 2 (IEEE 802.3) */ + + /* TCP parameters (RFC-1533, sect. 7) */ + "bTCP-def-ttl", /* 37: default time to live */ + "lTCP-KA-tmo", /* 38: keepalive time interval */ + "bTCP-KA-junk", /* 39: keepalive sends extra junk */ + + /* Application and Service Parameters (RFC-1533, sect. 8) */ + "aNISDOM", /* 40: NIS Domain (Sun YP) */ + "iNISSRV", /* 41: NIS Servers */ + "iNTPSRV", /* 42: NTP (time) Servers (RFC 1129) */ + "?VSINFO", /* 43: Vendor Specific Info (encapsulated) */ + "iNBiosNS", /* 44: NetBIOS Name Server (RFC-1001,1..2) */ + "iNBiosDD", /* 45: NetBIOS Datagram Dist. Server. */ + "bNBiosNT", /* 46: NetBIOS Note Type */ + "?NBiosS", /* 47: NetBIOS Scope */ + "iXW-FS", /* 48: X Window System Font Servers */ + "iXW-DM", /* 49: X Window System Display Managers */ + + /* DHCP extensions (RFC-1533, sect. 9) */ +#endif +}; +#define KNOWN_OPTIONS (sizeof(rfc1048_opts) / sizeof(rfc1048_opts[0])) + +static void print_string(); + +static void +rfc1048_print(bp, length) + register u_char *bp; + int length; +{ + u_char tag; + u_char *ep; + register int len, j; + u_int32 ul; + u_short us; + struct in_addr ia; + char *optstr; + + printf("-rfc1395"); + + /* Step over magic cookie */ + bp += sizeof(int32); + /* Setup end pointer */ + ep = bp + length; + while (bp < ep) { + tag = *bp++; + /* Check for tags with no data first. */ + if (tag == TAG_PAD) + continue; + if (tag == TAG_END) + return; + if (tag < KNOWN_OPTIONS) { + optstr = rfc1048_opts[tag]; + printf(" %s:", optstr + 1); + } else { + printf(" T%d:", tag); + optstr = "?"; + } + /* Now scan the length byte. */ + len = *bp++; + if (bp + len > ep) { + /* truncated option */ + printf(" |(%d>%d)", len, ep - bp); + return; + } + /* Print the option value(s). */ + switch (optstr[0]) { + + case 'a': /* ASCII string */ + printfn(bp, bp + len); + bp += len; + len = 0; + break; + + case 's': /* Word formats */ + while (len >= 2) { + bcopy((char *) bp, (char *) &us, 2); + printf("%d", ntohs(us)); + bp += 2; + len -= 2; + if (len) printf(","); + } + if (len) printf("(junk=%d)", len); + break; + + case 'l': /* Long words */ + while (len >= 4) { + bcopy((char *) bp, (char *) &ul, 4); + printf("%d", ntohl(ul)); + bp += 4; + len -= 4; + if (len) printf(","); + } + if (len) printf("(junk=%d)", len); + break; + + case 'i': /* INET addresses */ + while (len >= 4) { + bcopy((char *) bp, (char *) &ia, 4); + printf("%s", ipaddr_string(&ia)); + bp += 4; + len -= 4; + if (len) printf(","); + } + if (len) printf("(junk=%d)", len); + break; + + case 'b': + default: + break; + + } /* switch */ + + /* Print as characters, if appropriate. */ + if (len) { + dump_hex(bp, len); + if (isascii(*bp) && isprint(*bp)) { + printf("("); + printfn(bp, bp + len); + printf(")"); + } + bp += len; + len = 0; + } + } /* while bp < ep */ +} + +static void +cmu_print(bp, length) + register u_char *bp; + int length; +{ + struct cmu_vend *v; + u_char *ep; + + printf("-cmu"); + + v = (struct cmu_vend *) bp; + if (length < sizeof(*v)) { + printf(" |L=%d", length); + return; + } + /* Setup end pointer */ + ep = bp + length; + + /* Subnet mask */ + if (v->v_flags & VF_SMASK) { + printf(" SM:%s", ipaddr_string(&v->v_smask)); + } + /* Default gateway */ + if (v->v_dgate.s_addr) + printf(" GW:%s", ipaddr_string(&v->v_dgate)); + + /* Domain name servers */ + if (v->v_dns1.s_addr) + printf(" DNS1:%s", ipaddr_string(&v->v_dns1)); + if (v->v_dns2.s_addr) + printf(" DNS2:%s", ipaddr_string(&v->v_dns2)); + + /* IEN-116 name servers */ + if (v->v_ins1.s_addr) + printf(" INS1:%s", ipaddr_string(&v->v_ins1)); + if (v->v_ins2.s_addr) + printf(" INS2:%s", ipaddr_string(&v->v_ins2)); + + /* Time servers */ + if (v->v_ts1.s_addr) + printf(" TS1:%s", ipaddr_string(&v->v_ts1)); + if (v->v_ts2.s_addr) + printf(" TS2:%s", ipaddr_string(&v->v_ts2)); + +} + + +/* + * Print out arbitrary, unknown vendor data. + */ + +static void +other_print(bp, length) + register u_char *bp; + int length; +{ + u_char *ep; /* end pointer */ + u_char *zp; /* points one past last non-zero byte */ + register int i, j; + + /* Setup end pointer */ + ep = bp + length; + + /* Find the last non-zero byte. */ + for (zp = ep; zp > bp; zp--) { + if (zp[-1] != 0) + break; + } + + /* Print the all-zero case in a compact representation. */ + if (zp == bp) { + printf("-all-zero"); + return; + } + printf("-unknown"); + + /* Are there enough trailing zeros to make "00..." worthwhile? */ + if (zp + 2 > ep) + zp = ep; /* print them all normally */ + + /* Now just print all the non-zero data. */ + while (bp < zp) { + printf(".%02X", *bp); + bp++; + } + + if (zp < ep) + printf(".00..."); + + return; +} + +static void +dump_hex(bp, len) + u_char *bp; + int len; +{ + while (len > 0) { + printf("%02X", *bp); + bp++; + len--; + if (len) printf("."); + } +} + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/usr.sbin/bootpd/readfile.c b/usr.sbin/bootpd/readfile.c new file mode 100644 index 00000000000..41b4a5c0e13 --- /dev/null +++ b/usr.sbin/bootpd/readfile.c @@ -0,0 +1,2097 @@ +/************************************************************************ + Copyright 1988, 1991 by Carnegie Mellon University + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, provided +that the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of Carnegie Mellon University not be used +in advertising or publicity pertaining to distribution of the software +without specific, written prior permission. + +CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. +************************************************************************/ + +#ifndef lint +static char rcsid[] = "$Id: readfile.c,v 1.1 1995/10/18 08:47:27 deraadt Exp $"; +#endif + + +/* + * bootpd configuration file reading code. + * + * The routines in this file deal with reading, interpreting, and storing + * the information found in the bootpd configuration file (usually + * /etc/bootptab). + */ + + +#include <sys/errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/file.h> +#include <sys/time.h> +#include <netinet/in.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <assert.h> +#include <syslog.h> + +#ifndef USE_BFUNCS +#include <memory.h> +/* Yes, memcpy is OK here (no overlapped copies). */ +#define bcopy(a,b,c) memcpy(b,a,c) +#define bzero(p,l) memset(p,0,l) +#define bcmp(a,b,c) memcmp(a,b,c) +#endif + +#include "bootp.h" +#include "hash.h" +#include "hwaddr.h" +#include "lookup.h" +#include "readfile.h" +#include "report.h" +#include "tzone.h" +#include "bootpd.h" + +#define HASHTABLESIZE 257 /* Hash table size (prime) */ + +/* Non-standard hardware address type (see bootp.h) */ +#define HTYPE_DIRECT 0 + +/* Error codes returned by eval_symbol: */ +#define SUCCESS 0 +#define E_END_OF_ENTRY (-1) +#define E_SYNTAX_ERROR (-2) +#define E_UNKNOWN_SYMBOL (-3) +#define E_BAD_IPADDR (-4) +#define E_BAD_HWADDR (-5) +#define E_BAD_LONGWORD (-6) +#define E_BAD_HWATYPE (-7) +#define E_BAD_PATHNAME (-8) +#define E_BAD_VALUE (-9) + +/* Tag idendities. */ +#define SYM_NULL 0 +#define SYM_BOOTFILE 1 +#define SYM_COOKIE_SERVER 2 +#define SYM_DOMAIN_SERVER 3 +#define SYM_GATEWAY 4 +#define SYM_HWADDR 5 +#define SYM_HOMEDIR 6 +#define SYM_HTYPE 7 +#define SYM_IMPRESS_SERVER 8 +#define SYM_IPADDR 9 +#define SYM_LOG_SERVER 10 +#define SYM_LPR_SERVER 11 +#define SYM_NAME_SERVER 12 +#define SYM_RLP_SERVER 13 +#define SYM_SUBNET_MASK 14 +#define SYM_TIME_OFFSET 15 +#define SYM_TIME_SERVER 16 +#define SYM_VENDOR_MAGIC 17 +#define SYM_SIMILAR_ENTRY 18 +#define SYM_NAME_SWITCH 19 +#define SYM_BOOTSIZE 20 +#define SYM_BOOT_SERVER 22 +#define SYM_TFTPDIR 23 +#define SYM_DUMP_FILE 24 +#define SYM_DOMAIN_NAME 25 +#define SYM_SWAP_SERVER 26 +#define SYM_ROOT_PATH 27 +#define SYM_EXTEN_FILE 28 +#define SYM_REPLY_ADDR 29 +#define SYM_NIS_DOMAIN 30 /* RFC 1533 */ +#define SYM_NIS_SERVER 31 /* RFC 1533 */ +#define SYM_NTP_SERVER 32 /* RFC 1533 */ +#define SYM_EXEC_FILE 33 /* YORK_EX_OPTION */ +#define SYM_MSG_SIZE 34 +#define SYM_MIN_WAIT 35 +/* XXX - Add new tags here */ + +#define OP_ADDITION 1 /* Operations on tags */ +#define OP_DELETION 2 +#define OP_BOOLEAN 3 + +#define MAXINADDRS 16 /* Max size of an IP address list */ +#define MAXBUFLEN 256 /* Max temp buffer space */ +#define MAXENTRYLEN 2048 /* Max size of an entire entry */ + + + +/* + * Structure used to map a configuration-file symbol (such as "ds") to a + * unique integer. + */ + +struct symbolmap { + char *symbol; + int symbolcode; +}; + + +struct htypename { + char *name; + byte htype; +}; + + +PRIVATE int nhosts; /* Number of hosts (/w hw or IP address) */ +PRIVATE int nentries; /* Total number of entries */ +PRIVATE int32 modtime = 0; /* Last modification time of bootptab */ +PRIVATE char *current_hostname; /* Name of the current entry. */ +PRIVATE char current_tagname[8]; + +/* + * List of symbolic names used in the bootptab file. The order and actual + * values of the symbol codes (SYM_. . .) are unimportant, but they must + * all be unique. + */ + +PRIVATE struct symbolmap symbol_list[] = { + {"bf", SYM_BOOTFILE}, + {"bs", SYM_BOOTSIZE}, + {"cs", SYM_COOKIE_SERVER}, + {"df", SYM_DUMP_FILE}, + {"dn", SYM_DOMAIN_NAME}, + {"ds", SYM_DOMAIN_SERVER}, + {"ef", SYM_EXTEN_FILE}, + {"ex", SYM_EXEC_FILE}, /* YORK_EX_OPTION */ + {"gw", SYM_GATEWAY}, + {"ha", SYM_HWADDR}, + {"hd", SYM_HOMEDIR}, + {"hn", SYM_NAME_SWITCH}, + {"ht", SYM_HTYPE}, + {"im", SYM_IMPRESS_SERVER}, + {"ip", SYM_IPADDR}, + {"lg", SYM_LOG_SERVER}, + {"lp", SYM_LPR_SERVER}, + {"ms", SYM_MSG_SIZE}, + {"mw", SYM_MIN_WAIT}, + {"ns", SYM_NAME_SERVER}, + {"nt", SYM_NTP_SERVER}, + {"ra", SYM_REPLY_ADDR}, + {"rl", SYM_RLP_SERVER}, + {"rp", SYM_ROOT_PATH}, + {"sa", SYM_BOOT_SERVER}, + {"sm", SYM_SUBNET_MASK}, + {"sw", SYM_SWAP_SERVER}, + {"tc", SYM_SIMILAR_ENTRY}, + {"td", SYM_TFTPDIR}, + {"to", SYM_TIME_OFFSET}, + {"ts", SYM_TIME_SERVER}, + {"vm", SYM_VENDOR_MAGIC}, + {"yd", SYM_NIS_DOMAIN}, + {"ys", SYM_NIS_SERVER}, + /* XXX - Add new tags here */ +}; + + +/* + * List of symbolic names for hardware types. Name translates into + * hardware type code listed with it. Names must begin with a letter + * and must be all lowercase. This is searched linearly, so put + * commonly-used entries near the beginning. + */ + +PRIVATE struct htypename htnamemap[] = { + {"ethernet", HTYPE_ETHERNET}, + {"ethernet3", HTYPE_EXP_ETHERNET}, + {"ether", HTYPE_ETHERNET}, + {"ether3", HTYPE_EXP_ETHERNET}, + {"ieee802", HTYPE_IEEE802}, + {"tr", HTYPE_IEEE802}, + {"token-ring", HTYPE_IEEE802}, + {"pronet", HTYPE_PRONET}, + {"chaos", HTYPE_CHAOS}, + {"arcnet", HTYPE_ARCNET}, + {"ax.25", HTYPE_AX25}, + {"direct", HTYPE_DIRECT}, + {"serial", HTYPE_DIRECT}, + {"slip", HTYPE_DIRECT}, + {"ppp", HTYPE_DIRECT} +}; + + + +/* + * Externals and forward declarations. + */ + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +extern boolean iplookcmp(); +boolean nmcmp P((hash_datum *, hash_datum *)); + +PRIVATE void + adjust P((char **)); +PRIVATE void + del_string P((struct shared_string *)); +PRIVATE void + del_bindata P((struct shared_bindata *)); +PRIVATE void + del_iplist P((struct in_addr_list *)); +PRIVATE void + eat_whitespace P((char **)); +PRIVATE int + eval_symbol P((char **, struct host *)); +PRIVATE void + fill_defaults P((struct host *, char **)); +PRIVATE void + free_host P((hash_datum *)); +PRIVATE struct in_addr_list * + get_addresses P((char **)); +PRIVATE struct shared_string * + get_shared_string P((char **)); +PRIVATE char * + get_string P((char **, char *, u_int *)); +PRIVATE u_int32 + get_u_long P((char **)); +PRIVATE boolean + goodname P((char *)); +PRIVATE boolean + hwinscmp P((hash_datum *, hash_datum *)); +PRIVATE int + interp_byte P((char **, byte *)); +PRIVATE void + makelower P((char *)); +PRIVATE boolean + nullcmp P((hash_datum *, hash_datum *)); +PRIVATE int + process_entry P((struct host *, char *)); +PRIVATE int + process_generic P((char **, struct shared_bindata **, u_int)); +PRIVATE byte * + prs_haddr P((char **, u_int)); +PRIVATE int + prs_inetaddr P((char **, u_int32 *)); +PRIVATE void + read_entry P((FILE *, char *, u_int *)); +PRIVATE char * + smalloc P((u_int)); + +#undef P + + +/* + * Vendor magic cookies for CMU and RFC1048 + */ +u_char vm_cmu[4] = VM_CMU; +u_char vm_rfc1048[4] = VM_RFC1048; + +/* + * Main hash tables + */ +hash_tbl *hwhashtable; +hash_tbl *iphashtable; +hash_tbl *nmhashtable; + +/* + * Allocate hash tables for hardware address, ip address, and hostname + * (shared by bootpd and bootpef) + */ +void +rdtab_init() +{ + hwhashtable = hash_Init(HASHTABLESIZE); + iphashtable = hash_Init(HASHTABLESIZE); + nmhashtable = hash_Init(HASHTABLESIZE); + if (!(hwhashtable && iphashtable && nmhashtable)) { + report(LOG_ERR, "Unable to allocate hash tables."); + exit(1); + } +} + + +/* + * Read bootptab database file. Avoid rereading the file if the + * write date hasn't changed since the last time we read it. + */ + +void +readtab(force) + int force; +{ + struct host *hp; + FILE *fp; + struct stat st; + unsigned hashcode, buflen; + static char buffer[MAXENTRYLEN]; + + /* + * Check the last modification time. + */ + if (stat(bootptab, &st) < 0) { + report(LOG_ERR, "stat on \"%s\": %s", + bootptab, get_errmsg()); + return; + } +#ifdef DEBUG + if (debug > 3) { + char timestr[28]; + strcpy(timestr, ctime(&(st.st_mtime))); + /* zap the newline */ + timestr[24] = '\0'; + report(LOG_INFO, "bootptab mtime: %s", + timestr); + } +#endif + if ((force == 0) && + (st.st_mtime == modtime) && + st.st_nlink) { + /* + * hasn't been modified or deleted yet. + */ + return; + } + if (debug) + report(LOG_INFO, "reading %s\"%s\"", + (modtime != 0L) ? "new " : "", + bootptab); + + /* + * Open bootptab file. + */ + if ((fp = fopen(bootptab, "r")) == NULL) { + report(LOG_ERR, "error opening \"%s\": %s", bootptab, get_errmsg()); + return; + } + /* + * Record file modification time. + */ + if (fstat(fileno(fp), &st) < 0) { + report(LOG_ERR, "fstat: %s", get_errmsg()); + fclose(fp); + return; + } + modtime = st.st_mtime; + + /* + * Entirely erase all hash tables. + */ + hash_Reset(hwhashtable, free_host); + hash_Reset(iphashtable, free_host); + hash_Reset(nmhashtable, free_host); + + nhosts = 0; + nentries = 0; + while (TRUE) { + buflen = sizeof(buffer); + read_entry(fp, buffer, &buflen); + if (buflen == 0) { /* More entries? */ + break; + } + hp = (struct host *) smalloc(sizeof(struct host)); + bzero((char *) hp, sizeof(*hp)); + /* the link count it zero */ + + /* + * Get individual info + */ + if (process_entry(hp, buffer) < 0) { + hp->linkcount = 1; + free_host((hash_datum *) hp); + continue; + } + /* + * If this is not a dummy entry, and the IP or HW + * address is not yet set, try to get them here. + * Dummy entries have . as first char of name. + */ + if (goodname(hp->hostname->string)) { + char *hn = hp->hostname->string; + u_int32 value; + if (hp->flags.iaddr == 0) { + if (lookup_ipa(hn, &value)) { + report(LOG_ERR, "can not get IP addr for %s", hn); + report(LOG_ERR, "(dummy names should start with '.')"); + } else { + hp->iaddr.s_addr = value; + hp->flags.iaddr = TRUE; + } + } + /* Set default subnet mask. */ + if (hp->flags.subnet_mask == 0) { + if (lookup_netmask(hp->iaddr.s_addr, &value)) { + report(LOG_ERR, "can not get netmask for %s", hn); + } else { + hp->subnet_mask.s_addr = value; + hp->flags.subnet_mask = TRUE; + } + } + } + if (hp->flags.iaddr) { + nhosts++; + } + /* Register by HW addr if known. */ + if (hp->flags.htype && hp->flags.haddr) { + /* We will either insert it or free it. */ + hp->linkcount++; + hashcode = hash_HashFunction(hp->haddr, haddrlength(hp->htype)); + if (hash_Insert(hwhashtable, hashcode, hwinscmp, hp, hp) < 0) { + report(LOG_NOTICE, "duplicate %s address: %s", + netname(hp->htype), + haddrtoa(hp->haddr, hp->htype)); + free_host((hash_datum *) hp); + continue; + } + } + /* Register by IP addr if known. */ + if (hp->flags.iaddr) { + hashcode = hash_HashFunction((u_char *) & (hp->iaddr.s_addr), 4); + if (hash_Insert(iphashtable, hashcode, nullcmp, hp, hp) < 0) { + report(LOG_ERR, + "hash_Insert() failed on IP address insertion"); + } else { + /* Just inserted the host struct in a new hash list. */ + hp->linkcount++; + } + } + /* Register by Name (always known) */ + hashcode = hash_HashFunction((u_char *) hp->hostname->string, + strlen(hp->hostname->string)); + if (hash_Insert(nmhashtable, hashcode, nullcmp, + hp->hostname->string, hp) < 0) { + report(LOG_ERR, + "hash_Insert() failed on insertion of hostname: \"%s\"", + hp->hostname->string); + } else { + /* Just inserted the host struct in a new hash list. */ + hp->linkcount++; + } + + nentries++; + } + + fclose(fp); + if (debug) + report(LOG_INFO, "read %d entries (%d hosts) from \"%s\"", + nentries, nhosts, bootptab); + return; +} + + + +/* + * Read an entire host entry from the file pointed to by "fp" and insert it + * into the memory pointed to by "buffer". Leading whitespace and comments + * starting with "#" are ignored (removed). Backslashes (\) always quote + * the next character except that newlines preceeded by a backslash cause + * line-continuation onto the next line. The entry is terminated by a + * newline character which is not preceeded by a backslash. Sequences + * surrounded by double quotes are taken literally (including newlines, but + * not backslashes). + * + * The "bufsiz" parameter points to an unsigned int which specifies the + * maximum permitted buffer size. Upon return, this value will be replaced + * with the actual length of the entry (not including the null terminator). + * + * This code is a little scary. . . . I don't like using gotos in C + * either, but I first wrote this as an FSM diagram and gotos seemed like + * the easiest way to implement it. Maybe later I'll clean it up. + */ + +PRIVATE void +read_entry(fp, buffer, bufsiz) + FILE *fp; + char *buffer; + unsigned *bufsiz; +{ + int c, length; + + length = 0; + + /* + * Eat whitespace, blank lines, and comment lines. + */ + top: + c = fgetc(fp); + if (c < 0) { + goto done; /* Exit if end-of-file */ + } + if (isspace(c)) { + goto top; /* Skip over whitespace */ + } + if (c == '#') { + while (TRUE) { /* Eat comments after # */ + c = fgetc(fp); + if (c < 0) { + goto done; /* Exit if end-of-file */ + } + if (c == '\n') { + goto top; /* Try to read the next line */ + } + } + } + ungetc(c, fp); /* Other character, push it back to reprocess it */ + + + /* + * Now we're actually reading a data entry. Get each character and + * assemble it into the data buffer, processing special characters like + * double quotes (") and backslashes (\). + */ + + mainloop: + c = fgetc(fp); + switch (c) { + case EOF: + case '\n': + goto done; /* Exit on EOF or newline */ + case '\\': + c = fgetc(fp); /* Backslash, read a new character */ + if (c < 0) { + goto done; /* Exit on EOF */ + } + *buffer++ = c; /* Store the literal character */ + length++; + if (length < *bufsiz - 1) { + goto mainloop; + } else { + goto done; + } + case '"': + *buffer++ = '"'; /* Store double-quote */ + length++; + if (length >= *bufsiz - 1) { + goto done; + } + while (TRUE) { /* Special quote processing loop */ + c = fgetc(fp); + switch (c) { + case EOF: + goto done; /* Exit on EOF . . . */ + case '"': + *buffer++ = '"';/* Store matching quote */ + length++; + if (length < *bufsiz - 1) { + goto mainloop; /* And continue main loop */ + } else { + goto done; + } + case '\\': + if ((c = fgetc(fp)) < 0) { /* Backslash */ + goto done; /* EOF. . . .*/ + } /* else fall through */ + default: + *buffer++ = c; /* Other character, store it */ + length++; + if (length >= *bufsiz - 1) { + goto done; + } + } + } + case ':': + *buffer++ = c; /* Store colons */ + length++; + if (length >= *bufsiz - 1) { + goto done; + } + do { /* But remove whitespace after them */ + c = fgetc(fp); + if ((c < 0) || (c == '\n')) { + goto done; + } + } while (isspace(c)); /* Skip whitespace */ + + if (c == '\\') { /* Backslash quotes next character */ + c = fgetc(fp); + if (c < 0) { + goto done; + } + if (c == '\n') { + goto top; /* Backslash-newline continuation */ + } + } + /* fall through if "other" character */ + default: + *buffer++ = c; /* Store other characters */ + length++; + if (length >= *bufsiz - 1) { + goto done; + } + } + goto mainloop; /* Keep going */ + + done: + *buffer = '\0'; /* Terminate string */ + *bufsiz = length; /* Tell the caller its length */ +} + + + +/* + * Parse out all the various tags and parameters in the host entry pointed + * to by "src". Stuff all the data into the appropriate fields of the + * host structure pointed to by "host". If there is any problem with the + * entry, an error message is reported via report(), no further processing + * is done, and -1 is returned. Successful calls return 0. + * + * (Some errors probably shouldn't be so completely fatal. . . .) + */ + +PRIVATE int +process_entry(host, src) + struct host *host; + char *src; +{ + int retval; + char *msg; + + if (!host || *src == '\0') { + return -1; + } + host->hostname = get_shared_string(&src); +#if 0 + /* Be more liberal for the benefit of dummy tag names. */ + if (!goodname(host->hostname->string)) { + report(LOG_ERR, "bad hostname: \"%s\"", host->hostname->string); + del_string(host->hostname); + return -1; + } +#endif + current_hostname = host->hostname->string; + adjust(&src); + while (TRUE) { + retval = eval_symbol(&src, host); + if (retval == SUCCESS) { + adjust(&src); + continue; + } + if (retval == E_END_OF_ENTRY) { + /* The default subnet mask is set in readtab() */ + return 0; + } + /* Some kind of error. */ + switch (retval) { + case E_SYNTAX_ERROR: + msg = "bad syntax"; + break; + case E_UNKNOWN_SYMBOL: + msg = "unknown symbol"; + break; + case E_BAD_IPADDR: + msg = "bad INET address"; + break; + case E_BAD_HWADDR: + msg = "bad hardware address"; + break; + case E_BAD_LONGWORD: + msg = "bad longword value"; + break; + case E_BAD_HWATYPE: + msg = "bad HW address type"; + break; + case E_BAD_PATHNAME: + msg = "bad pathname (need leading '/')"; + case E_BAD_VALUE: + msg = "bad value"; + default: + msg = "unkown error"; + break; + } /* switch */ + report(LOG_ERR, "in entry named \"%s\", symbol \"%s\": %s", + current_hostname, current_tagname, msg); + return -1; + } +} + + +/* + * Macros for use in the function below: + */ + +/* Parse one INET address stored directly in MEMBER. */ +#define PARSE_IA1(MEMBER) do \ +{ \ + if (optype == OP_BOOLEAN) \ + return E_SYNTAX_ERROR; \ + hp->flags.MEMBER = FALSE; \ + if (optype == OP_ADDITION) { \ + if (prs_inetaddr(symbol, &value) < 0) \ + return E_BAD_IPADDR; \ + hp->MEMBER.s_addr = value; \ + hp->flags.MEMBER = TRUE; \ + } \ +} while (0) + +/* Parse a list of INET addresses pointed to by MEMBER */ +#define PARSE_IAL(MEMBER) do \ +{ \ + if (optype == OP_BOOLEAN) \ + return E_SYNTAX_ERROR; \ + if (hp->flags.MEMBER) { \ + hp->flags.MEMBER = FALSE; \ + assert(hp->MEMBER); \ + del_iplist(hp->MEMBER); \ + hp->MEMBER = NULL; \ + } \ + if (optype == OP_ADDITION) { \ + hp->MEMBER = get_addresses(symbol); \ + if (hp->MEMBER == NULL) \ + return E_SYNTAX_ERROR; \ + hp->flags.MEMBER = TRUE; \ + } \ +} while (0) + +/* Parse a shared string pointed to by MEMBER */ +#define PARSE_STR(MEMBER) do \ +{ \ + if (optype == OP_BOOLEAN) \ + return E_SYNTAX_ERROR; \ + if (hp->flags.MEMBER) { \ + hp->flags.MEMBER = FALSE; \ + assert(hp->MEMBER); \ + del_string(hp->MEMBER); \ + hp->MEMBER = NULL; \ + } \ + if (optype == OP_ADDITION) { \ + hp->MEMBER = get_shared_string(symbol); \ + if (hp->MEMBER == NULL) \ + return E_SYNTAX_ERROR; \ + hp->flags.MEMBER = TRUE; \ + } \ +} while (0) + +/* Parse an integer value for MEMBER */ +#define PARSE_INT(MEMBER) do \ +{ \ + if (optype == OP_BOOLEAN) \ + return E_SYNTAX_ERROR; \ + hp->flags.MEMBER = FALSE; \ + if (optype == OP_ADDITION) { \ + value = get_u_long(symbol); \ + hp->MEMBER = value; \ + hp->flags.MEMBER = TRUE; \ + } \ +} while (0) + +/* + * Evaluate the two-character tag symbol pointed to by "symbol" and place + * the data in the structure pointed to by "hp". The pointer pointed to + * by "symbol" is updated to point past the source string (but may not + * point to the next tag entry). + * + * Obviously, this need a few more comments. . . . + */ +PRIVATE int +eval_symbol(symbol, hp) + char **symbol; + struct host *hp; +{ + char tmpstr[MAXSTRINGLEN]; + byte *tmphaddr; + struct shared_string *ss; + struct symbolmap *symbolptr; + u_int32 value; + int32 timeoff; + int i, numsymbols; + unsigned len; + int optype; /* Indicates boolean, addition, or deletion */ + + eat_whitespace(symbol); + + /* Make sure this is set before returning. */ + current_tagname[0] = (*symbol)[0]; + current_tagname[1] = (*symbol)[1]; + current_tagname[2] = 0; + + if ((*symbol)[0] == '\0') { + return E_END_OF_ENTRY; + } + if ((*symbol)[0] == ':') { + return SUCCESS; + } + if ((*symbol)[0] == 'T') { /* generic symbol */ + (*symbol)++; + value = get_u_long(symbol); + sprintf(current_tagname, "T%d", value); + eat_whitespace(symbol); + if ((*symbol)[0] != '=') { + return E_SYNTAX_ERROR; + } + (*symbol)++; + if (!(hp->generic)) { + hp->generic = (struct shared_bindata *) + smalloc(sizeof(struct shared_bindata)); + } + if (process_generic(symbol, &(hp->generic), (byte) (value & 0xFF))) + return E_SYNTAX_ERROR; + hp->flags.generic = TRUE; + return SUCCESS; + } + /* + * Determine the type of operation to be done on this symbol + */ + switch ((*symbol)[2]) { + case '=': + optype = OP_ADDITION; + break; + case '@': + optype = OP_DELETION; + break; + case ':': + case '\0': + optype = OP_BOOLEAN; + break; + default: + return E_SYNTAX_ERROR; + } + + symbolptr = symbol_list; + numsymbols = sizeof(symbol_list) / sizeof(struct symbolmap); + for (i = 0; i < numsymbols; i++) { + if (((symbolptr->symbol)[0] == (*symbol)[0]) && + ((symbolptr->symbol)[1] == (*symbol)[1])) { + break; + } + symbolptr++; + } + if (i >= numsymbols) { + return E_UNKNOWN_SYMBOL; + } + /* + * Skip past the = or @ character (to point to the data) if this + * isn't a boolean operation. For boolean operations, just skip + * over the two-character tag symbol (and nothing else. . . .). + */ + (*symbol) += (optype == OP_BOOLEAN) ? 2 : 3; + + eat_whitespace(symbol); + + /* The cases below are in order by symbolcode value. */ + switch (symbolptr->symbolcode) { + + case SYM_BOOTFILE: + PARSE_STR(bootfile); + break; + + case SYM_COOKIE_SERVER: + PARSE_IAL(cookie_server); + break; + + case SYM_DOMAIN_SERVER: + PARSE_IAL(domain_server); + break; + + case SYM_GATEWAY: + PARSE_IAL(gateway); + break; + + case SYM_HWADDR: + if (optype == OP_BOOLEAN) + return E_SYNTAX_ERROR; + hp->flags.haddr = FALSE; + if (optype == OP_ADDITION) { + /* Default the HW type to Ethernet */ + if (hp->flags.htype == 0) { + hp->flags.htype = TRUE; + hp->htype = HTYPE_ETHERNET; + } + tmphaddr = prs_haddr(symbol, hp->htype); + if (!tmphaddr) + return E_BAD_HWADDR; + bcopy(tmphaddr, hp->haddr, haddrlength(hp->htype)); + hp->flags.haddr = TRUE; + } + break; + + case SYM_HOMEDIR: + PARSE_STR(homedir); + break; + + case SYM_HTYPE: + if (optype == OP_BOOLEAN) + return E_SYNTAX_ERROR; + hp->flags.htype = FALSE; + if (optype == OP_ADDITION) { + value = 0L; /* Assume an illegal value */ + eat_whitespace(symbol); + if (isdigit(**symbol)) { + value = get_u_long(symbol); + } else { + len = sizeof(tmpstr); + (void) get_string(symbol, tmpstr, &len); + makelower(tmpstr); + numsymbols = sizeof(htnamemap) / + sizeof(struct htypename); + for (i = 0; i < numsymbols; i++) { + if (!strcmp(htnamemap[i].name, tmpstr)) { + break; + } + } + if (i < numsymbols) { + value = htnamemap[i].htype; + } + } + if (value >= hwinfocnt) { + return E_BAD_HWATYPE; + } + hp->htype = (byte) (value & 0xFF); + hp->flags.htype = TRUE; + } + break; + + case SYM_IMPRESS_SERVER: + PARSE_IAL(impress_server); + break; + + case SYM_IPADDR: + PARSE_IA1(iaddr); + break; + + case SYM_LOG_SERVER: + PARSE_IAL(log_server); + break; + + case SYM_LPR_SERVER: + PARSE_IAL(lpr_server); + break; + + case SYM_NAME_SERVER: + PARSE_IAL(name_server); + break; + + case SYM_RLP_SERVER: + PARSE_IAL(rlp_server); + break; + + case SYM_SUBNET_MASK: + PARSE_IA1(subnet_mask); + break; + + case SYM_TIME_OFFSET: + if (optype == OP_BOOLEAN) + return E_SYNTAX_ERROR; + hp->flags.time_offset = FALSE; + if (optype == OP_ADDITION) { + len = sizeof(tmpstr); + (void) get_string(symbol, tmpstr, &len); + if (!strncmp(tmpstr, "auto", 4)) { + hp->time_offset = secondswest; + } else { + if (sscanf(tmpstr, "%d", &timeoff) != 1) + return E_BAD_LONGWORD; + hp->time_offset = timeoff; + } + hp->flags.time_offset = TRUE; + } + break; + + case SYM_TIME_SERVER: + PARSE_IAL(time_server); + break; + + case SYM_VENDOR_MAGIC: + if (optype == OP_BOOLEAN) + return E_SYNTAX_ERROR; + hp->flags.vm_cookie = FALSE; + if (optype == OP_ADDITION) { + if (strncmp(*symbol, "auto", 4)) { + /* The string is not "auto" */ + if (!strncmp(*symbol, "rfc", 3)) { + bcopy(vm_rfc1048, hp->vm_cookie, 4); + } else if (!strncmp(*symbol, "cmu", 3)) { + bcopy(vm_cmu, hp->vm_cookie, 4); + } else { + if (!isdigit(**symbol)) + return E_BAD_IPADDR; + if (prs_inetaddr(symbol, &value) < 0) + return E_BAD_IPADDR; + bcopy(&value, hp->vm_cookie, 4); + } + hp->flags.vm_cookie = TRUE; + } + } + break; + + case SYM_SIMILAR_ENTRY: + switch (optype) { + case OP_ADDITION: + fill_defaults(hp, symbol); + break; + default: + return E_SYNTAX_ERROR; + } + break; + + case SYM_NAME_SWITCH: + switch (optype) { + case OP_ADDITION: + return E_SYNTAX_ERROR; + case OP_DELETION: + hp->flags.send_name = FALSE; + hp->flags.name_switch = FALSE; + break; + case OP_BOOLEAN: + hp->flags.send_name = TRUE; + hp->flags.name_switch = TRUE; + break; + } + break; + + case SYM_BOOTSIZE: + switch (optype) { + case OP_ADDITION: + if (!strncmp(*symbol, "auto", 4)) { + hp->flags.bootsize = TRUE; + hp->flags.bootsize_auto = TRUE; + } else { + hp->bootsize = (unsigned int) get_u_long(symbol); + hp->flags.bootsize = TRUE; + hp->flags.bootsize_auto = FALSE; + } + break; + case OP_DELETION: + hp->flags.bootsize = FALSE; + break; + case OP_BOOLEAN: + hp->flags.bootsize = TRUE; + hp->flags.bootsize_auto = TRUE; + break; + } + break; + + case SYM_BOOT_SERVER: + PARSE_IA1(bootserver); + break; + + case SYM_TFTPDIR: + PARSE_STR(tftpdir); + if ((hp->tftpdir != NULL) && + (hp->tftpdir->string[0] != '/')) + return E_BAD_PATHNAME; + break; + + case SYM_DUMP_FILE: + PARSE_STR(dump_file); + break; + + case SYM_DOMAIN_NAME: + PARSE_STR(domain_name); + break; + + case SYM_SWAP_SERVER: + PARSE_IA1(swap_server); + break; + + case SYM_ROOT_PATH: + PARSE_STR(root_path); + break; + + case SYM_EXTEN_FILE: + PARSE_STR(exten_file); + break; + + case SYM_REPLY_ADDR: + PARSE_IA1(reply_addr); + break; + + case SYM_NIS_DOMAIN: + PARSE_STR(nis_domain); + break; + + case SYM_NIS_SERVER: + PARSE_IAL(nis_server); + break; + + case SYM_NTP_SERVER: + PARSE_IAL(ntp_server); + break; + +#ifdef YORK_EX_OPTION + case SYM_EXEC_FILE: + PARSE_STR(exec_file); + break; +#endif + + case SYM_MSG_SIZE: + PARSE_INT(msg_size); + if (hp->msg_size < BP_MINPKTSZ || + hp->msg_size > MAX_MSG_SIZE) + return E_BAD_VALUE; + break; + + case SYM_MIN_WAIT: + PARSE_INT(min_wait); + if (hp->min_wait < 0) + return E_BAD_VALUE; + break; + + /* XXX - Add new tags here */ + + default: + return E_UNKNOWN_SYMBOL; + + } /* switch symbolcode */ + + return SUCCESS; +} +#undef PARSE_IA1 +#undef PARSE_IAL +#undef PARSE_STR + + + + +/* + * Read a string from the buffer indirectly pointed to through "src" and + * move it into the buffer pointed to by "dest". A pointer to the maximum + * allowable length of the string (including null-terminator) is passed as + * "length". The actual length of the string which was read is returned in + * the unsigned integer pointed to by "length". This value is the same as + * that which would be returned by applying the strlen() function on the + * destination string (i.e the terminating null is not counted as a + * character). Trailing whitespace is removed from the string. For + * convenience, the function returns the new value of "dest". + * + * The string is read until the maximum number of characters, an unquoted + * colon (:), or a null character is read. The return string in "dest" is + * null-terminated. + */ + +PRIVATE char * +get_string(src, dest, length) + char **src, *dest; + unsigned *length; +{ + int n, len, quoteflag; + + quoteflag = FALSE; + n = 0; + len = *length - 1; + while ((n < len) && (**src)) { + if (!quoteflag && (**src == ':')) { + break; + } + if (**src == '"') { + (*src)++; + quoteflag = !quoteflag; + continue; + } + if (**src == '\\') { + (*src)++; + if (!**src) { + break; + } + } + *dest++ = *(*src)++; + n++; + } + + /* + * Remove that troublesome trailing whitespace. . . + */ + while ((n > 0) && isspace(dest[-1])) { + dest--; + n--; + } + + *dest = '\0'; + *length = n; + return dest; +} + + + +/* + * Read the string indirectly pointed to by "src", update the caller's + * pointer, and return a pointer to a malloc'ed shared_string structure + * containing the string. + * + * The string is read using the same rules as get_string() above. + */ + +PRIVATE struct shared_string * +get_shared_string(src) + char **src; +{ + char retstring[MAXSTRINGLEN]; + struct shared_string *s; + unsigned length; + + length = sizeof(retstring); + (void) get_string(src, retstring, &length); + + s = (struct shared_string *) smalloc(sizeof(struct shared_string) + + length); + s->linkcount = 1; + strcpy(s->string, retstring); + + return s; +} + + + +/* + * Load RFC1048 generic information directly into a memory buffer. + * + * "src" indirectly points to the ASCII representation of the generic data. + * "dest" points to a string structure which is updated to point to a new + * string with the new data appended to the old string. The old string is + * freed. + * + * The given tag value is inserted with the new data. + * + * The data may be represented as either a stream of hexadecimal numbers + * representing bytes (any or all bytes may optionally start with '0x' and + * be separated with periods ".") or as a quoted string of ASCII + * characters (the quotes are required). + */ + +PRIVATE int +process_generic(src, dest, tagvalue) + char **src; + struct shared_bindata **dest; + u_int tagvalue; +{ + byte tmpbuf[MAXBUFLEN]; + byte *str; + struct shared_bindata *bdata; + u_int newlength, oldlength; + + str = tmpbuf; + *str++ = (tagvalue & 0xFF); /* Store tag value */ + str++; /* Skip over length field */ + if ((*src)[0] == '"') { /* ASCII data */ + newlength = sizeof(tmpbuf) - 2; /* Set maximum allowed length */ + (void) get_string(src, (char *) str, &newlength); + newlength++; /* null terminator */ + } else { /* Numeric data */ + newlength = 0; + while (newlength < sizeof(tmpbuf) - 2) { + if (interp_byte(src, str++) < 0) + break; + newlength++; + if (**src == '.') { + (*src)++; + } + } + } + if ((*src)[0] != ':') + return -1; + + tmpbuf[1] = (newlength & 0xFF); + oldlength = ((*dest)->length); + bdata = (struct shared_bindata *) smalloc(sizeof(struct shared_bindata) + + oldlength + newlength + 1); + if (oldlength > 0) { + bcopy((*dest)->data, bdata->data, oldlength); + } + bcopy(tmpbuf, bdata->data + oldlength, newlength + 2); + bdata->length = oldlength + newlength + 2; + bdata->linkcount = 1; + if (*dest) { + del_bindata(*dest); + } + *dest = bdata; + return 0; +} + + + +/* + * Verify that the given string makes sense as a hostname (according to + * Appendix 1, page 29 of RFC882). + * + * Return TRUE for good names, FALSE otherwise. + */ + +PRIVATE boolean +goodname(hostname) + register char *hostname; +{ + do { + if (!isalpha(*hostname++)) { /* First character must be a letter */ + return FALSE; + } + while (isalnum(*hostname) || + (*hostname == '-') || + (*hostname == '_') ) + { + hostname++; /* Alphanumeric or a hyphen */ + } + if (!isalnum(hostname[-1])) { /* Last must be alphanumeric */ + return FALSE; + } + if (*hostname == '\0') {/* Done? */ + return TRUE; + } + } while (*hostname++ == '.'); /* Dot, loop for next label */ + + return FALSE; /* If it's not a dot, lose */ +} + + + +/* + * Null compare function -- always returns FALSE so an element is always + * inserted into a hash table (i.e. there is never a collision with an + * existing element). + */ + +PRIVATE boolean +nullcmp(d1, d2) + hash_datum *d1, *d2; +{ + return FALSE; +} + + +/* + * Function for comparing a string with the hostname field of a host + * structure. + */ + +boolean +nmcmp(d1, d2) + hash_datum *d1, *d2; +{ + char *name = (char *) d1; /* XXX - OK? */ + struct host *hp = (struct host *) d2; + + return !strcmp(name, hp->hostname->string); +} + + +/* + * Compare function to determine whether two hardware addresses are + * equivalent. Returns TRUE if "host1" and "host2" are equivalent, FALSE + * otherwise. + * + * If the hardware addresses of "host1" and "host2" are identical, but + * they are on different IP subnets, this function returns FALSE. + * + * This function is used when inserting elements into the hardware address + * hash table. + */ + +PRIVATE boolean +hwinscmp(d1, d2) + hash_datum *d1, *d2; +{ + struct host *host1 = (struct host *) d1; + struct host *host2 = (struct host *) d2; + + if (host1->htype != host2->htype) { + return FALSE; + } + if (bcmp(host1->haddr, host2->haddr, haddrlength(host1->htype))) { + return FALSE; + } + /* XXX - Is the subnet_mask field set yet? */ + if ((host1->subnet_mask.s_addr) == (host2->subnet_mask.s_addr)) { + if (((host1->iaddr.s_addr) & (host1->subnet_mask.s_addr)) != + ((host2->iaddr.s_addr) & (host2->subnet_mask.s_addr))) + { + return FALSE; + } + } + return TRUE; +} + + +/* + * Macros for use in the function below: + */ + +#define DUP_COPY(MEMBER) do \ +{ \ + if (!hp->flags.MEMBER) { \ + if ((hp->flags.MEMBER = hp2->flags.MEMBER) != 0) { \ + hp->MEMBER = hp2->MEMBER; \ + } \ + } \ +} while (0) + +#define DUP_LINK(MEMBER) do \ +{ \ + if (!hp->flags.MEMBER) { \ + if ((hp->flags.MEMBER = hp2->flags.MEMBER) != 0) { \ + assert(hp2->MEMBER); \ + hp->MEMBER = hp2->MEMBER; \ + (hp->MEMBER->linkcount)++; \ + } \ + } \ +} while (0) + +/* + * Process the "similar entry" symbol. + * + * The host specified as the value of the "tc" symbol is used as a template + * for the current host entry. Symbol values not explicitly set in the + * current host entry are inferred from the template entry. + */ +PRIVATE void +fill_defaults(hp, src) + struct host *hp; + char **src; +{ + unsigned int tlen, hashcode; + struct host *hp2; + char tstring[MAXSTRINGLEN]; + + tlen = sizeof(tstring); + (void) get_string(src, tstring, &tlen); + hashcode = hash_HashFunction((u_char *) tstring, tlen); + hp2 = (struct host *) hash_Lookup(nmhashtable, hashcode, nmcmp, tstring); + + if (hp2 == NULL) { + report(LOG_ERR, "can't find tc=\"%s\"", tstring); + return; + } + DUP_LINK(bootfile); + DUP_LINK(cookie_server); + DUP_LINK(domain_server); + DUP_LINK(gateway); + /* haddr not copied */ + DUP_LINK(homedir); + DUP_COPY(htype); + + DUP_LINK(impress_server); + /* iaddr not copied */ + DUP_LINK(log_server); + DUP_LINK(lpr_server); + DUP_LINK(name_server); + DUP_LINK(rlp_server); + + DUP_COPY(subnet_mask); + DUP_COPY(time_offset); + DUP_LINK(time_server); + + if (!hp->flags.vm_cookie) { + if ((hp->flags.vm_cookie = hp2->flags.vm_cookie)) { + bcopy(hp2->vm_cookie, hp->vm_cookie, 4); + } + } + if (!hp->flags.name_switch) { + if ((hp->flags.name_switch = hp2->flags.name_switch)) { + hp->flags.send_name = hp2->flags.send_name; + } + } + if (!hp->flags.bootsize) { + if ((hp->flags.bootsize = hp2->flags.bootsize)) { + hp->flags.bootsize_auto = hp2->flags.bootsize_auto; + hp->bootsize = hp2->bootsize; + } + } + DUP_COPY(bootserver); + + DUP_LINK(tftpdir); + DUP_LINK(dump_file); + DUP_LINK(domain_name); + + DUP_COPY(swap_server); + DUP_LINK(root_path); + DUP_LINK(exten_file); + + DUP_COPY(reply_addr); + + DUP_LINK(nis_domain); + DUP_LINK(nis_server); + DUP_LINK(ntp_server); + +#ifdef YORK_EX_OPTION + DUP_LINK(exec_file); +#endif + + DUP_COPY(msg_size); + DUP_COPY(min_wait); + + /* XXX - Add new tags here */ + + DUP_LINK(generic); + +} +#undef DUP_COPY +#undef DUP_LINK + + + +/* + * This function adjusts the caller's pointer to point just past the + * first-encountered colon. If it runs into a null character, it leaves + * the pointer pointing to it. + */ + +PRIVATE void +adjust(s) + char **s; +{ + register char *t; + + t = *s; + while (*t && (*t != ':')) { + t++; + } + if (*t) { + t++; + } + *s = t; +} + + + + +/* + * This function adjusts the caller's pointer to point to the first + * non-whitespace character. If it runs into a null character, it leaves + * the pointer pointing to it. + */ + +PRIVATE void +eat_whitespace(s) + char **s; +{ + register char *t; + + t = *s; + while (*t && isspace(*t)) { + t++; + } + *s = t; +} + + + +/* + * This function converts the given string to all lowercase. + */ + +PRIVATE void +makelower(s) + char *s; +{ + while (*s) { + if (isupper(*s)) { + *s = tolower(*s); + } + s++; + } +} + + + +/* + * + * N O T E : + * + * In many of the functions which follow, a parameter such as "src" or + * "symbol" is passed as a pointer to a pointer to something. This is + * done for the purpose of letting the called function update the + * caller's copy of the parameter (i.e. to effect call-by-reference + * parameter passing). The value of the actual parameter is only used + * to locate the real parameter of interest and then update this indirect + * parameter. + * + * I'm sure somebody out there won't like this. . . . + * (Yea, because it usually makes code slower... -gwr) + * + */ + + + +/* + * "src" points to a character pointer which points to an ASCII string of + * whitespace-separated IP addresses. A pointer to an in_addr_list + * structure containing the list of addresses is returned. NULL is + * returned if no addresses were found at all. The pointer pointed to by + * "src" is updated to point to the first non-address (illegal) character. + */ + +PRIVATE struct in_addr_list * +get_addresses(src) + char **src; +{ + struct in_addr tmpaddrlist[MAXINADDRS]; + struct in_addr *address1, *address2; + struct in_addr_list *result; + unsigned addrcount, totalsize; + + address1 = tmpaddrlist; + for (addrcount = 0; addrcount < MAXINADDRS; addrcount++) { + while (isspace(**src) || (**src == ',')) { + (*src)++; + } + if (!**src) { /* Quit if nothing more */ + break; + } + if (prs_inetaddr(src, &(address1->s_addr)) < 0) { + break; + } + address1++; /* Point to next address slot */ + } + if (addrcount < 1) { + result = NULL; + } else { + totalsize = sizeof(struct in_addr_list) + + (addrcount - 1) * sizeof(struct in_addr); + result = (struct in_addr_list *) smalloc(totalsize); + result->linkcount = 1; + result->addrcount = addrcount; + address1 = tmpaddrlist; + address2 = result->addr; + for (; addrcount > 0; addrcount--) { + address2->s_addr = address1->s_addr; + address1++; + address2++; + } + } + return result; +} + + + +/* + * prs_inetaddr(src, result) + * + * "src" is a value-result parameter; the pointer it points to is updated + * to point to the next data position. "result" points to an unsigned long + * in which an address is returned. + * + * This function parses the IP address string in ASCII "dot notation" pointed + * to by (*src) and places the result (in network byte order) in the unsigned + * long pointed to by "result". For malformed addresses, -1 is returned, + * (*src) points to the first illegal character, and the unsigned long pointed + * to by "result" is unchanged. Successful calls return 0. + */ + +PRIVATE int +prs_inetaddr(src, result) + char **src; + u_int32 *result; +{ + char tmpstr[MAXSTRINGLEN]; + register u_int32 value; + u_int32 parts[4], *pp; + int n; + char *s, *t; + +#if 1 /* XXX - experimental */ + /* Leading alpha char causes IP addr lookup. */ + if (isalpha(**src)) { + /* Lookup IP address. */ + s = *src; + t = tmpstr; + while ((isalnum(*s) || (*s == '.') || + (*s == '-') || (*s == '_') ) && + (t < &tmpstr[MAXSTRINGLEN - 1]) ) + *t++ = *s++; + *t = '\0'; + *src = s; + + n = lookup_ipa(tmpstr, result); + if (n < 0) + report(LOG_ERR, "can not get IP addr for %s", tmpstr); + return n; + } +#endif + + /* + * Parse an address in Internet format: + * a.b.c.d + * a.b.c (with c treated as 16-bits) + * a.b (with b treated as 24 bits) + */ + pp = parts; + loop: + /* If it's not a digit, return error. */ + if (!isdigit(**src)) + return -1; + *pp++ = get_u_long(src); + if (**src == '.') { + if (pp < (parts + 4)) { + (*src)++; + goto loop; + } + return (-1); + } +#if 0 + /* This is handled by the caller. */ + if (**src && !(isspace(**src) || (**src == ':'))) { + return (-1); + } +#endif + + /* + * Construct the address according to + * the number of parts specified. + */ + n = pp - parts; + switch (n) { + case 1: /* a -- 32 bits */ + value = parts[0]; + break; + case 2: /* a.b -- 8.24 bits */ + value = (parts[0] << 24) | (parts[1] & 0xFFFFFF); + break; + case 3: /* a.b.c -- 8.8.16 bits */ + value = (parts[0] << 24) | ((parts[1] & 0xFF) << 16) | + (parts[2] & 0xFFFF); + break; + case 4: /* a.b.c.d -- 8.8.8.8 bits */ + value = (parts[0] << 24) | ((parts[1] & 0xFF) << 16) | + ((parts[2] & 0xFF) << 8) | (parts[3] & 0xFF); + break; + default: + return (-1); + } + *result = htonl(value); + return (0); +} + + + +/* + * "src" points to a pointer which in turn points to a hexadecimal ASCII + * string. This string is interpreted as a hardware address and returned + * as a pointer to the actual hardware address, represented as an array of + * bytes. + * + * The ASCII string must have the proper number of digits for the specified + * hardware type (e.g. twelve digits for a 48-bit Ethernet address). + * Two-digit sequences (bytes) may be separated with periods (.) and/or + * prefixed with '0x' for readability, but this is not required. + * + * For bad addresses, the pointer which "src" points to is updated to point + * to the start of the first two-digit sequence which was bad, and the + * function returns a NULL pointer. + */ + +PRIVATE byte * +prs_haddr(src, htype) + char **src; + u_int htype; +{ + static byte haddr[MAXHADDRLEN]; + byte *hap; + char tmpstr[MAXSTRINGLEN]; + u_int tmplen; + unsigned hal; + char *p; + + hal = haddrlength(htype); /* Get length of this address type */ + if (hal <= 0) { + report(LOG_ERR, "Invalid addr type for HW addr parse"); + return NULL; + } + tmplen = sizeof(tmpstr); + get_string(src, tmpstr, &tmplen); + p = tmpstr; + +#if 1 /* XXX - experimental */ + /* If it's a valid host name, try to lookup the HW address. */ + if (goodname(p)) { + /* Lookup Hardware Address for hostname. */ + if ((hap = lookup_hwa(p, htype)) != NULL) + return hap; /* success */ + report(LOG_ERR, "Add 0x prefix if hex value starts with A-F"); + /* OK, assume it must be numeric. */ + } +#endif + + hap = haddr; + while (hap < haddr + hal) { + if (*p == '.') + p++; + if (interp_byte(&p, hap++) < 0) { + return NULL; + } + } + return haddr; +} + + + +/* + * "src" is a pointer to a character pointer which in turn points to a + * hexadecimal ASCII representation of a byte. This byte is read, the + * character pointer is updated, and the result is deposited into the + * byte pointed to by "retbyte". + * + * The usual '0x' notation is allowed but not required. The number must be + * a two digit hexadecimal number. If the number is invalid, "src" and + * "retbyte" are left untouched and -1 is returned as the function value. + * Successful calls return 0. + */ + +PRIVATE int +interp_byte(src, retbyte) + char **src; + byte *retbyte; +{ + int v; + + if ((*src)[0] == '0' && + ((*src)[1] == 'x' || + (*src)[1] == 'X')) { + (*src) += 2; /* allow 0x for hex, but don't require it */ + } + if (!isxdigit((*src)[0]) || !isxdigit((*src)[1])) { + return -1; + } + if (sscanf(*src, "%2x", &v) != 1) { + return -1; + } + (*src) += 2; + *retbyte = (byte) (v & 0xFF); + return 0; +} + + + +/* + * The parameter "src" points to a character pointer which points to an + * ASCII string representation of an unsigned number. The number is + * returned as an unsigned long and the character pointer is updated to + * point to the first illegal character. + */ + +PRIVATE u_int32 +get_u_long(src) + char **src; +{ + register u_int32 value, base; + char c; + + /* + * Collect number up to first illegal character. Values are specified + * as for C: 0x=hex, 0=octal, other=decimal. + */ + value = 0; + base = 10; + if (**src == '0') { + base = 8; + (*src)++; + } + if (**src == 'x' || **src == 'X') { + base = 16; + (*src)++; + } + while ((c = **src)) { + if (isdigit(c)) { + value = (value * base) + (c - '0'); + (*src)++; + continue; + } + if (base == 16 && isxdigit(c)) { + value = (value << 4) + ((c & ~32) + 10 - 'A'); + (*src)++; + continue; + } + break; + } + return value; +} + + + +/* + * Routines for deletion of data associated with the main data structure. + */ + + +/* + * Frees the entire host data structure given. Does nothing if the passed + * pointer is NULL. + */ + +PRIVATE void +free_host(hmp) + hash_datum *hmp; +{ + struct host *hostptr = (struct host *) hmp; + if (hostptr == NULL) + return; + assert(hostptr->linkcount > 0); + if (--(hostptr->linkcount)) + return; /* Still has references */ + del_iplist(hostptr->cookie_server); + del_iplist(hostptr->domain_server); + del_iplist(hostptr->gateway); + del_iplist(hostptr->impress_server); + del_iplist(hostptr->log_server); + del_iplist(hostptr->lpr_server); + del_iplist(hostptr->name_server); + del_iplist(hostptr->rlp_server); + del_iplist(hostptr->time_server); + del_iplist(hostptr->nis_server); + del_iplist(hostptr->ntp_server); + + /* + * XXX - Add new tags here + * (if the value is an IP list) + */ + + del_string(hostptr->hostname); + del_string(hostptr->homedir); + del_string(hostptr->bootfile); + del_string(hostptr->tftpdir); + del_string(hostptr->root_path); + del_string(hostptr->domain_name); + del_string(hostptr->dump_file); + del_string(hostptr->exten_file); + del_string(hostptr->nis_domain); + +#ifdef YORK_EX_OPTION + del_string(hostptr->exec_file); +#endif + + /* + * XXX - Add new tags here + * (if it is a shared string) + */ + + del_bindata(hostptr->generic); + free((char *) hostptr); +} + + + +/* + * Decrements the linkcount on the given IP address data structure. If the + * linkcount goes to zero, the memory associated with the data is freed. + */ + +PRIVATE void +del_iplist(iplist) + struct in_addr_list *iplist; +{ + if (iplist) { + if (!(--(iplist->linkcount))) { + free((char *) iplist); + } + } +} + + + +/* + * Decrements the linkcount on a string data structure. If the count + * goes to zero, the memory associated with the string is freed. Does + * nothing if the passed pointer is NULL. + */ + +PRIVATE void +del_string(stringptr) + struct shared_string *stringptr; +{ + if (stringptr) { + if (!(--(stringptr->linkcount))) { + free((char *) stringptr); + } + } +} + + + +/* + * Decrements the linkcount on a shared_bindata data structure. If the + * count goes to zero, the memory associated with the data is freed. Does + * nothing if the passed pointer is NULL. + */ + +PRIVATE void +del_bindata(dataptr) + struct shared_bindata *dataptr; +{ + if (dataptr) { + if (!(--(dataptr->linkcount))) { + free((char *) dataptr); + } + } +} + + + + +/* smalloc() -- safe malloc() + * + * Always returns a valid pointer (if it returns at all). The allocated + * memory is initialized to all zeros. If malloc() returns an error, a + * message is printed using the report() function and the program aborts + * with a status of 1. + */ + +PRIVATE char * +smalloc(nbytes) + unsigned nbytes; +{ + char *retvalue; + + retvalue = malloc(nbytes); + if (!retvalue) { + report(LOG_ERR, "malloc() failure -- exiting"); + exit(1); + } + bzero(retvalue, nbytes); + return retvalue; +} + + +/* + * Compare function to determine whether two hardware addresses are + * equivalent. Returns TRUE if "host1" and "host2" are equivalent, FALSE + * otherwise. + * + * This function is used when retrieving elements from the hardware address + * hash table. + */ + +boolean +hwlookcmp(d1, d2) + hash_datum *d1, *d2; +{ + struct host *host1 = (struct host *) d1; + struct host *host2 = (struct host *) d2; + + if (host1->htype != host2->htype) { + return FALSE; + } + if (bcmp(host1->haddr, host2->haddr, haddrlength(host1->htype))) { + return FALSE; + } + return TRUE; +} + + +/* + * Compare function for doing IP address hash table lookup. + */ + +boolean +iplookcmp(d1, d2) + hash_datum *d1, *d2; +{ + struct host *host1 = (struct host *) d1; + struct host *host2 = (struct host *) d2; + + return (host1->iaddr.s_addr == host2->iaddr.s_addr); +} + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/usr.sbin/bootpd/readfile.h b/usr.sbin/bootpd/readfile.h new file mode 100644 index 00000000000..3913455857c --- /dev/null +++ b/usr.sbin/bootpd/readfile.h @@ -0,0 +1,19 @@ +/* readfile.h */ + +#include "bptypes.h" +#include "hash.h" + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +extern boolean hwlookcmp P((hash_datum *, hash_datum *)); +extern boolean iplookcmp P((hash_datum *, hash_datum *)); +extern boolean nmcmp P((hash_datum *, hash_datum *)); +extern void readtab P((int)); +extern void rdtab_init P((void)); + +#undef P + diff --git a/usr.sbin/bootpd/report.c b/usr.sbin/bootpd/report.c new file mode 100644 index 00000000000..4f7f03616a2 --- /dev/null +++ b/usr.sbin/bootpd/report.c @@ -0,0 +1,154 @@ +/* + * report() - calls syslog + */ + +#ifdef __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +#include <stdio.h> +#include <syslog.h> + +#include "report.h" + +#ifndef LOG_NDELAY +#define LOG_NDELAY 0 +#endif +#ifndef LOG_DAEMON +#define LOG_DAEMON 0 +#endif +#ifndef LOG_BOOTP +#define LOG_BOOTP LOG_DAEMON +#endif + +extern int debug; +extern char *progname; + +/* + * This is initialized so you get stderr until you call + * report_init() + */ +static int stderr_only = 1; + +void +report_init(nolog) + int nolog; +{ + stderr_only = nolog; +#ifdef SYSLOG + if (!stderr_only) { + openlog(progname, LOG_PID | LOG_NDELAY, LOG_BOOTP); + } +#endif +} + +/* + * This routine reports errors and such via stderr and syslog() if + * appopriate. It just helps avoid a lot of "#ifdef SYSLOG" constructs + * from being scattered throughout the code. + * + * The syntax is identical to syslog(3), but %m is not considered special + * for output to stderr (i.e. you'll see "%m" in the output. . .). Also, + * control strings should normally end with \n since newlines aren't + * automatically generated for stderr output (whereas syslog strips out all + * newlines and adds its own at the end). + */ + +static char *levelnames[] = { +#ifdef LOG_SALERT + "level(0): ", + "alert(1): ", + "alert(2): ", + "emerg(3): ", + "error(4): ", + "crit(5): ", + "warn(6): ", + "note(7): ", + "info(8): ", + "debug(9): ", + "level(?): " +#else + "emerg(0): ", + "alert(1): ", + "crit(2): ", + "error(3): ", + "warn(4): ", + "note(5): ", + "info(6): ", + "debug(7): ", + "level(?): " +#endif +}; +static int numlevels = sizeof(levelnames) / sizeof(levelnames[0]); + + +/* + * Print a log message using syslog(3) and/or stderr. + * The message passed in should not include a newline. + */ +#ifdef __STDC__ +void +report(int priority, char *fmt,...) +#else +/*VARARGS2*/ +void +report(priority, fmt, va_alist) + int priority; + char *fmt; + va_dcl +#endif +{ + va_list ap; + static char buf[128]; + + if ((priority < 0) || (priority >= numlevels)) { + priority = numlevels - 1; + } +#ifdef __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + vsprintf(buf, fmt, ap); + va_end(ap); + + /* + * Print the message + */ + if (stderr_only || (debug > 2)) { + fprintf(stderr, "%s: %s %s\n", + progname, levelnames[priority], buf); + } +#ifdef SYSLOG + if (!stderr_only) + syslog((priority | LOG_BOOTP), "%s", buf); +#endif +} + + + +/* + * Return pointer to static string which gives full filesystem error message. + */ +char * +get_errmsg() +{ + extern int errno; + extern char *strerror(); + + return strerror(errno); +} + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/usr.sbin/bootpd/report.h b/usr.sbin/bootpd/report.h new file mode 100644 index 00000000000..0bf63d60fbb --- /dev/null +++ b/usr.sbin/bootpd/report.h @@ -0,0 +1,13 @@ +/* report.h */ + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +extern void report_init P((int nolog)); +extern void report P((int, char *, ...)); +extern char *get_errmsg P((void)); + +#undef P diff --git a/usr.sbin/bootpd/syslog.conf b/usr.sbin/bootpd/syslog.conf new file mode 100644 index 00000000000..2c135af4974 --- /dev/null +++ b/usr.sbin/bootpd/syslog.conf @@ -0,0 +1,63 @@ +# +# syslog configuration file for SunOS 4.X +# (modified to do local2 separately) +# +# This file is processed by m4 so be careful to quote (`') names +# that match m4 reserved words. Also, within ifdef's, arguments +# containing commas must be quoted. +# +# Note: Have to exclude user from most lines so that user.alert +# and user.emerg are not included, because old sendmails +# will generate them for debugging information. If you +# have no 4.2BSD based systems doing network logging, you +# can remove all the special cases for "user" logging. + +#*.err;kern.debug;auth.notice;user.none /dev/console +kern.debug;user,mail.crit;auth.notice /dev/console +daemon,syslog,lpr,news,uucp,cron.err /dev/console + +#*.err;kern.debug;daemon,auth.notice;mail.crit;user.none /var/adm/messages +kern.debug;user,mail.crit;auth.notice /var/adm/messages +daemon.notice;syslog,news,uucp,cron.err /var/adm/messages + +lpr.debug /var/adm/lpd-errs + +*.alert;kern.err;daemon.err;user.none operator +*.alert;user.none root + +*.emerg;user.none * + +# for loghost machines, to have authentication messages (su, login, etc.) +# logged to a file, un-comment out the following line and adjust the file name +# as appropriate. +# +# if a non-loghost machine chooses to have such messages +# sent to the loghost machine, un-comment out the following line. +# +#auth.notice ifdef(`LOGHOST', /var/log/authlog, @loghost) + +mail.debug ifdef(`LOGHOST', /var/log/syslog, @loghost) + +# following line for compatibility with old sendmails. they will send +# messages with no facility code, which will be turned into "user" messages +# by the local syslog daemon. only the "loghost" machine needs the following +# line, to cause these old sendmail log messages to be logged in the +# mail syslog file. +# +ifdef(`LOGHOST', +user.alert /var/log/syslog +) +# +# non-loghost machines will use the following lines to cause "user" +# log messages to be logged locally. +# +ifdef(`LOGHOST', , +user.err /dev/console +user.err /var/adm/messages +user.alert `root, operator' +user.emerg * +) + +# Local2: (bootpd, pppd) +local2.debug /dev/console +#local2.debug /var/log/local2 diff --git a/usr.sbin/bootpd/trygetea.c b/usr.sbin/bootpd/trygetea.c new file mode 100644 index 00000000000..e9314aede96 --- /dev/null +++ b/usr.sbin/bootpd/trygetea.c @@ -0,0 +1,46 @@ +/* + * trygetea.c - test program for getether.c + */ + +#include <sys/types.h> +#include <sys/socket.h> + +#if defined(SUNOS) || defined(SVR4) +#include <sys/sockio.h> +#endif + +#include <net/if.h> /* for struct ifreq */ +#include <netinet/in.h> +#include <arpa/inet.h> /* inet_ntoa */ + +#include <netdb.h> +#include <stdio.h> +#include <ctype.h> +#include <errno.h> + +int debug = 0; +char *progname; + +main(argc, argv) + char **argv; +{ + u_char ea[16]; /* Ethernet address */ + int i; + + progname = argv[0]; /* for report */ + + if (argc < 2) { + printf("need interface name\n"); + exit(1); + } + if ((i = getether(argv[1], ea)) < 0) { + printf("Could not get Ethernet address (rc=%d)\n", i); + exit(1); + } + printf("Ether-addr"); + for (i = 0; i < 6; i++) + printf(":%x", ea[i] & 0xFF); + printf("\n"); + + exit(0); +} diff --git a/usr.sbin/bootpd/trygetif.c b/usr.sbin/bootpd/trygetif.c new file mode 100644 index 00000000000..894f17b6e91 --- /dev/null +++ b/usr.sbin/bootpd/trygetif.c @@ -0,0 +1,66 @@ +/* + * trygetif.c - test program for getif.c + */ + +#include <sys/types.h> +#include <sys/socket.h> + +#if defined(SUNOS) || defined(SVR4) +#include <sys/sockio.h> +#endif + +#include <net/if.h> /* for struct ifreq */ +#include <netinet/in.h> +#include <arpa/inet.h> /* inet_ntoa */ + +#include <netdb.h> +#include <stdio.h> +#include <ctype.h> +#include <errno.h> + +#include "getif.h" + +int debug = 0; +char *progname; + +main(argc, argv) + char **argv; +{ + struct hostent *hep; + struct sockaddr ea; /* Ethernet address */ + struct sockaddr_in *sip; /* Interface address */ + struct ifreq *ifr; + struct in_addr dst_addr; + struct in_addr *dap; + int i, s; + + progname = argv[0]; /* for report */ + + dap = NULL; + if (argc > 1) { + dap = &dst_addr; + if (inet_aton(argv[1], &dst_addr) == 0) { + hep = gethostbyname(argv[1]); + if (!hep) { + printf("gethostbyname(%s)\n", argv[1]); + exit(1); + } + memcpy(&dst_addr, hep->h_addr, sizeof(dst_addr)); + } + } + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket open"); + exit(1); + } + ifr = getif(s, dap); + if (!ifr) { + printf("no interface for address\n"); + exit(1); + } + printf("Intf-name:%s\n", ifr->ifr_name); + sip = (struct sockaddr_in *) &(ifr->ifr_addr); + printf("Intf-addr:%s\n", inet_ntoa(sip->sin_addr)); + + exit(0); +} diff --git a/usr.sbin/bootpd/trylook.c b/usr.sbin/bootpd/trylook.c new file mode 100644 index 00000000000..40652a21159 --- /dev/null +++ b/usr.sbin/bootpd/trylook.c @@ -0,0 +1,50 @@ +/* + * trylook.c - test program for lookup.c + */ + +#include <sys/types.h> +#include <netinet/in.h> +#include <stdio.h> + +#include "report.h" +#include "lookup.h" + +extern char *ether_ntoa(); +extern char *inet_ntoa(); + +int debug = 0; +char *progname; + +main(argc, argv) + char **argv; +{ + int i; + struct in_addr in; + char *a; + u_char *hwa; + + progname = argv[0]; /* for report */ + + for (i = 1; i < argc; i++) { + + /* Host name */ + printf("%s:", argv[i]); + + /* IP addr */ + if (lookup_ipa(argv[i], &in.s_addr)) + a = "?"; + else + a = inet_ntoa(in); + printf(" ipa=%s", a); + + /* Ether addr */ + hwa = lookup_hwa(argv[i], 1); + if (!hwa) + a = "?"; + else + a = ether_ntoa(hwa); + printf(" hwa=%s\n", a); + + } + exit(0); +} diff --git a/usr.sbin/bootpd/tzone.c b/usr.sbin/bootpd/tzone.c new file mode 100644 index 00000000000..526764e5bae --- /dev/null +++ b/usr.sbin/bootpd/tzone.c @@ -0,0 +1,46 @@ +/* + * tzone.c - get the timezone + * + * This is shared by bootpd and bootpef + */ + +#include <sys/types.h> + +#ifdef SVR4 +/* XXX - Is this really SunOS specific? -gwr */ +/* This is in <time.h> but only visible if (__STDC__ == 1). */ +extern long timezone; +#else /* SVR4 */ +/* BSD or SunOS */ +# include <sys/time.h> +# include <syslog.h> +#endif /* SVR4 */ + +#include "bptypes.h" +#include "report.h" +#include "tzone.h" + +/* This is what other modules use. */ +int32 secondswest; + +/* + * Get our timezone offset so we can give it to clients if the + * configuration file doesn't specify one. + */ +void +tzone_init() +{ +#ifdef SVR4 + /* XXX - Is this really SunOS specific? -gwr */ + secondswest = timezone; +#else /* SVR4 */ + struct timezone tzp; /* Time zone offset for clients */ + struct timeval tp; /* Time (extra baggage) */ + if (gettimeofday(&tp, &tzp) < 0) { + secondswest = 0; /* Assume GMT for lack of anything better */ + report(LOG_ERR, "gettimeofday: %s", get_errmsg()); + } else { + secondswest = 60L * tzp.tz_minuteswest; /* Convert to seconds */ + } +#endif /* SVR4 */ +} diff --git a/usr.sbin/bootpd/tzone.h b/usr.sbin/bootpd/tzone.h new file mode 100644 index 00000000000..ddd67c4b625 --- /dev/null +++ b/usr.sbin/bootpd/tzone.h @@ -0,0 +1,3 @@ +/* tzone.h */ +extern int32 secondswest; +extern void tzone_init(); |