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