Module Name: src Committed By: dyoung Date: Thu Apr 22 20:05:15 UTC 2010
Modified Files: src/sys/netinet6: in6.c Log Message: 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. To generate a diff of this commit: cvs rdiff -u -r1.155 -r1.156 src/sys/netinet6/in6.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/netinet6/in6.c diff -u src/sys/netinet6/in6.c:1.155 src/sys/netinet6/in6.c:1.156 --- src/sys/netinet6/in6.c:1.155 Wed Apr 7 22:59:15 2010 +++ src/sys/netinet6/in6.c Thu Apr 22 20:05:15 2010 @@ -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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ { 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 @@ 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])