I would like VyOS to support a higher-level, map-based way to set connection marks and route based on those marks. The goal is to reduce repetitive firewall and policy-route rules in multi-WAN / multi-VPN DNAT setups where return traffic must follow the ingress interface.
VyOS already exposes the building blocks: DNAT, firewall connection marks, policy route rules, and multiple routing tables. The missing piece is a compact way to express a repeated mapping such as:
DNAT entered via wg1 -> connection-mark 102 -> route table 102 DNAT entered via wg2 -> connection-mark 103 -> route table 103 DNAT entered via wg3 -> connection-mark 104 -> route table 104 DNAT entered via wg4 -> connection-mark 105 -> route table 105 DNAT entered via wg5 -> connection-mark 106 -> route table 106
At the nftables level this can be represented naturally as a map, but in VyOS CLI today this requires many near-identical firewall and policy-route rules.
Problem
In a multi-WAN or multi-VPN setup with destination NAT, return-path symmetry is difficult to keep maintainable.
For example, if a service is exposed through a specific WireGuard provider interface, the reply traffic from the LAN server must return through the same provider interface. Otherwise, the connection may fail or leak through an unintended route.
Currently, this often requires maintaining the same intent in several places:
1. DNAT rule: public interface/address/port -> internal host/port 2. Firewall allow rule or groups: allow the translated destination/port 3. Source-based PBR group: internal host -> selected route table 4. Policy route rule: source group -> route table 5. Static table: route table -> egress interface
This becomes fragile. If the user updates the DNAT but forgets to update the source-PBR group, the port forward may work only accidentally, or may fail during failover.
The problem is not that VyOS lacks the primitives. The problem is that repeated per-interface/per-table mappings create rule bloat and make the actual routing intent harder to audit.
Current workaround
A current workaround is to create separate firewall rules that set connection marks per ingress interface, then separate policy-route rules that map each connection mark to a routing table.
Example:
set firewall ipv4 forward filter rule 70 inbound-interface name 'wg1' set firewall ipv4 forward filter rule 70 connection-status nat 'destination' set firewall ipv4 forward filter rule 70 set connection-mark '102' set firewall ipv4 forward filter rule 71 inbound-interface name 'wg2' set firewall ipv4 forward filter rule 71 connection-status nat 'destination' set firewall ipv4 forward filter rule 71 set connection-mark '103' set firewall ipv4 forward filter rule 72 inbound-interface name 'wg3' set firewall ipv4 forward filter rule 72 connection-status nat 'destination' set firewall ipv4 forward filter rule 72 set connection-mark '104'
Then:
set policy route LAN-PBR rule 7010 connection-mark '102' set policy route LAN-PBR rule 7010 set table '102' set policy route LAN-PBR rule 7020 connection-mark '103' set policy route LAN-PBR rule 7020 set table '103' set policy route LAN-PBR rule 7030 connection-mark '104' set policy route LAN-PBR rule 7030 set table '104'
This works, but it is repetitive. With five or more VPN interfaces, the number of rules grows quickly even though the logic is just a simple mapping.
Raw nftables equivalent
At the nftables level, the same logic can be represented much more compactly using a map:
table inet pbr_marks {
map dnat_iif_to_return_mark {
type ifname : mark
elements = {
"wg1" : 0x0102,
"wg2" : 0x0103,
"wg3" : 0x0104,
"wg4" : 0x0105,
"wg5" : 0x0106
}
}
chain mark_dnat_forward {
type filter hook forward priority mangle; policy accept;
ct state new ct status dnat ct mark set iifname map @dnat_iif_to_return_mark
}
}This expresses the user intent directly:
For new DNATed forwarded connections, set the connection mark according to the ingress interface.
The VyOS CLI currently requires this to be expanded into several similar rules.
Proposed feature
Add a map-style abstraction for setting connection marks based on packet attributes.
Possible CLI shape:
set firewall ipv4 connection-mark-map DNAT-RETURN entry wg1 match inbound-interface 'wg1' set firewall ipv4 connection-mark-map DNAT-RETURN entry wg1 set connection-mark '102' set firewall ipv4 connection-mark-map DNAT-RETURN entry wg2 match inbound-interface 'wg2' set firewall ipv4 connection-mark-map DNAT-RETURN entry wg2 set connection-mark '103' set firewall ipv4 connection-mark-map DNAT-RETURN entry wg3 match inbound-interface 'wg3' set firewall ipv4 connection-mark-map DNAT-RETURN entry wg3 set connection-mark '104' set firewall ipv4 forward filter rule 70 description 'Mark DNAT connections by ingress interface' set firewall ipv4 forward filter rule 70 connection-status nat 'destination' set firewall ipv4 forward filter rule 70 state 'new' set firewall ipv4 forward filter rule 70 set connection-mark-map 'DNAT-RETURN' set firewall ipv4 forward filter rule 70 action 'continue'
This would allow one rule to apply a map rather than requiring one rule per ingress interface.
Optional related feature: mark-to-table map
A second useful abstraction would be a policy-route map from connection mark to routing table.
Possible CLI shape:
set policy route-map-table DNAT-RETURN entry 102 match connection-mark '102' set policy route-map-table DNAT-RETURN entry 102 set table '102' set policy route-map-table DNAT-RETURN entry 103 match connection-mark '103' set policy route-map-table DNAT-RETURN entry 103 set table '103' set policy route-map-table DNAT-RETURN entry 104 match connection-mark '104' set policy route-map-table DNAT-RETURN entry 104 set table '104' set policy route LAN-PBR rule 7000 description 'Route DNAT replies by connection mark' set policy route LAN-PBR rule 7000 destination group network-group '!RFC1918' set policy route LAN-PBR rule 7000 set table-map 'DNAT-RETURN'
This would reduce multiple repeated policy-route rules into a single rule that applies a mapping.
If dynamic table selection is not practical in the current policy-route backend, then the first part alone — map-based connection marking — would still be useful.
Alternative CLI idea
Another possible syntax could be closer to nftables maps:
set firewall ipv4 map DNAT-IIF-TO-CONNMARK type 'inbound-interface-to-connection-mark' set firewall ipv4 map DNAT-IIF-TO-CONNMARK entry wg1 key 'wg1' set firewall ipv4 map DNAT-IIF-TO-CONNMARK entry wg1 value '102' set firewall ipv4 map DNAT-IIF-TO-CONNMARK entry wg2 key 'wg2' set firewall ipv4 map DNAT-IIF-TO-CONNMARK entry wg2 value '103' set firewall ipv4 forward filter rule 70 connection-status nat 'destination' set firewall ipv4 forward filter rule 70 state 'new' set firewall ipv4 forward filter rule 70 set connection-mark from-map 'DNAT-IIF-TO-CONNMARK'
The exact syntax is not important. The important part is exposing a persistent, VyOS-managed map abstraction instead of forcing users to manually expand the map into many individual rules.
Why this would be useful
This would improve maintainability for:
- multi-WAN routers;
- commercial VPN provider routing;
- DNAT/port-forward setups with several ingress interfaces;
- users migrating from pfSense-style reply-to behaviour;
- source-independent return-path symmetry;
- large homelab or service-hosting configurations.
It would reduce repeated rule blocks such as:
wg1 -> mark 102 wg2 -> mark 103 wg3 -> mark 104 wg4 -> mark 105 wg5 -> mark 106
from many rules down to one rule plus one map.
Benefits
1. Less rule bloat
Instead of five or ten near-identical firewall rules, the user could maintain a single map.
2. Clearer intent
The configuration would show the actual relationship directly:
ingress interface -> connection mark -> route table
rather than spreading the intent across many numbered rules.
3. Fewer configuration mistakes
Today, adding a new DNAT often requires updating DNAT, firewall groups, source-PBR groups, and route policy. A map-based connection-mark design would reduce the chance of forgetting one of those pieces.
4. Better separation of policy
It allows users to separate two concepts:
DNAT return symmetry: handled by connection mark Host-wide outbound policy: handled separately by source-based PBR if needed
This is cleaner than using source-PBR as a proxy for DNAT return routing.
5. Better alignment with nftables capabilities
nftables already supports maps. VyOS could expose this capability in a managed and persistent way instead of users resorting to unsupported hand-written nftables rules.
Example use case
A router has several WireGuard provider interfaces:
wg1 -> table 102 wg2 -> table 103 wg3 -> table 104 wg4 -> table 105 wg5 -> table 106
Several internal services are exposed through DNAT on those provider interfaces.
The desired behaviour is:
If a DNATed connection enters wg2, replies should leave through wg2. If a DNATed connection enters wg4, replies should leave through wg4.
This should not require the user to add every internal service host to source-based PBR groups.
Instead, the ingress interface should be marked once on the connection, then routing should use that mark for return traffic.
Non-goals
This request is not asking for VyOS to guess routing behaviour automatically.
It is also not asking for unmanaged custom nftables snippets.
The request is for an explicit VyOS-managed abstraction that lets users define maps such as:
interface -> connection mark connection mark -> table source address -> mark
and apply those maps from normal firewall or policy-route rules.
Possible implementation direction
Internally, this could compile to nftables maps such as:
map dnat_iif_to_return_mark {
type ifname : mark
elements = {
"wg1" : 0x0102,
"wg2" : 0x0103,
"wg3" : 0x0104,
"wg4" : 0x0105,
"wg5" : 0x0106
}
}
ct state new ct status dnat ct mark set iifname map @dnat_iif_to_return_markA separate routing-policy layer could then use existing ip rule behaviour, or a new higher-level map abstraction if feasible.
Final justification
The current CLI can express this behaviour, but only by expanding simple mappings into many repeated rules. That works technically, but it scales poorly and makes configurations harder to audit.
A VyOS-managed map abstraction would let users express the actual intent directly:
For DNATed flows, remember the ingress interface as a connection mark. Then route replies according to that mark.
This would reduce rule count, reduce mistakes, and make complex multi-WAN / multi-VPN DNAT configurations easier to understand and maintain.