iptables is the user-space command-line interface to the Linux kernel’s Netfilter packet filtering framework, the dominant firewall tool on Linux from its introduction in 2001 until nftables began replacing it in the mid-2010s. Netfilter inserts hook points at five positions in the kernel’s IPv4 (and separately IPv6, via ip6tables) packet processing path: PREROUTING (immediately after a packet arrives, before routing), INPUT (packets destined for the local host), FORWARD (packets being routed through the host), OUTPUT (packets generated by local processes), and POSTROUTING (after routing, before transmission). At each hook point, Netfilter calls into the active tables, each of which contains ordered chains of rules. A rule is a match condition (source IP, destination port, protocol, connection state, interface, packet mark, and many more via match extensions) paired with a target — the action to take if the rule matches: ACCEPT, DROP, REJECT, LOG, MASQUERADE, DNAT, SNAT, or a jump to a user-defined chain. Rules are evaluated in order; the first matching rule’s target is applied and evaluation stops (unless the target is LOG or another non-terminating target). If no rule matches, the chain’s policy (the default target) applies.
The four built-in tables serve distinct purposes. The filter table is the primary firewall: its INPUT, FORWARD, and OUTPUT chains are where ACCEPT and DROP rules live. The nat table handles Network Address Translation in PREROUTING (DNAT — rewriting destination addresses, as in port forwarding), OUTPUT (locally generated NAT), and POSTROUTING (SNAT/MASQUERADE — rewriting source addresses for outbound traffic from a private network). The mangle table modifies packet headers — TTL, TOS/DSCP bits, packet marks via --set-mark — and is present at all five hooks. The raw table, evaluated before connection tracking, is used to exempt specific traffic from Netfilter’s connection tracking with NOTRACK, reducing overhead for high-volume trusted flows. Connection tracking (conntrack) is the stateful inspection layer shared across all tables: it classifies each packet as NEW, ESTABLISHED, RELATED, or INVALID, allowing a single rule like -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT to permit all return traffic for established connections without enumerating every possible source. The iptables-save and iptables-restore commands serialise the complete ruleset to text for persistence and atomic bulk loading, since individual iptables rule commands are slow when applied one at a time to large rulesets.
iptables has two well-known operational limitations that drove the development of nftables. First, performance at scale: each rule is evaluated linearly; a ruleset with thousands of source IP ranges (common in Kubernetes, where kube-proxy historically wrote one rule per Service endpoint) causes measurable packet processing overhead because the kernel walks the list for every packet. In Kubernetes clusters with hundreds of Services and thousands of endpoints, this linear walk was a significant contributor to connection latency, driving the adoption of eBPF-based replacements like Cilium that bypass iptables entirely. Second, atomicity and API fragmentation: rules are added one at a time via individual kernel calls with no transactional semantics; ip6tables, arptables, and ebtables are separate tools with separate rulesets despite all sitting on Netfilter; and the user-space API is unwieldy for programmatic management. Despite these limitations, iptables remains universally present on Linux systems as the iptables-legacy backend or — on modern distributions — as a compatibility shim (iptables-nft) that translates iptables commands into nftables rules under the hood, allowing legacy tooling and Kubernetes components that emit iptables commands to function on systems that have fully transitioned to nftables.
