How to Configure IP Forwarding and NAT on Fedora

Enable IP forwarding and configure NAT on Fedora using sysctl and iptables commands to route traffic between networks.

You plugged in a second network and the traffic stops at the gateway

You have a Fedora machine with two network interfaces. One connects to your router or the internet. The other connects to a switch with a few workstations or a lab network. You assign IP addresses, set the default gateway on the workstations to point at Fedora, and expect the workstations to reach the internet. You ping an external address from a workstation. The ping fails. The packets arrive at Fedora and vanish.

The kernel is dropping them by default. Linux treats every interface as a separate security zone. Unless you explicitly tell the kernel to route traffic between interfaces, it discards packets that arrive on one interface and are destined for another. You also need to rewrite the source address of outgoing packets so the return traffic knows where to go. Private IP addresses are not routable on the public internet.

What the kernel is doing with your packets

The Linux kernel has a routing table that determines where packets go. When a packet arrives on the internal interface destined for 8.8.8.8, the kernel checks the routing table. The table says the destination is reachable via the external interface. However, the kernel also checks a policy flag called net.ipv4.ip_forward. If this flag is zero, the kernel drops the packet immediately. This is a security feature. A server should not accidentally become a router and leak traffic between network segments.

Enabling IP forwarding flips the switch. The kernel accepts the packet, looks up the route, and sends it out the external interface.

Forwarding alone is not enough. Your internal devices likely use addresses like 192.168.1.x. When the packet leaves Fedora with a source IP of 192.168.1.50, the upstream router or ISP sees a private address and drops it. Even if the ISP accepted it, the reply would be sent to 192.168.1.50 on the public internet, which is ambiguous and unreachable.

NAT (Network Address Translation) solves this. NAT rewrites the source IP of the outgoing packet to the public IP of Fedora's external interface. The kernel maintains a connection tracking table. It records the mapping: InternalIP:Port maps to ExternalIP:Port. When the reply comes back from the internet, the kernel looks up the table, rewrites the destination IP back to 192.168.1.50, and routes the packet to the internal interface.

Think of NAT like a receptionist. The receptionist takes a call from an internal extension, dials the outside number, and tells the outside party "This is the main office." When the outside party speaks, the receptionist routes the audio back to the correct extension. The outside party never knows the extension number exists.

Check the routing table before blaming the firewall. If the gateway is wrong, NAT won't save you.

Enable forwarding and configure NAT

Fedora uses firewalld to manage firewall rules and NAT. You should use firewalld commands rather than raw iptables. Raw iptables commands can conflict with firewalld and may be wiped on reboot. firewalld persists configuration correctly and integrates with the system.

First, enable IP forwarding. You need to do this for the current session and persist it across reboots.

Here is how to enable IP forwarding immediately and make it survive a reboot.

# Enable IP forwarding for the current session
# This takes effect instantly without a reboot
sudo sysctl -w net.ipv4.ip_forward=1

# Create a persistent configuration file in /etc/sysctl.d/
# Files in this directory are loaded at boot
# The 99- prefix ensures this loads after default settings
sudo sh -c 'echo "net.ipv4.ip_forward=1" > /etc/sysctl.d/99-ip-forward.conf'

Config files in /etc/sysctl.d/ override defaults. Never edit files in /usr/lib/sysctl.d/. Those files ship with packages and will be overwritten on update. Your local changes belong in /etc/.

Next, configure NAT using firewalld. You need to enable masquerading on the zone associated with your external interface. Masquerade is a form of dynamic NAT that automatically uses the current IP address of the interface. This is ideal for interfaces that get their IP via DHCP.

Here is how to identify your zones and enable masquerading on the external zone.

# List active zones to find the zone for your external interface
# The output shows the zone name and the interface name
# Look for the interface connected to the internet
sudo firewall-cmd --get-active-zones

# Enable masquerading on the external zone
# Replace 'public' with the zone name from the previous command
# Masquerade rewrites the source IP to the interface's current IP
sudo firewall-cmd --permanent --zone=public --add-masquerade

# Reload the firewall to apply persistent changes to the runtime
# Skipping this leaves the runtime config stale
sudo firewall-cmd --reload

The --permanent flag writes to the configuration file. The --reload flag applies the configuration to the running firewall. You must run firewall-cmd --reload after every persistent change. Otherwise, the runtime config and the persistent config diverge, and your changes won't work until the next reboot.

Reload the firewall. The persistent config is useless until the runtime catches up.

Verify the configuration

Run these commands to confirm forwarding is active and masquerading is enabled.

# Verify forwarding is active
# Output should be 1
cat /proc/sys/net/ipv4/ip_forward

# Verify masquerade is enabled in the zone
# Replace 'public' with your external zone
# Output should be 'yes'
sudo firewall-cmd --zone=public --query-masquerade

Test traffic from a client on the internal network. Ping an external address. If the ping succeeds, the configuration is working. If the ping fails, check the client's gateway setting. The client must use the Fedora internal IP as its default gateway.

Reboot the system. Persistence is a promise, not a guarantee, until you test the reboot.

Common pitfalls and error messages

Masquerade on the wrong zone. Masquerading must be enabled on the zone used by the external interface. If you enable it on the internal zone, outgoing traffic won't be translated. Check your zones with firewall-cmd --get-active-zones. Ensure the interface facing the internet is in the zone where you added masquerade.

Missing DHCP server. NAT does not provide IP addresses to clients. Your internal devices need an IP address, subnet mask, and default gateway. If you don't have a DHCP server, you must configure static IPs on the clients. The default gateway on the client must point to the Fedora internal interface IP.

iptables conflicts. If you run raw iptables commands while firewalld is active, you may see errors like this:

Error: COMMAND_FAILED: '/usr/sbin/iptables ...' failed: iptables: No chain/target/match by that name.

This error often appears when firewalld is using the nftables backend and you try to manipulate iptables directly. firewalld manages the rules. Use firewall-cmd to add rules. If you must debug with iptables, use iptables -t nat -L -n -v to view rules, but do not modify them with iptables -A.

SELinux denials. SELinux rarely blocks routing or NAT. If firewalld fails to start, check the journal. SELinux denials for firewalld usually indicate a misconfiguration, not a policy block.

# Check firewalld logs if masquerade fails to enable
# The -xe flags add explanation and jump to the end
journalctl -xeu firewalld

Read the journal. The error message usually names the missing chain or the wrong zone.

When to use masquerade versus other routing methods

Use masquerade when your external interface gets its IP via DHCP or the IP changes occasionally.

Use static NAT when your external interface has a fixed public IP and you need predictable port mappings for services.

Use a bridge when you want the Fedora system to act as a transparent switch and not participate in the IP layer.

Use iptables directly only when you are debugging packet flow or writing a custom firewall script that replaces firewalld.

Where to go next