ip: Use rb trees for IP frag queue
Subsystems (4)
v2 cover ip: Use rb trees for IP frag queue
This patchset
* changes IPv4 defrag behavior to match that of IPv6: overlapping fragments now cause the whole IP datagram to be discarded (suggested by David Miller): there are no legitimate use cases for overlapping fragments;
* changes IPv4 defrag queue from a list to a rb tree (suggested by Eric Dumazet): this change removes a potential attach vector.
Upcoming patches will contain similar changes for IPv6 frag queue, as well as a comprehensive IP defrag self-test (temporarily delayed).
Peter Oskolkov (3):
ip: discard IPv4 datagrams with overlapping segments.
net: modify skb_rbtree_purge to return the truesize of all purged
skbs.
ip: use rb trees for IP frag queue.
include/linux/skbuff.h | 11 +- include/net/inet_frag.h | 3 +- include/uapi/linux/snmp.h | 1 + net/core/skbuff.c | 6 +- net/ipv4/inet_fragment.c | 16 +- net/ipv4/ip_fragment.c | 239 +++++++++++------------- net/ipv4/proc.c | 1 + net/ipv6/netfilter/nf_conntrack_reasm.c | 1 + net/ipv6/reassembly.c | 1 + 9 files changed, 139 insertions(+), 140 deletions(-)
Signature · 1 line
2.18.0.597.ga71716f1ad-goog
PATCH v2 1/3 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).
--- include/uapi/linux/snmp.h | 1 + net/ipv4/ip_fragment.c | 75 ++++++++++----------------------------- net/ipv4/proc.c | 1 + 3 files changed, 21 insertions(+), 56 deletions(-)
include/uapi/linux/snmp.h +1 -0
@@ -56,6 +56,7 @@ enumIPSTATS_MIB_ECT1PKTS ,/* InECT1Pkts */ IPSTATS_MIB_ECT0PKTS ,/* InECT0Pkts */ IPSTATS_MIB_CEPKTS ,/* InCEPkts */ +IPSTATS_MIB_REASM_OVERLAPS ,/* ReasmOverlaps */ __IPSTATS_MIB_MAX };
net/ipv4/ip_fragment.c +19 -56
96 lines not on this page · raw message
net/ipv4/proc.c +1 -0
@@ -119,6 +119,7 @@ static const struct snmp_mib snmp4_ipextstats_list[] = {SNMP_MIB_ITEM ("InECT1Pkts" ,IPSTATS_MIB_ECT1PKTS ),SNMP_MIB_ITEM ("InECT0Pkts" ,IPSTATS_MIB_ECT0PKTS ),SNMP_MIB_ITEM ("InCEPkts" ,IPSTATS_MIB_CEPKTS ), +SNMP_MIB_ITEM ("ReasmOverlaps" ,IPSTATS_MIB_REASM_OVERLAPS ), SNMP_MIB_SENTINEL };
Signature · 1 line
2.18.0.597.ga71716f1ad-goog
Re: [PATCH v2 net-next 1/3] ip: discard IPv4 datagrams with overlapping segments.
Quoted 10 lines · On Thu, 2 Aug 2018 23:34:37 +0000 Peter Oskolkov <[email protected]> wrote:
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_defragselftest (not yet available uptream).Suggested-by David S. Miller <[email protected]>Signed-off-by Peter Oskolkov <[email protected]>Signed-off-by Eric Dumazet <[email protected]>Cc Florian Westphal <[email protected]>
There are a couple of relevant RFC's
RFC 1858 - Security Considerations for IP Fragment Filtering RFC 2460 - Handling of Overlapping IPv6 Fragments
PATCH v2 2/3 net: modify skb_rbtree_purge to return the truesize of all purged skbs.
In reply to Peter OskolkovTested: see the next patch is the series.
--- include/linux/skbuff.h | 2 +- net/core/skbuff.c | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-)
include/linux/skbuff.h +1 -1
@@ -2585,7 +2585,7 @@ static inline void __skb_queue_purge(struct sk_buff_head *list)kfree_skb (skb); } -void skb_rbtree_purge (struct rb_root *root); +unsigned int skb_rbtree_purge (struct rb_root *root);void *netdev_alloc_frag (unsigned int fragsz);
net/core/skbuff.c +5 -1
@@ -2858,23 +2858,27 @@ EXPORT_SYMBOL(skb_queue_purge);/** * skb_rbtree_purge - empty a skb rbtree * @root: root of the rbtree to empty +* Return value: the sum of truesizes of all purged skbs. * * Delete all buffers on an &sk_buff rbtree. Each buffer is removed from * the list and one reference dropped. This function does not take * any lock. Synchronization should be handled by the caller (e.g., TCP * out-of-order queue is protected by the socket lock). */ -void skb_rbtree_purge (struct rb_root *root) +unsigned int skb_rbtree_purge (struct rb_root *root) {struct rb_node *p =rb_first (root); +unsigned int sum =0 ;while (p) { struct sk_buff *skb = rb_entry(p, struct sk_buff, rbnode); p =rb_next (p);rb_erase (&skb->rbnode, root); + sum += skb->truesize;kfree_skb (skb); } +return sum; }/**
Signature · 1 line
2.18.0.597.ga71716f1ad-goog
PATCH v2 3/3 ip: use rb trees for IP frag queue.
In reply to Peter OskolkovSimilar to TCP OOO RX queue, it makes sense to use rb trees to store IP fragments, so that OOO fragments are inserted faster.
Tested:
- a follow-up patch contains a rather comprehensive ip defrag self-test (functional)
- ran neper udp_stream -c -H <host> -F 100 -l 300 -T 20:
netstat --statistics
Ip:
282078937 total packets received
0 forwarded
0 incoming packets discarded
946760 incoming packets delivered
18743456 requests sent out
101 fragments dropped after timeout
282077129 reassemblies required
944952 packets reassembled ok
262734239 packet reassembles failed (The numbers/stats above are somewhat better re:
reassemblies vs a kernel without this patchset. More comprehensive performance testing TBD).
--- include/linux/skbuff.h | 9 +- include/net/inet_frag.h | 3 +- net/ipv4/inet_fragment.c | 16 ++- net/ipv4/ip_fragment.c | 182 +++++++++++++----------- net/ipv6/netfilter/nf_conntrack_reasm.c | 1 + net/ipv6/reassembly.c | 1 + 6 files changed, 121 insertions(+), 91 deletions(-)
include/linux/skbuff.h +6 -3
@@ -676,13 +676,16 @@ struct sk_buff {* UDP receive path is one user. */ unsigned long dev_scratch; -int ip_defrag_offset; }; }; -struct rb_node rbnode;/* used in netem & tcp stack */ +struct rb_node rbnode;/* used in netem, ip4 defrag, and tcp stack */ struct list_head list; }; -struct sock *sk; + +union { +struct sock *sk; +int ip_defrag_offset; + };union {ktime_t tstamp;
include/net/inet_frag.h +2 -1
@@ -75,7 +75,8 @@ struct inet_frag_queue {struct timer_list timer;spinlock_t lock;refcount_t refcnt; -struct sk_buff *fragments; +struct sk_buff *fragments;/* Used in IPv6. */ +struct rb_root rb_fragments;/* Used in IPv4. */ struct sk_buff *fragments_tail;ktime_t stamp;int len;
net/ipv4/inet_fragment.c +10 -6
@@ -137,12 +137,16 @@ void inet_frag_destroy(struct inet_frag_queue *q) fp = q->fragments; nf = q->net; f = nf->f; -while (fp) { -struct sk_buff *xp = fp->next; - - sum_truesize += fp->truesize; -kfree_skb (fp); - fp = xp; +if (fp) { +do { +struct sk_buff *xp = fp->next; + + sum_truesize += fp->truesize; +kfree_skb (fp); + fp = xp; + }while (fp); + }else { + sum_truesize =skb_rbtree_purge (&q->rb_fragments); } sum = sum_truesize + f->qsize;
net/ipv4/ip_fragment.c +101 -81
321 lines not on this page · raw message
net/ipv6/netfilter/nf_conntrack_reasm.c +1 -0
@@ -463,6 +463,7 @@ nf_ct_frag6_reasm(struct frag_queue *fq, struct sk_buff *prev, struct net_devic head->csum); fq->q.fragments =NULL ; + fq->q.rb_fragments =RB_ROOT ; fq->q.fragments_tail =NULL ;return true;
net/ipv6/reassembly.c +1 -0
@@ -405,6 +405,7 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev,__IP6_INC_STATS (net,__in6_dev_get (dev),IPSTATS_MIB_REASMOKS );rcu_read_unlock (); fq->q.fragments =NULL ; + fq->q.rb_fragments =RB_ROOT ; fq->q.fragments_tail =NULL ;return 1 ;
Signature · 1 line
2.18.0.597.ga71716f1ad-goog
Quoted 28 lines · Peter Oskolkov
This patchset
* changes IPv4 defrag behavior to match that of IPv6: overlapping fragments now cause the whole IP datagram to be discarded (suggested by David Miller): there are no legitimate use cases for overlapping fragments;
* changes IPv4 defrag queue from a list to a rb tree (suggested by Eric Dumazet): this change removes a potential attach vector.Upcoming patches will contain similar changes for IPv6 frag queue, as well as a comprehensive IP defrag self-test (temporarily delayed).
Peter Oskolkov (3):
ip: discard IPv4 datagrams with overlapping segments.
net: modifyskb_rbtree_purgeto return the truesize of all purged
skbs.
ip: use rb trees for IP frag queue.include/linux/skbuff.h | 11 +- include/net/inet_frag.h | 3 +- include/uapi/linux/snmp.h | 1 + net/core/skbuff.c | 6 +- net/ipv4/inet_fragment.c | 16 +- net/ipv4/ip_fragment.c | 239 +++++++++++------------- net/ipv4/proc.c | 1 + net/ipv6/netfilter/nf_conntrack_reasm.c | 1 + net/ipv6/reassembly.c | 1 + 9 files changed, 139 insertions(+), 140 deletions(-)
Quoted 4 lines
--
2.18.0.597.ga71716f1ad-goog
Peter
I just tested your patches along with Florian's on top of net-next. Things look much better wrt this type of attack. Thanks for doing this. I'm wondering if we want to put an optional mechanism in place to limit the size of the tree in terms of skbs it can hold? Otherwise an attacker can send ~1400 8 byte frags and consume all frag memory (default high thresh is 4M) pretty easily and I believe also evict other frags which may have been pending? I am guessing this is what Florian's min MTU patches are trying to help with.
Signature · 1 line
Josh
Quoted 35 lines · Josh Hunt
Quoted 29 lines · Peter Oskolkov
This patchset
* changes IPv4 defrag behavior to match that of IPv6: overlapping fragments now cause the whole IP datagram to be discarded (suggested by David Miller): there are no legitimate use cases for overlapping fragments;
* changes IPv4 defrag queue from a list to a rb tree (suggested by Eric Dumazet): this change removes a potential attach vector.Upcoming patches will contain similar changes for IPv6 frag queue, as well as a comprehensive IP defrag self-test (temporarily delayed).
Peter Oskolkov (3):
ip: discard IPv4 datagrams with overlapping segments.
net: modifyskb_rbtree_purgeto return the truesize of all purged
skbs.
ip: use rb trees for IP frag queue.include/linux/skbuff.h | 11 +- include/net/inet_frag.h | 3 +- include/uapi/linux/snmp.h | 1 + net/core/skbuff.c | 6 +- net/ipv4/inet_fragment.c | 16 +- net/ipv4/ip_fragment.c | 239 +++++++++++------------- net/ipv4/proc.c | 1 + net/ipv6/netfilter/nf_conntrack_reasm.c | 1 + net/ipv6/reassembly.c | 1 + 9 files changed, 139 insertions(+), 140 deletions(-)Quoted 3 lines
--
2.18.0.597.ga71716f1ad-goog
Quoted 6 lines
Peter
I just tested your patches along with Florian's on top of net-next. Things look much better wrt this type of attack. Thanks for doing this. I'm wondering if we want to put an optional mechanism in place to limit the size of the tree in terms of skbs it can hold? Otherwise an attacker can send ~1400 8 byte frags and consume all frag memory (default high thresh is 4M) pretty easily and I believe also evict other frags which may have been pending? I am guessing this is what Florian's min MTU patches are trying to help with.
--
Josh
Hi Josh,
It will be really easy to limit the size of the queue/tree (e.g. based on a sysctl parameter). I can send a follow-up patch if there is a consensus that this behavior is needed/useful.
Thanks,
Peter