]>
Commit | Line | Data |
---|---|---|
415969cc SS |
1 | commit ed07d9a021df6da53456663a76999189badc432a |
2 | Author: Martynas Pumputis <martynas@weave.works> | |
3 | Date: Mon Jul 2 16:52:14 2018 +0200 | |
4 | ||
5 | netfilter: nf_conntrack: resolve clash for matching conntracks | |
6 | ||
7 | This patch enables the clash resolution for NAT (disabled in | |
8 | "590b52e10d41") if clashing conntracks match (i.e. both tuples are equal) | |
9 | and a protocol allows it. | |
10 | ||
11 | The clash might happen for a connections-less protocol (e.g. UDP) when | |
12 | two threads in parallel writes to the same socket and consequent calls | |
13 | to "get_unique_tuple" return the same tuples (incl. reply tuples). | |
14 | ||
15 | In this case it is safe to perform the resolution, as the losing CT | |
16 | describes the same mangling as the winning CT, so no modifications to | |
17 | the packet are needed, and the result of rules traversal for the loser's | |
18 | packet stays valid. | |
19 | ||
20 | Signed-off-by: Martynas Pumputis <martynas@weave.works> | |
21 | Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> | |
22 | ||
23 | diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c | |
24 | index 5123e91b1982..4ced7c7102b6 100644 | |
25 | --- a/net/netfilter/nf_conntrack_core.c | |
26 | +++ b/net/netfilter/nf_conntrack_core.c | |
27 | @@ -632,6 +632,18 @@ nf_ct_key_equal(struct nf_conntrack_tuple_hash *h, | |
28 | net_eq(net, nf_ct_net(ct)); | |
29 | } | |
30 | ||
31 | +static inline bool | |
32 | +nf_ct_match(const struct nf_conn *ct1, const struct nf_conn *ct2) | |
33 | +{ | |
34 | + return nf_ct_tuple_equal(&ct1->tuplehash[IP_CT_DIR_ORIGINAL].tuple, | |
35 | + &ct2->tuplehash[IP_CT_DIR_ORIGINAL].tuple) && | |
36 | + nf_ct_tuple_equal(&ct1->tuplehash[IP_CT_DIR_REPLY].tuple, | |
37 | + &ct2->tuplehash[IP_CT_DIR_REPLY].tuple) && | |
38 | + nf_ct_zone_equal(ct1, nf_ct_zone(ct2), IP_CT_DIR_ORIGINAL) && | |
39 | + nf_ct_zone_equal(ct1, nf_ct_zone(ct2), IP_CT_DIR_REPLY) && | |
40 | + net_eq(nf_ct_net(ct1), nf_ct_net(ct2)); | |
41 | +} | |
42 | + | |
43 | /* caller must hold rcu readlock and none of the nf_conntrack_locks */ | |
44 | static void nf_ct_gc_expired(struct nf_conn *ct) | |
45 | { | |
46 | @@ -825,19 +837,21 @@ static int nf_ct_resolve_clash(struct net *net, struct sk_buff *skb, | |
47 | /* This is the conntrack entry already in hashes that won race. */ | |
48 | struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(h); | |
49 | const struct nf_conntrack_l4proto *l4proto; | |
50 | + enum ip_conntrack_info oldinfo; | |
51 | + struct nf_conn *loser_ct = nf_ct_get(skb, &oldinfo); | |
52 | ||
53 | l4proto = __nf_ct_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct)); | |
54 | if (l4proto->allow_clash && | |
55 | - ((ct->status & IPS_NAT_DONE_MASK) == 0) && | |
56 | !nf_ct_is_dying(ct) && | |
57 | atomic_inc_not_zero(&ct->ct_general.use)) { | |
58 | - enum ip_conntrack_info oldinfo; | |
59 | - struct nf_conn *loser_ct = nf_ct_get(skb, &oldinfo); | |
60 | - | |
61 | - nf_ct_acct_merge(ct, ctinfo, loser_ct); | |
62 | - nf_conntrack_put(&loser_ct->ct_general); | |
63 | - nf_ct_set(skb, ct, oldinfo); | |
64 | - return NF_ACCEPT; | |
65 | + if (((ct->status & IPS_NAT_DONE_MASK) == 0) || | |
66 | + nf_ct_match(ct, loser_ct)) { | |
67 | + nf_ct_acct_merge(ct, ctinfo, loser_ct); | |
68 | + nf_conntrack_put(&loser_ct->ct_general); | |
69 | + nf_ct_set(skb, ct, oldinfo); | |
70 | + return NF_ACCEPT; | |
71 | + } | |
72 | + nf_ct_put(ct); | |
73 | } | |
74 | NF_CT_STAT_INC(net, drop); | |
75 | return NF_DROP; |