view usr/src/cmd/cmd-inet/usr.sbin/in.rarpd.c @ 0:c9caec207d52 b86

Initial porting based on b86
author Koji Uno <koji.uno@sun.com>
date Tue, 02 Jun 2009 18:56:50 +0900
parents
children 1a15d5aaf794
line wrap: on
line source

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */
/*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved  	*/

/*
 * Portions of this source code were derived from Berkeley 4.3 BSD
 * under license from the Regents of the University of California.
 */

#pragma ident	"@(#)in.rarpd.c	1.39	07/06/12 SMI"

/*
 * rarpd.c  Reverse-ARP server.
 * Refer to RFC 903 "A Reverse Address Resolution Protocol".
 */

#define	_REENTRANT

#include	<thread.h>
#include	<synch.h>
#include	<stdlib.h>
#include	<unistd.h>
#include	<sys/resource.h>
#include	<stdio.h>
#include	<stdio_ext.h>
#include	<stdarg.h>
#include	<string.h>
#include	<fcntl.h>
#include	<sys/types.h>
#include	<dirent.h>
#include	<syslog.h>
#include	<netdb.h>
#include	<errno.h>
#include	<sys/socket.h>
#include	<sys/sockio.h>
#include	<net/if.h>
#include	<netinet/if_ether.h>
#include	<netinet/in.h>
#include	<arpa/inet.h>
#include	<stropts.h>
#include	<libinetutil.h>
#include	<libdlpi.h>
#include	<net/if_types.h>
#include	<net/if_dl.h>

#define	BOOTDIR		"/tftpboot"	/* boot files directory */
#define	DEVIP		"/dev/ip"	/* path to ip driver */
#define	DEVARP		"/dev/arp"	/* path to arp driver */

#define	BUFSIZE		2048		/* max receive frame length */
#define	MAXPATHL	128		/* max path length */
#define	MAXHOSTL	128		/* max host name length */
#define	MAXIFS		256

/*
 * Logical network devices
 */
struct	ifdev {
	char		ldevice[IFNAMSIZ];
	int		lunit;
	ipaddr_t	ipaddr;			/* network order */
	ipaddr_t	if_netmask;		/* host order */
	ipaddr_t	if_ipaddr;		/* host order */
	ipaddr_t	if_netnum;		/* host order, with subnet */
	struct ifdev *next;
};

/*
 * Physical network device
 */
struct	rarpdev {
	char		device[DLPI_LINKNAME_MAX];
	uint_t		unit;
	dlpi_handle_t	dh_rarp;
	uchar_t		physaddr[DLPI_PHYSADDR_MAX];
						/* mac address of interface */
	uint_t		physaddrlen;		/* mac address length */
	int		ifrarplen;		/* size of rarp data packet */
	struct ifdev	*ifdev;			/* private interface info */
	struct rarpdev	*next;			/* list of managed devices */
};

struct	rarpreply {
	struct rarpdev		*rdev;		/* which device reply for */
	struct timeval		tv;		/* send RARP reply by when */
	uchar_t			*lldest;	/* target mac to send reply */
	uchar_t			*arprep;	/* [R]ARP response */
	struct rarpreply	*next;
};

static struct rarpreply	*delay_list;
static sema_t		delay_sema;
static mutex_t		delay_mutex;
static mutex_t		debug_mutex;

static struct rarpdev	*rarpdev_head;

/*
 * Globals initialized before multi-threading
 */
static char	*cmdname;		/* command name from argv[0] */
static int	dflag = 0;		/* enable diagnostics */
static int	aflag = 0;		/* start rarpd on all interfaces */

static void	getintf(void);
static struct rarpdev *find_device(ifspec_t *);
static void	init_rarpdev(struct rarpdev *);
static void	do_rarp(void *);
static void	rarp_request(struct rarpdev *, struct arphdr *,
		    uchar_t *);
static void	add_arp(struct rarpdev *, uchar_t *, uchar_t *);
static void	arp_request(struct rarpdev *, struct arphdr *, uchar_t *);
static void	do_delay_write(void *);
static void	delay_write(struct rarpdev *, struct rarpreply *);
static int	mightboot(ipaddr_t);
static void	get_ifdata(char *, int, ipaddr_t *, ipaddr_t *);
static int	get_ipaddr(struct rarpdev *, uchar_t *, uchar_t *, ipaddr_t *);
static int	strioctl(int, int, int, int, char *);
static void	usage();
static void	syserr(const char *);
/*PRINTFLIKE1*/
static void	error(const char *, ...);
static void	debug(char *, ...);

extern	int	optind;
extern	char	*optarg;

int
main(int argc, char *argv[])
{
	int		c;
	struct rlimit rl;
	struct rarpdev	*rdev;
	int		i;

	cmdname = argv[0];

	while ((c = getopt(argc, argv, "ad")) != -1) {
		switch (c) {
		case 'a':
			aflag = 1;
			break;

		case 'd':
			dflag = 1;
			break;

		default:
			usage();
		}
	}

	if ((!aflag && (argc - optind) != 2) ||
	    (aflag && (argc - optind) != 0)) {
		usage();
		/* NOTREACHED */
	}

	if (!dflag) {
		/*
		 * Background
		 */
		switch (fork()) {
			case -1:	/* error */
				syserr("fork");
				/*NOTREACHED*/

			case 0:		/* child */
				break;

			default:	/* parent */
				return (0);
		}
		for (i = 0; i < 3; i++) {
			(void) close(i);
		}
		(void) open("/", O_RDONLY, 0);
		(void) dup2(0, 1);
		(void) dup2(0, 2);
		/*
		 * Detach terminal
		 */
		if (setsid() < 0)
			syserr("setsid");
	}

	rl.rlim_cur = RLIM_INFINITY;
	rl.rlim_max = RLIM_INFINITY;
	if (setrlimit(RLIMIT_NOFILE, &rl) == -1)
		syserr("setrlimit");
	(void) enable_extended_FILE_stdio(-1, -1);

	(void) openlog(cmdname, LOG_PID, LOG_DAEMON);

	if (aflag) {
		/*
		 * Get each interface name and load rarpdev list.
		 */
		getintf();
	} else {
		ifspec_t	ifsp;
		struct ifdev	*ifdev;
		char		buf[IFNAMSIZ + 1];

		/*
		 * Load specified device as only element of the list.
		 */
		rarpdev_head = (struct rarpdev *)calloc(1,
		    sizeof (struct rarpdev));
		if (rarpdev_head == NULL) {
			error("out of memory");
		}
		(void) strncpy(buf, argv[optind], IFNAMSIZ);
		(void) strncat(buf, argv[optind + 1], IFNAMSIZ - strlen(buf));

		if ((ifdev = calloc(1, sizeof (struct ifdev))) == NULL) {
			error("out of memory");
		}

		if (!ifparse_ifspec(buf, &ifsp) || ifsp.ifsp_modcnt != 0) {
			error("invalid interface specification");
		}

		if (ifsp.ifsp_lunvalid) {
			(void) snprintf(ifdev->ldevice,
			    sizeof (ifdev->ldevice), "%s%d:",
			    ifsp.ifsp_devnm, ifsp.ifsp_ppa);
			ifdev->lunit = ifsp.ifsp_lun;
		} else {
			ifdev->lunit = -1; /* no logical unit */
		}
		(void) strlcpy(rarpdev_head->device, ifsp.ifsp_devnm,
		    sizeof (rarpdev_head->device));
		rarpdev_head->unit = ifsp.ifsp_ppa;

		ifdev->next = rarpdev_head->ifdev;
		rarpdev_head->ifdev = ifdev;
	}

	/*
	 * Initialize each rarpdev.
	 */
	for (rdev = rarpdev_head; rdev != NULL; rdev = rdev->next) {
		init_rarpdev(rdev);
	}

	(void) sema_init(&delay_sema, 0, USYNC_THREAD, NULL);
	(void) mutex_init(&delay_mutex, USYNC_THREAD, NULL);
	(void) mutex_init(&debug_mutex, USYNC_THREAD, NULL);

	/*
	 * Start delayed processing thread.
	 */
	(void) thr_create(NULL, NULL, (void *(*)(void *))do_delay_write, NULL,
	    THR_NEW_LWP, NULL);

	/*
	 * Start RARP processing for each device.
	 */
	for (rdev = rarpdev_head; rdev != NULL; rdev = rdev->next) {
		if (rdev->dh_rarp != NULL) {
			(void) thr_create(NULL, NULL,
			    (void *(*)(void *))do_rarp, (void *)rdev,
			    THR_NEW_LWP, NULL);
		}
	}

	/*
	 * Exit main() thread
	 */
	thr_exit(NULL);

	return (0);
}

static void
getintf(void)
{
	int		fd;
	int		numifs;
	unsigned	bufsize;
	struct ifreq	*reqbuf;
	struct ifconf	ifconf;
	struct ifreq	*ifr;
	struct rarpdev	*rdev;
	struct ifdev	*ifdev;

	/*
	 * Open the IP provider.
	 */
	if ((fd = open(DEVIP, 0)) < 0)
		syserr(DEVIP);

	/*
	 * Ask IP for the list of configured interfaces.
	 */
	if (ioctl(fd, SIOCGIFNUM, (char *)&numifs) < 0) {
		numifs = MAXIFS;
	}
	bufsize = numifs * sizeof (struct ifreq);
	reqbuf = (struct ifreq *)malloc(bufsize);
	if (reqbuf == NULL) {
		error("out of memory");
	}

	ifconf.ifc_len = bufsize;
	ifconf.ifc_buf = (caddr_t)reqbuf;
	if (ioctl(fd, SIOCGIFCONF, (char *)&ifconf) < 0)
		syserr("SIOCGIFCONF");

	/*
	 * Initialize a rarpdev for each interface.
	 */
	for (ifr = ifconf.ifc_req; ifconf.ifc_len > 0;
	    ifr++, ifconf.ifc_len -= sizeof (struct ifreq)) {
		ifspec_t	ifsp;

		if (ioctl(fd, SIOCGIFFLAGS, (char *)ifr) < 0) {
			syserr("ioctl SIOCGIFFLAGS");
			exit(1);
		}
		if ((ifr->ifr_flags & IFF_LOOPBACK) ||
		    !(ifr->ifr_flags & IFF_UP) ||
		    !(ifr->ifr_flags & IFF_BROADCAST) ||
		    (ifr->ifr_flags & IFF_NOARP) ||
		    (ifr->ifr_flags & IFF_POINTOPOINT))
			continue;

		if (!ifparse_ifspec(ifr->ifr_name, &ifsp))
			error("ifparse_ifspec failed");

		/*
		 * Look for an existing device for logical interfaces.
		 */
		if ((rdev = find_device(&ifsp)) == NULL) {
			rdev = calloc(1, sizeof (struct rarpdev));
			if (rdev == NULL)
				error("out of memory");

			(void) strlcpy(rdev->device, ifsp.ifsp_devnm,
			    sizeof (rdev->device));
			rdev->unit = ifsp.ifsp_ppa;

			rdev->next = rarpdev_head;
			rarpdev_head = rdev;
		}

		if ((ifdev = calloc(1, sizeof (struct ifdev))) == NULL)
			error("out of memory");

		if (ifsp.ifsp_lunvalid) {
			(void) snprintf(ifdev->ldevice,
			    sizeof (ifdev->ldevice), "%s%d:",
			    ifsp.ifsp_devnm, ifsp.ifsp_ppa);
			ifdev->lunit = ifsp.ifsp_lun;
		} else
			ifdev->lunit = -1; /* no logical unit */

		ifdev->next = rdev->ifdev;
		rdev->ifdev = ifdev;
	}
	(void) free((char *)reqbuf);
}

static struct rarpdev *
find_device(ifspec_t *specp)
{
	struct rarpdev	*rdev;

	for (rdev = rarpdev_head; rdev != NULL; rdev = rdev->next) {
		if (specp->ifsp_ppa == rdev->unit &&
		    strcmp(specp->ifsp_devnm, rdev->device) == 0)
			return (rdev);
	}
	return (NULL);
}

static void
init_rarpdev(struct rarpdev *rdev)
{
	char 		*dev;
	int 		unit;
	struct ifdev 	*ifdev;
	int		retval;
	char		*str = NULL;
	uint_t		physaddrlen = DLPI_PHYSADDR_MAX;
	char		linkname[DLPI_LINKNAME_MAX];
	dlpi_handle_t	dh;

	(void) snprintf(linkname, DLPI_LINKNAME_MAX, "%s%d", rdev->device,
	    rdev->unit);
	/*
	 * Open datalink provider and get our mac address.
	 */
	if ((retval = dlpi_open(linkname, &dh, 0)) != DLPI_SUCCESS) {
		error("cannot open link %s: %s", linkname,
		    dlpi_strerror(retval));
	}

	if ((retval = dlpi_bind(dh, ETHERTYPE_REVARP, NULL)) != DLPI_SUCCESS) {
		dlpi_close(dh);
		error("dlpi_bind failed: %s", dlpi_strerror(retval));
	}

	/*
	 * Save our mac address.
	 */
	if ((retval = dlpi_get_physaddr(dh, DL_CURR_PHYS_ADDR, rdev->physaddr,
	    &physaddrlen)) != DLPI_SUCCESS) {
		dlpi_close(dh);
		error("dlpi_get_physaddr failed: %s", dlpi_strerror(retval));
	}

	rdev->physaddrlen = physaddrlen;
	rdev->ifrarplen = sizeof (struct arphdr) + (2 * sizeof (ipaddr_t)) +
	    (2 * physaddrlen);

	if (dflag) {
		str = _link_ntoa(rdev->physaddr, str,
		    rdev->physaddrlen, IFT_OTHER);
		if (str != NULL) {
			debug("device %s physical address %s", linkname, str);
			free(str);
		}
	}

	/*
	 * Assign dlpi handle to rdev.
	 */
	rdev->dh_rarp = dh;

	/*
	 * Get the IP address and netmask from directory service for
	 * each logical interface.
	 */
	for (ifdev = rdev->ifdev; ifdev != NULL; ifdev = ifdev->next) {
		/*
		 * If lunit == -1 then this is the primary interface name.
		 */
		if (ifdev->lunit == -1) {
			dev = rdev->device;
			unit = rdev->unit;
		} else {
			dev = ifdev->ldevice;
			unit = ifdev->lunit;
		}
		get_ifdata(dev, unit, &ifdev->if_ipaddr, &ifdev->if_netmask);

		/*
		 * Use IP address of the interface.
		 */
		ifdev->if_netnum = ifdev->if_ipaddr & ifdev->if_netmask;
		ifdev->ipaddr = (ipaddr_t)htonl(ifdev->if_ipaddr);
	}
}

static void
do_rarp(void *buf)
{
	struct rarpdev *rdev = buf;
	char	*cause;
	struct arphdr *ans;
	uchar_t *shost;
	uint_t	saddrlen;
	size_t	anslen = rdev->ifrarplen;
	char	*str = NULL;
	int	retval;

	if (((shost = malloc(rdev->physaddrlen)) == NULL) ||
	    ((ans = malloc(rdev->ifrarplen)) == NULL))
		syserr("malloc");

	if (dflag) {
		str = _link_ntoa(rdev->physaddr, str, rdev->physaddrlen,
		    IFT_OTHER);
		if (str != NULL) {
			debug("starting rarp service on device %s%d physical"
			    " address %s", rdev->device, rdev->unit, str);
			free(str);
		}
	}

	/*
	 * Read RARP packets and respond to them.
	 */
	for (;;) {
		saddrlen = DLPI_PHYSADDR_MAX;
		retval = dlpi_recv(rdev->dh_rarp, shost,
		    &saddrlen, ans, &anslen, -1, NULL);
		if (retval == DLPI_ETIMEDOUT) {
			continue;
		} else if (retval != DLPI_SUCCESS) {
			error("error in dlpi_recv %s: %s", rdev->dh_rarp,
			    dlpi_strerror(retval));
		}

		cause = NULL;

		if (anslen < rdev->ifrarplen)
			cause = "short packet";
		else if (ans->ar_hrd != htons(ARPHRD_ETHER))
			cause = "hardware type not Ethernet";
		else if (ans->ar_pro != htons(ETHERTYPE_IP))
			cause = "protocol type not IP";
		else if (ans->ar_hln != rdev->physaddrlen)
			cause = "unexpected hardware address length";
		else if (ans->ar_pln != sizeof (ipaddr_t))
			cause = "unexpected protocol address length";
		if (cause != NULL) {
			if (dflag)
				debug("RARP packet received but "
				    "discarded: %s", cause);
			continue;
		}

		/*
		 * Handle the request.
		 */
		switch (ntohs(ans->ar_op)) {
		case REVARP_REQUEST:
			rarp_request(rdev, ans, shost);
			break;

		case ARPOP_REQUEST:
			arp_request(rdev, ans, shost);
			break;

		case REVARP_REPLY:
			if (dflag)
				debug("REVARP_REPLY ignored");
			break;

		case ARPOP_REPLY:
			if (dflag)
				debug("ARPOP_REPLY ignored");
			break;

		default:
			if (dflag)
				debug("unknown opcode 0x%x", ans->ar_op);
			break;
		}
	}
}

/*
 * Reverse address determination and allocation code.
 */
static void
rarp_request(struct rarpdev *rdev, struct arphdr *rp, uchar_t *shost)
{
	ipaddr_t		tpa,  spa;
	struct	rarpreply	*rrp;
	uchar_t			*shap, *thap, *spap, *tpap;
	char			*str = NULL;
	int			retval;

	shap = (uchar_t *)rp + sizeof (struct arphdr);
	spap = shap + rp->ar_hln;
	thap = spap + rp->ar_pln;
	tpap = thap + rp->ar_hln;

	if (dflag) {
		str = _link_ntoa(thap, str, rdev->physaddrlen, IFT_OTHER);
		if (str != NULL) {
			debug("RARP_REQUEST for %s", str);
			free(str);
		}
	}

	/*
	 * Third party lookups are rare and wonderful.
	 */
	if ((memcmp(shap, thap, rdev->physaddrlen) != 0) ||
	    (memcmp(shap, shost, rdev->physaddrlen) != 0)) {
		if (dflag)
			debug("weird (3rd party lookup)");
	}

	/*
	 * Fill in given parts of reply packet.
	 */
	(void) memcpy(shap, rdev->physaddr, rdev->physaddrlen);

	/*
	 * If a good address is stored in our lookup tables, return it
	 * immediately or after a delay.  Store it in our kernel's ARP cache.
	 */
	if (get_ipaddr(rdev, thap, tpap, &spa))
		return;
	(void) memcpy(spap, &spa, sizeof (spa));

	add_arp(rdev, tpap, thap);

	rp->ar_op = htons(REVARP_REPLY);

	if (dflag) {
		struct in_addr addr;

		(void) memcpy(&addr, tpap, sizeof (ipaddr_t));
		debug("good lookup, maps to %s", inet_ntoa(addr));
	}

	rrp = calloc(1, sizeof (struct rarpreply) + rdev->physaddrlen +
	    rdev->ifrarplen);
	if (rrp == NULL)
		error("out of memory");
	rrp->lldest = (uchar_t *)rrp + sizeof (struct rarpreply);
	rrp->arprep = rrp->lldest + rdev->physaddrlen;

	/*
	 * Create rarpreply structure.
	 */
	(void) gettimeofday(&rrp->tv, NULL);
	rrp->tv.tv_sec += 3;	/* delay */
	rrp->rdev = rdev;
	(void) memcpy(rrp->lldest, shost, rdev->physaddrlen);
	(void) memcpy(rrp->arprep, rp, rdev->ifrarplen);

	/*
	 * If this is diskless and we're not its bootserver, let the
	 * bootserver reply first by delaying a while.
	 */
	(void) memcpy(&tpa, tpap, sizeof (ipaddr_t));
	if (mightboot(ntohl(tpa))) {
		retval = dlpi_send(rdev->dh_rarp, rrp->lldest,
		    rdev->physaddrlen, rrp->arprep, rdev->ifrarplen, NULL);
		if (retval != DLPI_SUCCESS) {
			error("dlpi_send failed: %s", dlpi_strerror(retval));
		} else if (dflag) {
			debug("immediate reply sent");
		}
		(void) free(rrp);
	} else {
		delay_write(rdev, rrp);
	}
}

/*
 * Download an ARP entry into our kernel.
 */
static void
add_arp(struct rarpdev *rdev, uchar_t *ip, uchar_t *laddr)
{
	struct xarpreq ar;
	struct sockaddr_in	*sin;
	int	fd;

	/*
	 * Common part of query or set.
	 */
	(void) memset(&ar, 0, sizeof (ar));
	ar.xarp_pa.ss_family = AF_INET;
	sin = (struct sockaddr_in *)&ar.xarp_pa;
	(void) memcpy(&sin->sin_addr, ip, sizeof (ipaddr_t));

	/*
	 * Open the IP provider.
	 */
	if ((fd = open(DEVARP, 0)) < 0)
		syserr(DEVARP);

	/*
	 * Set the entry.
	 */
	(void) memcpy(LLADDR(&ar.xarp_ha), laddr, rdev->physaddrlen);
	ar.xarp_ha.sdl_alen = rdev->physaddrlen;
	ar.xarp_ha.sdl_family = AF_LINK;
	(void) strioctl(fd, SIOCDXARP, -1, sizeof (struct xarpreq),
	    (char *)&ar);
	if (strioctl(fd, SIOCSXARP, -1, sizeof (struct xarpreq),
	    (char *)&ar) < 0)
		syserr("SIOCSXARP");

	(void) close(fd);
}

/*
 * The RARP spec says we must be able to process ARP requests,
 * even through the packet type is RARP.  Let's hope this feature
 * is not heavily used.
 */
static void
arp_request(struct rarpdev *rdev, struct arphdr *rp, uchar_t *shost)
{
	struct	rarpreply	*rrp;
	struct ifdev		*ifdev;
	uchar_t			*shap, *thap, *spap, *tpap;
	int			retval;

	shap = (uchar_t *)rp + sizeof (struct arphdr);
	spap = shap + rp->ar_hln;
	thap = spap + rp->ar_pln;
	tpap = thap + rp->ar_hln;

	if (dflag)
		debug("ARPOP_REQUEST");

	for (ifdev = rdev->ifdev; ifdev != NULL; ifdev = ifdev->next) {
		if (memcmp(&ifdev->ipaddr, tpap, sizeof (ipaddr_t)) == 0)
			break;
	}
	if (ifdev == NULL)
		return;

	rp->ar_op = ARPOP_REPLY;
	(void) memcpy(shap, rdev->physaddr, rdev->physaddrlen);
	(void) memcpy(spap, &ifdev->ipaddr, sizeof (ipaddr_t));
	(void) memcpy(thap, rdev->physaddr, rdev->physaddrlen);

	add_arp(rdev, tpap, thap);

	/*
	 * Create rarp reply structure.
	 */
	rrp = calloc(1, sizeof (struct rarpreply) + rdev->physaddrlen +
	    rdev->ifrarplen);
	if (rrp == NULL)
		error("out of memory");
	rrp->lldest = (uchar_t *)rrp + sizeof (struct rarpreply);
	rrp->arprep = rrp->lldest + rdev->physaddrlen;
	rrp->rdev = rdev;

	(void) memcpy(rrp->lldest, shost, rdev->physaddrlen);
	(void) memcpy(rrp->arprep, rp, rdev->ifrarplen);

	retval = dlpi_send(rdev->dh_rarp, rrp->lldest, rdev->physaddrlen,
	    rrp->arprep, rdev->ifrarplen, NULL);
	free(rrp);
	if (retval != DLPI_SUCCESS)
		error("dlpi_send failed: %s", dlpi_strerror(retval));
}

/* ARGSUSED */
static void
do_delay_write(void *buf)
{
	struct	timeval		tv;
	struct	rarpreply	*rrp;
	struct	rarpdev		*rdev;
	int			err;

	for (;;) {
		if ((err = sema_wait(&delay_sema)) != 0) {
			if (err == EINTR)
				continue;
			error("do_delay_write: sema_wait failed");
		}

		(void) mutex_lock(&delay_mutex);
		rrp = delay_list;
		rdev = rrp->rdev;
		delay_list = delay_list->next;
		(void) mutex_unlock(&delay_mutex);

		(void) gettimeofday(&tv, NULL);
		if (tv.tv_sec < rrp->tv.tv_sec)
			(void) sleep(rrp->tv.tv_sec - tv.tv_sec);

		err = dlpi_send(rdev->dh_rarp, rrp->lldest, rdev->physaddrlen,
		    rrp->arprep, rdev->ifrarplen, NULL);
		if (err != DLPI_SUCCESS)
			error("dlpi_send failed: %s", dlpi_strerror(err));

		(void) free(rrp);
	}
}

/* ARGSUSED */
static void
delay_write(struct rarpdev *rdev, struct rarpreply *rrp)
{
	struct	rarpreply	*trp;

	(void) mutex_lock(&delay_mutex);
	if (delay_list == NULL) {
		delay_list = rrp;
	} else {
		trp = delay_list;
		while (trp->next != NULL)
			trp = trp->next;
		trp->next = rrp;
	}
	(void) mutex_unlock(&delay_mutex);

	(void) sema_post(&delay_sema);
}

/*
 * See if we have a TFTP boot file for this guy. Filenames in TFTP
 * boot requests are of the form <ipaddr> for Sun-3's and of the form
 * <ipaddr>.<arch> for all other architectures.  Since we don't know
 * the client's architecture, either format will do.
 */
static int
mightboot(ipaddr_t ipa)
{
	char path[MAXPATHL];
	DIR *dirp;
	struct dirent *dp;

	(void) snprintf(path, sizeof (path), "%s/%08X", BOOTDIR, ipa);

	/*
	 * Try a quick access() first.
	 */
	if (access(path, 0) == 0)
		return (1);

	/*
	 * Not there, do it the slow way by
	 * reading through the directory.
	 */
	(void) sprintf(path, "%08X", ipa);

	if (!(dirp = opendir(BOOTDIR)))
		return (0);

	while ((dp = readdir(dirp)) != NULL) {
		if (strncmp(dp->d_name, path, 8) != 0)
			continue;
		if ((strlen(dp->d_name) != 8) && (dp->d_name[8] != '.'))
			continue;
		break;
	}

	(void) closedir(dirp);

	return ((dp != NULL) ? 1 : 0);
}

/*
 * Get our IP address and local netmask.
 */
static void
get_ifdata(char *dev, int unit, ipaddr_t *ipp, ipaddr_t *maskp)
{
	int	fd;
	struct	ifreq	ifr;
	struct	sockaddr_in	*sin;

	/* LINTED pointer */
	sin = (struct sockaddr_in *)&ifr.ifr_addr;

	/*
	 * Open the IP provider.
	 */
	if ((fd = open(DEVIP, 0)) < 0)
		syserr(DEVIP);

	/*
	 * Ask IP for our IP address.
	 */
	(void) snprintf(ifr.ifr_name, sizeof (ifr.ifr_name), "%s%d", dev, unit);
	if (strioctl(fd, SIOCGIFADDR, -1, sizeof (struct ifreq),
	    (char *)&ifr) < 0)
		syserr("SIOCGIFADDR");
	*ipp = (ipaddr_t)ntohl(sin->sin_addr.s_addr);

	if (dflag)
		debug("device %s%d address %s", dev, unit,
		    inet_ntoa(sin->sin_addr));

	/*
	 * Ask IP for our netmask.
	 */
	if (strioctl(fd, SIOCGIFNETMASK, -1, sizeof (struct ifreq),
	    (char *)&ifr) < 0)
		syserr("SIOCGIFNETMASK");
	*maskp = (ipaddr_t)ntohl(sin->sin_addr.s_addr);

	if (dflag)
		debug("device %s%d subnet mask %s", dev, unit,
		    inet_ntoa(sin->sin_addr));

	/*
	 * Thankyou ip.
	 */
	(void) close(fd);
}

/*
 * Translate mac address to IP address.
 * Return 0 on success, nonzero on failure.
 */
static int
get_ipaddr(struct rarpdev *rdev, uchar_t *laddr, uchar_t *ipp, ipaddr_t *ipaddr)
{
	char host[MAXHOSTL];
	char hbuffer[BUFSIZE];
	struct hostent *hp, res;
	int herror;
	struct in_addr addr;
	char	**p;
	struct ifdev *ifdev;

	if (rdev->physaddrlen != ETHERADDRL) {
		if (dflag)
			debug("%s %s", " cannot map non 6 byte hardware ",
			    "address to IP address");
		return (1);
	}

	/*
	 * Translate mac address to hostname and IP address.
	 */
	if (ether_ntohost(host, (struct ether_addr *)laddr) != 0 ||
	    !(hp = gethostbyname_r(host, &res, hbuffer, sizeof (hbuffer),
	    &herror)) ||
	    hp->h_addrtype != AF_INET || hp->h_length != sizeof (ipaddr_t)) {
		if (dflag)
			debug("could not map hardware address to IP address");
		return (1);
	}

	/*
	 * Find the IP address on the right net.
	 */
	for (p = hp->h_addr_list; *p; p++) {
		(void) memcpy(&addr, *p, sizeof (ipaddr_t));
		for (ifdev = rdev->ifdev; ifdev != NULL; ifdev = ifdev->next) {
			if (dflag) {
				struct in_addr daddr;
				ipaddr_t netnum;

				netnum = htonl(ifdev->if_netnum);
				(void) memcpy(&daddr, &netnum,
				    sizeof (ipaddr_t));
				if (ifdev->lunit == -1)
					debug("trying physical netnum %s"
					    " mask %x", inet_ntoa(daddr),
					    ifdev->if_netmask);
				else
					debug("trying logical %d netnum %s"
					    " mask %x", ifdev->lunit,
					    inet_ntoa(daddr),
					    ifdev->if_netmask);
			}
			if ((ntohl(addr.s_addr) & ifdev->if_netmask) ==
			    ifdev->if_netnum) {
				/*
				 * Return the correct IP address.
				 */
				(void) memcpy(ipp, &addr, sizeof (ipaddr_t));

				/*
				 * Return the interface's ipaddr
				 */
				(void) memcpy(ipaddr, &ifdev->ipaddr,
				    sizeof (ipaddr_t));

				return (0);
			}
		}
	}

	if (dflag)
		debug("got host entry but no IP address on this net");
	return (1);
}

static int
strioctl(int fd, int cmd, int timout, int len, char *dp)
{
	struct	strioctl	si;

	si.ic_cmd = cmd;
	si.ic_timout = timout;
	si.ic_len = len;
	si.ic_dp = dp;
	return (ioctl(fd, I_STR, &si));
}

static void
usage(void)
{
	error("Usage:  %s [ -ad ] device unit", cmdname);
}

static void
syserr(const char *s)
{
	char buf[256];
	int status = 1;

	(void) snprintf(buf, sizeof (buf), "%s: %s", s, strerror(errno));
	(void) fprintf(stderr, "%s:  %s\n", cmdname, buf);
	syslog(LOG_ERR, "%s", buf);
	thr_exit(&status);
}

static void
error(const char *fmt, ...)
{
	char buf[256];
	va_list ap;
	int status = 1;

	va_start(ap, fmt);
	(void) vsprintf(buf, fmt, ap);
	va_end(ap);
	(void) fprintf(stderr, "%s:  %s\n", cmdname, buf);
	syslog(LOG_ERR, buf);
	thr_exit(&status);
}

/*PRINTFLIKE1*/
static void
debug(char *fmt, ...)
{
	va_list ap;

	(void) mutex_lock(&debug_mutex);
	va_start(ap, fmt);
	(void) fprintf(stderr, "%s:[%u]  ", cmdname, thr_self());
	(void) vfprintf(stderr, fmt, ap);
	(void) fprintf(stderr, "\n");
	va_end(ap);
	(void) mutex_unlock(&debug_mutex);
}