]> git.hungrycats.org Git - linux/commitdiff
ipvs: drop first packet to redirect conntrack
authorJulian Anastasov <ja@ssi.bg>
Sat, 5 Mar 2016 13:03:22 +0000 (15:03 +0200)
committerSasha Levin <sasha.levin@oracle.com>
Mon, 11 Jul 2016 03:07:16 +0000 (23:07 -0400)
[ Upstream commit f719e3754ee2f7275437e61a6afd520181fdd43b ]

Jiri Bohac is reporting for a problem where the attempt
to reschedule existing connection to another real server
needs proper redirect for the conntrack used by the IPVS
connection. For example, when IPVS connection is created
to NAT-ed real server we alter the reply direction of
conntrack. If we later decide to select different real
server we can not alter again the conntrack. And if we
expire the old connection, the new connection is left
without conntrack.

So, the only way to redirect both the IPVS connection and
the Netfilter's conntrack is to drop the SYN packet that
hits existing connection, to wait for the next jiffie
to expire the old connection and its conntrack and to rely
on client's retransmission to create new connection as
usually.

Jiri Bohac provided a fix that drops all SYNs on rescheduling,
I extended his patch to do such drops only for connections
that use conntrack. Here is the original report from Jiri Bohac:

Since commit dc7b3eb900aa ("ipvs: Fix reuse connection if real server
is dead"), new connections to dead servers are redistributed
immediately to new servers.  The old connection is expired using
ip_vs_conn_expire_now() which sets the connection timer to expire
immediately.

However, before the timer callback, ip_vs_conn_expire(), is run
to clean the connection's conntrack entry, the new redistributed
connection may already be established and its conntrack removed
instead.

Fix this by dropping the first packet of the new connection
instead, like we do when the destination server is not available.
The timer will have deleted the old conntrack entry long before
the first packet of the new connection is retransmitted.

Fixes: dc7b3eb900aa ("ipvs: Fix reuse connection if real server is dead")
Signed-off-by: Jiri Bohac <jbohac@suse.cz>
Signed-off-by: Julian Anastasov <ja@ssi.bg>
Signed-off-by: Simon Horman <horms@verge.net.au>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
include/net/ip_vs.h
net/netfilter/ipvs/ip_vs_core.c

index 4e3731ee4eac802e24e5d7259d0e4db36566d2f6..cd8594528d4bbb4f90431031e2332cf15a5d4627 100644 (file)
@@ -1584,6 +1584,23 @@ static inline void ip_vs_conn_drop_conntrack(struct ip_vs_conn *cp)
 }
 #endif /* CONFIG_IP_VS_NFCT */
 
+/* Really using conntrack? */
+static inline bool ip_vs_conn_uses_conntrack(struct ip_vs_conn *cp,
+                                            struct sk_buff *skb)
+{
+#ifdef CONFIG_IP_VS_NFCT
+       enum ip_conntrack_info ctinfo;
+       struct nf_conn *ct;
+
+       if (!(cp->flags & IP_VS_CONN_F_NFCT))
+               return false;
+       ct = nf_ct_get(skb, &ctinfo);
+       if (ct && !nf_ct_is_untracked(ct))
+               return true;
+#endif
+       return false;
+}
+
 static inline int
 ip_vs_dest_conn_overhead(struct ip_vs_dest *dest)
 {
index 38fbc194b9cb72835c497439713d23f16d99ca8c..a26bd65328295902776f09143dd1e44c7e7c6f38 100644 (file)
@@ -1689,15 +1689,34 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af)
        cp = pp->conn_in_get(af, skb, &iph, 0);
 
        conn_reuse_mode = sysctl_conn_reuse_mode(ipvs);
-       if (conn_reuse_mode && !iph.fragoffs &&
-           is_new_conn(skb, &iph) && cp &&
-           ((unlikely(sysctl_expire_nodest_conn(ipvs)) && cp->dest &&
-             unlikely(!atomic_read(&cp->dest->weight))) ||
-            unlikely(is_new_conn_expected(cp, conn_reuse_mode)))) {
-               if (!atomic_read(&cp->n_control))
-                       ip_vs_conn_expire_now(cp);
-               __ip_vs_conn_put(cp);
-               cp = NULL;
+       if (conn_reuse_mode && !iph.fragoffs && is_new_conn(skb, &iph) && cp) {
+               bool uses_ct = false, resched = false;
+
+               if (unlikely(sysctl_expire_nodest_conn(ipvs)) && cp->dest &&
+                   unlikely(!atomic_read(&cp->dest->weight))) {
+                       resched = true;
+                       uses_ct = ip_vs_conn_uses_conntrack(cp, skb);
+               } else if (is_new_conn_expected(cp, conn_reuse_mode)) {
+                       uses_ct = ip_vs_conn_uses_conntrack(cp, skb);
+                       if (!atomic_read(&cp->n_control)) {
+                               resched = true;
+                       } else {
+                               /* Do not reschedule controlling connection
+                                * that uses conntrack while it is still
+                                * referenced by controlled connection(s).
+                                */
+                               resched = !uses_ct;
+                       }
+               }
+
+               if (resched) {
+                       if (!atomic_read(&cp->n_control))
+                               ip_vs_conn_expire_now(cp);
+                       __ip_vs_conn_put(cp);
+                       if (uses_ct)
+                               return NF_DROP;
+                       cp = NULL;
+               }
        }
 
        if (unlikely(!cp) && !iph.fragoffs) {