Skip to content

Commit 50d9ce9

Browse files
Florian Westphalummakynes
authored andcommitted
netfilter: nf_nat: also check reverse tuple to obtain clashing entry
The logic added in the blamed commit was supposed to only omit nat source port allocation if neither the existing nor the new entry are subject to NAT. However, its not enough to lookup the conntrack based on the proposed tuple, we must also check the reverse direction. Otherwise there are esoteric cases where the collision is in the reverse direction because that colliding connection has a port rewrite, but the new entry doesn't. In this case, we only check the new entry and then erronously conclude that no clash exists anymore. The existing (udp) tuple is: a:p -> b:P, with nat translation to s:P, i.e. pure daddr rewrite, reverse tuple in conntrack table is s:P -> a:p. When another UDP packet is sent directly to s, i.e. a:p->s:P, this is correctly detected as a colliding entry: tuple is taken by existing reply tuple in reverse direction. But the colliding conntrack is only searched for with unreversed direction, and we can't find such entry matching a:p->s:P. The incorrect conclusion is that the clashing entry has timed out and that no port address translation is required. Such conntrack will then be discarded at nf_confirm time because the proposed reverse direction clashes with an existing mapping in the conntrack table. Search for the reverse tuple too, this will then check the NAT bits of the colliding entry and triggers port reallocation. Followp patch extends nft_nat.sh selftest to cover this scenario. The IPS_SEQ_ADJUST change is also a bug fix: Instead of checking for SEQ_ADJ this tested for SEEN_REPLY and ASSURED by accident -- _BIT is only for use with the test_bit() API. This bug has little consequence in practice, because the sequence number adjustments are only useful for TCP which doesn't support clash resolution. The existing test case (conntrack_reverse_clash.sh) exercise a race condition path (parallel conntrack creation on different CPUs), so the colliding entries have neither SEEN_REPLY nor ASSURED set. Thanks to Yafang Shao and Shaun Brady for an initial investigation of this bug. Fixes: d8f84a9 ("netfilter: nf_nat: don't try nat source port reallocation for reverse dir clash") Closes: https://bugzilla.netfilter.org/show_bug.cgi?id=1795 Reported-by: Yafang Shao <[email protected]> Reported-by: Shaun Brady <[email protected]> Signed-off-by: Florian Westphal <[email protected]> Tested-by: Yafang Shao <[email protected]> Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent 38399f2 commit 50d9ce9

File tree

1 file changed

+9
-3
lines changed

1 file changed

+9
-3
lines changed

net/netfilter/nf_nat_core.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ static noinline bool
248248
nf_nat_used_tuple_new(const struct nf_conntrack_tuple *tuple,
249249
const struct nf_conn *ignored_ct)
250250
{
251-
static const unsigned long uses_nat = IPS_NAT_MASK | IPS_SEQ_ADJUST_BIT;
251+
static const unsigned long uses_nat = IPS_NAT_MASK | IPS_SEQ_ADJUST;
252252
const struct nf_conntrack_tuple_hash *thash;
253253
const struct nf_conntrack_zone *zone;
254254
struct nf_conn *ct;
@@ -287,8 +287,14 @@ nf_nat_used_tuple_new(const struct nf_conntrack_tuple *tuple,
287287
zone = nf_ct_zone(ignored_ct);
288288

289289
thash = nf_conntrack_find_get(net, zone, tuple);
290-
if (unlikely(!thash)) /* clashing entry went away */
291-
return false;
290+
if (unlikely(!thash)) {
291+
struct nf_conntrack_tuple reply;
292+
293+
nf_ct_invert_tuple(&reply, tuple);
294+
thash = nf_conntrack_find_get(net, zone, &reply);
295+
if (!thash) /* clashing entry went away */
296+
return false;
297+
}
292298

293299
ct = nf_ct_tuplehash_to_ctrack(thash);
294300

0 commit comments

Comments
 (0)