Interfaces and plain routing
Start with plain routing: an address on each interface, and a default route to the ISP. There's no NAT yet.
Then test from both LANs. The host on the public LAN, 198.51.100.2, works immediately: the ISP has a route for 198.51.100.0/28 pointing at R1, so replies come back. The server's log shows it as itself.
The host on the NAT-LAN, 10.0.0.10, times out. A capture on the server shows why. The request does arrive (R1 routed it out, with its private source address untouched), and the server answers. But the reply is addressed to 10.0.0.10, and the ISP has no route to a private network, so it sends back "net unreachable" and drops it. This is exactly what happens on the real internet, just one hop earlier: many ISPs drop private-source packets at their edge before they get anywhere.
This is the gap NAT fills: give the private host's packets a source address the internet can route back to.
set system host-name 'R1'
set interfaces ethernet eth0 address '203.0.113.2/30'
set interfaces ethernet eth0 description 'WAN'
set interfaces ethernet eth1 address '10.0.0.1/24'
set interfaces ethernet eth1 description 'NAT-LAN'
set interfaces ethernet eth2 address '198.51.100.1/28'
set interfaces ethernet eth2 description 'PUBLIC-LAN'
set protocols static route 0.0.0.0/0 next-hop '203.0.113.1'- Lines 2–3: The WAN: a /30 with the ISP, whose end is 203.0.113.1.
- Lines 4–5: The NAT-LAN: private addresses.
- Lines 6–7: The public LAN: the /28 the ISP routes to us. R1 takes the first address as the gateway.
- Line 8: Everything else goes to the ISP.
vyos@R1:~$ show interfaces
Codes: S - State, L - Link, u - Up, D - Down, A - Admin Down
Interface IP Address MAC VRF MTU S/L Description
----------- --------------- ----------------- ------- ----- ----- -------------
eth0 203.0.113.2/30 52:54:00:01:00:01 default 1500 u/u WAN
eth1 10.0.0.1/24 52:54:00:01:01:01 default 1500 u/u NAT-LAN
eth2 198.51.100.1/28 52:54:00:01:02:01 default 1500 u/u PUBLIC-LAN
lo 127.0.0.1/8 00:00:00:00:00:00 default 65536 u/u
::1/128
vyos@R1:~$ show ip route
Codes: K - kernel route, C - connected, L - local, S - static,
R - RIP, O - OSPF, I - IS-IS, B - BGP, E - EIGRP, N - NHRP,
T - Table, v - VNC, V - VNC-Direct, A - Babel, F - PBR,
f - OpenFabric, t - Table-Direct,
> - selected route, * - FIB route, q - queued, r - rejected, b - backup
t - trapped, o - offload failure
IPv4 unicast VRF default:
S>* 0.0.0.0/0 [1/0] via 203.0.113.1, eth0, weight 1, 00:00:19
C>* 10.0.0.0/24 is directly connected, eth1, weight 1, 00:00:29
L>* 10.0.0.1/32 is directly connected, eth1, weight 1, 00:00:29
C>* 198.51.100.0/28 is directly connected, eth2, weight 1, 00:00:22
L>* 198.51.100.1/32 is directly connected, eth2, weight 1, 00:00:22
C>* 203.0.113.0/30 is directly connected, eth0, weight 1, 00:00:26
L>* 203.0.113.2/32 is directly connected, eth0, weight 1, 00:00:26- Lines 5–7: One line per role.
- Line 19: The static default route.
- Lines 20–23: Each LAN is directly connected.
root@pubhost:~# curl -sS -m 5 http://192.0.2.10/
hello from server
root@lanhost:~# curl -sS -m 5 http://192.0.2.10/
curl: (28) Connection timed out after 5006 milliseconds- Lines 1–2: The public host works straight away.
- Lines 3–4: The private host times out.
root@server:~# tcpdump -ni eth0 -c 6 "tcp port 80 or icmp"
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
00:52:26.118654 IP 10.0.0.10.36156 > 192.0.2.10.80: Flags [S], seq 726241814, win 64240, options [mss 1460,sackOK,TS val 1809398008 ecr 0,nop,wscale 10], length 0
00:52:26.118667 IP 192.0.2.10.80 > 10.0.0.10.36156: Flags [S.], seq 519742404, ack 726241815, win 65160, options [mss 1460,sackOK,TS val 428386760 ecr 1809398008,nop,wscale 10], length 0
00:52:26.118679 IP 192.0.2.1 > 192.0.2.10: ICMP net 10.0.0.10 unreachable, length 68
00:52:27.151024 IP 10.0.0.10.36156 > 192.0.2.10.80: Flags [S], seq 726241814, win 64240, options [mss 1460,sackOK,TS val 1809399039 ecr 0,nop,wscale 10], length 0
00:52:27.151093 IP 192.0.2.10.80 > 10.0.0.10.36156: Flags [S.], seq 535873778, ack 726241815, win 65160, options [mss 1460,sackOK,TS val 428387793 ecr 1809399039,nop,wscale 10], length 0
00:52:27.151187 IP 192.0.2.1 > 192.0.2.10: ICMP net 10.0.0.10 unreachable, length 68
6 packets captured
6 packets received by filter
0 packets dropped by kernel
root@server:~# cat /tmp/http.log
198.51.100.2 - - [17/Oct/2026 00:52:23] "GET / HTTP/1.1" 200 -- Line 4: The request arrives with its private source address…
- Line 5: …the server answers it…
- Line 6: …and the ISP can't route the reply to 10.0.0.10, so it sends "net unreachable".
- Line 14: The public host's visit, logged under its own address.
Check your understanding
Without NAT, where did the private host's connection fail?
- At R1, which refused to forward private addresses
- At the server, which refused private clients
- On the way back: the ISP had no route to 10.0.0.10
- In DNS