kernel: Backport patch to fix a netfilter contrack related issue.

Message ID 20190920183305.2752-1-stefan.schantl@ipfire.org
State Accepted
Commit 415969cc1b8edd06ee84375614c4eb06cf182d36
Headers
Series kernel: Backport patch to fix a netfilter contrack related issue. |

Commit Message

Stefan Schantl Sept. 20, 2019, 6:33 p.m. UTC
  This fixes the packet drop issue when using suricata on IPFire.

Signed-off-by: Stefan Schantl <stefan.schantl@ipfire.org>
---
 lfs/linux                                     |  3 +
 ....0-netfilter-conntrack-resolve-clash.patch | 75 +++++++++++++++++++
 2 files changed, 78 insertions(+)
 create mode 100644 src/patches/linux/linux-5.0-netfilter-conntrack-resolve-clash.patch
  

Patch

diff --git a/lfs/linux b/lfs/linux
index a9e30714f..a0b28652d 100644
--- a/lfs/linux
+++ b/lfs/linux
@@ -146,6 +146,9 @@  $(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
 	# Fix uevent PHYSDEVDRIVER
 	cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/linux/linux-2.6.32.27_mcs7830-fix-driver-name.patch
 
+	# Fix for netfilter nf_conntrack: resolve clash for matching conntracks
+	cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/linux/linux-5.0-netfilter-conntrack-resolve-clash.patch
+
 ifeq "$(KCFG)" "-kirkwood"
 	cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/linux/linux-4.14.40-kirkwood-dtb.patch
 endif
diff --git a/src/patches/linux/linux-5.0-netfilter-conntrack-resolve-clash.patch b/src/patches/linux/linux-5.0-netfilter-conntrack-resolve-clash.patch
new file mode 100644
index 000000000..914cd0675
--- /dev/null
+++ b/src/patches/linux/linux-5.0-netfilter-conntrack-resolve-clash.patch
@@ -0,0 +1,75 @@ 
+commit ed07d9a021df6da53456663a76999189badc432a
+Author: Martynas Pumputis <martynas@weave.works>
+Date:   Mon Jul 2 16:52:14 2018 +0200
+
+    netfilter: nf_conntrack: resolve clash for matching conntracks
+    
+    This patch enables the clash resolution for NAT (disabled in
+    "590b52e10d41") if clashing conntracks match (i.e. both tuples are equal)
+    and a protocol allows it.
+    
+    The clash might happen for a connections-less protocol (e.g. UDP) when
+    two threads in parallel writes to the same socket and consequent calls
+    to "get_unique_tuple" return the same tuples (incl. reply tuples).
+    
+    In this case it is safe to perform the resolution, as the losing CT
+    describes the same mangling as the winning CT, so no modifications to
+    the packet are needed, and the result of rules traversal for the loser's
+    packet stays valid.
+    
+    Signed-off-by: Martynas Pumputis <martynas@weave.works>
+    Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+
+diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
+index 5123e91b1982..4ced7c7102b6 100644
+--- a/net/netfilter/nf_conntrack_core.c
++++ b/net/netfilter/nf_conntrack_core.c
+@@ -632,6 +632,18 @@ nf_ct_key_equal(struct nf_conntrack_tuple_hash *h,
+ 	       net_eq(net, nf_ct_net(ct));
+ }
+ 
++static inline bool
++nf_ct_match(const struct nf_conn *ct1, const struct nf_conn *ct2)
++{
++	return nf_ct_tuple_equal(&ct1->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
++				 &ct2->tuplehash[IP_CT_DIR_ORIGINAL].tuple) &&
++	       nf_ct_tuple_equal(&ct1->tuplehash[IP_CT_DIR_REPLY].tuple,
++				 &ct2->tuplehash[IP_CT_DIR_REPLY].tuple) &&
++	       nf_ct_zone_equal(ct1, nf_ct_zone(ct2), IP_CT_DIR_ORIGINAL) &&
++	       nf_ct_zone_equal(ct1, nf_ct_zone(ct2), IP_CT_DIR_REPLY) &&
++	       net_eq(nf_ct_net(ct1), nf_ct_net(ct2));
++}
++
+ /* caller must hold rcu readlock and none of the nf_conntrack_locks */
+ static void nf_ct_gc_expired(struct nf_conn *ct)
+ {
+@@ -825,19 +837,21 @@ static int nf_ct_resolve_clash(struct net *net, struct sk_buff *skb,
+ 	/* This is the conntrack entry already in hashes that won race. */
+ 	struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(h);
+ 	const struct nf_conntrack_l4proto *l4proto;
++	enum ip_conntrack_info oldinfo;
++	struct nf_conn *loser_ct = nf_ct_get(skb, &oldinfo);
+ 
+ 	l4proto = __nf_ct_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct));
+ 	if (l4proto->allow_clash &&
+-	    ((ct->status & IPS_NAT_DONE_MASK) == 0) &&
+ 	    !nf_ct_is_dying(ct) &&
+ 	    atomic_inc_not_zero(&ct->ct_general.use)) {
+-		enum ip_conntrack_info oldinfo;
+-		struct nf_conn *loser_ct = nf_ct_get(skb, &oldinfo);
+-
+-		nf_ct_acct_merge(ct, ctinfo, loser_ct);
+-		nf_conntrack_put(&loser_ct->ct_general);
+-		nf_ct_set(skb, ct, oldinfo);
+-		return NF_ACCEPT;
++		if (((ct->status & IPS_NAT_DONE_MASK) == 0) ||
++		    nf_ct_match(ct, loser_ct)) {
++			nf_ct_acct_merge(ct, ctinfo, loser_ct);
++			nf_conntrack_put(&loser_ct->ct_general);
++			nf_ct_set(skb, ct, oldinfo);
++			return NF_ACCEPT;
++		}
++		nf_ct_put(ct);
+ 	}
+ 	NF_CT_STAT_INC(net, drop);
+ 	return NF_DROP;