aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4
diff options
context:
space:
mode:
authorTimo Teräs <timo.teras@iki.fi>2013-05-27 16:46:31 -0400
committerDavid S. Miller <davem@davemloft.net>2013-06-03 03:07:42 -0400
commit387aa65a89434abe3128d36d1a6fc3842c94905d (patch)
tree65ca8ab0c6224eb39941455b1eb0abd2fa410035 /net/ipv4
parent829a5071ccf040b78bd93286a218580c66833b00 (diff)
ipv4: properly refresh rtable entries on pmtu/redirect events
This reverts commit 05ab86c5 (xfrm4: Invalidate all ipv4 routes on IPsec pmtu events). Flushing all cached entries is not needed. Instead, invalidate only the related next hop dsts to recheck for the added next hop exception where needed. This also fixes a subtle race due to bumping generation id's before updating the pmtu. Cc: Steffen Klassert <steffen.klassert@secunet.com> Signed-off-by: Timo Teräs <timo.teras@iki.fi> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4')
-rw-r--r--net/ipv4/ah4.c7
-rw-r--r--net/ipv4/esp4.c7
-rw-r--r--net/ipv4/ipcomp.c7
-rw-r--r--net/ipv4/route.c63
4 files changed, 43 insertions, 41 deletions
diff --git a/net/ipv4/ah4.c b/net/ipv4/ah4.c
index 2e7f1948216f..717902669d2f 100644
--- a/net/ipv4/ah4.c
+++ b/net/ipv4/ah4.c
@@ -419,12 +419,9 @@ static void ah4_err(struct sk_buff *skb, u32 info)
419 if (!x) 419 if (!x)
420 return; 420 return;
421 421
422 if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH) { 422 if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH)
423 atomic_inc(&flow_cache_genid);
424 rt_genid_bump(net);
425
426 ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_AH, 0); 423 ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_AH, 0);
427 } else 424 else
428 ipv4_redirect(skb, net, 0, 0, IPPROTO_AH, 0); 425 ipv4_redirect(skb, net, 0, 0, IPPROTO_AH, 0);
429 xfrm_state_put(x); 426 xfrm_state_put(x);
430} 427}
diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c
index 4cfe34d4cc96..ab3d814bc80a 100644
--- a/net/ipv4/esp4.c
+++ b/net/ipv4/esp4.c
@@ -502,12 +502,9 @@ static void esp4_err(struct sk_buff *skb, u32 info)
502 if (!x) 502 if (!x)
503 return; 503 return;
504 504
505 if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH) { 505 if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH)
506 atomic_inc(&flow_cache_genid);
507 rt_genid_bump(net);
508
509 ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_ESP, 0); 506 ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_ESP, 0);
510 } else 507 else
511 ipv4_redirect(skb, net, 0, 0, IPPROTO_ESP, 0); 508 ipv4_redirect(skb, net, 0, 0, IPPROTO_ESP, 0);
512 xfrm_state_put(x); 509 xfrm_state_put(x);
513} 510}
diff --git a/net/ipv4/ipcomp.c b/net/ipv4/ipcomp.c
index 59cb8c769056..826be4cb482a 100644
--- a/net/ipv4/ipcomp.c
+++ b/net/ipv4/ipcomp.c
@@ -47,12 +47,9 @@ static void ipcomp4_err(struct sk_buff *skb, u32 info)
47 if (!x) 47 if (!x)
48 return; 48 return;
49 49
50 if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH) { 50 if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH)
51 atomic_inc(&flow_cache_genid);
52 rt_genid_bump(net);
53
54 ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_COMP, 0); 51 ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_COMP, 0);
55 } else 52 else
56 ipv4_redirect(skb, net, 0, 0, IPPROTO_COMP, 0); 53 ipv4_redirect(skb, net, 0, 0, IPPROTO_COMP, 0);
57 xfrm_state_put(x); 54 xfrm_state_put(x);
58} 55}
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 550781a17b34..561a37833d86 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -594,11 +594,25 @@ static inline u32 fnhe_hashfun(__be32 daddr)
594 return hval & (FNHE_HASH_SIZE - 1); 594 return hval & (FNHE_HASH_SIZE - 1);
595} 595}
596 596
597static void fill_route_from_fnhe(struct rtable *rt, struct fib_nh_exception *fnhe)
598{
599 rt->rt_pmtu = fnhe->fnhe_pmtu;
600 rt->dst.expires = fnhe->fnhe_expires;
601
602 if (fnhe->fnhe_gw) {
603 rt->rt_flags |= RTCF_REDIRECTED;
604 rt->rt_gateway = fnhe->fnhe_gw;
605 rt->rt_uses_gateway = 1;
606 }
607}
608
597static void update_or_create_fnhe(struct fib_nh *nh, __be32 daddr, __be32 gw, 609static void update_or_create_fnhe(struct fib_nh *nh, __be32 daddr, __be32 gw,
598 u32 pmtu, unsigned long expires) 610 u32 pmtu, unsigned long expires)
599{ 611{
600 struct fnhe_hash_bucket *hash; 612 struct fnhe_hash_bucket *hash;
601 struct fib_nh_exception *fnhe; 613 struct fib_nh_exception *fnhe;
614 struct rtable *rt;
615 unsigned int i;
602 int depth; 616 int depth;
603 u32 hval = fnhe_hashfun(daddr); 617 u32 hval = fnhe_hashfun(daddr);
604 618
@@ -627,8 +641,12 @@ static void update_or_create_fnhe(struct fib_nh *nh, __be32 daddr, __be32 gw,
627 fnhe->fnhe_gw = gw; 641 fnhe->fnhe_gw = gw;
628 if (pmtu) { 642 if (pmtu) {
629 fnhe->fnhe_pmtu = pmtu; 643 fnhe->fnhe_pmtu = pmtu;
630 fnhe->fnhe_expires = expires; 644 fnhe->fnhe_expires = max(1UL, expires);
631 } 645 }
646 /* Update all cached dsts too */
647 rt = rcu_dereference(fnhe->fnhe_rth);
648 if (rt)
649 fill_route_from_fnhe(rt, fnhe);
632 } else { 650 } else {
633 if (depth > FNHE_RECLAIM_DEPTH) 651 if (depth > FNHE_RECLAIM_DEPTH)
634 fnhe = fnhe_oldest(hash); 652 fnhe = fnhe_oldest(hash);
@@ -644,6 +662,18 @@ static void update_or_create_fnhe(struct fib_nh *nh, __be32 daddr, __be32 gw,
644 fnhe->fnhe_gw = gw; 662 fnhe->fnhe_gw = gw;
645 fnhe->fnhe_pmtu = pmtu; 663 fnhe->fnhe_pmtu = pmtu;
646 fnhe->fnhe_expires = expires; 664 fnhe->fnhe_expires = expires;
665
666 /* Exception created; mark the cached routes for the nexthop
667 * stale, so anyone caching it rechecks if this exception
668 * applies to them.
669 */
670 for_each_possible_cpu(i) {
671 struct rtable __rcu **prt;
672 prt = per_cpu_ptr(nh->nh_pcpu_rth_output, i);
673 rt = rcu_dereference(*prt);
674 if (rt)
675 rt->dst.obsolete = DST_OBSOLETE_KILL;
676 }
647 } 677 }
648 678
649 fnhe->fnhe_stamp = jiffies; 679 fnhe->fnhe_stamp = jiffies;
@@ -917,13 +947,6 @@ static void __ip_rt_update_pmtu(struct rtable *rt, struct flowi4 *fl4, u32 mtu)
917 if (mtu < ip_rt_min_pmtu) 947 if (mtu < ip_rt_min_pmtu)
918 mtu = ip_rt_min_pmtu; 948 mtu = ip_rt_min_pmtu;
919 949
920 if (!rt->rt_pmtu) {
921 dst->obsolete = DST_OBSOLETE_KILL;
922 } else {
923 rt->rt_pmtu = mtu;
924 dst->expires = max(1UL, jiffies + ip_rt_mtu_expires);
925 }
926
927 rcu_read_lock(); 950 rcu_read_lock();
928 if (fib_lookup(dev_net(dst->dev), fl4, &res) == 0) { 951 if (fib_lookup(dev_net(dst->dev), fl4, &res) == 0) {
929 struct fib_nh *nh = &FIB_RES_NH(res); 952 struct fib_nh *nh = &FIB_RES_NH(res);
@@ -1063,11 +1086,11 @@ static struct dst_entry *ipv4_dst_check(struct dst_entry *dst, u32 cookie)
1063 * DST_OBSOLETE_FORCE_CHK which forces validation calls down 1086 * DST_OBSOLETE_FORCE_CHK which forces validation calls down
1064 * into this function always. 1087 * into this function always.
1065 * 1088 *
1066 * When a PMTU/redirect information update invalidates a 1089 * When a PMTU/redirect information update invalidates a route,
1067 * route, this is indicated by setting obsolete to 1090 * this is indicated by setting obsolete to DST_OBSOLETE_KILL or
1068 * DST_OBSOLETE_KILL. 1091 * DST_OBSOLETE_DEAD by dst_free().
1069 */ 1092 */
1070 if (dst->obsolete == DST_OBSOLETE_KILL || rt_is_expired(rt)) 1093 if (dst->obsolete != DST_OBSOLETE_FORCE_CHK || rt_is_expired(rt))
1071 return NULL; 1094 return NULL;
1072 return dst; 1095 return dst;
1073} 1096}
@@ -1215,20 +1238,8 @@ static bool rt_bind_exception(struct rtable *rt, struct fib_nh_exception *fnhe,
1215 fnhe->fnhe_pmtu = 0; 1238 fnhe->fnhe_pmtu = 0;
1216 fnhe->fnhe_expires = 0; 1239 fnhe->fnhe_expires = 0;
1217 } 1240 }
1218 if (fnhe->fnhe_pmtu) { 1241 fill_route_from_fnhe(rt, fnhe);
1219 unsigned long expires = fnhe->fnhe_expires; 1242 if (!rt->rt_gateway)
1220 unsigned long diff = expires - jiffies;
1221
1222 if (time_before(jiffies, expires)) {
1223 rt->rt_pmtu = fnhe->fnhe_pmtu;
1224 dst_set_expires(&rt->dst, diff);
1225 }
1226 }
1227 if (fnhe->fnhe_gw) {
1228 rt->rt_flags |= RTCF_REDIRECTED;
1229 rt->rt_gateway = fnhe->fnhe_gw;
1230 rt->rt_uses_gateway = 1;
1231 } else if (!rt->rt_gateway)
1232 rt->rt_gateway = daddr; 1243 rt->rt_gateway = daddr;
1233 1244
1234 rcu_assign_pointer(fnhe->fnhe_rth, rt); 1245 rcu_assign_pointer(fnhe->fnhe_rth, rt);