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