route6d.c revision 243231
162607Sitojun/*	$FreeBSD: head/usr.sbin/route6d/route6d.c 243231 2012-11-18 15:11:47Z hrs $	*/
2122677Sume/*	$KAME: route6d.c,v 1.104 2003/10/31 00:30:20 itojun Exp $	*/
355163Sshin
455163Sshin/*
555163Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
655163Sshin * All rights reserved.
762607Sitojun *
855163Sshin * Redistribution and use in source and binary forms, with or without
955163Sshin * modification, are permitted provided that the following conditions
1055163Sshin * are met:
1155163Sshin * 1. Redistributions of source code must retain the above copyright
1255163Sshin *    notice, this list of conditions and the following disclaimer.
1355163Sshin * 2. Redistributions in binary form must reproduce the above copyright
1455163Sshin *    notice, this list of conditions and the following disclaimer in the
1555163Sshin *    documentation and/or other materials provided with the distribution.
1655163Sshin * 3. Neither the name of the project nor the names of its contributors
1755163Sshin *    may be used to endorse or promote products derived from this software
1855163Sshin *    without specific prior written permission.
1962607Sitojun *
2055163Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
2155163Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2255163Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2355163Sshin * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2455163Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2555163Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2655163Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2755163Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2855163Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2955163Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3055163Sshin * SUCH DAMAGE.
3155163Sshin */
3255163Sshin
3355163Sshin#ifndef	lint
34122677Sumestatic char _rcsid[] = "$KAME: route6d.c,v 1.104 2003/10/31 00:30:20 itojun Exp $";
3555163Sshin#endif
3655163Sshin
3755163Sshin#include <stdio.h>
3855163Sshin
3955163Sshin#include <time.h>
4055163Sshin#include <unistd.h>
4155163Sshin#include <stdlib.h>
4255163Sshin#include <string.h>
4355163Sshin#include <signal.h>
4455163Sshin#ifdef __STDC__
4555163Sshin#include <stdarg.h>
4655163Sshin#else
4755163Sshin#include <varargs.h>
4855163Sshin#endif
4955163Sshin#include <syslog.h>
5055163Sshin#include <stddef.h>
5162607Sitojun#include <errno.h>
5255163Sshin#include <err.h>
53119076Sume#ifdef HAVE_POLL_H
54119076Sume#include <poll.h>
55119076Sume#endif
5655163Sshin
5755163Sshin#include <sys/types.h>
5855163Sshin#include <sys/param.h>
5955163Sshin#include <sys/file.h>
6055163Sshin#include <sys/socket.h>
6155163Sshin#include <sys/ioctl.h>
6255163Sshin#include <sys/sysctl.h>
6355163Sshin#include <sys/uio.h>
6455163Sshin#include <net/if.h>
6555163Sshin#include <net/if_var.h>
6662607Sitojun#define	_KERNEL	1
6755163Sshin#include <net/route.h>
6862607Sitojun#undef _KERNEL
6955163Sshin#include <netinet/in.h>
7055163Sshin#include <netinet/in_var.h>
7155163Sshin#include <netinet/ip6.h>
7255163Sshin#include <netinet/udp.h>
7355163Sshin#include <netdb.h>
7462607Sitojun#include <ifaddrs.h>
7555163Sshin
7655163Sshin#include <arpa/inet.h>
7755163Sshin
7855163Sshin#include "route6d.h"
7955163Sshin
8055163Sshin#define	MAXFILTER	40
8155163Sshin
8255163Sshin#ifdef	DEBUG
8355163Sshin#define	INIT_INTERVAL6	6
8455163Sshin#else
85119039Sume#define	INIT_INTERVAL6	10	/* Wait to submit an initial riprequest */
8655163Sshin#endif
8755163Sshin
8855163Sshin/* alignment constraint for routing socket */
8962607Sitojun#define ROUNDUP(a) \
9055163Sshin	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
9162607Sitojun#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
9255163Sshin
9355163Sshinstruct	ifc {			/* Configuration of an interface */
9455163Sshin	char	*ifc_name;			/* if name */
9555163Sshin	struct	ifc *ifc_next;
9655163Sshin	int	ifc_index;			/* if index */
9755163Sshin	int	ifc_mtu;			/* if mtu */
9855163Sshin	int	ifc_metric;			/* if metric */
9978064Sume	u_int	ifc_flags;			/* flags */
10078064Sume	short	ifc_cflags;			/* IFC_XXX */
10155163Sshin	struct	in6_addr ifc_mylladdr;		/* my link-local address */
10255163Sshin	struct	sockaddr_in6 ifc_ripsin;	/* rip multicast address */
10355163Sshin	struct	iff *ifc_filter;		/* filter structure */
10455163Sshin	struct	ifac *ifc_addr;			/* list of AF_INET6 addresses */
10555163Sshin	int	ifc_joined;			/* joined to ff02::9 */
10655163Sshin};
10755163Sshin
10862607Sitojunstruct	ifac {			/* Adddress associated to an interface */
10955163Sshin	struct	ifc *ifa_conf;		/* back pointer */
11055163Sshin	struct	ifac *ifa_next;
11155163Sshin	struct	in6_addr ifa_addr;	/* address */
11255163Sshin	struct	in6_addr ifa_raddr;	/* remote address, valid in p2p */
113243231Shrs	int	ifa_scope_id;		/* scope id */
11455163Sshin	int	ifa_plen;		/* prefix length */
11555163Sshin};
11655163Sshin
11755163Sshinstruct	iff {
11855163Sshin	int	iff_type;
11955163Sshin	struct	in6_addr iff_addr;
12055163Sshin	int	iff_plen;
12155163Sshin	struct	iff *iff_next;
12255163Sshin};
12355163Sshin
12455163Sshinstruct	ifc *ifc;
12555163Sshinint	nifc;		/* number of valid ifc's */
12655163Sshinstruct	ifc **index2ifc;
12755163Sshinint	nindex2ifc;
12855163Sshinstruct	ifc *loopifcp = NULL;	/* pointing to loopback */
129119076Sume#ifdef HAVE_POLL_H
130119076Sumestruct	pollfd set[2];
131119076Sume#else
132119070Sumefd_set	*sockvecp;	/* vector to select() for receiving */
133119070Sumefd_set	*recvecp;
134119070Sumeint	fdmasks;
135119070Sumeint	maxfd;		/* maximum fd for select() */
136119076Sume#endif
13755163Sshinint	rtsock;		/* the routing socket */
13855163Sshinint	ripsock;	/* socket to send/receive RIP datagram */
13955163Sshin
14055163Sshinstruct	rip6 *ripbuf;	/* packet buffer for sending */
14155163Sshin
14255163Sshin/*
14378064Sume * Maintain the routes in a linked list.  When the number of the routes
14455163Sshin * grows, somebody would like to introduce a hash based or a radix tree
14578064Sume * based structure.  I believe the number of routes handled by RIP is
14655163Sshin * limited and I don't have to manage a complex data structure, however.
14755163Sshin *
14855163Sshin * One of the major drawbacks of the linear linked list is the difficulty
14978064Sume * of representing the relationship between a couple of routes.  This may
15055163Sshin * be a significant problem when we have to support route aggregation with
151228990Suqs * suppressing the specifics covered by the aggregate.
15255163Sshin */
15355163Sshin
15455163Sshinstruct	riprt {
15555163Sshin	struct	riprt *rrt_next;	/* next destination */
15655163Sshin	struct	riprt *rrt_same;	/* same destination - future use */
15755163Sshin	struct	netinfo6 rrt_info;	/* network info */
15855163Sshin	struct	in6_addr rrt_gw;	/* gateway */
15962607Sitojun	u_long	rrt_flags;		/* kernel routing table flags */
16062607Sitojun	u_long	rrt_rflags;		/* route6d routing table flags */
16155163Sshin	time_t	rrt_t;			/* when the route validated */
16255163Sshin	int	rrt_index;		/* ifindex from which this route got */
16355163Sshin};
16455163Sshin
16555163Sshinstruct	riprt *riprt = 0;
16655163Sshin
16755163Sshinint	dflag = 0;	/* debug flag */
16855163Sshinint	qflag = 0;	/* quiet flag */
16955163Sshinint	nflag = 0;	/* don't update kernel routing table */
17055163Sshinint	aflag = 0;	/* age out even the statically defined routes */
17155163Sshinint	hflag = 0;	/* don't split horizon */
17255163Sshinint	lflag = 0;	/* exchange site local routes */
17355163Sshinint	sflag = 0;	/* announce static routes w/ split horizon */
17455163Sshinint	Sflag = 0;	/* announce static routes to every interface */
17562607Sitojununsigned long routetag = 0;	/* route tag attached on originating case */
17655163Sshin
17755163Sshinchar	*filter[MAXFILTER];
17855163Sshinint	filtertype[MAXFILTER];
17955163Sshinint	nfilter = 0;
18055163Sshin
18155163Sshinpid_t	pid;
18255163Sshin
18355163Sshinstruct	sockaddr_storage ripsin;
18455163Sshin
18555163Sshinint	interval = 1;
18655163Sshintime_t	nextalarm = 0;
18755163Sshintime_t	sup_trig_update = 0;
18855163Sshin
18955163SshinFILE	*rtlog = NULL;
19055163Sshin
19155163Sshinint logopened = 0;
19255163Sshin
193119085Sumestatic	int	seq = 0;
19455163Sshin
19578064Sumevolatile sig_atomic_t seenalrm;
19678064Sumevolatile sig_atomic_t seenquit;
19778064Sumevolatile sig_atomic_t seenusr1;
19878064Sume
19962607Sitojun#define	RRTF_AGGREGATE		0x08000000
20062607Sitojun#define	RRTF_NOADVERTISE	0x10000000
20162607Sitojun#define	RRTF_NH_NOT_LLADDR	0x20000000
20262607Sitojun#define RRTF_SENDANYWAY		0x40000000
20362607Sitojun#define	RRTF_CHANGED		0x80000000
20455163Sshin
205173412Skevloint main(int, char **);
206173412Skevlovoid sighandler(int);
207173412Skevlovoid ripalarm(void);
208173412Skevlovoid riprecv(void);
209173412Skevlovoid ripsend(struct ifc *, struct sockaddr_in6 *, int);
210173412Skevloint out_filter(struct riprt *, struct ifc *);
211173412Skevlovoid init(void);
212173412Skevlovoid sockopt(struct ifc *);
213173412Skevlovoid ifconfig(void);
214173412Skevlovoid ifconfig1(const char *, const struct sockaddr *, struct ifc *, int);
215173412Skevlovoid rtrecv(void);
216173412Skevloint rt_del(const struct sockaddr_in6 *, const struct sockaddr_in6 *,
217173412Skevlo	const struct sockaddr_in6 *);
218173412Skevloint rt_deladdr(struct ifc *, const struct sockaddr_in6 *,
219173412Skevlo	const struct sockaddr_in6 *);
220173412Skevlovoid filterconfig(void);
221173412Skevloint getifmtu(int);
222173412Skevloconst char *rttypes(struct rt_msghdr *);
223173412Skevloconst char *rtflags(struct rt_msghdr *);
224173412Skevloconst char *ifflags(int);
225173412Skevloint ifrt(struct ifc *, int);
226173412Skevlovoid ifrt_p2p(struct ifc *, int);
227173412Skevlovoid applymask(struct in6_addr *, struct in6_addr *);
228173412Skevlovoid applyplen(struct in6_addr *, int);
229173412Skevlovoid ifrtdump(int);
230173412Skevlovoid ifdump(int);
231173412Skevlovoid ifdump0(FILE *, const struct ifc *);
232173412Skevlovoid rtdump(int);
233173412Skevlovoid rt_entry(struct rt_msghdr *, int);
234173412Skevlovoid rtdexit(void);
235173412Skevlovoid riprequest(struct ifc *, struct netinfo6 *, int,
236173412Skevlo	struct sockaddr_in6 *);
237173412Skevlovoid ripflush(struct ifc *, struct sockaddr_in6 *);
238173412Skevlovoid sendrequest(struct ifc *);
239173412Skevloint sin6mask2len(const struct sockaddr_in6 *);
240173412Skevloint mask2len(const struct in6_addr *, int);
241173412Skevloint sendpacket(struct sockaddr_in6 *, int);
242173412Skevloint addroute(struct riprt *, const struct in6_addr *, struct ifc *);
243173412Skevloint delroute(struct netinfo6 *, struct in6_addr *);
244173412Skevlostruct in6_addr *getroute(struct netinfo6 *, struct in6_addr *);
245173412Skevlovoid krtread(int);
246173412Skevloint tobeadv(struct riprt *, struct ifc *);
247173412Skevlochar *allocopy(char *);
248173412Skevlochar *hms(void);
249173412Skevloconst char *inet6_n2p(const struct in6_addr *);
250173412Skevlostruct ifac *ifa_match(const struct ifc *, const struct in6_addr *, int);
251173412Skevlostruct in6_addr *plen2mask(int);
252173412Skevlostruct riprt *rtsearch(struct netinfo6 *, struct riprt **);
253173412Skevloint ripinterval(int);
254173412Skevlotime_t ripsuptrig(void);
255173412Skevlovoid fatal(const char *, ...)
25666807Skris	__attribute__((__format__(__printf__, 1, 2)));
257173412Skevlovoid trace(int, const char *, ...)
25866807Skris	__attribute__((__format__(__printf__, 2, 3)));
259173412Skevlovoid tracet(int, const char *, ...)
26066807Skris	__attribute__((__format__(__printf__, 2, 3)));
261173412Skevlounsigned int if_maxindex(void);
262173412Skevlostruct ifc *ifc_find(char *);
263173412Skevlostruct iff *iff_find(struct ifc *, int);
264173412Skevlovoid setindex2ifc(int, struct ifc *);
26555163Sshin
26655163Sshin#define	MALLOC(type)	((type *)malloc(sizeof(type)))
26755163Sshin
26855163Sshinint
26955163Sshinmain(argc, argv)
27055163Sshin	int	argc;
27155163Sshin	char	**argv;
27255163Sshin{
27355163Sshin	int	ch;
27455163Sshin	int	error = 0;
27555163Sshin	struct	ifc *ifcp;
27655163Sshin	sigset_t mask, omask;
27755163Sshin	FILE	*pidfile;
27855163Sshin	char *progname;
27962607Sitojun	char *ep;
28055163Sshin
28155163Sshin	progname = strrchr(*argv, '/');
28255163Sshin	if (progname)
28355163Sshin		progname++;
28455163Sshin	else
28555163Sshin		progname = *argv;
28655163Sshin
28755163Sshin	pid = getpid();
28878064Sume	while ((ch = getopt(argc, argv, "A:N:O:R:T:L:t:adDhlnqsS")) != -1) {
28955163Sshin		switch (ch) {
29055163Sshin		case 'A':
29155163Sshin		case 'N':
29255163Sshin		case 'O':
29355163Sshin		case 'T':
29455163Sshin		case 'L':
29562607Sitojun			if (nfilter >= MAXFILTER) {
29655163Sshin				fatal("Exceeds MAXFILTER");
29762607Sitojun				/*NOTREACHED*/
29862607Sitojun			}
29955163Sshin			filtertype[nfilter] = ch;
30055163Sshin			filter[nfilter++] = allocopy(optarg);
30155163Sshin			break;
30255163Sshin		case 't':
30362607Sitojun			ep = NULL;
30462607Sitojun			routetag = strtoul(optarg, &ep, 0);
30562607Sitojun			if (!ep || *ep != '\0' || (routetag & ~0xffff) != 0) {
30655163Sshin				fatal("invalid route tag");
30755163Sshin				/*NOTREACHED*/
30855163Sshin			}
30955163Sshin			break;
31055163Sshin		case 'R':
31162607Sitojun			if ((rtlog = fopen(optarg, "w")) == NULL) {
31255163Sshin				fatal("Can not write to routelog");
31362607Sitojun				/*NOTREACHED*/
31462607Sitojun			}
31555163Sshin			break;
31662607Sitojun#define	FLAG(c, flag, n)	case c: do { flag = n; break; } while(0)
31762607Sitojun		FLAG('a', aflag, 1); break;
31862607Sitojun		FLAG('d', dflag, 1); break;
31962607Sitojun		FLAG('D', dflag, 2); break;
32062607Sitojun		FLAG('h', hflag, 1); break;
32162607Sitojun		FLAG('l', lflag, 1); break;
32262607Sitojun		FLAG('n', nflag, 1); break;
32362607Sitojun		FLAG('q', qflag, 1); break;
32462607Sitojun		FLAG('s', sflag, 1); break;
32562607Sitojun		FLAG('S', Sflag, 1); break;
32655163Sshin#undef	FLAG
32755163Sshin		default:
32855163Sshin			fatal("Invalid option specified, terminating");
32962607Sitojun			/*NOTREACHED*/
33055163Sshin		}
33155163Sshin	}
33255163Sshin	argc -= optind;
33355163Sshin	argv += optind;
33478064Sume	if (argc > 0) {
33555163Sshin		fatal("bogus extra arguments");
33678064Sume		/*NOTREACHED*/
33778064Sume	}
33855163Sshin
33955163Sshin	if (geteuid()) {
34055163Sshin		nflag = 1;
34155163Sshin		fprintf(stderr, "No kernel update is allowed\n");
34255163Sshin	}
343119037Sume
344119037Sume	if (dflag == 0) {
345119037Sume		if (daemon(0, 0) < 0) {
346119037Sume			fatal("daemon");
347119037Sume			/*NOTREACHED*/
348119037Sume		}
349119037Sume	}
350119037Sume
35155163Sshin	openlog(progname, LOG_NDELAY|LOG_PID, LOG_DAEMON);
35255163Sshin	logopened++;
35378064Sume
35478064Sume	if ((ripbuf = (struct rip6 *)malloc(RIP6_MAXMTU)) == NULL)
35578064Sume		fatal("malloc");
35678064Sume	memset(ripbuf, 0, RIP6_MAXMTU);
35778064Sume	ripbuf->rip6_cmd = RIP6_RESPONSE;
35878064Sume	ripbuf->rip6_vers = RIP6_VERSION;
35978064Sume	ripbuf->rip6_res1[0] = 0;
36078064Sume	ripbuf->rip6_res1[1] = 0;
36178064Sume
36255163Sshin	init();
36355163Sshin	ifconfig();
36455163Sshin	for (ifcp = ifc; ifcp; ifcp = ifcp->ifc_next) {
36555163Sshin		if (ifcp->ifc_index < 0) {
36655163Sshin			fprintf(stderr,
36755163Sshin"No ifindex found at %s (no link-local address?)\n",
36855163Sshin				ifcp->ifc_name);
36955163Sshin			error++;
37055163Sshin		}
37155163Sshin	}
37255163Sshin	if (error)
37355163Sshin		exit(1);
37478064Sume	if (loopifcp == NULL) {
37555163Sshin		fatal("No loopback found");
37678064Sume		/*NOTREACHED*/
37778064Sume	}
37855163Sshin	for (ifcp = ifc; ifcp; ifcp = ifcp->ifc_next)
37955163Sshin		ifrt(ifcp, 0);
38055163Sshin	filterconfig();
38155163Sshin	krtread(0);
38255163Sshin	if (dflag)
38355163Sshin		ifrtdump(0);
38455163Sshin
385122677Sume#if 1
38655163Sshin	pid = getpid();
38755163Sshin	if ((pidfile = fopen(ROUTE6D_PID, "w")) != NULL) {
38855163Sshin		fprintf(pidfile, "%d\n", pid);
38955163Sshin		fclose(pidfile);
39055163Sshin	}
391122677Sume#endif
39255163Sshin
39378064Sume	if ((ripbuf = (struct rip6 *)malloc(RIP6_MAXMTU)) == NULL) {
39455163Sshin		fatal("malloc");
39578064Sume		/*NOTREACHED*/
39678064Sume	}
39762607Sitojun	memset(ripbuf, 0, RIP6_MAXMTU);
39855163Sshin	ripbuf->rip6_cmd = RIP6_RESPONSE;
39955163Sshin	ripbuf->rip6_vers = RIP6_VERSION;
40055163Sshin	ripbuf->rip6_res1[0] = 0;
40155163Sshin	ripbuf->rip6_res1[1] = 0;
40255163Sshin
40378064Sume	if (signal(SIGALRM, sighandler) == SIG_ERR ||
40478064Sume	    signal(SIGQUIT, sighandler) == SIG_ERR ||
40578064Sume	    signal(SIGTERM, sighandler) == SIG_ERR ||
40678064Sume	    signal(SIGUSR1, sighandler) == SIG_ERR ||
40778064Sume	    signal(SIGHUP, sighandler) == SIG_ERR ||
40878064Sume	    signal(SIGINT, sighandler) == SIG_ERR) {
40978064Sume		fatal("signal");
41078064Sume		/*NOTREACHED*/
41178064Sume	}
41255163Sshin	/*
41355163Sshin	 * To avoid rip packet congestion (not on a cable but in this
41455163Sshin	 * process), wait for a moment to send the first RIP6_RESPONSE
41555163Sshin	 * packets.
41655163Sshin	 */
41755163Sshin	alarm(ripinterval(INIT_INTERVAL6));
41855163Sshin
41955163Sshin	for (ifcp = ifc; ifcp; ifcp = ifcp->ifc_next) {
420119041Sume		if (iff_find(ifcp, 'N'))
421119041Sume			continue;
42255163Sshin		if (ifcp->ifc_index > 0 && (ifcp->ifc_flags & IFF_UP))
42355163Sshin			sendrequest(ifcp);
42455163Sshin	}
42555163Sshin
42655163Sshin	syslog(LOG_INFO, "**** Started ****");
42755163Sshin	sigemptyset(&mask);
42855163Sshin	sigaddset(&mask, SIGALRM);
42955163Sshin	while (1) {
43078064Sume		if (seenalrm) {
43178064Sume			ripalarm();
43278064Sume			seenalrm = 0;
43378064Sume			continue;
43478064Sume		}
43578064Sume		if (seenquit) {
43678064Sume			rtdexit();
43778064Sume			seenquit = 0;
43878064Sume			continue;
43978064Sume		}
44078064Sume		if (seenusr1) {
44178064Sume			ifrtdump(SIGUSR1);
44278064Sume			seenusr1 = 0;
44378064Sume			continue;
44478064Sume		}
44578064Sume
446119076Sume#ifdef HAVE_POLL_H
447119076Sume		switch (poll(set, 2, INFTIM))
448119076Sume#else
449119070Sume		memcpy(recvecp, sockvecp, fdmasks);
450119076Sume		switch (select(maxfd + 1, recvecp, 0, 0, 0))
451119076Sume#endif
452119076Sume		{
45355163Sshin		case -1:
45478064Sume			if (errno != EINTR) {
45578064Sume				fatal("select");
45678064Sume				/*NOTREACHED*/
45778064Sume			}
45878064Sume			continue;
45955163Sshin		case 0:
46055163Sshin			continue;
46155163Sshin		default:
462119076Sume#ifdef HAVE_POLL_H
463119076Sume			if (set[0].revents & POLLIN)
464119076Sume#else
465119076Sume			if (FD_ISSET(ripsock, recvecp))
466119076Sume#endif
467119076Sume			{
46855163Sshin				sigprocmask(SIG_BLOCK, &mask, &omask);
46955163Sshin				riprecv();
47055163Sshin				sigprocmask(SIG_SETMASK, &omask, NULL);
47155163Sshin			}
472119076Sume#ifdef HAVE_POLL_H
473119076Sume			if (set[1].revents & POLLIN)
474119076Sume#else
475119076Sume			if (FD_ISSET(rtsock, recvecp))
476119076Sume#endif
477119076Sume			{
47855163Sshin				sigprocmask(SIG_BLOCK, &mask, &omask);
47955163Sshin				rtrecv();
48055163Sshin				sigprocmask(SIG_SETMASK, &omask, NULL);
48155163Sshin			}
48255163Sshin		}
48355163Sshin	}
48455163Sshin}
48555163Sshin
48678064Sumevoid
487119079Sumesighandler(signo)
488119079Sume	int signo;
48978064Sume{
49078064Sume
49178064Sume	switch (signo) {
49278064Sume	case SIGALRM:
49378064Sume		seenalrm++;
49478064Sume		break;
49578064Sume	case SIGQUIT:
49678064Sume	case SIGTERM:
49778064Sume		seenquit++;
49878064Sume		break;
49978064Sume	case SIGUSR1:
50078064Sume	case SIGHUP:
50178064Sume	case SIGINT:
50278064Sume		seenusr1++;
50378064Sume		break;
50478064Sume	}
50578064Sume}
50678064Sume
50755163Sshin/*
50855163Sshin * gracefully exits after resetting sockopts.
50955163Sshin */
51055163Sshin/* ARGSUSED */
51155163Sshinvoid
51278064Sumertdexit()
51355163Sshin{
51455163Sshin	struct	riprt *rrt;
51555163Sshin
51655163Sshin	alarm(0);
51755163Sshin	for (rrt = riprt; rrt; rrt = rrt->rrt_next) {
51862607Sitojun		if (rrt->rrt_rflags & RRTF_AGGREGATE) {
51955163Sshin			delroute(&rrt->rrt_info, &rrt->rrt_gw);
52055163Sshin		}
52155163Sshin	}
52255163Sshin	close(ripsock);
52355163Sshin	close(rtsock);
52455163Sshin	syslog(LOG_INFO, "**** Terminated ****");
52555163Sshin	closelog();
52655163Sshin	exit(1);
52755163Sshin}
52855163Sshin
52955163Sshin/*
53055163Sshin * Called periodically:
53155163Sshin *	1. age out the learned route. remove it if necessary.
53255163Sshin *	2. submit RIP6_RESPONSE packets.
53378064Sume * Invoked in every SUPPLY_INTERVAL6 (30) seconds.  I believe we don't have
53455163Sshin * to invoke this function in every 1 or 5 or 10 seconds only to age the
53555163Sshin * routes more precisely.
53655163Sshin */
53755163Sshin/* ARGSUSED */
53855163Sshinvoid
53978064Sumeripalarm()
54055163Sshin{
54155163Sshin	struct	ifc *ifcp;
54255163Sshin	struct	riprt *rrt, *rrt_prev, *rrt_next;
54355163Sshin	time_t	t_lifetime, t_holddown;
54455163Sshin
54555163Sshin	/* age the RIP routes */
54655163Sshin	rrt_prev = 0;
54755163Sshin	t_lifetime = time(NULL) - RIP_LIFETIME;
54855163Sshin	t_holddown = t_lifetime - RIP_HOLDDOWN;
54955163Sshin	for (rrt = riprt; rrt; rrt = rrt_next) {
55055163Sshin		rrt_next = rrt->rrt_next;
55155163Sshin
55255163Sshin		if (rrt->rrt_t == 0) {
55355163Sshin			rrt_prev = rrt;
55455163Sshin			continue;
55555163Sshin		}
55655163Sshin		if (rrt->rrt_t < t_holddown) {
55755163Sshin			if (rrt_prev) {
55855163Sshin				rrt_prev->rrt_next = rrt->rrt_next;
55955163Sshin			} else {
56055163Sshin				riprt = rrt->rrt_next;
56155163Sshin			}
56255163Sshin			delroute(&rrt->rrt_info, &rrt->rrt_gw);
56355163Sshin			free(rrt);
56455163Sshin			continue;
56555163Sshin		}
56655163Sshin		if (rrt->rrt_t < t_lifetime)
56755163Sshin			rrt->rrt_info.rip6_metric = HOPCNT_INFINITY6;
56855163Sshin		rrt_prev = rrt;
56955163Sshin	}
57055163Sshin	/* Supply updates */
57155163Sshin	for (ifcp = ifc; ifcp; ifcp = ifcp->ifc_next) {
57255163Sshin		if (ifcp->ifc_index > 0 && (ifcp->ifc_flags & IFF_UP))
57355163Sshin			ripsend(ifcp, &ifcp->ifc_ripsin, 0);
57455163Sshin	}
57555163Sshin	alarm(ripinterval(SUPPLY_INTERVAL6));
57655163Sshin}
57755163Sshin
57855163Sshinvoid
57955163Sshininit()
58055163Sshin{
581119034Sume	int	error;
582119034Sume	const int int0 = 0, int1 = 1, int255 = 255;
58355163Sshin	struct	addrinfo hints, *res;
584119081Sume	char	port[NI_MAXSERV];
58555163Sshin
58655163Sshin	ifc = (struct ifc *)NULL;
58755163Sshin	nifc = 0;
58855163Sshin	nindex2ifc = 0;	/*initial guess*/
58955163Sshin	index2ifc = NULL;
590119081Sume	snprintf(port, sizeof(port), "%u", RIP6_PORT);
59155163Sshin
59255163Sshin	memset(&hints, 0, sizeof(hints));
59355163Sshin	hints.ai_family = PF_INET6;
59455163Sshin	hints.ai_socktype = SOCK_DGRAM;
595119080Sume	hints.ai_protocol = IPPROTO_UDP;
59655163Sshin	hints.ai_flags = AI_PASSIVE;
59755163Sshin	error = getaddrinfo(NULL, port, &hints, &res);
59878064Sume	if (error) {
59966807Skris		fatal("%s", gai_strerror(error));
60078064Sume		/*NOTREACHED*/
60178064Sume	}
60278064Sume	if (res->ai_next) {
60355163Sshin		fatal(":: resolved to multiple address");
60478064Sume		/*NOTREACHED*/
60578064Sume	}
60655163Sshin
60755163Sshin	ripsock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
60878064Sume	if (ripsock < 0) {
60955163Sshin		fatal("rip socket");
61078064Sume		/*NOTREACHED*/
61178064Sume	}
612119034Sume#ifdef IPV6_V6ONLY
613119034Sume	if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_V6ONLY,
614119034Sume	    &int1, sizeof(int1)) < 0) {
615119034Sume		fatal("rip IPV6_V6ONLY");
616119034Sume		/*NOTREACHED*/
617119034Sume	}
618119034Sume#endif
61978064Sume	if (bind(ripsock, res->ai_addr, res->ai_addrlen) < 0) {
62055163Sshin		fatal("rip bind");
62178064Sume		/*NOTREACHED*/
62278064Sume	}
62355163Sshin	if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
62478064Sume	    &int255, sizeof(int255)) < 0) {
62555163Sshin		fatal("rip IPV6_MULTICAST_HOPS");
62678064Sume		/*NOTREACHED*/
62778064Sume	}
62855163Sshin	if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
62978064Sume	    &int0, sizeof(int0)) < 0) {
63055163Sshin		fatal("rip IPV6_MULTICAST_LOOP");
63178064Sume		/*NOTREACHED*/
63278064Sume	}
63362921Sume
63462607Sitojun#ifdef IPV6_RECVPKTINFO
635119034Sume	if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_RECVPKTINFO,
636119034Sume	    &int1, sizeof(int1)) < 0) {
63762607Sitojun		fatal("rip IPV6_RECVPKTINFO");
63878064Sume		/*NOTREACHED*/
63978064Sume	}
64062607Sitojun#else  /* old adv. API */
641119034Sume	if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_PKTINFO,
642119034Sume	    &int1, sizeof(int1)) < 0) {
64355163Sshin		fatal("rip IPV6_PKTINFO");
64478064Sume		/*NOTREACHED*/
64578064Sume	}
64662607Sitojun#endif
64755163Sshin
648164339Ssuz#ifdef IPV6_RECVPKTINFO
649164339Ssuz	if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
650164339Ssuz	    &int1, sizeof(int1)) < 0) {
651164339Ssuz		fatal("rip IPV6_RECVHOPLIMIT");
652164339Ssuz		/*NOTREACHED*/
653164339Ssuz	}
654164339Ssuz#else  /* old adv. API */
655164339Ssuz	if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_HOPLIMIT,
656164339Ssuz	    &int1, sizeof(int1)) < 0) {
657164339Ssuz		fatal("rip IPV6_HOPLIMIT");
658164339Ssuz		/*NOTREACHED*/
659164339Ssuz	}
660164339Ssuz#endif
661164339Ssuz
66255163Sshin	memset(&hints, 0, sizeof(hints));
66355163Sshin	hints.ai_family = PF_INET6;
66455163Sshin	hints.ai_socktype = SOCK_DGRAM;
665119080Sume	hints.ai_protocol = IPPROTO_UDP;
66655163Sshin	error = getaddrinfo(RIP6_DEST, port, &hints, &res);
66778064Sume	if (error) {
66866807Skris		fatal("%s", gai_strerror(error));
66978064Sume		/*NOTREACHED*/
67078064Sume	}
67178064Sume	if (res->ai_next) {
67255163Sshin		fatal("%s resolved to multiple address", RIP6_DEST);
67378064Sume		/*NOTREACHED*/
67478064Sume	}
67555163Sshin	memcpy(&ripsin, res->ai_addr, res->ai_addrlen);
67655163Sshin
677119076Sume#ifdef HAVE_POLL_H
678119076Sume	set[0].fd = ripsock;
679119076Sume	set[0].events = POLLIN;
680119076Sume#else
681119070Sume	maxfd = ripsock;
682119076Sume#endif
68355163Sshin
68455163Sshin	if (nflag == 0) {
68578064Sume		if ((rtsock = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) {
68655163Sshin			fatal("route socket");
68778064Sume			/*NOTREACHED*/
68878064Sume		}
689119076Sume#ifdef HAVE_POLL_H
690119076Sume		set[1].fd = rtsock;
691119076Sume		set[1].events = POLLIN;
692119076Sume#else
693119070Sume		if (rtsock > maxfd)
694119070Sume			maxfd = rtsock;
695119076Sume#endif
696119076Sume	} else {
697119076Sume#ifdef HAVE_POLL_H
698119076Sume		set[1].fd = -1;
699119076Sume#else
70055163Sshin		rtsock = -1;	/*just for safety */
701119076Sume#endif
702119076Sume	}
703119070Sume
704119076Sume#ifndef HAVE_POLL_H
705119070Sume	fdmasks = howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask);
706119070Sume	if ((sockvecp = malloc(fdmasks)) == NULL) {
707119070Sume		fatal("malloc");
708119070Sume		/*NOTREACHED*/
709119070Sume	}
710119070Sume	if ((recvecp = malloc(fdmasks)) == NULL) {
711119070Sume		fatal("malloc");
712119070Sume		/*NOTREACHED*/
713119070Sume	}
714119070Sume	memset(sockvecp, 0, fdmasks);
715119070Sume	FD_SET(ripsock, sockvecp);
716119070Sume	if (rtsock >= 0)
717119070Sume		FD_SET(rtsock, sockvecp);
718119076Sume#endif
71955163Sshin}
72055163Sshin
72162607Sitojun#define	RIPSIZE(n) \
72262607Sitojun	(sizeof(struct rip6) + ((n)-1) * sizeof(struct netinfo6))
72355163Sshin
72455163Sshin/*
72555163Sshin * ripflush flushes the rip datagram stored in the rip buffer
72655163Sshin */
72755163Sshinstatic int nrt;
72855163Sshinstatic struct netinfo6 *np;
72955163Sshin
73055163Sshinvoid
731119031Sumeripflush(ifcp, sin6)
73255163Sshin	struct ifc *ifcp;
733119031Sume	struct sockaddr_in6 *sin6;
73455163Sshin{
73555163Sshin	int i;
73655163Sshin	int error;
73755163Sshin
73855163Sshin	if (ifcp)
73955163Sshin		tracet(1, "Send(%s): info(%d) to %s.%d\n",
74055163Sshin			ifcp->ifc_name, nrt,
741119031Sume			inet6_n2p(&sin6->sin6_addr), ntohs(sin6->sin6_port));
74255163Sshin	else
74355163Sshin		tracet(1, "Send: info(%d) to %s.%d\n",
744119031Sume			nrt, inet6_n2p(&sin6->sin6_addr), ntohs(sin6->sin6_port));
74555163Sshin	if (dflag >= 2) {
74655163Sshin		np = ripbuf->rip6_nets;
74755163Sshin		for (i = 0; i < nrt; i++, np++) {
74855163Sshin			if (np->rip6_metric == NEXTHOP_METRIC) {
74955163Sshin				if (IN6_IS_ADDR_UNSPECIFIED(&np->rip6_dest))
75062607Sitojun					trace(2, "    NextHop reset");
75155163Sshin				else {
75255163Sshin					trace(2, "    NextHop %s",
75355163Sshin						inet6_n2p(&np->rip6_dest));
75455163Sshin				}
75555163Sshin			} else {
75655163Sshin				trace(2, "    %s/%d[%d]",
75755163Sshin					inet6_n2p(&np->rip6_dest),
75855163Sshin					np->rip6_plen, np->rip6_metric);
75955163Sshin			}
76055163Sshin			if (np->rip6_tag) {
76155163Sshin				trace(2, "  tag=0x%04x",
76255163Sshin					ntohs(np->rip6_tag) & 0xffff);
76355163Sshin			}
76455163Sshin			trace(2, "\n");
76555163Sshin		}
76655163Sshin	}
767119031Sume	error = sendpacket(sin6, RIPSIZE(nrt));
76855163Sshin	if (error == EAFNOSUPPORT) {
76955163Sshin		/* Protocol not supported */
77055163Sshin		tracet(1, "Could not send info to %s (%s): "
77155163Sshin			"set IFF_UP to 0\n",
77255163Sshin			ifcp->ifc_name, inet6_n2p(&ifcp->ifc_ripsin.sin6_addr));
77355163Sshin		ifcp->ifc_flags &= ~IFF_UP;	/* As if down for AF_INET6 */
77455163Sshin	}
77555163Sshin	nrt = 0; np = ripbuf->rip6_nets;
77655163Sshin}
77755163Sshin
77855163Sshin/*
77955163Sshin * Generate RIP6_RESPONSE packets and send them.
78055163Sshin */
78155163Sshinvoid
782119031Sumeripsend(ifcp, sin6, flag)
78355163Sshin	struct	ifc *ifcp;
784119031Sume	struct	sockaddr_in6 *sin6;
78555163Sshin	int flag;
78655163Sshin{
78755163Sshin	struct	riprt *rrt;
78855163Sshin	struct	in6_addr *nh;	/* next hop */
78978064Sume	int	maxrte;
79055163Sshin
791119084Sume	if (qflag)
792119084Sume		return;
793119084Sume
79455163Sshin	if (ifcp == NULL) {
79555163Sshin		/*
79655163Sshin		 * Request from non-link local address is not
79755163Sshin		 * a regular route6d update.
79855163Sshin		 */
79962607Sitojun		maxrte = (IFMINMTU - sizeof(struct ip6_hdr) -
80062607Sitojun				sizeof(struct udphdr) -
80155163Sshin				sizeof(struct rip6) + sizeof(struct netinfo6)) /
80255163Sshin				sizeof(struct netinfo6);
80355163Sshin		nrt = 0; np = ripbuf->rip6_nets; nh = NULL;
80455163Sshin		for (rrt = riprt; rrt; rrt = rrt->rrt_next) {
80562607Sitojun			if (rrt->rrt_rflags & RRTF_NOADVERTISE)
80655163Sshin				continue;
80755163Sshin			/* Put the route to the buffer */
80855163Sshin			*np = rrt->rrt_info;
80955163Sshin			np++; nrt++;
81055163Sshin			if (nrt == maxrte) {
811119031Sume				ripflush(NULL, sin6);
81255163Sshin				nh = NULL;
81355163Sshin			}
81455163Sshin		}
81555163Sshin		if (nrt)	/* Send last packet */
816119031Sume			ripflush(NULL, sin6);
81755163Sshin		return;
81855163Sshin	}
81955163Sshin
82062607Sitojun	if ((flag & RRTF_SENDANYWAY) == 0 &&
82155163Sshin	    (qflag || (ifcp->ifc_flags & IFF_LOOPBACK)))
82255163Sshin		return;
82378064Sume
82478064Sume	/* -N: no use */
82555163Sshin	if (iff_find(ifcp, 'N') != NULL)
82655163Sshin		return;
82778064Sume
82878064Sume	/* -T: generate default route only */
82955163Sshin	if (iff_find(ifcp, 'T') != NULL) {
83055163Sshin		struct netinfo6 rrt_info;
83155163Sshin		memset(&rrt_info, 0, sizeof(struct netinfo6));
83255163Sshin		rrt_info.rip6_dest = in6addr_any;
83355163Sshin		rrt_info.rip6_plen = 0;
83455163Sshin		rrt_info.rip6_metric = 1;
83578064Sume		rrt_info.rip6_metric += ifcp->ifc_metric;
83655163Sshin		rrt_info.rip6_tag = htons(routetag & 0xffff);
83755163Sshin		np = ripbuf->rip6_nets;
83855163Sshin		*np = rrt_info;
83955163Sshin		nrt = 1;
840119031Sume		ripflush(ifcp, sin6);
84155163Sshin		return;
84255163Sshin	}
84378064Sume
84462607Sitojun	maxrte = (ifcp->ifc_mtu - sizeof(struct ip6_hdr) -
84562607Sitojun			sizeof(struct udphdr) -
84655163Sshin			sizeof(struct rip6) + sizeof(struct netinfo6)) /
84755163Sshin			sizeof(struct netinfo6);
84878064Sume
84955163Sshin	nrt = 0; np = ripbuf->rip6_nets; nh = NULL;
85055163Sshin	for (rrt = riprt; rrt; rrt = rrt->rrt_next) {
85162607Sitojun		if (rrt->rrt_rflags & RRTF_NOADVERTISE)
85255163Sshin			continue;
85378064Sume
85478064Sume		/* Need to check filter here */
85578064Sume		if (out_filter(rrt, ifcp) == 0)
85655163Sshin			continue;
85778064Sume
85855163Sshin		/* Check split horizon and other conditions */
85955163Sshin		if (tobeadv(rrt, ifcp) == 0)
86055163Sshin			continue;
86178064Sume
86255163Sshin		/* Only considers the routes with flag if specified */
86362607Sitojun		if ((flag & RRTF_CHANGED) &&
86462607Sitojun		    (rrt->rrt_rflags & RRTF_CHANGED) == 0)
86555163Sshin			continue;
86678064Sume
86755163Sshin		/* Check nexthop */
86855163Sshin		if (rrt->rrt_index == ifcp->ifc_index &&
86955163Sshin		    !IN6_IS_ADDR_UNSPECIFIED(&rrt->rrt_gw) &&
87062607Sitojun		    (rrt->rrt_rflags & RRTF_NH_NOT_LLADDR) == 0) {
87155163Sshin			if (nh == NULL || !IN6_ARE_ADDR_EQUAL(nh, &rrt->rrt_gw)) {
87255163Sshin				if (nrt == maxrte - 2)
873119031Sume					ripflush(ifcp, sin6);
87455163Sshin				np->rip6_dest = rrt->rrt_gw;
87555163Sshin				np->rip6_plen = 0;
87655163Sshin				np->rip6_tag = 0;
87755163Sshin				np->rip6_metric = NEXTHOP_METRIC;
87855163Sshin				nh = &rrt->rrt_gw;
87955163Sshin				np++; nrt++;
88055163Sshin			}
88155163Sshin		} else if (nh && (rrt->rrt_index != ifcp->ifc_index ||
88255163Sshin			          !IN6_ARE_ADDR_EQUAL(nh, &rrt->rrt_gw) ||
88362607Sitojun				  rrt->rrt_rflags & RRTF_NH_NOT_LLADDR)) {
88455163Sshin			/* Reset nexthop */
88555163Sshin			if (nrt == maxrte - 2)
886119031Sume				ripflush(ifcp, sin6);
88755163Sshin			memset(np, 0, sizeof(struct netinfo6));
88855163Sshin			np->rip6_metric = NEXTHOP_METRIC;
88955163Sshin			nh = NULL;
89055163Sshin			np++; nrt++;
89155163Sshin		}
89278064Sume
89355163Sshin		/* Put the route to the buffer */
89455163Sshin		*np = rrt->rrt_info;
89555163Sshin		np++; nrt++;
89655163Sshin		if (nrt == maxrte) {
897119031Sume			ripflush(ifcp, sin6);
89855163Sshin			nh = NULL;
89955163Sshin		}
90055163Sshin	}
90155163Sshin	if (nrt)	/* Send last packet */
902119031Sume		ripflush(ifcp, sin6);
90355163Sshin}
90455163Sshin
90555163Sshin/*
90678064Sume * outbound filter logic, per-route/interface.
90778064Sume */
90878064Sumeint
90978064Sumeout_filter(rrt, ifcp)
91078064Sume	struct riprt *rrt;
91178064Sume	struct ifc *ifcp;
91278064Sume{
91378064Sume	struct iff *iffp;
91478064Sume	struct in6_addr ia;
91578064Sume	int ok;
91678064Sume
91778064Sume	/*
91878064Sume	 * -A: filter out less specific routes, if we have aggregated
91978064Sume	 * route configured.
92078064Sume	 */
92178064Sume	for (iffp = ifcp->ifc_filter; iffp; iffp = iffp->iff_next) {
92278064Sume		if (iffp->iff_type != 'A')
92378064Sume			continue;
92478064Sume		if (rrt->rrt_info.rip6_plen <= iffp->iff_plen)
92578064Sume			continue;
92678064Sume		ia = rrt->rrt_info.rip6_dest;
92778064Sume		applyplen(&ia, iffp->iff_plen);
92878064Sume		if (IN6_ARE_ADDR_EQUAL(&ia, &iffp->iff_addr))
92978064Sume			return 0;
93078064Sume	}
93178064Sume
93278064Sume	/*
93378064Sume	 * if it is an aggregated route, advertise it only to the
93478064Sume	 * interfaces specified on -A.
93578064Sume	 */
93678064Sume	if ((rrt->rrt_rflags & RRTF_AGGREGATE) != 0) {
93778064Sume		ok = 0;
93878064Sume		for (iffp = ifcp->ifc_filter; iffp; iffp = iffp->iff_next) {
93978064Sume			if (iffp->iff_type != 'A')
94078064Sume				continue;
94178064Sume			if (rrt->rrt_info.rip6_plen == iffp->iff_plen &&
94278064Sume			    IN6_ARE_ADDR_EQUAL(&rrt->rrt_info.rip6_dest,
94378064Sume			    &iffp->iff_addr)) {
94478064Sume				ok = 1;
94578064Sume				break;
94678064Sume			}
94778064Sume		}
94878064Sume		if (!ok)
94978064Sume			return 0;
95078064Sume	}
95178064Sume
95278064Sume	/*
95378064Sume	 * -O: advertise only if prefix matches the configured prefix.
95478064Sume	 */
95578064Sume	if (iff_find(ifcp, 'O')) {
95678064Sume		ok = 0;
95778064Sume		for (iffp = ifcp->ifc_filter; iffp; iffp = iffp->iff_next) {
95878064Sume			if (iffp->iff_type != 'O')
95978064Sume				continue;
96078064Sume			if (rrt->rrt_info.rip6_plen < iffp->iff_plen)
96178064Sume				continue;
96278064Sume			ia = rrt->rrt_info.rip6_dest;
96378064Sume			applyplen(&ia, iffp->iff_plen);
96478064Sume			if (IN6_ARE_ADDR_EQUAL(&ia, &iffp->iff_addr)) {
96578064Sume				ok = 1;
96678064Sume				break;
96778064Sume			}
96878064Sume		}
96978064Sume		if (!ok)
97078064Sume			return 0;
97178064Sume	}
97278064Sume
97378064Sume	/* the prefix should be advertised */
97478064Sume	return 1;
97578064Sume}
97678064Sume
97778064Sume/*
97855163Sshin * Determine if the route is to be advertised on the specified interface.
97955163Sshin * It checks options specified in the arguments and the split horizon rule.
98055163Sshin */
98155163Sshinint
98255163Sshintobeadv(rrt, ifcp)
98355163Sshin	struct riprt *rrt;
98455163Sshin	struct ifc *ifcp;
98555163Sshin{
98655163Sshin
98755163Sshin	/* Special care for static routes */
98855163Sshin	if (rrt->rrt_flags & RTF_STATIC) {
98962607Sitojun		/* XXX don't advertise reject/blackhole routes */
99062607Sitojun		if (rrt->rrt_flags & (RTF_REJECT | RTF_BLACKHOLE))
99162607Sitojun			return 0;
99262607Sitojun
99355163Sshin		if (Sflag)	/* Yes, advertise it anyway */
99455163Sshin			return 1;
99555163Sshin		if (sflag && rrt->rrt_index != ifcp->ifc_index)
99655163Sshin			return 1;
99755163Sshin		return 0;
99855163Sshin	}
99955163Sshin	/* Regular split horizon */
100055163Sshin	if (hflag == 0 && rrt->rrt_index == ifcp->ifc_index)
100155163Sshin		return 0;
100255163Sshin	return 1;
100355163Sshin}
100455163Sshin
100555163Sshin/*
100655163Sshin * Send a rip packet actually.
100755163Sshin */
100855163Sshinint
1009119031Sumesendpacket(sin6, len)
1010119031Sume	struct	sockaddr_in6 *sin6;
101155163Sshin	int	len;
101255163Sshin{
101355163Sshin	struct msghdr m;
101455163Sshin	struct cmsghdr *cm;
101555163Sshin	struct iovec iov[2];
101655163Sshin	u_char cmsgbuf[256];
101755163Sshin	struct in6_pktinfo *pi;
101878064Sume	int idx;
101955163Sshin	struct sockaddr_in6 sincopy;
102055163Sshin
102155163Sshin	/* do not overwrite the given sin */
1022119031Sume	sincopy = *sin6;
1023119031Sume	sin6 = &sincopy;
102455163Sshin
1025119035Sume	if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) ||
1026243231Shrs	    IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
1027243231Shrs		idx = sin6->sin6_scope_id;
1028243231Shrs	else
102978064Sume		idx = 0;
103055163Sshin
1031119031Sume	m.msg_name = (caddr_t)sin6;
1032119031Sume	m.msg_namelen = sizeof(*sin6);
103355163Sshin	iov[0].iov_base = (caddr_t)ripbuf;
103455163Sshin	iov[0].iov_len = len;
103555163Sshin	m.msg_iov = iov;
103655163Sshin	m.msg_iovlen = 1;
103778064Sume	if (!idx) {
103855163Sshin		m.msg_control = NULL;
103955163Sshin		m.msg_controllen = 0;
104055163Sshin	} else {
104155163Sshin		memset(cmsgbuf, 0, sizeof(cmsgbuf));
104255163Sshin		cm = (struct cmsghdr *)cmsgbuf;
104355163Sshin		m.msg_control = (caddr_t)cm;
104455163Sshin		m.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
104555163Sshin
104655163Sshin		cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
104755163Sshin		cm->cmsg_level = IPPROTO_IPV6;
104855163Sshin		cm->cmsg_type = IPV6_PKTINFO;
104955163Sshin		pi = (struct in6_pktinfo *)CMSG_DATA(cm);
105055163Sshin		memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*::*/
105178064Sume		pi->ipi6_ifindex = idx;
105255163Sshin	}
105355163Sshin
105455163Sshin	if (sendmsg(ripsock, &m, 0 /*MSG_DONTROUTE*/) < 0) {
105555163Sshin		trace(1, "sendmsg: %s\n", strerror(errno));
105655163Sshin		return errno;
105755163Sshin	}
105862921Sume
105955163Sshin	return 0;
106055163Sshin}
106155163Sshin
106255163Sshin/*
106378064Sume * Receive and process RIP packets.  Update the routes/kernel forwarding
106455163Sshin * table if necessary.
106555163Sshin */
106655163Sshinvoid
106755163Sshinriprecv()
106855163Sshin{
106955163Sshin	struct	ifc *ifcp, *ic;
107055163Sshin	struct	sockaddr_in6 fsock;
107155163Sshin	struct	in6_addr nh;	/* next hop */
107255163Sshin	struct	rip6 *rp;
107355163Sshin	struct	netinfo6 *np, *nq;
107455163Sshin	struct	riprt *rrt;
1075122677Sume	ssize_t	len, nn;
1076122677Sume	unsigned int need_trigger, idx;
107755163Sshin	char	buf[4 * RIP6_MAXMTU];
107855163Sshin	time_t	t;
107955163Sshin	struct msghdr m;
108055163Sshin	struct cmsghdr *cm;
108155163Sshin	struct iovec iov[2];
108255163Sshin	u_char cmsgbuf[256];
1083164339Ssuz	struct in6_pktinfo *pi = NULL;
1084164339Ssuz	int *hlimp = NULL;
108555163Sshin	struct iff *iffp;
108655163Sshin	struct in6_addr ia;
108755163Sshin	int ok;
108878064Sume	time_t t_half_lifetime;
108955163Sshin
109055163Sshin	need_trigger = 0;
109162921Sume
109255163Sshin	m.msg_name = (caddr_t)&fsock;
109355163Sshin	m.msg_namelen = sizeof(fsock);
109455163Sshin	iov[0].iov_base = (caddr_t)buf;
109555163Sshin	iov[0].iov_len = sizeof(buf);
109655163Sshin	m.msg_iov = iov;
109755163Sshin	m.msg_iovlen = 1;
109855163Sshin	cm = (struct cmsghdr *)cmsgbuf;
109955163Sshin	m.msg_control = (caddr_t)cm;
110055163Sshin	m.msg_controllen = sizeof(cmsgbuf);
110178064Sume	if ((len = recvmsg(ripsock, &m, 0)) < 0) {
110255163Sshin		fatal("recvmsg");
110378064Sume		/*NOTREACHED*/
110478064Sume	}
110578064Sume	idx = 0;
110655163Sshin	for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&m);
110755163Sshin	     cm;
110855163Sshin	     cm = (struct cmsghdr *)CMSG_NXTHDR(&m, cm)) {
1109164339Ssuz		if (cm->cmsg_level != IPPROTO_IPV6)
1110164339Ssuz		    continue;
1111164339Ssuz		switch (cm->cmsg_type) {
1112164339Ssuz		case IPV6_PKTINFO:
1113164339Ssuz			if (cm->cmsg_len != CMSG_LEN(sizeof(*pi))) {
1114164339Ssuz				trace(1,
1115164339Ssuz				    "invalid cmsg length for IPV6_PKTINFO\n");
1116164339Ssuz				return;
1117164339Ssuz			}
111855163Sshin			pi = (struct in6_pktinfo *)(CMSG_DATA(cm));
111978064Sume			idx = pi->ipi6_ifindex;
112055163Sshin			break;
1121164339Ssuz		case IPV6_HOPLIMIT:
1122164339Ssuz			if (cm->cmsg_len != CMSG_LEN(sizeof(int))) {
1123164339Ssuz				trace(1,
1124164339Ssuz				    "invalid cmsg length for IPV6_HOPLIMIT\n");
1125164339Ssuz				return;
1126164339Ssuz			}
1127164339Ssuz			hlimp = (int *)CMSG_DATA(cm);
1128164339Ssuz			break;
112955163Sshin		}
113055163Sshin	}
113155163Sshin
1132121779Ssuz	if (len < sizeof(struct rip6)) {
1133121779Ssuz		trace(1, "Packet too short\n");
1134121779Ssuz		return;
1135121779Ssuz	}
1136121779Ssuz
1137164339Ssuz	if (pi == NULL || hlimp == NULL) {
1138164339Ssuz		/*
1139164339Ssuz		 * This can happen when the kernel failed to allocate memory
1140164339Ssuz		 * for the ancillary data.  Although we might be able to handle
1141164339Ssuz		 * some cases without this info, those are minor and not so
1142164339Ssuz		 * important, so it's better to discard the packet for safer
1143164339Ssuz		 * operation.
1144164339Ssuz		 */
1145164339Ssuz		trace(1, "IPv6 packet information cannot be retrieved\n");
1146164339Ssuz		return;
1147164339Ssuz	}
1148164339Ssuz
114955163Sshin	nh = fsock.sin6_addr;
115055163Sshin	nn = (len - sizeof(struct rip6) + sizeof(struct netinfo6)) /
115155163Sshin		sizeof(struct netinfo6);
115255163Sshin	rp = (struct rip6 *)buf;
115355163Sshin	np = rp->rip6_nets;
115455163Sshin
1155119035Sume	if (rp->rip6_vers != RIP6_VERSION) {
115655163Sshin		trace(1, "Incorrect RIP version %d\n", rp->rip6_vers);
115755163Sshin		return;
115855163Sshin	}
115955163Sshin	if (rp->rip6_cmd == RIP6_REQUEST) {
116078064Sume		if (idx && idx < nindex2ifc) {
116178064Sume			ifcp = index2ifc[idx];
116255163Sshin			riprequest(ifcp, np, nn, &fsock);
116355163Sshin		} else {
116455163Sshin			riprequest(NULL, np, nn, &fsock);
116555163Sshin		}
116662607Sitojun		return;
116762607Sitojun	}
116855163Sshin
116955163Sshin	if (!IN6_IS_ADDR_LINKLOCAL(&fsock.sin6_addr)) {
1170164339Ssuz		trace(1, "Response from non-ll addr: %s\n",
117178064Sume		    inet6_n2p(&fsock.sin6_addr));
117255163Sshin		return;		/* Ignore packets from non-link-local addr */
117355163Sshin	}
1174164339Ssuz	if (ntohs(fsock.sin6_port) != RIP6_PORT) {
1175164339Ssuz		trace(1, "Response from non-rip port from %s\n",
1176164339Ssuz		    inet6_n2p(&fsock.sin6_addr));
1177164339Ssuz		return;
1178164339Ssuz	}
1179164339Ssuz	if (IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr) && *hlimp != 255) {
1180164339Ssuz		trace(1,
1181164339Ssuz		    "Response packet with a smaller hop limit (%d) from %s\n",
1182164339Ssuz		    *hlimp, inet6_n2p(&fsock.sin6_addr));
1183164339Ssuz		return;
1184164339Ssuz	}
1185164339Ssuz	/*
1186164339Ssuz	 * Further validation: since this program does not send off-link
1187164339Ssuz	 * requests, an incoming response must always come from an on-link
1188164339Ssuz	 * node.  Although this is normally ensured by the source address
1189164339Ssuz	 * check above, it may not 100% be safe because there are router
1190164339Ssuz	 * implementations that (invalidly) allow a packet with a link-local
1191164339Ssuz	 * source address to be forwarded to a different link.
1192164339Ssuz	 * So we also check whether the destination address is a link-local
1193164339Ssuz	 * address or the hop limit is 255.  Note that RFC2080 does not require
1194164339Ssuz	 * the specific hop limit for a unicast response, so we cannot assume
1195164339Ssuz	 * the limitation.
1196164339Ssuz	 */
1197164339Ssuz	if (!IN6_IS_ADDR_LINKLOCAL(&pi->ipi6_addr) && *hlimp != 255) {
1198164339Ssuz		trace(1,
1199164339Ssuz		    "Response packet possibly from an off-link node: "
1200164339Ssuz		    "from %s to %s hlim=%d\n",
1201164339Ssuz		    inet6_n2p(&fsock.sin6_addr),
1202164339Ssuz		    inet6_n2p(&pi->ipi6_addr), *hlimp);
1203164339Ssuz		return;
1204164339Ssuz	}
1205164339Ssuz
1206243231Shrs	idx = fsock.sin6_scope_id;
120778064Sume	ifcp = (idx < nindex2ifc) ? index2ifc[idx] : NULL;
120855163Sshin	if (!ifcp) {
120978064Sume		trace(1, "Packets to unknown interface index %d\n", idx);
121055163Sshin		return;		/* Ignore it */
121155163Sshin	}
121255163Sshin	if (IN6_ARE_ADDR_EQUAL(&ifcp->ifc_mylladdr, &fsock.sin6_addr))
121355163Sshin		return;		/* The packet is from me; ignore */
121455163Sshin	if (rp->rip6_cmd != RIP6_RESPONSE) {
121555163Sshin		trace(1, "Invalid command %d\n", rp->rip6_cmd);
121662607Sitojun		return;
121755163Sshin	}
121878064Sume
121978064Sume	/* -N: no use */
122055163Sshin	if (iff_find(ifcp, 'N') != NULL)
122155163Sshin		return;
122278064Sume
1223228674Sdim	tracet(1, "Recv(%s): from %s.%d info(%zd)\n",
122478064Sume	    ifcp->ifc_name, inet6_n2p(&nh), ntohs(fsock.sin6_port), nn);
122555163Sshin
122655163Sshin	t = time(NULL);
122778064Sume	t_half_lifetime = t - (RIP_LIFETIME/2);
122855163Sshin	for (; nn; nn--, np++) {
122955163Sshin		if (np->rip6_metric == NEXTHOP_METRIC) {
123055163Sshin			/* modify neighbor address */
123155163Sshin			if (IN6_IS_ADDR_LINKLOCAL(&np->rip6_dest)) {
123255163Sshin				nh = np->rip6_dest;
123355163Sshin				trace(1, "\tNexthop: %s\n", inet6_n2p(&nh));
123455163Sshin			} else if (IN6_IS_ADDR_UNSPECIFIED(&np->rip6_dest)) {
123555163Sshin				nh = fsock.sin6_addr;
123655163Sshin				trace(1, "\tNexthop: %s\n", inet6_n2p(&nh));
123755163Sshin			} else {
123855163Sshin				nh = fsock.sin6_addr;
123955163Sshin				trace(1, "\tInvalid Nexthop: %s\n",
124078064Sume				    inet6_n2p(&np->rip6_dest));
124155163Sshin			}
124255163Sshin			continue;
124355163Sshin		}
124455163Sshin		if (IN6_IS_ADDR_MULTICAST(&np->rip6_dest)) {
124555163Sshin			trace(1, "\tMulticast netinfo6: %s/%d [%d]\n",
124655163Sshin				inet6_n2p(&np->rip6_dest),
124755163Sshin				np->rip6_plen, np->rip6_metric);
124855163Sshin			continue;
124955163Sshin		}
125055163Sshin		if (IN6_IS_ADDR_LOOPBACK(&np->rip6_dest)) {
125155163Sshin			trace(1, "\tLoopback netinfo6: %s/%d [%d]\n",
125255163Sshin				inet6_n2p(&np->rip6_dest),
125355163Sshin				np->rip6_plen, np->rip6_metric);
125455163Sshin			continue;
125555163Sshin		}
125655163Sshin		if (IN6_IS_ADDR_LINKLOCAL(&np->rip6_dest)) {
125755163Sshin			trace(1, "\tLink Local netinfo6: %s/%d [%d]\n",
125855163Sshin				inet6_n2p(&np->rip6_dest),
125955163Sshin				np->rip6_plen, np->rip6_metric);
126055163Sshin			continue;
126155163Sshin		}
126255163Sshin		/* may need to pass sitelocal prefix in some case, however*/
126355163Sshin		if (IN6_IS_ADDR_SITELOCAL(&np->rip6_dest) && !lflag) {
126455163Sshin			trace(1, "\tSite Local netinfo6: %s/%d [%d]\n",
126555163Sshin				inet6_n2p(&np->rip6_dest),
126655163Sshin				np->rip6_plen, np->rip6_metric);
126755163Sshin			continue;
126855163Sshin		}
126955163Sshin		trace(2, "\tnetinfo6: %s/%d [%d]",
127055163Sshin			inet6_n2p(&np->rip6_dest),
127155163Sshin			np->rip6_plen, np->rip6_metric);
127255163Sshin		if (np->rip6_tag)
127355163Sshin			trace(2, "  tag=0x%04x", ntohs(np->rip6_tag) & 0xffff);
127462607Sitojun		if (dflag >= 2) {
127562607Sitojun			ia = np->rip6_dest;
127662607Sitojun			applyplen(&ia, np->rip6_plen);
127762607Sitojun			if (!IN6_ARE_ADDR_EQUAL(&ia, &np->rip6_dest))
127862607Sitojun				trace(2, " [junk outside prefix]");
127962607Sitojun		}
128055163Sshin
128178064Sume		/*
128278064Sume		 * -L: listen only if the prefix matches the configuration
128378064Sume		 */
128455163Sshin		ok = 1;		/* if there's no L filter, it is ok */
128555163Sshin		for (iffp = ifcp->ifc_filter; iffp; iffp = iffp->iff_next) {
128655163Sshin			if (iffp->iff_type != 'L')
128755163Sshin				continue;
128855163Sshin			ok = 0;
128955163Sshin			if (np->rip6_plen < iffp->iff_plen)
129055163Sshin				continue;
129155163Sshin			/* special rule: ::/0 means default, not "in /0" */
129255163Sshin			if (iffp->iff_plen == 0 && np->rip6_plen > 0)
129355163Sshin				continue;
129462607Sitojun			ia = np->rip6_dest;
129555163Sshin			applyplen(&ia, iffp->iff_plen);
129655163Sshin			if (IN6_ARE_ADDR_EQUAL(&ia, &iffp->iff_addr)) {
129755163Sshin				ok = 1;
129855163Sshin				break;
129955163Sshin			}
130055163Sshin		}
130155163Sshin		if (!ok) {
130255163Sshin			trace(2, "  (filtered)\n");
130355163Sshin			continue;
130455163Sshin		}
130555163Sshin
130655163Sshin		trace(2, "\n");
130755163Sshin		np->rip6_metric++;
130855163Sshin		np->rip6_metric += ifcp->ifc_metric;
130955163Sshin		if (np->rip6_metric > HOPCNT_INFINITY6)
131055163Sshin			np->rip6_metric = HOPCNT_INFINITY6;
131155163Sshin
131255163Sshin		applyplen(&np->rip6_dest, np->rip6_plen);
131378064Sume		if ((rrt = rtsearch(np, NULL)) != NULL) {
131455163Sshin			if (rrt->rrt_t == 0)
131555163Sshin				continue;	/* Intf route has priority */
131655163Sshin			nq = &rrt->rrt_info;
131755163Sshin			if (nq->rip6_metric > np->rip6_metric) {
131855163Sshin				if (rrt->rrt_index == ifcp->ifc_index &&
131955163Sshin				    IN6_ARE_ADDR_EQUAL(&nh, &rrt->rrt_gw)) {
132055163Sshin					/* Small metric from the same gateway */
132155163Sshin					nq->rip6_metric = np->rip6_metric;
132255163Sshin				} else {
132355163Sshin					/* Better route found */
132455163Sshin					rrt->rrt_index = ifcp->ifc_index;
132555163Sshin					/* Update routing table */
132655163Sshin					delroute(nq, &rrt->rrt_gw);
132755163Sshin					rrt->rrt_gw = nh;
132855163Sshin					*nq = *np;
132955163Sshin					addroute(rrt, &nh, ifcp);
133055163Sshin				}
133162607Sitojun				rrt->rrt_rflags |= RRTF_CHANGED;
133255163Sshin				rrt->rrt_t = t;
133355163Sshin				need_trigger = 1;
133455163Sshin			} else if (nq->rip6_metric < np->rip6_metric &&
133555163Sshin				   rrt->rrt_index == ifcp->ifc_index &&
133655163Sshin				   IN6_ARE_ADDR_EQUAL(&nh, &rrt->rrt_gw)) {
133755163Sshin				/* Got worse route from same gw */
133855163Sshin				nq->rip6_metric = np->rip6_metric;
133955163Sshin				rrt->rrt_t = t;
134062607Sitojun				rrt->rrt_rflags |= RRTF_CHANGED;
134155163Sshin				need_trigger = 1;
134255163Sshin			} else if (nq->rip6_metric == np->rip6_metric &&
134355163Sshin				   np->rip6_metric < HOPCNT_INFINITY6) {
134478064Sume				if (rrt->rrt_index == ifcp->ifc_index &&
134578064Sume				   IN6_ARE_ADDR_EQUAL(&nh, &rrt->rrt_gw)) {
134678064Sume					/* same metric, same route from same gw */
134778064Sume					rrt->rrt_t = t;
134878064Sume				} else if (rrt->rrt_t < t_half_lifetime) {
134978064Sume					/* Better route found */
135078064Sume					rrt->rrt_index = ifcp->ifc_index;
135178064Sume					/* Update routing table */
135278064Sume					delroute(nq, &rrt->rrt_gw);
135378064Sume					rrt->rrt_gw = nh;
135478064Sume					*nq = *np;
135578064Sume					addroute(rrt, &nh, ifcp);
135678064Sume					rrt->rrt_rflags |= RRTF_CHANGED;
135778064Sume					rrt->rrt_t = t;
135878064Sume				}
135955163Sshin			}
136062607Sitojun			/*
136155163Sshin			 * if nq->rip6_metric == HOPCNT_INFINITY6 then
136278064Sume			 * do not update age value.  Do nothing.
136355163Sshin			 */
136455163Sshin		} else if (np->rip6_metric < HOPCNT_INFINITY6) {
136555163Sshin			/* Got a new valid route */
136678064Sume			if ((rrt = MALLOC(struct riprt)) == NULL) {
136755163Sshin				fatal("malloc: struct riprt");
136878064Sume				/*NOTREACHED*/
136978064Sume			}
137062607Sitojun			memset(rrt, 0, sizeof(*rrt));
137155163Sshin			nq = &rrt->rrt_info;
137255163Sshin
137355163Sshin			rrt->rrt_same = NULL;
137455163Sshin			rrt->rrt_index = ifcp->ifc_index;
137555163Sshin			rrt->rrt_flags = RTF_UP|RTF_GATEWAY;
137655163Sshin			rrt->rrt_gw = nh;
137755163Sshin			*nq = *np;
137855163Sshin			applyplen(&nq->rip6_dest, nq->rip6_plen);
137955163Sshin			if (nq->rip6_plen == sizeof(struct in6_addr) * 8)
138055163Sshin				rrt->rrt_flags |= RTF_HOST;
138155163Sshin
138255163Sshin			/* Put the route to the list */
138355163Sshin			rrt->rrt_next = riprt;
138455163Sshin			riprt = rrt;
138555163Sshin			/* Update routing table */
138655163Sshin			addroute(rrt, &nh, ifcp);
138762607Sitojun			rrt->rrt_rflags |= RRTF_CHANGED;
138855163Sshin			need_trigger = 1;
138955163Sshin			rrt->rrt_t = t;
139055163Sshin		}
139155163Sshin	}
139255163Sshin	/* XXX need to care the interval between triggered updates */
139355163Sshin	if (need_trigger) {
139455163Sshin		if (nextalarm > time(NULL) + RIP_TRIG_INT6_MAX) {
139555163Sshin			for (ic = ifc; ic; ic = ic->ifc_next) {
139655163Sshin				if (ifcp->ifc_index == ic->ifc_index)
139755163Sshin					continue;
139855163Sshin				if (ic->ifc_flags & IFF_UP)
139955163Sshin					ripsend(ic, &ic->ifc_ripsin,
140062607Sitojun						RRTF_CHANGED);
140155163Sshin			}
140255163Sshin		}
140355163Sshin		/* Reset the flag */
140455163Sshin		for (rrt = riprt; rrt; rrt = rrt->rrt_next)
140562607Sitojun			rrt->rrt_rflags &= ~RRTF_CHANGED;
140655163Sshin	}
140755163Sshin}
140855163Sshin
140955163Sshin/*
141055163Sshin * Send all routes request packet to the specified interface.
141155163Sshin */
141255163Sshinvoid
141355163Sshinsendrequest(ifcp)
141455163Sshin	struct ifc *ifcp;
141555163Sshin{
141655163Sshin	struct netinfo6 *np;
141755163Sshin	int error;
141855163Sshin
141955163Sshin	if (ifcp->ifc_flags & IFF_LOOPBACK)
142055163Sshin		return;
142155163Sshin	ripbuf->rip6_cmd = RIP6_REQUEST;
142255163Sshin	np = ripbuf->rip6_nets;
142355163Sshin	memset(np, 0, sizeof(struct netinfo6));
142455163Sshin	np->rip6_metric = HOPCNT_INFINITY6;
142555163Sshin	tracet(1, "Send rtdump Request to %s (%s)\n",
142655163Sshin		ifcp->ifc_name, inet6_n2p(&ifcp->ifc_ripsin.sin6_addr));
142755163Sshin	error = sendpacket(&ifcp->ifc_ripsin, RIPSIZE(1));
142855163Sshin	if (error == EAFNOSUPPORT) {
142955163Sshin		/* Protocol not supported */
143055163Sshin		tracet(1, "Could not send rtdump Request to %s (%s): "
143155163Sshin			"set IFF_UP to 0\n",
143255163Sshin			ifcp->ifc_name, inet6_n2p(&ifcp->ifc_ripsin.sin6_addr));
143355163Sshin		ifcp->ifc_flags &= ~IFF_UP;	/* As if down for AF_INET6 */
143455163Sshin	}
143555163Sshin	ripbuf->rip6_cmd = RIP6_RESPONSE;
143655163Sshin}
143755163Sshin
143855163Sshin/*
143955163Sshin * Process a RIP6_REQUEST packet.
144055163Sshin */
144155163Sshinvoid
1442119031Sumeriprequest(ifcp, np, nn, sin6)
144355163Sshin	struct ifc *ifcp;
144455163Sshin	struct netinfo6 *np;
144555163Sshin	int nn;
1446119031Sume	struct sockaddr_in6 *sin6;
144755163Sshin{
144855163Sshin	int i;
144955163Sshin	struct riprt *rrt;
145055163Sshin
145155163Sshin	if (!(nn == 1 && IN6_IS_ADDR_UNSPECIFIED(&np->rip6_dest) &&
145255163Sshin	      np->rip6_plen == 0 && np->rip6_metric == HOPCNT_INFINITY6)) {
145355163Sshin		/* Specific response, don't split-horizon */
145455163Sshin		trace(1, "\tRIP Request\n");
145555163Sshin		for (i = 0; i < nn; i++, np++) {
145678064Sume			rrt = rtsearch(np, NULL);
145755163Sshin			if (rrt)
145855163Sshin				np->rip6_metric = rrt->rrt_info.rip6_metric;
145955163Sshin			else
146055163Sshin				np->rip6_metric = HOPCNT_INFINITY6;
146155163Sshin		}
1462119031Sume		(void)sendpacket(sin6, RIPSIZE(nn));
146355163Sshin		return;
146455163Sshin	}
146555163Sshin	/* Whole routing table dump */
146655163Sshin	trace(1, "\tRIP Request -- whole routing table\n");
1467119031Sume	ripsend(ifcp, sin6, RRTF_SENDANYWAY);
146855163Sshin}
146955163Sshin
147055163Sshin/*
147155163Sshin * Get information of each interface.
147255163Sshin */
147355163Sshinvoid
147455163Sshinifconfig()
147555163Sshin{
147662607Sitojun	struct ifaddrs *ifap, *ifa;
147762607Sitojun	struct ifc *ifcp;
147862607Sitojun	struct ipv6_mreq mreq;
147962607Sitojun	int s;
148062607Sitojun
148178064Sume	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
148262607Sitojun		fatal("socket");
148378064Sume		/*NOTREACHED*/
148478064Sume	}
148562607Sitojun
148678064Sume	if (getifaddrs(&ifap) != 0) {
148762607Sitojun		fatal("getifaddrs");
148878064Sume		/*NOTREACHED*/
148978064Sume	}
149062607Sitojun
149162607Sitojun	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
149262607Sitojun		if (ifa->ifa_addr->sa_family != AF_INET6)
149362607Sitojun			continue;
149462607Sitojun		ifcp = ifc_find(ifa->ifa_name);
149562607Sitojun		/* we are interested in multicast-capable interfaces */
149662607Sitojun		if ((ifa->ifa_flags & IFF_MULTICAST) == 0)
149762607Sitojun			continue;
149862607Sitojun		if (!ifcp) {
149962607Sitojun			/* new interface */
150078064Sume			if ((ifcp = MALLOC(struct ifc)) == NULL) {
150162607Sitojun				fatal("malloc: struct ifc");
150278064Sume				/*NOTREACHED*/
150378064Sume			}
150462607Sitojun			memset(ifcp, 0, sizeof(*ifcp));
150562607Sitojun			ifcp->ifc_index = -1;
150662607Sitojun			ifcp->ifc_next = ifc;
150762607Sitojun			ifc = ifcp;
150862607Sitojun			nifc++;
150962607Sitojun			ifcp->ifc_name = allocopy(ifa->ifa_name);
151062607Sitojun			ifcp->ifc_addr = 0;
151162607Sitojun			ifcp->ifc_filter = 0;
151262607Sitojun			ifcp->ifc_flags = ifa->ifa_flags;
151362607Sitojun			trace(1, "newif %s <%s>\n", ifcp->ifc_name,
151462607Sitojun				ifflags(ifcp->ifc_flags));
151562607Sitojun			if (!strcmp(ifcp->ifc_name, LOOPBACK_IF))
151662607Sitojun				loopifcp = ifcp;
151762607Sitojun		} else {
151862607Sitojun			/* update flag, this may be up again */
151962607Sitojun			if (ifcp->ifc_flags != ifa->ifa_flags) {
152062607Sitojun				trace(1, "%s: <%s> -> ", ifcp->ifc_name,
152162607Sitojun					ifflags(ifcp->ifc_flags));
152262607Sitojun				trace(1, "<%s>\n", ifflags(ifa->ifa_flags));
152378064Sume				ifcp->ifc_cflags |= IFC_CHANGED;
152462607Sitojun			}
152562607Sitojun			ifcp->ifc_flags = ifa->ifa_flags;
152662607Sitojun		}
152762607Sitojun		ifconfig1(ifa->ifa_name, ifa->ifa_addr, ifcp, s);
152862607Sitojun		if ((ifcp->ifc_flags & (IFF_LOOPBACK | IFF_UP)) == IFF_UP
152962607Sitojun		 && 0 < ifcp->ifc_index && !ifcp->ifc_joined) {
153062607Sitojun			mreq.ipv6mr_multiaddr = ifcp->ifc_ripsin.sin6_addr;
153162607Sitojun			mreq.ipv6mr_interface = ifcp->ifc_index;
153278064Sume			if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
153378064Sume			    &mreq, sizeof(mreq)) < 0) {
153462607Sitojun				fatal("IPV6_JOIN_GROUP");
153578064Sume				/*NOTREACHED*/
153678064Sume			}
153762607Sitojun			trace(1, "join %s %s\n", ifcp->ifc_name, RIP6_DEST);
153862607Sitojun			ifcp->ifc_joined++;
153962607Sitojun		}
154062607Sitojun	}
154162607Sitojun	close(s);
154262607Sitojun	freeifaddrs(ifap);
154355163Sshin}
154455163Sshin
154555163Sshinvoid
154662607Sitojunifconfig1(name, sa, ifcp, s)
154762607Sitojun	const char *name;
154862607Sitojun	const struct sockaddr *sa;
154955163Sshin	struct	ifc *ifcp;
155055163Sshin	int	s;
155155163Sshin{
155255163Sshin	struct	in6_ifreq ifr;
1553119031Sume	const struct sockaddr_in6 *sin6;
155455163Sshin	struct	ifac *ifa;
155555163Sshin	int	plen;
155655163Sshin	char	buf[BUFSIZ];
155755163Sshin
1558119031Sume	sin6 = (const struct sockaddr_in6 *)sa;
1559122677Sume	if (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr) && !lflag)
1560122677Sume		return;
1561119031Sume	ifr.ifr_addr = *sin6;
1562119032Sume	strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
156378064Sume	if (ioctl(s, SIOCGIFNETMASK_IN6, (char *)&ifr) < 0) {
156455163Sshin		fatal("ioctl: SIOCGIFNETMASK_IN6");
156578064Sume		/*NOTREACHED*/
156678064Sume	}
156778064Sume	plen = sin6mask2len(&ifr.ifr_addr);
1568119031Sume	if ((ifa = ifa_match(ifcp, &sin6->sin6_addr, plen)) != NULL) {
156955163Sshin		/* same interface found */
157055163Sshin		/* need check if something changed */
157155163Sshin		/* XXX not yet implemented */
157255163Sshin		return;
157355163Sshin	}
157455163Sshin	/*
157555163Sshin	 * New address is found
157655163Sshin	 */
157778064Sume	if ((ifa = MALLOC(struct ifac)) == NULL) {
157855163Sshin		fatal("malloc: struct ifac");
157978064Sume		/*NOTREACHED*/
158078064Sume	}
158162607Sitojun	memset(ifa, 0, sizeof(*ifa));
158255163Sshin	ifa->ifa_conf = ifcp;
158355163Sshin	ifa->ifa_next = ifcp->ifc_addr;
158455163Sshin	ifcp->ifc_addr = ifa;
1585119031Sume	ifa->ifa_addr = sin6->sin6_addr;
158655163Sshin	ifa->ifa_plen = plen;
1587243231Shrs	ifa->ifa_scope_id= sin6->sin6_scope_id;
158855163Sshin	if (ifcp->ifc_flags & IFF_POINTOPOINT) {
1589119031Sume		ifr.ifr_addr = *sin6;
159078064Sume		if (ioctl(s, SIOCGIFDSTADDR_IN6, (char *)&ifr) < 0) {
159155163Sshin			fatal("ioctl: SIOCGIFDSTADDR_IN6");
159278064Sume			/*NOTREACHED*/
159378064Sume		}
159455163Sshin		ifa->ifa_raddr = ifr.ifr_dstaddr.sin6_addr;
159555163Sshin		inet_ntop(AF_INET6, (void *)&ifa->ifa_raddr, buf, sizeof(buf));
159655163Sshin		trace(1, "found address %s/%d -- %s\n",
159755163Sshin			inet6_n2p(&ifa->ifa_addr), ifa->ifa_plen, buf);
159855163Sshin	} else {
159955163Sshin		trace(1, "found address %s/%d\n",
160055163Sshin			inet6_n2p(&ifa->ifa_addr), ifa->ifa_plen);
160155163Sshin	}
160255163Sshin	if (ifcp->ifc_index < 0 && IN6_IS_ADDR_LINKLOCAL(&ifa->ifa_addr)) {
160355163Sshin		ifcp->ifc_mylladdr = ifa->ifa_addr;
1604243231Shrs		ifcp->ifc_index = ifa->ifa_scope_id;
160555163Sshin		memcpy(&ifcp->ifc_ripsin, &ripsin, ripsin.ss_len);
1606243231Shrs		ifcp->ifc_ripsin.sin6_scope_id = ifcp->ifc_index;
160755163Sshin		setindex2ifc(ifcp->ifc_index, ifcp);
160855163Sshin		ifcp->ifc_mtu = getifmtu(ifcp->ifc_index);
160955163Sshin		if (ifcp->ifc_mtu > RIP6_MAXMTU)
161055163Sshin			ifcp->ifc_mtu = RIP6_MAXMTU;
161178064Sume		if (ioctl(s, SIOCGIFMETRIC, (char *)&ifr) < 0) {
161255163Sshin			fatal("ioctl: SIOCGIFMETRIC");
161378064Sume			/*NOTREACHED*/
161478064Sume		}
161555163Sshin		ifcp->ifc_metric = ifr.ifr_metric;
161655163Sshin		trace(1, "\tindex: %d, mtu: %d, metric: %d\n",
161755163Sshin			ifcp->ifc_index, ifcp->ifc_mtu, ifcp->ifc_metric);
161878064Sume	} else
161978064Sume		ifcp->ifc_cflags |= IFC_CHANGED;
162055163Sshin}
162155163Sshin
162255163Sshin/*
162355163Sshin * Receive and process routing messages.
162455163Sshin * Update interface information as necesssary.
162555163Sshin */
162655163Sshinvoid
162755163Sshinrtrecv()
162855163Sshin{
162955163Sshin	char buf[BUFSIZ];
163055163Sshin	char *p, *q;
163155163Sshin	struct rt_msghdr *rtm;
163255163Sshin	struct ifa_msghdr *ifam;
163355163Sshin	struct if_msghdr *ifm;
163455163Sshin	int len;
163578064Sume	struct ifc *ifcp, *ic;
163655163Sshin	int iface = 0, rtable = 0;
163755163Sshin	struct sockaddr_in6 *rta[RTAX_MAX];
163878064Sume	struct sockaddr_in6 mask;
163955163Sshin	int i, addrs;
164078064Sume	struct riprt *rrt;
164155163Sshin
164255163Sshin	if ((len = read(rtsock, buf, sizeof(buf))) < 0) {
164355163Sshin		perror("read from rtsock");
164478064Sume		exit(1);
164555163Sshin	}
164655163Sshin	if (len < sizeof(*rtm)) {
164769279Sume		trace(1, "short read from rtsock: %d (should be > %lu)\n",
164869279Sume			len, (u_long)sizeof(*rtm));
164955163Sshin		return;
165055163Sshin	}
1651119042Sume	if (dflag >= 2) {
1652119042Sume		fprintf(stderr, "rtmsg:\n");
1653119042Sume		for (i = 0; i < len; i++) {
1654119042Sume			fprintf(stderr, "%02x ", buf[i] & 0xff);
1655119042Sume			if (i % 16 == 15) fprintf(stderr, "\n");
1656119042Sume		}
1657119042Sume		fprintf(stderr, "\n");
1658119042Sume	}
165955163Sshin
166055163Sshin	for (p = buf; p - buf < len; p += ((struct rt_msghdr *)p)->rtm_msglen) {
166155163Sshin		/* safety against bogus message */
166255163Sshin		if (((struct rt_msghdr *)p)->rtm_msglen <= 0) {
166355163Sshin			trace(1, "bogus rtmsg: length=%d\n",
166455163Sshin				((struct rt_msghdr *)p)->rtm_msglen);
166555163Sshin			break;
166655163Sshin		}
166755163Sshin		rtm = NULL;
166855163Sshin		ifam = NULL;
166955163Sshin		ifm = NULL;
167055163Sshin		switch (((struct rt_msghdr *)p)->rtm_type) {
167155163Sshin		case RTM_NEWADDR:
167255163Sshin		case RTM_DELADDR:
167355163Sshin			ifam = (struct ifa_msghdr *)p;
167455163Sshin			addrs = ifam->ifam_addrs;
167555163Sshin			q = (char *)(ifam + 1);
167655163Sshin			break;
167755163Sshin		case RTM_IFINFO:
167855163Sshin			ifm = (struct if_msghdr *)p;
167955163Sshin			addrs = ifm->ifm_addrs;
168055163Sshin			q = (char *)(ifm + 1);
168155163Sshin			break;
168255163Sshin		default:
168355163Sshin			rtm = (struct rt_msghdr *)p;
168455163Sshin			addrs = rtm->rtm_addrs;
168555163Sshin			q = (char *)(rtm + 1);
168655163Sshin			if (rtm->rtm_version != RTM_VERSION) {
168755163Sshin				trace(1, "unexpected rtmsg version %d "
168855163Sshin					"(should be %d)\n",
168955163Sshin					rtm->rtm_version, RTM_VERSION);
169055163Sshin				continue;
169155163Sshin			}
169255163Sshin			if (rtm->rtm_pid == pid) {
169355163Sshin#if 0
169455163Sshin				trace(1, "rtmsg looped back to me, ignored\n");
169555163Sshin#endif
169655163Sshin				continue;
169755163Sshin			}
169855163Sshin			break;
169955163Sshin		}
170055163Sshin		memset(&rta, 0, sizeof(rta));
170155163Sshin		for (i = 0; i < RTAX_MAX; i++) {
170255163Sshin			if (addrs & (1 << i)) {
170355163Sshin				rta[i] = (struct sockaddr_in6 *)q;
170455163Sshin				q += ROUNDUP(rta[i]->sin6_len);
170555163Sshin			}
170655163Sshin		}
170755163Sshin
170855163Sshin		trace(1, "rtsock: %s (addrs=%x)\n",
170955163Sshin			rttypes((struct rt_msghdr *)p), addrs);
171055163Sshin		if (dflag >= 2) {
171155163Sshin			for (i = 0;
171255163Sshin			     i < ((struct rt_msghdr *)p)->rtm_msglen;
171355163Sshin			     i++) {
171455163Sshin				fprintf(stderr, "%02x ", p[i] & 0xff);
171555163Sshin				if (i % 16 == 15) fprintf(stderr, "\n");
171655163Sshin			}
171755163Sshin			fprintf(stderr, "\n");
171855163Sshin		}
171955163Sshin
172055163Sshin		/*
172155163Sshin		 * Easy ones first.
172255163Sshin		 *
172355163Sshin		 * We may be able to optimize by using ifm->ifm_index or
172455163Sshin		 * ifam->ifam_index.  For simplicity we don't do that here.
172555163Sshin		 */
172655163Sshin		switch (((struct rt_msghdr *)p)->rtm_type) {
172755163Sshin		case RTM_NEWADDR:
172855163Sshin		case RTM_IFINFO:
172955163Sshin			iface++;
173055163Sshin			continue;
173155163Sshin		case RTM_ADD:
173255163Sshin			rtable++;
173355163Sshin			continue;
173455163Sshin		case RTM_LOSING:
173555163Sshin		case RTM_MISS:
173655163Sshin		case RTM_GET:
173755163Sshin		case RTM_LOCK:
173855163Sshin			/* nothing to be done here */
173955163Sshin			trace(1, "\tnothing to be done, ignored\n");
174055163Sshin			continue;
174155163Sshin		}
174255163Sshin
174355163Sshin#if 0
174455163Sshin		if (rta[RTAX_DST] == NULL) {
174555163Sshin			trace(1, "\tno destination, ignored\n");
174662607Sitojun			continue;
174755163Sshin		}
174855163Sshin		if (rta[RTAX_DST]->sin6_family != AF_INET6) {
174955163Sshin			trace(1, "\taf mismatch, ignored\n");
175055163Sshin			continue;
175155163Sshin		}
175255163Sshin		if (IN6_IS_ADDR_LINKLOCAL(&rta[RTAX_DST]->sin6_addr)) {
175355163Sshin			trace(1, "\tlinklocal destination, ignored\n");
175455163Sshin			continue;
175555163Sshin		}
175655163Sshin		if (IN6_ARE_ADDR_EQUAL(&rta[RTAX_DST]->sin6_addr, &in6addr_loopback)) {
175755163Sshin			trace(1, "\tloopback destination, ignored\n");
175855163Sshin			continue;		/* Loopback */
175955163Sshin		}
176055163Sshin		if (IN6_IS_ADDR_MULTICAST(&rta[RTAX_DST]->sin6_addr)) {
176155163Sshin			trace(1, "\tmulticast destination, ignored\n");
176255163Sshin			continue;
176355163Sshin		}
176455163Sshin#endif
176555163Sshin
176655163Sshin		/* hard ones */
176755163Sshin		switch (((struct rt_msghdr *)p)->rtm_type) {
176855163Sshin		case RTM_NEWADDR:
176955163Sshin		case RTM_IFINFO:
177055163Sshin		case RTM_ADD:
177155163Sshin		case RTM_LOSING:
177255163Sshin		case RTM_MISS:
177355163Sshin		case RTM_GET:
177455163Sshin		case RTM_LOCK:
177555163Sshin			/* should already be handled */
177655163Sshin			fatal("rtrecv: never reach here");
177778064Sume			/*NOTREACHED*/
177855163Sshin		case RTM_DELETE:
177978064Sume			if (!rta[RTAX_DST] || !rta[RTAX_GATEWAY]) {
178078064Sume				trace(1, "\tsome of dst/gw/netamsk are "
178178064Sume				    "unavailable, ignored\n");
178255163Sshin				break;
178355163Sshin			}
178478064Sume			if ((rtm->rtm_flags & RTF_HOST) != 0) {
178578064Sume				mask.sin6_len = sizeof(mask);
178678064Sume				memset(&mask.sin6_addr, 0xff,
178778064Sume				    sizeof(mask.sin6_addr));
178878064Sume				rta[RTAX_NETMASK] = &mask;
178978064Sume			} else if (!rta[RTAX_NETMASK]) {
179078064Sume				trace(1, "\tsome of dst/gw/netamsk are "
179178064Sume				    "unavailable, ignored\n");
179278064Sume				break;
179378064Sume			}
179478064Sume			if (rt_del(rta[RTAX_DST], rta[RTAX_GATEWAY],
179578064Sume			    rta[RTAX_NETMASK]) == 0) {
179655163Sshin				rtable++;	/*just to be sure*/
179755163Sshin			}
179855163Sshin			break;
179955163Sshin		case RTM_CHANGE:
180055163Sshin		case RTM_REDIRECT:
180155163Sshin			trace(1, "\tnot supported yet, ignored\n");
180255163Sshin			break;
180355163Sshin		case RTM_DELADDR:
180455163Sshin			if (!rta[RTAX_NETMASK] || !rta[RTAX_IFA]) {
180555163Sshin				trace(1, "\tno netmask or ifa given, ignored\n");
180655163Sshin				break;
180755163Sshin			}
180855163Sshin			if (ifam->ifam_index < nindex2ifc)
180955163Sshin				ifcp = index2ifc[ifam->ifam_index];
181055163Sshin			else
181155163Sshin				ifcp = NULL;
181255163Sshin			if (!ifcp) {
181355163Sshin				trace(1, "\tinvalid ifam_index %d, ignored\n",
181455163Sshin					ifam->ifam_index);
181555163Sshin				break;
181655163Sshin			}
181778064Sume			if (!rt_deladdr(ifcp, rta[RTAX_IFA], rta[RTAX_NETMASK]))
181878064Sume				iface++;
181955163Sshin			break;
182055163Sshin		case RTM_OLDADD:
182155163Sshin		case RTM_OLDDEL:
182255163Sshin			trace(1, "\tnot supported yet, ignored\n");
182355163Sshin			break;
182455163Sshin		}
182555163Sshin
182655163Sshin	}
182755163Sshin
182855163Sshin	if (iface) {
182955163Sshin		trace(1, "rtsock: reconfigure interfaces, refresh interface routes\n");
183055163Sshin		ifconfig();
183155163Sshin		for (ifcp = ifc; ifcp; ifcp = ifcp->ifc_next)
183278064Sume			if (ifcp->ifc_cflags & IFC_CHANGED) {
183378064Sume				if (ifrt(ifcp, 1)) {
183478064Sume					for (ic = ifc; ic; ic = ic->ifc_next) {
183578064Sume						if (ifcp->ifc_index == ic->ifc_index)
183678064Sume							continue;
183778064Sume						if (ic->ifc_flags & IFF_UP)
183878064Sume							ripsend(ic, &ic->ifc_ripsin,
183978064Sume							RRTF_CHANGED);
184078064Sume					}
184178064Sume					/* Reset the flag */
184278064Sume					for (rrt = riprt; rrt; rrt = rrt->rrt_next)
184378064Sume						rrt->rrt_rflags &= ~RRTF_CHANGED;
184478064Sume				}
184578064Sume				ifcp->ifc_cflags &= ~IFC_CHANGED;
184678064Sume			}
184755163Sshin	}
184855163Sshin	if (rtable) {
184955163Sshin		trace(1, "rtsock: read routing table again\n");
185055163Sshin		krtread(1);
185155163Sshin	}
185255163Sshin}
185355163Sshin
185455163Sshin/*
185555163Sshin * remove specified route from the internal routing table.
185655163Sshin */
185755163Sshinint
185855163Sshinrt_del(sdst, sgw, smask)
185955163Sshin	const struct sockaddr_in6 *sdst;
186055163Sshin	const struct sockaddr_in6 *sgw;
186155163Sshin	const struct sockaddr_in6 *smask;
186255163Sshin{
186355163Sshin	const struct in6_addr *dst = NULL;
186455163Sshin	const struct in6_addr *gw = NULL;
186555163Sshin	int prefix;
186655163Sshin	struct netinfo6 ni6;
186755163Sshin	struct riprt *rrt = NULL;
186855163Sshin	time_t t_lifetime;
186955163Sshin
187055163Sshin	if (sdst->sin6_family != AF_INET6) {
187155163Sshin		trace(1, "\tother AF, ignored\n");
187255163Sshin		return -1;
187355163Sshin	}
187455163Sshin	if (IN6_IS_ADDR_LINKLOCAL(&sdst->sin6_addr)
187555163Sshin	 || IN6_ARE_ADDR_EQUAL(&sdst->sin6_addr, &in6addr_loopback)
187655163Sshin	 || IN6_IS_ADDR_MULTICAST(&sdst->sin6_addr)) {
187755163Sshin		trace(1, "\taddress %s not interesting, ignored\n",
187855163Sshin			inet6_n2p(&sdst->sin6_addr));
187955163Sshin		return -1;
188055163Sshin	}
188155163Sshin	dst = &sdst->sin6_addr;
188278064Sume	if (sgw->sin6_family == AF_INET6) {
188355163Sshin		/* easy case */
188455163Sshin		gw = &sgw->sin6_addr;
188578064Sume		prefix = sin6mask2len(smask);
188655163Sshin	} else if (sgw->sin6_family == AF_LINK) {
188755163Sshin		/*
188855163Sshin		 * Interface route... a hard case.  We need to get the prefix
188955163Sshin		 * length from the kernel, but we now are parsing rtmsg.
189055163Sshin		 * We'll purge matching routes from my list, then get the
189155163Sshin		 * fresh list.
189255163Sshin		 */
189355163Sshin		struct riprt *longest;
1894108533Sschweikh		trace(1, "\t%s is an interface route, guessing prefixlen\n",
189555163Sshin			inet6_n2p(dst));
189655163Sshin		longest = NULL;
189755163Sshin		for (rrt = riprt; rrt; rrt = rrt->rrt_next) {
189855163Sshin			if (IN6_ARE_ADDR_EQUAL(&rrt->rrt_info.rip6_dest,
189955163Sshin					&sdst->sin6_addr)
190055163Sshin			 && IN6_IS_ADDR_LOOPBACK(&rrt->rrt_gw)) {
190155163Sshin				if (!longest
190255163Sshin				 || longest->rrt_info.rip6_plen <
190355163Sshin						 rrt->rrt_info.rip6_plen) {
190455163Sshin					longest = rrt;
190555163Sshin				}
190655163Sshin			}
190755163Sshin		}
190855163Sshin		rrt = longest;
190955163Sshin		if (!rrt) {
191055163Sshin			trace(1, "\tno matching interface route found\n");
191155163Sshin			return -1;
191255163Sshin		}
191355163Sshin		gw = &in6addr_loopback;
191455163Sshin		prefix = rrt->rrt_info.rip6_plen;
191555163Sshin	} else {
191678064Sume		trace(1, "\tunsupported af: (gw=%d)\n", sgw->sin6_family);
191755163Sshin		return -1;
191855163Sshin	}
191955163Sshin
192055163Sshin	trace(1, "\tdeleting %s/%d ", inet6_n2p(dst), prefix);
192155163Sshin	trace(1, "gw %s\n", inet6_n2p(gw));
192255163Sshin	t_lifetime = time(NULL) - RIP_LIFETIME;
192355163Sshin	/* age route for interface address */
192455163Sshin	memset(&ni6, 0, sizeof(ni6));
192555163Sshin	ni6.rip6_dest = *dst;
192655163Sshin	ni6.rip6_plen = prefix;
192755163Sshin	applyplen(&ni6.rip6_dest, ni6.rip6_plen);	/*to be sure*/
192855163Sshin	trace(1, "\tfind route %s/%d\n", inet6_n2p(&ni6.rip6_dest),
192955163Sshin		ni6.rip6_plen);
193078064Sume	if (!rrt && (rrt = rtsearch(&ni6, NULL)) == NULL) {
193155163Sshin		trace(1, "\tno route found\n");
193255163Sshin		return -1;
193355163Sshin	}
193478064Sume#if 0
193555163Sshin	if ((rrt->rrt_flags & RTF_STATIC) == 0) {
193655163Sshin		trace(1, "\tyou can delete static routes only\n");
193778064Sume	} else
193878064Sume#endif
193978064Sume	if (!IN6_ARE_ADDR_EQUAL(&rrt->rrt_gw, gw)) {
194055163Sshin		trace(1, "\tgw mismatch: %s <-> ",
194155163Sshin			inet6_n2p(&rrt->rrt_gw));
194255163Sshin		trace(1, "%s\n", inet6_n2p(gw));
194355163Sshin	} else {
194455163Sshin		trace(1, "\troute found, age it\n");
194555163Sshin		if (rrt->rrt_t == 0 || rrt->rrt_t > t_lifetime) {
194655163Sshin			rrt->rrt_t = t_lifetime;
194755163Sshin			rrt->rrt_info.rip6_metric = HOPCNT_INFINITY6;
194855163Sshin		}
194955163Sshin	}
195055163Sshin	return 0;
195155163Sshin}
195255163Sshin
195355163Sshin/*
195455163Sshin * remove specified address from internal interface/routing table.
195555163Sshin */
195655163Sshinint
195755163Sshinrt_deladdr(ifcp, sifa, smask)
195855163Sshin	struct ifc *ifcp;
195955163Sshin	const struct sockaddr_in6 *sifa;
196055163Sshin	const struct sockaddr_in6 *smask;
196155163Sshin{
196255163Sshin	const struct in6_addr *addr = NULL;
196355163Sshin	int prefix;
196455163Sshin	struct ifac *ifa = NULL;
196555163Sshin	struct netinfo6 ni6;
196655163Sshin	struct riprt *rrt = NULL;
196755163Sshin	time_t t_lifetime;
196855163Sshin	int updated = 0;
196955163Sshin
197078064Sume	if (sifa->sin6_family != AF_INET6) {
197155163Sshin		trace(1, "\tother AF, ignored\n");
197255163Sshin		return -1;
197355163Sshin	}
197455163Sshin	addr = &sifa->sin6_addr;
197578064Sume	prefix = sin6mask2len(smask);
197655163Sshin
197755163Sshin	trace(1, "\tdeleting %s/%d from %s\n",
197855163Sshin		inet6_n2p(addr), prefix, ifcp->ifc_name);
197955163Sshin	ifa = ifa_match(ifcp, addr, prefix);
198055163Sshin	if (!ifa) {
198155163Sshin		trace(1, "\tno matching ifa found for %s/%d on %s\n",
198255163Sshin			inet6_n2p(addr), prefix, ifcp->ifc_name);
198355163Sshin		return -1;
198455163Sshin	}
198555163Sshin	if (ifa->ifa_conf != ifcp) {
198655163Sshin		trace(1, "\taddress table corrupt: back pointer does not match "
198755163Sshin			"(%s != %s)\n",
198855163Sshin			ifcp->ifc_name, ifa->ifa_conf->ifc_name);
198955163Sshin		return -1;
199055163Sshin	}
199155163Sshin	/* remove ifa from interface */
199255163Sshin	if (ifcp->ifc_addr == ifa)
199355163Sshin		ifcp->ifc_addr = ifa->ifa_next;
199455163Sshin	else {
199555163Sshin		struct ifac *p;
199655163Sshin		for (p = ifcp->ifc_addr; p; p = p->ifa_next) {
199755163Sshin			if (p->ifa_next == ifa) {
199855163Sshin				p->ifa_next = ifa->ifa_next;
199955163Sshin				break;
200055163Sshin			}
200155163Sshin		}
200255163Sshin	}
200355163Sshin	ifa->ifa_next = NULL;
200455163Sshin	ifa->ifa_conf = NULL;
200555163Sshin	t_lifetime = time(NULL) - RIP_LIFETIME;
200655163Sshin	/* age route for interface address */
200755163Sshin	memset(&ni6, 0, sizeof(ni6));
200855163Sshin	ni6.rip6_dest = ifa->ifa_addr;
200955163Sshin	ni6.rip6_plen = ifa->ifa_plen;
201055163Sshin	applyplen(&ni6.rip6_dest, ni6.rip6_plen);
201155163Sshin	trace(1, "\tfind interface route %s/%d on %d\n",
201255163Sshin		inet6_n2p(&ni6.rip6_dest), ni6.rip6_plen, ifcp->ifc_index);
201378064Sume	if ((rrt = rtsearch(&ni6, NULL)) != NULL) {
201455163Sshin		struct in6_addr none;
201555163Sshin		memset(&none, 0, sizeof(none));
201678064Sume		if (rrt->rrt_index == ifcp->ifc_index &&
201778064Sume		    (IN6_ARE_ADDR_EQUAL(&rrt->rrt_gw, &none) ||
201878064Sume		     IN6_IS_ADDR_LOOPBACK(&rrt->rrt_gw))) {
201955163Sshin			trace(1, "\troute found, age it\n");
202055163Sshin			if (rrt->rrt_t == 0 || rrt->rrt_t > t_lifetime) {
202155163Sshin				rrt->rrt_t = t_lifetime;
202255163Sshin				rrt->rrt_info.rip6_metric = HOPCNT_INFINITY6;
202355163Sshin			}
202455163Sshin			updated++;
202555163Sshin		} else {
202655163Sshin			trace(1, "\tnon-interface route found: %s/%d on %d\n",
202755163Sshin				inet6_n2p(&rrt->rrt_info.rip6_dest),
202855163Sshin				rrt->rrt_info.rip6_plen,
202955163Sshin				rrt->rrt_index);
203055163Sshin		}
203155163Sshin	} else
203255163Sshin		trace(1, "\tno interface route found\n");
203355163Sshin	/* age route for p2p destination */
203455163Sshin	if (ifcp->ifc_flags & IFF_POINTOPOINT) {
203555163Sshin		memset(&ni6, 0, sizeof(ni6));
203655163Sshin		ni6.rip6_dest = ifa->ifa_raddr;
203755163Sshin		ni6.rip6_plen = 128;
203855163Sshin		applyplen(&ni6.rip6_dest, ni6.rip6_plen);	/*to be sure*/
203955163Sshin		trace(1, "\tfind p2p route %s/%d on %d\n",
204055163Sshin			inet6_n2p(&ni6.rip6_dest), ni6.rip6_plen,
204155163Sshin			ifcp->ifc_index);
204278064Sume		if ((rrt = rtsearch(&ni6, NULL)) != NULL) {
204378064Sume			if (rrt->rrt_index == ifcp->ifc_index &&
204478064Sume			    IN6_ARE_ADDR_EQUAL(&rrt->rrt_gw, &ifa->ifa_addr)) {
204555163Sshin				trace(1, "\troute found, age it\n");
204655163Sshin				if (rrt->rrt_t == 0 || rrt->rrt_t > t_lifetime) {
204755163Sshin					rrt->rrt_t = t_lifetime;
204855163Sshin					rrt->rrt_info.rip6_metric =
204978064Sume					    HOPCNT_INFINITY6;
205055163Sshin					updated++;
205155163Sshin				}
205255163Sshin			} else {
205355163Sshin				trace(1, "\tnon-p2p route found: %s/%d on %d\n",
205455163Sshin					inet6_n2p(&rrt->rrt_info.rip6_dest),
205555163Sshin					rrt->rrt_info.rip6_plen,
205655163Sshin					rrt->rrt_index);
205755163Sshin			}
205855163Sshin		} else
205955163Sshin			trace(1, "\tno p2p route found\n");
206055163Sshin	}
206155163Sshin	return updated ? 0 : -1;
206255163Sshin}
206355163Sshin
206455163Sshin/*
206555163Sshin * Get each interface address and put those interface routes to the route
206655163Sshin * list.
206755163Sshin */
206878064Sumeint
206955163Sshinifrt(ifcp, again)
207062607Sitojun	struct ifc *ifcp;
207155163Sshin	int again;
207255163Sshin{
207362607Sitojun	struct ifac *ifa;
2074119042Sume	struct riprt *rrt = NULL, *search_rrt, *prev_rrt, *loop_rrt;
207562607Sitojun	struct netinfo6 *np;
207678064Sume	time_t t_lifetime;
207778064Sume	int need_trigger = 0;
207855163Sshin
2079122677Sume#if 0
208055163Sshin	if (ifcp->ifc_flags & IFF_LOOPBACK)
208178064Sume		return 0;			/* ignore loopback */
2082122677Sume#endif
2083122677Sume
208462607Sitojun	if (ifcp->ifc_flags & IFF_POINTOPOINT) {
208562607Sitojun		ifrt_p2p(ifcp, again);
208678064Sume		return 0;
208762607Sitojun	}
208862607Sitojun
208955163Sshin	for (ifa = ifcp->ifc_addr; ifa; ifa = ifa->ifa_next) {
209062607Sitojun		if (IN6_IS_ADDR_LINKLOCAL(&ifa->ifa_addr)) {
209162607Sitojun#if 0
209262607Sitojun			trace(1, "route: %s on %s: "
209362607Sitojun			    "skip linklocal interface address\n",
209462607Sitojun			    inet6_n2p(&ifa->ifa_addr), ifcp->ifc_name);
209562607Sitojun#endif
209662607Sitojun			continue;
209762607Sitojun		}
209862607Sitojun		if (IN6_IS_ADDR_UNSPECIFIED(&ifa->ifa_addr)) {
209962607Sitojun#if 0
210062607Sitojun			trace(1, "route: %s: skip unspec interface address\n",
210162607Sitojun			    ifcp->ifc_name);
210262607Sitojun#endif
210362607Sitojun			continue;
210462607Sitojun		}
2105122677Sume		if (IN6_IS_ADDR_LOOPBACK(&ifa->ifa_addr)) {
2106122677Sume#if 0
2107122677Sume			trace(1, "route: %s: skip loopback address\n",
2108122677Sume			    ifcp->ifc_name);
2109122677Sume#endif
2110122677Sume			continue;
2111122677Sume		}
211278064Sume		if (ifcp->ifc_flags & IFF_UP) {
211378064Sume			if ((rrt = MALLOC(struct riprt)) == NULL)
211478064Sume				fatal("malloc: struct riprt");
211578064Sume			memset(rrt, 0, sizeof(*rrt));
211678064Sume			rrt->rrt_same = NULL;
211778064Sume			rrt->rrt_index = ifcp->ifc_index;
211878064Sume			rrt->rrt_t = 0;	/* don't age */
211978064Sume			rrt->rrt_info.rip6_dest = ifa->ifa_addr;
212078064Sume			rrt->rrt_info.rip6_tag = htons(routetag & 0xffff);
212178064Sume			rrt->rrt_info.rip6_metric = 1 + ifcp->ifc_metric;
212278064Sume			rrt->rrt_info.rip6_plen = ifa->ifa_plen;
2123186119Sqingli			rrt->rrt_flags = RTF_HOST;
212478064Sume			rrt->rrt_rflags |= RRTF_CHANGED;
212578064Sume			applyplen(&rrt->rrt_info.rip6_dest, ifa->ifa_plen);
212678064Sume			memset(&rrt->rrt_gw, 0, sizeof(struct in6_addr));
212778064Sume			rrt->rrt_gw = ifa->ifa_addr;
212878064Sume			np = &rrt->rrt_info;
212978064Sume			search_rrt = rtsearch(np, &prev_rrt);
213078064Sume			if (search_rrt != NULL) {
2131119042Sume				if (search_rrt->rrt_info.rip6_metric <=
213278064Sume				    rrt->rrt_info.rip6_metric) {
213378064Sume					/* Already have better route */
213478064Sume					if (!again) {
213578064Sume						trace(1, "route: %s/%d: "
213678064Sume						    "already registered (%s)\n",
213778064Sume						    inet6_n2p(&np->rip6_dest), np->rip6_plen,
213878064Sume						    ifcp->ifc_name);
213978064Sume					}
2140119042Sume					goto next;
214178064Sume				}
2142119042Sume
2143119042Sume				if (prev_rrt)
2144119042Sume					prev_rrt->rrt_next = rrt->rrt_next;
2145119042Sume				else
2146119042Sume					riprt = rrt->rrt_next;
2147119042Sume				delroute(&rrt->rrt_info, &rrt->rrt_gw);
214878064Sume			}
214955163Sshin			/* Attach the route to the list */
215062607Sitojun			trace(1, "route: %s/%d: register route (%s)\n",
215162607Sitojun			    inet6_n2p(&np->rip6_dest), np->rip6_plen,
215262607Sitojun			    ifcp->ifc_name);
215355163Sshin			rrt->rrt_next = riprt;
215455163Sshin			riprt = rrt;
215578064Sume			addroute(rrt, &rrt->rrt_gw, ifcp);
2156119042Sume			rrt = NULL;
215778064Sume			sendrequest(ifcp);
215878064Sume			ripsend(ifcp, &ifcp->ifc_ripsin, 0);
215978064Sume			need_trigger = 1;
216055163Sshin		} else {
216178064Sume			for (loop_rrt = riprt; loop_rrt; loop_rrt = loop_rrt->rrt_next) {
216278064Sume				if (loop_rrt->rrt_index == ifcp->ifc_index) {
216378064Sume					t_lifetime = time(NULL) - RIP_LIFETIME;
216478064Sume					if (loop_rrt->rrt_t == 0 || loop_rrt->rrt_t > t_lifetime) {
216578064Sume						loop_rrt->rrt_t = t_lifetime;
216678064Sume						loop_rrt->rrt_info.rip6_metric = HOPCNT_INFINITY6;
216778064Sume						loop_rrt->rrt_rflags |= RRTF_CHANGED;
216878064Sume						need_trigger = 1;
216978064Sume					}
217078064Sume				}
217155163Sshin			}
217278064Sume                }
2173119042Sume	next:
2174119042Sume		if (rrt)
2175119042Sume			free(rrt);
217662607Sitojun	}
217778064Sume	return need_trigger;
217862607Sitojun}
217955163Sshin
218062607Sitojun/*
218162607Sitojun * there are couple of p2p interface routing models.  "behavior" lets
218262607Sitojun * you pick one.  it looks that gated behavior fits best with BSDs,
2183119035Sume * since BSD kernels do not look at prefix length on p2p interfaces.
218462607Sitojun */
218562607Sitojunvoid
218662607Sitojunifrt_p2p(ifcp, again)
218762607Sitojun	struct ifc *ifcp;
218862607Sitojun	int again;
218962607Sitojun{
219062607Sitojun	struct ifac *ifa;
219178064Sume	struct riprt *rrt, *orrt, *prevrrt;
219262607Sitojun	struct netinfo6 *np;
219362607Sitojun	struct in6_addr addr, dest;
219462607Sitojun	int advert, ignore, i;
219562607Sitojun#define P2PADVERT_NETWORK	1
219662607Sitojun#define P2PADVERT_ADDR		2
219762607Sitojun#define P2PADVERT_DEST		4
219862607Sitojun#define P2PADVERT_MAX		4
219962607Sitojun	const enum { CISCO, GATED, ROUTE6D } behavior = GATED;
220078064Sume	const char *category = "";
220162607Sitojun	const char *noadv;
220262607Sitojun
220362607Sitojun	for (ifa = ifcp->ifc_addr; ifa; ifa = ifa->ifa_next) {
220462607Sitojun		addr = ifa->ifa_addr;
220562607Sitojun		dest = ifa->ifa_raddr;
220662607Sitojun		applyplen(&addr, ifa->ifa_plen);
220762607Sitojun		applyplen(&dest, ifa->ifa_plen);
220862607Sitojun		advert = ignore = 0;
220962607Sitojun		switch (behavior) {
221062607Sitojun		case CISCO:
221162607Sitojun			/*
221262607Sitojun			 * honor addr/plen, just like normal shared medium
221362607Sitojun			 * interface.  this may cause trouble if you reuse
221462607Sitojun			 * addr/plen on other interfaces.
221562607Sitojun			 *
221662607Sitojun			 * advertise addr/plen.
221762607Sitojun			 */
221862607Sitojun			advert |= P2PADVERT_NETWORK;
221962607Sitojun			break;
222062607Sitojun		case GATED:
222162607Sitojun			/*
222262607Sitojun			 * prefixlen on p2p interface is meaningless.
222362607Sitojun			 * advertise addr/128 and dest/128.
222462607Sitojun			 *
222562607Sitojun			 * do not install network route to route6d routing
222662607Sitojun			 * table (if we do, it would prevent route installation
222762607Sitojun			 * for other p2p interface that shares addr/plen).
222878064Sume			 *
222978064Sume			 * XXX what should we do if dest is ::?  it will not
223078064Sume			 * get announced anyways (see following filter),
223178064Sume			 * but we need to think.
223262607Sitojun			 */
223362607Sitojun			advert |= P2PADVERT_ADDR;
223462607Sitojun			advert |= P2PADVERT_DEST;
223562607Sitojun			ignore |= P2PADVERT_NETWORK;
223662607Sitojun			break;
223762607Sitojun		case ROUTE6D:
223862607Sitojun			/*
223978064Sume			 * just for testing.  actually the code is redundant
224078064Sume			 * given the current p2p interface address assignment
224178064Sume			 * rule for kame kernel.
224278064Sume			 *
224378064Sume			 * intent:
224478064Sume			 *	A/n -> announce A/n
224578064Sume			 *	A B/n, A and B share prefix -> A/n (= B/n)
224678064Sume			 *	A B/n, do not share prefix -> A/128 and B/128
224778064Sume			 * actually, A/64 and A B/128 are the only cases
224878064Sume			 * permitted by the kernel:
224978064Sume			 *	A/64 -> A/64
225078064Sume			 *	A B/128 -> A/128 and B/128
225162607Sitojun			 */
225278064Sume			if (!IN6_IS_ADDR_UNSPECIFIED(&ifa->ifa_raddr)) {
225378064Sume				if (IN6_ARE_ADDR_EQUAL(&addr, &dest))
225478064Sume					advert |= P2PADVERT_NETWORK;
225578064Sume				else {
225678064Sume					advert |= P2PADVERT_ADDR;
225778064Sume					advert |= P2PADVERT_DEST;
225878064Sume					ignore |= P2PADVERT_NETWORK;
225978064Sume				}
226078064Sume			} else
226162607Sitojun				advert |= P2PADVERT_NETWORK;
226262607Sitojun			break;
226362607Sitojun		}
226462607Sitojun
226562607Sitojun		for (i = 1; i <= P2PADVERT_MAX; i *= 2) {
226662607Sitojun			if ((ignore & i) != 0)
226762607Sitojun				continue;
226878064Sume			if ((rrt = MALLOC(struct riprt)) == NULL) {
226955163Sshin				fatal("malloc: struct riprt");
227078064Sume				/*NOTREACHED*/
227178064Sume			}
227262607Sitojun			memset(rrt, 0, sizeof(*rrt));
227355163Sshin			rrt->rrt_same = NULL;
227455163Sshin			rrt->rrt_index = ifcp->ifc_index;
227562607Sitojun			rrt->rrt_t = 0;	/* don't age */
227662607Sitojun			switch (i) {
227762607Sitojun			case P2PADVERT_NETWORK:
227862607Sitojun				rrt->rrt_info.rip6_dest = ifa->ifa_addr;
227962607Sitojun				rrt->rrt_info.rip6_plen = ifa->ifa_plen;
228062607Sitojun				applyplen(&rrt->rrt_info.rip6_dest,
228162607Sitojun				    ifa->ifa_plen);
228262607Sitojun				category = "network";
228362607Sitojun				break;
228462607Sitojun			case P2PADVERT_ADDR:
228562607Sitojun				rrt->rrt_info.rip6_dest = ifa->ifa_addr;
228662607Sitojun				rrt->rrt_info.rip6_plen = 128;
228778064Sume				rrt->rrt_gw = in6addr_loopback;
228862607Sitojun				category = "addr";
228962607Sitojun				break;
229062607Sitojun			case P2PADVERT_DEST:
229162607Sitojun				rrt->rrt_info.rip6_dest = ifa->ifa_raddr;
229262607Sitojun				rrt->rrt_info.rip6_plen = 128;
229378064Sume				rrt->rrt_gw = ifa->ifa_addr;
229462607Sitojun				category = "dest";
229562607Sitojun				break;
229662607Sitojun			}
229762607Sitojun			if (IN6_IS_ADDR_UNSPECIFIED(&rrt->rrt_info.rip6_dest) ||
229862607Sitojun			    IN6_IS_ADDR_LINKLOCAL(&rrt->rrt_info.rip6_dest)) {
229962607Sitojun#if 0
230062607Sitojun				trace(1, "route: %s: skip unspec/linklocal "
230162607Sitojun				    "(%s on %s)\n", category, ifcp->ifc_name);
230262607Sitojun#endif
230362607Sitojun				free(rrt);
230462607Sitojun				continue;
230562607Sitojun			}
230662607Sitojun			if ((advert & i) == 0) {
230762607Sitojun				rrt->rrt_rflags |= RRTF_NOADVERTISE;
230862607Sitojun				noadv = ", NO-ADV";
230962607Sitojun			} else
231062607Sitojun				noadv = "";
231155163Sshin			rrt->rrt_info.rip6_tag = htons(routetag & 0xffff);
231262607Sitojun			rrt->rrt_info.rip6_metric = 1 + ifcp->ifc_metric;
231355163Sshin			np = &rrt->rrt_info;
231478064Sume			orrt = rtsearch(np, &prevrrt);
231578064Sume			if (!orrt) {
231655163Sshin				/* Attach the route to the list */
231762607Sitojun				trace(1, "route: %s/%d: register route "
231862607Sitojun				    "(%s on %s%s)\n",
231962607Sitojun				    inet6_n2p(&np->rip6_dest), np->rip6_plen,
232062607Sitojun				    category, ifcp->ifc_name, noadv);
232155163Sshin				rrt->rrt_next = riprt;
232255163Sshin				riprt = rrt;
232378064Sume			} else if (rrt->rrt_index != orrt->rrt_index ||
232478064Sume			    rrt->rrt_info.rip6_metric != orrt->rrt_info.rip6_metric) {
232578064Sume				/* swap route */
232678064Sume				rrt->rrt_next = orrt->rrt_next;
232778064Sume				if (prevrrt)
232878064Sume					prevrrt->rrt_next = rrt;
232978064Sume				else
233078064Sume					riprt = rrt;
233178064Sume				free(orrt);
233278064Sume
233378064Sume				trace(1, "route: %s/%d: update (%s on %s%s)\n",
233478064Sume				    inet6_n2p(&np->rip6_dest), np->rip6_plen,
233578064Sume				    category, ifcp->ifc_name, noadv);
233655163Sshin			} else {
233755163Sshin				/* Already found */
233855163Sshin				if (!again) {
233962607Sitojun					trace(1, "route: %s/%d: "
234062607Sitojun					    "already registered (%s on %s%s)\n",
234162607Sitojun					    inet6_n2p(&np->rip6_dest),
234262607Sitojun					    np->rip6_plen, category,
234362607Sitojun					    ifcp->ifc_name, noadv);
234455163Sshin				}
234555163Sshin				free(rrt);
234655163Sshin			}
234755163Sshin		}
234855163Sshin	}
234962607Sitojun#undef P2PADVERT_NETWORK
235062607Sitojun#undef P2PADVERT_ADDR
235162607Sitojun#undef P2PADVERT_DEST
235262607Sitojun#undef P2PADVERT_MAX
235355163Sshin}
235455163Sshin
235555163Sshinint
235655163Sshingetifmtu(ifindex)
235755163Sshin	int	ifindex;
235855163Sshin{
235955163Sshin	int	mib[6];
236055163Sshin	char	*buf;
236155163Sshin	size_t	msize;
236255163Sshin	struct	if_msghdr *ifm;
236355163Sshin	int	mtu;
236455163Sshin
236555163Sshin	mib[0] = CTL_NET;
236655163Sshin	mib[1] = PF_ROUTE;
236755163Sshin	mib[2] = 0;
236855163Sshin	mib[3] = AF_INET6;
236955163Sshin	mib[4] = NET_RT_IFLIST;
237055163Sshin	mib[5] = ifindex;
237178064Sume	if (sysctl(mib, 6, NULL, &msize, NULL, 0) < 0) {
237255163Sshin		fatal("sysctl estimate NET_RT_IFLIST");
237378064Sume		/*NOTREACHED*/
237478064Sume	}
237578064Sume	if ((buf = malloc(msize)) == NULL) {
237655163Sshin		fatal("malloc");
237778064Sume		/*NOTREACHED*/
237878064Sume	}
237978064Sume	if (sysctl(mib, 6, buf, &msize, NULL, 0) < 0) {
238055163Sshin		fatal("sysctl NET_RT_IFLIST");
238178064Sume		/*NOTREACHED*/
238278064Sume	}
238355163Sshin	ifm = (struct if_msghdr *)buf;
238455163Sshin	mtu = ifm->ifm_data.ifi_mtu;
238578064Sume	if (ifindex != ifm->ifm_index) {
238655163Sshin		fatal("ifindex does not match with ifm_index");
238778064Sume		/*NOTREACHED*/
238878064Sume	}
238955163Sshin	free(buf);
239055163Sshin	return mtu;
239155163Sshin}
239255163Sshin
239355163Sshinconst char *
239455163Sshinrttypes(rtm)
239555163Sshin	struct rt_msghdr *rtm;
239655163Sshin{
239762607Sitojun#define	RTTYPE(s, f) \
239862607Sitojundo { \
239962607Sitojun	if (rtm->rtm_type == (f)) \
240062607Sitojun		return (s); \
240162607Sitojun} while (0)
240255163Sshin	RTTYPE("ADD", RTM_ADD);
240355163Sshin	RTTYPE("DELETE", RTM_DELETE);
240455163Sshin	RTTYPE("CHANGE", RTM_CHANGE);
240555163Sshin	RTTYPE("GET", RTM_GET);
240655163Sshin	RTTYPE("LOSING", RTM_LOSING);
240755163Sshin	RTTYPE("REDIRECT", RTM_REDIRECT);
240855163Sshin	RTTYPE("MISS", RTM_MISS);
240955163Sshin	RTTYPE("LOCK", RTM_LOCK);
241055163Sshin	RTTYPE("OLDADD", RTM_OLDADD);
241155163Sshin	RTTYPE("OLDDEL", RTM_OLDDEL);
241255163Sshin	RTTYPE("NEWADDR", RTM_NEWADDR);
241355163Sshin	RTTYPE("DELADDR", RTM_DELADDR);
241455163Sshin	RTTYPE("IFINFO", RTM_IFINFO);
241578064Sume#ifdef RTM_OLDADD
241678064Sume	RTTYPE("OLDADD", RTM_OLDADD);
241778064Sume#endif
241878064Sume#ifdef RTM_OLDDEL
241978064Sume	RTTYPE("OLDDEL", RTM_OLDDEL);
242078064Sume#endif
242178064Sume#ifdef RTM_OIFINFO
242278064Sume	RTTYPE("OIFINFO", RTM_OIFINFO);
242378064Sume#endif
242478064Sume#ifdef RTM_IFANNOUNCE
242578064Sume	RTTYPE("IFANNOUNCE", RTM_IFANNOUNCE);
242678064Sume#endif
242778064Sume#ifdef RTM_NEWMADDR
242878064Sume	RTTYPE("NEWMADDR", RTM_NEWMADDR);
242978064Sume#endif
243078064Sume#ifdef RTM_DELMADDR
243178064Sume	RTTYPE("DELMADDR", RTM_DELMADDR);
243278064Sume#endif
243355163Sshin#undef RTTYPE
243455163Sshin	return NULL;
243555163Sshin}
243655163Sshin
243755163Sshinconst char *
243855163Sshinrtflags(rtm)
243955163Sshin	struct rt_msghdr *rtm;
244055163Sshin{
244155163Sshin	static char buf[BUFSIZ];
244255163Sshin
244378064Sume	/*
244478064Sume	 * letter conflict should be okay.  painful when *BSD diverges...
244578064Sume	 */
244678064Sume	strlcpy(buf, "", sizeof(buf));
244762607Sitojun#define	RTFLAG(s, f) \
244862607Sitojundo { \
244962607Sitojun	if (rtm->rtm_flags & (f)) \
245078064Sume		strlcat(buf, (s), sizeof(buf)); \
245162607Sitojun} while (0)
245255163Sshin	RTFLAG("U", RTF_UP);
245355163Sshin	RTFLAG("G", RTF_GATEWAY);
245455163Sshin	RTFLAG("H", RTF_HOST);
245555163Sshin	RTFLAG("R", RTF_REJECT);
245655163Sshin	RTFLAG("D", RTF_DYNAMIC);
245755163Sshin	RTFLAG("M", RTF_MODIFIED);
245855163Sshin	RTFLAG("d", RTF_DONE);
245955163Sshin#ifdef	RTF_MASK
246055163Sshin	RTFLAG("m", RTF_MASK);
246155163Sshin#endif
2462186119Sqingli#ifdef RTF_CLONING
246355163Sshin	RTFLAG("C", RTF_CLONING);
2464186119Sqingli#endif
246578064Sume#ifdef RTF_CLONED
246678064Sume	RTFLAG("c", RTF_CLONED);
246778064Sume#endif
246878064Sume#ifdef RTF_PRCLONING
246978064Sume	RTFLAG("c", RTF_PRCLONING);
247078064Sume#endif
247178064Sume#ifdef RTF_WASCLONED
247278064Sume	RTFLAG("W", RTF_WASCLONED);
247378064Sume#endif
247455163Sshin	RTFLAG("X", RTF_XRESOLVE);
2475186119Sqingli#ifdef RTF_LLINFO
247655163Sshin	RTFLAG("L", RTF_LLINFO);
2477186119Sqingli#endif
247855163Sshin	RTFLAG("S", RTF_STATIC);
247955163Sshin	RTFLAG("B", RTF_BLACKHOLE);
248078064Sume#ifdef RTF_PROTO3
248178064Sume	RTFLAG("3", RTF_PROTO3);
248278064Sume#endif
248355163Sshin	RTFLAG("2", RTF_PROTO2);
248455163Sshin	RTFLAG("1", RTF_PROTO1);
248578064Sume#ifdef RTF_BROADCAST
248678064Sume	RTFLAG("b", RTF_BROADCAST);
248778064Sume#endif
248878064Sume#ifdef RTF_DEFAULT
248978064Sume	RTFLAG("d", RTF_DEFAULT);
249078064Sume#endif
249178064Sume#ifdef RTF_ISAROUTER
249278064Sume	RTFLAG("r", RTF_ISAROUTER);
249378064Sume#endif
249478064Sume#ifdef RTF_TUNNEL
249578064Sume	RTFLAG("T", RTF_TUNNEL);
249678064Sume#endif
249778064Sume#ifdef RTF_AUTH
249878064Sume	RTFLAG("A", RTF_AUTH);
249978064Sume#endif
250078064Sume#ifdef RTF_CRYPT
250178064Sume	RTFLAG("E", RTF_CRYPT);
250278064Sume#endif
250355163Sshin#undef RTFLAG
250455163Sshin	return buf;
250555163Sshin}
250655163Sshin
250755163Sshinconst char *
250855163Sshinifflags(flags)
250955163Sshin	int flags;
251055163Sshin{
251155163Sshin	static char buf[BUFSIZ];
251255163Sshin
251378064Sume	strlcpy(buf, "", sizeof(buf));
251462607Sitojun#define	IFFLAG(s, f) \
251562607Sitojundo { \
2516119040Sume	if (flags & (f)) { \
251762607Sitojun		if (buf[0]) \
251878064Sume			strlcat(buf, ",", sizeof(buf)); \
2519119040Sume		strlcat(buf, (s), sizeof(buf)); \
252062607Sitojun	} \
252162607Sitojun} while (0)
252255163Sshin	IFFLAG("UP", IFF_UP);
252355163Sshin	IFFLAG("BROADCAST", IFF_BROADCAST);
252455163Sshin	IFFLAG("DEBUG", IFF_DEBUG);
252555163Sshin	IFFLAG("LOOPBACK", IFF_LOOPBACK);
252655163Sshin	IFFLAG("POINTOPOINT", IFF_POINTOPOINT);
252755163Sshin#ifdef IFF_NOTRAILERS
252855163Sshin	IFFLAG("NOTRAILERS", IFF_NOTRAILERS);
252955163Sshin#endif
253078064Sume#ifdef IFF_SMART
253178064Sume	IFFLAG("SMART", IFF_SMART);
253278064Sume#endif
253355163Sshin	IFFLAG("RUNNING", IFF_RUNNING);
253455163Sshin	IFFLAG("NOARP", IFF_NOARP);
253555163Sshin	IFFLAG("PROMISC", IFF_PROMISC);
253655163Sshin	IFFLAG("ALLMULTI", IFF_ALLMULTI);
253755163Sshin	IFFLAG("OACTIVE", IFF_OACTIVE);
253855163Sshin	IFFLAG("SIMPLEX", IFF_SIMPLEX);
253955163Sshin	IFFLAG("LINK0", IFF_LINK0);
254055163Sshin	IFFLAG("LINK1", IFF_LINK1);
254155163Sshin	IFFLAG("LINK2", IFF_LINK2);
254255163Sshin	IFFLAG("MULTICAST", IFF_MULTICAST);
254355163Sshin#undef IFFLAG
254455163Sshin	return buf;
254555163Sshin}
254655163Sshin
254755163Sshinvoid
254855163Sshinkrtread(again)
254955163Sshin	int again;
255055163Sshin{
255155163Sshin	int mib[6];
255255163Sshin	size_t msize;
255355163Sshin	char *buf, *p, *lim;
255455163Sshin	struct rt_msghdr *rtm;
255555163Sshin	int retry;
255655163Sshin	const char *errmsg;
255755163Sshin
255855163Sshin	retry = 0;
255955163Sshin	buf = NULL;
256055163Sshin	mib[0] = CTL_NET;
256155163Sshin	mib[1] = PF_ROUTE;
256255163Sshin	mib[2] = 0;
256355163Sshin	mib[3] = AF_INET6;	/* Address family */
256455163Sshin	mib[4] = NET_RT_DUMP;	/* Dump the kernel routing table */
256555163Sshin	mib[5] = 0;		/* No flags */
256655163Sshin	do {
256755163Sshin		retry++;
256855163Sshin		errmsg = NULL;
256955163Sshin		if (buf)
257055163Sshin			free(buf);
257155163Sshin		if (sysctl(mib, 6, NULL, &msize, NULL, 0) < 0) {
257255163Sshin			errmsg = "sysctl estimate";
257355163Sshin			continue;
257455163Sshin		}
257555163Sshin		if ((buf = malloc(msize)) == NULL) {
257655163Sshin			errmsg = "malloc";
257755163Sshin			continue;
257855163Sshin		}
257955163Sshin		if (sysctl(mib, 6, buf, &msize, NULL, 0) < 0) {
258055163Sshin			errmsg = "sysctl NET_RT_DUMP";
258155163Sshin			continue;
258255163Sshin		}
258355163Sshin	} while (retry < 5 && errmsg != NULL);
258478064Sume	if (errmsg) {
258569279Sume		fatal("%s (with %d retries, msize=%lu)", errmsg, retry,
258669279Sume		    (u_long)msize);
258778064Sume		/*NOTREACHED*/
258878064Sume	} else if (1 < retry)
258955163Sshin		syslog(LOG_INFO, "NET_RT_DUMP %d retires", retry);
259055163Sshin
259155163Sshin	lim = buf + msize;
259255163Sshin	for (p = buf; p < lim; p += rtm->rtm_msglen) {
259355163Sshin		rtm = (struct rt_msghdr *)p;
259455163Sshin		rt_entry(rtm, again);
259555163Sshin	}
259655163Sshin	free(buf);
259755163Sshin}
259855163Sshin
259955163Sshinvoid
260055163Sshinrt_entry(rtm, again)
260155163Sshin	struct rt_msghdr *rtm;
260255163Sshin	int again;
260355163Sshin{
260455163Sshin	struct	sockaddr_in6 *sin6_dst, *sin6_gw, *sin6_mask;
260555163Sshin	struct	sockaddr_in6 *sin6_genmask, *sin6_ifp;
260655163Sshin	char	*rtmp, *ifname = NULL;
260778064Sume	struct	riprt *rrt, *orrt;
260855163Sshin	struct	netinfo6 *np;
260955163Sshin	int	s;
261055163Sshin
261155163Sshin	sin6_dst = sin6_gw = sin6_mask = sin6_genmask = sin6_ifp = 0;
261255163Sshin	if ((rtm->rtm_flags & RTF_UP) == 0 || rtm->rtm_flags &
2613186119Sqingli		(RTF_XRESOLVE|RTF_BLACKHOLE)) {
261455163Sshin		return;		/* not interested in the link route */
261562607Sitojun	}
261669279Sume	/* do not look at cloned routes */
261769279Sume#ifdef RTF_WASCLONED
261869279Sume	if (rtm->rtm_flags & RTF_WASCLONED)
261969279Sume		return;
262069279Sume#endif
262169279Sume#ifdef RTF_CLONED
262269279Sume	if (rtm->rtm_flags & RTF_CLONED)
262369279Sume		return;
262469279Sume#endif
262569279Sume	/*
262669279Sume	 * do not look at dynamic routes.
262769279Sume	 * netbsd/openbsd cloned routes have UGHD.
262869279Sume	 */
262969279Sume	if (rtm->rtm_flags & RTF_DYNAMIC)
263069279Sume		return;
263155163Sshin	rtmp = (char *)(rtm + 1);
263255163Sshin	/* Destination */
263355163Sshin	if ((rtm->rtm_addrs & RTA_DST) == 0)
263455163Sshin		return;		/* ignore routes without destination address */
263555163Sshin	sin6_dst = (struct sockaddr_in6 *)rtmp;
263664631Sitojun	rtmp += ROUNDUP(sin6_dst->sin6_len);
263755163Sshin	if (rtm->rtm_addrs & RTA_GATEWAY) {
263855163Sshin		sin6_gw = (struct sockaddr_in6 *)rtmp;
263955163Sshin		rtmp += ROUNDUP(sin6_gw->sin6_len);
264055163Sshin	}
264155163Sshin	if (rtm->rtm_addrs & RTA_NETMASK) {
264255163Sshin		sin6_mask = (struct sockaddr_in6 *)rtmp;
264355163Sshin		rtmp += ROUNDUP(sin6_mask->sin6_len);
264455163Sshin	}
264555163Sshin	if (rtm->rtm_addrs & RTA_GENMASK) {
264655163Sshin		sin6_genmask = (struct sockaddr_in6 *)rtmp;
264755163Sshin		rtmp += ROUNDUP(sin6_genmask->sin6_len);
264855163Sshin	}
264955163Sshin	if (rtm->rtm_addrs & RTA_IFP) {
265055163Sshin		sin6_ifp = (struct sockaddr_in6 *)rtmp;
265155163Sshin		rtmp += ROUNDUP(sin6_ifp->sin6_len);
265255163Sshin	}
265355163Sshin
265455163Sshin	/* Destination */
265555163Sshin	if (sin6_dst->sin6_family != AF_INET6)
265655163Sshin		return;
265755163Sshin	if (IN6_IS_ADDR_LINKLOCAL(&sin6_dst->sin6_addr))
265855163Sshin		return;		/* Link-local */
265955163Sshin	if (IN6_ARE_ADDR_EQUAL(&sin6_dst->sin6_addr, &in6addr_loopback))
266055163Sshin		return;		/* Loopback */
266155163Sshin	if (IN6_IS_ADDR_MULTICAST(&sin6_dst->sin6_addr))
266255163Sshin		return;
266355163Sshin
266478064Sume	if ((rrt = MALLOC(struct riprt)) == NULL) {
266555163Sshin		fatal("malloc: struct riprt");
266678064Sume		/*NOTREACHED*/
266778064Sume	}
266862607Sitojun	memset(rrt, 0, sizeof(*rrt));
266955163Sshin	np = &rrt->rrt_info;
267055163Sshin	rrt->rrt_same = NULL;
267155163Sshin	rrt->rrt_t = time(NULL);
267255163Sshin	if (aflag == 0 && (rtm->rtm_flags & RTF_STATIC))
267355163Sshin		rrt->rrt_t = 0;	/* Don't age static routes */
2674122677Sume	if ((rtm->rtm_flags & (RTF_HOST|RTF_GATEWAY)) == RTF_HOST)
2675122677Sume		rrt->rrt_t = 0;	/* Don't age non-gateway host routes */
267655163Sshin	np->rip6_tag = 0;
267755163Sshin	np->rip6_metric = rtm->rtm_rmx.rmx_hopcount;
267855163Sshin	if (np->rip6_metric < 1)
267955163Sshin		np->rip6_metric = 1;
268055163Sshin	rrt->rrt_flags = rtm->rtm_flags;
268155163Sshin	np->rip6_dest = sin6_dst->sin6_addr;
268255163Sshin
268355163Sshin	/* Mask or plen */
268455163Sshin	if (rtm->rtm_flags & RTF_HOST)
268555163Sshin		np->rip6_plen = 128;	/* Host route */
268678064Sume	else if (sin6_mask)
268778064Sume		np->rip6_plen = sin6mask2len(sin6_mask);
268878064Sume	else
268955163Sshin		np->rip6_plen = 0;
269055163Sshin
269178064Sume	orrt = rtsearch(np, NULL);
269278064Sume	if (orrt && orrt->rrt_info.rip6_metric != HOPCNT_INFINITY6) {
269355163Sshin		/* Already found */
269455163Sshin		if (!again) {
269555163Sshin			trace(1, "route: %s/%d flags %s: already registered\n",
269655163Sshin				inet6_n2p(&np->rip6_dest), np->rip6_plen,
269755163Sshin				rtflags(rtm));
269855163Sshin		}
269955163Sshin		free(rrt);
270055163Sshin		return;
270155163Sshin	}
270255163Sshin	/* Gateway */
270355163Sshin	if (!sin6_gw)
270455163Sshin		memset(&rrt->rrt_gw, 0, sizeof(struct in6_addr));
270555163Sshin	else {
270655163Sshin		if (sin6_gw->sin6_family == AF_INET6)
270755163Sshin			rrt->rrt_gw = sin6_gw->sin6_addr;
270855163Sshin		else if (sin6_gw->sin6_family == AF_LINK) {
270955163Sshin			/* XXX in case ppp link? */
271055163Sshin			rrt->rrt_gw = in6addr_loopback;
271155163Sshin		} else
271255163Sshin			memset(&rrt->rrt_gw, 0, sizeof(struct in6_addr));
271355163Sshin	}
271455163Sshin	trace(1, "route: %s/%d flags %s",
271555163Sshin		inet6_n2p(&np->rip6_dest), np->rip6_plen, rtflags(rtm));
271655163Sshin	trace(1, " gw %s", inet6_n2p(&rrt->rrt_gw));
271755163Sshin
271855163Sshin	/* Interface */
271955163Sshin	s = rtm->rtm_index;
272055163Sshin	if (s < nindex2ifc && index2ifc[s])
272155163Sshin		ifname = index2ifc[s]->ifc_name;
272258070Sshin	else {
272358070Sshin		trace(1, " not configured\n");
272462607Sitojun		free(rrt);
272558070Sshin		return;
272658070Sshin	}
272762607Sitojun	trace(1, " if %s sock %d", ifname, s);
272855163Sshin	rrt->rrt_index = s;
272955163Sshin
273062607Sitojun	trace(1, "\n");
273162607Sitojun
273255163Sshin	/* Check gateway */
273355163Sshin	if (!IN6_IS_ADDR_LINKLOCAL(&rrt->rrt_gw) &&
2734122677Sume	    !IN6_IS_ADDR_LOOPBACK(&rrt->rrt_gw) &&
2735122677Sume	    (rrt->rrt_flags & RTF_LOCAL) == 0) {
273655163Sshin		trace(0, "***** Gateway %s is not a link-local address.\n",
273755163Sshin			inet6_n2p(&rrt->rrt_gw));
273855163Sshin		trace(0, "*****     dest(%s) if(%s) -- Not optimized.\n",
273962607Sitojun			inet6_n2p(&rrt->rrt_info.rip6_dest), ifname);
274062607Sitojun		rrt->rrt_rflags |= RRTF_NH_NOT_LLADDR;
274155163Sshin	}
274255163Sshin
274355163Sshin	/* Put it to the route list */
274478064Sume	if (orrt && orrt->rrt_info.rip6_metric == HOPCNT_INFINITY6) {
274578064Sume		/* replace route list */
274678064Sume		rrt->rrt_next = orrt->rrt_next;
274778064Sume		*orrt = *rrt;
274878064Sume		trace(1, "route: %s/%d flags %s: replace new route\n",
274978064Sume		    inet6_n2p(&np->rip6_dest), np->rip6_plen,
275078064Sume		    rtflags(rtm));
275178064Sume		free(rrt);
275278064Sume	} else {
275378064Sume		rrt->rrt_next = riprt;
275478064Sume		riprt = rrt;
275578064Sume	}
275655163Sshin}
275755163Sshin
275855163Sshinint
275955163Sshinaddroute(rrt, gw, ifcp)
276055163Sshin	struct riprt *rrt;
276155163Sshin	const struct in6_addr *gw;
276255163Sshin	struct ifc *ifcp;
276355163Sshin{
276455163Sshin	struct	netinfo6 *np;
276555163Sshin	u_char	buf[BUFSIZ], buf1[BUFSIZ], buf2[BUFSIZ];
276655163Sshin	struct	rt_msghdr	*rtm;
2767119031Sume	struct	sockaddr_in6	*sin6;
276855163Sshin	int	len;
276955163Sshin
277055163Sshin	np = &rrt->rrt_info;
277178064Sume	inet_ntop(AF_INET6, (const void *)gw, (char *)buf1, sizeof(buf1));
277255163Sshin	inet_ntop(AF_INET6, (void *)&ifcp->ifc_mylladdr, (char *)buf2, sizeof(buf2));
277355163Sshin	tracet(1, "ADD: %s/%d gw %s [%d] ifa %s\n",
277455163Sshin		inet6_n2p(&np->rip6_dest), np->rip6_plen, buf1,
277555163Sshin		np->rip6_metric - 1, buf2);
277655163Sshin	if (rtlog)
277755163Sshin		fprintf(rtlog, "%s: ADD: %s/%d gw %s [%d] ifa %s\n", hms(),
277855163Sshin			inet6_n2p(&np->rip6_dest), np->rip6_plen, buf1,
277955163Sshin			np->rip6_metric - 1, buf2);
278055163Sshin	if (nflag)
278155163Sshin		return 0;
278255163Sshin
278355163Sshin	memset(buf, 0, sizeof(buf));
278455163Sshin	rtm = (struct rt_msghdr *)buf;
278555163Sshin	rtm->rtm_type = RTM_ADD;
278655163Sshin	rtm->rtm_version = RTM_VERSION;
278755163Sshin	rtm->rtm_seq = ++seq;
278855163Sshin	rtm->rtm_pid = pid;
278962607Sitojun	rtm->rtm_flags = rrt->rrt_flags;
279055163Sshin	rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
279155163Sshin	rtm->rtm_rmx.rmx_hopcount = np->rip6_metric - 1;
279255163Sshin	rtm->rtm_inits = RTV_HOPCOUNT;
2793119031Sume	sin6 = (struct sockaddr_in6 *)&buf[sizeof(struct rt_msghdr)];
279455163Sshin	/* Destination */
2795119031Sume	sin6->sin6_len = sizeof(struct sockaddr_in6);
2796119031Sume	sin6->sin6_family = AF_INET6;
2797119031Sume	sin6->sin6_addr = np->rip6_dest;
2798119031Sume	sin6 = (struct sockaddr_in6 *)((char *)sin6 + ROUNDUP(sin6->sin6_len));
279955163Sshin	/* Gateway */
2800119031Sume	sin6->sin6_len = sizeof(struct sockaddr_in6);
2801119031Sume	sin6->sin6_family = AF_INET6;
2802119031Sume	sin6->sin6_addr = *gw;
2803119031Sume	sin6 = (struct sockaddr_in6 *)((char *)sin6 + ROUNDUP(sin6->sin6_len));
280455163Sshin	/* Netmask */
2805119031Sume	sin6->sin6_len = sizeof(struct sockaddr_in6);
2806119031Sume	sin6->sin6_family = AF_INET6;
2807119031Sume	sin6->sin6_addr = *(plen2mask(np->rip6_plen));
2808119031Sume	sin6 = (struct sockaddr_in6 *)((char *)sin6 + ROUNDUP(sin6->sin6_len));
280955163Sshin
2810119031Sume	len = (char *)sin6 - (char *)buf;
281155163Sshin	rtm->rtm_msglen = len;
281255163Sshin	if (write(rtsock, buf, len) > 0)
281355163Sshin		return 0;
281455163Sshin
281555163Sshin	if (errno == EEXIST) {
281655163Sshin		trace(0, "ADD: Route already exists %s/%d gw %s\n",
2817119035Sume		    inet6_n2p(&np->rip6_dest), np->rip6_plen, buf1);
281855163Sshin		if (rtlog)
281955163Sshin			fprintf(rtlog, "ADD: Route already exists %s/%d gw %s\n",
2820119035Sume			    inet6_n2p(&np->rip6_dest), np->rip6_plen, buf1);
282155163Sshin	} else {
282255163Sshin		trace(0, "Can not write to rtsock (addroute): %s\n",
2823119035Sume		    strerror(errno));
282455163Sshin		if (rtlog)
282555163Sshin			fprintf(rtlog, "\tCan not write to rtsock: %s\n",
2826119035Sume			    strerror(errno));
282755163Sshin	}
282855163Sshin	return -1;
282955163Sshin}
283055163Sshin
283155163Sshinint
283255163Sshindelroute(np, gw)
283355163Sshin	struct netinfo6 *np;
283455163Sshin	struct in6_addr *gw;
283555163Sshin{
283655163Sshin	u_char	buf[BUFSIZ], buf2[BUFSIZ];
283755163Sshin	struct	rt_msghdr	*rtm;
2838119031Sume	struct	sockaddr_in6	*sin6;
283955163Sshin	int	len;
284055163Sshin
284155163Sshin	inet_ntop(AF_INET6, (void *)gw, (char *)buf2, sizeof(buf2));
284255163Sshin	tracet(1, "DEL: %s/%d gw %s\n", inet6_n2p(&np->rip6_dest),
284355163Sshin		np->rip6_plen, buf2);
284455163Sshin	if (rtlog)
284555163Sshin		fprintf(rtlog, "%s: DEL: %s/%d gw %s\n",
284655163Sshin			hms(), inet6_n2p(&np->rip6_dest), np->rip6_plen, buf2);
284755163Sshin	if (nflag)
284855163Sshin		return 0;
284955163Sshin
285055163Sshin	memset(buf, 0, sizeof(buf));
285155163Sshin	rtm = (struct rt_msghdr *)buf;
285255163Sshin	rtm->rtm_type = RTM_DELETE;
285355163Sshin	rtm->rtm_version = RTM_VERSION;
285455163Sshin	rtm->rtm_seq = ++seq;
285555163Sshin	rtm->rtm_pid = pid;
285655163Sshin	rtm->rtm_flags = RTF_UP | RTF_GATEWAY;
285778064Sume	if (np->rip6_plen == sizeof(struct in6_addr) * 8)
285878064Sume		rtm->rtm_flags |= RTF_HOST;
285955163Sshin	rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
2860119031Sume	sin6 = (struct sockaddr_in6 *)&buf[sizeof(struct rt_msghdr)];
286155163Sshin	/* Destination */
2862119031Sume	sin6->sin6_len = sizeof(struct sockaddr_in6);
2863119031Sume	sin6->sin6_family = AF_INET6;
2864119031Sume	sin6->sin6_addr = np->rip6_dest;
2865119031Sume	sin6 = (struct sockaddr_in6 *)((char *)sin6 + ROUNDUP(sin6->sin6_len));
286655163Sshin	/* Gateway */
2867119031Sume	sin6->sin6_len = sizeof(struct sockaddr_in6);
2868119031Sume	sin6->sin6_family = AF_INET6;
2869119031Sume	sin6->sin6_addr = *gw;
2870119031Sume	sin6 = (struct sockaddr_in6 *)((char *)sin6 + ROUNDUP(sin6->sin6_len));
287155163Sshin	/* Netmask */
2872119031Sume	sin6->sin6_len = sizeof(struct sockaddr_in6);
2873119031Sume	sin6->sin6_family = AF_INET6;
2874119031Sume	sin6->sin6_addr = *(plen2mask(np->rip6_plen));
2875119031Sume	sin6 = (struct sockaddr_in6 *)((char *)sin6 + ROUNDUP(sin6->sin6_len));
287655163Sshin
2877119031Sume	len = (char *)sin6 - (char *)buf;
287855163Sshin	rtm->rtm_msglen = len;
287955163Sshin	if (write(rtsock, buf, len) >= 0)
288055163Sshin		return 0;
288155163Sshin
288255163Sshin	if (errno == ESRCH) {
288355163Sshin		trace(0, "RTDEL: Route does not exist: %s/%d gw %s\n",
2884119035Sume		    inet6_n2p(&np->rip6_dest), np->rip6_plen, buf2);
288555163Sshin		if (rtlog)
288655163Sshin			fprintf(rtlog, "RTDEL: Route does not exist: %s/%d gw %s\n",
2887119035Sume			    inet6_n2p(&np->rip6_dest), np->rip6_plen, buf2);
288855163Sshin	} else {
288955163Sshin		trace(0, "Can not write to rtsock (delroute): %s\n",
2890119035Sume		    strerror(errno));
289155163Sshin		if (rtlog)
289255163Sshin			fprintf(rtlog, "\tCan not write to rtsock: %s\n",
2893119035Sume			    strerror(errno));
289455163Sshin	}
289555163Sshin	return -1;
289655163Sshin}
289755163Sshin
289855163Sshinstruct in6_addr *
289955163Sshingetroute(np, gw)
290055163Sshin	struct netinfo6 *np;
290155163Sshin	struct in6_addr *gw;
290255163Sshin{
290355163Sshin	u_char buf[BUFSIZ];
2904119085Sume	int myseq;
290555163Sshin	int len;
290655163Sshin	struct rt_msghdr *rtm;
2907119031Sume	struct sockaddr_in6 *sin6;
290855163Sshin
290955163Sshin	rtm = (struct rt_msghdr *)buf;
291055163Sshin	len = sizeof(struct rt_msghdr) + sizeof(struct sockaddr_in6);
291155163Sshin	memset(rtm, 0, len);
291255163Sshin	rtm->rtm_type = RTM_GET;
291355163Sshin	rtm->rtm_version = RTM_VERSION;
291455163Sshin	myseq = ++seq;
291555163Sshin	rtm->rtm_seq = myseq;
291655163Sshin	rtm->rtm_addrs = RTA_DST;
291755163Sshin	rtm->rtm_msglen = len;
2918119031Sume	sin6 = (struct sockaddr_in6 *)&buf[sizeof(struct rt_msghdr)];
2919119031Sume	sin6->sin6_len = sizeof(struct sockaddr_in6);
2920119031Sume	sin6->sin6_family = AF_INET6;
2921119031Sume	sin6->sin6_addr = np->rip6_dest;
292255163Sshin	if (write(rtsock, buf, len) < 0) {
292355163Sshin		if (errno == ESRCH)	/* No such route found */
292455163Sshin			return NULL;
292555163Sshin		perror("write to rtsock");
292678064Sume		exit(1);
292755163Sshin	}
292855163Sshin	do {
292955163Sshin		if ((len = read(rtsock, buf, sizeof(buf))) < 0) {
293055163Sshin			perror("read from rtsock");
293178064Sume			exit(1);
293255163Sshin		}
293355163Sshin		rtm = (struct rt_msghdr *)buf;
293455163Sshin	} while (rtm->rtm_seq != myseq || rtm->rtm_pid != pid);
2935119031Sume	sin6 = (struct sockaddr_in6 *)&buf[sizeof(struct rt_msghdr)];
293655163Sshin	if (rtm->rtm_addrs & RTA_DST) {
2937119031Sume		sin6 = (struct sockaddr_in6 *)
2938119031Sume			((char *)sin6 + ROUNDUP(sin6->sin6_len));
293955163Sshin	}
294055163Sshin	if (rtm->rtm_addrs & RTA_GATEWAY) {
2941119031Sume		*gw = sin6->sin6_addr;
294255163Sshin		return gw;
294355163Sshin	}
294455163Sshin	return NULL;
294555163Sshin}
294655163Sshin
294755163Sshinconst char *
294855163Sshininet6_n2p(p)
294955163Sshin	const struct in6_addr *p;
295055163Sshin{
295155163Sshin	static char buf[BUFSIZ];
295255163Sshin
295378064Sume	return inet_ntop(AF_INET6, (const void *)p, buf, sizeof(buf));
295455163Sshin}
295555163Sshin
295655163Sshinvoid
295755163Sshinifrtdump(sig)
295855163Sshin	int sig;
295955163Sshin{
296055163Sshin
296155163Sshin	ifdump(sig);
296255163Sshin	rtdump(sig);
296355163Sshin}
296455163Sshin
296555163Sshinvoid
296655163Sshinifdump(sig)
296755163Sshin	int sig;
296855163Sshin{
296955163Sshin	struct ifc *ifcp;
297055163Sshin	FILE *dump;
297155163Sshin	int i;
297255163Sshin
297355163Sshin	if (sig == 0)
297455163Sshin		dump = stderr;
297555163Sshin	else
297655163Sshin		if ((dump = fopen(ROUTE6D_DUMP, "a")) == NULL)
297755163Sshin			dump = stderr;
297855163Sshin
297955163Sshin	fprintf(dump, "%s: Interface Table Dump\n", hms());
298055163Sshin	fprintf(dump, "  Number of interfaces: %d\n", nifc);
298155163Sshin	for (i = 0; i < 2; i++) {
298255163Sshin		fprintf(dump, "  %sadvertising interfaces:\n", i ? "non-" : "");
298355163Sshin		for (ifcp = ifc; ifcp; ifcp = ifcp->ifc_next) {
298455163Sshin			if (i == 0) {
298555163Sshin				if ((ifcp->ifc_flags & IFF_UP) == 0)
298655163Sshin					continue;
298755163Sshin				if (iff_find(ifcp, 'N') != NULL)
298855163Sshin					continue;
298955163Sshin			} else {
299055163Sshin				if (ifcp->ifc_flags & IFF_UP)
299155163Sshin					continue;
299255163Sshin			}
299355163Sshin			ifdump0(dump, ifcp);
299455163Sshin		}
299555163Sshin	}
299655163Sshin	fprintf(dump, "\n");
299755163Sshin	if (dump != stderr)
299855163Sshin		fclose(dump);
299955163Sshin}
300055163Sshin
300155163Sshinvoid
300255163Sshinifdump0(dump, ifcp)
300355163Sshin	FILE *dump;
300455163Sshin	const struct ifc *ifcp;
300555163Sshin{
300655163Sshin	struct ifac *ifa;
300755163Sshin	struct iff *iffp;
300855163Sshin	char buf[BUFSIZ];
300955163Sshin	const char *ft;
301055163Sshin	int addr;
301155163Sshin
301255163Sshin	fprintf(dump, "    %s: index(%d) flags(%s) addr(%s) mtu(%d) metric(%d)\n",
301355163Sshin		ifcp->ifc_name, ifcp->ifc_index, ifflags(ifcp->ifc_flags),
301455163Sshin		inet6_n2p(&ifcp->ifc_mylladdr),
301555163Sshin		ifcp->ifc_mtu, ifcp->ifc_metric);
301655163Sshin	for (ifa = ifcp->ifc_addr; ifa; ifa = ifa->ifa_next) {
301755163Sshin		if (ifcp->ifc_flags & IFF_POINTOPOINT) {
301855163Sshin			inet_ntop(AF_INET6, (void *)&ifa->ifa_raddr,
301955163Sshin				buf, sizeof(buf));
302055163Sshin			fprintf(dump, "\t%s/%d -- %s\n",
302155163Sshin				inet6_n2p(&ifa->ifa_addr),
302255163Sshin				ifa->ifa_plen, buf);
302355163Sshin		} else {
302455163Sshin			fprintf(dump, "\t%s/%d\n",
302555163Sshin				inet6_n2p(&ifa->ifa_addr),
302655163Sshin				ifa->ifa_plen);
302755163Sshin		}
302855163Sshin	}
302955163Sshin	if (ifcp->ifc_filter) {
303055163Sshin		fprintf(dump, "\tFilter:");
303155163Sshin		for (iffp = ifcp->ifc_filter; iffp; iffp = iffp->iff_next) {
303255163Sshin			addr = 0;
303355163Sshin			switch (iffp->iff_type) {
303455163Sshin			case 'A':
303555163Sshin				ft = "Aggregate"; addr++; break;
303655163Sshin			case 'N':
303778064Sume				ft = "No-use"; break;
303855163Sshin			case 'O':
303955163Sshin				ft = "Advertise-only"; addr++; break;
304055163Sshin			case 'T':
304155163Sshin				ft = "Default-only"; break;
304255163Sshin			case 'L':
304355163Sshin				ft = "Listen-only"; addr++; break;
304455163Sshin			default:
304555163Sshin				snprintf(buf, sizeof(buf), "Unknown-%c", iffp->iff_type);
304655163Sshin				ft = buf;
304755163Sshin				addr++;
304855163Sshin				break;
304955163Sshin			}
305055163Sshin			fprintf(dump, " %s", ft);
305155163Sshin			if (addr) {
305255163Sshin				fprintf(dump, "(%s/%d)", inet6_n2p(&iffp->iff_addr),
305355163Sshin					iffp->iff_plen);
305455163Sshin			}
305555163Sshin		}
305655163Sshin		fprintf(dump, "\n");
305755163Sshin	}
305855163Sshin}
305955163Sshin
306055163Sshinvoid
306155163Sshinrtdump(sig)
306255163Sshin	int sig;
306355163Sshin{
306455163Sshin	struct	riprt *rrt;
306555163Sshin	char	buf[BUFSIZ];
306655163Sshin	FILE	*dump;
306755163Sshin	time_t	t, age;
306855163Sshin
306955163Sshin	if (sig == 0)
307055163Sshin		dump = stderr;
307155163Sshin	else
307255163Sshin		if ((dump = fopen(ROUTE6D_DUMP, "a")) == NULL)
307355163Sshin			dump = stderr;
307455163Sshin
307555163Sshin	t = time(NULL);
307655163Sshin	fprintf(dump, "\n%s: Routing Table Dump\n", hms());
307755163Sshin	for (rrt = riprt; rrt; rrt = rrt->rrt_next) {
307855163Sshin		if (rrt->rrt_t == 0)
307955163Sshin			age = 0;
308055163Sshin		else
308155163Sshin			age = t - rrt->rrt_t;
308255163Sshin		inet_ntop(AF_INET6, (void *)&rrt->rrt_info.rip6_dest,
308355163Sshin			buf, sizeof(buf));
308455163Sshin		fprintf(dump, "    %s/%d if(%d:%s) gw(%s) [%d] age(%ld)",
308555163Sshin			buf, rrt->rrt_info.rip6_plen, rrt->rrt_index,
308655163Sshin			index2ifc[rrt->rrt_index]->ifc_name,
308755163Sshin			inet6_n2p(&rrt->rrt_gw),
308855163Sshin			rrt->rrt_info.rip6_metric, (long)age);
308955163Sshin		if (rrt->rrt_info.rip6_tag) {
309055163Sshin			fprintf(dump, " tag(0x%04x)",
309155163Sshin				ntohs(rrt->rrt_info.rip6_tag) & 0xffff);
309255163Sshin		}
309362607Sitojun		if (rrt->rrt_rflags & RRTF_NH_NOT_LLADDR)
309455163Sshin			fprintf(dump, " NOT-LL");
309562607Sitojun		if (rrt->rrt_rflags & RRTF_NOADVERTISE)
309655163Sshin			fprintf(dump, " NO-ADV");
309755163Sshin		fprintf(dump, "\n");
309855163Sshin	}
309955163Sshin	fprintf(dump, "\n");
310055163Sshin	if (dump != stderr)
310155163Sshin		fclose(dump);
310255163Sshin}
310355163Sshin
310455163Sshin/*
310555163Sshin * Parse the -A (and -O) options and put corresponding filter object to the
310678064Sume * specified interface structures.  Each of the -A/O option has the following
310755163Sshin * syntax:	-A 5f09:c400::/32,ef0,ef1  (aggregate)
310855163Sshin * 		-O 5f09:c400::/32,ef0,ef1  (only when match)
310955163Sshin */
311055163Sshinvoid
311155163Sshinfilterconfig()
311255163Sshin{
311355163Sshin	int i;
3114119083Sume	char *p, *ap, *iflp, *ifname, *ep;
311578064Sume	struct iff ftmp, *iff_obj;
311678064Sume	struct ifc *ifcp;
311778064Sume	struct riprt *rrt;
311864631Sitojun#if 0
311978064Sume	struct in6_addr gw;
312064631Sitojun#endif
3121119083Sume	u_long plen;
312255163Sshin
312355163Sshin	for (i = 0; i < nfilter; i++) {
312455163Sshin		ap = filter[i];
312555163Sshin		iflp = NULL;
312655163Sshin		ifcp = NULL;
312755163Sshin		if (filtertype[i] == 'N' || filtertype[i] == 'T') {
312855163Sshin			iflp = ap;
312955163Sshin			goto ifonly;
313055163Sshin		}
3131119038Sume		if ((p = strchr(ap, ',')) != NULL) {
313255163Sshin			*p++ = '\0';
313355163Sshin			iflp = p;
313455163Sshin		}
3135119038Sume		if ((p = strchr(ap, '/')) == NULL) {
313655163Sshin			fatal("no prefixlen specified for '%s'", ap);
313778064Sume			/*NOTREACHED*/
313878064Sume		}
313955163Sshin		*p++ = '\0';
314078064Sume		if (inet_pton(AF_INET6, ap, &ftmp.iff_addr) != 1) {
314155163Sshin			fatal("invalid prefix specified for '%s'", ap);
314278064Sume			/*NOTREACHED*/
314378064Sume		}
3144119083Sume		errno = 0;
3145119083Sume		ep = NULL;
3146119083Sume		plen = strtoul(p, &ep, 10);
3147119083Sume		if (errno || !*p || *ep || plen > sizeof(ftmp.iff_addr) * 8) {
3148119083Sume			fatal("invalid prefix length specified for '%s'", ap);
3149119083Sume			/*NOTREACHED*/
3150119083Sume		}
3151119083Sume		ftmp.iff_plen = plen;
315255163Sshin		ftmp.iff_next = NULL;
315355163Sshin		applyplen(&ftmp.iff_addr, ftmp.iff_plen);
315455163Sshinifonly:
315555163Sshin		ftmp.iff_type = filtertype[i];
315678064Sume		if (iflp == NULL || *iflp == '\0') {
315755163Sshin			fatal("no interface specified for '%s'", ap);
315878064Sume			/*NOTREACHED*/
315978064Sume		}
316055163Sshin		/* parse the interface listing portion */
316155163Sshin		while (iflp) {
316255163Sshin			ifname = iflp;
3163119038Sume			if ((iflp = strchr(iflp, ',')) != NULL)
316455163Sshin				*iflp++ = '\0';
316555163Sshin			ifcp = ifc_find(ifname);
316678064Sume			if (ifcp == NULL) {
316755163Sshin				fatal("no interface %s exists", ifname);
316878064Sume				/*NOTREACHED*/
316978064Sume			}
317055163Sshin			iff_obj = (struct iff *)malloc(sizeof(struct iff));
317178064Sume			if (iff_obj == NULL) {
317255163Sshin				fatal("malloc of iff_obj");
317378064Sume				/*NOTREACHED*/
317478064Sume			}
317555163Sshin			memcpy((void *)iff_obj, (void *)&ftmp,
317678064Sume			    sizeof(struct iff));
317755163Sshin			/* link it to the interface filter */
317855163Sshin			iff_obj->iff_next = ifcp->ifc_filter;
317955163Sshin			ifcp->ifc_filter = iff_obj;
318055163Sshin		}
318178064Sume
318278064Sume		/*
318378064Sume		 * -A: aggregate configuration.
318478064Sume		 */
318555163Sshin		if (filtertype[i] != 'A')
318655163Sshin			continue;
318755163Sshin		/* put the aggregate to the kernel routing table */
318855163Sshin		rrt = (struct riprt *)malloc(sizeof(struct riprt));
318978064Sume		if (rrt == NULL) {
319055163Sshin			fatal("malloc: rrt");
319178064Sume			/*NOTREACHED*/
319278064Sume		}
319355163Sshin		memset(rrt, 0, sizeof(struct riprt));
319455163Sshin		rrt->rrt_info.rip6_dest = ftmp.iff_addr;
319555163Sshin		rrt->rrt_info.rip6_plen = ftmp.iff_plen;
319655163Sshin		rrt->rrt_info.rip6_metric = 1;
319755163Sshin		rrt->rrt_info.rip6_tag = htons(routetag & 0xffff);
319855163Sshin		rrt->rrt_gw = in6addr_loopback;
319962607Sitojun		rrt->rrt_flags = RTF_UP | RTF_REJECT;
320062607Sitojun		rrt->rrt_rflags = RRTF_AGGREGATE;
320155163Sshin		rrt->rrt_t = 0;
3202119039Sume		rrt->rrt_index = loopifcp->ifc_index;
320364631Sitojun#if 0
320464631Sitojun		if (getroute(&rrt->rrt_info, &gw)) {
320564631Sitojun#if 0
320664631Sitojun			/*
320764631Sitojun			 * When the address has already been registered in the
320864631Sitojun			 * kernel routing table, it should be removed
320964631Sitojun			 */
321064631Sitojun			delroute(&rrt->rrt_info, &gw);
321164631Sitojun#else
321278064Sume			/* it is safer behavior */
321364631Sitojun			errno = EINVAL;
321464631Sitojun			fatal("%s/%u already in routing table, "
321564631Sitojun			    "cannot aggregate",
321664631Sitojun			    inet6_n2p(&rrt->rrt_info.rip6_dest),
321764631Sitojun			    rrt->rrt_info.rip6_plen);
321878064Sume			/*NOTREACHED*/
321964631Sitojun#endif
322064631Sitojun		}
322164631Sitojun#endif
322255163Sshin		/* Put the route to the list */
322355163Sshin		rrt->rrt_next = riprt;
322455163Sshin		riprt = rrt;
322555163Sshin		trace(1, "Aggregate: %s/%d for %s\n",
322655163Sshin			inet6_n2p(&ftmp.iff_addr), ftmp.iff_plen,
322755163Sshin			ifcp->ifc_name);
322855163Sshin		/* Add this route to the kernel */
322955163Sshin		if (nflag) 	/* do not modify kernel routing table */
323055163Sshin			continue;
323155163Sshin		addroute(rrt, &in6addr_loopback, loopifcp);
323255163Sshin	}
323355163Sshin}
323455163Sshin
323555163Sshin/***************** utility functions *****************/
323655163Sshin
323755163Sshin/*
323855163Sshin * Returns a pointer to ifac whose address and prefix length matches
323955163Sshin * with the address and prefix length specified in the arguments.
324055163Sshin */
324155163Sshinstruct ifac *
324255163Sshinifa_match(ifcp, ia, plen)
324355163Sshin	const struct ifc *ifcp;
324455163Sshin	const struct in6_addr *ia;
324555163Sshin	int plen;
324655163Sshin{
324755163Sshin	struct ifac *ifa;
324855163Sshin
324955163Sshin	for (ifa = ifcp->ifc_addr; ifa; ifa = ifa->ifa_next) {
325055163Sshin		if (IN6_ARE_ADDR_EQUAL(&ifa->ifa_addr, ia) &&
325155163Sshin		    ifa->ifa_plen == plen)
325255163Sshin			break;
325355163Sshin	}
325455163Sshin	return ifa;
325555163Sshin}
325655163Sshin
325755163Sshin/*
325855163Sshin * Return a pointer to riprt structure whose address and prefix length
325955163Sshin * matches with the address and prefix length found in the argument.
326078064Sume * Note: This is not a rtalloc().  Therefore exact match is necessary.
326155163Sshin */
326255163Sshinstruct riprt *
326378064Sumertsearch(np, prev_rrt)
326455163Sshin	struct	netinfo6 *np;
326578064Sume	struct	riprt **prev_rrt;
326655163Sshin{
326755163Sshin	struct	riprt	*rrt;
326855163Sshin
326978064Sume	if (prev_rrt)
327078064Sume		*prev_rrt = NULL;
327155163Sshin	for (rrt = riprt; rrt; rrt = rrt->rrt_next) {
327255163Sshin		if (rrt->rrt_info.rip6_plen == np->rip6_plen &&
327355163Sshin		    IN6_ARE_ADDR_EQUAL(&rrt->rrt_info.rip6_dest,
327455163Sshin				       &np->rip6_dest))
327555163Sshin			return rrt;
327678064Sume		if (prev_rrt)
327778064Sume			*prev_rrt = rrt;
327855163Sshin	}
327978064Sume	if (prev_rrt)
328078064Sume		*prev_rrt = NULL;
328155163Sshin	return 0;
328255163Sshin}
328355163Sshin
328455163Sshinint
328578064Sumesin6mask2len(sin6)
328678064Sume	const struct sockaddr_in6 *sin6;
328778064Sume{
328878064Sume
328978064Sume	return mask2len(&sin6->sin6_addr,
329078064Sume	    sin6->sin6_len - offsetof(struct sockaddr_in6, sin6_addr));
329178064Sume}
329278064Sume
329378064Sumeint
329455163Sshinmask2len(addr, lenlim)
329555163Sshin	const struct in6_addr *addr;
329655163Sshin	int lenlim;
329755163Sshin{
329855163Sshin	int i = 0, j;
329978064Sume	const u_char *p = (const u_char *)addr;
330062607Sitojun
330155163Sshin	for (j = 0; j < lenlim; j++, p++) {
330255163Sshin		if (*p != 0xff)
330355163Sshin			break;
330455163Sshin		i += 8;
330555163Sshin	}
330655163Sshin	if (j < lenlim) {
330755163Sshin		switch (*p) {
330862607Sitojun#define	MASKLEN(m, l)	case m: do { i += l; break; } while (0)
330962607Sitojun		MASKLEN(0xfe, 7); break;
331062607Sitojun		MASKLEN(0xfc, 6); break;
331162607Sitojun		MASKLEN(0xf8, 5); break;
331262607Sitojun		MASKLEN(0xf0, 4); break;
331362607Sitojun		MASKLEN(0xe0, 3); break;
331462607Sitojun		MASKLEN(0xc0, 2); break;
331562607Sitojun		MASKLEN(0x80, 1); break;
331655163Sshin#undef	MASKLEN
331755163Sshin		}
331855163Sshin	}
331955163Sshin	return i;
332055163Sshin}
332155163Sshin
332255163Sshinvoid
332355163Sshinapplymask(addr, mask)
332455163Sshin	struct in6_addr *addr, *mask;
332555163Sshin{
332655163Sshin	int	i;
332755163Sshin	u_long	*p, *q;
332855163Sshin
332955163Sshin	p = (u_long *)addr; q = (u_long *)mask;
333055163Sshin	for (i = 0; i < 4; i++)
333155163Sshin		*p++ &= *q++;
333255163Sshin}
333355163Sshin
333455163Sshinstatic const u_char plent[8] = {
333555163Sshin	0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe
333655163Sshin};
333755163Sshin
333855163Sshinvoid
333955163Sshinapplyplen(ia, plen)
334055163Sshin	struct	in6_addr *ia;
334155163Sshin	int	plen;
334255163Sshin{
334355163Sshin	u_char	*p;
334455163Sshin	int	i;
334555163Sshin
334655163Sshin	p = ia->s6_addr;
334755163Sshin	for (i = 0; i < 16; i++) {
334855163Sshin		if (plen <= 0)
334955163Sshin			*p = 0;
335055163Sshin		else if (plen < 8)
335155163Sshin			*p &= plent[plen];
335255163Sshin		p++, plen -= 8;
335355163Sshin	}
335455163Sshin}
335555163Sshin
335655163Sshinstatic const int pl2m[9] = {
335755163Sshin	0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff
335855163Sshin};
335955163Sshin
336055163Sshinstruct in6_addr *
336155163Sshinplen2mask(n)
336255163Sshin	int	n;
336355163Sshin{
336455163Sshin	static struct in6_addr ia;
336555163Sshin	u_char	*p;
336655163Sshin	int	i;
336755163Sshin
336855163Sshin	memset(&ia, 0, sizeof(struct in6_addr));
336955163Sshin	p = (u_char *)&ia;
337055163Sshin	for (i = 0; i < 16; i++, p++, n -= 8) {
337155163Sshin		if (n >= 8) {
337255163Sshin			*p = 0xff;
337355163Sshin			continue;
337455163Sshin		}
337555163Sshin		*p = pl2m[n];
337655163Sshin		break;
337755163Sshin	}
337855163Sshin	return &ia;
337955163Sshin}
338055163Sshin
338155163Sshinchar *
338255163Sshinallocopy(p)
338355163Sshin	char *p;
338455163Sshin{
3385119033Sume	int len = strlen(p) + 1;
3386119033Sume	char *q = (char *)malloc(len);
338755163Sshin
3388119033Sume	if (!q) {
3389119033Sume		fatal("malloc");
3390119033Sume		/*NOTREACHED*/
3391119033Sume	}
3392119033Sume
3393119033Sume	strlcpy(q, p, len);
339455163Sshin	return q;
339555163Sshin}
339655163Sshin
339755163Sshinchar *
339855163Sshinhms()
339955163Sshin{
340055163Sshin	static char buf[BUFSIZ];
340155163Sshin	time_t t;
340255163Sshin	struct	tm *tm;
340355163Sshin
340455163Sshin	t = time(NULL);
340578064Sume	if ((tm = localtime(&t)) == 0) {
340655163Sshin		fatal("localtime");
340778064Sume		/*NOTREACHED*/
340878064Sume	}
340978064Sume	snprintf(buf, sizeof(buf), "%02d:%02d:%02d", tm->tm_hour, tm->tm_min,
341078064Sume	    tm->tm_sec);
341155163Sshin	return buf;
341255163Sshin}
341355163Sshin
341455163Sshin#define	RIPRANDDEV	1.0	/* 30 +- 15, max - min = 30 */
341555163Sshin
341655163Sshinint
341755163Sshinripinterval(timer)
341855163Sshin	int timer;
341955163Sshin{
342055163Sshin	double r = rand();
342155163Sshin
342255163Sshin	interval = (int)(timer + timer * RIPRANDDEV * (r / RAND_MAX - 0.5));
342355163Sshin	nextalarm = time(NULL) + interval;
342455163Sshin	return interval;
342555163Sshin}
342655163Sshin
342755163Sshintime_t
342855163Sshinripsuptrig()
342955163Sshin{
343055163Sshin	time_t t;
343155163Sshin
343255163Sshin	double r = rand();
343362607Sitojun	t  = (int)(RIP_TRIG_INT6_MIN +
343478064Sume		(RIP_TRIG_INT6_MAX - RIP_TRIG_INT6_MIN) * (r / RAND_MAX));
343555163Sshin	sup_trig_update = time(NULL) + t;
343655163Sshin	return t;
343755163Sshin}
343855163Sshin
343955163Sshinvoid
344055163Sshin#ifdef __STDC__
344155163Sshinfatal(const char *fmt, ...)
344255163Sshin#else
344355163Sshinfatal(fmt, va_alist)
344455163Sshin	char	*fmt;
344555163Sshin	va_dcl
344655163Sshin#endif
344755163Sshin{
344855163Sshin	va_list ap;
344955163Sshin	char buf[1024];
345055163Sshin
345155163Sshin#ifdef __STDC__
345255163Sshin	va_start(ap, fmt);
345355163Sshin#else
345455163Sshin	va_start(ap);
345555163Sshin#endif
345655163Sshin	vsnprintf(buf, sizeof(buf), fmt, ap);
3457119043Sume	va_end(ap);
345855163Sshin	perror(buf);
3459119043Sume	if (errno)
3460119043Sume		syslog(LOG_ERR, "%s: %s", buf, strerror(errno));
3461119043Sume	else
3462119043Sume		syslog(LOG_ERR, "%s", buf);
346378064Sume	rtdexit();
346455163Sshin}
346555163Sshin
346655163Sshinvoid
346755163Sshin#ifdef __STDC__
346855163Sshintracet(int level, const char *fmt, ...)
346955163Sshin#else
347055163Sshintracet(level, fmt, va_alist)
347155163Sshin	int level;
347255163Sshin	char *fmt;
347355163Sshin	va_dcl
347455163Sshin#endif
347555163Sshin{
347655163Sshin	va_list ap;
347755163Sshin
3478119043Sume	if (level <= dflag) {
347955163Sshin#ifdef __STDC__
3480119043Sume		va_start(ap, fmt);
348155163Sshin#else
3482119043Sume		va_start(ap);
348355163Sshin#endif
348455163Sshin		fprintf(stderr, "%s: ", hms());
348555163Sshin		vfprintf(stderr, fmt, ap);
3486119043Sume		va_end(ap);
348755163Sshin	}
348855163Sshin	if (dflag) {
3489119043Sume#ifdef __STDC__
3490119043Sume		va_start(ap, fmt);
3491119043Sume#else
3492119043Sume		va_start(ap);
3493119043Sume#endif
349455163Sshin		if (level > 0)
349555163Sshin			vsyslog(LOG_DEBUG, fmt, ap);
349655163Sshin		else
349755163Sshin			vsyslog(LOG_WARNING, fmt, ap);
3498119043Sume		va_end(ap);
349955163Sshin	}
350055163Sshin}
350155163Sshin
350255163Sshinvoid
350355163Sshin#ifdef __STDC__
350455163Sshintrace(int level, const char *fmt, ...)
350555163Sshin#else
350655163Sshintrace(level, fmt, va_alist)
350755163Sshin	int level;
350855163Sshin	char *fmt;
350955163Sshin	va_dcl
351055163Sshin#endif
351155163Sshin{
351255163Sshin	va_list ap;
351355163Sshin
3514119043Sume	if (level <= dflag) {
351555163Sshin#ifdef __STDC__
3516119043Sume		va_start(ap, fmt);
351755163Sshin#else
3518119043Sume		va_start(ap);
351955163Sshin#endif
352055163Sshin		vfprintf(stderr, fmt, ap);
3521119043Sume		va_end(ap);
3522119043Sume	}
352355163Sshin	if (dflag) {
3524119043Sume#ifdef __STDC__
3525119043Sume		va_start(ap, fmt);
3526119043Sume#else
3527119043Sume		va_start(ap);
3528119043Sume#endif
352955163Sshin		if (level > 0)
353055163Sshin			vsyslog(LOG_DEBUG, fmt, ap);
353155163Sshin		else
353255163Sshin			vsyslog(LOG_WARNING, fmt, ap);
3533119043Sume		va_end(ap);
353455163Sshin	}
353555163Sshin}
353655163Sshin
353755163Sshinunsigned int
353855163Sshinif_maxindex()
353955163Sshin{
354055163Sshin	struct if_nameindex *p, *p0;
354155163Sshin	unsigned int max = 0;
354255163Sshin
354355163Sshin	p0 = if_nameindex();
354455163Sshin	for (p = p0; p && p->if_index && p->if_name; p++) {
354555163Sshin		if (max < p->if_index)
354655163Sshin			max = p->if_index;
354755163Sshin	}
354855163Sshin	if_freenameindex(p0);
354955163Sshin	return max;
355055163Sshin}
355155163Sshin
355255163Sshinstruct ifc *
355355163Sshinifc_find(name)
355455163Sshin	char *name;
355555163Sshin{
355655163Sshin	struct ifc *ifcp;
355755163Sshin
355855163Sshin	for (ifcp = ifc; ifcp; ifcp = ifcp->ifc_next) {
355955163Sshin		if (strcmp(name, ifcp->ifc_name) == 0)
356055163Sshin			return ifcp;
356155163Sshin	}
356255163Sshin	return (struct ifc *)NULL;
356355163Sshin}
356455163Sshin
356555163Sshinstruct iff *
356655163Sshiniff_find(ifcp, type)
356755163Sshin	struct ifc *ifcp;
356855163Sshin	int type;
356955163Sshin{
357055163Sshin	struct iff *iffp;
357155163Sshin
357255163Sshin	for (iffp = ifcp->ifc_filter; iffp; iffp = iffp->iff_next) {
357355163Sshin		if (iffp->iff_type == type)
357455163Sshin			return iffp;
357555163Sshin	}
357655163Sshin	return NULL;
357755163Sshin}
357855163Sshin
357955163Sshinvoid
358078064Sumesetindex2ifc(idx, ifcp)
358178064Sume	int idx;
358255163Sshin	struct ifc *ifcp;
358355163Sshin{
3584122677Sume	int n, nsize;
358562607Sitojun	struct ifc **p;
358655163Sshin
358755163Sshin	if (!index2ifc) {
358855163Sshin		nindex2ifc = 5;	/*initial guess*/
358955163Sshin		index2ifc = (struct ifc **)
359055163Sshin			malloc(sizeof(*index2ifc) * nindex2ifc);
359178064Sume		if (index2ifc == NULL) {
359255163Sshin			fatal("malloc");
359378064Sume			/*NOTREACHED*/
359478064Sume		}
359555163Sshin		memset(index2ifc, 0, sizeof(*index2ifc) * nindex2ifc);
359655163Sshin	}
359755163Sshin	n = nindex2ifc;
3598122677Sume	for (nsize = nindex2ifc; nsize <= idx; nsize *= 2)
3599122677Sume		;
3600122677Sume	if (n != nsize) {
360162607Sitojun		p = (struct ifc **)realloc(index2ifc,
3602122677Sume		    sizeof(*index2ifc) * nsize);
360378064Sume		if (p == NULL) {
360455163Sshin			fatal("realloc");
360578064Sume			/*NOTREACHED*/
360678064Sume		}
360778064Sume		memset(p + n, 0, sizeof(*index2ifc) * (nindex2ifc - n));
360862607Sitojun		index2ifc = p;
3609122677Sume		nindex2ifc = nsize;
361055163Sshin	}
361178064Sume	index2ifc[idx] = ifcp;
361255163Sshin}
3613