nftables supports dynamically populated sets which are documented in nftables wiki. The first example on the wiki page is following:

table ip my_filter_table {
        set my_ssh_meter {
                type ipv4_addr
                size 65535
                flags dynamic

        chain my_input_chain {
                type filter hook input priority filter; policy accept;
                tcp dport 22 ct state new add @my_ssh_meter { ip saddr limit rate 10/second } accept

nftables configuration above is explained as follows:

In this example we create a rule to match new TCP ssh (port 22) connections, which uses a dynamic set named my_ssh_meter to limit the traffic rate to 10 connections per second for each source IP address.

How to read this rule or how to understand it? I mean the source IP addresses(ip saddr) with limit rate 10/second data is added to named set my_ssh_meter if Linux connection tracking subsystem sees a new connection(ct state new) to TCP destination port 22(tcp dport 22). However, it looks like the my_ssh_meter set is never used? And is the accept action executed when populating the set succeeded?

1 Answer 1


What happens:

  • when a new ssh connection arrives, its source plus attached meter is added (if not already present: it's a set, so each element is present only once) to the set and evaluated to provide a boolean result true/false

  • if the evaluation is true, the rule can continue, else the rule stops.

    note: without the attached meter, adding an element would always evaluate true: adding to a set is not terminal.


  • if it evaluated true (ie: it didn't flood), the rule continues to accept ending the current input hook (ie: chain my_input_chain) evaluation.

  • if it evaluated false, the rule ends before its final accept statement

    • it continues to the next rule, or if none
    • the default chain policy is used: accept

So here whatever happens, the new connection packet is always accepted: this ruleset as-is has no visible effect except populating the set.

The wiki's ruleset example is incomplete. It should be followed by a drop (or reject) within the same conditions (some rule factorization is certainly possible):

% nft add rule ip my_filter_table my_input_chain tcp dport 22 ct state new drop

So if the metric did overflow, the accept final part of the previous rule is not evaluated and the incoming connection is dropped in the next rule. As long as attempts are being done, it will be accounted in the metric's tocken bucket. So as long as attempts keep staying too fast, zero new connection will be possible (note that with a default token bucket burst of 5, the first connections will always succeed even if immediately flooding).

The important thing to know here is that as with many other statements, the set statement (with its special syntax using @set) both does an action with a change (an element is added in a set) and has a boolean result: true or false which conditions further evaluation of the remaining part of the rule: both roles are in use at the same time.

To do tests, simply replace 10/second with 10/minute or 10/hour: the behavior will now become more obvious (after the 5 first connections: by default limit's token bucket allows a burst of 5 packets.)

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .