Skip to content

Netfilter Architecture

What is Netfilter?

Netfilter is the packet filtering framework built into the Linux kernel. It provides:

  • Packet filtering (firewalling)
  • Network Address Translation (NAT)
  • Packet mangling (modification)
  • Connection tracking
  • Packet logging

Everything else (iptables, nftables, UFW) is a frontend to netfilter.

The Hook Points

Netfilter inserts "hooks" at specific points in the network stack where packets can be intercepted:

                                    ┌─────────────────┐
                                    │  Local Process  │
                                    └────────┬────────┘
                                    ┌────────▼────────┐
                                    │     OUTPUT      │
                                    │    (hook 4)     │
                                    └────────┬────────┘
┌──────────────┐    ┌───────────────┐    ┌───▼───┐    ┌──────────────┐
│  PREROUTING  │───▶│    ROUTING    │    │ROUTING│───▶│ POSTROUTING  │
│   (hook 1)   │    │   DECISION    │    │       │    │   (hook 5)   │
└──────────────┘    └───────┬───────┘    └───────┘    └──────┬───────┘
       │                    │                                 │
       │                    ▼ (forward)                       │
       │            ┌───────────────┐                        │
       │            │    FORWARD    │────────────────────────┘
       │            │   (hook 3)    │
       │            └───────────────┘
       │ (local)
┌──────────────┐
│    INPUT     │
│   (hook 2)   │
└──────┬───────┘
┌──────────────┐
│ Local Process│
└──────────────┘

Hook Descriptions

Hook Number When Triggered
PREROUTING 1 Immediately after packet received, before routing
INPUT 2 Packet is destined for local process
FORWARD 3 Packet is being routed through the host
OUTPUT 4 Packet generated by local process
POSTROUTING 5 Just before packet leaves the interface

Connection Tracking (conntrack)

Netfilter tracks connections to enable stateful firewalling:

Connection States

State Description
NEW First packet of a connection
ESTABLISHED Part of an established connection
RELATED New connection related to existing (e.g., FTP data)
INVALID Packet doesn't match known connection
UNTRACKED Explicitly not tracked

Viewing Connections

# Install conntrack tools
sudo apt install conntrack

# View all tracked connections
sudo conntrack -L

# Count connections
sudo conntrack -C

# View connections in real-time
sudo conntrack -E

# Filter by state
sudo conntrack -L -p tcp --state ESTABLISHED

Connection Tracking Table

# View conntrack table size
cat /proc/sys/net/netfilter/nf_conntrack_max

# View current count
cat /proc/sys/net/netfilter/nf_conntrack_count

# Increase if needed
echo 262144 | sudo tee /proc/sys/net/netfilter/nf_conntrack_max

Example Connection Entry

tcp      6 431999 ESTABLISHED src=192.168.1.100 dst=93.184.216.34 sport=54321 dport=443
    src=93.184.216.34 dst=192.168.1.100 sport=443 dport=54321 [ASSURED] mark=0 use=1

Components: - Protocol (tcp, 6) - TTL (431999 seconds) - State (ESTABLISHED) - Original direction (src/dst/sport/dport) - Reply direction (reversed) - Flags (ASSURED = seen traffic both ways)

NAT Types

Netfilter supports several NAT types:

Source NAT (SNAT)

Changes the source address of outgoing packets:

Internal Host                    NAT Gateway                    External Server
192.168.1.100:54321  ───────▶   [SNAT to 203.0.113.1]  ───────▶  93.184.216.34:443
                     Response tracks back automatically

Used for: - Allowing private IPs to access the internet - Hiding internal network structure

Masquerade

Dynamic SNAT that uses the outgoing interface's address:

# SNAT with static IP
iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to-source 203.0.113.1

# Masquerade (dynamic IP)
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

Destination NAT (DNAT)

Changes the destination address of incoming packets:

External Client                  NAT Gateway                    Internal Server
93.184.216.34:54321  ───────▶   [DNAT to 192.168.1.10]  ──────▶  192.168.1.10:80
                     Response tracks back automatically

Used for: - Port forwarding - Load balancing - Exposing internal services

Redirect

DNAT to the local machine:

# Redirect port 80 to 8080 locally
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 8080

Tables and Chains

Netfilter organizes rules into tables, each with predefined chains:

Tables

Table Purpose Chains
filter Packet filtering INPUT, FORWARD, OUTPUT
nat Address translation PREROUTING, OUTPUT, POSTROUTING
mangle Packet modification All five hooks
raw Connection tracking exceptions PREROUTING, OUTPUT
security SELinux marking INPUT, FORWARD, OUTPUT

Processing Order

When a packet arrives, tables are processed in this order:

PREROUTING:  raw → mangle → nat
INPUT:       mangle → filter → security
FORWARD:     mangle → filter → security
OUTPUT:      raw → mangle → nat → filter → security
POSTROUTING: mangle → nat

Custom Chains

You can create custom chains for organization:

# Create custom chain
iptables -N MY_CHAIN

# Jump to custom chain
iptables -A INPUT -j MY_CHAIN

# Add rules to custom chain
iptables -A MY_CHAIN -p tcp --dport 22 -j ACCEPT

# Return to calling chain
iptables -A MY_CHAIN -j RETURN

Targets

When a rule matches, a target determines what happens:

Terminating Targets

Target Action
ACCEPT Allow packet
DROP Silently discard
REJECT Discard with ICMP error
QUEUE Send to userspace

Non-Terminating Targets

Target Action
LOG Log and continue processing
MARK Set packet mark
CONNMARK Set connection mark
RETURN Return to calling chain

NAT Targets

Target Action
SNAT Change source address
DNAT Change destination address
MASQUERADE Dynamic source NAT
REDIRECT Redirect to local port

Packet Flow Examples

Local Delivery

Packet arrives for local process:

1. NIC receives packet
2. PREROUTING (raw → mangle → nat)
   - DNAT possible here
3. Routing decision: local delivery
4. INPUT (mangle → filter)
   - Filtering happens here
5. Delivered to socket

Forwarding

Packet routed through host:

1. NIC receives packet
2. PREROUTING (raw → mangle → nat)
   - DNAT possible here
3. Routing decision: forward
4. FORWARD (mangle → filter)
   - Filtering happens here
5. POSTROUTING (mangle → nat)
   - SNAT/MASQUERADE here
6. Transmitted out interface

Local Generation

Packet generated by local process:

1. Process creates packet
2. OUTPUT (raw → mangle → nat → filter)
   - All operations possible
3. Routing decision
4. POSTROUTING (mangle → nat)
   - SNAT/MASQUERADE here
5. Transmitted out interface

Connection Tracking Helpers

Some protocols need special handling:

FTP Helper

FTP uses separate control and data connections:

# Load FTP helper
sudo modprobe nf_conntrack_ftp

# View loaded helpers
lsmod | grep nf_conntrack

# Allow related connections
iptables -A INPUT -m conntrack --ctstate RELATED -j ACCEPT

Other Helpers

  • nf_conntrack_tftp - TFTP
  • nf_conntrack_sip - SIP (VoIP)
  • nf_conntrack_h323 - H.323 (VoIP)
  • nf_conntrack_pptp - PPTP VPN

Security Considerations

Helpers can be security risks. In secure environments:

# Disable automatic helper assignment
echo 0 | sudo tee /proc/sys/net/netfilter/nf_conntrack_helper

# Explicitly assign helpers only where needed
iptables -t raw -A PREROUTING -p tcp --dport 21 -j CT --helper ftp

Performance Considerations

Conntrack Table Sizing

# Current size
cat /proc/sys/net/netfilter/nf_conntrack_max

# Memory usage per entry: ~300 bytes
# For 262144 entries: ~75 MB

# Set larger size
echo "net.netfilter.nf_conntrack_max = 524288" >> /etc/sysctl.conf

Hash Table Size

# View hash table size
cat /sys/module/nf_conntrack/parameters/hashsize

# Optimal: nf_conntrack_max / 4
echo 131072 | sudo tee /sys/module/nf_conntrack/parameters/hashsize

Timeouts

# View timeouts
sysctl net.netfilter.nf_conntrack_tcp_timeout_established
# Default: 432000 (5 days)

# Reduce for high-traffic servers
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=86400

Netfilter and Containers

Docker, LXC, and other container runtimes interact with netfilter:

Network Namespaces

Each network namespace has its own netfilter tables:

# View rules in container's namespace
sudo ip netns exec container_ns iptables -L

Bridge Filtering

By default, bridged traffic is filtered:

# Check bridge filtering
cat /proc/sys/net/bridge/bridge-nf-call-iptables

# Disable (for performance, reduces security)
echo 0 | sudo tee /proc/sys/net/bridge/bridge-nf-call-iptables

The Problem

When enabled, packets crossing a bridge are processed by iptables:

  1. Packet enters bridge port
  2. Bridge decides to forward
  3. iptables FORWARD chain processes packet
  4. This is where Docker/UFW conflicts occur

We'll explore this in detail in the Docker sections.