From ArchWiki
Jump to: navigation, search

Tango-view-fullscreen.pngThis article or section needs expansion.Tango-view-fullscreen.png

Reason: As of October, 2015: while nftables has been around for a while, few people seem to have practical experience using it. The documentation often leaves questions open. If you'd like to pioneer, help out and document how you got it to work. The best place to ask questions is the Netfilter mailing list. (Discuss in Talk:Nftables#)

nftables is a netfilter project that aims to replace the existing ip-, ip6-, arp-, and ebtables framework. It provides a new packet filtering framework, a new user-space utility (nft), and a compatibility layer for ip- and ip6tables. It uses the existing hooks, connection tracking system, user-space queueing component, and logging subsystem of netfilter.

It consists of three main components: a kernel implementation, the libnl netlink communication and the nftables user-space front-end. The kernel provides a netlink configuration interface, as well as run-time rule-set evaluation, libnl contains the low-level functions for communicating with the kernel, and the nftables front-end is what the user interacts with via nft.

You can also visit the official nftables wiki page for more information.


Nftables is supported in the Arch Linux kernel. Userspace utilities are are provided by the package nftables or the git version nftables-gitAUR.

Basic implementation

Like other firewalls, nftables makes a distinction between temporary rules made in the commandline and permanent ones loaded from or saved to a file. The default file is /etc/nftables.conf which already contains a simple ipv4/ipv6 firewall table named "inet filter".

Load the basic default ruleset

To use it start/enable the nftables.service.

You can check the ruleset with

# nft list ruleset

If it returns the inet filter table setup, you're good to go for basic desktop internet usage.

Note: You may have to create /etc/modules-load.d/nftables.conf with all of the nftables related modules you require as entries for the systemd service to work correctly. You can get a list of modules using this command:
$ lsmod | grep '^nf'
Otherwise, you could end up with the dreaded Error: Could not process rule: No such file or directory error.


nftables' user-space utility nft now performs most of the rule-set evaluation before handing rule-sets to the kernel. Because of this, nftables provides no default tables or chains; although, a user can emulate an iptables-like setup.

It works in a fashion similar to ifconfig or iproute2. The commands are a long, structured sequence rather than using argument switches like in iptables. For example:

nft add rule ip6 filter input ip6 saddr ::1 accept

add is the command. rule is a subcommand of add. ip6 is an argument of rule, telling it to use the ip6 family. filter and input are arguments of rule specifying the table and chain to use, respectively. The rest that follows is a rule definition, which includes matches (ip), their parameters (saddr), parameter arguments (::1), and jumps (accept).

The following is an incomplete list of the commands available in nft:

  tables [family]
  table [family] <name>
  chain [family] <table> <name>

  table [family] <name>
  chain [family] <table> <name> [chain definitions]
  rule [family] <table> <chain> <rule definition>

table [family] <name> (shortcut for `add table`)

  rule [family] <table> <chain> <rule definition>

  table [family] <name>
  chain [family] <table> <name>
  rule [family] <table> <handle>

  table [family] <name>
  chain [family] <table> <name>

family is optional, see section on family below.


The purpose of tables is to hold chains. Unlike tables in iptables, there are no built-in tables in nftables. How many tables one uses, or their naming, is largely a matter of style and personal preference. However, each table has a (network) family and only applies to packets of this family. Tables can have one of five families specified, which unifies the various iptables utilities into one:

nftables family iptables utility
ip iptables
ip6 ip6tables
inet iptables and ip6tables
arp arptables
bridge ebtables


ip (i.e. IPv4) is the default family and will be used if family is not specified.

IPv6 is specified as ip6.

To create one rule that applies to both IPv4 and IPv6, use inet. inet allows for the unification of the ip and ip6 families to make defining rules for both easier.

Note: inet does not work for nat-type chains, only for filter-type chains. (source)


You can list the current tables in a family with the nft list command.

# nft list tables
# nft list tables ip6

You can list a full table definition by specifying a table name:

# nft list table foo
# nft list table ip6 foo


Tables can be added via two commands — one just being a shortcut for the other. Here is an example of how to add an ip table called foo and an ip6 table called foo:

# nft add table foo
# nft table ip6 foo

You can have two tables with the same name as long as they are in different families.


Tables can only be deleted if there are no chains in them.

# nft delete table foo
# nft delete table ip6 foo


The purpose of chains is to hold rules. Unlike chains in iptables, there are no built-in chains in nftables. This means that if no chain uses any types or hooks in the netfilter framework, packets that would flow through those chains will not be touched by nftables, unlike iptables.


The nft list table foo command will list all the chains in the foo table. You can also list rules from an individual chain.

# nft list chain foo bar
# nft list chain ip6 foo bar

These commands will list the bar chains in the ip and ip6 foo tables.


Chains can be added when a table is created in a file definition or one at time via the nft add chain command.

# nft add chain foo bar
# nft add chain ip6 foo bar

These commands will add a chain called bar to the ip and ip6 foo tables.


Because nftables has no built-in chains, it allows chains to access certain features of the netfilter framework.

# nft add chain filter input \{ type filter hook input priority 0\; \}

This command tells nftables to add a chain called input to the filter table and defines its type, hook, and priority. These properties essentially replace the built-in tables and chains in iptables.


There are three types a chain can have and they correspond to the tables used in iptables:

  • filter
  • nat
  • route (mangle)

There are six hooks a chain can use and all except ingress correspond to chains used in iptables:

  • ingress
  • input
  • output
  • forward
  • prerouting
  • postrouting

The ingress hook is an alternative to the existing tc utility.

  • Priorities do not currently appear to have any effect on which chain sees packets first.
  • Since the priority seems to be an unsigned integer, negative priorities will be converted into very high priorities.

Priorities tell nftables which chains packets should pass through first. They are integers, and the higher the integer, the higher the priority.


To edit a chain, simply call it by its name and define the rules you want to change.

# nft chain <table> <family> <chain> { [ type <type> hook <hook> device <device> priority <priority> \; policy <policy> \; ] }

If for example, you just want to change the input chain policy of the default table from "accept" to "drop"

# nft chain inet filter input { policy drop \; }


Chains can only be deleted if there are no rules in them.

# nft delete chain foo bar
# nft delete chain ip6 foo bar

These commands delete the bar chains from the ip and ip6 foo tables.


The purpose of rules is to identify packets (match) and carry out tasks (jump). Like in iptables, there are various matches and jumps available, though not all of them are feature-complete in nftables.


You can list the current rules in a table with the nft list command, using the same method as listing a table. You can also list rules from an individual chain.

# nft list chain foo bar
# nft list chain ip6 foo bar

These commands will list the rules in the bar chains in the ip and ip6 foo tables.


Rules can be added when a table is created in a file definition or one at time via the nft add rule command.

# nft add rule foo bar ip saddr accept
# nft add rule ip6 foo bar ip saddr ::1 accept

These commands will add a rule to the bar chains in the ip and ip6 foo tables that matches an ip packet when its saddr (source address) is (IPv4) or ::1 (IPv6) and accepts those packets.


There are various matches available in nftables and, for the most part, coincide with their iptables counterparts. The most noticeable difference is that there are no generic or implicit matches anymore. A generic match was one that was always available, such as --protocol or --source. Implicit matches were protocol-specific, such as --sport when a packet was determined to be TCP.

The following is an incomplete list of the matches available:

  • meta (meta properties, e.g. interfaces)
  • icmp (ICMP protocol)
  • icmpv6 (ICMPv6 protocol)
  • ip (IP protocol)
  • ip6 (IPv6 protocol)
  • tcp (TCP protocol)
  • udp (UDP protocol)
  • sctp (SCTP protocol)
  • ct (connection tracking)

The following is an incomplete list of match arguments (for a more complete list, see nft(8)):

  oif <output interface INDEX>
  iif <input interface INDEX>
  oifname <output interface NAME>
  iifname <input interface NAME>

  (oif and iif accept string arguments and are converted to interface indexes)
  (oifname and iifname are more dynamic, but slower because of string matching)

  type <icmp type>

  type <icmpv6 type>

  protocol <protocol>
  daddr <destination address>
  saddr <source address>

  daddr <destination address>
  saddr <source address>

  dport <destination port>
  sport <source port>

  dport <destination port>
  sport <source port>

  dport <destination port>
  sport <source port>

  state <new | established | related | invalid>


Jumps work the same as they do in iptables, except multiple jumps can now be used in one rule.

# nft add rule filter input tcp dport 22 log accept

The following is an incomplete list of jumps:

  • accept (accept a packet)
  • reject (reject a packet)
  • drop (drop a packet)
  • snat (perform source NAT on a packet)
  • dnat (perform destination NAT on a packet)
  • log (log a packet)
  • counter (keep a counter on a packet; counters are optional in nftables)
  • return (stop traversing the chain)
  • jump <chain> (jump to another chain)
  • goto <chain> (jump to another chain, but do not return)



Rules can be prepended to chains with the nft insert rule command.

# nft insert rule filter input ct state established,related accept

At a given position

Nftables uses handles to define the position of a rule. To get this information, you need to list the ruleset with the -a flag:

 # nft list ruleset -a

To add a rule after another rule with a given handler, you have to type:

 # nft add rule table_name chain_name position handler_number [rule-definition]


Individual rules can only be deleted by their handles. The nft --handle list command must be used to determine rule handles. Note the --handle switch, which tells nft to list handles in its output.

The following determines the handle for a rule and then deletes it. The --number argument is useful for viewing some numeric output, like unresolved IP addresses.

# nft --handle --numeric list chain filter input
table ip fltrTable {
     chain input {
          type filter hook input priority 0;
          ip saddr accept # handle 10
# nft delete rule fltrTable input handle 10

All the chains in a table can be flushed with the nft flush table command. Individual chains can be flushed using either the nft flush chain or nft delete rule commands.

# nft flush table foo
# nft flush chain foo bar
# nft delete rule ip6 foo bar

The first command flushes all of the chains in the ip foo table. The second flushes the bar chain in the ip foo table. The third deletes all of the rules in bar chain in the ip6 foo table.

Atomic reloading

Flush the current ruleset:

# echo "flush ruleset" > /tmp/nftables 

Dump the current ruleset:

# nft list ruleset >> /tmp/nftables

Now you can edit /tmp/nftables and apply your changes with:

# nft -f /tmp/nftables

File definitions

File definitions can be used by the nft -f command, which acts like the iptables-restore command. However, unlike iptables-restore, this command does not flush out your existing ruleset, to do so you have to prepend the flush command.

flush table ip filter
table ip filter {
     chain input {
          type filter hook input priority 0;
          ct state established,related accept
          ip saddr accept
          tcp dport 22 log accept

To export your rules (like iptables-save):

# nft list ruleset

Getting started

The below example shows nft commands to configure a basic IPv4 only firewall. If you want to filter both IPv4 and IPv6 you should look at the other examples in /usr/share/nftables or just start with the default provided in /etc/nftables.conf which already works with IPv4/IPv6.

To get an iptables-like chain set up, you will first need to use the provided IPv4 filter file:

# nft -f /usr/share/nftables/ipv4-filter

To list the resulting chain:

# nft list table filter

Drop output to a destination:

# nft add rule ip filter output ip daddr drop

Drop packets destined for local port 80:

# nft add rule ip filter input tcp dport 80 drop

Delete all rules in a chain:

# nft delete rule filter output


Simple IP/IPv6 firewall

# A simple firewall

flush ruleset

table inet filter {
	chain input {
		type filter hook input priority 0; policy drop;

		# established/related connections
		ct state established,related accept

		# invalid connections
		ct state invalid drop
		# loopback interface
		iif lo accept

		# ICMP
		# routers may also want: mld-listener-query, nd-router-solicit
		ip6 nexthdr icmpv6 icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } accept
		ip protocol icmp icmp type { destination-unreachable, router-advertisement, time-exceeded, parameter-problem } accept

		# SSH (port 22)
		tcp dport ssh accept

		# HTTP (ports 80 & 443)
		tcp dport { http, https } accept

Limit rate IP/IPv6 firewall

table inet filter {
	chain input {
		type filter hook input priority 0; policy drop;

		# no ping floods:
		ip6 nexthdr icmpv6 icmpv6 type echo-request limit rate 10/second accept
		ip protocol icmp icmp type echo-request limit rate 10/second accept

		ct state established,related accept
		ct state invalid drop

		iifname lo accept

		# avoid brute force on ssh:
		tcp dport ssh limit rate 15/minute accept



When using jumps in config file, it is necessary to define the target chain first. Otherwise one could end up with Error: Could not process rule: No such file or directory.

table inet filter {
    chain web {
        tcp dport http accept
        tcp dport 8080 accept
    chain input {
        type filter hook input priority 0;
        ip saddr jump web

Different rules for different interfaces

If your box has more than one network interface, and you'd like to use different rules for different interfaces, you may want to use a "dispatching" filter chain, and then interface-specific filter chains. For example, let's assume your box acts as a home router, you want to run a web server accessible over the LAN (interface nsp3s0), but not from the public internet (interface enp2s0), you may want to consider a structure like this:

table inet filter {
  chain input { # this chain serves as a dispatcher
    type filter hook input priority 0;

    iifname lo accept # always accept loopback
    iifname enp2s0 jump input_enp2s0
    iifname enp3s0 jump input_enp3s0

    reject with icmp type port-unreachable # refuse traffic from all other interfaces
  chain input_enp2s0 { # rules applicable to public interface interface
    ct state {established,related} accept
    ct state invalid drop
    udp dport bootpc accept
    tcp dport bootpc accept
    reject with icmp type port-unreachable # all other traffic
  chain input_enp3s0 {
    ct state {established,related} accept
    ct state invalid drop
    udp dport bootpc accept
    tcp dport bootpc accept
    tcp port http accept
    tcp port https accept
    reject with icmp type port-unreachable # all other traffic
  chain ouput { # we let everything out
    type filter hook output priority 0;

Alternatively you could choose only one iifname statement, such as for the single upstream interface, and put the default rules for all other interfaces in one place, instead of dispatching for each interface.


nftables has a special keyword masquerade "where the source address is automagically set to the address of the output interface" (source). This is particularly useful for situations in which the IP address of the interface is unpredictable or unstable, such as the upstream interface of routers connecting to many ISPs. Without it, the Network Address Translation rules would have to be updated every time the IP address of the interface changed.

To use it:

  • make sure masquerading is enabled in the kernel (true if you use the default kernel), otherwise during kernel configuration, set
  • the masquerade keyword can only be used in chains of type nat, which in turn cannot be contained in a table with family inet. Use a table with family ip and/or ip6 instead.
  • masquerading is a kind of source NAT, so only works in the output path.

Example for a machine with two interfaces: LAN connected to nsp3s0, and public internet connected to enp2s0:

table ip nat {
  chain prerouting {
    type nat hook prerouting priority 0;
  chain postrouting {
    type nat hook postrouting priority 0;
    oifname "enp0s2" masquerade

See also