Skip to content

Commit 0ff89ef

Browse files
Peter Oskolkovdavem330
authored andcommitted
ip: fail fast on IP defrag errors
The current behavior of IP defragmentation is inconsistent: - some overlapping/wrong length fragments are dropped without affecting the queue; - most overlapping fragments cause the whole frag queue to be dropped. This patch brings consistency: if a bad fragment is detected, the whole frag queue is dropped. Two major benefits: - fail fast: corrupted frag queues are cleared immediately, instead of by timeout; - testing of overlapping fragments is now much easier: any kind of random fragment length mutation now leads to the frag queue being discarded (IP packet dropped); before this patch, some overlaps were "corrected", with tests not seeing expected packet drops. Note that in one case (see "if (end&7)" conditional) the current behavior is preserved as there are concerns that this could be legitimate padding. Signed-off-by: Peter Oskolkov <posk@google.com> Reviewed-by: Eric Dumazet <edumazet@google.com> Reviewed-by: Willem de Bruijn <willemb@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent b943f17 commit 0ff89ef

File tree

1 file changed

+12
-9
lines changed

1 file changed

+12
-9
lines changed

net/ipv4/ip_fragment.c

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
382382
*/
383383
if (end < qp->q.len ||
384384
((qp->q.flags & INET_FRAG_LAST_IN) && end != qp->q.len))
385-
goto err;
385+
goto discard_qp;
386386
qp->q.flags |= INET_FRAG_LAST_IN;
387387
qp->q.len = end;
388388
} else {
@@ -394,20 +394,20 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
394394
if (end > qp->q.len) {
395395
/* Some bits beyond end -> corruption. */
396396
if (qp->q.flags & INET_FRAG_LAST_IN)
397-
goto err;
397+
goto discard_qp;
398398
qp->q.len = end;
399399
}
400400
}
401401
if (end == offset)
402-
goto err;
402+
goto discard_qp;
403403

404404
err = -ENOMEM;
405405
if (!pskb_pull(skb, skb_network_offset(skb) + ihl))
406-
goto err;
406+
goto discard_qp;
407407

408408
err = pskb_trim_rcsum(skb, end - offset);
409409
if (err)
410-
goto err;
410+
goto discard_qp;
411411

412412
/* Note : skb->rbnode and skb->dev share the same location. */
413413
dev = skb->dev;
@@ -423,6 +423,7 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
423423
* We do the same here for IPv4 (and increment an snmp counter).
424424
*/
425425

426+
err = -EINVAL;
426427
/* Find out where to put this fragment. */
427428
prev_tail = qp->q.fragments_tail;
428429
if (!prev_tail)
@@ -431,7 +432,7 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
431432
/* This is the common case: skb goes to the end. */
432433
/* Detect and discard overlaps. */
433434
if (offset < prev_tail->ip_defrag_offset + prev_tail->len)
434-
goto discard_qp;
435+
goto overlap;
435436
if (offset == prev_tail->ip_defrag_offset + prev_tail->len)
436437
ip4_frag_append_to_last_run(&qp->q, skb);
437438
else
@@ -450,7 +451,7 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
450451
FRAG_CB(skb1)->frag_run_len)
451452
rbn = &parent->rb_right;
452453
else /* Found an overlap with skb1. */
453-
goto discard_qp;
454+
goto overlap;
454455
} while (*rbn);
455456
/* Here we have parent properly set, and rbn pointing to
456457
* one of its NULL left/right children. Insert skb.
@@ -487,16 +488,18 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
487488
skb->_skb_refdst = 0UL;
488489
err = ip_frag_reasm(qp, skb, prev_tail, dev);
489490
skb->_skb_refdst = orefdst;
491+
if (err)
492+
inet_frag_kill(&qp->q);
490493
return err;
491494
}
492495

493496
skb_dst_drop(skb);
494497
return -EINPROGRESS;
495498

499+
overlap:
500+
__IP_INC_STATS(net, IPSTATS_MIB_REASM_OVERLAPS);
496501
discard_qp:
497502
inet_frag_kill(&qp->q);
498-
err = -EINVAL;
499-
__IP_INC_STATS(net, IPSTATS_MIB_REASM_OVERLAPS);
500503
err:
501504
kfree_skb(skb);
502505
return err;

0 commit comments

Comments
 (0)