This should be easier to read and follows the 8 rules in Section 5 of
RFC 6724.

I tried to hit all (implemented) rules of RFC 6724 and found only one
behavioural difference, if there are two global unicast addresses
configured on an interface, like this:

        inet6 fe80::fce1:baff:fed4:35e3%vether1 prefixlen 64 scopeid 0xb3
        inet6 2001:db8:0:1::3 prefixlen 64
        inet6 2001:db8:0:1::5 prefixlen 64

and the default gateway is at 2001:db8:0:1::1 the old code would pick
    2001:db8:0:1::5
as source address to reach an off-link global unicast address while
the new code will pick
    2001:db8:0:1::3
which I believe is better anyway.

I think the old behaviour comes from this:
                if (oifp == ifp) /* (a) */
                        goto replace;
but I have not verified that.

To review it's probably easiest to apply and read the result.

Tests, comments, OKs?

diff --git netinet6/in6.c netinet6/in6.c
index ca8c78c7b9f..b1e62f4d850 100644
--- netinet6/in6.c
+++ netinet6/in6.c
@@ -1337,11 +1337,7 @@ in6_ifawithscope(struct ifnet *oifp, struct in6_addr 
*dst, u_int rdomain)
                return (NULL);
        }
 
-       /*
-        * We search for all addresses on all interfaces from the beginning.
-        * Comparing an interface with the outgoing interface will be done
-        * only at the final stage of tiebreaking.
-        */
+       /* We search for all addresses on all interfaces from the beginning. */
        TAILQ_FOREACH(ifp, &ifnet, if_list) {
                if (ifp->if_rdomain != rdomain)
                        continue;
@@ -1363,36 +1359,13 @@ in6_ifawithscope(struct ifnet *oifp, struct in6_addr 
*dst, u_int rdomain)
                        continue;
 
                TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
-                       int tlen = -1, dscopecmp, bscopecmp, matchcmp;
+                       int tlen = -1;
 
                        if (ifa->ifa_addr->sa_family != AF_INET6)
                                continue;
 
                        src_scope = in6_addrscope(IFA_IN6(ifa));
 
-#ifdef ADDRSELECT_DEBUG                /* should be removed after 
stabilization */
-               {
-                       char adst[INET6_ADDRSTRLEN], asrc[INET6_ADDRSTRLEN];
-                       char bestaddr[INET6_ADDRSTRLEN];
-
-
-                       dscopecmp = IN6_ARE_SCOPE_CMP(src_scope, dst_scope);
-                       printf("%s: dst=%s bestaddr=%s, "
-                           "newaddr=%s, scope=%x, dcmp=%d, bcmp=%d, "
-                           "matchlen=%d, flgs=%x\n", __func__,
-                           inet_ntop(AF_INET6, dst, adst, sizeof(adst)),
-                           (ia6_best == NULL) ? "none" :
-                           inet_ntop(AF_INET6, &ia6_best->ia_addr.sin6_addr,
-                           bestaddr, sizeof(bestaddr)),
-                           inet_ntop(AF_INET6, IFA_IN6(ifa),
-                           asrc, sizeof(asrc)),
-                           src_scope, dscopecmp, ia6_best ?
-                           IN6_ARE_SCOPE_CMP(src_scope, best_scope) : -1,
-                           in6_matchlen(IFA_IN6(ifa), dst),
-                           ifatoia6(ifa)->ia6_flags);
-               }
-#endif
-
                        /*
                         * Don't use an address before completing DAD
                         * nor a duplicated address.
@@ -1401,7 +1374,16 @@ in6_ifawithscope(struct ifnet *oifp, struct in6_addr 
*dst, u_int rdomain)
                            (IN6_IFF_TENTATIVE|IN6_IFF_DUPLICATED))
                                continue;
 
-                       /* XXX: is there any case to allow anycasts? */
+                       /*
+                        * RFC 6724 allows anycast addresses as source address
+                        * because the restriction was removed in RFC 4291.
+                        * However RFC 4443 states that ICMPv6 responses
+                        * MUST use a unicast source address.
+                        *
+                        * XXX Skip anycast addresses for now since
+                        * icmp6_reflect() uses this function for source
+                        * address selection.
+                        */
                        if (ifatoia6(ifa)->ia6_flags & IN6_IFF_ANYCAST)
                                continue;
 
@@ -1421,26 +1403,26 @@ in6_ifawithscope(struct ifnet *oifp, struct in6_addr 
*dst, u_int rdomain)
                         */
 
                        /*
-                        * If ia6_best has a smaller scope than dst and
-                        * the current address has a larger one than
-                        * (or equal to) dst, always replace ia6_best.
-                        * Also, if the current address has a smaller scope
-                        * than dst, ignore it unless ia6_best also has a
-                        * smaller scope.
+                        * Rule 2: Prefer appropriate scope.
+                        * Find the address with the smallest scope that is
+                        * bigger (or equal) to the scope of the destination
+                        * address.
+                        * Accept an address with smaller scope than the
+                        * destination if non exists with bigger scope.
                         */
-                       if (IN6_ARE_SCOPE_CMP(best_scope, dst_scope) < 0 &&
-                           IN6_ARE_SCOPE_CMP(src_scope, dst_scope) >= 0)
-                               goto replace;
-                       if (IN6_ARE_SCOPE_CMP(src_scope, dst_scope) < 0 &&
-                           IN6_ARE_SCOPE_CMP(best_scope, dst_scope) >= 0)
-                               continue;
+                       if (best_scope < src_scope) {
+                               if (best_scope < dst_scope)
+                                       goto replace;
+                               else
+                                       continue;
+                       } else if (src_scope < best_scope) {
+                               if (src_scope < dst_scope)
+                                       continue;
+                               else
+                                       goto replace;
+                       }
 
-                       /*
-                        * A deprecated address SHOULD NOT be used in new
-                        * communications if an alternate (non-deprecated)
-                        * address is available and has sufficient scope.
-                        * RFC 2462, Section 5.5.4.
-                        */
+                       /* Rule 3: Avoid deprecated addresses. */
                        if (ifatoia6(ifa)->ia6_flags & IN6_IFF_DEPRECATED) {
                                /*
                                 * Ignore any deprecated addresses if
@@ -1456,120 +1438,43 @@ in6_ifawithscope(struct ifnet *oifp, struct in6_addr 
*dst, u_int rdomain)
                                if ((ia6_best->ia6_flags & IN6_IFF_DEPRECATED)
                                    == 0)
                                        continue;
-                       }
+                       } else if ((ia6_best->ia6_flags & IN6_IFF_DEPRECATED))
+                               goto replace;
 
                        /*
-                        * A non-deprecated address is always preferred
-                        * to a deprecated one regardless of scopes and
-                        * address matching.
+                        * Rule 4: Prefer home addresses.
+                        * We do not support home addresses.
                         */
-                       if ((ia6_best->ia6_flags & IN6_IFF_DEPRECATED) &&
-                           (ifatoia6(ifa)->ia6_flags &
-                            IN6_IFF_DEPRECATED) == 0)
-                               goto replace;
 
-                       /* RFC 3484 5. Rule 5: Prefer outgoing interface */
+                       /* Rule 5: Prefer outgoing interface */
                        if (ia6_best->ia_ifp == oifp && ifp != oifp)
                                continue;
                        if (ia6_best->ia_ifp != oifp && ifp == oifp)
                                goto replace;
 
                        /*
-                        * At this point, we have two cases:
-                        * 1. we are looking at a non-deprecated address,
-                        *    and ia6_best is also non-deprecated.
-                        * 2. we are looking at a deprecated address,
-                        *    and ia6_best is also deprecated.
-                        * Also, we do not have to consider a case where
-                        * the scope of if_best is larger(smaller) than dst and
-                        * the scope of the current address is smaller(larger)
-                        * than dst. Such a case has already been covered.
-                        * Tiebreaking is done according to the following
-                        * items:
-                        * - the scope comparison between the address and
-                        *   dst (dscopecmp)
-                        * - the scope comparison between the address and
-                        *   ia6_best (bscopecmp)
-                        * - if the address match dst longer than ia6_best
-                        *   (matchcmp)
-                        * - if the address is on the outgoing I/F (outI/F)
-                        *
-                        * Roughly speaking, the selection policy is
-                        * - the most important item is scope. The same scope
-                        *   is best. Then search for a larger scope.
-                        *   Smaller scopes are the last resort.
-                        * - A deprecated address is chosen only when we have
-                        *   no address that has an enough scope, but is
-                        *   prefered to any addresses of smaller scopes.
-                        * - Longest address match against dst is considered
-                        *   only for addresses that has the same scope of dst.
-                        * - If there is no other reasons to choose one,
-                        *   addresses on the outgoing I/F are preferred.
-                        *
-                        * The precise decision table is as follows:
-                        * dscopecmp bscopecmp matchcmp outI/F | replace?
-                        *    !equal     equal      N/A    Yes |      Yes (1)
-                        *    !equal     equal      N/A     No |       No (2)
-                        *    larger    larger      N/A    N/A |       No (3)
-                        *    larger   smaller      N/A    N/A |      Yes (4)
-                        *   smaller    larger      N/A    N/A |      Yes (5)
-                        *   smaller   smaller      N/A    N/A |       No (6)
-                        *     equal   smaller      N/A    N/A |      Yes (7)
-                        *     equal    larger       (already done)
-                        *     equal     equal   larger    N/A |      Yes (8)
-                        *     equal     equal  smaller    N/A |       No (9)
-                        *     equal     equal    equal    Yes |      Yes (a)
-                        *     equal     equal    equal     No |       No (b)
+                        * Rule 5.5: Prefer addresses in a prefix advertised
+                        * by the next-hop.
+                        * XXX We do not track this information.
                         */
-                       dscopecmp = IN6_ARE_SCOPE_CMP(src_scope, dst_scope);
-                       bscopecmp = IN6_ARE_SCOPE_CMP(src_scope, best_scope);
-
-                       if (dscopecmp && bscopecmp == 0) {
-                               if (oifp == ifp) /* (1) */
-                                       goto replace;
-                               continue; /* (2) */
-                       }
-                       if (dscopecmp > 0) {
-                               if (bscopecmp > 0) /* (3) */
-                                       continue;
-                               goto replace; /* (4) */
-                       }
-                       if (dscopecmp < 0) {
-                               if (bscopecmp > 0) /* (5) */
-                                       goto replace;
-                               continue; /* (6) */
-                       }
-
-                       /* now dscopecmp must be 0 */
-                       if (bscopecmp < 0)
-                               goto replace; /* (7) */
 
                        /*
-                        * At last both dscopecmp and bscopecmp must be 0.
-                        * We need address matching against dst for
-                        * tiebreaking.
-                        * Privacy addresses are preferred over public
-                        * addresses (RFC3484 requires a config knob for
-                        * this which we don't provide).
+                        * Rule 6: Prefer matching label.
+                        * XXX We do not implement policy tables.
                         */
-                       if (oifp == ifp) {
-                               /* Do not replace temporary autoconf addresses
-                                * with non-temporary addresses. */
-                               if ((ia6_best->ia6_flags & IN6_IFF_PRIVACY) &&
-                                   !(ifatoia6(ifa)->ia6_flags &
-                                   IN6_IFF_PRIVACY))
-                                       continue;
 
-                               /* Replace non-temporary autoconf addresses
-                                * with temporary addresses. */
-                               if (!(ia6_best->ia6_flags & IN6_IFF_PRIVACY) &&
-                                   (ifatoia6(ifa)->ia6_flags &
-                                   IN6_IFF_PRIVACY))
-                                       goto replace;
-                       }
+
+                       /* Rule 7: Prefer temporary addresses. */
+                       if ((ia6_best->ia6_flags & IN6_IFF_PRIVACY) &&
+                           !(ifatoia6(ifa)->ia6_flags & IN6_IFF_PRIVACY))
+                               continue;
+                       if (!(ia6_best->ia6_flags & IN6_IFF_PRIVACY) &&
+                           (ifatoia6(ifa)->ia6_flags & IN6_IFF_PRIVACY))
+                               goto replace;
+
+                       /* Rule 8: Use longest matching prefix. */
                        tlen = in6_matchlen(IFA_IN6(ifa), dst);
-                       matchcmp = tlen - blen;
-                       if (matchcmp > 0) { /* (8) */
+                       if (tlen > blen) {
 #if NCARP > 0
                                /*
                                 * Don't let carp interfaces win a tie against
@@ -1589,12 +1494,7 @@ in6_ifawithscope(struct ifnet *oifp, struct in6_addr 
*dst, u_int rdomain)
 #endif
                                goto replace;
                        }
-                       if (matchcmp < 0) /* (9) */
-                               continue;
-                       if (oifp == ifp) /* (a) */
-                               goto replace;
-                       continue; /* (b) */
-
+                       continue;
                  replace:
                        ia6_best = ifatoia6(ifa);
                        blen = tlen >= 0 ? tlen :


-- 
I'm not entirely sure you are real.

Reply via email to