aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKristof Provost <kp@FreeBSD.org>2021-07-24 11:59:34 +0000
committerKristof Provost <kp@FreeBSD.org>2021-09-29 13:11:54 +0000
commitbf8637181a2bb81206ff8c685f1632d07b8feb13 (patch)
tree0f9b99ef31c7bcac7e230aa8e980e1cc7aab0316
parent4a331971d2f1083f35b87197da0614fa3e0e53cb (diff)
downloadsrc-bf8637181a2bb81206ff8c685f1632d07b8feb13.tar.gz
src-bf8637181a2bb81206ff8c685f1632d07b8feb13.zip
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
-rw-r--r--sys/net/pfvar.h5
-rw-r--r--sys/netpfil/pf/pf.c15
-rw-r--r--sys/netpfil/pf/pf_syncookies.c51
3 files changed, 65 insertions, 6 deletions
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index b2c177fba68c..90ef19c59172 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -1382,7 +1382,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
@@ -1402,6 +1403,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 ac329a37f7bd..b67f688c3f3f 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -500,6 +500,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;
}
@@ -1931,6 +1940,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);
@@ -4035,6 +4049,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);
}
/*