Difference between revisions of "Meters"

From nftables wiki
Jump to navigation Jump to search
(→‎Listing meters: update with "my" and update new syntax)
(→‎Doing connlimit with nft: document caveats)
(4 intermediate revisions by 3 users not shown)
Line 1: Line 1:
== Meters ==
== Meters ==


Meters used to be known as ''flow tables'' before nft v0.8.1 and Linux kernel 4.3, and are a way to use maps with stateful objects.
'''Dynamic sets/maps''' or meters are a way to use maps with stateful objects. They used to be known as ''flow tables'' before nft v0.8.1 and Linux kernel 4.3.


Among other things, they provide a native replacement for the ''hashlimit'' match in iptables. However, meters are a lot more flexible since you can use any selector, one or many through [[concatenations]].
Among other things, they provide a native replacement for the ''hashlimit'' match in iptables. However, meters are a lot more flexible since you can use any selector, one or many through [[concatenations]].
Line 34: Line 34:


<source lang="bash">
<source lang="bash">
% nft list map my_filter_table my_ssh_meter
% nft list set my_filter_table my_ssh_meter
table ip my_filter_table {
table ip my_filter_table {
map my_ssh_meter {
        set my_ssh_meter {
type ipv4_addr . inet_service
                type ipv4_addr
                 size 65535
                 size 65535
flags dynamic,timeout
                flags dynamic
elements = { 64.62.190.36 . 55000 expires 38s : counter packets 2 bytes 220, 83.98.201.47 . 35460 expires 39s : counter packets 10 bytes 5988, 172.217.7.142 . 43254 expires 46s : counter packets 1 bytes 98}
                elements = { 10.141.10.2 limit rate 10/second }
}
        }
}
}
</source>
</source>
Line 51: Line 51:
The following example shows how to do ''connlimit'' from nftables:
The following example shows how to do ''connlimit'' from nftables:


  table ip filter {
  table ip my_filter_table {
         set connlimit {
         set my_connlimit {
                 type ipv4_addr
                 type ipv4_addr
                 size 65535
                 size 65535
Line 58: Line 58:
         }
         }
   
   
         chain y {
         chain my_output_chain {
                 type filter hook output priority filter; policy accept;
                 type filter hook output priority filter; policy accept;
                 ct state new add @connlimit { ip daddr ct count over 20 } counter packets 0 bytes 0 drop
                 ct state new add @my_connlimit { ip daddr ct count over 20 } counter packets 0 bytes 0 drop
         }
         }
  }
  }


The example above dynamically populates the set ''connlimit'' from the packet path. For the first packet of each connection (ie. packets matching ''ct state new''), this adds an entry into ''connlimit'' set, this entry uses the IPv4 destination address as a key. If the number of connection goes over 20, then packets are dropped.
The example above dynamically populates the set ''connlimit'' from the packet path. For the first packet of each connection (ie. packets matching ''ct state new''), this adds an entry into ''connlimit'' set, this entry uses the IPv4 destination address as a key. If the number of connections goes over 20, then packets are dropped.


Since ''connlimit'' is a set, you can perform any operation on it, such as listing and flushing its content.
Since ''connlimit'' is a set, you can perform any operation on it, such as listing and flushing its content.
'''Caveats''':
* Do not define a set with a timeout. There is a garbage collector that removes the set element whenever ''ct count'' becomes zero to improve memory usage.
* For the same reason, you cannot use the ''update'' set statement which allows to refresh the timeout of your set element. Therefore, the ''ct count'' statement can only be used with the ''add'' set statement.
If you define a set with a timeout or you use the ''update'' set statement, then you will hit the "Operation is not supported" error.


== Doing iptables hashlimit with nft ==
== Doing iptables hashlimit with nft ==

Revision as of 21:36, 5 July 2021

Meters

Dynamic sets/maps or meters are a way to use maps with stateful objects. They used to be known as flow tables before nft v0.8.1 and Linux kernel 4.3.

Among other things, they provide a native replacement for the hashlimit match in iptables. However, meters are a lot more flexible since you can use any selector, one or many through concatenations.

Note that the meter keyword is obsolete, the dynamic set and map syntax is now preferred for consistency.

Using meters

The following commands create a table named my_filter_table, a chain named my_input_chain which hooks incoming traffic and a rule that uses a meter:

% nft add table my_filter_table
% nft add chain my_filter_table my_input_chain {type filter hook input priority 0\;}
% nft add set my_filter_table my_ssh_meter { type ipv4_addr\; flags dynamic \;}
% nft add rule my_filter_table my_input_chain tcp dport 22 ct state new add @my_ssh_meter { ip saddr limit rate 10/second } accept

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. The available time units on limits are: second, minute, hour, day and week.

You can also use concatenations to build selectors:

% nft add set my_filter_table my_ssh_meter { type ipv4_addr . inet_service\; flags timeout, dynamic \;}
% nft add rule my_filter_table my_input_chain tcp dport 22 ct state new add @my_ssh_meter { ip saddr . tcp dport timeout 60s limit rate 10/second } accept

This rule counts incoming connections (packets with state new) based on the tuple (IP source address, TCP destination port), the counters are dropped after 60 seconds without update.

Listing meters

To list the content matched by the meter use:

% nft list set my_filter_table my_ssh_meter
table ip my_filter_table {
        set my_ssh_meter {
                type ipv4_addr
                size 65535
                flags dynamic
                elements = { 10.141.10.2 limit rate 10/second }
        }
}

Doing connlimit with nft

Since 4.18, there is a new ct count selector that allows you to count the number of existing connections. This extension uses the information available in the Connection Tracking System table, therefore, the counting of connection is based on the existing entries in the table.

The following example shows how to do connlimit from nftables:

table ip my_filter_table {
       set my_connlimit {
               type ipv4_addr
               size 65535
               flags dynamic
       }

       chain my_output_chain {
               type filter hook output priority filter; policy accept;
               ct state new add @my_connlimit { ip daddr ct count over 20 } counter packets 0 bytes 0 drop
       }
}

The example above dynamically populates the set connlimit from the packet path. For the first packet of each connection (ie. packets matching ct state new), this adds an entry into connlimit set, this entry uses the IPv4 destination address as a key. If the number of connections goes over 20, then packets are dropped.

Since connlimit is a set, you can perform any operation on it, such as listing and flushing its content.

Caveats:

  • Do not define a set with a timeout. There is a garbage collector that removes the set element whenever ct count becomes zero to improve memory usage.
  • For the same reason, you cannot use the update set statement which allows to refresh the timeout of your set element. Therefore, the ct count statement can only be used with the add set statement.

If you define a set with a timeout or you use the update set statement, then you will hit the "Operation is not supported" error.

Doing iptables hashlimit with nft

Meters replace iptables hashlimit in nft. From iptables v1.6.2 onward, you can use the tool iptables-translate to see how to translate hashlimit rules.

Almost all hashlimit options are available in nft, starting with --hashlimit-mode, it is replaced by the selector in a meter. All modes are available except no mode, a meter demands a selector, an iptables rule without hashlimit-mode isn't supported in nft. A simple rule translation is:

$ iptables-translate -A INPUT -m tcp -p tcp --dport 80 -m hashlimit --hashlimit-above 200/sec --hashlimit-mode srcip,dstport --hashlimit-name http1 -j DROP
nft add rule ip filter INPUT tcp dport 80 meter http1 { tcp dport . ip saddr limit rate over 200/second } counter drop

Notice that a meter is named, like hashlimit, and using multiple hashlimit-modes is similar to using a concatenation of selectors. Also, --hashlimit-above is translated to limit rate over, to simulate --hashlimit-upto just omit or replace over with until in the rule.

The options --hashlimit-burst and --hashlimit-htable-expire are translated to burst and timeout in a meter:

$ iptables-translate -A INPUT -m tcp -p tcp --dport 80 -m hashlimit --hashlimit-above 200kb/s --hashlimit-burst 1mb --hashlimit-mode srcip,dstport --hashlimit-name http2 --hashlimit-htable-expire 3000 -j DROP
nft add rule ip filter INPUT tcp dport 80 meter http2 { tcp dport . ip saddr timeout 3s limit rate over 200 kbytes/second burst 1 mbytes} counter drop

This rule shows how timeout and burst are used in a meter, also notice that meters, similarly to hashlimit, accepts limiting rates by bytes frequency instead of packets.

Another hashlimit option is to limit the traffic rate on subnets, of IP source or destination addresses, using the options --hashlimit-srcmask and --hashlimit-dstmask. This feature is available in nft by attaching a subnet mask to a meter selector, attach to ip saddr for source address and to ip daddr for destination adress:

$ iptables-translate -A INPUT -m tcp -p tcp --dport 80 -m hashlimit --hashlimit-upto 200 --hashlimit-mode srcip --hashlimit-name http3 --hashlimit-srcmask 24 -j DROP
nft add rule ip filter INPUT tcp dport 80 meter http3 { ip saddr and 255.255.255.0 limit rate 200/second } counter drop

This rule will limit packets rate, grouping subnets determined by the first 24 bits of the IP source address, from the incoming packets on port 80.

The remaining options, --hashlimit-htable-max, --hashlimit-htable-size and --hashlimit-htable-gcinterval don't apply to meters.