Configuring chains

From nftables wiki
Jump to: navigation, search

As in iptables, you attach your rules to chains. However, contrary to the iptables modus operandi, the nftables infrastructure comes with no predefined chains, so you need to register your base chains in first place before you can add any rule. This allows very flexible configurations.

Adding base chains

The syntax to add base chains is the following:

% nft add chain [<family>] <table-name> <chain-name> { type <type> hook <hook> priority <value> \; [policy <policy>] }

Base chains are those that are registered into the Netfilter hooks, ie. these chains see packets flowing through your Linux TCP/IP stack.

The following example show how you can add new base chains to the foo table through the following command:

% nft add chain ip foo input { type filter hook input priority 0 \; }

Important: You have to escape the semicolon if you are running this command from bash.

This command registers the input chain, that it attached to the input hook so it will see packets that are addressed to the local processes.

The priority is important since it determines the ordering of the chains, thus, if you have several chains in the input hook, you can decide which one sees packets before another.

If you want to use nftables to filter traffic for desktop Linux computers, ie. a computer which does not forward traffic, you can also register the output chain:

% nft add chain ip foo output { type filter hook output priority 0 \; }

Now you are ready to filter incoming (directed to local processes) and outgoing (generated by local processes) traffic.

Important note: If you don't include the chain configuration that is specified enclosed in the curly braces, you are creating a non-base chain that will not see any packets (similar to iptables -N chain-name).

Since nftables 0.5, you can also specify the default policy for base chains as in iptables:

% nft add chain ip foo output { type filter hook output priority 0 \; policy accept\; }

As in iptables, the two possible default policies are accept and drop.

When adding a chain on ingress hook, it is mandatory to specify the device where the chain will be attached:

% nft add chain netdev foo dev0filter { type filter hook ingress device eth0 priority 0 \; }

Base chain types

The possible chain types are:

  • filter, which is obviously used to filter packets. This is supported by the arp, bridge, ip, ip6 and inet table families.
  • route, which is used to reroute packets if any relevant IP header field or the packet mark is modified. If you are familiar with iptables, this chain type provides equivalent semantics to the mangle table but only for the output hook (for other hooks use type filter instead). This is supported by the ip and ip6 table families.
  • nat, which is used to perform Networking Address Translation (NAT). The first packet that belongs to a flow always hits this chain, follow up packets not. Therefore, never use this chain for filtering. This is supported by the ip and ip6 table families.

Base chain hooks

The possible hooks that you can use when you configure your chain are:

  • prerouting: the routing decision for those packets didn't happen yet, so you don't know if they are addressed to the local or remote systems.
  • input: It happens after the routing decision, you can see packets that are directed to the local system and processes running in system.
  • forward: It also happens after the routing decision, you can see packet that are not directed to the local machine.
  • output: to catch packets that are originated from processes in the local machine.
  • postrouting: After the routing decision for packets leaving the local system.
  • ingress (only available at the netdev family): Since Linux kernel 4.2, you can filter traffic way before prerouting, after the packet is passed up from the NIC driver. So you have an alternative to tc.

Base chain priority

The priority can be used to order the chains or to put them before or after some Netfilter internal operations. For example, a chain on the prerouting hook with the priority -300 will be placed before connection tracking operations.

For reference, here's the list of different priority used in iptables:

  • NF_IP_PRI_CONNTRACK_DEFRAG (-400): priority of defragmentation
  • NF_IP_PRI_RAW (-300): traditional priority of the raw table placed before connection tracking operation
  • NF_IP_PRI_SELINUX_FIRST (-225): SELinux operations
  • NF_IP_PRI_CONNTRACK (-200): Connection tracking operations
  • NF_IP_PRI_MANGLE (-150): mangle operation
  • NF_IP_PRI_NAT_DST (-100): destination NAT
  • NF_IP_PRI_FILTER (0): filtering operation, the filter table
  • NF_IP_PRI_SECURITY (50): Place of security table where secmark can be set for example
  • NF_IP_PRI_NAT_SRC (100): source NAT
  • NF_IP_PRI_SELINUX_LAST (225): SELinux at packet exit
  • NF_IP_PRI_CONNTRACK_HELPER (300): connection tracking at exit


NOTE: if a packet gets accepted/dropped and there is a later chain in the same hook which is ordered with a later priority, the packet will be evaluated again. This is, the packet will traverse all the chains in a given hook.

Example ruleset of this behavior:

table inet filter {
        # this chain is evaluated first due to priority
        chain ssh {
                type filter hook input priority 0; policy accept;
                # ssh packet accepted
                tcp dport ssh accept
        }

        # this chain is evaluated last due to priority
        chain input {
                type filter hook input priority 1; policy drop;
                # the same ssh packet is dropped here by means of default policy
        }
}

Base chain policy

This is the default verdict that will be applied to packets reaching the end of the chain (i.e, no more rules to be evaluated against). Currently there are 2 policies: accept or drop.

  • The accept verdict means that the packet will keep traversing the network stack.
  • The drop verdict means that the packet is discarded when this hook traversal is ended if no other verdict is applied later on (for example, in a higher priority chain in the same hook).

Adding non-base chains

You can also create non-base chains as in iptables via:

% nft add chain ip foo test

Note that this chain does not see any traffic as it is not attached to any hook, but it can be very useful to arrange your rule-set in a tree of chains by using the jump to chain action.

Deleting chains

You can delete the chains that you don't need, eg.

% nft delete chain ip foo input

The only condition is that the chain you want to delete needs to be empty, otherwise the kernel will tell you that such chain is in used.

% nft delete chain ip foo input
<cmdline>:1:1-28: Error: Could not delete chain: Device or resource busy
delete chain ip foo input
^^^^^^^^^^^^^^^^^^^^^^^^^

You will have to flush the ruleset in that chain before you can remove the chain.

Flushing chain

You can also flush the content of a chain. If you want to flush all the rule in the chain input of the foo table, you have to type:

nft flush chain foo input

Example configuration: Filtering traffic for your standalone computer

You can create a table with two base chains to define rule to filter traffic coming to and leaving from your computer, asumming IPv4 connectivity:

% nft add table ip filter
% nft add chain ip filter input { type filter hook input priority 0 \; }
% nft add chain ip filter output { type filter hook output priority 0 \; }

Now, you can start attaching rules to these two base chains. Note that you don't need the forward chain in this case since this example assumes that you're configuring nftables to filter traffic for a standalone computer that doesn't behave as router.