* [PATCH nft v6 1/3] doc: add overall description of the ruleset evaluation
2025-10-28 14:54 [PATCH nft v6 0/3] doc: miscellaneous improvements Florian Westphal
@ 2025-10-28 14:54 ` Florian Westphal
2025-10-28 14:54 ` [PATCH nft v6 2/3] doc: fix/improve documentation of verdicts Florian Westphal
` (2 subsequent siblings)
3 siblings, 0 replies; 8+ messages in thread
From: Florian Westphal @ 2025-10-28 14:54 UTC (permalink / raw)
To: netfilter-devel; +Cc: mail, Florian Westphal
From: Christoph Anton Mitterer <mail@christoph.anton.mitterer.name>
Signed-off-by: Christoph Anton Mitterer <mail@christoph.anton.mitterer.name>
Signed-off-by: Florian Westphal <fw@strlen.de>
---
doc/nft.txt | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 91 insertions(+)
diff --git a/doc/nft.txt b/doc/nft.txt
index b4c889afb353..d30481677c4d 100644
--- a/doc/nft.txt
+++ b/doc/nft.txt
@@ -572,6 +572,97 @@ table inet filter {
nft delete rule inet filter input handle 5
-------------------------
+OVERALL EVALUATION OF THE RULESET
+---------------------------------
+This is a summary of how the ruleset is evaluated.
+
+* Even if a packet is accepted by the ruleset it may
+ still get discarded by other means, for example Linux generally ignores
+ various ICMP types and there are sysctl options like
+ `net.ipv{4,6}.conf.*.forwarding` or `net.ipv4.conf.*.rp_filter`.
+* Tables are merely a concept of nftables to structure the ruleset.
+ They are not relevant to the evaluation of the ruleset.
+* Packets traverse the network stack and at various hooks (see
+ <<ADDRESS_FAMILIES>> above for lists of hooks per address family) they’re
+ evaluated by any base chains attached to these hooks.
+* Base chains may call regular chains (via *jump* and *goto*).
+ Evaluation continues in the called chain. Regular chains can call
+ other regular chains.
+ Chains residing in a different table cannot be called.
+* For each hook, the attached base chains are evaluated in order of their
+ priorities.
+ Chains with lower priority values are evaluated before those with higher ones.
+ The order of chains with the same priority value is undefined.
+* An *accept* verdict (including an implict one via the base chain’s policy)
+ ends the evaluation of the current base chain.
+ It is not relevant if the *accept* verdict is issued in the base chain itself
+ or a regular chain called from the base chain.
+ The packet advances to the next base chain.
+ Thus a packet is ultimately accepted if and only if no (matching) rule or base
+ chain policy issues a *drop* verdict.
+ All this applies to verdict-like statements that imply *accept*,
+ for example the NAT statements.
+* A *drop* verdict (including an implict one via the base chain’s policy)
+ immediately ends the evaluation of the whole ruleset.
+ No further chains of any hook are consulted.
+ It is therefore not possible to have a *drop*
+ verdict changed to an *accept* in a later chain.
+ Thus, if any base chain uses drop as its policy, the same base chain (or a
+ regular chain directly or indirectly called by it) must accept a packet or
+ all traffic will be blocked.
+ This also applies to other terminal statements that imply *drop*,
+ for example *reject*.
+* Given the semantics of *accept*/*drop* and only with respect to the utlimate
+ decision of whether a packet is accepted or dropped, the ordering of the
+ various base chains per hook via their priorities matters only in so far, as
+ any of them modifies the packet or its meta data and that has an influence on
+ the verdicts issued by the chains – other than that, the ordering shouldn’t
+ matter (except for performance and other side effects).
+ It also means that short-circuiting the ultimate decision is only possible via
+ *drop* verdicts (respectively verdict-like statements that imply *drop*, for
+ example *reject*).
+* A *jump* verdict causes the current position to be stored in the call stack of
+ chains and evaluation to continue at the beginning of the called regular
+ chain.
+ Called chains must be from the same table and cannot be base chains.
+ When the end of the called chain is reached, an implicit *return* verdict is
+ issued.
+ Other verdicts (respectively verdict-like statements) are processed as
+ described above and below.
+* A *goto* verdict is equal to *jump* except that the current position is not
+ stored in the call stack of chains.
+* A *return* verdict ends the evaluation of the current chain, pops the most
+ recently added position from the call stack of chains and causes evaluation to
+ continue after that position.
+ When there’s no position to pop (which is the case when the current chain is
+ either the base chain or a regular chain that was reached solely via *goto*
+ verdicts) it ends the evaluation of the current base chain (and any regular
+ chains called from it) using the base chain’s policy as implicit verdict.
+* Examples for *jump*/*goto*/*return*:
+ * 'base' {*jump*}→ 'regular-1' {*jump*}→ 'regular-2'
+ At the end of 'regular-2' or when a *return* is issued in that, evaluation
+ continues after the *jump* position in 'regular-1'.
+ At the end of 'regular-1' or when a *return* is issued in that, evaluation
+ continues after the *jump* position in 'base'.
+ * 'base' {*jump*}→ 'regular-1' {*goto*}→ 'regular-2'
+ At the end of 'regular-2' or when a *return* is issued in that, evaluation
+ continues after the *jump* position in 'base'.
+ * 'base' {*jump*}→ 'regular-1' {*jump*}→ 'regular-2' {*goto*}→ 'regular-3'
+ At the end of 'regular-3' or when a *return* is issued in that, evaluation
+ continues after the *jump* position in 'regular-1'.
+ At the end of 'regular-1' or when a *return* is issued in that, evaluation
+ continues after the *jump* position in 'base'.
+ * 'base' {*jump*}→ 'regular-1' {*goto*}→ 'regular-2' {*goto*}→ 'regular-3'
+ At the end of 'regular-3' or when a *return* is issued in that, evaluation
+ continues after the *jump* position in 'base'.
+* Verdicts (that is: *accept*, *drop*, *jump*, *goto*, *return* and *continue*)
+ as well as statements that imply a verdict (like *reject* or the NAT
+ statements) also end the evaluation of any later statements in their
+ respective rules (respectively cause an error when loading such rules).
+ For example in `… counter accept` the `counter` statement is processed, but in
+ `… accept counter` it is not.
+ This does not apply to the `comment` statement, which is always evaluated.
+
SETS
----
nftables offers two kinds of set concepts. Anonymous sets are sets that have no
--
2.51.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH nft v6 2/3] doc: fix/improve documentation of verdicts
2025-10-28 14:54 [PATCH nft v6 0/3] doc: miscellaneous improvements Florian Westphal
2025-10-28 14:54 ` [PATCH nft v6 1/3] doc: add overall description of the ruleset evaluation Florian Westphal
@ 2025-10-28 14:54 ` Florian Westphal
2025-10-28 14:54 ` [PATCH nft v6 3/3] doc: minor improvements the `reject` statement Florian Westphal
2025-10-29 0:19 ` [PATCH nft v6 0/3] doc: miscellaneous improvements Christoph Anton Mitterer
3 siblings, 0 replies; 8+ messages in thread
From: Florian Westphal @ 2025-10-28 14:54 UTC (permalink / raw)
To: netfilter-devel; +Cc: mail, Florian Westphal
From: Christoph Anton Mitterer <mail@christoph.anton.mitterer.name>
- Clarify that a terminating statement also prevents the execution of later
statements in the same rule and give an example about that.
- Correct that `accept` won’t terminate the evaluation of the ruleset (which is
generally used for the whole set of all chains, rules, etc.) but only that of
the current base chain (and any regular chains called from that).
Indicate that `accept` only accepts the packet from the current base chain’s
point of view.
Clarify that not only chains of a later hook could still drop the packet, but
also ones from the same hook if they have a higher priority.
- Various other minor improvements/clarifications to wording.
Link: https://lore.kernel.org/netfilter-devel/3c7ddca7029fa04baa2402d895f3a594a6480a3a.camel@scientia.org/T/#t
Signed-off-by: Christoph Anton Mitterer <mail@christoph.anton.mitterer.name>
Signed-off-by: Florian Westphal <fw@strlen.de>
---
doc/nft.txt | 1 +
doc/statements.txt | 24 ++++++++++++++----------
2 files changed, 15 insertions(+), 10 deletions(-)
diff --git a/doc/nft.txt b/doc/nft.txt
index d30481677c4d..4615c3ead8be 100644
--- a/doc/nft.txt
+++ b/doc/nft.txt
@@ -572,6 +572,7 @@ table inet filter {
nft delete rule inet filter input handle 5
-------------------------
+[[OVERALL_EVALUATION_OF_THE_RULESET]]
OVERALL EVALUATION OF THE RULESET
---------------------------------
This is a summary of how the ruleset is evaluated.
diff --git a/doc/statements.txt b/doc/statements.txt
index 6f438c047b86..5d197aeb0cee 100644
--- a/doc/statements.txt
+++ b/doc/statements.txt
@@ -10,18 +10,22 @@ ____
'CHAIN' := 'chain_name' | *{* 'statement' ... *}*
____
-*accept* and *drop* are absolute verdicts -- they terminate ruleset evaluation immediately.
+*accept* and *drop* are absolute verdicts -- they terminate chain evaluation,
+as if the packet would have reached the end of the base chain with the equivalent
+policy decision set. See <<OVERALL_EVALUATION_OF_THE_RULESET>> for more details.
[horizontal]
-*accept*:: Terminate ruleset evaluation and accept the packet.
-The packet can still be dropped later by another hook, for instance accept
-in the forward hook still allows one to drop the packet later in the postrouting hook,
-or another forward base chain that has a higher priority number and is evaluated
-afterwards in the processing pipeline.
-*drop*:: Terminate ruleset evaluation and drop the packet.
-The drop occurs instantly, no further chains or hooks are evaluated.
-It is not possible to accept the packet in a later chain again, as those
-are not evaluated anymore for the packet.
+*accept*:: Terminate evaluation early.
+ Evaluation continues in the next base chain of higher or possibly equal
+ priority from the same hook or in the first base chain of a later hook, if any.
+ This means the packet can still be dropped in another base chain as well as
+ any chain called from it.
+ For example, an *accept* verdict in a chain of the *forward* hook still allows one to
+ *drop* the packet in another *forward* hook base chain (or a user-defined chain called from it)
+ that has a higher priority number or in a chain attached to the *postrouting* hook.
+*drop*:: Immediately drop the packet and terminate ruleset evaluation.
+ No further evaluation takes place. It is not possible to override a *drop*
+ verdict.
*jump* 'CHAIN':: Store the current position in the call stack of chains and
continue evaluation at the first rule of 'CHAIN'.
When the end of 'CHAIN' is reached, an implicit *return* verdict is issued.
--
2.51.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH nft v6 3/3] doc: minor improvements the `reject` statement
2025-10-28 14:54 [PATCH nft v6 0/3] doc: miscellaneous improvements Florian Westphal
2025-10-28 14:54 ` [PATCH nft v6 1/3] doc: add overall description of the ruleset evaluation Florian Westphal
2025-10-28 14:54 ` [PATCH nft v6 2/3] doc: fix/improve documentation of verdicts Florian Westphal
@ 2025-10-28 14:54 ` Florian Westphal
2025-10-29 0:19 ` [PATCH nft v6 0/3] doc: miscellaneous improvements Christoph Anton Mitterer
3 siblings, 0 replies; 8+ messages in thread
From: Florian Westphal @ 2025-10-28 14:54 UTC (permalink / raw)
To: netfilter-devel; +Cc: mail, Florian Westphal
From: Christoph Anton Mitterer <mail@christoph.anton.mitterer.name>
Signed-off-by: Christoph Anton Mitterer <mail@christoph.anton.mitterer.name>
Signed-off-by: Florian Westphal <fw@strlen.de>
---
doc/statements.txt | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/doc/statements.txt b/doc/statements.txt
index 5d197aeb0cee..f380a60aaa71 100644
--- a/doc/statements.txt
+++ b/doc/statements.txt
@@ -1,3 +1,4 @@
+[[VERDICT_STATEMENTS]]
VERDICT STATEMENTS
~~~~~~~~~~~~~~~~~~
The verdict statements alter control flow in the ruleset and issue policy decisions for packets.
@@ -201,10 +202,11 @@ ____
*tcp reset*
____
-A reject statement is used to send back an error packet in response to the
-matched packet otherwise it is equivalent to drop so it is a terminating
-statement, ending rule traversal. This statement is only valid in base chains
-using the *prerouting*, *input*,
+A reject statement tries to send back an error packet in response to the matched
+packet and then interally issues a *drop* verdict.
+It’s thus a terminating statement with all consequences of the latter (see
+<<OVERALL_EVALUATION_OF_THE_RULESET>> respectively <<VERDICT_STATEMENTS>>).
+This statement is only valid in base chains using the *prerouting*, *input*,
*forward* or *output* hooks, and user-defined chains which are only called from
those chains.
--
2.51.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [PATCH nft v6 0/3] doc: miscellaneous improvements
2025-10-28 14:54 [PATCH nft v6 0/3] doc: miscellaneous improvements Florian Westphal
` (2 preceding siblings ...)
2025-10-28 14:54 ` [PATCH nft v6 3/3] doc: minor improvements the `reject` statement Florian Westphal
@ 2025-10-29 0:19 ` Christoph Anton Mitterer
2025-10-29 11:28 ` Florian Westphal
3 siblings, 1 reply; 8+ messages in thread
From: Christoph Anton Mitterer @ 2025-10-29 0:19 UTC (permalink / raw)
To: Florian Westphal, netfilter-devel
On Tue, 2025-10-28 at 15:54 +0100, Florian Westphal wrote:
> Christoph, let me know if you are ok with this
> and I will apply this some time tomorrow.
regarding:
[PATCH nft v6 1/3] doc: add overall description of the ruleset evaluation
> +* Base chains may call regular chains (via *jump* and *goto*).
> + Evaluation continues in the called chain. Regular chains can call
> + other regular chains.
IMO that makes things a bit more convoluted, first explaining who can
call who, then where evaluation continues, then again explaining who
can call who.
But, simpler sentences, so I don't mind.
> + Chains residing in a different table cannot be called.
> +* For each hook, the attached base chains are evaluated in order of
> their
> + priorities.
I intentionally had no "base" in there, because even though not
directly, the regular chains are still (indirectly) evaluated for each
hook.
With respect to the changes to *accept* and *drop*... I see that for an
dev or expert (or even me) your condensed version is enough, yet it
still think the previous more elaborate versions would have been better
for beginners, as it described things more explicitly requiring the
reader (and I presume devs/experts will never really visit the summary
chapter since they know it anyway in flesh and blood) to draw more
conclusions (for which he cannot be sure whether they're really correct
or not.
But, we can probably discuss about this forever and I guess at the
current point it's better to simply get it merged.
I would however suggest to reconsider in prarticular "all traffic will
be blocked".
"all traffic" is... well "all traffic"... but the decision is just
about one packet, ain't it?
Also "blocked" is IMO a bit fuzzy. Is the term used before? I'd rather
interpret it as some generic term that could be either drop or reject
or similar, but here the example was particularly about when any chain
uses drop as policy.
regarding:
[PATCH nft v6 2/3] doc: fix/improve documentation of verdicts:
Some of the minor changes are ok, I guess, other's I'd personally
think explain it now less clear, at leat from a beginners' PoV.
But I guess it's pointless to argue over such wordings preferences
over and over again :-)
With respect to the example that illustrates what it means that the
rule itself is immediately terminated, you refer this now to the
overall summary chapter?
I just wondered because that's merely a summary and it's now kinda
missing in the "detailed" chapter about the verdicts.
IMO it doesn't make things easier for a beginner, if one basically
has to read through everything to find all information.
So I rather have some limited repetition, but that's again of course
merely personal taste.
Als, "or a user-defined", ain't the base chains user-defined,
too?
So merge from my side to get this done with.
Thanks,
Chris.
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [PATCH nft v6 0/3] doc: miscellaneous improvements
2025-10-29 0:19 ` [PATCH nft v6 0/3] doc: miscellaneous improvements Christoph Anton Mitterer
@ 2025-10-29 11:28 ` Florian Westphal
2025-10-30 1:04 ` Christoph Anton Mitterer
0 siblings, 1 reply; 8+ messages in thread
From: Florian Westphal @ 2025-10-29 11:28 UTC (permalink / raw)
To: Christoph Anton Mitterer; +Cc: netfilter-devel
Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> wrote:
> IMO that makes things a bit more convoluted, first explaining who can
> call who, then where evaluation continues, then again explaining who
> can call who.
I swapped the two sentences.
> I would however suggest to reconsider in prarticular "all traffic will
> be blocked".
> "all traffic" is... well "all traffic"... but the decision is just
> about one packet, ain't it?
> Also "blocked" is IMO a bit fuzzy. Is the term used before? I'd rather
> interpret it as some generic term that could be either drop or reject
> or similar, but here the example was particularly about when any chain
> uses drop as policy.
What about this:
Thus, if any base chain uses drop as its policy, the same base chain (or a
regular chain directly or indirectly called by it) must contain at least one
*accept* rule to avoid all traffic from getting dropped.
> IMO it doesn't make things easier for a beginner, if one basically
> has to read through everything to find all information.
I added a reference. Also keep in mind that nftables will already tell
you about terminal statement not at end.
nft add rule ip f c drop counter
Error: Statement after terminal statement has no effect
> Als, "or a user-defined", ain't the base chains user-defined,
> too?
Thanks, user-defined is iptables-legacy lingo (base chains always
exist), old habit.
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH nft v6 0/3] doc: miscellaneous improvements
2025-10-29 11:28 ` Florian Westphal
@ 2025-10-30 1:04 ` Christoph Anton Mitterer
2025-10-30 10:34 ` Florian Westphal
0 siblings, 1 reply; 8+ messages in thread
From: Christoph Anton Mitterer @ 2025-10-30 1:04 UTC (permalink / raw)
To: Florian Westphal; +Cc: netfilter-devel
On Wed, 2025-10-29 at 12:28 +0100, Florian Westphal wrote:
>
> What about this:
> Thus, if any base chain uses drop as its policy, the same base
> chain (or a
> regular chain directly or indirectly called by it) must contain at
> least one
> *accept* rule to avoid all traffic from getting dropped.
Isn't that still effectively the same?
I mean the whole summary chapter explains things from the view of a
single packet, as was also the case for my last version of this
sentence:
> + Thus, if any base chain uses drop as its policy, the same base chain (or any
> + regular chain directly or indirectly called by it) must accept a packet or it
> + is ensured to be ultimately dropped by it.
Your wording changes this now to refer to "all traffic", which I think
make an unnecessary specialised case, namely that, where all packets
would be dropped, unless there's at least one accept rule.
The typical firewalling case is however that for most packets (that
might end up on the system) there actually is no single rule that would
accept them and only some of them get accept.
I simply would let the reference frame on a single packet.
> > IMO it doesn't make things easier for a beginner, if one basically
> > has to read through everything to find all information.
>
> I added a reference. Also keep in mind that nftables will already
> tell
> you about terminal statement not at end.
>
> nft add rule ip f c drop counter
> Error: Statement after terminal statement has no effect
Sure. I know.
Is it still mentioned somewhere that "comment" is an exception to the
rule?
I think that should be mentioned, because while people could just try
it out, they shouldn't have to... and even then they couldn't be sure
whether that's actually intended or merely a bug.
> > Als, "or a user-defined", ain't the base chains user-defined,
> > too?
>
> Thanks, user-defined is iptables-legacy lingo (base chains always
> exist), old habit.
You're welcome :-)
Cheers,
Chris.
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH nft v6 0/3] doc: miscellaneous improvements
2025-10-30 1:04 ` Christoph Anton Mitterer
@ 2025-10-30 10:34 ` Florian Westphal
0 siblings, 0 replies; 8+ messages in thread
From: Florian Westphal @ 2025-10-30 10:34 UTC (permalink / raw)
To: Christoph Anton Mitterer; +Cc: netfilter-devel
Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> wrote:
> On Wed, 2025-10-29 at 12:28 +0100, Florian Westphal wrote:
> >
> > What about this:
> > Thus, if any base chain uses drop as its policy, the same base
> > chain (or a
> > regular chain directly or indirectly called by it) must contain at
> > least one
> > *accept* rule to avoid all traffic from getting dropped.
>
> Isn't that still effectively the same?
?
> I mean the whole summary chapter explains things from the view of a
> single packet, as was also the case for my last version of this
> sentence:
> > + Thus, if any base chain uses drop as its policy, the same base chain (or any
> > + regular chain directly or indirectly called by it) must accept a packet or it
> > + is ensured to be ultimately dropped by it.
Whats that supposed to convey?
I will take your version (because I tire to iterate this again and again
but I think my version is better.
drop policy and no accept rule -> thats NOT what you want.
And thats what the 'all traffic' in my version intends to say.
> Your wording changes this now to refer to "all traffic", which I think
> make an unnecessary specialised case, namely that, where all packets
> would be dropped, unless there's at least one accept rule.
Isn't that a rather important point?
> The typical firewalling case is however that for most packets (that
> might end up on the system) there actually is no single rule that would
> accept them and only some of them get accept.
I would say most are accepted.
> > > IMO it doesn't make things easier for a beginner, if one basically
> > > has to read through everything to find all information.
> >
> > I added a reference. Also keep in mind that nftables will already
> > tell
> > you about terminal statement not at end.
> >
> > nft add rule ip f c drop counter
> > Error: Statement after terminal statement has no effect
>
> Sure. I know.
>
> Is it still mentioned somewhere that "comment" is an exception to the
> rule?
Its not an exception, comment is not a statement. Its isn't
executed/evaluated.
Its no different than
.... accept # do this
... except that "# do this" won't be sent to kernel and not included
when running 'list ruleset' and so on.
^ permalink raw reply [flat|nested] 8+ messages in thread