Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 01 Jan 2022 19:30:57 +0000
From:      bugzilla-noreply@freebsd.org
To:        bugs@FreeBSD.org
Subject:   [Bug 260867] [pf][patch] divert-to packets infinitely loop when written back to divert socket
Message-ID:  <bug-260867-227@https.bugs.freebsd.org/bugzilla/>

next in thread | raw e-mail | index | archive | help
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=3D260867

            Bug ID: 260867
           Summary: [pf][patch] divert-to packets infinitely loop when
                    written back to divert socket
           Product: Base System
           Version: CURRENT
          Hardware: Any
                OS: Any
            Status: New
          Severity: Affects Some People
          Priority: ---
         Component: kern
          Assignee: bugs@FreeBSD.org
          Reporter: damjan.jov@gmail.com
 Attachment #230608 text/plain
         mime type:

Created attachment 230608
  --> https://bugs.freebsd.org/bugzilla/attachment.cgi?id=3D230608&action=
=3Dedit
Divert socket test code

On https://forums.freebsd.org/threads/pf-divert-to-loop-problem.81508 the
poster describes how the "divert-to" rule creates packet loops on FreeBSD 1=
2.2,
and I also independently reproduced this bug on 13.0 and 14-CURRENT too.

It can be reproduced with this pf rule:

pass out on em0 divert-to 0.0.0.0 port 2000

while running the attached C code, which binds a divert socket to 0.0.0.0:2=
000
and reads packets and writes them back unchanged.

Adding some logging to the pf kernel module, I noticed that the
PF_PACKET_LOOPED flag never gets set in the pf_test() function. Checking for
conditions that set it which aren't being met, I think I found out why.

The following one line change fixes the issue for me:

---snip---
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index 1686def4626..bd71d338517 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -6496,7 +6496,7 @@ pf_test(int dir, int pflags, struct ifnet *ifp, struct
mbuf **m0, struct inpcb *
        if (__predict_false(ip_divert_ptr !=3D NULL) &&
            ((ipfwtag =3D m_tag_locate(m, MTAG_IPFW_RULE, 0, NULL)) !=3D NU=
LL)) {
                struct ipfw_rule_ref *rr =3D (struct ipfw_rule_ref *)(ipfwt=
ag+1);
-               if (rr->info & IPFW_IS_DIVERT && rr->rulenum =3D=3D 0) {
+               if (rr->info & IPFW_IS_DIVERT /*&& rr->rulenum =3D=3D 0*/) {
                        if (pd.pf_mtag =3D=3D NULL &&
                            ((pd.pf_mtag =3D pf_get_mtag(m)) =3D=3D NULL)) {
                                action =3D PF_DROP;
---snip---


Why does that work?

It appears that the "rulenum" field is only written to in this one place:
                        ((struct ipfw_rule_ref *)(ipfwtag+1))->rulenum =3D =
dir;

and if "dir" is what I think it is, then as per /usr/include/netpfil/pf/pf.=
h:

enum    { PF_INOUT, PF_IN, PF_OUT };

the 0 in "rr->rulenum =3D=3D 0" would be PF_INOUT, which packets never are.=
 However
checking for values 1 and 2 instead, didn't seem to fix the issue either. O=
nly
deleting the entire rr->rulenum check seems to fix it.

--=20
You are receiving this mail because:
You are the assignee for the bug.=



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?bug-260867-227>