git: bf05236727cf - main - pf: Send syncookies from the receiving thread
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 28 Aug 2026 13:58:08 UTC
The branch main has been updated by bnovkov:
URL: https://cgit.FreeBSD.org/src/commit/?id=bf05236727cf367ee8e22ef47febebb319db9ddb
commit bf05236727cf367ee8e22ef47febebb319db9ddb
Author: Bojan Novković <bnovkov@FreeBSD.org>
AuthorDate: 2026-08-17 14:10:36 +0000
Commit: Bojan Novković <bnovkov@FreeBSD.org>
CommitDate: 2026-08-28 13:56:26 +0000
pf: Send syncookies from the receiving thread
pf sends outbound packets by offloading them to a single per-vnet SWI handler
through the `V_pf_sendqueue` mbuf queue. A large DDoS attack may overwhelm
that per-vnet queue with syncookie packets and cause contention in the SWI
handler that negatively affects other pf operations.
Fix this by sending the initial syncookie challenge from the context
of the receiving thread. This avoids the syncookie-induced contention on
the `pf_intr` mbuf queue.
Sponsored by: Klara, Inc.
Sponsored by: Entersekt
MFC after: 3 weeks
Reviewed by: kp
Differential Revision: https://reviews.freebsd.org/D59068
---
sys/netpfil/pf/pf.c | 67 ++++++++++++++++++++++++------------------
sys/netpfil/pf/pf.h | 9 +++++-
sys/netpfil/pf/pf_syncookies.c | 32 ++++++++++++++++++--
3 files changed, 76 insertions(+), 32 deletions(-)
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index 92b441d1306a..54cf68cee50e 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -2690,6 +2690,43 @@ pf_icmp_mapping(struct pf_pdesc *pd, u_int8_t type,
return (0); /* These types match to their own state */
}
+#ifdef INET
+void
+pf_send_ip_direct(struct mbuf *m)
+{
+ if (pf_isforlocal(m, AF_INET)) {
+ KASSERT(m->m_pkthdr.rcvif == V_loif,
+ ("%s: rcvif != loif", __func__));
+
+ m->m_flags |= M_SKIP_FIREWALL;
+ m->m_pkthdr.csum_flags |= CSUM_IP_VALID | CSUM_IP_CHECKED |
+ CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
+ m->m_pkthdr.csum_data = 0xffff;
+ ip_input(m);
+ } else {
+ ip_output(m, NULL, NULL, 0, NULL, NULL);
+ }
+}
+#endif
+
+#ifdef INET6
+void
+pf_send_ip6_direct(struct mbuf *m)
+{
+ if (pf_isforlocal(m, AF_INET6)) {
+ KASSERT(m->m_pkthdr.rcvif == V_loif,
+ ("%s: rcvif != loif", __func__));
+
+ m->m_flags |= M_SKIP_FIREWALL | M_LOOP;
+ m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
+ m->m_pkthdr.csum_data = 0xffff;
+ ip6_input(m);
+ } else {
+ ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL);
+ }
+}
+#endif
+
void
pf_intr(void *v)
{
@@ -2710,20 +2747,7 @@ pf_intr(void *v)
switch (pfse->pfse_type) {
#ifdef INET
case PFSE_IP: {
- if (pf_isforlocal(pfse->pfse_m, AF_INET)) {
- KASSERT(pfse->pfse_m->m_pkthdr.rcvif == V_loif,
- ("%s: rcvif != loif", __func__));
-
- pfse->pfse_m->m_flags |= M_SKIP_FIREWALL;
- pfse->pfse_m->m_pkthdr.csum_flags |=
- CSUM_IP_VALID | CSUM_IP_CHECKED |
- CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
- pfse->pfse_m->m_pkthdr.csum_data = 0xffff;
- ip_input(pfse->pfse_m);
- } else {
- ip_output(pfse->pfse_m, NULL, NULL, 0, NULL,
- NULL);
- }
+ pf_send_ip_direct(pfse->pfse_m);
break;
}
case PFSE_ICMP:
@@ -2733,20 +2757,7 @@ pf_intr(void *v)
#endif /* INET */
#ifdef INET6
case PFSE_IP6:
- if (pf_isforlocal(pfse->pfse_m, AF_INET6)) {
- KASSERT(pfse->pfse_m->m_pkthdr.rcvif == V_loif,
- ("%s: rcvif != loif", __func__));
-
- pfse->pfse_m->m_flags |= M_SKIP_FIREWALL |
- M_LOOP;
- pfse->pfse_m->m_pkthdr.csum_flags |=
- CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
- pfse->pfse_m->m_pkthdr.csum_data = 0xffff;
- ip6_input(pfse->pfse_m);
- } else {
- ip6_output(pfse->pfse_m, NULL, NULL, 0, NULL,
- NULL, NULL);
- }
+ pf_send_ip6_direct(pfse->pfse_m);
break;
case PFSE_ICMP6:
icmp6_error(pfse->pfse_m, pfse->icmpopts.type,
diff --git a/sys/netpfil/pf/pf.h b/sys/netpfil/pf/pf.h
index 09bcd424db3e..6644499d9ce8 100644
--- a/sys/netpfil/pf/pf.h
+++ b/sys/netpfil/pf/pf.h
@@ -752,5 +752,12 @@ RB_PROTOTYPE(pf_anchor_global, pf_anchor, entry_global, pf_anchor_compare);
RB_PROTOTYPE(pf_anchor_node, pf_anchor, entry_node, pf_anchor_compare);
int pf_get_ruleset_number(u_int8_t);
-
+#ifdef _KERNEL
+#ifdef INET
+void pf_send_ip_direct(struct mbuf *m);
+#endif
+#ifdef INET6
+void pf_send_ip6_direct(struct mbuf *m);
+#endif
+#endif /* _KERNEL */
#endif /* _NET_PF_H_ */
diff --git a/sys/netpfil/pf/pf_syncookies.c b/sys/netpfil/pf/pf_syncookies.c
index d11551ffb6ae..1a689b9e1df5 100644
--- a/sys/netpfil/pf/pf_syncookies.c
+++ b/sys/netpfil/pf/pf_syncookies.c
@@ -60,6 +60,9 @@
//#include "pflog.h"
+#include "opt_inet.h"
+#include "opt_inet6.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
@@ -80,11 +83,16 @@
#include <net/route.h>
#include <netinet/in.h>
+#include <netinet/in_var.h>
#include <netinet/in_pcb.h>
#include <netinet/ip.h>
+#include <netinet/ip_var.h>
#include <netinet/tcp.h>
#include <netinet/tcp_var.h>
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+
#include <net/pfvar.h>
#include <netpfil/pf/pf_nv.h>
@@ -291,13 +299,31 @@ pf_syncookie_send(struct pf_pdesc *pd, u_short *reason)
{
uint16_t mss;
uint32_t iss;
+ struct mbuf *m;
mss = max(V_tcp_mssdflt, pf_get_mss(pd));
iss = pf_syncookie_generate(pd, mss);
- pf_send_tcp(NULL, pd->af, pd->dst, pd->src, *pd->dport, *pd->sport,
- iss, ntohl(pd->hdr.tcp.th_seq) + 1, TH_SYN|TH_ACK, 0, mss,
- 0, M_SKIP_FIREWALL | (pd->m->m_flags & M_LOOP), 0, 0,
+
+ m = pf_build_tcp(NULL, pd->af, pd->dst, pd->src, *pd->dport, *pd->sport,
+ iss, ntohl(pd->hdr.tcp.th_seq) + 1, TH_SYN | TH_ACK, 0, mss, 0,
+ M_SKIP_FIREWALL | (pd->m->m_flags & M_LOOP), 0, 0, 0,
pd->act.rtableid, reason);
+ if (m == NULL)
+ return;
+ switch (pd->af) {
+#ifdef INET
+ case AF_INET:
+ pf_send_ip_direct(m);
+ break;
+#endif /* INET */
+#ifdef INET6
+ case AF_INET6:
+ pf_send_ip6_direct(m);
+ break;
+#endif /* INET6 */
+ default:
+ unhandled_af(pd->af);
+ }
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],