rtsold.c revision 119026
1119026Sume/*	$KAME: rtsold.c,v 1.67 2003/05/17 18:16:15 itojun 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 119026 2003-08-17 11:11:32Z 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>
55118916Sume#ifdef HAVE_POLL_H
56118916Sume#include <poll.h>
57118916Sume#endif
58118664Sume
5955163Sshin#include "rtsold.h"
6055163Sshin
6155163Sshinstruct ifinfo *iflist;
6255163Sshinstruct timeval tm_max =	{0x7fffffff, 0x7fffffff};
63118664Sumestatic int log_upto = 999;
64118664Sumestatic int fflag = 0;
65118664Sume
6666776Skrisint aflag = 0;
6766776Skrisint dflag = 0;
68118664Sume
69118661Sumechar *otherconf_script;
7055163Sshin
7155163Sshin/* protocol constatns */
7262632Skris#define MAX_RTR_SOLICITATION_DELAY	1 /* second */
7362632Skris#define RTR_SOLICITATION_INTERVAL	4 /* seconds */
7462632Skris#define MAX_RTR_SOLICITATIONS		3 /* times */
7555163Sshin
76118664Sume/*
77118910Sume * implementation dependent constants in seconds
78118664Sume * XXX: should be configurable
79118664Sume */
80118664Sume#define PROBE_INTERVAL 60
8155163Sshin
8255163Sshin/* utility macros */
8355163Sshin/* a < b */
8462632Skris#define TIMEVAL_LT(a, b) (((a).tv_sec < (b).tv_sec) ||\
8555163Sshin			  (((a).tv_sec == (b).tv_sec) && \
8655163Sshin			    ((a).tv_usec < (b).tv_usec)))
8755163Sshin
8855163Sshin/* a <= b */
8962632Skris#define TIMEVAL_LEQ(a, b) (((a).tv_sec < (b).tv_sec) ||\
9055163Sshin			   (((a).tv_sec == (b).tv_sec) &&\
91118664Sume			    ((a).tv_usec <= (b).tv_usec)))
9255163Sshin
9355163Sshin/* a == b */
9462632Skris#define TIMEVAL_EQ(a, b) (((a).tv_sec==(b).tv_sec) && ((a).tv_usec==(b).tv_usec))
9555163Sshin
96118664Sumeint main __P((int, char **));
9755163Sshin
9855163Sshin/* static variables and functions */
9955163Sshinstatic int mobile_node = 0;
10055163Sshinstatic int do_dump;
10162632Skrisstatic char *dumpfilename = "/var/run/rtsold.dump"; /* XXX: should be configurable */
10255163Sshinstatic char *pidfilename = "/var/run/rtsold.pid"; /* should be configurable */
10355163Sshin
10462632Skris#if 0
105118664Sumestatic int ifreconfig __P((char *));
10662632Skris#endif
107118664Sumestatic int make_packet __P((struct ifinfo *));
10855163Sshinstatic struct timeval *rtsol_check_timer __P((void));
109118664Sumestatic void TIMEVAL_ADD __P((struct timeval *, struct timeval *,
110118664Sume	struct timeval *));
111118664Sumestatic void TIMEVAL_SUB __P((struct timeval *, struct timeval *,
112118664Sume	struct timeval *));
11355163Sshin
114118910Sumestatic void rtsold_set_dump_file __P((int));
115118664Sumestatic void usage __P((char *));
11655163Sshin
11755163Sshinint
11855163Sshinmain(argc, argv)
11955163Sshin	int argc;
120118664Sume	char **argv;
12155163Sshin{
122118909Sume	int s, ch, once = 0;
12355163Sshin	struct timeval *timeout;
124118664Sume	char *argv0, *opts;
125118916Sume#ifdef HAVE_POLL_H
126118916Sume	struct pollfd set[2];
127118916Sume#else
128118909Sume	fd_set *fdsetp, *selectfdp;
129118909Sume	int fdmasks;
130118909Sume	int maxfd;
131118916Sume#endif
132118909Sume	int rtsock;
13355163Sshin
13455163Sshin	/*
13555163Sshin	 * Initialization
13655163Sshin	 */
13755163Sshin	argv0 = argv[0];
13855163Sshin
13955163Sshin	/* get option */
14055163Sshin	if (argv0 && argv0[strlen(argv0) - 1] != 'd') {
14155163Sshin		fflag = 1;
14255163Sshin		once = 1;
143118661Sume		opts = "adDO:";
14455163Sshin	} else
145118661Sume		opts = "adDfm1O:";
14655163Sshin
14755163Sshin	while ((ch = getopt(argc, argv, opts)) != -1) {
14866776Skris		switch (ch) {
14966776Skris		case 'a':
15066776Skris			aflag = 1;
15166776Skris			break;
15266776Skris		case 'd':
15366776Skris			dflag = 1;
15466776Skris			break;
15566776Skris		case 'D':
15666776Skris			dflag = 2;
15766776Skris			break;
15866776Skris		case 'f':
15966776Skris			fflag = 1;
16066776Skris			break;
16166776Skris		case 'm':
16266776Skris			mobile_node = 1;
16366776Skris			break;
16466776Skris		case '1':
16566776Skris			once = 1;
16666776Skris			break;
167118661Sume		case 'O':
168118661Sume			otherconf_script = optarg;
169118661Sume			break;
17066776Skris		default:
17166776Skris			usage(argv0);
17266776Skris			/*NOTREACHED*/
17355163Sshin		}
17455163Sshin	}
17555163Sshin	argc -= optind;
17655163Sshin	argv += optind;
17766776Skris
178119026Sume	if ((!aflag && argc == 0) || (aflag && argc != 0)) {
17955163Sshin		usage(argv0);
18066776Skris		/*NOTREACHED*/
18166776Skris	}
18255163Sshin
18355163Sshin	/* set log level */
18455163Sshin	if (dflag == 0)
18555163Sshin		log_upto = LOG_NOTICE;
18655163Sshin	if (!fflag) {
18755163Sshin		char *ident;
188118664Sume
18955163Sshin		ident = strrchr(argv0, '/');
19055163Sshin		if (!ident)
19155163Sshin			ident = argv0;
19255163Sshin		else
19355163Sshin			ident++;
19455163Sshin		openlog(ident, LOG_NDELAY|LOG_PID, LOG_DAEMON);
19555163Sshin		if (log_upto >= 0)
19655163Sshin			setlogmask(LOG_UPTO(log_upto));
19755163Sshin	}
19855163Sshin
199118661Sume	if (otherconf_script && *otherconf_script != '/') {
200118661Sume		errx(1, "configuration script (%s) must be an absolute path",
201118661Sume		    otherconf_script);
202118661Sume	}
203118661Sume
20462632Skris#ifndef HAVE_ARC4RANDOM
205118664Sume	/* random value initialization */
20655163Sshin	srandom((u_long)time(NULL));
20762632Skris#endif
20855163Sshin
20955163Sshin	/* warn if accept_rtadv is down */
21055163Sshin	if (!getinet6sysctl(IPV6CTL_ACCEPT_RTADV))
21155163Sshin		warnx("kernel is configured not to accept RAs");
21266776Skris	/* warn if forwarding is up */
21366776Skris	if (getinet6sysctl(IPV6CTL_FORWARDING))
21466776Skris		warnx("kernel is configured as a router, not a host");
21555163Sshin
21655163Sshin	/* initialization to dump internal status to a file */
217118910Sume	signal(SIGUSR1, rtsold_set_dump_file);
21855163Sshin
219118914Sume	if (!fflag)
220118914Sume		daemon(0, 0);		/* act as a daemon */
221118914Sume
22262632Skris	/*
22362632Skris	 * Open a socket for sending RS and receiving RA.
22462632Skris	 * This should be done before calling ifinit(), since the function
22562632Skris	 * uses the socket.
22662632Skris	 */
22766776Skris	if ((s = sockopen()) < 0) {
228118914Sume		warnmsg(LOG_ERR, __func__, "failed to open a socket");
229118914Sume		exit(1);
23066776Skris		/*NOTREACHED*/
23166776Skris	}
232118916Sume#ifdef HAVE_POLL_H
233118916Sume	set[0].fd = s;
234118916Sume	set[0].events = POLLIN;
235118916Sume#else
23678064Sume	maxfd = s;
237118916Sume#endif
238118916Sume
239118916Sume#ifdef HAVE_POLL_H
240118916Sume	set[1].fd = -1;
241118916Sume#endif
242118916Sume
24378064Sume	if ((rtsock = rtsock_open()) < 0) {
244118914Sume		warnmsg(LOG_ERR, __func__, "failed to open a socket");
245118914Sume		exit(1);
24678064Sume		/*NOTREACHED*/
24778064Sume	}
248118916Sume#ifdef HAVE_POLL_H
249118916Sume	set[1].fd = rtsock;
250118916Sume	set[1].events = POLLIN;
251118916Sume#else
25278064Sume	if (rtsock > maxfd)
25378064Sume		maxfd = rtsock;
254118916Sume#endif
25562632Skris
256118916Sume#ifndef HAVE_POLL_H
257118909Sume	fdmasks = howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask);
258118909Sume	if ((fdsetp = malloc(fdmasks)) == NULL) {
259118909Sume		err(1, "malloc");
260118909Sume		/*NOTREACHED*/
261118909Sume	}
262118909Sume	if ((selectfdp = malloc(fdmasks)) == NULL) {
263118909Sume		err(1, "malloc");
264118909Sume		/*NOTREACHED*/
265118909Sume	}
266118916Sume#endif
267118909Sume
26855163Sshin	/* configuration per interface */
26966776Skris	if (ifinit()) {
270118914Sume		warnmsg(LOG_ERR, __func__,
271118914Sume		    "failed to initilizatoin interfaces");
272118914Sume		exit(1);
27366776Skris		/*NOTREACHED*/
27466776Skris	}
275119026Sume	if (aflag)
276119026Sume		argv = autoifprobe();
277119026Sume	while (argv && *argv) {
27866776Skris		if (ifconfig(*argv)) {
279118914Sume			warnmsg(LOG_ERR, __func__,
280118914Sume			    "failed to initialize %s", *argv);
281118914Sume			exit(1);
28266776Skris			/*NOTREACHED*/
28366776Skris		}
28455163Sshin		argv++;
28555163Sshin	}
28655163Sshin
28755163Sshin	/* setup for probing default routers */
28866776Skris	if (probe_init()) {
289118914Sume		warnmsg(LOG_ERR, __func__,
290118914Sume		    "failed to setup for probing routers");
291118914Sume		exit(1);
29266776Skris		/*NOTREACHED*/
29366776Skris	}
29455163Sshin
29555163Sshin	/* dump the current pid */
29655163Sshin	if (!once) {
29755163Sshin		pid_t pid = getpid();
29855163Sshin		FILE *fp;
29955163Sshin
30055163Sshin		if ((fp = fopen(pidfilename, "w")) == NULL)
301118660Sume			warnmsg(LOG_ERR, __func__,
302118664Sume			    "failed to open a pid log file(%s): %s",
303118664Sume			    pidfilename, strerror(errno));
30455163Sshin		else {
30555163Sshin			fprintf(fp, "%d\n", pid);
30655163Sshin			fclose(fp);
30755163Sshin		}
30855163Sshin	}
30955163Sshin
310118916Sume#ifndef HAVE_POLL_H
311118909Sume	memset(fdsetp, 0, fdmasks);
312118909Sume	FD_SET(s, fdsetp);
313118909Sume	FD_SET(rtsock, fdsetp);
314118916Sume#endif
31555163Sshin	while (1) {		/* main loop */
31655163Sshin		int e;
31755163Sshin
318118916Sume#ifndef HAVE_POLL_H
319118909Sume		memcpy(selectfdp, fdsetp, fdmasks);
320118916Sume#endif
321118909Sume
32255163Sshin		if (do_dump) {	/* SIGUSR1 */
32355163Sshin			do_dump = 0;
32455163Sshin			rtsold_dump_file(dumpfilename);
32555163Sshin		}
326118664Sume
32755163Sshin		timeout = rtsol_check_timer();
32855163Sshin
32955163Sshin		if (once) {
33055163Sshin			struct ifinfo *ifi;
33155163Sshin
33255163Sshin			/* if we have no timeout, we are done (or failed) */
33355163Sshin			if (timeout == NULL)
33455163Sshin				break;
33555163Sshin
33655163Sshin			/* if all interfaces have got RA packet, we are done */
33755163Sshin			for (ifi = iflist; ifi; ifi = ifi->next) {
33855163Sshin				if (ifi->state != IFS_DOWN && ifi->racnt == 0)
33955163Sshin					break;
34055163Sshin			}
34155163Sshin			if (ifi == NULL)
34255163Sshin				break;
34355163Sshin		}
344118916Sume#ifdef HAVE_POLL_H
345118916Sume		e = poll(set, 2, timeout ? (timeout->tv_sec * 1000 + timeout->tv_usec / 1000) : INFTIM);
346118916Sume#else
347118909Sume		e = select(maxfd + 1, selectfdp, NULL, NULL, timeout);
348118916Sume#endif
34978064Sume		if (e < 1) {
35055163Sshin			if (e < 0 && errno != EINTR) {
351118660Sume				warnmsg(LOG_ERR, __func__, "select: %s",
352118664Sume				    strerror(errno));
35355163Sshin			}
35455163Sshin			continue;
35555163Sshin		}
35655163Sshin
35755163Sshin		/* packet reception */
358118916Sume#ifdef HAVE_POLL_H
359118916Sume		if (set[1].revents & POLLIN)
360118916Sume#else
361118909Sume		if (FD_ISSET(rtsock, selectfdp))
362118916Sume#endif
36378064Sume			rtsock_input(rtsock);
364118916Sume#ifdef HAVE_POLL_H
365118916Sume		if (set[0].revents & POLLIN)
366118916Sume#else
367118909Sume		if (FD_ISSET(s, selectfdp))
368118916Sume#endif
36955163Sshin			rtsol_input(s);
37055163Sshin	}
37155163Sshin	/* NOTREACHED */
37255163Sshin
37355163Sshin	return 0;
37455163Sshin}
37555163Sshin
376119026Sumeint
37755163Sshinifconfig(char *ifname)
37855163Sshin{
37955163Sshin	struct ifinfo *ifinfo;
38055163Sshin	struct sockaddr_dl *sdl;
38155163Sshin	int flags;
38255163Sshin
38355163Sshin	if ((sdl = if_nametosdl(ifname)) == NULL) {
384118660Sume		warnmsg(LOG_ERR, __func__,
385118664Sume		    "failed to get link layer information for %s", ifname);
38655163Sshin		return(-1);
38755163Sshin	}
38855163Sshin	if (find_ifinfo(sdl->sdl_index)) {
389118660Sume		warnmsg(LOG_ERR, __func__,
390118664Sume		    "interface %s was already configured", ifname);
39162632Skris		free(sdl);
39255163Sshin		return(-1);
39355163Sshin	}
39455163Sshin
39555163Sshin	if ((ifinfo = malloc(sizeof(*ifinfo))) == NULL) {
396118660Sume		warnmsg(LOG_ERR, __func__, "memory allocation failed");
39762632Skris		free(sdl);
39855163Sshin		return(-1);
39955163Sshin	}
40055163Sshin	memset(ifinfo, 0, sizeof(*ifinfo));
40155163Sshin	ifinfo->sdl = sdl;
40255163Sshin
403118786Sume	strlcpy(ifinfo->ifname, ifname, sizeof(ifinfo->ifname));
40455163Sshin
40555163Sshin	/* construct a router solicitation message */
40655163Sshin	if (make_packet(ifinfo))
40755163Sshin		goto bad;
40855163Sshin
409119026Sume	/* set link ID of this interface. */
410119026Sume#ifdef HAVE_SCOPELIB
411119026Sume	if (inet_zoneid(AF_INET6, 2, ifname, &ifinfo->linkid))
412119026Sume		goto bad;
413119026Sume#else
414119026Sume	/* XXX: assume interface IDs as link IDs */
415119026Sume	ifinfo->linkid = ifinfo->sdl->sdl_index;
416119026Sume#endif
417119026Sume
41855163Sshin	/*
41955163Sshin	 * check if the interface is available.
42055163Sshin	 * also check if SIOCGIFMEDIA ioctl is OK on the interface.
42155163Sshin	 */
42255163Sshin	ifinfo->mediareqok = 1;
42355163Sshin	ifinfo->active = interface_status(ifinfo);
42455163Sshin	if (!ifinfo->mediareqok) {
42555163Sshin		/*
42655163Sshin		 * probe routers periodically even if the link status
42755163Sshin		 * does not change.
42855163Sshin		 */
42955163Sshin		ifinfo->probeinterval = PROBE_INTERVAL;
43055163Sshin	}
43155163Sshin
43255163Sshin	/* activate interface: interface_up returns 0 on success */
43355163Sshin	flags = interface_up(ifinfo->ifname);
43455163Sshin	if (flags == 0)
43555163Sshin		ifinfo->state = IFS_DELAY;
43655163Sshin	else if (flags == IFS_TENTATIVE)
43755163Sshin		ifinfo->state = IFS_TENTATIVE;
43855163Sshin	else
43955163Sshin		ifinfo->state = IFS_DOWN;
44055163Sshin
44155163Sshin	rtsol_timer_update(ifinfo);
44255163Sshin
44355163Sshin	/* link into chain */
44455163Sshin	if (iflist)
44555163Sshin		ifinfo->next = iflist;
44655163Sshin	iflist = ifinfo;
44755163Sshin
44855163Sshin	return(0);
44955163Sshin
450118664Sumebad:
45162632Skris	free(ifinfo->sdl);
45255163Sshin	free(ifinfo);
45355163Sshin	return(-1);
45455163Sshin}
45555163Sshin
456119026Sumevoid
457119026Sumeiflist_init()
458119026Sume{
459119026Sume	struct ifinfo *ifi, *next;
460119026Sume
461119026Sume	for (ifi = iflist; ifi; ifi = next) {
462119026Sume		next = ifi->next;
463119026Sume		if (ifi->sdl)
464119026Sume			free(ifi->sdl);
465119026Sume		if (ifi->rs_data)
466119026Sume			free(ifi->rs_data);
467119026Sume		free(ifi);
468119026Sume		iflist = NULL;
469119026Sume	}
470119026Sume}
471119026Sume
47262632Skris#if 0
47362632Skrisstatic int
47462632Skrisifreconfig(char *ifname)
47562632Skris{
47662632Skris	struct ifinfo *ifi, *prev;
47762632Skris	int rv;
47862632Skris
47962632Skris	prev = NULL;
48062632Skris	for (ifi = iflist; ifi; ifi = ifi->next) {
48162632Skris		if (strncmp(ifi->ifname, ifname, sizeof(ifi->ifname)) == 0)
48262632Skris			break;
48362632Skris		prev = ifi;
48462632Skris	}
48562632Skris	prev->next = ifi->next;
48662632Skris
48762632Skris	rv = ifconfig(ifname);
48862632Skris
48962632Skris	/* reclaim it after ifconfig() in case ifname is pointer inside ifi */
49062632Skris	if (ifi->rs_data)
49162632Skris		free(ifi->rs_data);
49262632Skris	free(ifi->sdl);
49362632Skris	free(ifi);
49462632Skris	return rv;
49562632Skris}
49662632Skris#endif
49762632Skris
49855163Sshinstruct ifinfo *
49955163Sshinfind_ifinfo(int ifindex)
50055163Sshin{
50155163Sshin	struct ifinfo *ifi;
50255163Sshin
50355163Sshin	for (ifi = iflist; ifi; ifi = ifi->next)
50455163Sshin		if (ifi->sdl->sdl_index == ifindex)
50555163Sshin			return(ifi);
50655163Sshin	return(NULL);
50755163Sshin}
50855163Sshin
50955163Sshinstatic int
51055163Sshinmake_packet(struct ifinfo *ifinfo)
51155163Sshin{
512118664Sume	size_t packlen = sizeof(struct nd_router_solicit), lladdroptlen = 0;
513118664Sume	struct nd_router_solicit *rs;
51455163Sshin	char *buf;
51555163Sshin
51655163Sshin	if ((lladdroptlen = lladdropt_length(ifinfo->sdl)) == 0) {
517118660Sume		warnmsg(LOG_INFO, __func__,
518118664Sume		    "link-layer address option has null length"
519118664Sume		    " on %s. Treat as not included.", ifinfo->ifname);
52055163Sshin	}
52155163Sshin	packlen += lladdroptlen;
52255163Sshin	ifinfo->rs_datalen = packlen;
52355163Sshin
52455163Sshin	/* allocate buffer */
52555163Sshin	if ((buf = malloc(packlen)) == NULL) {
526118660Sume		warnmsg(LOG_ERR, __func__,
527118664Sume		    "memory allocation failed for %s", ifinfo->ifname);
52855163Sshin		return(-1);
52955163Sshin	}
53055163Sshin	ifinfo->rs_data = buf;
53155163Sshin
53255163Sshin	/* fill in the message */
53355163Sshin	rs = (struct nd_router_solicit *)buf;
53455163Sshin	rs->nd_rs_type = ND_ROUTER_SOLICIT;
53555163Sshin	rs->nd_rs_code = 0;
53655163Sshin	rs->nd_rs_cksum = 0;
53755163Sshin	rs->nd_rs_reserved = 0;
53855163Sshin	buf += sizeof(*rs);
53955163Sshin
54055163Sshin	/* fill in source link-layer address option */
54155163Sshin	if (lladdroptlen)
54255163Sshin		lladdropt_fill(ifinfo->sdl, (struct nd_opt_hdr *)buf);
54355163Sshin
54455163Sshin	return(0);
54555163Sshin}
54655163Sshin
54755163Sshinstatic struct timeval *
54855163Sshinrtsol_check_timer()
54955163Sshin{
55055163Sshin	static struct timeval returnval;
55155163Sshin	struct timeval now, rtsol_timer;
55255163Sshin	struct ifinfo *ifinfo;
55355163Sshin	int flags;
55455163Sshin
55555163Sshin	gettimeofday(&now, NULL);
55655163Sshin
55755163Sshin	rtsol_timer = tm_max;
55855163Sshin
55955163Sshin	for (ifinfo = iflist; ifinfo; ifinfo = ifinfo->next) {
56055163Sshin		if (TIMEVAL_LEQ(ifinfo->expire, now)) {
56155163Sshin			if (dflag > 1)
562118660Sume				warnmsg(LOG_DEBUG, __func__,
563118664Sume				    "timer expiration on %s, "
564118664Sume				    "state = %d", ifinfo->ifname,
565118664Sume				    ifinfo->state);
56655163Sshin
56766776Skris			switch (ifinfo->state) {
56855163Sshin			case IFS_DOWN:
56955163Sshin			case IFS_TENTATIVE:
57055163Sshin				/* interface_up returns 0 on success */
57155163Sshin				flags = interface_up(ifinfo->ifname);
57255163Sshin				if (flags == 0)
57355163Sshin					ifinfo->state = IFS_DELAY;
57455163Sshin				else if (flags == IFS_TENTATIVE)
57555163Sshin					ifinfo->state = IFS_TENTATIVE;
57655163Sshin				else
57755163Sshin					ifinfo->state = IFS_DOWN;
57855163Sshin				break;
57955163Sshin			case IFS_IDLE:
58055163Sshin			{
58155163Sshin				int oldstatus = ifinfo->active;
58255163Sshin				int probe = 0;
58355163Sshin
584118664Sume				ifinfo->active = interface_status(ifinfo);
58555163Sshin
58655163Sshin				if (oldstatus != ifinfo->active) {
587118660Sume					warnmsg(LOG_DEBUG, __func__,
588118664Sume					    "%s status is changed"
589118664Sume					    " from %d to %d",
590118664Sume					    ifinfo->ifname,
591118664Sume					    oldstatus, ifinfo->active);
59255163Sshin					probe = 1;
59355163Sshin					ifinfo->state = IFS_DELAY;
594118664Sume				} else if (ifinfo->probeinterval &&
595118664Sume				    (ifinfo->probetimer -=
596118664Sume				    ifinfo->timer.tv_sec) <= 0) {
59755163Sshin					/* probe timer expired */
59855163Sshin					ifinfo->probetimer =
599118664Sume					    ifinfo->probeinterval;
60055163Sshin					probe = 1;
60155163Sshin					ifinfo->state = IFS_PROBE;
60255163Sshin				}
60355163Sshin
604118661Sume				/*
605118661Sume				 * If we need a probe, clear the previous
606118661Sume				 * status wrt the "other" configuration.
607118661Sume				 */
608118661Sume				if (probe)
609118661Sume					ifinfo->otherconfig = 0;
610118661Sume
61155163Sshin				if (probe && mobile_node)
612119026Sume					defrouter_probe(ifinfo);
61355163Sshin				break;
61455163Sshin			}
61555163Sshin			case IFS_DELAY:
61655163Sshin				ifinfo->state = IFS_PROBE;
61755163Sshin				sendpacket(ifinfo);
61855163Sshin				break;
61955163Sshin			case IFS_PROBE:
62055163Sshin				if (ifinfo->probes < MAX_RTR_SOLICITATIONS)
62155163Sshin					sendpacket(ifinfo);
62255163Sshin				else {
623118660Sume					warnmsg(LOG_INFO, __func__,
624118664Sume					    "No answer after sending %d RSs",
625118664Sume					    ifinfo->probes);
62655163Sshin					ifinfo->probes = 0;
62755163Sshin					ifinfo->state = IFS_IDLE;
62855163Sshin				}
62955163Sshin				break;
63055163Sshin			}
63155163Sshin			rtsol_timer_update(ifinfo);
63255163Sshin		}
63355163Sshin
63455163Sshin		if (TIMEVAL_LT(ifinfo->expire, rtsol_timer))
63555163Sshin			rtsol_timer = ifinfo->expire;
63655163Sshin	}
63755163Sshin
63855163Sshin	if (TIMEVAL_EQ(rtsol_timer, tm_max)) {
639118660Sume		warnmsg(LOG_DEBUG, __func__, "there is no timer");
64055163Sshin		return(NULL);
641118664Sume	} else if (TIMEVAL_LT(rtsol_timer, now))
64255163Sshin		/* this may occur when the interval is too small */
64355163Sshin		returnval.tv_sec = returnval.tv_usec = 0;
64455163Sshin	else
64555163Sshin		TIMEVAL_SUB(&rtsol_timer, &now, &returnval);
64655163Sshin
64755163Sshin	if (dflag > 1)
648118660Sume		warnmsg(LOG_DEBUG, __func__, "New timer is %ld:%08ld",
649118664Sume		    (long)returnval.tv_sec, (long)returnval.tv_usec);
65055163Sshin
65155163Sshin	return(&returnval);
65255163Sshin}
65355163Sshin
65455163Sshinvoid
65555163Sshinrtsol_timer_update(struct ifinfo *ifinfo)
65655163Sshin{
65762632Skris#define MILLION 1000000
65862632Skris#define DADRETRY 10		/* XXX: adhoc */
65955163Sshin	long interval;
66055163Sshin	struct timeval now;
66155163Sshin
66255163Sshin	bzero(&ifinfo->timer, sizeof(ifinfo->timer));
66355163Sshin
66455163Sshin	switch (ifinfo->state) {
66555163Sshin	case IFS_DOWN:
66655163Sshin	case IFS_TENTATIVE:
66755163Sshin		if (++ifinfo->dadcount > DADRETRY) {
66855163Sshin			ifinfo->dadcount = 0;
66955163Sshin			ifinfo->timer.tv_sec = PROBE_INTERVAL;
670118664Sume		} else
67155163Sshin			ifinfo->timer.tv_sec = 1;
67255163Sshin		break;
67355163Sshin	case IFS_IDLE:
67455163Sshin		if (mobile_node) {
675118664Sume			/* XXX should be configurable */
67655163Sshin			ifinfo->timer.tv_sec = 3;
67755163Sshin		}
67855163Sshin		else
67955163Sshin			ifinfo->timer = tm_max;	/* stop timer(valid?) */
68055163Sshin		break;
68155163Sshin	case IFS_DELAY:
68262632Skris#ifndef HAVE_ARC4RANDOM
68355163Sshin		interval = random() % (MAX_RTR_SOLICITATION_DELAY * MILLION);
68462632Skris#else
68562632Skris		interval = arc4random() % (MAX_RTR_SOLICITATION_DELAY * MILLION);
68662632Skris#endif
68755163Sshin		ifinfo->timer.tv_sec = interval / MILLION;
68855163Sshin		ifinfo->timer.tv_usec = interval % MILLION;
68955163Sshin		break;
69055163Sshin	case IFS_PROBE:
69178064Sume		if (ifinfo->probes < MAX_RTR_SOLICITATIONS)
69278064Sume			ifinfo->timer.tv_sec = RTR_SOLICITATION_INTERVAL;
69378064Sume		else {
69478064Sume			/*
69578064Sume			 * After sending MAX_RTR_SOLICITATIONS solicitations,
69678064Sume			 * we're just waiting for possible replies; there
69778064Sume			 * will be no more solicatation.  Thus, we change
69878064Sume			 * the timer value to MAX_RTR_SOLICITATION_DELAY based
69978064Sume			 * on RFC 2461, Section 6.3.7.
70078064Sume			 */
70178064Sume			ifinfo->timer.tv_sec = MAX_RTR_SOLICITATION_DELAY;
70278064Sume		}
70355163Sshin		break;
70455163Sshin	default:
705118660Sume		warnmsg(LOG_ERR, __func__,
706118664Sume		    "illegal interface state(%d) on %s",
707118664Sume		    ifinfo->state, ifinfo->ifname);
70855163Sshin		return;
70955163Sshin	}
71055163Sshin
71155163Sshin	/* reset the timer */
71255163Sshin	if (TIMEVAL_EQ(ifinfo->timer, tm_max)) {
71355163Sshin		ifinfo->expire = tm_max;
714118660Sume		warnmsg(LOG_DEBUG, __func__,
715118664Sume		    "stop timer for %s", ifinfo->ifname);
716118664Sume	} else {
71755163Sshin		gettimeofday(&now, NULL);
71855163Sshin		TIMEVAL_ADD(&now, &ifinfo->timer, &ifinfo->expire);
71955163Sshin
72055163Sshin		if (dflag > 1)
721118660Sume			warnmsg(LOG_DEBUG, __func__,
722118664Sume			    "set timer for %s to %d:%d", ifinfo->ifname,
723118664Sume			    (int)ifinfo->timer.tv_sec,
724118664Sume			    (int)ifinfo->timer.tv_usec);
72555163Sshin	}
72655163Sshin
72755163Sshin#undef MILLION
72855163Sshin}
72955163Sshin
73055163Sshin/* timer related utility functions */
73162632Skris#define MILLION 1000000
73255163Sshin
73355163Sshin/* result = a + b */
73455163Sshinstatic void
73555163SshinTIMEVAL_ADD(struct timeval *a, struct timeval *b, struct timeval *result)
73655163Sshin{
73755163Sshin	long l;
73855163Sshin
73955163Sshin	if ((l = a->tv_usec + b->tv_usec) < MILLION) {
74055163Sshin		result->tv_usec = l;
74155163Sshin		result->tv_sec = a->tv_sec + b->tv_sec;
742118664Sume	} else {
74355163Sshin		result->tv_usec = l - MILLION;
74455163Sshin		result->tv_sec = a->tv_sec + b->tv_sec + 1;
74555163Sshin	}
74655163Sshin}
74755163Sshin
74855163Sshin/*
74955163Sshin * result = a - b
75055163Sshin * XXX: this function assumes that a >= b.
75155163Sshin */
75255163Sshinvoid
75355163SshinTIMEVAL_SUB(struct timeval *a, struct timeval *b, struct timeval *result)
75455163Sshin{
75555163Sshin	long l;
75655163Sshin
75755163Sshin	if ((l = a->tv_usec - b->tv_usec) >= 0) {
75855163Sshin		result->tv_usec = l;
75955163Sshin		result->tv_sec = a->tv_sec - b->tv_sec;
760118664Sume	} else {
76155163Sshin		result->tv_usec = MILLION + l;
76255163Sshin		result->tv_sec = a->tv_sec - b->tv_sec - 1;
76355163Sshin	}
76455163Sshin}
76555163Sshin
76655163Sshinstatic void
767118910Sumertsold_set_dump_file(sig)
768118910Sume	int sig;
76955163Sshin{
77055163Sshin	do_dump = 1;
77155163Sshin}
77255163Sshin
77355163Sshinstatic void
77455163Sshinusage(char *progname)
77555163Sshin{
77666776Skris	if (progname && progname[strlen(progname) - 1] != 'd') {
77766776Skris		fprintf(stderr, "usage: rtsol [-dD] interfaces...\n");
77866776Skris		fprintf(stderr, "usage: rtsol [-dD] -a\n");
77966776Skris	} else {
78066776Skris		fprintf(stderr, "usage: rtsold [-adDfm1] interfaces...\n");
78166776Skris		fprintf(stderr, "usage: rtsold [-dDfm1] -a\n");
78266776Skris	}
78355163Sshin	exit(1);
78455163Sshin}
78555163Sshin
78655163Sshinvoid
78755163Sshin#if __STDC__
78855163Sshinwarnmsg(int priority, const char *func, const char *msg, ...)
78955163Sshin#else
79055163Sshinwarnmsg(priority, func, msg, va_alist)
79155163Sshin	int priority;
79255163Sshin	const char *func;
79355163Sshin	const char *msg;
79455163Sshin	va_dcl
79555163Sshin#endif
79655163Sshin{
79755163Sshin	va_list ap;
79855163Sshin	char buf[BUFSIZ];
79955163Sshin
80055163Sshin	va_start(ap, msg);
80155163Sshin	if (fflag) {
80255163Sshin		if (priority <= log_upto) {
80355163Sshin			(void)vfprintf(stderr, msg, ap);
80455163Sshin			(void)fprintf(stderr, "\n");
80555163Sshin		}
80655163Sshin	} else {
80755163Sshin		snprintf(buf, sizeof(buf), "<%s> %s", func, msg);
80866776Skris		msg = buf;
80966776Skris		vsyslog(priority, msg, ap);
81055163Sshin	}
81155163Sshin	va_end(ap);
81255163Sshin}
81366776Skris
814119026Sume/*
815119026Sume * return a list of interfaces which is suitable to sending an RS.
816119026Sume */
817119026Sumechar **
81866776Skrisautoifprobe()
81966776Skris{
820119026Sume	static char **argv = NULL;
821119026Sume	static int n = 0;
822119026Sume	char **a;
823119026Sume	int i, found;
82466776Skris	struct ifaddrs *ifap, *ifa, *target;
82566776Skris
826119026Sume	/* initialize */
827119026Sume	while (n--)
828119026Sume		free(argv[n]);
829119026Sume	if (argv) {
830119026Sume		free(argv);
831119026Sume		argv = NULL;
832119026Sume	}
833119026Sume	n = 0;
834119026Sume
83566776Skris	if (getifaddrs(&ifap) != 0)
83666776Skris		return NULL;
83766776Skris
83866776Skris	target = NULL;
83966776Skris	/* find an ethernet */
84066776Skris	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
84166776Skris		if ((ifa->ifa_flags & IFF_UP) == 0)
84266776Skris			continue;
84366776Skris		if ((ifa->ifa_flags & IFF_POINTOPOINT) != 0)
84466776Skris			continue;
84566776Skris		if ((ifa->ifa_flags & IFF_LOOPBACK) != 0)
84666776Skris			continue;
84766776Skris		if ((ifa->ifa_flags & IFF_MULTICAST) == 0)
84866776Skris			continue;
84966776Skris
85066776Skris		if (ifa->ifa_addr->sa_family != AF_INET6)
85166776Skris			continue;
85266776Skris
853119026Sume		found = 0;
854119026Sume		for (i = 0; i < n; i++) {
855119026Sume			if (strcmp(argv[i], ifa->ifa_name) == 0) {
856119026Sume				found++;
857119026Sume				break;
858119026Sume			}
859119026Sume		}
860119026Sume		if (found)
86166776Skris			continue;
86266776Skris
863119026Sume		/* if we find multiple candidates, just warn. */
864119026Sume		if (n != 0 && dflag > 1)
865119026Sume			warnx("multiple interfaces found");
866119026Sume
867119026Sume		a = (char **)realloc(argv, (n + 1) * sizeof(char **));
868119026Sume		if (a == NULL)
869119026Sume			err(1, "realloc");
870119026Sume		argv = a;
871119026Sume		argv[n] = strdup(ifa->ifa_name);
872119026Sume		if (!argv[n])
873119026Sume			err(1, "malloc");
874119026Sume		n++;
875119026Sume		argv[n] = NULL;
87666776Skris	}
87766776Skris
878119026Sume	if (n) {
879119026Sume		a = (char **)realloc(argv, (n + 1) * sizeof(char **));
880119026Sume		if (a == NULL)
881119026Sume			err(1, "realloc");
882119026Sume		argv = a;
883119026Sume		argv[n] = NULL;
88466776Skris
885119026Sume		if (dflag > 0) {
886119026Sume			for (i = 0; i < n; i++)
887119026Sume				warnx("probing %s", argv[i]);
888119026Sume		}
88966776Skris	}
89066776Skris	freeifaddrs(ifap);
891119026Sume	return argv;
89266776Skris}
893