bootpgw.c revision 13572
113572Spst/*
213572Spst * bootpgw.c - BOOTP GateWay
313572Spst * This program forwards BOOTP Request packets to a BOOTP server.
413572Spst */
513572Spst
613572Spst/************************************************************************
713572Spst          Copyright 1988, 1991 by Carnegie Mellon University
813572Spst
913572Spst                          All Rights Reserved
1013572Spst
1113572SpstPermission to use, copy, modify, and distribute this software and its
1213572Spstdocumentation for any purpose and without fee is hereby granted, provided
1313572Spstthat the above copyright notice appear in all copies and that both that
1413572Spstcopyright notice and this permission notice appear in supporting
1513572Spstdocumentation, and that the name of Carnegie Mellon University not be used
1613572Spstin advertising or publicity pertaining to distribution of the software
1713572Spstwithout specific, written prior permission.
1813572Spst
1913572SpstCARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
2013572SpstSOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
2113572SpstIN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
2213572SpstDAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
2313572SpstPROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
2413572SpstACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
2513572SpstSOFTWARE.
2613572Spst************************************************************************/
2713572Spst
2813572Spst/*
2913572Spst * BOOTPGW is typically used to forward BOOTP client requests from
3013572Spst * one subnet to a BOOTP server on a different subnet.
3113572Spst */
3213572Spst
3313572Spst#include <sys/types.h>
3413572Spst#include <sys/param.h>
3513572Spst#include <sys/socket.h>
3613572Spst#include <sys/ioctl.h>
3713572Spst#include <sys/file.h>
3813572Spst#include <sys/time.h>
3913572Spst#include <sys/stat.h>
4013572Spst#include <sys/utsname.h>
4113572Spst
4213572Spst#include <net/if.h>
4313572Spst#include <netinet/in.h>
4413572Spst#include <arpa/inet.h>	/* inet_ntoa */
4513572Spst
4613572Spst#ifndef	NO_UNISTD
4713572Spst#include <unistd.h>
4813572Spst#endif
4913572Spst
5013572Spst#include <stdlib.h>
5113572Spst#include <signal.h>
5213572Spst#include <stdio.h>
5313572Spst#include <string.h>
5413572Spst#include <errno.h>
5513572Spst#include <ctype.h>
5613572Spst#include <netdb.h>
5713572Spst#include <syslog.h>
5813572Spst#include <assert.h>
5913572Spst
6013572Spst#ifdef	NO_SETSID
6113572Spst# include <fcntl.h>		/* for O_RDONLY, etc */
6213572Spst#endif
6313572Spst
6413572Spst#ifndef	USE_BFUNCS
6513572Spst# include <memory.h>
6613572Spst/* Yes, memcpy is OK here (no overlapped copies). */
6713572Spst# define bcopy(a,b,c)    memcpy(b,a,c)
6813572Spst# define bzero(p,l)      memset(p,0,l)
6913572Spst# define bcmp(a,b,c)     memcmp(a,b,c)
7013572Spst#endif
7113572Spst
7213572Spst#include "bootp.h"
7313572Spst#include "getif.h"
7413572Spst#include "hwaddr.h"
7513572Spst#include "report.h"
7613572Spst#include "patchlevel.h"
7713572Spst
7813572Spst/* Local definitions: */
7913572Spst#define MAX_MSG_SIZE			(3*512)	/* Maximum packet size */
8013572Spst#define TRUE 1
8113572Spst#define FALSE 0
8213572Spst#define get_network_errmsg get_errmsg
8313572Spst
8413572Spst
8513572Spst
8613572Spst/*
8713572Spst * Externals, forward declarations, and global variables
8813572Spst */
8913572Spst
9013572Spst#ifdef	__STDC__
9113572Spst#define P(args) args
9213572Spst#else
9313572Spst#define P(args) ()
9413572Spst#endif
9513572Spst
9613572Spststatic void usage P((void));
9713572Spststatic void handle_reply P((void));
9813572Spststatic void handle_request P((void));
9913572Spst
10013572Spst#undef	P
10113572Spst
10213572Spst/*
10313572Spst * IP port numbers for client and server obtained from /etc/services
10413572Spst */
10513572Spst
10613572Spstu_short bootps_port, bootpc_port;
10713572Spst
10813572Spst
10913572Spst/*
11013572Spst * Internet socket and interface config structures
11113572Spst */
11213572Spst
11313572Spststruct sockaddr_in bind_addr;	/* Listening */
11413572Spststruct sockaddr_in recv_addr;	/* Packet source */
11513572Spststruct sockaddr_in send_addr;	/*  destination */
11613572Spst
11713572Spst
11813572Spst/*
11913572Spst * option defaults
12013572Spst */
12113572Spstint debug = 0;					/* Debugging flag (level) */
12213572Spststruct timeval actualtimeout =
12313572Spst{								/* fifteen minutes */
12413572Spst	15 * 60L,					/* tv_sec */
12513572Spst	0							/* tv_usec */
12613572Spst};
12713572Spstu_int maxhops = 4;				/* Number of hops allowed for requests. */
12813572Spstu_int minwait = 3;				/* Number of seconds client must wait before
12913572Spst						   its bootrequest packets are forwarded. */
13013572Spst
13113572Spst/*
13213572Spst * General
13313572Spst */
13413572Spst
13513572Spstint s;							/* Socket file descriptor */
13613572Spstchar *pktbuf;					/* Receive packet buffer */
13713572Spstint pktlen;
13813572Spstchar *progname;
13913572Spstchar *servername;
14013572Spstint32 server_ipa;				/* Real server IP address, network order. */
14113572Spst
14213572Spststruct in_addr my_ip_addr;
14313572Spst
14413572Spststruct utsname my_uname;
14513572Spstchar *hostname;
14613572Spst
14713572Spst
14813572Spst
14913572Spst
15013572Spst
15113572Spst/*
15213572Spst * Initialization such as command-line processing is done and then the
15313572Spst * main server loop is started.
15413572Spst */
15513572Spst
15613572Spstvoid
15713572Spstmain(argc, argv)
15813572Spst	int argc;
15913572Spst	char **argv;
16013572Spst{
16113572Spst	struct timeval *timeout;
16213572Spst	struct bootp *bp;
16313572Spst	struct servent *servp;
16413572Spst	struct hostent *hep;
16513572Spst	char *stmp;
16613572Spst	int n, ba_len, ra_len;
16713572Spst	int nfound, readfds;
16813572Spst	int standalone;
16913572Spst
17013572Spst	progname = strrchr(argv[0], '/');
17113572Spst	if (progname) progname++;
17213572Spst	else progname = argv[0];
17313572Spst
17413572Spst	/*
17513572Spst	 * Initialize logging.
17613572Spst	 */
17713572Spst	report_init(0);				/* uses progname */
17813572Spst
17913572Spst	/*
18013572Spst	 * Log startup
18113572Spst	 */
18213572Spst	report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL);
18313572Spst
18413572Spst	/* Debugging for compilers with struct padding. */
18513572Spst	assert(sizeof(struct bootp) == BP_MINPKTSZ);
18613572Spst
18713572Spst	/* Get space for receiving packets and composing replies. */
18813572Spst	pktbuf = malloc(MAX_MSG_SIZE);
18913572Spst	if (!pktbuf) {
19013572Spst		report(LOG_ERR, "malloc failed");
19113572Spst		exit(1);
19213572Spst	}
19313572Spst	bp = (struct bootp *) pktbuf;
19413572Spst
19513572Spst	/*
19613572Spst	 * Check to see if a socket was passed to us from inetd.
19713572Spst	 *
19813572Spst	 * Use getsockname() to determine if descriptor 0 is indeed a socket
19913572Spst	 * (and thus we are probably a child of inetd) or if it is instead
20013572Spst	 * something else and we are running standalone.
20113572Spst	 */
20213572Spst	s = 0;
20313572Spst	ba_len = sizeof(bind_addr);
20413572Spst	bzero((char *) &bind_addr, ba_len);
20513572Spst	errno = 0;
20613572Spst	standalone = TRUE;
20713572Spst	if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) {
20813572Spst		/*
20913572Spst		 * Descriptor 0 is a socket.  Assume we are a child of inetd.
21013572Spst		 */
21113572Spst		if (bind_addr.sin_family == AF_INET) {
21213572Spst			standalone = FALSE;
21313572Spst			bootps_port = ntohs(bind_addr.sin_port);
21413572Spst		} else {
21513572Spst			/* Some other type of socket? */
21613572Spst			report(LOG_INFO, "getsockname: not an INET socket");
21713572Spst		}
21813572Spst	}
21913572Spst	/*
22013572Spst	 * Set defaults that might be changed by option switches.
22113572Spst	 */
22213572Spst	stmp = NULL;
22313572Spst	timeout = &actualtimeout;
22413572Spst
22513572Spst	if (uname(&my_uname) < 0) {
22613572Spst		fprintf(stderr, "bootpgw: can't get hostname\n");
22713572Spst		exit(1);
22813572Spst	}
22913572Spst	hostname = my_uname.nodename;
23013572Spst
23113572Spst	hep = gethostbyname(hostname);
23213572Spst	if (!hep) {
23313572Spst		printf("Can not get my IP address\n");
23413572Spst		exit(1);
23513572Spst	}
23613572Spst	bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr));
23713572Spst
23813572Spst	/*
23913572Spst	 * Read switches.
24013572Spst	 */
24113572Spst	for (argc--, argv++; argc > 0; argc--, argv++) {
24213572Spst		if (argv[0][0] != '-')
24313572Spst			break;
24413572Spst		switch (argv[0][1]) {
24513572Spst
24613572Spst		case 'd':				/* debug level */
24713572Spst			if (argv[0][2]) {
24813572Spst				stmp = &(argv[0][2]);
24913572Spst			} else if (argv[1] && argv[1][0] == '-') {
25013572Spst				/*
25113572Spst				 * Backwards-compatible behavior:
25213572Spst				 * no parameter, so just increment the debug flag.
25313572Spst				 */
25413572Spst				debug++;
25513572Spst				break;
25613572Spst			} else {
25713572Spst				argc--;
25813572Spst				argv++;
25913572Spst				stmp = argv[0];
26013572Spst			}
26113572Spst			if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
26213572Spst				fprintf(stderr,
26313572Spst						"%s: invalid debug level\n", progname);
26413572Spst				break;
26513572Spst			}
26613572Spst			debug = n;
26713572Spst			break;
26813572Spst
26913572Spst		case 'h':				/* hop count limit */
27013572Spst			if (argv[0][2]) {
27113572Spst				stmp = &(argv[0][2]);
27213572Spst			} else {
27313572Spst				argc--;
27413572Spst				argv++;
27513572Spst				stmp = argv[0];
27613572Spst			}
27713572Spst			if (!stmp || (sscanf(stmp, "%d", &n) != 1) ||
27813572Spst				(n < 0) || (n > 16))
27913572Spst			{
28013572Spst				fprintf(stderr,
28113572Spst						"bootpgw: invalid hop count limit\n");
28213572Spst				break;
28313572Spst			}
28413572Spst			maxhops = (u_int)n;
28513572Spst			break;
28613572Spst
28713572Spst		case 'i':				/* inetd mode */
28813572Spst			standalone = FALSE;
28913572Spst			break;
29013572Spst
29113572Spst		case 's':				/* standalone mode */
29213572Spst			standalone = TRUE;
29313572Spst			break;
29413572Spst
29513572Spst		case 't':				/* timeout */
29613572Spst			if (argv[0][2]) {
29713572Spst				stmp = &(argv[0][2]);
29813572Spst			} else {
29913572Spst				argc--;
30013572Spst				argv++;
30113572Spst				stmp = argv[0];
30213572Spst			}
30313572Spst			if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
30413572Spst				fprintf(stderr,
30513572Spst						"%s: invalid timeout specification\n", progname);
30613572Spst				break;
30713572Spst			}
30813572Spst			actualtimeout.tv_sec = (int32) (60 * n);
30913572Spst			/*
31013572Spst			 * If the actual timeout is zero, pass a NULL pointer
31113572Spst			 * to select so it blocks indefinitely, otherwise,
31213572Spst			 * point to the actual timeout value.
31313572Spst			 */
31413572Spst			timeout = (n > 0) ? &actualtimeout : NULL;
31513572Spst			break;
31613572Spst
31713572Spst		case 'w':				/* wait time */
31813572Spst			if (argv[0][2]) {
31913572Spst				stmp = &(argv[0][2]);
32013572Spst			} else {
32113572Spst				argc--;
32213572Spst				argv++;
32313572Spst				stmp = argv[0];
32413572Spst			}
32513572Spst			if (!stmp || (sscanf(stmp, "%d", &n) != 1) ||
32613572Spst				(n < 0) || (n > 60))
32713572Spst			{
32813572Spst				fprintf(stderr,
32913572Spst						"bootpgw: invalid wait time\n");
33013572Spst				break;
33113572Spst			}
33213572Spst			minwait = (u_int)n;
33313572Spst			break;
33413572Spst
33513572Spst		default:
33613572Spst			fprintf(stderr, "%s: unknown switch: -%c\n",
33713572Spst					progname, argv[0][1]);
33813572Spst			usage();
33913572Spst			break;
34013572Spst
34113572Spst		} /* switch */
34213572Spst	} /* for args */
34313572Spst
34413572Spst	/* Make sure server name argument is suplied. */
34513572Spst	servername = argv[0];
34613572Spst	if (!servername) {
34713572Spst		fprintf(stderr, "bootpgw: missing server name\n");
34813572Spst		usage();
34913572Spst	}
35013572Spst	/*
35113572Spst	 * Get address of real bootp server.
35213572Spst	 */
35313572Spst	if (isdigit(servername[0]))
35413572Spst		server_ipa = inet_addr(servername);
35513572Spst	else {
35613572Spst		hep = gethostbyname(servername);
35713572Spst		if (!hep) {
35813572Spst			fprintf(stderr, "bootpgw: can't get addr for %s\n", servername);
35913572Spst			exit(1);
36013572Spst		}
36113572Spst		bcopy(hep->h_addr, (char *)&server_ipa, sizeof(server_ipa));
36213572Spst	}
36313572Spst
36413572Spst	if (standalone) {
36513572Spst		/*
36613572Spst		 * Go into background and disassociate from controlling terminal.
36713572Spst		 * XXX - This is not the POSIX way (Should use setsid). -gwr
36813572Spst		 */
36913572Spst		if (debug < 3) {
37013572Spst			if (fork())
37113572Spst				exit(0);
37213572Spst#ifdef	NO_SETSID
37313572Spst			setpgrp(0,0);
37413572Spst#ifdef TIOCNOTTY
37513572Spst			n = open("/dev/tty", O_RDWR);
37613572Spst			if (n >= 0) {
37713572Spst				ioctl(n, TIOCNOTTY, (char *) 0);
37813572Spst				(void) close(n);
37913572Spst			}
38013572Spst#endif	/* TIOCNOTTY */
38113572Spst#else	/* SETSID */
38213572Spst			if (setsid() < 0)
38313572Spst				perror("setsid");
38413572Spst#endif	/* SETSID */
38513572Spst		} /* if debug < 3 */
38613572Spst		/*
38713572Spst		 * Nuke any timeout value
38813572Spst		 */
38913572Spst		timeout = NULL;
39013572Spst
39113572Spst		/*
39213572Spst		 * Here, bootpd would do:
39313572Spst		 *	chdir
39413572Spst		 *	tzone_init
39513572Spst		 *	rdtab_init
39613572Spst		 *	readtab
39713572Spst		 */
39813572Spst
39913572Spst		/*
40013572Spst		 * Create a socket.
40113572Spst		 */
40213572Spst		if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
40313572Spst			report(LOG_ERR, "socket: %s", get_network_errmsg());
40413572Spst			exit(1);
40513572Spst		}
40613572Spst		/*
40713572Spst		 * Get server's listening port number
40813572Spst		 */
40913572Spst		servp = getservbyname("bootps", "udp");
41013572Spst		if (servp) {
41113572Spst			bootps_port = ntohs((u_short) servp->s_port);
41213572Spst		} else {
41313572Spst			bootps_port = (u_short) IPPORT_BOOTPS;
41413572Spst			report(LOG_ERR,
41513572Spst				   "udp/bootps: unknown service -- assuming port %d",
41613572Spst				   bootps_port);
41713572Spst		}
41813572Spst
41913572Spst		/*
42013572Spst		 * Bind socket to BOOTPS port.
42113572Spst		 */
42213572Spst		bind_addr.sin_family = AF_INET;
42313572Spst		bind_addr.sin_port = htons(bootps_port);
42413572Spst		bind_addr.sin_addr.s_addr = INADDR_ANY;
42513572Spst		if (bind(s, (struct sockaddr *) &bind_addr,
42613572Spst				 sizeof(bind_addr)) < 0)
42713572Spst		{
42813572Spst			report(LOG_ERR, "bind: %s", get_network_errmsg());
42913572Spst			exit(1);
43013572Spst		}
43113572Spst	} /* if standalone */
43213572Spst	/*
43313572Spst	 * Get destination port number so we can reply to client
43413572Spst	 */
43513572Spst	servp = getservbyname("bootpc", "udp");
43613572Spst	if (servp) {
43713572Spst		bootpc_port = ntohs(servp->s_port);
43813572Spst	} else {
43913572Spst		report(LOG_ERR,
44013572Spst			   "udp/bootpc: unknown service -- assuming port %d",
44113572Spst			   IPPORT_BOOTPC);
44213572Spst		bootpc_port = (u_short) IPPORT_BOOTPC;
44313572Spst	}
44413572Spst
44513572Spst	/* no signal catchers */
44613572Spst
44713572Spst	/*
44813572Spst	 * Process incoming requests.
44913572Spst	 */
45013572Spst	for (;;) {
45113572Spst		struct timeval tv;
45213572Spst
45313572Spst		readfds = 1 << s;
45413572Spst		if (timeout)
45513572Spst			tv = *timeout;
45613572Spst
45713572Spst		nfound = select(s + 1, (fd_set *)&readfds, NULL, NULL,
45813572Spst						(timeout) ? &tv : NULL);
45913572Spst		if (nfound < 0) {
46013572Spst			if (errno != EINTR) {
46113572Spst				report(LOG_ERR, "select: %s", get_errmsg());
46213572Spst			}
46313572Spst			continue;
46413572Spst		}
46513572Spst		if (!(readfds & (1 << s))) {
46613572Spst			report(LOG_INFO, "exiting after %ld minutes of inactivity",
46713572Spst				   actualtimeout.tv_sec / 60);
46813572Spst			exit(0);
46913572Spst		}
47013572Spst		ra_len = sizeof(recv_addr);
47113572Spst		n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0,
47213572Spst					 (struct sockaddr *) &recv_addr, &ra_len);
47313572Spst		if (n <= 0) {
47413572Spst			continue;
47513572Spst		}
47613572Spst		if (debug > 3) {
47713572Spst			report(LOG_INFO, "recvd pkt from IP addr %s",
47813572Spst				   inet_ntoa(recv_addr.sin_addr));
47913572Spst		}
48013572Spst		if (n < sizeof(struct bootp)) {
48113572Spst			if (debug) {
48213572Spst				report(LOG_INFO, "received short packet");
48313572Spst			}
48413572Spst			continue;
48513572Spst		}
48613572Spst		pktlen = n;
48713572Spst
48813572Spst		switch (bp->bp_op) {
48913572Spst		case BOOTREQUEST:
49013572Spst			handle_request();
49113572Spst			break;
49213572Spst		case BOOTREPLY:
49313572Spst			handle_reply();
49413572Spst			break;
49513572Spst		}
49613572Spst	}
49713572Spst}
49813572Spst
49913572Spst
50013572Spst
50113572Spst
50213572Spst/*
50313572Spst * Print "usage" message and exit
50413572Spst */
50513572Spst
50613572Spststatic void
50713572Spstusage()
50813572Spst{
50913572Spst	fprintf(stderr,
51013572Spst			"usage:  bootpgw [-d level] [-i] [-s] [-t timeout] server\n");
51113572Spst	fprintf(stderr, "\t -d n\tset debug level\n");
51213572Spst	fprintf(stderr, "\t -h n\tset max hop count\n");
51313572Spst	fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n");
51413572Spst	fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n");
51513572Spst	fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n");
51613572Spst	fprintf(stderr, "\t -w n\tset min wait time (secs)\n");
51713572Spst	exit(1);
51813572Spst}
51913572Spst
52013572Spst
52113572Spst
52213572Spst/*
52313572Spst * Process BOOTREQUEST packet.
52413572Spst *
52513572Spst * Note, this just forwards the request to a real server.
52613572Spst */
52713572Spststatic void
52813572Spsthandle_request()
52913572Spst{
53013572Spst	struct bootp *bp = (struct bootp *) pktbuf;
53113572Spst	u_short secs, hops;
53213572Spst
53313572Spst	/* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */
53413572Spst
53513572Spst	if (debug) {
53613572Spst		report(LOG_INFO, "request from %s",
53713572Spst			   inet_ntoa(recv_addr.sin_addr));
53813572Spst	}
53913572Spst	/* Has the client been waiting long enough? */
54013572Spst	secs = ntohs(bp->bp_secs);
54113572Spst	if (secs < minwait)
54213572Spst		return;
54313572Spst
54413572Spst	/* Has this packet hopped too many times? */
54513572Spst	hops = ntohs(bp->bp_hops);
54613572Spst	if (++hops > maxhops) {
54713572Spst		report(LOG_NOTICE, "reqest from %s reached hop limit",
54813572Spst			   inet_ntoa(recv_addr.sin_addr));
54913572Spst		return;
55013572Spst	}
55113572Spst	bp->bp_hops = htons(hops);
55213572Spst
55313572Spst	/*
55413572Spst	 * Here one might discard a request from the same subnet as the
55513572Spst	 * real server, but we can assume that the real server will send
55613572Spst	 * a reply to the client before it waits for minwait seconds.
55713572Spst	 */
55813572Spst
55913572Spst	/* If gateway address is not set, put in local interface addr. */
56013572Spst	if (bp->bp_giaddr.s_addr == 0) {
56113572Spst#if 0	/* BUG */
56213572Spst		struct sockaddr_in *sip;
56313572Spst		struct ifreq *ifr;
56413572Spst		/*
56513572Spst		 * XXX - This picks the wrong interface when the receive addr
56613572Spst		 * is the broadcast address.  There is no  portable way to
56713572Spst		 * find out which interface a broadcast was received on. -gwr
56813572Spst		 * (Thanks to <walker@zk3.dec.com> for finding this bug!)
56913572Spst		 */
57013572Spst		ifr = getif(s, &recv_addr.sin_addr);
57113572Spst		if (!ifr) {
57213572Spst			report(LOG_NOTICE, "no interface for request from %s",
57313572Spst				   inet_ntoa(recv_addr.sin_addr));
57413572Spst			return;
57513572Spst		}
57613572Spst		sip = (struct sockaddr_in *) &(ifr->ifr_addr);
57713572Spst		bp->bp_giaddr = sip->sin_addr;
57813572Spst#else	/* BUG */
57913572Spst		/*
58013572Spst		 * XXX - Just set "giaddr" to our "official" IP address.
58113572Spst		 * RFC 1532 says giaddr MUST be set to the address of the
58213572Spst		 * interface on which the request was received.  Setting
58313572Spst		 * it to our "default" IP address is not strictly correct,
58413572Spst		 * but is good enough to allow the real BOOTP server to
58513572Spst		 * get the reply back here.  Then, before we forward the
58613572Spst		 * reply to the client, the giaddr field is corrected.
58713572Spst		 * (In case the client uses giaddr, which it should not.)
58813572Spst		 * See handle_reply()
58913572Spst		 */
59013572Spst		bp->bp_giaddr = my_ip_addr;
59113572Spst#endif	/* BUG */
59213572Spst
59313572Spst		/*
59413572Spst		 * XXX - DHCP says to insert a subnet mask option into the
59513572Spst		 * options area of the request (if vendor magic == std).
59613572Spst		 */
59713572Spst	}
59813572Spst	/* Set up socket address for send. */
59913572Spst	send_addr.sin_family = AF_INET;
60013572Spst	send_addr.sin_port = htons(bootps_port);
60113572Spst	send_addr.sin_addr.s_addr = server_ipa;
60213572Spst
60313572Spst	/* Send reply with same size packet as request used. */
60413572Spst	if (sendto(s, pktbuf, pktlen, 0,
60513572Spst			   (struct sockaddr *) &send_addr,
60613572Spst			   sizeof(send_addr)) < 0)
60713572Spst	{
60813572Spst		report(LOG_ERR, "sendto: %s", get_network_errmsg());
60913572Spst	}
61013572Spst}
61113572Spst
61213572Spst
61313572Spst
61413572Spst/*
61513572Spst * Process BOOTREPLY packet.
61613572Spst */
61713572Spststatic void
61813572Spsthandle_reply()
61913572Spst{
62013572Spst	struct bootp *bp = (struct bootp *) pktbuf;
62113572Spst	struct ifreq *ifr;
62213572Spst	struct sockaddr_in *sip;
62313572Spst	unsigned char *ha;
62413572Spst	int len, haf;
62513572Spst
62613572Spst	if (debug) {
62713572Spst		report(LOG_INFO, "   reply for %s",
62813572Spst			   inet_ntoa(bp->bp_yiaddr));
62913572Spst	}
63013572Spst	/* Make sure client is directly accessible. */
63113572Spst	ifr = getif(s, &(bp->bp_yiaddr));
63213572Spst	if (!ifr) {
63313572Spst		report(LOG_NOTICE, "no interface for reply to %s",
63413572Spst			   inet_ntoa(bp->bp_yiaddr));
63513572Spst		return;
63613572Spst	}
63713572Spst#if 1	/* Experimental (see BUG above) */
63813572Spst/* #ifdef CATER_TO_OLD_CLIENTS ? */
63913572Spst	/*
64013572Spst	 * The giaddr field has been set to our "default" IP address
64113572Spst	 * which might not be on the same interface as the client.
64213572Spst	 * In case the client looks at giaddr, (which it should not)
64313572Spst	 * giaddr is now set to the address of the correct interface.
64413572Spst	 */
64513572Spst	sip = (struct sockaddr_in *) &(ifr->ifr_addr);
64613572Spst	bp->bp_giaddr = sip->sin_addr;
64713572Spst#endif
64813572Spst
64913572Spst	/* Set up socket address for send to client. */
65013572Spst	send_addr.sin_family = AF_INET;
65113572Spst	send_addr.sin_addr = bp->bp_yiaddr;
65213572Spst	send_addr.sin_port = htons(bootpc_port);
65313572Spst
65413572Spst	/* Create an ARP cache entry for the client. */
65513572Spst	ha = bp->bp_chaddr;
65613572Spst	len = bp->bp_hlen;
65713572Spst	if (len > MAXHADDRLEN)
65813572Spst		len = MAXHADDRLEN;
65913572Spst	haf = (int) bp->bp_htype;
66013572Spst	if (haf == 0)
66113572Spst		haf = HTYPE_ETHERNET;
66213572Spst
66313572Spst	if (debug > 1)
66413572Spst		report(LOG_INFO, "setarp %s - %s",
66513572Spst			   inet_ntoa(bp->bp_yiaddr), haddrtoa(ha, len));
66613572Spst	setarp(s, &bp->bp_yiaddr, haf, ha, len);
66713572Spst
66813572Spst	/* Send reply with same size packet as request used. */
66913572Spst	if (sendto(s, pktbuf, pktlen, 0,
67013572Spst			   (struct sockaddr *) &send_addr,
67113572Spst			   sizeof(send_addr)) < 0)
67213572Spst	{
67313572Spst		report(LOG_ERR, "sendto: %s", get_network_errmsg());
67413572Spst	}
67513572Spst}
67613572Spst
67713572Spst/*
67813572Spst * Local Variables:
67913572Spst * tab-width: 4
68013572Spst * c-indent-level: 4
68113572Spst * c-argdecl-indent: 4
68213572Spst * c-continued-statement-offset: 4
68313572Spst * c-continued-brace-offset: -4
68413572Spst * c-label-offset: -4
68513572Spst * c-brace-offset: 0
68613572Spst * End:
68713572Spst */
688