| From foo@baz Fri Jan 4 20:27:35 CET 2019 |
| From: Michal Kubecek <mkubecek@suse.cz> |
| Date: Thu, 13 Dec 2018 17:23:32 +0100 |
| Subject: net: ipv4: do not handle duplicate fragments as overlapping |
| |
| From: Michal Kubecek <mkubecek@suse.cz> |
| |
| [ Upstream commit ade446403bfb79d3528d56071a84b15351a139ad ] |
| |
| Since commit 7969e5c40dfd ("ip: discard IPv4 datagrams with overlapping |
| segments.") IPv4 reassembly code drops the whole queue whenever an |
| overlapping fragment is received. However, the test is written in a way |
| which detects duplicate fragments as overlapping so that in environments |
| with many duplicate packets, fragmented packets may be undeliverable. |
| |
| Add an extra test and for (potentially) duplicate fragment, only drop the |
| new fragment rather than the whole queue. Only starting offset and length |
| are checked, not the contents of the fragments as that would be too |
| expensive. For similar reason, linear list ("run") of a rbtree node is not |
| iterated, we only check if the new fragment is a subset of the interval |
| covered by existing consecutive fragments. |
| |
| v2: instead of an exact check iterating through linear list of an rbtree |
| node, only check if the new fragment is subset of the "run" (suggested |
| by Eric Dumazet) |
| |
| Fixes: 7969e5c40dfd ("ip: discard IPv4 datagrams with overlapping segments.") |
| Signed-off-by: Michal Kubecek <mkubecek@suse.cz> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| net/ipv4/ip_fragment.c | 18 ++++++++++++------ |
| 1 file changed, 12 insertions(+), 6 deletions(-) |
| |
| --- a/net/ipv4/ip_fragment.c |
| +++ b/net/ipv4/ip_fragment.c |
| @@ -345,10 +345,10 @@ static int ip_frag_queue(struct ipq *qp, |
| struct net *net = container_of(qp->q.net, struct net, ipv4.frags); |
| struct rb_node **rbn, *parent; |
| struct sk_buff *skb1, *prev_tail; |
| + int ihl, end, skb1_run_end; |
| struct net_device *dev; |
| unsigned int fragsize; |
| int flags, offset; |
| - int ihl, end; |
| int err = -ENOENT; |
| u8 ecn; |
| |
| @@ -418,7 +418,9 @@ static int ip_frag_queue(struct ipq *qp, |
| * overlapping fragment, the entire datagram (and any constituent |
| * fragments) MUST be silently discarded. |
| * |
| - * We do the same here for IPv4 (and increment an snmp counter). |
| + * We do the same here for IPv4 (and increment an snmp counter) but |
| + * we do not want to drop the whole queue in response to a duplicate |
| + * fragment. |
| */ |
| |
| /* Find out where to put this fragment. */ |
| @@ -442,13 +444,17 @@ static int ip_frag_queue(struct ipq *qp, |
| do { |
| parent = *rbn; |
| skb1 = rb_to_skb(parent); |
| + skb1_run_end = skb1->ip_defrag_offset + |
| + FRAG_CB(skb1)->frag_run_len; |
| if (end <= skb1->ip_defrag_offset) |
| rbn = &parent->rb_left; |
| - else if (offset >= skb1->ip_defrag_offset + |
| - FRAG_CB(skb1)->frag_run_len) |
| + else if (offset >= skb1_run_end) |
| rbn = &parent->rb_right; |
| - else /* Found an overlap with skb1. */ |
| - goto discard_qp; |
| + else if (offset >= skb1->ip_defrag_offset && |
| + end <= skb1_run_end) |
| + goto err; /* No new data, potential duplicate */ |
| + else |
| + goto discard_qp; /* Found an overlap */ |
| } while (*rbn); |
| /* Here we have parent properly set, and rbn pointing to |
| * one of its NULL left/right children. Insert skb. |