git: 9c142388e4de - stable/15 - pf: Send syncookies from the receiving thread
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Mon, 12 Oct 2026 10:42:45 UTC
The branch stable/15 has been updated by bnovkov:
URL: https://cgit.FreeBSD.org/src/commit/?id=9c142388e4dec145109dbf02e7728967c2cbf3b9
commit 9c142388e4dec145109dbf02e7728967c2cbf3b9
Author: Bojan Novković <bnovkov@FreeBSD.org>
AuthorDate: 2026-08-17 14:10:36 +0000
Commit: Bojan Novković <bnovkov@FreeBSD.org>
CommitDate: 2026-10-12 09:37:05 +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
(cherry picked from commit bf05236727cf367ee8e22ef47febebb319db9ddb)
---
sys/netpfil/pf/pf.c | 67 ++++++++++++++++++++++++------------------
sys/netpfil/pf/pf.h | 9 +++++-
sys/netpfil/pf/pf_syncookies.c | 31 +++++++++++++++++--
3 files changed, 75 insertions(+), 32 deletions(-)
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index 6092d0b5566e..1e65bb4dfd96 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -2444,6 +2444,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)
{
@@ -2464,20 +2501,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:
@@ -2487,20 +2511,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 f8646b782fce..edb3bf265afd 100644
--- a/sys/netpfil/pf/pf.h
+++ b/sys/netpfil/pf/pf.h
@@ -737,5 +737,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 4a935bc65767..debf3fc29a8a 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,30 @@ pf_syncookie_send(struct pf_pdesc *pd)
{
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);
+ 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],