backport of IP fragmentation fixes
Subsystems (3)
v2 cover backport of IP fragmentation fixes
This is based on Stephen's v4.14 patches, with the necessary merge conflicts, and the lack of timer_setup() on the 4.9 baseline.
Perf results on a gigabit capable system, before and after are below.
Series can also be found here:
https://github.com/ffainelli/linux/commits/fragment-stack-v4.9-v2
Changes in v2:
- drop "net: sk_buff rbnode reorg"
- added original "ip: use rb trees for IP frag queue." commit
Before patches:
PerfTop: 180 irqs/sec kernel:78.9% exact: 0.0% [4000Hz cycles:ppp], (all, 4 CPUs)
-------------------------------------------------------------------------------
34.81% [kernel] [k] ip_defrag
4.57% [kernel] [k] arch_cpu_idle
2.09% [kernel] [k] fib_table_lookup
1.74% [kernel] [k] finish_task_switch
1.57% [kernel] [k] v7_dma_inv_range
1.47% [kernel] [k] __netif_receive_skb_core
1.06% [kernel] [k] __slab_free
1.04% [kernel] [k] __netdev_alloc_skb
0.99% [kernel] [k] ip_route_input_noref
0.96% [kernel] [k] dev_gro_receive
0.96% [kernel] [k] tick_nohz_idle_enter
0.93% [kernel] [k] bcm_sysport_poll
0.92% [kernel] [k] skb_release_data
0.91% [kernel] [k] __memzero
0.90% [kernel] [k] __free_page_frag
0.87% [kernel] [k] ip_rcv
0.77% [kernel] [k] eth_type_trans
0.71% [kernel] [k] _raw_spin_unlock_irqrestore
0.68% [kernel] [k] tick_nohz_idle_exit
0.65% [kernel] [k] bcm_sysport_rx_refillAfter patches:
PerfTop: 214 irqs/sec kernel:80.4% exact: 0.0% [4000Hz cycles:ppp], (all, 4 CPUs)
-------------------------------------------------------------------------------
6.61% [kernel] [k] arch_cpu_idle
3.77% [kernel] [k] ip_defrag
3.65% [kernel] [k] v7_dma_inv_range
3.18% [kernel] [k] fib_table_lookup
3.04% [kernel] [k] __netif_receive_skb_core
2.31% [kernel] [k] finish_task_switch
2.31% [kernel] [k] _raw_spin_unlock_irqrestore
1.65% [kernel] [k] bcm_sysport_poll
1.63% [kernel] [k] ip_route_input_noref
1.63% [kernel] [k] __memzero
1.58% [kernel] [k] __netdev_alloc_skb
1.47% [kernel] [k] tick_nohz_idle_enter
1.40% [kernel] [k] __slab_free
1.32% [kernel] [k] ip_rcv
1.32% [kernel] [k] __softirqentry_text_start
1.30% [kernel] [k] dev_gro_receive
1.23% [kernel] [k] bcm_sysport_rx_refill
1.11% [kernel] [k] tick_nohz_idle_exit
1.06% [kernel] [k] memcmp
1.02% [kernel] [k] dma_cache_maint_pageDan Carpenter (1):
ipv4: frags: precedence bug in ip_expire()
Eric Dumazet (21):
inet: frags: change inet_frags_init_net() return value
inet: frags: add a pointer to struct netns_frags
inet: frags: refactor ipfrag_init()
inet: frags: refactor ipv6_frag_init()
inet: frags: refactor lowpan_net_frag_init()
ipv6: export ip6 fragments sysctl to unprivileged users
rhashtable: add schedule points
inet: frags: use rhashtables for reassembly units
inet: frags: remove some helpers
inet: frags: get rif of inet_frag_evicting()
inet: frags: remove inet_frag_maybe_warn_overflow()
inet: frags: break the 2GB limit for frags storage
inet: frags: do not clone skb in ip_expire()
ipv6: frags: rewrite ip6_expire_frag_queue()
rhashtable: reorganize struct rhashtable layout
inet: frags: reorganize struct netns_frags
inet: frags: get rid of ipfrag_skb_cb/FRAG_CB
inet: frags: fix ip6frag_low_thresh boundary
net: speed up skb_rbtree_purge()
net: pskb_trim_rcsum() and CHECKSUM_COMPLETE are friends
net: add rb_to_skb() and other rb tree helpers
Florian Westphal (1):
ipv6: defrag: drop non-last frags smaller than min mtu
Peter Oskolkov (5):
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.
ip: add helpers to process in-order fragments faster.
ip: process in-order fragments efficiently
Taehee Yoo (1):
ip: frags: fix crash in ip_do_fragment()
Documentation/networking/ip-sysctl.txt | 13 +- include/linux/rhashtable.h | 4 +- include/linux/skbuff.h | 34 +- include/net/inet_frag.h | 133 +++--- include/net/ip.h | 1 - include/net/ipv6.h | 26 +- include/uapi/linux/snmp.h | 1 + lib/rhashtable.c | 5 +- net/core/skbuff.c | 31 +- net/ieee802154/6lowpan/6lowpan_i.h | 26 +- net/ieee802154/6lowpan/reassembly.c | 148 +++--- net/ipv4/inet_fragment.c | 379 ++++------------ net/ipv4/ip_fragment.c | 573 +++++++++++++----------- net/ipv4/proc.c | 7 +- net/ipv4/tcp_input.c | 33 +- net/ipv6/netfilter/nf_conntrack_reasm.c | 100 ++--- net/ipv6/proc.c | 5 +- net/ipv6/reassembly.c | 212 ++++----- 18 files changed, 774 insertions(+), 957 deletions(-)
Signature · 1 line
2.17.1
PATCH v2 1/29 inet: frags: change inet_frags_init_net() return value
From: Eric Dumazet <[email protected]>
We will soon initialize one rhashtable per struct netns_frags in inet_frags_init_net().
This patch changes the return value to eventually propagate an error.
--- include/net/inet_frag.h | 3 ++- net/ieee802154/6lowpan/reassembly.c | 11 ++++++++--- net/ipv4/ip_fragment.c | 12 +++++++++--- net/ipv6/netfilter/nf_conntrack_reasm.c | 12 +++++++++--- net/ipv6/reassembly.c | 11 +++++++++-- 5 files changed, 37 insertions(+), 12 deletions(-)
include/net/inet_frag.h +2 -1
@@ -103,9 +103,10 @@ struct inet_frags {int inet_frags_init (struct inet_frags *);void inet_frags_fini (struct inet_frags *); -static inline void inet_frags_init_net (struct netns_frags *nf) +static inline int inet_frags_init_net (struct netns_frags *nf) {atomic_set (&nf->mem,0 ); +return 0 ; }void inet_frags_exit_net (struct netns_frags *nf,struct inet_frags *f);
net/ieee802154/6lowpan/reassembly.c +8 -3
@@ -580,14 +580,19 @@ static int __net_init lowpan_frags_init_net(struct net *net) {struct netns_ieee802154_lowpan *ieee802154_lowpan =net_ieee802154_lowpan (net); +int res; ieee802154_lowpan->frags.high_thresh =IPV6_FRAG_HIGH_THRESH ; ieee802154_lowpan->frags.low_thresh =IPV6_FRAG_LOW_THRESH ; ieee802154_lowpan->frags.timeout =IPV6_FRAG_TIMEOUT ; -inet_frags_init_net (&ieee802154_lowpan->frags); - -return lowpan_frags_ns_sysctl_register (net); + res =inet_frags_init_net (&ieee802154_lowpan->frags); +if (res <0 ) +return res; + res =lowpan_frags_ns_sysctl_register (net); +if (res <0 ) +inet_frags_exit_net (&ieee802154_lowpan->frags, &lowpan_frags); +return res; } static void __net_exit lowpan_frags_exit_net(struct net *net)
net/ipv4/ip_fragment.c +9 -3
@@ -849,6 +849,8 @@ static void __init ip4_frags_ctl_register(void)static int __net_init ipv4_frags_init_net (struct net *net) { +int res; +/* Fragment cache limits. * * The fragment memory accounting code, (tries to) account for
@@ -874,9 +876,13 @@ static int __net_init ipv4_frags_init_net(struct net *net) net->ipv4.frags.max_dist =64 ; -inet_frags_init_net (&net->ipv4.frags); - -return ip4_frags_ns_ctl_register (net); + res =inet_frags_init_net (&net->ipv4.frags); +if (res <0 ) +return res; + res =ip4_frags_ns_ctl_register (net); +if (res <0 ) +inet_frags_exit_net (&net->ipv4.frags, &ip4_frags); +return res; } static void __net_exit ipv4_frags_exit_net(struct net *net)
net/ipv6/netfilter/nf_conntrack_reasm.c +9 -3
@@ -630,12 +630,18 @@ EXPORT_SYMBOL_GPL(nf_ct_frag6_gather);static int nf_ct_net_init (struct net *net) { +int res; + net->nf_frag.frags.high_thresh =IPV6_FRAG_HIGH_THRESH ; net->nf_frag.frags.low_thresh =IPV6_FRAG_LOW_THRESH ; net->nf_frag.frags.timeout =IPV6_FRAG_TIMEOUT ; -inet_frags_init_net (&net->nf_frag.frags); - -return nf_ct_frag6_sysctl_register (net); + res =inet_frags_init_net (&net->nf_frag.frags); +if (res <0 ) +return res; + res =nf_ct_frag6_sysctl_register (net); +if (res <0 ) +inet_frags_exit_net (&net->nf_frag.frags, &nf_frags); +return res; }static void nf_ct_net_exit (struct net *net)
net/ipv6/reassembly.c +9 -2
@@ -709,13 +709,20 @@ static void ip6_frags_sysctl_unregister(void)static int __net_init ipv6_frags_init_net (struct net *net) { +int res; + net->ipv6.frags.high_thresh =IPV6_FRAG_HIGH_THRESH ; net->ipv6.frags.low_thresh =IPV6_FRAG_LOW_THRESH ; net->ipv6.frags.timeout =IPV6_FRAG_TIMEOUT ; -inet_frags_init_net (&net->ipv6.frags); + res =inet_frags_init_net (&net->ipv6.frags); +if (res <0 ) +return res; -return ip6_frags_ns_sysctl_register (net); + res =ip6_frags_ns_sysctl_register (net); +if (res <0 ) +inet_frags_exit_net (&net->ipv6.frags, &ip6_frags); +return res; } static void __net_exit ipv6_frags_exit_net(struct net *net)
Signature · 1 line
2.17.1
PATCH v2 2/29 inet: frags: add a pointer to struct netns_frags
In reply to Florian FainelliFrom: Eric Dumazet <[email protected]>
In order to simplify the API, add a pointer to struct inet_frags. This will allow us to make things less complex.
These functions no longer have a struct inet_frags parameter :
inet_frag_destroy(struct inet_frag_queue *q /*, struct inet_frags *f */) inet_frag_put(struct inet_frag_queue *q /*, struct inet_frags *f */) inet_frag_kill(struct inet_frag_queue *q /*, struct inet_frags *f */) inet_frags_exit_net(struct netns_frags *nf /*, struct inet_frags *f */) ip6_expire_frag_queue(struct net *net, struct frag_queue *fq)
--- include/net/inet_frag.h | 11 ++++++----- include/net/ipv6.h | 3 +-- net/ieee802154/6lowpan/reassembly.c | 13 +++++++------ net/ipv4/inet_fragment.c | 17 ++++++++++------- net/ipv4/ip_fragment.c | 9 +++++---- net/ipv6/netfilter/nf_conntrack_reasm.c | 16 +++++++++------- net/ipv6/reassembly.c | 20 ++++++++++---------- 7 files changed, 48 insertions(+), 41 deletions(-)
include/net/inet_frag.h +6 -5
@@ -9,6 +9,7 @@ struct netns_frags {int high_thresh;int low_thresh;int max_dist; +struct inet_frags *f; };/**
@@ -108,20 +109,20 @@ static inline int inet_frags_init_net(struct netns_frags *nf)atomic_set (&nf->mem,0 );return 0 ; } -void inet_frags_exit_net (struct netns_frags *nf,struct inet_frags *f); +void inet_frags_exit_net (struct netns_frags *nf); -void inet_frag_kill (struct inet_frag_queue *q,struct inet_frags *f); -void inet_frag_destroy (struct inet_frag_queue *q,struct inet_frags *f); +void inet_frag_kill (struct inet_frag_queue *q); +void inet_frag_destroy (struct inet_frag_queue *q);struct inet_frag_queue *inet_frag_find (struct netns_frags *nf,struct inet_frags *f,void *key,unsigned int hash);void inet_frag_maybe_warn_overflow (struct inet_frag_queue *q,const char *prefix); -static inline void inet_frag_put (struct inet_frag_queue *q,struct inet_frags *f) +static inline void inet_frag_put (struct inet_frag_queue *q) {if (atomic_dec_and_test (&q->refcnt)) -inet_frag_destroy (q, f); +inet_frag_destroy (q); }static inline bool inet_frag_evicting (struct inet_frag_queue *q)
include/net/ipv6.h +1 -2
@@ -559,8 +559,7 @@ struct frag_queue {u8 ecn; }; -void ip6_expire_frag_queue (struct net *net,struct frag_queue *fq, -struct inet_frags *frags); +void ip6_expire_frag_queue (struct net *net,struct frag_queue *fq);static inline bool ipv6_addr_any (const struct in6_addr *a) {
net/ieee802154/6lowpan/reassembly.c +7 -6
@@ -93,10 +93,10 @@ static void lowpan_frag_expire(unsigned long data)if (fq->q.flags &INET_FRAG_COMPLETE ) goto out; -inet_frag_kill (&fq->q, &lowpan_frags); +inet_frag_kill (&fq->q); out:spin_unlock (&fq->q.lock); -inet_frag_put (&fq->q, &lowpan_frags); +inet_frag_put (&fq->q); } static inline struct lowpan_frag_queue *
@@ -229,7 +229,7 @@ static int lowpan_frag_reasm(struct lowpan_frag_queue *fq, struct sk_buff *prev,struct sk_buff *fp, *head = fq->q.fragments;int sum_truesize; -inet_frag_kill (&fq->q, &lowpan_frags); +inet_frag_kill (&fq->q);/* Make the one we just received the head. */ if (prev) {
@@ -437,7 +437,7 @@ int lowpan_frag_rcv(struct sk_buff *skb, u8 frag_type) ret =lowpan_frag_queue (fq, skb, frag_type);spin_unlock (&fq->q.lock); -inet_frag_put (&fq->q, &lowpan_frags); +inet_frag_put (&fq->q);return ret; }
@@ -585,13 +585,14 @@ static int __net_init lowpan_frags_init_net(struct net *net) ieee802154_lowpan->frags.high_thresh =IPV6_FRAG_HIGH_THRESH ; ieee802154_lowpan->frags.low_thresh =IPV6_FRAG_LOW_THRESH ; ieee802154_lowpan->frags.timeout =IPV6_FRAG_TIMEOUT ; + ieee802154_lowpan->frags.f = &lowpan_frags; res =inet_frags_init_net (&ieee802154_lowpan->frags);if (res <0 )return res; res =lowpan_frags_ns_sysctl_register (net);if (res <0 ) -inet_frags_exit_net (&ieee802154_lowpan->frags, &lowpan_frags); +inet_frags_exit_net (&ieee802154_lowpan->frags);return res; }
@@ -601,7 +602,7 @@ static void __net_exit lowpan_frags_exit_net(struct net *net)net_ieee802154_lowpan (net);lowpan_frags_ns_sysctl_unregister (net); -inet_frags_exit_net (&ieee802154_lowpan->frags, &lowpan_frags); +inet_frags_exit_net (&ieee802154_lowpan->frags); }static struct pernet_operations lowpan_frags_ops = {
net/ipv4/inet_fragment.c +10 -7
@@ -219,8 +219,9 @@ void inet_frags_fini(struct inet_frags *f) }EXPORT_SYMBOL (inet_frags_fini); -void inet_frags_exit_net (struct netns_frags *nf,struct inet_frags *f) +void inet_frags_exit_net (struct netns_frags *nf) { +struct inet_frags *f =nf->f;unsigned int seq;int i;
@@ -264,33 +265,34 @@ __acquires(hb->chain_lock)return hb; } -static inline void fq_unlink (struct inet_frag_queue *fq,struct inet_frags *f) +static inline void fq_unlink (struct inet_frag_queue *fq) {struct inet_frag_bucket *hb; - hb =get_frag_bucket_locked (fq, f); + hb =get_frag_bucket_locked (fq, fq->net->f);hlist_del (&fq->list); fq->flags |=INET_FRAG_COMPLETE ;spin_unlock (&hb->chain_lock); } -void inet_frag_kill (struct inet_frag_queue *fq,struct inet_frags *f) +void inet_frag_kill (struct inet_frag_queue *fq) {if (del_timer (&fq->timer))atomic_dec (&fq->refcnt);if (!(fq->flags &INET_FRAG_COMPLETE )) { -fq_unlink (fq, f); +fq_unlink (fq);atomic_dec (&fq->refcnt); } }EXPORT_SYMBOL (inet_frag_kill); -void inet_frag_destroy (struct inet_frag_queue *q,struct inet_frags *f) +void inet_frag_destroy (struct inet_frag_queue *q) {struct sk_buff *fp;struct netns_frags *nf;unsigned int sum, sum_truesize =0 ; +struct inet_frags *f;WARN_ON (!(q->flags &INET_FRAG_COMPLETE ));WARN_ON (del_timer (&q->timer) !=0 );
@@ -298,6 +300,7 @@ void inet_frag_destroy(struct inet_frag_queue *q, struct inet_frags *f)/* Release all fragment data. */ fp = q->fragments; nf = q->net; + f = nf->f;while (fp) {struct sk_buff *xp = fp->next;
@@ -333,7 +336,7 @@ static struct inet_frag_queue *inet_frag_intern(struct netns_frags *nf,atomic_inc (&qp->refcnt);spin_unlock (&hb->chain_lock); qp_in->flags |=INET_FRAG_COMPLETE ; -inet_frag_put (qp_in, f); +inet_frag_put (qp_in);return qp; } }
net/ipv4/ip_fragment.c +5 -4
@@ -167,7 +167,7 @@ static void ip4_frag_free(struct inet_frag_queue *q)static void ipq_put (struct ipq *ipq) { -inet_frag_put (&ipq->q, &ip4_frags); +inet_frag_put (&ipq->q); }/* Kill ipq entry. It is not destroyed immediately,
@@ -175,7 +175,7 @@ static void ipq_put(struct ipq *ipq)*/ static void ipq_kill (struct ipq *ipq) { -inet_frag_kill (&ipq->q, &ip4_frags); +inet_frag_kill (&ipq->q); }static bool frag_expire_skip_icmp (u32 user)
@@ -875,20 +875,21 @@ static int __net_init ipv4_frags_init_net(struct net *net) net->ipv4.frags.timeout =IP_FRAG_TIME ; net->ipv4.frags.max_dist =64 ; + net->ipv4.frags.f = &ip4_frags; res =inet_frags_init_net (&net->ipv4.frags);if (res <0 )return res; res =ip4_frags_ns_ctl_register (net);if (res <0 ) -inet_frags_exit_net (&net->ipv4.frags, &ip4_frags); +inet_frags_exit_net (&net->ipv4.frags);return res; } static void __net_exit ipv4_frags_exit_net(struct net *net) {ip4_frags_ns_ctl_unregister (net); -inet_frags_exit_net (&net->ipv4.frags, &ip4_frags); +inet_frags_exit_net (&net->ipv4.frags); }static struct pernet_operations ip4_frags_ops = {
net/ipv6/netfilter/nf_conntrack_reasm.c +9 -7
@@ -177,7 +177,7 @@ static void nf_ct_frag6_expire(unsigned long data) fq = container_of((struct inet_frag_queue *)data, struct frag_queue, q); net = container_of(fq->q.net, struct net, nf_frag.frags); -ip6_expire_frag_queue (net, fq, &nf_frags); +ip6_expire_frag_queue (net, fq); }/* Creation primitives. */
@@ -263,7 +263,7 @@ static int nf_ct_frag6_queue(struct frag_queue *fq, struct sk_buff *skb,* this case. -DaveM */ pr_debug ("end of fragment not rounded to 8 bytes.\n" ); -inet_frag_kill (&fq->q, &nf_frags); +inet_frag_kill (&fq->q);return -EPROTO ; }if (end > fq->q.len) {
@@ -356,7 +356,7 @@ static int nf_ct_frag6_queue(struct frag_queue *fq, struct sk_buff *skb,return 0 ; discard_fq: -inet_frag_kill (&fq->q, &nf_frags); +inet_frag_kill (&fq->q); err:return -EINVAL ; }
@@ -378,7 +378,7 @@ nf_ct_frag6_reasm(struct frag_queue *fq, struct sk_buff *prev, struct net_devicint payload_len;u8 ecn; -inet_frag_kill (&fq->q, &nf_frags); +inet_frag_kill (&fq->q);WARN_ON (head ==NULL );WARN_ON (NFCT_FRAG6_CB (head)->offset !=0 );
@@ -623,7 +623,7 @@ int nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 user) out_unlock:spin_unlock_bh (&fq->q.lock); -inet_frag_put (&fq->q, &nf_frags); +inet_frag_put (&fq->q);return ret; }EXPORT_SYMBOL_GPL (nf_ct_frag6_gather);
@@ -635,19 +635,21 @@ static int nf_ct_net_init(struct net *net) net->nf_frag.frags.high_thresh =IPV6_FRAG_HIGH_THRESH ; net->nf_frag.frags.low_thresh =IPV6_FRAG_LOW_THRESH ; net->nf_frag.frags.timeout =IPV6_FRAG_TIMEOUT ; + net->nf_frag.frags.f = &nf_frags; + res =inet_frags_init_net (&net->nf_frag.frags);if (res <0 )return res; res =nf_ct_frag6_sysctl_register (net);if (res <0 ) -inet_frags_exit_net (&net->nf_frag.frags, &nf_frags); +inet_frags_exit_net (&net->nf_frag.frags);return res; }static void nf_ct_net_exit (struct net *net) {nf_ct_frags6_sysctl_unregister (net); -inet_frags_exit_net (&net->nf_frag.frags, &nf_frags); +inet_frags_exit_net (&net->nf_frag.frags); }static struct pernet_operations nf_ct_net_ops = {
net/ipv6/reassembly.c +10 -10
@@ -128,8 +128,7 @@ void ip6_frag_init(struct inet_frag_queue *q, const void *a) }EXPORT_SYMBOL (ip6_frag_init); -void ip6_expire_frag_queue (struct net *net,struct frag_queue *fq, -struct inet_frags *frags) +void ip6_expire_frag_queue (struct net *net,struct frag_queue *fq) {struct net_device *dev =NULL ;
@@ -138,7 +137,7 @@ void ip6_expire_frag_queue(struct net *net, struct frag_queue *fq,if (fq->q.flags &INET_FRAG_COMPLETE ) goto out; -inet_frag_kill (&fq->q, frags); +inet_frag_kill (&fq->q);rcu_read_lock (); dev =dev_get_by_index_rcu (net, fq->iif);
@@ -166,7 +165,7 @@ void ip6_expire_frag_queue(struct net *net, struct frag_queue *fq,rcu_read_unlock (); out:spin_unlock (&fq->q.lock); -inet_frag_put (&fq->q, frags); +inet_frag_put (&fq->q); }EXPORT_SYMBOL (ip6_expire_frag_queue);
@@ -178,7 +177,7 @@ static void ip6_frag_expire(unsigned long data) fq = container_of((struct inet_frag_queue *)data, struct frag_queue, q); net = container_of(fq->q.net, struct net, ipv6.frags); -ip6_expire_frag_queue (net, fq, &ip6_frags); +ip6_expire_frag_queue (net, fq); } static struct frag_queue *
@@ -359,7 +358,7 @@ static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb,return -1 ; discard_fq: -inet_frag_kill (&fq->q, &ip6_frags); +inet_frag_kill (&fq->q); err:__IP6_INC_STATS (net,ip6_dst_idev (skb_dst (skb)),IPSTATS_MIB_REASMFAILS );
@@ -386,7 +385,7 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev,int sum_truesize;u8 ecn; -inet_frag_kill (&fq->q, &ip6_frags); +inet_frag_kill (&fq->q); ecn = ip_frag_ecn_table[fq->ecn];if (unlikely (ecn ==0xff ))
@@ -563,7 +562,7 @@ static int ipv6_frag_rcv(struct sk_buff *skb) ret =ip6_frag_queue (fq, skb, fhdr,IP6CB (skb)->nhoff);spin_unlock (&fq->q.lock); -inet_frag_put (&fq->q, &ip6_frags); +inet_frag_put (&fq->q);return ret; }
@@ -714,6 +713,7 @@ static int __net_init ipv6_frags_init_net(struct net *net) net->ipv6.frags.high_thresh =IPV6_FRAG_HIGH_THRESH ; net->ipv6.frags.low_thresh =IPV6_FRAG_LOW_THRESH ; net->ipv6.frags.timeout =IPV6_FRAG_TIMEOUT ; + net->ipv6.frags.f = &ip6_frags; res =inet_frags_init_net (&net->ipv6.frags);if (res <0 )
@@ -721,14 +721,14 @@ static int __net_init ipv6_frags_init_net(struct net *net) res =ip6_frags_ns_sysctl_register (net);if (res <0 ) -inet_frags_exit_net (&net->ipv6.frags, &ip6_frags); +inet_frags_exit_net (&net->ipv6.frags);return res; } static void __net_exit ipv6_frags_exit_net(struct net *net) {ip6_frags_ns_sysctl_unregister (net); -inet_frags_exit_net (&net->ipv6.frags, &ip6_frags); +inet_frags_exit_net (&net->ipv6.frags); }static struct pernet_operations ip6_frags_ops = {
Signature · 1 line
2.17.1
PATCH v2 3/29 inet: frags: refactor ipfrag_init()
In reply to Florian FainelliFrom: Eric Dumazet <[email protected]>
We need to call inet_frags_init() before register_pernet_subsys(),
as a prereq for following patch ("inet: frags: use rhashtables for reassembly units")
--- net/ipv4/ip_fragment.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
net/ipv4/ip_fragment.c +2 -2
@@ -899,8 +899,6 @@ static struct pernet_operations ip4_frags_ops = {void __init ipfrag_init (void ) { -ip4_frags_ctl_register (); -register_pernet_subsys (&ip4_frags_ops); ip4_frags.hashfn = ip4_hashfn; ip4_frags.constructor = ip4_frag_init; ip4_frags.destructor = ip4_frag_free;
@@ -910,4 +908,6 @@ void __init ipfrag_init(void) ip4_frags.frags_cache_name = ip_frag_cache_name;if (inet_frags_init (&ip4_frags))panic ("IP: failed to allocate ip4_frags cache\n" ); +ip4_frags_ctl_register (); +register_pernet_subsys (&ip4_frags_ops); }
Signature · 1 line
2.17.1
PATCH v2 4/29 inet: frags: refactor ipv6_frag_init()
In reply to Florian FainelliFrom: Eric Dumazet <[email protected]>
We want to call inet_frags_init() earlier.
This is a prereq to "inet: frags: use rhashtables for reassembly units"
--- net/ipv6/reassembly.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-)
net/ipv6/reassembly.c +14 -11
@@ -740,10 +740,21 @@ int __init ipv6_frag_init(void) {int ret; - ret =inet6_add_protocol (&frag_protocol,IPPROTO_FRAGMENT ); + ip6_frags.hashfn = ip6_hashfn; + ip6_frags.constructor = ip6_frag_init; + ip6_frags.destructor =NULL ; + ip6_frags.qsize =sizeof (struct frag_queue ); + ip6_frags.match = ip6_frag_match; + ip6_frags.frag_expire = ip6_frag_expire; + ip6_frags.frags_cache_name = ip6_frag_cache_name; + ret =inet_frags_init (&ip6_frags);if (ret) goto out; + ret =inet6_add_protocol (&frag_protocol,IPPROTO_FRAGMENT ); +if (ret) + goto err_protocol; + ret =ip6_frags_sysctl_register ();if (ret) goto err_sysctl;
@@ -752,16 +763,6 @@ int __init ipv6_frag_init(void)if (ret) goto err_pernet; - ip6_frags.hashfn = ip6_hashfn; - ip6_frags.constructor = ip6_frag_init; - ip6_frags.destructor =NULL ; - ip6_frags.qsize =sizeof (struct frag_queue ); - ip6_frags.match = ip6_frag_match; - ip6_frags.frag_expire = ip6_frag_expire; - ip6_frags.frags_cache_name = ip6_frag_cache_name; - ret =inet_frags_init (&ip6_frags); -if (ret) - goto err_pernet; out:return ret;
@@ -769,6 +770,8 @@ int __init ipv6_frag_init(void)ip6_frags_sysctl_unregister (); err_sysctl:inet6_del_protocol (&frag_protocol,IPPROTO_FRAGMENT ); +err_protocol: +inet_frags_fini (&ip6_frags); goto out; }
Signature · 1 line
2.17.1
PATCH v2 5/29 inet: frags: refactor lowpan_net_frag_init()
In reply to Florian FainelliFrom: Eric Dumazet <[email protected]>
We want to call lowpan_net_frag_init() earlier. Similar to commit "inet: frags: refactor ipv6_frag_init()"
This is a prereq to "inet: frags: use rhashtables for reassembly units"
--- net/ieee802154/6lowpan/reassembly.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-)
net/ieee802154/6lowpan/reassembly.c +11 -9
@@ -614,14 +614,6 @@ int __init lowpan_net_frag_init(void) {int ret; - ret =lowpan_frags_sysctl_register (); -if (ret) -return ret; - - ret =register_pernet_subsys (&lowpan_frags_ops); -if (ret) - goto err_pernet; - lowpan_frags.hashfn = lowpan_hashfn; lowpan_frags.constructor = lowpan_frag_init; lowpan_frags.destructor =NULL ;
@@ -631,11 +623,21 @@ int __init lowpan_net_frag_init(void) lowpan_frags.frags_cache_name = lowpan_frags_cache_name; ret =inet_frags_init (&lowpan_frags);if (ret) - goto err_pernet; + goto out; + ret =lowpan_frags_sysctl_register (); +if (ret) + goto err_sysctl; + + ret =register_pernet_subsys (&lowpan_frags_ops); +if (ret) + goto err_pernet; +out:return ret; err_pernet:lowpan_frags_sysctl_unregister (); +err_sysctl: +inet_frags_fini (&lowpan_frags);return ret; }
Signature · 1 line
2.17.1
PATCH v2 6/29 ipv6: export ip6 fragments sysctl to unprivileged users
In reply to Florian FainelliFrom: Eric Dumazet <[email protected]>
IPv4 was changed in commit 52a773d645e9 ("net: Export ip fragment sysctl to unprivileged users")
The only sysctl that is not per-netns is not used :ip6frag_secret_interval
--- net/ipv6/reassembly.c | 4 ---- 1 file changed, 4 deletions(-)
net/ipv6/reassembly.c +0 -4
@@ -643,10 +643,6 @@ static int __net_init ip6_frags_ns_sysctl_register(struct net *net) table[1 ].data = &net->ipv6.frags.low_thresh; table[1 ].extra2 = &net->ipv6.frags.high_thresh; table[2 ].data = &net->ipv6.frags.timeout; - -/* Don't export sysctls to unprivileged users */ -if (net->user_ns != &init_user_ns) - table[0 ].procname =NULL ; } hdr =register_net_sysctl (net,"net/ipv6" , table);
Signature · 1 line
2.17.1
PATCH v2 7/29 rhashtable: add schedule points
In reply to Florian FainelliFrom: Eric Dumazet <[email protected]>
Rehashing and destroying large hash table takes a lot of time, and happens in process context. It is safe to add cond_resched() in rhashtable_rehash_table() and rhashtable_free_and_destroy()
--- lib/rhashtable.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
lib/rhashtable.c +4 -1
@@ -251,8 +251,10 @@ static int rhashtable_rehash_table(struct rhashtable *ht)if (!new_tbl)return 0 ; -for (old_hash =0 ; old_hash < old_tbl->size; old_hash++) +for (old_hash =0 ; old_hash < old_tbl->size; old_hash++) {rhashtable_rehash_chain (ht, old_hash); +cond_resched (); + }/* Publish the new table pointer. */ rcu_assign_pointer (ht->tbl, new_tbl);
@@ -993,6 +995,7 @@ void rhashtable_free_and_destroy(struct rhashtable *ht,for (i =0 ; i < tbl->size; i++) {struct rhash_head *pos, *next; +cond_resched (); for (pos = rht_dereference(tbl->buckets[i], ht), next = !rht_is_a_nulls(pos) ? rht_dereference(pos->next, ht) : NULL;
Signature · 1 line
2.17.1
PATCH v2 8/29 inet: frags: use rhashtables for reassembly units
In reply to Florian FainelliFrom: Eric Dumazet <[email protected]>
Some applications still rely on IP fragmentation, and to be fair linux reassembly unit is not working under any serious load.
It uses static hash tables of 1024 buckets, and up to 128 items per bucket (!!!)
A work queue is supposed to garbage collect items when host is under memory pressure, and doing a hash rebuild, changing seed used in hash computations.
This work queue blocks softirqs for up to 25 ms when doing a hash rebuild, occurring every 5 seconds if host is under fire.
Then there is the problem of sharing this hash table for all netns.
It is time to switch to rhashtables, and allocate one of them per netns to speedup netns dismantle, since this is a critical metric these days.
Lookup is now using RCU. A followup patch will even remove the refcount hold/release left from prior implementation and save a couple of atomic operations.
Before this patch, 16 cpus (16 RX queue NIC) could not handle more than 1 Mpps frags DDOS.
After the patch, I reach 9 Mpps without any tuning, and can use up to 2GB of storage for the fragments (exact number depends on frags being evicted after timeout)
$ grep FRAG /proc/net/sockstat FRAG: inuse 1966916 memory 2140004608
A followup patch will change the limits for 64bit arches.
--- Documentation/networking/ip-sysctl.txt | 7 +- include/net/inet_frag.h | 81 +++--- include/net/ipv6.h | 16 +- net/ieee802154/6lowpan/6lowpan_i.h | 26 +- net/ieee802154/6lowpan/reassembly.c | 91 +++--- net/ipv4/inet_fragment.c | 349 +++++------------------- net/ipv4/ip_fragment.c | 112 ++++---- net/ipv6/netfilter/nf_conntrack_reasm.c | 51 +--- net/ipv6/reassembly.c | 110 ++++---- 9 files changed, 267 insertions(+), 576 deletions(-)
Documentation/networking/ip-sysctl.txt +2 -5
@@ -123,13 +123,10 @@ min_adv_mss - INTEGER IP Fragmentation: ipfrag_high_thresh - INTEGER - Maximum memory used to reassemble IP fragments. When - ipfrag_high_thresh bytes of memory is allocated for this purpose, - the fragment handler will toss packets until ipfrag_low_thresh - is reached. This also serves as a maximum limit to namespaces - different from the initial one. + Maximum memory used to reassemble IP fragments. ipfrag_low_thresh - INTEGER + (Obsolete since linux-4.17) Maximum memory used to reassemble IP fragments before the kernel begins to remove incomplete fragment queues to free up resources. The kernel still accepts new fragments for defragmentation.
include/net/inet_frag.h +37 -44
141 lines not on this page · raw message
include/net/ipv6.h +1 -15
@@ -530,17 +530,8 @@ enum ip6_defrag_users { __IP6_DEFRAG_CONNTRACK_BRIDGE_IN = IP6_DEFRAG_CONNTRACK_BRIDGE_IN + USHRT_MAX, }; -struct ip6_create_arg { -__be32 id; -u32 user; -const struct in6_addr *src; -const struct in6_addr *dst; -int iif; -u8 ecn; -}; -void ip6_frag_init (struct inet_frag_queue *q,const void *a); -bool ip6_frag_match (const struct inet_frag_queue *q,const void *a); +extern const struct rhashtable_params ip6_rhash_params;/* * Equivalent of ipv4 struct ip
@@ -548,11 +539,6 @@ bool ip6_frag_match(const struct inet_frag_queue *q, const void *a);struct frag_queue {struct inet_frag_queue q; -__be32 id;/* fragment id */ -u32 user; -struct in6_addr saddr; -struct in6_addr daddr; -int iif;unsigned int csum;__u16 nhoffset;
net/ieee802154/6lowpan/6lowpan_i.h +4 -22
@@ -16,37 +16,19 @@ typedef unsigned __bitwise__ lowpan_rx_result;#define LOWPAN_DISPATCH_FRAG1 0xc0#define LOWPAN_DISPATCH_FRAGN 0xe0 -struct lowpan_create_arg { +struct frag_lowpan_compare_key {u16 tag;u16 d_size; -const struct ieee802154_addr *src; -const struct ieee802154_addr *dst; +const struct ieee802154_addr src; +const struct ieee802154_addr dst; }; -/* Equivalent of ipv4 struct ip +/* Equivalent of ipv4 struct ipq */ struct lowpan_frag_queue {struct inet_frag_queue q; - -u16 tag; -u16 d_size; -struct ieee802154_addr saddr; -struct ieee802154_addr daddr; }; -static inline u32 ieee802154_addr_hash (const struct ieee802154_addr *a) -{ -switch (a->mode) { -case IEEE802154_ADDR_LONG : -return (((__force u64 )a->extended_addr) >>32 ) ^ - (((__force u64 )a->extended_addr) &0xffffffff ); -case IEEE802154_ADDR_SHORT : -return (__force u32)(a->short_addr + (a->pan_id <<16 )); -default : -return 0 ; - } -} -int lowpan_frag_rcv (struct sk_buff *skb,const u8 frag_type);void lowpan_net_frag_exit (void );int lowpan_net_frag_init (void );
net/ieee802154/6lowpan/reassembly.c +42 -49
127 lines not on this page · raw message
net/ipv4/inet_fragment.c +68 -281
442 lines not on this page · raw message
net/ipv4/ip_fragment.c +49 -63
168 lines not on this page · raw message
net/ipv6/netfilter/nf_conntrack_reasm.c +13 -38
83 lines not on this page · raw message
net/ipv6/reassembly.c +51 -59
162 lines not on this page · raw message
Signature · 1 line
2.17.1
PATCH v2 9/29 inet: frags: remove some helpers
In reply to Florian FainelliFrom: Eric Dumazet <[email protected]>
Remove sum_frag_mem_limit(), ip_frag_mem() & ip6_frag_mem()
Also since we use rhashtable we can bring back the number of fragments in "grep FRAG /proc/net/sockstat /proc/net/sockstat6" that was removed in commit 434d305405ab ("inet: frag: don't account number of fragment queues")
--- include/net/inet_frag.h | 5 ----- include/net/ip.h | 1 - include/net/ipv6.h | 7 ------- net/ipv4/ip_fragment.c | 5 ----- net/ipv4/proc.c | 6 +++--- net/ipv6/proc.c | 5 +++-- 6 files changed, 6 insertions(+), 23 deletions(-)
include/net/inet_frag.h +0 -5
@@ -140,11 +140,6 @@ static inline void add_frag_mem_limit(struct netns_frags *nf, int i)atomic_add (i, &nf->mem); } -static inline int sum_frag_mem_limit (struct netns_frags *nf) -{ -return atomic_read (&nf->mem); -} -/* RFC 3168 support : * We want to check ECN values of all fragments, do detect invalid combinations. * In ipq->ecn, we store the OR value of each ip4_frag_ecn() fragment value.
include/net/ip.h +0 -1
@@ -548,7 +548,6 @@ static inline struct sk_buff *ip_check_defrag(struct net *net, struct sk_buff *s return skb; }#endif -int ip_frag_mem (struct net *net);/* * Functions provided by ip_forward.c
include/net/ipv6.h +0 -7
@@ -330,13 +330,6 @@ static inline bool ipv6_accept_ra(struct inet6_dev *idev) idev->cnf.accept_ra; } -#if IS_ENABLED (CONFIG_IPV6 ) -static inline int ip6_frag_mem (struct net *net) -{ -return sum_frag_mem_limit (&net->ipv6.frags); -} -#endif -#define IPV6_FRAG_HIGH_THRESH (4 * 1024*1024)/* 4194304 */ #define IPV6_FRAG_LOW_THRESH (3 * 1024*1024)/* 3145728 */ #define IPV6_FRAG_TIMEOUT (60 * HZ)/* 60 seconds */
net/ipv4/ip_fragment.c +0 -5
@@ -82,11 +82,6 @@ static u8 ip4_frag_ecn(u8 tos)static struct inet_frags ip4_frags; -int ip_frag_mem (struct net *net) -{ -return sum_frag_mem_limit (&net->ipv4.frags); -} -static int ip_frag_reasm (struct ipq *qp,struct sk_buff *prev,struct net_device *dev);
net/ipv4/proc.c +3 -3
@@ -54,7 +54,6 @@static int sockstat_seq_show (struct seq_file *seq,void *v) {struct net *net = seq->private; -unsigned int frag_mem;int orphans, sockets;local_bh_disable ();
@@ -74,8 +73,9 @@ static int sockstat_seq_show(struct seq_file *seq, void *v) sock_prot_inuse_get(net, &udplite_prot));seq_printf (seq,"RAW: inuse %d\n" ,sock_prot_inuse_get (net, &raw_prot)); - frag_mem =ip_frag_mem (net); -seq_printf (seq,"FRAG: inuse %u memory %u\n" , !!frag_mem, frag_mem); +seq_printf (seq,"FRAG: inuse %u memory %u\n" , +atomic_read (&net->ipv4.frags.rhashtable.nelems), +frag_mem_limit (&net->ipv4.frags));return 0 ; }
net/ipv6/proc.c +3 -2
@@ -38,7 +38,6 @@static int sockstat6_seq_show (struct seq_file *seq,void *v) {struct net *net = seq->private; -unsigned int frag_mem =ip6_frag_mem (net);seq_printf (seq,"TCP6: inuse %d\n" ,sock_prot_inuse_get (net, &tcpv6_prot));
@@ -48,7 +47,9 @@ static int sockstat6_seq_show(struct seq_file *seq, void *v) sock_prot_inuse_get(net, &udplitev6_prot));seq_printf (seq,"RAW6: inuse %d\n" ,sock_prot_inuse_get (net, &rawv6_prot)); -seq_printf (seq,"FRAG6: inuse %u memory %u\n" , !!frag_mem, frag_mem); +seq_printf (seq,"FRAG6: inuse %u memory %u\n" , +atomic_read (&net->ipv6.frags.rhashtable.nelems), +frag_mem_limit (&net->ipv6.frags));return 0 ; }
Signature · 1 line
2.17.1
PATCH v2 10/29 inet: frags: get rif of inet_frag_evicting()
In reply to Florian FainelliFrom: Eric Dumazet <[email protected]>
This refactors ip_expire() since one indentation level is removed.
Note: in the future, we should try hard to avoid the skb_clone() since this is a serious performance cost.
Under DDOS, the ICMP message wont be sent because of rate limits.
Fact that ip6_expire_frag_queue() does not use skb_clone() is disturbing too. Presumably IPv6 should have the same issue than the one we fixed in commit ec4fbd64751d ("inet: frag: release spinlock before calling icmp_send()")
--- include/net/inet_frag.h | 5 ---- net/ipv4/ip_fragment.c | 65 ++++++++++++++++++++--------------------- net/ipv6/reassembly.c | 4 --- 3 files changed, 32 insertions(+), 42 deletions(-)
include/net/inet_frag.h +0 -5
@@ -118,11 +118,6 @@ static inline void inet_frag_put(struct inet_frag_queue *q)inet_frag_destroy (q); } -static inline bool inet_frag_evicting (struct inet_frag_queue *q) -{ -return false; -} -/* Memory Tracking Functions. */ static inline int frag_mem_limit (struct netns_frags *nf)
net/ipv4/ip_fragment.c +32 -33
85 lines not on this page · raw message
net/ipv6/reassembly.c +0 -4
@@ -106,10 +106,6 @@ void ip6_expire_frag_queue(struct net *net, struct frag_queue *fq) goto out_rcu_unlock;__IP6_INC_STATS (net,__in6_dev_get (dev),IPSTATS_MIB_REASMFAILS ); - -if (inet_frag_evicting (&fq->q)) - goto out_rcu_unlock; -__IP6_INC_STATS (net,__in6_dev_get (dev),IPSTATS_MIB_REASMTIMEOUT );/* Don't send error if the first segment did not arrive. */
Signature · 1 line
2.17.1
PATCH v2 11/29 inet: frags: remove inet_frag_maybe_warn_overflow()
In reply to Florian FainelliFrom: Eric Dumazet <[email protected]>
This function is obsolete, after rhashtable addition to inet defrag.
--- include/net/inet_frag.h | 2 -- net/ieee802154/6lowpan/reassembly.c | 5 ++--- net/ipv4/inet_fragment.c | 11 ----------- net/ipv4/ip_fragment.c | 5 ++--- net/ipv6/netfilter/nf_conntrack_reasm.c | 5 ++--- net/ipv6/reassembly.c | 5 ++--- 6 files changed, 8 insertions(+), 25 deletions(-)
include/net/inet_frag.h +0 -2
@@ -109,8 +109,6 @@ void inet_frags_exit_net(struct netns_frags *nf);void inet_frag_kill (struct inet_frag_queue *q);void inet_frag_destroy (struct inet_frag_queue *q);struct inet_frag_queue *inet_frag_find (struct netns_frags *nf,void *key); -void inet_frag_maybe_warn_overflow (struct inet_frag_queue *q, -const char *prefix);static inline void inet_frag_put (struct inet_frag_queue *q) {
net/ieee802154/6lowpan/reassembly.c +2 -3
@@ -83,10 +83,9 @@ fq_find(struct net *net, const struct lowpan_802154_cb *cb,struct inet_frag_queue *q; q =inet_frag_find (&ieee802154_lowpan->frags, &key); -if (IS_ERR_OR_NULL (q)) { -inet_frag_maybe_warn_overflow (q,pr_fmt ()); +if (!q)return NULL ; - } + return container_of(q, struct lowpan_frag_queue, q); }
net/ipv4/inet_fragment.c +0 -11
@@ -217,14 +217,3 @@ struct inet_frag_queue *inet_frag_find(struct netns_frags *nf, void *key)return inet_frag_create (nf, key); }EXPORT_SYMBOL (inet_frag_find); - -void inet_frag_maybe_warn_overflow (struct inet_frag_queue *q, -const char *prefix) -{ -static const char msg[] ="inet_frag_find: Fragment hash bucket" -" list length grew over limit. Dropping fragment.\n" ; - -if (PTR_ERR (q) == -ENOBUFS ) -net_dbg_ratelimited ("%s%s" , prefix, msg); -} -EXPORT_SYMBOL (inet_frag_maybe_warn_overflow);
net/ipv4/ip_fragment.c +2 -3
@@ -219,10 +219,9 @@ static struct ipq *ip_find(struct net *net, struct iphdr *iph,struct inet_frag_queue *q; q =inet_frag_find (&net->ipv4.frags, &key); -if (IS_ERR_OR_NULL (q)) { -inet_frag_maybe_warn_overflow (q,pr_fmt ()); +if (!q)return NULL ; - } + return container_of(q, struct ipq, q); }
net/ipv6/netfilter/nf_conntrack_reasm.c +2 -3
@@ -177,10 +177,9 @@ static struct frag_queue *fq_find(struct net *net, __be32 id, u32 user,struct inet_frag_queue *q; q =inet_frag_find (&net->nf_frag.frags, &key); -if (IS_ERR_OR_NULL (q)) { -inet_frag_maybe_warn_overflow (q,pr_fmt ()); +if (!q)return NULL ; - } + return container_of(q, struct frag_queue, q); }
net/ipv6/reassembly.c +2 -3
@@ -154,10 +154,9 @@ fq_find(struct net *net, __be32 id, const struct ipv6hdr *hdr, int iif) key.iif =0 ; q =inet_frag_find (&net->ipv6.frags, &key); -if (IS_ERR_OR_NULL (q)) { -inet_frag_maybe_warn_overflow (q,pr_fmt ()); +if (!q)return NULL ; - } + return container_of(q, struct frag_queue, q); }
Signature · 1 line
2.17.1
PATCH v2 12/29 inet: frags: break the 2GB limit for frags storage
In reply to Florian FainelliFrom: Eric Dumazet <[email protected]>
Some users are willing to provision huge amounts of memory to be able to perform reassembly reasonnably well under pressure.
Current memory tracking is using one atomic_t and integers.
Switch to atomic_long_t so that 64bit arches can use more than 2GB, without any cost for 32bit arches.
Note that this patch avoids an overflow error, if high_thresh was set to ~2GB, since this test in inet_frag_alloc() was never true :
if (... || frag_mem_limit(nf) > nf->high_thresh)
Tested:
$ echo 16000000000 >/proc/sys/net/ipv4/ipfrag_high_thresh
<frag DDOS>
$ grep FRAG /proc/net/sockstat FRAG: inuse 14705885 memory 16000002880
$ nstat -n ; sleep 1 ; nstat | grep Reas IpReasmReqds 3317150 0.0 IpReasmFails 3317112 0.0
--- Documentation/networking/ip-sysctl.txt | 4 ++-- include/net/inet_frag.h | 20 ++++++++++---------- net/ieee802154/6lowpan/reassembly.c | 10 +++++----- net/ipv4/ip_fragment.c | 10 +++++----- net/ipv4/proc.c | 2 +- net/ipv6/netfilter/nf_conntrack_reasm.c | 10 +++++----- net/ipv6/proc.c | 2 +- net/ipv6/reassembly.c | 6 +++--- 8 files changed, 32 insertions(+), 32 deletions(-)
Documentation/networking/ip-sysctl.txt +2 -2
@@ -122,10 +122,10 @@ min_adv_mss - INTEGER IP Fragmentation: -ipfrag_high_thresh - INTEGER +ipfrag_high_thresh - LONG INTEGER Maximum memory used to reassemble IP fragments. -ipfrag_low_thresh - INTEGER +ipfrag_low_thresh - LONG INTEGER (Obsolete since linux-4.17) Maximum memory used to reassemble IP fragments before the kernel begins to remove incomplete fragment queues to free up resources.
include/net/inet_frag.h +10 -10
@@ -7,11 +7,11 @@ struct netns_frags {struct rhashtable rhashtable____cacheline_aligned_in_smp ;/* Keep atomic mem on separate cachelines in structs that include it */ -atomic_t mem____cacheline_aligned_in_smp ; +atomic_long_t mem____cacheline_aligned_in_smp ;/* sysctls */ +long high_thresh; +long low_thresh;int timeout; -int high_thresh; -int low_thresh;int max_dist;struct inet_frags *f; };
@@ -101,7 +101,7 @@ void inet_frags_fini(struct inet_frags *);static inline int inet_frags_init_net (struct netns_frags *nf) { -atomic_set (&nf->mem,0 ); +atomic_long_set (&nf->mem,0 );return rhashtable_init (&nf->rhashtable, &nf->f->rhash_params); }void inet_frags_exit_net (struct netns_frags *nf);
@@ -118,19 +118,19 @@ static inline void inet_frag_put(struct inet_frag_queue *q)/* Memory Tracking Functions. */ -static inline int frag_mem_limit (struct netns_frags *nf) +static inline long frag_mem_limit (const struct netns_frags *nf) { -return atomic_read (&nf->mem); +return atomic_long_read (&nf->mem); } -static inline void sub_frag_mem_limit (struct netns_frags *nf,int i) +static inline void sub_frag_mem_limit (struct netns_frags *nf,long val) { -atomic_sub (i, &nf->mem); +atomic_long_sub (val, &nf->mem); } -static inline void add_frag_mem_limit (struct netns_frags *nf,int i) +static inline void add_frag_mem_limit (struct netns_frags *nf,long val) { -atomic_add (i, &nf->mem); +atomic_long_add (val, &nf->mem); }/* RFC 3168 support :
net/ieee802154/6lowpan/reassembly.c +5 -5
@@ -410,23 +410,23 @@ int lowpan_frag_rcv(struct sk_buff *skb, u8 frag_type) }#ifdef CONFIG_SYSCTL -static int zero; +static long zero;static struct ctl_table lowpan_frags_ns_ctl_table[] = { { .procname ="6lowpanfrag_high_thresh" , .data = &init_net.ieee802154_lowpan.frags.high_thresh, - .maxlen =sizeof (int ), + .maxlen =sizeof (unsigned long ), .mode =0644 , - .proc_handler = proc_dointvec_minmax, + .proc_handler = proc_doulongvec_minmax, .extra1 = &init_net.ieee802154_lowpan.frags.low_thresh }, { .procname ="6lowpanfrag_low_thresh" , .data = &init_net.ieee802154_lowpan.frags.low_thresh, - .maxlen =sizeof (int ), + .maxlen =sizeof (unsigned long ), .mode =0644 , - .proc_handler = proc_dointvec_minmax, + .proc_handler = proc_doulongvec_minmax, .extra1 = &zero, .extra2 = &init_net.ieee802154_lowpan.frags.high_thresh },
net/ipv4/ip_fragment.c +5 -5
@@ -681,23 +681,23 @@ struct sk_buff *ip_check_defrag(struct net *net, struct sk_buff *skb, u32 user)EXPORT_SYMBOL (ip_check_defrag);#ifdef CONFIG_SYSCTL -static int zero; +static long zero;static struct ctl_table ip4_frags_ns_ctl_table[] = { { .procname ="ipfrag_high_thresh" , .data = &init_net.ipv4.frags.high_thresh, - .maxlen =sizeof (int ), + .maxlen =sizeof (unsigned long ), .mode =0644 , - .proc_handler = proc_dointvec_minmax, + .proc_handler = proc_doulongvec_minmax, .extra1 = &init_net.ipv4.frags.low_thresh }, { .procname ="ipfrag_low_thresh" , .data = &init_net.ipv4.frags.low_thresh, - .maxlen =sizeof (int ), + .maxlen =sizeof (unsigned long ), .mode =0644 , - .proc_handler = proc_dointvec_minmax, + .proc_handler = proc_doulongvec_minmax, .extra1 = &zero, .extra2 = &init_net.ipv4.frags.high_thresh },
net/ipv4/proc.c +1 -1
@@ -73,7 +73,7 @@ static int sockstat_seq_show(struct seq_file *seq, void *v) sock_prot_inuse_get(net, &udplite_prot));seq_printf (seq,"RAW: inuse %d\n" ,sock_prot_inuse_get (net, &raw_prot)); -seq_printf (seq,"FRAG: inuse %u memory %u\n" , +seq_printf (seq,"FRAG: inuse %u memory %lu\n" ,atomic_read (&net->ipv4.frags.rhashtable.nelems),frag_mem_limit (&net->ipv4.frags));return 0 ;
net/ipv6/netfilter/nf_conntrack_reasm.c +5 -5
@@ -63,7 +63,7 @@ struct nf_ct_frag6_skb_cbstatic struct inet_frags nf_frags; #ifdef CONFIG_SYSCTL -static int zero; +static long zero; static struct ctl_table nf_ct_frag6_sysctl_table[] = { {
@@ -76,18 +76,18 @@ static struct ctl_table nf_ct_frag6_sysctl_table[] = { { .procname = "nf_conntrack_frag6_low_thresh", .data = &init_net.nf_frag.frags.low_thresh, - .maxlen = sizeof(unsigned int), + .maxlen = sizeof(unsigned long), .mode = 0644, - .proc_handler = proc_dointvec_minmax, + .proc_handler = proc_doulongvec_minmax, .extra1 = &zero, .extra2 = &init_net.nf_frag.frags.high_thresh }, { .procname = "nf_conntrack_frag6_high_thresh", .data = &init_net.nf_frag.frags.high_thresh, - .maxlen = sizeof(unsigned int), + .maxlen = sizeof(unsigned long), .mode = 0644, - .proc_handler = proc_dointvec_minmax, + .proc_handler = proc_doulongvec_minmax, .extra1 = &init_net.nf_frag.frags.low_thresh }, { }
net/ipv6/proc.c +1 -1
@@ -47,7 +47,7 @@ static int sockstat6_seq_show(struct seq_file *seq, void *v) sock_prot_inuse_get(net, &udplitev6_prot));seq_printf (seq,"RAW6: inuse %d\n" ,sock_prot_inuse_get (net, &rawv6_prot)); -seq_printf (seq,"FRAG6: inuse %u memory %u\n" , +seq_printf (seq,"FRAG6: inuse %u memory %lu\n" ,atomic_read (&net->ipv6.frags.rhashtable.nelems),frag_mem_limit (&net->ipv6.frags));return 0 ;
net/ipv6/reassembly.c +3 -3
@@ -546,15 +546,15 @@ static struct ctl_table ip6_frags_ns_ctl_table[] = { { .procname = "ip6frag_high_thresh", .data = &init_net.ipv6.frags.high_thresh, - .maxlen = sizeof(int), + .maxlen = sizeof(unsigned long), .mode = 0644, - .proc_handler = proc_dointvec_minmax, + .proc_handler = proc_doulongvec_minmax, .extra1 = &init_net.ipv6.frags.low_thresh }, { .procname = "ip6frag_low_thresh", .data = &init_net.ipv6.frags.low_thresh, - .maxlen = sizeof(int), + .maxlen = sizeof(unsigned long), .mode = 0644, .proc_handler = proc_dointvec_minmax, .extra1 = &zero,
Signature · 1 line
2.17.1
PATCH v2 13/29 inet: frags: do not clone skb in ip_expire()
In reply to Florian FainelliFrom: Eric Dumazet <[email protected]>
An skb_clone() was added in commit ec4fbd64751d ("inet: frag: release spinlock before calling icmp_send()")
While fixing the bug at that time, it also added a very high cost for DDOS frags, as the ICMP rate limit is applied after this expensive operation (skb_clone() + consume_skb(), implying memory allocations, copy, and freeing)
We can use skb_get(head) here, all we want is to make sure skb wont be freed by another cpu.
--- net/ipv4/ip_fragment.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-)
net/ipv4/ip_fragment.c +6 -10
@@ -141,8 +141,8 @@ static bool frag_expire_skip_icmp(u32 user)*/ static void ip_expire (unsigned long arg) { -struct sk_buff *clone, *head;const struct iphdr *iph; +struct sk_buff *head;struct net *net;struct ipq *qp;int err;
@@ -185,16 +185,12 @@ static void ip_expire(unsigned long arg) (skb_rtable(head)->rt_type != RTN_LOCAL)) goto out; - clone =skb_clone (head,GFP_ATOMIC ); +skb_get (head); +spin_unlock (&qp->q.lock); +icmp_send (head,ICMP_TIME_EXCEEDED ,ICMP_EXC_FRAGTIME ,0 ); +kfree_skb (head); + goto out_rcu_unlock; -/* Send an ICMP "Fragment Reassembly Timeout" message. */ -if (clone) { -spin_unlock (&qp->q.lock); -icmp_send (clone,ICMP_TIME_EXCEEDED , -ICMP_EXC_FRAGTIME ,0 ); -consume_skb (clone); - goto out_rcu_unlock; - } out:spin_unlock (&qp->q.lock); out_rcu_unlock:
Signature · 1 line
2.17.1
PATCH v2 14/29 ipv6: frags: rewrite ip6_expire_frag_queue()
In reply to Florian FainelliFrom: Eric Dumazet <[email protected]>
Make it similar to IPv4 ip_expire(), and release the lock before calling icmp functions.
--- net/ipv6/reassembly.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-)
net/ipv6/reassembly.c +16 -8
@@ -92,7 +92,9 @@ EXPORT_SYMBOL(ip6_frag_init);void ip6_expire_frag_queue (struct net *net,struct frag_queue *fq) {struct net_device *dev =NULL ; +struct sk_buff *head; +rcu_read_lock ();spin_lock (&fq->q.lock);if (fq->q.flags &INET_FRAG_COMPLETE )
@@ -100,28 +102,34 @@ void ip6_expire_frag_queue(struct net *net, struct frag_queue *fq)inet_frag_kill (&fq->q); -rcu_read_lock (); dev =dev_get_by_index_rcu (net, fq->iif);if (!dev) - goto out_rcu_unlock; + goto out;__IP6_INC_STATS (net,__in6_dev_get (dev),IPSTATS_MIB_REASMFAILS );__IP6_INC_STATS (net,__in6_dev_get (dev),IPSTATS_MIB_REASMTIMEOUT );/* Don't send error if the first segment did not arrive. */ -if (!(fq->q.flags &INET_FRAG_FIRST_IN ) || !fq->q.fragments) - goto out_rcu_unlock; + head = fq->q.fragments; +if (!(fq->q.flags &INET_FRAG_FIRST_IN ) || !head) + goto out;/* But use as source device on which LAST ARRIVED * segment was received. And do not use fq->dev * pointer directly, device might already disappeared. */ - fq->q.fragments->dev = dev; -icmpv6_send (fq->q.fragments,ICMPV6_TIME_EXCEED ,ICMPV6_EXC_FRAGTIME ,0 ); -out_rcu_unlock: -rcu_read_unlock (); + head->dev = dev; +skb_get (head); +spin_unlock (&fq->q.lock); + +icmpv6_send (head,ICMPV6_TIME_EXCEED ,ICMPV6_EXC_FRAGTIME ,0 ); +kfree_skb (head); + goto out_rcu_unlock; + out:spin_unlock (&fq->q.lock); +out_rcu_unlock: +rcu_read_unlock ();inet_frag_put (&fq->q); }EXPORT_SYMBOL (ip6_expire_frag_queue);
Signature · 1 line
2.17.1
PATCH v2 15/29 rhashtable: reorganize struct rhashtable layout
In reply to Florian FainelliFrom: Eric Dumazet <[email protected]>
While under frags DDOS I noticed unfortunate false sharing between @nelems and @params.automatic_shrinking
Move @nelems at the end of struct rhashtable so that first cache line is shared between all cpus, because almost never dirtied.
--- include/linux/rhashtable.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
include/linux/rhashtable.h +2 -2
@@ -138,7 +138,6 @@ struct rhashtable_params {/** * struct rhashtable - Hash table handle * @tbl: Bucket table -* @nelems: Number of elements in table * @key_len: Key length for hashfn * @elasticity: Maximum chain length before rehash * @p: Configuration parameters
@@ -146,10 +145,10 @@ struct rhashtable_params {* @run_work: Deferred worker to expand/shrink asynchronously * @mutex: Mutex to protect current/future table swapping * @lock: Spin lock to protect walker list +* @nelems: Number of elements in table */ struct rhashtable {struct bucket_table __rcu *tbl; -atomic_t nelems;unsigned int key_len;unsigned int elasticity;struct rhashtable_params p;
@@ -157,6 +156,7 @@ struct rhashtable {struct work_struct run_work;struct mutex mutex;spinlock_t lock; +atomic_t nelems; };/**
Signature · 1 line
2.17.1
PATCH v2 16/29 inet: frags: reorganize struct netns_frags
In reply to Florian FainelliFrom: Eric Dumazet <[email protected]>
Put the read-mostly fields in a separate cache line at the beginning of struct netns_frags, to reduce false sharing noticed in inet_frag_kill()
--- include/net/inet_frag.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
include/net/inet_frag.h +5 -4
@@ -4,16 +4,17 @@#include <linux/rhashtable.h> struct netns_frags { -struct rhashtable rhashtable____cacheline_aligned_in_smp ; - -/* Keep atomic mem on separate cachelines in structs that include it */ -atomic_long_t mem____cacheline_aligned_in_smp ;/* sysctls */ long high_thresh;long low_thresh;int timeout;int max_dist;struct inet_frags *f; + +struct rhashtable rhashtable____cacheline_aligned_in_smp ; + +/* Keep atomic mem on separate cachelines in structs that include it */ +atomic_long_t mem____cacheline_aligned_in_smp ; };/**
Signature · 1 line
2.17.1
PATCH v2 17/29 inet: frags: get rid of ipfrag_skb_cb/FRAG_CB
In reply to Florian FainelliFrom: Eric Dumazet <[email protected]>
ip_defrag uses skb->cb[] to store the fragment offset, and unfortunately this integer is currently in a different cache line than skb->next, meaning that we use two cache lines per skb when finding the insertion point.
By aliasing skb->ip_defrag_offset and skb->dev, we pack all the fields in a single cache line and save precious memory bandwidth.
Note that after the fast path added by Changli Gao in commit d6bebca92c66 ("fragment: add fast path for in-order fragments") this change wont help the fast path, since we still need to access prev->len (2nd cache line), but will show great benefits when slow path is entered, since we perform a linear scan of a potentially long list.
Also, note that this potential long list is an attack vector, we might consider also using an rb-tree there eventually.
--- include/linux/skbuff.h | 5 +++++ 1 file changed, 5 insertions(+)
include/linux/skbuff.h +5 -0
@@ -645,6 +645,11 @@ struct sk_buff { };struct rb_node rbnode;/* used in netem & tcp stack */ }; + +union { +int ip_defrag_offset; + }; +struct sock *sk;struct net_device *dev;
Signature · 1 line
2.17.1
PATCH v2 18/29 inet: frags: fix ip6frag_low_thresh boundary
In reply to Florian FainelliFrom: Eric Dumazet <[email protected]>
Giving an integer to proc_doulongvec_minmax() is dangerous on 64bit arches, since linker might place next to it a non zero value preventing a change to ip6frag_low_thresh.
ip6frag_low_thresh is not used anymore in the kernel, but we do not want to prematuraly break user scripts wanting to change it.
Since specifying a minimal value of 0 for proc_doulongvec_minmax() is moot, let's remove these zero values in all defrag units.
--- net/ieee802154/6lowpan/reassembly.c | 2 -- net/ipv4/ip_fragment.c | 40 ++++++++++--------------- net/ipv6/netfilter/nf_conntrack_reasm.c | 2 -- net/ipv6/reassembly.c | 4 +-- 4 files changed, 17 insertions(+), 31 deletions(-)
net/ieee802154/6lowpan/reassembly.c +0 -2
@@ -410,7 +410,6 @@ int lowpan_frag_rcv(struct sk_buff *skb, u8 frag_type) } #ifdef CONFIG_SYSCTL -static long zero; static struct ctl_table lowpan_frags_ns_ctl_table[] = { {
@@ -427,7 +426,6 @@ static struct ctl_table lowpan_frags_ns_ctl_table[] = { .maxlen = sizeof(unsigned long), .mode = 0644, .proc_handler = proc_doulongvec_minmax, - .extra1 = &zero, .extra2 = &init_net.ieee802154_lowpan.frags.high_thresh }, {
net/ipv4/ip_fragment.c +16 -24
@@ -56,14 +56,6 @@*/ static const char ip_frag_cache_name[] ="ip4-frags" ; -struct ipfrag_skb_cb -{ -struct inet_skb_parm h; -int offset; -}; - -#define FRAG_CB (skb) ((struct ipfrag_skb_cb *)((skb)->cb)) -/* Describe an entry in the "incomplete datagrams" queue. */ struct ipq {struct inet_frag_queue q;
@@ -351,13 +343,13 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)* this fragment, right? */ prev = qp->q.fragments_tail; -if (!prev ||FRAG_CB (prev)->offset < offset) { +if (!prev || prev->ip_defrag_offset < offset) { next =NULL ; goto found; } prev =NULL ;for (next = qp->q.fragments; next !=NULL ; next = next->next) { -if (FRAG_CB (next)->offset >= offset) +if (next->ip_defrag_offset >= offset)break ;/* bingo! */ prev = next; }
@@ -368,7 +360,7 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)* any overlaps are eliminated. */ if (prev) { -int i = (FRAG_CB (prev)->offset + prev->len) - offset; +int i = (prev->ip_defrag_offset + prev->len) - offset;if (i >0 ) { offset += i;
@@ -385,8 +377,8 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb) err = -ENOMEM ; -while (next &&FRAG_CB (next)->offset < end) { -int i = end -FRAG_CB (next)->offset;/* overlap is 'i' bytes */ +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;
@@ -399,7 +391,7 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb) delta += next->truesize;if (delta)add_frag_mem_limit (qp->q.net, delta); -FRAG_CB (next)->offset += i; + next->ip_defrag_offset += i; qp->q.meat -= i;if (next->ip_summed !=CHECKSUM_UNNECESSARY ) next->ip_summed =CHECKSUM_NONE ;
@@ -423,7 +415,13 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb) } } -FRAG_CB (skb)->offset = offset; +/* Note : skb->ip_defrag_offset and skb->dev share the same location */ + dev = skb->dev; +if (dev) + qp->iif = dev->ifindex; +/* Makes sure compiler wont do silly aliasing games */ +barrier (); + skb->ip_defrag_offset = offset;/* Insert this fragment in the chain of fragments. */ skb->next = next;
@@ -434,11 +432,6 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)else qp->q.fragments = skb; - dev = skb->dev; -if (dev) { - qp->iif = dev->ifindex; - skb->dev =NULL ; - } qp->q.stamp = skb->tstamp; qp->q.meat += skb->len; qp->ecn |= ecn;
@@ -514,7 +507,7 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev, }WARN_ON (!head); -WARN_ON (FRAG_CB (head)->offset !=0 ); +WARN_ON (head->ip_defrag_offset !=0 );/* Allocate a new buffer for the datagram. */ ihlen =ip_hdrlen (head);
@@ -677,7 +670,7 @@ struct sk_buff *ip_check_defrag(struct net *net, struct sk_buff *skb, u32 user)EXPORT_SYMBOL (ip_check_defrag); #ifdef CONFIG_SYSCTL -static long zero; +static int dist_min; static struct ctl_table ip4_frags_ns_ctl_table[] = { {
@@ -694,7 +687,6 @@ static struct ctl_table ip4_frags_ns_ctl_table[] = { .maxlen = sizeof(unsigned long), .mode = 0644, .proc_handler = proc_doulongvec_minmax, - .extra1 = &zero, .extra2 = &init_net.ipv4.frags.high_thresh }, {
@@ -710,7 +702,7 @@ static struct ctl_table ip4_frags_ns_ctl_table[] = { .maxlen = sizeof(int), .mode = 0644, .proc_handler = proc_dointvec_minmax, - .extra1 = &zero + .extra1 = &dist_min, }, { } };
net/ipv6/netfilter/nf_conntrack_reasm.c +0 -2
@@ -63,7 +63,6 @@ struct nf_ct_frag6_skb_cbstatic struct inet_frags nf_frags; #ifdef CONFIG_SYSCTL -static long zero; static struct ctl_table nf_ct_frag6_sysctl_table[] = { {
@@ -79,7 +78,6 @@ static struct ctl_table nf_ct_frag6_sysctl_table[] = { .maxlen = sizeof(unsigned long), .mode = 0644, .proc_handler = proc_doulongvec_minmax, - .extra1 = &zero, .extra2 = &init_net.nf_frag.frags.high_thresh }, {
net/ipv6/reassembly.c +1 -3
@@ -548,7 +548,6 @@ static const struct inet6_protocol frag_protocol = { }; #ifdef CONFIG_SYSCTL -static int zero; static struct ctl_table ip6_frags_ns_ctl_table[] = { {
@@ -564,8 +563,7 @@ static struct ctl_table ip6_frags_ns_ctl_table[] = { .data = &init_net.ipv6.frags.low_thresh, .maxlen = sizeof(unsigned long), .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &zero, + .proc_handler = proc_doulongvec_minmax, .extra2 = &init_net.ipv6.frags.high_thresh }, {
Signature · 1 line
2.17.1
PATCH v2 19/29 ip: discard IPv4 datagrams with overlapping segments.
In reply to Florian FainelliFrom: Peter Oskolkov <[email protected]>
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
@@ -55,6 +55,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
@@ -134,6 +134,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.17.1
PATCH v2 20/29 net: speed up skb_rbtree_purge()
In reply to Florian FainelliFrom: Eric Dumazet <[email protected]>
As measured in my prior patch ("sch_netem: faster rb tree removal"), rbtree_postorder_for_each_entry_safe() is nice looking but much slower than using rb_next() directly, except when tree is small enough to fit in CPU caches (then the cost is the same)
Also note that there is not even an increase of text size :
$ size net/core/skbuff.o.before net/core/skbuff.o text data bss dec hex filename 40711 1298 0 42009 a419 net/core/skbuff.o.before 40711 1298 0 42009 a419 net/core/skbuff.o
From: Eric Dumazet <[email protected]>
--- net/core/skbuff.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-)
net/core/skbuff.c +7 -4
@@ -2433,12 +2433,15 @@ EXPORT_SYMBOL(skb_queue_purge);*/ void skb_rbtree_purge (struct rb_root *root) { -struct sk_buff *skb, *next; +struct rb_node *p =rb_first (root); -rbtree_postorder_for_each_entry_safe (skb, next, root, rbnode) -kfree_skb (skb); +while (p) { + struct sk_buff *skb = rb_entry(p, struct sk_buff, rbnode); - *root =RB_ROOT ; + p =rb_next (p); +rb_erase (&skb->rbnode, root); +kfree_skb (skb); + } }/**
Signature · 1 line
2.17.1
PATCH v2 21/29 net: modify skb_rbtree_purge to return the truesize of all purged skbs.
In reply to Florian FainelliFrom: Peter Oskolkov <[email protected]>
Tested: 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
@@ -2418,7 +2418,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
@@ -2425,23 +2425,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.17.1
PATCH v2 22/29 ipv6: defrag: drop non-last frags smaller than min mtu
In reply to Florian FainelliFrom: Florian Westphal <[email protected]>
don't bother with pathological cases, they only waste cycles. IPv6 requires a minimum MTU of 1280 so we should never see fragments smaller than this (except last frag).
v3: don't use awkward "-offset + len"
v2: drop IPv4 part, which added same check w. IPV4_MIN_MTU (68).
There were concerns that there could be even smaller frags generated by intermediate nodes, e.g. on radio networks.
--- net/ipv6/netfilter/nf_conntrack_reasm.c | 4 ++++ net/ipv6/reassembly.c | 4 ++++ 2 files changed, 8 insertions(+)
net/ipv6/netfilter/nf_conntrack_reasm.c +4 -0
@@ -564,6 +564,10 @@ int nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 user) hdr =ipv6_hdr (skb); fhdr = (struct frag_hdr *)skb_transport_header (skb); +if (skb->len -skb_network_offset (skb) <IPV6_MIN_MTU && + fhdr->frag_off &htons (IP6_MF )) +return -EINVAL ; +skb_orphan (skb); fq =fq_find (net, fhdr->identification, user, hdr, skb->dev ? skb->dev->ifindex :0 );
net/ipv6/reassembly.c +4 -0
@@ -516,6 +516,10 @@ static int ipv6_frag_rcv(struct sk_buff *skb)return 1 ; } +if (skb->len -skb_network_offset (skb) <IPV6_MIN_MTU && + fhdr->frag_off &htons (IP6_MF )) + goto fail_hdr; + iif = skb->dev ? skb->dev->ifindex :0 ; fq =fq_find (net, fhdr->identification, hdr, iif);if (fq) {
Signature · 1 line
2.17.1
PATCH v2 23/29 net: pskb_trim_rcsum() and CHECKSUM_COMPLETE are friends
In reply to Florian FainelliFrom: Eric Dumazet <[email protected]>
After working on IP defragmentation lately, I found that some large packets defeat CHECKSUM_COMPLETE optimization because of NIC adding zero paddings on the last (small) fragment.
While removing the padding with pskb_trim_rcsum(), we set skb->ip_summed to CHECKSUM_NONE, forcing a full csum validation, even if all prior fragments had CHECKSUM_COMPLETE set.
We can instead compute the checksum of the part we are trimming, usually smaller than the part we keep.
--- include/linux/skbuff.h | 5 ++--- net/core/skbuff.c | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-)
include/linux/skbuff.h +2 -3
@@ -2954,6 +2954,7 @@ static inline unsigned char *skb_push_rcsum(struct sk_buff *skb,return skb->data; } +int pskb_trim_rcsum_slow (struct sk_buff *skb,unsigned int len);/** * pskb_trim_rcsum - trim received skb and update checksum * @skb: buffer to trim
@@ -2967,9 +2968,7 @@ static inline int pskb_trim_rcsum(struct sk_buff *skb, unsigned int len) {if (likely (len >= skb->len))return 0 ; -if (skb->ip_summed ==CHECKSUM_COMPLETE ) - skb->ip_summed =CHECKSUM_NONE ; -return __pskb_trim (skb, len); +return pskb_trim_rcsum_slow (skb, len); }static inline int __skb_trim_rcsum (struct sk_buff *skb,unsigned int len)
net/core/skbuff.c +14 -0
@@ -1578,6 +1578,20 @@ int ___pskb_trim(struct sk_buff *skb, unsigned int len) }EXPORT_SYMBOL (___pskb_trim); +/* Note : use pskb_trim_rcsum() instead of calling this directly +*/ +int pskb_trim_rcsum_slow (struct sk_buff *skb,unsigned int len) +{ +if (skb->ip_summed ==CHECKSUM_COMPLETE ) { +int delta = skb->len - len; + + skb->csum =csum_sub (skb->csum, +skb_checksum (skb, len, delta,0 )); + } +return __pskb_trim (skb, len); +} +EXPORT_SYMBOL (pskb_trim_rcsum_slow); +/** * __pskb_pull_tail - advance tail of skb header * @skb: buffer to reallocate
Signature · 1 line
2.17.1
PATCH v2 24/29 net: add rb_to_skb() and other rb tree helpers
In reply to Florian FainelliFrom: Eric Dumazet <[email protected]>
Geeralize private netem_rb_to_skb()
TCP rtx queue will soon be converted to rb-tree, so we will need skb_rbtree_walk() helpers.
--- include/linux/skbuff.h | 18 ++++++++++++++++++ net/ipv4/tcp_input.c | 33 ++++++++++++--------------------- 2 files changed, 30 insertions(+), 21 deletions(-)
include/linux/skbuff.h +18 -0
@@ -2988,6 +2988,12 @@ static inline int __skb_grow_rcsum(struct sk_buff *skb, unsigned int len)#define rb_to_skb (rb) rb_entry_safe(rb, struct sk_buff, rbnode) +#define rb_to_skb (rb) rb_entry_safe(rb, struct sk_buff, rbnode) +#define skb_rb_first (root) rb_to_skb(rb_first(root)) +#define skb_rb_last (root) rb_to_skb(rb_last(root)) +#define skb_rb_next (skb) rb_to_skb(rb_next(&(skb)->rbnode)) +#define skb_rb_prev (skb) rb_to_skb(rb_prev(&(skb)->rbnode)) +#define skb_queue_walk (queue, skb) \ for (skb = (queue)->next; \ skb != (struct sk_buff *)(queue); \
@@ -3002,6 +3008,18 @@ static inline int __skb_grow_rcsum(struct sk_buff *skb, unsigned int len)for (; skb != (struct sk_buff *)(queue); \ skb = skb->next) +#define skb_rbtree_walk (skb, root) \ + for (skb = skb_rb_first(root); skb != NULL; \ + skb = skb_rb_next(skb)) + +#define skb_rbtree_walk_from (skb) \ + for (; skb != NULL; \ + skb = skb_rb_next(skb)) + +#define skb_rbtree_walk_from_safe (skb, tmp) \ + for (; tmp = skb ? skb_rb_next(skb) : NULL, (skb != NULL); \ + skb = tmp) +#define skb_queue_walk_from_safe (queue, skb, tmp) \ for (tmp = skb->next; \ skb != (struct sk_buff *)(queue); \
net/ipv4/tcp_input.c +12 -21
@@ -4406,7 +4406,7 @@ static void tcp_ofo_queue(struct sock *sk) p =rb_first (&tp->out_of_order_queue);while (p) { - skb = rb_entry(p, struct sk_buff, rbnode); + skb =rb_to_skb (p);if (after (TCP_SKB_CB (skb)->seq, tp->rcv_nxt))break ;
@@ -4470,7 +4470,7 @@ static int tcp_try_rmem_schedule(struct sock *sk, struct sk_buff *skb,static void tcp_data_queue_ofo (struct sock *sk,struct sk_buff *skb) {struct tcp_sock *tp =tcp_sk (sk); -struct rb_node **p, *q, *parent; +struct rb_node **p, *parent;struct sk_buff *skb1;u32 seq, end_seq;bool fragstolen;
@@ -4529,7 +4529,7 @@ static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb) parent =NULL ;while (*p) { parent = *p; - skb1 = rb_entry(parent, struct sk_buff, rbnode); + skb1 =rb_to_skb (parent);if (before (seq,TCP_SKB_CB (skb1)->seq)) { p = &parent->rb_left;continue ;
@@ -4574,9 +4574,7 @@ static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb) merge_right:/* Remove other segments covered by skb. */ -while ((q =rb_next (&skb->rbnode)) !=NULL ) { - skb1 = rb_entry(q, struct sk_buff, rbnode); - +while ((skb1 =skb_rb_next (skb)) !=NULL ) {if (!after (end_seq,TCP_SKB_CB (skb1)->seq))break ;if (before (end_seq,TCP_SKB_CB (skb1)->end_seq)) {
@@ -4591,7 +4589,7 @@ static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb)tcp_drop (sk, skb1); }/* If there is no skb after us, we are the last_skb ! */ -if (!q) +if (!skb1) tp->ooo_last_skb = skb; add_sack:
@@ -4792,7 +4790,7 @@ static struct sk_buff *tcp_skb_next(struct sk_buff *skb, struct sk_buff_head *liif (list)return !skb_queue_is_last (list, skb) ? skb->next :NULL ; - return rb_entry_safe(rb_next(&skb->rbnode), struct sk_buff, rbnode); +return skb_rb_next (skb); } static struct sk_buff *tcp_collapse_one(struct sock *sk, struct sk_buff *skb,
@@ -4821,7 +4819,7 @@ static void tcp_rbtree_insert(struct rb_root *root, struct sk_buff *skb)while (*p) { parent = *p; - skb1 = rb_entry(parent, struct sk_buff, rbnode); + skb1 =rb_to_skb (parent);if (before (TCP_SKB_CB (skb)->seq,TCP_SKB_CB (skb1)->seq)) p = &parent->rb_left;else
@@ -4941,19 +4939,12 @@ static void tcp_collapse_ofo_queue(struct sock *sk)struct tcp_sock *tp =tcp_sk (sk);u32 range_truesize, sum_tiny =0 ;struct sk_buff *skb, *head; -struct rb_node *p;u32 start, end; - p =rb_first (&tp->out_of_order_queue); - skb = rb_entry_safe(p, struct sk_buff, rbnode); + skb =skb_rb_first (&tp->out_of_order_queue); new_range:if (!skb) { - p =rb_last (&tp->out_of_order_queue); -/* Note: This is possible p is NULL here. We do not -* use rb_entry_safe(), as ooo_last_skb is valid only -* if rbtree is not empty. -*/ - tp->ooo_last_skb = rb_entry(p, struct sk_buff, rbnode); + tp->ooo_last_skb =skb_rb_last (&tp->out_of_order_queue);return ; } start =TCP_SKB_CB (skb)->seq;
@@ -4961,7 +4952,7 @@ static void tcp_collapse_ofo_queue(struct sock *sk) range_truesize = skb->truesize;for (head = skb;;) { - skb =tcp_skb_next (skb,NULL ); + skb =skb_rb_next (skb);/* Range is terminated when we see a gap or when * we are at the queue end.
@@ -5017,7 +5008,7 @@ static bool tcp_prune_ofo_queue(struct sock *sk) prev =rb_prev (node);rb_erase (node, &tp->out_of_order_queue); goal -=rb_to_skb (node)->truesize; - tcp_drop(sk, rb_entry(node, struct sk_buff, rbnode)); +tcp_drop (sk,rb_to_skb (node));if (!prev || goal <=0 ) {sk_mem_reclaim (sk); if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf &&
@@ -5027,7 +5018,7 @@ static bool tcp_prune_ofo_queue(struct sock *sk) } node = prev; }while (node); - tp->ooo_last_skb = rb_entry(prev, struct sk_buff, rbnode); + tp->ooo_last_skb =rb_to_skb (prev);/* Reset SACK state. A conforming SACK implementation will * do the same at a timeout based retransmit. When a connection
Signature · 1 line
2.17.1
PATCH v2 25/29 ip: use rb trees for IP frag queue.
In reply to Florian FainelliFrom: Peter Oskolkov <[email protected]>
(commit fa0f527358bd900ef92f925878ed6bfbd51305cc upstream)
Similar 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 | 4 +- 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, 117 insertions(+), 90 deletions(-)
include/linux/skbuff.h +2 -2
@@ -643,14 +643,14 @@ struct sk_buff {struct skb_mstamp skb_mstamp; }; }; -struct rb_node rbnode;/* used in netem & tcp stack */ +struct rb_node rbnode;/* used in netem, ip4 defrag, and tcp stack */ };union { +struct sock *sk;int ip_defrag_offset; }; -struct sock *sk;struct net_device *dev;/*
include/net/inet_frag.h +2 -1
@@ -74,7 +74,8 @@ struct inet_frag_queue {struct timer_list timer;spinlock_t lock;atomic_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
@@ -136,12 +136,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
@@ -470,6 +470,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
@@ -466,6 +466,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.17.1
PATCH v2 26/29 ip: add helpers to process in-order fragments faster.
In reply to Florian FainelliFrom: Peter Oskolkov <[email protected]>
This patch introduces several helper functions/macros that will be used in the follow-up patch. No runtime changes yet.
The new logic (fully implemented in the second patch) is as follows:
* Nodes in the rb-tree will now contain not single fragments, but lists of consecutive fragments ("runs").
* At each point in time, the current "active" run at the tail is maintained/tracked. Fragments that arrive in-order, adjacent to the previous tail fragment, are added to this tail run without triggering the re-balancing of the rb-tree.
* If a fragment arrives out of order with the offset _before_ the tail run, it is inserted into the rb-tree as a single fragment.
* If a fragment arrives after the current tail fragment (with a gap), it starts a new "tail" run, as is inserted into the rb-tree at the end as the head of the new run.
skb->cb is used to store additional information
needed here (suggested by Eric Dumazet).
--- include/net/inet_frag.h | 6 ++++ net/ipv4/ip_fragment.c | 73 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+)
include/net/inet_frag.h +6 -0
@@ -56,7 +56,9 @@ struct frag_v6_compare_key {* @lock: spinlock protecting this frag * @refcnt: reference count of the queue * @fragments: received fragments head +* @rb_fragments: received fragments rb-tree root * @fragments_tail: received fragments tail +* @last_run_head: the head of the last "run". see ip_fragment.c * @stamp: timestamp of the last received fragment * @len: total length of the original datagram * @meat: length of received fragments so far
@@ -77,6 +79,7 @@ struct inet_frag_queue {struct sk_buff *fragments;/* Used in IPv6. */ struct rb_root rb_fragments;/* Used in IPv4. */ struct sk_buff *fragments_tail; +struct sk_buff *last_run_head;ktime_t stamp;int len;int meat;
@@ -112,6 +115,9 @@ void inet_frag_kill(struct inet_frag_queue *q);void inet_frag_destroy (struct inet_frag_queue *q);struct inet_frag_queue *inet_frag_find (struct netns_frags *nf,void *key); +/* Free all skbs in the queue; return the sum of their truesizes. */ +unsigned int inet_frag_rbtree_purge (struct rb_root *root); +static inline void inet_frag_put (struct inet_frag_queue *q) {if (atomic_dec_and_test (&q->refcnt))
net/ipv4/ip_fragment.c +73 -0
85 lines not on this page · raw message
Signature · 1 line
2.17.1
PATCH v2 27/29 ip: process in-order fragments efficiently
In reply to Florian FainelliFrom: Peter Oskolkov <[email protected]>
This patch changes the runtime behavior of IP defrag queue:
incoming in-order fragments are added to the end of the current list/"run" of in-order fragments at the tail.
On some workloads, UDP stream performance is substantially improved:
RX: ./udp_stream -F 10 -T 2 -l 60
TX: ./udp_stream -c -H <host> -F 10 -T 5 -l 60
with this patchset applied on a 10Gbps receiver:
throughput=9524.18
throughput_units=Mbit/s
upstream (net-next):
throughput=4608.93
throughput_units=Mbit/s
--- net/ipv4/inet_fragment.c | 2 +- net/ipv4/ip_fragment.c | 110 ++++++++++++++++++++++++--------------- 2 files changed, 70 insertions(+), 42 deletions(-)
net/ipv4/inet_fragment.c +1 -1
@@ -145,7 +145,7 @@ void inet_frag_destroy(struct inet_frag_queue *q) fp = xp; }while (fp); }else { - sum_truesize =skb_rbtree_purge (&q->rb_fragments); + sum_truesize =inet_frag_rbtree_purge (&q->rb_fragments); } sum = sum_truesize + f->qsize;
net/ipv4/ip_fragment.c +69 -41
203 lines not on this page · raw message
Signature · 1 line
2.17.1
PATCH v2 28/29 ip: frags: fix crash in ip_do_fragment()
In reply to Florian FainelliFrom: Taehee Yoo <[email protected]>
commit 5d407b071dc369c26a38398326ee2be53651cfe4 upstream
A kernel crash occurrs when defragmented packet is fragmented in ip_do_fragment().
In defragment routine, skb_orphan() is called and skb->ip_defrag_offset is set. but skb->sk and skb->ip_defrag_offset are same union member. so that frag->sk is not NULL.
Hence crash occurrs in skb->sk check routine in ip_do_fragment() when defragmented packet is fragmented.
test commands:
%iptables -t nat -I POSTROUTING -j MASQUERADE
%hping3 192.168.4.2 -s 1000 -p 2000 -d 60000
splat looks like:
[ 261.069429] kernel BUG at net/ipv4/ip_output.c:636! [ 261.075753] invalid opcode: 0000 [#1] SMP DEBUG_PAGEALLOC KASAN PTI [ 261.083854] CPU: 1 PID: 1349 Comm: hping3 Not tainted 4.19.0-rc2+ #3 [ 261.100977] RIP: 0010:ip_do_fragment+0x1613/0x2600 [ 261.106945] Code: e8 e2 38 e3 fe 4c 8b 44 24 18 48 8b 74 24 08 e9 92 f6 ff ff 80 3c 02 00 0f 85 da 07 00 00 48 8b b5 d0 00 00 00 e9 25 f6 ff ff <0f> 0b 0f 0b 44 8b 54 24 58 4c 8b 4c 24 18 4c 8b 5c 24 60 4c 8b 6c [ 261.127015] RSP: 0018:ffff8801031cf2c0 EFLAGS: 00010202 [ 261.134156] RAX: 1ffff1002297537b RBX: ffffed0020639e6e RCX: 0000000000000004 [ 261.142156] RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff880114ba9bd8 [ 261.150157] RBP: ffff880114ba8a40 R08: ffffed0022975395 R09: ffffed0022975395 [ 261.158157] R10: 0000000000000001 R11: ffffed0022975394 R12: ffff880114ba9ca4 [ 261.166159] R13: 0000000000000010 R14: ffff880114ba9bc0 R15: dffffc0000000000 [ 261.174169] FS: 00007fbae2199700(0000) GS:ffff88011b400000(0000) knlGS:0000000000000000 [ 261.183012] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 261.189013] CR2: 00005579244fe000 CR3: 0000000119bf4000 CR4: 00000000001006e0 [ 261.198158] Call Trace: [ 261.199018] ? dst_output+0x180/0x180 [ 261.205011] ? save_trace+0x300/0x300 [ 261.209018] ? ip_copy_metadata+0xb00/0xb00 [ 261.213034] ? sched_clock_local+0xd4/0x140 [ 261.218158] ? kill_l4proto+0x120/0x120 [nf_conntrack] [ 261.223014] ? rt_cpu_seq_stop+0x10/0x10 [ 261.227014] ? find_held_lock+0x39/0x1c0 [ 261.233008] ip_finish_output+0x51d/0xb50 [ 261.237006] ? ip_fragment.constprop.56+0x220/0x220 [ 261.243011] ? nf_ct_l4proto_register_one+0x5b0/0x5b0 [nf_conntrack] [ 261.250152] ? rcu_is_watching+0x77/0x120 [ 261.255010] ? nf_nat_ipv4_out+0x1e/0x2b0 [nf_nat_ipv4] [ 261.261033] ? nf_hook_slow+0xb1/0x160 [ 261.265007] ip_output+0x1c7/0x710 [ 261.269005] ? ip_mc_output+0x13f0/0x13f0 [ 261.273002] ? __local_bh_enable_ip+0xe9/0x1b0 [ 261.278152] ? ip_fragment.constprop.56+0x220/0x220 [ 261.282996] ? nf_hook_slow+0xb1/0x160 [ 261.287007] raw_sendmsg+0x21f9/0x4420 [ 261.291008] ? dst_output+0x180/0x180 [ 261.297003] ? sched_clock_cpu+0x126/0x170 [ 261.301003] ? find_held_lock+0x39/0x1c0 [ 261.306155] ? stop_critical_timings+0x420/0x420 [ 261.311004] ? check_flags.part.36+0x450/0x450 [ 261.315005] ? _raw_spin_unlock_irq+0x29/0x40 [ 261.320995] ? _raw_spin_unlock_irq+0x29/0x40 [ 261.326142] ? cyc2ns_read_end+0x10/0x10 [ 261.330139] ? raw_bind+0x280/0x280 [ 261.334138] ? sched_clock_cpu+0x126/0x170 [ 261.338995] ? check_flags.part.36+0x450/0x450 [ 261.342991] ? __lock_acquire+0x4500/0x4500 [ 261.348994] ? inet_sendmsg+0x11c/0x500 [ 261.352989] ? dst_output+0x180/0x180 [ 261.357012] inet_sendmsg+0x11c/0x500
[ ... ]
v2:
- clear skb->sk at reassembly routine.(Eric Dumarzet)
--- net/ipv4/ip_fragment.c | 1 + net/ipv6/netfilter/nf_conntrack_reasm.c | 1 + 2 files changed, 2 insertions(+)
net/ipv4/ip_fragment.c +1 -0
@@ -597,6 +597,7 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *skb, nextp = &fp->next; fp->prev =NULL ;memset (&fp->rbnode,0 ,sizeof (fp->rbnode)); + fp->sk =NULL ; head->data_len += fp->len; head->len += fp->len;if (head->ip_summed != fp->ip_summed)
net/ipv6/netfilter/nf_conntrack_reasm.c +1 -0
@@ -452,6 +452,7 @@ nf_ct_frag6_reasm(struct frag_queue *fq, struct sk_buff *prev, struct net_devicelse if (head->ip_summed ==CHECKSUM_COMPLETE ) head->csum =csum_add (head->csum, fp->csum); head->truesize += fp->truesize; + fp->sk =NULL ; }sub_frag_mem_limit (fq->q.net, head->truesize);
Signature · 1 line
2.17.1
PATCH v2 29/29 ipv4: frags: precedence bug in ip_expire()
In reply to Florian FainelliFrom: Dan Carpenter <[email protected]>
(commit 70837ffe3085c9a91488b52ca13ac84424da1042 upstream)
We accidentally removed the parentheses here, but they are required because '!' has higher precedence than '&'.
--- net/ipv4/ip_fragment.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/ipv4/ip_fragment.c +1 -1
@@ -203,7 +203,7 @@ static void ip_expire(unsigned long arg)__IP_INC_STATS (net,IPSTATS_MIB_REASMFAILS );__IP_INC_STATS (net,IPSTATS_MIB_REASMTIMEOUT ); -if (!qp->q.flags &INET_FRAG_FIRST_IN ) +if (!(qp->q.flags &INET_FRAG_FIRST_IN )) goto out;/* sk_buff::dev and sk_buff::rbnode are unionized. So we
Signature · 1 line
2.17.1
Quoted 5 lines · Florian Fainelli
This is based on Stephen's v4.14 patches, with the necessary merge conflicts, and the lack of
timer_setup()on the 4.9 baseline.Perf results on a gigabit capable system, before and after are below.
Quoted 8 lines
Series can also be found here:
https://github.com/ffainelli/linux/commits/fragment-stack-v4.9-v2
Changes in v2:
- drop "net:
sk_buffrbnode reorg"
- added original "ip: use rb trees for IP frag queue." commit
Eric, does this look reasonable to you?
Quoted 114 lines
Before patches:
PerfTop: 180 irqs/sec kernel:78.9% exact: 0.0% [4000Hz cycles:ppp], (all, 4 CPUs)-------------------------------------------------------------------------------
34.81% [kernel] [k] ip_defrag 4.57% [kernel] [k] arch_cpu_idle 2.09% [kernel] [k] fib_table_lookup 1.74% [kernel] [k] finish_task_switch 1.57% [kernel] [k] v7_dma_inv_range 1.47% [kernel] [k] __netif_receive_skb_core 1.06% [kernel] [k] __slab_free 1.04% [kernel] [k] __netdev_alloc_skb 0.99% [kernel] [k] ip_route_input_noref 0.96% [kernel] [k] dev_gro_receive 0.96% [kernel] [k] tick_nohz_idle_enter 0.93% [kernel] [k] bcm_sysport_poll 0.92% [kernel] [k] skb_release_data 0.91% [kernel] [k] __memzero 0.90% [kernel] [k] __free_page_frag 0.87% [kernel] [k] ip_rcv 0.77% [kernel] [k] eth_type_trans 0.71% [kernel] [k] _raw_spin_unlock_irqrestore 0.68% [kernel] [k] tick_nohz_idle_exit 0.65% [kernel] [k] bcm_sysport_rx_refillAfter patches:
PerfTop: 214 irqs/sec kernel:80.4% exact: 0.0% [4000Hz cycles:ppp], (all, 4 CPUs)-------------------------------------------------------------------------------
6.61% [kernel] [k] arch_cpu_idle 3.77% [kernel] [k] ip_defrag 3.65% [kernel] [k] v7_dma_inv_range 3.18% [kernel] [k] fib_table_lookup 3.04% [kernel] [k] __netif_receive_skb_core 2.31% [kernel] [k] finish_task_switch 2.31% [kernel] [k] _raw_spin_unlock_irqrestore 1.65% [kernel] [k] bcm_sysport_poll 1.63% [kernel] [k] ip_route_input_noref 1.63% [kernel] [k] __memzero 1.58% [kernel] [k] __netdev_alloc_skb 1.47% [kernel] [k] tick_nohz_idle_enter 1.40% [kernel] [k] __slab_free 1.32% [kernel] [k] ip_rcv 1.32% [kernel] [k] __softirqentry_text_start 1.30% [kernel] [k] dev_gro_receive 1.23% [kernel] [k] bcm_sysport_rx_refill 1.11% [kernel] [k] tick_nohz_idle_exit 1.06% [kernel] [k] memcmp 1.02% [kernel] [k] dma_cache_maint_pageDan Carpenter (1):
ipv4: frags: precedence bug inip_expire()Eric Dumazet (21):
inet: frags: changeinet_frags_init_net()return value
inet: frags: add a pointer to structnetns_frags
inet: frags: refactoripfrag_init()
inet: frags: refactoripv6_frag_init()
inet: frags: refactorlowpan_net_frag_init()
ipv6: export ip6 fragments sysctl to unprivileged users
rhashtable: add schedule points
inet: frags: use rhashtables for reassembly units
inet: frags: remove some helpers
inet: frags: get rif ofinet_frag_evicting()
inet: frags: removeinet_frag_maybe_warn_overflow()
inet: frags: break the 2GB limit for frags storage
inet: frags: do not clone skb inip_expire()
ipv6: frags: rewriteip6_expire_frag_queue()
rhashtable: reorganize struct rhashtable layout
inet: frags: reorganize structnetns_frags
inet: frags: get rid of ipfrag_skb_cb/FRAG_CB
inet: frags: fixip6frag_low_threshboundary
net: speed upskb_rbtree_purge()
net:pskb_trim_rcsum()andCHECKSUM_COMPLETEare friends
net: addrb_to_skb()and other rb tree helpersFlorian Westphal (1):
ipv6: defrag: drop non-last frags smaller than min mtuPeter Oskolkov (5):
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.
ip: add helpers to process in-order fragments faster.
ip: process in-order fragments efficientlyTaehee Yoo (1):
ip: frags: fix crash inip_do_fragment()Documentation/networking/ip-sysctl.txt | 13 +- include/linux/rhashtable.h | 4 +- include/linux/skbuff.h | 34 +- include/net/inet_frag.h | 133 +++--- include/net/ip.h | 1 - include/net/ipv6.h | 26 +- include/uapi/linux/snmp.h | 1 + lib/rhashtable.c | 5 +- net/core/skbuff.c | 31 +- net/ieee802154/6lowpan/6lowpan_i.h | 26 +- net/ieee802154/6lowpan/reassembly.c | 148 +++--- net/ipv4/inet_fragment.c | 379 ++++------------ net/ipv4/ip_fragment.c | 573 +++++++++++++----------- net/ipv4/proc.c | 7 +- net/ipv4/tcp_input.c | 33 +- net/ipv6/netfilter/nf_conntrack_reasm.c | 100 ++--- net/ipv6/proc.c | 5 +- net/ipv6/reassembly.c | 212 ++++----- 18 files changed, 774 insertions(+), 957 deletions(-)
Signature · 1 line
Florian
Quoted 11 lines · Florian Fainelli
Quoted 7 lines · Florian Fainelli
This is based on Stephen's v4.14 patches, with the necessary merge conflicts, and the lack of
timer_setup()on the 4.9 baseline.Perf results on a gigabit capable system, before and after are below.
Series can also be found here:
Quoted 8 lines
Quoted 6 lines
https://github.com/ffainelli/linux/commits/fragment-stack-v4.9-v2
Changes in v2:
- drop "net:
sk_buffrbnode reorg"
- added original "ip: use rb trees for IP frag queue." commitEric, does this look reasonable to you?
Yes, thanks a lot Florian.
Quoted 118 lines
Quoted 106 lines
Before patches:
PerfTop: 180 irqs/sec kernel:78.9% exact: 0.0% [4000Hz cycles:ppp], (all, 4 CPUs)-------------------------------------------------------------------------------
34.81% [kernel] [k] ip_defrag 4.57% [kernel] [k] arch_cpu_idle 2.09% [kernel] [k] fib_table_lookup 1.74% [kernel] [k] finish_task_switch 1.57% [kernel] [k] v7_dma_inv_range 1.47% [kernel] [k] __netif_receive_skb_core 1.06% [kernel] [k] __slab_free 1.04% [kernel] [k] __netdev_alloc_skb 0.99% [kernel] [k] ip_route_input_noref 0.96% [kernel] [k] dev_gro_receive 0.96% [kernel] [k] tick_nohz_idle_enter 0.93% [kernel] [k] bcm_sysport_poll 0.92% [kernel] [k] skb_release_data 0.91% [kernel] [k] __memzero 0.90% [kernel] [k] __free_page_frag 0.87% [kernel] [k] ip_rcv 0.77% [kernel] [k] eth_type_trans 0.71% [kernel] [k] _raw_spin_unlock_irqrestore 0.68% [kernel] [k] tick_nohz_idle_exit 0.65% [kernel] [k] bcm_sysport_rx_refillAfter patches:
PerfTop: 214 irqs/sec kernel:80.4% exact: 0.0% [4000Hz cycles:ppp], (all, 4 CPUs)-------------------------------------------------------------------------------
6.61% [kernel] [k] arch_cpu_idle 3.77% [kernel] [k] ip_defrag 3.65% [kernel] [k] v7_dma_inv_range 3.18% [kernel] [k] fib_table_lookup 3.04% [kernel] [k] __netif_receive_skb_core 2.31% [kernel] [k] finish_task_switch 2.31% [kernel] [k] _raw_spin_unlock_irqrestore 1.65% [kernel] [k] bcm_sysport_poll 1.63% [kernel] [k] ip_route_input_noref 1.63% [kernel] [k] __memzero 1.58% [kernel] [k] __netdev_alloc_skb 1.47% [kernel] [k] tick_nohz_idle_enter 1.40% [kernel] [k] __slab_free 1.32% [kernel] [k] ip_rcv 1.32% [kernel] [k] __softirqentry_text_start 1.30% [kernel] [k] dev_gro_receive 1.23% [kernel] [k] bcm_sysport_rx_refill 1.11% [kernel] [k] tick_nohz_idle_exit 1.06% [kernel] [k] memcmp 1.02% [kernel] [k] dma_cache_maint_pageDan Carpenter (1):
ipv4: frags: precedence bug inip_expire()Eric Dumazet (21):
inet: frags: changeinet_frags_init_net()return value
inet: frags: add a pointer to structnetns_frags
inet: frags: refactoripfrag_init()
inet: frags: refactoripv6_frag_init()
inet: frags: refactorlowpan_net_frag_init()
ipv6: export ip6 fragments sysctl to unprivileged users
rhashtable: add schedule points
inet: frags: use rhashtables for reassembly units
inet: frags: remove some helpers
inet: frags: get rif ofinet_frag_evicting()
inet: frags: removeinet_frag_maybe_warn_overflow()
inet: frags: break the 2GB limit for frags storage
inet: frags: do not clone skb inip_expire()
ipv6: frags: rewriteip6_expire_frag_queue()
rhashtable: reorganize struct rhashtable layout
inet: frags: reorganize structnetns_frags
inet: frags: get rid of ipfrag_skb_cb/FRAG_CB
inet: frags: fixip6frag_low_threshboundary
net: speed upskb_rbtree_purge()
net:pskb_trim_rcsum()andCHECKSUM_COMPLETEare friends
net: addrb_to_skb()and other rb tree helpersFlorian Westphal (1):
ipv6: defrag: drop non-last frags smaller than min mtuPeter Oskolkov (5):
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.
ip: add helpers to process in-order fragments faster.
ip: process in-order fragments efficientlyTaehee Yoo (1):
ip: frags: fix crash inip_do_fragment()Documentation/networking/ip-sysctl.txt | 13 +- include/linux/rhashtable.h | 4 +- include/linux/skbuff.h | 34 +- include/net/inet_frag.h | 133 +++--- include/net/ip.h | 1 - include/net/ipv6.h | 26 +- include/uapi/linux/snmp.h | 1 + lib/rhashtable.c | 5 +- net/core/skbuff.c | 31 +- net/ieee802154/6lowpan/6lowpan_i.h | 26 +- net/ieee802154/6lowpan/reassembly.c | 148 +++---Quoted 9 lines
net/ipv4/inet_fragment.c | 379 ++++------------ net/ipv4/ip_fragment.c | 573 +++++++++++++----------- net/ipv4/proc.c | 7 +- net/ipv4/tcp_input.c | 33 +- net/ipv6/netfilter/nf_conntrack_reasm.c | 100 ++--- net/ipv6/proc.c | 5 +- net/ipv6/reassembly.c | 212 ++++----- 18 files changed, 774 insertions(+), 957 deletions(-)--
Florian
Greg Kroah-Hartman
maintainerQuoted 14 lines · Eric Dumazet
Quoted 13 lines · Florian Fainelli
Quoted 9 lines · Florian Fainelli
This is based on Stephen's v4.14 patches, with the necessary merge conflicts, and the lack of
timer_setup()on the 4.9 baseline.Perf results on a gigabit capable system, before and after are below.
Series can also be found here:
https://github.com/ffainelli/linux/commits/fragment-stack-v4.9-v2
Quoted 8 lines
Quoted 6 lines
Quoted 4 lines
Changes in v2:
- drop "net:
sk_buffrbnode reorg"
- added original "ip: use rb trees for IP frag queue." commitEric, does this look reasonable to you?
Yes, thanks a lot Florian.
Wonderful, all now queued up, thanks!
greg k-h