rtsold.c revision 118909
178064Sume/*	$KAME: rtsold.c,v 1.31 2001/05/22 06:03:06 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/rtsold.c 118909 2003-08-14 15:47:31Z ume $
3255163Sshin */
3355163Sshin
3455163Sshin#include <sys/types.h>
3555163Sshin#include <sys/time.h>
3662632Skris#include <sys/socket.h>
37118909Sume#include <sys/param.h>
3855163Sshin
3962632Skris#include <net/if.h>
4055163Sshin#include <net/if_dl.h>
4155163Sshin
4255163Sshin#include <netinet/in.h>
4355163Sshin#include <netinet/icmp6.h>
4455163Sshin
4555163Sshin#include <signal.h>
4655163Sshin#include <unistd.h>
4755163Sshin#include <syslog.h>
4855163Sshin#include <string.h>
4955163Sshin#include <stdlib.h>
5055163Sshin#include <stdio.h>
5155163Sshin#include <errno.h>
5255163Sshin#include <err.h>
5355163Sshin#include <stdarg.h>
5466776Skris#include <ifaddrs.h>
55118664Sume
5655163Sshin#include "rtsold.h"
5755163Sshin
5855163Sshinstruct ifinfo *iflist;
5955163Sshinstruct timeval tm_max =	{0x7fffffff, 0x7fffffff};
60118664Sumestatic int log_upto = 999;
61118664Sumestatic int fflag = 0;
62118664Sume
6366776Skrisint aflag = 0;
6466776Skrisint dflag = 0;
65118664Sume
66118661Sumechar *otherconf_script;
6755163Sshin
6855163Sshin/* protocol constatns */
6962632Skris#define MAX_RTR_SOLICITATION_DELAY	1 /* second */
7062632Skris#define RTR_SOLICITATION_INTERVAL	4 /* seconds */
7162632Skris#define MAX_RTR_SOLICITATIONS		3 /* times */
7255163Sshin
73118664Sume/*
74118664Sume * implementation dependent constants in secondes
75118664Sume * XXX: should be configurable
76118664Sume */
77118664Sume#define PROBE_INTERVAL 60
7855163Sshin
7955163Sshin/* utility macros */
8055163Sshin/* a < b */
8162632Skris#define TIMEVAL_LT(a, b) (((a).tv_sec < (b).tv_sec) ||\
8255163Sshin			  (((a).tv_sec == (b).tv_sec) && \
8355163Sshin			    ((a).tv_usec < (b).tv_usec)))
8455163Sshin
8555163Sshin/* a <= b */
8662632Skris#define TIMEVAL_LEQ(a, b) (((a).tv_sec < (b).tv_sec) ||\
8755163Sshin			   (((a).tv_sec == (b).tv_sec) &&\
88118664Sume			    ((a).tv_usec <= (b).tv_usec)))
8955163Sshin
9055163Sshin/* a == b */
9162632Skris#define TIMEVAL_EQ(a, b) (((a).tv_sec==(b).tv_sec) && ((a).tv_usec==(b).tv_usec))
9255163Sshin
93118664Sumeint main __P((int, char **));
9455163Sshin
9555163Sshin/* static variables and functions */
9655163Sshinstatic int mobile_node = 0;
9755163Sshinstatic int do_dump;
9862632Skrisstatic char *dumpfilename = "/var/run/rtsold.dump"; /* XXX: should be configurable */
9955163Sshinstatic char *pidfilename = "/var/run/rtsold.pid"; /* should be configurable */
10055163Sshin
101118664Sumestatic int ifconfig __P((char *));
10262632Skris#if 0
103118664Sumestatic int ifreconfig __P((char *));
10462632Skris#endif
105118664Sumestatic int make_packet __P((struct ifinfo *));
10655163Sshinstatic struct timeval *rtsol_check_timer __P((void));
107118664Sumestatic void TIMEVAL_ADD __P((struct timeval *, struct timeval *,
108118664Sume	struct timeval *));
109118664Sumestatic void TIMEVAL_SUB __P((struct timeval *, struct timeval *,
110118664Sume	struct timeval *));
11155163Sshin
11262632Skrisstatic void rtsold_set_dump_file __P((void));
113118664Sumestatic void usage __P((char *));
11466776Skrisstatic char **autoifprobe __P((void));
11555163Sshin
11655163Sshinint
11755163Sshinmain(argc, argv)
11855163Sshin	int argc;
119118664Sume	char **argv;
12055163Sshin{
121118909Sume	int s, ch, once = 0;
12255163Sshin	struct timeval *timeout;
123118664Sume	char *argv0, *opts;
124118909Sume	fd_set *fdsetp, *selectfdp;
125118909Sume	int fdmasks;
126118909Sume	int maxfd;
127118909Sume	int rtsock;
12855163Sshin
12955163Sshin	/*
13055163Sshin	 * Initialization
13155163Sshin	 */
13255163Sshin	argv0 = argv[0];
13355163Sshin
13455163Sshin	/* get option */
13555163Sshin	if (argv0 && argv0[strlen(argv0) - 1] != 'd') {
13655163Sshin		fflag = 1;
13755163Sshin		once = 1;
138118661Sume		opts = "adDO:";
13955163Sshin	} else
140118661Sume		opts = "adDfm1O:";
14155163Sshin
14255163Sshin	while ((ch = getopt(argc, argv, opts)) != -1) {
14366776Skris		switch (ch) {
14466776Skris		case 'a':
14566776Skris			aflag = 1;
14666776Skris			break;
14766776Skris		case 'd':
14866776Skris			dflag = 1;
14966776Skris			break;
15066776Skris		case 'D':
15166776Skris			dflag = 2;
15266776Skris			break;
15366776Skris		case 'f':
15466776Skris			fflag = 1;
15566776Skris			break;
15666776Skris		case 'm':
15766776Skris			mobile_node = 1;
15866776Skris			break;
15966776Skris		case '1':
16066776Skris			once = 1;
16166776Skris			break;
162118661Sume		case 'O':
163118661Sume			otherconf_script = optarg;
164118661Sume			break;
16566776Skris		default:
16666776Skris			usage(argv0);
16766776Skris			/*NOTREACHED*/
16855163Sshin		}
16955163Sshin	}
17055163Sshin	argc -= optind;
17155163Sshin	argv += optind;
17266776Skris
17366776Skris	if (aflag) {
17466776Skris		int i;
17566776Skris
17666776Skris		if (argc != 0) {
17766776Skris			usage(argv0);
17866776Skris			/*NOTREACHED*/
17966776Skris		}
18066776Skris
18166776Skris		argv = autoifprobe();
18266776Skris		if (!argv) {
18366776Skris			errx(1, "could not autoprobe interface");
18466776Skris			/*NOTREACHED*/
18566776Skris		}
18666776Skris
18766776Skris		for (i = 0; argv[i]; i++)
18866776Skris			;
18966776Skris		argc = i;
19066776Skris	}
19166776Skris	if (argc == 0) {
19255163Sshin		usage(argv0);
19366776Skris		/*NOTREACHED*/
19466776Skris	}
19555163Sshin
19655163Sshin	/* set log level */
19755163Sshin	if (dflag == 0)
19855163Sshin		log_upto = LOG_NOTICE;
19955163Sshin	if (!fflag) {
20055163Sshin		char *ident;
201118664Sume
20255163Sshin		ident = strrchr(argv0, '/');
20355163Sshin		if (!ident)
20455163Sshin			ident = argv0;
20555163Sshin		else
20655163Sshin			ident++;
20755163Sshin		openlog(ident, LOG_NDELAY|LOG_PID, LOG_DAEMON);
20855163Sshin		if (log_upto >= 0)
20955163Sshin			setlogmask(LOG_UPTO(log_upto));
21055163Sshin	}
21155163Sshin
212118661Sume	if (otherconf_script && *otherconf_script != '/') {
213118661Sume		errx(1, "configuration script (%s) must be an absolute path",
214118661Sume		    otherconf_script);
215118661Sume	}
216118661Sume
21762632Skris#ifndef HAVE_ARC4RANDOM
218118664Sume	/* random value initialization */
21955163Sshin	srandom((u_long)time(NULL));
22062632Skris#endif
22155163Sshin
22255163Sshin	/* warn if accept_rtadv is down */
22355163Sshin	if (!getinet6sysctl(IPV6CTL_ACCEPT_RTADV))
22455163Sshin		warnx("kernel is configured not to accept RAs");
22566776Skris	/* warn if forwarding is up */
22666776Skris	if (getinet6sysctl(IPV6CTL_FORWARDING))
22766776Skris		warnx("kernel is configured as a router, not a host");
22855163Sshin
22955163Sshin	/* initialization to dump internal status to a file */
23066776Skris	if (signal(SIGUSR1, (void *)rtsold_set_dump_file) < 0) {
23155163Sshin		errx(1, "failed to set signal for dump status");
23266776Skris		/*NOTREACHED*/
23366776Skris	}
23455163Sshin
23562632Skris	/*
23662632Skris	 * Open a socket for sending RS and receiving RA.
23762632Skris	 * This should be done before calling ifinit(), since the function
23862632Skris	 * uses the socket.
23962632Skris	 */
24066776Skris	if ((s = sockopen()) < 0) {
24162632Skris		errx(1, "failed to open a socket");
24266776Skris		/*NOTREACHED*/
24366776Skris	}
24478064Sume	maxfd = s;
24578064Sume	if ((rtsock = rtsock_open()) < 0) {
24678064Sume		errx(1, "failed to open a socket");
24778064Sume		/*NOTREACHED*/
24878064Sume	}
24978064Sume	if (rtsock > maxfd)
25078064Sume		maxfd = rtsock;
25162632Skris
252118909Sume	fdmasks = howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask);
253118909Sume	if ((fdsetp = malloc(fdmasks)) == NULL) {
254118909Sume		err(1, "malloc");
255118909Sume		/*NOTREACHED*/
256118909Sume	}
257118909Sume	if ((selectfdp = malloc(fdmasks)) == NULL) {
258118909Sume		err(1, "malloc");
259118909Sume		/*NOTREACHED*/
260118909Sume	}
261118909Sume
26255163Sshin	/* configuration per interface */
26366776Skris	if (ifinit()) {
26455163Sshin		errx(1, "failed to initilizatoin interfaces");
26566776Skris		/*NOTREACHED*/
26666776Skris	}
26755163Sshin	while (argc--) {
26866776Skris		if (ifconfig(*argv)) {
26962632Skris			errx(1, "failed to initialize %s", *argv);
27066776Skris			/*NOTREACHED*/
27166776Skris		}
27255163Sshin		argv++;
27355163Sshin	}
27455163Sshin
27555163Sshin	/* setup for probing default routers */
27666776Skris	if (probe_init()) {
27755163Sshin		errx(1, "failed to setup for probing routers");
27866776Skris		/*NOTREACHED*/
27966776Skris	}
28055163Sshin
28155163Sshin	if (!fflag)
28255163Sshin		daemon(0, 0);		/* act as a daemon */
28355163Sshin
28455163Sshin	/* dump the current pid */
28555163Sshin	if (!once) {
28655163Sshin		pid_t pid = getpid();
28755163Sshin		FILE *fp;
28855163Sshin
28955163Sshin		if ((fp = fopen(pidfilename, "w")) == NULL)
290118660Sume			warnmsg(LOG_ERR, __func__,
291118664Sume			    "failed to open a pid log file(%s): %s",
292118664Sume			    pidfilename, strerror(errno));
29355163Sshin		else {
29455163Sshin			fprintf(fp, "%d\n", pid);
29555163Sshin			fclose(fp);
29655163Sshin		}
29755163Sshin	}
29855163Sshin
299118909Sume	memset(fdsetp, 0, fdmasks);
300118909Sume	FD_SET(s, fdsetp);
301118909Sume	FD_SET(rtsock, fdsetp);
30255163Sshin	while (1) {		/* main loop */
30355163Sshin		int e;
30455163Sshin
305118909Sume		memcpy(selectfdp, fdsetp, fdmasks);
306118909Sume
30755163Sshin		if (do_dump) {	/* SIGUSR1 */
30855163Sshin			do_dump = 0;
30955163Sshin			rtsold_dump_file(dumpfilename);
31055163Sshin		}
311118664Sume
31255163Sshin		timeout = rtsol_check_timer();
31355163Sshin
31455163Sshin		if (once) {
31555163Sshin			struct ifinfo *ifi;
31655163Sshin
31755163Sshin			/* if we have no timeout, we are done (or failed) */
31855163Sshin			if (timeout == NULL)
31955163Sshin				break;
32055163Sshin
32155163Sshin			/* if all interfaces have got RA packet, we are done */
32255163Sshin			for (ifi = iflist; ifi; ifi = ifi->next) {
32355163Sshin				if (ifi->state != IFS_DOWN && ifi->racnt == 0)
32455163Sshin					break;
32555163Sshin			}
32655163Sshin			if (ifi == NULL)
32755163Sshin				break;
32855163Sshin		}
329118909Sume		e = select(maxfd + 1, selectfdp, NULL, NULL, timeout);
33078064Sume		if (e < 1) {
33155163Sshin			if (e < 0 && errno != EINTR) {
332118660Sume				warnmsg(LOG_ERR, __func__, "select: %s",
333118664Sume				    strerror(errno));
33455163Sshin			}
33555163Sshin			continue;
33655163Sshin		}
33755163Sshin
33855163Sshin		/* packet reception */
339118909Sume		if (FD_ISSET(rtsock, selectfdp))
34078064Sume			rtsock_input(rtsock);
341118909Sume		if (FD_ISSET(s, selectfdp))
34255163Sshin			rtsol_input(s);
34355163Sshin	}
34455163Sshin	/* NOTREACHED */
34555163Sshin
34655163Sshin	return 0;
34755163Sshin}
34855163Sshin
34955163Sshinstatic int
35055163Sshinifconfig(char *ifname)
35155163Sshin{
35255163Sshin	struct ifinfo *ifinfo;
35355163Sshin	struct sockaddr_dl *sdl;
35455163Sshin	int flags;
35555163Sshin
35655163Sshin	if ((sdl = if_nametosdl(ifname)) == NULL) {
357118660Sume		warnmsg(LOG_ERR, __func__,
358118664Sume		    "failed to get link layer information for %s", ifname);
35955163Sshin		return(-1);
36055163Sshin	}
36155163Sshin	if (find_ifinfo(sdl->sdl_index)) {
362118660Sume		warnmsg(LOG_ERR, __func__,
363118664Sume		    "interface %s was already configured", ifname);
36462632Skris		free(sdl);
36555163Sshin		return(-1);
36655163Sshin	}
36755163Sshin
36855163Sshin	if ((ifinfo = malloc(sizeof(*ifinfo))) == NULL) {
369118660Sume		warnmsg(LOG_ERR, __func__, "memory allocation failed");
37062632Skris		free(sdl);
37155163Sshin		return(-1);
37255163Sshin	}
37355163Sshin	memset(ifinfo, 0, sizeof(*ifinfo));
37455163Sshin	ifinfo->sdl = sdl;
37555163Sshin
376118786Sume	strlcpy(ifinfo->ifname, ifname, sizeof(ifinfo->ifname));
37755163Sshin
37855163Sshin	/* construct a router solicitation message */
37955163Sshin	if (make_packet(ifinfo))
38055163Sshin		goto bad;
38155163Sshin
38255163Sshin	/*
38355163Sshin	 * check if the interface is available.
38455163Sshin	 * also check if SIOCGIFMEDIA ioctl is OK on the interface.
38555163Sshin	 */
38655163Sshin	ifinfo->mediareqok = 1;
38755163Sshin	ifinfo->active = interface_status(ifinfo);
38855163Sshin	if (!ifinfo->mediareqok) {
38955163Sshin		/*
39055163Sshin		 * probe routers periodically even if the link status
39155163Sshin		 * does not change.
39255163Sshin		 */
39355163Sshin		ifinfo->probeinterval = PROBE_INTERVAL;
39455163Sshin	}
39555163Sshin
39655163Sshin	/* activate interface: interface_up returns 0 on success */
39755163Sshin	flags = interface_up(ifinfo->ifname);
39855163Sshin	if (flags == 0)
39955163Sshin		ifinfo->state = IFS_DELAY;
40055163Sshin	else if (flags == IFS_TENTATIVE)
40155163Sshin		ifinfo->state = IFS_TENTATIVE;
40255163Sshin	else
40355163Sshin		ifinfo->state = IFS_DOWN;
40455163Sshin
40555163Sshin	rtsol_timer_update(ifinfo);
40655163Sshin
40755163Sshin	/* link into chain */
40855163Sshin	if (iflist)
40955163Sshin		ifinfo->next = iflist;
41055163Sshin	iflist = ifinfo;
41155163Sshin
41255163Sshin	return(0);
41355163Sshin
414118664Sumebad:
41562632Skris	free(ifinfo->sdl);
41655163Sshin	free(ifinfo);
41755163Sshin	return(-1);
41855163Sshin}
41955163Sshin
42062632Skris#if 0
42162632Skrisstatic int
42262632Skrisifreconfig(char *ifname)
42362632Skris{
42462632Skris	struct ifinfo *ifi, *prev;
42562632Skris	int rv;
42662632Skris
42762632Skris	prev = NULL;
42862632Skris	for (ifi = iflist; ifi; ifi = ifi->next) {
42962632Skris		if (strncmp(ifi->ifname, ifname, sizeof(ifi->ifname)) == 0)
43062632Skris			break;
43162632Skris		prev = ifi;
43262632Skris	}
43362632Skris	prev->next = ifi->next;
43462632Skris
43562632Skris	rv = ifconfig(ifname);
43662632Skris
43762632Skris	/* reclaim it after ifconfig() in case ifname is pointer inside ifi */
43862632Skris	if (ifi->rs_data)
43962632Skris		free(ifi->rs_data);
44062632Skris	free(ifi->sdl);
44162632Skris	free(ifi);
44262632Skris	return rv;
44362632Skris}
44462632Skris#endif
44562632Skris
44655163Sshinstruct ifinfo *
44755163Sshinfind_ifinfo(int ifindex)
44855163Sshin{
44955163Sshin	struct ifinfo *ifi;
45055163Sshin
45155163Sshin	for (ifi = iflist; ifi; ifi = ifi->next)
45255163Sshin		if (ifi->sdl->sdl_index == ifindex)
45355163Sshin			return(ifi);
45455163Sshin	return(NULL);
45555163Sshin}
45655163Sshin
45755163Sshinstatic int
45855163Sshinmake_packet(struct ifinfo *ifinfo)
45955163Sshin{
460118664Sume	size_t packlen = sizeof(struct nd_router_solicit), lladdroptlen = 0;
461118664Sume	struct nd_router_solicit *rs;
46255163Sshin	char *buf;
46355163Sshin
46455163Sshin	if ((lladdroptlen = lladdropt_length(ifinfo->sdl)) == 0) {
465118660Sume		warnmsg(LOG_INFO, __func__,
466118664Sume		    "link-layer address option has null length"
467118664Sume		    " on %s. Treat as not included.", ifinfo->ifname);
46855163Sshin	}
46955163Sshin	packlen += lladdroptlen;
47055163Sshin	ifinfo->rs_datalen = packlen;
47155163Sshin
47255163Sshin	/* allocate buffer */
47355163Sshin	if ((buf = malloc(packlen)) == NULL) {
474118660Sume		warnmsg(LOG_ERR, __func__,
475118664Sume		    "memory allocation failed for %s", ifinfo->ifname);
47655163Sshin		return(-1);
47755163Sshin	}
47855163Sshin	ifinfo->rs_data = buf;
47955163Sshin
48055163Sshin	/* fill in the message */
48155163Sshin	rs = (struct nd_router_solicit *)buf;
48255163Sshin	rs->nd_rs_type = ND_ROUTER_SOLICIT;
48355163Sshin	rs->nd_rs_code = 0;
48455163Sshin	rs->nd_rs_cksum = 0;
48555163Sshin	rs->nd_rs_reserved = 0;
48655163Sshin	buf += sizeof(*rs);
48755163Sshin
48855163Sshin	/* fill in source link-layer address option */
48955163Sshin	if (lladdroptlen)
49055163Sshin		lladdropt_fill(ifinfo->sdl, (struct nd_opt_hdr *)buf);
49155163Sshin
49255163Sshin	return(0);
49355163Sshin}
49455163Sshin
49555163Sshinstatic struct timeval *
49655163Sshinrtsol_check_timer()
49755163Sshin{
49855163Sshin	static struct timeval returnval;
49955163Sshin	struct timeval now, rtsol_timer;
50055163Sshin	struct ifinfo *ifinfo;
50155163Sshin	int flags;
50255163Sshin
50355163Sshin	gettimeofday(&now, NULL);
50455163Sshin
50555163Sshin	rtsol_timer = tm_max;
50655163Sshin
50755163Sshin	for (ifinfo = iflist; ifinfo; ifinfo = ifinfo->next) {
50855163Sshin		if (TIMEVAL_LEQ(ifinfo->expire, now)) {
50955163Sshin			if (dflag > 1)
510118660Sume				warnmsg(LOG_DEBUG, __func__,
511118664Sume				    "timer expiration on %s, "
512118664Sume				    "state = %d", ifinfo->ifname,
513118664Sume				    ifinfo->state);
51455163Sshin
51566776Skris			switch (ifinfo->state) {
51655163Sshin			case IFS_DOWN:
51755163Sshin			case IFS_TENTATIVE:
51855163Sshin				/* interface_up returns 0 on success */
51955163Sshin				flags = interface_up(ifinfo->ifname);
52055163Sshin				if (flags == 0)
52155163Sshin					ifinfo->state = IFS_DELAY;
52255163Sshin				else if (flags == IFS_TENTATIVE)
52355163Sshin					ifinfo->state = IFS_TENTATIVE;
52455163Sshin				else
52555163Sshin					ifinfo->state = IFS_DOWN;
52655163Sshin				break;
52755163Sshin			case IFS_IDLE:
52855163Sshin			{
52955163Sshin				int oldstatus = ifinfo->active;
53055163Sshin				int probe = 0;
53155163Sshin
532118664Sume				ifinfo->active = interface_status(ifinfo);
53355163Sshin
53455163Sshin				if (oldstatus != ifinfo->active) {
535118660Sume					warnmsg(LOG_DEBUG, __func__,
536118664Sume					    "%s status is changed"
537118664Sume					    " from %d to %d",
538118664Sume					    ifinfo->ifname,
539118664Sume					    oldstatus, ifinfo->active);
54055163Sshin					probe = 1;
54155163Sshin					ifinfo->state = IFS_DELAY;
542118664Sume				} else if (ifinfo->probeinterval &&
543118664Sume				    (ifinfo->probetimer -=
544118664Sume				    ifinfo->timer.tv_sec) <= 0) {
54555163Sshin					/* probe timer expired */
54655163Sshin					ifinfo->probetimer =
547118664Sume					    ifinfo->probeinterval;
54855163Sshin					probe = 1;
54955163Sshin					ifinfo->state = IFS_PROBE;
55055163Sshin				}
55155163Sshin
552118661Sume				/*
553118661Sume				 * If we need a probe, clear the previous
554118661Sume				 * status wrt the "other" configuration.
555118661Sume				 */
556118661Sume				if (probe)
557118661Sume					ifinfo->otherconfig = 0;
558118661Sume
55955163Sshin				if (probe && mobile_node)
56055163Sshin					defrouter_probe(ifinfo->sdl->sdl_index);
56155163Sshin				break;
56255163Sshin			}
56355163Sshin			case IFS_DELAY:
56455163Sshin				ifinfo->state = IFS_PROBE;
56555163Sshin				sendpacket(ifinfo);
56655163Sshin				break;
56755163Sshin			case IFS_PROBE:
56855163Sshin				if (ifinfo->probes < MAX_RTR_SOLICITATIONS)
56955163Sshin					sendpacket(ifinfo);
57055163Sshin				else {
571118660Sume					warnmsg(LOG_INFO, __func__,
572118664Sume					    "No answer after sending %d RSs",
573118664Sume					    ifinfo->probes);
57455163Sshin					ifinfo->probes = 0;
57555163Sshin					ifinfo->state = IFS_IDLE;
57655163Sshin				}
57755163Sshin				break;
57855163Sshin			}
57955163Sshin			rtsol_timer_update(ifinfo);
58055163Sshin		}
58155163Sshin
58255163Sshin		if (TIMEVAL_LT(ifinfo->expire, rtsol_timer))
58355163Sshin			rtsol_timer = ifinfo->expire;
58455163Sshin	}
58555163Sshin
58655163Sshin	if (TIMEVAL_EQ(rtsol_timer, tm_max)) {
587118660Sume		warnmsg(LOG_DEBUG, __func__, "there is no timer");
58855163Sshin		return(NULL);
589118664Sume	} else if (TIMEVAL_LT(rtsol_timer, now))
59055163Sshin		/* this may occur when the interval is too small */
59155163Sshin		returnval.tv_sec = returnval.tv_usec = 0;
59255163Sshin	else
59355163Sshin		TIMEVAL_SUB(&rtsol_timer, &now, &returnval);
59455163Sshin
59555163Sshin	if (dflag > 1)
596118660Sume		warnmsg(LOG_DEBUG, __func__, "New timer is %ld:%08ld",
597118664Sume		    (long)returnval.tv_sec, (long)returnval.tv_usec);
59855163Sshin
59955163Sshin	return(&returnval);
60055163Sshin}
60155163Sshin
60255163Sshinvoid
60355163Sshinrtsol_timer_update(struct ifinfo *ifinfo)
60455163Sshin{
60562632Skris#define MILLION 1000000
60662632Skris#define DADRETRY 10		/* XXX: adhoc */
60755163Sshin	long interval;
60855163Sshin	struct timeval now;
60955163Sshin
61055163Sshin	bzero(&ifinfo->timer, sizeof(ifinfo->timer));
61155163Sshin
61255163Sshin	switch (ifinfo->state) {
61355163Sshin	case IFS_DOWN:
61455163Sshin	case IFS_TENTATIVE:
61555163Sshin		if (++ifinfo->dadcount > DADRETRY) {
61655163Sshin			ifinfo->dadcount = 0;
61755163Sshin			ifinfo->timer.tv_sec = PROBE_INTERVAL;
618118664Sume		} else
61955163Sshin			ifinfo->timer.tv_sec = 1;
62055163Sshin		break;
62155163Sshin	case IFS_IDLE:
62255163Sshin		if (mobile_node) {
623118664Sume			/* XXX should be configurable */
62455163Sshin			ifinfo->timer.tv_sec = 3;
62555163Sshin		}
62655163Sshin		else
62755163Sshin			ifinfo->timer = tm_max;	/* stop timer(valid?) */
62855163Sshin		break;
62955163Sshin	case IFS_DELAY:
63062632Skris#ifndef HAVE_ARC4RANDOM
63155163Sshin		interval = random() % (MAX_RTR_SOLICITATION_DELAY * MILLION);
63262632Skris#else
63362632Skris		interval = arc4random() % (MAX_RTR_SOLICITATION_DELAY * MILLION);
63462632Skris#endif
63555163Sshin		ifinfo->timer.tv_sec = interval / MILLION;
63655163Sshin		ifinfo->timer.tv_usec = interval % MILLION;
63755163Sshin		break;
63855163Sshin	case IFS_PROBE:
63978064Sume		if (ifinfo->probes < MAX_RTR_SOLICITATIONS)
64078064Sume			ifinfo->timer.tv_sec = RTR_SOLICITATION_INTERVAL;
64178064Sume		else {
64278064Sume			/*
64378064Sume			 * After sending MAX_RTR_SOLICITATIONS solicitations,
64478064Sume			 * we're just waiting for possible replies; there
64578064Sume			 * will be no more solicatation.  Thus, we change
64678064Sume			 * the timer value to MAX_RTR_SOLICITATION_DELAY based
64778064Sume			 * on RFC 2461, Section 6.3.7.
64878064Sume			 */
64978064Sume			ifinfo->timer.tv_sec = MAX_RTR_SOLICITATION_DELAY;
65078064Sume		}
65155163Sshin		break;
65255163Sshin	default:
653118660Sume		warnmsg(LOG_ERR, __func__,
654118664Sume		    "illegal interface state(%d) on %s",
655118664Sume		    ifinfo->state, ifinfo->ifname);
65655163Sshin		return;
65755163Sshin	}
65855163Sshin
65955163Sshin	/* reset the timer */
66055163Sshin	if (TIMEVAL_EQ(ifinfo->timer, tm_max)) {
66155163Sshin		ifinfo->expire = tm_max;
662118660Sume		warnmsg(LOG_DEBUG, __func__,
663118664Sume		    "stop timer for %s", ifinfo->ifname);
664118664Sume	} else {
66555163Sshin		gettimeofday(&now, NULL);
66655163Sshin		TIMEVAL_ADD(&now, &ifinfo->timer, &ifinfo->expire);
66755163Sshin
66855163Sshin		if (dflag > 1)
669118660Sume			warnmsg(LOG_DEBUG, __func__,
670118664Sume			    "set timer for %s to %d:%d", ifinfo->ifname,
671118664Sume			    (int)ifinfo->timer.tv_sec,
672118664Sume			    (int)ifinfo->timer.tv_usec);
67355163Sshin	}
67455163Sshin
67555163Sshin#undef MILLION
67655163Sshin}
67755163Sshin
67855163Sshin/* timer related utility functions */
67962632Skris#define MILLION 1000000
68055163Sshin
68155163Sshin/* result = a + b */
68255163Sshinstatic void
68355163SshinTIMEVAL_ADD(struct timeval *a, struct timeval *b, struct timeval *result)
68455163Sshin{
68555163Sshin	long l;
68655163Sshin
68755163Sshin	if ((l = a->tv_usec + b->tv_usec) < MILLION) {
68855163Sshin		result->tv_usec = l;
68955163Sshin		result->tv_sec = a->tv_sec + b->tv_sec;
690118664Sume	} else {
69155163Sshin		result->tv_usec = l - MILLION;
69255163Sshin		result->tv_sec = a->tv_sec + b->tv_sec + 1;
69355163Sshin	}
69455163Sshin}
69555163Sshin
69655163Sshin/*
69755163Sshin * result = a - b
69855163Sshin * XXX: this function assumes that a >= b.
69955163Sshin */
70055163Sshinvoid
70155163SshinTIMEVAL_SUB(struct timeval *a, struct timeval *b, struct timeval *result)
70255163Sshin{
70355163Sshin	long l;
70455163Sshin
70555163Sshin	if ((l = a->tv_usec - b->tv_usec) >= 0) {
70655163Sshin		result->tv_usec = l;
70755163Sshin		result->tv_sec = a->tv_sec - b->tv_sec;
708118664Sume	} else {
70955163Sshin		result->tv_usec = MILLION + l;
71055163Sshin		result->tv_sec = a->tv_sec - b->tv_sec - 1;
71155163Sshin	}
71255163Sshin}
71355163Sshin
71455163Sshinstatic void
71555163Sshinrtsold_set_dump_file()
71655163Sshin{
71755163Sshin	do_dump = 1;
71855163Sshin}
71955163Sshin
72055163Sshinstatic void
72155163Sshinusage(char *progname)
72255163Sshin{
72366776Skris	if (progname && progname[strlen(progname) - 1] != 'd') {
72466776Skris		fprintf(stderr, "usage: rtsol [-dD] interfaces...\n");
72566776Skris		fprintf(stderr, "usage: rtsol [-dD] -a\n");
72666776Skris	} else {
72766776Skris		fprintf(stderr, "usage: rtsold [-adDfm1] interfaces...\n");
72866776Skris		fprintf(stderr, "usage: rtsold [-dDfm1] -a\n");
72966776Skris	}
73055163Sshin	exit(1);
73155163Sshin}
73255163Sshin
73355163Sshinvoid
73455163Sshin#if __STDC__
73555163Sshinwarnmsg(int priority, const char *func, const char *msg, ...)
73655163Sshin#else
73755163Sshinwarnmsg(priority, func, msg, va_alist)
73855163Sshin	int priority;
73955163Sshin	const char *func;
74055163Sshin	const char *msg;
74155163Sshin	va_dcl
74255163Sshin#endif
74355163Sshin{
74455163Sshin	va_list ap;
74555163Sshin	char buf[BUFSIZ];
74655163Sshin
74755163Sshin	va_start(ap, msg);
74855163Sshin	if (fflag) {
74955163Sshin		if (priority <= log_upto) {
75055163Sshin			(void)vfprintf(stderr, msg, ap);
75155163Sshin			(void)fprintf(stderr, "\n");
75255163Sshin		}
75355163Sshin	} else {
75455163Sshin		snprintf(buf, sizeof(buf), "<%s> %s", func, msg);
75566776Skris		msg = buf;
75666776Skris		vsyslog(priority, msg, ap);
75755163Sshin	}
75855163Sshin	va_end(ap);
75955163Sshin}
76066776Skris
76166776Skrisstatic char **
76266776Skrisautoifprobe()
76366776Skris{
76466776Skris	static char ifname[IFNAMSIZ + 1];
76566776Skris	static char *argv[2];
76666776Skris	struct ifaddrs *ifap, *ifa, *target;
76766776Skris
76866776Skris	if (getifaddrs(&ifap) != 0)
76966776Skris		return NULL;
77066776Skris
77166776Skris	target = NULL;
77266776Skris	/* find an ethernet */
77366776Skris	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
77466776Skris		if ((ifa->ifa_flags & IFF_UP) == 0)
77566776Skris			continue;
77666776Skris		if ((ifa->ifa_flags & IFF_POINTOPOINT) != 0)
77766776Skris			continue;
77866776Skris		if ((ifa->ifa_flags & IFF_LOOPBACK) != 0)
77966776Skris			continue;
78066776Skris		if ((ifa->ifa_flags & IFF_MULTICAST) == 0)
78166776Skris			continue;
78266776Skris
78366776Skris		if (ifa->ifa_addr->sa_family != AF_INET6)
78466776Skris			continue;
78566776Skris
78666776Skris		if (target && strcmp(target->ifa_name, ifa->ifa_name) == 0)
78766776Skris			continue;
78866776Skris
78966776Skris		if (!target)
79066776Skris			target = ifa;
79166776Skris		else {
79266776Skris			/* if we find multiple candidates, failure. */
79366776Skris			if (dflag > 1)
79466776Skris				warnx("multiple interfaces found");
79566776Skris			target = NULL;
79666776Skris			break;
79766776Skris		}
79866776Skris	}
79966776Skris
80066776Skris	if (target) {
80166776Skris		strncpy(ifname, target->ifa_name, sizeof(ifname) - 1);
80266776Skris		ifname[sizeof(ifname) - 1] = '\0';
80366776Skris		argv[0] = ifname;
80466776Skris		argv[1] = NULL;
80566776Skris
80666776Skris		if (dflag > 0)
80766776Skris			warnx("probing %s", argv[0]);
80866776Skris	}
80966776Skris	freeifaddrs(ifap);
81066776Skris	if (target)
81166776Skris		return argv;
81266776Skris	else
81366776Skris		return (char **)NULL;
81466776Skris}
815