rtsold.c revision 124524
1212904Sdim/*	$KAME: rtsold.c,v 1.67 2003/05/17 18:16:15 itojun Exp $	*/
2193326Sed
3193326Sed/*
4193326Sed * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
5193326Sed * All rights reserved.
6193326Sed *
7193326Sed * Redistribution and use in source and binary forms, with or without
8193326Sed * modification, are permitted provided that the following conditions
9193326Sed * are met:
10193326Sed * 1. Redistributions of source code must retain the above copyright
11193326Sed *    notice, this list of conditions and the following disclaimer.
12193326Sed * 2. Redistributions in binary form must reproduce the above copyright
13193326Sed *    notice, this list of conditions and the following disclaimer in the
14193326Sed *    documentation and/or other materials provided with the distribution.
15193326Sed * 3. Neither the name of the project nor the names of its contributors
16193326Sed *    may be used to endorse or promote products derived from this software
17193326Sed *    without specific prior written permission.
18193326Sed *
19193326Sed * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20193326Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21193326Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22193326Sed * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23212904Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24193326Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25193326Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26193326Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27193326Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28193326Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29193326Sed * SUCH DAMAGE.
30193326Sed *
31193326Sed * $FreeBSD: head/usr.sbin/rtsold/rtsold.c 124524 2004-01-14 17:16:19Z ume $
32193326Sed */
33193326Sed
34193326Sed#include <sys/types.h>
35193326Sed#include <sys/time.h>
36212904Sdim#include <sys/socket.h>
37212904Sdim#include <sys/param.h>
38212904Sdim
39193326Sed#include <net/if.h>
40193326Sed#include <net/if_dl.h>
41193326Sed
42206084Srdivacky#include <netinet/in.h>
43249423Sdim#include <netinet/icmp6.h>
44193326Sed
45249423Sdim#include <signal.h>
46249423Sdim#include <unistd.h>
47193326Sed#include <syslog.h>
48193326Sed#include <string.h>
49206084Srdivacky#include <stdlib.h>
50249423Sdim#include <stdio.h>
51193326Sed#include <errno.h>
52249423Sdim#include <err.h>
53193326Sed#include <stdarg.h>
54193326Sed#include <ifaddrs.h>
55206084Srdivacky#ifdef HAVE_POLL_H
56249423Sdim#include <poll.h>
57193326Sed#endif
58249423Sdim
59249423Sdim#include "rtsold.h"
60193326Sed
61193326Sedstruct ifinfo *iflist;
62206084Srdivackystruct timeval tm_max =	{0x7fffffff, 0x7fffffff};
63249423Sdimstatic int log_upto = 999;
64193326Sedstatic int fflag = 0;
65249423Sdim
66193326Sedint aflag = 0;
67193326Sedint dflag = 0;
68206084Srdivacky
69249423Sdimchar *otherconf_script;
70193326Sed
71249423Sdim/* protocol constatns */
72249423Sdim#define MAX_RTR_SOLICITATION_DELAY	1 /* second */
73193326Sed#define RTR_SOLICITATION_INTERVAL	4 /* seconds */
74193326Sed#define MAX_RTR_SOLICITATIONS		3 /* times */
75206084Srdivacky
76249423Sdim/*
77193326Sed * implementation dependent constants in seconds
78249423Sdim * XXX: should be configurable
79193326Sed */
80193326Sed#define PROBE_INTERVAL 60
81206084Srdivacky
82249423Sdimint main __P((int, char **));
83193326Sed
84249423Sdim/* static variables and functions */
85249423Sdimstatic int mobile_node = 0;
86193326Sedstatic int do_dump;
87193326Sedstatic char *dumpfilename = "/var/run/rtsold.dump"; /* XXX: should be configurable */
88206084Srdivackystatic char *pidfilename = "/var/run/rtsold.pid"; /* should be configurable */
89249423Sdim
90193326Sed#if 0
91249423Sdimstatic int ifreconfig __P((char *));
92193326Sed#endif
93193326Sedstatic int make_packet __P((struct ifinfo *));
94206084Srdivackystatic struct timeval *rtsol_check_timer __P((void));
95249423Sdim
96193326Sedstatic void rtsold_set_dump_file __P((int));
97249423Sdimstatic void usage __P((char *));
98249423Sdim
99193326Sedint
100193326Sedmain(int argc, char **argv)
101206084Srdivacky{
102249423Sdim	int s, ch, once = 0;
103193326Sed	struct timeval *timeout;
104249423Sdim	char *argv0, *opts;
105193326Sed#ifdef HAVE_POLL_H
106193326Sed	struct pollfd set[2];
107206084Srdivacky#else
108249423Sdim	fd_set *fdsetp, *selectfdp;
109193326Sed	int fdmasks;
110249423Sdim	int maxfd;
111193326Sed#endif
112193326Sed	int rtsock;
113206084Srdivacky
114249423Sdim	/*
115193326Sed	 * Initialization
116249423Sdim	 */
117193326Sed	argv0 = argv[0];
118193326Sed
119206084Srdivacky	/* get option */
120249423Sdim	if (argv0 && argv0[strlen(argv0) - 1] != 'd') {
121193326Sed		fflag = 1;
122249423Sdim		once = 1;
123193326Sed		opts = "adDO:";
124193326Sed	} else
125206084Srdivacky		opts = "adDfm1O:";
126249423Sdim
127193326Sed	while ((ch = getopt(argc, argv, opts)) != -1) {
128249423Sdim		switch (ch) {
129193326Sed		case 'a':
130193326Sed			aflag = 1;
131206084Srdivacky			break;
132249423Sdim		case 'd':
133193326Sed			dflag = 1;
134249423Sdim			break;
135193326Sed		case 'D':
136193326Sed			dflag = 2;
137206084Srdivacky			break;
138249423Sdim		case 'f':
139193326Sed			fflag = 1;
140249423Sdim			break;
141193326Sed		case 'm':
142193326Sed			mobile_node = 1;
143206084Srdivacky			break;
144249423Sdim		case '1':
145193326Sed			once = 1;
146249423Sdim			break;
147193326Sed		case 'O':
148193326Sed			otherconf_script = optarg;
149206084Srdivacky			break;
150249423Sdim		default:
151193326Sed			usage(argv0);
152249423Sdim			/*NOTREACHED*/
153193326Sed		}
154193326Sed	}
155206084Srdivacky	argc -= optind;
156249423Sdim	argv += optind;
157193326Sed
158249423Sdim	if ((!aflag && argc == 0) || (aflag && argc != 0)) {
159193326Sed		usage(argv0);
160193326Sed		/*NOTREACHED*/
161206084Srdivacky	}
162249423Sdim
163193326Sed	/* set log level */
164249423Sdim	if (dflag == 0)
165193326Sed		log_upto = LOG_NOTICE;
166193326Sed	if (!fflag) {
167206084Srdivacky		char *ident;
168249423Sdim
169193326Sed		ident = strrchr(argv0, '/');
170249423Sdim		if (!ident)
171193326Sed			ident = argv0;
172193326Sed		else
173206084Srdivacky			ident++;
174249423Sdim		openlog(ident, LOG_NDELAY|LOG_PID, LOG_DAEMON);
175193326Sed		if (log_upto >= 0)
176249423Sdim			setlogmask(LOG_UPTO(log_upto));
177193326Sed	}
178193326Sed
179206084Srdivacky	if (otherconf_script && *otherconf_script != '/') {
180249423Sdim		errx(1, "configuration script (%s) must be an absolute path",
181193326Sed		    otherconf_script);
182249423Sdim	}
183193326Sed
184193326Sed#ifndef HAVE_ARC4RANDOM
185206084Srdivacky	/* random value initialization */
186249423Sdim	srandom((u_long)time(NULL));
187193326Sed#endif
188249423Sdim
189193326Sed	/* warn if accept_rtadv is down */
190193326Sed	if (!getinet6sysctl(IPV6CTL_ACCEPT_RTADV))
191206084Srdivacky		warnx("kernel is configured not to accept RAs");
192249423Sdim	/* warn if forwarding is up */
193193326Sed	if (getinet6sysctl(IPV6CTL_FORWARDING))
194249423Sdim		warnx("kernel is configured as a router, not a host");
195193326Sed
196193326Sed	/* initialization to dump internal status to a file */
197206084Srdivacky	signal(SIGUSR1, rtsold_set_dump_file);
198249423Sdim
199193326Sed	if (!fflag)
200249423Sdim		daemon(0, 0);		/* act as a daemon */
201193326Sed
202193326Sed	/*
203206084Srdivacky	 * Open a socket for sending RS and receiving RA.
204249423Sdim	 * This should be done before calling ifinit(), since the function
205193326Sed	 * uses the socket.
206249423Sdim	 */
207193326Sed	if ((s = sockopen()) < 0) {
208193326Sed		warnmsg(LOG_ERR, __func__, "failed to open a socket");
209206084Srdivacky		exit(1);
210249423Sdim		/*NOTREACHED*/
211193326Sed	}
212249423Sdim#ifdef HAVE_POLL_H
213193326Sed	set[0].fd = s;
214193326Sed	set[0].events = POLLIN;
215206084Srdivacky#else
216249423Sdim	maxfd = s;
217193326Sed#endif
218249423Sdim
219193326Sed#ifdef HAVE_POLL_H
220193326Sed	set[1].fd = -1;
221206084Srdivacky#endif
222249423Sdim
223193326Sed	if ((rtsock = rtsock_open()) < 0) {
224249423Sdim		warnmsg(LOG_ERR, __func__, "failed to open a socket");
225193326Sed		exit(1);
226193326Sed		/*NOTREACHED*/
227206084Srdivacky	}
228249423Sdim#ifdef HAVE_POLL_H
229193326Sed	set[1].fd = rtsock;
230249423Sdim	set[1].events = POLLIN;
231193326Sed#else
232193326Sed	if (rtsock > maxfd)
233206084Srdivacky		maxfd = rtsock;
234249423Sdim#endif
235193326Sed
236249423Sdim#ifndef HAVE_POLL_H
237193326Sed	fdmasks = howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask);
238193326Sed	if ((fdsetp = malloc(fdmasks)) == NULL) {
239206084Srdivacky		err(1, "malloc");
240249423Sdim		/*NOTREACHED*/
241193326Sed	}
242249423Sdim	if ((selectfdp = malloc(fdmasks)) == NULL) {
243193326Sed		err(1, "malloc");
244193326Sed		/*NOTREACHED*/
245206084Srdivacky	}
246249423Sdim#endif
247193326Sed
248263508Sdim	/* configuration per interface */
249263508Sdim	if (ifinit()) {
250193326Sed		warnmsg(LOG_ERR, __func__,
251193326Sed		    "failed to initilizatoin interfaces");
252206084Srdivacky		exit(1);
253249423Sdim		/*NOTREACHED*/
254193326Sed	}
255263508Sdim	if (aflag)
256263508Sdim		argv = autoifprobe();
257193326Sed	while (argv && *argv) {
258193326Sed		if (ifconfig(*argv)) {
259206084Srdivacky			warnmsg(LOG_ERR, __func__,
260249423Sdim			    "failed to initialize %s", *argv);
261193326Sed			exit(1);
262249423Sdim			/*NOTREACHED*/
263193326Sed		}
264193326Sed		argv++;
265206084Srdivacky	}
266249423Sdim
267193326Sed	/* setup for probing default routers */
268249423Sdim	if (probe_init()) {
269193326Sed		warnmsg(LOG_ERR, __func__,
270193326Sed		    "failed to setup for probing routers");
271206084Srdivacky		exit(1);
272249423Sdim		/*NOTREACHED*/
273193326Sed	}
274249423Sdim
275193326Sed	/* dump the current pid */
276193326Sed	if (!once) {
277206084Srdivacky		pid_t pid = getpid();
278249423Sdim		FILE *fp;
279193326Sed
280249423Sdim		if ((fp = fopen(pidfilename, "w")) == NULL)
281193326Sed			warnmsg(LOG_ERR, __func__,
282193326Sed			    "failed to open a pid log file(%s): %s",
283206084Srdivacky			    pidfilename, strerror(errno));
284249423Sdim		else {
285193326Sed			fprintf(fp, "%d\n", pid);
286249423Sdim			fclose(fp);
287193326Sed		}
288193326Sed	}
289206084Srdivacky
290249423Sdim#ifndef HAVE_POLL_H
291193326Sed	memset(fdsetp, 0, fdmasks);
292263508Sdim	FD_SET(s, fdsetp);
293263508Sdim	FD_SET(rtsock, fdsetp);
294193326Sed#endif
295193326Sed	while (1) {		/* main loop */
296206084Srdivacky		int e;
297249423Sdim
298193326Sed#ifndef HAVE_POLL_H
299263508Sdim		memcpy(selectfdp, fdsetp, fdmasks);
300263508Sdim#endif
301193326Sed
302193326Sed		if (do_dump) {	/* SIGUSR1 */
303206084Srdivacky			do_dump = 0;
304249423Sdim			rtsold_dump_file(dumpfilename);
305193326Sed		}
306249423Sdim
307193326Sed		timeout = rtsol_check_timer();
308193326Sed
309206084Srdivacky		if (once) {
310249423Sdim			struct ifinfo *ifi;
311193326Sed
312249423Sdim			/* if we have no timeout, we are done (or failed) */
313193326Sed			if (timeout == NULL)
314193326Sed				break;
315206084Srdivacky
316249423Sdim			/* if all interfaces have got RA packet, we are done */
317193326Sed			for (ifi = iflist; ifi; ifi = ifi->next) {
318249423Sdim				if (ifi->state != IFS_DOWN && ifi->racnt == 0)
319193326Sed					break;
320193326Sed			}
321206084Srdivacky			if (ifi == NULL)
322249423Sdim				break;
323193326Sed		}
324249423Sdim#ifdef HAVE_POLL_H
325193326Sed		e = poll(set, 2, timeout ? (timeout->tv_sec * 1000 + timeout->tv_usec / 1000) : INFTIM);
326193326Sed#else
327206084Srdivacky		e = select(maxfd + 1, selectfdp, NULL, NULL, timeout);
328249423Sdim#endif
329226633Sdim		if (e < 1) {
330249423Sdim			if (e < 0 && errno != EINTR) {
331226633Sdim				warnmsg(LOG_ERR, __func__, "select: %s",
332226633Sdim				    strerror(errno));
333226633Sdim			}
334249423Sdim			continue;
335193326Sed		}
336249423Sdim
337193326Sed		/* packet reception */
338193326Sed#ifdef HAVE_POLL_H
339206084Srdivacky		if (set[1].revents & POLLIN)
340249423Sdim#else
341193326Sed		if (FD_ISSET(rtsock, selectfdp))
342249423Sdim#endif
343193326Sed			rtsock_input(rtsock);
344193326Sed#ifdef HAVE_POLL_H
345206084Srdivacky		if (set[0].revents & POLLIN)
346249423Sdim#else
347193326Sed		if (FD_ISSET(s, selectfdp))
348249423Sdim#endif
349193326Sed			rtsol_input(s);
350193326Sed	}
351206084Srdivacky	/* NOTREACHED */
352249423Sdim
353193326Sed	return 0;
354249423Sdim}
355193326Sed
356193326Sedint
357206084Srdivackyifconfig(char *ifname)
358249423Sdim{
359193326Sed	struct ifinfo *ifinfo;
360249423Sdim	struct sockaddr_dl *sdl;
361193326Sed	int flags;
362193326Sed
363206084Srdivacky	if ((sdl = if_nametosdl(ifname)) == NULL) {
364249423Sdim		warnmsg(LOG_ERR, __func__,
365226633Sdim		    "failed to get link layer information for %s", ifname);
366249423Sdim		return(-1);
367226633Sdim	}
368226633Sdim	if (find_ifinfo(sdl->sdl_index)) {
369226633Sdim		warnmsg(LOG_ERR, __func__,
370249423Sdim		    "interface %s was already configured", ifname);
371193326Sed		free(sdl);
372249423Sdim		return(-1);
373193326Sed	}
374193326Sed
375206084Srdivacky	if ((ifinfo = malloc(sizeof(*ifinfo))) == NULL) {
376249423Sdim		warnmsg(LOG_ERR, __func__, "memory allocation failed");
377193326Sed		free(sdl);
378249423Sdim		return(-1);
379193326Sed	}
380193326Sed	memset(ifinfo, 0, sizeof(*ifinfo));
381206084Srdivacky	ifinfo->sdl = sdl;
382249423Sdim
383193326Sed	strlcpy(ifinfo->ifname, ifname, sizeof(ifinfo->ifname));
384249423Sdim
385193326Sed	/* construct a router solicitation message */
386193326Sed	if (make_packet(ifinfo))
387206084Srdivacky		goto bad;
388249423Sdim
389193326Sed	/* set link ID of this interface. */
390249423Sdim#ifdef HAVE_SCOPELIB
391193326Sed	if (inet_zoneid(AF_INET6, 2, ifname, &ifinfo->linkid))
392193326Sed		goto bad;
393206084Srdivacky#else
394249423Sdim	/* XXX: assume interface IDs as link IDs */
395193326Sed	ifinfo->linkid = ifinfo->sdl->sdl_index;
396249423Sdim#endif
397193326Sed
398193326Sed	/*
399206084Srdivacky	 * check if the interface is available.
400249423Sdim	 * also check if SIOCGIFMEDIA ioctl is OK on the interface.
401193326Sed	 */
402249423Sdim	ifinfo->mediareqok = 1;
403193326Sed	ifinfo->active = interface_status(ifinfo);
404193326Sed	if (!ifinfo->mediareqok) {
405206084Srdivacky		/*
406249423Sdim		 * probe routers periodically even if the link status
407193326Sed		 * does not change.
408249423Sdim		 */
409249423Sdim		ifinfo->probeinterval = PROBE_INTERVAL;
410193326Sed	}
411193326Sed
412206084Srdivacky	/* activate interface: interface_up returns 0 on success */
413249423Sdim	flags = interface_up(ifinfo->ifname);
414193326Sed	if (flags == 0)
415249423Sdim		ifinfo->state = IFS_DELAY;
416249423Sdim	else if (flags == IFS_TENTATIVE)
417193326Sed		ifinfo->state = IFS_TENTATIVE;
418193326Sed	else
419206084Srdivacky		ifinfo->state = IFS_DOWN;
420249423Sdim
421193326Sed	rtsol_timer_update(ifinfo);
422249423Sdim
423249423Sdim	/* link into chain */
424193326Sed	if (iflist)
425193326Sed		ifinfo->next = iflist;
426206084Srdivacky	iflist = ifinfo;
427249423Sdim
428193326Sed	return(0);
429249423Sdim
430193326Sedbad:
431193326Sed	free(ifinfo->sdl);
432206084Srdivacky	free(ifinfo);
433249423Sdim	return(-1);
434193326Sed}
435249423Sdim
436193326Sedvoid
437193326Sediflist_init(void)
438206084Srdivacky{
439249423Sdim	struct ifinfo *ifi, *next;
440193326Sed
441249423Sdim	for (ifi = iflist; ifi; ifi = next) {
442193326Sed		next = ifi->next;
443193326Sed		if (ifi->sdl)
444206084Srdivacky			free(ifi->sdl);
445249423Sdim		if (ifi->rs_data)
446193326Sed			free(ifi->rs_data);
447249423Sdim		free(ifi);
448193326Sed		iflist = NULL;
449193326Sed	}
450206084Srdivacky}
451249423Sdim
452193326Sed#if 0
453249423Sdimstatic int
454193326Sedifreconfig(char *ifname)
455193326Sed{
456206084Srdivacky	struct ifinfo *ifi, *prev;
457249423Sdim	int rv;
458193326Sed
459249423Sdim	prev = NULL;
460193326Sed	for (ifi = iflist; ifi; ifi = ifi->next) {
461193326Sed		if (strncmp(ifi->ifname, ifname, sizeof(ifi->ifname)) == 0)
462206084Srdivacky			break;
463249423Sdim		prev = ifi;
464193326Sed	}
465249423Sdim	prev->next = ifi->next;
466193326Sed
467193326Sed	rv = ifconfig(ifname);
468206084Srdivacky
469249423Sdim	/* reclaim it after ifconfig() in case ifname is pointer inside ifi */
470193326Sed	if (ifi->rs_data)
471226633Sdim		free(ifi->rs_data);
472249423Sdim	free(ifi->sdl);
473226633Sdim	free(ifi);
474249423Sdim	return rv;
475249423Sdim}
476193326Sed#endif
477193326Sed
478193326Sedstruct ifinfo *
479193326Sedfind_ifinfo(int ifindex)
480206084Srdivacky{
481249423Sdim	struct ifinfo *ifi;
482193326Sed
483249423Sdim	for (ifi = iflist; ifi; ifi = ifi->next)
484249423Sdim		if (ifi->sdl->sdl_index == ifindex)
485193326Sed			return(ifi);
486193326Sed	return(NULL);
487206084Srdivacky}
488249423Sdim
489193326Sedstatic int
490223017Sdimmake_packet(struct ifinfo *ifinfo)
491249423Sdim{
492223017Sdim	size_t packlen = sizeof(struct nd_router_solicit), lladdroptlen = 0;
493249423Sdim	struct nd_router_solicit *rs;
494193326Sed	char *buf;
495193326Sed
496206084Srdivacky	if ((lladdroptlen = lladdropt_length(ifinfo->sdl)) == 0) {
497249423Sdim		warnmsg(LOG_INFO, __func__,
498193326Sed		    "link-layer address option has null length"
499226633Sdim		    " on %s. Treat as not included.", ifinfo->ifname);
500249423Sdim	}
501226633Sdim	packlen += lladdroptlen;
502249423Sdim	ifinfo->rs_datalen = packlen;
503249423Sdim
504193326Sed	/* allocate buffer */
505193326Sed	if ((buf = malloc(packlen)) == NULL) {
506206084Srdivacky		warnmsg(LOG_ERR, __func__,
507249423Sdim		    "memory allocation failed for %s", ifinfo->ifname);
508193326Sed		return(-1);
509226633Sdim	}
510249423Sdim	ifinfo->rs_data = buf;
511226633Sdim
512249423Sdim	/* fill in the message */
513249423Sdim	rs = (struct nd_router_solicit *)buf;
514193326Sed	rs->nd_rs_type = ND_ROUTER_SOLICIT;
515193326Sed	rs->nd_rs_code = 0;
516206084Srdivacky	rs->nd_rs_cksum = 0;
517249423Sdim	rs->nd_rs_reserved = 0;
518193326Sed	buf += sizeof(*rs);
519226633Sdim
520249423Sdim	/* fill in source link-layer address option */
521226633Sdim	if (lladdroptlen)
522249423Sdim		lladdropt_fill(ifinfo->sdl, (struct nd_opt_hdr *)buf);
523249423Sdim
524193326Sed	return(0);
525193326Sed}
526206084Srdivacky
527249423Sdimstatic struct timeval *
528193326Sedrtsol_check_timer(void)
529249423Sdim{
530193326Sed	static struct timeval returnval;
531193326Sed	struct timeval now, rtsol_timer;
532206084Srdivacky	struct ifinfo *ifinfo;
533249423Sdim	int flags;
534193326Sed
535249423Sdim	gettimeofday(&now, NULL);
536193326Sed
537193326Sed	rtsol_timer = tm_max;
538206084Srdivacky
539249423Sdim	for (ifinfo = iflist; ifinfo; ifinfo = ifinfo->next) {
540193326Sed		if (timercmp(&ifinfo->expire, &now, <=)) {
541249423Sdim			if (dflag > 1)
542193326Sed				warnmsg(LOG_DEBUG, __func__,
543193326Sed				    "timer expiration on %s, "
544206084Srdivacky				    "state = %d", ifinfo->ifname,
545249423Sdim				    ifinfo->state);
546193326Sed
547249423Sdim			switch (ifinfo->state) {
548193326Sed			case IFS_DOWN:
549193326Sed			case IFS_TENTATIVE:
550206084Srdivacky				/* interface_up returns 0 on success */
551193326Sed				flags = interface_up(ifinfo->ifname);
552193326Sed				if (flags == 0)
553193326Sed					ifinfo->state = IFS_DELAY;
554193326Sed				else if (flags == IFS_TENTATIVE)
555193326Sed					ifinfo->state = IFS_TENTATIVE;
556206084Srdivacky				else
557249423Sdim					ifinfo->state = IFS_DOWN;
558193326Sed				break;
559249423Sdim			case IFS_IDLE:
560193326Sed			{
561193326Sed				int oldstatus = ifinfo->active;
562206084Srdivacky				int probe = 0;
563249423Sdim
564193326Sed				ifinfo->active = interface_status(ifinfo);
565226633Sdim
566249423Sdim				if (oldstatus != ifinfo->active) {
567226633Sdim					warnmsg(LOG_DEBUG, __func__,
568249423Sdim					    "%s status is changed"
569193326Sed					    " from %d to %d",
570193326Sed					    ifinfo->ifname,
571206084Srdivacky					    oldstatus, ifinfo->active);
572249423Sdim					probe = 1;
573193326Sed					ifinfo->state = IFS_DELAY;
574226633Sdim				} else if (ifinfo->probeinterval &&
575249423Sdim				    (ifinfo->probetimer -=
576226633Sdim				    ifinfo->timer.tv_sec) <= 0) {
577249423Sdim					/* probe timer expired */
578249423Sdim					ifinfo->probetimer =
579193326Sed					    ifinfo->probeinterval;
580193326Sed					probe = 1;
581206084Srdivacky					ifinfo->state = IFS_PROBE;
582249423Sdim				}
583193326Sed
584249423Sdim				/*
585193326Sed				 * If we need a probe, clear the previous
586193326Sed				 * status wrt the "other" configuration.
587206084Srdivacky				 */
588249423Sdim				if (probe)
589193326Sed					ifinfo->otherconfig = 0;
590249423Sdim
591193326Sed				if (probe && mobile_node)
592193326Sed					defrouter_probe(ifinfo);
593206084Srdivacky				break;
594249423Sdim			}
595193326Sed			case IFS_DELAY:
596249423Sdim				ifinfo->state = IFS_PROBE;
597249423Sdim				sendpacket(ifinfo);
598193326Sed				break;
599193326Sed			case IFS_PROBE:
600206084Srdivacky				if (ifinfo->probes < MAX_RTR_SOLICITATIONS)
601249423Sdim					sendpacket(ifinfo);
602193326Sed				else {
603226633Sdim					warnmsg(LOG_INFO, __func__,
604249423Sdim					    "No answer after sending %d RSs",
605226633Sdim					    ifinfo->probes);
606249423Sdim					ifinfo->probes = 0;
607193326Sed					ifinfo->state = IFS_IDLE;
608193326Sed				}
609206084Srdivacky				break;
610249423Sdim			}
611193326Sed			rtsol_timer_update(ifinfo);
612226633Sdim		}
613249423Sdim
614226633Sdim		if (timercmp(&ifinfo->expire, &rtsol_timer, <))
615249423Sdim			rtsol_timer = ifinfo->expire;
616193326Sed	}
617193326Sed
618206084Srdivacky	if (timercmp(&rtsol_timer, &tm_max, ==)) {
619249423Sdim		warnmsg(LOG_DEBUG, __func__, "there is no timer");
620193326Sed		return(NULL);
621249423Sdim	} else if (timercmp(&rtsol_timer, &now, <))
622193326Sed		/* this may occur when the interval is too small */
623193326Sed		returnval.tv_sec = returnval.tv_usec = 0;
624206084Srdivacky	else
625249423Sdim		timersub(&rtsol_timer, &now, &returnval);
626193326Sed
627249423Sdim	if (dflag > 1)
628193326Sed		warnmsg(LOG_DEBUG, __func__, "New timer is %ld:%08ld",
629193326Sed		    (long)returnval.tv_sec, (long)returnval.tv_usec);
630206084Srdivacky
631249423Sdim	return(&returnval);
632193326Sed}
633249423Sdim
634193326Sedvoid
635193326Sedrtsol_timer_update(struct ifinfo *ifinfo)
636206084Srdivacky{
637249423Sdim#define MILLION 1000000
638193326Sed#define DADRETRY 10		/* XXX: adhoc */
639249423Sdim	long interval;
640193326Sed	struct timeval now;
641193326Sed
642206084Srdivacky	bzero(&ifinfo->timer, sizeof(ifinfo->timer));
643249423Sdim
644193326Sed	switch (ifinfo->state) {
645249423Sdim	case IFS_DOWN:
646193326Sed	case IFS_TENTATIVE:
647193326Sed		if (++ifinfo->dadcount > DADRETRY) {
648206084Srdivacky			ifinfo->dadcount = 0;
649249423Sdim			ifinfo->timer.tv_sec = PROBE_INTERVAL;
650193326Sed		} else
651249423Sdim			ifinfo->timer.tv_sec = 1;
652193326Sed		break;
653193326Sed	case IFS_IDLE:
654206084Srdivacky		if (mobile_node) {
655249423Sdim			/* XXX should be configurable */
656193326Sed			ifinfo->timer.tv_sec = 3;
657249423Sdim		}
658193326Sed		else
659193326Sed			ifinfo->timer = tm_max;	/* stop timer(valid?) */
660206084Srdivacky		break;
661249423Sdim	case IFS_DELAY:
662193326Sed#ifndef HAVE_ARC4RANDOM
663249423Sdim		interval = random() % (MAX_RTR_SOLICITATION_DELAY * MILLION);
664193326Sed#else
665193326Sed		interval = arc4random() % (MAX_RTR_SOLICITATION_DELAY * MILLION);
666206084Srdivacky#endif
667249423Sdim		ifinfo->timer.tv_sec = interval / MILLION;
668193326Sed		ifinfo->timer.tv_usec = interval % MILLION;
669249423Sdim		break;
670193326Sed	case IFS_PROBE:
671193326Sed		if (ifinfo->probes < MAX_RTR_SOLICITATIONS)
672206084Srdivacky			ifinfo->timer.tv_sec = RTR_SOLICITATION_INTERVAL;
673249423Sdim		else {
674193326Sed			/*
675249423Sdim			 * After sending MAX_RTR_SOLICITATIONS solicitations,
676193326Sed			 * we're just waiting for possible replies; there
677193326Sed			 * will be no more solicatation.  Thus, we change
678206084Srdivacky			 * the timer value to MAX_RTR_SOLICITATION_DELAY based
679249423Sdim			 * on RFC 2461, Section 6.3.7.
680193326Sed			 */
681249423Sdim			ifinfo->timer.tv_sec = MAX_RTR_SOLICITATION_DELAY;
682193326Sed		}
683193326Sed		break;
684206084Srdivacky	default:
685249423Sdim		warnmsg(LOG_ERR, __func__,
686193326Sed		    "illegal interface state(%d) on %s",
687249423Sdim		    ifinfo->state, ifinfo->ifname);
688193326Sed		return;
689193326Sed	}
690206084Srdivacky
691249423Sdim	/* reset the timer */
692193326Sed	if (timercmp(&ifinfo->timer, &tm_max, ==)) {
693249423Sdim		ifinfo->expire = tm_max;
694193326Sed		warnmsg(LOG_DEBUG, __func__,
695193326Sed		    "stop timer for %s", ifinfo->ifname);
696206084Srdivacky	} else {
697249423Sdim		gettimeofday(&now, NULL);
698193326Sed		timeradd(&now, &ifinfo->timer, &ifinfo->expire);
699249423Sdim
700193326Sed		if (dflag > 1)
701193326Sed			warnmsg(LOG_DEBUG, __func__,
702206084Srdivacky			    "set timer for %s to %d:%d", ifinfo->ifname,
703249423Sdim			    (int)ifinfo->timer.tv_sec,
704193326Sed			    (int)ifinfo->timer.tv_usec);
705249423Sdim	}
706193326Sed
707193326Sed#undef MILLION
708206084Srdivacky}
709249423Sdim
710193326Sed/* timer related utility functions */
711249423Sdim#define MILLION 1000000
712193326Sed
713193326Sedstatic void
714206084Srdivackyrtsold_set_dump_file(int sig)
715249423Sdim{
716193326Sed	do_dump = 1;
717249423Sdim}
718193326Sed
719193326Sedstatic void
720206084Srdivackyusage(char *progname)
721249423Sdim{
722193326Sed	if (progname && progname[strlen(progname) - 1] != 'd') {
723249423Sdim		fprintf(stderr, "usage: rtsol [-dD] interfaces...\n");
724193326Sed		fprintf(stderr, "usage: rtsol [-dD] -a\n");
725193326Sed	} else {
726206084Srdivacky		fprintf(stderr, "usage: rtsold [-adDfm1] interfaces...\n");
727249423Sdim		fprintf(stderr, "usage: rtsold [-dDfm1] -a\n");
728193326Sed	}
729249423Sdim	exit(1);
730193326Sed}
731193326Sed
732206084Srdivackyvoid
733249423Sdim#if __STDC__
734193326Sedwarnmsg(int priority, const char *func, const char *msg, ...)
735249423Sdim#else
736193326Sedwarnmsg(priority, func, msg, va_alist)
737193326Sed	int priority;
738206084Srdivacky	const char *func;
739249423Sdim	const char *msg;
740193326Sed	va_dcl
741249423Sdim#endif
742193326Sed{
743193326Sed	va_list ap;
744206084Srdivacky	char buf[BUFSIZ];
745249423Sdim
746193326Sed	va_start(ap, msg);
747249423Sdim	if (fflag) {
748193326Sed		if (priority <= log_upto) {
749193326Sed			(void)vfprintf(stderr, msg, ap);
750206084Srdivacky			(void)fprintf(stderr, "\n");
751249423Sdim		}
752193326Sed	} else {
753249423Sdim		snprintf(buf, sizeof(buf), "<%s> %s", func, msg);
754193326Sed		msg = buf;
755193326Sed		vsyslog(priority, msg, ap);
756206084Srdivacky	}
757249423Sdim	va_end(ap);
758193326Sed}
759249423Sdim
760193326Sed/*
761193326Sed * return a list of interfaces which is suitable to sending an RS.
762206084Srdivacky */
763249423Sdimchar **
764193326Sedautoifprobe(void)
765249423Sdim{
766193326Sed	static char **argv = NULL;
767193326Sed	static int n = 0;
768206084Srdivacky	char **a;
769249423Sdim	int i, found;
770193326Sed	struct ifaddrs *ifap, *ifa, *target;
771249423Sdim
772193326Sed	/* initialize */
773193326Sed	while (n--)
774206084Srdivacky		free(argv[n]);
775249423Sdim	if (argv) {
776193326Sed		free(argv);
777249423Sdim		argv = NULL;
778193326Sed	}
779193326Sed	n = 0;
780206084Srdivacky
781249423Sdim	if (getifaddrs(&ifap) != 0)
782193326Sed		return NULL;
783249423Sdim
784193326Sed	target = NULL;
785193326Sed	/* find an ethernet */
786206084Srdivacky	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
787249423Sdim		if ((ifa->ifa_flags & IFF_UP) == 0)
788193326Sed			continue;
789249423Sdim		if ((ifa->ifa_flags & IFF_POINTOPOINT) != 0)
790193326Sed			continue;
791193326Sed		if ((ifa->ifa_flags & IFF_LOOPBACK) != 0)
792206084Srdivacky			continue;
793249423Sdim		if ((ifa->ifa_flags & IFF_MULTICAST) == 0)
794193326Sed			continue;
795249423Sdim
796193326Sed		if (ifa->ifa_addr->sa_family != AF_INET6)
797193326Sed			continue;
798206084Srdivacky
799249423Sdim		found = 0;
800193326Sed		for (i = 0; i < n; i++) {
801249423Sdim			if (strcmp(argv[i], ifa->ifa_name) == 0) {
802193326Sed				found++;
803193326Sed				break;
804206084Srdivacky			}
805249423Sdim		}
806193326Sed		if (found)
807249423Sdim			continue;
808193326Sed
809193326Sed		/* if we find multiple candidates, just warn. */
810206084Srdivacky		if (n != 0 && dflag > 1)
811249423Sdim			warnx("multiple interfaces found");
812193326Sed
813249423Sdim		a = (char **)realloc(argv, (n + 1) * sizeof(char **));
814193326Sed		if (a == NULL)
815193326Sed			err(1, "realloc");
816206084Srdivacky		argv = a;
817249423Sdim		argv[n] = strdup(ifa->ifa_name);
818193326Sed		if (!argv[n])
819249423Sdim			err(1, "malloc");
820193326Sed		n++;
821193326Sed		argv[n] = NULL;
822206084Srdivacky	}
823249423Sdim
824193326Sed	if (n) {
825249423Sdim		a = (char **)realloc(argv, (n + 1) * sizeof(char **));
826193326Sed		if (a == NULL)
827193326Sed			err(1, "realloc");
828234353Sdim		argv = a;
829263508Sdim		argv[n] = NULL;
830234353Sdim
831263508Sdim		if (dflag > 0) {
832234353Sdim			for (i = 0; i < n; i++)
833193326Sed				warnx("probing %s", argv[i]);
834206084Srdivacky		}
835249423Sdim	}
836193326Sed	freeifaddrs(ifap);
837249423Sdim	return argv;
838193326Sed}
839193326Sed