When choosing IPv6 source addresses, respect the ifaddr preference

level such as one might set with 'ifconfig xx0 inet6 <address>
preference <pref>'.  I've been running this for many months without
any problems.
This commit is contained in:
dyoung 2010-04-22 20:05:15 +00:00
parent 7254a559b2
commit cb401b7946
1 changed files with 63 additions and 41 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: in6.c,v 1.155 2010/04/07 22:59:15 oki Exp $ */
/* $NetBSD: in6.c,v 1.156 2010/04/22 20:05:15 dyoung Exp $ */
/* $KAME: in6.c,v 1.198 2001/07/18 09:12:38 itojun Exp $ */
/*
@ -62,7 +62,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: in6.c,v 1.155 2010/04/07 22:59:15 oki Exp $");
__KERNEL_RCSID(0, "$NetBSD: in6.c,v 1.156 2010/04/22 20:05:15 dyoung Exp $");
#include "opt_inet.h"
#include "opt_pfil_hooks.h"
@ -1792,6 +1792,14 @@ in6_ifinit(struct ifnet *ifp, struct in6_ifaddr *ia,
return error;
}
static struct ifaddr *
bestifa(struct ifaddr *best_ifa, struct ifaddr *ifa)
{
if (best_ifa == NULL || best_ifa->ifa_preference < ifa->ifa_preference)
return ifa;
return best_ifa;
}
/*
* Find an IPv6 interface link-local address specific to an interface.
*/
@ -1809,10 +1817,7 @@ in6ifa_ifpforlinklocal(const struct ifnet *ifp, const int ignoreflags)
continue;
if ((((struct in6_ifaddr *)ifa)->ia6_flags & ignoreflags) != 0)
continue;
if (best_ifa == NULL)
best_ifa = ifa;
else if (best_ifa->ifa_preference < ifa->ifa_preference)
best_ifa = ifa;
best_ifa = bestifa(best_ifa, ifa);
}
return (struct in6_ifaddr *)best_ifa;
@ -1825,18 +1830,28 @@ in6ifa_ifpforlinklocal(const struct ifnet *ifp, const int ignoreflags)
struct in6_ifaddr *
in6ifa_ifpwithaddr(const struct ifnet *ifp, const struct in6_addr *addr)
{
struct ifaddr *ifa;
struct ifaddr *best_ifa = NULL, *ifa;
IFADDR_FOREACH(ifa, ifp) {
if (ifa->ifa_addr == NULL)
continue; /* just for safety */
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
if (IN6_ARE_ADDR_EQUAL(addr, IFA_IN6(ifa)))
break;
if (!IN6_ARE_ADDR_EQUAL(addr, IFA_IN6(ifa)))
continue;
best_ifa = bestifa(best_ifa, ifa);
}
return (struct in6_ifaddr *)ifa;
return (struct in6_ifaddr *)best_ifa;
}
static struct in6_ifaddr *
bestia(struct in6_ifaddr *best_ia, struct in6_ifaddr *ia)
{
if (best_ia == NULL ||
best_ia->ia_ifa.ifa_preference < ia->ia_ifa.ifa_preference)
return ia;
return best_ia;
}
/*
@ -1847,7 +1862,7 @@ struct in6_ifaddr *
in6ifa_ifplocaladdr(const struct ifnet *ifp, const struct in6_addr *addr)
{
struct ifaddr *ifa;
struct in6_ifaddr *ia;
struct in6_ifaddr *best_ia = NULL, *ia;
IFADDR_FOREACH(ifa, ifp) {
if (ifa->ifa_addr == NULL)
@ -1855,13 +1870,14 @@ in6ifa_ifplocaladdr(const struct ifnet *ifp, const struct in6_addr *addr)
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
ia = (struct in6_ifaddr *)ifa;
if (IN6_ARE_MASKED_ADDR_EQUAL(addr,
if (!IN6_ARE_MASKED_ADDR_EQUAL(addr,
&ia->ia_addr.sin6_addr,
&ia->ia_prefixmask.sin6_addr))
return ia;
continue;
best_ia = bestia(best_ia, ia);
}
return NULL;
return best_ia;
}
/*
@ -2043,7 +2059,7 @@ in6_ifawithifp(struct ifnet *ifp, struct in6_addr *dst)
{
int dst_scope = in6_addrscope(dst), blen = -1, tlen;
struct ifaddr *ifa;
struct in6_ifaddr *besta = 0;
struct in6_ifaddr *best_ia = NULL, *ia;
struct in6_ifaddr *dep[2]; /* last-resort: deprecated */
dep[0] = dep[1] = NULL;
@ -2057,54 +2073,60 @@ in6_ifawithifp(struct ifnet *ifp, struct in6_addr *dst)
IFADDR_FOREACH(ifa, ifp) {
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST)
ia = (struct in6_ifaddr *)ifa;
if (ia->ia6_flags & IN6_IFF_ANYCAST)
continue; /* XXX: is there any case to allow anycast? */
if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_NOTREADY)
if (ia->ia6_flags & IN6_IFF_NOTREADY)
continue; /* don't use this interface */
if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DETACHED)
if (ia->ia6_flags & IN6_IFF_DETACHED)
continue;
if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DEPRECATED) {
if (ia->ia6_flags & IN6_IFF_DEPRECATED) {
if (ip6_use_deprecated)
dep[0] = (struct in6_ifaddr *)ifa;
dep[0] = ia;
continue;
}
if (dst_scope == in6_addrscope(IFA_IN6(ifa))) {
/*
* call in6_matchlen() as few as possible
*/
if (besta) {
if (blen == -1)
blen = in6_matchlen(&besta->ia_addr.sin6_addr, dst);
tlen = in6_matchlen(IFA_IN6(ifa), dst);
if (tlen > blen) {
blen = tlen;
besta = (struct in6_ifaddr *)ifa;
}
} else
besta = (struct in6_ifaddr *)ifa;
if (dst_scope != in6_addrscope(IFA_IN6(ifa)))
continue;
/*
* call in6_matchlen() as few as possible
*/
if (best_ia == NULL) {
best_ia = ia;
continue;
}
if (blen == -1)
blen = in6_matchlen(&best_ia->ia_addr.sin6_addr, dst);
tlen = in6_matchlen(IFA_IN6(ifa), dst);
if (tlen > blen) {
blen = tlen;
best_ia = ia;
} else if (tlen == blen)
best_ia = bestia(best_ia, ia);
}
if (besta)
return besta;
if (best_ia != NULL)
return best_ia;
IFADDR_FOREACH(ifa, ifp) {
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST)
ia = (struct in6_ifaddr *)ifa;
if (ia->ia6_flags & IN6_IFF_ANYCAST)
continue; /* XXX: is there any case to allow anycast? */
if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_NOTREADY)
if (ia->ia6_flags & IN6_IFF_NOTREADY)
continue; /* don't use this interface */
if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DETACHED)
if (ia->ia6_flags & IN6_IFF_DETACHED)
continue;
if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DEPRECATED) {
if (ia->ia6_flags & IN6_IFF_DEPRECATED) {
if (ip6_use_deprecated)
dep[1] = (struct in6_ifaddr *)ifa;
continue;
}
return (struct in6_ifaddr *)ifa;
best_ia = bestia(best_ia, ia);
}
if (best_ia != NULL)
return best_ia;
/* use the last-resort values, that are, deprecated addresses */
if (dep[0])