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