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 --- 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 - 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 --- 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 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 --- 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 Signed-off-by: Christoph Anton Mitterer --- 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 Signed-off-by: Christoph Anton Mitterer --- 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 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 --- 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 Signed-off-by: Christoph Anton Mitterer --- 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