* nft manpage/wiki issues and improvement ideas
@ 2025-09-25 0:07 Christoph Anton Mitterer
2025-09-25 7:35 ` Pablo Neira Ayuso
` (3 more replies)
0 siblings, 4 replies; 63+ messages in thread
From: Christoph Anton Mitterer @ 2025-09-25 0:07 UTC (permalink / raw)
To: netfilter-devel
Hey.
I recently started migrating all my iptables config to nftables (better
late than never :-P)... and along reading through all the nftables.org
wiki pages and most of the manpage I've noticed all kinds of
documentation issues or things that might be improved...
But since I'm all but an expert (I merely have to do my netfilter
config at some university science cluster), I'm not really sure whether
I could give definite answers, so... a far too long O:-) list of things
that could be visited by some expert.
1) Non-documentation issue, could however be a downstream bug:
# nft describe icmpv6 code
payload expression, datatype icmpv6_code (icmpv6 code) (basetype integer), 8 bits
# nft describe icmp code
payload expression, datatype icmp_code (icmp code) (basetype integer), 8 bits
produce no (code) output as of at least v1.1.5.
That still worked in older versions.
In the manpage:
2) Section CHAINS
> The priority parameter accepts a signed integer value or a standard
> priority name which specifies the order in which chains with the same
> hook value are traversed.
IMO it would be helpful if something like "the same hook REGARDLESS OF
THEIR TABLE" would be added.
Maybe even elaborating a bit more that tables, AFAIU, aren't really
seen by netfilter at all and have no impact on any processing.
> The ordering is ascending, i.e. lower priority values have precedence
> over higher ones.
A bit ambiguous, IMO it would be better to say "chains with lower
priority values are processed first".
"Precedence" could be easily interpreted as "the verdict of such chains
winning", but AFAIU that's only the case if the verdict is drop, not if
accept.
3) Section VERDICT STATEMENT
> accept and drop are absolute verdicts — they terminate ruleset
> evaluation immediately.
and
> 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.
Seem contradicting and misleading.
"ruleset" is previously used as the whole set of all rules in all
chains + all set definitions.
The first paragraph says they'd end all evaluation of that immediately.
The 2nd says... no no.. other hooks can still change.
What I think the first paragraph wants to say is:
accept and deny terminate *even* the evaluation of the rule like in:
ip daddr 1.1.1.1 drop counter
counter wouldn't be executed (though many examples seem to use
comment after the verdict... not sure about that).
It also doesn't explain whether reject is also behaving like drop wrt
evaluation (so one must assume at that point: no), like in:
ip daddr 1.1.1.1 reject counter
And with respect to how chain processing is affected by the verdicts
(AFAIU):
- drop, regardless in which chain, as soon as it is encountered will
truly drop the packet.
No later chain (be it at the same hook with a higher priority, or
at another hook) can change that.
There is also no returning from regular chains back to their callers.
- accept, merely accepts the packet with respect to the current
call stack of chains.
Another base chain (or regular called from that) at the same hook
but of higher priority OR at another hook could still
drop(/reject?) (but not accept) it.
The 2nd paragraph rather confusingly (why mentioning the forward
hook?!) explains the one case... but that even a chain of the SAME hook
but with higher priority could still turn the accept to drop... is only
with much phantasy in that text.
- Again, no word about whether reject works here like drop.
I think it does, i.e. the reject of a chain would override another
chain's allow
The description of drop does a better job.
> jump CHAIN
> Continue evaluation at the first rule in CHAIN. The current position
> in the ruleset is pushed to a call stack and evaluation will continue
> there when the new chain is entirely evaluated or a return verdict is
> issued. In case an absolute verdict is issued by a rule in the chain,
> ruleset evaluation terminates immediately and the specific action is
> taken.
I don't think it makes sense for documentation to tell about pushing to
call stack.
A mere: at the end of the chain, or if a return verdict is found,
processing resumes right after the rule which caused the jump.
?
Again the wording that an absolute verdict terminates the (whole)
ruleset evaluation is IMO misleading. Only a a drop(/reject?) would do
so. An accept however would only end the evaluation of the call stack
of chains from the current base chain.
Not that of other base chains at the same hook with higher prio, or
that of other hooks.
> goto CHAIN
> Similar to jump, but the current position is not pushed to the call
> stack, meaning that after the new chain evaluation will continue at
> the last chain instead of the one containing the goto statement.
Maybe I misunderstood something, but that seems wrong.
AFAIU
(https://wiki.nftables.org/wiki-nftables/index.php/Jumping_to_chain see
jump vs goto), goto does *not* return, but simply uses the policy of
the base chain (not of the regular chain, which has no policy).
4) Neither the wiki nor the manpage seems to have a section which
briefly describes how tables/chains/rules are actually processed.
It's all rather widely dispersed over many pages/sections and
difficult to grasp, especially since some documentation seems plain
wrong and misleading.
AFAIU it works as follows:
- technically (in the sense how the actual evaluation is done) tables
don't matter at all
- packets traverse the network stack and at various hooks they're
evaluated by the chains attached to that hook
and even after netfilter they might still get reject (e.g. by things
like rp_filter, or when icmp.c simply discards certain ICMP types
- a drop/reject verdict (including a drop that results from chain
policy) actually drops the package and stops any further evaluation
of:
- the current chains
if a regular, also the ones up to the base chain that called it
- of other chains (in particular of higher priority) at the same hook
(regardless of their table)
- of other chains (of any priority) at other (in particular: later)
hooks
(regardless of their table)
=> Thus if any base-chain uses drop as policy, this chain must either
accept the package, or it will be (overall) dropped (as other base
chains cannot override the drop from the policy of that chain).
- an accept verdict (including an accept that results from chain
policy) *only* accepts the package from the current chain's point of
view (that is: the current regular chain up to the base chain from
which it was called or, if no regular chain, the current base chain).
- chains (regardless from which table) of higher priority at the same
hook as well as
- chains (of any priority and regardless from which table) of later
hooks
all may still deny/reject the package, in which case it would be
dropped/rejected as described above at drop/reject verdict
=> Thus a package is only actually accepted (from netfilter's PoV),
if none of the chains (regardless of their table) from all of the
relevant hooks does anything other than accept (be it via verdict,
policy or implicit policy default).
=> Thus the ordering of different call stacks of base chains via
priorities, doesn't change whether a packet gets
dropped/rejected/accepted, *unless* the package is modified or
things like marks are set, which would change the matching of
rules in other chains
- any terminating verdict (drop/reject/accept...TODO: also goto/jump?)
also end evaluation of the current rule, that is:
ip daddr 1.1.1.1 accept counter
causes counter to be ignored other than in:
ip daddr 1.1.1.1 counter accept
TODO: also the case with comment?
- jump C
- continues evaluation at the first rule of C
- a accept/drop/reject verdict in C via rule causes evaluation of
the call stack of chains to end and thus there will be no implicit
return to the calling chain
- a return verdict in C, causes to continue the evaluation in the
calling chain after the rule that caused the jump
- reaching the end of rules in C, causes an implicit return
- goto C
- continues evaluation at the first rule of C
- a accept/drop/reject verdict in C via rule causes evaluation of the
call stack of chains to end and thus there will be no implicit
return to the calling chain
- reaching the end of rules in C, causes the policy of the original
base chain to be used.
TODO: What I haven't checked now, but also seems not documented:
- Can one use return in a chain to which one got via goto and if
so, what happens?
- Can on jump/goto to other base chains?
And if so, which the policy of which base-chain would be used
when reaching the end of a regular chain one entered via goto?
5) Quite some syntax seems completely undocumented... e.g. what
operators one can use with tcp_flag and what "," means with
bitfields like in ct state.
Also the syntax introduced in
https://git.netfilter.org/nftables/commit/?id=c3d57114f119b89ec0caa0b4dfa8527826a38792
6) It doesn't seem to be documented how exactly the sorting is done
when including files (which may be quite important).
As far as I could see in the code, the wildcards are done via glob()
an since setlocale() doesn't seem to be handled throughout the code,
it seems to be the collation order of the C locale (which would of
course break, should localisation ever be added).
In the Wiki:
7) https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains
> 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.
Looks mostly right to me, but misses the point that chains of other
later hooks can also still drop/reject the package.
Also, misses whether or not rejects are like drops here.
> 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.
In principle right, but misses the point that a later hook (and its
chains) may still drop/reject the package.
8) https://wiki.nftables.org/wiki-nftables/index.php/Sets
Claims that the max length of set names is 16... but I created way
longer ones (which seemed to work... and it's really good to be able to
:-) ).
Also in there is chapter 2.1, which is part of chapter 2 named sets.
Not sure why 2.1 is in there, because its main new information is $VAR,
which is however, AFAIU, not a set.
In particular, what the example uses with:
> tcp dport { http, https } ip saddr $CDN accept
is an anonymous set, not a named one (and we're still in the chapter of
named ones).
The really interesting thing, namely "sets referencing other sets" like
in:
> define CDN = {
> $CDN_EDGE,
> $CDN_MONITORS
> }
I may be wrong, but these seem to be rather mere string operations
ultimately causing an anonymous set, right? One can e.g. also do:
> elements={{{1.1.1.1, 1.1.2.2}, 2.2.2.2}, 3.3.3.3 }
and it will simply remove any inner { }.
I think this should somehow be mentioned, so that people don't think
they could do dynamic things like { @setA, @setB}
9) Perhaps more a question to be sure:
> A hash sign (#) begins a comment. All following characters on the
> same line are ignored.
Is that really meant to imply that end-of-line comments work, i.e.
> ip dport 1.1.1.1 accept #foo bar baz
is supported?
I merely ask cause I've seen config parsers (I think it was either
ssh_config or sshd_confg) which did work with end of line comments but
were never intended to and it was ultimately removed.
10) https://wiki.nftables.org/wiki-nftables/index.php/Atomic_rule_replacement
- Missing from the manpage.
- What should IMO also be mentioned is, that if the new ruleset
contains errors, than despite the ruleset flush, the old rules
stay in place unmodified... which is quite important.
> What happens when you include 2 files which each have a statement for
> the filter table? If you have two included files both with statements
> for the filter table, but one adds a rule allowing traffic from
> 192.168.1.1 and the other allows traffic from 192.168.1.2 then both
> rules will be included in the chain, even if one or both files
> contains a flush statement.
and
> What about flush statements in either, or neither file? If there are
> any flush commands in any included file then those will be run at the
> moment the config swap is executed, not at the moment the file is
> loaded. If you do not include a flush statement in any included file,
> you will get duplicate rules. If you do include a flush statement,
> you will not get duplicate rules and the config from *both* files
> will be included.
Maybe I got something wrong, but this reads as if flush statements in
the two different files were effectively handled like one.
I tried a bit, and that doesn't seem to be the case. It rather seems as
if flush statements would be as if they were processed when encountered
during parsing.
E.g.
main.nft:
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority filter
iifname lo accept
}
}
include "included.nft"
included.nft:
#flush ruleset
table inet filter {
chain bla {
type filter hook input priority filter
ip daddr 1.1.1.1 drop
}
}
If I load it like this, I get both chains.
If however I uncomment the flash in included.nft, I only get the bla
chain, i.e. input must have been flushed away.
Generally missing (to my best knowledge):
11) ct state {a,b} vs. ct state a,b
or better said: what "," does in bitfields
based on Florian Westphal's answer[0] I'd assume that "," in
bitfields cause the statement to match, if any (or all) of the
named bits are set.
Also, from his explanation ct state {a,b} matches only if either a
(but not b) or b (but not a) is set.
Not sure about this, but I read the whole wiki and all generic
parts of the manpage, and I don't think it was ever mentioned that
matching sets work like this.
I mean it probably doesn't make a difference for things like
addresses, port ranges or ICMP types, where one can anyway only
have always one value,... but for things like bitfiedls it might.
12) is <predicate> <value> generally the same as <predicate> eq <value>
Like in:
dport 22
dport eq 22
13) "Teaching"
Well, obviously one can't explain everything, but I think for some
very common uses cases, it would be nice to give advise to users,
e.g.:
- If matching the loopback iface, iif, oif should always fine and
be faster (assuming the ID of lo is guaranteed to be always 1).
I tried to create further loopbacks or remove it, but that
generally seems to no longer work.
Would it be somehow possibly... and would iiftype oiftype be as
fast as checking the number... then maybe one should suggest
that?
At the same time, telling people this isn't safe for their
eth0/wlan0.
Yes, there is some note about this in the manpage:
> This is because internally the interface index is used. In case of
> dynamically created interfaces, such as tun/tap or dialup interfaces
> (ppp for example), it might be better to use iifname or oifnam
> instead.
But I wouldn't be surprised if may people are not experienced with
these types of ifaces, and might simply assume their eth/wlan is fine.
At least I found many wikis, blogs, which do use iif/oif for eth/wlan.
- Telling that:
ct state established,related accept
(who doesn't have such a rule ;-) )
is probably a bit faster than:
ct state {established,related} accept
Giving some performance guidelines:
- E.g. I blindly assume that the conntrack state of the packet is
already available and thus a check like ct state new is super
fast, and in particular faster than doing
tcp flags & (syn|ack|fin|rst) == syn
which in turn may or may not (I don't know) be slower than
tcp flags syn / syn,ack,fin,rst
of which there are countless examples to match "new" TCP
connections.
- What I quite often see is that people have some base rules and
then simple port based matches or TCP, UDP... often with a check
whether the connection is new.
So one get's lists of:
ct state new tcp dport ...
ct state new tcp dport ...
ct state new tcp dport ...
Even assuming ct state is fast... (when) would it be better to do e.g.:
ct state new jump new_queue
and only in new_queue do the tcp dport, udp dport rules?
- Does it performance wise make any difference to do e.g.
ct state new tcp dport 22
or
tcp dport 22 ct state new
respectively some guidelines *which* matching expressions are
super fast and which are rather slow?
- Assume e.g. the above case, where one has *many*:
tcp dport ... <do this>
tcp dport ... <do that>
udp dport ... <do this>
udp dport ... <do that>
What one could obviously do is.
meta l4proto tcp jump tcp_conns
meta l4proto udp jump udp_conns
and handle the port matching in these regular chains.
But this gives one basically:
- one extra expression that checks the type (which tcp/udp
statements would anyway already do
- the costs of the jump (and return)
The question is again: When is it worth it?
Thanks and best wishes,
Chris.
[0] https://lore.kernel.org/netfilter/aNPhP63SyX2ofE92@strlen.de/T/#m15841db7bf5bb588483fdd3576d70af7a71f5555
^ permalink raw reply [flat|nested] 63+ messages in thread* Re: nft manpage/wiki issues and improvement ideas 2025-09-25 0:07 nft manpage/wiki issues and improvement ideas Christoph Anton Mitterer @ 2025-09-25 7:35 ` Pablo Neira Ayuso 2025-09-25 20:37 ` Christoph Anton Mitterer ` (2 more replies) 2025-10-11 0:23 ` [PATCH v2 0/7] doc: miscellaneous improvements Christoph Anton Mitterer ` (2 subsequent siblings) 3 siblings, 3 replies; 63+ messages in thread From: Pablo Neira Ayuso @ 2025-09-25 7:35 UTC (permalink / raw) To: Christoph Anton Mitterer; +Cc: netfilter-devel On Thu, Sep 25, 2025 at 02:07:56AM +0200, Christoph Anton Mitterer wrote: > Hey. > > > I recently started migrating all my iptables config to nftables (better > late than never :-P)... and along reading through all the nftables.org > wiki pages and most of the manpage I've noticed all kinds of > documentation issues or things that might be improved... > > But since I'm all but an expert (I merely have to do my netfilter > config at some university science cluster), I'm not really sure whether > I could give definite answers, so... a far too long O:-) list of things > that could be visited by some expert. > > > 1) Non-documentation issue, could however be a downstream bug: > # nft describe icmpv6 code > payload expression, datatype icmpv6_code (icmpv6 code) (basetype integer), 8 bits > # nft describe icmp code > payload expression, datatype icmp_code (icmp code) (basetype integer), 8 bits > > produce no (code) output as of at least v1.1.5. > That still worked in older versions. What do you mean by no output? # nft -v nftables v1.1.5 (Commodore Bullmoose #6) # nft describe icmp code payload expression, datatype icmp_code (icmp code) (basetype integer), 8 bits # nft describe icmpv6 code payload expression, datatype icmpv6_code (icmpv6 code) (basetype integer), 8 bits As for your lengthy notes, it is easier for us if you send us individual patches for each issue that can be reviewed. This is more work on your side, but less work on us to digest this lengthy notes. Thanks. > In the manpage: > > > 2) Section CHAINS > > > The priority parameter accepts a signed integer value or a standard > > priority name which specifies the order in which chains with the same > > hook value are traversed. > > IMO it would be helpful if something like "the same hook REGARDLESS OF > THEIR TABLE" would be added. > Maybe even elaborating a bit more that tables, AFAIU, aren't really > seen by netfilter at all and have no impact on any processing. > > > The ordering is ascending, i.e. lower priority values have precedence > > over higher ones. > > A bit ambiguous, IMO it would be better to say "chains with lower > priority values are processed first". > "Precedence" could be easily interpreted as "the verdict of such chains > winning", but AFAIU that's only the case if the verdict is drop, not if > accept. > > > 3) Section VERDICT STATEMENT > > accept and drop are absolute verdicts — they terminate ruleset > > evaluation immediately. > > and > > > 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. > > Seem contradicting and misleading. > > "ruleset" is previously used as the whole set of all rules in all > chains + all set definitions. > The first paragraph says they'd end all evaluation of that immediately. > The 2nd says... no no.. other hooks can still change. > > What I think the first paragraph wants to say is: > > accept and deny terminate *even* the evaluation of the rule like in: > ip daddr 1.1.1.1 drop counter > counter wouldn't be executed (though many examples seem to use > comment after the verdict... not sure about that). > > It also doesn't explain whether reject is also behaving like drop wrt > evaluation (so one must assume at that point: no), like in: > ip daddr 1.1.1.1 reject counter > > > And with respect to how chain processing is affected by the verdicts > (AFAIU): > - drop, regardless in which chain, as soon as it is encountered will > truly drop the packet. > No later chain (be it at the same hook with a higher priority, or > at another hook) can change that. > There is also no returning from regular chains back to their callers. > - accept, merely accepts the packet with respect to the current > call stack of chains. > Another base chain (or regular called from that) at the same hook > but of higher priority OR at another hook could still > drop(/reject?) (but not accept) it. > > The 2nd paragraph rather confusingly (why mentioning the forward > hook?!) explains the one case... but that even a chain of the SAME hook > but with higher priority could still turn the accept to drop... is only > with much phantasy in that text. > > - Again, no word about whether reject works here like drop. > I think it does, i.e. the reject of a chain would override another > chain's allow > > The description of drop does a better job. > > > jump CHAIN > > Continue evaluation at the first rule in CHAIN. The current position > > in the ruleset is pushed to a call stack and evaluation will continue > > there when the new chain is entirely evaluated or a return verdict is > > issued. In case an absolute verdict is issued by a rule in the chain, > > ruleset evaluation terminates immediately and the specific action is > > taken. > > I don't think it makes sense for documentation to tell about pushing to > call stack. > A mere: at the end of the chain, or if a return verdict is found, > processing resumes right after the rule which caused the jump. > ? > > Again the wording that an absolute verdict terminates the (whole) > ruleset evaluation is IMO misleading. Only a a drop(/reject?) would do > so. An accept however would only end the evaluation of the call stack > of chains from the current base chain. > Not that of other base chains at the same hook with higher prio, or > that of other hooks. > > > goto CHAIN > > Similar to jump, but the current position is not pushed to the call > > stack, meaning that after the new chain evaluation will continue at > > the last chain instead of the one containing the goto statement. > > Maybe I misunderstood something, but that seems wrong. > > AFAIU > (https://wiki.nftables.org/wiki-nftables/index.php/Jumping_to_chain see > jump vs goto), goto does *not* return, but simply uses the policy of > the base chain (not of the regular chain, which has no policy). > > 4) Neither the wiki nor the manpage seems to have a section which > briefly describes how tables/chains/rules are actually processed. > It's all rather widely dispersed over many pages/sections and > difficult to grasp, especially since some documentation seems plain > wrong and misleading. > > AFAIU it works as follows: > > - technically (in the sense how the actual evaluation is done) tables > don't matter at all > - packets traverse the network stack and at various hooks they're > evaluated by the chains attached to that hook > and even after netfilter they might still get reject (e.g. by things > like rp_filter, or when icmp.c simply discards certain ICMP types > - a drop/reject verdict (including a drop that results from chain > policy) actually drops the package and stops any further evaluation > of: > - the current chains > if a regular, also the ones up to the base chain that called it > - of other chains (in particular of higher priority) at the same hook > (regardless of their table) > - of other chains (of any priority) at other (in particular: later) > hooks > (regardless of their table) > => Thus if any base-chain uses drop as policy, this chain must either > accept the package, or it will be (overall) dropped (as other base > chains cannot override the drop from the policy of that chain). > - an accept verdict (including an accept that results from chain > policy) *only* accepts the package from the current chain's point of > view (that is: the current regular chain up to the base chain from > which it was called or, if no regular chain, the current base chain). > - chains (regardless from which table) of higher priority at the same > hook as well as > - chains (of any priority and regardless from which table) of later > hooks > all may still deny/reject the package, in which case it would be > dropped/rejected as described above at drop/reject verdict > => Thus a package is only actually accepted (from netfilter's PoV), > if none of the chains (regardless of their table) from all of the > relevant hooks does anything other than accept (be it via verdict, > policy or implicit policy default). > => Thus the ordering of different call stacks of base chains via > priorities, doesn't change whether a packet gets > dropped/rejected/accepted, *unless* the package is modified or > things like marks are set, which would change the matching of > rules in other chains > - any terminating verdict (drop/reject/accept...TODO: also goto/jump?) > also end evaluation of the current rule, that is: > ip daddr 1.1.1.1 accept counter > causes counter to be ignored other than in: > ip daddr 1.1.1.1 counter accept > TODO: also the case with comment? > - jump C > - continues evaluation at the first rule of C > - a accept/drop/reject verdict in C via rule causes evaluation of > the call stack of chains to end and thus there will be no implicit > return to the calling chain > - a return verdict in C, causes to continue the evaluation in the > calling chain after the rule that caused the jump > - reaching the end of rules in C, causes an implicit return > - goto C > - continues evaluation at the first rule of C > - a accept/drop/reject verdict in C via rule causes evaluation of the > call stack of chains to end and thus there will be no implicit > return to the calling chain > - reaching the end of rules in C, causes the policy of the original > base chain to be used. > TODO: What I haven't checked now, but also seems not documented: > - Can one use return in a chain to which one got via goto and if > so, what happens? > - Can on jump/goto to other base chains? > And if so, which the policy of which base-chain would be used > when reaching the end of a regular chain one entered via goto? > > > 5) Quite some syntax seems completely undocumented... e.g. what > operators one can use with tcp_flag and what "," means with > bitfields like in ct state. > Also the syntax introduced in > https://git.netfilter.org/nftables/commit/?id=c3d57114f119b89ec0caa0b4dfa8527826a38792 > > > 6) It doesn't seem to be documented how exactly the sorting is done > when including files (which may be quite important). > As far as I could see in the code, the wildcards are done via glob() > an since setlocale() doesn't seem to be handled throughout the code, > it seems to be the collation order of the C locale (which would of > course break, should localisation ever be added). > > > In the Wiki: > > > 7) https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains > > 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. > > Looks mostly right to me, but misses the point that chains of other > later hooks can also still drop/reject the package. > Also, misses whether or not rejects are like drops here. > > > 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. > > In principle right, but misses the point that a later hook (and its > chains) may still drop/reject the package. > > > 8) https://wiki.nftables.org/wiki-nftables/index.php/Sets > Claims that the max length of set names is 16... but I created way > longer ones (which seemed to work... and it's really good to be able to > :-) ). > > Also in there is chapter 2.1, which is part of chapter 2 named sets. > Not sure why 2.1 is in there, because its main new information is $VAR, > which is however, AFAIU, not a set. > In particular, what the example uses with: > > tcp dport { http, https } ip saddr $CDN accept > > is an anonymous set, not a named one (and we're still in the chapter of > named ones). > > > The really interesting thing, namely "sets referencing other sets" like > in: > > define CDN = { > > $CDN_EDGE, > > $CDN_MONITORS > > } > > I may be wrong, but these seem to be rather mere string operations > ultimately causing an anonymous set, right? One can e.g. also do: > > elements={{{1.1.1.1, 1.1.2.2}, 2.2.2.2}, 3.3.3.3 } > and it will simply remove any inner { }. > I think this should somehow be mentioned, so that people don't think > they could do dynamic things like { @setA, @setB} > > > 9) Perhaps more a question to be sure: > > A hash sign (#) begins a comment. All following characters on the > > same line are ignored. > Is that really meant to imply that end-of-line comments work, i.e. > > ip dport 1.1.1.1 accept #foo bar baz > is supported? > > I merely ask cause I've seen config parsers (I think it was either > ssh_config or sshd_confg) which did work with end of line comments but > were never intended to and it was ultimately removed. > > > 10) https://wiki.nftables.org/wiki-nftables/index.php/Atomic_rule_replacement > - Missing from the manpage. > - What should IMO also be mentioned is, that if the new ruleset > contains errors, than despite the ruleset flush, the old rules > stay in place unmodified... which is quite important. > > > What happens when you include 2 files which each have a statement for > > the filter table? If you have two included files both with statements > > for the filter table, but one adds a rule allowing traffic from > > 192.168.1.1 and the other allows traffic from 192.168.1.2 then both > > rules will be included in the chain, even if one or both files > > contains a flush statement. > > and > > > What about flush statements in either, or neither file? If there are > > any flush commands in any included file then those will be run at the > > moment the config swap is executed, not at the moment the file is > > loaded. If you do not include a flush statement in any included file, > > you will get duplicate rules. If you do include a flush statement, > > you will not get duplicate rules and the config from *both* files > > will be included. > > Maybe I got something wrong, but this reads as if flush statements in > the two different files were effectively handled like one. > > I tried a bit, and that doesn't seem to be the case. It rather seems as > if flush statements would be as if they were processed when encountered > during parsing. > E.g. > main.nft: > #!/usr/sbin/nft -f > flush ruleset > table inet filter { > chain input { > type filter hook input priority filter > iifname lo accept > } > } > include "included.nft" > > > included.nft: > #flush ruleset > table inet filter { > chain bla { > type filter hook input priority filter > ip daddr 1.1.1.1 drop > } > } > > If I load it like this, I get both chains. > If however I uncomment the flash in included.nft, I only get the bla > chain, i.e. input must have been flushed away. > > > Generally missing (to my best knowledge): > > 11) ct state {a,b} vs. ct state a,b > or better said: what "," does in bitfields > based on Florian Westphal's answer[0] I'd assume that "," in > bitfields cause the statement to match, if any (or all) of the > named bits are set. > > Also, from his explanation ct state {a,b} matches only if either a > (but not b) or b (but not a) is set. > Not sure about this, but I read the whole wiki and all generic > parts of the manpage, and I don't think it was ever mentioned that > matching sets work like this. > I mean it probably doesn't make a difference for things like > addresses, port ranges or ICMP types, where one can anyway only > have always one value,... but for things like bitfiedls it might. > > > 12) is <predicate> <value> generally the same as <predicate> eq <value> > Like in: > dport 22 > dport eq 22 > > > 13) "Teaching" > Well, obviously one can't explain everything, but I think for some > very common uses cases, it would be nice to give advise to users, > e.g.: > - If matching the loopback iface, iif, oif should always fine and > be faster (assuming the ID of lo is guaranteed to be always 1). > I tried to create further loopbacks or remove it, but that > generally seems to no longer work. > Would it be somehow possibly... and would iiftype oiftype be as > fast as checking the number... then maybe one should suggest > that? > > At the same time, telling people this isn't safe for their > eth0/wlan0. > Yes, there is some note about this in the manpage: > > This is because internally the interface index is used. In case of > > dynamically created interfaces, such as tun/tap or dialup interfaces > > (ppp for example), it might be better to use iifname or oifnam > > instead. > But I wouldn't be surprised if may people are not experienced with > these types of ifaces, and might simply assume their eth/wlan is fine. > > At least I found many wikis, blogs, which do use iif/oif for eth/wlan. > > - Telling that: > ct state established,related accept > (who doesn't have such a rule ;-) ) > is probably a bit faster than: > ct state {established,related} accept > > Giving some performance guidelines: > > - E.g. I blindly assume that the conntrack state of the packet is > already available and thus a check like ct state new is super > fast, and in particular faster than doing > tcp flags & (syn|ack|fin|rst) == syn > which in turn may or may not (I don't know) be slower than > tcp flags syn / syn,ack,fin,rst > of which there are countless examples to match "new" TCP > connections. > > - What I quite often see is that people have some base rules and > then simple port based matches or TCP, UDP... often with a check > whether the connection is new. > So one get's lists of: > ct state new tcp dport ... > ct state new tcp dport ... > ct state new tcp dport ... > Even assuming ct state is fast... (when) would it be better to do e.g.: > ct state new jump new_queue > and only in new_queue do the tcp dport, udp dport rules? > > - Does it performance wise make any difference to do e.g. > ct state new tcp dport 22 > or > tcp dport 22 ct state new > respectively some guidelines *which* matching expressions are > super fast and which are rather slow? > > - Assume e.g. the above case, where one has *many*: > tcp dport ... <do this> > tcp dport ... <do that> > udp dport ... <do this> > udp dport ... <do that> > What one could obviously do is. > meta l4proto tcp jump tcp_conns > meta l4proto udp jump udp_conns > and handle the port matching in these regular chains. > > But this gives one basically: > - one extra expression that checks the type (which tcp/udp > statements would anyway already do > - the costs of the jump (and return) > > The question is again: When is it worth it? > > > Thanks and best wishes, > Chris. > > > [0] https://lore.kernel.org/netfilter/aNPhP63SyX2ofE92@strlen.de/T/#m15841db7bf5bb588483fdd3576d70af7a71f5555 > ^ permalink raw reply [flat|nested] 63+ messages in thread
* Re: nft manpage/wiki issues and improvement ideas 2025-09-25 7:35 ` Pablo Neira Ayuso @ 2025-09-25 20:37 ` Christoph Anton Mitterer 2025-09-26 1:52 ` [PATCH 0/7] doc: miscellaneois improvements Christoph Anton Mitterer 2025-09-26 2:32 ` nft manpage/wiki issues and improvement ideas Christoph Anton Mitterer 2 siblings, 0 replies; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-09-25 20:37 UTC (permalink / raw) To: Pablo Neira Ayuso; +Cc: netfilter-devel On Thu, 2025-09-25 at 09:35 +0200, Pablo Neira Ayuso wrote: > > 1) Non-documentation issue, could however be a downstream bug: > > # nft describe icmpv6 code > > payload expression, datatype icmpv6_code (icmpv6 code) > > (basetype integer), 8 bits > > # nft describe icmp code > > payload expression, datatype icmp_code (icmp code) (basetype > > integer), 8 bits > > > > produce no (code) output as of at least v1.1.5. > > That still worked in older versions. > > What do you mean by no output? Well, if I do it with nft v1.0.9 I get: $ nft describe icmp code payload expression, datatype icmp_code (icmp code) (basetype integer), 8 bits pre-defined symbolic constants (in decimal): net-unreachable 0 host-unreachable 1 prot-unreachable 2 port-unreachable 3 net-prohibited 9 host-prohibited 10 admin-prohibited 13 frag-needed 4 i.e. the codes are listed. But on my Debian I merely get: # nft describe icmp code payload expression, datatype icmp_code (icmp code) (basetype integer), 8 bits # (it does however work on Debian, for describe icmp type). Cheers, Chris. ^ permalink raw reply [flat|nested] 63+ messages in thread
* [PATCH 0/7] doc: miscellaneois improvements 2025-09-25 7:35 ` Pablo Neira Ayuso 2025-09-25 20:37 ` Christoph Anton Mitterer @ 2025-09-26 1:52 ` Christoph Anton Mitterer 2025-09-26 1:52 ` [PATCH 1/7] doc: clarify evaluation of chains Christoph Anton Mitterer ` (6 more replies) 2025-09-26 2:32 ` nft manpage/wiki issues and improvement ideas Christoph Anton Mitterer 2 siblings, 7 replies; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-09-26 1:52 UTC (permalink / raw) To: netfilter-devel; +Cc: Pablo Neira Ayuso Hey. On Thu, 2025-09-25 at 09:35 +0200, Pablo Neira Ayuso wrote: > for your lengthy notes, it is easier for us if you send us > dividual patches for each issue that can be reviewed. > > is is more work on your side, but less work on us to digest this > ngthy notes. Well... I didn't really want to O:-) in particular as I'm at best a nftables noob, but here you go. Please consider these more a RFC and someone who really knows how things work should throughly review and better double check. While I did try to test most of the claims I make... that was a bit difficult for some others and I might have made things up. And even if something works as observed, it may still not be as it should be. With respect to my original mail[0], the patches of this series should cover the following points with the following things where I'm unsure what do do: (2): [PATCH 1/7] doc: clarify evaluation of chains (3): [PATCH 2/7] doc: fix/improve documentation of verdicts HELP NEEDED: does queue work like drop, i.e. immediatey end any further evaluation? N/A: [PATCH 3/7] =?UTF-8?q?doc:=20minor=20improvements=20with=20respec?= HELP NEEDED: In these lines: https://git.netfilter.org/nftables/tree/doc/nft.txt?id=98e51e687616a4b54efa3b723917c292e3acc380#n520 https://git.netfilter.org/nftables/tree/doc/nft.txt?id=98e51e687616a4b54efa3b723917c292e3acc380#n757 https://git.netfilter.org/nftables/tree/doc/nft.txt?id=98e51e687616a4b54efa3b723917c292e3acc380#n826 I don't know whether ruleset should be replaced by rule. (4): [PATCH 4/7] doc: add overall description of the ruleset evaluation HELP NEEDED: queue verdict is missing, see above (5): [PATCH 5/7] doc: add some more documentation on bitmasks (6): [PATCH 6/7] =?UTF-8?q?doc:=20describe=20include=E2=80=99s=20colla?= (11): [PATCH 7/7] doc: describe how values match sets I don't know the answer to my original point (12) Also, I can't do any patches for the wiki, so I'll happily leave that up to you, i.e. my original points (7-11). O:-) Same for the "teaching" point (13), I think that should be done by someone who really knows what he's doing. Cheers, Chris. [0] https://lore.kernel.org/netfilter-devel/3c7ddca7029fa04baa2402d895f3a594a6480a3a.camel@scientia.org/T/#mb4387c098ee8cfec0baffecb42a7dfbac518adf7 ^ permalink raw reply [flat|nested] 63+ messages in thread
* [PATCH 1/7] doc: clarify evaluation of chains 2025-09-26 1:52 ` [PATCH 0/7] doc: miscellaneois improvements Christoph Anton Mitterer @ 2025-09-26 1:52 ` Christoph Anton Mitterer 2025-09-26 1:52 ` [PATCH 2/7] doc: fix/improve documentation of verdicts Christoph Anton Mitterer ` (5 subsequent siblings) 6 siblings, 0 replies; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-09-26 1:52 UTC (permalink / raw) To: netfilter-devel; +Cc: Pablo Neira Ayuso In particular: - Mention that grouping of chains in tables is irrelevant to the evaluation order. - Clarify that priorities only define the ordering of chains per hook. - Improved potentially ambiguous wording “lower priority values have precedence over higher ones”, which could be mistaken that rules from lower priority chains might “win” over such from higher ones (which is however only the case if they drop/reject packets). The new wording simply describes in which are evalauted first, which implicitly refers the question which verdict “wins” to the section where verdicts are described, but also should work when lower priority chains mangle packages (in which case they might actually be considered as having “precedence”). Link: https://lore.kernel.org/netfilter-devel/3c7ddca7029fa04baa2402d895f3a594a6480a3a.camel@scientia.org/T/#t Signed-off-by: Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> --- doc/nft.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/nft.txt b/doc/nft.txt index 87129819..c7d8500d 100644 --- a/doc/nft.txt +++ b/doc/nft.txt @@ -453,8 +453,10 @@ interface specified in the *device* parameter. The *priority* parameter accepts a signed integer value or a standard priority name which specifies the order in which chains with the same *hook* value are -traversed. The ordering is ascending, i.e. lower priority values have precedence -over higher ones. +traversed (regardless of the table to which they belong). The ordering is +ascending, i.e. per hook, chains with lower priority values are evaluated before +those with higher ones and the ordering of such with the same priority value +being undefined. With *nat* type chains, there's a lower excluding limit of -200 for *priority* values, because conntrack hooks at this priority and NAT requires it. -- 2.51.0 ^ permalink raw reply related [flat|nested] 63+ messages in thread
* [PATCH 2/7] doc: fix/improve documentation of verdicts 2025-09-26 1:52 ` [PATCH 0/7] doc: miscellaneois improvements Christoph Anton Mitterer 2025-09-26 1:52 ` [PATCH 1/7] doc: clarify evaluation of chains Christoph Anton Mitterer @ 2025-09-26 1:52 ` Christoph Anton Mitterer 2025-09-30 10:50 ` Florian Westphal 2025-09-26 1:52 ` [PATCH 3/7] doc: minor improvements with respect to the term “ruleset” Christoph Anton Mitterer ` (4 subsequent siblings) 6 siblings, 1 reply; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-09-26 1:52 UTC (permalink / raw) To: netfilter-devel; +Cc: Pablo Neira Ayuso - Include that `reject` behaves like `drop` with respect to ending the evaluations of rules (causing later statements to be not executed) and the whole ruleset (preventing any further evaluation of any other base chains with higher priorities at the same hook or any at later hooks. - 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 ruleset (which is generally used for the whole set of all chains, rules, etc.) but only that of the current base chain respectively any regular ones 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. - With respect to `return`/`jump`/`goto`, remove the “call stack” which seems rather an implementation detail and simply mention that the calling position is remembered. Also don’t use the wording “last chain”, which seems ambiguous. If the called chain itself called another chain and evaluation just returned from that, that might be misunderstood as the (chronologically) “last” one. “Calling chain” should be clear. Also don’t use the wording “new chain” (the chain isn’t strictly speaking “new”) and some places where merely “chain” was used, with `'CHAIN'` as the symbol for the called chain. - For `return`, more clearly differentiate between the types of chains and added the missing description of `return` in a regular chain called via `goto`. - For `jump`/`goto`, clarify, that their called chains return when a `return` verdict is issued *in them* not just when any `return` verdict (for example in another sub-chain) is issued. - For `goto`, I intentionally listed the cases when evaluation does not return to the calling case - because I guess it might actually do so, namely when the called chain itself explicitly jumps or go(to)es back. - 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> --- doc/statements.txt | 61 ++++++++++++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/doc/statements.txt b/doc/statements.txt index 6226713b..b085b3ab 100644 --- a/doc/statements.txt +++ b/doc/statements.txt @@ -10,34 +10,53 @@ ____ 'CHAIN' := 'chain_name' | *{* 'statement' ... *}* ____ -*accept* and *drop* are absolute verdicts -- they terminate ruleset evaluation immediately. +*accept* and *drop*/*reject* are absolute verdicts, which immediately terminate +the evaluation of the current rule, i.e. even any later statements of the +current rule won’t get executed. + +.*counter* will get executed: +------------------------------ +… counter accept +------------------------------ + +.*counter* won’t get executed: +------------------------------ +… accept counter +------------------------------ + +Further: [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 the evaluation of the current base chain (and any regular +chains called from it) and accept the packet from their point of view. +The packet may however still be dropped/rejected by another chain with a higher +priority of the same hook or by any chain of a later hook. +For example an 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*/*reject*:: Terminate ruleset evaluation and drop/reject the packet. This +occurs instantly, no further chains of any hooks are evaluated and it is thus +not possible to again accept the packet in a later chain, as those are not +evaluated anymore for the packet. *queue*:: Terminate ruleset evaluation and queue the packet to userspace. Userspace must provide a drop or accept verdict. In case of accept, processing resumes with the next base chain hook, not the rule following the queue verdict. *continue*:: Continue ruleset evaluation with the next rule. This is the default behaviour in case a rule issues no verdict. -*return*:: Return from the current chain and continue evaluation at the - next rule in the last chain. If issued in a base chain, it is equivalent to the - base chain policy. -*jump* 'CHAIN':: Continue evaluation at the first rule in 'CHAIN'. The current - position in the ruleset is pushed to a call stack and evaluation will continue - there when the new chain is entirely evaluated or a *return* verdict is issued. - In case an absolute verdict is issued by a rule in the chain, ruleset evaluation - terminates immediately and the specific action is taken. -*goto* 'CHAIN':: Similar to *jump*, but the current position is not pushed to the - call stack, meaning that after the new chain evaluation will continue at the last - chain instead of the one containing the goto statement. +*return*:: In a regular chain that was called via *jump*, end evaluation of that + chain and return to the calling chain, continuing evaluation there at the rule + after the calling rule. + In a regular chain that was called via *goto* or in a base chain, the *return* + verdict is equivalent to the base chain’s policy. +*jump* 'CHAIN':: Continue evaluation at the first rule of 'CHAIN'. The position + in the current chain is remembered and evaluation will continue there with the + next rule when 'CHAIN' is entirely evaluated or a *return* verdict is issued in + 'CHAIN' itself. + In case an absolute verdict is issued by a rule in 'CHAIN', evaluation + terminates as described above. +*goto* 'CHAIN':: Similar to *jump*, but the position in the current chain is not + remembered and evaluation will neihter return at the current chain when 'CHAIN' + is entirely evaluated nor when a *return* verdict is issued in 'CHAIN' itself. An alternative to specifying the name of an existing, regular chain in 'CHAIN' is to specify an anonymous chain ad-hoc. Like with anonymous sets, it can't be -- 2.51.0 ^ permalink raw reply related [flat|nested] 63+ messages in thread
* Re: [PATCH 2/7] doc: fix/improve documentation of verdicts 2025-09-26 1:52 ` [PATCH 2/7] doc: fix/improve documentation of verdicts Christoph Anton Mitterer @ 2025-09-30 10:50 ` Florian Westphal 2025-10-02 14:50 ` Christoph Anton Mitterer 0 siblings, 1 reply; 63+ messages in thread From: Florian Westphal @ 2025-09-30 10:50 UTC (permalink / raw) To: Christoph Anton Mitterer; +Cc: netfilter-devel, Pablo Neira Ayuso Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> wrote: > -*accept* and *drop* are absolute verdicts -- they terminate ruleset evaluation immediately. > +*accept* and *drop*/*reject* are absolute verdicts, which immediately terminate This isn't correct, strictly speaking, as 'reject' is not a verdict. The only verdicts accessible from userspace are accept and drop. (and queue, but thats a historic wart that should not be mentioned). 'reject' is also not the only statement that ends rule/basechain evaluation, other examples are redirect/dnat/snat/masquerade which will internally issue an accept verdict. Or synproxy, which will drop internally to consume the incoming packet. > +.*counter* will get executed: > +------------------------------ > +… counter accept > +------------------------------ > + > +.*counter* won’t get executed: > +------------------------------ > +… accept counter > +------------------------------ Thanks, this is a big improvement. > +*drop*/*reject*:: Terminate ruleset evaluation and drop/reject the packet. This > +occurs instantly, no further chains of any hooks are evaluated and it is thus > +not possible to again accept the packet in a later chain, as those are not > +evaluated anymore for the packet. As above, reject isn't a verdict, it will 'drop' internally. Its also not a 'drop' alias (it sends a reply packet). Maybe the 'REJECT STATEMENT' section can be extended a little, but I think its ok as-is. > +*return*:: In a regular chain that was called via *jump*, end evaluation of that > + chain and return to the calling chain, continuing evaluation there at the rule > + after the calling rule. Maybe we should mention that 'return' is the implicit thing at the end of a user-created non-base chain? Or do you think thats self-evident? > + In a regular chain that was called via *goto* or in a base chain, the *return* > + verdict is equivalent to the base chain’s policy. No, its not. I think this warrants an example. chain two { ... } chain one { ... goto two ip saddr .. # never matched } chain in { hook input type filter ... jump one ip saddr .. # evaluated for all packets not dropped/accepted yet } -> base chain calls 'one' and remembers this location -> 'one' calls 'two', but doesn't place it on chain stack. -> at the end of 'two' / on 'return', we resume after 'jump one', not after 'goto'. The sentence wrt. base chain policy is valid in case 'chain in' would contain 'goto one', as it doesn't remember the origin location, end-of-one / return is equal to explicit 'return' from the base chain. > +*jump* 'CHAIN':: Continue evaluation at the first rule of 'CHAIN'. The position > + in the current chain is remembered and evaluation will continue there with the > + next rule when 'CHAIN' is entirely evaluated or a *return* verdict is issued in > + 'CHAIN' itself. > + In case an absolute verdict is issued by a rule in 'CHAIN', evaluation > + terminates as described above. > +*goto* 'CHAIN':: Similar to *jump*, but the position in the current chain is not > + remembered and evaluation will neihter return at the current chain when 'CHAIN' > + is entirely evaluated nor when a *return* verdict is issued in 'CHAIN' itself. Maybe it should say that it will instead resume after the last jump (if there was any?, or not at all (base chain policy executes?) ^ permalink raw reply [flat|nested] 63+ messages in thread
* Re: [PATCH 2/7] doc: fix/improve documentation of verdicts 2025-09-30 10:50 ` Florian Westphal @ 2025-10-02 14:50 ` Christoph Anton Mitterer 2025-10-02 15:21 ` Florian Westphal 0 siblings, 1 reply; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-10-02 14:50 UTC (permalink / raw) To: Florian Westphal; +Cc: netfilter-devel, Pablo Neira Ayuso Hey. Thanks for having a look. :-) On Tue, 2025-09-30 at 12:50 +0200, Florian Westphal wrote: > This isn't correct, strictly speaking, as 'reject' is not a verdict. > The only verdicts accessible from userspace are accept and drop. > (and queue, but thats a historic wart that should not be mentioned). I see. Not that I'd reject reworking this, but wearing my user hat, I'd say that the user rather doesn't care so much whether in strict terminology this is a verdict or not - cause from the user's PoV this is simply like a verdict (i.e. causing - along with sending a reply - to either accept/reject the packet). What would you think about changing it roughly as follows (at this place, as well as perhaps in my new summary chapter about how evaluation works): - remove the rejects from next to the drops - add a paragraph which describes that there are verdict-like statements, which a) do some extra stuff and b) cause one of the true verdicts (drop/accept) - along with that add a list which explains which such statement causes which verdict What's IMO important from the user PoV is that for example at a sentence like: >*accept* and *drop*/*reject* are absolute verdicts, which immediately > terminate the evaluation of the current rule, i.e. even any later > statements of the current rule won’t get executed he also gets told that the very same concept (here: later statements not being executed) applies also to verdict-like statements like reject (if that's the case). The user doesn't want to have to read through the whole manpage to eventually realise that reject behaves effectively like drop here. And even if you refer to the REJECT section and mention it there, it makes things IMO more difficult to understand. That's basically the reason why I propose this evaluation summary chapter, because with the information spread out over many places it's more difficult to grasp the big picture. But back to verdict/verdict-like statements: Are jump/goto/return/etc. then true verdicts? I wouldn't guess so, yet they already are explained in the verdict section. I'm not sure how much we win, by differentiating between these two, and even if we do so, how shall we call things like reject? "verdict like statements"? "statements that imply a verdict"? > 'reject' is also not the only statement that ends rule/basechain > evaluation, > other examples are redirect/dnat/snat/masquerade which will > internally > issue an accept verdict. Or synproxy, which will drop internally to > consume the incoming packet. Is there a complete list of all these which are verdict-like and what verdict they actually imply? > > +.*counter* will get executed: > > +------------------------------ > > +… counter accept > > +------------------------------ > > + > > +.*counter* won’t get executed: > > +------------------------------ > > +… accept counter > > +------------------------------ > > Thanks, this is a big improvement. :-) > > +*drop*/*reject*:: Terminate ruleset evaluation and drop/reject the > > packet. This > > +occurs instantly, no further chains of any hooks are evaluated and > > it is thus > > +not possible to again accept the packet in a later chain, as those > > are not > > +evaluated anymore for the packet. > > As above, reject isn't a verdict, it will 'drop' internally. Its also > not a 'drop' alias (it sends a reply packet). > > Maybe the 'REJECT STATEMENT' section can be extended a little, but I > think its ok as-is. Same solution as I'd propose above?! > > +*return*:: In a regular chain that was called via *jump*, end > > evaluation of that > > + chain and return to the calling chain, continuing evaluation > > there at the rule > > + after the calling rule. > > > Maybe we should mention that 'return' is the implicit thing at the > end > of a user-created non-base chain? > > Or do you think thats self-evident? I do mention it in my summary chapter. In the section on the verdicts you're referring to it follows implicitly from the description of the *jump* statement, but at least there I'd also think it's perhaps good to explicitly mention it in parentheses. Whether we also mentioned it in the return description, I don't mind. We can, so to say as extra information, but it's IMO not strictly necessary. Because if someone looks up the book for the return statement he most likely wants to know what happens we issuing it - not which other things (he wasn't looking up) also behave in some cases like the statement he was looking up. But again... I'd be fine either way. > > + In a regular chain that was called via *goto* or in a base chain, > > the *return* > > + verdict is equivalent to the base chain’s policy. > > No, its not. > I think this warrants an example. > > chain two { ... } > chain one { > ... > goto two > ip saddr .. # never matched > } > > chain in { > hook input type filter ... > jump one > ip saddr .. # evaluated for all packets not dropped/accepted yet > } > > -> base chain calls 'one' and remembers this location > -> 'one' calls 'two', but doesn't place it on chain stack. > -> at the end of 'two' / on 'return', we resume after 'jump one', not > after 'goto'. > > The sentence wrt. base chain policy is valid in case 'chain in' would > contain 'goto one', as it doesn't remember the origin location, > end-of-one / return is equal to explicit 'return' from the base > chain. Uff... okay... that makes things quite a bit harder to describe. So effectively that means, return (explicit or implicit) it not really equivalent to the policy, as claimed for at least base-chains in the current manpage: > return > Return from the current chain and continue evaluation at the next > rule in the last chain. If issued in a base chain, it isequivalent to > the base chain policy. But rather *if* there's nowhere to return to (like when it's called from a base-chain) *then* the policy is applied, right!? I merely had checked calling return from a regular chain into which I've jumped to from the base-chain, ... that together with the above sentence from the manpage, made me think it would work that way. > > +*jump* 'CHAIN':: Continue evaluation at the first rule of 'CHAIN'. > > The position > > + in the current chain is remembered and evaluation will continue > > there with the > > + next rule when 'CHAIN' is entirely evaluated or a *return* > > verdict is issued in > > + 'CHAIN' itself. > > + In case an absolute verdict is issued by a rule in 'CHAIN', > > evaluation > > + terminates as described above. > > +*goto* 'CHAIN':: Similar to *jump*, but the position in the > > current chain is not > > + remembered and evaluation will neihter return at the current > > chain when 'CHAIN' > > + is entirely evaluated nor when a *return* verdict is issued in > > 'CHAIN' itself. > > Maybe it should say that it will instead resume after the last jump > (if > there was any?, or not at all (base chain policy executes?) As far as I understand it now: base --jump--> regularA --goto--> regularB then at the end of regularB or if return is called in it, while I don't return to regularA, I actually will return to the jump position in base, right? Similar in: base --jump--> regularA --jump--> regularB --goto--> regularC I will not return to regularB, but will return to the jump position of regularA and then to that of base, right? Very open for opinions, but I do think with that semantics it actually might be best to describe things with a call stack an give some examples. What do you think? Cheers, Chris. ^ permalink raw reply [flat|nested] 63+ messages in thread
* Re: [PATCH 2/7] doc: fix/improve documentation of verdicts 2025-10-02 14:50 ` Christoph Anton Mitterer @ 2025-10-02 15:21 ` Florian Westphal 2025-10-10 23:06 ` Christoph Anton Mitterer 0 siblings, 1 reply; 63+ messages in thread From: Florian Westphal @ 2025-10-02 15:21 UTC (permalink / raw) To: Christoph Anton Mitterer; +Cc: netfilter-devel, Pablo Neira Ayuso Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> wrote: > What would you think about changing it roughly as follows (at this > place, as well as perhaps in my new summary chapter about how > evaluation works): > - remove the rejects from next to the drops > - add a paragraph which describes that there are verdict-like > statements, which a) do some extra stuff and b) cause one of the true > verdicts (drop/accept) > - along with that add a list which explains which such statement causes > which verdict > > What's IMO important from the user PoV is that for example at a > sentence like: > >*accept* and *drop*/*reject* are absolute verdicts, which immediately > > terminate the evaluation of the current rule, i.e. even any later > > statements of the current rule won’t get executed Why not shorten it to "accept/drop/reject immediately terminate ..." > The user doesn't want to have to read through the whole manpage to > eventually realise that reject behaves effectively like drop here. > And even if you refer to the REJECT section and mention it there, it > makes things IMO more difficult to understand. Ok. > But back to verdict/verdict-like statements: > Are jump/goto/return/etc. then true verdicts? I wouldn't guess so, yet > they already are explained in the verdict section. Yes, this is because of netfilter verdicts vs. nftables verdicts. accept/drop are both, continue, goto, return the latter. This is because netfilter core has no idea what a ruleset is; nftables "plugs" into the netfilter core. The nf_tables base hooks interpret the user policy and then issue either NF_ACCEPT, NF_DROP, NF_QUEUE or NF_STOLEN. (the "core" verdicts). But I agree that this is an implementation detail that is irrelevant to the man page and that continue, goto, etc. should be called verdicts too. > I'm not sure how much we win, by differentiating between these two, and > even if we do so, how shall we call things like reject? "verdict like > statements"? "statements that imply a verdict"? What about "terminal statements"? This is already used in the man page in several places. > > 'reject' is also not the only statement that ends rule/basechain > > evaluation, > > other examples are redirect/dnat/snat/masquerade which will > > internally > > issue an accept verdict. Or synproxy, which will drop internally to > > consume the incoming packet. > > Is there a complete list of all these which are verdict-like and what > verdict they actually imply? No. net/bridge/netfilter/nft_reject_bridge.c: regs->verdict.code = NF_DROP; net/ipv4/netfilter/nft_reject_ipv4.c: regs->verdict.code = NF_DROP; net/ipv6/netfilter/nft_reject_ipv6.c: regs->verdict.code = NF_DROP; net/netfilter/nft_reject_inet.c: regs->verdict.code = NF_DROP; net/netfilter/nft_reject_netdev.c: regs->verdict.code = NF_DROP; These are "obvious", reject is a fancier drop. net/netfilter/nft_compat.c: regs->verdict.code = NF_ACCEPT; net/netfilter/nft_compat.c: regs->verdict.code = NF_DROP; irrelevant for nftables net/netfilter/nft_connlimit.c: regs->verdict.code = NF_DROP; Error handling only net/netfilter/nft_ct.c: regs->verdict.code = NF_DROP; net/netfilter/nft_ct.c: regs->verdict.code = NF_DROP; net/netfilter/nft_exthdr.c: regs->verdict.code = NF_DROP; net/netfilter/nft_fib_inet.c: regs->verdict.code = NF_DROP; Same, only errors net/netfilter/nft_fwd_netdev.c: regs->verdict.code = NF_STOLEN; Terminal (packet is redirected) net/netfilter/nft_synproxy.c: regs->verdict.code = NF_DROP; net/netfilter/nft_synproxy.c: regs->verdict.code = NF_STOLEN; net/netfilter/nft_synproxy.c: regs->verdict.code = NF_DROP; net/netfilter/nft_synproxy.c: regs->verdict.code = NF_STOLEN; Also terminal, 3whs packets are dropped resp. stolen for further processing. > > Maybe the 'REJECT STATEMENT' section can be extended a little, but I > > think its ok as-is. > > Same solution as I'd propose above?! Ok. > > Maybe we should mention that 'return' is the implicit thing at the > > end > > of a user-created non-base chain? > > > > Or do you think thats self-evident? > > I do mention it in my summary chapter. > > In the section on the verdicts you're referring to it follows > implicitly from the description of the *jump* statement, but at least > there I'd also think it's perhaps good to explicitly mention it in > parentheses. That seems fine. > Whether we also mentioned it in the return description, I don't mind. > We can, so to say as extra information, but it's IMO not strictly > necessary. Because if someone looks up the book for the return > statement he most likely wants to know what happens we issuing it - not > which other things (he wasn't looking up) also behave in some cases > like the statement he was looking up. Ok. > > > + In a regular chain that was called via *goto* or in a base chain, > > > the *return* > > > + verdict is equivalent to the base chain’s policy. > > > > No, its not. > > I think this warrants an example. > > > > chain two { ... } > > chain one { > > ... > > goto two > > ip saddr .. # never matched > > } > > > > chain in { > > hook input type filter ... > > jump one > > ip saddr .. # evaluated for all packets not dropped/accepted yet > > } > > > > -> base chain calls 'one' and remembers this location > > -> 'one' calls 'two', but doesn't place it on chain stack. > > -> at the end of 'two' / on 'return', we resume after 'jump one', not > > after 'goto'. > > > > The sentence wrt. base chain policy is valid in case 'chain in' would > > contain 'goto one', as it doesn't remember the origin location, > > end-of-one / return is equal to explicit 'return' from the base > > chain. > > Uff... okay... that makes things quite a bit harder to describe. Yes, I think an example would be prudent, it is probably simpler to unstand rather than describing the mechanism. > So effectively that means, return (explicit or implicit) it not really > equivalent to the policy, as claimed for at least base-chains in the > current manpage: > > return > > Return from the current chain and continue evaluation at the next > > rule in the last chain. If issued in a base chain, it isequivalent to > > the base chain policy. > But rather *if* there's nowhere to return to (like when it's called > from a base-chain) *then* the policy is applied, right!? Yes. > > Maybe it should say that it will instead resume after the last jump > > (if > > there was any?, or not at all (base chain policy executes?) Yes, you can also look into man iptables, --jump and --goto work the same way in nftables. > As far as I understand it now: > base --jump--> regularA --goto--> regularB > then at the end of regularB or if return is called in it, while I don't > return to regularA, I actually will return to the jump position in > base, right? Yes, rules in "regularB" are evaluated as if they would reside in regularA, it resumes in the base chain. > Similar in: > base --jump--> regularA --jump--> regularB --goto--> regularC > I will not return to regularB, but will return to the jump position of > regularA and then to that of base, right? Yes. > Very open for opinions, but I do think with that semantics it actually > might be best to describe things with a call stack an give some > examples. Yes, I think examples are best here. ^ permalink raw reply [flat|nested] 63+ messages in thread
* Re: [PATCH 2/7] doc: fix/improve documentation of verdicts 2025-10-02 15:21 ` Florian Westphal @ 2025-10-10 23:06 ` Christoph Anton Mitterer 0 siblings, 0 replies; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-10-10 23:06 UTC (permalink / raw) To: Florian Westphal; +Cc: netfilter-devel, Pablo Neira Ayuso Hey. On Thu, 2025-10-02 at 17:21 +0200, Florian Westphal wrote: > > What's IMO important from the user PoV is that for example at a > > sentence like: > > > *accept* and *drop*/*reject* are absolute verdicts, which > > > immediately > > > terminate the evaluation of the current rule, i.e. even any later > > > statements of the current rule won’t get executed > > Why not shorten it to "accept/drop/reject immediately terminate ..." I solved that a bit different now, please have a look whether that's fine for you. > > > I'm not sure how much we win, by differentiating between these two, > > and > > even if we do so, how shall we call things like reject? "verdict > > like > > statements"? "statements that imply a verdict"? > > What about "terminal statements"? > This is already used in the man page in several places. Hmm. Personally, I think "terminal" is a bit confusing, at least for beginners, because we have termination on multiple levels: - Termination of a rule and (AFAICS) then also always of the respective base chain and any regular chains called by that. As e.g. done by accept. - Termination of the overall ruleset evaluation. As e.g. done by drop, reject (by implying drop). From how I understood your explanation, the "terminating" is not an inherent property of e.g. reject itself, but rather of drop/accept, whereas the inherent property of reject is, that it implies a drop (at the end). So I'd have now gone the way to describe reject/synproxy and the NAT statements as implying a verdict and further explaining that they thus act like verdicts. Further, giving, some concrete examples (like that reject also completely ends ruleset evaluation, as drop does - which I think is important). > > > > Is there a complete list of all these which are verdict-like and > > what > > verdict they actually imply? > > No. > > net/bridge/netfilter/nft_reject_bridge.c: regs->verdict.code = > NF_DROP; > net/ipv4/netfilter/nft_reject_ipv4.c: regs->verdict.code = NF_DROP; > net/ipv6/netfilter/nft_reject_ipv6.c: regs->verdict.code = NF_DROP; > net/netfilter/nft_reject_inet.c: regs->verdict.code = NF_DROP; > net/netfilter/nft_reject_netdev.c: regs->verdict.code = NF_DROP; > > These are "obvious", reject is a fancier drop. > > net/netfilter/nft_compat.c: regs->verdict.code = > NF_ACCEPT; > net/netfilter/nft_compat.c: regs->verdict.code = NF_DROP; > > irrelevant for nftables > > net/netfilter/nft_connlimit.c: regs->verdict.code = NF_DROP; > > Error handling only > > net/netfilter/nft_ct.c: regs->verdict.code = NF_DROP; > net/netfilter/nft_ct.c: regs->verdict.code = NF_DROP; > net/netfilter/nft_exthdr.c: regs->verdict.code = NF_DROP; > net/netfilter/nft_fib_inet.c: regs->verdict.code = NF_DROP; > > Same, only errors > > net/netfilter/nft_fwd_netdev.c: regs->verdict.code = NF_STOLEN; > > Terminal (packet is redirected) > > net/netfilter/nft_synproxy.c: regs->verdict.code = > NF_DROP; > net/netfilter/nft_synproxy.c: regs->verdict.code = > NF_STOLEN; > net/netfilter/nft_synproxy.c: regs->verdict.code = NF_DROP; > net/netfilter/nft_synproxy.c: regs->verdict.code = > NF_STOLEN; > > Also terminal, 3whs packets are dropped resp. stolen > for further processing. As far as I understood this: - the NAT statements imply accept (and can later be overruled) - reject implies drop - synprxy implies drop Not sure what stolen does... since it doesn't seem to appear at all in nftables, I merely wrote now, that it implies drop. Please correct as needed. - the others don't even show up in nftables? Btw, one further question that just popped up: In the VERDICTS section, accept/drop are described as "absolute", which at this point I describe in my patch as: > which immediately terminate the > evaluation of the current rule, i.e. even any later statements of the > current > rule won’t get executed. a) Does the term "absolute" really just mean "ends the current rule" or does it mean additionally mean "and the current base chain and any regular ones called from that? Cause my current wording, would rather imply it means only the former (while the actual verdicts are still described as also causing the latter). b) Are jump/goto/return/queue/etc. i.e. all those which *DON'T* imply a accept or drop also absolute? I presume not, if the term also means "end current base/regular chains". But even if not, do these verdicts (jump/goto/return/queue/etc.) still cause the current rule to be ended? like in: jump counter vs counter jump Even with continue? Cause then I need to update that in another iteration of the patch series. > Yes, I think an example would be prudent, it is probably simpler > to unstand rather than describing the mechanism. I've changed things now as follows: - In the VERDICTS section, I keep things rather "abstract" and focused on the respectve verdict that we're just explaining. So examples will be in the evaluation summary chapter rather than there. Also e.g. at jump/goto it's *not* explained how the return position is determined... this is instead done at the return verdict. But what I do at jump is, describing what happens if there's an absolute verdict and if there's no verdict at all (i.e. the implicit return). Hope that split up makes kinda sense. goto I really describe as exactly jump, just without storing the position. - I also reordered the verdicts to an IMO more reasonable order, in particular, continue and queue are IMO the least interesting ones for most users. - In the summary chapter I give examples and describe it a bit more human readable. > > But rather *if* there's nowhere to return to (like when it's called > > from a base-chain) *then* the policy is applied, right!? > > Yes. I did that now in the VERDICTS section and there in the return statement. What I do not explicitly mention is that the policy is also applied when there *is* one last position to pop on the call stack, but when there's no rule after that (i.e. the jump was the last rule in a base chain). But I kinda hope that it's obvious that this then also applies. In principle that should perhaps be mentioned where policies are explained (and of course in the summary chapter, too). Or what would you think? > > > > As far as I understand it now: > > base --jump--> regularA --goto--> regularB > > then at the end of regularB or if return is called in it, while I > > don't > > return to regularA, I actually will return to the jump position in > > base, right? > > Yes, rules in "regularB" are evaluated as if they would reside in > regularA, it resumes in the base chain. > > > Similar in: > > base --jump--> regularA --jump--> regularB --goto--> regularC > > I will not return to regularB, but will return to the jump position > > of > > regularA and then to that of base, right? > > Yes. > > > Very open for opinions, but I do think with that semantics it > > actually > > might be best to describe things with a call stack an give some > > examples. > > Yes, I think examples are best here. I've added a number of examples to the summary chapter. Thanks, Chris. ^ permalink raw reply [flat|nested] 63+ messages in thread
* [PATCH 3/7] doc: minor improvements with respect to the term “ruleset” 2025-09-26 1:52 ` [PATCH 0/7] doc: miscellaneois improvements Christoph Anton Mitterer 2025-09-26 1:52 ` [PATCH 1/7] doc: clarify evaluation of chains Christoph Anton Mitterer 2025-09-26 1:52 ` [PATCH 2/7] doc: fix/improve documentation of verdicts Christoph Anton Mitterer @ 2025-09-26 1:52 ` Christoph Anton Mitterer 2025-09-26 1:52 ` [PATCH 4/7] doc: add overall description of the ruleset evaluation Christoph Anton Mitterer ` (3 subsequent siblings) 6 siblings, 0 replies; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-09-26 1:52 UTC (permalink / raw) To: netfilter-devel; +Cc: Pablo Neira Ayuso Statements are elements of rules. Non-terminal statement are in particular passive with respect to their rules (and thus automatically with respect to the whole ruleset). In “Continue ruleset evaluation”, it’s not necessary to mention the ruleset as it’s obvious that the evaluation of the current chain will be continued. Signed-off-by: Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> --- doc/nft.txt | 6 +++--- doc/statements.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/nft.txt b/doc/nft.txt index c7d8500d..f52b7fef 100644 --- a/doc/nft.txt +++ b/doc/nft.txt @@ -910,9 +910,9 @@ actions, such as logging, rejecting a packet, etc. + Statements exist in two kinds. Terminal statements unconditionally terminate evaluation of the current rule, non-terminal statements either only conditionally or never terminate evaluation of the current rule, in other words, -they are passive from the ruleset evaluation perspective. There can be an -arbitrary amount of non-terminal statements in a rule, but only a single -terminal statement as the final statement. +they are passive from the rule evaluation perspective. There can be an arbitrary +amount of non-terminal statements in a rule, but only a single terminal +statement as the final statement. include::statements.txt[] diff --git a/doc/statements.txt b/doc/statements.txt index b085b3ab..bddbf12f 100644 --- a/doc/statements.txt +++ b/doc/statements.txt @@ -41,7 +41,7 @@ evaluated anymore for the packet. *queue*:: Terminate ruleset evaluation and queue the packet to userspace. Userspace must provide a drop or accept verdict. In case of accept, processing resumes with the next base chain hook, not the rule following the queue verdict. -*continue*:: Continue ruleset evaluation with the next rule. This +*continue*:: Continue evaluation with the next rule. This is the default behaviour in case a rule issues no verdict. *return*:: In a regular chain that was called via *jump*, end evaluation of that chain and return to the calling chain, continuing evaluation there at the rule -- 2.51.0 ^ permalink raw reply related [flat|nested] 63+ messages in thread
* [PATCH 4/7] doc: add overall description of the ruleset evaluation 2025-09-26 1:52 ` [PATCH 0/7] doc: miscellaneois improvements Christoph Anton Mitterer ` (2 preceding siblings ...) 2025-09-26 1:52 ` [PATCH 3/7] doc: minor improvements with respect to the term “ruleset” Christoph Anton Mitterer @ 2025-09-26 1:52 ` Christoph Anton Mitterer 2025-09-30 11:50 ` Florian Westphal 2025-09-26 1:52 ` [PATCH 5/7] doc: add some more documentation on bitmasks Christoph Anton Mitterer ` (2 subsequent siblings) 6 siblings, 1 reply; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-09-26 1:52 UTC (permalink / raw) To: netfilter-devel; +Cc: Pablo Neira Ayuso Signed-off-by: Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> --- doc/nft.txt | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/doc/nft.txt b/doc/nft.txt index f52b7fef..4bbb6b56 100644 --- a/doc/nft.txt +++ b/doc/nft.txt @@ -560,6 +560,85 @@ 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 (and thus by netfilter), it may + still get discarded by other means, for example Linux generally ignores + various ICMP types and are sysctl options lik + `net.ipv{4,6}.conf.*.forwarding` or `net.ipv4.conf.*.rp_filter`. +* With respect to the evaluation tables don’t matter at all and are not known by + netfilter. + They’re merely used to structure the ruleset. +* Packets traverse the network stack and at various hooks they’re evaluated by + any base chains attached to these hooks. +* Base chains may call regular chains and regular chains may call other regular + chains (via *jump* or *goto* verdicts), in which case evaluation continues in + the called chain. + Base chains themsevlves cannot be called and only chains of the same table can + be called. +* For each hook, the attached chains are evaluated in order of their priorities + (with chains with lower priority values being evaluated before those with + higher values and the order of chains with the same value being undefined). +* An *accept* verdict (including an implict one via the base chain’s policy, + even if caused in certain cases by a *return* verdict) ends the evaluation of + the current base chain and any regular chains called from that. + It accepts the packet only with respect to the current base chain, which does + not mean that the packet is ultimately accepted. + Any other base chain (or regular chain called by such) with a higher priority + of the same hook as well as any other base chain (or regular chain called by + such) of any later hook may still utlimately *deny*/*reject* the packet with + an according verdict (with consequences as described below for + *deny*/*reject*). + Thus and merely from netfilter’s point of view, a packet is only accepted if + none of the chains (regardless of their tables) that are attached to any of + the respectively relevant hooks issues a *deny*/*reject* verdict (be it + explicitly or implicitly by policy) and if there’s at least on *accept* + verdict (be it explicitly or implicitly by policy). + In that, the ordering of the various base chains per hook via their priorities + matters (with respect to the packets utlimate fate) only in so far, if any of + then would modify the packet or its meta data and that has an influence on the + verdicts – if not, the ordering shouldn’t matter (except for performance). +* A *drop*/*reject* verdict (including an implict one via the base chain’s + policy even if caused in certain cases by a *return* verdict) immediately ends + the evaluation of the whole ruleset and ultimately drops/rejects the packet. + Unlike with an *accept* verdict, no further chains of any hook and regardless + of their table get evaluated and it’s therefore not possible to have an + *drop*/*reject* verdict overturned. + Thus, if any base chain uses drop as it’s 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. +* A *jump* verdict causes evaluation to continue at the first rule of the + regular chain it calls. Called chains must be of the same table and cannot be + base chains. + If no other verdict is issued in the called chain and if all rules of that + have been evaluated, evaluation will continue with the next rule after the + calling rule of the calling chain. + That is, reaching the end of the called chain causes a “jump back to the + calling chain” respectively an implicit *return* verdict. + Other verdicts are processed as described above and below. +* A *goto* verdict causes evaluation to continue at the first rule of the + regular chain it calls. Called chains must be of the same table and cannot be + base chains. + If no other verdict is issued in the called chain and if all rules of that + have been evaluated, evaluation of the current base chain and the regular + chains called by it end with an implicit verdict of the base chain’s policy. + That is, unlike with *jump*, reaching the end of the called chain does not + cause a “jump back to the calling chain”. + Other verdicts are processed as described above and below. +* A *return* verdict’s processing depend upon in which chain it is issued. + In a regular chain that was called via *jump* it ends evaluation of that chain + and return to the calling chain as described above. + In a regular chain that was called via *goto* or in a base chain, the *return* + verdict is equivalent to the base chain’s policy. +* All verdicts described above (that is: *accept*, *drop*, *reject*, *jump*, + *goto* and *return*) also end the evaluation of any later statements in their + respective rules (or even cause an error when loadin such rules) with the + exception of the `comment` statement. + That is, for example in `… counter accept` the `counter` statement is + processed, but in `… accept counter` it is not. + SETS ---- nftables offers two kinds of set concepts. Anonymous sets are sets that have no -- 2.51.0 ^ permalink raw reply related [flat|nested] 63+ messages in thread
* Re: [PATCH 4/7] doc: add overall description of the ruleset evaluation 2025-09-26 1:52 ` [PATCH 4/7] doc: add overall description of the ruleset evaluation Christoph Anton Mitterer @ 2025-09-30 11:50 ` Florian Westphal 2025-10-10 23:07 ` Christoph Anton Mitterer 0 siblings, 1 reply; 63+ messages in thread From: Florian Westphal @ 2025-09-30 11:50 UTC (permalink / raw) To: Christoph Anton Mitterer; +Cc: netfilter-devel, Pablo Neira Ayuso Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> wrote: > +OVERALL EVALUATION OF THE RULESET > +--------------------------------- > +This is a summary of how the ruleset is evaluated. > + > +* Even if a packet is accepted by the ruleset (and thus by netfilter), it may > + still get discarded by other means, for example Linux generally ignores > + various ICMP types and are sysctl options lik Minor typo, 'like'. > + `net.ipv{4,6}.conf.*.forwarding` or `net.ipv4.conf.*.rp_filter`. > +* With respect to the evaluation tables don’t matter at all and are not known by > + netfilter. > + They’re merely used to structure the ruleset. 'evaluation ordering'? Tables do matter in the sense that you can't have any chains without them. > +* Packets traverse the network stack and at various hooks they’re evaluated by > + any base chains attached to these hooks. Maybe add the hook names here? (preouting, input, and so on). Or a reference to this topic. > +* For each hook, the attached chains are evaluated in order of their priorities > + (with chains with lower priority values being evaluated before those with > + higher values and the order of chains with the same value being undefined). It took me a sec to parse this, maybe: ... higher values. The order of chains with identical priorities is undefined. (or similar). > +* An *accept* verdict (including an implict one via the base chain’s policy, > + even if caused in certain cases by a *return* verdict) ends the evaluation of > + the current base chain and any regular chains called from that. > + It accepts the packet only with respect to the current base chain, which does > + not mean that the packet is ultimately accepted. Maybe: 'It accepts the packet only with respect to the current base chain.' The rest is already made clear by your next sentence: > + Any other base chain (or regular chain called by such) with a higher priority > + of the same hook as well as any other base chain (or regular chain called by > + such) of any later hook may still utlimately *deny*/*reject* the packet with ~~~~~~~ ultimately, but i'd just remove this word. please avoid 'deny' and use 'drop' everywhere. In this case, .. may still drop the packet. ... is enough. > + Thus and merely from netfilter’s point of view, a packet is only accepted if > + none of the chains (regardless of their tables) that are attached to any of > + the respectively relevant hooks issues a *deny*/*reject* verdict (be it > + explicitly or implicitly by policy) and if there’s at least on *accept* > + verdict (be it explicitly or implicitly by policy). I'm not sure if the last part is needed as there is no such thing as a base chain without a policy, so i would simplify this to: Thus a packet is only accepted if no chain or rule issues a drop verdict, including chain policies. > + In that, the ordering of the various base chains per hook via their priorities > + matters (with respect to the packets utlimate fate) only in so far, if any of > + then would modify the packet or its meta data and that has an influence on the > + verdicts – if not, the ordering shouldn’t matter (except for performance). I'm not sure about this paragraph. While its correct, base chains and their priorities still have effects, e.g. if ip defragmentation has taken place or not. I think the previous paragraph is clear enough wrt. packet acceptance. > +* A *drop*/*reject* verdict (including an implict one via the base chain’s > + policy even if caused in certain cases by a *return* verdict) immediately ends > + the evaluation of the whole ruleset and ultimately drops/rejects the packet. > + Unlike with an *accept* verdict, no further chains of any hook and regardless > + of their table get evaluated and it’s therefore not possible to have an > + *drop*/*reject* verdict overturned. As noted elsewhere, reject is just a more fancy drop, it should not be mentioned here. > + Thus, if any base chain uses drop as it’s 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. Can that be reduced to something like: Thus, base chains that use 'policy drop' must contain at least one accept rule or must call another chain with an accept rule to avoid blocking all traffic. > +* A *jump* verdict causes evaluation to continue at the first rule of the > + regular chain it calls. Called chains must be of the same table and cannot be > + base chains. 'must reside in same table'? > + If no other verdict is issued in the called chain and if all rules of that .. chain... ? > + have been evaluated, evaluation will continue with the next rule after the > + calling rule of the calling chain. > + That is, reaching the end of the called chain causes a “jump back to the > + calling chain” respectively an implicit *return* verdict. Yes, this is why there is sometimes a reference to the call stack / or chain stack. 'jump' makes a 'where am I' note, 'goto' doesn't. > +* A *goto* verdict causes evaluation to continue at the first rule of the > + regular chain it calls. Called chains must be of the same table and cannot be > + base chains. > + If no other verdict is issued in the called chain and if all rules of that > + have been evaluated, evaluation of the current base chain and the regular > + chains called by it end with an implicit verdict of the base chain’s policy. > + That is, unlike with *jump*, reaching the end of the called chain does not > + cause a “jump back to the calling chain”. I think we should try to simplify this. jump and goto are almost the same, the only difference is that goto doesn't make a 'where am I' note, so the evaluation of the *called* chain behaves as if those rules were part of the chain that contains the 'goto' statement. > +* A *return* verdict’s processing depend upon in which chain it is issued. Hmm. Not so sure. Its always the same: End evaluation of this chain and go back to where you came from. > + In a regular chain that was called via *jump* it ends evaluation of that chain > + and return to the calling chain as described above. Right. > + In a regular chain that was called via *goto* or in a base chain, the *return* > + verdict is equivalent to the base chain’s policy. Yes, but thats because the called chain executes in the context of the calling chain. Since thats a base chain, the return statement causes a chain-stack-underflow. It has same effect as 'return' in ANY chain: 1. End evaluation of the chain. 2. Go back to where you cam from. We have nowhere else to go back to. So all of those are identical: chain base { hook ... ... # no more rules, implicit return -> base chain policy } ---- chain base { hook ... ... return # variant of above with explicit return } ---- chain user { return } chain base { hook ... goto user } ---- All end up with 'no terminal statement seen and no more rules to check'. > +* All verdicts described above (that is: *accept*, *drop*, *reject*, *jump*, > + *goto* and *return*) also end the evaluation of any later statements in their > + respective rules (or even cause an error when loadin such rules) with the > + exception of the `comment` statement. Why mention the comment statement here? Comment is special, its not a statement from the evaluation perspective. It tells the kernel to allocate some extra space to store the comment data, the interpreter doesn't know its there. > + That is, for example in `… counter accept` the `counter` statement is > + processed, but in `… accept counter` it is not. I think this was already mentioned, also, nft should already be informing the user that the rule has unreachable trailing statements. ^ permalink raw reply [flat|nested] 63+ messages in thread
* Re: [PATCH 4/7] doc: add overall description of the ruleset evaluation 2025-09-30 11:50 ` Florian Westphal @ 2025-10-10 23:07 ` Christoph Anton Mitterer 0 siblings, 0 replies; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-10-10 23:07 UTC (permalink / raw) To: Florian Westphal; +Cc: netfilter-devel, Pablo Neira Ayuso Hey. Sorry for the delay... but I've been travelling to some science conference. On Tue, 2025-09-30 at 13:50 +0200, Florian Westphal wrote: > > + various ICMP types and are sysctl options lik > > Minor typo, 'like'. Thx for catching. > > + `net.ipv{4,6}.conf.*.forwarding` or `net.ipv4.conf.*.rp_filter`. > > +* With respect to the evaluation tables don’t matter at all and > > are not known by > > + netfilter. > > + They’re merely used to structure the ruleset. > > 'evaluation ordering'? Tables do matter in the sense that you can't > have any chains without them. What would you think about merely adding a "," after "evaluation"? "Evaluation ordering" could be understood as implicitly meaning that it doesn't have an effect on the ordering of the evaluations, but might still have some other effect (from netfilter's PoV). > > +* Packets traverse the network stack and at various hooks they’re > > evaluated by > > + any base chains attached to these hooks. > > Maybe add the hook names here? (preouting, input, and so on). > Or a reference to this topic. I've added a reference to the ADDRESS_FAMILIES chapter. One may argue now whether this defeats the purpose of the summary chapter, if one has again to look things up somewhere else. I have no strong opinion in this case, but I would say that a user realises quite soon what hooks are (in particular input/output/forward are probably well known)... so I don't think it's necessary to explain here *what* they are,... but it's good to give a reference to the full list, so that a reader can cross check whether some foobar term he stumbled over is actually a hook or not. > > +* For each hook, the attached chains are evaluated in order of > > their priorities > > + (with chains with lower priority values being evaluated before > > those with > > + higher values and the order of chains with the same value being > > undefined). > > It took me a sec to parse this, maybe: > > ... higher values. The order of chains with identical priorities is > undefined. > > (or similar). Also disliked that sentence when I wrote it,... but found nothing better where I could keep it in parentheses. I removed those now and made two proper sentences. Also made an analogous change in patch 1 of this series. > > +* An *accept* verdict (including an implict one via the base > > chain’s policy, > > + even if caused in certain cases by a *return* verdict) ends the > > evaluation of > > + the current base chain and any regular chains called from that. > > + It accepts the packet only with respect to the current base > > chain, which does > > + not mean that the packet is ultimately accepted. > > Maybe: 'It accepts the packet only with respect to the current base > chain.' The rest is already made clear by your next sentence: done > > + Any other base chain (or regular chain called by such) with a > > higher priority > > + of the same hook as well as any other base chain (or regular > > chain called by > > + such) of any later hook may still utlimately *deny*/*reject* the > > packet with > ~~~~~~~ ultimately, but i'd just > remove this word. I thought it might help to reiterate that drop - unlike accept - is in fact ultimate, ... in particular to point out, that it can't be overridden by a later accept in another chain. Have merely fixed the typo for now, waiting for further feedback, whether I shall remove it altogether. > please avoid 'deny' and use 'drop' everywhere. In this case, > .. may still drop the packet. Mistakenly used... must have mixed up drop and deny at some point. O:-) > > + Thus and merely from netfilter’s point of view, a packet is only > > accepted if > > + none of the chains (regardless of their tables) that are > > attached to any of > > + the respectively relevant hooks issues a *deny*/*reject* verdict > > (be it > > + explicitly or implicitly by policy) and if there’s at least on > > *accept* > > + verdict (be it explicitly or implicitly by policy). > > I'm not sure if the last part is needed as there is no such thing as > a > base chain without a policy, so i would simplify this to: > > Thus a packet is only accepted if no chain or rule issues a drop > verdict, including chain policies. In principle yes, but that's IMO already "quite some" conclusions that a novice reader would need to draw, i.e. the combination of: - no drops by any mean plus - all chains have a policy plus - policy is any of drop/accept thus, there must be at least one accept, if nothing drops. In teaching, which this chapter is in principle meant for, I'd rather keep things as simple as possible not requiring the reader to draw implicit conclusions. I changed it however a bit to make it more clear that "at least one accept" is already implied by "nothing drops". If you still want it to removed, just tell. > > + In that, the ordering of the various base chains per hook via > > their priorities > > + matters (with respect to the packets utlimate fate) only in so > > far, if any of > > + then would modify the packet or its meta data and that has an > > influence on the > > + verdicts – if not, the ordering shouldn’t matter (except for > > performance). > > I'm not sure about this paragraph. While its correct, base chains > and > their priorities still have effects, e.g. if ip defragmentation has > taken place or not. I think the previous paragraph is clear enough > wrt. > packet acceptance. What I wanted to say with that paragraph was effectively: The ordering of chains via priorities, doesn't matter with respect to whether a packet is ultimately accepted or dropped. Except of course, an earlier chain modifies something on the packet, which no longer causes it to be dropped/accepted. While that is of course a logical conclusion from the earlier text, I think it's nevertheless good to have it explicitly mentioned. E.g. when I transitioned to nftables and looked at what fail2ban does when using its nftables backend, I was first a bit puzzled when I saw that it uses a chain priority of -1. At a first glance I thought +1 would be better so that my normal filter table could "fast-accept" e.g. all related,established packets and traversing the (potentially long) fail2ban chain would be short-cut. Of course I then realised that this is not really how it works, and short-circuiting is only possibly via drop/reject/etc.. > > +* A *drop*/*reject* verdict (including an implict one via the base > > chain’s > > + policy even if caused in certain cases by a *return* verdict) > > immediately ends > > + the evaluation of the whole ruleset and ultimately drops/rejects > > the packet. > > + Unlike with an *accept* verdict, no further chains of any hook > > and regardless > > + of their table get evaluated and it’s therefore not possible to > > have an > > + *drop*/*reject* verdict overturned. > > As noted elsewhere, reject is just a more fancy drop, it should not > be > mentioned here. I've reworked the integration of *reject* and verdict-like statements everywhere. In some places, like the one here where I describe drop, I still mention them, though less prominent. Simply because I think it's helpful if people get told at the important places, that reject/etc. effectively behave like drop (by internally issuing it). Just have a look whether you're fine with the way I did it, and if you still think it should be changed, we can of course reiterate. > > + Thus, if any base chain uses drop as it’s 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. > > Can that be reduced to something like: > > Thus, base chains that use 'policy drop' must contain at least one > accept rule or > must call another chain with an accept rule to avoid blocking all > traffic. That seems a bit imprecise to me? The sections describe things from the view of a single packet, but you're now referring to all traffic? Also, "must contain/call with"... isn't really exactly true, is it?! I mean the intention may just be to do that. > > +* A *jump* verdict causes evaluation to continue at the first rule > > of the > > + regular chain it calls. Called chains must be of the same table > > and cannot be > > + base chains. > > 'must reside in same table'? "reside" feels also awkward. What about "belong to" or "be from" (which I'd have chosen now)? > > + If no other verdict is issued in the called chain and if all > > rules of that > > .. chain... ? I've completely changed that part, so this is gone anyway. > > + have been evaluated, evaluation will continue with the next rule > > after the > > + calling rule of the calling chain. > > + That is, reaching the end of the called chain causes a “jump > > back to the > > + calling chain” respectively an implicit *return* verdict. > > Yes, this is why there is sometimes a reference to the call stack / > or > chain stack. > > 'jump' makes a 'where am I' note, 'goto' doesn't. I think I'm described that now correctly. Same with respect to your other comments, that were about my wrong description of how the returning from jump/goto works. Please check :-) > > +* All verdicts described above (that is: *accept*, *drop*, > > *reject*, *jump*, > > + *goto* and *return*) also end the evaluation of any later > > statements in their > > + respective rules (or even cause an error when loadin such rules) > > with the > > + exception of the `comment` statement. > > Why mention the comment statement here? > > Comment is special, its not a statement from the evaluation > perspective. It tells the kernel to allocate some extra space to > store the comment data, the interpreter doesn't know its there. Well, we've just described in that text, that any later statements will be ignored. A user doesn't really know whether or not comment is special and since it's actually not just a comment with respect to the nftables rules files, but really stored in the kernel memory - as you say - ... it's IMO not really obvious whether this happens e.g. in a rule like: drop comment "bar" The mentioning of "comment" (which btw. I've changed now slightly) is to explain just this - i.e. comment is *not* ignored, even if after the terminal statement. I could explain that with an example like: > drop comment "foo" >is the same as > comment "foo" drop but I thought that's overkill... if no, just tell me. > > + That is, for example in `… counter accept` the `counter` > > statement is > > + processed, but in `… accept counter` it is not. > > I think this was already mentioned It's already mentioned, but only in the VERDICTS chapter. So I think it should be re-iterated in the summary chapter. > , also, nft should already be > informing the user that the rule has unreachable trailing statements. This actually might be a valid reason to remove it, but: - Is this really enforced in all cases (or are there some statements where nftables would ignore it but not error out) - Could that behaviour (of nftables) ever change? Like by adding a --only-warn-on-ignored-statements option? In that case, we should IMO leave it in. - What about beyond-the-nftables-utility? E.g. if someone uses libnftnl or nftables from python... would it still be guaranteed to error out? Cause if that's only a mechanism in the utility, we should IMO still describe the true behaviour (of ignoring such statements) because users likely use the nft manpage for their education. Thanks, Chris. PS: v2 of the patch series will follow in a few minutes. ^ permalink raw reply [flat|nested] 63+ messages in thread
* [PATCH 5/7] doc: add some more documentation on bitmasks 2025-09-26 1:52 ` [PATCH 0/7] doc: miscellaneois improvements Christoph Anton Mitterer ` (3 preceding siblings ...) 2025-09-26 1:52 ` [PATCH 4/7] doc: add overall description of the ruleset evaluation Christoph Anton Mitterer @ 2025-09-26 1:52 ` Christoph Anton Mitterer 2025-09-30 11:51 ` Florian Westphal 2025-09-26 1:52 ` [PATCH 6/7] doc: describe include’s collation order to be that of the C locale Christoph Anton Mitterer 2025-09-26 1:52 ` [PATCH 7/7] doc: describe how values match sets Christoph Anton Mitterer 6 siblings, 1 reply; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-09-26 1:52 UTC (permalink / raw) To: netfilter-devel; +Cc: Pablo Neira Ayuso Signed-off-by: Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> --- doc/data-types.txt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/doc/data-types.txt b/doc/data-types.txt index 18af266a..47a0d25a 100644 --- a/doc/data-types.txt +++ b/doc/data-types.txt @@ -26,6 +26,22 @@ integer The bitmask type (*bitmask*) is used for bitmasks. +In expressions the bits of a bitmask may be specified as *'bit'[,'bit']...* with +'bit' being the value of the bit or a pre-defined symbolic constant, if any (for +example *ct state*’s bit 0x1 has the symbolic constant `new`). + +Equality of a value with such bitmask is given, if the value has any of the +bitmask’s bits set (and optionally others). + +The syntax *'expression' 'value' / 'mask'* is identical to +*'expression' and 'mask' == 'value'*. +For example `tcp flags syn,ack / syn,ack,fin,rst` is the same as +`tcp flags and (syn|ack|fin|rst) == syn|ack`. + +It should further be noted that *'expression' 'bit'[,'bit']...* is not the same +as *'expression' {'bit'[,'bit']...}*. + + STRING TYPE ~~~~~~~~~~~~ [options="header"] -- 2.51.0 ^ permalink raw reply related [flat|nested] 63+ messages in thread
* Re: [PATCH 5/7] doc: add some more documentation on bitmasks 2025-09-26 1:52 ` [PATCH 5/7] doc: add some more documentation on bitmasks Christoph Anton Mitterer @ 2025-09-30 11:51 ` Florian Westphal 2025-09-30 11:53 ` Florian Westphal 0 siblings, 1 reply; 63+ messages in thread From: Florian Westphal @ 2025-09-30 11:51 UTC (permalink / raw) To: Christoph Anton Mitterer; +Cc: netfilter-devel, Pablo Neira Ayuso Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> wrote: > +It should further be noted that *'expression' 'bit'[,'bit']...* is not the same > +as *'expression' {'bit'[,'bit']...}*. Yes, but i think in this case it should also tell why. ^ permalink raw reply [flat|nested] 63+ messages in thread
* Re: [PATCH 5/7] doc: add some more documentation on bitmasks 2025-09-30 11:51 ` Florian Westphal @ 2025-09-30 11:53 ` Florian Westphal 0 siblings, 0 replies; 63+ messages in thread From: Florian Westphal @ 2025-09-30 11:53 UTC (permalink / raw) To: Christoph Anton Mitterer; +Cc: netfilter-devel, Pablo Neira Ayuso Florian Westphal <fw@strlen.de> wrote: > Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> wrote: > > +It should further be noted that *'expression' 'bit'[,'bit']...* is not the same > > +as *'expression' {'bit'[,'bit']...}*. > > Yes, but i think in this case it should also tell why. Ah, i see its coming in a followup patch, never mind then. Maybe add a reference to it. ^ permalink raw reply [flat|nested] 63+ messages in thread
* [PATCH 6/7] doc: describe include’s collation order to be that of the C locale 2025-09-26 1:52 ` [PATCH 0/7] doc: miscellaneois improvements Christoph Anton Mitterer ` (4 preceding siblings ...) 2025-09-26 1:52 ` [PATCH 5/7] doc: add some more documentation on bitmasks Christoph Anton Mitterer @ 2025-09-26 1:52 ` Christoph Anton Mitterer 2025-09-26 1:52 ` [PATCH 7/7] doc: describe how values match sets Christoph Anton Mitterer 6 siblings, 0 replies; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-09-26 1:52 UTC (permalink / raw) To: netfilter-devel; +Cc: Pablo Neira Ayuso Currently, `nft` doesn’t call `setlocale(3)` and thus `glob(3)` uses the `C` locale. Document this as it’s possibly relevant to the ordering of included rules. This also makes the collation order “official” so any future localisation would need to adhere to that. Signed-off-by: Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> --- doc/nft.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/nft.txt b/doc/nft.txt index 4bbb6b56..899c38d6 100644 --- a/doc/nft.txt +++ b/doc/nft.txt @@ -165,8 +165,8 @@ Include statements support the usual shell wildcard symbols (*,?,[]). Having no matches for an include statement is not an error, if wildcard symbols are used in the include statement. This allows having potentially empty include directories for statements like **include "/etc/firewall/rules/*"**. The wildcard -matches are loaded in alphabetical order. Files beginning with dot (.) are not -matched by include statements. +matches are loaded in the collation order of the C locale. Files beginning with +dot (.) are not matched by include statements. SYMBOLIC VARIABLES ~~~~~~~~~~~~~~~~~~ -- 2.51.0 ^ permalink raw reply related [flat|nested] 63+ messages in thread
* [PATCH 7/7] doc: describe how values match sets 2025-09-26 1:52 ` [PATCH 0/7] doc: miscellaneois improvements Christoph Anton Mitterer ` (5 preceding siblings ...) 2025-09-26 1:52 ` [PATCH 6/7] doc: describe include’s collation order to be that of the C locale Christoph Anton Mitterer @ 2025-09-26 1:52 ` Christoph Anton Mitterer 6 siblings, 0 replies; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-09-26 1:52 UTC (permalink / raw) To: netfilter-devel; +Cc: Pablo Neira Ayuso Signed-off-by: Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> --- doc/nft.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/doc/nft.txt b/doc/nft.txt index 899c38d6..7e75381d 100644 --- a/doc/nft.txt +++ b/doc/nft.txt @@ -741,6 +741,16 @@ Example: When the set contains range *1.2.3.1-1.2.3.4*, then adding element *1.2 effect. Adding *1.2.3.5* changes the existing range to cover *1.2.3.1-1.2.3.5*. Without this flag, *1.2.3.2* can not be added and *1.2.3.5* is inserted as a new entry. +Equality of a value with a set is given if the value matches exactly one value +in the set. +It shall be noted that for bitmask values this means, that +*'expression' 'bit'[,'bit']...* (which yields true if *any* of the bits are set) +is not the same as *'expression' {'bit'[,'bit']...}* (which yields true if +exactly one of the bits are set). +It may however be (effectively) the same, in cases like +`ct state established,related` and `ct state {established,related}`, where these +states are mutually exclusive. + MAPS ----- [verse] -- 2.51.0 ^ permalink raw reply related [flat|nested] 63+ messages in thread
* Re: nft manpage/wiki issues and improvement ideas 2025-09-25 7:35 ` Pablo Neira Ayuso 2025-09-25 20:37 ` Christoph Anton Mitterer 2025-09-26 1:52 ` [PATCH 0/7] doc: miscellaneois improvements Christoph Anton Mitterer @ 2025-09-26 2:32 ` Christoph Anton Mitterer 2 siblings, 0 replies; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-09-26 2:32 UTC (permalink / raw) To: Pablo Neira Ayuso; +Cc: netfilter-devel Hey. There's one more... observation (bug?): What I've been trying to do was, come up with a base rule file (which basically allows nothing inbound except established,related and some limited ICMP but everything output). Like so (but other than the include in output it's not really interesting): #!/usr/sbin/nft -f flush ruleset table inet filter { set deny-icmp_types { type icmp_type elements = {source-quench, redirect, echo-request, router-advertisement, router-solicitation, timestamp-request, timestamp-reply, info-request, info-reply, address-mask-request, address-mask-reply} } set deny-icmpv6_types { type icmpv6_type elements = {echo-request, nd-redirect} } chain input { type filter hook input priority filter policy drop ct state established,related accept ct state invalid drop iifname lo accept icmp type @deny-icmp_types drop icmpv6 type @deny-icmpv6_types drop meta l4proto {icmp, ipv6-icmp} accept include "/etc/nftables/rules.d/*.nft" } chain output { type filter hook output priority filter policy accept ct state invalid drop ##oifname lo accept } chain forward { type filter hook forward priority filter policy drop ct state invalid drop } } Now, in /etc/nftables/rules.d/*.nft I would have placed template files like e.g.: allow-echo-request.nft: delete element inet filter deny-icmp_types {echo-request} delete element inet filter deny-icmpv6_types {echo-request} So one could do configuration based on whether the files are there or not, making it easy to update (instead of having one big monolithic file on some hundreds of university nodes). I also have e.g.: 50-accept-new-tcp-dports.nft: table inet filter { set accept-new-tcp-dports { type inet_service } } ct state new tcp dport @accept-new-tcp-dports accept Which merely adds a "framework" rule and then requires other files that actually adds ports to the set. So I could have one for SSH... and a gazillion for dCache (a mass storage management system used in many big science collaborations, LHC in my case). But... these includes fails miserably. ;-) E.g. for allow-echo-request.nft: In file included from /etc/nftables/rules.nft:38:3-40: /etc/nftables/rules.d/allow-echo-request.nft:4:8-14: Error: syntax error, unexpected element, expecting @ or '$' delete element inet filter deny-icmp_types {echo-request} ^^^^^^^ And for 50-accept-new-tcp-dports.nft: /etc/nftables/rules.d/50-accept-new-tcp-dports.nft:xxx: Error: syntax error, unexpected table table inet filter { ^^^^^ Which I can understand, but it does so even if I remove the surrounding table inet filter: In file included from /etc/nftables/rules.nft:xxx: /etc/nftables/rules.d/50-accept-new-tcp-dports.nft:xxx: Error: syntax error, unexpected string, expecting add or update or delete set accept-new-tcp-dports { ^^^^^^^^^^^^^^^^^^^^^ at least that I'd have kinda expected to work. So question is: What of all that should work? Or is there some special trick one needs to do when doing an include XXX inside some { } block? Or may include rather only be used on the "top level"? Thanks, Chris. ^ permalink raw reply [flat|nested] 63+ messages in thread
* [PATCH v2 0/7] doc: miscellaneous improvements 2025-09-25 0:07 nft manpage/wiki issues and improvement ideas Christoph Anton Mitterer 2025-09-25 7:35 ` Pablo Neira Ayuso @ 2025-10-11 0:23 ` Christoph Anton Mitterer 2025-10-11 0:23 ` [PATCH v2 1/7] doc: clarify evaluation of chains Christoph Anton Mitterer ` (6 more replies) 2025-10-19 1:38 ` [PATCH v3 0/6] doc: miscellaneous improvements Christoph Anton Mitterer 2025-10-20 23:49 ` [PATCH v4 0/5] doc: miscellaneous improvements Christoph Anton Mitterer 3 siblings, 7 replies; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-10-11 0:23 UTC (permalink / raw) To: netfilter-devel; +Cc: fw, pablo Hey. This is v2 of the series. Thanks to Florian for his review and helpful comments on v1. Given you've made significant contributions I'd be happy to add your Signed-off-by to the commits, if desired so. I have taken most of your comments into account, for those where I didn't (yet) I've explained so in the mails I've sent a bit earlier. Of course if you still think that something should be changed, just tell. :-) Also in these mails were a few further questions, which someone can hopefully answer, so most likely there'll be a v3 of this series (to the least). Thanks and best wishes, Chris. ^ permalink raw reply [flat|nested] 63+ messages in thread
* [PATCH v2 1/7] doc: clarify evaluation of chains 2025-10-11 0:23 ` [PATCH v2 0/7] doc: miscellaneous improvements Christoph Anton Mitterer @ 2025-10-11 0:23 ` Christoph Anton Mitterer 2025-10-15 11:46 ` Florian Westphal 2025-10-11 0:23 ` [PATCH v2 2/7] doc: fix/improve documentation of verdicts Christoph Anton Mitterer ` (5 subsequent siblings) 6 siblings, 1 reply; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-10-11 0:23 UTC (permalink / raw) To: netfilter-devel; +Cc: fw, pablo In particular: - Mention that grouping of chains in tables is irrelevant to the evaluation order. - Clarify that priorities only define the ordering of chains per hook. - Improved potentially ambiguous wording “lower priority values have precedence over higher ones”, which could be mistaken as that rules from lower priority chains might “win” over such from higher ones (which is however only the case if they drop/reject packets). The new wording merely describes which chains are evaluated first, implicitly referring the question which verdict “wins” to the section where verdicts are described, and also should work when lower priority chains mangle packets (in which case they might actually be considered as having “precedence”). Link: https://lore.kernel.org/netfilter-devel/3c7ddca7029fa04baa2402d895f3a594a6480a3a.camel@scientia.org/T/#t Signed-off-by: Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> --- doc/nft.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/nft.txt b/doc/nft.txt index 87129819..88c08618 100644 --- a/doc/nft.txt +++ b/doc/nft.txt @@ -453,8 +453,10 @@ interface specified in the *device* parameter. The *priority* parameter accepts a signed integer value or a standard priority name which specifies the order in which chains with the same *hook* value are -traversed. The ordering is ascending, i.e. lower priority values have precedence -over higher ones. +traversed (regardless of the table to which they belong). The ordering is +ascending, i.e. per hook, chains with lower priority values are evaluated before +such with higher ones. The ordering of any with the same priority value is +undefined. With *nat* type chains, there's a lower excluding limit of -200 for *priority* values, because conntrack hooks at this priority and NAT requires it. -- 2.51.0 ^ permalink raw reply related [flat|nested] 63+ messages in thread
* Re: [PATCH v2 1/7] doc: clarify evaluation of chains 2025-10-11 0:23 ` [PATCH v2 1/7] doc: clarify evaluation of chains Christoph Anton Mitterer @ 2025-10-15 11:46 ` Florian Westphal 0 siblings, 0 replies; 63+ messages in thread From: Florian Westphal @ 2025-10-15 11:46 UTC (permalink / raw) To: Christoph Anton Mitterer; +Cc: netfilter-devel, pablo Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> wrote: > In particular: > - Mention that grouping of chains in tables is irrelevant to the evaluation > order. Applied this one with minor edits, thanks! ^ permalink raw reply [flat|nested] 63+ messages in thread
* [PATCH v2 2/7] doc: fix/improve documentation of verdicts 2025-10-11 0:23 ` [PATCH v2 0/7] doc: miscellaneous improvements Christoph Anton Mitterer 2025-10-11 0:23 ` [PATCH v2 1/7] doc: clarify evaluation of chains Christoph Anton Mitterer @ 2025-10-11 0:23 ` Christoph Anton Mitterer 2025-10-15 11:42 ` Florian Westphal 2025-10-11 0:23 ` [PATCH v2 3/7] doc: minor improvements with respect to the term “ruleset” Christoph Anton Mitterer ` (4 subsequent siblings) 6 siblings, 1 reply; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-10-11 0:23 UTC (permalink / raw) To: netfilter-devel; +Cc: fw, pablo - 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. - Overhaul the description of `jump`/`goto`/`return`. `jump` only explains what the statement causes from the point of view of the new chain (that is: not, how the returning works), which includes that an implicit `return` is issued at the end of the chain. `goto` is explained in reference to `jump`. `return` describes abstractly how the return position is determined and what happens if there’s no position to return to (but not for example where an implicit `return` is issued). - Various other minor improvements/clarifications to wording. - List and explain verdict-like statements like `reject` which internally imply `accept` or `drop`. Further explain that with respect to evaluation these behave like their respectively implied verdicts. Link: https://lore.kernel.org/netfilter-devel/3c7ddca7029fa04baa2402d895f3a594a6480a3a.camel@scientia.org/T/#t Signed-off-by: Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> --- doc/statements.txt | 84 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 58 insertions(+), 26 deletions(-) diff --git a/doc/statements.txt b/doc/statements.txt index 6226713b..f812dec8 100644 --- a/doc/statements.txt +++ b/doc/statements.txt @@ -1,6 +1,7 @@ -VERDICT STATEMENT -~~~~~~~~~~~~~~~~~ -The verdict statement alters control flow in the ruleset and issues policy decisions for packets. +VERDICT STATEMENTS +~~~~~~~~~~~~~~~~~~ +The verdict statements alter control flow in the ruleset and issue policy +decisions for packets. [verse] ____ @@ -10,40 +11,71 @@ ____ 'CHAIN' := 'chain_name' | *{* 'statement' ... *}* ____ -*accept* and *drop* are absolute verdicts -- they terminate ruleset evaluation immediately. +*accept* and *drop* are absolute verdicts, which immediately terminate the +evaluation of the current rule, i.e. even any later statements of the current +rule won’t get executed. + +.*counter* will get executed: +------------------------------ +… counter accept +------------------------------ + +.*counter* won’t get executed: +------------------------------ +… accept counter +------------------------------ + +Further: [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 the evaluation of the current base chain (and any regular +chains called from it) and accept the packet from their point of view. +The packet may however still be dropped by either another chain with a higher +priority of the same hook or any chain of a later hook. +For example, an *accept* in a chain of the *forward* hook still allows one to +*drop* (or *reject*, etc.) the packet in another *forward* hook base chain (and +any regular chains called from it) that has a higher priority number as well as +later in a chain of the *postrouting* hook. +*drop*:: Terminate ruleset evaluation and drop the packet. This occurs +instantly, no further chains of any hooks are evaluated and it is thus not +possible to again accept the packet in a higher priority or later chain, as +those are not evaluated anymore for the packet. +*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. + When an absolute verdict is issued (respectively implied by a verdict-like + statement) in 'CHAIN', evaluation terminates as described above. +*goto* 'CHAIN':: Equal to *jump* except that the current position is not stored + in the call stack of chains. +*return*:: End evaluation of the current chain, pop the most recently added + position from the call stack of chains and continue evaluation 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) end evaluation of the current base chain (and any regular chains + called from it) using the base chain’s policy as implicit verdict. +*continue*:: Continue ruleset evaluation with the next rule. This + is the default behaviour in case a rule issues no verdict. *queue*:: Terminate ruleset evaluation and queue the packet to userspace. Userspace must provide a drop or accept verdict. In case of accept, processing resumes with the next base chain hook, not the rule following the queue verdict. -*continue*:: Continue ruleset evaluation with the next rule. This - is the default behaviour in case a rule issues no verdict. -*return*:: Return from the current chain and continue evaluation at the - next rule in the last chain. If issued in a base chain, it is equivalent to the - base chain policy. -*jump* 'CHAIN':: Continue evaluation at the first rule in 'CHAIN'. The current - position in the ruleset is pushed to a call stack and evaluation will continue - there when the new chain is entirely evaluated or a *return* verdict is issued. - In case an absolute verdict is issued by a rule in the chain, ruleset evaluation - terminates immediately and the specific action is taken. -*goto* 'CHAIN':: Similar to *jump*, but the current position is not pushed to the - call stack, meaning that after the new chain evaluation will continue at the last - chain instead of the one containing the goto statement. An alternative to specifying the name of an existing, regular chain in 'CHAIN' is to specify an anonymous chain ad-hoc. Like with anonymous sets, it can't be referenced from another rule and will be removed along with the rule containing it. +All the above applies analogously to statements that imply a verdict: +*redirect*, *dnat*, *snat* and *masquerade* internally issue eventually an +*accept* verdict. +*reject* and *synproxy* internally issue eventually a *drop* verdict. +These statements thus behave like their implied verdicts, but with side effects. + +For example, a *reject* also immediately terminates the evaluation of the +current rule, overrules any *accept* from any other chains and can itself not be +overruled, while the various NAT statements may be overruled by other *drop* +verdicts respectively statements that imply this. + .Using verdict statements ------------------- # process packets from eth0 and the internal network in from_lan -- 2.51.0 ^ permalink raw reply related [flat|nested] 63+ messages in thread
* Re: [PATCH v2 2/7] doc: fix/improve documentation of verdicts 2025-10-11 0:23 ` [PATCH v2 2/7] doc: fix/improve documentation of verdicts Christoph Anton Mitterer @ 2025-10-15 11:42 ` Florian Westphal 2025-10-17 2:30 ` Christoph Anton Mitterer 0 siblings, 1 reply; 63+ messages in thread From: Florian Westphal @ 2025-10-15 11:42 UTC (permalink / raw) To: Christoph Anton Mitterer; +Cc: netfilter-devel, pablo Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> wrote: > +*accept*:: Terminate the evaluation of the current base chain (and any regular > +chains called from it) and accept the packet from their point of view. Suggest: *accept*:: Terminate the evaluation of the chain. Evaluation continues in the next base chain, if any. > +The packet may however still be dropped by either another chain with a higher > +priority of the same hook or any chain of a later hook. ... This means the packet can still be dropped ... > +For example, an *accept* in a chain of the *forward* hook still allows one to > +*drop* (or *reject*, etc.) the packet in another *forward* hook base chain (and > +any regular chains called from it) that has a higher priority number as well as > +later in a chain of the *postrouting* hook. Thanks, that example is good to have. > +*drop*:: Terminate ruleset evaluation and drop the packet. This occurs > +instantly, no further chains of any hooks are evaluated and it is thus not > +possible to again accept the packet in a higher priority or later chain, as > +those are not evaluated anymore for the packet. Can this be compacted a bit? I feel this is a tad too verbose. *drop*: Packet is dropped immediately. No futher evaluation of any kind. I think thats enough, no? > +*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. > + When an absolute verdict is issued (respectively implied by a verdict-like > + statement) in 'CHAIN', evaluation terminates as described above. > +*goto* 'CHAIN':: Equal to *jump* except that the current position is not stored > + in the call stack of chains. > +*return*:: End evaluation of the current chain, pop the most recently added > + position from the call stack of chains and continue evaluation 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) end evaluation of the current base chain (and any regular chains > + called from it) using the base chain’s policy as implicit verdict. > +*continue*:: Continue ruleset evaluation with the next rule. This > + is the default behaviour in case a rule issues no verdict. > *queue*:: Terminate ruleset evaluation and queue the packet to userspace. > Userspace must provide a drop or accept verdict. In case of accept, processing > resumes with the next base chain hook, not the rule following the queue verdict. > +All the above applies analogously to statements that imply a verdict: > +*redirect*, *dnat*, *snat* and *masquerade* internally issue eventually an > +*accept* verdict. You can remove 'eventually'. > +*reject* and *synproxy* internally issue eventually a *drop* verdict. Same. > +These statements thus behave like their implied verdicts, but with side effects. > > +For example, a *reject* also immediately terminates the evaluation of the > +current rule, overrules any *accept* from any other chains No, not really. There is no *overrule*. We don't keep any 'verdict state'. There is no difference between 'drop' in the first rule of the first ever base chain or a drop somewhere later in the pipeline, aside from side effects from other matching expressions. I would suggest: For example, *reject* is like *drop*, but will attempt to send a error reply packet back to the sender before doing so. > +overruled, while the various NAT statements may be overruled by other *drop* > +verdicts respectively statements that imply this. There is no overrule. I would not mention NAT at all here. *accept* documentation already says that later chains in the pipeline can drop the packet (and so could the traffic scheduler, qdisc, NIC, network ...) ^ permalink raw reply [flat|nested] 63+ messages in thread
* Re: [PATCH v2 2/7] doc: fix/improve documentation of verdicts 2025-10-15 11:42 ` Florian Westphal @ 2025-10-17 2:30 ` Christoph Anton Mitterer 2025-10-18 13:25 ` Florian Westphal 0 siblings, 1 reply; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-10-17 2:30 UTC (permalink / raw) To: Florian Westphal; +Cc: netfilter-devel, pablo Hey. Thanks for already merging the first patch of the series :-) On Wed, 2025-10-15 at 13:42 +0200, Florian Westphal wrote: > Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> wrote: > > +*accept*:: Terminate the evaluation of the current base chain (and > > any regular > > +chains called from it) and accept the packet from their point of > > view. > > Suggest: > *accept*:: Terminate the evaluation of the chain. Evaluation > continues in the next base chain, if any. What I like about it, is that it avoids the slightly awkward "current base chain (and any regular chains called from it)" construct... What I'm neutral about: Strictly speaking, it does not mention whether evaluation of any "parent" chains is also terminated. You try to solve that by saying "Evaluation continues in the next base chain"... and it is indeed kinda reasonable that all from the current base chain are then stopped... but a weird system could in principle continue with the next base chain, and eventually go back to the previous. (Just like originally I completely misunderstood how return works when goto was involved). What I like less about it, is that is misses this additional context of "acceptance is only with respect to those chains". Yes it can be deduced from the following sentence ("The packet may however still be dropped by either")... and meanwhile, where I (hopefully ;-) ) understand how it works, that seems enough, but for a pure beginner it's IMO better to give such context and rather reinforce things twice in different words. What would you think about: 1st: Either: Terminate the evaluation of the current chain as well as any chains from which that was called and accept the packet with respect to the base chain of these. or: Terminate the evaluation of the current chain as well as any chains in the call stack and accept the packet with respect to the base chain of these. 2nd and also replacing: > The packet may however still be dropped by either another chain with a higher > priority of the same hook or any chain of a later hook. Evaluation continues in the next base chain (of higher or possibly equal priority from the same hook or of any priority from a later hook), if any. This means the packet can still be dropped in that next base chain as well as any regular chain (directly or indirectly) called from it. > > +The packet may however still be dropped by either another chain > > with a higher > > +priority of the same hook or any chain of a later hook. > > ... This means the packet can still be dropped ... Shamelessly stole and integrated that in my proposal above. > > > +*drop*:: Terminate ruleset evaluation and drop the packet. This > > occurs > > +instantly, no further chains of any hooks are evaluated and it is > > thus not > > +possible to again accept the packet in a higher priority or later > > chain, as > > +those are not evaluated anymore for the packet. > > Can this be compacted a bit? I feel this is a tad too verbose. > > *drop*: Packet is dropped immediately. No futher evaluation of any > kind. > > I think thats enough, no? Uhm... I made it a perhaps bit extra verbose, mostly because we have terms like "terminal statement/verdict", where not all of them are really ultimately terminal. What about the following compromise: O;-) *drop*: Immediately drop the packet and terminate ruleset evaluation. This means no further evaluation of any chains and it's thus - unlike with *accept* - not possible to again change the ultimate fate of the packet in any later chain. What I'd at least think would be nice to have is to re-iterate on that conceptual difference between accept (may be overruled) and drop (is ultimate). > > +All the above applies analogously to statements that imply a > > verdict: > > +*redirect*, *dnat*, *snat* and *masquerade* internally issue > > eventually an > > +*accept* verdict. > > You can remove 'eventually'. + > > +*reject* and *synproxy* internally issue eventually a *drop* > > verdict. > > Same. The idea of that was a slight indication that these statements do: <other things> + accept|drop. Admittedly eventually isn't really perfect … > > +These statements thus behave like their implied verdicts, but with > > side effects. … but since there's already this sentence, I think you're right an we can just leave out the "eventually" without loosing too much. > > +For example, a *reject* also immediately terminates the evaluation > > of the > > +current rule, overrules any *accept* from any other chains > > No, not really. There is no *overrule*. We don't keep any 'verdict > state'. There is no difference between 'drop' in the first rule of > the > first ever base chain or a drop somewhere later in the pipeline, > aside > from side effects from other matching expressions. Well first, whether you keep an internal verdict state or not... isn't that again some implementation detail which here not really matters for the user's understanding of how evaluation works? > I would suggest: > For example, *reject* is like *drop*, but will attempt to send a > error > reply packet back to the sender before doing so. I mean I'm open to change, but what I think should in one form or another go in, is explicitly reinforcing that reject has the same "power" like drop, i.e. it can render any further accepts (of other base chains) moot. That's what I mean with respect to "overruling" (i.e. and previous accept). You're proposal rather describes the side effects of *reject* which are however IMO not really relevant with respect to overall ruleset evaluation. > > +overruled, while the various NAT statements may be overruled by > > other *drop* > > +verdicts respectively statements that imply this. > > There is no overrule. I would not mention NAT at all here. > *accept* documentation already says that later chains in the pipeline > can drop the packet (and so could the traffic scheduler, qdisc, NIC, > network ...) Like above... the idea here was again to reinforce that the statements which internally issue an accept, have the same "weakness" as accept itself, i.e. they're not ultimate and any later drop/reject/similar may "overrule" them. Any other ideas how include these two points? At least I personally rather think that the actual side effect of "but will attempt to send a error reply packet back to the sender" is rather not that interesting with respect to the overall semantics of evaluation. I think it makes no sense to spam the list with a v3 until I've got your opinions on all the above points.... so I'm waiting for that and the make a v3. :-) Thanks and best wishes, Chris. ^ permalink raw reply [flat|nested] 63+ messages in thread
* Re: [PATCH v2 2/7] doc: fix/improve documentation of verdicts 2025-10-17 2:30 ` Christoph Anton Mitterer @ 2025-10-18 13:25 ` Florian Westphal 2025-10-19 0:11 ` Christoph Anton Mitterer 0 siblings, 1 reply; 63+ messages in thread From: Florian Westphal @ 2025-10-18 13:25 UTC (permalink / raw) To: Christoph Anton Mitterer; +Cc: netfilter-devel, pablo Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> wrote: > On Wed, 2025-10-15 at 13:42 +0200, Florian Westphal wrote: > > Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> wrote: > > > +*accept*:: Terminate the evaluation of the current base chain (and > > > any regular > > > +chains called from it) and accept the packet from their point of > > > view. > > > > Suggest: > > *accept*:: Terminate the evaluation of the chain. Evaluation > > continues in the next base chain, if any. > > What I like about it, is that it avoids the slightly awkward "current > base chain (and any regular chains called from it)" construct... > > What I'm neutral about: Strictly speaking, it does not mention whether > evaluation of any "parent" chains is also terminated. > You try to solve that by saying "Evaluation continues in the next base > chain"... and it is indeed kinda reasonable that all from the current > base chain are then stopped... but a weird system could in principle > continue with the next base chain, and eventually go back to the > previous. But this should describe netfilter and not something else :-) > (Just like originally I completely misunderstood how return works when > goto was involved). > > What I like less about it, is that is misses this additional context of > "acceptance is only with respect to those chains". Hm, I think "Terminates the evaluation of the chain" is pretty clear. And "Evaluation continues in ..." is also clear, packet is allowed to move on in the processing pipeline. > Yes it can be deduced from the following sentence ("The packet may > however still be dropped by either")... and meanwhile, where I > (hopefully ;-) ) understand how it works, that seems enough, but for a > pure beginner it's IMO better to give such context and rather reinforce > things twice in different words. > What would you think about: > 1st: > Either: > Terminate the evaluation of the current chain as well as any chains > from which that was called and accept the packet with respect to the > base chain of these. > or: > Terminate the evaluation of the current chain as well as any chains in > the call stack and accept the packet with respect to the base chain of > these. It correct but it sounds overly complicated IMO. > 2nd and also replacing: > > The packet may however still be dropped by either another chain with a higher > > priority of the same hook or any chain of a later hook. I would be fine with that, even though I also consider it too verbose. > Evaluation continues in the next base chain (of higher or possibly > equal priority from the same hook or of any priority from a later > hook), if any. Hmm, I am not sure. Is it really needed to mention all of this? Packet will just move on in the pipeline, it would be weird to assume that forward-accepted packet would e.g. bypass postrouting. > This means the packet can still be dropped in that next base chain as > well as any regular chain (directly or indirectly) called from it. ... or that it moes to the next base chain but that base chain ignores jumps to user-defined chains. Your suggestions aren't wrong of course but it seems very repetitive to me. > > > +*drop*:: Terminate ruleset evaluation and drop the packet. This > > > occurs > > > +instantly, no further chains of any hooks are evaluated and it is > > > thus not > > > +possible to again accept the packet in a higher priority or later > > > chain, as > > > +those are not evaluated anymore for the packet. > > > > Can this be compacted a bit? I feel this is a tad too verbose. > > > > *drop*: Packet is dropped immediately. No futher evaluation of any > > kind. > > > > I think thats enough, no? > > Uhm... I made it a perhaps bit extra verbose, mostly because we have > terms like "terminal statement/verdict", where not all of them are > really ultimately terminal. > > What about the following compromise: O;-) > > *drop*: Immediately drop the packet and terminate ruleset evaluation. > This means no further evaluation of any chains and it's thus - unlike > with *accept* - not possible to again change the ultimate fate of the > packet in any later chain. Thats fine. > What I'd at least think would be nice to have is to re-iterate on that > conceptual difference between accept (may be overruled) and drop (is > ultimate). Yes, I understand that some people see it that way. I see netfilter as a pipeline, where packet moves along a certain path, e.g. prerouting, forward, postrouting or prerouting -> input. accept is just a "move along", whereas drop stops the packet dead in its tracks. nftables is just a "user visible" part that allows to customize the move-along-or-not decisions. Hence, this "overrule old decision" idea doesn't apply. But I can see that others may have a different concept of how this all works under the hood. But I have no idea how to best describe this let alone make it clear that you can't back out of a "drop" decision. > > > +All the above applies analogously to statements that imply a > > > verdict: > > > +*redirect*, *dnat*, *snat* and *masquerade* internally issue > > > eventually an > > > +*accept* verdict. > > > > You can remove 'eventually'. > > > > +*reject* and *synproxy* internally issue eventually a *drop* > > > verdict. > > > > Same. > > The idea of that was a slight indication that these statements do: > <other things> + accept|drop. > > Admittedly eventually isn't really perfect … OK, never mind. I have no strong opinion here. > > > > +For example, a *reject* also immediately terminates the evaluation > > > of the > > > +current rule, overrules any *accept* from any other chains > > > > No, not really. There is no *overrule*. We don't keep any 'verdict > > state'. There is no difference between 'drop' in the first rule of > > the > > first ever base chain or a drop somewhere later in the pipeline, > > aside > > from side effects from other matching expressions. > > Well first, whether you keep an internal verdict state or not... isn't > that again some implementation detail which here not really matters for > the user's understanding of how evaluation works? I hope my comments above wrt. netfilter pipeline make sense and answer this question. > > I would suggest: > > For example, *reject* is like *drop*, but will attempt to send a > > error > > reply packet back to the sender before doing so. > > I mean I'm open to change, but what I think should in one form or > another go in, is explicitly reinforcing that reject has the same > "power" like drop, i.e. it can render any further accepts (of other > base chains) moot. Hmm, I feel there is a need to document the netfilter pipeline better. Perhaps we should add a netfilter man page document and ship that too to explain this "move on" thing that accept does behind the scenes? > That's what I mean with respect to "overruling" (i.e. and previous > accept). Yes, I understand that. > You're proposal rather describes the side effects of *reject* which are > however IMO not really relevant with respect to overall ruleset > evaluation. Yes, its not relevant, in eval terms reject and drop are the same. > > > +overruled, while the various NAT statements may be overruled by > > > other *drop* > > > +verdicts respectively statements that imply this. > > > > There is no overrule. I would not mention NAT at all here. > > *accept* documentation already says that later chains in the pipeline > > can drop the packet (and so could the traffic scheduler, qdisc, NIC, > > network ...) > > Like above... the idea here was again to reinforce that the statements > which internally issue an accept, have the same "weakness" as accept > itself, i.e. they're not ultimate and any later drop/reject/similar may > "overrule" them. I wonder where the idea that "accept" is a kind of magic teleporter that just fast-forwards a packet to the socket or NIC came from... This isn't the case even in old ipchains. > Any other ideas how include these two points? At least I personally > rather think that the actual side effect of "but will attempt to send a > error reply packet back to the sender" is rather not that interesting > with respect to the overall semantics of evaluation. Correct, it should be in the REJECT STATEMENT section only. > I think it makes no sense to spam the list with a v3 until I've got > your opinions on all the above points.... so I'm waiting for that and > the make a v3. :-) I suggest to send a smaller v3 first, with the less "controversial" stuff. Patch 3 seems ready to go for instance. Then, once thats in, revisit this patch in a (rebased and smaller) v4. There is nothing wrong with getting this merged in smaller batches over a longer period. ^ permalink raw reply [flat|nested] 63+ messages in thread
* Re: [PATCH v2 2/7] doc: fix/improve documentation of verdicts 2025-10-18 13:25 ` Florian Westphal @ 2025-10-19 0:11 ` Christoph Anton Mitterer 0 siblings, 0 replies; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-10-19 0:11 UTC (permalink / raw) To: Florian Westphal; +Cc: netfilter-devel, pablo On Sat, 2025-10-18 at 15:25 +0200, Florian Westphal wrote: > > What I'm neutral about: Strictly speaking, it does not mention > > whether > > evaluation of any "parent" chains is also terminated. > > You try to solve that by saying "Evaluation continues in the next > > base > > chain"... and it is indeed kinda reasonable that all from the > > current > > base chain are then stopped... but a weird system could in > > principle > > continue with the next base chain, and eventually go back to the > > previous. > > But this should describe netfilter and not something else :-) Sure... but for a beginner (which this chapter is IMO primarily intended for) - and who we must expect doesn't know about iptables either - it may all but obvious whether netfilter/nftables would eventually go back and revisit the other chains from the former base chain's call stack. It's a bit like my goto misunderstanding... totally obvious to a netfilter developer, but maybe not so for a starter. > > (Just like originally I completely misunderstood how return works > > when > > goto was involved). > > > > What I like less about it, is that is misses this additional > > context of > > "acceptance is only with respect to those chains". > > Hm, I think "Terminates the evaluation of the chain" is pretty clear. > And "Evaluation continues in ..." is also clear, packet is allowed to > move on in the processing pipeline. Again, if one already knows how it works - it's clear, sure. But if not, one could e.g. also think that evaluation continues only for the side effects (e.g. counters). > > > What would you think about: > > 1st: > > Either: > > Terminate the evaluation of the current chain as well as any chains > > from which that was called and accept the packet with respect to > > the > > base chain of these. > > or: > > Terminate the evaluation of the current chain as well as any chains > > in > > the call stack and accept the packet with respect to the base chain > > of > > these. > > It correct but it sounds overly complicated IMO. > > > 2nd and also replacing: > > > The packet may however still be dropped by either another chain > > > with a higher > > > priority of the same hook or any chain of a later hook. > > I would be fine with that, even though I also consider it too > verbose. > > > Evaluation continues in the next base chain (of higher or possibly > > equal priority from the same hook or of any priority from a later > > hook), if any. > > > Hmm, I am not sure. Is it really needed to mention all of this? > Packet will just move on in the pipeline, it would be weird to assume > that forward-accepted packet would e.g. bypass postrouting. Well... I don't know whether the majority of beginners would immediately grasp this or not. I, personally, like it always more of teaching is rather done extra elaborative... but that's of course just one approach. > > This means the packet can still be dropped in that next base chain > > as > > well as any regular chain (directly or indirectly) called from it. > > ... or that it moes to the next base chain but that base chain > ignores > jumps to user-defined chains. > > Your suggestions aren't wrong of course but it seems very repetitive > to > me. What about the following: I'll make a v3 now, *with* the changes as proposed before, and you simply redact things where you'd rather have them differently? Otherwise we just continue to haggle on the wording ;-) I'll be fine with whatever you choose. :-) > > What about the following compromise: O;-) > > > > *drop*: Immediately drop the packet and terminate ruleset > > evaluation. > > This means no further evaluation of any chains and it's thus - > > unlike > > with *accept* - not possible to again change the ultimate fate of > > the > > packet in any later chain. > > Thats fine. Applied. > > What I'd at least think would be nice to have is to re-iterate on > > that > > conceptual difference between accept (may be overruled) and drop > > (is > > ultimate). > > Yes, I understand that some people see it that way. > > I see netfilter as a pipeline, where packet moves along a certain > path, > e.g. prerouting, forward, postrouting or prerouting -> input. Well, compared to a pure beginner,... how much more parts of netfilter have been implemented by *you*? ;-) > accept is just a "move along", whereas drop stops the packet dead in > its > tracks. Don't forget... this *is* clear once you know how it works, but if you start from scratch... the pure meaning of the word "accept" would IMO rather imply: "let the packet through (ultimately)". Just as "drop" implies "into the torch with it (ultimately)" (which is actually the case here). > nftables is just a "user visible" part that allows to customize > the move-along-or-not decisions. > > Hence, this "overrule old decision" idea doesn't apply. > > But I can see that others may have a different concept of how > this all works under the hood. > > But I have no idea how to best describe this let alone make it > clear that you can't back out of a "drop" decision. I'd have two proposals now: a) We leave: > For example, a *reject* also immediately terminates the evaluation of > the current rule, overrules any *accept* from any other chains and > can itself not be overruled, while the various NAT statements may be > overruled by other *drop* verdicts respectively statements that imply > this. And add a sentence like: > (Note that “overruling” is only an abstract description and not how > netfilter actually processes that internally.) after it. Though I personally would say that this is rather not of interest for the evaluation summary chapter - only if it would make really a big difference in terms of understanding how the evaluation effectively works. (Meanwhile I've already added a " as well as of all chains" after " the current rule". I've also changed "*drop* verdicts" to singular as only one could do the "overruling") b) We change it to something like: > For example, a *reject* also immediately terminates the evaluation of > the current rule as well as of all chains, prevents any previous > *accept* from other chains to decide the ultimate fate of the packet > and cannot be turned into an *accept*, while the various NAT > statements only decide the ultimate fate of the packet if no other > *drop* verdict respectively statements that imply this are issued. But TBH, I think (b) is ugly and rather difficult to understand... and merely tries to avoid overruling/overriding/etc. at all costs. Again, I can make another round after you decide... but feel free to simply edit v3 as you wish. :-) > > > > +All the above applies analogously to statements that imply a > > > > verdict: > > > > +*redirect*, *dnat*, *snat* and *masquerade* internally issue > > > > eventually an > > > > +*accept* verdict. > > > > > > You can remove 'eventually'. > > > > > > +*reject* and *synproxy* internally issue eventually a *drop* > > > > verdict. > > > > > > Same. > > > > The idea of that was a slight indication that these statements do: > > <other things> + accept|drop. > > > > Admittedly eventually isn't really perfect … > > OK, never mind. I have no strong opinion here. I'd have changed it now to: > … internally issue an … verdict at the end of their respective > actions. Feel free to change it, and also to replace "actions" with a better word if you have one. > > > > > I would suggest: > > > For example, *reject* is like *drop*, but will attempt to send a > > > error > > > reply packet back to the sender before doing so. > > > > I mean I'm open to change, but what I think should in one form or > > another go in, is explicitly reinforcing that reject has the same > > "power" like drop, i.e. it can render any further accepts (of other > > base chains) moot. > > Hmm, I feel there is a need to document the netfilter pipeline > better. > > Perhaps we should add a netfilter man page document and > ship that too to explain this "move on" thing that accept does behind > the scenes? I think that would definitely make sense (then we could also simply let the above "Note" sentence refer to that). I mean such a document should then probably also describe the hooks, what they're intended for and in general some concepts how the whole networking thingy actually works (i.e. what happens from the arrival of the packet until it reaches some application and the other way round). Perhaps also elaborating a bit on concepts like NAT, forwarding, etc. ... which easily makes such a manpage quite some effort and go beyond netfilter into general networking. Though I think what would be even nicer would be some generic "teaching" on writing nftables rules... I've mentioned some examples before. E.g. I've been trying to implement the strong host model via: fib daddr . iif type != {local, broadcast, multicast} drop which nft(8) places in prerouting but some examples on the net in input I guess I might understand the difference ^^ ... but I tried to actually verify whether it does what it should (via some qemu VM with multiple ifaces and nping), but failed. And in particular also with respect to writing "performant" rules, e.g.: - I've seen many examples, where the rules for actually accepting new connections (like SSH, http, whatever) do things like these: in iptables, they often used --syn, which iptables-translate would translate, but - assuming the conntrack state is always readily available - I guess it's better (and equally fast if not faster) to use ct state new. But even then, many people do things like: ct state established,related accept ct state invalid drop meta iif lo accept In that light, does it even make sense to match on ct state new later? Perhaps also telling whther it makes sense to have the ct state established,related accept rule first, (assuming most packets will already be handled by it). I've seen quite some examples which accept the loopback first, but - under my above assumption - would also guess that having the established rule first should be at least equally fast. - Teaching people a bit about how ordering of statements should be done within one rule. Consider something like: ct state new iifname eth0 tcp dport ssh accept would e.g.: ct state new tcp dport ssh iifname eth0 accept be faster? Perhaps also telling a bit about: which information is already available and which requires further parsing when the statement is encoutnered, e.g. if tcp dport is encountered, does that first need to be extracted form the packet, or is that already done? Similarly, is it better to first match on ip [ds]addr and then on (tcp|udp) [ds]port or vice versa... or no difference or is it dependent on what one matches on? - Perhaps also on: when should one start to split things up in chains? Consider e.g.: ct state new tcp dport ssh accept ct state new tcp dport http accept ct state new tcp dport https accept ct state new udp dport domain accept For each packet, ct state new would be checked again, so when does it make sense (i.e. when is the jump/goto/return cheaper) to do: ct state new jump new-packets and evaluate tcp/udp dports there? Similarly, a UDP packet would be evaluated against all rules (even with a new-packets chain)... so roughly when does it make sense to make yet another sub chain for tcp and one for udp? - I've also seen a great many of "base rule sets" for workstations... like what which ICMPv[46] to let through, etc. ... all slightly different. Of course this may again be beyond the scope of nftables/netfilter and rather go into general teaching of networking. I mean all this probably doesn't matter on some workstation, but at the university I work we run a storage element for LHC with many PiB data and more or less constant high volume network,... there these things might matter? > > Like above... the idea here was again to reinforce that the > > statements > > which internally issue an accept, have the same "weakness" as > > accept > > itself, i.e. they're not ultimate and any later drop/reject/similar > > may > > "overrule" them. > > I wonder where the idea that "accept" is a kind of magic teleporter > that > just fast-forwards a packet to the socket or NIC came from... Well, it's kind of the anti-particle to drop, which does just that fast-forwarding (except into the void). > This isn't the case even in old ipchains. I guess that quite some users never ever touched chains other than INPUT and OUTPUT,... so from their PoV, -j ACCEPT might have been kinda like that. > > Any other ideas how include these two points? At least I personally > > rather think that the actual side effect of "but will attempt to > > send a > > error reply packet back to the sender" is rather not that > > interesting > > with respect to the overall semantics of evaluation. > > Correct, it should be in the REJECT STATEMENT section only. It already is: > 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. which may be considered a bit fuzzy again, cause it mentions that rule traversal is ended (which could be considered to mean only with respect to the current base chain)... and doesn't mention that it also "ignores" any further statements in the current rule. I've added another commit on top, which takes care of this. > I suggest to send a smaller v3 first, with the less "controversial" > stuff. Patch 3 seems ready to go for instance. Would you mind just dropping/editing the controversial stuff as you seems it fits? :-) Especially since I've kinda lost track which parts of it are now exactly still controversial and which not (I guess mostly the *accept* documentation now?) ^^ Thanks, Chris. ^ permalink raw reply [flat|nested] 63+ messages in thread
* [PATCH v2 3/7] doc: minor improvements with respect to the term “ruleset” 2025-10-11 0:23 ` [PATCH v2 0/7] doc: miscellaneous improvements Christoph Anton Mitterer 2025-10-11 0:23 ` [PATCH v2 1/7] doc: clarify evaluation of chains Christoph Anton Mitterer 2025-10-11 0:23 ` [PATCH v2 2/7] doc: fix/improve documentation of verdicts Christoph Anton Mitterer @ 2025-10-11 0:23 ` Christoph Anton Mitterer 2025-10-15 11:51 ` Florian Westphal 2025-10-11 0:24 ` [PATCH v2 4/7] doc: add overall description of the ruleset evaluation Christoph Anton Mitterer ` (3 subsequent siblings) 6 siblings, 1 reply; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-10-11 0:23 UTC (permalink / raw) To: netfilter-devel; +Cc: fw, pablo Statements are elements of rules. Non-terminal statement are in particular passive with respect to their rules (and thus automatically with respect to the whole ruleset). In “Continue ruleset evaluation”, it’s not necessary to mention the ruleset as it’s obvious that the evaluation of the current chain will be continued. Signed-off-by: Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> --- doc/nft.txt | 6 +++--- doc/statements.txt | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/nft.txt b/doc/nft.txt index 88c08618..a32fb10c 100644 --- a/doc/nft.txt +++ b/doc/nft.txt @@ -910,9 +910,9 @@ actions, such as logging, rejecting a packet, etc. + Statements exist in two kinds. Terminal statements unconditionally terminate evaluation of the current rule, non-terminal statements either only conditionally or never terminate evaluation of the current rule, in other words, -they are passive from the ruleset evaluation perspective. There can be an -arbitrary amount of non-terminal statements in a rule, but only a single -terminal statement as the final statement. +they are passive from the rule evaluation perspective. There can be an arbitrary +amount of non-terminal statements in a rule, but only a single terminal +statement as the final statement. include::statements.txt[] diff --git a/doc/statements.txt b/doc/statements.txt index f812dec8..850c32cb 100644 --- a/doc/statements.txt +++ b/doc/statements.txt @@ -54,8 +54,8 @@ those are not evaluated anymore for the packet. either the base chain or a regular chain that was reached solely via *goto* verdicts) end evaluation of the current base chain (and any regular chains called from it) using the base chain’s policy as implicit verdict. -*continue*:: Continue ruleset evaluation with the next rule. This - is the default behaviour in case a rule issues no verdict. +*continue*:: Continue evaluation with the next rule. This is the default + behaviour in case a rule issues no verdict. *queue*:: Terminate ruleset evaluation and queue the packet to userspace. Userspace must provide a drop or accept verdict. In case of accept, processing resumes with the next base chain hook, not the rule following the queue verdict. -- 2.51.0 ^ permalink raw reply related [flat|nested] 63+ messages in thread
* Re: [PATCH v2 3/7] doc: minor improvements with respect to the term “ruleset” 2025-10-11 0:23 ` [PATCH v2 3/7] doc: minor improvements with respect to the term “ruleset” Christoph Anton Mitterer @ 2025-10-15 11:51 ` Florian Westphal 0 siblings, 0 replies; 63+ messages in thread From: Florian Westphal @ 2025-10-15 11:51 UTC (permalink / raw) To: Christoph Anton Mitterer; +Cc: netfilter-devel, pablo Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> wrote: > Statements are elements of rules. Non-terminal statement are in particular > passive with respect to their rules (and thus automatically with respect to the > whole ruleset). > > In “Continue ruleset evaluation”, it’s not necessary to mention the ruleset as > it’s obvious that the evaluation of the current chain will be continued. LGTM, can you rebase this on top of nftables.git? I doesn't apply without the preceeding patch, but I don't want to apply that one, yet. ^ permalink raw reply [flat|nested] 63+ messages in thread
* [PATCH v2 4/7] doc: add overall description of the ruleset evaluation 2025-10-11 0:23 ` [PATCH v2 0/7] doc: miscellaneous improvements Christoph Anton Mitterer ` (2 preceding siblings ...) 2025-10-11 0:23 ` [PATCH v2 3/7] doc: minor improvements with respect to the term “ruleset” Christoph Anton Mitterer @ 2025-10-11 0:24 ` Christoph Anton Mitterer 2025-10-20 9:39 ` Florian Westphal 2025-10-11 0:24 ` [PATCH v2 5/7] doc: add some more documentation on bitmasks Christoph Anton Mitterer ` (2 subsequent siblings) 6 siblings, 1 reply; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-10-11 0:24 UTC (permalink / raw) To: netfilter-devel; +Cc: fw, pablo Signed-off-by: Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> --- doc/nft.txt | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/doc/nft.txt b/doc/nft.txt index a32fb10c..20c63f98 100644 --- a/doc/nft.txt +++ b/doc/nft.txt @@ -560,6 +560,108 @@ 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 (and thus by netfilter), 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 and not known + to netfilter itself. + They are thus irrelevant with respect to netfilter’s 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 and regular chains may call other regular + chains (via *jump* and *goto* verdicts), in which case evaluation continues in + the called chain. + Base chains themsevlves cannot be called and only chains of the same table can + be called. +* For each hook, the attached 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 (and any regular chains called + from that). + It accepts the packet only with respect to the current base chain. Any other + base chain (or regular chain called by such) with a higher priority of the + same hook as well as any other base chain (or regular chain called by such) of + any later hook may however still ultimately *drop* (which might also be done + via verdict-like statements that imply *drop*, like *reject*) the packet with + an according verdict (with consequences as described below for *drop*). + Thus and merely from netfilter’s point of view, a packet is only ultimately + accepted if none of the chains (regardless of their tables) that are attached + to any of the respectively relevant hooks issues a *drop* verdict (be it + explicitly or implicitly by policy or via a verdict-like statement that + implies *drop*, for example *reject*), which already means that there has to + be at least one *accept* verdict (be it explicitly or implicitly by policy). + All this applies analogously 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 and ultimately drops the + packet. + Unlike with an *accept* verdict, no further chains of any hook and regardless + of their table get evaluated and it’s therefore not possible to have an *drop* + verdict overruled. + 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. + All this applies analogously to verdict-like 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*, *continue* and + *queue*) 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] 63+ messages in thread
* Re: [PATCH v2 4/7] doc: add overall description of the ruleset evaluation 2025-10-11 0:24 ` [PATCH v2 4/7] doc: add overall description of the ruleset evaluation Christoph Anton Mitterer @ 2025-10-20 9:39 ` Florian Westphal 2025-10-20 23:48 ` Christoph Anton Mitterer 0 siblings, 1 reply; 63+ messages in thread From: Florian Westphal @ 2025-10-20 9:39 UTC (permalink / raw) To: Christoph Anton Mitterer; +Cc: netfilter-devel, pablo Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> wrote: > + It accepts the packet only with respect to the current base chain. Any other > + base chain (or regular chain called by such) with a higher priority of the > + same hook as well as any other base chain (or regular chain called by such) of > + any later hook may however still ultimately *drop* (which might also be done > + via verdict-like statements that imply *drop*, like *reject*) the packet with > + an according verdict (with consequences as described below for *drop*). ... > + Thus and merely from netfilter’s point of view, a packet is only ultimately > + accepted if none of the chains (regardless of their tables) that are attached > + to any of the respectively relevant hooks issues a *drop* verdict (be it > + explicitly or implicitly by policy or via a verdict-like statement that > + implies *drop*, for example *reject*), which already means that there has to > + be at least one *accept* verdict (be it explicitly or implicitly by policy). > + All this applies analogously to verdict-like statements that imply *accept*, > + for example the NAT statements. ... I think this is too confusing and verbose. packet ultimately passed: no drop verdict was issued. Its all there is to it, really. > +* A *drop* verdict (including an implict one via the base chain’s policy) > + immediately ends the evaluation of the whole ruleset and ultimately drops the > + packet. > + Unlike with an *accept* verdict, no further chains of any hook and regardless > + of their table get evaluated and it’s therefore not possible to have an *drop* > + verdict overruled. This is fine. > + 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. > + All this applies analogously to verdict-like statements that imply *drop*, > + for example *reject*. Same. > +* 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*). Maybe rework the *accept* part to say that the packet moves on to the next hook? (As opposed to *drop*, which is final). I think almost all of the overrule talk comes from this distinction (or lack thereof). ^ permalink raw reply [flat|nested] 63+ messages in thread
* Re: [PATCH v2 4/7] doc: add overall description of the ruleset evaluation 2025-10-20 9:39 ` Florian Westphal @ 2025-10-20 23:48 ` Christoph Anton Mitterer 0 siblings, 0 replies; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-10-20 23:48 UTC (permalink / raw) To: Florian Westphal; +Cc: netfilter-devel, pablo On Mon, 2025-10-20 at 11:39 +0200, Florian Westphal wrote: > ... I think this is too confusing and verbose. > packet ultimately passed: no drop verdict was issued. Its all there > is > to it, really. Phew... I mean we have four parts in there: > * An *accept* verdict (including an implict one via the base chain’s policy) > ends the evaluation of the current base chain (and any regular chains called > from that). That should IMO be uncontroversial and it's the counterpart to the first sentence in drop's description. > It accepts the packet only with respect to the current base chain. Any other > base chain (or regular chain called by such) with a higher priority of the > same hook as well as any other base chain (or regular chain called by such) of > any later hook may however still ultimately *drop* (which might also be done > via verdict-like statements that imply *drop*, like *reject*) the packet with > an according verdict (with consequences as described below for *drop*). That one is the counterpart to drop's "Unlike with an *accept* verdict …". I'm not sure how we could shorten that without loosing something valuable in terms of teaching. - We can't shorten "Any other base chain (or regular chain called by such)" to e.g. "any other chain" because that would IMO not make it clear that this is only for a new base chain. If we leave out the "regular ones", people might come to think a drop in those might not be effective. - We could leave out the whole "with a higher priority of the same hook as well as any other base chain (or regular chain called by such) of any later hook" ... but I think one would loose the nice understanding at which chains exactly the "overruling" (sorry O:-) ) could happen. > Thus and merely from netfilter’s point of view, a packet is only ultimately > accepted if none of the chains (regardless of their tables) that are attached > to any of the respectively relevant hooks issues a *drop* verdict (be it > explicitly or implicitly by policy or via a verdict-like statement that > implies *drop*, for example *reject*), which already means that there has to > be at least one *accept* verdict (be it explicitly or implicitly by policy). That's basically the counterpart to drop's "Thus, if any base chain uses drop as its policy…" sentence, … basically trying to explain the consequences of the above. I think it's nice to have because it teaches this concept of: "What is needed for a package to pass ultimately? No drops in any chain - which automatically means there's at least one accept". Could perhaps be shortened to: > Thus and merely from netfilter’s point of view, a packet is > ultimately only accepted if none of the chains of the relevant hooks > issues a *drop* verdict (be it explicitly or implicitly by policy or > via a verdict-like statement that implies *drop*, for example > *reject*), which already means that there has to be at least one > *accept* verdict (be it explicitly or implicitly by policy). I think the "of the relevant hooks" is important as packets do not go through all hooks, do they?! The whole "which already means..." could perhaps be dropped, but people might first come to think "Wait... I never said accept and it still goes through? WTF?!"... until they realise that "no drop" already means "at least one accept". > All this applies analogously to verdict-like statements that imply *accept*, > for example the NAT statements. Again, counterpart to the same in *drop*. > > +* A *drop* verdict (including an implict one via the base chain’s > > policy) > > + immediately ends the evaluation of the whole ruleset and > > ultimately drops the > > + packet. > > + Unlike with an *accept* verdict, no further chains of any hook > > and regardless > > + of their table get evaluated and it’s therefore not possible to > > have an *drop* > > + verdict overruled. > > This is fine. .oO( guess he must have overseen that mischievous in the end :P ) > > +* 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*). > > Maybe rework the *accept* part to say that the packet moves on to the > next hook? (As opposed to *drop*, which is final). You mean further above and not in the "Given the semantics of" point!? I mean effectively this "accept = packt moves on to the next chain [not hook]", is explained in the first two sentences ("An *accept* verdict…" and "It accepts the packet only…"). Shorten it that drastically,... with pure beginners who never used nf/iptables before probably having zero clue on how a packet moves through netfilter (let alone that this works like a pipeline)... IMO doesn't really add to understanding. If one has no knowledge, accept means just that "allow for good" and drop the other way round. So we must at least tell that accept does not ultimately accept, but only for the base chain. Thus we also need to tell it moves on to all other chains, but only those that are relevant. And we must bring in the concept that drop is more powerful than accept and can actually immediately and ultimately decide the fate. Even if we'd already introduce the pipeline concept at that stage... not sure how much that would add to understanding/confusion. I mean implicitly, we already kinda have it, I'd say... > I think almost all of the overrule talk comes from this distinction > (or lack thereof). Well right now one one place is left where "overruling" is mentioned, which you greenlit above ;-) And that we could easily change to: > it’s therefore not possible to have a *drop* changed to an *accept* > in a later chain. which I actually did. Will post v4 soon... not much changes for this patch though, so I guess you won't be satisfied and we'll need another round. ;-) Cheers, Chris. ^ permalink raw reply [flat|nested] 63+ messages in thread
* [PATCH v2 5/7] doc: add some more documentation on bitmasks 2025-10-11 0:23 ` [PATCH v2 0/7] doc: miscellaneous improvements Christoph Anton Mitterer ` (3 preceding siblings ...) 2025-10-11 0:24 ` [PATCH v2 4/7] doc: add overall description of the ruleset evaluation Christoph Anton Mitterer @ 2025-10-11 0:24 ` Christoph Anton Mitterer 2025-10-18 13:32 ` Florian Westphal 2025-10-11 0:24 ` [PATCH v2 6/7] doc: describe include’s collation order to be that of the C locale Christoph Anton Mitterer 2025-10-11 0:24 ` [PATCH v2 7/7] doc: describe how values match sets Christoph Anton Mitterer 6 siblings, 1 reply; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-10-11 0:24 UTC (permalink / raw) To: netfilter-devel; +Cc: fw, pablo Signed-off-by: Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> --- doc/data-types.txt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/doc/data-types.txt b/doc/data-types.txt index 18af266a..47a0d25a 100644 --- a/doc/data-types.txt +++ b/doc/data-types.txt @@ -26,6 +26,22 @@ integer The bitmask type (*bitmask*) is used for bitmasks. +In expressions the bits of a bitmask may be specified as *'bit'[,'bit']...* with +'bit' being the value of the bit or a pre-defined symbolic constant, if any (for +example *ct state*’s bit 0x1 has the symbolic constant `new`). + +Equality of a value with such bitmask is given, if the value has any of the +bitmask’s bits set (and optionally others). + +The syntax *'expression' 'value' / 'mask'* is identical to +*'expression' and 'mask' == 'value'*. +For example `tcp flags syn,ack / syn,ack,fin,rst` is the same as +`tcp flags and (syn|ack|fin|rst) == syn|ack`. + +It should further be noted that *'expression' 'bit'[,'bit']...* is not the same +as *'expression' {'bit'[,'bit']...}*. + + STRING TYPE ~~~~~~~~~~~~ [options="header"] -- 2.51.0 ^ permalink raw reply related [flat|nested] 63+ messages in thread
* Re: [PATCH v2 5/7] doc: add some more documentation on bitmasks 2025-10-11 0:24 ` [PATCH v2 5/7] doc: add some more documentation on bitmasks Christoph Anton Mitterer @ 2025-10-18 13:32 ` Florian Westphal 2025-10-19 1:31 ` Christoph Anton Mitterer 0 siblings, 1 reply; 63+ messages in thread From: Florian Westphal @ 2025-10-18 13:32 UTC (permalink / raw) To: Christoph Anton Mitterer; +Cc: netfilter-devel, pablo Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> wrote: > Signed-off-by: Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> > --- > doc/data-types.txt | 16 ++++++++++++++++ > 1 file changed, 16 insertions(+) > > diff --git a/doc/data-types.txt b/doc/data-types.txt > index 18af266a..47a0d25a 100644 > --- a/doc/data-types.txt > +++ b/doc/data-types.txt > @@ -26,6 +26,22 @@ integer > > The bitmask type (*bitmask*) is used for bitmasks. > > +In expressions the bits of a bitmask may be specified as *'bit'[,'bit']...* with > +'bit' being the value of the bit or a pre-defined symbolic constant, if any (for > +example *ct state*’s bit 0x1 has the symbolic constant `new`). [..] > +The syntax *'expression' 'value' / 'mask'* is identical to > +*'expression' and 'mask' == 'value'*. > +For example `tcp flags syn,ack / syn,ack,fin,rst` is the same as > +`tcp flags and (syn|ack|fin|rst) == syn|ack`. > + > +It should further be noted that *'expression' 'bit'[,'bit']...* is not the same > +as *'expression' {'bit'[,'bit']...}*. Maybe add another sentence, something like: Note that *'expression' 'bit'[,'bit']...* is not the same as *'expression' {'bit'[,'bit']...}*. The latter constitutes a lookup in an anonymous set and will match only if the set contains an exact match. And/or maybe also include an example involving tcp flags to make it clear. Do you think a reference to "nft describe tcp flags" should be made? There is normally no reason to muck with the raw values (or even a need to know that new is 0x1). This patch LGTM otherwise. ^ permalink raw reply [flat|nested] 63+ messages in thread
* Re: [PATCH v2 5/7] doc: add some more documentation on bitmasks 2025-10-18 13:32 ` Florian Westphal @ 2025-10-19 1:31 ` Christoph Anton Mitterer 0 siblings, 0 replies; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-10-19 1:31 UTC (permalink / raw) To: Florian Westphal; +Cc: netfilter-devel, pablo On Sat, 2025-10-18 at 15:32 +0200, Florian Westphal wrote: > > +It should further be noted that *'expression' 'bit'[,'bit']...* is > > not the same > > +as *'expression' {'bit'[,'bit']...}*. > > Maybe add another sentence, something like: > Note that *'expression' 'bit'[,'bit']...* is not the same > as *'expression' {'bit'[,'bit']...}*. The latter constitutes > a lookup in an anonymous set and will match only if the set > contains an exact match. I did that now, with some minor changes (noting that it's the same for named sets) and changing "an exact match" to "exactly one value that matches" (which I think makes the differences to bitmask matching a bit clearer)... ... but that now kinda overlaps with the "See <<SETS>> above." above, which - before squashing the two patches as you've asked for in the other mail - was added in: doc: describe how values match sets I still let that in, but added an "also" to it. > And/or maybe also include an example involving tcp flags to make it > clear. Done. Though I think the two sections SETS and BITMASK now overlap a bit... I kinda liked it more when BITMASK only mentioned that the two are different, and referred to SETS for the actual description. What do you think? (We should however definitely keep the new example, but then perhaps also rather in SETS?!) > Do you think a reference to "nft describe tcp flags" should be made? > There is normally no reason to muck with the raw values (or even a > need > to know that new is 0x1). With respect to the difference in how sets vs. bitmasks are matched... I rather don't think it would be necessary. But I think it would be nice to have it in general, not only for getting the true integer values (which are arguably only rarely needed,... perhaps only to check thinks like "which ICMP type is that actually compared to the RFC)... but also for getting all the symbolic names. One more thing that we should perhaps tackle: LEXICAL CONVENTIONS says: > Identifiers begin with an alphabetic character (a-z,A-Z), followed by > zero or more alphanumeric characters (a-z,A-Z,0-9) and the characters > slash (/), backslash (\), underscore (_) and dot (.). Not sure if identifiers here mean also e.g. set/var names... but at least set names also seem to allow '-'. And as mentioned previously, unlike claimed in the wiki (which says 16 chars is max) set names seem to be allowed pretty long names (I think I've stopped at 100 chars)... which is actually quite helpful, so please don't restrict ;-) > Identifiers using different characters or clashing with a keyword > need to be enclosed in double quotes ("). Maybe I'm misunderstanding something here... but at least it doesn't seem to work to create a set like with: set "foo@bar" { … } Cheers, Chris. ^ permalink raw reply [flat|nested] 63+ messages in thread
* [PATCH v2 6/7] doc: describe include’s collation order to be that of the C locale 2025-10-11 0:23 ` [PATCH v2 0/7] doc: miscellaneous improvements Christoph Anton Mitterer ` (4 preceding siblings ...) 2025-10-11 0:24 ` [PATCH v2 5/7] doc: add some more documentation on bitmasks Christoph Anton Mitterer @ 2025-10-11 0:24 ` Christoph Anton Mitterer 2025-10-18 13:35 ` Florian Westphal 2025-10-11 0:24 ` [PATCH v2 7/7] doc: describe how values match sets Christoph Anton Mitterer 6 siblings, 1 reply; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-10-11 0:24 UTC (permalink / raw) To: netfilter-devel; +Cc: fw, pablo Currently, `nft` doesn’t call `setlocale(3)` and thus `glob(3)` uses the `C` locale. Document this as it’s possibly relevant to the ordering of included rules. This also makes the collation order “official” so any future localisation would need to adhere to that. Signed-off-by: Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> --- doc/nft.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/nft.txt b/doc/nft.txt index 20c63f98..3fef1882 100644 --- a/doc/nft.txt +++ b/doc/nft.txt @@ -165,8 +165,8 @@ Include statements support the usual shell wildcard symbols (*,?,[]). Having no matches for an include statement is not an error, if wildcard symbols are used in the include statement. This allows having potentially empty include directories for statements like **include "/etc/firewall/rules/*"**. The wildcard -matches are loaded in alphabetical order. Files beginning with dot (.) are not -matched by include statements. +matches are loaded in the collation order of the C locale. Files beginning with +dot (.) are not matched by include statements. SYMBOLIC VARIABLES ~~~~~~~~~~~~~~~~~~ -- 2.51.0 ^ permalink raw reply related [flat|nested] 63+ messages in thread
* Re: [PATCH v2 6/7] doc: describe include’s collation order to be that of the C locale 2025-10-11 0:24 ` [PATCH v2 6/7] doc: describe include’s collation order to be that of the C locale Christoph Anton Mitterer @ 2025-10-18 13:35 ` Florian Westphal 2025-10-18 22:13 ` Christoph Anton Mitterer 0 siblings, 1 reply; 63+ messages in thread From: Florian Westphal @ 2025-10-18 13:35 UTC (permalink / raw) To: Christoph Anton Mitterer; +Cc: netfilter-devel, pablo Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> wrote: > Currently, `nft` doesn’t call `setlocale(3)` and thus `glob(3)` uses the `C` > locale. > > Document this as it’s possibly relevant to the ordering of included rules. > > This also makes the collation order “official” so any future localisation would > need to adhere to that. Pablo, whats your take on this? I wonder if libnftables should force POSIX locale. ^ permalink raw reply [flat|nested] 63+ messages in thread
* Re: [PATCH v2 6/7] doc: describe include’s collation order to be that of the C locale 2025-10-18 13:35 ` Florian Westphal @ 2025-10-18 22:13 ` Christoph Anton Mitterer 0 siblings, 0 replies; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-10-18 22:13 UTC (permalink / raw) To: Florian Westphal; +Cc: netfilter-devel, pablo On Sat, 2025-10-18 at 15:35 +0200, Florian Westphal wrote: > Pablo, whats your take on this? > I wonder if libnftables should force POSIX locale. One thing perhaps to add,... At the Austin WG there’s a request[0] to standardise the C.UTF-8 locale. There’s no real progress yet (at least none that would be public), but given that all world switches to UTF-8, and C.UTF-8 is already the reality for many systems... I guess that might sooner or later happen. Of course even if it would it’s not clear whether POSIX’ C.UTF-8 would match glibc's. Still... *if* nftables does something in that matter, maybe it should go straight to C.UTF-8? I'd guess LC_COLLATE (which is of interest here) is safely different fro the two versions (C uses strcmp(), C.UTF-8 uses wcscmp()). OTOH, not sure how well things would work with C.UTF-8, if people would use non-UTF-8 filenames for included files. Cheers, Chris. [0] https://austingroupbugs.net/view.php?id=1548 ^ permalink raw reply [flat|nested] 63+ messages in thread
* [PATCH v2 7/7] doc: describe how values match sets 2025-10-11 0:23 ` [PATCH v2 0/7] doc: miscellaneous improvements Christoph Anton Mitterer ` (5 preceding siblings ...) 2025-10-11 0:24 ` [PATCH v2 6/7] doc: describe include’s collation order to be that of the C locale Christoph Anton Mitterer @ 2025-10-11 0:24 ` Christoph Anton Mitterer 2025-10-18 13:51 ` Florian Westphal 6 siblings, 1 reply; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-10-11 0:24 UTC (permalink / raw) To: netfilter-devel; +Cc: fw, pablo Signed-off-by: Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> --- doc/data-types.txt | 1 + doc/nft.txt | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/doc/data-types.txt b/doc/data-types.txt index 47a0d25a..dad7e31b 100644 --- a/doc/data-types.txt +++ b/doc/data-types.txt @@ -40,6 +40,7 @@ For example `tcp flags syn,ack / syn,ack,fin,rst` is the same as It should further be noted that *'expression' 'bit'[,'bit']...* is not the same as *'expression' {'bit'[,'bit']...}*. +See <<SETS>> above. STRING TYPE diff --git a/doc/nft.txt b/doc/nft.txt index 3fef1882..4d1daf5c 100644 --- a/doc/nft.txt +++ b/doc/nft.txt @@ -764,6 +764,16 @@ Example: When the set contains range *1.2.3.1-1.2.3.4*, then adding element *1.2 effect. Adding *1.2.3.5* changes the existing range to cover *1.2.3.1-1.2.3.5*. Without this flag, *1.2.3.2* can not be added and *1.2.3.5* is inserted as a new entry. +Equality of a value with a set is given if the value matches exactly one value +in the set. +It shall be noted that for bitmask values this means, that +*'expression' 'bit'[,'bit']...* (which yields true if *any* of the bits are set) +is not the same as *'expression' {'bit'[,'bit']...}* (which yields true if +exactly one of the bits are set). +It may however be (effectively) the same, in cases like +`ct state established,related` and `ct state {established,related}`, where these +states are mutually exclusive. + MAPS ----- [verse] -- 2.51.0 ^ permalink raw reply related [flat|nested] 63+ messages in thread
* Re: [PATCH v2 7/7] doc: describe how values match sets 2025-10-11 0:24 ` [PATCH v2 7/7] doc: describe how values match sets Christoph Anton Mitterer @ 2025-10-18 13:51 ` Florian Westphal 2025-10-19 1:50 ` Christoph Anton Mitterer 0 siblings, 1 reply; 63+ messages in thread From: Florian Westphal @ 2025-10-18 13:51 UTC (permalink / raw) To: Christoph Anton Mitterer; +Cc: netfilter-devel, pablo Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> wrote: > diff --git a/doc/nft.txt b/doc/nft.txt > index 3fef1882..4d1daf5c 100644 > --- a/doc/nft.txt > +++ b/doc/nft.txt > @@ -764,6 +764,16 @@ Example: When the set contains range *1.2.3.1-1.2.3.4*, then adding element *1.2 > effect. Adding *1.2.3.5* changes the existing range to cover *1.2.3.1-1.2.3.5*. > Without this flag, *1.2.3.2* can not be added and *1.2.3.5* is inserted as a new entry. > > +Equality of a value with a set is given if the value matches exactly one value > +in the set. Yes, or if its contained in the range (for ranged sets). E.g. 1.2.3.4 will match a set containing 1.2.3.0/24 or 1.2.3.2-1.2.3.5 for example. > +It shall be noted that for bitmask values this means, that > +*'expression' 'bit'[,'bit']...* (which yields true if *any* of the bits are set) > +is not the same as *'expression' {'bit'[,'bit']...}* (which yields true if > +exactly one of the bits are set). > +It may however be (effectively) the same, in cases like > +`ct state established,related` and `ct state {established,related}`, where these > +states are mutually exclusive. Thanks, thats very similar to what I had in mind. You could merge this with the previous patch. ^ permalink raw reply [flat|nested] 63+ messages in thread
* Re: [PATCH v2 7/7] doc: describe how values match sets 2025-10-18 13:51 ` Florian Westphal @ 2025-10-19 1:50 ` Christoph Anton Mitterer 0 siblings, 0 replies; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-10-19 1:50 UTC (permalink / raw) To: Florian Westphal; +Cc: netfilter-devel, pablo On Sat, 2025-10-18 at 15:51 +0200, Florian Westphal wrote: > Yes, or if its contained in the range (for ranged sets). E.g. > > 1.2.3.4 will match a set containing 1.2.3.0/24 or 1.2.3.2-1.2.3.5 for > example. Damn... knew I forgot something. I've changed: diff --git a/doc/nft.txt b/doc/nft.txt index 09da6f28..e0ed4b11 100644 --- a/doc/nft.txt +++ b/doc/nft.txt @@ -777,7 +777,7 @@ effect. Adding *1.2.3.5* changes the existing range to cover *1.2.3.1-1.2.3.5*. Without this flag, *1.2.3.2* can not be added and *1.2.3.5* is inserted as a new entry. Equality of a value with a set is given if the value matches exactly one value -in the set. +in the set (which for intervals means that it’s contained in any of them). It shall be noted that for bitmask values this means, that *'expression' 'bit'[,'bit']...* (which yields true if *any* of the bits are set) is not the same as *'expression' {'bit'[,'bit']...}* (which yields true if It would be included in any further iteration of the series - I just didn't want to spam with a v4 with only that change now. Or you could just apply manually. The "in any of them" is a bit fuzzy, as intervals are, IIRC, always non-overlapping, so it can always be contained in only one of them. But I don't think this matters here and isn't disputed by the wording either. Cheers, Chris. ^ permalink raw reply related [flat|nested] 63+ messages in thread
* [PATCH v3 0/6] doc: miscellaneous improvements 2025-09-25 0:07 nft manpage/wiki issues and improvement ideas Christoph Anton Mitterer 2025-09-25 7:35 ` Pablo Neira Ayuso 2025-10-11 0:23 ` [PATCH v2 0/7] doc: miscellaneous improvements Christoph Anton Mitterer @ 2025-10-19 1:38 ` Christoph Anton Mitterer 2025-10-19 1:38 ` [PATCH v3 1/6] doc: fix/improve documentation of verdicts Christoph Anton Mitterer ` (5 more replies) 2025-10-20 23:49 ` [PATCH v4 0/5] doc: miscellaneous improvements Christoph Anton Mitterer 3 siblings, 6 replies; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-10-19 1:38 UTC (permalink / raw) To: netfilter-devel; +Cc: Florian Westphal, pablo Hey. v3, rebased on master, with some of the changes as discussed in the thread... and also with some of the "controversial" stuff (which you may change as it seems fit, or we can also re-iterate). Of course Florian may add his Signed-off-by:. :-) The former [PATCH v2 1/7] doc: clarify evaluation of chains has already been merged and was thus dropped from this series. Thanks, Chris. ^ permalink raw reply [flat|nested] 63+ messages in thread
* [PATCH v3 1/6] doc: fix/improve documentation of verdicts 2025-10-19 1:38 ` [PATCH v3 0/6] doc: miscellaneous improvements Christoph Anton Mitterer @ 2025-10-19 1:38 ` Christoph Anton Mitterer 2025-10-20 9:28 ` Florian Westphal 2025-10-19 1:38 ` [PATCH v3 2/6] doc: minor improvements with respect to the term “ruleset” Christoph Anton Mitterer ` (4 subsequent siblings) 5 siblings, 1 reply; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-10-19 1:38 UTC (permalink / raw) To: netfilter-devel; +Cc: Florian Westphal, pablo - 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. - Overhaul the description of `jump`/`goto`/`return`. `jump` only explains what the statement causes from the point of view of the new chain (that is: not, how the returning works), which includes that an implicit `return` is issued at the end of the chain. `goto` is explained in reference to `jump`. `return` describes abstractly how the return position is determined and what happens if there’s no position to return to (but not for example where an implicit `return` is issued). - Various other minor improvements/clarifications to wording. - List and explain verdict-like statements like `reject` which internally imply `accept` or `drop`. Further explain that with respect to evaluation these behave like their respectively implied verdicts. Link: https://lore.kernel.org/netfilter-devel/3c7ddca7029fa04baa2402d895f3a594a6480a3a.camel@scientia.org/T/#t Signed-off-by: Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> --- doc/statements.txt | 97 +++++++++++++++++++++++++++++++++------------- 1 file changed, 70 insertions(+), 27 deletions(-) diff --git a/doc/statements.txt b/doc/statements.txt index 834f95fb..1338471e 100644 --- a/doc/statements.txt +++ b/doc/statements.txt @@ -1,6 +1,7 @@ -VERDICT STATEMENT -~~~~~~~~~~~~~~~~~ -The verdict statement alters control flow in the ruleset and issues policy decisions for packets. +VERDICT STATEMENTS +~~~~~~~~~~~~~~~~~~ +The verdict statements alter control flow in the ruleset and issue policy +decisions for packets. [verse] ____ @@ -10,40 +11,82 @@ ____ 'CHAIN' := 'chain_name' | *{* 'statement' ... *}* ____ -*accept* and *drop* are absolute verdicts -- they terminate ruleset evaluation immediately. +*accept* and *drop* are absolute verdicts, which immediately terminate the +evaluation of the current rule, i.e. even any later statements of the current +rule won’t get executed. + +.*counter* will get executed: +------------------------------ +… counter accept +------------------------------ + +.*counter* won’t get executed: +------------------------------ +… accept counter +------------------------------ + +Further: [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. -*queue*:: Terminate ruleset evaluation and queue the packet to userspace. -Userspace must provide a drop or accept verdict. In case of accept, processing -resumes with the next base chain hook, not the rule following the queue verdict. +*accept*:: Terminate the evaluation of the current chain as well as any chains + in the call stack and accept the packet with respect to the base chain of + these. + Evaluation continues in the next base chain (of higher or possibly equal + priority from the same hook or of any priority from a later hook), if any. + This means the packet can still be dropped in any next base chain as well as + any regular chain (directly or indirectly) called from it. + For example, an *accept* in a chain of the *forward* hook still allows one to + *drop* (or *reject*, etc.) the packet in another *forward* hook base chain (and + any regular chains called from it) that has a higher priority number as well as + later in a chain of the *postrouting* hook. +*drop*:: Immediately drop the packet and terminate ruleset evaluation. + This means no further evaluation of any chains and it’s thus – unlike with + *accept* – not possible to again change the ultimate fate of the packet in any + later chain. + + +Terminate ruleset evaluation and drop the packet. This occurs + instantly, no further chains of any hooks are evaluated and it is thus not + possible to again accept the packet in a higher priority or later chain, as + those are not evaluated anymore for the packet. +*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. + When an absolute verdict is issued (respectively implied by a verdict-like + statement) in 'CHAIN', evaluation terminates as described above. +*goto* 'CHAIN':: Equal to *jump* except that the current position is not stored + in the call stack of chains. +*return*:: End evaluation of the current chain, pop the most recently added + position from the call stack of chains and continue evaluation 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) end evaluation of the current base chain (and any regular chains + called from it) using the base chain’s policy as implicit verdict. *continue*:: Continue ruleset evaluation with the next rule. This is the default behaviour in case a rule issues no verdict. -*return*:: Return from the current chain and continue evaluation at the - next rule in the last chain. If issued in a base chain, it is equivalent to the - base chain policy. -*jump* 'CHAIN':: Continue evaluation at the first rule in 'CHAIN'. The current - position in the ruleset is pushed to a call stack and evaluation will continue - there when the new chain is entirely evaluated or a *return* verdict is issued. - In case an absolute verdict is issued by a rule in the chain, ruleset evaluation - terminates immediately and the specific action is taken. -*goto* 'CHAIN':: Similar to *jump*, but the current position is not pushed to the - call stack, meaning that after the new chain evaluation will continue at the last - chain instead of the one containing the goto statement. +*queue*:: Terminate ruleset evaluation and queue the packet to userspace. + Userspace must provide a drop or accept verdict. In case of accept, processing + resumes with the next base chain hook, not the rule following the queue + verdict. An alternative to specifying the name of an existing, regular chain in 'CHAIN' is to specify an anonymous chain ad-hoc. Like with anonymous sets, it can't be referenced from another rule and will be removed along with the rule containing it. +All the above applies analogously to statements that imply a verdict: +*redirect*, *dnat*, *snat* and *masquerade* internally issue an *accept* +verdict at the end of their respective actions. +*reject* and *synproxy* internally issue a *drop* verdict at the end of their +respective actions. +These statements thus behave like their implied verdicts, but with side effects. + +For example, a *reject* also immediately terminates the evaluation of the +current rule as well as of all chains, overrules any *accept* from any other chains and can itself not be +overruled, while the various NAT statements may be overruled by other *drop* +verdict respectively statements that imply this. + .Using verdict statements ------------------- # process packets from eth0 and the internal network in from_lan -- 2.51.0 ^ permalink raw reply related [flat|nested] 63+ messages in thread
* Re: [PATCH v3 1/6] doc: fix/improve documentation of verdicts 2025-10-19 1:38 ` [PATCH v3 1/6] doc: fix/improve documentation of verdicts Christoph Anton Mitterer @ 2025-10-20 9:28 ` Florian Westphal 2025-10-20 22:13 ` Christoph Anton Mitterer 0 siblings, 1 reply; 63+ messages in thread From: Florian Westphal @ 2025-10-20 9:28 UTC (permalink / raw) To: Christoph Anton Mitterer; +Cc: netfilter-devel, pablo Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> wrote: > +*drop*:: Immediately drop the packet and terminate ruleset evaluation. > + This means no further evaluation of any chains and it’s thus – unlike with > + *accept* – not possible to again change the ultimate fate of the packet in any > + later chain. > + > + > +Terminate ruleset evaluation and drop the packet. This occurs Hmm, looks like something went wrong during a rebase? Why are there 2 blank lines followed by a rephrase of the first sentence? > +For example, a *reject* also immediately terminates the evaluation of the > +current rule as well as of all chains, overrules any *accept* from any other chains and can itself not be > +overruled, while the various NAT statements may be overruled by other *drop* > +verdict respectively statements that imply this. I totally dislike this sorry :-( There is no overruling, there is no 'verdict state tracking'. Or would you say that a qdisc that dropped a packet overruled a nft accept verdict...? Sorry for spinning on this again and again. Its important to me that users understand that packets traverse through netfilter hooks one after another until a drop verdict is seen or there are no more hooks. In a way, *accept* moves packet to the next basechain/hook, but thats all there is to it. All this talk about *overrule* makes it sound much more complicated than it is. Can you re-send this patch standalone, without this pragraph? Or perhaps just the 'For example, a *reject* also immediately *drops* the packet'. I did not spot anything else other than the format nit above. ^ permalink raw reply [flat|nested] 63+ messages in thread
* Re: [PATCH v3 1/6] doc: fix/improve documentation of verdicts 2025-10-20 9:28 ` Florian Westphal @ 2025-10-20 22:13 ` Christoph Anton Mitterer 0 siblings, 0 replies; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-10-20 22:13 UTC (permalink / raw) To: Florian Westphal; +Cc: netfilter-devel, pablo On Mon, 2025-10-20 at 11:28 +0200, Florian Westphal wrote: > Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> wrote: > > +*drop*:: Immediately drop the packet and terminate ruleset > > evaluation. > > + This means no further evaluation of any chains and it’s thus – > > unlike with > > + *accept* – not possible to again change the ultimate fate of the > > packet in any > > + later chain. > > + > > + > > +Terminate ruleset evaluation and drop the packet. This occurs > > > Hmm, looks like something went wrong during a rebase? > Why are there 2 blank lines followed by a rephrase of the first > sentence? Nah,... my fault, not rebase’s. O:-) Forgot to remove the old version. Sorry. > > +For example, a *reject* also immediately terminates the evaluation > > of the > > +current rule as well as of all chains, overrules any *accept* from > > any other chains and can itself not be > > +overruled, while the various NAT statements may be overruled by > > other *drop* > > +verdict respectively statements that imply this. > > I totally dislike this sorry :-( 😅️ > There is no overruling, there is no 'verdict state tracking'. > > Or would you say that a qdisc that dropped a packet overruled a nft > accept > verdict...? > > Sorry for spinning on this again and again. No worries. All fine. :-) > All this talk about *overrule* makes it sound much more complicated > than it is. > Can you re-send this patch standalone, without this pragraph? > > Or perhaps just the 'For example, a *reject* also immediately *drops* > the packet'. I removed these examples altogether. The sentence: > These statements thus behave like their implied verdicts, but with > side effects. should actually already be enough... maybe I overdid the "holding-the- reader's-hand-and-guiding-him-through" a bit here O:-) ^ permalink raw reply [flat|nested] 63+ messages in thread
* [PATCH v3 2/6] doc: minor improvements with respect to the term “ruleset” 2025-10-19 1:38 ` [PATCH v3 0/6] doc: miscellaneous improvements Christoph Anton Mitterer 2025-10-19 1:38 ` [PATCH v3 1/6] doc: fix/improve documentation of verdicts Christoph Anton Mitterer @ 2025-10-19 1:38 ` Christoph Anton Mitterer 2025-10-20 9:04 ` Florian Westphal 2025-10-19 1:38 ` [PATCH v3 3/6] doc: add overall description of the ruleset evaluation Christoph Anton Mitterer ` (3 subsequent siblings) 5 siblings, 1 reply; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-10-19 1:38 UTC (permalink / raw) To: netfilter-devel; +Cc: Florian Westphal, pablo Statements are elements of rules. Non-terminal statement are in particular passive with respect to their rules (and thus automatically with respect to the whole ruleset). In “Continue ruleset evaluation”, it’s not necessary to mention the ruleset as it’s obvious that the evaluation of the current chain will be continued. Signed-off-by: Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> --- doc/nft.txt | 6 +++--- doc/statements.txt | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/nft.txt b/doc/nft.txt index 78dbef66..49fffe2f 100644 --- a/doc/nft.txt +++ b/doc/nft.txt @@ -932,9 +932,9 @@ actions, such as logging, rejecting a packet, etc. + Statements exist in two kinds. Terminal statements unconditionally terminate evaluation of the current rule, non-terminal statements either only conditionally or never terminate evaluation of the current rule, in other words, -they are passive from the ruleset evaluation perspective. There can be an -arbitrary amount of non-terminal statements in a rule, but only a single -terminal statement as the final statement. +they are passive from the rule evaluation perspective. There can be an arbitrary +amount of non-terminal statements in a rule, but only a single terminal +statement as the final statement. include::statements.txt[] diff --git a/doc/statements.txt b/doc/statements.txt index 1338471e..61a4614d 100644 --- a/doc/statements.txt +++ b/doc/statements.txt @@ -63,8 +63,8 @@ Terminate ruleset evaluation and drop the packet. This occurs either the base chain or a regular chain that was reached solely via *goto* verdicts) end evaluation of the current base chain (and any regular chains called from it) using the base chain’s policy as implicit verdict. -*continue*:: Continue ruleset evaluation with the next rule. This - is the default behaviour in case a rule issues no verdict. +*continue*:: Continue evaluation with the next rule. This is the default + behaviour in case a rule issues no verdict. *queue*:: Terminate ruleset evaluation and queue the packet to userspace. Userspace must provide a drop or accept verdict. In case of accept, processing resumes with the next base chain hook, not the rule following the queue -- 2.51.0 ^ permalink raw reply related [flat|nested] 63+ messages in thread
* Re: [PATCH v3 2/6] doc: minor improvements with respect to the term “ruleset” 2025-10-19 1:38 ` [PATCH v3 2/6] doc: minor improvements with respect to the term “ruleset” Christoph Anton Mitterer @ 2025-10-20 9:04 ` Florian Westphal 0 siblings, 0 replies; 63+ messages in thread From: Florian Westphal @ 2025-10-20 9:04 UTC (permalink / raw) To: Christoph Anton Mitterer; +Cc: netfilter-devel, pablo Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> wrote: > Statements are elements of rules. Non-terminal statement are in particular > passive with respect to their rules (and thus automatically with respect to the > whole ruleset). > > In “Continue ruleset evaluation”, it’s not necessary to mention the ruleset as > it’s obvious that the evaluation of the current chain will be continued. > > Signed-off-by: Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> > --- > doc/nft.txt | 6 +++--- > doc/statements.txt | 4 ++-- > 2 files changed, 5 insertions(+), 5 deletions(-) > > diff --git a/doc/nft.txt b/doc/nft.txt > index 78dbef66..49fffe2f 100644 > --- a/doc/nft.txt > +++ b/doc/nft.txt > @@ -932,9 +932,9 @@ actions, such as logging, rejecting a packet, etc. + > Statements exist in two kinds. Terminal statements unconditionally terminate > evaluation of the current rule, non-terminal statements either only > conditionally or never terminate evaluation of the current rule, in other words, > -they are passive from the ruleset evaluation perspective. There can be an > -arbitrary amount of non-terminal statements in a rule, but only a single > -terminal statement as the final statement. > +they are passive from the rule evaluation perspective. There can be an arbitrary > +amount of non-terminal statements in a rule, but only a single terminal > +statement as the final statement. This is not really ideal as its hard to see what changes. I will apply this like this: -they are passive from the ruleset evaluation perspective. There can be an +they are passive from the rule evaluation perspective. There can be an ... as its much simpler to see the actual change without any of the formatting changes. No need to re-send this patch. ^ permalink raw reply [flat|nested] 63+ messages in thread
* [PATCH v3 3/6] doc: add overall description of the ruleset evaluation 2025-10-19 1:38 ` [PATCH v3 0/6] doc: miscellaneous improvements Christoph Anton Mitterer 2025-10-19 1:38 ` [PATCH v3 1/6] doc: fix/improve documentation of verdicts Christoph Anton Mitterer 2025-10-19 1:38 ` [PATCH v3 2/6] doc: minor improvements with respect to the term “ruleset” Christoph Anton Mitterer @ 2025-10-19 1:38 ` Christoph Anton Mitterer 2025-10-19 1:38 ` [PATCH v3 4/6] doc: add more documentation on bitmasks and sets Christoph Anton Mitterer ` (2 subsequent siblings) 5 siblings, 0 replies; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-10-19 1:38 UTC (permalink / raw) To: netfilter-devel; +Cc: Florian Westphal, pablo Signed-off-by: Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> --- doc/nft.txt | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/doc/nft.txt b/doc/nft.txt index 49fffe2f..363c67ba 100644 --- a/doc/nft.txt +++ b/doc/nft.txt @@ -572,6 +572,108 @@ 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 (and thus by netfilter), 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 and not known + to netfilter itself. + They are thus irrelevant with respect to netfilter’s 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 and regular chains may call other regular + chains (via *jump* and *goto* verdicts), in which case evaluation continues in + the called chain. + Base chains themsevlves cannot be called and only chains of the same table can + be called. +* For each hook, the attached 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 (and any regular chains called + from that). + It accepts the packet only with respect to the current base chain. Any other + base chain (or regular chain called by such) with a higher priority of the + same hook as well as any other base chain (or regular chain called by such) of + any later hook may however still ultimately *drop* (which might also be done + via verdict-like statements that imply *drop*, like *reject*) the packet with + an according verdict (with consequences as described below for *drop*). + Thus and merely from netfilter’s point of view, a packet is only ultimately + accepted if none of the chains (regardless of their tables) that are attached + to any of the respectively relevant hooks issues a *drop* verdict (be it + explicitly or implicitly by policy or via a verdict-like statement that + implies *drop*, for example *reject*), which already means that there has to + be at least one *accept* verdict (be it explicitly or implicitly by policy). + All this applies analogously 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 and ultimately drops the + packet. + Unlike with an *accept* verdict, no further chains of any hook and regardless + of their table get evaluated and it’s therefore not possible to have an *drop* + verdict overruled. + 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. + All this applies analogously to verdict-like 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*, *continue* and + *queue*) 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] 63+ messages in thread
* [PATCH v3 4/6] doc: add more documentation on bitmasks and sets 2025-10-19 1:38 ` [PATCH v3 0/6] doc: miscellaneous improvements Christoph Anton Mitterer ` (2 preceding siblings ...) 2025-10-19 1:38 ` [PATCH v3 3/6] doc: add overall description of the ruleset evaluation Christoph Anton Mitterer @ 2025-10-19 1:38 ` Christoph Anton Mitterer 2025-10-20 9:06 ` Florian Westphal 2025-10-19 1:38 ` [PATCH v3 5/6] doc: describe include’s collation order to be that of the C locale Christoph Anton Mitterer 2025-10-19 1:38 ` [PATCH v3 6/6] doc: minor improvements the `reject` statement Christoph Anton Mitterer 5 siblings, 1 reply; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-10-19 1:38 UTC (permalink / raw) To: netfilter-devel; +Cc: Florian Westphal, pablo Signed-off-by: Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> --- doc/data-types.txt | 26 ++++++++++++++++++++++++++ doc/nft.txt | 10 ++++++++++ 2 files changed, 36 insertions(+) diff --git a/doc/data-types.txt b/doc/data-types.txt index 18af266a..8a86060d 100644 --- a/doc/data-types.txt +++ b/doc/data-types.txt @@ -26,6 +26,32 @@ integer The bitmask type (*bitmask*) is used for bitmasks. +In expressions the bits of a bitmask may be specified as *'bit'[,'bit']...* with +'bit' being the value of the bit or a pre-defined symbolic constant, if any (for +example *ct state*’s bit 0x1 has the symbolic constant `new`). + +Equality of a value with such bitmask is given, if the value has any of the +bitmask’s bits set (and optionally others). + +The syntax *'expression' 'value' / 'mask'* is identical to +*'expression' and 'mask' == 'value'*. +For example `tcp flags syn,ack / syn,ack,fin,rst` is the same as +`tcp flags and (syn|ack|fin|rst) == syn|ack`. + +Note that *'expression' 'bit'[,'bit']...* is not the same as *'expression' +{'bit'[,'bit']...}* and analogously with a named set. +The latter constitute a lookup in a set and will match only if the set contains +exactly one value that matches. +For example: *tcp flags syn,ack* matches packets that have at least the flag SYN +, the flag ACK or the flags SYN and ACK set (regardless of whether or not any +other flags are set), whereas *tcp flags { syn, ack }* matches only packets that +have either only the flag SYN or only the flag ACK set (with all other flags +having to be not set). +See also <<SETS>> above. + +As usual, the the *nft describe* command may be used to get details on a data +type, which for bitmasks shows the symbolic names and values of the bits. + STRING TYPE ~~~~~~~~~~~~ [options="header"] diff --git a/doc/nft.txt b/doc/nft.txt index 363c67ba..09da6f28 100644 --- a/doc/nft.txt +++ b/doc/nft.txt @@ -776,6 +776,16 @@ Example: When the set contains range *1.2.3.1-1.2.3.4*, then adding element *1.2 effect. Adding *1.2.3.5* changes the existing range to cover *1.2.3.1-1.2.3.5*. Without this flag, *1.2.3.2* can not be added and *1.2.3.5* is inserted as a new entry. +Equality of a value with a set is given if the value matches exactly one value +in the set. +It shall be noted that for bitmask values this means, that +*'expression' 'bit'[,'bit']...* (which yields true if *any* of the bits are set) +is not the same as *'expression' {'bit'[,'bit']...}* (which yields true if +exactly one of the bits are set). +It may however be (effectively) the same, in cases like +`ct state established,related` and `ct state {established,related}`, where these +states are mutually exclusive. + MAPS ----- [verse] -- 2.51.0 ^ permalink raw reply related [flat|nested] 63+ messages in thread
* Re: [PATCH v3 4/6] doc: add more documentation on bitmasks and sets 2025-10-19 1:38 ` [PATCH v3 4/6] doc: add more documentation on bitmasks and sets Christoph Anton Mitterer @ 2025-10-20 9:06 ` Florian Westphal 2025-10-20 21:57 ` Christoph Anton Mitterer 0 siblings, 1 reply; 63+ messages in thread From: Florian Westphal @ 2025-10-20 9:06 UTC (permalink / raw) To: Christoph Anton Mitterer; +Cc: netfilter-devel, pablo Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> wrote: > Signed-off-by: Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> > --- > doc/data-types.txt | 26 ++++++++++++++++++++++++++ > doc/nft.txt | 10 ++++++++++ > 2 files changed, 36 insertions(+) > > diff --git a/doc/data-types.txt b/doc/data-types.txt > index 18af266a..8a86060d 100644 > --- a/doc/data-types.txt > +++ b/doc/data-types.txt > @@ -26,6 +26,32 @@ integer > > The bitmask type (*bitmask*) is used for bitmasks. > > +In expressions the bits of a bitmask may be specified as *'bit'[,'bit']...* with > +'bit' being the value of the bit or a pre-defined symbolic constant, if any (for > +example *ct state*’s bit 0x1 has the symbolic constant `new`). > + > +Equality of a value with such bitmask is given, if the value has any of the > +bitmask’s bits set (and optionally others). > + > +The syntax *'expression' 'value' / 'mask'* is identical to > +*'expression' and 'mask' == 'value'*. > +For example `tcp flags syn,ack / syn,ack,fin,rst` is the same as > +`tcp flags and (syn|ack|fin|rst) == syn|ack`. > + > +Note that *'expression' 'bit'[,'bit']...* is not the same as *'expression' > +{'bit'[,'bit']...}* and analogously with a named set. > +The latter constitute a lookup in a set and will match only if the set contains > +exactly one value that matches. > +For example: *tcp flags syn,ack* matches packets that have at least the flag SYN > +, the flag ACK or the flags SYN and ACK set (regardless of whether or not any > +other flags are set), whereas *tcp flags { syn, ack }* matches only packets that > +have either only the flag SYN or only the flag ACK set (with all other flags > +having to be not set). > +See also <<SETS>> above. > + > +As usual, the the *nft describe* command may be used to get details on a data > +type, which for bitmasks shows the symbolic names and values of the bits. > + > STRING TYPE > ~~~~~~~~~~~~ > [options="header"] > diff --git a/doc/nft.txt b/doc/nft.txt > index 363c67ba..09da6f28 100644 > --- a/doc/nft.txt > +++ b/doc/nft.txt > @@ -776,6 +776,16 @@ Example: When the set contains range *1.2.3.1-1.2.3.4*, then adding element *1.2 > effect. Adding *1.2.3.5* changes the existing range to cover *1.2.3.1-1.2.3.5*. > Without this flag, *1.2.3.2* can not be added and *1.2.3.5* is inserted as a new entry. > > +Equality of a value with a set is given if the value matches exactly one value > +in the set. That contradicts whats right above, which describes range handling. > +It shall be noted that for bitmask values this means, that > +*'expression' 'bit'[,'bit']...* (which yields true if *any* of the bits are set) > +is not the same as *'expression' {'bit'[,'bit']...}* (which yields true if > +exactly one of the bits are set). > +It may however be (effectively) the same, in cases like > +`ct state established,related` and `ct state {established,related}`, where these > +states are mutually exclusive. Would you object if I apply this patch but onlt rhe first part? I think the above just duplicates what is explained in the new bitmask part above it. ^ permalink raw reply [flat|nested] 63+ messages in thread
* Re: [PATCH v3 4/6] doc: add more documentation on bitmasks and sets 2025-10-20 9:06 ` Florian Westphal @ 2025-10-20 21:57 ` Christoph Anton Mitterer 2025-10-20 22:18 ` Florian Westphal 0 siblings, 1 reply; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-10-20 21:57 UTC (permalink / raw) To: Florian Westphal; +Cc: netfilter-devel, pablo On Mon, 2025-10-20 at 11:06 +0200, Florian Westphal wrote: > Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> wrote: > > +Equality of a value with a set is given if the value matches > > exactly one value > > +in the set. > > That contradicts whats right above, which describes range handling. Uhm... what exactly do you mean? But that's anyway missing the stuff about how interval values are matched, which I only sent as stand alone patch in some mail before. > > +It shall be noted that for bitmask values this means, that > > +*'expression' 'bit'[,'bit']...* (which yields true if *any* of the > > bits are set) > > +is not the same as *'expression' {'bit'[,'bit']...}* (which yields > > true if > > +exactly one of the bits are set). > > +It may however be (effectively) the same, in cases like > > +`ct state established,related` and `ct state > > {established,related}`, where these > > +states are mutually exclusive. > > Would you object if I apply this patch but onlt rhe first part? You mean: not the changes to nft.txt? What I think should be kept being added to the SETS documentation in nft.txt is: > Equality of a value with a set is given if the value matches exactly > one value > in the set (which for intervals means that it’s contained in any of > them). Cause that's currently nowhere really documented, AFAICS. The following: > It shall be noted that for bitmask values this means, that > *'expression' 'bit'[,'bit']...* (which yields true if *any* of the > bits are set) > is not the same as *'expression' {'bit'[,'bit']...}* (which yields > true if > exactly one of the bits are set). Can be skipped, or maybe one should add a small reference like: > See <<BITMASK TYPE>> for how equality checks differ between sets and > bitmasks. This: > It may however be (effectively) the same, in cases like > `ct state established,related` and `ct state {established,related}`, > where these > states are mutually exclusive. We can either simply drop, or move over to the BITMASK TYPE section. It's not super important, but I think there might be some value in understanding why these are identical (especially as many people use something like ct new {established,related}. > I think the above just duplicates what is explained in the new > bitmask part above it. Other than the above... I'd be fine with that :-) Wanna a new patch or are you going to do it yourself? Cheers, Chris. ^ permalink raw reply [flat|nested] 63+ messages in thread
* Re: [PATCH v3 4/6] doc: add more documentation on bitmasks and sets 2025-10-20 21:57 ` Christoph Anton Mitterer @ 2025-10-20 22:18 ` Florian Westphal 2025-10-20 23:51 ` Christoph Anton Mitterer 0 siblings, 1 reply; 63+ messages in thread From: Florian Westphal @ 2025-10-20 22:18 UTC (permalink / raw) To: Christoph Anton Mitterer; +Cc: netfilter-devel, pablo Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> wrote: > On Mon, 2025-10-20 at 11:06 +0200, Florian Westphal wrote: > > Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> wrote: > > > +Equality of a value with a set is given if the value matches > > > exactly one value > > > +in the set. > > > > That contradicts whats right above, which describes range handling. > > Uhm... what exactly do you mean? > > But that's anyway missing the stuff about how interval values are > matched, which I only sent as stand alone patch in some mail before. Yes, I meant wrt. intervals, there is no need for the value to be in the set, match can also happen via range. > > > +It shall be noted that for bitmask values this means, that > > > +*'expression' 'bit'[,'bit']...* (which yields true if *any* of the > > > bits are set) > > > +is not the same as *'expression' {'bit'[,'bit']...}* (which yields > > > true if > > > +exactly one of the bits are set). > > > +It may however be (effectively) the same, in cases like > > > +`ct state established,related` and `ct state > > > {established,related}`, where these > > > +states are mutually exclusive. > > > > Would you object if I apply this patch but onlt rhe first part? > > You mean: not the changes to nft.txt? Yes, only those in doc/data-types.txt, namely this: diff --git a/doc/data-types.txt b/doc/data-types.txt --- a/doc/data-types.txt +++ b/doc/data-types.txt @@ -26,6 +26,30 @@ integer The bitmask type (*bitmask*) is used for bitmasks. +In expressions the bits of a bitmask may be specified as *'bit'[,'bit']...* with +'bit' being the value of the bit or a pre-defined symbolic constant, if any (for +example *ct state*’s bit 0x1 has the symbolic constant `new`). + +Equality of a value with such bitmask is given, if the value has any of the +bitmask’s bits set (and optionally others). + +The syntax *'expression' 'value' / 'mask'* is identical to +*'expression' and 'mask' == 'value'*. +For example `tcp flags syn,ack / syn,ack,fin,rst` is the same as +`tcp flags and (syn|ack|fin|rst) == syn|ack`. + +Note that *'expression' 'bit'[,'bit']...* is not the same as *'expression' +{'bit'[,'bit']...}*. +The latter form is a lookup in an anonymous set and will match only if the set +contains a matching value. +Example: *tcp flags syn,ack* matches packets that have the SYN, the ACK, or both +SYN and ACK flags set. Other flags, such as PSH, are ignored. +*tcp flags { syn, ack }* matches packets that have only the SYN or only the ACK +flag set, all other flag bits must be unset. + +As usual, the the *nft describe* command may be used to get details on a data +type, which for bitmasks shows the symbolic names and values of the bits. This contains minor edits only, I don't see anything wrong with the above and I think that this is a worthwhile addition to the documentation. > What I think should be kept being added to the SETS documentation in > nft.txt is: > > Equality of a value with a set is given if the value matches exactly > > one value > > in the set (which for intervals means that it’s contained in any of > > them). > > Cause that's currently nowhere really documented, AFAICS. Oh, yes, makes sense to mention that. > The following: > > It shall be noted that for bitmask values this means, that > > *'expression' 'bit'[,'bit']...* (which yields true if *any* of the > > bits are set) > > is not the same as *'expression' {'bit'[,'bit']...}* (which yields > > true if > > exactly one of the bits are set). > > Can be skipped, or maybe one should add a small reference like: > > See <<BITMASK TYPE>> for how equality checks differ between sets and > > bitmasks. Reference is fine. > This: > > It may however be (effectively) the same, in cases like > > `ct state established,related` and `ct state {established,related}`, > > where these > > states are mutually exclusive. > > We can either simply drop, or move over to the BITMASK TYPE section. > It's not super important, but I think there might be some value in > understanding why these are identical (especially as many people use > something like ct new {established,related}. Yes, its an exception by virtue of a flow being either-or... If you think it should be documented then maybe it should be added to the bitmask example, it documents the difference with tcp flags example, maybe the counterexample can be made there. (Its not identical, however -- the established,related [ no set ] way is slightly faster). > Wanna a new patch or are you going to do it yourself? Can you just revamp this one patch? I thin its the one closest to applicable form. ^ permalink raw reply [flat|nested] 63+ messages in thread
* Re: [PATCH v3 4/6] doc: add more documentation on bitmasks and sets 2025-10-20 22:18 ` Florian Westphal @ 2025-10-20 23:51 ` Christoph Anton Mitterer 0 siblings, 0 replies; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-10-20 23:51 UTC (permalink / raw) To: Florian Westphal; +Cc: netfilter-devel, pablo On Tue, 2025-10-21 at 00:18 +0200, Florian Westphal wrote: > Yes, I meant wrt. intervals, there is no need for the value to be in > the set, match can also happen via range. Integrated this now. > +In expressions the bits of a bitmask may be specified as > *'bit'[,'bit']...* with > +'bit' being the value of the bit or a pre-defined symbolic constant, > if any (for > +example *ct state*’s bit 0x1 has the symbolic constant `new`). > + > +Equality of a value with such bitmask is given, if the value has any > of the > +bitmask’s bits set (and optionally others). > + > +The syntax *'expression' 'value' / 'mask'* is identical to > +*'expression' and 'mask' == 'value'*. > +For example `tcp flags syn,ack / syn,ack,fin,rst` is the same as > +`tcp flags and (syn|ack|fin|rst) == syn|ack`. > + > +Note that *'expression' 'bit'[,'bit']...* is not the same as > *'expression' > +{'bit'[,'bit']...}*. > +The latter form is a lookup in an anonymous set and will match only > if the set > +contains a matching value. > +Example: *tcp flags syn,ack* matches packets that have the SYN, the > ACK, or both > +SYN and ACK flags set. Other flags, such as PSH, are ignored. > +*tcp flags { syn, ack }* matches packets that have only the SYN or > only the ACK > +flag set, all other flag bits must be unset. > + > +As usual, the the *nft describe* command may be used to get details > on a data > +type, which for bitmasks shows the symbolic names and values of the > bits. > > This contains minor edits only, I don't see anything wrong with the > above and I think that this is a worthwhile addition to the > documentation. I also had some further edits there meanwhile and changed the example. Hope I haven't accidentally dropped anything from yours. Differences: - Added a "generally" to indicate that sometimes using bitmasks and sets is actually effectively the same. Gave that also as an example and mentioned that bitmask matches are faster. - I think it's better not to restrict that only on anonymous sets, cause then it's unclear whether it applies to named ones, too. So did that. - I think it's better to have "contains exactly one value that matches" rather than "contains a matching value". The latter wording would IMO allow for there being 1 or many matches. - In the example with SYN,ACK: - Sneaked back an "either [... or]", hoping you don't see it ;-) - Removed the PSH... IMO unnecessary here, it's enough to many that others are ignored. Also we give no example flag or the set case either. - Made "all other flag bits must be unset" a standalone sentence, like in the bitmask case. > > We can either simply drop, or move over to the BITMASK TYPE > > section. > > It's not super important, but I think there might be some value in > > understanding why these are identical (especially as many people > > use > > something like ct new {established,related}. > > Yes, its an exception by virtue of a flow being either-or... > > If you think it should be documented then maybe it should be added to > the bitmask example, it documents the difference with tcp flags > example, > maybe the counterexample can be made there. Had meanwhile already done basically that... please check when I send v4 :-) Cheers, Chris. ^ permalink raw reply [flat|nested] 63+ messages in thread
* [PATCH v3 5/6] doc: describe include’s collation order to be that of the C locale 2025-10-19 1:38 ` [PATCH v3 0/6] doc: miscellaneous improvements Christoph Anton Mitterer ` (3 preceding siblings ...) 2025-10-19 1:38 ` [PATCH v3 4/6] doc: add more documentation on bitmasks and sets Christoph Anton Mitterer @ 2025-10-19 1:38 ` Christoph Anton Mitterer 2025-10-19 1:38 ` [PATCH v3 6/6] doc: minor improvements the `reject` statement Christoph Anton Mitterer 5 siblings, 0 replies; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-10-19 1:38 UTC (permalink / raw) To: netfilter-devel; +Cc: Florian Westphal, pablo Currently, `nft` doesn’t call `setlocale(3)` and thus `glob(3)` uses the `C` locale. Document this as it’s possibly relevant to the ordering of included rules. This also makes the collation order “official” so any future localisation would need to adhere to that. Signed-off-by: Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> --- doc/nft.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/nft.txt b/doc/nft.txt index 09da6f28..15a54f23 100644 --- a/doc/nft.txt +++ b/doc/nft.txt @@ -165,8 +165,8 @@ Include statements support the usual shell wildcard symbols (*,?,[]). Having no matches for an include statement is not an error, if wildcard symbols are used in the include statement. This allows having potentially empty include directories for statements like **include "/etc/firewall/rules/*"**. The wildcard -matches are loaded in alphabetical order. Files beginning with dot (.) are not -matched by include statements. +matches are loaded in the collation order of the C locale. Files beginning with +dot (.) are not matched by include statements. SYMBOLIC VARIABLES ~~~~~~~~~~~~~~~~~~ -- 2.51.0 ^ permalink raw reply related [flat|nested] 63+ messages in thread
* [PATCH v3 6/6] doc: minor improvements the `reject` statement 2025-10-19 1:38 ` [PATCH v3 0/6] doc: miscellaneous improvements Christoph Anton Mitterer ` (4 preceding siblings ...) 2025-10-19 1:38 ` [PATCH v3 5/6] doc: describe include’s collation order to be that of the C locale Christoph Anton Mitterer @ 2025-10-19 1:38 ` Christoph Anton Mitterer 5 siblings, 0 replies; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-10-19 1:38 UTC (permalink / raw) To: netfilter-devel; +Cc: Florian Westphal, pablo Signed-off-by: Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> --- doc/statements.txt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/statements.txt b/doc/statements.txt index 61a4614d..5d34d03a 100644 --- a/doc/statements.txt +++ b/doc/statements.txt @@ -233,10 +233,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] 63+ messages in thread
* [PATCH v4 0/5] doc: miscellaneous improvements 2025-09-25 0:07 nft manpage/wiki issues and improvement ideas Christoph Anton Mitterer ` (2 preceding siblings ...) 2025-10-19 1:38 ` [PATCH v3 0/6] doc: miscellaneous improvements Christoph Anton Mitterer @ 2025-10-20 23:49 ` Christoph Anton Mitterer 2025-10-20 23:49 ` [PATCH v4 1/5] doc: fix/improve documentation of verdicts Christoph Anton Mitterer ` (4 more replies) 3 siblings, 5 replies; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-10-20 23:49 UTC (permalink / raw) To: netfilter-devel; +Cc: fw, pablo Hey. v4 as per discussion on the list. I guess #2 (and perhaps even #1) may still not make you completely happy, thogh. Thanks, Chris. ^ permalink raw reply [flat|nested] 63+ messages in thread
* [PATCH v4 1/5] doc: fix/improve documentation of verdicts 2025-10-20 23:49 ` [PATCH v4 0/5] doc: miscellaneous improvements Christoph Anton Mitterer @ 2025-10-20 23:49 ` Christoph Anton Mitterer 2025-10-20 23:49 ` [PATCH v4 2/5] doc: add overall description of the ruleset evaluation Christoph Anton Mitterer ` (3 subsequent siblings) 4 siblings, 0 replies; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-10-20 23:49 UTC (permalink / raw) To: netfilter-devel; +Cc: fw, pablo - 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. - Overhaul the description of `jump`/`goto`/`return`. `jump` only explains what the statement causes from the point of view of the new chain (that is: not, how the returning works), which includes that an implicit `return` is issued at the end of the chain. `goto` is explained in reference to `jump`. `return` describes abstractly how the return position is determined and what happens if there’s no position to return to (but not for example where an implicit `return` is issued). - Various other minor improvements/clarifications to wording. - List and explain verdict-like statements like `reject` which internally imply `accept` or `drop`. Further explain that with respect to evaluation these behave like their respectively implied verdicts. Link: https://lore.kernel.org/netfilter-devel/3c7ddca7029fa04baa2402d895f3a594a6480a3a.camel@scientia.org/T/#t Signed-off-by: Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> --- doc/statements.txt | 86 +++++++++++++++++++++++++++++++--------------- 1 file changed, 59 insertions(+), 27 deletions(-) diff --git a/doc/statements.txt b/doc/statements.txt index e275ee39..e1d8552c 100644 --- a/doc/statements.txt +++ b/doc/statements.txt @@ -1,6 +1,7 @@ -VERDICT STATEMENT -~~~~~~~~~~~~~~~~~ -The verdict statement alters control flow in the ruleset and issues policy decisions for packets. +VERDICT STATEMENTS +~~~~~~~~~~~~~~~~~~ +The verdict statements alter control flow in the ruleset and issue policy +decisions for packets. [verse] ____ @@ -10,40 +11,71 @@ ____ 'CHAIN' := 'chain_name' | *{* 'statement' ... *}* ____ -*accept* and *drop* are absolute verdicts -- they terminate ruleset evaluation immediately. +*accept* and *drop* are absolute verdicts, which immediately terminate the +evaluation of the current rule, i.e. even any later statements of the current +rule won’t get executed. + +.*counter* will get executed: +------------------------------ +… counter accept +------------------------------ + +.*counter* won’t get executed: +------------------------------ +… accept counter +------------------------------ + +Further: [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. -*queue*:: Terminate ruleset evaluation and queue the packet to userspace. -Userspace must provide a drop or accept verdict. In case of accept, processing -resumes with the next base chain hook, not the rule following the queue verdict. +*accept*:: Terminate the evaluation of the current chain as well as any chains + in the call stack and accept the packet with respect to the base chain of + these. + Evaluation continues in the next base chain (of higher or possibly equal + priority from the same hook or of any priority from a later hook), if any. + This means the packet can still be dropped in any next base chain as well as + any regular chain (directly or indirectly) called from it. + For example, an *accept* in a chain of the *forward* hook still allows one to + *drop* (or *reject*, etc.) the packet in another *forward* hook base chain (and + any regular chains called from it) that has a higher priority number as well as + later in a chain of the *postrouting* hook. +*drop*:: Immediately drop the packet and terminate ruleset evaluation. + This means no further evaluation of any chains and it’s thus – unlike with + *accept* – not possible to again change the ultimate fate of the packet in any + later chain. +*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. + When an absolute verdict is issued (respectively implied by a verdict-like + statement) in 'CHAIN', evaluation terminates as described above. +*goto* 'CHAIN':: Equal to *jump* except that the current position is not stored + in the call stack of chains. +*return*:: End evaluation of the current chain, pop the most recently added + position from the call stack of chains and continue evaluation 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) end evaluation of the current base chain (and any regular chains + called from it) using the base chain’s policy as implicit verdict. *continue*:: Continue evaluation with the next rule. This is the default behaviour in case a rule issues no verdict. -*return*:: Return from the current chain and continue evaluation at the - next rule in the last chain. If issued in a base chain, it is equivalent to the - base chain policy. -*jump* 'CHAIN':: Continue evaluation at the first rule in 'CHAIN'. The current - position in the ruleset is pushed to a call stack and evaluation will continue - there when the new chain is entirely evaluated or a *return* verdict is issued. - In case an absolute verdict is issued by a rule in the chain, ruleset evaluation - terminates immediately and the specific action is taken. -*goto* 'CHAIN':: Similar to *jump*, but the current position is not pushed to the - call stack, meaning that after the new chain evaluation will continue at the last - chain instead of the one containing the goto statement. +*queue*:: Terminate ruleset evaluation and queue the packet to userspace. + Userspace must provide a drop or accept verdict. In case of accept, processing + resumes with the next base chain hook, not the rule following the queue + verdict. An alternative to specifying the name of an existing, regular chain in 'CHAIN' is to specify an anonymous chain ad-hoc. Like with anonymous sets, it can't be referenced from another rule and will be removed along with the rule containing it. +All the above applies analogously to statements that imply a verdict: +*redirect*, *dnat*, *snat* and *masquerade* internally issue an *accept* +verdict at the end of their respective actions. +*reject* and *synproxy* internally issue a *drop* verdict at the end of their +respective actions. +These statements thus behave like their implied verdicts, but with side effects. + .Using verdict statements ------------------- # process packets from eth0 and the internal network in from_lan -- 2.51.0 ^ permalink raw reply related [flat|nested] 63+ messages in thread
* [PATCH v4 2/5] doc: add overall description of the ruleset evaluation 2025-10-20 23:49 ` [PATCH v4 0/5] doc: miscellaneous improvements Christoph Anton Mitterer 2025-10-20 23:49 ` [PATCH v4 1/5] doc: fix/improve documentation of verdicts Christoph Anton Mitterer @ 2025-10-20 23:49 ` Christoph Anton Mitterer 2025-10-20 23:49 ` [PATCH v4 3/5] doc: add more documentation on bitmasks and sets Christoph Anton Mitterer ` (2 subsequent siblings) 4 siblings, 0 replies; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-10-20 23:49 UTC (permalink / raw) To: netfilter-devel; +Cc: fw, pablo Signed-off-by: Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> --- doc/nft.txt | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/doc/nft.txt b/doc/nft.txt index 2ca601b1..4cd3fa8e 100644 --- a/doc/nft.txt +++ b/doc/nft.txt @@ -572,6 +572,107 @@ 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 (and thus by netfilter), 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 and not known + to netfilter itself. + They are thus irrelevant with respect to netfilter’s 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 and regular chains may call other regular + chains (via *jump* and *goto* verdicts), in which case evaluation continues in + the called chain. + Base chains themsevlves cannot be called and only chains of the same table can + be called. +* For each hook, the attached 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 (and any regular chains called + from that). + It accepts the packet only with respect to the current base chain. Any other + base chain (or regular chain called by such) with a higher priority of the + same hook as well as any other base chain (or regular chain called by such) of + any later hook may however still ultimately *drop* (which might also be done + via verdict-like statements that imply *drop*, like *reject*) the packet with + an according verdict (with consequences as described below for *drop*). + Thus and merely from netfilter’s point of view, a packet is only ultimately + accepted if none of the chains of the relevant hooks issues a *drop* verdict + (be it explicitly or implicitly by policy or via a verdict-like statement that + implies *drop*, for example *reject*), which already means that there has to + be at least one *accept* verdict (be it explicitly or implicitly by policy). + All this applies analogously 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 and ultimately drops the + packet. + Unlike with an *accept* verdict, no further chains of any hook and regardless + of their table get evaluated and it’s 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 any + regular chain directly or indirectly called by it) must accept a packet or it + is ensured to be ultimately dropped by it. + All this applies analogously to verdict-like 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*, *continue* and + *queue*) 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] 63+ messages in thread
* [PATCH v4 3/5] doc: add more documentation on bitmasks and sets 2025-10-20 23:49 ` [PATCH v4 0/5] doc: miscellaneous improvements Christoph Anton Mitterer 2025-10-20 23:49 ` [PATCH v4 1/5] doc: fix/improve documentation of verdicts Christoph Anton Mitterer 2025-10-20 23:49 ` [PATCH v4 2/5] doc: add overall description of the ruleset evaluation Christoph Anton Mitterer @ 2025-10-20 23:49 ` Christoph Anton Mitterer 2025-10-20 23:49 ` [PATCH v4 4/5] doc: describe include’s collation order to be that of the C locale Christoph Anton Mitterer 2025-10-20 23:49 ` [PATCH v4 5/5] doc: minor improvements the `reject` statement Christoph Anton Mitterer 4 siblings, 0 replies; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-10-20 23:49 UTC (permalink / raw) To: netfilter-devel; +Cc: fw, pablo Signed-off-by: Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> --- doc/data-types.txt | 31 +++++++++++++++++++++++++++++++ doc/nft.txt | 5 +++++ 2 files changed, 36 insertions(+) diff --git a/doc/data-types.txt b/doc/data-types.txt index 18af266a..b36ca768 100644 --- a/doc/data-types.txt +++ b/doc/data-types.txt @@ -26,6 +26,37 @@ integer The bitmask type (*bitmask*) is used for bitmasks. +In expressions the bits of a bitmask may be specified as *'bit'[,'bit']...* with +'bit' being the value of the bit or a pre-defined symbolic constant, if any (for +example *ct state*’s bit 0x1 has the symbolic constant `new`). + +Equality of a value with such bitmask is given, if the value has any of the +bitmask’s bits set (and optionally others). + +The syntax *'expression' 'value' / 'mask'* is identical to +*'expression' and 'mask' == 'value'*. +For example `tcp flags syn,ack / syn,ack,fin,rst` is the same as +`tcp flags and (syn|ack|fin|rst) == syn|ack`. + +Note that *'expression' 'bit'[,'bit']...* is not generally the same as +*'expression' {'bit'[,'bit']...}* and analogously with a named set. +The latter forms are lookups in a set and will match only if the set contains +exactly one value that matches. +They are however effectively the same (with matching bitmasks typically being +faster) when all bits are semantically mutually exclusive. + +Examples: +* *tcp flags syn,ack* matches packets that have the SYN, the ACK or both SYN and + ACK flags set. Other flags are ignored. + *tcp flags { syn, ack }* matches packets that have either only the SYN or only + the ACK flag set. All other flags must be unset. +* *ct state established,related* and *ct state { established, related } * match + exactly the same packets, because the bits of *ct state* are all mutually + exclusive. + +As usual, the the *nft describe* command may be used to get details on a data +type, which for bitmasks shows the symbolic names and values of the bits. + STRING TYPE ~~~~~~~~~~~~ [options="header"] diff --git a/doc/nft.txt b/doc/nft.txt index 4cd3fa8e..4ae998ed 100644 --- a/doc/nft.txt +++ b/doc/nft.txt @@ -775,6 +775,11 @@ Example: When the set contains range *1.2.3.1-1.2.3.4*, then adding element *1.2 effect. Adding *1.2.3.5* changes the existing range to cover *1.2.3.1-1.2.3.5*. Without this flag, *1.2.3.2* can not be added and *1.2.3.5* is inserted as a new entry. +Equality of a value with a set is given if the value matches exactly one value +in the set (which for intervals means that it’s contained in any of them). +See <<BITMASK TYPE>> for the subtle differences between syntactically similarly +looking equiality checks of sets and bitmasks. + MAPS ----- [verse] -- 2.51.0 ^ permalink raw reply related [flat|nested] 63+ messages in thread
* [PATCH v4 4/5] doc: describe include’s collation order to be that of the C locale 2025-10-20 23:49 ` [PATCH v4 0/5] doc: miscellaneous improvements Christoph Anton Mitterer ` (2 preceding siblings ...) 2025-10-20 23:49 ` [PATCH v4 3/5] doc: add more documentation on bitmasks and sets Christoph Anton Mitterer @ 2025-10-20 23:49 ` Christoph Anton Mitterer 2025-10-20 23:49 ` [PATCH v4 5/5] doc: minor improvements the `reject` statement Christoph Anton Mitterer 4 siblings, 0 replies; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-10-20 23:49 UTC (permalink / raw) To: netfilter-devel; +Cc: fw, pablo Currently, `nft` doesn’t call `setlocale(3)` and thus `glob(3)` uses the `C` locale. Document this as it’s possibly relevant to the ordering of included rules. This also makes the collation order “official” so any future localisation would need to adhere to that. Signed-off-by: Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> --- doc/nft.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/nft.txt b/doc/nft.txt index 4ae998ed..3fd19c8d 100644 --- a/doc/nft.txt +++ b/doc/nft.txt @@ -165,8 +165,8 @@ Include statements support the usual shell wildcard symbols (*,?,[]). Having no matches for an include statement is not an error, if wildcard symbols are used in the include statement. This allows having potentially empty include directories for statements like **include "/etc/firewall/rules/*"**. The wildcard -matches are loaded in alphabetical order. Files beginning with dot (.) are not -matched by include statements. +matches are loaded in the collation order of the C locale. Files beginning with +dot (.) are not matched by include statements. SYMBOLIC VARIABLES ~~~~~~~~~~~~~~~~~~ -- 2.51.0 ^ permalink raw reply related [flat|nested] 63+ messages in thread
* [PATCH v4 5/5] doc: minor improvements the `reject` statement 2025-10-20 23:49 ` [PATCH v4 0/5] doc: miscellaneous improvements Christoph Anton Mitterer ` (3 preceding siblings ...) 2025-10-20 23:49 ` [PATCH v4 4/5] doc: describe include’s collation order to be that of the C locale Christoph Anton Mitterer @ 2025-10-20 23:49 ` Christoph Anton Mitterer 2025-10-22 14:34 ` Florian Westphal 4 siblings, 1 reply; 63+ messages in thread From: Christoph Anton Mitterer @ 2025-10-20 23:49 UTC (permalink / raw) To: netfilter-devel; +Cc: fw, pablo Signed-off-by: Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> --- doc/statements.txt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/statements.txt b/doc/statements.txt index e1d8552c..fa2a1cc6 100644 --- a/doc/statements.txt +++ b/doc/statements.txt @@ -222,10 +222,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] 63+ messages in thread
* Re: [PATCH v4 5/5] doc: minor improvements the `reject` statement 2025-10-20 23:49 ` [PATCH v4 5/5] doc: minor improvements the `reject` statement Christoph Anton Mitterer @ 2025-10-22 14:34 ` Florian Westphal 0 siblings, 0 replies; 63+ messages in thread From: Florian Westphal @ 2025-10-22 14:34 UTC (permalink / raw) To: Christoph Anton Mitterer; +Cc: netfilter-devel, pablo Christoph Anton Mitterer <mail@christoph.anton.mitterer.name> wrote: > +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 lacks anchors, also in other references that get added, rendered man page has: ".. the latter (see ??? respectively ???)." ^ permalink raw reply [flat|nested] 63+ messages in thread
end of thread, other threads:[~2025-10-22 14:34 UTC | newest] Thread overview: 63+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-09-25 0:07 nft manpage/wiki issues and improvement ideas Christoph Anton Mitterer 2025-09-25 7:35 ` Pablo Neira Ayuso 2025-09-25 20:37 ` Christoph Anton Mitterer 2025-09-26 1:52 ` [PATCH 0/7] doc: miscellaneois improvements Christoph Anton Mitterer 2025-09-26 1:52 ` [PATCH 1/7] doc: clarify evaluation of chains Christoph Anton Mitterer 2025-09-26 1:52 ` [PATCH 2/7] doc: fix/improve documentation of verdicts Christoph Anton Mitterer 2025-09-30 10:50 ` Florian Westphal 2025-10-02 14:50 ` Christoph Anton Mitterer 2025-10-02 15:21 ` Florian Westphal 2025-10-10 23:06 ` Christoph Anton Mitterer 2025-09-26 1:52 ` [PATCH 3/7] doc: minor improvements with respect to the term “ruleset” Christoph Anton Mitterer 2025-09-26 1:52 ` [PATCH 4/7] doc: add overall description of the ruleset evaluation Christoph Anton Mitterer 2025-09-30 11:50 ` Florian Westphal 2025-10-10 23:07 ` Christoph Anton Mitterer 2025-09-26 1:52 ` [PATCH 5/7] doc: add some more documentation on bitmasks Christoph Anton Mitterer 2025-09-30 11:51 ` Florian Westphal 2025-09-30 11:53 ` Florian Westphal 2025-09-26 1:52 ` [PATCH 6/7] doc: describe include’s collation order to be that of the C locale Christoph Anton Mitterer 2025-09-26 1:52 ` [PATCH 7/7] doc: describe how values match sets Christoph Anton Mitterer 2025-09-26 2:32 ` nft manpage/wiki issues and improvement ideas Christoph Anton Mitterer 2025-10-11 0:23 ` [PATCH v2 0/7] doc: miscellaneous improvements Christoph Anton Mitterer 2025-10-11 0:23 ` [PATCH v2 1/7] doc: clarify evaluation of chains Christoph Anton Mitterer 2025-10-15 11:46 ` Florian Westphal 2025-10-11 0:23 ` [PATCH v2 2/7] doc: fix/improve documentation of verdicts Christoph Anton Mitterer 2025-10-15 11:42 ` Florian Westphal 2025-10-17 2:30 ` Christoph Anton Mitterer 2025-10-18 13:25 ` Florian Westphal 2025-10-19 0:11 ` Christoph Anton Mitterer 2025-10-11 0:23 ` [PATCH v2 3/7] doc: minor improvements with respect to the term “ruleset” Christoph Anton Mitterer 2025-10-15 11:51 ` Florian Westphal 2025-10-11 0:24 ` [PATCH v2 4/7] doc: add overall description of the ruleset evaluation Christoph Anton Mitterer 2025-10-20 9:39 ` Florian Westphal 2025-10-20 23:48 ` Christoph Anton Mitterer 2025-10-11 0:24 ` [PATCH v2 5/7] doc: add some more documentation on bitmasks Christoph Anton Mitterer 2025-10-18 13:32 ` Florian Westphal 2025-10-19 1:31 ` Christoph Anton Mitterer 2025-10-11 0:24 ` [PATCH v2 6/7] doc: describe include’s collation order to be that of the C locale Christoph Anton Mitterer 2025-10-18 13:35 ` Florian Westphal 2025-10-18 22:13 ` Christoph Anton Mitterer 2025-10-11 0:24 ` [PATCH v2 7/7] doc: describe how values match sets Christoph Anton Mitterer 2025-10-18 13:51 ` Florian Westphal 2025-10-19 1:50 ` Christoph Anton Mitterer 2025-10-19 1:38 ` [PATCH v3 0/6] doc: miscellaneous improvements Christoph Anton Mitterer 2025-10-19 1:38 ` [PATCH v3 1/6] doc: fix/improve documentation of verdicts Christoph Anton Mitterer 2025-10-20 9:28 ` Florian Westphal 2025-10-20 22:13 ` Christoph Anton Mitterer 2025-10-19 1:38 ` [PATCH v3 2/6] doc: minor improvements with respect to the term “ruleset” Christoph Anton Mitterer 2025-10-20 9:04 ` Florian Westphal 2025-10-19 1:38 ` [PATCH v3 3/6] doc: add overall description of the ruleset evaluation Christoph Anton Mitterer 2025-10-19 1:38 ` [PATCH v3 4/6] doc: add more documentation on bitmasks and sets Christoph Anton Mitterer 2025-10-20 9:06 ` Florian Westphal 2025-10-20 21:57 ` Christoph Anton Mitterer 2025-10-20 22:18 ` Florian Westphal 2025-10-20 23:51 ` Christoph Anton Mitterer 2025-10-19 1:38 ` [PATCH v3 5/6] doc: describe include’s collation order to be that of the C locale Christoph Anton Mitterer 2025-10-19 1:38 ` [PATCH v3 6/6] doc: minor improvements the `reject` statement Christoph Anton Mitterer 2025-10-20 23:49 ` [PATCH v4 0/5] doc: miscellaneous improvements Christoph Anton Mitterer 2025-10-20 23:49 ` [PATCH v4 1/5] doc: fix/improve documentation of verdicts Christoph Anton Mitterer 2025-10-20 23:49 ` [PATCH v4 2/5] doc: add overall description of the ruleset evaluation Christoph Anton Mitterer 2025-10-20 23:49 ` [PATCH v4 3/5] doc: add more documentation on bitmasks and sets Christoph Anton Mitterer 2025-10-20 23:49 ` [PATCH v4 4/5] doc: describe include’s collation order to be that of the C locale Christoph Anton Mitterer 2025-10-20 23:49 ` [PATCH v4 5/5] doc: minor improvements the `reject` statement Christoph Anton Mitterer 2025-10-22 14:34 ` Florian Westphal
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).