Home | History | Annotate | Line # | Download | only in bootpgw
bootpgw.c revision 1.8
      1  1.1       gwr /*
      2  1.1       gwr  * bootpgw.c - BOOTP GateWay
      3  1.1       gwr  * This program forwards BOOTP Request packets to a BOOTP server.
      4  1.1       gwr  */
      5  1.1       gwr 
      6  1.1       gwr /************************************************************************
      7  1.1       gwr           Copyright 1988, 1991 by Carnegie Mellon University
      8  1.1       gwr 
      9  1.1       gwr                           All Rights Reserved
     10  1.1       gwr 
     11  1.1       gwr Permission to use, copy, modify, and distribute this software and its
     12  1.1       gwr documentation for any purpose and without fee is hereby granted, provided
     13  1.1       gwr that the above copyright notice appear in all copies and that both that
     14  1.1       gwr copyright notice and this permission notice appear in supporting
     15  1.1       gwr documentation, and that the name of Carnegie Mellon University not be used
     16  1.1       gwr in advertising or publicity pertaining to distribution of the software
     17  1.1       gwr without specific, written prior permission.
     18  1.1       gwr 
     19  1.1       gwr CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
     20  1.1       gwr SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
     21  1.1       gwr IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
     22  1.1       gwr DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
     23  1.1       gwr PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
     24  1.1       gwr ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
     25  1.1       gwr SOFTWARE.
     26  1.1       gwr ************************************************************************/
     27  1.1       gwr 
     28  1.7     lukem #include <sys/cdefs.h>
     29  1.1       gwr #ifndef lint
     30  1.8       mrg __RCSID("$NetBSD: bootpgw.c,v 1.8 1998/07/06 07:02:17 mrg Exp $");
     31  1.1       gwr #endif
     32  1.1       gwr 
     33  1.1       gwr /*
     34  1.1       gwr  * BOOTPGW is typically used to forward BOOTP client requests from
     35  1.1       gwr  * one subnet to a BOOTP server on a different subnet.
     36  1.1       gwr  */
     37  1.1       gwr 
     38  1.1       gwr #include <sys/types.h>
     39  1.1       gwr #include <sys/param.h>
     40  1.1       gwr #include <sys/socket.h>
     41  1.1       gwr #include <sys/ioctl.h>
     42  1.1       gwr #include <sys/file.h>
     43  1.1       gwr #include <sys/time.h>
     44  1.1       gwr #include <sys/stat.h>
     45  1.1       gwr 
     46  1.1       gwr #include <net/if.h>
     47  1.1       gwr #include <netinet/in.h>
     48  1.1       gwr #include <arpa/inet.h>			/* inet_ntoa */
     49  1.1       gwr 
     50  1.1       gwr #ifndef	NO_UNISTD
     51  1.1       gwr #include <unistd.h>
     52  1.1       gwr #endif
     53  1.1       gwr #include <stdlib.h>
     54  1.1       gwr #include <signal.h>
     55  1.1       gwr #include <stdio.h>
     56  1.1       gwr #include <string.h>
     57  1.1       gwr #include <errno.h>
     58  1.1       gwr #include <ctype.h>
     59  1.1       gwr #include <netdb.h>
     60  1.1       gwr #include <syslog.h>
     61  1.1       gwr #include <assert.h>
     62  1.1       gwr 
     63  1.1       gwr #ifdef	NO_SETSID
     64  1.1       gwr # include <fcntl.h>		/* for O_RDONLY, etc */
     65  1.1       gwr #endif
     66  1.1       gwr 
     67  1.1       gwr #ifndef	USE_BFUNCS
     68  1.1       gwr # include <memory.h>
     69  1.1       gwr /* Yes, memcpy is OK here (no overlapped copies). */
     70  1.1       gwr # define bcopy(a,b,c)    memcpy(b,a,c)
     71  1.1       gwr # define bzero(p,l)      memset(p,0,l)
     72  1.1       gwr # define bcmp(a,b,c)     memcmp(a,b,c)
     73  1.1       gwr #endif
     74  1.1       gwr 
     75  1.1       gwr #include "bootp.h"
     76  1.1       gwr #include "getif.h"
     77  1.1       gwr #include "hwaddr.h"
     78  1.1       gwr #include "report.h"
     79  1.1       gwr #include "patchlevel.h"
     80  1.1       gwr 
     81  1.1       gwr /* Local definitions: */
     82  1.2       gwr #define MAX_MSG_SIZE			(3*512)	/* Maximum packet size */
     83  1.1       gwr #define TRUE 1
     84  1.1       gwr #define FALSE 0
     85  1.1       gwr #define get_network_errmsg get_errmsg
     86  1.1       gwr 
     87  1.1       gwr 
     89  1.1       gwr 
     90  1.1       gwr /*
     91  1.1       gwr  * Externals, forward declarations, and global variables
     92  1.1       gwr  */
     93  1.1       gwr 
     94  1.1       gwr #ifdef	__STDC__
     95  1.1       gwr #define P(args) args
     96  1.1       gwr #else
     97  1.1       gwr #define P(args) ()
     98  1.1       gwr #endif
     99  1.1       gwr 
    100  1.1       gwr static void usage P((void));
    101  1.1       gwr static void handle_reply P((void));
    102  1.7     lukem static void handle_request P((void));
    103  1.1       gwr int main P((int, char **));
    104  1.1       gwr 
    105  1.1       gwr #undef	P
    106  1.1       gwr 
    107  1.1       gwr /*
    108  1.1       gwr  * IP port numbers for client and server obtained from /etc/services
    109  1.1       gwr  */
    110  1.1       gwr 
    111  1.1       gwr u_short bootps_port, bootpc_port;
    112  1.1       gwr 
    113  1.1       gwr 
    114  1.1       gwr /*
    115  1.1       gwr  * Internet socket and interface config structures
    116  1.1       gwr  */
    117  1.1       gwr 
    118  1.4        ws struct sockaddr_in bind_addr;	/* Listening */
    119  1.4        ws struct sockaddr_in clnt_addr;	/* client address */
    120  1.1       gwr struct sockaddr_in serv_addr;	/* server address */
    121  1.1       gwr 
    122  1.1       gwr 
    123  1.1       gwr /*
    124  1.1       gwr  * option defaults
    125  1.1       gwr  */
    126  1.1       gwr int debug = 0;					/* Debugging flag (level) */
    127  1.1       gwr struct timeval actualtimeout =
    128  1.1       gwr {								/* fifteen minutes */
    129  1.1       gwr 	15 * 60L,					/* tv_sec */
    130  1.1       gwr 	0							/* tv_usec */
    131  1.1       gwr };
    132  1.1       gwr u_int maxhops = 4;				/* Number of hops allowed for requests. */
    133  1.1       gwr u_int minwait = 3;				/* Number of seconds client must wait before
    134  1.1       gwr 						   its bootrequest packets are forwarded. */
    135  1.1       gwr 
    136  1.1       gwr /*
    137  1.1       gwr  * General
    138  1.1       gwr  */
    139  1.1       gwr 
    140  1.1       gwr int s;							/* Socket file descriptor */
    141  1.1       gwr char *pktbuf;					/* Receive packet buffer */
    142  1.1       gwr int pktlen;
    143  1.1       gwr char *progname;
    144  1.1       gwr char *servername;
    145  1.8       mrg 
    146  1.1       gwr char myhostname[MAXHOSTNAMELEN + 1];
    147  1.1       gwr struct in_addr my_ip_addr;
    148  1.1       gwr 
    149  1.1       gwr 
    151  1.1       gwr 
    152  1.1       gwr 
    153  1.1       gwr /*
    154  1.1       gwr  * Initialization such as command-line processing is done and then the
    155  1.1       gwr  * main server loop is started.
    156  1.5  christos  */
    157  1.1       gwr 
    158  1.1       gwr int
    159  1.1       gwr main(argc, argv)
    160  1.1       gwr 	int argc;
    161  1.1       gwr 	char **argv;
    162  1.1       gwr {
    163  1.1       gwr 	struct timeval *timeout;
    164  1.1       gwr 	struct bootp *bp;
    165  1.1       gwr 	struct servent *servp;
    166  1.1       gwr 	struct hostent *hep;
    167  1.1       gwr 	char *stmp;
    168  1.1       gwr 	int n, ba_len, ra_len;
    169  1.1       gwr 	int nfound, readfds;
    170  1.1       gwr 	int standalone;
    171  1.1       gwr 
    172  1.1       gwr 	progname = strrchr(argv[0], '/');
    173  1.1       gwr 	if (progname) progname++;
    174  1.1       gwr 	else progname = argv[0];
    175  1.1       gwr 
    176  1.1       gwr 	/*
    177  1.1       gwr 	 * Initialize logging.
    178  1.1       gwr 	 */
    179  1.1       gwr 	report_init(0);				/* uses progname */
    180  1.1       gwr 
    181  1.1       gwr 	/*
    182  1.1       gwr 	 * Log startup
    183  1.1       gwr 	 */
    184  1.1       gwr 	report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL);
    185  1.1       gwr 
    186  1.1       gwr 	/* Debugging for compilers with struct padding. */
    187  1.1       gwr 	assert(sizeof(struct bootp) == BP_MINPKTSZ);
    188  1.2       gwr 
    189  1.1       gwr 	/* Get space for receiving packets and composing replies. */
    190  1.1       gwr 	pktbuf = malloc(MAX_MSG_SIZE);
    191  1.1       gwr 	if (!pktbuf) {
    192  1.1       gwr 		report(LOG_ERR, "malloc failed");
    193  1.1       gwr 		exit(1);
    194  1.1       gwr 	}
    195  1.1       gwr 	bp = (struct bootp *) pktbuf;
    196  1.1       gwr 
    197  1.1       gwr 	/*
    198  1.1       gwr 	 * Check to see if a socket was passed to us from inetd.
    199  1.1       gwr 	 *
    200  1.1       gwr 	 * Use getsockname() to determine if descriptor 0 is indeed a socket
    201  1.1       gwr 	 * (and thus we are probably a child of inetd) or if it is instead
    202  1.1       gwr 	 * something else and we are running standalone.
    203  1.1       gwr 	 */
    204  1.1       gwr 	s = 0;
    205  1.1       gwr 	ba_len = sizeof(bind_addr);
    206  1.1       gwr 	bzero((char *) &bind_addr, ba_len);
    207  1.1       gwr 	errno = 0;
    208  1.1       gwr 	standalone = TRUE;
    209  1.1       gwr 	if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) {
    210  1.1       gwr 		/*
    211  1.1       gwr 		 * Descriptor 0 is a socket.  Assume we are a child of inetd.
    212  1.1       gwr 		 */
    213  1.1       gwr 		if (bind_addr.sin_family == AF_INET) {
    214  1.1       gwr 			standalone = FALSE;
    215  1.1       gwr 			bootps_port = ntohs(bind_addr.sin_port);
    216  1.1       gwr 		} else {
    217  1.1       gwr 			/* Some other type of socket? */
    218  1.1       gwr 			report(LOG_INFO, "getsockname: not an INET socket");
    219  1.1       gwr 		}
    220  1.1       gwr 	}
    221  1.1       gwr 	/*
    222  1.1       gwr 	 * Set defaults that might be changed by option switches.
    223  1.1       gwr 	 */
    224  1.1       gwr 	stmp = NULL;
    225  1.8       mrg 	timeout = &actualtimeout;
    226  1.1       gwr 	gethostname(myhostname, sizeof(myhostname));
    227  1.1       gwr 	myhostname[sizeof(myhostname) - 1] = '\0';
    228  1.1       gwr 	hep = gethostbyname(myhostname);
    229  1.1       gwr 	if (!hep) {
    230  1.1       gwr 		printf("Can not get my IP address\n");
    231  1.1       gwr 		exit(1);
    232  1.1       gwr 	}
    233  1.1       gwr 	bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr));
    234  1.1       gwr 
    235  1.1       gwr 	/*
    236  1.1       gwr 	 * Read switches.
    237  1.1       gwr 	 */
    238  1.1       gwr 	for (argc--, argv++; argc > 0; argc--, argv++) {
    239  1.1       gwr 		if (argv[0][0] != '-')
    240  1.1       gwr 			break;
    241  1.1       gwr 		switch (argv[0][1]) {
    242  1.1       gwr 
    243  1.1       gwr 		case 'd':				/* debug level */
    244  1.1       gwr 			if (argv[0][2]) {
    245  1.1       gwr 				stmp = &(argv[0][2]);
    246  1.1       gwr 			} else if (argv[1] && argv[1][0] == '-') {
    247  1.1       gwr 				/*
    248  1.1       gwr 				 * Backwards-compatible behavior:
    249  1.1       gwr 				 * no parameter, so just increment the debug flag.
    250  1.1       gwr 				 */
    251  1.1       gwr 				debug++;
    252  1.1       gwr 				break;
    253  1.1       gwr 			} else {
    254  1.1       gwr 				argc--;
    255  1.1       gwr 				argv++;
    256  1.1       gwr 				stmp = argv[0];
    257  1.1       gwr 			}
    258  1.1       gwr 			if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
    259  1.1       gwr 				fprintf(stderr,
    260  1.1       gwr 						"%s: invalid debug level\n", progname);
    261  1.1       gwr 				break;
    262  1.1       gwr 			}
    263  1.1       gwr 			debug = n;
    264  1.1       gwr 			break;
    265  1.1       gwr 
    266  1.1       gwr 		case 'h':				/* hop count limit */
    267  1.1       gwr 			if (argv[0][2]) {
    268  1.1       gwr 				stmp = &(argv[0][2]);
    269  1.1       gwr 			} else {
    270  1.1       gwr 				argc--;
    271  1.1       gwr 				argv++;
    272  1.1       gwr 				stmp = argv[0];
    273  1.1       gwr 			}
    274  1.1       gwr 			if (!stmp || (sscanf(stmp, "%d", &n) != 1) ||
    275  1.1       gwr 				(n < 0) || (n > 16))
    276  1.1       gwr 			{
    277  1.1       gwr 				fprintf(stderr,
    278  1.1       gwr 						"bootpgw: invalid hop count limit\n");
    279  1.1       gwr 				break;
    280  1.1       gwr 			}
    281  1.1       gwr 			maxhops = (u_int)n;
    282  1.1       gwr 			break;
    283  1.1       gwr 
    284  1.1       gwr 		case 'i':				/* inetd mode */
    285  1.1       gwr 			standalone = FALSE;
    286  1.1       gwr 			break;
    287  1.1       gwr 
    288  1.1       gwr 		case 's':				/* standalone mode */
    289  1.1       gwr 			standalone = TRUE;
    290  1.1       gwr 			break;
    291  1.1       gwr 
    292  1.1       gwr 		case 't':				/* timeout */
    293  1.1       gwr 			if (argv[0][2]) {
    294  1.1       gwr 				stmp = &(argv[0][2]);
    295  1.1       gwr 			} else {
    296  1.1       gwr 				argc--;
    297  1.1       gwr 				argv++;
    298  1.1       gwr 				stmp = argv[0];
    299  1.1       gwr 			}
    300  1.1       gwr 			if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
    301  1.1       gwr 				fprintf(stderr,
    302  1.1       gwr 						"%s: invalid timeout specification\n", progname);
    303  1.1       gwr 				break;
    304  1.1       gwr 			}
    305  1.1       gwr 			actualtimeout.tv_sec = (int32) (60 * n);
    306  1.1       gwr 			/*
    307  1.1       gwr 			 * If the actual timeout is zero, pass a NULL pointer
    308  1.1       gwr 			 * to select so it blocks indefinitely, otherwise,
    309  1.1       gwr 			 * point to the actual timeout value.
    310  1.1       gwr 			 */
    311  1.1       gwr 			timeout = (n > 0) ? &actualtimeout : NULL;
    312  1.1       gwr 			break;
    313  1.1       gwr 
    314  1.1       gwr 		case 'w':				/* wait time */
    315  1.1       gwr 			if (argv[0][2]) {
    316  1.1       gwr 				stmp = &(argv[0][2]);
    317  1.1       gwr 			} else {
    318  1.1       gwr 				argc--;
    319  1.1       gwr 				argv++;
    320  1.1       gwr 				stmp = argv[0];
    321  1.1       gwr 			}
    322  1.1       gwr 			if (!stmp || (sscanf(stmp, "%d", &n) != 1) ||
    323  1.1       gwr 				(n < 0) || (n > 60))
    324  1.1       gwr 			{
    325  1.1       gwr 				fprintf(stderr,
    326  1.1       gwr 						"bootpgw: invalid wait time\n");
    327  1.1       gwr 				break;
    328  1.1       gwr 			}
    329  1.1       gwr 			minwait = (u_int)n;
    330  1.1       gwr 			break;
    331  1.1       gwr 
    332  1.1       gwr 		default:
    333  1.1       gwr 			fprintf(stderr, "%s: unknown switch: -%c\n",
    334  1.1       gwr 					progname, argv[0][1]);
    335  1.1       gwr 			usage();
    336  1.1       gwr 			break;
    337  1.1       gwr 
    338  1.1       gwr 		} /* switch */
    339  1.1       gwr 	} /* for args */
    340  1.1       gwr 
    341  1.1       gwr 	/* Make sure server name argument is suplied. */
    342  1.1       gwr 	servername = argv[0];
    343  1.1       gwr 	if (!servername) {
    344  1.1       gwr 		fprintf(stderr, "bootpgw: missing server name\n");
    345  1.1       gwr 		usage();
    346  1.1       gwr 	}
    347  1.1       gwr 	/*
    348  1.4        ws 	 * Get address of real bootp server.
    349  1.1       gwr 	 */
    350  1.1       gwr 	if (inet_aton(servername, &serv_addr.sin_addr) == 0) {
    351  1.1       gwr 		hep = gethostbyname(servername);
    352  1.1       gwr 		if (!hep) {
    353  1.1       gwr 			fprintf(stderr, "bootpgw: can't get addr for %s\n", servername);
    354  1.4        ws 			exit(1);
    355  1.4        ws 		}
    356  1.1       gwr 		memcpy(&serv_addr.sin_addr, hep->h_addr,
    357  1.1       gwr 		    sizeof(serv_addr.sin_addr));
    358  1.1       gwr 	}
    359  1.1       gwr 
    360  1.1       gwr 	if (standalone) {
    361  1.1       gwr 		/*
    362  1.1       gwr 		 * Go into background and disassociate from controlling terminal.
    363  1.1       gwr 		 * XXX - This is not the POSIX way (Should use setsid). -gwr
    364  1.1       gwr 		 */
    365  1.1       gwr 		if (debug < 3) {
    366  1.1       gwr 			if (fork())
    367  1.1       gwr 				exit(0);
    368  1.1       gwr #ifdef	NO_SETSID
    369  1.1       gwr 			setpgrp(0,0);
    370  1.1       gwr #ifdef TIOCNOTTY
    371  1.1       gwr 			n = open("/dev/tty", O_RDWR);
    372  1.1       gwr 			if (n >= 0) {
    373  1.1       gwr 				ioctl(n, TIOCNOTTY, (char *) 0);
    374  1.1       gwr 				(void) close(n);
    375  1.1       gwr 			}
    376  1.1       gwr #endif	/* TIOCNOTTY */
    377  1.1       gwr #else	/* SETSID */
    378  1.1       gwr 			if (setsid() < 0)
    379  1.1       gwr 				perror("setsid");
    380  1.1       gwr #endif	/* SETSID */
    381  1.1       gwr 		} /* if debug < 3 */
    382  1.1       gwr 		/*
    383  1.1       gwr 		 * Nuke any timeout value
    384  1.1       gwr 		 */
    385  1.1       gwr 		timeout = NULL;
    386  1.1       gwr 
    387  1.1       gwr 		/*
    388  1.1       gwr 		 * Here, bootpd would do:
    389  1.1       gwr 		 *	chdir
    390  1.1       gwr 		 *	tzone_init
    391  1.1       gwr 		 *	rdtab_init
    392  1.1       gwr 		 *	readtab
    393  1.1       gwr 		 */
    394  1.1       gwr 
    395  1.1       gwr 		/*
    396  1.1       gwr 		 * Create a socket.
    397  1.1       gwr 		 */
    398  1.1       gwr 		if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    399  1.1       gwr 			report(LOG_ERR, "socket: %s", get_network_errmsg());
    400  1.1       gwr 			exit(1);
    401  1.1       gwr 		}
    402  1.1       gwr 		/*
    403  1.1       gwr 		 * Get server's listening port number
    404  1.1       gwr 		 */
    405  1.1       gwr 		servp = getservbyname("bootps", "udp");
    406  1.1       gwr 		if (servp) {
    407  1.1       gwr 			bootps_port = ntohs((u_short) servp->s_port);
    408  1.1       gwr 		} else {
    409  1.1       gwr 			bootps_port = (u_short) IPPORT_BOOTPS;
    410  1.1       gwr 			report(LOG_ERR,
    411  1.1       gwr 				   "udp/bootps: unknown service -- assuming port %d",
    412  1.1       gwr 				   bootps_port);
    413  1.1       gwr 		}
    414  1.1       gwr 
    415  1.1       gwr 		/*
    416  1.1       gwr 		 * Bind socket to BOOTPS port.
    417  1.1       gwr 		 */
    418  1.1       gwr 		bind_addr.sin_family = AF_INET;
    419  1.1       gwr 		bind_addr.sin_port = htons(bootps_port);
    420  1.1       gwr 		bind_addr.sin_addr.s_addr = INADDR_ANY;
    421  1.1       gwr 		if (bind(s, (struct sockaddr *) &bind_addr,
    422  1.1       gwr 				 sizeof(bind_addr)) < 0)
    423  1.1       gwr 		{
    424  1.1       gwr 			report(LOG_ERR, "bind: %s", get_network_errmsg());
    425  1.1       gwr 			exit(1);
    426  1.1       gwr 		}
    427  1.1       gwr 	} /* if standalone */
    428  1.1       gwr 	/*
    429  1.1       gwr 	 * Get destination port number so we can reply to client
    430  1.1       gwr 	 */
    431  1.1       gwr 	servp = getservbyname("bootpc", "udp");
    432  1.1       gwr 	if (servp) {
    433  1.1       gwr 		bootpc_port = ntohs(servp->s_port);
    434  1.1       gwr 	} else {
    435  1.1       gwr 		report(LOG_ERR,
    436  1.1       gwr 			   "udp/bootpc: unknown service -- assuming port %d",
    437  1.1       gwr 			   IPPORT_BOOTPC);
    438  1.1       gwr 		bootpc_port = (u_short) IPPORT_BOOTPC;
    439  1.1       gwr 	}
    440  1.1       gwr 
    441  1.1       gwr 	/* no signal catchers */
    442  1.1       gwr 
    443  1.1       gwr 	/*
    444  1.1       gwr 	 * Process incoming requests.
    445  1.1       gwr 	 */
    446  1.1       gwr 	for (;;) {
    447  1.1       gwr 		readfds = 1 << s;
    448  1.1       gwr 		nfound = select(s + 1, (fd_set *)&readfds, NULL, NULL, timeout);
    449  1.1       gwr 		if (nfound < 0) {
    450  1.1       gwr 			if (errno != EINTR) {
    451  1.1       gwr 				report(LOG_ERR, "select: %s", get_errmsg());
    452  1.1       gwr 			}
    453  1.1       gwr 			continue;
    454  1.1       gwr 		}
    455  1.1       gwr 		if (!(readfds & (1 << s))) {
    456  1.1       gwr 			report(LOG_INFO, "exiting after %ld minutes of inactivity",
    457  1.1       gwr 				   actualtimeout.tv_sec / 60);
    458  1.4        ws 			exit(0);
    459  1.2       gwr 		}
    460  1.4        ws 		ra_len = sizeof(clnt_addr);
    461  1.1       gwr 		n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0,
    462  1.1       gwr 					 (struct sockaddr *) &clnt_addr, &ra_len);
    463  1.1       gwr 		if (n <= 0) {
    464  1.1       gwr 			continue;
    465  1.1       gwr 		}
    466  1.4        ws 		if (debug > 3) {
    467  1.1       gwr 			report(LOG_INFO, "recvd pkt from IP addr %s",
    468  1.1       gwr 				   inet_ntoa(clnt_addr.sin_addr));
    469  1.1       gwr 		}
    470  1.1       gwr 		if (n < sizeof(struct bootp)) {
    471  1.1       gwr 			if (debug) {
    472  1.1       gwr 				report(LOG_INFO, "received short packet");
    473  1.1       gwr 			}
    474  1.1       gwr 			continue;
    475  1.1       gwr 		}
    476  1.1       gwr 		pktlen = n;
    477  1.1       gwr 
    478  1.1       gwr 		switch (bp->bp_op) {
    479  1.1       gwr 		case BOOTREQUEST:
    480  1.1       gwr 			handle_request();
    481  1.1       gwr 			break;
    482  1.1       gwr 		case BOOTREPLY:
    483  1.1       gwr 			handle_reply();
    484  1.1       gwr 			break;
    485  1.1       gwr 		}
    486  1.1       gwr 	}
    487  1.1       gwr }
    488  1.1       gwr 
    489  1.1       gwr 
    491  1.1       gwr 
    492  1.1       gwr 
    493  1.1       gwr /*
    494  1.1       gwr  * Print "usage" message and exit
    495  1.1       gwr  */
    496  1.1       gwr 
    497  1.1       gwr static void
    498  1.1       gwr usage()
    499  1.1       gwr {
    500  1.1       gwr 	fprintf(stderr,
    501  1.1       gwr 			"usage:  bootpgw [-d level] [-i] [-s] [-t timeout] server\n");
    502  1.1       gwr 	fprintf(stderr, "\t -d n\tset debug level\n");
    503  1.1       gwr 	fprintf(stderr, "\t -h n\tset max hop count\n");
    504  1.1       gwr 	fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n");
    505  1.1       gwr 	fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n");
    506  1.1       gwr 	fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n");
    507  1.1       gwr 	fprintf(stderr, "\t -w n\tset min wait time (secs)\n");
    508  1.1       gwr 	exit(1);
    509  1.1       gwr }
    510  1.1       gwr 
    511  1.1       gwr 
    513  1.1       gwr 
    514  1.1       gwr /*
    515  1.1       gwr  * Process BOOTREQUEST packet.
    516  1.1       gwr  *
    517  1.1       gwr  * Note, this just forwards the request to a real server.
    518  1.1       gwr  */
    519  1.7     lukem static void
    520  1.1       gwr handle_request()
    521  1.7     lukem {
    522  1.1       gwr 	struct bootp *bp = (struct bootp *) pktbuf;
    523  1.1       gwr #if 0
    524  1.4        ws 	struct ifreq *ifr;
    525  1.1       gwr #endif
    526  1.1       gwr 	u_short secs, hops;
    527  1.1       gwr 
    528  1.4        ws 	/* XXX - SLIP init: Set bp_ciaddr = clnt_addr here? */
    529  1.1       gwr 
    530  1.1       gwr 	if (debug) {
    531  1.1       gwr 		report(LOG_INFO, "request from %s",
    532  1.1       gwr 			   inet_ntoa(clnt_addr.sin_addr));
    533  1.1       gwr 	}
    534  1.1       gwr 	/* Has the client been waiting long enough? */
    535  1.1       gwr 	secs = ntohs(bp->bp_secs);
    536  1.1       gwr 	if (secs < minwait)
    537  1.1       gwr 		return;
    538  1.4        ws 
    539  1.4        ws 	/* Has this packet hopped too many times? */
    540  1.1       gwr 	hops = ntohs(bp->bp_hops);
    541  1.1       gwr 	if (++hops > maxhops) {
    542  1.1       gwr 		report(LOG_NOTICE, "request from %s reached hop limit",
    543  1.1       gwr 			   inet_ntoa(clnt_addr.sin_addr));
    544  1.1       gwr 		return;
    545  1.1       gwr 	}
    546  1.1       gwr 	bp->bp_hops = htons(hops);
    547  1.1       gwr 
    548  1.1       gwr 	/*
    549  1.1       gwr 	 * Here one might discard a request from the same subnet as the
    550  1.1       gwr 	 * real server, but we can assume that the real server will send
    551  1.1       gwr 	 * a reply to the client before it waits for minwait seconds.
    552  1.1       gwr 	 */
    553  1.1       gwr 
    554  1.1       gwr 	/* If gateway address is not set, put in local interface addr. */
    555  1.1       gwr 	if (bp->bp_giaddr.s_addr == 0) {
    556  1.1       gwr #if 0	/* BUG */
    557  1.1       gwr 		struct sockaddr_in *sip;
    558  1.1       gwr 		/*
    559  1.1       gwr 		 * XXX - This picks the wrong interface when the receive addr
    560  1.4        ws 		 * is the broadcast address.  There is no  portable way to
    561  1.1       gwr 		 * find out which interface a broadcast was received on. -gwr
    562  1.1       gwr 		 * (Thanks to <walker (at) zk3.dec.com> for finding this bug!)
    563  1.4        ws 		 */
    564  1.1       gwr 		ifr = getif(s, &clnt_addr.sin_addr);
    565  1.1       gwr 		if (!ifr) {
    566  1.1       gwr 			report(LOG_NOTICE, "no interface for request from %s",
    567  1.1       gwr 				   inet_ntoa(clnt_addr.sin_addr));
    568  1.1       gwr 			return;
    569  1.1       gwr 		}
    570  1.1       gwr 		sip = (struct sockaddr_in *) &(ifr->ifr_addr);
    571  1.1       gwr 		bp->bp_giaddr = sip->sin_addr;
    572  1.1       gwr #else	/* BUG */
    573  1.1       gwr 		/*
    574  1.1       gwr 		 * XXX - Just set "giaddr" to our "official" IP address.
    575  1.1       gwr 		 * RFC 1532 says giaddr MUST be set to the address of the
    576  1.1       gwr 		 * interface on which the request was received.  Setting
    577  1.1       gwr 		 * it to our "default" IP address is not strictly correct,
    578  1.1       gwr 		 * but is good enough to allow the real BOOTP server to
    579  1.1       gwr 		 * get the reply back here.  Then, before we forward the
    580  1.1       gwr 		 * reply to the client, the giaddr field is corrected.
    581  1.1       gwr 		 * (In case the client uses giaddr, which it should not.)
    582  1.1       gwr 		 * See handle_reply()
    583  1.1       gwr 		 */
    584  1.1       gwr 		bp->bp_giaddr = my_ip_addr;
    585  1.1       gwr #endif	/* BUG */
    586  1.1       gwr 
    587  1.1       gwr 		/*
    588  1.1       gwr 		 * XXX - DHCP says to insert a subnet mask option into the
    589  1.4        ws 		 * options area of the request (if vendor magic == std).
    590  1.4        ws 		 */
    591  1.1       gwr 	}
    592  1.1       gwr 	/* Set up socket address for send. */
    593  1.1       gwr 	serv_addr.sin_family = AF_INET;
    594  1.4        ws 	serv_addr.sin_port = htons(bootps_port);
    595  1.4        ws 
    596  1.1       gwr 	/* Send reply with same size packet as request used. */
    597  1.1       gwr 	if (sendto(s, pktbuf, pktlen, 0,
    598  1.1       gwr 			   (struct sockaddr *) &serv_addr,
    599  1.1       gwr 			   sizeof(serv_addr)) < 0)
    600  1.1       gwr 	{
    601  1.1       gwr 		report(LOG_ERR, "sendto: %s", get_network_errmsg());
    602  1.1       gwr 	}
    603  1.1       gwr }
    604  1.1       gwr 
    605  1.1       gwr 
    607  1.1       gwr 
    608  1.1       gwr /*
    609  1.1       gwr  * Process BOOTREPLY packet.
    610  1.1       gwr  */
    611  1.1       gwr static void
    612  1.1       gwr handle_reply()
    613  1.1       gwr {
    614  1.1       gwr 	struct bootp *bp = (struct bootp *) pktbuf;
    615  1.1       gwr 	struct ifreq *ifr;
    616  1.1       gwr 	struct sockaddr_in *sip;
    617  1.1       gwr 	u_char canon_haddr[MAXHADDRLEN];
    618  1.1       gwr 	unsigned char *ha;
    619  1.1       gwr 	int len;
    620  1.1       gwr 
    621  1.1       gwr 	if (debug) {
    622  1.1       gwr 		report(LOG_INFO, "   reply for %s",
    623  1.1       gwr 			   inet_ntoa(bp->bp_yiaddr));
    624  1.1       gwr 	}
    625  1.1       gwr 	/* Make sure client is directly accessible. */
    626  1.1       gwr 	ifr = getif(s, &(bp->bp_yiaddr));
    627  1.1       gwr 	if (!ifr) {
    628  1.1       gwr 		report(LOG_NOTICE, "no interface for reply to %s",
    629  1.1       gwr 			   inet_ntoa(bp->bp_yiaddr));
    630  1.1       gwr 		return;
    631  1.1       gwr 	}
    632  1.1       gwr #if 1	/* Experimental (see BUG above) */
    633  1.1       gwr /* #ifdef CATER_TO_OLD_CLIENTS ? */
    634  1.1       gwr 	/*
    635  1.1       gwr 	 * The giaddr field has been set to our "default" IP address
    636  1.1       gwr 	 * which might not be on the same interface as the client.
    637  1.1       gwr 	 * In case the client looks at giaddr, (which it should not)
    638  1.1       gwr 	 * giaddr is now set to the address of the correct interface.
    639  1.1       gwr 	 */
    640  1.4        ws 	sip = (struct sockaddr_in *) &(ifr->ifr_addr);
    641  1.4        ws 	bp->bp_giaddr = sip->sin_addr;
    642  1.4        ws #endif
    643  1.1       gwr 
    644  1.1       gwr 	/* Set up socket address for send to client. */
    645  1.1       gwr 	clnt_addr.sin_family = AF_INET;
    646  1.1       gwr 	clnt_addr.sin_addr = bp->bp_yiaddr;
    647  1.1       gwr 	clnt_addr.sin_port = htons(bootpc_port);
    648  1.1       gwr 
    649  1.1       gwr 	/* Create an ARP cache entry for the client. */
    650  1.1       gwr 	ha = bp->bp_chaddr;
    651  1.1       gwr 	len = bp->bp_hlen;
    652  1.1       gwr 	if (len > MAXHADDRLEN)
    653  1.1       gwr 		len = MAXHADDRLEN;
    654  1.1       gwr 	if (bp->bp_htype == HTYPE_IEEE802) {
    655  1.1       gwr 		haddr_conv802(ha, canon_haddr, len);
    656  1.1       gwr 		ha = canon_haddr;
    657  1.1       gwr 	}
    658  1.1       gwr 	if (debug > 1)
    659  1.1       gwr 		report(LOG_INFO, "setarp %s - %s",
    660  1.4        ws 			   inet_ntoa(bp->bp_yiaddr), haddrtoa(ha, len));
    661  1.4        ws 	setarp(s, &bp->bp_yiaddr, ha, len);
    662  1.1       gwr 
    663  1.1       gwr 	/* Send reply with same size packet as request used. */
    664  1.1       gwr 	if (sendto(s, pktbuf, pktlen, 0,
    665  1.1       gwr 			   (struct sockaddr *) &clnt_addr,
    666  1.1       gwr 			   sizeof(clnt_addr)) < 0)
    667  1.1       gwr 	{
    668  1.1       gwr 		report(LOG_ERR, "sendto: %s", get_network_errmsg());
    669  1.1       gwr 	}
    670  1.1       gwr }
    671  1.1       gwr 
    672  1.1       gwr /*
    673  1.1       gwr  * Local Variables:
    674  1.1       gwr  * tab-width: 4
    675  1.1       gwr  * c-indent-level: 4
    676  1.1       gwr  * c-argdecl-indent: 4
    677  1.1       gwr  * c-continued-statement-offset: 4
    678                 * c-continued-brace-offset: -4
    679                 * c-label-offset: -4
    680                 * c-brace-offset: 0
    681                 * End:
    682                 */
    683