rtsold.c revision 118664
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 118664 2003-08-08 16:56:01Z ume $
3255163Sshin */
3355163Sshin
3455163Sshin#include <sys/types.h>
3555163Sshin#include <sys/time.h>
3662632Skris#include <sys/socket.h>
3755163Sshin
3862632Skris#include <net/if.h>
3955163Sshin#include <net/if_dl.h>
4055163Sshin
4155163Sshin#include <netinet/in.h>
4255163Sshin#include <netinet/icmp6.h>
4355163Sshin
4455163Sshin#include <signal.h>
4555163Sshin#include <unistd.h>
4655163Sshin#include <syslog.h>
4755163Sshin#include <string.h>
4855163Sshin#include <stdlib.h>
4955163Sshin#include <stdio.h>
5055163Sshin#include <errno.h>
5155163Sshin#include <err.h>
5255163Sshin#include <stdarg.h>
5366776Skris#include <ifaddrs.h>
54118664Sume
5555163Sshin#include "rtsold.h"
5655163Sshin
5755163Sshinstruct ifinfo *iflist;
5855163Sshinstruct timeval tm_max =	{0x7fffffff, 0x7fffffff};
59118664Sumestatic int log_upto = 999;
60118664Sumestatic int fflag = 0;
61118664Sume
6266776Skrisint aflag = 0;
6366776Skrisint dflag = 0;
64118664Sume
65118661Sumechar *otherconf_script;
6655163Sshin
6755163Sshin/* protocol constatns */
6862632Skris#define MAX_RTR_SOLICITATION_DELAY	1 /* second */
6962632Skris#define RTR_SOLICITATION_INTERVAL	4 /* seconds */
7062632Skris#define MAX_RTR_SOLICITATIONS		3 /* times */
7155163Sshin
72118664Sume/*
73118664Sume * implementation dependent constants in secondes
74118664Sume * XXX: should be configurable
75118664Sume */
76118664Sume#define PROBE_INTERVAL 60
7755163Sshin
7855163Sshin/* utility macros */
7955163Sshin/* a < b */
8062632Skris#define TIMEVAL_LT(a, b) (((a).tv_sec < (b).tv_sec) ||\
8155163Sshin			  (((a).tv_sec == (b).tv_sec) && \
8255163Sshin			    ((a).tv_usec < (b).tv_usec)))
8355163Sshin
8455163Sshin/* a <= b */
8562632Skris#define TIMEVAL_LEQ(a, b) (((a).tv_sec < (b).tv_sec) ||\
8655163Sshin			   (((a).tv_sec == (b).tv_sec) &&\
87118664Sume			    ((a).tv_usec <= (b).tv_usec)))
8855163Sshin
8955163Sshin/* a == b */
9062632Skris#define TIMEVAL_EQ(a, b) (((a).tv_sec==(b).tv_sec) && ((a).tv_usec==(b).tv_usec))
9155163Sshin
92118664Sumeint main __P((int, char **));
9355163Sshin
9455163Sshin/* static variables and functions */
9555163Sshinstatic int mobile_node = 0;
9655163Sshinstatic int do_dump;
9762632Skrisstatic char *dumpfilename = "/var/run/rtsold.dump"; /* XXX: should be configurable */
9855163Sshinstatic char *pidfilename = "/var/run/rtsold.pid"; /* should be configurable */
9955163Sshin
100118664Sumestatic int ifconfig __P((char *));
10162632Skris#if 0
102118664Sumestatic int ifreconfig __P((char *));
10362632Skris#endif
104118664Sumestatic int make_packet __P((struct ifinfo *));
10555163Sshinstatic struct timeval *rtsol_check_timer __P((void));
106118664Sumestatic void TIMEVAL_ADD __P((struct timeval *, struct timeval *,
107118664Sume	struct timeval *));
108118664Sumestatic void TIMEVAL_SUB __P((struct timeval *, struct timeval *,
109118664Sume	struct timeval *));
11055163Sshin
11162632Skrisstatic void rtsold_set_dump_file __P((void));
112118664Sumestatic void usage __P((char *));
11366776Skrisstatic char **autoifprobe __P((void));
11455163Sshin
11555163Sshinint
11655163Sshinmain(argc, argv)
11755163Sshin	int argc;
118118664Sume	char **argv;
11955163Sshin{
12078064Sume	int s, rtsock, maxfd, ch;
12155163Sshin	int once = 0;
12255163Sshin	struct timeval *timeout;
12355163Sshin	struct fd_set fdset;
124118664Sume	char *argv0, *opts;
12555163Sshin
12655163Sshin	/*
12755163Sshin	 * Initialization
12855163Sshin	 */
12955163Sshin	argv0 = argv[0];
13055163Sshin
13155163Sshin	/* get option */
13255163Sshin	if (argv0 && argv0[strlen(argv0) - 1] != 'd') {
13355163Sshin		fflag = 1;
13455163Sshin		once = 1;
135118661Sume		opts = "adDO:";
13655163Sshin	} else
137118661Sume		opts = "adDfm1O:";
13855163Sshin
13955163Sshin	while ((ch = getopt(argc, argv, opts)) != -1) {
14066776Skris		switch (ch) {
14166776Skris		case 'a':
14266776Skris			aflag = 1;
14366776Skris			break;
14466776Skris		case 'd':
14566776Skris			dflag = 1;
14666776Skris			break;
14766776Skris		case 'D':
14866776Skris			dflag = 2;
14966776Skris			break;
15066776Skris		case 'f':
15166776Skris			fflag = 1;
15266776Skris			break;
15366776Skris		case 'm':
15466776Skris			mobile_node = 1;
15566776Skris			break;
15666776Skris		case '1':
15766776Skris			once = 1;
15866776Skris			break;
159118661Sume		case 'O':
160118661Sume			otherconf_script = optarg;
161118661Sume			break;
16266776Skris		default:
16366776Skris			usage(argv0);
16466776Skris			/*NOTREACHED*/
16555163Sshin		}
16655163Sshin	}
16755163Sshin	argc -= optind;
16855163Sshin	argv += optind;
16966776Skris
17066776Skris	if (aflag) {
17166776Skris		int i;
17266776Skris
17366776Skris		if (argc != 0) {
17466776Skris			usage(argv0);
17566776Skris			/*NOTREACHED*/
17666776Skris		}
17766776Skris
17866776Skris		argv = autoifprobe();
17966776Skris		if (!argv) {
18066776Skris			errx(1, "could not autoprobe interface");
18166776Skris			/*NOTREACHED*/
18266776Skris		}
18366776Skris
18466776Skris		for (i = 0; argv[i]; i++)
18566776Skris			;
18666776Skris		argc = i;
18766776Skris	}
18866776Skris	if (argc == 0) {
18955163Sshin		usage(argv0);
19066776Skris		/*NOTREACHED*/
19166776Skris	}
19255163Sshin
19355163Sshin	/* set log level */
19455163Sshin	if (dflag == 0)
19555163Sshin		log_upto = LOG_NOTICE;
19655163Sshin	if (!fflag) {
19755163Sshin		char *ident;
198118664Sume
19955163Sshin		ident = strrchr(argv0, '/');
20055163Sshin		if (!ident)
20155163Sshin			ident = argv0;
20255163Sshin		else
20355163Sshin			ident++;
20455163Sshin		openlog(ident, LOG_NDELAY|LOG_PID, LOG_DAEMON);
20555163Sshin		if (log_upto >= 0)
20655163Sshin			setlogmask(LOG_UPTO(log_upto));
20755163Sshin	}
20855163Sshin
209118661Sume	if (otherconf_script && *otherconf_script != '/') {
210118661Sume		errx(1, "configuration script (%s) must be an absolute path",
211118661Sume		    otherconf_script);
212118661Sume	}
213118661Sume
21462632Skris#ifndef HAVE_ARC4RANDOM
215118664Sume	/* random value initialization */
21655163Sshin	srandom((u_long)time(NULL));
21762632Skris#endif
21855163Sshin
21955163Sshin	/* warn if accept_rtadv is down */
22055163Sshin	if (!getinet6sysctl(IPV6CTL_ACCEPT_RTADV))
22155163Sshin		warnx("kernel is configured not to accept RAs");
22266776Skris	/* warn if forwarding is up */
22366776Skris	if (getinet6sysctl(IPV6CTL_FORWARDING))
22466776Skris		warnx("kernel is configured as a router, not a host");
22555163Sshin
22655163Sshin	/* initialization to dump internal status to a file */
22766776Skris	if (signal(SIGUSR1, (void *)rtsold_set_dump_file) < 0) {
22855163Sshin		errx(1, "failed to set signal for dump status");
22966776Skris		/*NOTREACHED*/
23066776Skris	}
23155163Sshin
23262632Skris	/*
23362632Skris	 * Open a socket for sending RS and receiving RA.
23462632Skris	 * This should be done before calling ifinit(), since the function
23562632Skris	 * uses the socket.
23662632Skris	 */
23766776Skris	if ((s = sockopen()) < 0) {
23862632Skris		errx(1, "failed to open a socket");
23966776Skris		/*NOTREACHED*/
24066776Skris	}
24178064Sume	maxfd = s;
24278064Sume	if ((rtsock = rtsock_open()) < 0) {
24378064Sume		errx(1, "failed to open a socket");
24478064Sume		/*NOTREACHED*/
24578064Sume	}
24678064Sume	if (rtsock > maxfd)
24778064Sume		maxfd = rtsock;
24862632Skris
24955163Sshin	/* configuration per interface */
25066776Skris	if (ifinit()) {
25155163Sshin		errx(1, "failed to initilizatoin interfaces");
25266776Skris		/*NOTREACHED*/
25366776Skris	}
25455163Sshin	while (argc--) {
25566776Skris		if (ifconfig(*argv)) {
25662632Skris			errx(1, "failed to initialize %s", *argv);
25766776Skris			/*NOTREACHED*/
25866776Skris		}
25955163Sshin		argv++;
26055163Sshin	}
26155163Sshin
26255163Sshin	/* setup for probing default routers */
26366776Skris	if (probe_init()) {
26455163Sshin		errx(1, "failed to setup for probing routers");
26566776Skris		/*NOTREACHED*/
26666776Skris	}
26755163Sshin
26855163Sshin	if (!fflag)
26955163Sshin		daemon(0, 0);		/* act as a daemon */
27055163Sshin
27155163Sshin	/* dump the current pid */
27255163Sshin	if (!once) {
27355163Sshin		pid_t pid = getpid();
27455163Sshin		FILE *fp;
27555163Sshin
27655163Sshin		if ((fp = fopen(pidfilename, "w")) == NULL)
277118660Sume			warnmsg(LOG_ERR, __func__,
278118664Sume			    "failed to open a pid log file(%s): %s",
279118664Sume			    pidfilename, strerror(errno));
28055163Sshin		else {
28155163Sshin			fprintf(fp, "%d\n", pid);
28255163Sshin			fclose(fp);
28355163Sshin		}
28455163Sshin	}
28555163Sshin
28655163Sshin	FD_ZERO(&fdset);
28755163Sshin	FD_SET(s, &fdset);
28878064Sume	FD_SET(rtsock, &fdset);
28955163Sshin	while (1) {		/* main loop */
29055163Sshin		int e;
29155163Sshin		struct fd_set select_fd = fdset;
29255163Sshin
29355163Sshin		if (do_dump) {	/* SIGUSR1 */
29455163Sshin			do_dump = 0;
29555163Sshin			rtsold_dump_file(dumpfilename);
29655163Sshin		}
297118664Sume
29855163Sshin		timeout = rtsol_check_timer();
29955163Sshin
30055163Sshin		if (once) {
30155163Sshin			struct ifinfo *ifi;
30255163Sshin
30355163Sshin			/* if we have no timeout, we are done (or failed) */
30455163Sshin			if (timeout == NULL)
30555163Sshin				break;
30655163Sshin
30755163Sshin			/* if all interfaces have got RA packet, we are done */
30855163Sshin			for (ifi = iflist; ifi; ifi = ifi->next) {
30955163Sshin				if (ifi->state != IFS_DOWN && ifi->racnt == 0)
31055163Sshin					break;
31155163Sshin			}
31255163Sshin			if (ifi == NULL)
31355163Sshin				break;
31455163Sshin		}
31578064Sume		e = select(maxfd + 1, &select_fd, NULL, NULL, timeout);
31678064Sume		if (e < 1) {
31755163Sshin			if (e < 0 && errno != EINTR) {
318118660Sume				warnmsg(LOG_ERR, __func__, "select: %s",
319118664Sume				    strerror(errno));
32055163Sshin			}
32155163Sshin			continue;
32255163Sshin		}
32355163Sshin
32455163Sshin		/* packet reception */
32578064Sume		if (FD_ISSET(rtsock, &select_fd))
32678064Sume			rtsock_input(rtsock);
32778064Sume		if (FD_ISSET(s, &select_fd))
32855163Sshin			rtsol_input(s);
32955163Sshin	}
33055163Sshin	/* NOTREACHED */
33155163Sshin
33255163Sshin	return 0;
33355163Sshin}
33455163Sshin
33555163Sshinstatic int
33655163Sshinifconfig(char *ifname)
33755163Sshin{
33855163Sshin	struct ifinfo *ifinfo;
33955163Sshin	struct sockaddr_dl *sdl;
34055163Sshin	int flags;
34155163Sshin
34255163Sshin	if ((sdl = if_nametosdl(ifname)) == NULL) {
343118660Sume		warnmsg(LOG_ERR, __func__,
344118664Sume		    "failed to get link layer information for %s", ifname);
34555163Sshin		return(-1);
34655163Sshin	}
34755163Sshin	if (find_ifinfo(sdl->sdl_index)) {
348118660Sume		warnmsg(LOG_ERR, __func__,
349118664Sume		    "interface %s was already configured", ifname);
35062632Skris		free(sdl);
35155163Sshin		return(-1);
35255163Sshin	}
35355163Sshin
35455163Sshin	if ((ifinfo = malloc(sizeof(*ifinfo))) == NULL) {
355118660Sume		warnmsg(LOG_ERR, __func__, "memory allocation failed");
35662632Skris		free(sdl);
35755163Sshin		return(-1);
35855163Sshin	}
35955163Sshin	memset(ifinfo, 0, sizeof(*ifinfo));
36055163Sshin	ifinfo->sdl = sdl;
36155163Sshin
36255163Sshin	strncpy(ifinfo->ifname, ifname, sizeof(ifinfo->ifname));
36355163Sshin
36455163Sshin	/* construct a router solicitation message */
36555163Sshin	if (make_packet(ifinfo))
36655163Sshin		goto bad;
36755163Sshin
36855163Sshin	/*
36955163Sshin	 * check if the interface is available.
37055163Sshin	 * also check if SIOCGIFMEDIA ioctl is OK on the interface.
37155163Sshin	 */
37255163Sshin	ifinfo->mediareqok = 1;
37355163Sshin	ifinfo->active = interface_status(ifinfo);
37455163Sshin	if (!ifinfo->mediareqok) {
37555163Sshin		/*
37655163Sshin		 * probe routers periodically even if the link status
37755163Sshin		 * does not change.
37855163Sshin		 */
37955163Sshin		ifinfo->probeinterval = PROBE_INTERVAL;
38055163Sshin	}
38155163Sshin
38255163Sshin	/* activate interface: interface_up returns 0 on success */
38355163Sshin	flags = interface_up(ifinfo->ifname);
38455163Sshin	if (flags == 0)
38555163Sshin		ifinfo->state = IFS_DELAY;
38655163Sshin	else if (flags == IFS_TENTATIVE)
38755163Sshin		ifinfo->state = IFS_TENTATIVE;
38855163Sshin	else
38955163Sshin		ifinfo->state = IFS_DOWN;
39055163Sshin
39155163Sshin	rtsol_timer_update(ifinfo);
39255163Sshin
39355163Sshin	/* link into chain */
39455163Sshin	if (iflist)
39555163Sshin		ifinfo->next = iflist;
39655163Sshin	iflist = ifinfo;
39755163Sshin
39855163Sshin	return(0);
39955163Sshin
400118664Sumebad:
40162632Skris	free(ifinfo->sdl);
40255163Sshin	free(ifinfo);
40355163Sshin	return(-1);
40455163Sshin}
40555163Sshin
40662632Skris#if 0
40762632Skrisstatic int
40862632Skrisifreconfig(char *ifname)
40962632Skris{
41062632Skris	struct ifinfo *ifi, *prev;
41162632Skris	int rv;
41262632Skris
41362632Skris	prev = NULL;
41462632Skris	for (ifi = iflist; ifi; ifi = ifi->next) {
41562632Skris		if (strncmp(ifi->ifname, ifname, sizeof(ifi->ifname)) == 0)
41662632Skris			break;
41762632Skris		prev = ifi;
41862632Skris	}
41962632Skris	prev->next = ifi->next;
42062632Skris
42162632Skris	rv = ifconfig(ifname);
42262632Skris
42362632Skris	/* reclaim it after ifconfig() in case ifname is pointer inside ifi */
42462632Skris	if (ifi->rs_data)
42562632Skris		free(ifi->rs_data);
42662632Skris	free(ifi->sdl);
42762632Skris	free(ifi);
42862632Skris	return rv;
42962632Skris}
43062632Skris#endif
43162632Skris
43255163Sshinstruct ifinfo *
43355163Sshinfind_ifinfo(int ifindex)
43455163Sshin{
43555163Sshin	struct ifinfo *ifi;
43655163Sshin
43755163Sshin	for (ifi = iflist; ifi; ifi = ifi->next)
43855163Sshin		if (ifi->sdl->sdl_index == ifindex)
43955163Sshin			return(ifi);
44055163Sshin	return(NULL);
44155163Sshin}
44255163Sshin
44355163Sshinstatic int
44455163Sshinmake_packet(struct ifinfo *ifinfo)
44555163Sshin{
446118664Sume	size_t packlen = sizeof(struct nd_router_solicit), lladdroptlen = 0;
447118664Sume	struct nd_router_solicit *rs;
44855163Sshin	char *buf;
44955163Sshin
45055163Sshin	if ((lladdroptlen = lladdropt_length(ifinfo->sdl)) == 0) {
451118660Sume		warnmsg(LOG_INFO, __func__,
452118664Sume		    "link-layer address option has null length"
453118664Sume		    " on %s. Treat as not included.", ifinfo->ifname);
45455163Sshin	}
45555163Sshin	packlen += lladdroptlen;
45655163Sshin	ifinfo->rs_datalen = packlen;
45755163Sshin
45855163Sshin	/* allocate buffer */
45955163Sshin	if ((buf = malloc(packlen)) == NULL) {
460118660Sume		warnmsg(LOG_ERR, __func__,
461118664Sume		    "memory allocation failed for %s", ifinfo->ifname);
46255163Sshin		return(-1);
46355163Sshin	}
46455163Sshin	ifinfo->rs_data = buf;
46555163Sshin
46655163Sshin	/* fill in the message */
46755163Sshin	rs = (struct nd_router_solicit *)buf;
46855163Sshin	rs->nd_rs_type = ND_ROUTER_SOLICIT;
46955163Sshin	rs->nd_rs_code = 0;
47055163Sshin	rs->nd_rs_cksum = 0;
47155163Sshin	rs->nd_rs_reserved = 0;
47255163Sshin	buf += sizeof(*rs);
47355163Sshin
47455163Sshin	/* fill in source link-layer address option */
47555163Sshin	if (lladdroptlen)
47655163Sshin		lladdropt_fill(ifinfo->sdl, (struct nd_opt_hdr *)buf);
47755163Sshin
47855163Sshin	return(0);
47955163Sshin}
48055163Sshin
48155163Sshinstatic struct timeval *
48255163Sshinrtsol_check_timer()
48355163Sshin{
48455163Sshin	static struct timeval returnval;
48555163Sshin	struct timeval now, rtsol_timer;
48655163Sshin	struct ifinfo *ifinfo;
48755163Sshin	int flags;
48855163Sshin
48955163Sshin	gettimeofday(&now, NULL);
49055163Sshin
49155163Sshin	rtsol_timer = tm_max;
49255163Sshin
49355163Sshin	for (ifinfo = iflist; ifinfo; ifinfo = ifinfo->next) {
49455163Sshin		if (TIMEVAL_LEQ(ifinfo->expire, now)) {
49555163Sshin			if (dflag > 1)
496118660Sume				warnmsg(LOG_DEBUG, __func__,
497118664Sume				    "timer expiration on %s, "
498118664Sume				    "state = %d", ifinfo->ifname,
499118664Sume				    ifinfo->state);
50055163Sshin
50166776Skris			switch (ifinfo->state) {
50255163Sshin			case IFS_DOWN:
50355163Sshin			case IFS_TENTATIVE:
50455163Sshin				/* interface_up returns 0 on success */
50555163Sshin				flags = interface_up(ifinfo->ifname);
50655163Sshin				if (flags == 0)
50755163Sshin					ifinfo->state = IFS_DELAY;
50855163Sshin				else if (flags == IFS_TENTATIVE)
50955163Sshin					ifinfo->state = IFS_TENTATIVE;
51055163Sshin				else
51155163Sshin					ifinfo->state = IFS_DOWN;
51255163Sshin				break;
51355163Sshin			case IFS_IDLE:
51455163Sshin			{
51555163Sshin				int oldstatus = ifinfo->active;
51655163Sshin				int probe = 0;
51755163Sshin
518118664Sume				ifinfo->active = interface_status(ifinfo);
51955163Sshin
52055163Sshin				if (oldstatus != ifinfo->active) {
521118660Sume					warnmsg(LOG_DEBUG, __func__,
522118664Sume					    "%s status is changed"
523118664Sume					    " from %d to %d",
524118664Sume					    ifinfo->ifname,
525118664Sume					    oldstatus, ifinfo->active);
52655163Sshin					probe = 1;
52755163Sshin					ifinfo->state = IFS_DELAY;
528118664Sume				} else if (ifinfo->probeinterval &&
529118664Sume				    (ifinfo->probetimer -=
530118664Sume				    ifinfo->timer.tv_sec) <= 0) {
53155163Sshin					/* probe timer expired */
53255163Sshin					ifinfo->probetimer =
533118664Sume					    ifinfo->probeinterval;
53455163Sshin					probe = 1;
53555163Sshin					ifinfo->state = IFS_PROBE;
53655163Sshin				}
53755163Sshin
538118661Sume				/*
539118661Sume				 * If we need a probe, clear the previous
540118661Sume				 * status wrt the "other" configuration.
541118661Sume				 */
542118661Sume				if (probe)
543118661Sume					ifinfo->otherconfig = 0;
544118661Sume
54555163Sshin				if (probe && mobile_node)
54655163Sshin					defrouter_probe(ifinfo->sdl->sdl_index);
54755163Sshin				break;
54855163Sshin			}
54955163Sshin			case IFS_DELAY:
55055163Sshin				ifinfo->state = IFS_PROBE;
55155163Sshin				sendpacket(ifinfo);
55255163Sshin				break;
55355163Sshin			case IFS_PROBE:
55455163Sshin				if (ifinfo->probes < MAX_RTR_SOLICITATIONS)
55555163Sshin					sendpacket(ifinfo);
55655163Sshin				else {
557118660Sume					warnmsg(LOG_INFO, __func__,
558118664Sume					    "No answer after sending %d RSs",
559118664Sume					    ifinfo->probes);
56055163Sshin					ifinfo->probes = 0;
56155163Sshin					ifinfo->state = IFS_IDLE;
56255163Sshin				}
56355163Sshin				break;
56455163Sshin			}
56555163Sshin			rtsol_timer_update(ifinfo);
56655163Sshin		}
56755163Sshin
56855163Sshin		if (TIMEVAL_LT(ifinfo->expire, rtsol_timer))
56955163Sshin			rtsol_timer = ifinfo->expire;
57055163Sshin	}
57155163Sshin
57255163Sshin	if (TIMEVAL_EQ(rtsol_timer, tm_max)) {
573118660Sume		warnmsg(LOG_DEBUG, __func__, "there is no timer");
57455163Sshin		return(NULL);
575118664Sume	} else if (TIMEVAL_LT(rtsol_timer, now))
57655163Sshin		/* this may occur when the interval is too small */
57755163Sshin		returnval.tv_sec = returnval.tv_usec = 0;
57855163Sshin	else
57955163Sshin		TIMEVAL_SUB(&rtsol_timer, &now, &returnval);
58055163Sshin
58155163Sshin	if (dflag > 1)
582118660Sume		warnmsg(LOG_DEBUG, __func__, "New timer is %ld:%08ld",
583118664Sume		    (long)returnval.tv_sec, (long)returnval.tv_usec);
58455163Sshin
58555163Sshin	return(&returnval);
58655163Sshin}
58755163Sshin
58855163Sshinvoid
58955163Sshinrtsol_timer_update(struct ifinfo *ifinfo)
59055163Sshin{
59162632Skris#define MILLION 1000000
59262632Skris#define DADRETRY 10		/* XXX: adhoc */
59355163Sshin	long interval;
59455163Sshin	struct timeval now;
59555163Sshin
59655163Sshin	bzero(&ifinfo->timer, sizeof(ifinfo->timer));
59755163Sshin
59855163Sshin	switch (ifinfo->state) {
59955163Sshin	case IFS_DOWN:
60055163Sshin	case IFS_TENTATIVE:
60155163Sshin		if (++ifinfo->dadcount > DADRETRY) {
60255163Sshin			ifinfo->dadcount = 0;
60355163Sshin			ifinfo->timer.tv_sec = PROBE_INTERVAL;
604118664Sume		} else
60555163Sshin			ifinfo->timer.tv_sec = 1;
60655163Sshin		break;
60755163Sshin	case IFS_IDLE:
60855163Sshin		if (mobile_node) {
609118664Sume			/* XXX should be configurable */
61055163Sshin			ifinfo->timer.tv_sec = 3;
61155163Sshin		}
61255163Sshin		else
61355163Sshin			ifinfo->timer = tm_max;	/* stop timer(valid?) */
61455163Sshin		break;
61555163Sshin	case IFS_DELAY:
61662632Skris#ifndef HAVE_ARC4RANDOM
61755163Sshin		interval = random() % (MAX_RTR_SOLICITATION_DELAY * MILLION);
61862632Skris#else
61962632Skris		interval = arc4random() % (MAX_RTR_SOLICITATION_DELAY * MILLION);
62062632Skris#endif
62155163Sshin		ifinfo->timer.tv_sec = interval / MILLION;
62255163Sshin		ifinfo->timer.tv_usec = interval % MILLION;
62355163Sshin		break;
62455163Sshin	case IFS_PROBE:
62578064Sume		if (ifinfo->probes < MAX_RTR_SOLICITATIONS)
62678064Sume			ifinfo->timer.tv_sec = RTR_SOLICITATION_INTERVAL;
62778064Sume		else {
62878064Sume			/*
62978064Sume			 * After sending MAX_RTR_SOLICITATIONS solicitations,
63078064Sume			 * we're just waiting for possible replies; there
63178064Sume			 * will be no more solicatation.  Thus, we change
63278064Sume			 * the timer value to MAX_RTR_SOLICITATION_DELAY based
63378064Sume			 * on RFC 2461, Section 6.3.7.
63478064Sume			 */
63578064Sume			ifinfo->timer.tv_sec = MAX_RTR_SOLICITATION_DELAY;
63678064Sume		}
63755163Sshin		break;
63855163Sshin	default:
639118660Sume		warnmsg(LOG_ERR, __func__,
640118664Sume		    "illegal interface state(%d) on %s",
641118664Sume		    ifinfo->state, ifinfo->ifname);
64255163Sshin		return;
64355163Sshin	}
64455163Sshin
64555163Sshin	/* reset the timer */
64655163Sshin	if (TIMEVAL_EQ(ifinfo->timer, tm_max)) {
64755163Sshin		ifinfo->expire = tm_max;
648118660Sume		warnmsg(LOG_DEBUG, __func__,
649118664Sume		    "stop timer for %s", ifinfo->ifname);
650118664Sume	} else {
65155163Sshin		gettimeofday(&now, NULL);
65255163Sshin		TIMEVAL_ADD(&now, &ifinfo->timer, &ifinfo->expire);
65355163Sshin
65455163Sshin		if (dflag > 1)
655118660Sume			warnmsg(LOG_DEBUG, __func__,
656118664Sume			    "set timer for %s to %d:%d", ifinfo->ifname,
657118664Sume			    (int)ifinfo->timer.tv_sec,
658118664Sume			    (int)ifinfo->timer.tv_usec);
65955163Sshin	}
66055163Sshin
66155163Sshin#undef MILLION
66255163Sshin}
66355163Sshin
66455163Sshin/* timer related utility functions */
66562632Skris#define MILLION 1000000
66655163Sshin
66755163Sshin/* result = a + b */
66855163Sshinstatic void
66955163SshinTIMEVAL_ADD(struct timeval *a, struct timeval *b, struct timeval *result)
67055163Sshin{
67155163Sshin	long l;
67255163Sshin
67355163Sshin	if ((l = a->tv_usec + b->tv_usec) < MILLION) {
67455163Sshin		result->tv_usec = l;
67555163Sshin		result->tv_sec = a->tv_sec + b->tv_sec;
676118664Sume	} else {
67755163Sshin		result->tv_usec = l - MILLION;
67855163Sshin		result->tv_sec = a->tv_sec + b->tv_sec + 1;
67955163Sshin	}
68055163Sshin}
68155163Sshin
68255163Sshin/*
68355163Sshin * result = a - b
68455163Sshin * XXX: this function assumes that a >= b.
68555163Sshin */
68655163Sshinvoid
68755163SshinTIMEVAL_SUB(struct timeval *a, struct timeval *b, struct timeval *result)
68855163Sshin{
68955163Sshin	long l;
69055163Sshin
69155163Sshin	if ((l = a->tv_usec - b->tv_usec) >= 0) {
69255163Sshin		result->tv_usec = l;
69355163Sshin		result->tv_sec = a->tv_sec - b->tv_sec;
694118664Sume	} else {
69555163Sshin		result->tv_usec = MILLION + l;
69655163Sshin		result->tv_sec = a->tv_sec - b->tv_sec - 1;
69755163Sshin	}
69855163Sshin}
69955163Sshin
70055163Sshinstatic void
70155163Sshinrtsold_set_dump_file()
70255163Sshin{
70355163Sshin	do_dump = 1;
70455163Sshin}
70555163Sshin
70655163Sshinstatic void
70755163Sshinusage(char *progname)
70855163Sshin{
70966776Skris	if (progname && progname[strlen(progname) - 1] != 'd') {
71066776Skris		fprintf(stderr, "usage: rtsol [-dD] interfaces...\n");
71166776Skris		fprintf(stderr, "usage: rtsol [-dD] -a\n");
71266776Skris	} else {
71366776Skris		fprintf(stderr, "usage: rtsold [-adDfm1] interfaces...\n");
71466776Skris		fprintf(stderr, "usage: rtsold [-dDfm1] -a\n");
71566776Skris	}
71655163Sshin	exit(1);
71755163Sshin}
71855163Sshin
71955163Sshinvoid
72055163Sshin#if __STDC__
72155163Sshinwarnmsg(int priority, const char *func, const char *msg, ...)
72255163Sshin#else
72355163Sshinwarnmsg(priority, func, msg, va_alist)
72455163Sshin	int priority;
72555163Sshin	const char *func;
72655163Sshin	const char *msg;
72755163Sshin	va_dcl
72855163Sshin#endif
72955163Sshin{
73055163Sshin	va_list ap;
73155163Sshin	char buf[BUFSIZ];
73255163Sshin
73355163Sshin	va_start(ap, msg);
73455163Sshin	if (fflag) {
73555163Sshin		if (priority <= log_upto) {
73655163Sshin			(void)vfprintf(stderr, msg, ap);
73755163Sshin			(void)fprintf(stderr, "\n");
73855163Sshin		}
73955163Sshin	} else {
74055163Sshin		snprintf(buf, sizeof(buf), "<%s> %s", func, msg);
74166776Skris		msg = buf;
74266776Skris		vsyslog(priority, msg, ap);
74355163Sshin	}
74455163Sshin	va_end(ap);
74555163Sshin}
74666776Skris
74766776Skrisstatic char **
74866776Skrisautoifprobe()
74966776Skris{
75066776Skris	static char ifname[IFNAMSIZ + 1];
75166776Skris	static char *argv[2];
75266776Skris	struct ifaddrs *ifap, *ifa, *target;
75366776Skris
75466776Skris	if (getifaddrs(&ifap) != 0)
75566776Skris		return NULL;
75666776Skris
75766776Skris	target = NULL;
75866776Skris	/* find an ethernet */
75966776Skris	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
76066776Skris		if ((ifa->ifa_flags & IFF_UP) == 0)
76166776Skris			continue;
76266776Skris		if ((ifa->ifa_flags & IFF_POINTOPOINT) != 0)
76366776Skris			continue;
76466776Skris		if ((ifa->ifa_flags & IFF_LOOPBACK) != 0)
76566776Skris			continue;
76666776Skris		if ((ifa->ifa_flags & IFF_MULTICAST) == 0)
76766776Skris			continue;
76866776Skris
76966776Skris		if (ifa->ifa_addr->sa_family != AF_INET6)
77066776Skris			continue;
77166776Skris
77266776Skris		if (target && strcmp(target->ifa_name, ifa->ifa_name) == 0)
77366776Skris			continue;
77466776Skris
77566776Skris		if (!target)
77666776Skris			target = ifa;
77766776Skris		else {
77866776Skris			/* if we find multiple candidates, failure. */
77966776Skris			if (dflag > 1)
78066776Skris				warnx("multiple interfaces found");
78166776Skris			target = NULL;
78266776Skris			break;
78366776Skris		}
78466776Skris	}
78566776Skris
78666776Skris	if (target) {
78766776Skris		strncpy(ifname, target->ifa_name, sizeof(ifname) - 1);
78866776Skris		ifname[sizeof(ifname) - 1] = '\0';
78966776Skris		argv[0] = ifname;
79066776Skris		argv[1] = NULL;
79166776Skris
79266776Skris		if (dflag > 0)
79366776Skris			warnx("probing %s", argv[0]);
79466776Skris	}
79566776Skris	freeifaddrs(ifap);
79666776Skris	if (target)
79766776Skris		return argv;
79866776Skris	else
79966776Skris		return (char **)NULL;
80066776Skris}
801