Topic Overview
NAT & PAT
Understand Network Address Translation (NAT) and Port Address Translation (PAT) for connecting private networks to the internet and conserving IPv4 addresses.
NAT (Network Address Translation) allows private networks to use private IP addresses while accessing the internet through a public IP address. PAT (Port Address Translation), also called NAT overload, extends NAT to allow multiple devices to share a single public IP using different ports.
What is NAT?
NAT (Network Address Translation) translates private IP addresses to public IP addresses (and vice versa) at the network boundary.
Why needed:
- IPv4 exhaustion: Limited public IP addresses
- Private networks: Use RFC 1918 private addresses (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)
- Security: Hide internal network structure
- Cost: Reduce need for multiple public IPs
NAT Types
1. Static NAT
One-to-one mapping: One private IP → One public IP
Private Network Public Network
192.168.1.10 → 203.0.113.10
192.168.1.20 → 203.0.113.20
Use case: Servers that need consistent public IP
2. Dynamic NAT
Many-to-many mapping: Multiple private IPs → Pool of public IPs
Private Network Public IP Pool
192.168.1.10 → 203.0.113.10-15
192.168.1.20 → (assigned dynamically)
192.168.1.30 →
Use case: Multiple devices sharing a pool of public IPs
3. PAT (Port Address Translation / NAT Overload)
Many-to-one mapping: Multiple private IPs → Single public IP (using ports)
Private Network Public Network
192.168.1.10:5000 → 203.0.113.1:10000
192.168.1.20:5000 → 203.0.113.1:10001
192.168.1.30:5000 → 203.0.113.1:10002
Use case: Home networks, small offices (most common)
NAT Process
Outbound Traffic (Private → Public)
1. Device (192.168.1.10) sends packet to 8.8.8.8
Source: 192.168.1.10:5000
Dest: 8.8.8.8:53
2. NAT router receives packet
- Checks NAT table
- If no entry, creates translation
- Translates source IP/port
3. NAT router forwards packet
Source: 203.0.113.1:10000 (translated)
Dest: 8.8.8.8:53
4. Response comes back
Source: 8.8.8.8:53
Dest: 203.0.113.1:10000
5. NAT router translates back
Source: 8.8.8.8:53
Dest: 192.168.1.10:5000 (original)
NAT Table
NAT router maintains a translation table:
Inside Local Inside Global Outside Global Outside Local
192.168.1.10:5000 → 203.0.113.1:10000 ↔ 8.8.8.8:53 → 8.8.8.8:53
192.168.1.20:5000 → 203.0.113.1:10001 ↔ 93.184.216.34:80 → 93.184.216.34:80
Table entries expire after timeout (typically 30-60 seconds of inactivity)
PAT (Port Address Translation)
PAT allows multiple devices to share one public IP by using different ports.
PAT Example
Device A (192.168.1.10) → Public IP (203.0.113.1:10000)
Device B (192.168.1.20) → Public IP (203.0.113.1:10001)
Device C (192.168.1.30) → Public IP (203.0.113.1:10002)
How it works:
- Device sends packet with source port (e.g., 5000)
- NAT router assigns unique public port (e.g., 10000)
- Maps private IP:port → public IP:port
- Response uses same mapping to route back
Port range: Typically 1024-65535 (ephemeral ports)
Examples
NAT Configuration (Router)
# Static NAT (Cisco)
ip nat inside source static 192.168.1.10 203.0.113.10
interface GigabitEthernet0/0
ip nat inside
interface GigabitEthernet0/1
ip nat outside
# Dynamic NAT
ip nat pool PUBLIC_POOL 203.0.113.10 203.0.113.15 netmask 255.255.255.0
ip nat inside source list 1 pool PUBLIC_POOL
access-list 1 permit 192.168.1.0 0.0.0.255
# PAT (NAT Overload)
ip nat inside source list 1 interface GigabitEthernet0/1 overload
NAT Simulation (Python)
import socket
from collections import defaultdict
import time
class NATRouter:
def __init__(self, public_ip):
self.public_ip = public_ip
self.nat_table = {} # (private_ip, private_port) → (public_port, timestamp)
self.port_counter = 10000
self.timeout = 60 # seconds
def translate_outbound(self, private_ip, private_port, dest_ip, dest_port):
"""Translate outbound packet (private → public)"""
key = (private_ip, private_port)
# Check if translation exists
if key in self.nat_table:
public_port, timestamp = self.nat_table[key]
# Update timestamp
self.nat_table[key] = (public_port, time.time())
return self.public_ip, public_port
# Create new translation
public_port = self.port_counter
self.port_counter += 1
if self.port_counter > 65535:
self.port_counter = 10000
self.nat_table[key] = (public_port, time.time())
print(f"NAT: {private_ip}:{private_port} → {self.public_ip}:{public_port}")
return self.public_ip, public_port
def translate_inbound(self, public_port, dest_ip, dest_port):
"""Translate inbound packet (public → private)"""
# Find private IP:port for this public port
for (private_ip, private_port), (pub_port, timestamp) in self.nat_table.items():
if pub_port == public_port:
# Check timeout
if time.time() - timestamp > self.timeout:
del self.nat_table[(private_ip, private_port)]
continue
# Update timestamp
self.nat_table[(private_ip, private_port)] = (pub_port, time.time())
print(f"NAT: {self.public_ip}:{public_port} → {private_ip}:{private_port}")
return private_ip, private_port
return None, None # No translation found
def cleanup_expired(self):
"""Remove expired NAT entries"""
now = time.time()
expired = [
key for key, (port, timestamp) in self.nat_table.items()
if now - timestamp > self.timeout
]
for key in expired:
del self.nat_table[key]
# Usage
nat = NATRouter("203.0.113.1")
# Outbound translation
public_ip, public_port = nat.translate_outbound("192.168.1.10", 5000, "8.8.8.8", 53)
print(f"Translated to: {public_ip}:{public_port}")
# Inbound translation
private_ip, private_port = nat.translate_inbound(public_port, "8.8.8.8", 53)
print(f"Translated back to: {private_ip}:{private_port}")
NAT Table Monitoring
def show_nat_table(nat_router):
"""Display NAT translation table"""
print("NAT Translation Table:")
print(f"{'Inside Local':<20} {'Inside Global':<20} {'Age':<10}")
print("-" * 50)
now = time.time()
for (private_ip, private_port), (public_port, timestamp) in nat_router.nat_table.items():
age = int(now - timestamp)
inside_local = f"{private_ip}:{private_port}"
inside_global = f"{nat_router.public_ip}:{public_port}"
print(f"{inside_local:<20} {inside_global:<20} {age}s")
# Usage
show_nat_table(nat)
NAT Limitations
Problems with NAT
- End-to-end connectivity: Breaks end-to-end principle
- Peer-to-peer applications: Difficult for P2P (need port forwarding)
- IPsec: Can conflict with NAT (NAT traversal needed)
- Logging: Harder to track individual users
- Port exhaustion: PAT can run out of ports (65,535 limit)
NAT Traversal
NAT Traversal techniques for applications that need direct connections:
- STUN (Session Traversal Utilities for NAT): Discover public IP:port
- TURN (Traversal Using Relays around NAT): Relay server for NAT traversal
- ICE (Interactive Connectivity Establishment): Combines STUN and TURN
- UPnP (Universal Plug and Play): Automatic port forwarding
Common Pitfalls
- Port exhaustion: Too many connections exhaust available ports. Fix: Increase port range, use connection pooling, reduce timeout
- NAT table overflow: Too many translations consume memory. Fix: Reduce timeout, limit connections per device
- Breaking applications: Some apps don't work through NAT. Fix: Use NAT traversal (STUN/TURN), port forwarding
- Not understanding PAT: Confusing static NAT with PAT. Fix: Understand PAT uses ports, static NAT doesn't
- Double NAT: NAT behind NAT causes issues. Fix: Avoid nested NAT, use bridge mode
- Timeout too short: Connections drop prematurely. Fix: Increase NAT timeout for long-lived connections
- Security through obscurity: Relying on NAT for security. Fix: Use proper firewalls, don't rely on NAT alone
Interview Questions
Beginner
Q: What is NAT and why is it used?
A:
NAT (Network Address Translation) translates private IP addresses to public IP addresses at the network boundary.
Why used:
- IPv4 exhaustion: Limited public IP addresses available
- Private networks: Use RFC 1918 private addresses (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)
- Security: Hide internal network structure from internet
- Cost: Reduce need for multiple public IP addresses
- Flexibility: Easy to change internal network without affecting external
How it works:
Private Network NAT Router Internet
192.168.1.10 → 203.0.113.1 → 8.8.8.8
(Private IP) (Public IP) (Internet)
Types:
- Static NAT: One private IP → One public IP
- Dynamic NAT: Multiple private IPs → Pool of public IPs
- PAT (NAT Overload): Multiple private IPs → One public IP (using ports)
Intermediate
Q: Explain the difference between NAT and PAT. How does PAT allow multiple devices to share one public IP?
A:
NAT (Network Address Translation):
- Translates IP addresses only
- Requires one public IP per private IP (or pool)
- Used for static or dynamic mapping
PAT (Port Address Translation / NAT Overload):
- Translates both IP addresses and ports
- Allows multiple private IPs to share one public IP
- Uses different ports to distinguish connections
How PAT works:
Device A (192.168.1.10:5000) → Public (203.0.113.1:10000)
Device B (192.168.1.20:5000) → Public (203.0.113.1:10001)
Device C (192.168.1.30:5000) → Public (203.0.113.1:10002)
PAT Process:
- Device sends packet: Source 192.168.1.10:5000
- NAT router assigns unique public port: 10000
- Creates mapping: (192.168.1.10:5000) → (203.0.113.1:10000)
- Forwards packet with translated source
- Response comes to 203.0.113.1:10000
- NAT router looks up mapping, translates back to 192.168.1.10:5000
NAT Table:
Inside Local Inside Global
192.168.1.10:5000 → 203.0.113.1:10000
192.168.1.20:5000 → 203.0.113.1:10001
192.168.1.30:5000 → 203.0.113.1:10002
Benefits of PAT:
- Efficiency: One public IP for entire network
- Cost: No need for multiple public IPs
- Scalability: Support many devices (up to port limit)
Limitations:
- Port exhaustion: Maximum 65,535 ports
- Peer-to-peer: Difficult for P2P applications
- Logging: Harder to track individual users
Senior
Q: Design a high-performance NAT system for a cloud provider that handles millions of concurrent connections. How do you handle port exhaustion, connection tracking, and ensure low latency?
A:
class HighPerformanceNAT {
private natTable: DistributedNATTable;
private portAllocator: PortAllocator;
private connectionTracker: ConnectionTracker;
private loadBalancer: LoadBalancer;
constructor() {
// Distributed NAT table (Redis cluster)
this.natTable = new DistributedNATTable({
backend: 'redis-cluster',
replication: 3,
sharding: 'consistent-hashing'
});
// Port allocator with port pools
this.portAllocator = new PortAllocator({
portRange: [10000, 65535],
poolsPerIP: 1000
});
// Connection tracking
this.connectionTracker = new ConnectionTracker({
timeout: 60, // seconds
maxConnections: 1000000
});
}
// 1. Distributed NAT Translation
async translateOutbound(
privateIP: string,
privatePort: number,
destIP: string,
destPort: number
): Promise<Translation> {
const key = `${privateIP}:${privatePort}`;
// Check existing translation
const existing = await this.natTable.get(key);
if (existing && !this.isExpired(existing)) {
await this.natTable.updateTimestamp(key);
return existing;
}
// Allocate new translation
const publicIP = await this.loadBalancer.selectPublicIP();
const publicPort = await this.portAllocator.allocate(publicIP);
const translation = {
privateIP,
privatePort,
publicIP,
publicPort,
destIP,
destPort,
timestamp: Date.now(),
connectionId: this.generateConnectionId()
};
// Store in distributed table
await this.natTable.set(key, translation, { ttl: 60 });
// Track connection
await this.connectionTracker.track(translation);
return translation;
}
// 2. Port Allocation with Pools
class PortAllocator {
private portPools: Map<string, PortPool>;
async allocate(publicIP: string): Promise<number> {
let pool = this.portPools.get(publicIP);
if (!pool || pool.isExhausted()) {
pool = await this.createPortPool(publicIP);
this.portPools.set(publicIP, pool);
}
return await pool.allocate();
}
async createPortPool(publicIP: string): Promise<PortPool> {
// Allocate port range for this IP
const startPort = this.getNextPortRange();
const endPort = startPort + 1000; // 1000 ports per pool
return new PortPool(publicIP, startPort, endPort);
}
}
// 3. Connection Tracking
class ConnectionTracker {
private connections: Map<string, Connection>;
async track(translation: Translation): Promise<void> {
const connection = {
id: translation.connectionId,
translation,
packets: 0,
bytes: 0,
startTime: Date.now(),
lastActivity: Date.now()
};
this.connections.set(translation.connectionId, connection);
// Set expiration
setTimeout(() => {
this.cleanup(translation.connectionId);
}, 60000);
}
async updateActivity(connectionId: string, bytes: number): Promise<void> {
const connection = this.connections.get(connectionId);
if (connection) {
connection.packets++;
connection.bytes += bytes;
connection.lastActivity = Date.now();
}
}
}
// 4. Port Exhaustion Handling
async handlePortExhaustion(publicIP: string): Promise<void> {
// Strategy 1: Reuse expired ports
const expired = await this.natTable.getExpired(publicIP);
if (expired.length > 0) {
await this.portAllocator.release(expired.map(e => e.publicPort));
return;
}
// Strategy 2: Use additional public IP
const newPublicIP = await this.loadBalancer.addPublicIP();
await this.portAllocator.createPool(newPublicIP);
// Strategy 3: Increase timeout (aggressive cleanup)
await this.natTable.cleanupExpired(publicIP, { aggressive: true });
}
// 5. Load Balancing Across Public IPs
class LoadBalancer {
private publicIPs: string[];
private ipUsage: Map<string, number>;
async selectPublicIP(): Promise<string> {
// Select least used public IP
let minUsage = Infinity;
let selectedIP = this.publicIPs[0];
for (const ip of this.publicIPs) {
const usage = this.ipUsage.get(ip) || 0;
if (usage < minUsage) {
minUsage = usage;
selectedIP = ip;
}
}
this.ipUsage.set(selectedIP, (this.ipUsage.get(selectedIP) || 0) + 1);
return selectedIP;
}
}
// 6. Monitoring and Health Checks
async getMetrics(): Promise<Metrics> {
return {
totalTranslations: await this.natTable.getCount(),
activeConnections: await this.connectionTracker.getCount(),
portUtilization: await this.portAllocator.getUtilization(),
publicIPUsage: await this.loadBalancer.getUsage(),
averageLatency: await this.getAverageLatency()
};
}
}
Features:
- Distributed NAT table: Redis cluster for scalability
- Port pools: Allocate port ranges per public IP
- Connection tracking: Monitor active connections
- Port exhaustion handling: Reuse expired ports, add IPs, aggressive cleanup
- Load balancing: Distribute across multiple public IPs
- Monitoring: Track metrics, detect issues
Key Takeaways
- NAT: Translates private IPs to public IPs at network boundary
- Types: Static (1:1), Dynamic (many:many), PAT (many:1 with ports)
- PAT: Uses ports to allow multiple devices to share one public IP
- NAT table: Tracks translations, entries expire after timeout
- Process: Outbound (private→public), inbound (public→private) using table lookup
- Limitations: Breaks end-to-end connectivity, P2P issues, port exhaustion
- NAT traversal: STUN/TURN/ICE for applications needing direct connections
- Best practices: Monitor port usage, use connection pooling, implement proper timeouts