Skip to content

Commit 7969e5c

Browse files
posk-iodavem330
authored andcommitted
ip: discard IPv4 datagrams with overlapping segments.
This behavior is required in IPv6, and there is little need to tolerate overlapping fragments in IPv4. This change simplifies the code and eliminates potential DDoS attack vectors. Tested: ran ip_defrag selftest (not yet available uptream). Suggested-by: David S. Miller <[email protected]> Signed-off-by: Peter Oskolkov <[email protected]> Signed-off-by: Eric Dumazet <[email protected]> Cc: Florian Westphal <[email protected]> Acked-by: Stephen Hemminger <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent cfb4099 commit 7969e5c

File tree

3 files changed

+21
-56
lines changed

3 files changed

+21
-56
lines changed

include/uapi/linux/snmp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ enum
5656
IPSTATS_MIB_ECT1PKTS, /* InECT1Pkts */
5757
IPSTATS_MIB_ECT0PKTS, /* InECT0Pkts */
5858
IPSTATS_MIB_CEPKTS, /* InCEPkts */
59+
IPSTATS_MIB_REASM_OVERLAPS, /* ReasmOverlaps */
5960
__IPSTATS_MIB_MAX
6061
};
6162

net/ipv4/ip_fragment.c

Lines changed: 19 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ static int ip_frag_reinit(struct ipq *qp)
277277
/* Add new segment to existing queue. */
278278
static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
279279
{
280+
struct net *net = container_of(qp->q.net, struct net, ipv4.frags);
280281
struct sk_buff *prev, *next;
281282
struct net_device *dev;
282283
unsigned int fragsize;
@@ -357,65 +358,23 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
357358
}
358359

359360
found:
360-
/* We found where to put this one. Check for overlap with
361-
* preceding fragment, and, if needed, align things so that
362-
* any overlaps are eliminated.
361+
/* RFC5722, Section 4, amended by Errata ID : 3089
362+
* When reassembling an IPv6 datagram, if
363+
* one or more its constituent fragments is determined to be an
364+
* overlapping fragment, the entire datagram (and any constituent
365+
* fragments) MUST be silently discarded.
366+
*
367+
* We do the same here for IPv4.
363368
*/
364-
if (prev) {
365-
int i = (prev->ip_defrag_offset + prev->len) - offset;
366369

367-
if (i > 0) {
368-
offset += i;
369-
err = -EINVAL;
370-
if (end <= offset)
371-
goto err;
372-
err = -ENOMEM;
373-
if (!pskb_pull(skb, i))
374-
goto err;
375-
if (skb->ip_summed != CHECKSUM_UNNECESSARY)
376-
skb->ip_summed = CHECKSUM_NONE;
377-
}
378-
}
370+
/* Is there an overlap with the previous fragment? */
371+
if (prev &&
372+
(prev->ip_defrag_offset + prev->len) > offset)
373+
goto discard_qp;
379374

380-
err = -ENOMEM;
381-
382-
while (next && next->ip_defrag_offset < end) {
383-
int i = end - next->ip_defrag_offset; /* overlap is 'i' bytes */
384-
385-
if (i < next->len) {
386-
int delta = -next->truesize;
387-
388-
/* Eat head of the next overlapped fragment
389-
* and leave the loop. The next ones cannot overlap.
390-
*/
391-
if (!pskb_pull(next, i))
392-
goto err;
393-
delta += next->truesize;
394-
if (delta)
395-
add_frag_mem_limit(qp->q.net, delta);
396-
next->ip_defrag_offset += i;
397-
qp->q.meat -= i;
398-
if (next->ip_summed != CHECKSUM_UNNECESSARY)
399-
next->ip_summed = CHECKSUM_NONE;
400-
break;
401-
} else {
402-
struct sk_buff *free_it = next;
403-
404-
/* Old fragment is completely overridden with
405-
* new one drop it.
406-
*/
407-
next = next->next;
408-
409-
if (prev)
410-
prev->next = next;
411-
else
412-
qp->q.fragments = next;
413-
414-
qp->q.meat -= free_it->len;
415-
sub_frag_mem_limit(qp->q.net, free_it->truesize);
416-
kfree_skb(free_it);
417-
}
418-
}
375+
/* Is there an overlap with the next fragment? */
376+
if (next && next->ip_defrag_offset < end)
377+
goto discard_qp;
419378

420379
/* Note : skb->ip_defrag_offset and skb->dev share the same location */
421380
dev = skb->dev;
@@ -463,6 +422,10 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
463422
skb_dst_drop(skb);
464423
return -EINPROGRESS;
465424

425+
discard_qp:
426+
inet_frag_kill(&qp->q);
427+
err = -EINVAL;
428+
__IP_INC_STATS(net, IPSTATS_MIB_REASM_OVERLAPS);
466429
err:
467430
kfree_skb(skb);
468431
return err;

net/ipv4/proc.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ static const struct snmp_mib snmp4_ipextstats_list[] = {
119119
SNMP_MIB_ITEM("InECT1Pkts", IPSTATS_MIB_ECT1PKTS),
120120
SNMP_MIB_ITEM("InECT0Pkts", IPSTATS_MIB_ECT0PKTS),
121121
SNMP_MIB_ITEM("InCEPkts", IPSTATS_MIB_CEPKTS),
122+
SNMP_MIB_ITEM("ReasmOverlaps", IPSTATS_MIB_REASM_OVERLAPS),
122123
SNMP_MIB_SENTINEL
123124
};
124125

0 commit comments

Comments
 (0)