NetBSD/sys/netinet6/in6_prefix.c

447 lines
13 KiB
C

/* $NetBSD: in6_prefix.c,v 1.3 1999/07/03 21:30:18 thorpej 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>
#if !defined(__FreeBSD__) || __FreeBSD__ < 3
#include <sys/ioctl.h>
#endif
#include <sys/malloc.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/systm.h>
#include <sys/syslog.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet6/nd6.h>
extern int in6_interfaces;
/*
* 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);
}
}
/*
* Search prefix which matches arg prefix as specified in
* draft-ietf-ipngwg-router-renum-08.txt
*/
static struct nd_prefix *
search_matched_prefix(struct ifnet *ifp, struct in6_prefixreq *ipr)
{
struct ifprefix *ifpr;
/* search matched prefix */
for (ifpr = ifp->if_prefixlist; ifpr; ifpr = ifpr->ifpr_next) {
if (ifpr->ifpr_prefix->sa_family != AF_INET6)
continue;
if (ipr->ipr_plen <= in6_matchlen(&ipr->ipr_prefix.sin6_addr,
&IFPR_IN6(ifpr)))
break;
}
/*
* search matched addr, and then search prefix
* which matches the addr
*/
if (ifpr == 0) {
struct ifaddr *ifa;
#ifndef __NetBSD__
for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
#else
for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
#endif
{
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) {
for (ifpr = ifp->if_prefixlist; ifpr;
ifpr = ifpr->ifpr_next) {
if (ifpr->ifpr_prefix->sa_family != AF_INET6)
continue;
if (ifpr->ifpr_plen <=
in6_matchlen(&IFA_IN6(ifa),
&IFPR_IN6(ifpr)))
break;
}
}
}
return ifpr2ndpr(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)
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;
ifpr2ndpr(ifpr)->ndpr_statef_addmark = 1;
if (cmd == SIOCCIFPREFIX_IN6)
ifpr2ndpr(ifpr)->ndpr_statef_delmark = 1;
}
/*
* search matched addr, and then search prefixes
* which matche the addr
*/
#ifndef __NetBSD__
for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
#else
for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
#endif
{
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;
for (ifpr = ifp->if_prefixlist; ifpr; ifpr = ifpr->ifpr_next) {
if (ifpr->ifpr_prefix->sa_family != AF_INET6)
continue;
if (ifpr->ifpr_plen > in6_matchlen(&IFA_IN6(ifa),
&IFPR_IN6(ifpr)))
continue;
matched = 1;
ifpr2ndpr(ifpr)->ndpr_statef_addmark = 1;
if (cmd == SIOCCIFPREFIX_IN6)
ifpr2ndpr(ifpr)->ndpr_statef_delmark = 1;
}
}
return matched;
}
/*
* Mark global prefixes as to be deleted.
* Return 1 if anything matched, and 0 if nothing matched.
*/
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)
continue;
/* mark delete global prefix */
if (in6_addrscope(&irr->irr_matchprefix.sin6_addr) ==
IPV6_ADDR_SCOPE_GLOBAL)
ifpr2ndpr(ifpr)->ndpr_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)
continue;
/* unmark prefix */
ifpr2ndpr(ifpr)->ndpr_statef_addmark = 0;
ifpr2ndpr(ifpr)->ndpr_statef_delmark = 0;
}
}
static int
add_each_prefix(struct nd_prefix *ndpr)
{
int error = 0;
if ((error = in6_init_prefix_ltimes(ndpr)) != 0)
return error;
if ((error = prelist_update(ndpr, 0, NULL)) != 0)
return error;
if ((ndpr->ndpr_ifp->if_flags & IFF_LOOPBACK) == 0)
in6_interfaces++; /*XXX*/
return error;
}
static void
add_useprefixes(struct ifnet *ifp, struct in6_rrenumreq *irr)
{
struct ifprefix *ifpr, *nextifpr;
/* 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)
continue;
if (ifpr2ndpr(ifpr)->ndpr_statef_addmark) {
struct nd_prefix ndpr;
/* init ndpr */
bzero((caddr_t)&ndpr, sizeof(ndpr));
ndpr.ndpr_ifp = ifp;
ndpr.ndpr_plen = irr->irr_u_uselen +
irr->irr_u_keeplen;
ndpr.ndpr_prefix.sin6_len = sizeof(ndpr.ndpr_prefix);
ndpr.ndpr_prefix.sin6_family = AF_INET6;
bit_copy((char *)&ndpr.ndpr_prefix.sin6_addr,
sizeof(ndpr.ndpr_prefix.sin6_addr) << 3,
(char *)&irr->irr_useprefix.sin6_addr,
sizeof(irr->irr_useprefix.sin6_addr) << 3,
0, irr->irr_u_uselen);
bit_copy((char *)&ndpr.ndpr_prefix.sin6_addr,
sizeof(ndpr.ndpr_prefix.sin6_addr) << 3,
(char *)&
ifpr2ndpr(ifpr)->ndpr_prefix.sin6_addr,
sizeof(ifpr2ndpr(ifpr)->ndpr_prefix.sin6_addr)
<< 3,
irr->irr_u_uselen, irr->irr_u_keeplen);
ndpr.ndpr_vltime = irr->irr_vltime;
ndpr.ndpr_pltime = irr->irr_pltime;
ndpr.ndpr_raf_onlink = irr->irr_raf_mask_onlink ?
irr->irr_raf_onlink :
ifpr2ndpr(ifpr)->ndpr_raf_onlink;
ndpr.ndpr_raf_auto = irr->irr_raf_mask_auto ?
irr->irr_raf_auto :
ifpr2ndpr(ifpr)->ndpr_raf_auto;
/* Is some FlagMasks for rrf necessary? */
ndpr.ndpr_rrf = irr->irr_rrf;
ndpr.ndpr_origin = irr->irr_origin;
(void)add_each_prefix(&ndpr);
}
}
}
static int
delete_each_prefix(struct nd_prefix *ndpr, u_char origin)
{
int error = 0;
if (ndpr->ndpr_origin > origin)
return(EPERM);
if (!IN6_IS_ADDR_UNSPECIFIED(&ndpr->ndpr_addr))
in6_ifdel(ndpr->ndpr_ifp, &ndpr->ndpr_addr);
/* xxx ND_OPT_PI_FLAG_ONLINK processing */
prelist_remove(ndpr);
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)
continue;
if (ifpr2ndpr(ifpr)->ndpr_statef_delmark)
(void)delete_each_prefix(ifpr2ndpr(ifpr), origin);
}
}
int
in6_prefix_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp)
{
struct nd_prefix *ndpr, ndpr_tmp;
struct in6_prefixreq *ipr = (struct in6_prefixreq *)data;
struct in6_rrenumreq *irr = (struct in6_rrenumreq *)data;
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:
if (mark_matched_prefixes(cmd, ifp, irr)) {
if (irr->irr_u_uselen)
add_useprefixes(ifp, irr);
if (cmd != SIOCAIFPREFIX_IN6)
delete_prefixes(ifp, irr->irr_origin);
}
else
return (EADDRNOTAVAIL);
unmark_prefixes(ifp);
break;
case SIOCGIFPREFIX_IN6:
ndpr = search_matched_prefix(ifp, ipr);
if (ndpr == 0 || ifp != ndpr->ndpr_ifp)
return (EADDRNOTAVAIL);
ipr->ipr_origin = ndpr->ndpr_origin;
ipr->ipr_plen = ndpr->ndpr_plen;
ipr->ipr_vltime = ndpr->ndpr_vltime;
ipr->ipr_pltime = ndpr->ndpr_pltime;
ipr->ipr_flags = ndpr->ndpr_flags;
ipr->ipr_prefix = ndpr->ndpr_prefix;
break;
case SIOCSIFPREFIX_IN6:
/* init ndpr_tmp */
bzero((caddr_t)&ndpr_tmp, sizeof(ndpr_tmp));
ndpr_tmp.ndpr_ifp = ifp;
ndpr_tmp.ndpr_plen = ipr->ipr_plen;
ndpr_tmp.ndpr_prefix = ipr->ipr_prefix;
ndpr_tmp.ndpr_vltime = ipr->ipr_vltime;
ndpr_tmp.ndpr_pltime = ipr->ipr_pltime;
ndpr_tmp.ndpr_flags = ipr->ipr_flags;
ndpr_tmp.ndpr_origin = ipr->ipr_origin;
if ((error = add_each_prefix(&ndpr_tmp)) != 0)
return error;
break;
case SIOCDIFPREFIX_IN6:
ndpr = search_matched_prefix(ifp, ipr);
if (ndpr == 0 || ifp != ndpr->ndpr_ifp)
return (EADDRNOTAVAIL);
error = delete_each_prefix(ndpr, ipr->ipr_origin);
break;
}
return error;
}