[BACK]Return to in6_prefix.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / sys / netinet6

File: [cvs.NetBSD.org] / src / sys / netinet6 / Attic / in6_prefix.c (download)

Revision 1.12, Wed Jun 7 06:27:43 2000 UTC (20 years, 7 months ago) by itojun
Branch: MAIN
CVS Tags: netbsd-1-5-base, netbsd-1-5-RELEASE, netbsd-1-5-BETA2, netbsd-1-5-BETA, netbsd-1-5-ALPHA2
Branch point for: netbsd-1-5
Changes since 1.11: +47 -20 lines

fix anycast address determination.
correct interface address addition when link-local is added (check if ifp
matches).
make diff to kame repository easier (breaks some KNF)

sync with kame.

/*	$NetBSD: in6_prefix.c,v 1.12 2000/06/07 06:27:43 itojun Exp $	*/
/*	$KAME: in6_prefix.c,v 1.29 2000/06/07 05:59:38 itojun Exp $	*/

/*
 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the project nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * Copyright (c) 1982, 1986, 1991, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)in.c	8.2 (Berkeley) 11/15/93
 */

#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/sockio.h>
#include <sys/systm.h>
#include <sys/syslog.h>
#include <sys/proc.h>

#include <net/if.h>

#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet/ip6.h>
#include <netinet6/in6_prefix.h>
#include <netinet6/ip6_var.h>

struct rr_prhead rr_prefix;

struct callout in6_rr_timer_ch;

#include <net/net_osdep.h>

static void	add_each_addr __P((struct socket *so, struct rr_prefix *rpp,
				   struct rp_addr *rap));
static int create_ra_entry __P((struct rp_addr **rapp));
static int add_each_prefix __P((struct socket *so, struct rr_prefix *rpp));
static void free_rp_entries __P((struct rr_prefix *rpp));
static int link_stray_ia6s __P((struct rr_prefix *rpp));
static void	rp_remove __P((struct rr_prefix *rpp));

/*
 * Copy bits from src to tgt, from off bit for len bits.
 * Caller must specify collect tgtsize and srcsize.
 */
static void
bit_copy(char *tgt, u_int tgtsize, char *src, u_int srcsize,
	 u_int off, u_int len)
{
	char *sp, *tp;

	/* arg values check */
	if (srcsize < off || srcsize < (off + len) ||
	    tgtsize < off || tgtsize < (off + len)) {
		log(LOG_ERR,
		    "in6_prefix.c: bit_copy: invalid args: srcsize %d,\n"
		    "tgtsize %d, off %d, len %d\n", srcsize, tgtsize, off,
		    len);
		return;
	}

	/* search start point */
	for (sp = src, tp = tgt; off >= 8; sp++, tp++)
		off-=8;
	/* copy starting bits */
	if (off) {
		char setbit;
		int startbits;

		startbits = min((8 - off), len);

		for (setbit = (0x80 >> off); startbits;
		     setbit >>= 1, startbits--, len--)
				*tp  |= (setbit & *sp);
		tp++;
		sp++;
	}
	/* copy midium bits */
	for (; len >= 8; sp++, tp++) {
		*tp = *sp;
		len-=8;
	}
	/* copy ending bits */
	if (len) {
		char setbit;

		for (setbit = 0x80; len; setbit >>= 1, len--)
			*tp  |= (setbit & *sp);
	}
}

static struct ifprefix *
in6_prefixwithifp(struct ifnet *ifp, int plen, struct in6_addr *dst)
{
	struct ifprefix *ifpr;

	/* search matched prefix */
	for (ifpr = ifp->if_prefixlist; ifpr; ifpr = ifpr->ifpr_next)
	{
		if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
		    ifpr->ifpr_type != IN6_PREFIX_RR)
 			continue;
		if (plen <= in6_matchlen(dst, IFPR_IN6(ifpr)))
			break;
	}
	return (ifpr);
}

/*
 * Search prefix which matches arg prefix as specified in
 * draft-ietf-ipngwg-router-renum-08.txt
 */
static struct rr_prefix *
search_matched_prefix(struct ifnet *ifp, struct in6_prefixreq *ipr)
{
	struct ifprefix *ifpr;
	struct ifaddr *ifa;
	struct rr_prefix *rpp;

	/* search matched prefix */
	ifpr = in6_prefixwithifp(ifp, ipr->ipr_plen,
				 &ipr->ipr_prefix.sin6_addr);
	if (ifpr != NULL)
		return ifpr2rp(ifpr);

	/*
	 * search matched addr, and then search prefix
	 * which matches the addr
	 */

	for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
	{
		if (ifa->ifa_addr->sa_family != AF_INET6)
			continue;
		if (ipr->ipr_plen <=
		    in6_matchlen(&ipr->ipr_prefix.sin6_addr, IFA_IN6(ifa)))
			break;
	}
	if (ifa == NULL)
		return NULL;

	rpp = ifpr2rp(((struct in6_ifaddr *)ifa)->ia6_ifpr);
	if (rpp != 0)
		return rpp;

	for (ifpr = ifp->if_prefixlist; ifpr; ifpr = ifpr->ifpr_next)
	{
		if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
		    ifpr->ifpr_type != IN6_PREFIX_RR)
			continue;
		if (ifpr->ifpr_plen <= in6_matchlen(IFA_IN6(ifa),
						    IFPR_IN6(ifpr)))
			break;
	}
	if (ifpr != NULL)
		log(LOG_ERR,  "in6_prefix.c: search_matched_prefix: addr %s"
		    "has no pointer to prefix %s\n", ip6_sprintf(IFA_IN6(ifa)),
		    ip6_sprintf(IFPR_IN6(ifpr)));
	return ifpr2rp(ifpr);
}

/*
 * Search prefix which matches arg prefix as specified in
 * draft-ietf-ipngwg-router-renum-08.txt, and mark it if exists.
 * Return 1 if anything matched, and 0 if nothing matched.
 */
static int
mark_matched_prefixes(u_long cmd, struct ifnet *ifp, struct in6_rrenumreq *irr)
{
	struct ifprefix *ifpr;
	struct ifaddr *ifa;
	int matchlen, matched = 0;

	/* search matched prefixes */
	for (ifpr = ifp->if_prefixlist; ifpr; ifpr = ifpr->ifpr_next)
	{
		if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
		    ifpr->ifpr_type != IN6_PREFIX_RR)
			continue;
		matchlen = in6_matchlen(&irr->irr_matchprefix.sin6_addr,
					IFPR_IN6(ifpr));
		if (irr->irr_m_minlen > ifpr->ifpr_plen ||
		    irr->irr_m_maxlen < ifpr->ifpr_plen ||
		    irr->irr_m_len > matchlen)
 			continue;
		matched = 1;
		ifpr2rp(ifpr)->rp_statef_addmark = 1;
		if (cmd == SIOCCIFPREFIX_IN6)
			ifpr2rp(ifpr)->rp_statef_delmark = 1;
	}

	/*
	 * search matched addr, and then search prefixes
	 * which matche the addr
	 */
	for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
	{
		struct rr_prefix *rpp;

		if (ifa->ifa_addr->sa_family != AF_INET6)
			continue;
		matchlen = in6_matchlen(&irr->irr_matchprefix.sin6_addr,
					IFA_IN6(ifa));
		if (irr->irr_m_minlen > matchlen ||
		    irr->irr_m_maxlen < matchlen || irr->irr_m_len > matchlen)
 			continue;
		rpp = ifpr2rp(((struct in6_ifaddr *)ifa)->ia6_ifpr);
		if (rpp != 0) {
			matched = 1;
			rpp->rp_statef_addmark = 1;
			if (cmd == SIOCCIFPREFIX_IN6)
				rpp->rp_statef_delmark = 1;
		} else
			log(LOG_WARNING, "in6_prefix.c: mark_matched_prefixes:"
			    "no back pointer to ifprefix for %s. "
			    "ND autoconfigured addr?\n",
			    ip6_sprintf(IFA_IN6(ifa)));
	}
	return matched;
}

/*
 * Mark global prefixes as to be deleted.
 */
static void
delmark_global_prefixes(struct ifnet *ifp, struct in6_rrenumreq *irr)
{
	struct ifprefix *ifpr;

	/* search matched prefixes */
	for (ifpr = ifp->if_prefixlist; ifpr; ifpr = ifpr->ifpr_next)
	{
		if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
		    ifpr->ifpr_type != IN6_PREFIX_RR)
			continue;
		/* mark delete global prefix */
		if (in6_addrscope(RP_IN6(ifpr2rp(ifpr))) ==
		    IPV6_ADDR_SCOPE_GLOBAL)
			ifpr2rp(ifpr)->rp_statef_delmark = 1;
	}
}

/* Unmark prefixes */
static void
unmark_prefixes(struct ifnet *ifp)
{
	struct ifprefix *ifpr;

	/* unmark all prefix */
	for (ifpr = ifp->if_prefixlist; ifpr; ifpr = ifpr->ifpr_next)
	{
		if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
		    ifpr->ifpr_type != IN6_PREFIX_RR)
 			continue;
		/* unmark prefix */
		ifpr2rp(ifpr)->rp_statef_addmark = 0;
		ifpr2rp(ifpr)->rp_statef_delmark = 0;
	}
}

static void
init_prefix_ltimes(struct rr_prefix *rpp)
{
	long time_second = time.tv_sec;

	if (rpp->rp_pltime == RR_INFINITE_LIFETIME ||
	    rpp->rp_rrf_decrprefd == 0)
		rpp->rp_preferred = 0;
	else
		rpp->rp_preferred = time_second + rpp->rp_pltime;
	if (rpp->rp_vltime == RR_INFINITE_LIFETIME ||
	    rpp->rp_rrf_decrvalid == 0)
		rpp->rp_expire = 0;
	else
		rpp->rp_expire = time_second + rpp->rp_vltime;
}

static int
rr_are_ifid_equal(struct in6_addr *ii1, struct in6_addr *ii2, int ii_len)
{
	int ii_bytelen, ii_bitlen;
	int p_bytelen, p_bitlen;

	/* sanity check */
	if (1 > ii_len ||
	    ii_len > 124) { /* as RFC2373, prefix is at least 4 bit */
		log(LOG_ERR, "rr_are_ifid_equal: invalid ifid length(%d)\n",
		    ii_len);
		return(0);
	}

	ii_bytelen = ii_len / 8;
	ii_bitlen = ii_len % 8;

	p_bytelen = sizeof(struct in6_addr) - ii_bytelen - 1;
	p_bitlen = 8 - ii_bitlen;

	if (bcmp(ii1->s6_addr + p_bytelen + 1, ii2->s6_addr + p_bytelen + 1,
		 ii_bytelen))
		return(0);
	if (((ii1->s6_addr[p_bytelen] << p_bitlen) & 0xff) !=
	    ((ii2->s6_addr[p_bytelen] << p_bitlen) & 0xff))
		return(0);

	return(1);
}

static struct rp_addr *
search_ifidwithprefix(struct rr_prefix *rpp, struct in6_addr *ifid)
{
	struct rp_addr *rap;

	for (rap = rpp->rp_addrhead.lh_first; rap != NULL;
	     rap = rap->ra_entry.le_next)
	{
		if (rr_are_ifid_equal(ifid, &rap->ra_ifid,
				      (sizeof(struct in6_addr) << 3) -
				      rpp->rp_plen))
			break;
	}
	return rap;
}

static int
assigne_ra_entry(struct rr_prefix *rpp, int iilen, struct in6_ifaddr *ia)
{
	int error = 0;
	struct rp_addr *rap;
	int s;

	if ((error = create_ra_entry(&rap)) != 0)
		return error;

	/* copy interface id part */
	bit_copy((caddr_t)&rap->ra_ifid, sizeof(rap->ra_ifid) << 3,
		 (caddr_t)IA6_IN6(ia),
		 sizeof(*IA6_IN6(ia)) << 3, rpp->rp_plen, iilen);
	/* link to ia, and put into list */
	rap->ra_addr = ia;
	IFAREF(&rap->ra_addr->ia_ifa);
#if 0 /* Can't do this now, because rpp may be on th stack. should fix it? */
	ia->ia6_ifpr = rp2ifpr(rpp);
#endif
	s = splsoftnet();
	LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry);
	splx(s);

	return 0;
}

/*
 * add a link-local address to an interface.  we will add new interface address
 * (prefix database + new interface id).
 */
static int
in6_prefix_add_llifid(int iilen, struct in6_ifaddr *ia)
{
	struct rr_prefix *rpp;
	struct rp_addr *rap;
	struct socket so;
	int error, s;

	if ((error = create_ra_entry(&rap)) != 0)
		return(error);
	/* copy interface id part */
	bit_copy((caddr_t)&rap->ra_ifid, sizeof(rap->ra_ifid) << 3,
		 (caddr_t)IA6_IN6(ia), sizeof(*IA6_IN6(ia)) << 3,
		 64, (sizeof(rap->ra_ifid) << 3) - 64);
	/* XXX: init dummy so */
	bzero(&so, sizeof(so));
	/* insert into list */
	for (rpp = LIST_FIRST(&rr_prefix); rpp; rpp = LIST_NEXT(rpp, rp_entry))
	{
		/*
		 * do not attempt to add an address, if ifp does not match
		 */
		if (rpp->rp_ifp != ia->ia_ifp)
			continue;

		s = splnet();
		LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry);
		splx(s);
		add_each_addr(&so, rpp, rap);
	}
	return 0;
}

/*
 * add an address to an interface.  if the interface id portion is new,
 * we will add new interface address (prefix database + new interface id).
 */
int
in6_prefix_add_ifid(int iilen, struct in6_ifaddr *ia)
{
	int plen = (sizeof(*IA6_IN6(ia)) << 3) - iilen;
	struct ifprefix *ifpr;
	struct rp_addr *rap;
	int error = 0;

	if (IN6_IS_ADDR_LINKLOCAL(IA6_IN6(ia)))
		return(in6_prefix_add_llifid(iilen, ia));
	ifpr = in6_prefixwithifp(ia->ia_ifp, plen, IA6_IN6(ia));
	if (ifpr == NULL) {
		struct rr_prefix rp;
		struct socket so;
		int pplen = (plen == 128) ? 64 : plen;

		/* allocate a prefix for ia, with default properties */

		/* init rp */
		bzero(&rp, sizeof(rp));
		rp.rp_type = IN6_PREFIX_RR;
		rp.rp_ifp = ia->ia_ifp;
		rp.rp_plen = pplen;
		rp.rp_prefix.sin6_len = sizeof(rp.rp_prefix);
		rp.rp_prefix.sin6_family = AF_INET6;
		bit_copy((char *)RP_IN6(&rp), sizeof(*RP_IN6(&rp)) << 3,
			 (char *)&ia->ia_addr.sin6_addr,
			 sizeof(ia->ia_addr.sin6_addr) << 3,
			 0, pplen);
		rp.rp_vltime = rp.rp_pltime = RR_INFINITE_LIFETIME;
		rp.rp_raf_onlink = 1;
		rp.rp_raf_auto = 1;
		/* Is some FlagMasks for rrf necessary? */
		rp.rp_rrf_decrvalid = rp.rp_rrf_decrprefd = 0;
		rp.rp_origin = PR_ORIG_RR; /* can be renumbered */

		/* create ra_entry */
		error = link_stray_ia6s(&rp);
		if (error != 0) {
			free_rp_entries(&rp);
			return error;
		}

		/* XXX: init dummy so */
		bzero(&so, sizeof(so));

		error = add_each_prefix(&so, &rp);

		/* free each rp_addr entry */
		free_rp_entries(&rp);

		if (error != 0)
			return error;

		/* search again */
		ifpr = in6_prefixwithifp(ia->ia_ifp, pplen, IA6_IN6(ia));
		if (ifpr == NULL)
			return 0;
	}
	rap = search_ifidwithprefix(ifpr2rp(ifpr), IA6_IN6(ia));
	if (rap != NULL) {
		if (rap->ra_addr == NULL) {
			rap->ra_addr = ia;
			IFAREF(&rap->ra_addr->ia_ifa);
		} else if (rap->ra_addr != ia) {
			/* There may be some inconsistencies between addrs. */
			log(LOG_ERR, "ip6_prefix.c: addr %s/%d matched prefix"
			    "has already another ia %p(%s) on its ifid list\n",
			    ip6_sprintf(IA6_IN6(ia)), plen,
			    rap->ra_addr,
			    ip6_sprintf(IA6_IN6(rap->ra_addr)));
			return EADDRINUSE /* XXX */;
		}
		ia->ia6_ifpr = ifpr;
		return 0;
	}
	error = assigne_ra_entry(ifpr2rp(ifpr), iilen, ia);
	if (error == 0)
		ia->ia6_ifpr = ifpr;
	return (error);
}

void
in6_prefix_remove_ifid(int iilen, struct in6_ifaddr *ia)
{
	struct rp_addr *rap;

	if (ia->ia6_ifpr == NULL)
		return;
	rap = search_ifidwithprefix(ifpr2rp(ia->ia6_ifpr), IA6_IN6(ia));
	if (rap != NULL) {
		int s = splsoftnet();
		LIST_REMOVE(rap, ra_entry);
		splx(s);
		if (rap->ra_addr)
			IFAFREE(&rap->ra_addr->ia_ifa);
		free(rap, M_RR_ADDR);
	}
	if (LIST_FIRST(&ifpr2rp(ia->ia6_ifpr)->rp_addrhead) == NULL)
		rp_remove(ifpr2rp(ia->ia6_ifpr));
}

void
in6_purgeprefix(ifp)
	struct ifnet *ifp;
{
	struct ifprefix *ifpr, *nextifpr;

	/* delete prefixes before ifnet goes away */
	for (ifpr = ifp->if_prefixlist; ifpr; ifpr = nextifpr)
	{
		nextifpr = ifpr->ifpr_next;
		if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
		    ifpr->ifpr_type != IN6_PREFIX_RR)
 			continue;
		(void)delete_each_prefix(ifpr2rp(ifpr), PR_ORIG_KERNEL);
	}
}

static void
add_each_addr(struct socket *so, struct rr_prefix *rpp, struct rp_addr *rap)
{
	struct in6_ifaddr *ia6;
	struct in6_aliasreq ifra;
	int error;

	/* init ifra */
	bzero(&ifra, sizeof(ifra));
	strncpy(ifra.ifra_name, if_name(rpp->rp_ifp), sizeof(ifra.ifra_name));
	ifra.ifra_addr.sin6_family = ifra.ifra_prefixmask.sin6_family =
		AF_INET6;
	ifra.ifra_addr.sin6_len = ifra.ifra_prefixmask.sin6_len =
		sizeof(ifra.ifra_addr);
	/* copy prefix part */
	bit_copy((char *)&ifra.ifra_addr.sin6_addr,
		 sizeof(ifra.ifra_addr.sin6_addr) << 3,
		 (char *)RP_IN6(rpp), sizeof(*RP_IN6(rpp)) << 3,
		 0, rpp->rp_plen);
	/* copy interface id part */
	bit_copy((char *)&ifra.ifra_addr.sin6_addr,
		 sizeof(ifra.ifra_addr.sin6_addr) << 3,
		 (char *)&rap->ra_ifid, sizeof(rap->ra_ifid) << 3,
		 rpp->rp_plen, (sizeof(rap->ra_ifid) << 3) - rpp->rp_plen);
	in6_prefixlen2mask(&ifra.ifra_prefixmask.sin6_addr, rpp->rp_plen);
	/* don't care ifra_flags for now */

	ia6 = in6ifa_ifpwithaddr(rpp->rp_ifp, &ifra.ifra_addr.sin6_addr);
	if (ia6 != NULL) {
		if (ia6->ia6_ifpr == NULL) {
			/* link this addr and the prefix each other */
			IFAFREE(&rap->ra_addr->ia_ifa);
			rap->ra_addr = ia6;
			IFAREF(&rap->ra_addr->ia_ifa);
			ia6->ia6_ifpr = rp2ifpr(rpp);
			return;
		}
		if (ia6->ia6_ifpr == rp2ifpr(rpp)) {
			IFAFREE(&rap->ra_addr->ia_ifa);
			rap->ra_addr = ia6;
			IFAREF(&rap->ra_addr->ia_ifa);
			return;
		}
		/*
		 * The addr is already assigned to other
		 * prefix.
		 * There may be some inconsistencies between
		 * prefixes.
		 * e.g. overraped prefixes with common starting
		 *      part and different plefixlen.
		 *      Or, completely duplicated prefixes?
		 * log it and return.
		 */
		log(LOG_ERR, "in6_prefix.c: add_each_addr: addition of an addr"
		    "%s/%d failed because there is already another addr %s/%d\n",
		    ip6_sprintf(&ifra.ifra_addr.sin6_addr), rpp->rp_plen,
		    ip6_sprintf(IA6_IN6(ia6)),
		    in6_mask2len(&ia6->ia_prefixmask.sin6_addr));
		return;
	}
	/* propagate ANYCAST flag if it is set for ancestor addr */
	if (rap->ra_flags.anycast != 0)
		ifra.ifra_flags |= IN6_IFF_ANYCAST;
	error = in6_control(so, SIOCAIFADDR_IN6, (caddr_t)&ifra, rpp->rp_ifp
			    , curproc);
	if (error != 0)
		log(LOG_ERR, "in6_prefix.c: add_each_addr: addition of an addr"
		    "%s/%d failed because in6_control failed for error %d\n",
		    ip6_sprintf(&ifra.ifra_addr.sin6_addr), rpp->rp_plen,
		    error);
		return;

	/*
	 * link beween this addr and the prefix will be done
	 * in in6_prefix_add_ifid
	 */
}

static int
rrpr_update(struct socket *so, struct rr_prefix *new)
{
	struct rr_prefix *rpp;
	struct ifprefix *ifpr;
	struct rp_addr *rap;
	int s;

	/* search existing prefix */
	for (ifpr = new->rp_ifp->if_prefixlist; ifpr; ifpr = ifpr->ifpr_next)
	{
		if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
		    ifpr->ifpr_type != IN6_PREFIX_RR)
 			continue;
		if (ifpr->ifpr_plen == new->rp_plen &&
		    in6_are_prefix_equal(IFPR_IN6(ifpr), RP_IN6(new),
					 ifpr->ifpr_plen))
			break;
	}
	rpp = ifpr2rp(ifpr);
	if (rpp != NULL) {
		/*
		 * We got a prefix which we have seen in the past.
		 */
		/*
		 * If the origin of the already-installed prefix is more
		 * preferable than the new one, ignore installation request.
		 */
		if (rpp->rp_origin > new->rp_origin)
			return(EPERM);

		/* update prefix information */
		rpp->rp_flags.prf_ra = new->rp_flags.prf_ra;
		if (rpp->rp_origin >= PR_ORIG_RR)
			rpp->rp_flags.prf_rr = new->rp_flags.prf_rr;
		rpp->rp_vltime = new->rp_vltime;
		rpp->rp_pltime = new->rp_pltime;
		rpp->rp_expire = new->rp_expire;
		rpp->rp_preferred = new->rp_preferred;
		rpp->rp_statef_delmark = 0; /* cancel deletion */
		/*
		 * Interface id related update.
		 *  add rp_addr entries in new into rpp, if they have not
		 *  been already included in rpp.
		 */
		while (new->rp_addrhead.lh_first != NULL)
		{
			rap = LIST_FIRST(&new->rp_addrhead);
			LIST_REMOVE(rap, ra_entry);
			if (search_ifidwithprefix(rpp, &rap->ra_ifid)
			    != NULL) {
				if (rap->ra_addr)
					IFAFREE(&rap->ra_addr->ia_ifa);
				free(rap, M_RR_ADDR);
				continue;
			}
			s = splsoftnet();
			LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry);
			splx(s);
		}
	} else {
		/*
		 * We got a fresh prefix.
		 */
		/* create new prefix */
		rpp = (struct rr_prefix *)malloc(sizeof(*rpp), M_IP6RR,
						 M_NOWAIT);
		if (rpp == NULL) {
			log(LOG_ERR, "in6_prefix.c: rrpr_update:%d"
			    ": ENOBUFS for rr_prefix\n", __LINE__);
			return(ENOBUFS);
		}
		/* initilization */
		*rpp = *new;
		LIST_INIT(&rpp->rp_addrhead);
		/*  move rp_addr entries of new to rpp */
		while (new->rp_addrhead.lh_first != NULL)
		{
			rap = LIST_FIRST(&new->rp_addrhead);
			LIST_REMOVE(rap, ra_entry);
			LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry);
		}

		/* let rp_ifpr.ifpr_prefix point rr_prefix. */
		rpp->rp_ifpr.ifpr_prefix = (struct sockaddr *)&rpp->rp_prefix;
		/* link rr_prefix entry to if_prefixlist */
		{
			struct ifnet *ifp = rpp->rp_ifp;
			struct ifprefix *ifpr;

			if ((ifpr = ifp->if_prefixlist) != NULL) {
				for ( ; ifpr->ifpr_next;
				      ifpr = ifpr->ifpr_next)
					continue;
				ifpr->ifpr_next = rp2ifpr(rpp);
			} else
				ifp->if_prefixlist = rp2ifpr(rpp);
			rp2ifpr(rpp)->ifpr_type = IN6_PREFIX_RR;
		}
		/* link rr_prefix entry to rr_prefix list */
		s = splsoftnet();
		LIST_INSERT_HEAD(&rr_prefix, rpp, rp_entry);
		splx(s);
	}

	if (!new->rp_raf_auto)
		return 0;

	/*
	 * Add an address for each interface id, if it is not yet
	 * If it existed but not pointing to the prefix yet,
	 * init the prefix pointer.
	 */
	for (rap = rpp->rp_addrhead.lh_first; rap != NULL;
	     rap = rap->ra_entry.le_next)
	{
		if (rap->ra_addr != NULL) {
			if (rap->ra_addr->ia6_ifpr == NULL)
				rap->ra_addr->ia6_ifpr = rp2ifpr(rpp);
			continue;
		}
		add_each_addr(so, rpp, rap);
	}
	return 0;
}

static int
add_each_prefix(struct socket *so, struct rr_prefix *rpp)
{
	init_prefix_ltimes(rpp);
	return(rrpr_update(so, rpp));
}

static void
rp_remove(struct rr_prefix *rpp)
{
	int s;

	s = splsoftnet();
	/* unlink rp_entry from if_prefixlist */
	{
		struct ifnet *ifp = rpp->rp_ifp;
		struct ifprefix *ifpr;

		if ((ifpr = ifp->if_prefixlist) == rp2ifpr(rpp))
			ifp->if_prefixlist = ifpr->ifpr_next;
		else {
			while (ifpr->ifpr_next &&
			       (ifpr->ifpr_next != rp2ifpr(rpp)))
				ifpr = ifpr->ifpr_next;
			if (ifpr->ifpr_next)
				ifpr->ifpr_next = rp2ifpr(rpp)->ifpr_next;
			else
				printf("Couldn't unlink rr_prefix from ifp\n");
		}
	}
	/* unlink rp_entry from rr_prefix list */
	LIST_REMOVE(rpp, rp_entry);
	splx(s);
	free(rpp, M_IP6RR);
}

static int
create_ra_entry(struct rp_addr **rapp)
{
	*rapp = (struct rp_addr *)malloc(sizeof(struct rp_addr), M_RR_ADDR,
					 M_NOWAIT);
	if (*rapp == NULL) {
		log(LOG_ERR, "in6_prefix.c: init_newprefix:%d: ENOBUFS"
		    "for rp_addr\n", __LINE__);
		return ENOBUFS;
	}
	bzero(*rapp, sizeof(*(*rapp)));

	return 0;
}

static int
init_newprefix(struct in6_rrenumreq *irr, struct ifprefix *ifpr,
	       struct rr_prefix *rpp)
{
	struct rp_addr *orap;

	/* init rp */
	bzero(rpp, sizeof(*rpp));
	rpp->rp_type = IN6_PREFIX_RR;
	rpp->rp_ifp = ifpr->ifpr_ifp;
	rpp->rp_plen = ifpr->ifpr_plen;
	rpp->rp_prefix.sin6_len = sizeof(rpp->rp_prefix);
	rpp->rp_prefix.sin6_family = AF_INET6;
	bit_copy((char *)RP_IN6(rpp), sizeof(*RP_IN6(rpp)) << 3,
		 (char *)&irr->irr_useprefix.sin6_addr,
		 sizeof(irr->irr_useprefix.sin6_addr) << 3,
		 0, irr->irr_u_uselen);
	/* copy keeplen part if necessary as necessary len */
	if (irr->irr_u_uselen < ifpr->ifpr_plen)
		bit_copy((char *)RP_IN6(rpp), sizeof(*RP_IN6(rpp)) << 3,
			 (char *)IFPR_IN6(ifpr), sizeof(*IFPR_IN6(ifpr)) << 3,
			 irr->irr_u_uselen,
			 min(ifpr->ifpr_plen - irr->irr_u_uselen,
			     irr->irr_u_keeplen));
	for (orap = (ifpr2rp(ifpr)->rp_addrhead).lh_first; orap != NULL;
	     orap = orap->ra_entry.le_next)
	{
		struct rp_addr *rap;
		int error = 0;

		if ((error = create_ra_entry(&rap)) != 0)
			return error;
		rap->ra_ifid = orap->ra_ifid;
		rap->ra_flags.anycast = (orap->ra_addr != NULL &&
					 (orap->ra_addr->ia6_flags &
					  IN6_IFF_ANYCAST) != 0) ? 1 : 0;
		LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry);
	}
	rpp->rp_vltime = irr->irr_vltime;
	rpp->rp_pltime = irr->irr_pltime;
	rpp->rp_raf_onlink = irr->irr_raf_mask_onlink ? irr->irr_raf_onlink :
		ifpr2rp(ifpr)->rp_raf_onlink;
	rpp->rp_raf_auto = irr->irr_raf_mask_auto ? irr->irr_raf_auto :
		ifpr2rp(ifpr)->rp_raf_auto;
	/* Is some FlagMasks for rrf necessary? */
	rpp->rp_rrf = irr->irr_rrf;
	rpp->rp_origin = irr->irr_origin;

	return 0;
}

static void
free_rp_entries(struct rr_prefix *rpp)
{
	/*
	 * This func is only called with rpp on stack(not on list).
	 * So no splsoftnet() here
	 */
	while (rpp->rp_addrhead.lh_first != NULL)
	{
		struct rp_addr *rap;

		rap = LIST_FIRST(&rpp->rp_addrhead);
		LIST_REMOVE(rap, ra_entry);
		if (rap->ra_addr)
			IFAFREE(&rap->ra_addr->ia_ifa);
		free(rap, M_RR_ADDR);
	}
}

static int
add_useprefixes(struct socket *so, struct ifnet *ifp,
		struct in6_rrenumreq *irr)
{
	struct ifprefix *ifpr, *nextifpr;
	struct rr_prefix rp;
	int error = 0;

	/* add prefixes to each of marked prefix */
	for (ifpr = ifp->if_prefixlist; ifpr; ifpr = nextifpr)
	{
		nextifpr = ifpr->ifpr_next;
		if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
		    ifpr->ifpr_type != IN6_PREFIX_RR)
 			continue;
		if (ifpr2rp(ifpr)->rp_statef_addmark) {
			if ((error = init_newprefix(irr, ifpr, &rp)) != 0)
				break;
			error = add_each_prefix(so, &rp);
		}
	}
	/* free each rp_addr entry */
	free_rp_entries(&rp);

	return error;
}

static void
unprefer_prefix(struct rr_prefix *rpp)
{
	struct rp_addr *rap;
	long time_second = time.tv_sec;

	for (rap = rpp->rp_addrhead.lh_first; rap != NULL;
	     rap = rap->ra_entry.le_next) {
		if (rap->ra_addr == NULL)
			continue;
		rap->ra_addr->ia6_lifetime.ia6t_preferred = time_second;
		rap->ra_addr->ia6_lifetime.ia6t_pltime = 0;
	}
}

int
delete_each_prefix(struct rr_prefix *rpp, u_char origin)
{
	int error = 0;

	if (rpp->rp_origin > origin)
		return(EPERM);

	while (rpp->rp_addrhead.lh_first != NULL) {
		struct rp_addr *rap;
		int s;

		s = splsoftnet();
		rap = LIST_FIRST(&rpp->rp_addrhead);
		if (rap == NULL)
			break;
		LIST_REMOVE(rap, ra_entry);
		splx(s);
		if (rap->ra_addr == NULL) {
			free(rap, M_RR_ADDR);
			continue;
		}
		rap->ra_addr->ia6_ifpr = NULL;

		in6_purgeaddr(&rap->ra_addr->ia_ifa, rpp->rp_ifp);
		IFAFREE(&rap->ra_addr->ia_ifa);
		free(rap, M_RR_ADDR);
	}
	rp_remove(rpp);

	return error;
}

static void
delete_prefixes(struct ifnet *ifp, u_char origin)
{
	struct ifprefix *ifpr, *nextifpr;

	/* delete prefixes marked as tobe deleted */
	for (ifpr = ifp->if_prefixlist; ifpr; ifpr = nextifpr)
	{
		nextifpr = ifpr->ifpr_next;
		if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
		    ifpr->ifpr_type != IN6_PREFIX_RR)
 			continue;
		if (ifpr2rp(ifpr)->rp_statef_delmark)
			(void)delete_each_prefix(ifpr2rp(ifpr), origin);
	}
}

static int
link_stray_ia6s(struct rr_prefix *rpp)
{
	struct ifaddr *ifa;

	for (ifa = rpp->rp_ifp->if_addrlist.tqh_first; ifa;
	     ifa = ifa->ifa_list.tqe_next)
	{
		struct rp_addr *rap;
		struct rr_prefix *orpp;
		int error = 0;

		if (ifa->ifa_addr->sa_family != AF_INET6)
			continue;
		if (rpp->rp_plen > in6_matchlen(RP_IN6(rpp), IFA_IN6(ifa)))
			continue;

		orpp = ifpr2rp(((struct in6_ifaddr *)ifa)->ia6_ifpr);
		if (orpp != NULL) {
			if (!in6_are_prefix_equal(RP_IN6(orpp), RP_IN6(rpp),
						  rpp->rp_plen))
				log(LOG_ERR, "in6_prefix.c: link_stray_ia6s:"
				    "addr %s/%d already linked to a prefix"
				    "and it matches also %s/%d\n",
				    ip6_sprintf(IFA_IN6(ifa)), orpp->rp_plen,
				    ip6_sprintf(RP_IN6(rpp)),
				    rpp->rp_plen);
			continue;
		}
		if ((error = assigne_ra_entry(rpp,
					      (sizeof(rap->ra_ifid) << 3) -
					      rpp->rp_plen,
					      (struct in6_ifaddr *)ifa)) != 0)
			return error;
	}
	return 0;
}

/* XXX assumes that permission is already checked by the caller */
int
in6_prefix_ioctl(struct socket *so, u_long cmd, caddr_t data,
		 struct ifnet *ifp)
{
	struct rr_prefix *rpp, rp_tmp;
	struct rp_addr *rap;
	struct in6_prefixreq *ipr = (struct in6_prefixreq *)data;
	struct in6_rrenumreq *irr = (struct in6_rrenumreq *)data;
	struct ifaddr *ifa;
	int error = 0;

	/*
	 * Failsafe for errneous address config program.
	 * Let's hope rrenumd don't make a mistakes.
	 */
	if (ipr->ipr_origin <= PR_ORIG_RA)
		ipr->ipr_origin = PR_ORIG_STATIC;

	switch (cmd) {
	case SIOCSGIFPREFIX_IN6:
		delmark_global_prefixes(ifp, irr);
		/* FALL THROUGH */
	case SIOCAIFPREFIX_IN6:
	case SIOCCIFPREFIX_IN6:
		/* check if preferred lifetime > valid lifetime */
		if (irr->irr_pltime > irr->irr_vltime) {
			log(LOG_NOTICE,
			    "in6_prefix_ioctl: preferred lifetime"
			    "(%ld) is greater than valid lifetime(%ld)\n",
			    (u_long)irr->irr_pltime, (u_long)irr->irr_vltime);
			error = EINVAL;
			break;
		}
		if (mark_matched_prefixes(cmd, ifp, irr)) {
			if (irr->irr_u_uselen != 0)
				if ((error = add_useprefixes(so, ifp, irr))
				    != 0)
					goto failed;
			if (cmd != SIOCAIFPREFIX_IN6)
				delete_prefixes(ifp, irr->irr_origin);
		} else
			return (EADDRNOTAVAIL);
	failed:
		unmark_prefixes(ifp);
		break;
	case SIOCGIFPREFIX_IN6:
		rpp = search_matched_prefix(ifp, ipr);
		if (rpp == NULL || ifp != rpp->rp_ifp)
			return (EADDRNOTAVAIL);

		ipr->ipr_origin = rpp->rp_origin;
		ipr->ipr_plen = rpp->rp_plen;
		ipr->ipr_vltime = rpp->rp_vltime;
		ipr->ipr_pltime = rpp->rp_pltime;
		ipr->ipr_flags = rpp->rp_flags;
		ipr->ipr_prefix = rpp->rp_prefix;

		break;
	case SIOCSIFPREFIX_IN6:
		/* check if preferred lifetime > valid lifetime */
		if (ipr->ipr_pltime > ipr->ipr_vltime) {
			log(LOG_NOTICE,
			    "in6_prefix_ioctl: preferred lifetime"
			    "(%ld) is greater than valid lifetime(%ld)\n",
			    (u_long)ipr->ipr_pltime, (u_long)ipr->ipr_vltime);
			error = EINVAL;
			break;
		}

		/* init rp_tmp */
		bzero((caddr_t)&rp_tmp, sizeof(rp_tmp));
		rp_tmp.rp_ifp = ifp;
		rp_tmp.rp_plen = ipr->ipr_plen;
		rp_tmp.rp_prefix = ipr->ipr_prefix;
		rp_tmp.rp_vltime = ipr->ipr_vltime;
		rp_tmp.rp_pltime = ipr->ipr_pltime;
		rp_tmp.rp_flags = ipr->ipr_flags;
		rp_tmp.rp_origin = ipr->ipr_origin;

		/* create rp_addr entries, usually at least for lladdr */
		if ((error = link_stray_ia6s(&rp_tmp)) != 0) {
			free_rp_entries(&rp_tmp);
			break;
		}
		for (ifa = ifp->if_addrlist.tqh_first;
		     ifa;
		     ifa = ifa->ifa_list.tqe_next)
		{
			if (ifa->ifa_addr == NULL)
				continue;	/* just for safety */
			if (ifa->ifa_addr->sa_family != AF_INET6)
				continue;
			if (IN6_IS_ADDR_LINKLOCAL(IFA_IN6(ifa)) == 0)
				continue;

			if ((error = create_ra_entry(&rap)) != 0) {
				free_rp_entries(&rp_tmp);
				goto bad;
			}
			/* copy interface id part */
			bit_copy((caddr_t)&rap->ra_ifid,
				 sizeof(rap->ra_ifid) << 3,
				 (caddr_t)IFA_IN6(ifa),
				 sizeof(*IFA_IN6(ifa)) << 3,
				 rp_tmp.rp_plen,
				 (sizeof(rap->ra_ifid) << 3) - rp_tmp.rp_plen);
			/* insert into list */
			LIST_INSERT_HEAD(&rp_tmp.rp_addrhead, rap, ra_entry);
		}

		error = add_each_prefix(so, &rp_tmp);

		/* free each rp_addr entry */
		free_rp_entries(&rp_tmp);

		break;
	case SIOCDIFPREFIX_IN6:
		rpp = search_matched_prefix(ifp, ipr);
		if (rpp == NULL || ifp != rpp->rp_ifp)
			return (EADDRNOTAVAIL);

		error = delete_each_prefix(rpp, ipr->ipr_origin);
		break;
	}
 bad:
	return error;
}

void
in6_rr_timer(void *ignored_arg)
{
	int s;
	struct rr_prefix *rpp;
	long time_second = time.tv_sec;

	callout_reset(&in6_rr_timer_ch, ip6_rr_prune * hz,
	    in6_rr_timer, NULL);

	s = splsoftnet();
	/* expire */
	rpp = LIST_FIRST(&rr_prefix);
	while (rpp) {
		if (rpp->rp_expire && rpp->rp_expire < time_second) {
			struct rr_prefix *next_rpp;

			next_rpp = LIST_NEXT(rpp, rp_entry);
			delete_each_prefix(rpp, PR_ORIG_KERNEL);
			rpp = next_rpp;
			continue;
		}
		if (rpp->rp_preferred && rpp->rp_preferred < time_second)
			unprefer_prefix(rpp);
		rpp = LIST_NEXT(rpp, rp_entry);
	}
	splx(s);
}