Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7969e5c4 authored by Peter Oskolkov's avatar Peter Oskolkov Committed by David S. Miller
Browse files

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: default avatarDavid S. Miller <davem@davemloft.net>
Signed-off-by: default avatarPeter Oskolkov <posk@google.com>
Signed-off-by: default avatarEric Dumazet <edumazet@google.com>
Cc: Florian Westphal <fw@strlen.de>
Acked-by: default avatarStephen Hemminger <stephen@networkplumber.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent cfb4099f
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -56,6 +56,7 @@ enum
	IPSTATS_MIB_ECT1PKTS,			/* InECT1Pkts */
	IPSTATS_MIB_ECT1PKTS,			/* InECT1Pkts */
	IPSTATS_MIB_ECT0PKTS,			/* InECT0Pkts */
	IPSTATS_MIB_ECT0PKTS,			/* InECT0Pkts */
	IPSTATS_MIB_CEPKTS,			/* InCEPkts */
	IPSTATS_MIB_CEPKTS,			/* InCEPkts */
	IPSTATS_MIB_REASM_OVERLAPS,		/* ReasmOverlaps */
	__IPSTATS_MIB_MAX
	__IPSTATS_MIB_MAX
};
};


+19 −56
Original line number Original line Diff line number Diff line
@@ -277,6 +277,7 @@ static int ip_frag_reinit(struct ipq *qp)
/* Add new segment to existing queue. */
/* Add new segment to existing queue. */
static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
{
{
	struct net *net = container_of(qp->q.net, struct net, ipv4.frags);
	struct sk_buff *prev, *next;
	struct sk_buff *prev, *next;
	struct net_device *dev;
	struct net_device *dev;
	unsigned int fragsize;
	unsigned int fragsize;
@@ -357,65 +358,23 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
	}
	}


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

	 * We do the same here for IPv4.
		if (i > 0) {
			offset += i;
			err = -EINVAL;
			if (end <= offset)
				goto err;
			err = -ENOMEM;
			if (!pskb_pull(skb, i))
				goto err;
			if (skb->ip_summed != CHECKSUM_UNNECESSARY)
				skb->ip_summed = CHECKSUM_NONE;
		}
	}

	err = -ENOMEM;

	while (next && next->ip_defrag_offset < end) {
		int i = end - next->ip_defrag_offset; /* overlap is 'i' bytes */

		if (i < next->len) {
			int delta = -next->truesize;

			/* Eat head of the next overlapped fragment
			 * and leave the loop. The next ones cannot overlap.
			 */
			if (!pskb_pull(next, i))
				goto err;
			delta += next->truesize;
			if (delta)
				add_frag_mem_limit(qp->q.net, delta);
			next->ip_defrag_offset += i;
			qp->q.meat -= i;
			if (next->ip_summed != CHECKSUM_UNNECESSARY)
				next->ip_summed = CHECKSUM_NONE;
			break;
		} else {
			struct sk_buff *free_it = next;

			/* Old fragment is completely overridden with
			 * new one drop it.
	 */
	 */
			next = next->next;


			if (prev)
	/* Is there an overlap with the previous fragment? */
				prev->next = next;
	if (prev &&
			else
	    (prev->ip_defrag_offset + prev->len) > offset)
				qp->q.fragments = next;
		goto discard_qp;


			qp->q.meat -= free_it->len;
	/* Is there an overlap with the next fragment? */
			sub_frag_mem_limit(qp->q.net, free_it->truesize);
	if (next && next->ip_defrag_offset < end)
			kfree_skb(free_it);
		goto discard_qp;
		}
	}


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


discard_qp:
	inet_frag_kill(&qp->q);
	err = -EINVAL;
	__IP_INC_STATS(net, IPSTATS_MIB_REASM_OVERLAPS);
err:
err:
	kfree_skb(skb);
	kfree_skb(skb);
	return err;
	return err;
+1 −0
Original line number Original line Diff line number Diff line
@@ -119,6 +119,7 @@ static const struct snmp_mib snmp4_ipextstats_list[] = {
	SNMP_MIB_ITEM("InECT1Pkts", IPSTATS_MIB_ECT1PKTS),
	SNMP_MIB_ITEM("InECT1Pkts", IPSTATS_MIB_ECT1PKTS),
	SNMP_MIB_ITEM("InECT0Pkts", IPSTATS_MIB_ECT0PKTS),
	SNMP_MIB_ITEM("InECT0Pkts", IPSTATS_MIB_ECT0PKTS),
	SNMP_MIB_ITEM("InCEPkts", IPSTATS_MIB_CEPKTS),
	SNMP_MIB_ITEM("InCEPkts", IPSTATS_MIB_CEPKTS),
	SNMP_MIB_ITEM("ReasmOverlaps", IPSTATS_MIB_REASM_OVERLAPS),
	SNMP_MIB_SENTINEL
	SNMP_MIB_SENTINEL
};
};