diff options
| author | Steffen Klassert <steffen.klassert@secunet.com> | 2013-01-21 19:01:28 -0500 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2013-01-22 14:23:17 -0500 |
| commit | b44108dbdbaa07c609bb5755e8dd6c2035236251 (patch) | |
| tree | f5c9450d05411d8d76c97ac96d9d91243c1ea5bd /net/ipv4 | |
| parent | 0c8729c9b914cc0360ab87171472ca7653b2aa0e (diff) | |
ipv4: Fix route refcount on pmtu discovery
git commit 9cb3a50c (ipv4: Invalidate the socket cached route on
pmtu events if possible) introduced a refcount problem. We don't
get a refcount on the route if we get it from__sk_dst_get(), but
we need one if we want to reuse this route because __sk_dst_set()
releases the refcount of the old route. This patch adds proper
refcount handling for that case. We introduce a 'new' flag to
indicate that we are going to use a new route and we release the
old route only if we replace it by a new one.
Reported-by: Julian Anastasov <ja@ssi.bg>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4')
| -rw-r--r-- | net/ipv4/route.c | 11 |
1 files changed, 9 insertions, 2 deletions
diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 132737a7c83a..a0fcc47fee73 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c | |||
| @@ -985,6 +985,7 @@ void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu) | |||
| 985 | struct flowi4 fl4; | 985 | struct flowi4 fl4; |
| 986 | struct rtable *rt; | 986 | struct rtable *rt; |
| 987 | struct dst_entry *dst; | 987 | struct dst_entry *dst; |
| 988 | bool new = false; | ||
| 988 | 989 | ||
| 989 | bh_lock_sock(sk); | 990 | bh_lock_sock(sk); |
| 990 | rt = (struct rtable *) __sk_dst_get(sk); | 991 | rt = (struct rtable *) __sk_dst_get(sk); |
| @@ -1000,20 +1001,26 @@ void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu) | |||
| 1000 | rt = ip_route_output_flow(sock_net(sk), &fl4, sk); | 1001 | rt = ip_route_output_flow(sock_net(sk), &fl4, sk); |
| 1001 | if (IS_ERR(rt)) | 1002 | if (IS_ERR(rt)) |
| 1002 | goto out; | 1003 | goto out; |
| 1004 | |||
| 1005 | new = true; | ||
| 1003 | } | 1006 | } |
| 1004 | 1007 | ||
| 1005 | __ip_rt_update_pmtu((struct rtable *) rt->dst.path, &fl4, mtu); | 1008 | __ip_rt_update_pmtu((struct rtable *) rt->dst.path, &fl4, mtu); |
| 1006 | 1009 | ||
| 1007 | dst = dst_check(&rt->dst, 0); | 1010 | dst = dst_check(&rt->dst, 0); |
| 1008 | if (!dst) { | 1011 | if (!dst) { |
| 1012 | if (new) | ||
| 1013 | dst_release(&rt->dst); | ||
| 1014 | |||
| 1009 | rt = ip_route_output_flow(sock_net(sk), &fl4, sk); | 1015 | rt = ip_route_output_flow(sock_net(sk), &fl4, sk); |
| 1010 | if (IS_ERR(rt)) | 1016 | if (IS_ERR(rt)) |
| 1011 | goto out; | 1017 | goto out; |
| 1012 | 1018 | ||
| 1013 | dst = &rt->dst; | 1019 | new = true; |
| 1014 | } | 1020 | } |
| 1015 | 1021 | ||
| 1016 | __sk_dst_set(sk, dst); | 1022 | if (new) |
| 1023 | __sk_dst_set(sk, &rt->dst); | ||
| 1017 | 1024 | ||
| 1018 | out: | 1025 | out: |
| 1019 | bh_unlock_sock(sk); | 1026 | bh_unlock_sock(sk); |
