From nobody Wed Oct 06 08:47:27 2021 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 376A917E7282; Wed, 6 Oct 2021 08:47:28 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4HPSkm15NCz4Vrg; Wed, 6 Oct 2021 08:47:28 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 066571D28C; Wed, 6 Oct 2021 08:47:28 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.16.1/8.16.1) with ESMTP id 1968lRhV060732; Wed, 6 Oct 2021 08:47:27 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 1968lRsn060731; Wed, 6 Oct 2021 08:47:27 GMT (envelope-from git) Date: Wed, 6 Oct 2021 08:47:27 GMT Message-Id: <202110060847.1968lRsn060731@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Kristof Provost Subject: git: d23f5f76f56d - stable/12 - pf: implement adaptive mode List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-all@freebsd.org X-BeenThere: dev-commits-src-all@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: kp X-Git-Repository: src X-Git-Refname: refs/heads/stable/12 X-Git-Reftype: branch X-Git-Commit: d23f5f76f56d986008c49c7e74bf78e9cad63aa7 Auto-Submitted: auto-generated X-ThisMailContainsUnwantedMimeParts: N The branch stable/12 has been updated by kp: URL: https://cgit.FreeBSD.org/src/commit/?id=d23f5f76f56d986008c49c7e74bf78e9cad63aa7 commit d23f5f76f56d986008c49c7e74bf78e9cad63aa7 Author: Kristof Provost AuthorDate: 2021-07-24 11:59:34 +0000 Commit: Kristof Provost CommitDate: 2021-10-06 08:46:57 +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 57d19c6c6018..5e2022ae8828 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 2401dff57dff..dd4c49959a60 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; } @@ -1922,6 +1931,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); @@ -4015,6 +4029,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 c019640d7cb5..d054b6823e74 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); } /*