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$
3255163Sshin */
3355163Sshin
3455163Sshin#include <sys/types.h>
35203387Sume#include <sys/ioctl.h>
3662632Skris#include <sys/socket.h>
37118909Sume#include <sys/param.h>
3855163Sshin
3962632Skris#include <net/if.h>
4055163Sshin#include <net/if_dl.h>
41203387Sume#include <net/if_var.h>
4255163Sshin
4355163Sshin#include <netinet/in.h>
4455163Sshin#include <netinet/icmp6.h>
45203387Sume#include <netinet/in_var.h>
46222861Shrs#include <arpa/inet.h>
4755163Sshin
48203387Sume#include <netinet6/nd6.h>
49203387Sume
5055163Sshin#include <signal.h>
5155163Sshin#include <unistd.h>
5255163Sshin#include <syslog.h>
5355163Sshin#include <string.h>
5455163Sshin#include <stdlib.h>
5555163Sshin#include <stdio.h>
56253970Shrs#include <time.h>
5755163Sshin#include <errno.h>
5855163Sshin#include <err.h>
5955163Sshin#include <stdarg.h>
6066776Skris#include <ifaddrs.h>
61118916Sume#ifdef HAVE_POLL_H
62118916Sume#include <poll.h>
63118916Sume#endif
64118664Sume
6555163Sshin#include "rtsold.h"
6655163Sshin
67222732Shrs#define RTSOL_DUMPFILE	"/var/run/rtsold.dump";
68222732Shrs#define RTSOL_PIDFILE	"/var/run/rtsold.pid";
69222732Shrs
70253970Shrsstruct timespec tm_max;
71118664Sumestatic int log_upto = 999;
72118664Sumestatic int fflag = 0;
73118664Sume
74197141Shrsint Fflag = 0;	/* force setting sysctl parameters */
7566776Skrisint aflag = 0;
7666776Skrisint dflag = 0;
77225520Shrsint uflag = 0;
78118664Sume
79222732Shrsconst char *otherconf_script;
80222732Shrsconst char *resolvconf_script = "/sbin/resolvconf";
8155163Sshin
82147150Ssuz/* protocol constants */
8362632Skris#define MAX_RTR_SOLICITATION_DELAY	1 /* second */
8462632Skris#define RTR_SOLICITATION_INTERVAL	4 /* seconds */
8562632Skris#define MAX_RTR_SOLICITATIONS		3 /* times */
8655163Sshin
87118664Sume/*
88118910Sume * implementation dependent constants in seconds
89118664Sume * XXX: should be configurable
90118664Sume */
91118664Sume#define PROBE_INTERVAL 60
9255163Sshin
9355163Sshin/* static variables and functions */
9455163Sshinstatic int mobile_node = 0;
95222732Shrsstatic const char *pidfilename = RTSOL_PIDFILE;
96222732Shrs
97124526Sume#ifndef SMALL
9855163Sshinstatic int do_dump;
99222732Shrsstatic const char *dumpfilename = RTSOL_DUMPFILE;
100124526Sume#endif
10155163Sshin
10262632Skris#if 0
103173412Skevlostatic int ifreconfig(char *);
10462632Skris#endif
105222732Shrs
106173412Skevlostatic int make_packet(struct ifinfo *);
107253970Shrsstatic struct timespec *rtsol_check_timer(void);
10855163Sshin
109124526Sume#ifndef SMALL
110173412Skevlostatic void rtsold_set_dump_file(int);
111124526Sume#endif
112222732Shrsstatic void usage(void);
11355163Sshin
11455163Sshinint
115124524Sumemain(int argc, char **argv)
11655163Sshin{
117118909Sume	int s, ch, once = 0;
118253970Shrs	struct timespec *timeout;
119204407Suqs	const char *opts;
120118916Sume#ifdef HAVE_POLL_H
121118916Sume	struct pollfd set[2];
122118916Sume#else
123118909Sume	fd_set *fdsetp, *selectfdp;
124118909Sume	int fdmasks;
125118909Sume	int maxfd;
126118916Sume#endif
127118909Sume	int rtsock;
128222848Shrs	char *argv0;
12955163Sshin
130222732Shrs#ifndef SMALL
131222732Shrs	/* rtsold */
132225520Shrs	opts = "adDfFm1O:p:R:u";
133222732Shrs#else
134222732Shrs	/* rtsol */
135225520Shrs	opts = "adDFO:R:u";
136222732Shrs	fflag = 1;
137222732Shrs	once = 1;
138222732Shrs#endif
139222848Shrs	argv0 = argv[0];
140222848Shrs
14155163Sshin	while ((ch = getopt(argc, argv, opts)) != -1) {
14266776Skris		switch (ch) {
14366776Skris		case 'a':
14466776Skris			aflag = 1;
14566776Skris			break;
14666776Skris		case 'd':
147225520Shrs			dflag += 1;
14866776Skris			break;
14966776Skris		case 'D':
150225520Shrs			dflag += 2;
15166776Skris			break;
15266776Skris		case 'f':
15366776Skris			fflag = 1;
15466776Skris			break;
155124525Sume		case 'F':
156124525Sume			Fflag = 1;
157124525Sume			break;
15866776Skris		case 'm':
15966776Skris			mobile_node = 1;
16066776Skris			break;
16166776Skris		case '1':
16266776Skris			once = 1;
16366776Skris			break;
164118661Sume		case 'O':
165118661Sume			otherconf_script = optarg;
166118661Sume			break;
167225520Shrs		case 'p':
168222732Shrs			pidfilename = optarg;
169222732Shrs			break;
170222732Shrs		case 'R':
171222732Shrs			resolvconf_script = optarg;
172222732Shrs			break;
173225520Shrs		case 'u':
174225520Shrs			uflag = 1;
175225520Shrs			break;
17666776Skris		default:
177222732Shrs			usage();
178222732Shrs			exit(1);
17955163Sshin		}
18055163Sshin	}
18155163Sshin	argc -= optind;
18255163Sshin	argv += optind;
18366776Skris
184119026Sume	if ((!aflag && argc == 0) || (aflag && argc != 0)) {
185222732Shrs		usage();
186222732Shrs		exit(1);
18766776Skris	}
18855163Sshin
189253970Shrs	/* Generate maximum time in timespec. */
190253995Shrs	tm_max.tv_sec = (-1) & ~((time_t)1 << ((sizeof(tm_max.tv_sec) * 8) - 1));
191253995Shrs	tm_max.tv_nsec = (-1) & ~((long)1 << ((sizeof(tm_max.tv_nsec) * 8) - 1));
192253970Shrs
19355163Sshin	/* set log level */
194225520Shrs	if (dflag > 1)
195225520Shrs		log_upto = LOG_DEBUG;
196225520Shrs	else if (dflag > 0)
197225520Shrs		log_upto = LOG_INFO;
198225520Shrs	else
19955163Sshin		log_upto = LOG_NOTICE;
200225520Shrs
20155163Sshin	if (!fflag) {
20255163Sshin		char *ident;
203118664Sume
204222848Shrs		ident = strrchr(argv0, '/');
20555163Sshin		if (!ident)
206222848Shrs			ident = argv0;
20755163Sshin		else
20855163Sshin			ident++;
20955163Sshin		openlog(ident, LOG_NDELAY|LOG_PID, LOG_DAEMON);
21055163Sshin		if (log_upto >= 0)
21155163Sshin			setlogmask(LOG_UPTO(log_upto));
21255163Sshin	}
21355163Sshin
214118661Sume	if (otherconf_script && *otherconf_script != '/') {
215118661Sume		errx(1, "configuration script (%s) must be an absolute path",
216118661Sume		    otherconf_script);
217118661Sume	}
218222732Shrs	if (resolvconf_script && *resolvconf_script != '/') {
219222732Shrs		errx(1, "configuration script (%s) must be an absolute path",
220222732Shrs		    resolvconf_script);
221222732Shrs	}
222222732Shrs	if (pidfilename && *pidfilename != '/') {
223222732Shrs		errx(1, "pid filename (%s) must be an absolute path",
224222732Shrs		    pidfilename);
225222732Shrs	}
22655163Sshin
227225520Shrs#if (__FreeBSD_version < 900000)
228124525Sume	if (Fflag) {
229124525Sume		setinet6sysctl(IPV6CTL_FORWARDING, 0);
230124525Sume	} else {
231124525Sume		/* warn if forwarding is up */
232124525Sume		if (getinet6sysctl(IPV6CTL_FORWARDING))
233124525Sume			warnx("kernel is configured as a router, not a host");
234124525Sume	}
235225520Shrs#endif
23655163Sshin
237124526Sume#ifndef SMALL
23855163Sshin	/* initialization to dump internal status to a file */
239118910Sume	signal(SIGUSR1, rtsold_set_dump_file);
240124526Sume#endif
24155163Sshin
242118914Sume	if (!fflag)
243118914Sume		daemon(0, 0);		/* act as a daemon */
244118914Sume
24562632Skris	/*
24662632Skris	 * Open a socket for sending RS and receiving RA.
24762632Skris	 * This should be done before calling ifinit(), since the function
24862632Skris	 * uses the socket.
24962632Skris	 */
25066776Skris	if ((s = sockopen()) < 0) {
251118914Sume		warnmsg(LOG_ERR, __func__, "failed to open a socket");
252118914Sume		exit(1);
25366776Skris	}
254118916Sume#ifdef HAVE_POLL_H
255118916Sume	set[0].fd = s;
256118916Sume	set[0].events = POLLIN;
257118916Sume#else
25878064Sume	maxfd = s;
259118916Sume#endif
260118916Sume
261118916Sume#ifdef HAVE_POLL_H
262118916Sume	set[1].fd = -1;
263118916Sume#endif
264118916Sume
26578064Sume	if ((rtsock = rtsock_open()) < 0) {
266118914Sume		warnmsg(LOG_ERR, __func__, "failed to open a socket");
267118914Sume		exit(1);
26878064Sume	}
269118916Sume#ifdef HAVE_POLL_H
270118916Sume	set[1].fd = rtsock;
271118916Sume	set[1].events = POLLIN;
272118916Sume#else
27378064Sume	if (rtsock > maxfd)
27478064Sume		maxfd = rtsock;
275118916Sume#endif
27662632Skris
277118916Sume#ifndef HAVE_POLL_H
278118909Sume	fdmasks = howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask);
279118909Sume	if ((fdsetp = malloc(fdmasks)) == NULL) {
280222732Shrs		warnmsg(LOG_ERR, __func__, "malloc");
281222732Shrs		exit(1);
282118909Sume	}
283118909Sume	if ((selectfdp = malloc(fdmasks)) == NULL) {
284222732Shrs		warnmsg(LOG_ERR, __func__, "malloc");
285222732Shrs		exit(1);
286118909Sume	}
287118916Sume#endif
288118909Sume
28955163Sshin	/* configuration per interface */
29066776Skris	if (ifinit()) {
291118914Sume		warnmsg(LOG_ERR, __func__,
292147161Ssuz		    "failed to initialize interfaces");
293118914Sume		exit(1);
29466776Skris	}
295119026Sume	if (aflag)
296119026Sume		argv = autoifprobe();
297119026Sume	while (argv && *argv) {
29866776Skris		if (ifconfig(*argv)) {
299118914Sume			warnmsg(LOG_ERR, __func__,
300118914Sume			    "failed to initialize %s", *argv);
301118914Sume			exit(1);
30266776Skris		}
30355163Sshin		argv++;
30455163Sshin	}
30555163Sshin
30655163Sshin	/* setup for probing default routers */
30766776Skris	if (probe_init()) {
308118914Sume		warnmsg(LOG_ERR, __func__,
309118914Sume		    "failed to setup for probing routers");
310118914Sume		exit(1);
31166776Skris		/*NOTREACHED*/
31266776Skris	}
31355163Sshin
31455163Sshin	/* dump the current pid */
31555163Sshin	if (!once) {
31655163Sshin		pid_t pid = getpid();
31755163Sshin		FILE *fp;
31855163Sshin
31955163Sshin		if ((fp = fopen(pidfilename, "w")) == NULL)
320118660Sume			warnmsg(LOG_ERR, __func__,
321118664Sume			    "failed to open a pid log file(%s): %s",
322118664Sume			    pidfilename, strerror(errno));
32355163Sshin		else {
32455163Sshin			fprintf(fp, "%d\n", pid);
32555163Sshin			fclose(fp);
32655163Sshin		}
32755163Sshin	}
328118916Sume#ifndef HAVE_POLL_H
329118909Sume	memset(fdsetp, 0, fdmasks);
330118909Sume	FD_SET(s, fdsetp);
331118909Sume	FD_SET(rtsock, fdsetp);
332118916Sume#endif
33355163Sshin	while (1) {		/* main loop */
33455163Sshin		int e;
33555163Sshin
336118916Sume#ifndef HAVE_POLL_H
337118909Sume		memcpy(selectfdp, fdsetp, fdmasks);
338118916Sume#endif
339118909Sume
340124526Sume#ifndef SMALL
34155163Sshin		if (do_dump) {	/* SIGUSR1 */
34255163Sshin			do_dump = 0;
34355163Sshin			rtsold_dump_file(dumpfilename);
34455163Sshin		}
345124526Sume#endif
346118664Sume
34755163Sshin		timeout = rtsol_check_timer();
34855163Sshin
34955163Sshin		if (once) {
35055163Sshin			struct ifinfo *ifi;
35155163Sshin
35255163Sshin			/* if we have no timeout, we are done (or failed) */
35355163Sshin			if (timeout == NULL)
35455163Sshin				break;
35555163Sshin
35655163Sshin			/* if all interfaces have got RA packet, we are done */
357222732Shrs			TAILQ_FOREACH(ifi, &ifinfo_head, ifi_next) {
35855163Sshin				if (ifi->state != IFS_DOWN && ifi->racnt == 0)
35955163Sshin					break;
36055163Sshin			}
36155163Sshin			if (ifi == NULL)
36255163Sshin				break;
36355163Sshin		}
364118916Sume#ifdef HAVE_POLL_H
365253970Shrs		e = poll(set, 2, timeout ? (timeout->tv_sec * 1000 + timeout->tv_nsec / 1000 / 1000) : INFTIM);
366118916Sume#else
367118909Sume		e = select(maxfd + 1, selectfdp, NULL, NULL, timeout);
368118916Sume#endif
36978064Sume		if (e < 1) {
37055163Sshin			if (e < 0 && errno != EINTR) {
371118660Sume				warnmsg(LOG_ERR, __func__, "select: %s",
372118664Sume				    strerror(errno));
37355163Sshin			}
37455163Sshin			continue;
37555163Sshin		}
37655163Sshin
37755163Sshin		/* packet reception */
378118916Sume#ifdef HAVE_POLL_H
379118916Sume		if (set[1].revents & POLLIN)
380118916Sume#else
381118909Sume		if (FD_ISSET(rtsock, selectfdp))
382118916Sume#endif
38378064Sume			rtsock_input(rtsock);
384118916Sume#ifdef HAVE_POLL_H
385118916Sume		if (set[0].revents & POLLIN)
386118916Sume#else
387118909Sume		if (FD_ISSET(s, selectfdp))
388118916Sume#endif
38955163Sshin			rtsol_input(s);
39055163Sshin	}
39155163Sshin	/* NOTREACHED */
39255163Sshin
393222732Shrs	return (0);
39455163Sshin}
39555163Sshin
396119026Sumeint
39755163Sshinifconfig(char *ifname)
39855163Sshin{
399222732Shrs	struct ifinfo *ifi;
40055163Sshin	struct sockaddr_dl *sdl;
40155163Sshin	int flags;
40255163Sshin
40355163Sshin	if ((sdl = if_nametosdl(ifname)) == NULL) {
404118660Sume		warnmsg(LOG_ERR, __func__,
405118664Sume		    "failed to get link layer information for %s", ifname);
406222732Shrs		return (-1);
40755163Sshin	}
40855163Sshin	if (find_ifinfo(sdl->sdl_index)) {
409118660Sume		warnmsg(LOG_ERR, __func__,
410118664Sume		    "interface %s was already configured", ifname);
41162632Skris		free(sdl);
412222732Shrs		return (-1);
41355163Sshin	}
41455163Sshin
415225520Shrs	if (Fflag) {
416225520Shrs		struct in6_ndireq nd;
417225520Shrs		int s;
418225520Shrs
419225520Shrs		if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
420225520Shrs			warnmsg(LOG_ERR, __func__, "socket() failed.");
421225520Shrs			return (-1);
422225520Shrs		}
423225520Shrs		memset(&nd, 0, sizeof(nd));
424225520Shrs		strlcpy(nd.ifname, ifname, sizeof(nd.ifname));
425225520Shrs		if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) {
426225520Shrs			warnmsg(LOG_ERR, __func__,
427225520Shrs			    "cannot get accept_rtadv flag");
428225520Shrs			close(s);
429225520Shrs			return (-1);
430225520Shrs		}
431225520Shrs		nd.ndi.flags |= ND6_IFF_ACCEPT_RTADV;
432225520Shrs		if (ioctl(s, SIOCSIFINFO_IN6, (caddr_t)&nd) < 0) {
433225520Shrs			warnmsg(LOG_ERR, __func__,
434225520Shrs			    "cannot set accept_rtadv flag");
435225520Shrs			close(s);
436225520Shrs			return (-1);
437225520Shrs		}
438225520Shrs		close(s);
439225520Shrs	}
440225520Shrs
441222732Shrs	if ((ifi = malloc(sizeof(*ifi))) == NULL) {
442118660Sume		warnmsg(LOG_ERR, __func__, "memory allocation failed");
44362632Skris		free(sdl);
444222732Shrs		return (-1);
44555163Sshin	}
446222732Shrs	memset(ifi, 0, sizeof(*ifi));
447222732Shrs	ifi->sdl = sdl;
448222861Shrs	ifi->ifi_rdnss = IFI_DNSOPT_STATE_NOINFO;
449222861Shrs	ifi->ifi_dnssl = IFI_DNSOPT_STATE_NOINFO;
450222861Shrs	TAILQ_INIT(&ifi->ifi_rainfo);
451222732Shrs	strlcpy(ifi->ifname, ifname, sizeof(ifi->ifname));
45255163Sshin
45355163Sshin	/* construct a router solicitation message */
454222732Shrs	if (make_packet(ifi))
45555163Sshin		goto bad;
45655163Sshin
457119026Sume	/* set link ID of this interface. */
458119026Sume#ifdef HAVE_SCOPELIB
459222732Shrs	if (inet_zoneid(AF_INET6, 2, ifname, &ifi->linkid))
460119026Sume		goto bad;
461119026Sume#else
462119026Sume	/* XXX: assume interface IDs as link IDs */
463222732Shrs	ifi->linkid = ifi->sdl->sdl_index;
464119026Sume#endif
465119026Sume
46655163Sshin	/*
46755163Sshin	 * check if the interface is available.
46855163Sshin	 * also check if SIOCGIFMEDIA ioctl is OK on the interface.
46955163Sshin	 */
470222732Shrs	ifi->mediareqok = 1;
471222732Shrs	ifi->active = interface_status(ifi);
472222732Shrs	if (!ifi->mediareqok) {
47355163Sshin		/*
47455163Sshin		 * probe routers periodically even if the link status
47555163Sshin		 * does not change.
47655163Sshin		 */
477222732Shrs		ifi->probeinterval = PROBE_INTERVAL;
47855163Sshin	}
47955163Sshin
48055163Sshin	/* activate interface: interface_up returns 0 on success */
481222732Shrs	flags = interface_up(ifi->ifname);
48255163Sshin	if (flags == 0)
483222732Shrs		ifi->state = IFS_DELAY;
48455163Sshin	else if (flags == IFS_TENTATIVE)
485222732Shrs		ifi->state = IFS_TENTATIVE;
48655163Sshin	else
487222732Shrs		ifi->state = IFS_DOWN;
48855163Sshin
489222732Shrs	rtsol_timer_update(ifi);
49055163Sshin
491222732Shrs	TAILQ_INSERT_TAIL(&ifinfo_head, ifi, ifi_next);
492222732Shrs	return (0);
49355163Sshin
494118664Sumebad:
495222732Shrs	free(ifi->sdl);
496222732Shrs	free(ifi);
497222732Shrs	return (-1);
49855163Sshin}
49955163Sshin
500119026Sumevoid
501124524Sumeiflist_init(void)
502119026Sume{
503222732Shrs	struct ifinfo *ifi;
504119026Sume
505222732Shrs	while ((ifi = TAILQ_FIRST(&ifinfo_head)) != NULL) {
506222732Shrs		TAILQ_REMOVE(&ifinfo_head, ifi, ifi_next);
507222732Shrs		if (ifi->sdl != NULL)
508119026Sume			free(ifi->sdl);
509222732Shrs		if (ifi->rs_data != NULL)
510119026Sume			free(ifi->rs_data);
511119026Sume		free(ifi);
512119026Sume	}
513119026Sume}
514119026Sume
51562632Skris#if 0
51662632Skrisstatic int
51762632Skrisifreconfig(char *ifname)
51862632Skris{
51962632Skris	struct ifinfo *ifi, *prev;
52062632Skris	int rv;
52162632Skris
52262632Skris	prev = NULL;
523222732Shrs	TAILQ_FOREACH(ifi, &ifinfo_head, ifi_next) {
52462632Skris		if (strncmp(ifi->ifname, ifname, sizeof(ifi->ifname)) == 0)
52562632Skris			break;
52662632Skris		prev = ifi;
52762632Skris	}
52862632Skris	prev->next = ifi->next;
52962632Skris
53062632Skris	rv = ifconfig(ifname);
53162632Skris
53262632Skris	/* reclaim it after ifconfig() in case ifname is pointer inside ifi */
53362632Skris	if (ifi->rs_data)
53462632Skris		free(ifi->rs_data);
53562632Skris	free(ifi->sdl);
53662632Skris	free(ifi);
537222732Shrs
538222732Shrs	return (rv);
53962632Skris}
54062632Skris#endif
54162632Skris
542222861Shrsstruct rainfo *
543222861Shrsfind_rainfo(struct ifinfo *ifi, struct sockaddr_in6 *sin6)
544222861Shrs{
545222861Shrs	struct rainfo *rai;
546222861Shrs
547222861Shrs	TAILQ_FOREACH(rai, &ifi->ifi_rainfo, rai_next)
548222861Shrs		if (memcmp(&rai->rai_saddr.sin6_addr, &sin6->sin6_addr,
549222861Shrs		    sizeof(rai->rai_saddr.sin6_addr)) == 0)
550222861Shrs			return (rai);
551222861Shrs
552222861Shrs	return (NULL);
553222861Shrs}
554222861Shrs
55555163Sshinstruct ifinfo *
55655163Sshinfind_ifinfo(int ifindex)
55755163Sshin{
55855163Sshin	struct ifinfo *ifi;
55955163Sshin
560222732Shrs	TAILQ_FOREACH(ifi, &ifinfo_head, ifi_next) {
56155163Sshin		if (ifi->sdl->sdl_index == ifindex)
562222732Shrs			return (ifi);
563222732Shrs	}
564222732Shrs	return (NULL);
56555163Sshin}
56655163Sshin
56755163Sshinstatic int
568222732Shrsmake_packet(struct ifinfo *ifi)
56955163Sshin{
570118664Sume	size_t packlen = sizeof(struct nd_router_solicit), lladdroptlen = 0;
571118664Sume	struct nd_router_solicit *rs;
57255163Sshin	char *buf;
57355163Sshin
574222732Shrs	if ((lladdroptlen = lladdropt_length(ifi->sdl)) == 0) {
575118660Sume		warnmsg(LOG_INFO, __func__,
576118664Sume		    "link-layer address option has null length"
577222732Shrs		    " on %s. Treat as not included.", ifi->ifname);
57855163Sshin	}
57955163Sshin	packlen += lladdroptlen;
580222732Shrs	ifi->rs_datalen = packlen;
58155163Sshin
58255163Sshin	/* allocate buffer */
58355163Sshin	if ((buf = malloc(packlen)) == NULL) {
584118660Sume		warnmsg(LOG_ERR, __func__,
585222732Shrs		    "memory allocation failed for %s", ifi->ifname);
586222732Shrs		return (-1);
58755163Sshin	}
588222732Shrs	ifi->rs_data = buf;
58955163Sshin
59055163Sshin	/* fill in the message */
59155163Sshin	rs = (struct nd_router_solicit *)buf;
59255163Sshin	rs->nd_rs_type = ND_ROUTER_SOLICIT;
59355163Sshin	rs->nd_rs_code = 0;
59455163Sshin	rs->nd_rs_cksum = 0;
59555163Sshin	rs->nd_rs_reserved = 0;
59655163Sshin	buf += sizeof(*rs);
59755163Sshin
59855163Sshin	/* fill in source link-layer address option */
59955163Sshin	if (lladdroptlen)
600222732Shrs		lladdropt_fill(ifi->sdl, (struct nd_opt_hdr *)buf);
60155163Sshin
602222732Shrs	return (0);
60355163Sshin}
60455163Sshin
605253970Shrsstatic struct timespec *
606124524Sumertsol_check_timer(void)
60755163Sshin{
608253970Shrs	static struct timespec returnval;
609253970Shrs	struct timespec now, rtsol_timer;
610222732Shrs	struct ifinfo *ifi;
611222861Shrs	struct rainfo *rai;
612295898Smarkj	struct ra_opt *rao, *raotmp;
61355163Sshin	int flags;
61455163Sshin
615253970Shrs	clock_gettime(CLOCK_MONOTONIC_FAST, &now);
61655163Sshin
61755163Sshin	rtsol_timer = tm_max;
61855163Sshin
619222732Shrs	TAILQ_FOREACH(ifi, &ifinfo_head, ifi_next) {
620253970Shrs		if (TS_CMP(&ifi->expire, &now, <=)) {
621222861Shrs			warnmsg(LOG_DEBUG, __func__, "timer expiration on %s, "
622222861Shrs			    "state = %d", ifi->ifname, ifi->state);
62355163Sshin
624222861Shrs			while((rai = TAILQ_FIRST(&ifi->ifi_rainfo)) != NULL) {
625222861Shrs				/* Remove all RA options. */
626222861Shrs				TAILQ_REMOVE(&ifi->ifi_rainfo, rai, rai_next);
627222861Shrs				while ((rao = TAILQ_FIRST(&rai->rai_ra_opt)) !=
628222861Shrs				    NULL) {
629222861Shrs					TAILQ_REMOVE(&rai->rai_ra_opt, rao,
630222861Shrs					    rao_next);
631222861Shrs					if (rao->rao_msg != NULL)
632222861Shrs						free(rao->rao_msg);
633222861Shrs					free(rao);
634222861Shrs				}
635222861Shrs				free(rai);
636222732Shrs			}
637222732Shrs			switch (ifi->state) {
63855163Sshin			case IFS_DOWN:
63955163Sshin			case IFS_TENTATIVE:
64055163Sshin				/* interface_up returns 0 on success */
641222732Shrs				flags = interface_up(ifi->ifname);
64255163Sshin				if (flags == 0)
643222732Shrs					ifi->state = IFS_DELAY;
64455163Sshin				else if (flags == IFS_TENTATIVE)
645222732Shrs					ifi->state = IFS_TENTATIVE;
64655163Sshin				else
647222732Shrs					ifi->state = IFS_DOWN;
64855163Sshin				break;
64955163Sshin			case IFS_IDLE:
65055163Sshin			{
651222732Shrs				int oldstatus = ifi->active;
65255163Sshin				int probe = 0;
65355163Sshin
654222732Shrs				ifi->active = interface_status(ifi);
65555163Sshin
656222732Shrs				if (oldstatus != ifi->active) {
657118660Sume					warnmsg(LOG_DEBUG, __func__,
658118664Sume					    "%s status is changed"
659118664Sume					    " from %d to %d",
660222732Shrs					    ifi->ifname,
661222732Shrs					    oldstatus, ifi->active);
66255163Sshin					probe = 1;
663222732Shrs					ifi->state = IFS_DELAY;
664222732Shrs				} else if (ifi->probeinterval &&
665222732Shrs				    (ifi->probetimer -=
666222732Shrs				    ifi->timer.tv_sec) <= 0) {
66755163Sshin					/* probe timer expired */
668222732Shrs					ifi->probetimer =
669222732Shrs					    ifi->probeinterval;
67055163Sshin					probe = 1;
671222732Shrs					ifi->state = IFS_PROBE;
67255163Sshin				}
67355163Sshin
674118661Sume				/*
675118661Sume				 * If we need a probe, clear the previous
676118661Sume				 * status wrt the "other" configuration.
677118661Sume				 */
678118661Sume				if (probe)
679222732Shrs					ifi->otherconfig = 0;
680118661Sume
68155163Sshin				if (probe && mobile_node)
682222732Shrs					defrouter_probe(ifi);
68355163Sshin				break;
68455163Sshin			}
68555163Sshin			case IFS_DELAY:
686222732Shrs				ifi->state = IFS_PROBE;
687222732Shrs				sendpacket(ifi);
68855163Sshin				break;
68955163Sshin			case IFS_PROBE:
690222732Shrs				if (ifi->probes < MAX_RTR_SOLICITATIONS)
691222732Shrs					sendpacket(ifi);
69255163Sshin				else {
693118660Sume					warnmsg(LOG_INFO, __func__,
694118664Sume					    "No answer after sending %d RSs",
695222732Shrs					    ifi->probes);
696222732Shrs					ifi->probes = 0;
697222732Shrs					ifi->state = IFS_IDLE;
69855163Sshin				}
69955163Sshin				break;
70055163Sshin			}
701222732Shrs			rtsol_timer_update(ifi);
702222732Shrs		} else {
703222732Shrs			/* Expiration check for RA options. */
704222732Shrs			int expire = 0;
705222732Shrs
706222861Shrs			TAILQ_FOREACH(rai, &ifi->ifi_rainfo, rai_next) {
707295898Smarkj				TAILQ_FOREACH_SAFE(rao, &rai->rai_ra_opt,
708295898Smarkj				    rao_next, raotmp) {
709222732Shrs					warnmsg(LOG_DEBUG, __func__,
710222861Shrs					    "RA expiration timer: "
711222861Shrs					    "type=%d, msg=%s, expire=%s",
712222861Shrs					    rao->rao_type, (char *)rao->rao_msg,
713222861Shrs						sec2str(&rao->rao_expire));
714253970Shrs					if (TS_CMP(&now, &rao->rao_expire,
715222861Shrs					    >=)) {
716222861Shrs						warnmsg(LOG_DEBUG, __func__,
717222861Shrs						    "RA expiration timer: "
718222861Shrs						    "expired.");
719222861Shrs						TAILQ_REMOVE(&rai->rai_ra_opt,
720222861Shrs						    rao, rao_next);
721222861Shrs						if (rao->rao_msg != NULL)
722222861Shrs							free(rao->rao_msg);
723222861Shrs						free(rao);
724222861Shrs						expire = 1;
725222861Shrs					}
726222732Shrs				}
727222732Shrs			}
728222732Shrs			if (expire)
729222732Shrs				ra_opt_handler(ifi);
73055163Sshin		}
731253970Shrs		if (TS_CMP(&ifi->expire, &rtsol_timer, <))
732222732Shrs			rtsol_timer = ifi->expire;
73355163Sshin	}
73455163Sshin
735253970Shrs	if (TS_CMP(&rtsol_timer, &tm_max, ==)) {
736118660Sume		warnmsg(LOG_DEBUG, __func__, "there is no timer");
737222732Shrs		return (NULL);
738253970Shrs	} else if (TS_CMP(&rtsol_timer, &now, <))
73955163Sshin		/* this may occur when the interval is too small */
740253970Shrs		returnval.tv_sec = returnval.tv_nsec = 0;
74155163Sshin	else
742253970Shrs		TS_SUB(&rtsol_timer, &now, &returnval);
74355163Sshin
744222861Shrs	now.tv_sec += returnval.tv_sec;
745253970Shrs	now.tv_nsec += returnval.tv_nsec;
746222861Shrs	warnmsg(LOG_DEBUG, __func__, "New timer is %s",
747222861Shrs	    sec2str(&now));
74855163Sshin
749222732Shrs	return (&returnval);
75055163Sshin}
75155163Sshin
75255163Sshinvoid
753222732Shrsrtsol_timer_update(struct ifinfo *ifi)
75455163Sshin{
75562632Skris#define MILLION 1000000
75662632Skris#define DADRETRY 10		/* XXX: adhoc */
75755163Sshin	long interval;
758253970Shrs	struct timespec now;
75955163Sshin
760222732Shrs	bzero(&ifi->timer, sizeof(ifi->timer));
76155163Sshin
762222732Shrs	switch (ifi->state) {
76355163Sshin	case IFS_DOWN:
76455163Sshin	case IFS_TENTATIVE:
765222732Shrs		if (++ifi->dadcount > DADRETRY) {
766222732Shrs			ifi->dadcount = 0;
767222732Shrs			ifi->timer.tv_sec = PROBE_INTERVAL;
768118664Sume		} else
769222732Shrs			ifi->timer.tv_sec = 1;
77055163Sshin		break;
77155163Sshin	case IFS_IDLE:
77255163Sshin		if (mobile_node) {
773118664Sume			/* XXX should be configurable */
774222732Shrs			ifi->timer.tv_sec = 3;
77555163Sshin		}
77655163Sshin		else
777222732Shrs			ifi->timer = tm_max;	/* stop timer(valid?) */
77855163Sshin		break;
77955163Sshin	case IFS_DELAY:
780180824Sache		interval = arc4random_uniform(MAX_RTR_SOLICITATION_DELAY * MILLION);
781222732Shrs		ifi->timer.tv_sec = interval / MILLION;
782253970Shrs		ifi->timer.tv_nsec = (interval % MILLION) * 1000;
78355163Sshin		break;
78455163Sshin	case IFS_PROBE:
785222732Shrs		if (ifi->probes < MAX_RTR_SOLICITATIONS)
786222732Shrs			ifi->timer.tv_sec = RTR_SOLICITATION_INTERVAL;
78778064Sume		else {
78878064Sume			/*
78978064Sume			 * After sending MAX_RTR_SOLICITATIONS solicitations,
79078064Sume			 * we're just waiting for possible replies; there
791147150Ssuz			 * will be no more solicitation.  Thus, we change
79278064Sume			 * the timer value to MAX_RTR_SOLICITATION_DELAY based
79378064Sume			 * on RFC 2461, Section 6.3.7.
79478064Sume			 */
795222732Shrs			ifi->timer.tv_sec = MAX_RTR_SOLICITATION_DELAY;
79678064Sume		}
79755163Sshin		break;
79855163Sshin	default:
799118660Sume		warnmsg(LOG_ERR, __func__,
800118664Sume		    "illegal interface state(%d) on %s",
801222732Shrs		    ifi->state, ifi->ifname);
80255163Sshin		return;
80355163Sshin	}
80455163Sshin
80555163Sshin	/* reset the timer */
806253970Shrs	if (TS_CMP(&ifi->timer, &tm_max, ==)) {
807222732Shrs		ifi->expire = tm_max;
808118660Sume		warnmsg(LOG_DEBUG, __func__,
809222732Shrs		    "stop timer for %s", ifi->ifname);
810118664Sume	} else {
811253970Shrs		clock_gettime(CLOCK_MONOTONIC_FAST, &now);
812253970Shrs		TS_ADD(&now, &ifi->timer, &ifi->expire);
81355163Sshin
814222861Shrs		now.tv_sec += ifi->timer.tv_sec;
815253970Shrs		now.tv_nsec += ifi->timer.tv_nsec;
816222861Shrs		warnmsg(LOG_DEBUG, __func__, "set timer for %s to %s",
817222861Shrs		    ifi->ifname, sec2str(&now));
81855163Sshin	}
81955163Sshin
82055163Sshin#undef MILLION
82155163Sshin}
82255163Sshin
82355163Sshin/* timer related utility functions */
82462632Skris#define MILLION 1000000
82555163Sshin
826124526Sume#ifndef SMALL
82755163Sshinstatic void
828204407Suqsrtsold_set_dump_file(int sig __unused)
82955163Sshin{
83055163Sshin	do_dump = 1;
83155163Sshin}
832124526Sume#endif
83355163Sshin
83455163Sshinstatic void
835222732Shrsusage(void)
83655163Sshin{
837222732Shrs#ifndef SMALL
838222732Shrs	fprintf(stderr, "usage: rtsold [-dDfFm1] [-O script-name] "
839290576Sngie	    "[-p pidfile] [-R script-name] interface ...\n");
840290576Sngie	fprintf(stderr, "usage: rtsold [-dDfFm1] [-O script-name] "
841290576Sngie	    "[-p pidfile] [-R script-name] -a\n");
842222732Shrs#else
843222732Shrs	fprintf(stderr, "usage: rtsol [-dDF] [-O script-name] "
844290576Sngie	    "[-p pidfile] [-R script-name] interface ...\n");
845222732Shrs	fprintf(stderr, "usage: rtsol [-dDF] [-O script-name] "
846290576Sngie	    "[-p pidfile] [-R script-name] -a\n");
847222732Shrs#endif
84855163Sshin}
84955163Sshin
85055163Sshinvoid
85155163Sshinwarnmsg(int priority, const char *func, const char *msg, ...)
85255163Sshin{
85355163Sshin	va_list ap;
85455163Sshin	char buf[BUFSIZ];
85555163Sshin
85655163Sshin	va_start(ap, msg);
85755163Sshin	if (fflag) {
85855163Sshin		if (priority <= log_upto) {
85955163Sshin			(void)vfprintf(stderr, msg, ap);
86055163Sshin			(void)fprintf(stderr, "\n");
86155163Sshin		}
86255163Sshin	} else {
86355163Sshin		snprintf(buf, sizeof(buf), "<%s> %s", func, msg);
86466776Skris		msg = buf;
86566776Skris		vsyslog(priority, msg, ap);
86655163Sshin	}
86755163Sshin	va_end(ap);
86855163Sshin}
86966776Skris
870119026Sume/*
871119026Sume * return a list of interfaces which is suitable to sending an RS.
872119026Sume */
873119026Sumechar **
874124524Sumeautoifprobe(void)
87566776Skris{
876119026Sume	static char **argv = NULL;
877119026Sume	static int n = 0;
878119026Sume	char **a;
879204407Suqs	int s = 0, i, found;
880230357Seadler	struct ifaddrs *ifap, *ifa;
881203387Sume	struct in6_ndireq nd;
88266776Skris
883119026Sume	/* initialize */
884119026Sume	while (n--)
885119026Sume		free(argv[n]);
886119026Sume	if (argv) {
887119026Sume		free(argv);
888119026Sume		argv = NULL;
889119026Sume	}
890119026Sume	n = 0;
891119026Sume
89266776Skris	if (getifaddrs(&ifap) != 0)
893222732Shrs		return (NULL);
89466776Skris
895203387Sume	if (!Fflag && (s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
896222732Shrs		warnmsg(LOG_ERR, __func__, "socket");
897222732Shrs		exit(1);
898203387Sume	}
899203387Sume
90066776Skris	/* find an ethernet */
90166776Skris	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
90266776Skris		if ((ifa->ifa_flags & IFF_UP) == 0)
90366776Skris			continue;
90466776Skris		if ((ifa->ifa_flags & IFF_POINTOPOINT) != 0)
90566776Skris			continue;
90666776Skris		if ((ifa->ifa_flags & IFF_LOOPBACK) != 0)
90766776Skris			continue;
90866776Skris		if ((ifa->ifa_flags & IFF_MULTICAST) == 0)
90966776Skris			continue;
91066776Skris
91166776Skris		if (ifa->ifa_addr->sa_family != AF_INET6)
91266776Skris			continue;
91366776Skris
914119026Sume		found = 0;
915119026Sume		for (i = 0; i < n; i++) {
916119026Sume			if (strcmp(argv[i], ifa->ifa_name) == 0) {
917119026Sume				found++;
918119026Sume				break;
919119026Sume			}
920119026Sume		}
921119026Sume		if (found)
92266776Skris			continue;
92366776Skris
924203387Sume		/*
925203387Sume		 * Skip the interfaces which IPv6 and/or accepting RA
926203387Sume		 * is disabled.
927203387Sume		 */
928203387Sume		if (!Fflag) {
929203387Sume			memset(&nd, 0, sizeof(nd));
930203387Sume			strlcpy(nd.ifname, ifa->ifa_name, sizeof(nd.ifname));
931203387Sume			if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) {
932222732Shrs				warnmsg(LOG_ERR, __func__,
933222732Shrs					"ioctl(SIOCGIFINFO_IN6)");
934222732Shrs				exit(1);
935203387Sume			}
936203387Sume			if ((nd.ndi.flags & ND6_IFF_IFDISABLED))
937203387Sume				continue;
938203387Sume			if (!(nd.ndi.flags & ND6_IFF_ACCEPT_RTADV))
939203387Sume				continue;
940203387Sume		}
941203387Sume
942119026Sume		/* if we find multiple candidates, just warn. */
943119026Sume		if (n != 0 && dflag > 1)
944222732Shrs			warnmsg(LOG_WARNING, __func__,
945222732Shrs				"multiple interfaces found");
946119026Sume
947119026Sume		a = (char **)realloc(argv, (n + 1) * sizeof(char **));
948222732Shrs		if (a == NULL) {
949222732Shrs			warnmsg(LOG_ERR, __func__, "realloc");
950222732Shrs			exit(1);
951222732Shrs		}
952119026Sume		argv = a;
953119026Sume		argv[n] = strdup(ifa->ifa_name);
954222732Shrs		if (!argv[n]) {
955222732Shrs			warnmsg(LOG_ERR, __func__, "malloc");
956222732Shrs			exit(1);
957222732Shrs		}
958119026Sume		n++;
95966776Skris	}
96066776Skris
961119026Sume	if (n) {
962119026Sume		a = (char **)realloc(argv, (n + 1) * sizeof(char **));
963222732Shrs		if (a == NULL) {
964222732Shrs			warnmsg(LOG_ERR, __func__, "realloc");
965222732Shrs			exit(1);
966222732Shrs		}
967119026Sume		argv = a;
968119026Sume		argv[n] = NULL;
96966776Skris
970119026Sume		if (dflag > 0) {
971119026Sume			for (i = 0; i < n; i++)
972222732Shrs				warnmsg(LOG_WARNING, __func__, "probing %s",
973222732Shrs					argv[i]);
974119026Sume		}
97566776Skris	}
976203387Sume	if (!Fflag)
977203387Sume		close(s);
97866776Skris	freeifaddrs(ifap);
979222732Shrs	return (argv);
98066776Skris}
981