Skip to contentSkip to conversation

Combining/compacting 2 rules into 1

DISCUSSION William N. 4 participants 19 messages Apr 16, 2024 – Apr 20, 2024

lore
Share & tools
lore

William N.

author
#0 lore raw

Hello,

I am trying to "compact" the following rules:

table netdev filter {
	chain ingress {
		type filter hook ingress device "eth0" priority -500;
		# ...

		meta protocol ip \
			tcp flags syn \
			tcp option maxseg size lt 536 \
			log prefix "TCP MSS: " \
			counter packets 0 bytes 0 \
			drop

		meta protocol ip6 \
			tcp flags syn \
			tcp option maxseg size lt 1220 \
			log prefix "TCP MSS: " \
			counter packets 0 bytes 0 \
			drop
	}
}

into something like:

table netdev filter {
	chain ingress {
		type filter hook ingress device "eth0" priority -500;
		# ...

		meta protocol tcp option maxseg size map lt {
				ip : 536,
				ip6 : 1220
			} \
			tcp flags syn \
			log prefix "TCP MSS: " \
			counter packets 0 bytes 0 \
			drop
	}
}

but I am getting errors, as my syntax is obviously wrong. The first error is:

Error: syntax error, unexpected size, expecting newline or semicolon

                meta protocol tcp option maxseg size map lt {
                                                ^^^^

What is the correct syntax for this?

#1 lore raw
Quoted 43 lines · On Tue, 16 Apr 2024 17:47:48 -0000 "William N." <[email protected]> wrote:

Hello,

I am trying to "compact" the following rules:

table netdev filter {
	chain ingress {
		type filter hook ingress device "eth0" priority -500;
		# ...

		meta protocol ip \
			tcp flags syn \
			tcp option maxseg size lt 536 \
			log prefix "TCP MSS: " \
			counter packets 0 bytes 0 \
			drop

		meta protocol ip6 \
			tcp flags syn \
			tcp option maxseg size lt 1220 \
			log prefix "TCP MSS: " \
			counter packets 0 bytes 0 \
			drop
	}
}

into something like:

table netdev filter {
	chain ingress {
		type filter hook ingress device "eth0" priority -500;
		# ...

		meta protocol tcp option maxseg size map lt {
				ip : 536,
				ip6 : 1220
			} \
			tcp flags syn \
			log prefix "TCP MSS: " \
			counter packets 0 bytes 0 \
			drop
	}
}
Quoted 7 lines

but I am getting errors, as my syntax is obviously wrong. The first error is:

Error: syntax error, unexpected size, expecting newline or semicolon

                meta protocol tcp option maxseg size map lt {
                                                ^^^^

What is the correct syntax for this?

meta protocol . tcp option maxseg size {

		ip . 0-535,
		ip6 . 0-1219
	} \
	tcp flags syn \
	log prefix "TCP MSS: " \
	counter packets 0 bytes 0 \
	drop
}
Signature · 1 line
Kerin Millar
author
#2 lore raw

Thank you, Kerin!

Could you please also tell me:

What is the way to actually test this rule? I.e. how do I send "improper" packets to see it do its work?

I have been successfully testing my other rules using nmap from another host and watching the 'journal -kf' and 'nft monitor trace' but this one is difficult for me.

#3 lore raw
Quoted 11 lines · On Tue, 16 Apr 2024 19:12:50 -0000 "William N." <[email protected]> wrote:

Thank you, Kerin!

Could you please also tell me:

What is the way to actually test this rule? I.e. how do I send "improper" packets to see it do its work?

I have been successfully testing my other rules using nmap from another host and watching the 'journal -kf' and 'nft monitor trace' but this one is difficult for me.

Firstly, I wrote the rule in such a way as to ease the testing process.

tcp flags syn meta protocol . tcp option maxseg size { ip . 0-535 counter, ip6 . 0-1219 counter }

Secondly, I wrote a crude script to generate a SYN packet while being able to set the MSS option to an abitrarily value.

#!/usr/bin/perl
use Net::RawIP;
@ARGV == 3 or exit 1;
my ($saddr, $daddr, $mss) = @ARGV;
my $pkt = Net::RawIP->new;

$pkt->set({
    'ip'  => {
        'saddr' => $saddr,
        'daddr' => $daddr
    },
    'tcp' => {
        'source'  => 1234,
        'dest'    => 1234,
        'syn'     => 1
    }
});

$pkt->optset('tcp' => { 'type' => [ 2 ], 'data' => [ pack('n', $mss) ] }); $pkt->send(0, 1);

Note that this requires the Net::RawIP module, which should be offered by your distribution as an installable package.

Thirdly, I ran the script, specifying a value that was not expected to match the rule.

# ./test-mss 127.0.0.1 127.0.0.1 536

By listing the ruleset, I could see that the counter had not increased.

Fourthly, I ran the script again, specifying a value that was expected to match the rule.

# ./test-mss 127.0.0.1 127.0.0.1 535

By listing the ruleset, I could see that the counter had increased.

tcp flags syn meta protocol . tcp option maxseg size { ip . 0-535 counter packets 1 bytes 44, ip6 . 0-1219 counter packets 0 bytes 0 }

There are probably some utilities that can do this. Certainly, there are other libraries; I hear that Scapy is a particularly good one.

Signature · 1 line
Kerin Millar
author
#4 lore raw

Thank you!

Your tool works great for IPv4. I couldn't get it to work with IPv6 addresses, as it says:

host_to_ip: failed at /usr/lib/x86_64-linux-gnu/perl5/5.36/Net/RawIP.pm line 480.

I also had a look at Scapy - looks very powerful indeed. Needs a lot of learning as well :)

#5 lore raw

Dňa 17. 4. o 10:29 William N. napísal(a):

Quoted 4 lines

Thank you!

Your tool works great for IPv4. I couldn't get it to work with IPv6 addresses, as it says:

try hping3

regards

Signature · 2 lines
Slavko
https://www.slavino.sk/
#6 lore raw
Quoted 7 lines · Slavko

Dňa 17. 4. o 10:29 William N. napísal(a):

Quoted 4 lines

Thank you!

Your tool works great for IPv4. I couldn't get it to work with IPv6 addresses, as it says:

try hping3

It doesn't seem to have any way to directly control the value of the options field.

Signature · 1 line
Kerin Millar
#7 lore raw

Dňa 17. 4. o 11:16 Kerin Millar napísal(a):

Quoted 3 lines
Quoted 1 line

try hping3

It doesn't seem to have any way to directly control the value of the options field.

while i never tried it, is not --tcp-mss option for that?

     hping3 -S --tcp-mss <host>

From other side:

     ... Flags [S], seq 50957424, win 512, options [mss 128], length 0

regards

Signature · 2 lines
Slavko
https://www.slavino.sk/
#8 lore raw
Quoted 9 lines · Slavko

Dňa 17. 4. o 11:16 Kerin Millar napísal(a):

Quoted 3 lines
Quoted 1 line

try hping3

It doesn't seem to have any way to directly control the value of the options field.

while i never tried it, is not --tcp-mss option for that?

      hping3 -S --tcp-mss <host>

Apparently, it requires an additional patch.

https://sources.debian.org/patches/hping3/3.a2.ds2-10/160_tcp_mss.diff/

Signature · 1 line
Kerin Millar
author
#9 lore raw

Just tested on Debian 12:

# hping3 <host> -c 1 --syn --tcp-mss 100

This triggers the discussed rule (output from 'nft monitor trace'):

...
trace id 98d76ca4 netdev filter ingress rule meta protocol . tcp option maxseg size { ip . 0-535, ip6 . 0-1219 } tcp flags syn log prefix "TCP MSS: " counter packets 0 bytes 0 drop (verdict drop)

hping3 also doesn't work with IPv6 though.

#10 lore raw

Dňa 17. apríla 2024 14:12:30 UTC používateľ "William N." <[email protected]> napísal:

Quoted 1 line

hping3 also doesn't work with IPv6 though.

That is sad, i don't use hping3 often the bad boys run it for me :-D But i consider it as great tool for both, testing & learning, as good that i didn't realize missing IPv6, i am sorry for confusing you.

But i see that in many open sourde tools. A lot of them are very good, but old and lacks modern features...

regards

Signature · 2 lines
Slavko
https://www.slavino.sk/
author
#11 lore raw
In reply to Kerin Millar

Is it possible to combine, e.g.:

tcp flags fin,syn / fin,syn drop tcp flags syn,rst / syn,rst drop tcp flags fin,rst / fin,rst drop tcp flags fin / fin,ack drop

into something like:

tcp flags {
fin,syn / fin,syn, syn,rst / syn,rst, fin,rst / fin,rst, fin / fin,ack
} drop

just using some correct syntax?

#12 lore raw

Have you tried vmap?

Quoted 12 lines

VMAP STATEMENT
The verdict map (vmap) statement works analogous to the map statement, but contains verdicts as values.

expression vmap { VMAP_ELEMENTS }

VMAP_ELEMENTS := VMAP_ELEMENT [, VMAP_ELEMENTS] VMAP_ELEMENT := key : verdict

Using the vmap statement.

# jump to different chains depending on layer 4 protocol type:
nft add rule ip filter input ip protocol vmap { tcp : jump tcp-chain, udp : jump udp-chain , icmp : jump icmp-chain }

Quoted 18 lines · William N.

Is it possible to combine, e.g.:

tcp flags fin,syn / fin,syn drop tcp flags syn,rst / syn,rst drop tcp flags fin,rst / fin,rst drop tcp flags fin / fin,ack drop

into something like:

tcp flags {
fin,syn / fin,syn, syn,rst / syn,rst, fin,rst / fin,rst, fin / fin,ack
} drop

just using some correct syntax?

Signature · 1 line
Send unsolicited bulk mail to [email protected]
author
#13 lore raw
Quoted 1 line · On Fri, 19 Apr 2024 11:01:59 +0000 Serhii wrote:

Have you tried vmap?

No, because, IIUC, it would still need the repetition of 'drop' for every element. Isn't using an anonymous set possible?

author
#14 lore raw
In reply to Serhii
Quoted 1 line · On Fri, 19 Apr 2024 11:01:59 +0000 Serhii wrote:

Have you tried vmap?

OK. I tried.
Can't get the syntax right though. How do you do this?

#15 lore raw

Seems like it something not possible, unfortunately.

Quoted 3 lines

Error: conflicting intervals specified
add rule inet example example tcp flags vmap { 0x03 / 0x03 : drop, 0x05 / 0x05 : drop }

                                               ^^^^^^^^^^^         ~~~~~~~~~~~
Quoted 7 lines · William N.
Quoted 1 line · On Fri, 19 Apr 2024 11:01:59 +0000 Serhii wrote:

Have you tried vmap?

OK. I tried.
Can't get the syntax right though. How do you do this?

Signature · 1 line
Send unsolicited bulk mail to [email protected]
#16 lore raw
In reply to William N.
Quoted 9 lines · William N.

Is it possible to combine, e.g.:

tcp flags fin,syn / fin,syn drop tcp flags syn,rst / syn,rst drop tcp flags fin,rst / fin,rst drop tcp flags fin / fin,ack drop

into something like:

Quoted 8 lines

tcp flags {
fin,syn / fin,syn, syn,rst / syn,rst, fin,rst / fin,rst, fin / fin,ack
} drop

just using some correct syntax?

(This might have been better off as a new thread)

To begin with, I would recommend that you jettison these rules outright. It is probable that they would otherwise end up being useless. But why?

If you are using a stateful ruleset - that is, one that matches against ctstate in any capacity - then generally invalid TCP flag combinations will always be classifed as "invalid", which is particularly useful for the forwarding path. Here is proof of this claim.

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/netfilter/nf_conntrack_proto_tcp.c?h=v5.8#n700

This check is further augmented by the presence of a TCP state machine, by which a packet may also be classified as invalid. The following post explains it a little bit further.

https://marc.info/?l=netfilter&m=159842459323063&w=2

If, on the other hand, you have written a stateless ruleset for a host that is not being tasked with forwarding packets, it is by no means clear that the rules will confer any value other than, perhaps, for statistical purposes. The state machine implemented by the TCP stack should already know what it is doing.

...

That aside, it is my understanding that the rules in question cannot be expressed in the way that you are attempting. To write "tcp flags { ... }" is to attempt to match - in the sense of the "==" operator - against an anonymous set consisting of elements whose values are of the type, tcp_flag, which is an 8-bit integer.

# nft describe tcp_flag | head -n1
datatype tcp_flag (TCP flag) (basetype bitmask, integer), 8 bits

The problem you have is that, while "/" concerns the matter of bitwise arithmetic, it is not quite an arithmetic operator in its own right. Rather, it is a syntactical shortcut for treating "expression / flags" as if it were "expression & flags == flags". As such, it constitutes a test, not merely a constant value. Knowing that, you can probably see why things go off the rails in the course of trying to compose the set.

It doesn't help that this is entirely undocumented. Indeeed, arithmetic expressions are not mentioned by the manual at all, save for a vague indication that they are supported for relative priorities in the CHAINS section.

If such a thing were to be possible at all, it might entail a set of tcp_flag concatenations. For example:

{
fin|rst . fin|rst,
fin . fin|ack
}

But the imaginary syntax that might be necessary to wire up such a thing isn't available. At least, not that I can gather (if mistaken, I would welcome a correction). Given the number of bugs I just discovered in the course of experimenting with all this, maybe it is just as well.

Signature · 1 line
Kerin Millar
author
#17 lore raw

Kerin,

First, thank you very much for your attention, again.

Quoted 1 line · On Sat, 20 Apr 2024 03:36:00 +0100 Kerin Millar wrote:

(This might have been better off as a new thread)

I thought about that but I assumed it is closely related, i.e. similar. Obviously, I was wrong. So, I have replied to your current post in a separate thread (with further questions):

https://marc.info/?l=netfilter&m=171360276907019&w=2

Quoted 2 lines

Given the number of bugs I just discovered in the course of experimenting with all this, maybe it is just as well.

Could you link to the bug reports please?

#18 lore raw
Quoted 12 lines · William N.

Kerin,

First, thank you very much for your attention, again.

Quoted 1 line · On Sat, 20 Apr 2024 03:36:00 +0100 Kerin Millar wrote:

(This might have been better off as a new thread)

I thought about that but I assumed it is closely related, i.e. similar. Obviously, I was wrong. So, I have replied to your current post in a separate thread (with further questions):

Quoted 6 lines

https://marc.info/?l=netfilter&m=171360276907019&w=2

Quoted 2 lines

Given the number of bugs I just discovered in the course of experimenting with all this, maybe it is just as well.

Could you link to the bug reports please?

As you wish, though I haven't filed them yet.

Signature · 1 line
Kerin Millar