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