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