History

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).

Peter Oskolkov committed 8 years ago

Commit 7969e5c1 parent cfb40993 files · +21 −56Browse files

Unified · Split

3 files
  1. include/uapi/linux/snmp.h +1 −0
  2. net/ipv4/ip_fragment.c +19 −56
  3. net/ipv4/proc.c +1 −0
include/uapi/linux/snmp.h+1−0
@@ -56,6 +56,7 @@ enum · 55 unmodified lines
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+19−56
@@ -277,6 +277,7 @@ static int ip_frag_reinit(struct ipq *qp) · 276 unmodified lines
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) · 74 unmodified lines
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 @@ found: · 41 unmodified lines
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+1−0
@@ -119,6 +119,7 @@ static const struct snmp_mib snmp4_ipextstats_list[] = { · 118 unmodified lines
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