-From 7905dc07a3d1aac701694adcb0c0cb4439a47117 Mon Sep 17 00:00:00 2001
+From 55bd1742ee0cf2320e6fd058090b3d46c0eef1cf Mon Sep 17 00:00:00 2001
From: Peter Oskolkov <posk@google.com>
-Date: Mon, 8 Apr 2019 17:10:03 -0700
+Date: Tue, 23 Apr 2019 10:25:31 -0700
Subject: net: IP defrag: encapsulate rbtree defrag code into callable
functions
it moves rbtree-related code from IPv4-specific files/functions
into .h/.c defrag files shared with IPv6 defragmentation code.
+v2: make handling of overlapping packets match upstream.
+
Signed-off-by: Peter Oskolkov <posk@google.com>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Florian Westphal <fw@strlen.de>
Cc: Tom Herbert <tom@herbertland.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-Acked-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
include/net/inet_frag.h | 16 ++-
- net/ipv4/inet_fragment.c | 293 +++++++++++++++++++++++++++++++++++++++
- net/ipv4/ip_fragment.c | 290 ++++----------------------------------
- 3 files changed, 335 insertions(+), 264 deletions(-)
+ net/ipv4/inet_fragment.c | 293 +++++++++++++++++++++++++++++++++++++
+ net/ipv4/ip_fragment.c | 302 +++++----------------------------------
+ 3 files changed, 342 insertions(+), 269 deletions(-)
diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h
index 1662cbc0b46b..b02bf737d019 100644
+}
+EXPORT_SYMBOL(inet_frag_pull_head);
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c
-index d95b32af4a0e..15c609e6f12e 100644
+index d95b32af4a0e..5a1d39e32196 100644
--- a/net/ipv4/ip_fragment.c
+++ b/net/ipv4/ip_fragment.c
@@ -57,57 +57,6 @@
int err = -ENOENT;
u8 ecn;
-@@ -414,62 +343,13 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
+@@ -382,7 +311,7 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
+ */
+ if (end < qp->q.len ||
+ ((qp->q.flags & INET_FRAG_LAST_IN) && end != qp->q.len))
+- goto err;
++ goto discard_qp;
+ qp->q.flags |= INET_FRAG_LAST_IN;
+ qp->q.len = end;
+ } else {
+@@ -394,82 +323,33 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
+ if (end > qp->q.len) {
+ /* Some bits beyond end -> corruption. */
+ if (qp->q.flags & INET_FRAG_LAST_IN)
+- goto err;
++ goto discard_qp;
+ qp->q.len = end;
+ }
+ }
+ if (end == offset)
+- goto err;
++ goto discard_qp;
+
+ err = -ENOMEM;
+ if (!pskb_pull(skb, skb_network_offset(skb) + ihl))
+- goto err;
++ goto discard_qp;
+
+ err = pskb_trim_rcsum(skb, end - offset);
+ if (err)
+- goto err;
++ goto discard_qp;
+
+ /* Note : skb->rbnode and skb->dev share the same location. */
+ dev = skb->dev;
/* Makes sure compiler wont do silly aliasing games */
barrier();
qp->q.stamp = skb->tstamp;
qp->q.meat += skb->len;
-@@ -500,9 +380,16 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
+@@ -494,15 +374,24 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
+ skb->_skb_refdst = 0UL;
+ err = ip_frag_reasm(qp, skb, prev_tail, dev);
+ skb->_skb_refdst = orefdst;
++ if (err)
++ inet_frag_kill(&qp->q);
+ return err;
+ }
+
skb_dst_drop(skb);
return -EINPROGRESS;
err:
kfree_skb(skb);
return err;
-@@ -514,13 +401,8 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *skb,
+@@ -514,13 +403,8 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *skb,
{
struct net *net = container_of(qp->q.net, struct net, ipv4.frags);
struct iphdr *iph;
u8 ecn;
ipq_kill(qp);
-@@ -530,117 +412,23 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *skb,
+@@ -530,117 +414,23 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *skb,
err = -EINVAL;
goto out_fail;
}
- consume_skb(head);
- head = skb;
- }
--
-- WARN_ON(head->ip_defrag_offset != 0);
+- WARN_ON(head->ip_defrag_offset != 0);
+-
- /* Allocate a new buffer for the datagram. */
- ihlen = ip_hdrlen(head);
- len = ihlen + qp->q.len;
iph->tot_len = htons(len);
iph->tos |= ecn;
-@@ -653,7 +441,7 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *skb,
+@@ -653,7 +443,7 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *skb,
* from one very small df-fragment and one large non-df frag.
*/
if (qp->max_df_size == qp->q.max_size) {
iph->frag_off = htons(IP_DF);
} else {
iph->frag_off = 0;
-@@ -751,28 +539,6 @@ struct sk_buff *ip_check_defrag(struct net *net, struct sk_buff *skb, u32 user)
+@@ -751,28 +541,6 @@ struct sk_buff *ip_check_defrag(struct net *net, struct sk_buff *skb, u32 user)
}
EXPORT_SYMBOL(ip_check_defrag);
-From a4875906060ceefdd345989aed28133d070053d3 Mon Sep 17 00:00:00 2001
+From 203ae2b2db098f24e3e8f82c1bd3657e58b1400b Mon Sep 17 00:00:00 2001
From: Peter Oskolkov <posk@google.com>
-Date: Mon, 8 Apr 2019 17:10:04 -0700
+Date: Tue, 23 Apr 2019 10:25:32 -0700
Subject: net: IP6 defrag: use rbtrees for IPv6 defrag
[ Upstream commit d4289fcc9b16b89619ee1c54f829e05e56de8b9a ]
This patch re-uses common IP defragmentation queueing and reassembly
code in IPv6, removing the 1280 byte restriction.
+v2: change handling of overlaps to match that of upstream.
+
Signed-off-by: Peter Oskolkov <posk@google.com>
Reported-by: Tom Herbert <tom@herbertland.com>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Florian Westphal <fw@strlen.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
-Acked-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
include/net/ipv6_frag.h | 11 +-
- net/ipv6/reassembly.c | 233 +++++++++++-----------------------------
- 2 files changed, 71 insertions(+), 173 deletions(-)
+ net/ipv6/reassembly.c | 240 +++++++++++-----------------------------
+ 2 files changed, 75 insertions(+), 176 deletions(-)
diff --git a/include/net/ipv6_frag.h b/include/net/ipv6_frag.h
index 6ced1e6899b6..28aa9b30aece 100644
head->dev = dev;
diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c
-index 7c943392c128..642f9f53b01d 100644
+index 7c943392c128..095825f964e2 100644
--- a/net/ipv6/reassembly.c
+++ b/net/ipv6/reassembly.c
@@ -69,8 +69,8 @@ static u8 ip6_frag_ecn(const struct ipv6hdr *ipv6h)
return -1;
}
-@@ -170,62 +175,27 @@ static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb,
+@@ -145,7 +150,7 @@ static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb,
+ */
+ if (end < fq->q.len ||
+ ((fq->q.flags & INET_FRAG_LAST_IN) && end != fq->q.len))
+- goto err;
++ goto discard_fq;
+ fq->q.flags |= INET_FRAG_LAST_IN;
+ fq->q.len = end;
+ } else {
+@@ -162,70 +167,35 @@ static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb,
+ if (end > fq->q.len) {
+ /* Some bits beyond end -> corruption. */
+ if (fq->q.flags & INET_FRAG_LAST_IN)
+- goto err;
++ goto discard_fq;
+ fq->q.len = end;
+ }
+ }
+
if (end == offset)
- goto err;
+- goto err;
++ goto discard_fq;
+ err = -ENOMEM;
/* Point into the IP datagram 'data' part. */
if (!pskb_pull(skb, (u8 *) (fhdr + 1) - skb->data))
- goto err;
-
+- goto err;
+-
- if (pskb_trim_rcsum(skb, end - offset))
- goto err;
-
- /* Check for overlap with preceding fragment. */
- if (prev &&
- (prev->ip_defrag_offset + prev->len) > offset)
-- goto discard_fq;
--
+ goto discard_fq;
+
- /* Look for overlap with succeeding segment. */
- if (next && next->ip_defrag_offset < end)
+ err = pskb_trim_rcsum(skb, end - offset);
return 1;
out_oversize:
-@@ -463,10 +357,6 @@ static int ipv6_frag_rcv(struct sk_buff *skb)
+@@ -425,6 +319,7 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev,
+ rcu_read_lock();
+ __IP6_INC_STATS(net, __in6_dev_get(dev), IPSTATS_MIB_REASMFAILS);
+ rcu_read_unlock();
++ inet_frag_kill(&fq->q);
+ return -1;
+ }
+
+@@ -463,10 +358,6 @@ static int ipv6_frag_rcv(struct sk_buff *skb)
return 1;
}
iif = skb->dev ? skb->dev->ifindex : 0;
fq = fq_find(net, fhdr->identification, hdr, iif);
if (fq) {
-@@ -484,6 +374,7 @@ static int ipv6_frag_rcv(struct sk_buff *skb)
+@@ -484,6 +375,7 @@ static int ipv6_frag_rcv(struct sk_buff *skb)
if (prob_offset) {
__IP6_INC_STATS(net, __in6_dev_get_safely(skb->dev),
IPSTATS_MIB_INHDRERRORS);