The branch main has been updated by bnovkov: URL: https://cgit.FreeBSD.org/src/commit/?id=bf05236727cf367ee8e22ef47febebb319db9ddb
commit bf05236727cf367ee8e22ef47febebb319db9ddb Author: Bojan Novković <[email protected]> AuthorDate: 2026-08-17 14:10:36 +0000 Commit: Bojan Novković <[email protected]> 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],
