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#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);
88static void handle_reply(void);
89static void handle_request(void);
90
91/*
92 * IP port numbers for client and server obtained from /etc/services
93 */
94
95u_short bootps_port, bootpc_port;
96
97
98/*
99 * Internet socket and interface config structures
100 */
101
102struct sockaddr_in bind_addr;	/* Listening */
103struct sockaddr_in clnt_addr;	/* client address */
104struct sockaddr_in serv_addr;	/* server address */
105
106
107/*
108 * option defaults
109 */
110int debug = 0;					/* Debugging flag (level) */
111int actualtimeout = 15 * 60000;			/* fifteen minutes */
112u_int maxhops = 4;				/* Number of hops allowed for requests. */
113u_int minwait = 3;				/* Number of seconds client must wait before
114						   its bootrequest packets are forwarded. */
115
116/*
117 * General
118 */
119
120int s;							/* Socket file descriptor */
121char *pktbuf;					/* Receive packet buffer */
122int pktlen;
123char *progname;
124char *servername;
125
126char myhostname[MAXHOSTNAMELEN + 1];
127struct 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
136int
137main(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
476static void
477usage(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 */
497static void
498handle_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 */
588static void
589handle_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