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.