Just noticed something that is even worse: # nft add rule meh tcp_flags 'tcp flags { fin, rst, ack }' # nft add rule meh tcp_flags 'tcp flags == { fin, rst, ack }' # nft add rule meh tcp_flags 'tcp flags & ( fin | rst | ack ) != 0' # nft add rule meh tcp_flags 'tcp flags & ( fin | rst | ack ) == 0' # nft list table meh table ip meh { chain tcp_flags { tcp flags { fin, rst, ack } tcp flags { fin, rst, ack } tcp flags fin,rst,ack tcp flags ! fin,rst,ack } } On Tue, 27 Jul 2021 at 19:18, Tom Yan <tom.ty89@xxxxxxxxx> wrote: > > Hi all, > > I'm a bit uncertain how `tcp flags` works exactly. I once thought `tcp > flags syn` checks whether "syn and only syn is set", but after tests, > it looks more like it checks only whether "syn is set" (and it appears > that the right expression for the former is `tcp flags == syn`): > > # nft add rule meh tcp_flags 'tcp flags syn' > # nft add rule meh tcp_flags 'tcp flags ! syn' > # nft add rule meh tcp_flags 'tcp flags == syn' > # nft add rule meh tcp_flags 'tcp flags != syn' > # nft list table meh > table ip meh { > chain tcp_flags { > tcp flags syn > tcp flags ! syn > tcp flags == syn > tcp flags != syn > } > } > > Then I test the above respectively with a flag mask: > > # nft add rule meh tcp_flags 'tcp flags & (fin | syn | rst | ack) syn' > # nft add rule meh tcp_flags 'tcp flags & (fin | syn | rst | ack) ! syn' > # nft add rule meh tcp_flags 'tcp flags & (fin | syn | rst | ack) == syn' > # nft add rule meh tcp_flags 'tcp flags & (fin | syn | rst | ack) != syn' > # nft list table meh > table ip meh { > chain tcp_flags { > tcp flags & (fin | syn | rst | ack) syn > tcp flags & (fin | syn | rst | ack) ! syn > tcp flags syn / fin,syn,rst,ack > tcp flags syn / fin,syn,rst,ack > } > } > > I don't suppose the mask in the first two rules would matter. And with > `tcp flags syn / fin,syn,rst,ack`, I assume it would be false when > "syn is cleared and/or any/all of fin/rst/ack is/are set"? > > Also, as you can see, for the last two rules, `nft` interpreted them > as an identical rule, which I assume to be a bug. These does NOT seem > to workaround it either: > > # nft flush chain meh tcp_flags > # nft add rule meh tcp_flags 'tcp flags == syn / fin,syn,rst,ack' > # nft add rule meh tcp_flags 'tcp flags != syn / fin,syn,rst,ack' > # nft list table meh > table ip meh { > chain tcp_flags { > tcp flags syn / fin,syn,rst,ack > tcp flags syn / fin,syn,rst,ack > } > } > > I'm not sure if `! --syn` in iptables (legacy) is affected by this as > well. Anyway, I'm doing the following for now as a workaround: > > # nft flush chain meh tcp_flags > # nft add rule meh tcp_flags 'tcp flags ! syn reject with tcp reset' > # nft add rule meh tcp_flags 'tcp flags { fin, rst, ack } reject with tcp reset' > # nft list table meh > table ip meh { > chain tcp_flags { > tcp flags ! syn reject with tcp reset > # syn: 1, other bits: not checked > tcp flags { fin, rst, ack } reject with tcp reset > # syn: 1, fin: 0, rst: 0, ack: 0, other bits: not checked > ct state != invalid accept > } > } > > Are the comments in above correct? Are any of the assumptions in this > email incorrect? > > As a side question, is it even possible that any packet will be > considered `invalid` with (syn: 1, fin: 0, rst: 0, ack: 0)? > > Thanks in advance! > > Regards, > Tom