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
56 IPSTATS_MIB_ECT1PKTS, /* InECT1Pkts */56 IPSTATS_MIB_ECT1PKTS, /* InECT1Pkts */
57 IPSTATS_MIB_ECT0PKTS, /* InECT0Pkts */57 IPSTATS_MIB_ECT0PKTS, /* InECT0Pkts */
58 IPSTATS_MIB_CEPKTS, /* InCEPkts */58 IPSTATS_MIB_CEPKTS, /* InCEPkts */
59 IPSTATS_MIB_REASM_OVERLAPS, /* ReasmOverlaps */
59 __IPSTATS_MIB_MAX60 __IPSTATS_MIB_MAX
60};61};
61 62
net/ipv4/ip_fragment.c+19−56
@@ -277,6 +277,7 @@ static int ip_frag_reinit(struct ipq *qp) · 276 unmodified lines
277/* Add new segment to existing queue. */277/* Add new segment to existing queue. */
278static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)278static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
279{279{
280 struct net *net = container_of(qp->q.net, struct net, ipv4.frags);
280 struct sk_buff *prev, *next;281 struct sk_buff *prev, *next;
281 struct net_device *dev;282 struct net_device *dev;
282 unsigned int fragsize;283 unsigned int fragsize;
@@ -357,65 +358,23 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb) · 74 unmodified lines
357 }358 }
358 359
359found:360found:
360 /* We found where to put this one. Check for overlap with361 /* RFC5722, Section 4, amended by Errata ID : 3089
361 * preceding fragment, and, if needed, align things so that362 * When reassembling an IPv6 datagram, if
362 * any overlaps are eliminated.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.
363 */368 */
364 if (prev) {
365 int i = (prev->ip_defrag_offset + prev->len) - offset;
366 369
367 if (i > 0) {370 /* Is there an overlap with the previous fragment? */
368 offset += i;371 if (prev &&
369 err = -EINVAL;372 (prev->ip_defrag_offset + prev->len) > offset)
370 if (end <= offset)373 goto discard_qp;
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 }
379 374
380 err = -ENOMEM;375 /* Is there an overlap with the next fragment? */
381 376 if (next && next->ip_defrag_offset < end)
382 while (next && next->ip_defrag_offset < end) {377 goto discard_qp;
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 }
419 378
420 /* Note : skb->ip_defrag_offset and skb->dev share the same location */379 /* Note : skb->ip_defrag_offset and skb->dev share the same location */
421 dev = skb->dev;380 dev = skb->dev;
@@ -463,6 +422,10 @@ found: · 41 unmodified lines
463 skb_dst_drop(skb);422 skb_dst_drop(skb);
464 return -EINPROGRESS;423 return -EINPROGRESS;
465 424
425discard_qp:
426 inet_frag_kill(&qp->q);
427 err = -EINVAL;
428 __IP_INC_STATS(net, IPSTATS_MIB_REASM_OVERLAPS);
466err:429err:
467 kfree_skb(skb);430 kfree_skb(skb);
468 return err;431 return err;
net/ipv4/proc.c+1−0
@@ -119,6 +119,7 @@ static const struct snmp_mib snmp4_ipextstats_list[] = { · 118 unmodified lines
119 SNMP_MIB_ITEM("InECT1Pkts", IPSTATS_MIB_ECT1PKTS),119 SNMP_MIB_ITEM("InECT1Pkts", IPSTATS_MIB_ECT1PKTS),
120 SNMP_MIB_ITEM("InECT0Pkts", IPSTATS_MIB_ECT0PKTS),120 SNMP_MIB_ITEM("InECT0Pkts", IPSTATS_MIB_ECT0PKTS),
121 SNMP_MIB_ITEM("InCEPkts", IPSTATS_MIB_CEPKTS),121 SNMP_MIB_ITEM("InCEPkts", IPSTATS_MIB_CEPKTS),
122 SNMP_MIB_ITEM("ReasmOverlaps", IPSTATS_MIB_REASM_OVERLAPS),
122 SNMP_MIB_SENTINEL123 SNMP_MIB_SENTINEL
123};124};
124 125