diff options
Diffstat (limited to 'libexec/bootpd')
59 files changed, 11009 insertions, 0 deletions
diff --git a/libexec/bootpd/Announce b/libexec/bootpd/Announce new file mode 100644 index 000000000000..a76e18acd2a9 --- /dev/null +++ b/libexec/bootpd/Announce @@ -0,0 +1,64 @@ + +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: + NetBSD-1.0 (BSD-4.4 derivative) + 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) + Linux 1.1.81 + 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/libexec/bootpd/Changes b/libexec/bootpd/Changes new file mode 100644 index 000000000000..f62d89d32a48 --- /dev/null +++ b/libexec/bootpd/Changes @@ -0,0 +1,293 @@ + +Changes, most recent first +Date, <email> Real Name + what... + +--> bootp-2.4.3 + +03/27/96 gwr@mc.com (Gordon W. Ross) + Use LOG_NOTICE in place of LOG_INFO for messages related + to unsatisfied clients [at request of <otto@tukki.jyu.fi>] + Fix the irix Makefile targets, and other misc. + +03/25/95 gwr@mc.com (Gordon W. Ross) + Corrected a bug I introduced into SunOS setarp, where + bad IP address caused "network unreachable" errors. + [Thanks to andrew@ntplx.net (Andrew Lindh) for the fix!] + +--> bootp-2.4.2 + +01/14/95 middelin@polyware.iaf.nl (Pauline Middelink) + Corrected support for the Linux networking code. + Fixed lots of warnings (gcc -Wall) + Added "linux" Makefile target. + +01/02/95 Jukka Ukkonen <ukkonen@csc.fi> + Allow bootptab syntax: ha="0:0:c0:80:e8:a7" + +11/30/94 Tonny van Lankveld <A.L.M.G.v.Lankveld@urc.tue.nl> + Fix reporting of duplicate Ethernet addresses. + +09/06/94 longyear@netcom.com (Al Longyear) + Better setarp for linux, allows non-ether types. + +09/02/94 Robert MacKinnon <rbm@montrouge.mis.slb.com> + Add support for IBM's AIX 3.2.5 + +08/30/94 piercarl@ltd.c-d.com (Piercarlo Grandi) + Fix select calls on linux (modifies timeval arg). + Fix setarp (specify Ethernet type for now). + +08/27/94 drew@drewsun.FEITH.COM (Andrew B. Sudell) + Add support for Wollongong Win-TCP (SysVr4 variant). + +08/24/94 gwr@mc.com (Gordon W. Ross) + Use sigaction() on systems that define SA_NOCLDSTOP + (a symbol required by POSIX) for HP/UX and others. + +--> bootp-2.4.1 + +08/24/94 gwr@mc.com (Gordon W. Ross) + Fix bug in boot file name generation (missing init) + +--> 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/libexec/bootpd/ConvOldTab.sh b/libexec/bootpd/ConvOldTab.sh new file mode 100755 index 000000000000..00683f0c049c --- /dev/null +++ b/libexec/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/libexec/bootpd/Installation b/libexec/bootpd/Installation new file mode 100644 index 000000000000..466cabce0cdb --- /dev/null +++ b/libexec/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/libexec/bootpd/Makefile b/libexec/bootpd/Makefile new file mode 100644 index 000000000000..7d07cac1cc6c --- /dev/null +++ b/libexec/bootpd/Makefile @@ -0,0 +1,17 @@ +# bootpd/Makefile + +PROG= bootpd +CFLAGS+= -DETC_ETHERS +CFLAGS+= -DSYSLOG -DDEBUG -DVEND_CMU + +WARNS?= 2 + +SUBDIR= bootpgw tools + +SRCS= bootpd.c dovend.c readfile.c hash.c dumptab.c \ + lookup.c getif.c hwaddr.c report.c tzone.c rtmsg.c + +MAN= bootptab.5 bootpd.8 +MLINKS= bootpd.8 bootpgw.8 + +.include <bsd.prog.mk> diff --git a/libexec/bootpd/Makefile.UNIX b/libexec/bootpd/Makefile.UNIX new file mode 100644 index 000000000000..701eb2bcc106 --- /dev/null +++ b/libexec/bootpd/Makefile.UNIX @@ -0,0 +1,203 @@ +# +# 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) + +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 systems needing special treatment: +# (Most POSIX systems should work with just "make all") +# + +# 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" + +# IRIX 5.X (Silicon Graphics) +irix: + $(MAKE) SYSDEFS= SYSLIBS= + +# Linux 1.1.80+ on [34]86 +linux: + $(MAKE) SYSDEFS="-O6 -Wall -fomit-frame-pointer" + +# 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" + +# Solaris 2.X (i.e. SunOS 5.X) with GCC. Note that GCC normally +# defines __STDC__=1 which breaks many Solaris header files... +sunos5gcc: + $(MAKE) SYSDEFS="-DSVR4 -DETC_ETHERS -D__STDC__=0" \ + SYSLIBS="-lsocket -lnsl" CC="gcc -Wall" + +# UNIX System V Rel. 3 +svr3: + $(MAKE) SYSDEFS="-DSYSV" + +# UNIX System V Rel. 4 +svr4: + $(MAKE) SYSDEFS="-DSVR4" \ + SYSLIBS="-lsocket -lnsl" + +# AT&T/GIS - Both AT&T StarServer and NCR 3000 +# may work for others using Wollongong's WIN-TCP +wollongong gis : + $(MAKE) SYSDEFS="-DSVR4 -DWIN_TCP" \ + 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/libexec/bootpd/Makefile.depend b/libexec/bootpd/Makefile.depend new file mode 100644 index 000000000000..344a5d0e9310 --- /dev/null +++ b/libexec/bootpd/Makefile.depend @@ -0,0 +1,16 @@ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + include \ + include/arpa \ + include/xlocale \ + lib/${CSU_DIR} \ + lib/libc \ + lib/libcompiler_rt \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/libexec/bootpd/Makefile.inc b/libexec/bootpd/Makefile.inc new file mode 100644 index 000000000000..338eb907e37d --- /dev/null +++ b/libexec/bootpd/Makefile.inc @@ -0,0 +1,3 @@ +WARNS?= 1 + +.include "../Makefile.inc" diff --git a/libexec/bootpd/Problems b/libexec/bootpd/Problems new file mode 100644 index 000000000000..78d809e6b215 --- /dev/null +++ b/libexec/bootpd/Problems @@ -0,0 +1,65 @@ + +Common problems and ways to work around them: + +Bootpd complains: "bind: Address already in use" and fails to start. + You are already running something that has bound the + BOOTP listening port number. Check /etc/inetd.conf or + the equivalent for a bootp line (or in startup files). + +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) + +My PC clients running Sun's PC-NFS Pro v1.1 fail to receive +acceptable responses from the bootp server. + + These clients send a request with the DHCP "message length" + option and the (new) BOOTP "broadcast flag" both set. + The bootp server (on SunOS) will send a fragmented reply + unless you override the length with :ms=1024: (or less). + The "broadcast flag" is not yet supported, but there is + a simple work-around, just add :ra=255.255.255.255: + for any clients that need their reply broadcasted. + You may need to use a differnet broadcast address. + (Thanks to Ivan Auger <ivan.auger@wadsworth.org>) + diff --git a/libexec/bootpd/README b/libexec/bootpd/README new file mode 100644 index 000000000000..0901b2578598 --- /dev/null +++ b/libexec/bootpd/README @@ -0,0 +1,135 @@ + +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 +ConvOldTab.sh Script to convert old (1.x) bootptab files +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) +getether.h header for above +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/libexec/bootpd/ToDo b/libexec/bootpd/ToDo new file mode 100644 index 000000000000..261d24c72695 --- /dev/null +++ b/libexec/bootpd/ToDo @@ -0,0 +1,61 @@ +ToDo: -*- text -*- + +---------------------------------------------------------------------- +Memory allocation locality: + +Currently mallocs memory in a very haphazard manner. As such, most of +the program ends up core-resident all the time just to follow all the +stupid pointers around. . . . + +---------------------------------------------------------------------- +Input parser: + +The reader implemented in readfile.c could use improvement. Some sort +of "data-driven" parser should be used so the big switch statements +would have only one case for each data type instead of one case for +every recognized option symbol. Then adding a new tag would involve +only adding a new element to the data table describing known symbols. +Hopefully, this would shrink the code a bit too. -gwr + +---------------------------------------------------------------------- +SLIP Initialization via BOOTP: + +In the function handle_request(), both in bootpd and bootpgw, +we might want to add code like the following just before testing +the client IP address field for zero. (bp->bp_ciaddr == 0) +(David suggests we leave this out for now. -gwr) + +#if 1 /* XXX - Experimental */ + /* + * SLIP initialization support. + * + * If this packet came from a SLIP driver that does + * automatic IP address initialization, then the socket + * will have the IP address and the packet will + * have zeros for both the IP and HW addresses. + * + * Thanks to David P. Maynard <dpm@depend.com> + * for explaining how this works. -gwr + */ + if ((bp->bp_ciaddr.s_addr == 0) && + (bp->bp_htype == 0)) + { + /* Pretend the client knows its address. It will soon. */ + bp->bp_ciaddr = recv_addr.sin_addr; + if (debug) + report(LOG_INFO, "fixed blank request from IP addr %s", + inet_ntoa(recv_addr.sin_addr)); + } +#endif + +---------------------------------------------------------------------- +DHCP Support: + +There is a set of patches from Jeanette Pauline Middelink +<middelin@calvin.polyware.iaf.nl> to add DHCP support. + +Those patches will be integrated into the BOOTP release stream +very soon, but if you can't wait, you can get them from: +nimbus.anu.edu.au:/pub/tridge/samba/contributed/DHCP.patch + +---------------------------------------------------------------------- diff --git a/libexec/bootpd/bootp.h b/libexec/bootpd/bootp.h new file mode 100644 index 000000000000..343efb459327 --- /dev/null +++ b/libexec/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. + * + * + * 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) */ +/* Overhead to fit a bootp message into an Ethernet packet. */ +#define BP_MSG_OVERHEAD (14 + 20 + 8) /* Ethernet + IP + UDP headers */ + +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/libexec/bootpd/bootpd.8 b/libexec/bootpd/bootpd.8 new file mode 100644 index 000000000000..e0b780074ac1 --- /dev/null +++ b/libexec/bootpd/bootpd.8 @@ -0,0 +1,310 @@ +.\" Copyright (c) 1988, 1989, 1991 Carnegie Mellon University +.\" +.Dd May 21, 2019 +.Dt BOOTPD 8 +.Os +.Sh NAME +.Nm bootpd , bootpgw +.Nd Internet Boot Protocol server/gateway +.Sh SYNOPSIS +.Nm +.Op Fl i | s +.Op Fl c Ar chdir-path +.Op Fl d Ar level +.Op Fl h Ar hostname +.Op Fl t Ar timeout +.Oo +.Ar bootptab +.Op Ar dumpfile +.Oc +.Nm bootpgw +.Op Fl i | s +.Op Fl d Ar level +.Op Fl h Ar hostname +.Op Fl t Ar timeout +.Ar server +.Sh DESCRIPTION +The +.Nm +utility +implements an Internet Bootstrap Protocol (BOOTP) server as defined in +RFC951, RFC1532, and RFC1533. +The +.Nm bootpgw +utility 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.\& +.Nm ) +on another subnet. +While either +.Nm +or +.Nm bootpgw +will forward BOOTREPLY packets, only +.Nm bootpgw +will forward BOOTREQUEST packets. +.Pp +One host on each network segment is normally configured to run either +.Nm +or +.Nm bootpgw +from +.Xr inetd 8 +by including one of the following lines in the file +.Pa /etc/inetd.conf : +.Pp +.Dl bootps dgram udp wait root /usr/libexec/bootpd bootpd /etc/bootptab +.Dl bootps dgram udp wait root /usr/libexec/bootpgw bootpgw server +.Pp +This mode of operation is referred to as "inetd mode" and causes +.Nm +(or +.Nm 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 +.Fl t +option controls this timeout (see OPTIONS). +.Pp +It is also possible to run +.Nm +(or +.Nm bootpgw ) +in "standalone mode" (without +.Xr inetd 8 ) +by simply invoking it from a shell like any other regular command. +Standalone mode is particularly useful when +.Nm +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 +.Nm +from within +.Pa /etc/rc.local , +for example.) +Standalone mode is less useful for +.Nm bootpgw +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 +.Fl s +or +.Fl i +option may be used to force standalone or inetd mode respectively +(see OPTIONS). +.Sh OPTIONS +The following options are available: +.Bl -tag -width indent +.It Fl a +Skip ARP table modifications. +.It Fl t Ar timeout +Specify the +.Ar timeout +value (in minutes) that a +.Nm +or +.Nm bootpgw +process will wait for a BOOTP packet before exiting. +If no packets are received for +.Ar timeout +minutes, then the program will exit. +A timeout value of zero means "run forever". +In standalone mode, this option is forced to zero. +.It Fl d Ar debug-level +Set the +.Ar debug-level +variable that controls the amount of debugging messages generated. +For example, +.Fl d Ns 4 +or +.Fl d +4 will set the debugging level to 4. +For compatibility with older versions of +.Nm , +omitting the numeric parameter (i.e., just +.Fl d ) +will simply increment the debug level by one. +.It Fl c Ar chdir-path +Set the current directory used by +.Nm +while checking the existence and size of client boot files. +This is +useful when client boot files are specified as relative pathnames, and +.Nm +needs to use the same current directory as the TFTP server +(typically +.Pa /tftpboot ) . +This option is not recognized by +.Nm bootpgw . +.It Fl h Ar hostname +Specify the hostname corresponding to the IP address to listen on. +By default, +.Nm +listens on the IP address corresponding to the machine's hostname, as +returned by +.Xr gethostname 3 . +.It Fl i +Force inetd mode. +This option is obsolete, but remains for +compatibility with older versions of +.Nm . +.It Fl s +Force standalone mode. +This option is obsolete, but remains for +compatibility with older versions of +.Nm . +.It Ar bootptab +Specify the name of the configuration file from which +.Nm +loads its database of known clients and client options +.No ( Nm +only). +.It Ar dumpfile +Specify the name of the file that +.Nm +will dump its internal database into when it receives a +SIGUSR1 signal +.No ( Nm +only). +This option is only recognized if +.Nm +was compiled with the -DDEBUG flag. +.It Ar server +Specify the name of a BOOTP server to which +.Nm bootpgw +will forward all BOOTREQUEST packets it receives +.Pf ( Nm bootpgw +only). +.El +.Sh OPERATION +Both +.Nm +and +.Nm bootpgw +operate similarly in that both listen for any packets sent to the +.Em bootps +port, and both simply forward any BOOTREPLY packets. +They differ in their handling of BOOTREQUEST packets. +.Pp +When +.Nm bootpgw +is started, it determines the address of a BOOTP server +whose name is provided as a command line parameter. +When +.Nm 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 +.Nm +is started it reads a configuration file, (normally +.Pa /etc/bootptab ) +that initializes the internal database of known clients and client +options. +This internal database is reloaded +from the configuration file when +.Nm +receives a hangup signal (SIGHUP) or when it discovers that the +configuration file has changed. +.Pp +When +.Nm +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, +.Nm +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 +.Nm +is compiled with the -DDEBUG option, receipt of a SIGUSR1 signal causes +it to dump its internal database to the file +.Pa /tmp/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 +.Xr getservbyname 3 +(which normally uses +.Pa /etc/services ) . +Two service names (and port numbers) are used: +.Pp +.Dl bootps BOOTP Server listening port +.Dl bootpc BOOTP Client destination port +.Pp +If the port numbers cannot be determined using +.Xr getservbyname 3 +then the values default to bootps=67 and bootpc=68. +.Sh FILES +.Bl -tag -width /tmp/bootpd.dump -compact +.It Pa /etc/bootptab +Database file read by +.Nm . +.It Pa /tmp/bootpd.dump +Debugging dump file created by +.Nm . +.It Pa /etc/services +Internet service numbers. +.It Pa /tftpboot +Current directory typically used by the TFTP server and +.Nm . +.El +.Sh "SEE ALSO" +.Xr bootptab 5 , +.Xr inetd 8 , +.Xr tftpd 8 +.Pp +DARPA Internet Request For Comments: +.Bl -tag -width RFC1533 -compact +.It RFC951 +Bootstrap Protocol +.It RFC1532 +Clarifications and Extensions for the Bootstrap Protocol +.It RFC1533 +DHCP Options and BOOTP Vendor Extensions +.El +.Sh AUTHORS +This distribution is currently maintained by +.An Walter L. Wimer Aq Mt walt+@cmu.edu . +.Pp +The original BOOTP server was created by +.An Bill Croft +at Stanford University in January 1986. +.Pp +The current version of +.Nm +is primarily the work of +.An David Kovar , +.An Drew D. Perkins , +and +.An Walter L. Wimer , +at Carnegie Mellon University. +.Pp +Enhancements and bug-fixes have been contributed by: +.Pp +(in alphabetical order) +.Pp +.An -split +.An Danny Backx Aq Mt db@sunbim.be +.An John Brezak Aq Mt brezak@ch.hp.com +.An Frank da Cruz Aq Mt fdc@cc.columbia.edu +.An David R. Linn Aq Mt drl@vuse.vanderbilt.edu +.An Jim McKim Aq Mt mckim@lerc.nasa.gov +.An Gordon W. Ross Aq Mt gwr@mc.com +.An Jason Zions Aq Mt jazz@hal.com . +.Sh BUGS +Individual host entries must not exceed 1024 characters. diff --git a/libexec/bootpd/bootpd.c b/libexec/bootpd/bootpd.c new file mode 100644 index 000000000000..f73671dab074 --- /dev/null +++ b/libexec/bootpd/bootpd.c @@ -0,0 +1,1396 @@ +/************************************************************************ + 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. + +************************************************************************/ + +/* + * 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 <sys/utsname.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 <paths.h> +#include <syslog.h> +#include <assert.h> +#include <inttypes.h> + +#ifdef NO_SETSID +# include <fcntl.h> /* for O_RDONLY, etc */ +#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 + */ + +extern void dumptab(char *); + +PRIVATE void catcher(int); +PRIVATE int chk_access(char *, int32 *); +#ifdef VEND_CMU +PRIVATE void dovend_cmu(struct bootp *, struct host *); +#endif +PRIVATE void dovend_rfc1048(struct bootp *, struct host *, int32); +PRIVATE void handle_reply(void); +PRIVATE void handle_request(void); +PRIVATE void sendreply(int forward, int32 dest_override); +PRIVATE void usage(void); + +/* + * 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 */ +}; +int arpmod = TRUE; /* modify the ARP table */ + +/* + * General + */ + +int s; /* Socket file descriptor */ +char *pktbuf; /* Receive packet buffer */ +int pktlen; +char *progname; +char *chdir_path; +struct in_addr my_ip_addr; + +static const char *hostname; +static char default_hostname[MAXHOSTNAMELEN]; + +/* 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. + */ + +int +main(int argc, char **argv) +{ + struct timeval *timeout; + struct bootp *bp; + struct servent *servp; + struct hostent *hep; + char *stmp; + socklen_t ba_len, ra_len; + int n; + int nfound; + fd_set readfds; + int standalone; +#ifdef SA_NOCLDSTOP /* Have POSIX sigaction(2). */ + struct sigaction sa; +#endif + + 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; + + if (gethostname(default_hostname, sizeof(default_hostname) - 1) < 0) { + report(LOG_ERR, "bootpd: can't get hostname\n"); + exit(1); + } + default_hostname[sizeof(default_hostname) - 1] = '\0'; + hostname = default_hostname; + + /* + * Read switches. + */ + for (argc--, argv++; argc > 0; argc--, argv++) { + if (argv[0][0] != '-') + break; + switch (argv[0][1]) { + + case 'a': /* don't modify the ARP table */ + arpmod = FALSE; + break; + case 'c': /* chdir_path */ + if (argv[0][2]) { + stmp = &(argv[0][2]); + } else { + argc--; + argv++; + stmp = argv[0]; + } + if (!stmp || (stmp[0] != '/')) { + report(LOG_ERR, + "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)) { + report(LOG_ERR, + "%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) { + report(LOG_ERR, + "bootpd: missing hostname\n"); + break; + } + hostname = stmp; + 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)) { + report(LOG_ERR, + "%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: + report(LOG_ERR, "%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. + */ + + hep = gethostbyname(hostname); + if (!hep) { + report(LOG_ERR, "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(_PATH_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, + "bootps/udp: unknown service -- using 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, + "bootpc/udp: unknown service -- using port %d", + IPPORT_BOOTPC); + bootpc_port = (u_short) IPPORT_BOOTPC; + } + + /* + * Set up signals to read or dump the table. + */ +#ifdef SA_NOCLDSTOP /* Have POSIX sigaction(2). */ + sa.sa_handler = catcher; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + if (sigaction(SIGHUP, &sa, NULL) < 0) { + report(LOG_ERR, "sigaction: %s", get_errmsg()); + exit(1); + } + if (sigaction(SIGUSR1, &sa, NULL) < 0) { + report(LOG_ERR, "sigaction: %s", get_errmsg()); + exit(1); + } +#else /* SA_NOCLDSTOP */ + /* Old-fashioned UNIX signals */ + if ((int) signal(SIGHUP, catcher) < 0) { + report(LOG_ERR, "signal: %s", get_errmsg()); + exit(1); + } + if ((int) signal(SIGUSR1, catcher) < 0) { + report(LOG_ERR, "signal: %s", get_errmsg()); + exit(1); + } +#endif /* SA_NOCLDSTOP */ + + /* + * Process incoming requests. + */ + FD_ZERO(&readfds); + for (;;) { + struct timeval tv; + + FD_SET(s, &readfds); + if (timeout) + tv = *timeout; + + nfound = select(s + 1, &readfds, NULL, NULL, + (timeout) ? &tv : NULL); + 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 (!FD_ISSET(s, &readfds)) { + if (debug > 1) + report(LOG_INFO, "exiting after %jd minutes of inactivity", + (intmax_t)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_NOTICE, "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; + } + } + return 0; +} + + + + +/* + * Print "usage" message and exit + */ + +PRIVATE void +usage() +{ + fprintf(stderr, + "usage: bootpd [-a] [-i | -s] [-c chdir-path] [-d level] [-h hostname]\n" + " [-t timeout] [bootptab [dumpfile]]\n"); + fprintf(stderr, " -a\tdon't modify ARP table\n"); + fprintf(stderr, " -c n\tset current directory\n"); + fprintf(stderr, " -d n\tset debug level\n"); + fprintf(stderr, " -h n\tset the hostname to listen on\n"); + fprintf(stderr, " -i\tforce inetd mode (run as child of inetd)\n"); + fprintf(stderr, " -s\tforce standalone mode (run without inetd)\n"); + fprintf(stderr, " -t n\tset inetd exit timeout to n minutes\n"); + exit(1); +} + +/* Signal catchers */ +PRIVATE void +catcher(int sig) +{ + if (sig == SIGHUP) + do_readtab = 1; + if (sig == SIGUSR1) + do_dumptab = 1; +#if !defined(SA_NOCLDSTOP) && defined(SYSV) + /* For older "System V" derivatives with no 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(void) +{ + 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; + + if (bp->bp_htype >= hwinfocnt) { + report(LOG_NOTICE, "bad hw addr type %u", bp->bp_htype); + return; + } + bp->bp_file[sizeof(bp->bp_file)-1] = '\0'; + + /* 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 doesn't 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 %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) + report(LOG_NOTICE, "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) { + 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) { + snprintf(realpath, sizeof(realpath), "%s", 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); + +#ifdef CHECK_FILE_ACCESS +null_file_name: +#endif /* CHECK_FILE_ACCESS */ + + + /* + * 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(void) +{ + 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(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, haf; + + /* + * 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 compatibility) + * 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; + haf = (int) bp->bp_htype; + if (haf == 0) + haf = HTYPE_ETHERNET; + + if (arpmod) { + if (debug > 1) + report(LOG_INFO, "setarp %s - %s", + inet_ntoa(dst), haddrtoa(ha, len)); + setarp(s, &dst, haf, 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 publicly 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(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(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(struct bootp *bp, struct host *hp, int32 bootsize) +{ + int bytesleft, len; + byte *vp; + + static const 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) + BP_MSG_OVERHEAD) { + if (debug > 1) + report(LOG_INFO, "request has DHCP msglen=%d", msgsz); + pktlen = msgsz - BP_MSG_OVERHEAD; + } + } + } + + 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/libexec/bootpd/bootpd.h b/libexec/bootpd/bootpd.h new file mode 100644 index 000000000000..11c4a8b41d64 --- /dev/null +++ b/libexec/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/libexec/bootpd/bootpgw/Makefile b/libexec/bootpd/bootpgw/Makefile new file mode 100644 index 000000000000..c9b3feb04da6 --- /dev/null +++ b/libexec/bootpd/bootpgw/Makefile @@ -0,0 +1,11 @@ +# Makefile + +PROG= bootpgw +MAN= +SRCS= bootpgw.c getif.c hwaddr.c report.c rtmsg.c + +SRCDIR= ${.CURDIR}/.. +CFLAGS+=-I${SRCDIR} +.PATH: ${SRCDIR} + +.include <bsd.prog.mk> diff --git a/libexec/bootpd/bootpgw/Makefile.depend b/libexec/bootpd/bootpgw/Makefile.depend new file mode 100644 index 000000000000..344a5d0e9310 --- /dev/null +++ b/libexec/bootpd/bootpgw/Makefile.depend @@ -0,0 +1,16 @@ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + include \ + include/arpa \ + include/xlocale \ + lib/${CSU_DIR} \ + lib/libc \ + lib/libcompiler_rt \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/libexec/bootpd/bootpgw/bootpgw.c b/libexec/bootpd/bootpgw/bootpgw.c new file mode 100644 index 000000000000..168231002c0b --- /dev/null +++ b/libexec/bootpd/bootpgw/bootpgw.c @@ -0,0 +1,674 @@ +/* + * 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. +************************************************************************/ + +/* + * 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 <sys/utsname.h> + +#include <net/if.h> +#include <netinet/in.h> +#include <arpa/inet.h> /* inet_ntoa */ + +#ifndef NO_UNISTD +#include <unistd.h> +#endif + +#include <err.h> +#include <stdlib.h> +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <netdb.h> +#include <paths.h> +#include <syslog.h> +#include <assert.h> + +#ifdef NO_SETSID +# include <fcntl.h> /* for O_RDONLY, etc */ +#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 + */ + +static void usage(void) __dead2; +static void handle_reply(void); +static void handle_request(void); + +/* + * 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 */ +}; +u_char maxhops = 4; /* Number of hops allowed for requests. */ +u_int minwait = 3; /* Number of seconds client must wait before + its bootrequest packets are forwarded. */ +int arpmod = TRUE; /* modify the ARP table */ + +/* + * General + */ + +int s; /* Socket file descriptor */ +char *pktbuf; /* Receive packet buffer */ +int pktlen; +char *progname; +char *servername; +int32 server_ipa; /* Real server IP address, network order. */ + +struct in_addr my_ip_addr; + +struct utsname my_uname; +char *hostname; + + + + + +/* + * Initialization such as command-line processing is done and then the + * main server loop is started. + */ + +int +main(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; + + if (uname(&my_uname) < 0) + errx(1, "can't get hostname"); + hostname = my_uname.nodename; + + hep = gethostbyname(hostname); + 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 'a': /* don't modify the ARP table */ + arpmod = FALSE; + 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)) { + warnx("invalid debug level"); + 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)) + { + warnx("invalid hop count limit"); + break; + } + maxhops = (u_char)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)) { + warnx("invalid timeout specification"); + 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)) + { + warnx("invalid wait time"); + break; + } + minwait = (u_int)n; + break; + + default: + warnx("unknown switch: -%c", argv[0][1]); + usage(); + break; + + } /* switch */ + } /* for args */ + + /* Make sure server name argument is suplied. */ + servername = argv[0]; + if (!servername) { + warnx("missing server name"); + usage(); + } + /* + * Get address of real bootp server. + */ + if (isdigit(servername[0])) + server_ipa = inet_addr(servername); + else { + hep = gethostbyname(servername); + if (!hep) + errx(1, "can't get addr for %s", servername); + bcopy(hep->h_addr, (char *)&server_ipa, sizeof(server_ipa)); + } + + 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(_PATH_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, + "bootps/udp: unknown service -- using 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, + "bootpc/udp: unknown service -- using port %d", + IPPORT_BOOTPC); + bootpc_port = (u_short) IPPORT_BOOTPC; + } + + /* no signal catchers */ + + /* + * Process incoming requests. + */ + for (;;) { + struct timeval tv; + + readfds = 1 << s; + if (timeout) + tv = *timeout; + + nfound = select(s + 1, (fd_set *)&readfds, NULL, NULL, + (timeout) ? &tv : NULL); + 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", + (long)(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 > 3) { + 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; + + switch (bp->bp_op) { + case BOOTREQUEST: + handle_request(); + break; + case BOOTREPLY: + handle_reply(); + break; + } + } + return 0; +} + + + + +/* + * Print "usage" message and exit + */ + +static void +usage() +{ + fprintf(stderr, + "usage: bootpgw [-a] [-i | -s] [-d level] [-h count] [-t timeout]\n" + " [-w time] server\n"); + fprintf(stderr, "\t -a\tdon't modify ARP table\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; + u_short secs; + u_char hops; + + /* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */ + + if (debug) { + report(LOG_INFO, "request from %s", + inet_ntoa(recv_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 = bp->bp_hops; + if (++hops > maxhops) { + report(LOG_NOTICE, "request from %s reached hop limit", + inet_ntoa(recv_addr.sin_addr)); + return; + } + bp->bp_hops = 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; + struct ifreq *ifr; + /* + * 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, &recv_addr.sin_addr); + if (!ifr) { + report(LOG_NOTICE, "no interface for request from %s", + inet_ntoa(recv_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. */ + send_addr.sin_family = AF_INET; + send_addr.sin_port = htons(bootps_port); + send_addr.sin_addr.s_addr = server_ipa; + + /* 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()); + } +} + + + +/* + * Process BOOTREPLY packet. + */ +static void +handle_reply() +{ + struct bootp *bp = (struct bootp *) pktbuf; + struct ifreq *ifr; + struct sockaddr_in *sip; + unsigned char *ha; + int len, haf; + + 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. */ + send_addr.sin_family = AF_INET; + send_addr.sin_addr = bp->bp_yiaddr; + send_addr.sin_port = htons(bootpc_port); + + if (arpmod) { + /* Create an ARP cache entry for the client. */ + ha = bp->bp_chaddr; + len = bp->bp_hlen; + struct in_addr dst; + + if (len > MAXHADDRLEN) + len = MAXHADDRLEN; + haf = (int) bp->bp_htype; + if (haf == 0) + haf = HTYPE_ETHERNET; + + if (debug > 1) + report(LOG_INFO, "setarp %s - %s", + inet_ntoa(dst), haddrtoa(ha, len)); + setarp(s, &dst, haf, ha, len); + } + + /* 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()); + } +} + +/* + * 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/libexec/bootpd/bootptab.5 b/libexec/bootpd/bootptab.5 new file mode 100644 index 000000000000..86158c9f02ff --- /dev/null +++ b/libexec/bootpd/bootptab.5 @@ -0,0 +1,428 @@ +.\" Copyright (c) 1988, 1989, 1991 Carnegie Mellon University +.\" +.Dd October 31, 1991 +.Dt BOOTPTAB 5 +.Os +.Sh NAME +.Nm bootptab +.Nd Internet Bootstrap Protocol server database +.Sh DESCRIPTION +The +.Nm +file is the configuration database file for +.Xr bootpd 8 , +the Internet Bootstrap Protocol server. +Its format is similar to that of +.Xr 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 +.Dl "hostname:tg=value. . . :tg=value. . . :tg=value. . . ." +.Pp +where +.Em hostname +is the actual name of a bootp client (or a "dummy entry"), and +.Em 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 +.Em 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.\& +.Em :tg: ) . +The currently recognized tags are: +.Pp +.Bl -tag -width xxx -compact +.It bf +Bootfile +.It bs +Bootfile size in 512-octet blocks +.It cs +Cookie server address list +.It df +Merit dump file +.It dn +Domain name +.It ds +Domain name server address list +.It ef +Extension file +.It gw +Gateway address list +.It ha +Host hardware address +.It hd +Bootfile home directory +.It hn +Send client's hostname to client +.It ht +Host hardware type (see Assigned Numbers RFC) +.It im +Impress server address list +.It ip +Host IP address +.It lg +Log server address list +.It lp +LPR server address list +.It ns +IEN-116 name server address list +.It nt +NTP (time) Server (RFC 1129) +.It ra +Reply address override +.It rl +Resource location protocol server address list +.It rp +Root path to mount as root +.It sa +TFTP server address client should use +.It sm +Host subnet mask +.It sw +Swap server address +.It tc +Table continuation (points to similar "template" host entry) +.It td +TFTP root directory used by "secure" TFTP servers +.It to +Time offset in seconds from UTC +.It ts +Time server address list +.It vm +Vendor magic cookie selector +.It yd +YP (NIS) domain name +.It ys +YP (NIS) server address +.El +.Pp +There is also a generic tag, +.Pf T Em n , +where +.Em 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 +.Nm bootpd +first. +Generic data may be represented as either a stream of hexadecimal +numbers or as a quoted string of +.Tn 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: +.Em cs , +.Em ds , +.Em gw , +.Em im , +.Em lg , +.Em lp , +.Em ns , +.Em nt , +.Em ra , +.Em rl , +and +.Em ts . +The +.Em ip , +.Em sa , +.Em sw , +.Em sm , +and +.Em 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 +.Nm bootpd +to lookup the IP address for that host name using +.Xr gethostbyname 3 . +If the +.Em ip +tag is not specified, +.Nm 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 +.Em ht +tag specifies the hardware type code as either an unsigned decimal, octal, or +hexadecimal integer or one of the following symbolic names: +.Em ethernet +or +.Em ether +for 10Mb Ethernet, +.Em ethernet3 +or +.Em ether3 +for 3Mb experimental Ethernet, +.Em ieee802 , +.Em tr , +or +.Em token-ring +for IEEE 802 networks, +.Em pronet +for Proteon ProNET Token Ring, or +.Em chaos , +.Em arcnet , +or +.Em ax.25 +for Chaos, ARCNET, and AX.25 Amateur Radio networks, respectively. +The +.Em ha +tag takes a hardware address which may be specified as a host name +or in numeric form. +Note that the numeric form +.Em must +be specified in hexadecimal; optional periods and/or a leading '0x' may be +included for readability. +The +.Em ha +tag must be preceded by the +.Em ht +tag (either explicitly or implicitly; see +.Em tc +below). +If the hardware address is not specified and the type is specified +as either "ethernet" or "ieee802", then +.Nm bootpd +will try to determine the hardware address using +.Xr ether_hostton 3 . +.Pp +The hostname, home directory, and bootfile are +.Tn ASCII +strings which may be +optionally surrounded by double quotes ("). +The client's request and the +values of the +.Em hd +and +.Em 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 +.Em bf +option is specified its value is copied into the reply packet. +If the +.Em 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 +.Em bs Ns =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 +.Xr tftpd 8 +provide a security feature to change their root directory using +the +.Xr chroot 2 +system call. +The +.Em td +tag may be used to inform +.Nm bootpd +of this special root directory used by +.Nm tftpd . +(One may alternatively use the +.Nm bootpd +.Fl c Ar chdir +option.) +The +.Em hd +tag is actually relative to the root directory specified by the +.Em td +tag. +For example, if the real absolute path to your BOOTP client bootfile is +.Pa /tftpboot/bootfiles/bootimage , +and +.Nm tftpd +uses +.Pa /tftpboot +as its "secure" directory, then specify the following in +.Pa bootptab : +.Pp +.Dl :td=/tftpboot:hd=/bootfiles:bf=bootimage: +.Pp +If your bootfiles are located directly in +.Pa /tftpboot , +use: +.Pp +.Dl :td=/tftpboot:hd=/:bf=bootimage: +.Pp +The +.Em 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, +.Nm bootpd +will tell the client to perform TFTP to the same machine +.Nm bootpd +is running on. +.Pp +The time offset +.Em to +may be either a signed decimal integer specifying the client's +time zone offset in seconds from UTC, or the keyword +.Em auto +which uses the server's time zone offset. +Specifying the +.Em to +symbol as a boolean has the same effect as specifying +.Em auto +as its value. +.Pp +The bootfile size +.Em bs +may be either a decimal, octal, or hexadecimal integer specifying the size of +the bootfile in 512-octet blocks, or the keyword +.Em auto +which causes the server to automatically calculate the bootfile size at each +request. +As with the time offset, specifying the +.Em bs +symbol as a boolean has the same effect as specifying +.Em auto +as its value. +.Pp +The vendor magic cookie selector (the +.Em vm +tag) may take one of the following keywords: +.Em auto +(indicating that vendor information is determined by the client's request), +.Em rfc1048 +or +.Em rfc1084 +(which always forces an RFC1084-style reply), or +.Em cmu +(which always forces a CMU-style reply). +.Pp +The +.Em hn +tag is strictly a boolean tag; it does not take the usual equals-sign and +value. +Its presence indicates that the hostname should be sent to RFC1084 +clients. +.Nm 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 +.Em tc +(table continuation) mechanism. +Often, the template entry is a dummy host which does not actually exist and +never sends bootp requests. +This feature is similar to the +.Em tc +feature of +.Xr termcap 5 +for similar terminals. +Note that +.Nm bootpd +allows the +.Em tc +tag symbol to appear anywhere in the host entry, unlike +.Pa termcap +which requires it to be the last tag. +Information explicitly specified for a +host always overrides information implied by a +.Em tc +tag symbol, regardless of its location within the entry. +The +value of the +.Em 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 +.Em tc . +This can be done using the construction +.Em tag Ns @ +which removes the effect of +.Em tag +as in +.Xr termcap 5 . +For example, to completely undo an IEN-116 name server specification, use +.Em :ns@: +at an appropriate place in the configuration entry. +After removal +with +.Em @ , +a tag is eligible to be set again through the +.Em 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 +.Pa /etc/bootptab +file follows: +.Bd -literal -offset indent +# 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: +.Ed +.Sh FILES +.Bl -tag -width /etc/bootptab -compact +.It Pa /etc/bootptab +.El +.Sh "SEE ALSO" +.Xr bootpd 8 , +.Xr tftpd 8 +.Pp +DARPA Internet Request For Comments RFC951, RFC1048, RFC1084, Assigned Numbers diff --git a/libexec/bootpd/bootptab.cmu b/libexec/bootpd/bootptab.cmu new file mode 100644 index 000000000000..66212d421a23 --- /dev/null +++ b/libexec/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/libexec/bootpd/bootptab.mcs b/libexec/bootpd/bootptab.mcs new file mode 100644 index 000000000000..1d5c78788038 --- /dev/null +++ b/libexec/bootpd/bootptab.mcs @@ -0,0 +1,90 @@ +# /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 +bach: tc=.subnet16:ha="08:00:20:04:98:8d":bf=boot.sun4m: +xanadu:tc=.subnet17:ha="00:80:42:42:04:c7":bf=boot.sun4c: diff --git a/libexec/bootpd/bptypes.h b/libexec/bootpd/bptypes.h new file mode 100644 index 000000000000..3e5deaec8bd5 --- /dev/null +++ b/libexec/bootpd/bptypes.h @@ -0,0 +1,20 @@ +#ifndef BPTYPES_H +#define BPTYPES_H + +#include <sys/types.h> + +/* + * 32 bit integers are different types on various architectures + */ + +#define int32 int32_t +#define u_int32 u_int32_t + +/* + * Nice typedefs. . . + */ + +typedef int boolean; +typedef unsigned char byte; + +#endif /* BPTYPES_H */ diff --git a/libexec/bootpd/dovend.c b/libexec/bootpd/dovend.c new file mode 100644 index 000000000000..b32459ae3347 --- /dev/null +++ b/libexec/bootpd/dovend.c @@ -0,0 +1,385 @@ +/* + * 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> + +#include "bootp.h" +#include "bootpd.h" +#include "report.h" +#include "dovend.h" + +PRIVATE int insert_generic(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(struct host *hp, byte *buf, int len) +{ + int bytesleft = len; + byte *vp = buf; + + static const 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, "ys"); + } + 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), "yn"); + *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, "nt"); + } + /* + * 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). + */ + char *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(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(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(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/libexec/bootpd/dovend.h b/libexec/bootpd/dovend.h new file mode 100644 index 000000000000..4b9f7d6baa04 --- /dev/null +++ b/libexec/bootpd/dovend.h @@ -0,0 +1,5 @@ +/* dovend.h */ + +extern int dovend_rfc1497(struct host *hp, u_char *buf, int len); +extern int insert_ip(byte, struct in_addr_list *, byte **, int *); +extern void insert_u_long(u_int32, u_char **); diff --git a/libexec/bootpd/dumptab.c b/libexec/bootpd/dumptab.c new file mode 100644 index 000000000000..9b839c26c5b9 --- /dev/null +++ b/libexec/bootpd/dumptab.c @@ -0,0 +1,361 @@ +/* + * 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 <strings.h> +#include <syslog.h> +#include <time.h> + +#include "bootp.h" +#include "hash.h" +#include "hwaddr.h" +#include "report.h" +#include "patchlevel.h" +#include "bootpd.h" + +#ifdef DEBUG +static void dump_generic(FILE *, struct shared_bindata *); +static void dump_host(FILE *, struct host *); +static void list_ipaddresses(FILE *, struct in_addr_list *); +#endif + +#ifndef DEBUG +void +dumptab(char *filename) +{ + report(LOG_INFO, "No dumptab support!"); +} + +#else /* DEBUG */ + +/* + * Dump the internal memory database to bootpd_dump. + */ + +void +dumptab(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(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, "%lu:", (u_long)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=%lu:", (u_long)hp->msg_size); + } + if (hp->flags.min_wait) { + fprintf(fp, "\\\n\t:mw=%lu:", (u_long)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:", (long)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(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 address. + * + * Null lists produce no output (and no error). + */ + +static void +list_ipaddresses(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/libexec/bootpd/getether.c b/libexec/bootpd/getether.c new file mode 100644 index 000000000000..1a49ce574d38 --- /dev/null +++ b/libexec/bootpd/getether.c @@ -0,0 +1,385 @@ +/* + * 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> + +#ifndef NO_UNISTD +#include <unistd.h> +#endif + +#include <ctype.h> +#include <paths.h> +#include <string.h> +#include <syslog.h> + +#include "getether.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)); + strlcpy(&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(__FreeBSD__) || defined(__NetBSD__) +/* Thanks to John Brezak <brezak@ch.hp.com> for this code. */ +#include <sys/ioctl.h> +#include <sys/time.h> +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_types.h> + +int +getether(char *ifname, char *eap) +{ + int fd, rc = -1; + int n; + struct ifreq ibuf[16]; + struct ifconf ifc; + 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> +#include <string.h> +#ifndef NULL +#define NULL 0 +#endif + +int +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 */ + + snprintf(devname, sizeof(devname), "%s%s", _PATH_DEV, 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 a 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> and + * updated by Pauline Middelink <middelin@polyware.iaf.nl> + * + * 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 <memory.h> +#include <sys/ioctl.h> +#include <net/if.h> /* struct ifreq */ +#include <sys/socketio.h> /* Needed for IOCTL defs */ + +int +getether(ifname, eap) + char *ifname, *eap; +{ + int rc = -1; + int fd; + struct ifreq phys; + + memset(&phys, 0, 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 { + memcpy(eap, &phys.ifr_hwaddr.sa_data, 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 +int +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/libexec/bootpd/getether.h b/libexec/bootpd/getether.h new file mode 100644 index 000000000000..a68b1f8f12f5 --- /dev/null +++ b/libexec/bootpd/getether.h @@ -0,0 +1,3 @@ +/* getether.h */ + +extern int getether(char *ifname, char *eaptr); diff --git a/libexec/bootpd/getif.c b/libexec/bootpd/getif.c new file mode 100644 index 000000000000..6d2ca48eaf42 --- /dev/null +++ b/libexec/bootpd/getif.c @@ -0,0 +1,142 @@ +/* + * getif.c : get an interface structure + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> + +#if defined(SUNOS) || defined(SVR4) +#include <sys/sockio.h> +#endif +#ifdef SVR4 +#include <sys/stropts.h> +#endif + +#include <sys/time.h> /* for struct timeval in net/if.h */ +#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(u_char *ca, u_char *cb); + +/* Return a pointer to the interface struct for the passed address. */ +struct ifreq * +getif(int s, struct in_addr *addrp) +{ + 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((u_char *)addrp, (u_char *)&(sip->sin_addr)); + if (m > maxmatch) { + maxmatch = m; + ifrmax = ifrq; + } +#ifndef IFNAMSIZ + /* BSD not defined or earlier than 4.3 */ + incr = sizeof(*ifrq); +#else + incr = ifrq->ifr_addr.sa_len + IFNAMSIZ; +#endif + + p += incr; + len -= incr; + } + + return ifrmax; +} + +/* + * Return the number of leading bits matching in the + * internet addresses supplied. + */ +static int +nmatch(u_char *ca, u_char *cb) +{ + 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/libexec/bootpd/getif.h b/libexec/bootpd/getif.h new file mode 100644 index 000000000000..3199b6c151dc --- /dev/null +++ b/libexec/bootpd/getif.h @@ -0,0 +1,3 @@ +/* getif.h */ + +extern struct ifreq *getif(int, struct in_addr *); diff --git a/libexec/bootpd/hash.c b/libexec/bootpd/hash.c new file mode 100644 index 000000000000..735373c2f654 --- /dev/null +++ b/libexec/bootpd/hash.c @@ -0,0 +1,385 @@ +/************************************************************************ + 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 of the + * Information Technology Center at Carnegie Mellon. + */ + + +#include <sys/types.h> +#include <stdlib.h> +#include <strings.h> + +#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 + +PRIVATE void hashi_FreeMembers(hash_member *, hash_freefp); + + + + +/* + * 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(unsigned tablesize) +{ + hash_tbl *hashtblptr; + 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(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(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(unsigned char *string, unsigned len) +{ + 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(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 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(hash_tbl *hashtable, unsigned hashcode, hash_cmpfp compare, + hash_datum *key, hash_datum *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(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(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(hash_tbl *hashtable) +{ + unsigned bucket; + 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(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/libexec/bootpd/hash.h b/libexec/bootpd/hash.h new file mode 100644 index 000000000000..14aefe969d0d --- /dev/null +++ b/libexec/bootpd/hash.h @@ -0,0 +1,147 @@ +#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 + * guaranteed to use the best hash algorithm in existence. . . . + */ + + + +/* + * Various hash table definitions + */ + + +/* + * Define "hash_datum" as a universal data type + */ +typedef void hash_datum; + +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? */ + +typedef int (*hash_cmpfp)(hash_datum *, hash_datum *); +typedef void (*hash_freefp)(hash_datum *); + +extern hash_tbl *hash_Init(u_int tablesize); + +extern void hash_Reset(hash_tbl *tbl, hash_freefp); + +extern unsigned hash_HashFunction(u_char *str, u_int len); + +extern int hash_Exists(hash_tbl *, u_int code, + hash_cmpfp, hash_datum *key); + +extern int hash_Insert(hash_tbl *, u_int code, + hash_cmpfp, hash_datum *key, + hash_datum *element); + +extern int hash_Delete(hash_tbl *, u_int code, + hash_cmpfp, hash_datum *key, + hash_freefp); + +extern hash_datum *hash_Lookup(hash_tbl *, u_int code, + hash_cmpfp, hash_datum *key); + +extern hash_datum *hash_FirstEntry(hash_tbl *); + +extern hash_datum *hash_NextEntry(hash_tbl *); + +#endif /* HASH_H */ diff --git a/libexec/bootpd/hwaddr.c b/libexec/bootpd/hwaddr.c new file mode 100644 index 000000000000..4bac34957545 --- /dev/null +++ b/libexec/bootpd/hwaddr.c @@ -0,0 +1,333 @@ +/* + * 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 + +#ifdef _AIX32 +#include <sys/time.h> /* for struct timeval in net/if.h */ +#include <net/if.h> /* for struct ifnet in net/if_arp.h */ +#endif + +#include <net/if_arp.h> +#include <netinet/in.h> + +#ifdef WIN_TCP +#include <netinet/if_ether.h> +#include <sys/dlpi.h> +#endif + +#include <stdio.h> +#ifndef NO_UNISTD +#include <unistd.h> +#endif +#include <syslog.h> + +#ifndef ATF_INUSE /* Not defined on some systems (i.e. Linux) */ +#define ATF_INUSE 0 +#endif + +/* For BSD 4.4, set arp entry by writing to routing socket */ +#if defined(BSD) +#if BSD >= 199306 +extern int bsd_arp_set(struct in_addr *, char *, int); +#endif +#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(int s, struct in_addr *ia, int hafamily, u_char *haddr, int halen) +{ +#ifdef SIOCSARP +#ifdef WIN_TCP + /* This is an SVR4 with different networking code from + * Wollongong WIN-TCP. Not quite like the Lachman code. + * Code from: drew@drewsun.FEITH.COM (Andrew B. Sudell) + */ +#undef SIOCSARP +#define SIOCSARP ARP_ADD + struct arptab arpreq; /* Arp table entry */ + + bzero((caddr_t) &arpreq, sizeof(arpreq)); + arpreq.at_flags = ATF_COM; + + /* Set up IP address */ + arpreq.at_in = ia->s_addr; + + /* Set up Hardware Address */ + bcopy(haddr, arpreq.at_enaddr, halen); + + /* Set the Date Link type. */ + /* XXX - Translate (hafamily) to dltype somehow? */ + arpreq.at_dltype = DL_ETHER; + +#else /* WIN_TCP */ + /* Good old Berkeley way. */ + struct arpreq arpreq; /* Arp request ioctl block */ + struct sockaddr_in *si; + char *p; + + 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. */ +#ifdef __linux__ /* XXX - Do others need this? -gwr */ + /* + * Linux requires the sa_family field set. + * longyear@netcom.com (Al Longyear) + */ + arpreq.arp_ha.sa_family = hafamily; +#endif /* linux */ + + /* This variable is just to help catch type mismatches. */ + p = arpreq.arp_ha.sa_data; + bcopy(haddr, p, halen); +#endif /* WIN_TCP */ + +#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>, + */ + { + int fd; + struct strioctl iocb; + + 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 */ +#if defined(BSD) && (BSD >= 199306) + bsd_arp_set(ia, haddr, halen); +#else + /* + * Oh well, SIOCSARP is not defined. Just run arp(8). + * Need to delete partial entry first on some systems. + * XXX - Gag! + */ + int status; + char buf[256]; + char *a; + extern char *inet_ntoa(); + + a = inet_ntoa(*ia); + snprintf(buf, sizeof(buf), "arp -d %s; arp -s %s %s temp", + a, a, haddrtoa(haddr, halen)); + if (debug > 2) + report(LOG_INFO, "%s", buf); + status = system(buf); + if (status) + report(LOG_ERR, "arp failed, exit code=0x%x", status); + return; +#endif /* ! 4.4 BSD */ +#endif /* SIOCSARP */ +} + + +/* + * Convert a hardware address to an ASCII string. + */ +char * +haddrtoa(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(u_char *addr_in, u_char *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(int n) +{ + int i, r; + + r = 0; + for (i = 0; i < 8; i++) { + r <<= 1; + r |= (n & 1); + n >>= 1; + } + return r; +} + +void +main(void) +{ + 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/libexec/bootpd/hwaddr.h b/libexec/bootpd/hwaddr.h new file mode 100644 index 000000000000..8381666eea49 --- /dev/null +++ b/libexec/bootpd/hwaddr.h @@ -0,0 +1,34 @@ +/* + * 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; + +extern void setarp(int, struct in_addr *, int, u_char *, int); +extern char *haddrtoa(u_char *, int); +extern void haddr_conv802(u_char *, u_char *, int); + +/* + * 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/libexec/bootpd/lookup.c b/libexec/bootpd/lookup.c new file mode 100644 index 000000000000..c520e7a9004c --- /dev/null +++ b/libexec/bootpd/lookup.c @@ -0,0 +1,117 @@ +/* + * lookup.c - Lookup IP address, HW address, netmask + */ + +#include <sys/types.h> +#include <sys/socket.h> + +#include <sys/time.h> /* for struct timeval in net/if.h */ +#include <net/if.h> +#include <netinet/in.h> + +#ifdef ETC_ETHERS +#include <net/ethernet.h> +#endif + +#include <netdb.h> +#include <strings.h> +#include <syslog.h> + +#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(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(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. + * + * Both arguments are in network order + */ +int +lookup_netmask(u_int32 addr, 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/libexec/bootpd/lookup.h b/libexec/bootpd/lookup.h new file mode 100644 index 000000000000..3b1890967351 --- /dev/null +++ b/libexec/bootpd/lookup.h @@ -0,0 +1,7 @@ +/* lookup.h */ + +#include "bptypes.h" /* for int32, u_int32 */ + +extern u_char *lookup_hwa(char *hostname, int htype); +extern int lookup_ipa(char *hostname, u_int32 *addr); +extern int lookup_netmask(u_int32 addr, u_int32 *mask); diff --git a/libexec/bootpd/patchlevel.h b/libexec/bootpd/patchlevel.h new file mode 100644 index 000000000000..2cc0f4e0ae1f --- /dev/null +++ b/libexec/bootpd/patchlevel.h @@ -0,0 +1,6 @@ +/* + * patchlevel.h + */ + +#define VERSION "2.4" +#define PATCHLEVEL 3 diff --git a/libexec/bootpd/readfile.c b/libexec/bootpd/readfile.c new file mode 100644 index 000000000000..1d9ff2163395 --- /dev/null +++ b/libexec/bootpd/readfile.c @@ -0,0 +1,2035 @@ +/************************************************************************ + 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 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> + +#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. + */ + +boolean nmcmp(hash_datum *, hash_datum *); + +PRIVATE void + adjust(char **); +PRIVATE void + del_string(struct shared_string *); +PRIVATE void + del_bindata(struct shared_bindata *); +PRIVATE void + del_iplist(struct in_addr_list *); +PRIVATE void + eat_whitespace(char **); +PRIVATE int + eval_symbol(char **, struct host *); +PRIVATE void + fill_defaults(struct host *, char **); +PRIVATE void + free_host(hash_datum *); +PRIVATE struct in_addr_list * + get_addresses(char **); +PRIVATE struct shared_string * + get_shared_string(char **); +PRIVATE char * + get_string(char **, char *, u_int *); +PRIVATE u_int32 + get_u_long(char **); +PRIVATE boolean + goodname(char *); +PRIVATE boolean + hwinscmp(hash_datum *, hash_datum *); +PRIVATE int + interp_byte(char **, byte *); +PRIVATE void + makelower(char *); +PRIVATE boolean + nullcmp(hash_datum *, hash_datum *); +PRIVATE int + process_entry(struct host *, char *); +PRIVATE int + process_generic(char **, struct shared_bindata **, u_int); +PRIVATE byte * + prs_haddr(char **, u_int); +PRIVATE int + prs_inetaddr(char **, u_int32 *); +PRIVATE void + read_entry(FILE *, char *, u_int *); +PRIVATE char * + smalloc(u_int); + + +/* + * 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(void) +{ + 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(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, haddrlength(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 preceded by a backslash cause + * line-continuation onto the next line. The entry is terminated by a + * newline character which is not preceded 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(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. . . .*/ + } + /* FALLTHROUGH */ + 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 */ + } + } + /* FALLTHROUGH 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(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 '/')"; + break; + case E_BAD_VALUE: + msg = "bad value"; + break; + default: + msg = "unknown 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 unsigned integer value for MEMBER */ +#define PARSE_UINT(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(char **symbol, struct host *hp) +{ + char tmpstr[MAXSTRINGLEN]; + byte *tmphaddr; + 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); + snprintf(current_tagname, sizeof(current_tagname), + "T%d", (int)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", (int*)&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_UINT(msg_size); + if (hp->msg_size < BP_MINPKTSZ || + hp->msg_size > MAX_MSG_SIZE) + return E_BAD_VALUE; + break; + + case SYM_MIN_WAIT: + PARSE_UINT(min_wait); + 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(char **src, char *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(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(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(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(hash_datum *d1, hash_datum *d2) +{ + return FALSE; +} + + +/* + * Function for comparing a string with the hostname field of a host + * structure. + */ + +boolean +nmcmp(hash_datum *d1, hash_datum *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(hash_datum *d1, hash_datum *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(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(char **s) +{ + 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(char **s) +{ + char *t; + + t = *s; + while (*t && isspace(*t)) { + t++; + } + *s = t; +} + + + +/* + * This function converts the given string to all lowercase. + */ + +PRIVATE void +makelower(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(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(char **src, u_int32 *result) +{ + char tmpstr[MAXSTRINGLEN]; + u_int32 value; + u_int32 parts[4], *pp; + int n; + char *s, *t; + + /* 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; + } + + /* + * 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(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 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. */ + } + + hap = haddr; + while (hap < haddr + hal) { + if ((*p == '.') || (*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(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(char **src) +{ + 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(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(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(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(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(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(hash_datum *d1, hash_datum *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(hash_datum *d1, hash_datum *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/libexec/bootpd/readfile.h b/libexec/bootpd/readfile.h new file mode 100644 index 000000000000..5da0c42af4a2 --- /dev/null +++ b/libexec/bootpd/readfile.h @@ -0,0 +1,10 @@ +/* readfile.h */ + +#include "bptypes.h" +#include "hash.h" + +extern boolean hwlookcmp(hash_datum *, hash_datum *); +extern boolean iplookcmp(hash_datum *, hash_datum *); +extern boolean nmcmp(hash_datum *, hash_datum *); +extern void readtab(int); +extern void rdtab_init(void); diff --git a/libexec/bootpd/report.c b/libexec/bootpd/report.c new file mode 100644 index 000000000000..eac6a100dfcb --- /dev/null +++ b/libexec/bootpd/report.c @@ -0,0 +1,136 @@ + +/* + * report() - calls syslog + */ + +#include <stdarg.h> + +#include <stdio.h> +#include <syslog.h> +#include <string.h> +#include <errno.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(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. + */ +void +report(int priority, const char *fmt,...) +{ + va_list ap; + static char buf[128]; + + if ((priority < 0) || (priority >= numlevels)) { + priority = numlevels - 1; + } + va_start(ap, fmt); + vsnprintf(buf, sizeof(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. + */ +const char * +get_errmsg(void) +{ + 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/libexec/bootpd/report.h b/libexec/bootpd/report.h new file mode 100644 index 000000000000..a51e5f27f1f1 --- /dev/null +++ b/libexec/bootpd/report.h @@ -0,0 +1,5 @@ +/* report.h */ + +extern void report_init(int nolog); +extern void report(int, const char *, ...) __printflike(2, 3); +extern const char *get_errmsg(void); diff --git a/libexec/bootpd/rtmsg.c b/libexec/bootpd/rtmsg.c new file mode 100644 index 000000000000..f2dbaf6eae33 --- /dev/null +++ b/libexec/bootpd/rtmsg.c @@ -0,0 +1,236 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1984, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1994 + * Geoffrey M. Rehmet, All rights reserved. + * + * This code is derived from software which forms part of the 4.4-Lite + * Berkeley software distribution, which was in derived from software + * contributed to Berkeley by Sun Microsystems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the 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 BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * from arp.c 8.2 (Berkeley) 1/2/94 + */ + +#include <sys/param.h> +/* + * Verify that we are at least 4.4 BSD + */ +#if defined(BSD) +#if BSD >= 199306 + +#include <sys/socket.h> +#include <sys/filio.h> +#include <sys/time.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_types.h> +#include <net/route.h> + +#include <netinet/in.h> +#include <netinet/if_ether.h> + +#include <arpa/inet.h> + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> + +#include "report.h" + + +static int rtmsg(int); + +static int s = -1; /* routing socket */ + + +/* + * Open the routing socket + */ +static void getsocket () { + if (s < 0) { + s = socket(PF_ROUTE, SOCK_RAW, 0); + if (s < 0) { + report(LOG_ERR, "socket %s", strerror(errno)); + exit(1); + } + } else { + /* + * Drain the socket of any unwanted routing messages. + */ + int n; + char buf[512]; + + ioctl(s, FIONREAD, &n); + while (n > 0) { + read(s, buf, sizeof buf); + ioctl(s, FIONREAD, &n); + } + } +} + +static struct sockaddr_in so_mask = {8, 0, 0, { 0xffffffff}}; +static struct sockaddr_in blank_sin = {sizeof(blank_sin), AF_INET }, sin_m; +static struct sockaddr_dl blank_sdl = {sizeof(blank_sdl), AF_LINK }, sdl_m; +static int expire_time, flags, doing_proxy; +static struct { + struct rt_msghdr m_rtm; + char m_space[512]; +} m_rtmsg; + +/* + * Set an individual arp entry + */ +int +bsd_arp_set(struct in_addr *ia, char *eaddr, int len) +{ + struct sockaddr_in *sin = &sin_m; + struct sockaddr_dl *sdl; + struct rt_msghdr *rtm = &(m_rtmsg.m_rtm); + u_char *ea; + struct timespec tp; + int op = RTM_ADD; + + getsocket(); + sdl_m = blank_sdl; + sin_m = blank_sin; + sin->sin_addr = *ia; + + ea = (u_char *)LLADDR(&sdl_m); + bcopy(eaddr, ea, len); + sdl_m.sdl_alen = len; + doing_proxy = flags = expire_time = 0; + + /* make arp entry temporary */ + clock_gettime(CLOCK_MONOTONIC, &tp); + expire_time = tp.tv_sec + 20 * 60; + +tryagain: + if (rtmsg(RTM_GET) < 0) { + report(LOG_WARNING, "rtmget: %s", strerror(errno)); + return (1); + } + sin = (struct sockaddr_in *)(rtm + 1); + sdl = (struct sockaddr_dl *)(sin->sin_len + (char *)sin); + if (sin->sin_addr.s_addr == sin_m.sin_addr.s_addr) { + if (sdl->sdl_family == AF_LINK && + !(rtm->rtm_flags & RTF_GATEWAY)) switch (sdl->sdl_type) { + case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023: + case IFT_ISO88024: case IFT_ISO88025: + op = RTM_CHANGE; + goto overwrite; + } + if (doing_proxy == 0) { + report(LOG_WARNING, "set: can only proxy for %s\n", + inet_ntoa(sin->sin_addr)); + return (1); + } + goto tryagain; + } +overwrite: + if (sdl->sdl_family != AF_LINK) { + report(LOG_WARNING, + "cannot intuit interface index and type for %s\n", + inet_ntoa(sin->sin_addr)); + return (1); + } + sdl_m.sdl_type = sdl->sdl_type; + sdl_m.sdl_index = sdl->sdl_index; + return (rtmsg(op)); +} + + +static int +rtmsg(int cmd) +{ + static int seq; + int rlen; + struct rt_msghdr *rtm = &m_rtmsg.m_rtm; + char *cp = m_rtmsg.m_space; + int l; + + errno = 0; + bzero((char *)&m_rtmsg, sizeof(m_rtmsg)); + rtm->rtm_flags = flags; + rtm->rtm_version = RTM_VERSION; + + switch (cmd) { + default: + report(LOG_ERR, "set_arp: internal wrong cmd - exiting"); + exit(1); + case RTM_ADD: + case RTM_CHANGE: + rtm->rtm_addrs |= RTA_GATEWAY; + rtm->rtm_rmx.rmx_expire = expire_time; + rtm->rtm_inits = RTV_EXPIRE; + rtm->rtm_flags |= (RTF_HOST | RTF_STATIC | RTF_LLDATA); + if (doing_proxy) { + rtm->rtm_addrs |= RTA_NETMASK; + rtm->rtm_flags &= ~RTF_HOST; + } + /* FALLTHROUGH */ + case RTM_GET: + rtm->rtm_addrs |= RTA_DST; + } +#define NEXTADDR(w, s) \ + if (rtm->rtm_addrs & (w)) { \ + bcopy((char *)&s, cp, sizeof(s)); cp += sizeof(s);} + + NEXTADDR(RTA_DST, sin_m); + NEXTADDR(RTA_GATEWAY, sdl_m); + NEXTADDR(RTA_NETMASK, so_mask); + + rtm->rtm_msglen = cp - (char *)&m_rtmsg; + + l = rtm->rtm_msglen; + rtm->rtm_seq = ++seq; + rtm->rtm_type = cmd; + if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) { + if ((errno != ESRCH) && !(errno == EEXIST && cmd == RTM_ADD)){ + report(LOG_WARNING, "writing to routing socket: %s", + strerror(errno)); + return (-1); + } + } + do { + l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); + } while (l > 0 && (rtm->rtm_type != cmd || rtm->rtm_seq != seq || rtm->rtm_pid != getpid())); + if (l < 0) + report(LOG_WARNING, "arp: read from routing socket: %s\n", + strerror(errno)); + return (0); +} + +#endif /* BSD */ +#endif /* BSD >= 199306 */ diff --git a/libexec/bootpd/syslog.conf b/libexec/bootpd/syslog.conf new file mode 100644 index 000000000000..2c135af4974e --- /dev/null +++ b/libexec/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/libexec/bootpd/tools/Makefile b/libexec/bootpd/tools/Makefile new file mode 100644 index 000000000000..175a2cc2fa60 --- /dev/null +++ b/libexec/bootpd/tools/Makefile @@ -0,0 +1,5 @@ +# Makefile + +SUBDIR= bootpef bootptest + +.include <bsd.subdir.mk> diff --git a/libexec/bootpd/tools/Makefile.inc b/libexec/bootpd/tools/Makefile.inc new file mode 100644 index 000000000000..1017c3e74630 --- /dev/null +++ b/libexec/bootpd/tools/Makefile.inc @@ -0,0 +1,7 @@ +# Makefile.inc + +BINDIR= /usr/sbin + +WARNS?= 1 + +.include "../Makefile.inc" diff --git a/libexec/bootpd/tools/bootpef/Makefile b/libexec/bootpd/tools/bootpef/Makefile new file mode 100644 index 000000000000..bf51013c39c7 --- /dev/null +++ b/libexec/bootpd/tools/bootpef/Makefile @@ -0,0 +1,12 @@ +# Makefile + +PROG= bootpef +MAN= bootpef.8 +SRCS= bootpef.c dovend.c readfile.c hash.c dumptab.c lookup.c \ + hwaddr.c report.c tzone.c rtmsg.c + +SRCDIR= ${.CURDIR}/../.. +CFLAGS+=-I${SRCDIR} +.PATH: ${SRCDIR} + +.include <bsd.prog.mk> diff --git a/libexec/bootpd/tools/bootpef/Makefile.depend b/libexec/bootpd/tools/bootpef/Makefile.depend new file mode 100644 index 000000000000..344a5d0e9310 --- /dev/null +++ b/libexec/bootpd/tools/bootpef/Makefile.depend @@ -0,0 +1,16 @@ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + include \ + include/arpa \ + include/xlocale \ + lib/${CSU_DIR} \ + lib/libc \ + lib/libcompiler_rt \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/libexec/bootpd/tools/bootpef/bootpef.8 b/libexec/bootpd/tools/bootpef/bootpef.8 new file mode 100644 index 000000000000..f410dfd9c635 --- /dev/null +++ b/libexec/bootpd/tools/bootpef/bootpef.8 @@ -0,0 +1,63 @@ +.\" +.\" bootpef.8 +.Dd December 4, 1993 +.Dt BOOTPEF 8 +.Os +.Sh NAME +.Nm bootpef +.Nd "BOOTP Extension File compiler" +.Sh SYNOPSIS +.Bk -words +.Nm +.Op Fl c Ar chdir\-path +.Op Fl d Ar debug\-level +.Op Fl f Ar config\-file +.Op Ar client\-name ... +.Ek +.Sh DESCRIPTION +The +.Nm +utility builds the +.Em "Extension Path" +files described by +.%T "RFC 1497" +(tag 18). +If any +.Ar client\-name +arguments are specified, then +.Nm +compiles the extension files for only those clients. +.Sh OPTIONS +.Bl -tag -width indent +.It Fl c Ar chdir\-path +Sets the current directory used by +.Nm +while creating extension files. +This is useful when the +extension file names are specified as relative pathnames, and +.Nm +needs to use the same current directory as the TFTP server +(typically +.Pa /tftpboot ) . +.It Fl d Ar debug\-level +Sets the +.Ar debug\-level +variable that controls the amount of debugging messages generated. +For example, +.Fl d Ar 4 +will set the debugging level to 4. +.It Fl f Ar config\-file +Set the name of the config file that specifies the option +data to be sent to each client. +.El +.Sh SEE ALSO +.Xr bootpd 8 , +.Xr tftpd 8 +.Rs +.%O RFC951 +.%T "BOOTSTRAP PROTOCOL (BOOTP)" +.Re +.Rs +.%O RFC1497 +.%T "BOOTP Vendor Information Extensions" +.Re diff --git a/libexec/bootpd/tools/bootpef/bootpef.c b/libexec/bootpd/tools/bootpef/bootpef.c new file mode 100644 index 000000000000..f8b2eeeaaf2d --- /dev/null +++ b/libexec/bootpd/tools/bootpef/bootpef.c @@ -0,0 +1,318 @@ +/************************************************************************ + 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. + +************************************************************************/ + +/* + * 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 + */ + + + +#include <stdarg.h> + +#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> + +#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 + */ + +static void mktagfile(struct host *); +static void usage(void) __dead2; + +/* + * 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(void) +{ + 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. + */ +int +main(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); + } + return (0); +} + + + +/* + * Make a "TAG 18" file for this host. + * (Insert the RFC1497 options.) + */ + +static void +mktagfile(struct host *hp) +{ + FILE *fp; + int bytesleft, len; + byte *vp; + + 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); + +} /* mktagfile */ + +/* + * 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/libexec/bootpd/tools/bootptest/Makefile b/libexec/bootpd/tools/bootptest/Makefile new file mode 100644 index 000000000000..3a6fbd584606 --- /dev/null +++ b/libexec/bootpd/tools/bootptest/Makefile @@ -0,0 +1,11 @@ +# Makefile + +PROG= bootptest +MAN= bootptest.8 +SRCS= bootptest.c getether.c getif.c print-bootp.c report.c + +SRCDIR= ${.CURDIR}/../.. +CFLAGS+=-I${SRCDIR} +.PATH: ${SRCDIR} + +.include <bsd.prog.mk> diff --git a/libexec/bootpd/tools/bootptest/Makefile.depend b/libexec/bootpd/tools/bootptest/Makefile.depend new file mode 100644 index 000000000000..344a5d0e9310 --- /dev/null +++ b/libexec/bootpd/tools/bootptest/Makefile.depend @@ -0,0 +1,16 @@ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + include \ + include/arpa \ + include/xlocale \ + lib/${CSU_DIR} \ + lib/libc \ + lib/libcompiler_rt \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/libexec/bootpd/tools/bootptest/bootptest.8 b/libexec/bootpd/tools/bootptest/bootptest.8 new file mode 100644 index 000000000000..97de293d6590 --- /dev/null +++ b/libexec/bootpd/tools/bootptest/bootptest.8 @@ -0,0 +1,76 @@ +.\" +.\" bootptest.8 +.Dd June 10, 1993 +.Dt BOOTPTEST 8 +.Os +.Sh NAME +.Nm bootptest +.Nd "send BOOTP queries and print responses" +.Sh SYNOPSIS +.Nm +.Op Fl f Ar bootfile +.Op Fl h +.Op Fl m Ar magic_number +.Ar server\-name +.Op Ar template\-file +.Sh DESCRIPTION +The +.Nm +utility sends BOOTP requests to the host specified as +.Ar server\-name +at one\-second intervals until either a response is received, +or until ten requests have gone unanswered. +After a response is received, +.Nm +will wait one more second listening for additional responses. +.Sh OPTIONS +.Bl -tag -width indent +.It Fl f Ar bootfile +Fill in the boot file field of the request with +.Ar bootfile . +.It Fl 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. +.It Fl m Ar magic_number +Initialize the first word of the vendor options field with +.Ar magic_number . +.El +.Pp +A +.Ar template\-file +may be specified, in which case +.Nm +uses the (binary) contents of this file to initialize the +.Em options +area of the request packet. +.Sh SEE ALSO +.Xr bootpd 8 +.Rs +.%O RFC951 +.%T "BOOTSTRAP PROTOCOL (BOOTP)" +.Re +.Rs +.%O RFC1048 +.%T "BOOTP Vendor Information Extensions" +.Re +.Sh AUTHORS +The +.Nm +utility is a combination of original and derived works. +The main program module +.Pq Pa bootptest.c +is original work by +.An Gordon W. Ross Aq Mt gwr@mc.com . +The packet printing module +.Pq Pa print\-bootp.c +is a slightly modified +version of a file from the +.Bx +.Xr tcpdump 1 +program. +.Pp +This program includes software developed by the University of +California, Lawrence Berkeley Laboratory and its contributors. +(See the copyright notice in +.Pa print\-bootp.c . ) diff --git a/libexec/bootpd/tools/bootptest/bootptest.c b/libexec/bootpd/tools/bootptest/bootptest.c new file mode 100644 index 000000000000..1797bcb9574d --- /dev/null +++ b/libexec/bootpd/tools/bootptest/bootptest.c @@ -0,0 +1,510 @@ +/* + * 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> + * + */ + +#include <sys/cdefs.h> +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 <sys/utsname.h> + +#include <net/if.h> +#include <netinet/in.h> +#include <arpa/inet.h> /* inet_ntoa */ + +#ifndef NO_UNISTD +#include <unistd.h> +#endif + +#include <err.h> +#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 "getether.h" + +#include "patchlevel.h" + +static void send_request(int s); + +#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 *sndbuf; /* Send packet buffer */ +char *rcvbuf; /* Receive packet buffer */ + +struct utsname my_uname; +char *hostname; + +/* + * 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 */ + +/* + * Initialization such as command-line processing is done, then + * the receiver loop is started. Die when interrupted. + */ + +int +main(int argc, char **argv) +{ + struct bootp *bp; + struct servent *sep; + struct hostent *hep; + + char *servername = NULL; + char *vendor_file = NULL; + char *bp_file = NULL; + int32 server_addr; /* inet addr, network order */ + int s; /* Socket file descriptor */ + int n, 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); + + if (uname(&my_uname) < 0) + errx(1, "can't get hostname"); + hostname = my_uname.nodename; + + 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 request. */ + 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 { + warnx("bootps/udp: unknown service -- using port %d", + IPPORT_BOOTPS); + bootps_port = (u_short) IPPORT_BOOTPS; + } + + /* + * Set up server socket address (for send) + */ + if (servername) { + if (isdigit(servername[0])) + server_addr = inet_addr(servername); + else { + hep = gethostbyname(servername); + if (!hep) + errx(1, "%s: unknown host", servername); + bcopy(hep->h_addr, &server_addr, sizeof(server_addr)); + } + } else { + /* Get broadcast address */ + /* XXX - not yet */ + server_addr = INADDR_ANY; + } + sin_server.sin_family = AF_INET; + sin_server.sin_port = htons(bootps_port); + sin_server.sin_addr.s_addr = server_addr; + + /* + * Get client's listening port number + */ + sep = getservbyname("bootpc", "udp"); + if (sep) { + bootpc_port = ntohs(sep->s_port); + } else { + warnx("bootpc/udp: unknown service -- using port %d", + 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) { + if (errno == EACCES) { + warn("bind BOOTPC port"); + errx(1, "you need to run this as root"); + } + else + err(1, "bind BOOTPC port"); + } + /* + * 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, (char*)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. */ + 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((struct bootp *)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. + */ + } + errx(1, "no response from %s", servername); +} + +static void +send_request(int s) +{ + /* Print the request packet. */ + printf("Sending to %s", inet_ntoa(sin_server.sin_addr)); + bootp_print((struct bootp *)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(u_char *s, u_char *ep) +{ + u_char c; + + putchar('"'); + while ((c = *s++) != '\0') { + 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(struct in_addr *ina) +{ + static char b[24]; + u_char *p; + + p = (u_char *) ina; + snprintf(b, sizeof(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/libexec/bootpd/tools/bootptest/bootptest.h b/libexec/bootpd/tools/bootptest/bootptest.h new file mode 100644 index 000000000000..e4da8c6bbb47 --- /dev/null +++ b/libexec/bootpd/tools/bootptest/bootptest.h @@ -0,0 +1,17 @@ +/* bootptest.h */ +/* + * Hacks for sharing print-bootp.c between tcpdump and bootptest. + */ +#define ESRC(p) (p) +#define EDST(p) (p) + +extern int vflag; /* verbose flag */ + +/* global pointers to beginning and end of current packet (during printing) */ +extern unsigned char *packetp; +extern unsigned char *snapend; + +void bootp_print(struct bootp *bp, int length, u_short sport, + u_short dport); +char *ipaddr_string(struct in_addr *); +int printfn(u_char *s, u_char *ep); diff --git a/libexec/bootpd/tools/bootptest/print-bootp.c b/libexec/bootpd/tools/bootptest/print-bootp.c new file mode 100644 index 000000000000..dabab8115313 --- /dev/null +++ b/libexec/bootpd/tools/bootptest/print-bootp.c @@ -0,0 +1,476 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * 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. 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> + */ + +#include <stdio.h> + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/socket.h> + +#include <sys/time.h> /* for struct timeval in net/if.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(u_char *bp, int length); +static void cmu_print(u_char *bp, int length); +static void other_print(u_char *bp, int length); +static void dump_hex(u_char *bp, int len); + +/* + * Print bootp requests + */ +void +bootp_print(struct bootp *bp, int length, u_short sport, u_short 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 available 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) { + struct ether_header *eh; + 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 = NULL; + if (e == NULL || 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:%ld", (long)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 described 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 trailer 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 +rfc1048_print(u_char *bp, int length) +{ + u_char tag; + u_char *ep; + int len; + 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>%td)", 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("%ld", (long)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(u_char *bp, int length) +{ + struct cmu_vend *v; + + printf("-cmu"); + + v = (struct cmu_vend *) bp; + if (length < sizeof(*v)) { + printf(" |L=%d", length); + return; + } + + /* 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(u_char *bp, int length) +{ + u_char *ep; /* end pointer */ + u_char *zp; /* points one past last non-zero byte */ + + /* 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(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/libexec/bootpd/trygetea.c b/libexec/bootpd/trygetea.c new file mode 100644 index 000000000000..5510995386e9 --- /dev/null +++ b/libexec/bootpd/trygetea.c @@ -0,0 +1,53 @@ +/* + * 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 + +#ifdef _AIX32 +#include <sys/time.h> /* for struct timeval in net/if.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 "getether.h" + +int debug = 0; +char *progname; + +void +main(argc, argv) + int argc; + 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], (char*)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/libexec/bootpd/trygetif.c b/libexec/bootpd/trygetif.c new file mode 100644 index 000000000000..8c78ee34247a --- /dev/null +++ b/libexec/bootpd/trygetif.c @@ -0,0 +1,72 @@ +/* + * 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 + +#ifdef _AIX32 +#include <sys/time.h> /* for struct timeval in net/if.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; + +void +main(argc, argv) + int argc; + char **argv; +{ + struct hostent *hep; + struct sockaddr_in *sip; /* Interface address */ + struct ifreq *ifr; + struct in_addr dst_addr; + struct in_addr *dap; + int s; + + progname = argv[0]; /* for report */ + + dap = NULL; + if (argc > 1) { + dap = &dst_addr; + if (isdigit(argv[1][0])) + dst_addr.s_addr = inet_addr(argv[1]); + else { + 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/libexec/bootpd/trylook.c b/libexec/bootpd/trylook.c new file mode 100644 index 000000000000..0479166d2f35 --- /dev/null +++ b/libexec/bootpd/trylook.c @@ -0,0 +1,56 @@ +/* + * 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; + +void +main(argc, argv) + int argc; + 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 */ + printf(" hwa="); + hwa = lookup_hwa(argv[i], 1); + if (!hwa) + printf("?\n"); + else { + int i; + for (i = 0; i < 6; i++) + printf(":%x", hwa[i] & 0xFF); + putchar('\n'); + } + + } + exit(0); +} diff --git a/libexec/bootpd/tzone.c b/libexec/bootpd/tzone.c new file mode 100644 index 000000000000..d0c20a389c06 --- /dev/null +++ b/libexec/bootpd/tzone.c @@ -0,0 +1,46 @@ +/* + * tzone.c - get the timezone + * + * This is shared by bootpd and bootpef + */ + +#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 <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 tm *tm; + time_t now; + + (void)time(&now); + if ((tm = localtime(&now)) == NULL) { + secondswest = 0; /* Assume GMT for lack of anything better */ + report(LOG_ERR, "localtime() failed"); + } else { + secondswest = -tm->tm_gmtoff; + } +#endif /* SVR4 */ +} diff --git a/libexec/bootpd/tzone.h b/libexec/bootpd/tzone.h new file mode 100644 index 000000000000..ddd67c4b625c --- /dev/null +++ b/libexec/bootpd/tzone.h @@ -0,0 +1,3 @@ +/* tzone.h */ +extern int32 secondswest; +extern void tzone_init(); |