Rules follow this format:
<list of match conditions> actions <list of actions>
Rules are processed sequentially from top to bottom, in the exact order they are defined. This ensures a strict total ordering of rule execution. Mind the limits regarding the number of rules.
Match conditions use nftables syntax [1].
Matches IPv4 packets. Required to match saddr
and daddr
.
Matches IPv4 source/destination address of the packet against CIDR. It can
optionally be negated !=
.
Examples:
ip saddr 127.0.0.1
ip daddr 10.0.0.0/24
ip saddr != 127.0.0.1 daddr 10.0.0.0/8
Matches TCP or UDP packets. Required to match sport
and dport
.
Matches a single L4 source or destination port (exact match). It can
optionally be negated with !=
.
tcp dport 80
udp dport 1000 sport != 1000
Actions resemble nftables ones, but they aren't exact. For accept
and
drop
no other action can be defined.
For the rest, actions are accumulative.
Funnels traffic through <l4_funneling_proto>
, and set
funneling L4 header and sets sport
/dport
accordingly.
Examples:
funnel tcp dport 179 sport 540
✨ Tip
You can reuse the same
sport
for all UDP or TCP traffic. Originalsport
anddport
are preserved.
⚠️ WarningMake sure there isn't real traffic using
l4_funneling_proto
+sport
+dport
.
Make surel4_funneling_proto
+sport
+dport
is allowed in your firewall rules.
Undo the funnel
ing. The mandatory paramter <l4_proto>
is eThe mandatory It must have the L4 protocol it
Example:
unfunnel tcp
Don't touch the packet.
Drop the packet.
The current implementation is designed to handle fewer than 5 rules.
The use of statically allocated rules is an intentional design choice, as it eliminates the need to orchestrate BPF maps (see details here). However, there are limits to the total amount of global data that a BPF program can handle, which are enforced by the verifier. Experimentally, this translates (today) in ~500 IPv4 rules.
The lookup algorithm is a simple linear search, which scales very poorly once the number of rules exceeds few tenths of rules.
There hasn't been (yet) a use-case for it, but if you are interested in optimizing the lookup, or even conditionally (compile-time) use external BPF maps, you can look into lookup.h and send a PR.
[1] To the best of my ability. AFAIK, there is no existing Python3 library to parse nftable filters.