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	}
22662632Skris#ifndef HAVE_ARC4RANDOM
227118664Sume	/* random value initialization */
22855163Sshin	srandom((u_long)time(NULL));
22962632Skris#endif
23055163Sshin
231225520Shrs#if (__FreeBSD_version < 900000)
232124525Sume	if (Fflag) {
233124525Sume		setinet6sysctl(IPV6CTL_FORWARDING, 0);
234124525Sume	} else {
235124525Sume		/* warn if forwarding is up */
236124525Sume		if (getinet6sysctl(IPV6CTL_FORWARDING))
237124525Sume			warnx("kernel is configured as a router, not a host");
238124525Sume	}
239225520Shrs#endif
24055163Sshin
241124526Sume#ifndef SMALL
24255163Sshin	/* initialization to dump internal status to a file */
243118910Sume	signal(SIGUSR1, rtsold_set_dump_file);
244124526Sume#endif
24555163Sshin
246118914Sume	if (!fflag)
247118914Sume		daemon(0, 0);		/* act as a daemon */
248118914Sume
24962632Skris	/*
25062632Skris	 * Open a socket for sending RS and receiving RA.
25162632Skris	 * This should be done before calling ifinit(), since the function
25262632Skris	 * uses the socket.
25362632Skris	 */
25466776Skris	if ((s = sockopen()) < 0) {
255118914Sume		warnmsg(LOG_ERR, __func__, "failed to open a socket");
256118914Sume		exit(1);
25766776Skris	}
258118916Sume#ifdef HAVE_POLL_H
259118916Sume	set[0].fd = s;
260118916Sume	set[0].events = POLLIN;
261118916Sume#else
26278064Sume	maxfd = s;
263118916Sume#endif
264118916Sume
265118916Sume#ifdef HAVE_POLL_H
266118916Sume	set[1].fd = -1;
267118916Sume#endif
268118916Sume
26978064Sume	if ((rtsock = rtsock_open()) < 0) {
270118914Sume		warnmsg(LOG_ERR, __func__, "failed to open a socket");
271118914Sume		exit(1);
27278064Sume	}
273118916Sume#ifdef HAVE_POLL_H
274118916Sume	set[1].fd = rtsock;
275118916Sume	set[1].events = POLLIN;
276118916Sume#else
27778064Sume	if (rtsock > maxfd)
27878064Sume		maxfd = rtsock;
279118916Sume#endif
28062632Skris
281118916Sume#ifndef HAVE_POLL_H
282118909Sume	fdmasks = howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask);
283118909Sume	if ((fdsetp = malloc(fdmasks)) == NULL) {
284222732Shrs		warnmsg(LOG_ERR, __func__, "malloc");
285222732Shrs		exit(1);
286118909Sume	}
287118909Sume	if ((selectfdp = malloc(fdmasks)) == NULL) {
288222732Shrs		warnmsg(LOG_ERR, __func__, "malloc");
289222732Shrs		exit(1);
290118909Sume	}
291118916Sume#endif
292118909Sume
29355163Sshin	/* configuration per interface */
29466776Skris	if (ifinit()) {
295118914Sume		warnmsg(LOG_ERR, __func__,
296147161Ssuz		    "failed to initialize interfaces");
297118914Sume		exit(1);
29866776Skris	}
299119026Sume	if (aflag)
300119026Sume		argv = autoifprobe();
301119026Sume	while (argv && *argv) {
30266776Skris		if (ifconfig(*argv)) {
303118914Sume			warnmsg(LOG_ERR, __func__,
304118914Sume			    "failed to initialize %s", *argv);
305118914Sume			exit(1);
30666776Skris		}
30755163Sshin		argv++;
30855163Sshin	}
30955163Sshin
31055163Sshin	/* setup for probing default routers */
31166776Skris	if (probe_init()) {
312118914Sume		warnmsg(LOG_ERR, __func__,
313118914Sume		    "failed to setup for probing routers");
314118914Sume		exit(1);
31566776Skris		/*NOTREACHED*/
31666776Skris	}
31755163Sshin
31855163Sshin	/* dump the current pid */
31955163Sshin	if (!once) {
32055163Sshin		pid_t pid = getpid();
32155163Sshin		FILE *fp;
32255163Sshin
32355163Sshin		if ((fp = fopen(pidfilename, "w")) == NULL)
324118660Sume			warnmsg(LOG_ERR, __func__,
325118664Sume			    "failed to open a pid log file(%s): %s",
326118664Sume			    pidfilename, strerror(errno));
32755163Sshin		else {
32855163Sshin			fprintf(fp, "%d\n", pid);
32955163Sshin			fclose(fp);
33055163Sshin		}
33155163Sshin	}
332118916Sume#ifndef HAVE_POLL_H
333118909Sume	memset(fdsetp, 0, fdmasks);
334118909Sume	FD_SET(s, fdsetp);
335118909Sume	FD_SET(rtsock, fdsetp);
336118916Sume#endif
33755163Sshin	while (1) {		/* main loop */
33855163Sshin		int e;
33955163Sshin
340118916Sume#ifndef HAVE_POLL_H
341118909Sume		memcpy(selectfdp, fdsetp, fdmasks);
342118916Sume#endif
343118909Sume
344124526Sume#ifndef SMALL
34555163Sshin		if (do_dump) {	/* SIGUSR1 */
34655163Sshin			do_dump = 0;
34755163Sshin			rtsold_dump_file(dumpfilename);
34855163Sshin		}
349124526Sume#endif
350118664Sume
35155163Sshin		timeout = rtsol_check_timer();
35255163Sshin
35355163Sshin		if (once) {
35455163Sshin			struct ifinfo *ifi;
35555163Sshin
35655163Sshin			/* if we have no timeout, we are done (or failed) */
35755163Sshin			if (timeout == NULL)
35855163Sshin				break;
35955163Sshin
36055163Sshin			/* if all interfaces have got RA packet, we are done */
361222732Shrs			TAILQ_FOREACH(ifi, &ifinfo_head, ifi_next) {
36255163Sshin				if (ifi->state != IFS_DOWN && ifi->racnt == 0)
36355163Sshin					break;
36455163Sshin			}
36555163Sshin			if (ifi == NULL)
36655163Sshin				break;
36755163Sshin		}
368118916Sume#ifdef HAVE_POLL_H
369253970Shrs		e = poll(set, 2, timeout ? (timeout->tv_sec * 1000 + timeout->tv_nsec / 1000 / 1000) : INFTIM);
370118916Sume#else
371118909Sume		e = select(maxfd + 1, selectfdp, NULL, NULL, timeout);
372118916Sume#endif
37378064Sume		if (e < 1) {
37455163Sshin			if (e < 0 && errno != EINTR) {
375118660Sume				warnmsg(LOG_ERR, __func__, "select: %s",
376118664Sume				    strerror(errno));
37755163Sshin			}
37855163Sshin			continue;
37955163Sshin		}
38055163Sshin
38155163Sshin		/* packet reception */
382118916Sume#ifdef HAVE_POLL_H
383118916Sume		if (set[1].revents & POLLIN)
384118916Sume#else
385118909Sume		if (FD_ISSET(rtsock, selectfdp))
386118916Sume#endif
38778064Sume			rtsock_input(rtsock);
388118916Sume#ifdef HAVE_POLL_H
389118916Sume		if (set[0].revents & POLLIN)
390118916Sume#else
391118909Sume		if (FD_ISSET(s, selectfdp))
392118916Sume#endif
39355163Sshin			rtsol_input(s);
39455163Sshin	}
39555163Sshin	/* NOTREACHED */
39655163Sshin
397222732Shrs	return (0);
39855163Sshin}
39955163Sshin
400119026Sumeint
40155163Sshinifconfig(char *ifname)
40255163Sshin{
403222732Shrs	struct ifinfo *ifi;
40455163Sshin	struct sockaddr_dl *sdl;
40555163Sshin	int flags;
40655163Sshin
40755163Sshin	if ((sdl = if_nametosdl(ifname)) == NULL) {
408118660Sume		warnmsg(LOG_ERR, __func__,
409118664Sume		    "failed to get link layer information for %s", ifname);
410222732Shrs		return (-1);
41155163Sshin	}
41255163Sshin	if (find_ifinfo(sdl->sdl_index)) {
413118660Sume		warnmsg(LOG_ERR, __func__,
414118664Sume		    "interface %s was already configured", ifname);
41562632Skris		free(sdl);
416222732Shrs		return (-1);
41755163Sshin	}
41855163Sshin
419225520Shrs	if (Fflag) {
420225520Shrs		struct in6_ndireq nd;
421225520Shrs		int s;
422225520Shrs
423225520Shrs		if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
424225520Shrs			warnmsg(LOG_ERR, __func__, "socket() failed.");
425225520Shrs			return (-1);
426225520Shrs		}
427225520Shrs		memset(&nd, 0, sizeof(nd));
428225520Shrs		strlcpy(nd.ifname, ifname, sizeof(nd.ifname));
429225520Shrs		if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) {
430225520Shrs			warnmsg(LOG_ERR, __func__,
431225520Shrs			    "cannot get accept_rtadv flag");
432225520Shrs			close(s);
433225520Shrs			return (-1);
434225520Shrs		}
435225520Shrs		nd.ndi.flags |= ND6_IFF_ACCEPT_RTADV;
436225520Shrs		if (ioctl(s, SIOCSIFINFO_IN6, (caddr_t)&nd) < 0) {
437225520Shrs			warnmsg(LOG_ERR, __func__,
438225520Shrs			    "cannot set accept_rtadv flag");
439225520Shrs			close(s);
440225520Shrs			return (-1);
441225520Shrs		}
442225520Shrs		close(s);
443225520Shrs	}
444225520Shrs
445222732Shrs	if ((ifi = malloc(sizeof(*ifi))) == NULL) {
446118660Sume		warnmsg(LOG_ERR, __func__, "memory allocation failed");
44762632Skris		free(sdl);
448222732Shrs		return (-1);
44955163Sshin	}
450222732Shrs	memset(ifi, 0, sizeof(*ifi));
451222732Shrs	ifi->sdl = sdl;
452222861Shrs	ifi->ifi_rdnss = IFI_DNSOPT_STATE_NOINFO;
453222861Shrs	ifi->ifi_dnssl = IFI_DNSOPT_STATE_NOINFO;
454222861Shrs	TAILQ_INIT(&ifi->ifi_rainfo);
455222732Shrs	strlcpy(ifi->ifname, ifname, sizeof(ifi->ifname));
45655163Sshin
45755163Sshin	/* construct a router solicitation message */
458222732Shrs	if (make_packet(ifi))
45955163Sshin		goto bad;
46055163Sshin
461119026Sume	/* set link ID of this interface. */
462119026Sume#ifdef HAVE_SCOPELIB
463222732Shrs	if (inet_zoneid(AF_INET6, 2, ifname, &ifi->linkid))
464119026Sume		goto bad;
465119026Sume#else
466119026Sume	/* XXX: assume interface IDs as link IDs */
467222732Shrs	ifi->linkid = ifi->sdl->sdl_index;
468119026Sume#endif
469119026Sume
47055163Sshin	/*
47155163Sshin	 * check if the interface is available.
47255163Sshin	 * also check if SIOCGIFMEDIA ioctl is OK on the interface.
47355163Sshin	 */
474222732Shrs	ifi->mediareqok = 1;
475222732Shrs	ifi->active = interface_status(ifi);
476222732Shrs	if (!ifi->mediareqok) {
47755163Sshin		/*
47855163Sshin		 * probe routers periodically even if the link status
47955163Sshin		 * does not change.
48055163Sshin		 */
481222732Shrs		ifi->probeinterval = PROBE_INTERVAL;
48255163Sshin	}
48355163Sshin
48455163Sshin	/* activate interface: interface_up returns 0 on success */
485222732Shrs	flags = interface_up(ifi->ifname);
48655163Sshin	if (flags == 0)
487222732Shrs		ifi->state = IFS_DELAY;
48855163Sshin	else if (flags == IFS_TENTATIVE)
489222732Shrs		ifi->state = IFS_TENTATIVE;
49055163Sshin	else
491222732Shrs		ifi->state = IFS_DOWN;
49255163Sshin
493222732Shrs	rtsol_timer_update(ifi);
49455163Sshin
495222732Shrs	TAILQ_INSERT_TAIL(&ifinfo_head, ifi, ifi_next);
496222732Shrs	return (0);
49755163Sshin
498118664Sumebad:
499222732Shrs	free(ifi->sdl);
500222732Shrs	free(ifi);
501222732Shrs	return (-1);
50255163Sshin}
50355163Sshin
504119026Sumevoid
505124524Sumeiflist_init(void)
506119026Sume{
507222732Shrs	struct ifinfo *ifi;
508119026Sume
509222732Shrs	while ((ifi = TAILQ_FIRST(&ifinfo_head)) != NULL) {
510222732Shrs		TAILQ_REMOVE(&ifinfo_head, ifi, ifi_next);
511222732Shrs		if (ifi->sdl != NULL)
512119026Sume			free(ifi->sdl);
513222732Shrs		if (ifi->rs_data != NULL)
514119026Sume			free(ifi->rs_data);
515119026Sume		free(ifi);
516119026Sume	}
517119026Sume}
518119026Sume
51962632Skris#if 0
52062632Skrisstatic int
52162632Skrisifreconfig(char *ifname)
52262632Skris{
52362632Skris	struct ifinfo *ifi, *prev;
52462632Skris	int rv;
52562632Skris
52662632Skris	prev = NULL;
527222732Shrs	TAILQ_FOREACH(ifi, &ifinfo_head, ifi_next) {
52862632Skris		if (strncmp(ifi->ifname, ifname, sizeof(ifi->ifname)) == 0)
52962632Skris			break;
53062632Skris		prev = ifi;
53162632Skris	}
53262632Skris	prev->next = ifi->next;
53362632Skris
53462632Skris	rv = ifconfig(ifname);
53562632Skris
53662632Skris	/* reclaim it after ifconfig() in case ifname is pointer inside ifi */
53762632Skris	if (ifi->rs_data)
53862632Skris		free(ifi->rs_data);
53962632Skris	free(ifi->sdl);
54062632Skris	free(ifi);
541222732Shrs
542222732Shrs	return (rv);
54362632Skris}
54462632Skris#endif
54562632Skris
546222861Shrsstruct rainfo *
547222861Shrsfind_rainfo(struct ifinfo *ifi, struct sockaddr_in6 *sin6)
548222861Shrs{
549222861Shrs	struct rainfo *rai;
550222861Shrs
551222861Shrs	TAILQ_FOREACH(rai, &ifi->ifi_rainfo, rai_next)
552222861Shrs		if (memcmp(&rai->rai_saddr.sin6_addr, &sin6->sin6_addr,
553222861Shrs		    sizeof(rai->rai_saddr.sin6_addr)) == 0)
554222861Shrs			return (rai);
555222861Shrs
556222861Shrs	return (NULL);
557222861Shrs}
558222861Shrs
55955163Sshinstruct ifinfo *
56055163Sshinfind_ifinfo(int ifindex)
56155163Sshin{
56255163Sshin	struct ifinfo *ifi;
56355163Sshin
564222732Shrs	TAILQ_FOREACH(ifi, &ifinfo_head, ifi_next) {
56555163Sshin		if (ifi->sdl->sdl_index == ifindex)
566222732Shrs			return (ifi);
567222732Shrs	}
568222732Shrs	return (NULL);
56955163Sshin}
57055163Sshin
57155163Sshinstatic int
572222732Shrsmake_packet(struct ifinfo *ifi)
57355163Sshin{
574118664Sume	size_t packlen = sizeof(struct nd_router_solicit), lladdroptlen = 0;
575118664Sume	struct nd_router_solicit *rs;
57655163Sshin	char *buf;
57755163Sshin
578222732Shrs	if ((lladdroptlen = lladdropt_length(ifi->sdl)) == 0) {
579118660Sume		warnmsg(LOG_INFO, __func__,
580118664Sume		    "link-layer address option has null length"
581222732Shrs		    " on %s. Treat as not included.", ifi->ifname);
58255163Sshin	}
58355163Sshin	packlen += lladdroptlen;
584222732Shrs	ifi->rs_datalen = packlen;
58555163Sshin
58655163Sshin	/* allocate buffer */
58755163Sshin	if ((buf = malloc(packlen)) == NULL) {
588118660Sume		warnmsg(LOG_ERR, __func__,
589222732Shrs		    "memory allocation failed for %s", ifi->ifname);
590222732Shrs		return (-1);
59155163Sshin	}
592222732Shrs	ifi->rs_data = buf;
59355163Sshin
59455163Sshin	/* fill in the message */
59555163Sshin	rs = (struct nd_router_solicit *)buf;
59655163Sshin	rs->nd_rs_type = ND_ROUTER_SOLICIT;
59755163Sshin	rs->nd_rs_code = 0;
59855163Sshin	rs->nd_rs_cksum = 0;
59955163Sshin	rs->nd_rs_reserved = 0;
60055163Sshin	buf += sizeof(*rs);
60155163Sshin
60255163Sshin	/* fill in source link-layer address option */
60355163Sshin	if (lladdroptlen)
604222732Shrs		lladdropt_fill(ifi->sdl, (struct nd_opt_hdr *)buf);
60555163Sshin
606222732Shrs	return (0);
60755163Sshin}
60855163Sshin
609253970Shrsstatic struct timespec *
610124524Sumertsol_check_timer(void)
61155163Sshin{
612253970Shrs	static struct timespec returnval;
613253970Shrs	struct timespec now, rtsol_timer;
614222732Shrs	struct ifinfo *ifi;
615222861Shrs	struct rainfo *rai;
616222732Shrs	struct ra_opt *rao;
61755163Sshin	int flags;
61855163Sshin
619253970Shrs	clock_gettime(CLOCK_MONOTONIC_FAST, &now);
62055163Sshin
62155163Sshin	rtsol_timer = tm_max;
62255163Sshin
623222732Shrs	TAILQ_FOREACH(ifi, &ifinfo_head, ifi_next) {
624253970Shrs		if (TS_CMP(&ifi->expire, &now, <=)) {
625222861Shrs			warnmsg(LOG_DEBUG, __func__, "timer expiration on %s, "
626222861Shrs			    "state = %d", ifi->ifname, ifi->state);
62755163Sshin
628222861Shrs			while((rai = TAILQ_FIRST(&ifi->ifi_rainfo)) != NULL) {
629222861Shrs				/* Remove all RA options. */
630222861Shrs				TAILQ_REMOVE(&ifi->ifi_rainfo, rai, rai_next);
631222861Shrs				while ((rao = TAILQ_FIRST(&rai->rai_ra_opt)) !=
632222861Shrs				    NULL) {
633222861Shrs					TAILQ_REMOVE(&rai->rai_ra_opt, rao,
634222861Shrs					    rao_next);
635222861Shrs					if (rao->rao_msg != NULL)
636222861Shrs						free(rao->rao_msg);
637222861Shrs					free(rao);
638222861Shrs				}
639222861Shrs				free(rai);
640222732Shrs			}
641222732Shrs			switch (ifi->state) {
64255163Sshin			case IFS_DOWN:
64355163Sshin			case IFS_TENTATIVE:
64455163Sshin				/* interface_up returns 0 on success */
645222732Shrs				flags = interface_up(ifi->ifname);
64655163Sshin				if (flags == 0)
647222732Shrs					ifi->state = IFS_DELAY;
64855163Sshin				else if (flags == IFS_TENTATIVE)
649222732Shrs					ifi->state = IFS_TENTATIVE;
65055163Sshin				else
651222732Shrs					ifi->state = IFS_DOWN;
65255163Sshin				break;
65355163Sshin			case IFS_IDLE:
65455163Sshin			{
655222732Shrs				int oldstatus = ifi->active;
65655163Sshin				int probe = 0;
65755163Sshin
658222732Shrs				ifi->active = interface_status(ifi);
65955163Sshin
660222732Shrs				if (oldstatus != ifi->active) {
661118660Sume					warnmsg(LOG_DEBUG, __func__,
662118664Sume					    "%s status is changed"
663118664Sume					    " from %d to %d",
664222732Shrs					    ifi->ifname,
665222732Shrs					    oldstatus, ifi->active);
66655163Sshin					probe = 1;
667222732Shrs					ifi->state = IFS_DELAY;
668222732Shrs				} else if (ifi->probeinterval &&
669222732Shrs				    (ifi->probetimer -=
670222732Shrs				    ifi->timer.tv_sec) <= 0) {
67155163Sshin					/* probe timer expired */
672222732Shrs					ifi->probetimer =
673222732Shrs					    ifi->probeinterval;
67455163Sshin					probe = 1;
675222732Shrs					ifi->state = IFS_PROBE;
67655163Sshin				}
67755163Sshin
678118661Sume				/*
679118661Sume				 * If we need a probe, clear the previous
680118661Sume				 * status wrt the "other" configuration.
681118661Sume				 */
682118661Sume				if (probe)
683222732Shrs					ifi->otherconfig = 0;
684118661Sume
68555163Sshin				if (probe && mobile_node)
686222732Shrs					defrouter_probe(ifi);
68755163Sshin				break;
68855163Sshin			}
68955163Sshin			case IFS_DELAY:
690222732Shrs				ifi->state = IFS_PROBE;
691222732Shrs				sendpacket(ifi);
69255163Sshin				break;
69355163Sshin			case IFS_PROBE:
694222732Shrs				if (ifi->probes < MAX_RTR_SOLICITATIONS)
695222732Shrs					sendpacket(ifi);
69655163Sshin				else {
697118660Sume					warnmsg(LOG_INFO, __func__,
698118664Sume					    "No answer after sending %d RSs",
699222732Shrs					    ifi->probes);
700222732Shrs					ifi->probes = 0;
701222732Shrs					ifi->state = IFS_IDLE;
70255163Sshin				}
70355163Sshin				break;
70455163Sshin			}
705222732Shrs			rtsol_timer_update(ifi);
706222732Shrs		} else {
707222732Shrs			/* Expiration check for RA options. */
708222732Shrs			int expire = 0;
709222732Shrs
710222861Shrs			TAILQ_FOREACH(rai, &ifi->ifi_rainfo, rai_next) {
711222861Shrs				TAILQ_FOREACH(rao, &rai->rai_ra_opt, rao_next) {
712222732Shrs					warnmsg(LOG_DEBUG, __func__,
713222861Shrs					    "RA expiration timer: "
714222861Shrs					    "type=%d, msg=%s, expire=%s",
715222861Shrs					    rao->rao_type, (char *)rao->rao_msg,
716222861Shrs						sec2str(&rao->rao_expire));
717253970Shrs					if (TS_CMP(&now, &rao->rao_expire,
718222861Shrs					    >=)) {
719222861Shrs						warnmsg(LOG_DEBUG, __func__,
720222861Shrs						    "RA expiration timer: "
721222861Shrs						    "expired.");
722222861Shrs						TAILQ_REMOVE(&rai->rai_ra_opt,
723222861Shrs						    rao, rao_next);
724222861Shrs						if (rao->rao_msg != NULL)
725222861Shrs							free(rao->rao_msg);
726222861Shrs						free(rao);
727222861Shrs						expire = 1;
728222861Shrs					}
729222732Shrs				}
730222732Shrs			}
731222732Shrs			if (expire)
732222732Shrs				ra_opt_handler(ifi);
73355163Sshin		}
734253970Shrs		if (TS_CMP(&ifi->expire, &rtsol_timer, <))
735222732Shrs			rtsol_timer = ifi->expire;
73655163Sshin	}
73755163Sshin
738253970Shrs	if (TS_CMP(&rtsol_timer, &tm_max, ==)) {
739118660Sume		warnmsg(LOG_DEBUG, __func__, "there is no timer");
740222732Shrs		return (NULL);
741253970Shrs	} else if (TS_CMP(&rtsol_timer, &now, <))
74255163Sshin		/* this may occur when the interval is too small */
743253970Shrs		returnval.tv_sec = returnval.tv_nsec = 0;
74455163Sshin	else
745253970Shrs		TS_SUB(&rtsol_timer, &now, &returnval);
74655163Sshin
747222861Shrs	now.tv_sec += returnval.tv_sec;
748253970Shrs	now.tv_nsec += returnval.tv_nsec;
749222861Shrs	warnmsg(LOG_DEBUG, __func__, "New timer is %s",
750222861Shrs	    sec2str(&now));
75155163Sshin
752222732Shrs	return (&returnval);
75355163Sshin}
75455163Sshin
75555163Sshinvoid
756222732Shrsrtsol_timer_update(struct ifinfo *ifi)
75755163Sshin{
75862632Skris#define MILLION 1000000
75962632Skris#define DADRETRY 10		/* XXX: adhoc */
76055163Sshin	long interval;
761253970Shrs	struct timespec now;
76255163Sshin
763222732Shrs	bzero(&ifi->timer, sizeof(ifi->timer));
76455163Sshin
765222732Shrs	switch (ifi->state) {
76655163Sshin	case IFS_DOWN:
76755163Sshin	case IFS_TENTATIVE:
768222732Shrs		if (++ifi->dadcount > DADRETRY) {
769222732Shrs			ifi->dadcount = 0;
770222732Shrs			ifi->timer.tv_sec = PROBE_INTERVAL;
771118664Sume		} else
772222732Shrs			ifi->timer.tv_sec = 1;
77355163Sshin		break;
77455163Sshin	case IFS_IDLE:
77555163Sshin		if (mobile_node) {
776118664Sume			/* XXX should be configurable */
777222732Shrs			ifi->timer.tv_sec = 3;
77855163Sshin		}
77955163Sshin		else
780222732Shrs			ifi->timer = tm_max;	/* stop timer(valid?) */
78155163Sshin		break;
78255163Sshin	case IFS_DELAY:
78362632Skris#ifndef HAVE_ARC4RANDOM
78455163Sshin		interval = random() % (MAX_RTR_SOLICITATION_DELAY * MILLION);
78562632Skris#else
786180824Sache		interval = arc4random_uniform(MAX_RTR_SOLICITATION_DELAY * MILLION);
78762632Skris#endif
788222732Shrs		ifi->timer.tv_sec = interval / MILLION;
789253970Shrs		ifi->timer.tv_nsec = (interval % MILLION) * 1000;
79055163Sshin		break;
79155163Sshin	case IFS_PROBE:
792222732Shrs		if (ifi->probes < MAX_RTR_SOLICITATIONS)
793222732Shrs			ifi->timer.tv_sec = RTR_SOLICITATION_INTERVAL;
79478064Sume		else {
79578064Sume			/*
79678064Sume			 * After sending MAX_RTR_SOLICITATIONS solicitations,
79778064Sume			 * we're just waiting for possible replies; there
798147150Ssuz			 * will be no more solicitation.  Thus, we change
79978064Sume			 * the timer value to MAX_RTR_SOLICITATION_DELAY based
80078064Sume			 * on RFC 2461, Section 6.3.7.
80178064Sume			 */
802222732Shrs			ifi->timer.tv_sec = MAX_RTR_SOLICITATION_DELAY;
80378064Sume		}
80455163Sshin		break;
80555163Sshin	default:
806118660Sume		warnmsg(LOG_ERR, __func__,
807118664Sume		    "illegal interface state(%d) on %s",
808222732Shrs		    ifi->state, ifi->ifname);
80955163Sshin		return;
81055163Sshin	}
81155163Sshin
81255163Sshin	/* reset the timer */
813253970Shrs	if (TS_CMP(&ifi->timer, &tm_max, ==)) {
814222732Shrs		ifi->expire = tm_max;
815118660Sume		warnmsg(LOG_DEBUG, __func__,
816222732Shrs		    "stop timer for %s", ifi->ifname);
817118664Sume	} else {
818253970Shrs		clock_gettime(CLOCK_MONOTONIC_FAST, &now);
819253970Shrs		TS_ADD(&now, &ifi->timer, &ifi->expire);
82055163Sshin
821222861Shrs		now.tv_sec += ifi->timer.tv_sec;
822253970Shrs		now.tv_nsec += ifi->timer.tv_nsec;
823222861Shrs		warnmsg(LOG_DEBUG, __func__, "set timer for %s to %s",
824222861Shrs		    ifi->ifname, sec2str(&now));
82555163Sshin	}
82655163Sshin
82755163Sshin#undef MILLION
82855163Sshin}
82955163Sshin
83055163Sshin/* timer related utility functions */
83162632Skris#define MILLION 1000000
83255163Sshin
833124526Sume#ifndef SMALL
83455163Sshinstatic void
835204407Suqsrtsold_set_dump_file(int sig __unused)
83655163Sshin{
83755163Sshin	do_dump = 1;
83855163Sshin}
839124526Sume#endif
84055163Sshin
84155163Sshinstatic void
842222732Shrsusage(void)
84355163Sshin{
844222732Shrs#ifndef SMALL
845222732Shrs	fprintf(stderr, "usage: rtsold [-adDfFm1] [-O script-name] "
846222732Shrs	    "[-P pidfile] [-R script-name] interfaces...\n");
847222732Shrs	fprintf(stderr, "usage: rtsold [-dDfFm1] [-O script-name] "
848222732Shrs	    "[-P pidfile] [-R script-name] -a\n");
849222732Shrs#else
850222732Shrs	fprintf(stderr, "usage: rtsol [-dDF] [-O script-name] "
851222732Shrs	    "[-P pidfile] [-R script-name] interfaces...\n");
852222732Shrs	fprintf(stderr, "usage: rtsol [-dDF] [-O script-name] "
853222732Shrs	    "[-P pidfile] [-R script-name] -a\n");
854222732Shrs#endif
85555163Sshin}
85655163Sshin
85755163Sshinvoid
85855163Sshinwarnmsg(int priority, const char *func, const char *msg, ...)
85955163Sshin{
86055163Sshin	va_list ap;
86155163Sshin	char buf[BUFSIZ];
86255163Sshin
86355163Sshin	va_start(ap, msg);
86455163Sshin	if (fflag) {
86555163Sshin		if (priority <= log_upto) {
86655163Sshin			(void)vfprintf(stderr, msg, ap);
86755163Sshin			(void)fprintf(stderr, "\n");
86855163Sshin		}
86955163Sshin	} else {
87055163Sshin		snprintf(buf, sizeof(buf), "<%s> %s", func, msg);
87166776Skris		msg = buf;
87266776Skris		vsyslog(priority, msg, ap);
87355163Sshin	}
87455163Sshin	va_end(ap);
87555163Sshin}
87666776Skris
877119026Sume/*
878119026Sume * return a list of interfaces which is suitable to sending an RS.
879119026Sume */
880119026Sumechar **
881124524Sumeautoifprobe(void)
88266776Skris{
883119026Sume	static char **argv = NULL;
884119026Sume	static int n = 0;
885119026Sume	char **a;
886204407Suqs	int s = 0, i, found;
887230357Seadler	struct ifaddrs *ifap, *ifa;
888203387Sume	struct in6_ndireq nd;
88966776Skris
890119026Sume	/* initialize */
891119026Sume	while (n--)
892119026Sume		free(argv[n]);
893119026Sume	if (argv) {
894119026Sume		free(argv);
895119026Sume		argv = NULL;
896119026Sume	}
897119026Sume	n = 0;
898119026Sume
89966776Skris	if (getifaddrs(&ifap) != 0)
900222732Shrs		return (NULL);
90166776Skris
902203387Sume	if (!Fflag && (s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
903222732Shrs		warnmsg(LOG_ERR, __func__, "socket");
904222732Shrs		exit(1);
905203387Sume	}
906203387Sume
90766776Skris	/* find an ethernet */
90866776Skris	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
90966776Skris		if ((ifa->ifa_flags & IFF_UP) == 0)
91066776Skris			continue;
91166776Skris		if ((ifa->ifa_flags & IFF_POINTOPOINT) != 0)
91266776Skris			continue;
91366776Skris		if ((ifa->ifa_flags & IFF_LOOPBACK) != 0)
91466776Skris			continue;
91566776Skris		if ((ifa->ifa_flags & IFF_MULTICAST) == 0)
91666776Skris			continue;
91766776Skris
91866776Skris		if (ifa->ifa_addr->sa_family != AF_INET6)
91966776Skris			continue;
92066776Skris
921119026Sume		found = 0;
922119026Sume		for (i = 0; i < n; i++) {
923119026Sume			if (strcmp(argv[i], ifa->ifa_name) == 0) {
924119026Sume				found++;
925119026Sume				break;
926119026Sume			}
927119026Sume		}
928119026Sume		if (found)
92966776Skris			continue;
93066776Skris
931203387Sume		/*
932203387Sume		 * Skip the interfaces which IPv6 and/or accepting RA
933203387Sume		 * is disabled.
934203387Sume		 */
935203387Sume		if (!Fflag) {
936203387Sume			memset(&nd, 0, sizeof(nd));
937203387Sume			strlcpy(nd.ifname, ifa->ifa_name, sizeof(nd.ifname));
938203387Sume			if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) {
939222732Shrs				warnmsg(LOG_ERR, __func__,
940222732Shrs					"ioctl(SIOCGIFINFO_IN6)");
941222732Shrs				exit(1);
942203387Sume			}
943203387Sume			if ((nd.ndi.flags & ND6_IFF_IFDISABLED))
944203387Sume				continue;
945203387Sume			if (!(nd.ndi.flags & ND6_IFF_ACCEPT_RTADV))
946203387Sume				continue;
947203387Sume		}
948203387Sume
949119026Sume		/* if we find multiple candidates, just warn. */
950119026Sume		if (n != 0 && dflag > 1)
951222732Shrs			warnmsg(LOG_WARNING, __func__,
952222732Shrs				"multiple interfaces found");
953119026Sume
954119026Sume		a = (char **)realloc(argv, (n + 1) * sizeof(char **));
955222732Shrs		if (a == NULL) {
956222732Shrs			warnmsg(LOG_ERR, __func__, "realloc");
957222732Shrs			exit(1);
958222732Shrs		}
959119026Sume		argv = a;
960119026Sume		argv[n] = strdup(ifa->ifa_name);
961222732Shrs		if (!argv[n]) {
962222732Shrs			warnmsg(LOG_ERR, __func__, "malloc");
963222732Shrs			exit(1);
964222732Shrs		}
965119026Sume		n++;
96666776Skris	}
96766776Skris
968119026Sume	if (n) {
969119026Sume		a = (char **)realloc(argv, (n + 1) * sizeof(char **));
970222732Shrs		if (a == NULL) {
971222732Shrs			warnmsg(LOG_ERR, __func__, "realloc");
972222732Shrs			exit(1);
973222732Shrs		}
974119026Sume		argv = a;
975119026Sume		argv[n] = NULL;
97666776Skris
977119026Sume		if (dflag > 0) {
978119026Sume			for (i = 0; i < n; i++)
979222732Shrs				warnmsg(LOG_WARNING, __func__, "probing %s",
980222732Shrs					argv[i]);
981119026Sume		}
98266776Skris	}
983203387Sume	if (!Fflag)
984203387Sume		close(s);
98566776Skris	freeifaddrs(ifap);
986222732Shrs	return (argv);
98766776Skris}
988