Skip to main content
RunBook Academy

OPNsenseXII · NAT Fundamentals and Outbound NATOutbound NAT

Outbound NAT with multiple WANs — per-interface source selection

Intermediate⏱ ~14 minpfctltcpdumproute

What you'll learn

  • Pin a source subnet to a specific WAN IP
  • Read the generated nat-to clauses in PF output
  • Recognise return-path implications of source pinning
  • Configure hybrid NAT for multi-WAN estates

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

Not yet marked complete on this device.

Multi-WAN deployments have a return-path problem that single-WAN deployments do not. A packet from a LAN client to an Internet host leaves the firewall on some WAN; the remote host replies; the reply must arrive on the same WAN — otherwise the firewall’s state table will not match the reply, and the reply will be dropped. The reply is destined to the source IP the packet appeared to come from. If the source IP is the WAN-A IP, the reply must come back via WAN-A. If WAN-A is down and the reply arrives via WAN-B, the reply is to WAN-B’s IP — which the remote host never sent to.

Outbound NAT is what creates that binding. The translation pins the internal source to a specific WAN IP. The remote host replies to that WAN IP. The reply arrives on the WAN that owns that IP. The state entry binds the flow to that WAN. Return traffic works.

This lesson covers the NAT side of multi-WAN: the rules that pin specific subnets to specific WANs, the return-path implications, and the production patterns that work.

The relationship between NAT and routing

A multi-WAN estate uses both NAT and routing to deliver outbound traffic to the right WAN:

  • Routing determines which WAN the packet physically leaves on. The kernel routing table decides based on the destination IP and the longest prefix match (or, with policy routing, the source plus the destination).
  • NAT determines which WAN IP the source is translated to on the way out. The nat rule rewrites the source.

These two decisions are independent. The kernel can route the packet via WAN-B while the nat rule translates the source to the WAN-A IP. The result: a packet leaves on WAN-B but claims to be from WAN-A’s IP. The remote host replies to WAN-A’s IP; the reply arrives on WAN-A; the firewall’s state table matches the reply on WAN-A (because that is the WAN the source was translated to).

This is the failure mode of “auto outbound NAT + multi-WAN”.

The fix: make the routing decision and the NAT decision agree. Either route the packet via the WAN the source IP belongs to, or translate the source to the WAN IP of the egress interface.

The pattern: pin source subnet to WAN

The production pattern for multi-WAN outbound NAT:

  • For each source subnet that has a specific WAN requirement, create a manual nat rule that translates the source to that WAN’s IP and uses that WAN as the egress interface.
  • For each WAN, ensure the routing decision sends the flow out the matching WAN. This can be a gateway group bound to the LAN rule, a static route, or a default route via the matching gateway.
  • For source subnets with no specific requirement, use the default WAN (typically the WAN chosen by the gateway group’s tier 1).

The implementation:

# LAN-1 (192.0.2.0/24) -> WAN-A (198.51.100.1)
nat on igb1 inet from 192.0.2.0/24 to any -> (igb1:0)

# LAN-2 (10.99.0.0/24) -> WAN-B (198.51.100.5)
nat on igb2 inet from 10.99.0.0/24 to any -> (igb2:0)

# LAN-3 (10.10.0.0/24) -> WAN-A by default
nat on igb1 inet from 10.10.0.0/24 to any -> (igb1:0)

The matching routing decision is in the LAN firewall rules or static routes:

  • A firewall rule on 192.0.2.0/24 bound to gateway group WAN_A_primary.
  • A firewall rule on 10.99.0.0/24 bound to gateway group WAN_B_primary.
  • A firewall rule on 10.10.0.0/24 bound to gateway group WAN_A_primary (default).

The gateway group tells the kernel to route via the WAN the gateway points to; the nat rule tells PF to translate to the IP on that WAN. The two agree.

Read-only / Safemulti-WAN NAT ruleset
$ pfctl -s nat
nat on igb1 inet from 192.0.2.0/24 to any -> (igb1:0)
nat on igb2 inet from 10.99.0.0/24 to any -> (igb2:0)
nat on igb1 inet from 10.10.0.0/24 to any -> (igb1:0)

Illustrative output

The return-path trap

The return path is the most common source of “multi-WAN NAT breaks some flows” tickets. The flow:

  1. Client 192.0.2.50 sends a SYN to 203.0.113.50.
  2. Firewall routes via WAN-A (gateway group binding).
  3. NAT rule translates source to 198.51.100.1 (WAN-A’s IP).
  4. Packet leaves on WAN-A with source 198.51.100.1.
  5. Remote host replies to 198.51.100.1 with source 203.0.113.50.
  6. Reply arrives on WAN-A (because 198.51.100.1 is WAN-A’s IP).
  7. Firewall matches state, reverse-translates, delivers to client.

Now the failure mode:

  1. Client 192.0.2.50 sends a SYN to 203.0.113.50.
  2. Firewall routes via WAN-B (because the gateway group failed over from WAN-A to WAN-B).
  3. NAT rule still translates source to 198.51.100.1 (WAN-A’s IP) — because the rule is “from 192.0.2.0/24 to any, WAN address” using WAN-A’s IP.
  4. Packet leaves on WAN-B with source 198.51.100.1 — but 198.51.100.1 is not an IP on WAN-B.
  5. Remote host replies to 198.51.100.1 — but the reply goes back to WAN-A (because that is the network that owns 198.51.100.1).
  6. Reply arrives on WAN-A. The state entry says the flow left on WAN-B. PF drops the reply because of the route-to asymmetry.

The fix is one of:

  • Bind the nat rule’s translation target to the egress interface. Use (\<egress\>:0) or specify the IP dynamically. PF picks the egress interface at runtime based on the route-to clause, so the translation follows the route.
  • Disable the gateway group failover for that source. If WAN-A is down, do not failover the flow to WAN-B; let it fail instead. This is acceptable when the source has a hard requirement on WAN-A.
  • Use a single gateway and rely on failover at the WAN layer. One logical WAN with two physical interfaces; failover at the link layer keeps the IP stable.

Reading the generated ruleset for multi-WAN

The verification after configuring multi-WAN NAT:

  1. pfctl -s nat — every rule has the correct interface and target. WAN-A’s rules all reference igb1 and (igb1:0); WAN-B’s rules reference igb2 and (igb2:0).
  2. pfctl -s rules | grep route-to — every relevant firewall rule has the right route-to (igbN ...) clause.
  3. netstat -rn — the routing table has the expected default routes via the right gateways.
  4. tcpdump -nei igb1 host \<remote\> — capture shows the post-NAT source as 198.51.100.1 for flows that should leave on WAN-A.
  5. tcpdump -nei igb2 host \<remote\> — capture shows the post-NAT source as 198.51.100.5 for flows that should leave on WAN-B.

If a flow’s capture on the WAN shows the wrong source IP, the nat rule and the route-to are not aligned. Re-check both.

Production patterns

Three patterns cover the common multi-WAN cases.

Two WANs, subnet pinning, no failover

Each subnet is bound to one WAN. If that WAN is down, the subnet loses Internet. Used when each WAN has a hard contract (VoIP provider, payment processor) and the requirement is “this subnet uses this provider”.

Configuration: gateway groups with a single tier (no failover), and manual NAT rules pinning each subnet to its WAN.

Two WANs, primary/backup with failover

Most traffic uses WAN-A; WAN-B is the backup. If WAN-A goes down, all traffic moves to WAN-B.

Configuration: gateway group WAN_A_primary with tier 1 = WAN-A and tier 2 = WAN-B. The firewall rule on the LAN is bound to this group. The NAT rule translates to WAN-A’s IP. When WAN-A fails, the route-to clause moves to WAN-B, but the NAT rule still references WAN-A’s IP — the failure mode described above.

The fix for this pattern: do not use a static NAT target. Use the egress interface as the NAT target, so the translation follows the route. Or accept the broken return path during failover and document it.

Two WANs, load distribution

Half the traffic uses WAN-A; the other half uses WAN-B. This is the hardest pattern and rarely done correctly.

Configuration: split the LAN into two subnets, each pinned to one WAN via gateway group + NAT rule. The application layer controls which subnet it talks from.

Summary

  • In multi-WAN estates, the routing decision (which WAN the flow leaves on) and the NAT decision (which IP the source is rewritten to) must agree, or the return path breaks.
  • Auto outbound NAT in a multi-WAN estate produces the wrong binding. Use Manual outbound NAT and pin each subnet to its WAN.
  • The return-path trap: when a flow leaves WAN-B but the source is WAN-A’s IP, the reply goes to WAN-A and is dropped.
  • Verify with pfctl -s nat and tcpdump on each WAN. The post-NAT source must match the WAN the flow leaves on.
  • Gateway-group failover combined with static NAT target silently breaks return paths. Choose the failover pattern deliberately.

Knowledge check · 4 questions

  1. Q1. A multi-WAN estate has WAN-A (198.51.100.1) and WAN-B (198.51.100.5). A subnet 192.0.2.0/24 must always egress WAN-A. The gateway group for the LAN rule has WAN-A as tier 1 and WAN-B as tier 2 (failover). The outbound NAT rule for 192.0.2.0/24 translates to 198.51.100.1 (WAN-A static). What happens when WAN-A goes down?

  2. Q2. In PF, when a rule has both a route-to clause and a nat-to clause that reference different interfaces, PF follows the route-to interface for egress but uses the nat-to interface for translation. The two are independent.

  3. Q3. Which of the following are valid fixes for the multi-WAN failover NAT trap? Select all that apply.

  4. Q4. You capture on WAN-A and see a flow leaving with source 198.51.100.5 (WAN-B's IP). What does this tell you about the configuration?

Passing score: 75%. Answers are checked in this browser.