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 $ */ /* $KAME: in6.c,v 1.198 2001/07/18 09:12:38 itojun Exp $ */
/* /*
@ -62,7 +62,7 @@
*/ */
#include <sys/cdefs.h> #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_inet.h"
#include "opt_pfil_hooks.h" #include "opt_pfil_hooks.h"
@ -1792,6 +1792,14 @@ in6_ifinit(struct ifnet *ifp, struct in6_ifaddr *ia,
return error; 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. * 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; continue;
if ((((struct in6_ifaddr *)ifa)->ia6_flags & ignoreflags) != 0) if ((((struct in6_ifaddr *)ifa)->ia6_flags & ignoreflags) != 0)
continue; continue;
if (best_ifa == NULL) best_ifa = bestifa(best_ifa, ifa);
best_ifa = ifa;
else if (best_ifa->ifa_preference < ifa->ifa_preference)
best_ifa = ifa;
} }
return (struct in6_ifaddr *)best_ifa; return (struct in6_ifaddr *)best_ifa;
@ -1825,18 +1830,28 @@ in6ifa_ifpforlinklocal(const struct ifnet *ifp, const int ignoreflags)
struct in6_ifaddr * struct in6_ifaddr *
in6ifa_ifpwithaddr(const struct ifnet *ifp, const struct in6_addr *addr) in6ifa_ifpwithaddr(const struct ifnet *ifp, const struct in6_addr *addr)
{ {
struct ifaddr *ifa; struct ifaddr *best_ifa = NULL, *ifa;
IFADDR_FOREACH(ifa, ifp) { IFADDR_FOREACH(ifa, ifp) {
if (ifa->ifa_addr == NULL) if (ifa->ifa_addr == NULL)
continue; /* just for safety */ continue; /* just for safety */
if (ifa->ifa_addr->sa_family != AF_INET6) if (ifa->ifa_addr->sa_family != AF_INET6)
continue; continue;
if (IN6_ARE_ADDR_EQUAL(addr, IFA_IN6(ifa))) if (!IN6_ARE_ADDR_EQUAL(addr, IFA_IN6(ifa)))
break; 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) in6ifa_ifplocaladdr(const struct ifnet *ifp, const struct in6_addr *addr)
{ {
struct ifaddr *ifa; struct ifaddr *ifa;
struct in6_ifaddr *ia; struct in6_ifaddr *best_ia = NULL, *ia;
IFADDR_FOREACH(ifa, ifp) { IFADDR_FOREACH(ifa, ifp) {
if (ifa->ifa_addr == NULL) 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) if (ifa->ifa_addr->sa_family != AF_INET6)
continue; continue;
ia = (struct in6_ifaddr *)ifa; 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_addr.sin6_addr,
&ia->ia_prefixmask.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; int dst_scope = in6_addrscope(dst), blen = -1, tlen;
struct ifaddr *ifa; struct ifaddr *ifa;
struct in6_ifaddr *besta = 0; struct in6_ifaddr *best_ia = NULL, *ia;
struct in6_ifaddr *dep[2]; /* last-resort: deprecated */ struct in6_ifaddr *dep[2]; /* last-resort: deprecated */
dep[0] = dep[1] = NULL; dep[0] = dep[1] = NULL;
@ -2057,54 +2073,60 @@ in6_ifawithifp(struct ifnet *ifp, struct in6_addr *dst)
IFADDR_FOREACH(ifa, ifp) { IFADDR_FOREACH(ifa, ifp) {
if (ifa->ifa_addr->sa_family != AF_INET6) if (ifa->ifa_addr->sa_family != AF_INET6)
continue; 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? */ 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 */ continue; /* don't use this interface */
if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DETACHED) if (ia->ia6_flags & IN6_IFF_DETACHED)
continue; continue;
if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DEPRECATED) { if (ia->ia6_flags & IN6_IFF_DEPRECATED) {
if (ip6_use_deprecated) if (ip6_use_deprecated)
dep[0] = (struct in6_ifaddr *)ifa; dep[0] = ia;
continue; continue;
} }
if (dst_scope == in6_addrscope(IFA_IN6(ifa))) { if (dst_scope != in6_addrscope(IFA_IN6(ifa)))
/* continue;
* call in6_matchlen() as few as possible /*
*/ * call in6_matchlen() as few as possible
if (besta) { */
if (blen == -1) if (best_ia == NULL) {
blen = in6_matchlen(&besta->ia_addr.sin6_addr, dst); best_ia = ia;
tlen = in6_matchlen(IFA_IN6(ifa), dst); continue;
if (tlen > blen) {
blen = tlen;
besta = (struct in6_ifaddr *)ifa;
}
} else
besta = (struct in6_ifaddr *)ifa;
} }
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) if (best_ia != NULL)
return besta; return best_ia;
IFADDR_FOREACH(ifa, ifp) { IFADDR_FOREACH(ifa, ifp) {
if (ifa->ifa_addr->sa_family != AF_INET6) if (ifa->ifa_addr->sa_family != AF_INET6)
continue; 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? */ 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 */ continue; /* don't use this interface */
if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DETACHED) if (ia->ia6_flags & IN6_IFF_DETACHED)
continue; continue;
if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DEPRECATED) { if (ia->ia6_flags & IN6_IFF_DEPRECATED) {
if (ip6_use_deprecated) if (ip6_use_deprecated)
dep[1] = (struct in6_ifaddr *)ifa; dep[1] = (struct in6_ifaddr *)ifa;
continue; 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 */ /* use the last-resort values, that are, deprecated addresses */
if (dep[0]) if (dep[0])