5

I am moving from iptables to nftables. I have a basic questions about the packet processing order in nftables.

Since one can create multiple tables of same type, say inet, and also chains can be created inside each table with different or the same priority, what will be the processing order.

For example, if I create following, what will be the order.

table inet t1 {
    chain INPUT {
        type filter hook input priority 20; policy accept;
        ...
    }
}


table inet t2 {
    chain INPUT {
        type filter hook input priority 20; policy accept;
        ...
    }
}

while I understand that chains are hooked to different inputs, I yet to understand the logic behind having different tables.

Apology if is a stupid or basic question

4

The ordering in the example will be undefined, but both chains will be traversed (unless for example the packet gets dropped in the first chain seen).

Netfilter and the Network/Routing stack provide the ordering

Here's the Packet flow in Netfilter and General Networking schematic:

Packet flow in Netfilter and General Networking

While it was made with iptables in mind, the overall behaviour is the same when applied to nftables with minor differences (eg: no separation between mangle and filter, it's all filter in nftables with the exception of mangle/OUTPUT which should probably be translated into type route hook output, or most of the bridge mingling between ebtables and iptables seen in the lower part doesn't exist with nftables).

Role of tables

A table in nftables is not equivalent to a table in iptables: it's something less rigid. In nftables, the table is a container to organise chains, set and other kinds of objets, and limit their scope. Contrary to iptables It's perfectly acceptable and sometimes required to mix different chain types (eg: nat, filter, route) in the same table: for example that's the only way they can access a common set since it's scoped to the table and not global (like would be iptables' companion ipset).

Then it's also perfectly acceptable to have multiple tables of the same family including again the same kind of chains, for specific handling or to handle specific traffic: there's no risk of altering rules in an other table when changing the contents of this table (though there's still the risk of having clashing effects as an overall result). It helps managing rules. For example the nftlb load-balancer creates tables (in various families) all named nftlb, intended to be managed only by itself and not clashing with other user-defined tables.

Ordering between hooks and within hooks

In a given family (netdev, bridge, arp, ip, ip6), chains registered to different hooks (ingress, prerouting, input, forward, output, postrouting) are ordered from the hook order provided by Netfilter as seen in the schematic above. Priority's scope is limited to the same hook and doesn't matter here. For example type filter hook prerouting priority 500 still happens before type filter hook forward priority -500 in the case of a forwarded packet.

Where applicable, for each possible hook of a given family, each chain will be competing with other chains registered at the same place. The tables play no role here, except defining the family. As long as the priority is different, within a given hook type, a packet will traverse chains within this hook from the lowest priority to the highest. If exactly the same priority is used for two chains of the same family and hook type, order becomes undefined. When creating chains, will the current kernel version add the chain before or after a chain with the same priority in the corresponding list structure? Will the next kernel version still keep the same behaviour or will some optimization change this order? It's not documented. Both hooks will still be called, but the order they are called in is undefined.

How could this matter? Here's as quote from man page below, just to clarify that a packet can be accepted (or not) multiple times in the same hook:

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 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.

For example if one chain accepts a certain packet, and the other chain drops this same packet, the overall result will always be a drop. But one hook might have done additional actions leading to side effects: for example it could have added the packet's source address in a set and the other chain called next have dropped the packet. If the order is reversed and the packet is dropped first, this "side effect" action will not have happened and the set will not have been updated. So one should avoid using the exact same priority in this case. For other cases, mostly when no drop happens, this would not matter. One should avoid using the same priority unless knowing it won't matter.

Relation to other networking subsystems

Within a hook, all the integer range is available to choose the order, but some specific thresholds do matter.

From nftables' wiki, Here are the legacy iptables hook values valid for the ip family, which also include other subsystems:

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

Of those only a few really matter: those not coming from iptables. For example (non-exhaustive) in the ip family:

  • NF_IP_PRI_CONNTRACK_DEFRAG (-400): for a chain to ever see incoming IPv4 fragments, it should register in prerouting at a priority lower than -400. After this only reassembled packets are seen (and rules checking for the presence of fragments never match).
  • NF_IP_PRI_CONNTRACK (-200): for a chain to act before conntrack or nat it should register in prerouting or in output at a prority lower than -200. Example, register at priority NF_IP_PRI_RAW (-300) (or any other value < -200 but still > -400 if one want to match the port in all cases) to add a notrack statement to prevent conntrack to create a connection entry for this packet. So the nftables equivalent of iptables' raw/PREROUTING is just filter prerouting with an adequate priority.

Misc

I left other families and special cases out: for example the inet family registers within ip and ip6 families' hooks at the same time. Or the type nat which might behave differently when a NAT rule matches (it might not traverse again other nat chains of the same hook, I'm not completely sure and it might depend on kernel version) and is really dependent on conntrack (eg: prerouting at priority -200) and at least since kernel 4.18 competes only with other nat type chains, not with chains of an other type (it will always be seen at priority -200 for type filter chains).

When also using iptables-legacy (or iptables-nft) all this still applies, and priority choices can matter. NAT rules from both iptables-legacy and nftables shouldn't be mixed with a kernel < 4.18 or undefined behaviour can happen (eg: one chain will handle all the NAT, the other won't be able to, but the first subsystem to register, rather than the lowest priority chain, wins).

4
  • Thanks for the great explanation. So is it correct say following? "chains are grouped together by kernel from all the tables and ordered by priority irrespective of which table they come from. Table only serve as scope limiter for various elements chain is using for example sets, maps, etc" – Rahul Sep 2 '20 at 4:39
  • 1
    Yes. Just don't forget the table's family. There's one "prerouting" per family and that's as many separately handled prerouting lists (ok,there are exceptions special cases related to netdev and inet families, and with nat). – A.B Sep 2 '20 at 8:17
  • @A.B For me it is still not clear what will be behaviour for multiple hooks registered in same family for same type of hook. E.g. inet filter hook priority 0 would accept ipv4 packet foo and ip filter hook priority 0 would drop packet foo. If inet hook happens to be evaluated first the "ruleset evaluation termination" is only for inet hook (and ip hook will drop the packet later)? or "ruleset evaluation termination" is for all filter hooks, or for all same priority filter hooks? – Slimak Sep 8 '20 at 10:37
  • Rereading linked man page (especially wording around "queue" verdict statement) suggests that accept terminates evaluation of only current hook, (not whole ruleset like what is printed by nft list ruleset), and that same priority hook will be evaluated later. – Slimak Sep 8 '20 at 11:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.