So I have two input chains, input
and dyn
which is dynamically generated.
However the rules of dyn
just don't work because of input
. I've tried setting the priority of input
to 1
, and the dyn
to 0
even -200
. Still nothing.
When I flush the input
rules, then dyn
works.
What am I doing wrong here?
sudo nft list ruleset
table inet filter {
chain input {
type filter hook input priority filter + 1; policy accept;
iif "lo" accept
ct state established,related accept
tcp dport 299 ip saddr 3x.xx.xx.xx accept
icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, echo-request, echo-reply, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, 148, 149 } accept
ip6 saddr fe80::/10 icmpv6 type { mld-listener-query, mld-listener-report, mld-listener-done, mld2-listener-report, 151, 152, 153 } accept
counter packets 10 bytes 5255 drop
}
chain dyn {
type filter hook input priority filter; policy accept;
iif "lo" accept
ct state established,related accept
ip saddr 2x.xx.xx.xx udp dport 8999 log prefix "dyn" accept
ip6 saddr xxx:xxxx:xxxx:xxxx::9999 udp dport 8999 log prefix "dyn" accept
ip saddr 2x.xx.xx.xx tcp dport 7999 log prefix "dyn" accept
ip6 saddr xxx:xxxx:xxxx:xxxx::9999 tcp dport 7999 log prefix "dyn" accept
ip saddr 2x.xx.xx.xx icmp type echo-request log prefix "dyn" accept
ip6 saddr xxx:xxxx:xxxx:xxxx::9999 icmp type echo-request log prefix "dyn"
ip saddr 2x.xx.xx.xx tcp dport 6999 log prefix "dyn" accept
ip6 saddr xxx:xxxx:xxxx:xxxx::aaaa tcp dport 6999 log prefix "dyn" accept
}
}
Best Answer
Each chain hooks into Netfilter: as long as a packet exists, Netfilter will call all chains hooking in the current phase (eg: input).
What this means is that each chain provides a chance to get the packet dropped. Once the packet is dropped, it doesn't exist anymore: remaining chains won't be called for traversal since there's nothing to be used for traversal: the packet already disappeared.
So whatever the priority order of the two chains, when each chain has its own rule to accept or drop a packet, independently from the other chain, the final outcome is a logical AND between each outcome, where drop means false and accept means true:
This is reminded in
nft(8)
in theVERDICT STATEMENT
section:If you want to avoid this, then the evaluation must not be independent. For example you can pass a message between the first chain and the 2nd chain (in order of hook and hook's priority) using a firewall mark to be interpreted as "decision already made, accept this packet". This requires cooperation from both sides, so can't be slapped on an unaware tool (eg: can't add this to a ruleset handled by firewalld without modifying firewalld to account for this, either as the message creator or as the message consumer.
For your case, you could replace your ruleset with a variation of this:
This is an example made without any simplification, I'm sure better can be done.
So whenever the dyn chain deems a packet should stay accepted, the packet is tagged (internally, in the current network stack only) with a mark. The first test in the altered input chain is to check for this mark: if present, the packet is immediately accepted without further processing: the decision made in the first chain is honored in the 2nd chain.