Scenario 1: missing NAT
Symptom: the client can reach R1, but nothing beyond it. Ping to the server: 100% loss, no errors.
Investigation:
- R1 has a route to the server's network: it's directly connected on eth1.
monitor trafficon R1's WAN interface shows the echo requests leaving, with source address 10.0.0.10.- A capture on the server shows them arriving. No replies are sent, because the server has no route to 10.0.0.0/24.
This is how private addressing fails on the internet: RFC 1918 addresses aren't routed there, so a request can leave but the reply has nowhere to go. The same pattern appears with any missing return route, for example a new subnet that the far side's routing doesn't know about yet.
Fix: source NAT (masquerade) on R1's WAN interface. Outgoing packets take R1's address, and conntrack maps the replies back.
- The request leaves R1 unchanged. R1 routes the client's echo request out of eth1 with its private source 10.0.0.10.
- The server receives it…. …and wants to reply to 10.0.0.10. It only knows its own subnet, just as an internet host knows nothing about your private range.
- …but has nowhere to send the reply. No route to 10.0.0.0/24: the reply is never sent. From the client this looks like 100% loss.
- With source NAT. R1 rewrites the source to its own WAN address 203.0.113.1 and remembers the mapping in conntrack. The server replies to R1, which reverses the translation.
root@client:~# ping -c 3 -W 1 203.0.113.20
PING 203.0.113.20 (203.0.113.20) 56(84) bytes of data.
--- 203.0.113.20 ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 2067ms- Line 5: 100% loss and no error message: the packets left, and something downstream didn't answer.
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:
C>* 10.0.0.0/24 is directly connected, eth0, weight 1, 00:02:09
L>* 10.0.0.1/32 is directly connected, eth0, weight 1, 00:02:09
C>* 203.0.113.0/24 is directly connected, eth1, weight 1, 00:02:04
L>* 203.0.113.1/32 is directly connected, eth1, weight 1, 00:02:04- Lines 10–13:
Cconnected andLlocal routes for both interfaces. The server's subnet is directly connected on eth1, so routing on R1 isn't the problem.
vyos@R1:~$ monitor traffic interface eth1 filter "icmp"
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), snapshot length 262144 bytes
21:19:06.670939 IP 10.0.0.10 > 203.0.113.20: ICMP echo request, id 32367, seq 1, length 64
21:19:07.734452 IP 10.0.0.10 > 203.0.113.20: ICMP echo request, id 32367, seq 2, length 64
^C
2 packets captured
2 packets received by filter
0 packets dropped by kernel- Line 1:
monitor trafficis tcpdump on the router. Stop it with Ctrl-C. - Lines 4–5: The requests leave with the client's private address 10.0.0.10 as their source. Nothing comes back. (VyOS's clock runs behind the containers' clocks in this emulated lab.)
root@server:~# timeout 5 tcpdump -l -nn -i eth0 icmp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
21:36:15.177297 IP 10.0.0.10 > 203.0.113.20: ICMP echo request, id 32368, seq 1, length 64
21:36:16.228439 IP 10.0.0.10 > 203.0.113.20: ICMP echo request, id 32368, seq 2, length 64
2 packets captured
2 packets received by filter
0 packets dropped by kernel
root@server:~# ip route get 10.0.0.10
RTNETLINK answers: Network is unreachable- Lines 4–5: The requests arrive. The forward path works end to end.
- Lines 10–11: The server can't reply: it has no route to 10.0.0.0/24.
vyos@R1:~$ configure
WARNING: You are currently configuring a live-ISO environment, changes will not persist until installed
[edit]
vyos@R1# set nat source rule 100 description "LAN to WAN"
[edit]
vyos@R1# set nat source rule 100 outbound-interface name eth1
[edit]
vyos@R1# set nat source rule 100 source address 10.0.0.0/24
[edit]
vyos@R1# set nat source rule 100 translation address masquerade
[edit]
vyos@R1# commit
[edit]
vyos@R1# exit
Warning: configuration changes have not been saved.- Line 2: Expected in this lab: the VM boots straight from the ISO, so configuration lives in memory only.
- Line 6: Only translate traffic leaving the WAN interface.
- Line 8: Only for our LAN.
- Line 10: Masquerade: use whatever address eth1 has, which is right for dynamic WAN addresses.
- Line 12: Commit applies the NAT rules to nftables.
root@client:~# ping -c 3 -W 1 203.0.113.20
PING 203.0.113.20 (203.0.113.20) 56(84) bytes of data.
64 bytes from 203.0.113.20: icmp_seq=1 ttl=63 time=6.75 ms
64 bytes from 203.0.113.20: icmp_seq=2 ttl=63 time=3.49 ms
64 bytes from 203.0.113.20: icmp_seq=3 ttl=63 time=2.81 ms
--- 203.0.113.20 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2019ms
rtt min/avg/max/mdev = 2.806/4.347/6.747/1.719 ms- Lines 3–5: Replies, with TTL 63: one router (R1) on the way back.
vyos@R1:~$ monitor traffic interface eth1 filter "icmp"
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), snapshot length 262144 bytes
21:19:57.381159 IP 203.0.113.1 > 203.0.113.20: ICMP echo request, id 32370, seq 1, length 64
21:19:57.382120 IP 203.0.113.20 > 203.0.113.1: ICMP echo reply, id 32370, seq 1, length 64
21:19:58.395613 IP 203.0.113.1 > 203.0.113.20: ICMP echo request, id 32370, seq 2, length 64
21:19:58.396207 IP 203.0.113.20 > 203.0.113.1: ICMP echo reply, id 32370, seq 2, length 64
^C
4 packets captured
4 packets received by filter
0 packets dropped by kernel- Lines 4–7: Now the source is R1's own WAN address, 203.0.113.1, and replies come back to it.
vyos@R1:~$ show nat source rules
Rule Source Destination Proto Out-Int Translation
------ ----------- ------------- ------- --------- -------------
100 10.0.0.0/24 0.0.0.0/0 any eth1 masquerade
sport any dport any
vyos@R1:~$ show nat source translations
Pre-NAT Post-NAT Proto Timeout Mark Zone
--------- ----------- ------- --------- ------ ------
10.0.0.10 203.0.113.1 icmp 19 0
10.0.0.10 203.0.113.1 icmp 5 0
10.0.0.10 203.0.113.1 icmp 29 0- Line 4: The rule as R1 understands it: 10.0.0.0/24 to anywhere, out of eth1, masquerade.
- Lines 9–11: Active translations from conntrack, one per ping flow, each with a countdown timeout.
Check your understanding
In a capture on the server, the requests arrive with source 10.0.0.10. What does that tell you?
- NAT is working correctly
- No source NAT was applied on the way, so the server must have a route back to 10.0.0.0/24, which it doesn't
- The firewall on R1 dropped the packets
- The server's ARP cache is stale