diff options
author | Eric Dumazet <edumazet@google.com> | 2018-03-31 15:58:55 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2018-03-31 23:25:39 -0400 |
commit | 05c0b86b9696802fd0ce5676a92a63f1b455bdf3 (patch) | |
tree | fd13fe02679973cf774f54ac96f155dc82905baa | |
parent | 1eec5d5670084ee644597bd26c25e22c69b9f748 (diff) |
ipv6: frags: rewrite ip6_expire_frag_queue()
Make it similar to IPv4 ip_expire(), and release the lock
before calling icmp functions.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/ipv6/reassembly.c | 24 |
1 files changed, 16 insertions, 8 deletions
diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index 905a8aee2671..2127da130dc2 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c | |||
@@ -92,7 +92,9 @@ EXPORT_SYMBOL(ip6_frag_init); | |||
92 | void ip6_expire_frag_queue(struct net *net, struct frag_queue *fq) | 92 | void ip6_expire_frag_queue(struct net *net, struct frag_queue *fq) |
93 | { | 93 | { |
94 | struct net_device *dev = NULL; | 94 | struct net_device *dev = NULL; |
95 | struct sk_buff *head; | ||
95 | 96 | ||
97 | rcu_read_lock(); | ||
96 | spin_lock(&fq->q.lock); | 98 | spin_lock(&fq->q.lock); |
97 | 99 | ||
98 | if (fq->q.flags & INET_FRAG_COMPLETE) | 100 | if (fq->q.flags & INET_FRAG_COMPLETE) |
@@ -100,28 +102,34 @@ void ip6_expire_frag_queue(struct net *net, struct frag_queue *fq) | |||
100 | 102 | ||
101 | inet_frag_kill(&fq->q); | 103 | inet_frag_kill(&fq->q); |
102 | 104 | ||
103 | rcu_read_lock(); | ||
104 | dev = dev_get_by_index_rcu(net, fq->iif); | 105 | dev = dev_get_by_index_rcu(net, fq->iif); |
105 | if (!dev) | 106 | if (!dev) |
106 | goto out_rcu_unlock; | 107 | goto out; |
107 | 108 | ||
108 | __IP6_INC_STATS(net, __in6_dev_get(dev), IPSTATS_MIB_REASMFAILS); | 109 | __IP6_INC_STATS(net, __in6_dev_get(dev), IPSTATS_MIB_REASMFAILS); |
109 | __IP6_INC_STATS(net, __in6_dev_get(dev), IPSTATS_MIB_REASMTIMEOUT); | 110 | __IP6_INC_STATS(net, __in6_dev_get(dev), IPSTATS_MIB_REASMTIMEOUT); |
110 | 111 | ||
111 | /* Don't send error if the first segment did not arrive. */ | 112 | /* Don't send error if the first segment did not arrive. */ |
112 | if (!(fq->q.flags & INET_FRAG_FIRST_IN) || !fq->q.fragments) | 113 | head = fq->q.fragments; |
113 | goto out_rcu_unlock; | 114 | if (!(fq->q.flags & INET_FRAG_FIRST_IN) || !head) |
115 | goto out; | ||
114 | 116 | ||
115 | /* But use as source device on which LAST ARRIVED | 117 | /* But use as source device on which LAST ARRIVED |
116 | * segment was received. And do not use fq->dev | 118 | * segment was received. And do not use fq->dev |
117 | * pointer directly, device might already disappeared. | 119 | * pointer directly, device might already disappeared. |
118 | */ | 120 | */ |
119 | fq->q.fragments->dev = dev; | 121 | head->dev = dev; |
120 | icmpv6_send(fq->q.fragments, ICMPV6_TIME_EXCEED, ICMPV6_EXC_FRAGTIME, 0); | 122 | skb_get(head); |
121 | out_rcu_unlock: | 123 | spin_unlock(&fq->q.lock); |
122 | rcu_read_unlock(); | 124 | |
125 | icmpv6_send(head, ICMPV6_TIME_EXCEED, ICMPV6_EXC_FRAGTIME, 0); | ||
126 | kfree_skb(head); | ||
127 | goto out_rcu_unlock; | ||
128 | |||
123 | out: | 129 | out: |
124 | spin_unlock(&fq->q.lock); | 130 | spin_unlock(&fq->q.lock); |
131 | out_rcu_unlock: | ||
132 | rcu_read_unlock(); | ||
125 | inet_frag_put(&fq->q); | 133 | inet_frag_put(&fq->q); |
126 | } | 134 | } |
127 | EXPORT_SYMBOL(ip6_expire_frag_queue); | 135 | EXPORT_SYMBOL(ip6_expire_frag_queue); |