The branch stable/13 has been updated by kp:

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

commit dc23abfdea971252ad4041a750167366d8aed0df
Author:     Kristof Provost <k...@freebsd.org>
AuthorDate: 2021-07-24 11:59:34 +0000
Commit:     Kristof Provost <k...@freebsd.org>
CommitDate: 2021-10-06 08:46:53 +0000

    pf: implement adaptive mode
    
    Use atomic counters to ensure that we correctly track the number of half
    open states and syncookie responses in-flight.
    This determines if we activate or deactivate syncookies in adaptive
    mode.
    
    MFC after:      1 week
    Sponsored by:   Modirum MDPay
    Differential Revision:  https://reviews.freebsd.org/D32134
    
    (cherry picked from commit bf8637181a2bb81206ff8c685f1632d07b8feb13)
---
 sys/net/pfvar.h                |  5 ++++-
 sys/netpfil/pf/pf.c            | 15 +++++++++++++
 sys/netpfil/pf/pf_syncookies.c | 51 +++++++++++++++++++++++++++++++++++++-----
 3 files changed, 65 insertions(+), 6 deletions(-)

diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index 1eee2ec36351..b8267e43c0c4 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -1369,7 +1369,8 @@ struct pf_pdesc {
 enum pf_syncookies_mode {
        PF_SYNCOOKIES_NEVER = 0,
        PF_SYNCOOKIES_ALWAYS = 1,
-       PF_SYNCOOKIES_MODE_MAX = PF_SYNCOOKIES_ALWAYS
+       PF_SYNCOOKIES_ADAPTIVE = 2,
+       PF_SYNCOOKIES_MODE_MAX = PF_SYNCOOKIES_ADAPTIVE
 };
 
 #ifdef _KERNEL
@@ -1389,6 +1390,8 @@ struct pf_kstatus {
        bool            keep_counters;
        enum pf_syncookies_mode syncookies_mode;
        bool            syncookies_active;
+       uint64_t        syncookies_inflight[2];
+       uint32_t        states_halfopen;
 };
 #endif
 
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index 20e775148b7a..90c856ce5fcf 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -493,6 +493,15 @@ pf_set_protostate(struct pf_kstate *s, int which, u_int8_t 
newstate)
                s->dst.state = newstate;
        if (which == PF_PEER_DST)
                return;
+       if (s->src.state == newstate)
+               return;
+       if (s->creatorid == V_pf_status.hostid &&
+           s->key[PF_SK_STACK] != NULL &&
+           s->key[PF_SK_STACK]->proto == IPPROTO_TCP &&
+           !(TCPS_HAVEESTABLISHED(s->src.state) ||
+           s->src.state == TCPS_CLOSED) &&
+           (TCPS_HAVEESTABLISHED(newstate) || newstate == TCPS_CLOSED))
+               atomic_add_32(&V_pf_status.states_halfopen, -1);
 
        s->src.state = newstate;
 }
@@ -1924,6 +1933,11 @@ pf_unlink_state(struct pf_kstate *s, u_int flags)
 
        s->timeout = PFTM_UNLINKED;
 
+       /* Ensure we remove it from the list of halfopen states, if needed. */
+       if (s->key[PF_SK_STACK] != NULL &&
+           s->key[PF_SK_STACK]->proto == IPPROTO_TCP)
+               pf_set_protostate(s, PF_PEER_BOTH, TCPS_CLOSED);
+
        PF_HASHROW_UNLOCK(ih);
 
        pf_detach_state(s);
@@ -4014,6 +4028,7 @@ pf_create_state(struct pf_krule *r, struct pf_krule *nr, 
struct pf_krule *a,
                pf_set_protostate(s, PF_PEER_SRC, TCPS_SYN_SENT);
                pf_set_protostate(s, PF_PEER_DST, TCPS_CLOSED);
                s->timeout = PFTM_TCP_FIRST_PACKET;
+               atomic_add_32(&V_pf_status.states_halfopen, 1);
                break;
        case IPPROTO_UDP:
                pf_set_protostate(s, PF_PEER_SRC, PFUDPS_SINGLE);
diff --git a/sys/netpfil/pf/pf_syncookies.c b/sys/netpfil/pf/pf_syncookies.c
index 4eabbb5e2744..11093b636777 100644
--- a/sys/netpfil/pf/pf_syncookies.c
+++ b/sys/netpfil/pf/pf_syncookies.c
@@ -106,6 +106,8 @@ struct pf_syncookie_status {
        struct callout  keytimeout;
        uint8_t         oddeven;
        uint8_t         key[2][SIPHASH_KEY_LENGTH];
+       uint32_t        hiwat;  /* absolute; # of states */
+       uint32_t        lowat;
 };
 VNET_DEFINE_STATIC(struct pf_syncookie_status, pf_syncookie_status);
 #define V_pf_syncookie_status  VNET(pf_syncookie_status)
@@ -242,7 +244,24 @@ pf_synflood_check(struct pf_pdesc *pd)
        if (pd->pf_mtag && (pd->pf_mtag->tag & PF_TAG_SYNCOOKIE_RECREATED))
                return (0);
 
-       return (V_pf_status.syncookies_mode);
+       if (V_pf_status.syncookies_mode != PF_SYNCOOKIES_ADAPTIVE)
+               return (V_pf_status.syncookies_mode);
+
+       if (!V_pf_status.syncookies_active &&
+           atomic_load_32(&V_pf_status.states_halfopen) >
+           V_pf_syncookie_status.hiwat) {
+               /* We'd want to 'pf_syncookie_newkey()' here, but that requires
+                * the rules write lock, which we can't get with the read lock
+                * held. */
+               callout_reset(&V_pf_syncookie_status.keytimeout, 0,
+                   pf_syncookie_rotate, curvnet);
+               V_pf_status.syncookies_active = true;
+               DPFPRINTF(LOG_WARNING,
+                   ("synflood detected, enabling syncookies\n"));
+               // XXXTODO V_pf_status.lcounters[LCNT_SYNFLOODS]++;
+       }
+
+       return (V_pf_status.syncookies_active);
 }
 
 void
@@ -257,6 +276,9 @@ pf_syncookie_send(struct mbuf *m, int off, struct pf_pdesc 
*pd)
            iss, ntohl(pd->hdr.tcp.th_seq) + 1, TH_SYN|TH_ACK, 0, mss,
            0, 1, 0);
        counter_u64_add(V_pf_status.lcounters[KLCNT_SYNCOOKIES_SENT], 1);
+       /* XXX Maybe only in adaptive mode? */
+       
atomic_add_64(&V_pf_status.syncookies_inflight[V_pf_syncookie_status.oddeven],
+           1);
 }
 
 uint8_t
@@ -272,11 +294,17 @@ pf_syncookie_validate(struct pf_pdesc *pd)
        ack = ntohl(pd->hdr.tcp.th_ack) - 1;
        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])
+           == 0)
+                return (0);
+
        hash = pf_syncookie_mac(pd, cookie, seq);
        if ((ack & ~0xff) != (hash & ~0xff))
                return (0);
 
        counter_u64_add(V_pf_status.lcounters[KLCNT_SYNCOOKIES_VALID], 1);
+       atomic_add_64(&V_pf_status.syncookies_inflight[cookie.flags.oddeven], 
-1);
 
        return (1);
 }
@@ -290,13 +318,22 @@ pf_syncookie_rotate(void *arg)
        CURVNET_SET((struct vnet *)arg);
 
        /* do we want to disable syncookies? */
-       if (V_pf_status.syncookies_active) {
+       if (V_pf_status.syncookies_active &&
+           ((V_pf_status.syncookies_mode == PF_SYNCOOKIES_ADAPTIVE &&
+           (atomic_load_32(&V_pf_status.states_halfopen) +
+           atomic_load_64(&V_pf_status.syncookies_inflight[0]) +
+           atomic_load_64(&V_pf_status.syncookies_inflight[1])) <
+           V_pf_syncookie_status.lowat) ||
+           V_pf_status.syncookies_mode == PF_SYNCOOKIES_NEVER)
+                       ) {
                V_pf_status.syncookies_active = false;
-               DPFPRINTF(PF_DEBUG_MISC, ("syncookies disabled"));
+               DPFPRINTF(PF_DEBUG_MISC, ("syncookies disabled\n"));
        }
 
        /* nothing in flight any more? delete keys and return */
-       if (!V_pf_status.syncookies_active) {
+       if (!V_pf_status.syncookies_active &&
+           atomic_load_64(&V_pf_status.syncookies_inflight[0]) == 0 &&
+           atomic_load_64(&V_pf_status.syncookies_inflight[1]) == 0) {
                memset(V_pf_syncookie_status.key[0], 0,
                    PF_SYNCOOKIE_SECRET_SIZE);
                memset(V_pf_syncookie_status.key[1], 0,
@@ -305,8 +342,10 @@ pf_syncookie_rotate(void *arg)
                return;
        }
 
+       PF_RULES_WLOCK();
        /* new key, including timeout */
        pf_syncookie_newkey();
+       PF_RULES_WUNLOCK();
 
        CURVNET_RESTORE();
 }
@@ -316,11 +355,13 @@ pf_syncookie_newkey(void)
 {
        PF_RULES_WASSERT();
 
+       MPASS(V_pf_syncookie_status.oddeven < 2);
        V_pf_syncookie_status.oddeven = (V_pf_syncookie_status.oddeven + 1) & 
0x1;
+       
atomic_store_64(&V_pf_status.syncookies_inflight[V_pf_syncookie_status.oddeven],
 0);
        arc4random_buf(V_pf_syncookie_status.key[V_pf_syncookie_status.oddeven],
            PF_SYNCOOKIE_SECRET_SIZE);
        callout_reset(&V_pf_syncookie_status.keytimeout,
-           PF_SYNCOOKIE_SECRET_LIFETIME, pf_syncookie_rotate, curvnet);
+           PF_SYNCOOKIE_SECRET_LIFETIME * hz, pf_syncookie_rotate, curvnet);
 }
 
 /*

Reply via email to