-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;