Hermes Wiki

IP Routing Tables and NAT

Concept

At OSI Layer 3, getting a packet from one host to another — especially across subnet boundaries — is the job of routing: every network interface consults a route table, an ordered list of rules mapping a destination IP range (a CIDR block, e.g. 10.0.1.0/24) to a next hop (another router, a gateway, or "local" for directly-attached networks). When a packet needs to leave a host, its destination address is checked against every route in the table, and the most specific match wins — this is longest-prefix matching: a route for 10.0.1.0/24 beats a route for 10.0.0.0/16 for a packet addressed to 10.0.1.5, because the more specific prefix is a tighter, more precise description of where that address actually lives. A route table typically ends with a default route (0.0.0.0/0), the catch-all next hop for any destination not matched by anything more specific — usually a gateway to the wider internet.

NAT (Network Address Translation) solves a different but adjacent problem: letting hosts with private, non-internet-routable addresses (the RFC 1918 ranges: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) originate connections to the public internet, which has no route back to a private address at all. A NAT device sits at the boundary and rewrites packet headers as traffic crosses it: for outbound traffic, it replaces the private source IP (and often the source port too, a variant called PAT, Port Address Translation, or NAPT) with its own public IP, and keeps a translation table mapping that rewritten (public IP, public port) back to the original (private IP, private port). Return traffic arrives addressed to the NAT device's public IP/port, gets looked up in that table, and is rewritten back to the private address before being forwarded inward. Because the mapping only exists after an outbound connection creates it, an unsolicited inbound connection to a NAT'd private address has nothing to match against and is simply dropped — which is why NAT incidentally also hides private hosts from being directly reachable, even though that was never NAT's original design goal (address conservation was — this side effect just happens to double as a coarse security boundary).

Cloud VPCs make both of these mechanics explicit, configurable objects rather than kernel internals: a route table attached to a subnet is exactly the Layer-3 concept above, and a NAT Gateway is a managed instance of exactly the translation mechanism above, sitting in a public subnet with its own public (Elastic) IP, that a private subnet's route table points its 0.0.0.0/0 route at.

Tradeoffs

Approach Benefit Cost
Public IP on every host (no NAT) No translation layer, no NAT device to scale or pay for, simplest to trace end-to-end Exhausts scarce public IPv4 address space; every host is directly internet-addressable, which is rarely what you want for internal-only resources
NAT (private addressing + shared public egress) Conserves public IPv4 addresses; private hosts aren't directly reachable from the internet by default Adds a translation device that must scale with connection count (each outbound connection consumes an ephemeral port on the shared public IP — a real limit at high concurrency); adds one more hop to trace when debugging connectivity
Static routes (hand-maintained route tables) Fully predictable, easy to reason about for a small, stable topology Doesn't scale to a topology that changes often — every new subnet or path change needs a manual route table update everywhere it's relevant
Dynamic routing protocols (BGP, OSPF) Routers exchange reachability information and adapt automatically to topology changes or failures Real operational complexity — a resource cloud VPCs mostly hide behind managed route tables, but very much alive in ISP/backbone/hybrid-cloud interconnects

When to use / when not to

  • Reach for a route table trace, not a security-group/firewall-rule check, when the symptom is "this resource can't reach that address at all" (as opposed to "it's blocked") — routing and packet filtering are different layers and a misconfigured route produces a different failure mode (no path at all) than a filtering rule (path exists, packet dropped/rejected).
  • Use NAT (private subnet + NAT gateway/instance) for any resource that needs to initiate outbound connections (call a third-party API, pull a package) but must never be directly reachable from the internet — this is the default posture for application servers, workers, and databases in a well-architected VPC.
  • Don't put a resource that needs to receive unsolicited inbound internet traffic behind NAT alone — NAT has no mapping to match an inbound connection against, so it will simply be dropped; that resource needs a public IP (or a load balancer/reverse proxy in front of it) instead.
  • Don't assume "it has a private IP so it's secure" — NAT's reachability restriction is a side effect of how translation state is created, not an access-control policy; a security group/NACL is the actual, intentional access-control layer and the two should never be conflated when reasoning about what's actually protected.

Common pitfall

Diagnosing a "can't reach the internet" failure by checking security group/firewall rules first, when the actual cause is a missing or misdirected route — a private subnet's route table with no 0.0.0.0/0 entry pointing at a NAT gateway (or pointing at one that doesn't itself have a route to an Internet Gateway) produces the exact same symptom, from the application's point of view, as being blocked by a rule: the connection just times out. The two layers require different debugging: trace the actual path a packet would take — this subnet's route table → NAT gateway → the NAT gateway's own route to an Internet Gateway → back — before assuming a filtering rule is at fault.

Engineering Lens

Routing and NAT are the layer underneath almost every "why can't service A reach service B" incident that isn't actually a filtering-rule problem, and the two get conflated constantly because both produce a dead connection from the application's point of view. The engineering habit that actually resolves these fast is keeping the layers straight: routing answers "is there a path at all," NAT/translation answers "what address does the far end actually see," and access control (security groups, NACLs, firewalls) answers "is this specific path allowed." Naming which of the three actually failed — instead of guessing across all three — is what turns a long trial-and-error debugging session into a two-minute root cause.

Sources

Hermes Wiki