Anyone? Tests on multi homed machines would be particularly interesting.
Thanks,
Florian
On Thu, Jun 04, 2020 at 07:33:32PM +0200, Florian Obser wrote:
> 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.
>
--
I'm not entirely sure you are real.