Bug 284866 - pf: state-policy if-bound breaks ICMPv6 error delivery
Summary: pf: state-policy if-bound breaks ICMPv6 error delivery
Status: New
Alias: None
Product: Base System
Classification: Unclassified
Component: kern (show other bugs)
Version: 15.0-CURRENT
Hardware: Any Any
: --- Affects Only Me
Assignee: freebsd-pf (Nobody)
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2025-02-17 21:57 UTC by Lexi Winter
Modified: 2025-02-19 13:35 UTC (History)
1 user (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Lexi Winter freebsd_triage 2025-02-17 21:57:17 UTC
interface configuration:

--o<--
cxl3: flags=1008843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST,LOWER_UP> metric 0 mtu 1500
        options=6ec07bb<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,JUMBO_MTU,VLAN_HWCSUM,TSO4,TSO6,LRO,VLAN_HWTSO,LINKSTATE,RXCSUM_IPV6,TXCSUM_IPV6,HWSTATS,HWRXTSTMP,MEXTPG>
        ether 00:07:43:3f:e7:78
        inet 81.2.96.162/28 broadcast 81.2.96.175
        inet6 fe80::207:43ff:fe3f:e778%cxl3/64 scopeid 0x4
        inet6 2001:8b0:aab5:c401::1:5/64
        inet6 fd5b:a83:b06b:c401::1:5/64
        media: Ethernet 10Gbase-Twinax <full-duplex,rxpause,txpause>
        status: active
        nd6 options=21<PERFORMNUD,AUTO_LINKLOCAL>
--o<--

pf.conf:

--o<--
set skip on lo
set reassemble yes
set state-policy floating

block return in log
block return in quick proto tcp flags /S
pass out

# + a few 'pass in on cxl3 ...' rules for services
--o<--

with state-policy floating, traceroute works:

# traceroute6 -I ns1.burble.dn42
traceroute6 to ns1.burble.dn42 (fd42:4242:2601:ac53::1) from fd5b:a83:b06b:c401::1:5, 64 hops max, 20 byte packets
 1  vlan401.core-1.inet.eden.le-fay.dn42 (fd5b:a83:b06b:c401::1)  0.219 ms  0.144 ms  0.136 ms
 2  ix0-3004.willow.eden.le-fay.org (2001:8b0:aab5:3004::2)  0.139 ms  0.081 ms  0.073 ms
 3  yarrow.eden.le-fay.dn42 (fd5b:a83:b06b:10::1)  6.846 ms  7.246 ms  6.996 ms
 4  uk-lon1.burble.dn42 (fd42:4242:2601:35::1)  8.576 ms  8.873 ms  8.756 ms
 5  ns1.burble.dn42 (fd42:4242:2601:ac53::1)  8.445 ms  8.829 ms  8.325 ms

with state-policy if-bound, traceroute doesn't work:

# traceroute6 -I ns1.burble.dn42
traceroute6 to ns1.burble.dn42 (fd42:4242:2601:ac53::1) from fd5b:a83:b06b:c401::1:5, 64 hops max, 20 byte packets
 1  * * *
 2  * * *

because the ICMP errors are blocked by pf:

21:53:39.119122 rule 0/0(match): block in on cxl3: fd5b:a83:b06b:c401::1 > fd5b:a83:b06b:c401::1:5: ICMP6, time exceeded in-transit for fd42:4242:2601:ac53::1, length 68
21:53:44.174904 rule 0/0(match): block in on cxl3: fd5b:a83:b06b:c401::1 > fd5b:a83:b06b:c401::1:5: ICMP6, time exceeded in-transit for fd42:4242:2601:ac53::1, length 68
21:53:49.724459 rule 0/0(match): block in on cxl3: fd5b:a83:b06b:c401::1 > fd5b:a83:b06b:c401::1:5: ICMP6, time exceeded in-transit for fd42:4242:2601:ac53::1, length 68
21:53:54.846427 rule 0/0(match): block in on cxl3: 2001:8b0:aab5:3004::2 > fd5b:a83:b06b:c401::1:5: ICMP6, time exceeded in-transit for fd42:4242:2601:ac53::1, length 68
21:54:00.869729 rule 0/0(match): block in on cxl3: 2001:8b0:aab5:3004::2 > fd5b:a83:b06b:c401::1:5: ICMP6, time exceeded in-transit for fd42:4242:2601:ac53::1, length 68
21:54:06.568530 rule 0/0(match): block in on cxl3: 2001:8b0:aab5:3004::2 > fd5b:a83:b06b:c401::1:5: ICMP6, time exceeded in-transit for fd42:4242:2601:ac53::1, length 68

using src f5aff1871d3273b3cd3621ea5d3e37cdd807e66f on amd64, pf is statically compiler with PF_DEFAULT_TO_DROP.
Comment 1 Kristof Provost freebsd_committer freebsd_triage 2025-02-18 12:59:49 UTC
Try this:

```
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index 8bd6c72ce05f..f9715cd18166 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -7793,6 +7793,7 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd,
                pd2.sidx = (pd->dir == PF_IN) ? 1 : 0;
                pd2.didx = (pd->dir == PF_IN) ? 0 : 1;
                pd2.m = pd->m;
+               pd2.kif = pd->kif;
                switch (pd->af) {
 #ifdef INET
                case AF_INET:
```

If that doesn't work re-try without PF_DEFAULT_TO_DROP, because that's know to be buggy in at least one other situation.
Also gather state information and network captures.
Comment 2 Lexi Winter freebsd_triage 2025-02-18 22:53:42 UTC
the suggested patch seems to fix the problem, thanks.
Comment 3 commit-hook freebsd_committer freebsd_triage 2025-02-19 13:35:08 UTC
A commit in branch main references this bug:

URL: https://cgit.FreeBSD.org/src/commit/?id=86f2641b99f01eb8e8191c4435f22c17433b0c2f

commit 86f2641b99f01eb8e8191c4435f22c17433b0c2f
Author:     Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2025-02-19 12:28:33 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2025-02-19 13:34:45 +0000

    pf: fix icmp-in-icmp handling with if-bound states

    When we receive an ICMP packet containing another ICMP packet we look up the
    original ICMP state. This is done through a second struct pf_pdesc ('pd2'),
    containing relevant information (i.e. addresses, type, id, ..).
    pd2 did not contain the network interface ('kif'), leading to state lookup
    failures. This only affected if-bound mode, because floating states match all
    interfaces.

    Set kif in pd2.

    Extend the icmp.py:test_fragmentation_needed test case to use if-bound mode. It
    already checked that we handled icmp-in-icmp correctly.

    PR:             284866
    MFC after:      2 weeks
    Sponsored by:   Rubicon Communications, LLC ("Netgate")

 sys/netpfil/pf/pf.c          | 1 +
 tests/sys/netpfil/pf/icmp.py | 1 +
 2 files changed, 2 insertions(+)