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