Linux routing and Linux firewall rules operate on the same packets, but they are not interchangeable. A route selects the next hop and output interface. NAT rewrites an address or port. Filtering accepts or drops traffic. A packet mark can influence a later policy-routing decision, but the mark does not create that route.
Confusing these functions leads to rules that look plausible yet never match, one-way connections after port forwarding, or a multi-WAN policy that marks packets without changing where they go.
The packet path in one view
For traffic passing through a Linux router, the simplified sequence is:
- A packet arrives on an interface.
- Prerouting processing can classify it, mark it, or apply DNAT.
- The routing subsystem selects a local destination or an output interface.
- Input filtering applies to traffic addressed to the router itself; forward filtering applies to routed traffic.
- Postrouting processing can apply SNAT or MASQUERADE before the packet leaves.
- Connection tracking applies the corresponding translation to reply traffic.
Locally generated traffic begins later in the path, so a DNAT rule written only for PREROUTING will not affect a connection created by the router itself. Exact hooks and priorities matter in nftables; the diagram above is a reasoning model, not a replacement for the active ruleset.
Routing belongs to the kernel FIB
The main routing table is inspected and changed with the ip command:
ip route show
ip rule show
ip route get 203.0.113.20
ip route get is particularly useful because it asks the kernel how it would route a specific destination under the current policy. It does not send a packet.
A default route, connected subnet, static route, or policy-routing table determines the next hop. Neither iptables -t nat nor a firewalld masquerade setting creates a route to an internal server.
Packet forwarding must also be enabled when the host is acting as a router. Check before changing it:
sysctl net.ipv4.ip_forward
The Linux kernel’s IP sysctl documentation defines net.ipv4.ip_forward as the switch for forwarding packets between interfaces. Enabling it is necessary for IPv4 routing, but it does not add routes or permit traffic through a restrictive forward policy.
NAT is the category; DNAT and SNAT are directions
Network Address Translation changes addresses and, optionally, ports while connection tracking keeps the reverse mapping for the flow.
DNAT changes the destination
DNAT is typically used before route selection for port forwarding. A packet sent to a router’s public address can be rewritten to an internal server:
public-address:443 -> 192.0.2.40:443
After the rewrite, the routing subsystem must know how to reach 192.0.2.40, and the forward policy must allow the flow. The internal server also needs a return path through the translating router, or the design needs an explicit source translation appropriate to the topology.
SNAT changes the source
SNAT is normally applied after route selection when traffic leaves the router:
192.0.2.40:any-port -> public-static-address:any-port
Use an explicit SNAT address when the egress address is stable and the policy requires that exact identity.
MASQUERADE is dynamic source NAT
MASQUERADE is a form of source NAT that takes the current address of the outgoing interface. It is convenient for DHCP, PPP, or another egress whose address can change.
The official nftables NAT documentation defines masquerade as a special case of SNAT in which the source is set to the output interface’s address. Use explicit SNAT when policy requires a fixed translated address. MASQUERADE is not the generic name for all NAT and does not perform inbound port forwarding.
Filter and mangle are not NAT types
In legacy iptables terminology, filter, nat, and mangle are tables. Targets such as DNAT, SNAT, and MASQUERADE perform translations in the nat table. The filter table makes allow or deny decisions. The mangle table is used for packet alterations and metadata such as marks.
A mark is an internal integer associated with a packet. It is not written into the IP header and does not travel across the network as a routing instruction. A typical policy-routing design has both halves:
iptables -t mangle -A PREROUTING -s 192.0.2.0/24 -j MARK --set-mark 0x1
ip rule add fwmark 0x1 lookup 100
ip route add default via 198.51.100.1 dev wan1 table 100
The first command classifies packets. The ip rule connects mark 0x1 to routing table 100. The route in table 100 provides the next hop. Without the last two pieces, setting the mark alone does not redirect traffic.
This example is illustrative. Interface names, gateways, priorities, reverse-path filtering, IPv6, and connection-mark handling must be designed for the actual network. Multi-WAN systems also need symmetric return routing and a plan for established flows when an uplink fails.
Where iptables fits on a current CentOS or RHEL system
iptables is a user-space command interface to the kernel’s Netfilter subsystem. It is often called a firewall, but the enforcement comes from kernel rules and connection tracking.
On current RHEL-family systems, firewalld is the supported management layer for common host-firewall policy and uses nftables as its backend. The iptables-nft compatibility commands may translate legacy syntax to nftables, but that does not make a hand-maintained iptables ruleset and firewalld two independent firewalls.
Before making changes, determine what owns the policy:
firewall-cmd --state
firewall-cmd --get-active-zones
nft list ruleset
iptables-save
Do not alternately edit firewalld, raw nftables, and compatibility iptables rules without a documented ownership model. A reload from the higher-level manager can replace an ad-hoc lower-level change.
For a new RHEL 9 design, choose either firewalld for managed policy or a deliberately maintained nftables ruleset for advanced requirements. The official firewalld documentation explains its zones, runtime and permanent configurations, and lower-level integration.
A complete port forward needs more than DNAT
When publishing an internal HTTPS service, validate every dependency:
- the public address and inbound interface receive the traffic;
- the DNAT rule matches the correct protocol and port;
- IP forwarding is enabled;
- the forward chain permits the new flow and its replies;
- the router has a route to the server;
- the server returns traffic through the router or a suitable source translation is applied;
- the service listens on the translated address and port;
- upstream routers or cloud security controls permit the connection.
Use counters and packet capture to find where the path stops:
nft list ruleset
ip route get 192.0.2.40
ss -lnt
Capture on both ingress and internal interfaces when authorized. Seeing the SYN arrive on one interface but not leave the other narrows the fault to the router. Seeing it leave with no reply moves the investigation to the server or return path.
Restricting WireGuard by country or an IP allowlist
WireGuard does not have a country selector. Restricting who can send handshakes to its UDP listen port is a firewall decision made before WireGuard authenticates the peer.
A maintained allowlist of known source addresses is usually more deterministic than GeoIP. Country datasets change, mobile and corporate users can egress elsewhere, VPNs bypass geography, and IPv6 requires a separate complete policy. An allowlist also fails when legitimate clients have dynamic addresses, so select the control based on the real access model.
Do not download a text file from a website and immediately replace the production firewall set. A safer update process:
- Fetch over authenticated HTTPS from a controlled source.
- Validate every entry as an IPv4 or IPv6 address or prefix.
- Enforce a maximum entry count and reject an empty result.
- Load a temporary set.
- Atomically swap the validated set into the active rule.
- Preserve the prior set and an out-of-band management path.
- Log update failures without opening access by default.
The firewall limits exposure of the UDP port; WireGuard keys still provide peer authentication. Keep both layers.
Diagnose from observation to change
When NAT or routing fails, use this order:
- Draw the intended source, destination, translation, and return path.
- Inspect addresses, routes, policy rules, and forwarding state.
- Identify whether firewalld, nftables, or a legacy compatibility layer owns the rules.
- Read the active ruleset with counters.
- Test route selection with
ip route get. - Capture an authorized test flow at each interface.
- Change one layer and retest the same flow.
- Make the working policy persistent through its owning configuration system.
Routing decides where a packet goes. DNAT and SNAT change what addresses the endpoints appear to use. MASQUERADE chooses the changing egress address automatically. Filtering decides whether the flow is permitted, and a mark is useful only when another subsystem acts on it. Keeping those responsibilities separate makes the final configuration much easier to prove.