diff options
author | Florian Obser <florian@cvs.openbsd.org> | 2020-08-07 18:09:17 +0000 |
---|---|---|
committer | Florian Obser <florian@cvs.openbsd.org> | 2020-08-07 18:09:17 +0000 |
commit | 94b260d1be63a4c20225d69f039dad7a8ca7b5b1 (patch) | |
tree | 8181a0aa899cfa316862d0b3e72bbacc00647fa0 /sys/netinet6 | |
parent | f2cb2908e9c3ae4f026113c83826fcd6f0f4ad90 (diff) |
The IPv6 source address selection rewrite had one (known) difference
to the previous behavior: In case of a tie the new implementation
would keep the current best address while the old implementation
replaced the best address.
Since IPv6 addresses are stored in a TAILQ this meant that the rewrite
would use the "oldest" address while the previous behavior was to use
the "newest".
RFC 6724 section 5 has no opinion which one is better and leaves the
tie break up to implementers.
naddy found out the hard way that this breaks his IPv6 connectivity in
case of flash renumbering events when the link on his cpe flaps and a
new prefix is used since we would always pick an old address.
While we could pick the newest address in a tie break this feels too
much like an implementation detail, a solution much more in the spirit
of IPv6 is to pick the address with the highest preferred lifetime (or
valid lifetime in case of another tie).
very patient testing naddy@
Diffstat (limited to 'sys/netinet6')
-rw-r--r-- | sys/netinet6/in6.c | 35 |
1 files changed, 33 insertions, 2 deletions
diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c index cbc7ccc9bcd..f26eed31bdd 100644 --- a/sys/netinet6/in6.c +++ b/sys/netinet6/in6.c @@ -1,4 +1,4 @@ -/* $OpenBSD: in6.c,v 1.241 2020/08/04 17:05:52 anton Exp $ */ +/* $OpenBSD: in6.c,v 1.242 2020/08/07 18:09:16 florian Exp $ */ /* $KAME: in6.c,v 1.372 2004/06/14 08:14:21 itojun Exp $ */ /* @@ -1498,7 +1498,38 @@ in6_ifawithscope(struct ifnet *oifp, struct in6_addr *dst, u_int rdomain) continue; #endif goto replace; - } + } else if (tlen < blen) + continue; + + /* + * If the eight rules fail to choose a single address, + * the tiebreaker is implementation-specific. + */ + + /* Prefer address with highest pltime. */ + if (ia6_best->ia6_updatetime + + ia6_best->ia6_lifetime.ia6t_pltime < + ifatoia6(ifa)->ia6_updatetime + + ifatoia6(ifa)->ia6_lifetime.ia6t_pltime) + goto replace; + else if (ia6_best->ia6_updatetime + + ia6_best->ia6_lifetime.ia6t_pltime > + ifatoia6(ifa)->ia6_updatetime + + ifatoia6(ifa)->ia6_lifetime.ia6t_pltime) + continue; + + /* Prefer address with highest vltime. */ + if (ia6_best->ia6_updatetime + + ia6_best->ia6_lifetime.ia6t_vltime < + ifatoia6(ifa)->ia6_updatetime + + ifatoia6(ifa)->ia6_lifetime.ia6t_vltime) + goto replace; + else if (ia6_best->ia6_updatetime + + ia6_best->ia6_lifetime.ia6t_vltime > + ifatoia6(ifa)->ia6_updatetime + + ifatoia6(ifa)->ia6_lifetime.ia6t_vltime) + continue; + continue; replace: ia6_best = ifatoia6(ifa); |