diff options
author | Florian Westphal <fw@strlen.de> | 2023-01-11 14:42:32 +0100 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2023-01-17 23:00:06 +0100 |
commit | c410cb974f2ba562920ecb8492ee66945dcf88af (patch) | |
tree | af479529f15ee899a919e0a853b56f45555d8a8f /net/netfilter | |
parent | 1f3bd64ad921f051254591fbed04fd30b306cde6 (diff) | |
download | linux-stable-c410cb974f2ba562920ecb8492ee66945dcf88af.tar.gz linux-stable-c410cb974f2ba562920ecb8492ee66945dcf88af.tar.bz2 linux-stable-c410cb974f2ba562920ecb8492ee66945dcf88af.zip |
netfilter: conntrack: handle tcp challenge acks during connection reuse
When a connection is re-used, following can happen:
[ connection starts to close, fin sent in either direction ]
> syn # initator quickly reuses connection
< ack # peer sends a challenge ack
> rst # rst, sequence number == ack_seq of previous challenge ack
> syn # this syn is expected to pass
Problem is that the rst will fail window validation, so it gets
tagged as invalid.
If ruleset drops such packets, we get repeated syn-retransmits until
initator gives up or peer starts responding with syn/ack.
Before the commit indicated in the "Fixes" tag below this used to work:
The challenge-ack made conntrack re-init state based on the challenge
ack itself, so the following rst would pass window validation.
Add challenge-ack support: If we get ack for syn, record the ack_seq,
and then check if the rst sequence number matches the last ack number
seen in reverse direction.
Fixes: c7aab4f17021 ("netfilter: nf_conntrack_tcp: re-init for syn packets only")
Reported-by: Michal Tesar <mtesar@redhat.com>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'net/netfilter')
-rw-r--r-- | net/netfilter/nf_conntrack_proto_tcp.c | 15 |
1 files changed, 15 insertions, 0 deletions
diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index 656631083177..3ac1af6f59fc 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -1068,6 +1068,13 @@ int nf_conntrack_tcp_packet(struct nf_conn *ct, ct->proto.tcp.last_flags |= IP_CT_EXP_CHALLENGE_ACK; } + + /* possible challenge ack reply to syn */ + if (old_state == TCP_CONNTRACK_SYN_SENT && + index == TCP_ACK_SET && + dir == IP_CT_DIR_REPLY) + ct->proto.tcp.last_ack = ntohl(th->ack_seq); + spin_unlock_bh(&ct->lock); nf_ct_l4proto_log_invalid(skb, ct, state, "packet (index %d) in dir %d ignored, state %s", @@ -1193,6 +1200,14 @@ int nf_conntrack_tcp_packet(struct nf_conn *ct, * segments we ignored. */ goto in_window; } + + /* Reset in response to a challenge-ack we let through earlier */ + if (old_state == TCP_CONNTRACK_SYN_SENT && + ct->proto.tcp.last_index == TCP_ACK_SET && + ct->proto.tcp.last_dir == IP_CT_DIR_REPLY && + ntohl(th->seq) == ct->proto.tcp.last_ack) + goto in_window; + break; default: /* Keep compilers happy. */ |