aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv4/ip_fragment.c')
-rw-r--r--net/ipv4/ip_fragment.c25
1 files changed, 17 insertions, 8 deletions
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c
index e2e162432aa3..7057a1b09b5e 100644
--- a/net/ipv4/ip_fragment.c
+++ b/net/ipv4/ip_fragment.c
@@ -200,6 +200,7 @@ static void ip_expire(unsigned long arg)
200 qp = container_of((struct inet_frag_queue *) arg, struct ipq, q); 200 qp = container_of((struct inet_frag_queue *) arg, struct ipq, q);
201 net = container_of(qp->q.net, struct net, ipv4.frags); 201 net = container_of(qp->q.net, struct net, ipv4.frags);
202 202
203 rcu_read_lock();
203 spin_lock(&qp->q.lock); 204 spin_lock(&qp->q.lock);
204 205
205 if (qp->q.flags & INET_FRAG_COMPLETE) 206 if (qp->q.flags & INET_FRAG_COMPLETE)
@@ -209,7 +210,7 @@ static void ip_expire(unsigned long arg)
209 IP_INC_STATS_BH(net, IPSTATS_MIB_REASMFAILS); 210 IP_INC_STATS_BH(net, IPSTATS_MIB_REASMFAILS);
210 211
211 if (!inet_frag_evicting(&qp->q)) { 212 if (!inet_frag_evicting(&qp->q)) {
212 struct sk_buff *head = qp->q.fragments; 213 struct sk_buff *clone, *head = qp->q.fragments;
213 const struct iphdr *iph; 214 const struct iphdr *iph;
214 int err; 215 int err;
215 216
@@ -218,32 +219,40 @@ static void ip_expire(unsigned long arg)
218 if (!(qp->q.flags & INET_FRAG_FIRST_IN) || !qp->q.fragments) 219 if (!(qp->q.flags & INET_FRAG_FIRST_IN) || !qp->q.fragments)
219 goto out; 220 goto out;
220 221
221 rcu_read_lock();
222 head->dev = dev_get_by_index_rcu(net, qp->iif); 222 head->dev = dev_get_by_index_rcu(net, qp->iif);
223 if (!head->dev) 223 if (!head->dev)
224 goto out_rcu_unlock; 224 goto out;
225
225 226
226 /* skb has no dst, perform route lookup again */ 227 /* skb has no dst, perform route lookup again */
227 iph = ip_hdr(head); 228 iph = ip_hdr(head);
228 err = ip_route_input_noref(head, iph->daddr, iph->saddr, 229 err = ip_route_input_noref(head, iph->daddr, iph->saddr,
229 iph->tos, head->dev); 230 iph->tos, head->dev);
230 if (err) 231 if (err)
231 goto out_rcu_unlock; 232 goto out;
232 233
233 /* Only an end host needs to send an ICMP 234 /* Only an end host needs to send an ICMP
234 * "Fragment Reassembly Timeout" message, per RFC792. 235 * "Fragment Reassembly Timeout" message, per RFC792.
235 */ 236 */
236 if (frag_expire_skip_icmp(qp->user) && 237 if (frag_expire_skip_icmp(qp->user) &&
237 (skb_rtable(head)->rt_type != RTN_LOCAL)) 238 (skb_rtable(head)->rt_type != RTN_LOCAL))
238 goto out_rcu_unlock; 239 goto out;
240
241 clone = skb_clone(head, GFP_ATOMIC);
239 242
240 /* Send an ICMP "Fragment Reassembly Timeout" message. */ 243 /* Send an ICMP "Fragment Reassembly Timeout" message. */
241 icmp_send(head, ICMP_TIME_EXCEEDED, ICMP_EXC_FRAGTIME, 0); 244 if (clone) {
242out_rcu_unlock: 245 spin_unlock(&qp->q.lock);
243 rcu_read_unlock(); 246 icmp_send(clone, ICMP_TIME_EXCEEDED,
247 ICMP_EXC_FRAGTIME, 0);
248 consume_skb(clone);
249 goto out_rcu_unlock;
250 }
244 } 251 }
245out: 252out:
246 spin_unlock(&qp->q.lock); 253 spin_unlock(&qp->q.lock);
254out_rcu_unlock:
255 rcu_read_unlock();
247 ipq_put(qp); 256 ipq_put(qp);
248} 257}
249 258