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