From 4728648a0c10aed97c2ed1e84b46cc5b5b6af24c Mon Sep 17 00:00:00 2001 From: lgcosta Date: Wed, 11 Nov 2026 19:19:53 -0300 Subject: [PATCH] divert: consume PF_TAG_DIVERTED after passthrough (BSD ip_input role); divert layer owns its own skb ref; gate uses pfput path --- module/pfsrc/divert_sock.c | 12 +++++++++--- module/pfsrc/pfk_hooks.c | 12 ++++++++++++ tests/vm/11-divert.sh | 6 +++--- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/module/pfsrc/divert_sock.c b/module/pfsrc/divert_sock.c index 86495e9..7684142 100644 --- a/module/pfsrc/divert_sock.c +++ b/module/pfsrc/divert_sock.c @@ -166,13 +166,19 @@ divert_packet(struct mbuf *m, int dir, u_int16_t divert_port) { struct pf_divert *dv = pf_find_divert(m); - if (dv) + if (dv) { type = dv->type; + if (divert_port == 0) + divert_port = dv->port; + } } skb = m_to_skb(m); - m->skb = NULL; /* ownership moves into the divert layer */ + if (skb) + skb_get(skb); /* divert layer's own reference */ + m->skb = NULL; m_freem(m); - pfk_divert_queue(skb, dir, type, divert_port); + if (skb) + pfk_divert_queue(skb, dir, type, divert_port); } void diff --git a/module/pfsrc/pfk_hooks.c b/module/pfsrc/pfk_hooks.c index 676b9f4..5d6ea37 100644 --- a/module/pfsrc/pfk_hooks.c +++ b/module/pfsrc/pfk_hooks.c @@ -22,6 +22,7 @@ #include extern int pf_test(sa_family_t, int, struct pfk_ifnet *, struct mbuf **); +extern void divert_packet(struct mbuf *, int, u_int16_t); extern struct workqueue_struct *pfk_netisr_wq; /* PFLINUX: netisr work item — carries ownership of one sk_buff */ @@ -91,6 +92,17 @@ pfk_pkt_work(struct work_struct *w) * and packet walk run concurrently from here on — documented. */ action = pf_test(pk->af, pk->dir, pk->ifp, &m); + /* PF_IN rules with `divert-to` only TAG the packet (BSD + * netinet/ip_input.c delivers tagged packets to the divert socket); + * do that here. divert_packet() consumes the mbuf wrapper and holds + * its own skb reference, so our wrapper pointer becomes invalid. */ + if (action == PF_PASS && m != NULL && + (m->m_pkthdr.pf.flags & PF_TAG_DIVERTED)) { + m->m_pkthdr.pf.flags &= ~PF_TAG_DIVERTED; + divert_packet(m, pk->dir, 0); + m = NULL; + } + if (m == m0) { /* same buffer, possibly rewritten in place: our wrapper * held one extra skb reference — release it */ diff --git a/tests/vm/11-divert.sh b/tests/vm/11-divert.sh index 2bb3b2b..0be38bc 100755 --- a/tests/vm/11-divert.sh +++ b/tests/vm/11-divert.sh @@ -16,8 +16,8 @@ FAILED=0 pf0 "insmod /tmp/pf.ko 2>/dev/null || insmod /opt/pf/pf.ko; true" pf0 '/opt/pf/pfctl -e; /opt/pf/pfctl -f /dev/null' >/dev/null -pfput ../../tools/out/pdivert /opt/pf/pdivert || bad "pdivert deploy" -pf0 'chmod +x /opt/pf/pdivert' >/dev/null +PDIV=$(pfput ../../tools/out/pdivert) || bad "pdivert deploy" +pf0 "chmod +x $PDIV" >/dev/null cat > /tmp/g11.conf < /tmp/td.txt 2>&1 &' 2>/dev/null sleep 1 -pf0 "setsid /opt/pf/pdivert -p $PORT -q > /tmp/pdiv.log 2>&1 & echo STARTED" | grep -q STARTED || { bad "pdivert start"; finish; } +pf0 "setsid ${PDIV:-/tmp/pdivert} -p $PORT -q > /tmp/pdiv.log 2>&1 & echo STARTED" | grep -q STARTED || { bad "pdivert start"; finish; } sleep 1 cli "ping -c 2 -W 2 $PFLAN >/dev/null 2>&1" && ok "ping passes through divert+rewrite" || bad "ping with listener" sleep 2