diff options
author | Paul Marks <pmarks@google.com> | 2013-09-25 18:12:55 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-09-30 15:06:19 -0400 |
commit | c9d55d5bff05084b5829f751aebd03d0c8f632f5 (patch) | |
tree | 54aa6121beb9b2ef2301c1e8d8cbb402902be10b /net/ipv6/addrconf.c | |
parent | d4a71b155c12d0d429c6b69d94076d6d57e2a7a7 (diff) |
ipv6: Fix preferred_lft not updating in some cases
Consider the scenario where an IPv6 router is advertising a fixed
preferred_lft of 1800 seconds, while the valid_lft begins at 3600
seconds and counts down in realtime.
A client should reset its preferred_lft to 1800 every time the RA is
received, but a bug is causing Linux to ignore the update.
The core problem is here:
if (prefered_lft != ifp->prefered_lft) {
Note that ifp->prefered_lft is an offset, so it doesn't decrease over
time. Thus, the comparison is always (1800 != 1800), which fails to
trigger an update.
The most direct solution would be to compute a "stored_prefered_lft",
and use that value in the comparison. But I think that trying to filter
out unnecessary updates here is a premature optimization. In order for
the filter to apply, both of these would need to hold:
- The advertised valid_lft and preferred_lft are both declining in
real time.
- No clock skew exists between the router & client.
So in this patch, I've set "update_lft = 1" unconditionally, which
allows the surrounding code to be greatly simplified.
Signed-off-by: Paul Marks <pmarks@google.com>
Acked-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6/addrconf.c')
-rw-r--r-- | net/ipv6/addrconf.c | 52 |
1 files changed, 15 insertions, 37 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index a0c3abe72461..cd3fb301da38 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c | |||
@@ -2220,43 +2220,21 @@ ok: | |||
2220 | else | 2220 | else |
2221 | stored_lft = 0; | 2221 | stored_lft = 0; |
2222 | if (!update_lft && !create && stored_lft) { | 2222 | if (!update_lft && !create && stored_lft) { |
2223 | if (valid_lft > MIN_VALID_LIFETIME || | 2223 | const u32 minimum_lft = min( |
2224 | valid_lft > stored_lft) | 2224 | stored_lft, (u32)MIN_VALID_LIFETIME); |
2225 | update_lft = 1; | 2225 | valid_lft = max(valid_lft, minimum_lft); |
2226 | else if (stored_lft <= MIN_VALID_LIFETIME) { | 2226 | |
2227 | /* valid_lft <= stored_lft is always true */ | 2227 | /* RFC4862 Section 5.5.3e: |
2228 | /* | 2228 | * "Note that the preferred lifetime of the |
2229 | * RFC 4862 Section 5.5.3e: | 2229 | * corresponding address is always reset to |
2230 | * "Note that the preferred lifetime of | 2230 | * the Preferred Lifetime in the received |
2231 | * the corresponding address is always | 2231 | * Prefix Information option, regardless of |
2232 | * reset to the Preferred Lifetime in | 2232 | * whether the valid lifetime is also reset or |
2233 | * the received Prefix Information | 2233 | * ignored." |
2234 | * option, regardless of whether the | 2234 | * |
2235 | * valid lifetime is also reset or | 2235 | * So we should always update prefered_lft here. |
2236 | * ignored." | 2236 | */ |
2237 | * | 2237 | update_lft = 1; |
2238 | * So if the preferred lifetime in | ||
2239 | * this advertisement is different | ||
2240 | * than what we have stored, but the | ||
2241 | * valid lifetime is invalid, just | ||
2242 | * reset prefered_lft. | ||
2243 | * | ||
2244 | * We must set the valid lifetime | ||
2245 | * to the stored lifetime since we'll | ||
2246 | * be updating the timestamp below, | ||
2247 | * else we'll set it back to the | ||
2248 | * minimum. | ||
2249 | */ | ||
2250 | if (prefered_lft != ifp->prefered_lft) { | ||
2251 | valid_lft = stored_lft; | ||
2252 | update_lft = 1; | ||
2253 | } | ||
2254 | } else { | ||
2255 | valid_lft = MIN_VALID_LIFETIME; | ||
2256 | if (valid_lft < prefered_lft) | ||
2257 | prefered_lft = valid_lft; | ||
2258 | update_lft = 1; | ||
2259 | } | ||
2260 | } | 2238 | } |
2261 | 2239 | ||
2262 | if (update_lft) { | 2240 | if (update_lft) { |