aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Marks <pmarks@google.com>2013-09-25 18:12:55 -0400
committerDavid S. Miller <davem@davemloft.net>2013-09-30 15:06:19 -0400
commitc9d55d5bff05084b5829f751aebd03d0c8f632f5 (patch)
tree54aa6121beb9b2ef2301c1e8d8cbb402902be10b
parentd4a71b155c12d0d429c6b69d94076d6d57e2a7a7 (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>
-rw-r--r--net/ipv6/addrconf.c52
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) {