Configuring chains: Difference between revisions

From nftables wiki
Jump to navigation Jump to search
m (Change "package" to "packet")
m (Remove a superfluous newline)
 
(47 intermediate revisions by 5 users not shown)
Line 1: Line 1:
As in ''iptables'', you attach your [[Simple rule management|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.
As in ''iptables'', with ''nftables'' you attach your [[Simple rule management|rules]] to chains. Unlike in ''iptables'', there are no predefined chains like INPUT, OUTPUT, etc. Instead, to filter packets at a particular processing step, you explicitly create a '''base chain''' with name of your choosing, and attach it to the appropriate [[Netfilter hooks | Netfilter hook]]. This allows very flexible configurations without slowing Netfilter down with built-in chains not needed by your ruleset.


= Adding base chains =
= Syntactic conventions =
 
Naturally, in order to make use of ''nftables'' commands and directives, it is necessary to become familiar with them, along with their supported grammar. Even with that knowledge, users sometimes experience difficulties where passing ''nftables'' commands as arguments to the nft(8) utility. That's because shells recognise certain characters as metacharacters and treat them in a special way unless they are quoted.
 
Throughout this article, examples that begin with the hash character (U+23) can be taken as examples of valid shell code. That is, they are syntactically valid if entered into any implementation of the Shell Command Language, including bash. By contrast, examples that do not begin with the hash character can be interpreted as either synopses of ''nftables'' grammar, similar in style to the nft(8) man page, or as examples of complete rulesets, if so indicated.
 
As such, examples of shell code shall generally be of the following form.
 
<source>
# nft 'nftables commands go here'
</source>


The syntax to add base chains is the following:
Note that enclosing the ''nftables'' commands within single quotes is a straightforward way of preventing the shell from interpreting characters as metacharacters, most notably the semicolon. Rather, the shell will consider all that is between the pair of single quotes as a single word before passing it on to the nft(8) utility as a single argument, verbatim. Another way to prevent the shell from attempting to parse metacharacters is to run nft(8) in its interactive mode.


<source lang="bash">
<source>
% nft add chain [<family>] <table-name> <chain-name> { type <type> hook <hook> priority <value> \; [policy <policy>] }
# nft -i
</source>
</source>


Base chains are those that are registered into the [[Netfilter hooks]], ie. these chains see packets flowing through your Linux TCP/IP stack.
In that case, nft(8) will directly interpret any further input given until such time as it is either interrupted, encounters the end-of-file (EOF) condition, or encounters the "quit" command. In most terminal emulators, EOF can be conveyed with the Ctrl+D key combination.
 
Examples describing ''nftables'' grammar shall employ square brackets (U+5B, U+5D) to denote optional components of syntax, and angle brackets (U+3C, U+3D) to denote user-specified values.
 
= Adding base chains =


The following example show how you can add new base chains to the ''foo'' table through the following command:
Base chains are those that are registered into the [[Netfilter hooks]], i.e. these chains see packets flowing through your Linux TCP/IP stack. The syntax for adding base chains is as follows.


<source lang="bash">
<source>
% nft add chain ip foo input { type filter hook input priority 0 \; }  
add chain [<family>] <table_name> <chain_name> { type <type> hook <hook> [device <device>] priority <priority> ; [policy <policy> ;] [comment <comment> ;] }
</source>
</source>


'''Important''': You have to escape the semicolon if you are running this command from ''bash''.
The following example shows how to add a new base chain ''input'' to the ''filter'' table (which must have been previously created):
 
<source>
# nft 'add chain ip filter input { type filter hook input priority 0; }'
</source>


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 ''add chain'' 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.
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. For example, input chains with priorities -12, -1, 0, 10 would be consulted exactly in that order. It's possible to give two base chains the same priority, but there is no guaranteed evaluation order of base chains with identical priority that are attached to the same hook location.


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:
If you want to use ''nftables'' to filter traffic for desktop Linux computers, i.e. a computer which does not forward traffic, you can also register the output chain:


<source lang="bash">
<source>
% nft add chain ip foo output { type filter hook output priority 0 \; }  
# nft 'add chain ip filter output { type filter hook output priority 0; }'
</source>
</source>


Now you are ready to filter incoming (directed to local processes) and outgoing (generated by local processes) traffic.
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'').
'''Important note''': If you don't include the chain configuration that is specified enclosed in the curly braces, you are creating a regular 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'':
Since nftables 0.5, you can also specify the default policy for base chains as in ''iptables'':


<source lang="bash">
<source>
% nft add chain ip foo output { type filter hook output priority 0 \; policy accept\; }  
# nft 'add chain ip filter output { type filter hook output priority 0; policy accept; }'
</source>
</source>


Line 42: Line 60:


When adding a chain on '''ingress''' hook, it is mandatory to specify the device where the chain will be attached:  
When adding a chain on '''ingress''' hook, it is mandatory to specify the device where the chain will be attached:  
<source lang="bash">
<source>
% nft add chain netdev foo dev0filter { type filter hook ingress device eth0 priority 0 \; }  
# nft 'add chain netdev filter eth0_filter { type filter hook ingress device eth0 priority 0; }'
</source>
</source>


Line 50: Line 68:
The possible chain types are:
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.
* '''filter''', which is 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.
* '''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, ip6 and inet 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.
* '''nat''', which is used to perform Networking Address Translation (NAT). Only the first packet of a given flow hits this chain; subsequent packets bypass it. Therefore, never use this chain for filtering. The ''nat'' chain type is supported by the ip, ip6 and inet table families.


== Base chain hooks ==
== Base chain hooks ==


The possible hooks that you can use when you configure your chain are:
The possible [[Netfilter_hooks | '''hooks''']] that you can use when you configure your base 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.
* '''ingress''' (only in ''netdev'' family since Linux kernel 4.2, and ''inet'' family since Linux kernel 5.10): sees packets immediately after they are passed up from the NIC driver, before even prerouting. So you have an alternative to ''tc''.
* '''input''': It happens after the routing decision, you can see packets that are directed to the local system and processes running in system.
* '''prerouting''': sees all incoming packets, before any routing decision has been made. Packets may be addressed to the local or remote systems.
* '''forward''': It also happens after the routing decision, you can see packet that are not directed to the local machine.
* '''input''': sees incoming packets that are addressed to and have now been routed to the local system and processes running there.
* '''output''': to catch packets that are originated from processes in the local machine.
* '''forward''': sees incoming packets that are not addressed to the local system.
* '''postrouting''': After the routing decision for packets leaving the local system.
* '''output''': sees packets that originated from processes in the local machine.
* '''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''.
* '''postrouting''': sees all packets after routing, just before they leave the local system.


== Base chain priority ==
== 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.  
Each nftables base chain is assigned a [[Netfilter_hooks#Priority_within_hook|'''priority''']] that defines its ordering among other base chains, flowtables, and Netfilter internal operations at the same hook. For example, a chain on the ''prerouting'' hook with priority ''-300'' will be placed before connection tracking operations.


For reference, here's the list of different priority used in iptables:
'''NOTE''': If a packet is accepted and there is another chain, bearing the same hook type and with a later priority, then the packet will subsequently traverse this other chain. Hence, an accept verdict - be it by way of a rule or the default chain policy - isn't necessarily final. However, the same is ''not'' true of packets that are subjected to a drop verdict. Instead, drops take immediate effect, with no further rules or chains being evaluated.


* NF_IP_PRI_CONNTRACK_DEFRAG (-400): priority of defragmentation
The following ruleset demonstrates this potentially surprising distinction in behaviour:
* 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


<pre>
table ip filter {
        # This chain is evaluated first due to priority
        chain services {
                type filter hook input priority 0; policy accept;


'''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'''.
                # If matched, this rule will prevent any further evaluation
This is, the packet will traverse all the chains in a given hook.
                tcp dport http drop


Example ruleset of this behavior:
                # If matched, and despite the accept verdict, the packet proceeds to enter the chain below
                tcp dport ssh accept


<pre>
                # Likewise for any packets that get this far and hit the default policy
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
         # This chain is evaluated last due to priority
         chain input {
         chain input {
                 type filter hook input priority 1; policy drop;
                 type filter hook input priority 1; policy drop;
                 # the same ssh packet is dropped here by means of default policy
                 # All ingress packets end up being dropped here!
         }
         }
}
}
</pre>
</pre>
If the priority of the 'input' chain above were to be changed to -1, the only difference would be that no packets have the opportunity to enter the 'services' chain. Either way, this ruleset will result in all ingress packets being dropped.
In summary, packets will traverse all of the chains within the scope of a given hook until they are either dropped or no more base chains exist. An accept verdict is only guaranteed to be final in the case that there is no later chain bearing the same type of hook as the chain that the packet originally entered.
Netfilter's hook execution mechanism is described in more detail in [http://people.netfilter.org/pablo/docs/login.pdf Pablo's paper on connection tracking].


== Base chain 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).
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.
Currently there are 2 policies: '''accept''' (default) or '''drop'''.
* 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).
 
* The ''accept'' verdict means that the packet will keep traversing the network stack (default).
* The ''drop'' verdict means that the packet is discarded if the packet reaches the end of the base chain.
 
'''NOTE''': If no policy is explicitly selected, the default policy '''accept''' will be used.


= Adding non-base chains =
= Adding regular chains =


You can also create non-base chains as in ''iptables'' via:
You can also create regular chains, analogous to ''iptables'' user-defined chains, the syntax for which is as follows.


<source lang="bash">
<source>
% nft add chain ip foo test
add chain [family] <table_name> <chain_name> [comment <comment>]
</source>
</source>


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 [[jumping to chain|jump to chain]] action.
The chain name is an arbitrary string, with arbitrary case.
 
Note that no ''hook'' keyword is included when adding a regular chain. Because it is not attached to a Netfilter hook, '''by itself a regular chain does not see any traffic'''. But one or more base chains can include rules that [[jumping to chain|jump]] or goto this chain -- following which, the regular chain processes packets in exactly the same way as the calling base chain. It can be very useful to arrange your ruleset into a tree of base and regular chains by using the [[jumping to chain|jump]] and/or goto actions. (Though we're getting a bit ahead of ourselves, nftables [[Verdict_Maps_(vmaps)|vmaps]] provide an even more powerful way to construct highly-efficient branched rulesets.)


= Deleting chains =
= Deleting chains =


You can delete the chains that you don't need, eg.
The syntax for deleting chains is as follows.


<source lang="bash">
<source>
% nft delete chain ip foo input
delete chain [family] <table_name> <chain_name>
</source>
</source>


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.
The only condition is that the chain you want to delete needs to be empty, otherwise the kernel will complain that the chain is still in use.


<source lang="bash">
<source>
% nft delete chain ip foo input
# nft 'delete chain ip filter input'
<cmdline>:1:1-28: Error: Could not delete chain: Device or resource busy
<cmdline>:1:1-28: Error: Could not delete chain: Device or resource busy
delete chain ip foo input
delete chain ip filter input
^^^^^^^^^^^^^^^^^^^^^^^^^
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
</source>
</source>


You will have to [[Simple rule management|flush the ruleset]] in that chain before you can remove the chain.
You will have to [[Simple rule management|flush the ruleset]] in that chain before you can remove the chain.


= Flushing chain =
= Flushing chains =


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:
To flush a chain means to delete all of the rules that it contains, if any. The syntax for doing so is as follows.


<source lang="bash">
<source>
nft flush chain foo input
flush chain [family] <table_name> <chain_name>
</source>
</source>


Line 155: Line 174:
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:
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:


<source lang="bash">
<source>
% nft add table ip filter
# nft 'add table ip filter'
% nft add chain ip filter input { type filter hook input priority 0 \; }
# nft 'add chain ip filter input { type filter hook input priority 0; }'
% nft add chain ip filter output { type filter hook output priority 0 \; }
# nft 'add chain ip filter output { type filter hook output priority 0; }'
</source>
</source>


Now, you can start attaching [[Simple rule management|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.
Now, you can start attaching [[Simple rule management|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.
Here is the exact same policy, only shown in the declarative style. That is, in the same format as would be employed upon issuing the "list ruleset" command. Despite the marked difference in style, it constitutes valid ''nftables'' syntax and is equivalent to the combination of "add table" and "add chain" commands shown above.
<source>
table ip filter {
chain input {
type filter hook input priority filter; policy accept;
}
chain output {
type filter hook output priority filter; policy accept;
}
}
</source>

Latest revision as of 23:35, 8 October 2024

As in iptables, with nftables you attach your rules to chains. Unlike in iptables, there are no predefined chains like INPUT, OUTPUT, etc. Instead, to filter packets at a particular processing step, you explicitly create a base chain with name of your choosing, and attach it to the appropriate Netfilter hook. This allows very flexible configurations without slowing Netfilter down with built-in chains not needed by your ruleset.

Syntactic conventions

Naturally, in order to make use of nftables commands and directives, it is necessary to become familiar with them, along with their supported grammar. Even with that knowledge, users sometimes experience difficulties where passing nftables commands as arguments to the nft(8) utility. That's because shells recognise certain characters as metacharacters and treat them in a special way unless they are quoted.

Throughout this article, examples that begin with the hash character (U+23) can be taken as examples of valid shell code. That is, they are syntactically valid if entered into any implementation of the Shell Command Language, including bash. By contrast, examples that do not begin with the hash character can be interpreted as either synopses of nftables grammar, similar in style to the nft(8) man page, or as examples of complete rulesets, if so indicated.

As such, examples of shell code shall generally be of the following form.

# nft 'nftables commands go here'

Note that enclosing the nftables commands within single quotes is a straightforward way of preventing the shell from interpreting characters as metacharacters, most notably the semicolon. Rather, the shell will consider all that is between the pair of single quotes as a single word before passing it on to the nft(8) utility as a single argument, verbatim. Another way to prevent the shell from attempting to parse metacharacters is to run nft(8) in its interactive mode.

# nft -i

In that case, nft(8) will directly interpret any further input given until such time as it is either interrupted, encounters the end-of-file (EOF) condition, or encounters the "quit" command. In most terminal emulators, EOF can be conveyed with the Ctrl+D key combination.

Examples describing nftables grammar shall employ square brackets (U+5B, U+5D) to denote optional components of syntax, and angle brackets (U+3C, U+3D) to denote user-specified values.

Adding base chains

Base chains are those that are registered into the Netfilter hooks, i.e. these chains see packets flowing through your Linux TCP/IP stack. The syntax for adding base chains is as follows.

add chain [<family>] <table_name> <chain_name> { type <type> hook <hook> [device <device>] priority <priority> ; [policy <policy> ;] [comment <comment> ;] }

The following example shows how to add a new base chain input to the filter table (which must have been previously created):

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

The add chain 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. For example, input chains with priorities -12, -1, 0, 10 would be consulted exactly in that order. It's possible to give two base chains the same priority, but there is no guaranteed evaluation order of base chains with identical priority that are attached to the same hook location.

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

# nft 'add chain ip filter 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 regular 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 filter 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 filter eth0_filter { type filter hook ingress device eth0 priority 0; }'

Base chain types

The possible chain types are:

  • filter, which is 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, ip6 and inet table families.
  • nat, which is used to perform Networking Address Translation (NAT). Only the first packet of a given flow hits this chain; subsequent packets bypass it. Therefore, never use this chain for filtering. The nat chain type is supported by the ip, ip6 and inet table families.

Base chain hooks

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

  • ingress (only in netdev family since Linux kernel 4.2, and inet family since Linux kernel 5.10): sees packets immediately after they are passed up from the NIC driver, before even prerouting. So you have an alternative to tc.
  • prerouting: sees all incoming packets, before any routing decision has been made. Packets may be addressed to the local or remote systems.
  • input: sees incoming packets that are addressed to and have now been routed to the local system and processes running there.
  • forward: sees incoming packets that are not addressed to the local system.
  • output: sees packets that originated from processes in the local machine.
  • postrouting: sees all packets after routing, just before they leave the local system.

Base chain priority

Each nftables base chain is assigned a priority that defines its ordering among other base chains, flowtables, and Netfilter internal operations at the same hook. For example, a chain on the prerouting hook with priority -300 will be placed before connection tracking operations.

NOTE: If a packet is accepted and there is another chain, bearing the same hook type and with a later priority, then the packet will subsequently traverse this other chain. Hence, an accept verdict - be it by way of a rule or the default chain policy - isn't necessarily final. However, the same is not true of packets that are subjected to a drop verdict. Instead, drops take immediate effect, with no further rules or chains being evaluated.

The following ruleset demonstrates this potentially surprising distinction in behaviour:

table ip filter {
        # This chain is evaluated first due to priority
        chain services {
                type filter hook input priority 0; policy accept;

                # If matched, this rule will prevent any further evaluation
                tcp dport http drop

                # If matched, and despite the accept verdict, the packet proceeds to enter the chain below
                tcp dport ssh accept

                # Likewise for any packets that get this far and hit the default policy
        }

        # This chain is evaluated last due to priority
        chain input {
                type filter hook input priority 1; policy drop;
                # All ingress packets end up being dropped here!
        }
}

If the priority of the 'input' chain above were to be changed to -1, the only difference would be that no packets have the opportunity to enter the 'services' chain. Either way, this ruleset will result in all ingress packets being dropped.

In summary, packets will traverse all of the chains within the scope of a given hook until they are either dropped or no more base chains exist. An accept verdict is only guaranteed to be final in the case that there is no later chain bearing the same type of hook as the chain that the packet originally entered.

Netfilter's hook execution mechanism is described in more detail in Pablo's paper on connection tracking.

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 (default) or drop.

  • The accept verdict means that the packet will keep traversing the network stack (default).
  • The drop verdict means that the packet is discarded if the packet reaches the end of the base chain.

NOTE: If no policy is explicitly selected, the default policy accept will be used.

Adding regular chains

You can also create regular chains, analogous to iptables user-defined chains, the syntax for which is as follows.

add chain [family] <table_name> <chain_name> [comment <comment>]

The chain name is an arbitrary string, with arbitrary case.

Note that no hook keyword is included when adding a regular chain. Because it is not attached to a Netfilter hook, by itself a regular chain does not see any traffic. But one or more base chains can include rules that jump or goto this chain -- following which, the regular chain processes packets in exactly the same way as the calling base chain. It can be very useful to arrange your ruleset into a tree of base and regular chains by using the jump and/or goto actions. (Though we're getting a bit ahead of ourselves, nftables vmaps provide an even more powerful way to construct highly-efficient branched rulesets.)

Deleting chains

The syntax for deleting chains is as follows.

delete chain [family] <table_name> <chain_name>

The only condition is that the chain you want to delete needs to be empty, otherwise the kernel will complain that the chain is still in use.

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

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

Flushing chains

To flush a chain means to delete all of the rules that it contains, if any. The syntax for doing so is as follows.

flush chain [family] <table_name> <chain_name>

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.

Here is the exact same policy, only shown in the declarative style. That is, in the same format as would be employed upon issuing the "list ruleset" command. Despite the marked difference in style, it constitutes valid nftables syntax and is equivalent to the combination of "add table" and "add chain" commands shown above.

table ip filter {
	chain input {
		type filter hook input priority filter; policy accept;
	}

	chain output {
		type filter hook output priority filter; policy accept;
	}
}