rtsold.c revision 173412
1295016Sjkim/*	$KAME: rtsold.c,v 1.67 2003/05/17 18:16:15 itojun Exp $	*/
2238384Sjkim
3238384Sjkim/*
4238384Sjkim * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
5238384Sjkim * All rights reserved.
6238384Sjkim *
7238384Sjkim * Redistribution and use in source and binary forms, with or without
8238384Sjkim * modification, are permitted provided that the following conditions
9238384Sjkim * are met:
10238384Sjkim * 1. Redistributions of source code must retain the above copyright
11238384Sjkim *    notice, this list of conditions and the following disclaimer.
12238384Sjkim * 2. Redistributions in binary form must reproduce the above copyright
13238384Sjkim *    notice, this list of conditions and the following disclaimer in the
14238384Sjkim *    documentation and/or other materials provided with the distribution.
15238384Sjkim * 3. Neither the name of the project nor the names of its contributors
16238384Sjkim *    may be used to endorse or promote products derived from this software
17238384Sjkim *    without specific prior written permission.
18238384Sjkim *
19238384Sjkim * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20238384Sjkim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21238384Sjkim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22238384Sjkim * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23238384Sjkim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24238384Sjkim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25238384Sjkim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26238384Sjkim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27238384Sjkim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28238384Sjkim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29238384Sjkim * SUCH DAMAGE.
30238384Sjkim *
31238384Sjkim * $FreeBSD: head/usr.sbin/rtsold/rtsold.c 173412 2007-11-07 10:53:41Z kevlo $
32238384Sjkim */
33238384Sjkim
34280304Sjkim#include <sys/types.h>
35238384Sjkim#include <sys/time.h>
36238384Sjkim#include <sys/socket.h>
37238384Sjkim#include <sys/param.h>
38238384Sjkim
39238384Sjkim#include <net/if.h>
40238384Sjkim#include <net/if_dl.h>
41238384Sjkim
42238384Sjkim#include <netinet/in.h>
43238384Sjkim#include <netinet/icmp6.h>
44238384Sjkim
45238384Sjkim#include <signal.h>
46238384Sjkim#include <unistd.h>
47238384Sjkim#include <syslog.h>
48238384Sjkim#include <string.h>
49238384Sjkim#include <stdlib.h>
50238384Sjkim#include <stdio.h>
51238384Sjkim#include <errno.h>
52238384Sjkim#include <err.h>
53238384Sjkim#include <stdarg.h>
54238384Sjkim#include <ifaddrs.h>
55238384Sjkim#ifdef HAVE_POLL_H
56238384Sjkim#include <poll.h>
57238384Sjkim#endif
58238384Sjkim
59238384Sjkim#include "rtsold.h"
60238384Sjkim
61238384Sjkimstruct ifinfo *iflist;
62238384Sjkimstruct timeval tm_max =	{0x7fffffff, 0x7fffffff};
63238384Sjkimstatic int log_upto = 999;
64280304Sjkimstatic int fflag = 0;
65280304Sjkimstatic int Fflag = 0;	/* force setting sysctl parameters */
66280304Sjkim
67238384Sjkimint aflag = 0;
68280304Sjkimint dflag = 0;
69280304Sjkim
70238384Sjkimchar *otherconf_script;
71280304Sjkim
72238384Sjkim/* protocol constants */
73238384Sjkim#define MAX_RTR_SOLICITATION_DELAY	1 /* second */
74238384Sjkim#define RTR_SOLICITATION_INTERVAL	4 /* seconds */
75238384Sjkim#define MAX_RTR_SOLICITATIONS		3 /* times */
76238384Sjkim
77238384Sjkim/*
78238384Sjkim * implementation dependent constants in seconds
79238384Sjkim * XXX: should be configurable
80238384Sjkim */
81238384Sjkim#define PROBE_INTERVAL 60
82280304Sjkim
83238384Sjkimint main(int, char **);
84238384Sjkim
85280304Sjkim/* static variables and functions */
86238384Sjkimstatic int mobile_node = 0;
87238384Sjkim#ifndef SMALL
88280304Sjkimstatic int do_dump;
89238384Sjkimstatic char *dumpfilename = "/var/run/rtsold.dump"; /* XXX: should be configurable */
90238384Sjkim#endif
91238384Sjkim#if 1
92238384Sjkimstatic char *pidfilename = "/var/run/rtsold.pid"; /* should be configurable */
93238384Sjkim#endif
94238384Sjkim
95238384Sjkim#if 0
96280304Sjkimstatic int ifreconfig(char *);
97280304Sjkim#endif
98280304Sjkimstatic int make_packet(struct ifinfo *);
99280304Sjkimstatic struct timeval *rtsol_check_timer(void);
100280304Sjkim
101280304Sjkim#ifndef SMALL
102280304Sjkimstatic void rtsold_set_dump_file(int);
103280304Sjkim#endif
104238384Sjkimstatic void usage(char *);
105238384Sjkim
106280304Sjkimint
107238384Sjkimmain(int argc, char **argv)
108238384Sjkim{
109238384Sjkim	int s, ch, once = 0;
110238384Sjkim	struct timeval *timeout;
111238384Sjkim	char *argv0, *opts;
112238384Sjkim#ifdef HAVE_POLL_H
113238384Sjkim	struct pollfd set[2];
114238384Sjkim#else
115238384Sjkim	fd_set *fdsetp, *selectfdp;
116238384Sjkim	int fdmasks;
117280304Sjkim	int maxfd;
118238384Sjkim#endif
119238384Sjkim	int rtsock;
120238384Sjkim
121238384Sjkim	/*
122238384Sjkim	 * Initialization
123238384Sjkim	 */
124238384Sjkim	argv0 = argv[0];
125238384Sjkim
126238384Sjkim	/* get option */
127238384Sjkim	if (argv0 && argv0[strlen(argv0) - 1] != 'd') {
128238384Sjkim		fflag = 1;
129238384Sjkim		once = 1;
130238384Sjkim		opts = "adDFO:";
131238384Sjkim	} else
132238384Sjkim		opts = "adDfFm1O:";
133238384Sjkim
134238384Sjkim	while ((ch = getopt(argc, argv, opts)) != -1) {
135238384Sjkim		switch (ch) {
136238384Sjkim		case 'a':
137238384Sjkim			aflag = 1;
138238384Sjkim			break;
139238384Sjkim		case 'd':
140238384Sjkim			dflag = 1;
141238384Sjkim			break;
142238384Sjkim		case 'D':
143238384Sjkim			dflag = 2;
144238384Sjkim			break;
145238384Sjkim		case 'f':
146238384Sjkim			fflag = 1;
147238384Sjkim			break;
148238384Sjkim		case 'F':
149238384Sjkim			Fflag = 1;
150238384Sjkim			break;
151238384Sjkim		case 'm':
152238384Sjkim			mobile_node = 1;
153238384Sjkim			break;
154238384Sjkim		case '1':
155238384Sjkim			once = 1;
156238384Sjkim			break;
157238384Sjkim		case 'O':
158238384Sjkim			otherconf_script = optarg;
159238384Sjkim			break;
160238384Sjkim		default:
161238384Sjkim			usage(argv0);
162238384Sjkim			/*NOTREACHED*/
163238384Sjkim		}
164238384Sjkim	}
165238384Sjkim	argc -= optind;
166238384Sjkim	argv += optind;
167238384Sjkim
168238384Sjkim	if ((!aflag && argc == 0) || (aflag && argc != 0)) {
169238384Sjkim		usage(argv0);
170238384Sjkim		/*NOTREACHED*/
171238384Sjkim	}
172238384Sjkim
173238384Sjkim	/* set log level */
174238384Sjkim	if (dflag == 0)
175238384Sjkim		log_upto = LOG_NOTICE;
176238384Sjkim	if (!fflag) {
177238384Sjkim		char *ident;
178238384Sjkim
179238384Sjkim		ident = strrchr(argv0, '/');
180238384Sjkim		if (!ident)
181238384Sjkim			ident = argv0;
182238384Sjkim		else
183238384Sjkim			ident++;
184238384Sjkim		openlog(ident, LOG_NDELAY|LOG_PID, LOG_DAEMON);
185238384Sjkim		if (log_upto >= 0)
186238384Sjkim			setlogmask(LOG_UPTO(log_upto));
187238384Sjkim	}
188238384Sjkim
189238384Sjkim	if (otherconf_script && *otherconf_script != '/') {
190238384Sjkim		errx(1, "configuration script (%s) must be an absolute path",
191238384Sjkim		    otherconf_script);
192238384Sjkim	}
193238384Sjkim
194238384Sjkim#ifndef HAVE_ARC4RANDOM
195238384Sjkim	/* random value initialization */
196238384Sjkim	srandom((u_long)time(NULL));
197238384Sjkim#endif
198238384Sjkim
199238384Sjkim	if (Fflag) {
200238384Sjkim		setinet6sysctl(IPV6CTL_ACCEPT_RTADV, 1);
201238384Sjkim		setinet6sysctl(IPV6CTL_FORWARDING, 0);
202238384Sjkim	} else {
203238384Sjkim		/* warn if accept_rtadv is down */
204238384Sjkim		if (!getinet6sysctl(IPV6CTL_ACCEPT_RTADV))
205238384Sjkim			warnx("kernel is configured not to accept RAs");
206238384Sjkim		/* warn if forwarding is up */
207238384Sjkim		if (getinet6sysctl(IPV6CTL_FORWARDING))
208238384Sjkim			warnx("kernel is configured as a router, not a host");
209238384Sjkim	}
210238384Sjkim
211238384Sjkim#ifndef SMALL
212238384Sjkim	/* initialization to dump internal status to a file */
213238384Sjkim	signal(SIGUSR1, rtsold_set_dump_file);
214238384Sjkim#endif
215238384Sjkim
216238384Sjkim	if (!fflag)
217238384Sjkim		daemon(0, 0);		/* act as a daemon */
218238384Sjkim
219238384Sjkim	/*
220238384Sjkim	 * Open a socket for sending RS and receiving RA.
221238384Sjkim	 * This should be done before calling ifinit(), since the function
222238384Sjkim	 * uses the socket.
223238384Sjkim	 */
224238384Sjkim	if ((s = sockopen()) < 0) {
225238384Sjkim		warnmsg(LOG_ERR, __func__, "failed to open a socket");
226238384Sjkim		exit(1);
227238384Sjkim		/*NOTREACHED*/
228238384Sjkim	}
229238384Sjkim#ifdef HAVE_POLL_H
230238384Sjkim	set[0].fd = s;
231238384Sjkim	set[0].events = POLLIN;
232238384Sjkim#else
233238384Sjkim	maxfd = s;
234238384Sjkim#endif
235238384Sjkim
236238384Sjkim#ifdef HAVE_POLL_H
237238384Sjkim	set[1].fd = -1;
238238384Sjkim#endif
239238384Sjkim
240238384Sjkim	if ((rtsock = rtsock_open()) < 0) {
241238384Sjkim		warnmsg(LOG_ERR, __func__, "failed to open a socket");
242238384Sjkim		exit(1);
243238384Sjkim		/*NOTREACHED*/
244238384Sjkim	}
245238384Sjkim#ifdef HAVE_POLL_H
246238384Sjkim	set[1].fd = rtsock;
247238384Sjkim	set[1].events = POLLIN;
248238384Sjkim#else
249238384Sjkim	if (rtsock > maxfd)
250238384Sjkim		maxfd = rtsock;
251238384Sjkim#endif
252238384Sjkim
253238384Sjkim#ifndef HAVE_POLL_H
254238384Sjkim	fdmasks = howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask);
255238384Sjkim	if ((fdsetp = malloc(fdmasks)) == NULL) {
256238384Sjkim		err(1, "malloc");
257238384Sjkim		/*NOTREACHED*/
258238384Sjkim	}
259238384Sjkim	if ((selectfdp = malloc(fdmasks)) == NULL) {
260238384Sjkim		err(1, "malloc");
261238384Sjkim		/*NOTREACHED*/
262238384Sjkim	}
263238384Sjkim#endif
264238384Sjkim
265238384Sjkim	/* configuration per interface */
266238384Sjkim	if (ifinit()) {
267238384Sjkim		warnmsg(LOG_ERR, __func__,
268238384Sjkim		    "failed to initialize interfaces");
269238384Sjkim		exit(1);
270238384Sjkim		/*NOTREACHED*/
271238384Sjkim	}
272238384Sjkim	if (aflag)
273238384Sjkim		argv = autoifprobe();
274238384Sjkim	while (argv && *argv) {
275238384Sjkim		if (ifconfig(*argv)) {
276238384Sjkim			warnmsg(LOG_ERR, __func__,
277238384Sjkim			    "failed to initialize %s", *argv);
278238384Sjkim			exit(1);
279238384Sjkim			/*NOTREACHED*/
280238384Sjkim		}
281238384Sjkim		argv++;
282238384Sjkim	}
283238384Sjkim
284238384Sjkim	/* setup for probing default routers */
285238384Sjkim	if (probe_init()) {
286238384Sjkim		warnmsg(LOG_ERR, __func__,
287238384Sjkim		    "failed to setup for probing routers");
288238384Sjkim		exit(1);
289238384Sjkim		/*NOTREACHED*/
290238384Sjkim	}
291238384Sjkim
292238384Sjkim#if 1
293238384Sjkim	/* dump the current pid */
294238384Sjkim	if (!once) {
295238384Sjkim		pid_t pid = getpid();
296238384Sjkim		FILE *fp;
297238384Sjkim
298238384Sjkim		if ((fp = fopen(pidfilename, "w")) == NULL)
299238384Sjkim			warnmsg(LOG_ERR, __func__,
300238384Sjkim			    "failed to open a pid log file(%s): %s",
301238384Sjkim			    pidfilename, strerror(errno));
302238384Sjkim		else {
303238384Sjkim			fprintf(fp, "%d\n", pid);
304238384Sjkim			fclose(fp);
305238384Sjkim		}
306238384Sjkim	}
307238384Sjkim#endif
308238384Sjkim
309238384Sjkim#ifndef HAVE_POLL_H
310238384Sjkim	memset(fdsetp, 0, fdmasks);
311238384Sjkim	FD_SET(s, fdsetp);
312238384Sjkim	FD_SET(rtsock, fdsetp);
313238384Sjkim#endif
314238384Sjkim	while (1) {		/* main loop */
315238384Sjkim		int e;
316238384Sjkim
317238384Sjkim#ifndef HAVE_POLL_H
318238384Sjkim		memcpy(selectfdp, fdsetp, fdmasks);
319238384Sjkim#endif
320238384Sjkim
321238384Sjkim#ifndef SMALL
322238384Sjkim		if (do_dump) {	/* SIGUSR1 */
323238384Sjkim			do_dump = 0;
324238384Sjkim			rtsold_dump_file(dumpfilename);
325238384Sjkim		}
326238384Sjkim#endif
327238384Sjkim
328238384Sjkim		timeout = rtsol_check_timer();
329238384Sjkim
330238384Sjkim		if (once) {
331238384Sjkim			struct ifinfo *ifi;
332238384Sjkim
333238384Sjkim			/* if we have no timeout, we are done (or failed) */
334238384Sjkim			if (timeout == NULL)
335238384Sjkim				break;
336238384Sjkim
337238384Sjkim			/* if all interfaces have got RA packet, we are done */
338238384Sjkim			for (ifi = iflist; ifi; ifi = ifi->next) {
339238384Sjkim				if (ifi->state != IFS_DOWN && ifi->racnt == 0)
340238384Sjkim					break;
341238384Sjkim			}
342238384Sjkim			if (ifi == NULL)
343238384Sjkim				break;
344238384Sjkim		}
345238384Sjkim#ifdef HAVE_POLL_H
346238384Sjkim		e = poll(set, 2, timeout ? (timeout->tv_sec * 1000 + timeout->tv_usec / 1000) : INFTIM);
347238384Sjkim#else
348238384Sjkim		e = select(maxfd + 1, selectfdp, NULL, NULL, timeout);
349238384Sjkim#endif
350238384Sjkim		if (e < 1) {
351238384Sjkim			if (e < 0 && errno != EINTR) {
352238384Sjkim				warnmsg(LOG_ERR, __func__, "select: %s",
353238384Sjkim				    strerror(errno));
354238384Sjkim			}
355238384Sjkim			continue;
356238384Sjkim		}
357238384Sjkim
358238384Sjkim		/* packet reception */
359238384Sjkim#ifdef HAVE_POLL_H
360238384Sjkim		if (set[1].revents & POLLIN)
361238384Sjkim#else
362238384Sjkim		if (FD_ISSET(rtsock, selectfdp))
363238384Sjkim#endif
364238384Sjkim			rtsock_input(rtsock);
365238384Sjkim#ifdef HAVE_POLL_H
366238384Sjkim		if (set[0].revents & POLLIN)
367238384Sjkim#else
368238384Sjkim		if (FD_ISSET(s, selectfdp))
369238384Sjkim#endif
370238384Sjkim			rtsol_input(s);
371238384Sjkim	}
372238384Sjkim	/* NOTREACHED */
373238384Sjkim
374238384Sjkim	return 0;
375238384Sjkim}
376238384Sjkim
377238384Sjkimint
378238384Sjkimifconfig(char *ifname)
379238384Sjkim{
380238384Sjkim	struct ifinfo *ifinfo;
381238384Sjkim	struct sockaddr_dl *sdl;
382238384Sjkim	int flags;
383238384Sjkim
384238384Sjkim	if ((sdl = if_nametosdl(ifname)) == NULL) {
385238384Sjkim		warnmsg(LOG_ERR, __func__,
386238384Sjkim		    "failed to get link layer information for %s", ifname);
387238384Sjkim		return(-1);
388238384Sjkim	}
389238384Sjkim	if (find_ifinfo(sdl->sdl_index)) {
390238384Sjkim		warnmsg(LOG_ERR, __func__,
391238384Sjkim		    "interface %s was already configured", ifname);
392238384Sjkim		free(sdl);
393238384Sjkim		return(-1);
394238384Sjkim	}
395238384Sjkim
396238384Sjkim	if ((ifinfo = malloc(sizeof(*ifinfo))) == NULL) {
397238384Sjkim		warnmsg(LOG_ERR, __func__, "memory allocation failed");
398238384Sjkim		free(sdl);
399238384Sjkim		return(-1);
400238384Sjkim	}
401238384Sjkim	memset(ifinfo, 0, sizeof(*ifinfo));
402238384Sjkim	ifinfo->sdl = sdl;
403238384Sjkim
404238384Sjkim	strlcpy(ifinfo->ifname, ifname, sizeof(ifinfo->ifname));
405238384Sjkim
406238384Sjkim	/* construct a router solicitation message */
407238384Sjkim	if (make_packet(ifinfo))
408238384Sjkim		goto bad;
409238384Sjkim
410238384Sjkim	/* set link ID of this interface. */
411238384Sjkim#ifdef HAVE_SCOPELIB
412238384Sjkim	if (inet_zoneid(AF_INET6, 2, ifname, &ifinfo->linkid))
413238384Sjkim		goto bad;
414238384Sjkim#else
415238384Sjkim	/* XXX: assume interface IDs as link IDs */
416238384Sjkim	ifinfo->linkid = ifinfo->sdl->sdl_index;
417238384Sjkim#endif
418238384Sjkim
419238384Sjkim	/*
420238384Sjkim	 * check if the interface is available.
421238384Sjkim	 * also check if SIOCGIFMEDIA ioctl is OK on the interface.
422238384Sjkim	 */
423238384Sjkim	ifinfo->mediareqok = 1;
424238384Sjkim	ifinfo->active = interface_status(ifinfo);
425238384Sjkim	if (!ifinfo->mediareqok) {
426238384Sjkim		/*
427238384Sjkim		 * probe routers periodically even if the link status
428238384Sjkim		 * does not change.
429238384Sjkim		 */
430238384Sjkim		ifinfo->probeinterval = PROBE_INTERVAL;
431238384Sjkim	}
432238384Sjkim
433238384Sjkim	/* activate interface: interface_up returns 0 on success */
434238384Sjkim	flags = interface_up(ifinfo->ifname);
435238384Sjkim	if (flags == 0)
436238384Sjkim		ifinfo->state = IFS_DELAY;
437238384Sjkim	else if (flags == IFS_TENTATIVE)
438238384Sjkim		ifinfo->state = IFS_TENTATIVE;
439238384Sjkim	else
440238384Sjkim		ifinfo->state = IFS_DOWN;
441238384Sjkim
442238384Sjkim	rtsol_timer_update(ifinfo);
443238384Sjkim
444238384Sjkim	/* link into chain */
445238384Sjkim	if (iflist)
446238384Sjkim		ifinfo->next = iflist;
447238384Sjkim	iflist = ifinfo;
448238384Sjkim
449238384Sjkim	return(0);
450238384Sjkim
451238384Sjkimbad:
452238384Sjkim	free(ifinfo->sdl);
453238384Sjkim	free(ifinfo);
454238384Sjkim	return(-1);
455238384Sjkim}
456238384Sjkim
457238384Sjkimvoid
458238384Sjkimiflist_init(void)
459238384Sjkim{
460238384Sjkim	struct ifinfo *ifi, *next;
461238384Sjkim
462238384Sjkim	for (ifi = iflist; ifi; ifi = next) {
463238384Sjkim		next = ifi->next;
464238384Sjkim		if (ifi->sdl)
465238384Sjkim			free(ifi->sdl);
466238384Sjkim		if (ifi->rs_data)
467238384Sjkim			free(ifi->rs_data);
468238384Sjkim		free(ifi);
469238384Sjkim		iflist = NULL;
470238384Sjkim	}
471280304Sjkim}
472280304Sjkim
473238384Sjkim#if 0
474280304Sjkimstatic int
475280304Sjkimifreconfig(char *ifname)
476280304Sjkim{
477238384Sjkim	struct ifinfo *ifi, *prev;
478280304Sjkim	int rv;
479280304Sjkim
480280304Sjkim	prev = NULL;
481280304Sjkim	for (ifi = iflist; ifi; ifi = ifi->next) {
482238384Sjkim		if (strncmp(ifi->ifname, ifname, sizeof(ifi->ifname)) == 0)
483280304Sjkim			break;
484238384Sjkim		prev = ifi;
485280304Sjkim	}
486280304Sjkim	prev->next = ifi->next;
487280304Sjkim
488280304Sjkim	rv = ifconfig(ifname);
489280304Sjkim
490280304Sjkim	/* reclaim it after ifconfig() in case ifname is pointer inside ifi */
491238384Sjkim	if (ifi->rs_data)
492280304Sjkim		free(ifi->rs_data);
493280304Sjkim	free(ifi->sdl);
494280304Sjkim	free(ifi);
495280304Sjkim	return rv;
496280304Sjkim}
497280304Sjkim#endif
498280304Sjkim
499280304Sjkimstruct ifinfo *
500280304Sjkimfind_ifinfo(int ifindex)
501280304Sjkim{
502280304Sjkim	struct ifinfo *ifi;
503280304Sjkim
504280304Sjkim	for (ifi = iflist; ifi; ifi = ifi->next)
505280304Sjkim		if (ifi->sdl->sdl_index == ifindex)
506280304Sjkim			return(ifi);
507280304Sjkim	return(NULL);
508280304Sjkim}
509280304Sjkim
510280304Sjkimstatic int
511280304Sjkimmake_packet(struct ifinfo *ifinfo)
512280304Sjkim{
513280304Sjkim	size_t packlen = sizeof(struct nd_router_solicit), lladdroptlen = 0;
514280304Sjkim	struct nd_router_solicit *rs;
515280304Sjkim	char *buf;
516280304Sjkim
517280304Sjkim	if ((lladdroptlen = lladdropt_length(ifinfo->sdl)) == 0) {
518280304Sjkim		warnmsg(LOG_INFO, __func__,
519280304Sjkim		    "link-layer address option has null length"
520280304Sjkim		    " on %s. Treat as not included.", ifinfo->ifname);
521280304Sjkim	}
522280304Sjkim	packlen += lladdroptlen;
523280304Sjkim	ifinfo->rs_datalen = packlen;
524280304Sjkim
525280304Sjkim	/* allocate buffer */
526280304Sjkim	if ((buf = malloc(packlen)) == NULL) {
527280304Sjkim		warnmsg(LOG_ERR, __func__,
528280304Sjkim		    "memory allocation failed for %s", ifinfo->ifname);
529280304Sjkim		return(-1);
530280304Sjkim	}
531280304Sjkim	ifinfo->rs_data = buf;
532280304Sjkim
533280304Sjkim	/* fill in the message */
534280304Sjkim	rs = (struct nd_router_solicit *)buf;
535280304Sjkim	rs->nd_rs_type = ND_ROUTER_SOLICIT;
536280304Sjkim	rs->nd_rs_code = 0;
537280304Sjkim	rs->nd_rs_cksum = 0;
538280304Sjkim	rs->nd_rs_reserved = 0;
539280304Sjkim	buf += sizeof(*rs);
540280304Sjkim
541280304Sjkim	/* fill in source link-layer address option */
542280304Sjkim	if (lladdroptlen)
543280304Sjkim		lladdropt_fill(ifinfo->sdl, (struct nd_opt_hdr *)buf);
544280304Sjkim
545280304Sjkim	return(0);
546280304Sjkim}
547280304Sjkim
548280304Sjkimstatic struct timeval *
549280304Sjkimrtsol_check_timer(void)
550280304Sjkim{
551280304Sjkim	static struct timeval returnval;
552280304Sjkim	struct timeval now, rtsol_timer;
553280304Sjkim	struct ifinfo *ifinfo;
554280304Sjkim	int flags;
555280304Sjkim
556280304Sjkim	gettimeofday(&now, NULL);
557280304Sjkim
558280304Sjkim	rtsol_timer = tm_max;
559280304Sjkim
560280304Sjkim	for (ifinfo = iflist; ifinfo; ifinfo = ifinfo->next) {
561280304Sjkim		if (timercmp(&ifinfo->expire, &now, <=)) {
562238384Sjkim			if (dflag > 1)
563280304Sjkim				warnmsg(LOG_DEBUG, __func__,
564280304Sjkim				    "timer expiration on %s, "
565280304Sjkim				    "state = %d", ifinfo->ifname,
566280304Sjkim				    ifinfo->state);
567238384Sjkim
568238384Sjkim			switch (ifinfo->state) {
569238384Sjkim			case IFS_DOWN:
570238384Sjkim			case IFS_TENTATIVE:
571238384Sjkim				/* interface_up returns 0 on success */
572238384Sjkim				flags = interface_up(ifinfo->ifname);
573280304Sjkim				if (flags == 0)
574280304Sjkim					ifinfo->state = IFS_DELAY;
575238384Sjkim				else if (flags == IFS_TENTATIVE)
576280304Sjkim					ifinfo->state = IFS_TENTATIVE;
577280304Sjkim				else
578280304Sjkim					ifinfo->state = IFS_DOWN;
579238384Sjkim				break;
580280304Sjkim			case IFS_IDLE:
581280304Sjkim			{
582280304Sjkim				int oldstatus = ifinfo->active;
583280304Sjkim				int probe = 0;
584238384Sjkim
585280304Sjkim				ifinfo->active = interface_status(ifinfo);
586238384Sjkim
587280304Sjkim				if (oldstatus != ifinfo->active) {
588280304Sjkim					warnmsg(LOG_DEBUG, __func__,
589280304Sjkim					    "%s status is changed"
590280304Sjkim					    " from %d to %d",
591280304Sjkim					    ifinfo->ifname,
592280304Sjkim					    oldstatus, ifinfo->active);
593280304Sjkim					probe = 1;
594280304Sjkim					ifinfo->state = IFS_DELAY;
595280304Sjkim				} else if (ifinfo->probeinterval &&
596280304Sjkim				    (ifinfo->probetimer -=
597238384Sjkim				    ifinfo->timer.tv_sec) <= 0) {
598280304Sjkim					/* probe timer expired */
599280304Sjkim					ifinfo->probetimer =
600238384Sjkim					    ifinfo->probeinterval;
601280304Sjkim					probe = 1;
602280304Sjkim					ifinfo->state = IFS_PROBE;
603280304Sjkim				}
604280304Sjkim
605280304Sjkim				/*
606280304Sjkim				 * If we need a probe, clear the previous
607280304Sjkim				 * status wrt the "other" configuration.
608280304Sjkim				 */
609280304Sjkim				if (probe)
610280304Sjkim					ifinfo->otherconfig = 0;
611280304Sjkim
612280304Sjkim				if (probe && mobile_node)
613280304Sjkim					defrouter_probe(ifinfo);
614280304Sjkim				break;
615238384Sjkim			}
616280304Sjkim			case IFS_DELAY:
617280304Sjkim				ifinfo->state = IFS_PROBE;
618238384Sjkim				sendpacket(ifinfo);
619280304Sjkim				break;
620280304Sjkim			case IFS_PROBE:
621280304Sjkim				if (ifinfo->probes < MAX_RTR_SOLICITATIONS)
622238384Sjkim					sendpacket(ifinfo);
623280304Sjkim				else {
624238384Sjkim					warnmsg(LOG_INFO, __func__,
625280304Sjkim					    "No answer after sending %d RSs",
626280304Sjkim					    ifinfo->probes);
627280304Sjkim					ifinfo->probes = 0;
628280304Sjkim					ifinfo->state = IFS_IDLE;
629280304Sjkim				}
630280304Sjkim				break;
631280304Sjkim			}
632280304Sjkim			rtsol_timer_update(ifinfo);
633280304Sjkim		}
634280304Sjkim
635280304Sjkim		if (timercmp(&ifinfo->expire, &rtsol_timer, <))
636280304Sjkim			rtsol_timer = ifinfo->expire;
637280304Sjkim	}
638280304Sjkim
639280304Sjkim	if (timercmp(&rtsol_timer, &tm_max, ==)) {
640280304Sjkim		warnmsg(LOG_DEBUG, __func__, "there is no timer");
641280304Sjkim		return(NULL);
642280304Sjkim	} else if (timercmp(&rtsol_timer, &now, <))
643280304Sjkim		/* this may occur when the interval is too small */
644280304Sjkim		returnval.tv_sec = returnval.tv_usec = 0;
645238384Sjkim	else
646280304Sjkim		timersub(&rtsol_timer, &now, &returnval);
647280304Sjkim
648238384Sjkim	if (dflag > 1)
649238384Sjkim		warnmsg(LOG_DEBUG, __func__, "New timer is %ld:%08ld",
650238384Sjkim		    (long)returnval.tv_sec, (long)returnval.tv_usec);
651238384Sjkim
652238384Sjkim	return(&returnval);
653238384Sjkim}
654238384Sjkim
655280304Sjkimvoid
656280304Sjkimrtsol_timer_update(struct ifinfo *ifinfo)
657238384Sjkim{
658280304Sjkim#define MILLION 1000000
659280304Sjkim#define DADRETRY 10		/* XXX: adhoc */
660280304Sjkim	long interval;
661238384Sjkim	struct timeval now;
662280304Sjkim
663280304Sjkim	bzero(&ifinfo->timer, sizeof(ifinfo->timer));
664238384Sjkim
665280304Sjkim	switch (ifinfo->state) {
666280304Sjkim	case IFS_DOWN:
667280304Sjkim	case IFS_TENTATIVE:
668280304Sjkim		if (++ifinfo->dadcount > DADRETRY) {
669280304Sjkim			ifinfo->dadcount = 0;
670280304Sjkim			ifinfo->timer.tv_sec = PROBE_INTERVAL;
671280304Sjkim		} else
672280304Sjkim			ifinfo->timer.tv_sec = 1;
673238384Sjkim		break;
674238384Sjkim	case IFS_IDLE:
675280304Sjkim		if (mobile_node) {
676238384Sjkim			/* XXX should be configurable */
677280304Sjkim			ifinfo->timer.tv_sec = 3;
678280304Sjkim		}
679280304Sjkim		else
680280304Sjkim			ifinfo->timer = tm_max;	/* stop timer(valid?) */
681280304Sjkim		break;
682280304Sjkim	case IFS_DELAY:
683280304Sjkim#ifndef HAVE_ARC4RANDOM
684280304Sjkim		interval = random() % (MAX_RTR_SOLICITATION_DELAY * MILLION);
685280304Sjkim#else
686280304Sjkim		interval = arc4random() % (MAX_RTR_SOLICITATION_DELAY * MILLION);
687280304Sjkim#endif
688280304Sjkim		ifinfo->timer.tv_sec = interval / MILLION;
689280304Sjkim		ifinfo->timer.tv_usec = interval % MILLION;
690280304Sjkim		break;
691280304Sjkim	case IFS_PROBE:
692280304Sjkim		if (ifinfo->probes < MAX_RTR_SOLICITATIONS)
693238384Sjkim			ifinfo->timer.tv_sec = RTR_SOLICITATION_INTERVAL;
694280304Sjkim		else {
695280304Sjkim			/*
696280304Sjkim			 * After sending MAX_RTR_SOLICITATIONS solicitations,
697238384Sjkim			 * we're just waiting for possible replies; there
698280304Sjkim			 * will be no more solicitation.  Thus, we change
699280304Sjkim			 * the timer value to MAX_RTR_SOLICITATION_DELAY based
700280304Sjkim			 * on RFC 2461, Section 6.3.7.
701280304Sjkim			 */
702280304Sjkim			ifinfo->timer.tv_sec = MAX_RTR_SOLICITATION_DELAY;
703238384Sjkim		}
704280304Sjkim		break;
705280304Sjkim	default:
706238384Sjkim		warnmsg(LOG_ERR, __func__,
707280304Sjkim		    "illegal interface state(%d) on %s",
708280304Sjkim		    ifinfo->state, ifinfo->ifname);
709280304Sjkim		return;
710238384Sjkim	}
711280304Sjkim
712280304Sjkim	/* reset the timer */
713280304Sjkim	if (timercmp(&ifinfo->timer, &tm_max, ==)) {
714238384Sjkim		ifinfo->expire = tm_max;
715280304Sjkim		warnmsg(LOG_DEBUG, __func__,
716280304Sjkim		    "stop timer for %s", ifinfo->ifname);
717280304Sjkim	} else {
718280304Sjkim		gettimeofday(&now, NULL);
719280304Sjkim		timeradd(&now, &ifinfo->timer, &ifinfo->expire);
720280304Sjkim
721280304Sjkim		if (dflag > 1)
722280304Sjkim			warnmsg(LOG_DEBUG, __func__,
723280304Sjkim			    "set timer for %s to %d:%d", ifinfo->ifname,
724280304Sjkim			    (int)ifinfo->timer.tv_sec,
725280304Sjkim			    (int)ifinfo->timer.tv_usec);
726280304Sjkim	}
727280304Sjkim
728280304Sjkim#undef MILLION
729280304Sjkim}
730280304Sjkim
731280304Sjkim/* timer related utility functions */
732280304Sjkim#define MILLION 1000000
733280304Sjkim
734280304Sjkim#ifndef SMALL
735238384Sjkimstatic void
736280304Sjkimrtsold_set_dump_file(int sig)
737238384Sjkim{
738238384Sjkim	do_dump = 1;
739238384Sjkim}
740238384Sjkim#endif
741238384Sjkim
742238384Sjkimstatic void
743280304Sjkimusage(char *progname)
744280304Sjkim{
745280304Sjkim	if (progname && progname[strlen(progname) - 1] != 'd') {
746280304Sjkim		fprintf(stderr, "usage: rtsol [-dDF] interfaces...\n");
747280304Sjkim		fprintf(stderr, "usage: rtsol [-dDF] -a\n");
748280304Sjkim	} else {
749280304Sjkim		fprintf(stderr, "usage: rtsold [-adDfFm1] interfaces...\n");
750280304Sjkim		fprintf(stderr, "usage: rtsold [-dDfFm1] -a\n");
751280304Sjkim	}
752280304Sjkim	exit(1);
753280304Sjkim}
754280304Sjkim
755280304Sjkimvoid
756280304Sjkim#if __STDC__
757280304Sjkimwarnmsg(int priority, const char *func, const char *msg, ...)
758280304Sjkim#else
759238384Sjkimwarnmsg(priority, func, msg, va_alist)
760280304Sjkim	int priority;
761280304Sjkim	const char *func;
762280304Sjkim	const char *msg;
763280304Sjkim	va_dcl
764238384Sjkim#endif
765280304Sjkim{
766280304Sjkim	va_list ap;
767280304Sjkim	char buf[BUFSIZ];
768280304Sjkim
769280304Sjkim	va_start(ap, msg);
770238384Sjkim	if (fflag) {
771280304Sjkim		if (priority <= log_upto) {
772280304Sjkim			(void)vfprintf(stderr, msg, ap);
773238384Sjkim			(void)fprintf(stderr, "\n");
774280304Sjkim		}
775280304Sjkim	} else {
776280304Sjkim		snprintf(buf, sizeof(buf), "<%s> %s", func, msg);
777238384Sjkim		msg = buf;
778280304Sjkim		vsyslog(priority, msg, ap);
779280304Sjkim	}
780280304Sjkim	va_end(ap);
781238384Sjkim}
782280304Sjkim
783280304Sjkim/*
784280304Sjkim * return a list of interfaces which is suitable to sending an RS.
785280304Sjkim */
786280304Sjkimchar **
787280304Sjkimautoifprobe(void)
788280304Sjkim{
789280304Sjkim	static char **argv = NULL;
790280304Sjkim	static int n = 0;
791280304Sjkim	char **a;
792280304Sjkim	int i, found;
793280304Sjkim	struct ifaddrs *ifap, *ifa, *target;
794280304Sjkim
795280304Sjkim	/* initialize */
796280304Sjkim	while (n--)
797280304Sjkim		free(argv[n]);
798280304Sjkim	if (argv) {
799280304Sjkim		free(argv);
800280304Sjkim		argv = NULL;
801280304Sjkim	}
802238384Sjkim	n = 0;
803280304Sjkim
804238384Sjkim	if (getifaddrs(&ifap) != 0)
805238384Sjkim		return NULL;
806280304Sjkim
807280304Sjkim	target = NULL;
808280304Sjkim	/* find an ethernet */
809238384Sjkim	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
810280304Sjkim		if ((ifa->ifa_flags & IFF_UP) == 0)
811238384Sjkim			continue;
812280304Sjkim		if ((ifa->ifa_flags & IFF_POINTOPOINT) != 0)
813280304Sjkim			continue;
814280304Sjkim		if ((ifa->ifa_flags & IFF_LOOPBACK) != 0)
815280304Sjkim			continue;
816280304Sjkim		if ((ifa->ifa_flags & IFF_MULTICAST) == 0)
817280304Sjkim			continue;
818280304Sjkim
819280304Sjkim		if (ifa->ifa_addr->sa_family != AF_INET6)
820280304Sjkim			continue;
821280304Sjkim
822280304Sjkim		found = 0;
823280304Sjkim		for (i = 0; i < n; i++) {
824280304Sjkim			if (strcmp(argv[i], ifa->ifa_name) == 0) {
825280304Sjkim				found++;
826280304Sjkim				break;
827280304Sjkim			}
828280304Sjkim		}
829280304Sjkim		if (found)
830280304Sjkim			continue;
831280304Sjkim
832280304Sjkim		/* if we find multiple candidates, just warn. */
833280304Sjkim		if (n != 0 && dflag > 1)
834280304Sjkim			warnx("multiple interfaces found");
835280304Sjkim
836238384Sjkim		a = (char **)realloc(argv, (n + 1) * sizeof(char **));
837280304Sjkim		if (a == NULL)
838280304Sjkim			err(1, "realloc");
839280304Sjkim		argv = a;
840280304Sjkim		argv[n] = strdup(ifa->ifa_name);
841280304Sjkim		if (!argv[n])
842280304Sjkim			err(1, "malloc");
843280304Sjkim		n++;
844280304Sjkim		argv[n] = NULL;
845280304Sjkim	}
846280304Sjkim
847280304Sjkim	if (n) {
848280304Sjkim		a = (char **)realloc(argv, (n + 1) * sizeof(char **));
849280304Sjkim		if (a == NULL)
850280304Sjkim			err(1, "realloc");
851280304Sjkim		argv = a;
852280304Sjkim		argv[n] = NULL;
853280304Sjkim
854280304Sjkim		if (dflag > 0) {
855280304Sjkim			for (i = 0; i < n; i++)
856280304Sjkim				warnx("probing %s", argv[i]);
857280304Sjkim		}
858280304Sjkim	}
859280304Sjkim	freeifaddrs(ifap);
860280304Sjkim	return argv;
861238384Sjkim}
862238384Sjkim