aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Oskolkov <posk@google.com>2018-08-02 19:34:37 -0400
committerDavid S. Miller <davem@davemloft.net>2018-08-05 20:16:46 -0400
commit7969e5c40dfd04799d4341f1b7cd266b6e47f227 (patch)
tree167103e66b8f8ebf96bf4ca7644c3e0f7b3bca10
parentcfb4099fb4c101dad283a163c9525240ef4a1a99 (diff)
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 <davem@davemloft.net> Signed-off-by: Peter Oskolkov <posk@google.com> Signed-off-by: Eric Dumazet <edumazet@google.com> Cc: Florian Westphal <fw@strlen.de> Acked-by: Stephen Hemminger <stephen@networkplumber.org> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/uapi/linux/snmp.h1
-rw-r--r--net/ipv4/ip_fragment.c75
-rw-r--r--net/ipv4/proc.c1
3 files changed, 21 insertions, 56 deletions
diff --git a/include/uapi/linux/snmp.h b/include/uapi/linux/snmp.h
index e5ebc83827ab..f80135e5feaa 100644
--- a/include/uapi/linux/snmp.h
+++ b/include/uapi/linux/snmp.h
@@ -56,6 +56,7 @@ enum
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_MAX 60 __IPSTATS_MIB_MAX
60}; 61};
61 62
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c
index d14d741fb05e..960bf5eab59f 100644
--- a/net/ipv4/ip_fragment.c
+++ b/net/ipv4/ip_fragment.c
@@ -277,6 +277,7 @@ static int ip_frag_reinit(struct ipq *qp)
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)
357 } 358 }
358 359
359found: 360found:
360 /* We found where to put this one. Check for overlap with 361 /* RFC5722, Section 4, amended by Errata ID : 3089
361 * preceding fragment, and, if needed, align things so that 362 * 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:
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;
diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c
index b46e4cf9a55a..70289682a670 100644
--- a/net/ipv4/proc.c
+++ b/net/ipv4/proc.c
@@ -119,6 +119,7 @@ static const struct snmp_mib snmp4_ipextstats_list[] = {
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_SENTINEL 123 SNMP_MIB_SENTINEL
123}; 124};
124 125