OPNsenseII · Routing FundamentalsRouting fundamentals
Policy routing and source-based routing
What you'll learn
- Explain the difference between destination-based and source-based routing
- Configure a gateway group and bind firewall rules to a gateway
- Recognise the production traps of policy routing: state, return path, MTU
- Choose between gateway groups, static routes, and policy routing for a given requirement
Prerequisites
Verified against OPNsense 25.x · FreeBSD 14.x · PF (FreeBSD packet filter) FreeBSD 14.x · Unbound 1.20+ · Kea DHCP OPNsense 25.x plugin · WireGuard in-kernel + OPNsense plugin · strongSwan (IPsec plugin) OPNsense 25.x plugin · OpenVPN 2.6.x · Suricata 7.x · 2026-08-14
Most routing decisions are destination-based: the kernel looks at the destination IP, finds the longest matching prefix, and forwards the packet. That is the default and it is correct for almost every flow on almost every network.
Sometimes, however, the operator needs the routing decision to depend on the source of the packet too. Two LANs on the same firewall, one destined for ISP-A and one for ISP-B. A VIP network whose traffic must always leave on a specific WAN. A VoIP subnet that requires a particular latency profile. These are policy-routing requirements, and OPNsense supports them via gateway groups, per-rule gateway selection, and the lower-level FreeBSD FIB abstractions underneath.
Destination routing, briefly
The default FreeBSD kernel maintains a single routing table per forwarding domain. The destination IP is the key. For every packet, the kernel finds the longest matching prefix and uses the corresponding gateway and interface. Source is irrelevant.
This is what RFC 1812 calls “destination-based forwarding” and it has been the IP default since the protocol was designed. Source routing (RFC 791) was specified but is not widely used in production for security reasons: allowing the sender to dictate the path is a powerful tool for attackers, and most operators filter source-routed packets at the edge.
Source-based routing, in practice
Source-based routing (sometimes called policy routing) routes a
packet based on a combination of source and destination, not
destination alone. On FreeBSD, the kernel supports multiple
routing tables via the setfib mechanism, and OPNsense exposes
a higher-level abstraction on top.
The OPNsense abstraction is gateway groups:
- A gateway is a next-hop IP the firewall can reach — typically the WAN router on a specific ISP link.
- A gateway group is a named collection of gateways with a priority order. Tier 1 is preferred; tier 2 is the failover.
- A firewall rule can be bound to a gateway group: when the rule matches a packet, the firewall overrides the default route lookup and uses the gateway group’s chosen gateway.
This is policy routing as OPNsense implements it: the firewall rule selects the gateway, and the gateway determines the egress interface and next hop.
$ pfctl -s rules -R <rule-number> 2>/dev/null | head -20@87 pass in quick on igb0 inet proto tcp from 192.0.2.0/24 to any port = https rdr-to <voip-nat-ip> round-robin
@92 pass in on igb0 inet proto tcp from 192.0.2.0/24 to any port = https route-to (igb1 198.51.100.1) round-robinIllustrative output
Configuring a gateway group
The OPNsense path is System → Gateways → Groups. The operator
creates a group, names it, picks the tier order:
- Tier 1: primary WAN gateway (e.g. WAN_DHCP)
- Tier 2: secondary WAN gateway (e.g. WAN2_STATIC)
Then in Firewall → Rules → LAN, the operator edits a rule and
selects the gateway group under “Advanced → Gateway”. When that
rule matches traffic, the firewall overrides the FIB and uses
the gateway group’s primary tier; if that gateway is down, the
firewall uses the failover tier.
Failover behaviour is implemented by the apinger service that
ships with OPNsense. apinger sends periodic probes to each
gateway; when the loss exceeds the configured threshold, the
gateway is marked down and traffic moves to the next tier. The
lesson on multi-WAN covers apinger in detail.
When to use what
Three routing mechanisms exist on OPNsense. The right one depends on what the operator is trying to express:
| Requirement | Tool | Why |
|---|---|---|
| “Send all Internet traffic out WAN-A, with WAN-B as failover” | Gateway group on the LAN rule | One rule, simple failover |
| “Send 192.0.2.0/24 to ISP-A’s gateway specifically” | Static route for that subnet | More specific prefix wins by LPM; no firewall rule needed |
| “Send the VoIP subnet out WAN-A regardless of failover state” | Gateway group with a single tier on a VoIP rule | Forces the path |
| “Send return traffic for ISP-A-bound flows back via ISP-A” | Outbound NAT pinning source to WAN-A IP | Keeps the return on the right WAN |
The mistake is over-using policy routing where a static route
would do. Static routes are evaluated by LPM and are visible in
netstat -rn. Policy routing hides the decision in firewall
rules and requires reading pfctl -s rules to verify what is
happening. Use the simplest tool that expresses the
requirement.
Production traps
Three traps recur:
- State split across gateways. A rule on WAN-A binds the gateway, but a matching rule on WAN-B does not. Returns come in on WAN-B and PF drops them. Always bind both directions of a flow to the same gateway group, or rely on a single rule.
- apinger downtime is not gateway downtime. A gateway can be up (responds to ping) but unable to forward traffic (routing loop upstream, congested link). apinger’s default loss threshold may not detect this. Configure apinger to send probes that exercise the path, not just the gateway IP.
- MTU changes per WAN. Different ISPs have different path MTUs. If a policy-routed flow goes through a tunnel or VPN with a smaller MTU, the connection may silently fragment or fail. Always test the path MTU end-to-end when changing a flow’s egress interface.
Summary
- Destination-based routing is the default. Policy routing adds source as a factor.
- Gateway groups are OPNsense’s policy-routing abstraction. Firewall rules can be bound to a gateway group, and the group’s tier order determines failover.
- The rule’s
route-toclause is what PF records in state. Returns must arrive on that interface. - Choose the simplest tool: gateway group for “send this traffic out this WAN”, static route for “send this subnet to this gateway”, NAT for “rewrite the source so the return path is forced”.
Knowledge check · 4 questions
Q1. You have two WAN interfaces and a LAN subnet 192.0.2.0/24 whose traffic must always leave on WAN-A, even if WAN-A is degraded. Which OPNsense mechanism expresses this most directly?
Q2. When a firewall rule with a route-to clause creates PF state, the state is bound to the route-to interface. Return traffic must arrive on that interface for PF to match the state.
Q3. Which of the following are valid OPNsense mechanisms for source-based or policy routing? Select all that apply.
Q4. A policy-routed flow has matching firewall rules on both WAN-A and WAN-B but only the WAN-A rule has a route-to clause. The WAN-B rule is a simple permit. The flow appears to work for some hosts but resets for others. What is the most likely cause?
Passing score: 75%. Answers are checked in this browser.