TTR Networks

Intermediate · Packets That Never Come Back · section 4 of 7

Scenario 1: missing NAT

✓ Lab-tested 17 October 2026 on VyOS 2026.10.16-0024-rolling, Debian 12

Symptom: the client can reach R1, but nothing beyond it. Ping to the server: 100% loss, no errors.

Investigation:

  1. R1 has a route to the server's network: it's directly connected on eth1.
  2. monitor traffic on R1's WAN interface shows the echo requests leaving, with source address 10.0.0.10.
  3. 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.

Diagram: Scenario 1: no NAT — Why the reply never comes, and how source NAT fixes it.
  1. The request leaves R1 unchanged. R1 routes the client's echo request out of eth1 with its private source 10.0.0.10.
  2. 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.
  3. …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.
  4. 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.
Symptom on the client (real output)
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.
R1 knows the way (real output)
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: C connected and L local routes for both interfaces. The server's subnet is directly connected on eth1, so routing on R1 isn't the problem.
Leaving R1's WAN interface (real output)
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 traffic is 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.)
Arriving at the server (real output)
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.
The fix: source NAT on R1 (real output)
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.
After the fix (real output)
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.
R1's WAN interface after the fix (real output)
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.
NAT rules and live translations (real output)
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