Page MenuHomeVyOS Platform

Support `fib daddr type` / `fib saddr type` matching in firewall rules (especially `prerouting raw`)
Closed, ResolvedPublicFEATURE REQUEST

Description

Feature Request: Support fib daddr type / fib saddr type matching in firewall rules (especially prerouting raw)

Summary

Expose nftables' fib daddr type (and ideally fib saddr type) expression as a match condition in VyOS firewall rules, particularly under `firewall <ipv4|ipv6> prerouting
raw`. This would let a rule match "destination is one of this router's own local addresses" (fib daddr type local) without requiring the operator to manually enumerate eve
ry locally-configured address in a network-group/ipv6-network-group.

Motivation / real-world use case

We run a DFZ edge router (VyOS 1.5.1, FRR 10.5.2) carrying full BGP tables (~1M IPv4 / ~250k IPv6 prefixes) from multiple transit/peering sessions, with no NAT and no statef
ul forward policy on these interfaces. We wanted to stop connection-tracking pure forwarded/transit traffic on these interfaces (via action notrack in `firewall <family> p
rerouting raw`) to keep the conntrack table small and avoid tuning system conntrack table-size upward just to accommodate transient full-table transit flows that carry no
functional benefit from being tracked.

The problem: notrack decisions are evaluated in the raw table at PREROUTING, before the kernel's routing/FIB lookup determines whether a packet will be locally delivered (
INPUT) or forwarded (FORWARD). A blanket notrack scoped only by inbound-interface therefore also untracks traffic destined *to the router itself* on that interface — BGP
, BFD, NTP, DNS client queries, RPKI cache sessions, etc. Several of these relied entirely on the ct state established/related shortcut in our *_LOCAL input-filter rul
esets to admit return traffic (no dedicated stateless allow rule existed for some of them). Once notrack was applied, that shortcut stopped firing (an untracked packet's `ct
state` is untracked, never established), and — combined with a firewall rule that only matched one direction of a bidirectional TCP service (BGP: destination port 179
only, no source port 179 counterpart for sessions where our router was the connecting/client side) — this caused a real, multi-minute production BGP outage when first att
empted, before being identified and rolled back.

What we actually want

Distinguish, at the raw/PREROUTING stage, between:

  • traffic destined to the router itself (keep tracking — control plane, small volume, benefits from the established/related shortcut), and
  • traffic being forwarded through the router (safe to notrack — pure transit, no NAT, no stateful policy).

Current workaround (and why it's not ideal)

We had to manually build a network-group/ipv6-network-group containing every address configured on the router (loopback + every interface address, including addresses th
at are duplicated across multiple "unnumbered" point-to-point links), then add two rules per notracked interface:

set firewall ipv4 prerouting raw rule 1 inbound-interface name 'eth0'
set firewall ipv4 prerouting raw rule 1 destination group network-group 'R1-OWN-ADDRESSES-V4'
set firewall ipv4 prerouting raw rule 1 action 'accept'

set firewall ipv4 prerouting raw rule 2 inbound-interface name 'eth0'
set firewall ipv4 prerouting raw rule 2 action 'notrack'

This works, but:

  • It requires manual enumeration of every local address and ongoing maintenance whenever an address is added, removed, or an interface is renumbered.
  • There is no commit-time check that the list is complete. A missed address silently falls through to the catch-all notrack rule — a newly added service/anycast address co

uld unexpectedly lose the established/related shortcut it might be relying on, with no warning.

  • It has to be repeated per router and kept in sync across a fleet (we run four routers with this pattern).

We verified via live tab-completion on VyOS 1.5.1 that no such match currently exists:

vyos@r2# set firewall ipv4 prerouting raw rule 1 destination ?
Possible completions:
   address              IP address, subnet, or range
   address-mask         IP mask
   fqdn                 Fully qualified domain name
 > geoip                GeoIP options - Data provided by DB-IP.com
 > group                Group
   mac-address          MAC address
   port                 Port

No fib/local-address-type option is exposed anywhere in this rule's match tree.

Requested syntax (example)

set firewall ipv4 prerouting raw rule 1 destination fib-type 'local'
set firewall ipv4 prerouting raw rule 1 action 'accept'
set firewall ipv4 prerouting raw rule 2 action 'notrack'

fib-type values would map directly to the nftables fib expression's type values (local, unicast, broadcast, anycast, etc. — local is the one relevant to this use case). This is not a request for new kernel/nftables functionality — fib daddr type local is a standard, already-supported nftables expression. The ask is purely to expose it through VyOS's declarative config schema (interface-definitions XML) so it can be used from the CLI/config like any other match.

Why this matters beyond our specific case

This is the general pattern for any router that wants to distinguish "traffic destined to me" from "traffic passing through me" at the raw/PREROUTING stage — relevant wherever notrack-based conntrack optimization is desirable on a box that forwards a large number of flows (DFZ/transit routers) but also runs a small number of local control-plane/management services. It is the same semantic goal that other platforms solve architecturally (e.g. Junos: a firewall filter applied to lo0.0 catches all locally-destined traffic regardless of the physical ingress interface, without needing to enumerate addresses) — VyOS/Linux has the underlying primitive (fib daddr type) but doesn't currently surface it.

Related, lower-priority idea

A more general "dynamically-derived group" mechanism — analogous to Junos's policy-options prefix-list <name> apply-path "<config path>", which populates a prefix-list directly from another part of the configuration (e.g. every configured BGP neighbor address) instead of requiring duplicate manual entry — would solve a related but distinct drift problem: keeping firewall groups like IPv4_EBGP_HOSTS in sync with the actual configured BGP peers. We're not aware of any VyOS equivalent to apply-path; firewall groups are currently static and must be maintained by hand in parallel with the routing config they're meant to reflect. This is a bigger ask (effectively a new declarative subsystem) and is mentioned here for context/completeness rather than as the primary request.

Environment

  • VyOS 1.5.1 (circinus), official generic ISO, FRR 10.5.2
  • Verified via live CLI tab-completion on production hardware (HP ProLiant DL360p Gen8)

Details

Version
-
Is it a breaking change?
Unspecified (possibly destroys the router)
Issue type
Feature (new functionality)

Event Timeline

rherold created this object in space S1 VyOS Public.
Viacheslav triaged this task as Normal priority.Aug 3 2026, 4:50 PM

@rherold

Your addition is still useful because it lets you match only on specific interfaces or traffic, but have you seen that you can already do this?:

set firewall ipv4 forward filter disable-conntrack

That generates this nftables config:

chain VYOS_DISABLE_CONNTRACK_INP_FWD {
        type filter hook prerouting priority -320; policy accept;
        fib daddr . iif type unicast notrack counter packets 3 bytes 704 comment "DISABLE-CT-FWD"
}

This allows you to not have conntrack run on forwarded traffic, while still allowing it for local traffic. Maybe that'll work for you while you wait for your PR to get merged.

@L0crian thx for this information, this could help with my current setup. I will try it in the lab. But I also see that my patched could be usefull

@L0crian the command :

set firewall ipv4 forward filter disable-conntrack

does not exist in 1.5.1

Correct, if you want to test it you'd have to test on rolling. If the feature works for you, you could then request the feature get backported to 1.5. The commit was on the 1.5 board, so I'm not sure why it wasn't backported before the GA was released.

Also currently missing in Stream 2026.03:

[edit]
vyos@vyos# set firewall ipv4 forward filter 
Possible completions:
   default-action       Default-action for rule-set (default: accept)
   default-log          Log packets hitting default-action
   description          Description
+> rule                 IPv4 Firewall forward filter rule number

      
[edit]
vyos@vyos# set firewall ipv6 forward filter 
Possible completions:
   default-action       Default-action for rule-set (default: accept)
   default-log          Log packets hitting default-action
   description          Description
+> rule                 IPv6 Firewall forward filter rule number

Is this the same (exists in Stream 2026.03)?

[edit]
vyos@vyos# set system conntrack ignore ipv4 rule 1 
Possible completions:
   description          Description
 > destination          Destination parameters
   inbound-interface    Interface to ignore connections tracking on
   protocol             Protocol
 > source               Source parameters
 > tcp                  TCP options to match

      
[edit]
vyos@vyos# set system conntrack ignore ipv6 rule 1 
Possible completions:
   description          Description
 > destination          Destination parameters
   inbound-interface    Interface to ignore connections tracking on
   protocol             Protocol
 > source               Source parameters
 > tcp                  TCP options to match
Thanks both for the input.

Re: disable-conntrack as an alternative — worth noting these are different things under the hood, and it isn't a fully reliable substitute for a plain fib-type match. VyOS's disable-conntrack feature (rolling) generates fib daddr . iif type unicast notrack — a combined address+ingress-interface FIB lookup. In testing on routers using an
IP-unnumbered-style addressing scheme (the same address appearing on multiple devices — loopback plus several point-to-point sub-interfaces used for routing-protocol neighbors), that combined daddr . iif lookup became ambiguous and classified traffic destined to the router's own address as unicast instead of local, even though ip route get
correctly reported local for the identical case. This caused a routing-protocol session drop until the rule was removed. The ambiguity appears specific to combining an address with iif; a plain fib daddr type / fib saddr type lookup (no iif) — which is exactly what this ticket's patch (PR #5372) implements — does not exhibit it.

So for the DFZ-edge use case in the ticket description, I'd still recommend the dedicated fib-type match over relying on disable-conntrack as a stand-in, at least on topologies with this kind of multi-device address reuse.

Re: Stream 2026.03 — confirmed, this landed on rolling only so far (PR #5372, still in review). Happy to see a backport requested once it's merged, if that's useful for LTS users.

@rherold

Can you provide your config you tested with. I just tested and it works as expected even with using ip unnumbered. This is with forwarding traffic going across the router:

Using ip unnumbered BGP over OSPF:
vyos@vyos# run show conntrack table ipv4
Original src    Original dst    Original packets    Original bytes    Reply src     Reply dst       Reply packets    Reply bytes    Protocol    State        Timeout    Mark    Zone
--------------  --------------  ------------------  ----------------  ------------  --------------  ---------------  -------------  ----------  -----------  ---------  ------  ------
10.0.0.1        224.0.0.5       0                   0                 224.0.0.5     10.0.0.1        0                0              unknown                  596        0
10.0.0.2        224.0.0.5       0                   0                 224.0.0.5     10.0.0.2        0                0              unknown                  598        0
10.0.0.1:43585  10.0.0.2:179    0                   0                 10.0.0.2:179  10.0.0.1:43585  0                0              tcp         ESTABLISHED  431985     0

And if I then delete the disable-conntrack, you can see forward traffic now enters conntrack:

vyos@vyos# delete firewall ipv4 forward filter disable-conntrack 
vyos@vyos# commit
vyos@vyos# run show conntrack table ipv4
Original src    Original dst    Original packets    Original bytes    Reply src     Reply dst       Reply packets    Reply bytes    Protocol    State        Timeout    Mark    Zone
--------------  --------------  ------------------  ----------------  ------------  --------------  ---------------  -------------  ----------  -----------  ---------  ------  ------
10.0.10.10      10.0.11.10      0                   0                 10.0.11.10    10.0.10.10      0                0              icmp                     27         0
10.0.0.1        224.0.0.5       0                   0                 224.0.0.5     10.0.0.1        0                0              unknown                  596        0
10.0.0.2        224.0.0.5       0                   0                 224.0.0.5     10.0.0.2        0                0              unknown                  597        0
10.0.0.1:43585  10.0.0.2:179    0                   0                 10.0.0.2:179  10.0.0.1:43585  0                0              tcp         ESTABLISHED  431979     0
Using ip unnumbered only BGP:
vyos@vyos# run show conntrack table ipv4
Entries not found
vyos@vyos# run show conntrack table ipv6
Original src               Original dst                 Original packets    Original bytes    Reply src                    Reply dst                  Reply packets    Reply bytes    Protocol    State        Timeout    Mark    Zone
-------------------------  ---------------------------  ------------------  ----------------  ---------------------------  -------------------------  ---------------  -------------  ----------  -----------  ---------  ------  ------
fe80::ecd:a4ff:fec2:0:179  fe80::eda:29ff:fe4e:0:39492  0                   0                 fe80::eda:29ff:fe4e:0:39492  fe80::ecd:a4ff:fec2:0:179  0                0              tcp         ESTABLISHED  431999     0
fe80::ecd:a4ff:fec2:0:179  fe80::eda:29ff:fe4e:0:39496  0                   0                 fe80::eda:29ff:fe4e:0:39496  fe80::ecd:a4ff:fec2:0:179  0                0              tcp         ESTABLISHED  431999     0

@L0crian: When you did that test did you try to reboot in between?

Because you say that the first printout have disable-conntrack but then why are the OSPF sessions being shown there?

@Apachez

OSPF and BGP is for input, so they should be there.

Yes, reboot and reset conntrack tested.

Thanks for testing this — appreciate the data. To reconcile with what we saw: our issue wasn't IP-unnumbered per se, but specifically that our own address appears on
multiple devices simultaneously in the local route table (ip route show table local showed our loopback duplicated across lo + 3 P2P bond interfaces used for OSPF, since
our full-mesh core reuses the loopback address on each P2P link). We proved with a logging-only rule (fib daddr . iif type unicast log) that real traffic to our own
address was classified unicast, never local, even though ip route get reported local for the same case.

Could you share how many P2P links in your unnumbered setup share the same address? If it's a single link (vs. our 3+), that might explain the different result — the
ambiguity may only manifest once an address exists on 3+ local devices, not with unnumbered addressing generally.

I tested with 2 interfaces when testing BGP over OSPF, but with 8 for BGP only ip unnumbered:

For BGP ip unnumbered, it worked fine (expected since they all use a unique link-local address), but you are correct that ECMP local traffic appears to to be classified as unicast when doing ip unnumbered using BGP over OSPF (and presumably other IGPs). An nftrace shows that the traffic is hitting the notrack rule, but then gets sent to the INPUT chain. So it initially identifies it as unicast traffic, but then says it is local traffic:

root@vyos:/home/vyos# python3 nftrace_story.py test.trace --id 5d446144
Trace 5d446144

Story:
  - 10.0.0.2 → 10.0.0.1 arrived on interface "eth0.106".
  - It was last observed near the INPUT hook (L27).
  - Final verdict: DROP (L27).
  - Tables visited:
    - vyos_filter.VYOS_DISABLE_CONNTRACK_INP_FWD
    - vyos_conntrack.PREROUTING
    - vyos_conntrack.VYOS_CT_IGNORE
    - vyos_conntrack.FW_CONNTRACK
    - vyos_filter.VYOS_PREROUTING_raw
    - vrf_zones.vrf_zones_ct_in
    - raw.VYOS_PREROUTING_HOOK
    - raw.vyos_rpfilter
    - raw.vyos_global_rpfilter
    - vyos_conntrack.PREROUTING_HELPER
    - vyos_conntrack.VYOS_CT_HELPER
    - vyos_filter.VYOS_INPUT_filter
    - vyos_filter.VYOS_STATE_POLICY
  - Rules hit:
    - vyos_filter.VYOS_DISABLE_CONNTRACK_INP_FWD L2: tcp dport 179 meta nftrace set 1 (verdict continue)
    - vyos_filter.VYOS_DISABLE_CONNTRACK_INP_FWD L3: fib daddr . iif type unicast notrack counter packets 1611 bytes 148820 comment "DISABLE-CT-FWD" (verdict continue)
    - vyos_conntrack.PREROUTING L6: counter packets 2947 bytes 255732 jump VYOS_CT_IGNORE (verdict jump VYOS_CT_IGNORE)
    - vyos_conntrack.VYOS_CT_IGNORE L7: return (verdict return)
    - vyos_conntrack.PREROUTING L8: counter packets 2947 bytes 255732 jump FW_CONNTRACK (verdict jump FW_CONNTRACK)
    - vyos_conntrack.FW_CONNTRACK L9: accept (verdict accept)
    - vyos_filter.VYOS_PREROUTING_raw L11: counter packets 2947 bytes 255732 accept comment "PRE-raw default-action accept" (verdict accept)
    - raw.vyos_rpfilter L17: counter packets 2947 bytes 255732 jump vyos_global_rpfilter (verdict jump vyos_global_rpfilter)
    - raw.vyos_global_rpfilter L18: return (verdict return)
    - vyos_conntrack.PREROUTING_HELPER L21: counter packets 2947 bytes 255732 jump VYOS_CT_HELPER (verdict jump VYOS_CT_HELPER)
    - vyos_conntrack.VYOS_CT_HELPER L22: return (verdict return)
    - vyos_filter.VYOS_INPUT_filter L25: jump VYOS_STATE_POLICY (verdict jump VYOS_STATE_POLICY)
    - vyos_filter.VYOS_STATE_POLICY L26: return (verdict return)
    - vyos_filter.VYOS_INPUT_filter L27: counter packets 379 bytes 22740 log prefix "[ipv4-INP-filter-default-D]" drop comment "INP-filter default-action drop" (verdict drop)

Flow: 10.0.0.2 → 10.0.0.1
Ingress: "eth0.106"

This does appear to be a bug that may need to be reported upstream. Even when doing an ip route get with asymmetric paths, iproute2 still says the route is local:

vyos@vyos# sudo ip route get 10.0.0.1 from 10.0.0.2 iif eth0.100 oif eth0.101
local 10.0.0.1 from 10.0.0.2 dev lo 
    cache <local> iif eth0.100

And nftables eventually says the traffic is local, evident by it entering the input hook instead of the forward hook.

Thanks for reproducing this independently — matches what we found on our own multi-device-unnumbered topology (loopback address duplicated across lo + 3 P2P bond
interfaces per router, full-mesh OSPF core). We proved the same underlying inconsistency with a logging-only rule (no notrack, just log+counter, run in parallel with live
traffic, zero production impact):

ip daddr <own-addr> fib daddr . iif type unicast log counter  -> packets > 0 (real traffic)
ip daddr <own-addr> fib daddr . iif type local  log counter  -> packets 0

— real, live BGP traffic to our own address was classified unicast, never local, by the nftables fib expression itself. Structural cause we found: ip route show table 
local shows our own address present on 4 separate devices simultaneously (lo + 3 P2P links reusing the loopback address, IP-unnumbered style) — that duplication across
devices in the local table is the most likely reason the combined daddr . iif lookup goes ambiguous.

Your nftrace finding (packet still correctly reaches INPUT despite the fib expression itself saying unicast) is a cleaner reproduction than what we had — shows the
kernel's own routing decision is right, only nftables' own fib expression evaluation disagrees with it internally. Sounds like a solid basis for an upstream report
(netfilter bugzilla, since this is nftables/kernel FIB evaluation, not VyOS-specific) — you're better positioned for that given the cleaner repro. Happy to provide our
structural angle/repro data as supporting detail if it's useful for the report.
Viacheslav assigned this task to rherold.
Viacheslav moved this task from Need Triage to Completed on the VyOS Rolling board.