git: 9c041b450d5e - main - pf: fix syncookies in conjunction with tcp fast port reuse

From: Kristof Provost <kp_at_FreeBSD.org>
Date: Fri, 13 Jan 2023 10:16:04 UTC
The branch main has been updated by kp:

URL: https://cgit.FreeBSD.org/src/commit/?id=9c041b450d5e604c3e35b5799b60a2c53795feef

commit 9c041b450d5e604c3e35b5799b60a2c53795feef
Author:     Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2022-12-31 14:59:10 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2023-01-13 22:14:12 +0000

    pf: fix syncookies in conjunction with tcp fast port reuse
    
    Basic scenario: we have a closed connection (In TCPS_FIN_WAIT_2), and
    get a new connection (i.e. SYN) re-using the tuple.
    
    Without syncookies we look at the SYN, and completely unlink the old,
    closed state on the SYN.
    With syncookies we send a generated SYN|ACK back, and drop the SYN,
    never looking at the state table.
    
    So when the ACK (i.e. the third step in the three way handshake for
    connection setup) turns up, we’ve not actually removed the old state, so
    we find it, and don’t do the syncookie dance, or allow the new
    connection to get set up.
    
    Explicitly check for this in pf_test_state_tcp(). If we find a state in
    TCPS_FIN_WAIT_2 and the syncookie is valid we delete the existing state
    so we can set up the new state.
    Note that when we verify the syncookie in pf_test_state_tcp() we don't
    decrement the number of half-open connections to avoid an incorrect
    double decrement.
    
    MFC after:      2 weeks
    Differential Revision:  https://reviews.freebsd.org/D37919
---
 sys/net/pfvar.h                |  1 +
 sys/netpfil/pf/pf.c            |  8 +++++---
 sys/netpfil/pf/pf_syncookies.c | 23 +++++++++++++++++++----
 3 files changed, 25 insertions(+), 7 deletions(-)

diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index f08ef0d0bfa0..bb058c8e8ae6 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -2263,6 +2263,7 @@ int			 pf_set_syncookies(struct pfioc_nv *);
 int			 pf_synflood_check(struct pf_pdesc *);
 void			 pf_syncookie_send(struct mbuf *m, int off,
 			    struct pf_pdesc *);
+bool			 pf_syncookie_check(struct pf_pdesc *);
 u_int8_t		 pf_syncookie_validate(struct pf_pdesc *);
 struct mbuf *		 pf_syncookie_recreate_syn(uint8_t, int,
 			    struct pf_pdesc *);
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index c0c34fba0409..ca08023ea05d 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -5446,9 +5446,11 @@ pf_test_state_tcp(struct pf_kstate **state, int direction, struct pfi_kkif *kif,
 	if ((action = pf_synproxy(pd, state, reason)) != PF_PASS)
 		return (action);
 
-	if (((th->th_flags & (TH_SYN|TH_ACK)) == TH_SYN) &&
-	    dst->state >= TCPS_FIN_WAIT_2 &&
-	    src->state >= TCPS_FIN_WAIT_2) {
+	if (dst->state >= TCPS_FIN_WAIT_2 &&
+	    src->state >= TCPS_FIN_WAIT_2 &&
+	    (((th->th_flags & (TH_SYN|TH_ACK)) == TH_SYN) ||
+	    ((th->th_flags & (TH_SYN|TH_ACK|TH_RST)) == TH_ACK &&
+	    pf_syncookie_check(pd) && pd->dir == PF_IN))) {
 		if (V_pf_status.debug >= PF_DEBUG_MISC) {
 			printf("pf: state reuse ");
 			pf_print_state(*state);
diff --git a/sys/netpfil/pf/pf_syncookies.c b/sys/netpfil/pf/pf_syncookies.c
index 0c9b5a459301..db232579d595 100644
--- a/sys/netpfil/pf/pf_syncookies.c
+++ b/sys/netpfil/pf/pf_syncookies.c
@@ -301,8 +301,8 @@ pf_syncookie_send(struct mbuf *m, int off, struct pf_pdesc *pd)
 	    1);
 }
 
-uint8_t
-pf_syncookie_validate(struct pf_pdesc *pd)
+bool
+pf_syncookie_check(struct pf_pdesc *pd)
 {
 	uint32_t		 hash, ack, seq;
 	union pf_syncookie	 cookie;
@@ -315,14 +315,29 @@ pf_syncookie_validate(struct pf_pdesc *pd)
 	cookie.cookie = (ack & 0xff) ^ (ack >> 24);
 
 	/* we don't know oddeven before setting the cookie (union) */
-        if (atomic_load_64(&V_pf_status.syncookies_inflight[cookie.flags.oddeven])
+	if (atomic_load_64(&V_pf_status.syncookies_inflight[cookie.flags.oddeven])
 	    == 0)
-                return (0);
+		return (0);
 
 	hash = pf_syncookie_mac(pd, cookie, seq);
 	if ((ack & ~0xff) != (hash & ~0xff))
+		return (false);
+
+	return (true);
+}
+
+uint8_t
+pf_syncookie_validate(struct pf_pdesc *pd)
+{
+	uint32_t		 ack;
+	union pf_syncookie	 cookie;
+
+	if (! pf_syncookie_check(pd))
 		return (0);
 
+	ack = ntohl(pd->hdr.tcp.th_ack) - 1;
+	cookie.cookie = (ack & 0xff) ^ (ack >> 24);
+
 	counter_u64_add(V_pf_status.lcounters[KLCNT_SYNCOOKIES_VALID], 1);
 	atomic_add_64(&V_pf_status.syncookies_inflight[cookie.flags.oddeven], -1);