| |

LFCE 81 🐧 Masquerading and Source NAT

⏱️ Estimated read: 12 min | |

The previous chapter covered NAT concepts: the difference between source NAT and destination NAT, the connection-tracking requirement, and the role of the nat table. This chapter focuses on the source-NAT side: the two mechanisms that rewrite the source address of outgoing packets, the choice between them, and the Linux configuration for each. The snat statement rewrites the source to a fixed address, and the masquerade statement rewrites it to the address of the outgoing interface. The LFCE exam objectives include configuring a system to perform Network Address Translation, and the choice between these two forms is the first decision the administrator makes .

The choice is determined by the address type. A router with a static public address uses snat with that address. A router with a dynamic address—assigned by DHCP, PPPoE, or a VPN—uses masquerade, because the address can change without updating the rule. The Netfilter documentation states the distinction plainly: masquerading “should only be used for dynamically-assigned IP addresses, such as standard dialups (for static IP addresses, use SNAT above)” . The Red Hat documentation gives the same guidance: masquerading for dynamic addresses, SNAT for static ones .

Key point: Source NAT rewrites the source address of outgoing packets in the postrouting chain, after the routing decision. The snat statement requires a fixed address, and the masquerade statement uses the address of the outgoing interface. Both require net.ipv4.ip_forward=1 and a forward chain rule that permits the traffic, or the packets are dropped after the translation. The masquerade statement also translates the source port, which allows multiple internal hosts to share a single public address.


Why masquerading and SNAT are separate mechanisms

The dynamic-address problem.

A router with a DHCP-assigned public address does not know the address in advance. A snat rule with a hardcoded address would break when the lease renews and the address changes. The masquerade statement reads the address from the outgoing interface at the moment the packet is sent, so the rule remains correct across address changes . The Netfilter documentation notes that masquerading also “forgets” the connections when the link goes down, which reduces the glitches when the link comes back up with a new address .

The port-translation problem.

Multiple internal hosts may use the same source port for their connections. If the router rewrites only the address, the replies cannot be distinguished. Both snat and masquerade translate the source port as well, assigning a unique port to each connection and recording the mapping in the connection-tracking table . The Netfilter documentation describes the implicit source-port mapping: a second connection that would collide with an existing one is assigned a different port, so the two connections remain distinguishable .

The static-address problem.

A router with a static public address should use snat because the address is known and does not change. The masquerade statement works for a static address as well, but it performs an extra lookup of the interface’s address on every packet, which is unnecessary overhead when the address is fixed. The Red Hat documentation recommends snat for static addresses and masquerade for dynamic ones .


a. Configuring masquerading with nftables

The masquerade statement is added to the postrouting chain of the nat table. The chain must be created with the nat type and the postrouting hook.

# Create the nat table
sudo nft add table ip nat

# Create the postrouting chain
sudo nft add chain ip nat postrouting { type nat hook postrouting priority 100\; }

# Add the masquerade rule
sudo nft add rule ip nat postrouting oifname "eth0" masquerade

The oifname "eth0" expression matches packets that leave on the eth0 interface, and the masquerade statement rewrites the source address to the interface’s address . The priority 100 is the standard priority for the nat postrouting chain. The rule applies to every packet that leaves on eth0, regardless of the source network, which is appropriate when the router has a single internal network.

For a router with multiple internal networks, the rule can match the source subnet.

# Masquerade only the internal subnet
sudo nft add rule ip nat postrouting ip saddr 192.168.1.0/24 oifname "eth0" masquerade

The ip saddr 192.168.1.0/24 expression restricts the rule to packets from the internal network. The masquerade statement rewrites the source to the eth0 address, and the connection-tracking subsystem records the translation so that the replies can be reversed .


b. Configuring SNAT with nftables

The snat statement is used when the public address is fixed. The rule specifies the address to rewrite the source to.

# Create the nat table and postrouting chain
sudo nft add table ip nat
sudo nft add chain ip nat postrouting { type nat hook postrouting priority 100\; }

# Add the snat rule with a fixed address
sudo nft add rule ip nat postrouting ip saddr 192.168.1.0/24 oifname "eth0" snat to 203.0.113.5

The snat to 203.0.113.5 statement rewrites the source address of the matching packets to the fixed address . The ip saddr 192.168.1.0/24 and oifname "eth0" expressions restrict the rule to the internal network and the outgoing interface. The snat statement translates the source port as well, which is the same port-translation behavior as masquerade.

The snat statement can also specify a range of addresses, which is useful when the router has a block of public addresses and distributes the connections across them.

# SNAT to a range of addresses
sudo nft add rule ip nat postrouting ip saddr 192.168.1.0/24 oifname "eth0" snat to 203.0.113.5-203.0.113.10

The snat to 203.0.113.5-203.0.113.10 statement assigns the source address from the range, distributing the connections across the available addresses . The connection-tracking subsystem records which address was used for each connection, so the replies are reversed correctly.


c. The forwarding requirement and verification

NAT is not sufficient on its own. The kernel must forward the packets, and the forward chain must allow the traffic.

# Enable IP forwarding
sudo sysctl -w net.ipv4.ip_forward=1
echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.d/99-forwarding.conf

# Allow forwarding in the filter table
sudo nft add rule inet filter forward iif eth0 oif eth1 ct state established,related accept
sudo nft add rule inet filter forward iif eth1 oif eth0 accept

The sysctl -w net.ipv4.ip_forward=1 command enables the kernel’s forwarding capability . The filter table’s forward chain must allow the traffic in both directions, or the packets are dropped after the NAT translation. The iif eth0 oif eth1 rule allows the outgoing traffic, and the iif eth1 oif eth0 rule allows the replies.

The nft list table ip nat command shows the NAT rules, and the conntrack command shows the active translations.

# List the NAT rules
sudo nft list table ip nat

# Show the active connection tracking entries
sudo conntrack -L
# Output shows the original and translated addresses and ports

The nft list table ip nat output shows the postrouting chain with the masquerade or snat rule . The conntrack -L output shows the original source address and port, the translated source address and port, and the destination. A connection that is being masqueraded shows the internal address on the left and the public address on the right.


Complete Example Session

# ============================================
# PART 1: ENABLE IP FORWARDING
# ============================================
sudo sysctl -w net.ipv4.ip_forward=1
echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.d/99-forwarding.conf

# ============================================
# PART 2: CREATE THE NAT TABLE
# ============================================
sudo nft add table ip nat

# ============================================
# PART 3: CREATE THE POSTROUTING CHAIN
# ============================================
sudo nft add chain ip nat postrouting { type nat hook postrouting priority 100\; }

# ============================================
# PART 4: ADD THE MASQUERADE RULE
# ============================================
sudo nft add rule ip nat postrouting ip saddr 192.168.1.0/24 oifname "eth0" masquerade

# ============================================
# PART 5: ALTERNATIVE: ADD THE SNAT RULE
# ============================================
# If the public address is static, use snat instead
# sudo nft add rule ip nat postrouting ip saddr 192.168.1.0/24 oifname "eth0" snat to 203.0.113.5

# ============================================
# PART 6: ALLOW FORWARDING
# ============================================
sudo nft add rule inet filter forward iif eth1 oif eth0 ct state established,related accept
sudo nft add rule inet filter forward iif eth1 oif eth0 accept

# ============================================
# PART 7: VERIFY THE NAT RULES
# ============================================
sudo nft list table ip nat
# Output shows the postrouting chain with the masquerade rule

# ============================================
# PART 8: VERIFY THE CONNECTION TRACKING
# ============================================
sudo conntrack -L | head -5
# Output shows the original and translated addresses

# ============================================
# PART 9: TEST FROM AN INTERNAL HOST
# ============================================
# From 192.168.1.50: curl http://ifconfig.me
# The public address is the router's eth0 address

# ============================================
# PART 10: VERIFY THE TRANSLATION
# ============================================
# On the router: conntrack -L | grep 192.168.1.50
# The output shows the original address and the translated address

The ten parts demonstrate the source-NAT configuration: forwarding, the nat table, the postrouting chain, the masquerade rule, the snat alternative, the forwarding rules, the verification, the connection-tracking check, the internal test, and the translation verification. The critical distinction is between Parts 4 and 5: masquerade uses the interface’s address, and snat uses a fixed address.


Quick Reference

nftables Statements

StatementChainAddress SourceUse Case
masqueradepostroutingOutgoing interfaceDynamic public address
snat to <addr>postroutingFixed addressStatic public address

Prerequisites

RequirementCommand
IP forwardingsysctl -w net.ipv4.ip_forward=1
Forward chainAllow traffic in filter forward
Connection trackingEnabled by default

Verification Commands

CommandPurpose
nft list table ip natShow the NAT rules
conntrack -LShow active translations
conntrack -L | grep <ip>Show a specific host’s translation
sysctl net.ipv4.ip_forwardConfirm forwarding is enabled

Masquerade vs SNAT

AspectMasqueradeSNAT
AddressInterface’s addressFixed address
Dynamic IPsYesNo
Static IPsWorks but unnecessaryYes
OverheadInterface lookup per packetNo lookup

Best Practices

✅ Do This:

# Use masquerade for dynamic addresses
oifname "eth0" masquerade                                      # ✅

# Use snat for static addresses
oifname "eth0" snat to 203.0.113.5                             # ✅

# Match the internal subnet
ip saddr 192.168.1.0/24 oifname "eth0" masquerade              # ✅

# Enable forwarding
sysctl -w net.ipv4.ip_forward=1                                # ✅

# Allow forwarding in the filter table
iif eth1 oif eth0 ct state established,related accept          # ✅

# Verify with conntrack
conntrack -L | grep 192.168.1.50                               # ✅

❌ Don’t Do This:

# Use snat with a dynamic address
snat to 203.0.113.5    # Breaks when the address changes       # ❌

# Forget ip_forward
# The NAT rules have no effect                               # ❌

# Put the source-NAT rule in prerouting
prerouting ... masquerade    # Too early; routing decision    # ❌

# Forget the forward chain rules
# The packets are dropped after NAT                          # ❌

# Use masquerade for a static address
# Unnecessary overhead                                       # ❌

Common Pitfalls

PitfallWhy It HappensFix
NAT has no effectip_forward disabledsysctl -w net.ipv4.ip_forward=1
Replies not returnedForward chain deniesAllow the traffic in filter forward
SNAT breaks on address changeUsed snat with a dynamic addressUse masquerade
Masquerade overheadUsed masquerade with a static addressUse snat
Rule not matchingWrong source subnet or interfaceVerify the ip saddr and oifname

Real-World Examples

1. Masquerade

nft add rule ip nat postrouting oifname "eth0" masquerade

2. Masquerade for a subnet

nft add rule ip nat postrouting ip saddr 192.168.1.0/24 oifname "eth0" masquerade

3. SNAT

nft add rule ip nat postrouting ip saddr 192.168.1.0/24 oifname "eth0" snat to 203.0.113.5

4. SNAT to a range

nft add rule ip nat postrouting oifname "eth0" snat to 203.0.113.5-203.0.113.10

5. Enable forwarding

sysctl -w net.ipv4.ip_forward=1

6. Allow forwarding

nft add rule inet filter forward iif eth1 oif eth0 accept

7. Verify NAT rules

nft list table ip nat

8. Verify translations

conntrack -L | grep 192.168.1.50

9. Check forwarding state

sysctl net.ipv4.ip_forward

10. Flush the NAT table

nft flush table ip nat

Visual

Masquerade

┌──────────────────────────────────────────────────────────────┐
│  MASQUERADE USES THE INTERFACE'S ADDRESS                     │
│                                                              │
│  Internal host (192.168.1.50)                                │
│       │ src: 192.168.1.50:12345                              │
│       ▼                                                      │
│  Router                                                      │
│       │ postrouting: masquerade                              │
│       │ → reads eth0's address (203.0.113.5)                 │
│       │ → rewrites src to 203.0.113.5:54321                  │
│       ▼                                                      │
│  Internet                                                    │
│       │ src: 203.0.113.5:54321                               │
│       ▼                                                      │
│  Reply to 203.0.113.5:54321                                  │
│       │                                                      │
│       ▼                                                      │
│  Router (conntrack reverses)                                 │
│       │ dst: 192.168.1.50:12345                              │
│       ▼                                                      │
│  Internal host receives the reply                            │
└──────────────────────────────────────────────────────────────┘

SNAT

┌──────────────────────────────────────────────────────────────┐
│  SNAT USES A FIXED ADDRESS                                   │
│                                                              │
│  Internal host (192.168.1.50)                                │
│       │ src: 192.168.1.50:12345                              │
│       ▼                                                      │
│  Router                                                      │
│       │ postrouting: snat to 203.0.113.5                     │
│       │ → rewrites src to 203.0.113.5:54321                  │
│       ▼                                                      │
│  Internet                                                    │
│       │ src: 203.0.113.5:54321                               │
│       ▼                                                      │
│  Reply to 203.0.113.5:54321                                  │
│       │                                                      │
│       ▼                                                      │
│  Router (conntrack reverses)                                 │
│       │ dst: 192.168.1.50:12345                              │
│       ▼                                                      │
│  Internal host receives the reply                            │
└──────────────────────────────────────────────────────────────┘

The Forwarding Requirement

┌──────────────────────────────────────────────────────────────┐
│  NAT WITHOUT FORWARDING DOES NOT WORK                        │
│                                                              │
│  Packet arrives at the router                                │
│       │                                                      │
│       ▼                                                      │
│  nat postrouting: source address rewritten                   │
│       │                                                      │
│       ▼                                                      │
│  filter forward: is the packet allowed?                      │
│       │                                                      │
│       ├── Yes → packet is forwarded                          │
│       │                                                      │
│       └── No → packet is dropped                             │
│                                                              │
│  The NAT rule rewrites the address,                         │
│  and the forward rule decides whether the packet passes.     │
└──────────────────────────────────────────────────────────────┘

Summary

ItemValue
Source NAT chainpostrouting
MasqueradeUses the outgoing interface’s address
SNATUses a fixed address
Dynamic addressUse masquerade
Static addressUse snat
Port translationBoth translate the source port
Prerequisitenet.ipv4.ip_forward=1
Forwarding rulefilter forward chain
Verificationnft list table ip nat, conntrack -L

Key takeaways:

  • Masquerade and SNAT are both source NAT, but they differ in the address source. masquerade uses the outgoing interface’s address, which is correct for dynamic addresses. snat uses a fixed address, which is correct for static addresses .
  • The choice is determined by the address type. A router with a DHCP-assigned public address uses masquerade. A router with a static public address uses snat to avoid the interface lookup on every packet .
  • Both mechanisms translate the source port. The connection-tracking subsystem records the translation so that the replies can be reversed. Without port translation, multiple internal hosts could not share a single public address .
  • The forwarding requirement is separate from the NAT rule. The kernel must have ip_forward enabled, and the filter forward chain must allow the traffic. The NAT rule rewrites the address, and the forward rule decides whether the packet is allowed to pass .
  • The conntrack -L command shows the active translations. It displays the original and translated addresses and ports, which is the definitive verification that the source NAT is working.
  • The postrouting chain is the correct location for source NAT. The rule is evaluated after the routing decision, when the outgoing interface is known. A source-NAT rule in the prerouting chain is too early.

Remember: Source NAT rewrites the source address of outgoing packets. masquerade uses the interface’s address and is correct for dynamic addresses. snat uses a fixed address and is correct for static addresses. Both translate the source port and require connection tracking to reverse the translation. Both require ip_forward and a filter forward rule that allows the traffic. The postrouting chain is the correct location, and the conntrack -L command is the verification. The result is a private network that reaches the internet through a single public address, with each connection tracked and reversed correctly.


Was this helpful?


Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!