Commit f0a5e4d7 authored by Julian Anastasov's avatar Julian Anastasov Committed by Pablo Neira Ayuso

ipvs: allow connection reuse for unconfirmed conntrack

YangYuxi is reporting that connection reuse
is causing one-second delay when SYN hits
existing connection in TIME_WAIT state.
Such delay was added to give time to expire
both the IPVS connection and the corresponding
conntrack. This was considered a rare case
at that time but it is causing problem for
some environments such as Kubernetes.

As nf_conntrack_tcp_packet() can decide to
release the conntrack in TIME_WAIT state and
to replace it with a fresh NEW conntrack, we
can use this to allow rescheduling just by
tuning our check: if the conntrack is
confirmed we can not schedule it to different
real server and the one-second delay still
applies but if new conntrack was created,
we are free to select new real server without
any delays.

YangYuxi lists some of the problem reports:

- One second connection delay in masquerading mode:
https://marc.info/?t=151683118100004&r=1&w=2

- IPVS low throughput #70747
https://github.com/kubernetes/kubernetes/issues/70747

- Apache Bench can fill up ipvs service proxy in seconds #544
https://github.com/cloudnativelabs/kube-router/issues/544

- Additional 1s latency in `host -> service IP -> pod`
https://github.com/kubernetes/kubernetes/issues/90854

Fixes: f719e375 ("ipvs: drop first packet to redirect conntrack")
Co-developed-by: default avatarYangYuxi <yx.atom1@gmail.com>
Signed-off-by: default avatarYangYuxi <yx.atom1@gmail.com>
Signed-off-by: default avatarJulian Anastasov <ja@ssi.bg>
Reviewed-by: default avatarSimon Horman <horms@verge.net.au>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent f9200a52
...@@ -1626,18 +1626,16 @@ static inline void ip_vs_conn_drop_conntrack(struct ip_vs_conn *cp) ...@@ -1626,18 +1626,16 @@ static inline void ip_vs_conn_drop_conntrack(struct ip_vs_conn *cp)
} }
#endif /* CONFIG_IP_VS_NFCT */ #endif /* CONFIG_IP_VS_NFCT */
/* Really using conntrack? */ /* Using old conntrack that can not be redirected to another real server? */
static inline bool ip_vs_conn_uses_conntrack(struct ip_vs_conn *cp, static inline bool ip_vs_conn_uses_old_conntrack(struct ip_vs_conn *cp,
struct sk_buff *skb) struct sk_buff *skb)
{ {
#ifdef CONFIG_IP_VS_NFCT #ifdef CONFIG_IP_VS_NFCT
enum ip_conntrack_info ctinfo; enum ip_conntrack_info ctinfo;
struct nf_conn *ct; struct nf_conn *ct;
if (!(cp->flags & IP_VS_CONN_F_NFCT))
return false;
ct = nf_ct_get(skb, &ctinfo); ct = nf_ct_get(skb, &ctinfo);
if (ct) if (ct && nf_ct_is_confirmed(ct))
return true; return true;
#endif #endif
return false; return false;
......
...@@ -2066,14 +2066,14 @@ ip_vs_in(struct netns_ipvs *ipvs, unsigned int hooknum, struct sk_buff *skb, int ...@@ -2066,14 +2066,14 @@ ip_vs_in(struct netns_ipvs *ipvs, unsigned int hooknum, struct sk_buff *skb, int
conn_reuse_mode = sysctl_conn_reuse_mode(ipvs); conn_reuse_mode = sysctl_conn_reuse_mode(ipvs);
if (conn_reuse_mode && !iph.fragoffs && is_new_conn(skb, &iph) && cp) { if (conn_reuse_mode && !iph.fragoffs && is_new_conn(skb, &iph) && cp) {
bool uses_ct = false, resched = false; bool old_ct = false, resched = false;
if (unlikely(sysctl_expire_nodest_conn(ipvs)) && cp->dest && if (unlikely(sysctl_expire_nodest_conn(ipvs)) && cp->dest &&
unlikely(!atomic_read(&cp->dest->weight))) { unlikely(!atomic_read(&cp->dest->weight))) {
resched = true; resched = true;
uses_ct = ip_vs_conn_uses_conntrack(cp, skb); old_ct = ip_vs_conn_uses_old_conntrack(cp, skb);
} else if (is_new_conn_expected(cp, conn_reuse_mode)) { } else if (is_new_conn_expected(cp, conn_reuse_mode)) {
uses_ct = ip_vs_conn_uses_conntrack(cp, skb); old_ct = ip_vs_conn_uses_old_conntrack(cp, skb);
if (!atomic_read(&cp->n_control)) { if (!atomic_read(&cp->n_control)) {
resched = true; resched = true;
} else { } else {
...@@ -2081,15 +2081,17 @@ ip_vs_in(struct netns_ipvs *ipvs, unsigned int hooknum, struct sk_buff *skb, int ...@@ -2081,15 +2081,17 @@ ip_vs_in(struct netns_ipvs *ipvs, unsigned int hooknum, struct sk_buff *skb, int
* that uses conntrack while it is still * that uses conntrack while it is still
* referenced by controlled connection(s). * referenced by controlled connection(s).
*/ */
resched = !uses_ct; resched = !old_ct;
} }
} }
if (resched) { if (resched) {
if (!old_ct)
cp->flags &= ~IP_VS_CONN_F_NFCT;
if (!atomic_read(&cp->n_control)) if (!atomic_read(&cp->n_control))
ip_vs_conn_expire_now(cp); ip_vs_conn_expire_now(cp);
__ip_vs_conn_put(cp); __ip_vs_conn_put(cp);
if (uses_ct) if (old_ct)
return NF_DROP; return NF_DROP;
cp = NULL; cp = NULL;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment