Commit c4edc3cc authored by Ali Abdallah's avatar Ali Abdallah Committed by Pablo Neira Ayuso
Browse files

netfilter: conntrack: improve RST handling when tuple is re-used



If we receive a SYN packet in original direction on an existing
connection tracking entry, we let this SYN through because conntrack
might be out-of-sync.

Conntrack gets back in sync when server responds with SYN/ACK and state
gets updated accordingly.

However, if server replies with RST, this packet might be marked as
INVALID because td_maxack value reflects the *old* conntrack state
and not the state of the originator of the RST.

Avoid td_maxack-based checks if previous packet was a SYN.

Unfortunately that is not be enough: an out of order ACK in original
direction updates last_index, so we still end up marking valid RST.

Thus disable the sequence check when we are not in established state and
the received RST has a sequence of 0.

Because marking RSTs as invalid usually leads to unwanted timeouts,
also skip RST sequence checks if a conntrack entry is already closing.

Such entries can already be evicted via GC in case the table is full.

Co-developed-by: default avatarFlorian Westphal <fw@strlen.de>
Signed-off-by: default avatarFlorian Westphal <fw@strlen.de>
Signed-off-by: default avatarAli Abdallah <aabdallah@suse.de>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent c23a9fd2
Loading
Loading
Loading
Loading
+36 −17
Original line number Diff line number Diff line
@@ -823,6 +823,22 @@ static noinline bool tcp_new(struct nf_conn *ct, const struct sk_buff *skb,
	return true;
}

static bool tcp_can_early_drop(const struct nf_conn *ct)
{
	switch (ct->proto.tcp.state) {
	case TCP_CONNTRACK_FIN_WAIT:
	case TCP_CONNTRACK_LAST_ACK:
	case TCP_CONNTRACK_TIME_WAIT:
	case TCP_CONNTRACK_CLOSE:
	case TCP_CONNTRACK_CLOSE_WAIT:
		return true;
	default:
		break;
	}

	return false;
}

/* Returns verdict for packet, or -1 for invalid. */
int nf_conntrack_tcp_packet(struct nf_conn *ct,
			    struct sk_buff *skb,
@@ -1030,9 +1046,28 @@ int nf_conntrack_tcp_packet(struct nf_conn *ct,
		if (index != TCP_RST_SET)
			break;

		if (ct->proto.tcp.seen[!dir].flags & IP_CT_TCP_FLAG_MAXACK_SET) {
		/* If we are closing, tuple might have been re-used already.
		 * last_index, last_ack, and all other ct fields used for
		 * sequence/window validation are outdated in that case.
		 *
		 * As the conntrack can already be expired by GC under pressure,
		 * just skip validation checks.
		 */
		if (tcp_can_early_drop(ct))
			goto in_window;

		/* td_maxack might be outdated if we let a SYN through earlier */
		if ((ct->proto.tcp.seen[!dir].flags & IP_CT_TCP_FLAG_MAXACK_SET) &&
		    ct->proto.tcp.last_index != TCP_SYN_SET) {
			u32 seq = ntohl(th->seq);

			/* If we are not in established state and SEQ=0 this is most
			 * likely an answer to a SYN we let go through above (last_index
			 * can be updated due to out-of-order ACKs).
			 */
			if (seq == 0 && !nf_conntrack_tcp_established(ct))
				break;

			if (before(seq, ct->proto.tcp.seen[!dir].td_maxack)) {
				/* Invalid RST  */
				spin_unlock_bh(&ct->lock);
@@ -1165,22 +1200,6 @@ int nf_conntrack_tcp_packet(struct nf_conn *ct,
	return NF_ACCEPT;
}

static bool tcp_can_early_drop(const struct nf_conn *ct)
{
	switch (ct->proto.tcp.state) {
	case TCP_CONNTRACK_FIN_WAIT:
	case TCP_CONNTRACK_LAST_ACK:
	case TCP_CONNTRACK_TIME_WAIT:
	case TCP_CONNTRACK_CLOSE:
	case TCP_CONNTRACK_CLOSE_WAIT:
		return true;
	default:
		break;
	}

	return false;
}

#if IS_ENABLED(CONFIG_NF_CT_NETLINK)

#include <linux/netfilter/nfnetlink.h>