Home | History | Annotate | Line # | Download | only in bootpgw
bootpgw.c revision 1.2
      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 #ifndef lint
     29 static char rcsid[] = "$Id: bootpgw.c,v 1.2 1994/08/22 22:14:48 gwr Exp $";
     30 #endif
     31 
     32 /*
     33  * BOOTPGW is typically used to forward BOOTP client requests from
     34  * one subnet to a BOOTP server on a different subnet.
     35  */
     36 
     37 #include <sys/types.h>
     38 #include <sys/param.h>
     39 #include <sys/socket.h>
     40 #include <sys/ioctl.h>
     41 #include <sys/file.h>
     42 #include <sys/time.h>
     43 #include <sys/stat.h>
     44 
     45 #include <net/if.h>
     46 #include <netinet/in.h>
     47 #include <arpa/inet.h>			/* inet_ntoa */
     48 
     49 #ifndef	NO_UNISTD
     50 #include <unistd.h>
     51 #endif
     52 #include <stdlib.h>
     53 #include <signal.h>
     54 #include <stdio.h>
     55 #include <string.h>
     56 #include <errno.h>
     57 #include <ctype.h>
     58 #include <netdb.h>
     59 #include <syslog.h>
     60 #include <assert.h>
     61 
     62 #ifdef	NO_SETSID
     63 # include <fcntl.h>		/* for O_RDONLY, etc */
     64 #endif
     65 
     66 #ifndef	USE_BFUNCS
     67 # include <memory.h>
     68 /* Yes, memcpy is OK here (no overlapped copies). */
     69 # define bcopy(a,b,c)    memcpy(b,a,c)
     70 # define bzero(p,l)      memset(p,0,l)
     71 # define bcmp(a,b,c)     memcmp(a,b,c)
     72 #endif
     73 
     74 #include "bootp.h"
     75 #include "getif.h"
     76 #include "hwaddr.h"
     77 #include "report.h"
     78 #include "patchlevel.h"
     79 
     80 /* Local definitions: */
     81 #define MAX_MSG_SIZE			(3*512)	/* Maximum packet size */
     82 #define TRUE 1
     83 #define FALSE 0
     84 #define get_network_errmsg get_errmsg
     85 
     86 
     88 
     89 /*
     90  * Externals, forward declarations, and global variables
     91  */
     92 
     93 #ifdef	__STDC__
     94 #define P(args) args
     95 #else
     96 #define P(args) ()
     97 #endif
     98 
     99 static void usage P((void));
    100 static void handle_reply P((void));
    101 static void handle_request P((void));
    102 
    103 #undef	P
    104 
    105 /*
    106  * IP port numbers for client and server obtained from /etc/services
    107  */
    108 
    109 u_short bootps_port, bootpc_port;
    110 
    111 
    112 /*
    113  * Internet socket and interface config structures
    114  */
    115 
    116 struct sockaddr_in bind_addr;	/* Listening */
    117 struct sockaddr_in recv_addr;	/* Packet source */
    118 struct sockaddr_in send_addr;	/*  destination */
    119 
    120 
    121 /*
    122  * option defaults
    123  */
    124 int debug = 0;					/* Debugging flag (level) */
    125 struct timeval actualtimeout =
    126 {								/* fifteen minutes */
    127 	15 * 60L,					/* tv_sec */
    128 	0							/* tv_usec */
    129 };
    130 u_int maxhops = 4;				/* Number of hops allowed for requests. */
    131 u_int minwait = 3;				/* Number of seconds client must wait before
    132 						   its bootrequest packets are forwarded. */
    133 
    134 /*
    135  * General
    136  */
    137 
    138 int s;							/* Socket file descriptor */
    139 char *pktbuf;					/* Receive packet buffer */
    140 int pktlen;
    141 char *progname;
    142 char *servername;
    143 int32 server_ipa;				/* Real server IP address, network order. */
    144 
    145 char myhostname[64];
    146 struct in_addr my_ip_addr;
    147 
    148 
    150 
    151 
    152 /*
    153  * Initialization such as command-line processing is done and then the
    154  * main server loop is started.
    155  */
    156 
    157 void
    158 main(argc, argv)
    159 	int argc;
    160 	char **argv;
    161 {
    162 	struct timeval *timeout;
    163 	struct bootp *bp;
    164 	struct servent *servp;
    165 	struct hostent *hep;
    166 	char *stmp;
    167 	int n, ba_len, ra_len;
    168 	int nfound, readfds;
    169 	int standalone;
    170 
    171 	progname = strrchr(argv[0], '/');
    172 	if (progname) progname++;
    173 	else progname = argv[0];
    174 
    175 	/*
    176 	 * Initialize logging.
    177 	 */
    178 	report_init(0);				/* uses progname */
    179 
    180 	/*
    181 	 * Log startup
    182 	 */
    183 	report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL);
    184 
    185 	/* Debugging for compilers with struct padding. */
    186 	assert(sizeof(struct bootp) == BP_MINPKTSZ);
    187 
    188 	/* Get space for receiving packets and composing replies. */
    189 	pktbuf = malloc(MAX_MSG_SIZE);
    190 	if (!pktbuf) {
    191 		report(LOG_ERR, "malloc failed");
    192 		exit(1);
    193 	}
    194 	bp = (struct bootp *) pktbuf;
    195 
    196 	/*
    197 	 * Check to see if a socket was passed to us from inetd.
    198 	 *
    199 	 * Use getsockname() to determine if descriptor 0 is indeed a socket
    200 	 * (and thus we are probably a child of inetd) or if it is instead
    201 	 * something else and we are running standalone.
    202 	 */
    203 	s = 0;
    204 	ba_len = sizeof(bind_addr);
    205 	bzero((char *) &bind_addr, ba_len);
    206 	errno = 0;
    207 	standalone = TRUE;
    208 	if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) {
    209 		/*
    210 		 * Descriptor 0 is a socket.  Assume we are a child of inetd.
    211 		 */
    212 		if (bind_addr.sin_family == AF_INET) {
    213 			standalone = FALSE;
    214 			bootps_port = ntohs(bind_addr.sin_port);
    215 		} else {
    216 			/* Some other type of socket? */
    217 			report(LOG_INFO, "getsockname: not an INET socket");
    218 		}
    219 	}
    220 	/*
    221 	 * Set defaults that might be changed by option switches.
    222 	 */
    223 	stmp = NULL;
    224 	timeout = &actualtimeout;
    225 	gethostname(myhostname, sizeof(myhostname));
    226 	hep = gethostbyname(myhostname);
    227 	if (!hep) {
    228 		printf("Can not get my IP address\n");
    229 		exit(1);
    230 	}
    231 	bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr));
    232 
    233 	/*
    234 	 * Read switches.
    235 	 */
    236 	for (argc--, argv++; argc > 0; argc--, argv++) {
    237 		if (argv[0][0] != '-')
    238 			break;
    239 		switch (argv[0][1]) {
    240 
    241 		case 'd':				/* debug level */
    242 			if (argv[0][2]) {
    243 				stmp = &(argv[0][2]);
    244 			} else if (argv[1] && argv[1][0] == '-') {
    245 				/*
    246 				 * Backwards-compatible behavior:
    247 				 * no parameter, so just increment the debug flag.
    248 				 */
    249 				debug++;
    250 				break;
    251 			} else {
    252 				argc--;
    253 				argv++;
    254 				stmp = argv[0];
    255 			}
    256 			if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
    257 				fprintf(stderr,
    258 						"%s: invalid debug level\n", progname);
    259 				break;
    260 			}
    261 			debug = n;
    262 			break;
    263 
    264 		case 'h':				/* hop count limit */
    265 			if (argv[0][2]) {
    266 				stmp = &(argv[0][2]);
    267 			} else {
    268 				argc--;
    269 				argv++;
    270 				stmp = argv[0];
    271 			}
    272 			if (!stmp || (sscanf(stmp, "%d", &n) != 1) ||
    273 				(n < 0) || (n > 16))
    274 			{
    275 				fprintf(stderr,
    276 						"bootpgw: invalid hop count limit\n");
    277 				break;
    278 			}
    279 			maxhops = (u_int)n;
    280 			break;
    281 
    282 		case 'i':				/* inetd mode */
    283 			standalone = FALSE;
    284 			break;
    285 
    286 		case 's':				/* standalone mode */
    287 			standalone = TRUE;
    288 			break;
    289 
    290 		case 't':				/* timeout */
    291 			if (argv[0][2]) {
    292 				stmp = &(argv[0][2]);
    293 			} else {
    294 				argc--;
    295 				argv++;
    296 				stmp = argv[0];
    297 			}
    298 			if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
    299 				fprintf(stderr,
    300 						"%s: invalid timeout specification\n", progname);
    301 				break;
    302 			}
    303 			actualtimeout.tv_sec = (int32) (60 * n);
    304 			/*
    305 			 * If the actual timeout is zero, pass a NULL pointer
    306 			 * to select so it blocks indefinitely, otherwise,
    307 			 * point to the actual timeout value.
    308 			 */
    309 			timeout = (n > 0) ? &actualtimeout : NULL;
    310 			break;
    311 
    312 		case 'w':				/* wait time */
    313 			if (argv[0][2]) {
    314 				stmp = &(argv[0][2]);
    315 			} else {
    316 				argc--;
    317 				argv++;
    318 				stmp = argv[0];
    319 			}
    320 			if (!stmp || (sscanf(stmp, "%d", &n) != 1) ||
    321 				(n < 0) || (n > 60))
    322 			{
    323 				fprintf(stderr,
    324 						"bootpgw: invalid wait time\n");
    325 				break;
    326 			}
    327 			minwait = (u_int)n;
    328 			break;
    329 
    330 		default:
    331 			fprintf(stderr, "%s: unknown switch: -%c\n",
    332 					progname, argv[0][1]);
    333 			usage();
    334 			break;
    335 
    336 		} /* switch */
    337 	} /* for args */
    338 
    339 	/* Make sure server name argument is suplied. */
    340 	servername = argv[0];
    341 	if (!servername) {
    342 		fprintf(stderr, "bootpgw: missing server name\n");
    343 		usage();
    344 	}
    345 	/*
    346 	 * Get address of real bootp server.
    347 	 */
    348 	if (isdigit(servername[0]))
    349 		server_ipa = inet_addr(servername);
    350 	else {
    351 		hep = gethostbyname(servername);
    352 		if (!hep) {
    353 			fprintf(stderr, "bootpgw: can't get addr for %s\n", servername);
    354 			exit(1);
    355 		}
    356 		bcopy(hep->h_addr, (char *)&server_ipa, sizeof(server_ipa));
    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(recv_addr);
    460 		n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0,
    461 					 (struct sockaddr *) &recv_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(recv_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 	struct ifreq *ifr;
    523 	u_short secs, hops;
    524 
    525 	/* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */
    526 
    527 	if (debug) {
    528 		report(LOG_INFO, "request from %s",
    529 			   inet_ntoa(recv_addr.sin_addr));
    530 	}
    531 	/* Has the client been waiting long enough? */
    532 	secs = ntohs(bp->bp_secs);
    533 	if (secs < minwait)
    534 		return;
    535 
    536 	/* Has this packet hopped too many times? */
    537 	hops = ntohs(bp->bp_hops);
    538 	if (++hops > maxhops) {
    539 		report(LOG_NOTICE, "reqest from %s reached hop limit",
    540 			   inet_ntoa(recv_addr.sin_addr));
    541 		return;
    542 	}
    543 	bp->bp_hops = htons(hops);
    544 
    545 	/*
    546 	 * Here one might discard a request from the same subnet as the
    547 	 * real server, but we can assume that the real server will send
    548 	 * a reply to the client before it waits for minwait seconds.
    549 	 */
    550 
    551 	/* If gateway address is not set, put in local interface addr. */
    552 	if (bp->bp_giaddr.s_addr == 0) {
    553 #if 0	/* BUG */
    554 		struct sockaddr_in *sip;
    555 		/*
    556 		 * XXX - This picks the wrong interface when the receive addr
    557 		 * is the broadcast address.  There is no  portable way to
    558 		 * find out which interface a broadcast was received on. -gwr
    559 		 * (Thanks to <walker (at) zk3.dec.com> for finding this bug!)
    560 		 */
    561 		ifr = getif(s, &recv_addr.sin_addr);
    562 		if (!ifr) {
    563 			report(LOG_NOTICE, "no interface for request from %s",
    564 				   inet_ntoa(recv_addr.sin_addr));
    565 			return;
    566 		}
    567 		sip = (struct sockaddr_in *) &(ifr->ifr_addr);
    568 		bp->bp_giaddr = sip->sin_addr;
    569 #else	/* BUG */
    570 		/*
    571 		 * XXX - Just set "giaddr" to our "official" IP address.
    572 		 * RFC 1532 says giaddr MUST be set to the address of the
    573 		 * interface on which the request was received.  Setting
    574 		 * it to our "default" IP address is not strictly correct,
    575 		 * but is good enough to allow the real BOOTP server to
    576 		 * get the reply back here.  Then, before we forward the
    577 		 * reply to the client, the giaddr field is corrected.
    578 		 * (In case the client uses giaddr, which it should not.)
    579 		 * See handle_reply()
    580 		 */
    581 		bp->bp_giaddr = my_ip_addr;
    582 #endif	/* BUG */
    583 
    584 		/*
    585 		 * XXX - DHCP says to insert a subnet mask option into the
    586 		 * options area of the request (if vendor magic == std).
    587 		 */
    588 	}
    589 	/* Set up socket address for send. */
    590 	send_addr.sin_family = AF_INET;
    591 	send_addr.sin_port = htons(bootps_port);
    592 	send_addr.sin_addr.s_addr = server_ipa;
    593 
    594 	/* Send reply with same size packet as request used. */
    595 	if (sendto(s, pktbuf, pktlen, 0,
    596 			   (struct sockaddr *) &send_addr,
    597 			   sizeof(send_addr)) < 0)
    598 	{
    599 		report(LOG_ERR, "sendto: %s", get_network_errmsg());
    600 	}
    601 }
    602 
    603 
    605 
    606 /*
    607  * Process BOOTREPLY packet.
    608  */
    609 static void
    610 handle_reply()
    611 {
    612 	struct bootp *bp = (struct bootp *) pktbuf;
    613 	struct ifreq *ifr;
    614 	struct sockaddr_in *sip;
    615 	u_char canon_haddr[MAXHADDRLEN];
    616 	unsigned char *ha;
    617 	int len;
    618 
    619 	if (debug) {
    620 		report(LOG_INFO, "   reply for %s",
    621 			   inet_ntoa(bp->bp_yiaddr));
    622 	}
    623 	/* Make sure client is directly accessible. */
    624 	ifr = getif(s, &(bp->bp_yiaddr));
    625 	if (!ifr) {
    626 		report(LOG_NOTICE, "no interface for reply to %s",
    627 			   inet_ntoa(bp->bp_yiaddr));
    628 		return;
    629 	}
    630 #if 1	/* Experimental (see BUG above) */
    631 /* #ifdef CATER_TO_OLD_CLIENTS ? */
    632 	/*
    633 	 * The giaddr field has been set to our "default" IP address
    634 	 * which might not be on the same interface as the client.
    635 	 * In case the client looks at giaddr, (which it should not)
    636 	 * giaddr is now set to the address of the correct interface.
    637 	 */
    638 	sip = (struct sockaddr_in *) &(ifr->ifr_addr);
    639 	bp->bp_giaddr = sip->sin_addr;
    640 #endif
    641 
    642 	/* Set up socket address for send to client. */
    643 	send_addr.sin_family = AF_INET;
    644 	send_addr.sin_addr = bp->bp_yiaddr;
    645 	send_addr.sin_port = htons(bootpc_port);
    646 
    647 	/* Create an ARP cache entry for the client. */
    648 	ha = bp->bp_chaddr;
    649 	len = bp->bp_hlen;
    650 	if (len > MAXHADDRLEN)
    651 		len = MAXHADDRLEN;
    652 	if (bp->bp_htype == HTYPE_IEEE802) {
    653 		haddr_conv802(ha, canon_haddr, len);
    654 		ha = canon_haddr;
    655 	}
    656 	if (debug > 1)
    657 		report(LOG_INFO, "setarp %s - %s",
    658 			   inet_ntoa(bp->bp_yiaddr), haddrtoa(ha, len));
    659 	setarp(s, &bp->bp_yiaddr, ha, len);
    660 
    661 	/* Send reply with same size packet as request used. */
    662 	if (sendto(s, pktbuf, pktlen, 0,
    663 			   (struct sockaddr *) &send_addr,
    664 			   sizeof(send_addr)) < 0)
    665 	{
    666 		report(LOG_ERR, "sendto: %s", get_network_errmsg());
    667 	}
    668 }
    669 
    670 /*
    671  * Local Variables:
    672  * tab-width: 4
    673  * c-indent-level: 4
    674  * c-argdecl-indent: 4
    675  * c-continued-statement-offset: 4
    676  * c-continued-brace-offset: -4
    677  * c-label-offset: -4
    678  * c-brace-offset: 0
    679  * End:
    680  */
    681