rtsol.c revision 118661
195043Ssuz/*	$KAME: rtsol.c,v 1.12 2001/11/12 11:47:11 jinmei Exp $	*/
266776Skris
355163Sshin/*
455163Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
555163Sshin * All rights reserved.
662632Skris *
755163Sshin * Redistribution and use in source and binary forms, with or without
855163Sshin * modification, are permitted provided that the following conditions
955163Sshin * are met:
1055163Sshin * 1. Redistributions of source code must retain the above copyright
1155163Sshin *    notice, this list of conditions and the following disclaimer.
1255163Sshin * 2. Redistributions in binary form must reproduce the above copyright
1355163Sshin *    notice, this list of conditions and the following disclaimer in the
1455163Sshin *    documentation and/or other materials provided with the distribution.
1555163Sshin * 3. Neither the name of the project nor the names of its contributors
1655163Sshin *    may be used to endorse or promote products derived from this software
1755163Sshin *    without specific prior written permission.
1862632Skris *
1955163Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
2055163Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2155163Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2255163Sshin * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2355163Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2455163Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2555163Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2655163Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2755163Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2855163Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2955163Sshin * SUCH DAMAGE.
3055163Sshin *
3155163Sshin * $FreeBSD: head/usr.sbin/rtsold/rtsol.c 118661 2003-08-08 16:42:37Z ume $
3255163Sshin */
3355163Sshin
3455163Sshin#include <sys/param.h>
3555163Sshin#include <sys/socket.h>
3655163Sshin#include <sys/uio.h>
3755163Sshin#include <sys/time.h>
38118661Sume#include <fcntl.h>
3966776Skris#include <sys/queue.h>
40118661Sume#include <sys/wait.h>
41118661Sume#include <sys/stat.h>
4255163Sshin
4355163Sshin#include <net/if.h>
4455163Sshin#include <net/route.h>
4555163Sshin#include <net/if_dl.h>
4655163Sshin
4755163Sshin#include <netinet/in.h>
4855163Sshin#include <netinet/ip6.h>
4955163Sshin#include <netinet6/ip6_var.h>
5055163Sshin#include <netinet/icmp6.h>
5155163Sshin
5255163Sshin#include <arpa/inet.h>
5355163Sshin
5455163Sshin#include <time.h>
5555163Sshin#include <unistd.h>
5655163Sshin#include <stdio.h>
5755163Sshin#include <err.h>
5855163Sshin#include <errno.h>
5955163Sshin#include <string.h>
6055163Sshin#include <stdlib.h>
6155163Sshin#include <syslog.h>
6255163Sshin#include "rtsold.h"
6355163Sshin
6462632Skris#define ALLROUTER "ff02::2"
6555163Sshin
6655163Sshinstatic struct msghdr rcvmhdr;
6755163Sshinstatic struct msghdr sndmhdr;
6855163Sshinstatic struct iovec rcviov[2];
6955163Sshinstatic struct iovec sndiov[2];
7055163Sshinstatic struct sockaddr_in6 from;
7155163Sshin
7262632Skrisint rssock;
7355163Sshin
7455163Sshinstatic struct sockaddr_in6 sin6_allrouters = {sizeof(sin6_allrouters), AF_INET6};
7555163Sshin
76118661Sumestatic void call_script __P((char *, char *));
77118661Sumestatic int safefile __P((const char *));
78118661Sume
7955163Sshinint
8055163Sshinsockopen()
8155163Sshin{
8255163Sshin	int on;
8355163Sshin	struct icmp6_filter filt;
8455163Sshin	static u_char answer[1500];
8562632Skris	int rcvcmsglen, sndcmsglen;
8662632Skris	static u_char *rcvcmsgbuf = NULL, *sndcmsgbuf = NULL;
8755163Sshin
8862632Skris	sndcmsglen = rcvcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
8962632Skris		CMSG_SPACE(sizeof(int));
9062632Skris	if (rcvcmsgbuf == NULL && (rcvcmsgbuf = malloc(rcvcmsglen)) == NULL) {
91118660Sume		warnmsg(LOG_ERR, __func__,
9262632Skris			"malloc for receive msghdr failed");
9362632Skris		return(-1);
9462632Skris	}
9562632Skris	if (sndcmsgbuf == NULL && (sndcmsgbuf = malloc(sndcmsglen)) == NULL) {
96118660Sume		warnmsg(LOG_ERR, __func__,
9762632Skris			"malloc for send msghdr failed");
9862632Skris		return(-1);
9962632Skris	}
10055163Sshin	memset(&sin6_allrouters, 0, sizeof(struct sockaddr_in6));
10195043Ssuz	sin6_allrouters.sin6_family = AF_INET6;
10295043Ssuz	sin6_allrouters.sin6_len = sizeof(sin6_allrouters);
10355163Sshin	if (inet_pton(AF_INET6, ALLROUTER,
10455163Sshin		      &sin6_allrouters.sin6_addr.s6_addr) != 1) {
105118660Sume		warnmsg(LOG_ERR, __func__, "inet_pton failed for %s",
10655163Sshin		       ALLROUTER);
10755163Sshin		return(-1);
10855163Sshin	}
10955163Sshin
11055163Sshin	if ((rssock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) {
111118660Sume		warnmsg(LOG_ERR, __func__, "socket: %s", strerror(errno));
11255163Sshin		return(-1);
11355163Sshin	}
11455163Sshin
11555163Sshin	/* specify to tell receiving interface */
11655163Sshin	on = 1;
11762632Skris#ifdef IPV6_RECVPKTINFO
11862632Skris	if (setsockopt(rssock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
11962632Skris		       sizeof(on)) < 0) {
120118660Sume		warnmsg(LOG_ERR, __func__, "IPV6_RECVPKTINFO: %s",
12162632Skris		       strerror(errno));
12262632Skris		exit(1);
12362632Skris	}
12462632Skris#else  /* old adv. API */
12555163Sshin	if (setsockopt(rssock, IPPROTO_IPV6, IPV6_PKTINFO, &on,
12655163Sshin		       sizeof(on)) < 0) {
127118660Sume		warnmsg(LOG_ERR, __func__, "IPV6_PKTINFO: %s",
12855163Sshin		       strerror(errno));
12955163Sshin		exit(1);
13055163Sshin	}
13162632Skris#endif
13255163Sshin
13355163Sshin	on = 1;
13455163Sshin	/* specify to tell value of hoplimit field of received IP6 hdr */
13562632Skris#ifdef IPV6_RECVHOPLIMIT
13662632Skris	if (setsockopt(rssock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on,
13762632Skris		       sizeof(on)) < 0) {
138118660Sume		warnmsg(LOG_ERR, __func__, "IPV6_RECVHOPLIMIT: %s",
13962632Skris		       strerror(errno));
14062632Skris		exit(1);
14162632Skris	}
14262632Skris#else  /* old adv. API */
14355163Sshin	if (setsockopt(rssock, IPPROTO_IPV6, IPV6_HOPLIMIT, &on,
14455163Sshin		       sizeof(on)) < 0) {
145118660Sume		warnmsg(LOG_ERR, __func__, "IPV6_HOPLIMIT: %s",
14655163Sshin		       strerror(errno));
14755163Sshin		exit(1);
14855163Sshin	}
14962632Skris#endif
15055163Sshin
15155163Sshin	/* specfiy to accept only router advertisements on the socket */
15255163Sshin	ICMP6_FILTER_SETBLOCKALL(&filt);
15355163Sshin	ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt);
15455163Sshin	if (setsockopt(rssock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
15555163Sshin		       sizeof(filt)) == -1) {
156118660Sume		warnmsg(LOG_ERR, __func__, "setsockopt(ICMP6_FILTER): %s",
15755163Sshin		       strerror(errno));
15855163Sshin		return(-1);
15955163Sshin	}
16055163Sshin
16155163Sshin	/* initialize msghdr for receiving packets */
16255163Sshin	rcviov[0].iov_base = (caddr_t)answer;
16355163Sshin	rcviov[0].iov_len = sizeof(answer);
16455163Sshin	rcvmhdr.msg_name = (caddr_t)&from;
16555163Sshin	rcvmhdr.msg_namelen = sizeof(from);
16655163Sshin	rcvmhdr.msg_iov = rcviov;
16755163Sshin	rcvmhdr.msg_iovlen = 1;
16855163Sshin	rcvmhdr.msg_control = (caddr_t) rcvcmsgbuf;
16962632Skris	rcvmhdr.msg_controllen = rcvcmsglen;
17055163Sshin
17155163Sshin	/* initialize msghdr for sending packets */
17255163Sshin	sndmhdr.msg_namelen = sizeof(struct sockaddr_in6);
17355163Sshin	sndmhdr.msg_iov = sndiov;
17455163Sshin	sndmhdr.msg_iovlen = 1;
17555163Sshin	sndmhdr.msg_control = (caddr_t)sndcmsgbuf;
17662632Skris	sndmhdr.msg_controllen = sndcmsglen;
17755163Sshin
17855163Sshin	return(rssock);
17955163Sshin}
18055163Sshin
18155163Sshinvoid
18255163Sshinsendpacket(struct ifinfo *ifinfo)
18355163Sshin{
18455163Sshin	int i;
18555163Sshin	struct cmsghdr *cm;
18655163Sshin	struct in6_pktinfo *pi;
18755163Sshin
18855163Sshin	sndmhdr.msg_name = (caddr_t)&sin6_allrouters;
18955163Sshin	sndmhdr.msg_iov[0].iov_base = (caddr_t)ifinfo->rs_data;
19055163Sshin	sndmhdr.msg_iov[0].iov_len = ifinfo->rs_datalen;
19155163Sshin
19255163Sshin	cm = CMSG_FIRSTHDR(&sndmhdr);
19355163Sshin	/* specify the outgoing interface */
19455163Sshin	cm->cmsg_level = IPPROTO_IPV6;
19555163Sshin	cm->cmsg_type = IPV6_PKTINFO;
19655163Sshin	cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
19755163Sshin	pi = (struct in6_pktinfo *)CMSG_DATA(cm);
19855163Sshin	memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr));	/*XXX*/
19955163Sshin	pi->ipi6_ifindex = ifinfo->sdl->sdl_index;
20055163Sshin
20155163Sshin	/* specify the hop limit of the packet */
20255163Sshin	{
20355163Sshin		int hoplimit = 255;
20455163Sshin
20555163Sshin		cm = CMSG_NXTHDR(&sndmhdr, cm);
20655163Sshin		cm->cmsg_level = IPPROTO_IPV6;
20755163Sshin		cm->cmsg_type = IPV6_HOPLIMIT;
20855163Sshin		cm->cmsg_len = CMSG_LEN(sizeof(int));
20955163Sshin		memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int));
21055163Sshin	}
21155163Sshin
21255163Sshin	warnmsg(LOG_DEBUG,
213118660Sume	       __func__, "send RS on %s, whose state is %d",
21455163Sshin	       ifinfo->ifname, ifinfo->state);
21555163Sshin
21655163Sshin	i = sendmsg(rssock, &sndmhdr, 0);
21755163Sshin
21855163Sshin	if (i < 0 || i != ifinfo->rs_datalen) {
21955163Sshin		/*
22055163Sshin		 * ENETDOWN is not so serious, especially when using several
22155163Sshin		 * network cards on a mobile node. We ignore it.
22255163Sshin		 */
22355163Sshin		if (errno != ENETDOWN || dflag > 0)
224118660Sume			warnmsg(LOG_ERR, __func__, "sendmsg on %s: %s",
22555163Sshin				ifinfo->ifname, strerror(errno));
22655163Sshin	}
22755163Sshin
22855163Sshin	/* update counter */
22955163Sshin	ifinfo->probes++;
23055163Sshin}
23155163Sshin
23255163Sshinvoid
23355163Sshinrtsol_input(int s)
23455163Sshin{
23555163Sshin	int i;
23655163Sshin	int *hlimp = NULL;
23755163Sshin	struct icmp6_hdr *icp;
23855163Sshin	int ifindex = 0;
23955163Sshin	struct cmsghdr *cm;
24055163Sshin	struct in6_pktinfo *pi = NULL;
24155163Sshin	struct ifinfo *ifi = NULL;
242118661Sume	struct nd_router_advert *nd_ra;
24355163Sshin	u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
24455163Sshin
24555163Sshin	/* get message */
24655163Sshin	if ((i = recvmsg(s, &rcvmhdr, 0)) < 0) {
247118660Sume		warnmsg(LOG_ERR, __func__, "recvmsg: %s", strerror(errno));
24855163Sshin		return;
24955163Sshin	}
25055163Sshin
25155163Sshin	/* extract optional information via Advanced API */
25255163Sshin	for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvmhdr);
25355163Sshin	     cm;
25455163Sshin	     cm = (struct cmsghdr *)CMSG_NXTHDR(&rcvmhdr, cm)) {
25555163Sshin		if (cm->cmsg_level == IPPROTO_IPV6 &&
25655163Sshin		    cm->cmsg_type == IPV6_PKTINFO &&
25755163Sshin		    cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) {
25855163Sshin			pi = (struct in6_pktinfo *)(CMSG_DATA(cm));
25955163Sshin			ifindex = pi->ipi6_ifindex;
26055163Sshin		}
26155163Sshin		if (cm->cmsg_level == IPPROTO_IPV6 &&
26255163Sshin		    cm->cmsg_type == IPV6_HOPLIMIT &&
26355163Sshin		    cm->cmsg_len == CMSG_LEN(sizeof(int)))
26455163Sshin			hlimp = (int *)CMSG_DATA(cm);
26555163Sshin	}
26655163Sshin
26755163Sshin	if (ifindex == 0) {
26855163Sshin		warnmsg(LOG_ERR,
269118660Sume		       __func__, "failed to get receiving interface");
27055163Sshin		return;
27155163Sshin	}
27255163Sshin	if (hlimp == NULL) {
27355163Sshin		warnmsg(LOG_ERR,
274118660Sume		       __func__, "failed to get receiving hop limit");
27555163Sshin		return;
27655163Sshin	}
27755163Sshin
27855163Sshin	if (i < sizeof(struct nd_router_advert)) {
27955163Sshin		warnmsg(LOG_ERR,
280118660Sume		       __func__, "packet size(%d) is too short", i);
28155163Sshin		return;
28255163Sshin	}
28355163Sshin
28455163Sshin	icp = (struct icmp6_hdr *)rcvmhdr.msg_iov[0].iov_base;
28555163Sshin
28655163Sshin	if (icp->icmp6_type != ND_ROUTER_ADVERT) {
287118660Sume		warnmsg(LOG_ERR, __func__,
28855163Sshin			"invalid icmp type(%d) from %s on %s", icp->icmp6_type,
28955163Sshin		       inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
29055163Sshin				 INET6_ADDRSTRLEN),
29155163Sshin		       if_indextoname(pi->ipi6_ifindex, ifnamebuf));
29255163Sshin		return;
29355163Sshin	}
29455163Sshin
29555163Sshin	if (icp->icmp6_code != 0) {
296118660Sume		warnmsg(LOG_ERR, __func__,
29755163Sshin			"invalid icmp code(%d) from %s on %s", icp->icmp6_code,
29855163Sshin		       inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
29955163Sshin				 INET6_ADDRSTRLEN),
30055163Sshin		       if_indextoname(pi->ipi6_ifindex, ifnamebuf));
30155163Sshin		return;
30255163Sshin	}
30355163Sshin
30455163Sshin	if (*hlimp != 255) {
305118660Sume		warnmsg(LOG_NOTICE, __func__,
30655163Sshin			"invalid RA with hop limit(%d) from %s on %s",
30755163Sshin		       *hlimp,
30855163Sshin		       inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
30955163Sshin				 INET6_ADDRSTRLEN),
31055163Sshin		       if_indextoname(pi->ipi6_ifindex, ifnamebuf));
31155163Sshin		return;
31255163Sshin	}
31355163Sshin
31455163Sshin	if (pi && !IN6_IS_ADDR_LINKLOCAL(&from.sin6_addr)) {
315118660Sume		warnmsg(LOG_NOTICE, __func__,
31655163Sshin			"invalid RA with non link-local source from %s on %s",
31755163Sshin		       inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
31855163Sshin				 INET6_ADDRSTRLEN),
31955163Sshin		       if_indextoname(pi->ipi6_ifindex, ifnamebuf));
32055163Sshin		return;
32155163Sshin	}
32255163Sshin
32355163Sshin	/* xxx: more validation? */
32455163Sshin
32555163Sshin	if ((ifi = find_ifinfo(pi->ipi6_ifindex)) == NULL) {
326118660Sume		warnmsg(LOG_NOTICE, __func__,
32755163Sshin			"received RA from %s on an unexpeced IF(%s)",
32855163Sshin		       inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
32955163Sshin				 INET6_ADDRSTRLEN),
33055163Sshin		       if_indextoname(pi->ipi6_ifindex, ifnamebuf));
33155163Sshin		return;
33255163Sshin	}
33355163Sshin
334118660Sume	warnmsg(LOG_DEBUG, __func__,
33555163Sshin		"received RA from %s on %s, state is %d",
33655163Sshin	       inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
33755163Sshin			 INET6_ADDRSTRLEN),
33855163Sshin	       ifi->ifname, ifi->state);
33955163Sshin
340118661Sume	nd_ra = (struct nd_router_advert *)icp;
341118661Sume
342118661Sume	/*
343118661Sume	 * Process the "O bit."
344118661Sume	 * If the value of OtherConfigFlag changes from FALSE to TRUE, the
345118661Sume	 * host should invoke the stateful autoconfiguration protocol,
346118661Sume	 * requesting information.
347118661Sume	 * [RFC 2462 Section 5.5.3]
348118661Sume	 */
349118661Sume	if (((nd_ra->nd_ra_flags_reserved) & ND_RA_FLAG_OTHER) &&
350118661Sume	    !ifi->otherconfig) {
351118661Sume		warnmsg(LOG_DEBUG, __func__,
352118661Sume		    "OtherConfigFlag on %s is turned on", ifi->ifname);
353118661Sume		ifi->otherconfig = 1;
354118661Sume		call_script(otherconf_script, ifi->ifname);
355118661Sume	}
356118661Sume
35755163Sshin	ifi->racnt++;
35855163Sshin
35955163Sshin	switch(ifi->state) {
36055163Sshin	 case IFS_IDLE:		/* should be ignored */
36155163Sshin	 case IFS_DELAY:		/* right? */
36255163Sshin		 break;
36355163Sshin	 case IFS_PROBE:
36455163Sshin		 ifi->state = IFS_IDLE;
36555163Sshin		 ifi->probes = 0;
36655163Sshin		 rtsol_timer_update(ifi);
36755163Sshin		 break;
36855163Sshin	}
36955163Sshin}
370118661Sume
371118661Sumestatic void
372118661Sumecall_script(scriptpath, ifname)
373118661Sume	char *scriptpath, *ifname;
374118661Sume{
375118661Sume	pid_t pid, wpid;
376118661Sume
377118661Sume	if (scriptpath == NULL)
378118661Sume		return;
379118661Sume
380118661Sume	/* launch the script */
381118661Sume	pid = fork();
382118661Sume	if (pid < 0) {
383118661Sume		warnmsg(LOG_ERR, __func__,
384118661Sume		    "failed to fork: %s", strerror(errno));
385118661Sume		return;
386118661Sume	} else if (pid) {
387118661Sume		int wstatus;
388118661Sume
389118661Sume		do {
390118661Sume			wpid = wait(&wstatus);
391118661Sume		} while (wpid != pid && wpid > 0);
392118661Sume
393118661Sume		if (wpid < 0)
394118661Sume			warnmsg(LOG_ERR, __func__,
395118661Sume			    "wait: %s", strerror(errno));
396118661Sume		else {
397118661Sume			warnmsg(LOG_DEBUG, __func__,
398118661Sume			    "script \"%s\" terminated", scriptpath);
399118661Sume		}
400118661Sume	} else {
401118661Sume		char *argv[3];
402118661Sume		int fd;
403118661Sume
404118661Sume		argv[0] = scriptpath;
405118661Sume		argv[1] = ifname;
406118661Sume		argv[2] = NULL;
407118661Sume
408118661Sume		if (safefile(scriptpath)) {
409118661Sume			warnmsg(LOG_ERR, __func__,
410118661Sume			    "script \"%s\" cannot be executed safely",
411118661Sume			    scriptpath);
412118661Sume			exit(1);
413118661Sume		}
414118661Sume
415118661Sume		if ((fd = open("/dev/null", O_RDWR)) != -1) {
416118661Sume			dup2(fd, STDIN_FILENO);
417118661Sume			dup2(fd, STDOUT_FILENO);
418118661Sume			dup2(fd, STDERR_FILENO);
419118661Sume			if (fd > STDERR_FILENO)
420118661Sume				close(fd);
421118661Sume		}
422118661Sume
423118661Sume		execv(scriptpath, argv);
424118661Sume
425118661Sume		warnmsg(LOG_ERR, __func__, "child: exec failed: %s",
426118661Sume		    strerror(errno));
427118661Sume		exit(0);
428118661Sume	}
429118661Sume
430118661Sume	return;
431118661Sume}
432118661Sume
433118661Sumestatic int
434118661Sumesafefile(path)
435118661Sume	const char *path;
436118661Sume{
437118661Sume	struct stat s;
438118661Sume	uid_t myuid;
439118661Sume
440118661Sume	/* no setuid */
441118661Sume	if (getuid() != geteuid()) {
442118661Sume		warnmsg(LOG_NOTICE, __func__,
443118661Sume		    "setuid'ed execution not allowed\n");
444118661Sume		return (-1);
445118661Sume	}
446118661Sume
447118661Sume	if (lstat(path, &s) != 0) {
448118661Sume		warnmsg(LOG_NOTICE, __func__, "lstat failed: %s",
449118661Sume		    strerror(errno));
450118661Sume		return (-1);
451118661Sume	}
452118661Sume
453118661Sume	/* the file must be owned by the running uid */
454118661Sume	myuid = getuid();
455118661Sume	if (s.st_uid != myuid) {
456118661Sume		warnmsg(LOG_NOTICE, __func__,
457118661Sume		    "%s has invalid owner uid\n", path);
458118661Sume		return (-1);
459118661Sume	}
460118661Sume
461118661Sume	switch (s.st_mode & S_IFMT) {
462118661Sume	case S_IFREG:
463118661Sume		break;
464118661Sume	default:
465118661Sume		warnmsg(LOG_NOTICE, __func__,
466118661Sume		    "%s is an invalid file type 0x%o\n",
467118661Sume		    path, (s.st_mode & S_IFMT));
468118661Sume		return (-1);
469118661Sume	}
470118661Sume
471118661Sume	return (0);
472118661Sume}
473