IPCHAINS HOWTO: from ipchains and iptables to nftables

Updated November 2026

ipchains was the packet filter of the Linux 2.2 kernel. No current kernel or distribution has it. Its job passed to iptables in Linux 2.4 and then to nftables, which every current distribution uses. If you still have an ipchains or iptables script, this page shows how the old rules map to nftables and how to move them over without breaking access to the machine.

Four generations of Linux firewalls

Linux packet filter tools
ToolKernelsStatus as of November 2026
ipfwadm2.0Historic
ipchains2.1.102 and later, the 2.2 seriesHistoric; removed from current kernels
iptables (and ip6tables, arptables, ebtables)2.4 and laterStill maintained (iptables 1.8.13 as of November 2026); usually runs as iptables-nft on top of nftables
nftables (nft)3.13 and laterThe current framework (nftables 1.1.7 as of November 2026)

There is no tool in current distributions that reads ipchains syntax. The practical path is to rewrite ipchains rules by hand into iptables form (the table below helps), then let the official translator turn iptables rules into nftables. Or skip the middle step and write the nftables rules directly; for a small rule set that is often quicker.

What changed between the generations

Most ipchains rules have a direct equivalent, but one change in meaning catches people out. In ipchains, the input chain saw every packet coming in, including packets that would be forwarded to another machine. From iptables on, INPUT only sees packets addressed to the machine itself, and forwarded packets only pass through FORWARD. nftables keeps that model: its input and forward hooks are separate.

The same rule in three syntaxes
Taskipchains (2.2)iptablesnftables
Default deny-P input DENY-P INPUT DROPpolicy drop; in the chain declaration
Chain nameslower case: input, forward, outputupper case: INPUT, FORWARD, OUTPUTyou choose the names; the hook decides where they run
Port matchport after the address: -d 0/0 22-p tcp --dport 22tcp dport 22
New TCP connections-y--syntcp flags syn / fin,syn,rst,ack or ct state new
Logging-l on a rule-j LOGlog prefix "..."
Masquerading-A forward ... -j MASQ-t nat -A POSTROUTING ... -j MASQUERADEmasquerade in a type nat hook postrouting chain
Transparent proxy-j REDIRECT 3128-t nat -A PREROUTING ... -j REDIRECT --to-ports 3128redirect to :3128
IPv6not supportedseparate ip6tables rulesone inet table covers IPv4 and IPv6

In iptables, interface matches also became stricter: in the filter table, -i (incoming interface) works only in INPUT and FORWARD, and -o (outgoing) only in FORWARD and OUTPUT (in the nat table, PREROUTING takes -i and POSTROUTING takes -o). In nftables the same idea is iifname and oifname.

Step 1: check what you have

iptables -V
sudo iptables-save
sudo nft list ruleset

iptables -V tells you which variant the iptables command is. (nf_tables) means it is iptables-nft: it accepts the old syntax but stores the rules in nftables, so nft list ruleset shows them as tables named ip filter, ip nat and so on. (legacy) means the old in-kernel xtables code. Debian switches between the two with update-alternatives --set iptables /usr/sbin/iptables-nft (or -legacy).

Do not mix the legacy tools with nftables on the same machine. Rules from both are active at the same time, and the result is hard to predict.

Step 2: translate the rules

The iptables package includes two translators. They only print text; they do not change the running firewall.

One command at a time:

$ iptables-translate -A INPUT -p tcp --dport 22 -j ACCEPT
nft 'add rule ip filter INPUT tcp dport 22 counter accept'

$ iptables-translate -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j MASQUERADE
nft 'add rule ip nat POSTROUTING oifname "eth0" ip saddr 192.168.1.0/24 counter masquerade'

A whole saved rule set, including chain policies:

sudo iptables-save > save.txt
iptables-restore-translate -f save.txt > ruleset.nft
sudo ip6tables-save > save6.txt
ip6tables-restore-translate -f save6.txt > ruleset6.nft

For a small IPv4 rule set, the output looks like this (the date comments are left out):

add table ip filter
add chain ip filter INPUT { type filter hook input priority 0; policy drop; }
add chain ip filter FORWARD { type filter hook forward priority 0; policy drop; }
add chain ip filter OUTPUT { type filter hook output priority 0; policy accept; }
add rule ip filter INPUT iifname "lo" counter accept
add rule ip filter INPUT ct state related,established counter accept
add rule ip filter INPUT tcp dport 22 counter accept
add rule ip filter INPUT ip protocol icmp counter accept

Read the output before you load it

Step 3: tidy up into native nftables

A literal translation keeps the old structure: separate IPv4 and IPv6 tables, one rule per port. Once it works, rewrite it the nftables way. The translated rules above become:

#!/usr/sbin/nft -f
flush ruleset

table inet filter {
    chain input {
        type filter hook input priority filter; policy drop;
        ct state established,related accept
        ct state invalid drop
        iifname "lo" accept
        meta l4proto { icmp, ipv6-icmp } accept
        tcp dport 22 accept
    }
    chain forward {
        type filter hook forward priority filter; policy drop;
    }
}

One inet table now covers IPv4 and IPv6, and a set like tcp dport { 22, 80, 443 } replaces a list of separate rules (iptables needed the multiport match for that). Note the added ICMPv6 rule: an old ip6tables set that dropped all ICMPv6 breaks IPv6 neighbor discovery.

Step 4: switch over safely

  1. Set a rollback timer if you are working over SSH (see avoiding lock-out).
  2. Check the new file: sudo nft -c -f ruleset.nft.
  3. Stop and disable whatever loaded the old rules at boot (for example a netfilter-persistent service on Debian or Ubuntu, iptables.service on Fedora or RHEL, or a script in /etc/rc.local).
  4. Load the new rules with sudo nft -f ruleset.nft, then test a new SSH login and each service.
  5. Install the file where the nftables service loads it at boot (/etc/nftables.conf on Debian, an include in /etc/sysconfig/nftables.conf on Fedora and RHEL) and enable the service.
  6. Reboot once in a planned window and check sudo nft list ruleset.

Programs that still speak iptables. Docker, some VPN scripts and older fail2ban setups add their own iptables rules. With iptables-nft those land in nftables tables of their own and keep working next to yours, as long as your file does not start with flush ruleset. On such hosts, replace only your own table, as shown in the Firewall HOWTO.

Translating old masquerading

The classic ipchains “three-line masquerade” turned on forwarding, set the forward policy to deny, and masqueraded the LAN. Today the same job takes a NAT chain plus a forward chain that allows LAN traffic out; the full, tested setup is in the IP Masquerade HOWTO. The old ipmasqadm port forwarding becomes a dnat to rule in a type nat hook prerouting chain.

The classic 2000 version of the IPCHAINS HOWTO, with the full ipchains syntax, is kept by the LDP at tldp.org.

Sources

The translator output on this page was produced with iptables 1.8.11 (Debian 13) and the rule sets were test-loaded with nftables 1.1.3.