16823Swpaul/*
219859Swpaul * Copyright (c) 1990, 1991, 1992, 1993, 1996
319859Swpaul *	The Regents of the University of California.  All rights reserved.
46823Swpaul *
56823Swpaul * Redistribution and use in source and binary forms, with or without
66823Swpaul * modification, are permitted provided that: (1) source code distributions
76823Swpaul * retain the above copyright notice and this paragraph in its entirety, (2)
86823Swpaul * distributions including binary code include the above copyright notice and
96823Swpaul * this paragraph in its entirety in the documentation or other materials
10133249Simp * provided with the distribution
11133249Simp *
126823Swpaul * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
136823Swpaul * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
146823Swpaul * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
156823Swpaul */
1630372Scharnier
17114601Sobrien#if 0
186823Swpaul#ifndef lint
1930372Scharnierstatic const char copyright[] =
2019859Swpaul"@(#) Copyright (c) 1990, 1991, 1992, 1993, 1996\n\
2119859SwpaulThe Regents of the University of California.  All rights reserved.\n";
226823Swpaul#endif /* not lint */
23114601Sobrien#endif
24114601Sobrien#include <sys/cdefs.h>
25114601Sobrien__FBSDID("$FreeBSD$");
266823Swpaul
278857Srgrimes/*
286823Swpaul * rarpd - Reverse ARP Daemon
296823Swpaul *
30238282Shrs * Usage:	rarpd -a [-dfsv] [-t directory] [-P pidfile] [hostname]
31238282Shrs *		rarpd [-dfsv] [-t directory] [-P pidfile] interface [hostname]
328857Srgrimes *
336823Swpaul * 'hostname' is optional solely for backwards compatibility with Sun's rarpd.
346823Swpaul * Currently, the argument is ignored.
356823Swpaul */
3619859Swpaul#include <sys/param.h>
3719859Swpaul#include <sys/file.h>
3819859Swpaul#include <sys/ioctl.h>
3919859Swpaul#include <sys/socket.h>
4019859Swpaul#include <sys/time.h>
416823Swpaul
426823Swpaul#include <net/bpf.h>
4378057Sroam#include <net/ethernet.h>
446823Swpaul#include <net/if.h>
4519859Swpaul#include <net/if_types.h>
4619859Swpaul#include <net/if_dl.h>
4719859Swpaul#include <net/route.h>
4819859Swpaul
496823Swpaul#include <netinet/in.h>
506823Swpaul#include <netinet/if_ether.h>
5119859Swpaul
5219859Swpaul#include <arpa/inet.h>
5319859Swpaul
54117446Smux#include <dirent.h>
5519859Swpaul#include <errno.h>
56117446Smux#include <ifaddrs.h>
576823Swpaul#include <netdb.h>
5878402Sroam#include <stdarg.h>
5919859Swpaul#include <stdio.h>
6019859Swpaul#include <string.h>
6119859Swpaul#include <syslog.h>
6219859Swpaul#include <stdlib.h>
6319859Swpaul#include <unistd.h>
64238282Shrs#include <libutil.h>
656823Swpaul
66128469Sjoerg/* Cast a struct sockaddr to a struct sockaddr_in */
6719859Swpaul#define SATOSIN(sa) ((struct sockaddr_in *)(sa))
6819859Swpaul
6919859Swpaul#ifndef TFTP_DIR
7019859Swpaul#define TFTP_DIR "/tftpboot"
7119859Swpaul#endif
7219859Swpaul
7319859Swpaul#define ARPSECS (20 * 60)		/* as per code in netinet/if_ether.c */
7419859Swpaul#define REVARP_REQUEST ARPOP_REVREQUEST
7519859Swpaul#define REVARP_REPLY ARPOP_REVREPLY
7619859Swpaul
776823Swpaul/*
788857Srgrimes * The structure for each interface.
796823Swpaul */
806823Swpaulstruct if_info {
81117446Smux	struct if_info	*ii_next;
82117446Smux	int		ii_fd;			/* BPF file descriptor */
83117446Smux	in_addr_t	ii_ipaddr;		/* IP address */
84117446Smux	in_addr_t	ii_netmask;		/* subnet or net mask */
85117446Smux	u_char		ii_eaddr[ETHER_ADDR_LEN];	/* ethernet address */
86117446Smux	char		ii_ifname[IF_NAMESIZE];
876823Swpaul};
886823Swpaul
896823Swpaul/*
906823Swpaul * The list of all interfaces that are being listened to.  rarp_loop()
916823Swpaul * "selects" on the descriptors in this list.
926823Swpaul */
93249234Smariusstatic struct if_info *iflist;
946823Swpaul
95249234Smariusstatic int verbose;		/* verbose messages */
96249234Smariusstatic const char *tftp_dir = TFTP_DIR;	/* tftp directory */
976823Swpaul
98249234Smariusstatic int dflag;		/* messages to stdout/stderr, not syslog(3) */
99249234Smariusstatic int sflag;		/* ignore /tftpboot */
10019859Swpaul
10119859Swpaulstatic	u_char zero[6];
10219859Swpaul
103238282Shrsstatic char pidfile_buf[PATH_MAX];
104238282Shrsstatic char *pidfile;
105238282Shrs#define	RARPD_PIDFILE	"/var/run/rarpd.%s.pid"
106238282Shrsstatic struct pidfh *pidfile_fh;
107238282Shrs
10878057Sroamstatic int	bpf_open(void);
109116369Sjmgstatic in_addr_t	choose_ipaddr(in_addr_t **, in_addr_t, in_addr_t);
11078057Sroamstatic char	*eatoa(u_char *);
11178402Sroamstatic int	expand_syslog_m(const char *fmt, char **newfmt);
11278057Sroamstatic void	init(char *);
113128474Sjoergstatic void	init_one(struct ifaddrs *, char *, int);
114116369Sjmgstatic char	*intoa(in_addr_t);
115116369Sjmgstatic in_addr_t	ipaddrtonetmask(in_addr_t);
11678402Sroamstatic void	logmsg(int, const char *, ...) __printflike(2, 3);
117116369Sjmgstatic int	rarp_bootable(in_addr_t);
11878057Sroamstatic int	rarp_check(u_char *, u_int);
11978057Sroamstatic void	rarp_loop(void);
12078057Sroamstatic int	rarp_open(char *);
12178057Sroamstatic void	rarp_process(struct if_info *, u_char *, u_int);
12278402Sroamstatic void	rarp_reply(struct if_info *, struct ether_header *,
123116369Sjmg		in_addr_t, u_int);
124116369Sjmgstatic void	update_arptab(u_char *, in_addr_t);
12578057Sroamstatic void	usage(void);
12619986Sfenner
12751287Speterint
12878057Sroammain(int argc, char *argv[])
1296823Swpaul{
13019859Swpaul	int op;
131230346Seadler	char *ifname, *name;
1326823Swpaul
1336823Swpaul	int aflag = 0;		/* listen on "all" interfaces  */
1346823Swpaul	int fflag = 0;		/* don't fork */
1356823Swpaul
13619859Swpaul	if ((name = strrchr(argv[0], '/')) != NULL)
1376823Swpaul		++name;
1386823Swpaul	else
1396823Swpaul		name = argv[0];
1406823Swpaul	if (*name == '-')
1416823Swpaul		++name;
1426823Swpaul
1438857Srgrimes	/*
14478402Sroam	 * All error reporting is done through syslog, unless -d is specified
1456823Swpaul	 */
14619859Swpaul	openlog(name, LOG_PID | LOG_CONS, LOG_DAEMON);
1476823Swpaul
1486823Swpaul	opterr = 0;
149238282Shrs	while ((op = getopt(argc, argv, "adfsP:t:v")) != -1)
1506823Swpaul		switch (op) {
1516823Swpaul		case 'a':
1526823Swpaul			++aflag;
1536823Swpaul			break;
1546823Swpaul
15578402Sroam		case 'd':
15678402Sroam			++dflag;
15778402Sroam			break;
15878402Sroam
1596823Swpaul		case 'f':
1606823Swpaul			++fflag;
1616823Swpaul			break;
1626823Swpaul
16319986Sfenner		case 's':
16419986Sfenner			++sflag;
16519986Sfenner			break;
16619986Sfenner
167238282Shrs		case 'P':
168238282Shrs			strncpy(pidfile_buf, optarg, sizeof(pidfile_buf) - 1);
169238282Shrs			pidfile_buf[sizeof(pidfile_buf) - 1] = '\0';
170238282Shrs			pidfile = pidfile_buf;
171238282Shrs			break;
172238282Shrs
17386460Srwatson		case 't':
17486460Srwatson			tftp_dir = optarg;
17586460Srwatson			break;
17686460Srwatson
17719859Swpaul		case 'v':
17819859Swpaul			++verbose;
17919859Swpaul			break;
18019859Swpaul
1816823Swpaul		default:
1826823Swpaul			usage();
1836823Swpaul			/* NOTREACHED */
1846823Swpaul		}
185119003Scharnier	argc -= optind;
186119003Scharnier	argv += optind;
187119003Scharnier
188119003Scharnier	ifname = (aflag == 0) ? argv[0] : NULL;
189119003Scharnier
19019859Swpaul	if ((aflag && ifname) || (!aflag && ifname == NULL))
1916823Swpaul		usage();
1926823Swpaul
193119003Scharnier	init(ifname);
1948857Srgrimes
19519859Swpaul	if (!fflag) {
196238282Shrs		if (pidfile == NULL && ifname != NULL && aflag == 0) {
197238282Shrs			snprintf(pidfile_buf, sizeof(pidfile_buf) - 1,
198238282Shrs			    RARPD_PIDFILE, ifname);
199238282Shrs			pidfile_buf[sizeof(pidfile_buf) - 1] = '\0';
200238282Shrs			pidfile = pidfile_buf;
201238282Shrs		}
202238282Shrs		/* If pidfile == NULL, /var/run/<progname>.pid will be used. */
203238282Shrs		pidfile_fh = pidfile_open(pidfile, 0600, NULL);
204238282Shrs		if (pidfile_fh == NULL)
205238282Shrs			logmsg(LOG_ERR, "Cannot open or create pidfile: %s",
206238282Shrs			    (pidfile == NULL) ? "/var/run/rarpd.pid" : pidfile);
2079577Swpaul		if (daemon(0,0)) {
20878402Sroam			logmsg(LOG_ERR, "cannot fork");
209238282Shrs			pidfile_remove(pidfile_fh);
21019859Swpaul			exit(1);
2116823Swpaul		}
212238282Shrs		pidfile_write(pidfile_fh);
21319859Swpaul	}
2146823Swpaul	rarp_loop();
21553766Scharnier	return(0);
2166823Swpaul}
2176823Swpaul
2186823Swpaul/*
21919859Swpaul * Add to the interface list.
2206823Swpaul */
221117446Smuxstatic void
222128474Sjoerginit_one(struct ifaddrs *ifa, char *target, int pass1)
2236823Swpaul{
224128474Sjoerg	struct if_info *ii, *ii2;
22578057Sroam	struct sockaddr_dl *ll;
22619859Swpaul	int family;
2276823Swpaul
228117446Smux	family = ifa->ifa_addr->sa_family;
22919859Swpaul	switch (family) {
23019859Swpaul	case AF_INET:
231128474Sjoerg		if (pass1)
232128474Sjoerg			/* Consider only AF_LINK during pass1. */
233128474Sjoerg			return;
234128474Sjoerg		/* FALLTHROUGH */
23519859Swpaul	case AF_LINK:
236117446Smux		if (!(ifa->ifa_flags & IFF_UP) ||
237117446Smux		    (ifa->ifa_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)))
23819859Swpaul			return;
23919859Swpaul		break;
24019859Swpaul	default:
24119859Swpaul		return;
24219859Swpaul	}
24319859Swpaul
24419859Swpaul	/* Don't bother going any further if not the target interface */
245117446Smux	if (target != NULL && strcmp(ifa->ifa_name, target) != 0)
24619859Swpaul		return;
24719859Swpaul
24819859Swpaul	/* Look for interface in list */
24919859Swpaul	for (ii = iflist; ii != NULL; ii = ii->ii_next)
250117446Smux		if (strcmp(ifa->ifa_name, ii->ii_ifname) == 0)
25119859Swpaul			break;
25219859Swpaul
253128474Sjoerg	if (pass1 && ii != NULL)
254128474Sjoerg		/* We've already seen that interface once. */
255128474Sjoerg		return;
256128474Sjoerg
25719859Swpaul	/* Allocate a new one if not found */
25819859Swpaul	if (ii == NULL) {
25919859Swpaul		ii = (struct if_info *)malloc(sizeof(*ii));
26019859Swpaul		if (ii == NULL) {
26178402Sroam			logmsg(LOG_ERR, "malloc: %m");
262238282Shrs			pidfile_remove(pidfile_fh);
26319859Swpaul			exit(1);
26419859Swpaul		}
26519859Swpaul		bzero(ii, sizeof(*ii));
26619859Swpaul		ii->ii_fd = -1;
267117446Smux		strlcpy(ii->ii_ifname, ifa->ifa_name, sizeof(ii->ii_ifname));
26819859Swpaul		ii->ii_next = iflist;
26919859Swpaul		iflist = ii;
270128474Sjoerg	} else if (!pass1 && ii->ii_ipaddr != 0) {
271128474Sjoerg		/*
272128474Sjoerg		 * Second AF_INET definition for that interface: clone
273128474Sjoerg		 * the existing one, and work on that cloned one.
274128474Sjoerg		 * This must be another IP address for this interface,
275128474Sjoerg		 * so avoid killing the previous configuration.
276128474Sjoerg		 */
277128474Sjoerg		ii2 = (struct if_info *)malloc(sizeof(*ii2));
278128474Sjoerg		if (ii2 == NULL) {
279128474Sjoerg			logmsg(LOG_ERR, "malloc: %m");
280238282Shrs			pidfile_remove(pidfile_fh);
281128474Sjoerg			exit(1);
282128474Sjoerg		}
283128474Sjoerg		memcpy(ii2, ii, sizeof(*ii2));
284128474Sjoerg		ii2->ii_fd = -1;
285128474Sjoerg		ii2->ii_next = iflist;
286128474Sjoerg		iflist = ii2;
287128474Sjoerg
288128474Sjoerg		ii = ii2;
28919859Swpaul	}
29019859Swpaul
29119859Swpaul	switch (family) {
29219859Swpaul	case AF_INET:
293117446Smux		ii->ii_ipaddr = SATOSIN(ifa->ifa_addr)->sin_addr.s_addr;
294117446Smux		ii->ii_netmask = SATOSIN(ifa->ifa_netmask)->sin_addr.s_addr;
29519859Swpaul		if (ii->ii_netmask == 0)
29619859Swpaul			ii->ii_netmask = ipaddrtonetmask(ii->ii_ipaddr);
297117446Smux		if (ii->ii_fd < 0)
29819859Swpaul			ii->ii_fd = rarp_open(ii->ii_ifname);
29919859Swpaul		break;
30019859Swpaul
301117446Smux	case AF_LINK:
302117446Smux		ll = (struct sockaddr_dl *)ifa->ifa_addr;
303238282Shrs		switch (ll->sdl_type) {
304238282Shrs		case IFT_ETHER:
305238282Shrs		case IFT_L2VLAN:
306117446Smux			bcopy(LLADDR(ll), ii->ii_eaddr, 6);
307238282Shrs		}
308117446Smux		break;
309117446Smux	}
3106823Swpaul}
311249234Smarius
3126823Swpaul/*
3136823Swpaul * Initialize all "candidate" interfaces that are in the system
3146823Swpaul * configuration list.  A "candidate" is up, not loopback and not
3156823Swpaul * point to point.
3166823Swpaul */
317117446Smuxstatic void
31878057Sroaminit(char *target)
3196823Swpaul{
32078057Sroam	struct if_info *ii, *nii, *lii;
321117446Smux	struct ifaddrs *ifhead, *ifa;
322117446Smux	int error;
3236823Swpaul
324117446Smux	error = getifaddrs(&ifhead);
325117446Smux	if (error) {
326117446Smux		logmsg(LOG_ERR, "getifaddrs: %m");
327238282Shrs		pidfile_remove(pidfile_fh);
3286823Swpaul		exit(1);
3296823Swpaul	}
330128474Sjoerg	/*
331128474Sjoerg	 * We make two passes over the list we have got.  In the first
332128474Sjoerg	 * one, we only collect AF_LINK interfaces, and initialize our
333128474Sjoerg	 * list of interfaces from them.  In the second pass, we
334128474Sjoerg	 * collect the actual IP addresses from the AF_INET
335128474Sjoerg	 * interfaces, and allow for the same interface name to appear
336128474Sjoerg	 * multiple times (in case of more than one IP address).
337128474Sjoerg	 */
338117446Smux	for (ifa = ifhead; ifa != NULL; ifa = ifa->ifa_next)
339128474Sjoerg		init_one(ifa, target, 1);
340128474Sjoerg	for (ifa = ifhead; ifa != NULL; ifa = ifa->ifa_next)
341128474Sjoerg		init_one(ifa, target, 0);
342117446Smux	freeifaddrs(ifhead);
34319859Swpaul
34419859Swpaul	/* Throw away incomplete interfaces */
34519859Swpaul	lii = NULL;
34619859Swpaul	for (ii = iflist; ii != NULL; ii = nii) {
34719859Swpaul		nii = ii->ii_next;
34819859Swpaul		if (ii->ii_ipaddr == 0 ||
34919859Swpaul		    bcmp(ii->ii_eaddr, zero, 6) == 0) {
35019859Swpaul			if (lii == NULL)
35119859Swpaul				iflist = nii;
35219859Swpaul			else
35319859Swpaul				lii->ii_next = nii;
35419859Swpaul			if (ii->ii_fd >= 0)
35519859Swpaul				close(ii->ii_fd);
35619859Swpaul			free(ii);
35719859Swpaul			continue;
3586823Swpaul		}
35919859Swpaul		lii = ii;
3606823Swpaul	}
36119859Swpaul
36219859Swpaul	/* Verbose stuff */
36319859Swpaul	if (verbose)
36419859Swpaul		for (ii = iflist; ii != NULL; ii = ii->ii_next)
365116369Sjmg			logmsg(LOG_DEBUG, "%s %s 0x%08x %s",
36619889Swpaul			    ii->ii_ifname, intoa(ntohl(ii->ii_ipaddr)),
367116369Sjmg			    (in_addr_t)ntohl(ii->ii_netmask), eatoa(ii->ii_eaddr));
3686823Swpaul}
3696823Swpaul
370117446Smuxstatic void
37178057Sroamusage(void)
3726823Swpaul{
373249234Smarius
374119003Scharnier	(void)fprintf(stderr, "%s\n%s\n",
375238282Shrs	    "usage: rarpd -a [-dfsv] [-t directory] [-P pidfile]",
376238282Shrs	    "       rarpd [-dfsv] [-t directory] [-P pidfile] interface");
3776823Swpaul	exit(1);
3786823Swpaul}
3796823Swpaul
380117446Smuxstatic int
38178057Sroambpf_open(void)
3826823Swpaul{
3836823Swpaul	int fd;
3846823Swpaul	int n = 0;
3856823Swpaul	char device[sizeof "/dev/bpf000"];
3866823Swpaul
3876823Swpaul	/*
3888857Srgrimes	 * Go through all the minors and find one that isn't in use.
3896823Swpaul	 */
3906823Swpaul	do {
3916823Swpaul		(void)sprintf(device, "/dev/bpf%d", n++);
3926823Swpaul		fd = open(device, O_RDWR);
39378057Sroam	} while ((fd == -1) && (errno == EBUSY));
3946823Swpaul
39578057Sroam	if (fd == -1) {
39678402Sroam		logmsg(LOG_ERR, "%s: %m", device);
397238282Shrs		pidfile_remove(pidfile_fh);
39819859Swpaul		exit(1);
3996823Swpaul	}
4006823Swpaul	return fd;
4016823Swpaul}
4026823Swpaul
4036823Swpaul/*
4046823Swpaul * Open a BPF file and attach it to the interface named 'device'.
4056823Swpaul * Set immediate mode, and set a filter that accepts only RARP requests.
4066823Swpaul */
407117446Smuxstatic int
40878057Sroamrarp_open(char *device)
4096823Swpaul{
4106823Swpaul	int fd;
4116823Swpaul	struct ifreq ifr;
41219859Swpaul	u_int dlt;
41319859Swpaul	int immediate;
4146823Swpaul
4156823Swpaul	static struct bpf_insn insns[] = {
41619859Swpaul		BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 12),
41719859Swpaul		BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, ETHERTYPE_REVARP, 0, 3),
41819859Swpaul		BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 20),
41919859Swpaul		BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, REVARP_REQUEST, 0, 1),
42019859Swpaul		BPF_STMT(BPF_RET|BPF_K, sizeof(struct ether_arp) +
42119859Swpaul			 sizeof(struct ether_header)),
42219859Swpaul		BPF_STMT(BPF_RET|BPF_K, 0),
42319859Swpaul	};
42419859Swpaul	static struct bpf_program filter = {
42519859Swpaul		sizeof insns / sizeof(insns[0]),
42619859Swpaul		insns
42719859Swpaul	};
4286823Swpaul
4296823Swpaul	fd = bpf_open();
4308857Srgrimes	/*
4316823Swpaul	 * Set immediate mode so packets are processed as they arrive.
4326823Swpaul	 */
4336823Swpaul	immediate = 1;
43478057Sroam	if (ioctl(fd, BIOCIMMEDIATE, &immediate) == -1) {
43578402Sroam		logmsg(LOG_ERR, "BIOCIMMEDIATE: %m");
436238282Shrs		goto rarp_open_err;
4376823Swpaul	}
438117446Smux	strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
43978057Sroam	if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) == -1) {
44078402Sroam		logmsg(LOG_ERR, "BIOCSETIF: %m");
441238282Shrs		goto rarp_open_err;
4426823Swpaul	}
4436823Swpaul	/*
4446823Swpaul	 * Check that the data link layer is an Ethernet; this code won't
4456823Swpaul	 * work with anything else.
4466823Swpaul	 */
44778057Sroam	if (ioctl(fd, BIOCGDLT, (caddr_t)&dlt) == -1) {
44878402Sroam		logmsg(LOG_ERR, "BIOCGDLT: %m");
449238282Shrs		goto rarp_open_err;
4506823Swpaul	}
45119859Swpaul	if (dlt != DLT_EN10MB) {
45278402Sroam		logmsg(LOG_ERR, "%s is not an ethernet", device);
453238282Shrs		goto rarp_open_err;
4546823Swpaul	}
4556823Swpaul	/*
4566823Swpaul	 * Set filter program.
4576823Swpaul	 */
45878057Sroam	if (ioctl(fd, BIOCSETF, (caddr_t)&filter) == -1) {
45978402Sroam		logmsg(LOG_ERR, "BIOCSETF: %m");
460238282Shrs		goto rarp_open_err;
4616823Swpaul	}
4626823Swpaul	return fd;
463238282Shrs
464238282Shrsrarp_open_err:
465238282Shrs	pidfile_remove(pidfile_fh);
466238282Shrs	exit(1);
4676823Swpaul}
4686823Swpaul
4696823Swpaul/*
4706823Swpaul * Perform various sanity checks on the RARP request packet.  Return
4716823Swpaul * false on failure and log the reason.
4726823Swpaul */
473117446Smuxstatic int
47478057Sroamrarp_check(u_char *p, u_int len)
4756823Swpaul{
4766823Swpaul	struct ether_header *ep = (struct ether_header *)p;
4776823Swpaul	struct ether_arp *ap = (struct ether_arp *)(p + sizeof(*ep));
4786823Swpaul
4796823Swpaul	if (len < sizeof(*ep) + sizeof(*ap)) {
48078402Sroam		logmsg(LOG_ERR, "truncated request, got %u, expected %lu",
48178357Sjlemon				len, (u_long)(sizeof(*ep) + sizeof(*ap)));
4826823Swpaul		return 0;
4836823Swpaul	}
4846823Swpaul	/*
4856823Swpaul	 * XXX This test might be better off broken out...
4866823Swpaul	 */
48719859Swpaul	if (ntohs(ep->ether_type) != ETHERTYPE_REVARP ||
48819859Swpaul	    ntohs(ap->arp_hrd) != ARPHRD_ETHER ||
48919859Swpaul	    ntohs(ap->arp_op) != REVARP_REQUEST ||
49019859Swpaul	    ntohs(ap->arp_pro) != ETHERTYPE_IP ||
4916823Swpaul	    ap->arp_hln != 6 || ap->arp_pln != 4) {
49278402Sroam		logmsg(LOG_DEBUG, "request fails sanity check");
4936823Swpaul		return 0;
4946823Swpaul	}
4956823Swpaul	if (bcmp((char *)&ep->ether_shost, (char *)&ap->arp_sha, 6) != 0) {
49678402Sroam		logmsg(LOG_DEBUG, "ether/arp sender address mismatch");
4976823Swpaul		return 0;
4986823Swpaul	}
4996823Swpaul	if (bcmp((char *)&ap->arp_sha, (char *)&ap->arp_tha, 6) != 0) {
50078402Sroam		logmsg(LOG_DEBUG, "ether/arp target address mismatch");
5016823Swpaul		return 0;
5026823Swpaul	}
5036823Swpaul	return 1;
5046823Swpaul}
5056823Swpaul
5066823Swpaul/*
5078857Srgrimes * Loop indefinitely listening for RARP requests on the
5086823Swpaul * interfaces in 'iflist'.
5096823Swpaul */
510117446Smuxstatic void
51178057Sroamrarp_loop(void)
5126823Swpaul{
51319859Swpaul	u_char *buf, *bp, *ep;
5146823Swpaul	int cc, fd;
5156823Swpaul	fd_set fds, listeners;
5166823Swpaul	int bufsize, maxfd = 0;
5176823Swpaul	struct if_info *ii;
5186823Swpaul
51919859Swpaul	if (iflist == NULL) {
52078402Sroam		logmsg(LOG_ERR, "no interfaces");
521238282Shrs		goto rarpd_loop_err;
5226823Swpaul	}
52378057Sroam	if (ioctl(iflist->ii_fd, BIOCGBLEN, (caddr_t)&bufsize) == -1) {
52478402Sroam		logmsg(LOG_ERR, "BIOCGBLEN: %m");
525238282Shrs		goto rarpd_loop_err;
5266823Swpaul	}
52778163Sbde	buf = malloc(bufsize);
52819859Swpaul	if (buf == NULL) {
52978402Sroam		logmsg(LOG_ERR, "malloc: %m");
530238282Shrs		goto rarpd_loop_err;
53119859Swpaul	}
5326823Swpaul
53319859Swpaul	while (1) {
53419889Swpaul		/*
53519889Swpaul		 * Find the highest numbered file descriptor for select().
53619889Swpaul		 * Initialize the set of descriptors to listen to.
53719889Swpaul		 */
53819859Swpaul		FD_ZERO(&fds);
53919859Swpaul		for (ii = iflist; ii != NULL; ii = ii->ii_next) {
54019859Swpaul			FD_SET(ii->ii_fd, &fds);
54119859Swpaul			if (ii->ii_fd > maxfd)
54219859Swpaul				maxfd = ii->ii_fd;
54319859Swpaul		}
5446823Swpaul		listeners = fds;
54578057Sroam		if (select(maxfd + 1, &listeners, NULL, NULL, NULL) == -1) {
54619859Swpaul			/* Don't choke when we get ptraced */
54719859Swpaul			if (errno == EINTR)
54819859Swpaul				continue;
54978402Sroam			logmsg(LOG_ERR, "select: %m");
550238282Shrs			goto rarpd_loop_err;
5516823Swpaul		}
55219859Swpaul		for (ii = iflist; ii != NULL; ii = ii->ii_next) {
5536823Swpaul			fd = ii->ii_fd;
55419859Swpaul			if (!FD_ISSET(fd, &listeners))
55519859Swpaul				continue;
55619859Swpaul		again:
55719859Swpaul			cc = read(fd, (char *)buf, bufsize);
55819859Swpaul			/* Don't choke when we get ptraced */
55978057Sroam			if ((cc == -1) && (errno == EINTR))
56019859Swpaul				goto again;
5616823Swpaul
56219859Swpaul			/* Loop through the packet(s) */
56319859Swpaul#define bhp ((struct bpf_hdr *)bp)
56419859Swpaul			bp = buf;
56519859Swpaul			ep = bp + cc;
56619859Swpaul			while (bp < ep) {
56778057Sroam				u_int caplen, hdrlen;
56819859Swpaul
56919859Swpaul				caplen = bhp->bh_caplen;
57019859Swpaul				hdrlen = bhp->bh_hdrlen;
57119859Swpaul				if (rarp_check(bp + hdrlen, caplen))
57219985Sfenner					rarp_process(ii, bp + hdrlen, caplen);
57319859Swpaul				bp += BPF_WORDALIGN(hdrlen + caplen);
5746823Swpaul			}
5756823Swpaul		}
5766823Swpaul	}
57719859Swpaul#undef bhp
578238282Shrs	return;
579238282Shrs
580238282Shrsrarpd_loop_err:
581238282Shrs	pidfile_remove(pidfile_fh);
582238282Shrs	exit(1);
5836823Swpaul}
5846823Swpaul
5856823Swpaul/*
5866823Swpaul * True if this server can boot the host whose IP address is 'addr'.
5876823Swpaul * This check is made by looking in the tftp directory for the
5886823Swpaul * configuration file.
5896823Swpaul */
590117446Smuxstatic int
591116369Sjmgrarp_bootable(in_addr_t addr)
5926823Swpaul{
59378057Sroam	struct dirent *dent;
59478057Sroam	DIR *d;
5956823Swpaul	char ipname[9];
59619859Swpaul	static DIR *dd = NULL;
5976823Swpaul
598116369Sjmg	(void)sprintf(ipname, "%08X", (in_addr_t)ntohl(addr));
59919859Swpaul
6006823Swpaul	/*
6016823Swpaul	 * If directory is already open, rewind it.  Otherwise, open it.
6026823Swpaul	 */
60319859Swpaul	if ((d = dd) != NULL)
6046823Swpaul		rewinddir(d);
6056823Swpaul	else {
60619859Swpaul		if (chdir(tftp_dir) == -1) {
60778402Sroam			logmsg(LOG_ERR, "chdir: %s: %m", tftp_dir);
608238282Shrs			goto rarp_bootable_err;
6096823Swpaul		}
6106823Swpaul		d = opendir(".");
61119859Swpaul		if (d == NULL) {
61278402Sroam			logmsg(LOG_ERR, "opendir: %m");
613238282Shrs			goto rarp_bootable_err;
6146823Swpaul		}
6156823Swpaul		dd = d;
6166823Swpaul	}
61719859Swpaul	while ((dent = readdir(d)) != NULL)
6186823Swpaul		if (strncmp(dent->d_name, ipname, 8) == 0)
6196823Swpaul			return 1;
6206823Swpaul	return 0;
621238282Shrs
622238282Shrsrarp_bootable_err:
623238282Shrs	pidfile_remove(pidfile_fh);
624238282Shrs	exit(1);
6256823Swpaul}
6266823Swpaul
6276823Swpaul/*
6286823Swpaul * Given a list of IP addresses, 'alist', return the first address that
6296823Swpaul * is on network 'net'; 'netmask' is a mask indicating the network portion
6306823Swpaul * of the address.
6316823Swpaul */
632117446Smuxstatic in_addr_t
633116369Sjmgchoose_ipaddr(in_addr_t **alist, in_addr_t net, in_addr_t netmask)
6346823Swpaul{
635249234Smarius
63619859Swpaul	for (; *alist; ++alist)
6376823Swpaul		if ((**alist & netmask) == net)
6386823Swpaul			return **alist;
6396823Swpaul	return 0;
6406823Swpaul}
6416823Swpaul
6426823Swpaul/*
6436823Swpaul * Answer the RARP request in 'pkt', on the interface 'ii'.  'pkt' has
6446823Swpaul * already been checked for validity.  The reply is overlaid on the request.
6456823Swpaul */
646117446Smuxstatic void
64778057Sroamrarp_process(struct if_info *ii, u_char *pkt, u_int len)
6486823Swpaul{
6496823Swpaul	struct ether_header *ep;
6506823Swpaul	struct hostent *hp;
651116369Sjmg	in_addr_t target_ipaddr;
6526823Swpaul	char ename[256];
6536823Swpaul
6546823Swpaul	ep = (struct ether_header *)pkt;
65519889Swpaul	/* should this be arp_tha? */
65678057Sroam	if (ether_ntohost(ename, (struct ether_addr *)&ep->ether_shost) != 0) {
65778402Sroam		logmsg(LOG_ERR, "cannot map %s to name",
65819985Sfenner			eatoa(ep->ether_shost));
65919859Swpaul		return;
66019985Sfenner	}
66119859Swpaul
66219985Sfenner	if ((hp = gethostbyname(ename)) == NULL) {
66378402Sroam		logmsg(LOG_ERR, "cannot map %s to IP address", ename);
66419859Swpaul		return;
66519985Sfenner	}
66619985Sfenner
6676823Swpaul	/*
66819859Swpaul	 * Choose correct address from list.
6696823Swpaul	 */
67019859Swpaul	if (hp->h_addrtype != AF_INET) {
67178402Sroam		logmsg(LOG_ERR, "cannot handle non IP addresses for %s",
67219889Swpaul								ename);
67319889Swpaul		return;
6746823Swpaul	}
675116369Sjmg	target_ipaddr = choose_ipaddr((in_addr_t **)hp->h_addr_list,
67619859Swpaul				      ii->ii_ipaddr & ii->ii_netmask,
67719889Swpaul				      ii->ii_netmask);
67819859Swpaul	if (target_ipaddr == 0) {
67978402Sroam		logmsg(LOG_ERR, "cannot find %s on net %s",
68019889Swpaul		       ename, intoa(ntohl(ii->ii_ipaddr & ii->ii_netmask)));
68119859Swpaul		return;
68219859Swpaul	}
68319986Sfenner	if (sflag || rarp_bootable(target_ipaddr))
68419859Swpaul		rarp_reply(ii, ep, target_ipaddr, len);
68519859Swpaul	else if (verbose > 1)
68678402Sroam		logmsg(LOG_INFO, "%s %s at %s DENIED (not bootable)",
68719859Swpaul		    ii->ii_ifname,
68819889Swpaul		    eatoa(ep->ether_shost),
68919889Swpaul		    intoa(ntohl(target_ipaddr)));
6906823Swpaul}
6916823Swpaul
6926823Swpaul/*
69319859Swpaul * Poke the kernel arp tables with the ethernet/ip address combinataion
69419859Swpaul * given.  When processing a reply, we must do this so that the booting
69519859Swpaul * host (i.e. the guy running rarpd), won't try to ARP for the hardware
69619859Swpaul * address of the guy being booted (he cannot answer the ARP).
6976823Swpaul */
698249234Smariusstatic struct sockaddr_in sin_inarp = {
699246143Sglebius	sizeof(struct sockaddr_in), AF_INET, 0,
70078057Sroam	{0},
70178057Sroam	{0},
70219859Swpaul};
703249234Smarius
704249234Smariusstatic struct sockaddr_dl sin_dl = {
70578057Sroam	sizeof(struct sockaddr_dl), AF_LINK, 0, IFT_ETHER, 0, 6,
70696254Sroberto	0, ""
70719859Swpaul};
708249234Smarius
709249234Smariusstatic struct {
71019859Swpaul	struct rt_msghdr rthdr;
71119859Swpaul	char rtspace[512];
71219859Swpaul} rtmsg;
71319859Swpaul
714117446Smuxstatic void
715116369Sjmgupdate_arptab(u_char *ep, in_addr_t ipaddr)
7166823Swpaul{
717216225Sglebius	struct timespec tp;
71878057Sroam	int cc;
719246143Sglebius	struct sockaddr_in *ar, *ar2;
72078057Sroam	struct sockaddr_dl *ll, *ll2;
72178057Sroam	struct rt_msghdr *rt;
72278057Sroam	int xtype, xindex;
72319859Swpaul	static pid_t pid;
72435003Sroberto	int r;
72578057Sroam	static int seq;
7266823Swpaul
72735003Sroberto	r = socket(PF_ROUTE, SOCK_RAW, 0);
72878057Sroam	if (r == -1) {
72978402Sroam		logmsg(LOG_ERR, "raw route socket: %m");
730238282Shrs		pidfile_remove(pidfile_fh);
73135003Sroberto		exit(1);
7326823Swpaul	}
73335003Sroberto	pid = getpid();
7346823Swpaul
73519859Swpaul	ar = &sin_inarp;
73619889Swpaul	ar->sin_addr.s_addr = ipaddr;
73719859Swpaul	ll = &sin_dl;
73819859Swpaul	bcopy(ep, LLADDR(ll), 6);
7396823Swpaul
74019859Swpaul	/* Get the type and interface index */
74119859Swpaul	rt = &rtmsg.rthdr;
74219859Swpaul	bzero(rt, sizeof(rtmsg));
74319859Swpaul	rt->rtm_version = RTM_VERSION;
74419859Swpaul	rt->rtm_addrs = RTA_DST;
74519859Swpaul	rt->rtm_type = RTM_GET;
74619859Swpaul	rt->rtm_seq = ++seq;
747246143Sglebius	ar2 = (struct sockaddr_in *)rtmsg.rtspace;
74819859Swpaul	bcopy(ar, ar2, sizeof(*ar));
74919859Swpaul	rt->rtm_msglen = sizeof(*rt) + sizeof(*ar);
75019859Swpaul	errno = 0;
75178057Sroam	if ((write(r, rt, rt->rtm_msglen) == -1) && (errno != ESRCH)) {
75278402Sroam		logmsg(LOG_ERR, "rtmsg get write: %m");
75335003Sroberto		close(r);
75419889Swpaul		return;
7556823Swpaul	}
75619859Swpaul	do {
75719859Swpaul		cc = read(r, rt, sizeof(rtmsg));
75819859Swpaul	} while (cc > 0 && (rt->rtm_seq != seq || rt->rtm_pid != pid));
75978057Sroam	if (cc == -1) {
76078402Sroam		logmsg(LOG_ERR, "rtmsg get read: %m");
76135003Sroberto		close(r);
76219889Swpaul		return;
7636823Swpaul	}
76419859Swpaul	ll2 = (struct sockaddr_dl *)((u_char *)ar2 + ar2->sin_len);
76519859Swpaul	if (ll2->sdl_family != AF_LINK) {
76619859Swpaul		/*
76719859Swpaul		 * XXX I think this means the ip address is not on a
76819859Swpaul		 * directly connected network (the family is AF_INET in
76919859Swpaul		 * this case).
77019859Swpaul		 */
771116369Sjmg		logmsg(LOG_ERR, "bogus link family (%d) wrong net for %08X?\n",
77219889Swpaul		    ll2->sdl_family, ipaddr);
77335003Sroberto		close(r);
77419859Swpaul		return;
77519859Swpaul	}
77619859Swpaul	xtype = ll2->sdl_type;
77719859Swpaul	xindex = ll2->sdl_index;
77819859Swpaul
77919859Swpaul	/* Set the new arp entry */
78019859Swpaul	bzero(rt, sizeof(rtmsg));
78119859Swpaul	rt->rtm_version = RTM_VERSION;
78219859Swpaul	rt->rtm_addrs = RTA_DST | RTA_GATEWAY;
78319859Swpaul	rt->rtm_inits = RTV_EXPIRE;
784216225Sglebius	clock_gettime(CLOCK_MONOTONIC, &tp);
785216225Sglebius	rt->rtm_rmx.rmx_expire = tp.tv_sec + ARPSECS;
78619859Swpaul	rt->rtm_flags = RTF_HOST | RTF_STATIC;
78719859Swpaul	rt->rtm_type = RTM_ADD;
78819859Swpaul	rt->rtm_seq = ++seq;
78919859Swpaul
79019859Swpaul	bcopy(ar, ar2, sizeof(*ar));
79119859Swpaul
79219859Swpaul	ll2 = (struct sockaddr_dl *)((u_char *)ar2 + sizeof(*ar2));
79319859Swpaul	bcopy(ll, ll2, sizeof(*ll));
79419859Swpaul	ll2->sdl_type = xtype;
79519859Swpaul	ll2->sdl_index = xindex;
79619859Swpaul
79719859Swpaul	rt->rtm_msglen = sizeof(*rt) + sizeof(*ar2) + sizeof(*ll2);
79819859Swpaul	errno = 0;
79978057Sroam	if ((write(r, rt, rt->rtm_msglen) == -1) && (errno != EEXIST)) {
80078402Sroam		logmsg(LOG_ERR, "rtmsg add write: %m");
80135003Sroberto		close(r);
80219889Swpaul		return;
8036823Swpaul	}
80419859Swpaul	do {
80519859Swpaul		cc = read(r, rt, sizeof(rtmsg));
80619859Swpaul	} while (cc > 0 && (rt->rtm_seq != seq || rt->rtm_pid != pid));
80735003Sroberto	close(r);
80878057Sroam	if (cc == -1) {
80978402Sroam		logmsg(LOG_ERR, "rtmsg add read: %m");
81019889Swpaul		return;
81119859Swpaul	}
8126823Swpaul}
8136823Swpaul
8146823Swpaul/*
8156823Swpaul * Build a reverse ARP packet and sent it out on the interface.
81619859Swpaul * 'ep' points to a valid REVARP_REQUEST.  The REVARP_REPLY is built
8176823Swpaul * on top of the request, then written to the network.
8186823Swpaul *
8196823Swpaul * RFC 903 defines the ether_arp fields as follows.  The following comments
8206823Swpaul * are taken (more or less) straight from this document.
8216823Swpaul *
82219859Swpaul * REVARP_REQUEST
8236823Swpaul *
8246823Swpaul * arp_sha is the hardware address of the sender of the packet.
8256823Swpaul * arp_spa is undefined.
8266823Swpaul * arp_tha is the 'target' hardware address.
8276823Swpaul *   In the case where the sender wishes to determine his own
8286823Swpaul *   protocol address, this, like arp_sha, will be the hardware
8296823Swpaul *   address of the sender.
8306823Swpaul * arp_tpa is undefined.
8316823Swpaul *
83219859Swpaul * REVARP_REPLY
8336823Swpaul *
8346823Swpaul * arp_sha is the hardware address of the responder (the sender of the
8356823Swpaul *   reply packet).
8366823Swpaul * arp_spa is the protocol address of the responder (see the note below).
8376823Swpaul * arp_tha is the hardware address of the target, and should be the same as
8386823Swpaul *   that which was given in the request.
8396823Swpaul * arp_tpa is the protocol address of the target, that is, the desired address.
8408857Srgrimes *
8416823Swpaul * Note that the requirement that arp_spa be filled in with the responder's
8428857Srgrimes * protocol is purely for convenience.  For instance, if a system were to use
8438857Srgrimes * both ARP and RARP, then the inclusion of the valid protocol-hardware
8448857Srgrimes * address pair (arp_spa, arp_sha) may eliminate the need for a subsequent
8456823Swpaul * ARP request.
8466823Swpaul */
847117446Smuxstatic void
848116369Sjmgrarp_reply(struct if_info *ii, struct ether_header *ep, in_addr_t ipaddr,
84978057Sroam		u_int len)
8506823Swpaul{
85178057Sroam	u_int n;
8526823Swpaul	struct ether_arp *ap = (struct ether_arp *)(ep + 1);
8536823Swpaul
8546823Swpaul	update_arptab((u_char *)&ap->arp_sha, ipaddr);
8556823Swpaul
8566823Swpaul	/*
8576823Swpaul	 * Build the rarp reply by modifying the rarp request in place.
8586823Swpaul	 */
85919859Swpaul	ap->arp_op = htons(REVARP_REPLY);
8606823Swpaul
86119859Swpaul#ifdef BROKEN_BPF
86219859Swpaul	ep->ether_type = ETHERTYPE_REVARP;
86319859Swpaul#endif
8646823Swpaul	bcopy((char *)&ap->arp_sha, (char *)&ep->ether_dhost, 6);
8656823Swpaul	bcopy((char *)ii->ii_eaddr, (char *)&ep->ether_shost, 6);
8666823Swpaul	bcopy((char *)ii->ii_eaddr, (char *)&ap->arp_sha, 6);
8676823Swpaul
8686823Swpaul	bcopy((char *)&ipaddr, (char *)ap->arp_tpa, 4);
8696823Swpaul	/* Target hardware is unchanged. */
8706823Swpaul	bcopy((char *)&ii->ii_ipaddr, (char *)ap->arp_spa, 4);
8716823Swpaul
87219859Swpaul	/* Zero possible garbage after packet. */
87319859Swpaul	bzero((char *)ep + (sizeof(*ep) + sizeof(*ap)),
87419859Swpaul			len - (sizeof(*ep) + sizeof(*ap)));
8756823Swpaul	n = write(ii->ii_fd, (char *)ep, len);
87619859Swpaul	if (n != len)
87778402Sroam		logmsg(LOG_ERR, "write: only %d of %d bytes written", n, len);
87819859Swpaul	if (verbose)
87978402Sroam		logmsg(LOG_INFO, "%s %s at %s REPLIED", ii->ii_ifname,
88019985Sfenner		    eatoa(ap->arp_tha),
88119889Swpaul		    intoa(ntohl(ipaddr)));
8826823Swpaul}
8836823Swpaul
8846823Swpaul/*
8856823Swpaul * Get the netmask of an IP address.  This routine is used if
8866823Swpaul * SIOCGIFNETMASK doesn't work.
8876823Swpaul */
888117446Smuxstatic in_addr_t
889116369Sjmgipaddrtonetmask(in_addr_t addr)
8906823Swpaul{
891249234Smarius
89219889Swpaul	addr = ntohl(addr);
8936823Swpaul	if (IN_CLASSA(addr))
89419889Swpaul		return htonl(IN_CLASSA_NET);
8956823Swpaul	if (IN_CLASSB(addr))
89619889Swpaul		return htonl(IN_CLASSB_NET);
8976823Swpaul	if (IN_CLASSC(addr))
89819889Swpaul		return htonl(IN_CLASSC_NET);
899116369Sjmg	logmsg(LOG_DEBUG, "unknown IP address class: %08X", addr);
90019889Swpaul	return htonl(0xffffffff);
9016823Swpaul}
90219859Swpaul
90319859Swpaul/*
90419859Swpaul * A faster replacement for inet_ntoa().
90519859Swpaul */
906117446Smuxstatic char *
907116369Sjmgintoa(in_addr_t addr)
90819859Swpaul{
90978057Sroam	char *cp;
91078057Sroam	u_int byte;
91178057Sroam	int n;
91219859Swpaul	static char buf[sizeof(".xxx.xxx.xxx.xxx")];
91319859Swpaul
91419859Swpaul	cp = &buf[sizeof buf];
91519859Swpaul	*--cp = '\0';
91619859Swpaul
91719859Swpaul	n = 4;
91819859Swpaul	do {
91919859Swpaul		byte = addr & 0xff;
92019859Swpaul		*--cp = byte % 10 + '0';
92119859Swpaul		byte /= 10;
92219859Swpaul		if (byte > 0) {
92319859Swpaul			*--cp = byte % 10 + '0';
92419859Swpaul			byte /= 10;
92519859Swpaul			if (byte > 0)
92619859Swpaul				*--cp = byte + '0';
92719859Swpaul		}
92819859Swpaul		*--cp = '.';
92919859Swpaul		addr >>= 8;
93019859Swpaul	} while (--n > 0);
93119859Swpaul
93219859Swpaul	return cp + 1;
93319859Swpaul}
93419859Swpaul
935117446Smuxstatic char *
93678057Sroameatoa(u_char *ea)
93719859Swpaul{
93819859Swpaul	static char buf[sizeof("xx:xx:xx:xx:xx:xx")];
93919859Swpaul
94019859Swpaul	(void)sprintf(buf, "%x:%x:%x:%x:%x:%x",
94119859Swpaul	    ea[0], ea[1], ea[2], ea[3], ea[4], ea[5]);
94219859Swpaul	return (buf);
94319859Swpaul}
94478402Sroam
945117446Smuxstatic void
94678402Sroamlogmsg(int pri, const char *fmt, ...)
94778402Sroam{
94878402Sroam	va_list v;
94978402Sroam	FILE *fp;
95078402Sroam	char *newfmt;
95178402Sroam
95278402Sroam	va_start(v, fmt);
95378402Sroam	if (dflag) {
95478402Sroam		if (pri == LOG_ERR)
95578402Sroam			fp = stderr;
95678402Sroam		else
95778402Sroam			fp = stdout;
95878402Sroam		if (expand_syslog_m(fmt, &newfmt) == -1) {
95978402Sroam			vfprintf(fp, fmt, v);
96078402Sroam		} else {
96178402Sroam			vfprintf(fp, newfmt, v);
96278402Sroam			free(newfmt);
96378402Sroam		}
96478402Sroam		fputs("\n", fp);
96578402Sroam		fflush(fp);
96678402Sroam	} else {
96778402Sroam		vsyslog(pri, fmt, v);
96878402Sroam	}
96978402Sroam	va_end(v);
97078402Sroam}
97178402Sroam
972117446Smuxstatic int
97378402Sroamexpand_syslog_m(const char *fmt, char **newfmt) {
97478402Sroam	const char *str, *m;
97578402Sroam	char *p, *np;
97678402Sroam
97778402Sroam	p = strdup("");
97878402Sroam	str = fmt;
97978402Sroam	while ((m = strstr(str, "%m")) != NULL) {
98079333Smjacob		asprintf(&np, "%s%.*s%s", p, (int)(m - str),
98179333Smjacob		    str, strerror(errno));
98278402Sroam		free(p);
98378402Sroam		if (np == NULL) {
98478402Sroam			errno = ENOMEM;
98578402Sroam			return (-1);
98678402Sroam		}
98778402Sroam		p = np;
98878402Sroam		str = m + 2;
98978402Sroam	}
99078402Sroam
99178402Sroam	if (*str != '\0') {
99278402Sroam		asprintf(&np, "%s%s", p, str);
99378402Sroam		free(p);
99478402Sroam		if (np == NULL) {
99578402Sroam			errno = ENOMEM;
99678402Sroam			return (-1);
99778402Sroam		}
99878402Sroam		p = np;
99978402Sroam	}
100078402Sroam
100178402Sroam	*newfmt = p;
100278402Sroam	return (0);
100378402Sroam}
1004