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