Contents
Note : This page just shows some examples, to get better nftables documentation visit the nftables wiki
Initial setup
This page just shows some examples, for better nftables documentation visit the http://wiki.nftables.org.
Build guide and required kernel configuration can be found here. This document is taken from there and here.
Improved syntax wrt. iptables
Suppose you want to log and drop a packet with iptables, you had to write two rules. One for drop and one for logging:
{{{iptables -A FORWARD -p tcp --dport 22 -j LOG iptables -A FORWARD -p tcp --dport 22 -j DROP}}} With nft, you can combined both targets: {{{nft add rule filter forward tcp dport 22 log drop }}} Suppose you want to allow packets for different ports and allow different icmpv6 types. With iptables, you need to use something like: {{{ip6tables -A INPUT -p tcp -m multiport --dports 23,80,443 -j ACCEPT ip6tables -A INPUT -p icmpv6 --icmpv6-type neighbor-solicitation -j ACCEPT ip6tables -A INPUT -p icmpv6 --icmpv6-type echo-request -j ACCEPT ip6tables -A INPUT -p icmpv6 --icmpv6-type router-advertisement -j ACCEPT ip6tables -A INPUT -p icmpv6 --icmpv6-type neighbor-advertisement -j ACCEPT }}} With nft, sets can be use on any element in a rule: {{{nft add rule ip6 filter input tcp dport {telnet, http, https} accept nft add rule ip6 filter input icmpv6 type { nd-neighbor-solicit, echo-request, nd-router-advert, nd-neighbor-advert } accept }}} You can also use named set to be able to make them evolve other time: {{{# nft -i # use interactive mode nft> add set global ipv4_ad { type ipv4_address;} nft> add element global ipv4_ad { 192.168.1.4, 192.168.1.5 } nft> add rule ip global filter ip saddr @ipv4_ad drop }}} And later when a new bad boy is detected: {{{# nft -i nft> add element global ipv4_ad { 192.168.3.4 } }}}
Basic rule handling
To drop output to a destination
nft add rule ip filter output ip daddr 1.2.3.4 drop
Rule counters are optional with nftables and the counter keyword need to be used to activate it:
nft add rule ip filter output ip daddr 1.2.3.4 counter drop
To add a rule to a network, you can directly use:
nft add rule ip filter output ip daddr 192.168.1.0/24 counter
To drop packet to port 80 the syntax is the following:
nft add rule ip filter input tcp dport 80 drop
To accept ICMP echo request:
nft add rule filter input icmp type echo-request accept
To combine filtering, you just have to specify multiple time the ip syntax:
nft add rule ip filter output ip protocol icmp ip daddr 1.2.3.4 counter drop
To delete all rules in a chain:
nft delete rule filter output
To delete one specific rule, you need to use the -a flag on nft to get the handle number:
# nft list table filter -a table global { chain filter { ip protocol icmp ip daddr 1.2.3.4 counter packets 5 bytes 420 drop # handle 10 ...
You can then delete rule 10 with:
nft delete rule filter output handle 10
You can also flush the filter table:
nft flush table filter
It is possible to insert a rule:
nft insert rule filter input tcp dport 80 counter accept
It is possible to insert or add a rule at a specific position. To do so you need to get the handle of the rule where you want to insert or add a new one. This is done by using the -a flag in the list operation:
# nft list table filter -n -a table filter { chain output { table filter hook output priority 0; ip protocol tcp counter packets 82 bytes 9680 # handle 8 ip saddr 127.0.0.1 ip daddr 127.0.0.6 drop # handle 7 } } # nft add rule filter output position 8 ip daddr 127.0.0.8 drop # nft list table filter -n -a table filter { chain output { table filter hook output priority 0; ip protocol tcp counter packets 190 bytes 21908 # handle 8 ip daddr 127.0.0.8 drop # handle 10 ip saddr 127.0.0.1 ip daddr 127.0.0.6 drop # handle 7 } }
Here, we’ve added a rule after the rule with handle 8. To add before the rule with a given handle, you can use:
nft insert rule filter output position 8 ip daddr 127.0.0.12 drop
If you only want to match on a protocol, you can use something like:
nft insert rule filter output ip protocol tcp counter
IPv6
Like for IPv4, you need to create some chains. For that you can use:
nft -f files/nftables/ipv6-filter
You can then add rule:
nft add rule ip6 filter output ip6 daddr home.regit.org counter
The listing of the rules can be made with:
nft list table ip6 filter
To accept dynamic IPv6 configuration and neighbor discovery, one can use:
nft add rule ip6 filter input icmpv6 type nd-neighbor-solicit accept nft add rule ip6 filter input icmpv6 type nd-router-advert accept
Connection tracking
To accept all incoming packets of an established connection:nft ins
nft insert rule filter input ct state established accept
Filter on interface
To accept all packets going out on loopback interface:
nft insert rule filter output meta oif lo accept
And for packet coming in on eth2:
nft insert rule filter input meta iif eth2 accept
Please note that oif is in reality a match on the integer which is the index of the interface inside of the kernel. Userspace is converting the given name to the interface index when the nft rule is evaluated (before being sent to kernel). A consequence of this is that the rule can not be added if the interface does not exist. An other consequence, is that if the interface is removed and created again, the match will not occur as the index of added interfaces in kernel is monotonically increasing. Thus, oif is a fast filter but it can lead to some issues when dynamic interfaces are used. It is possible to do a filter on interface name but it has a performance cost because a string match is done instead of an integer match. To do a filter on interface name, one has to use oifname:
nft insert rule filter input meta oifname ppp0 accept
Logging
Logging is using the Netfilter logging framework. This means the logging is depending on the loaded kernel module. Kernel module available are:
- xt_LOG: printk based logging, outputting everything to syslog
- nfnetlink_log: netlink based logging requiring to setup ulogd2 to get the events
To use one of the two modules, load them with modprobe.
You can then setup logging on a per-protocol basis. The configuration is available in /proc:
# cat /proc/net/netfilter/nf_log 0 NONE (nfnetlink_log) 1 NONE (nfnetlink_log) 2 nfnetlink_log (nfnetlink_log,ipt_LOG) 3 NONE (nfnetlink_log) 4 NONE (nfnetlink_log) 5 NONE (nfnetlink_log) 6 NONE (nfnetlink_log) 7 nfnetlink_log (nfnetlink_log) 8 NONE (nfnetlink_log) 9 NONE (nfnetlink_log) 10 nfnetlink_log (nfnetlink_log,ip6t_LOG) 11 NONE (nfnetlink_log) 12 NONE (nfnetlink_log)
Here nfnetlink_log was loaded first and ulogd was started. For example, if you want to use ipt_LOG for IPv4 (2 in the list), you can do:
echo "ipt_LOG" >/proc/sys/net/netfilter/nf_log/2
This will active ipt_LOG for IPv4 logging:
# cat /proc/net/netfilter/nf_log 0 NONE (nfnetlink_log) 1 NONE (nfnetlink_log) 2 ipt_LOG (nfnetlink_log,ipt_LOG) 3 NONE (nfnetlink_log) 4 NONE (nfnetlink_log) 5 NONE (nfnetlink_log) 6 NONE (nfnetlink_log) 7 nfnetlink_log (nfnetlink_log) 8 NONE (nfnetlink_log) 9 NONE (nfnetlink_log) 10 nfnetlink_log (nfnetlink_log,ip6t_LOG) 11 NONE (nfnetlink_log) 12 NONE (nfnetlink_log)
If you want to do some easy testing, simply load xt_LOG module before nfnetlink_log. It will bind to IPv4 and IPv6 protocol and provide you logging.
Using one single chain
The chains are defined by user and can be arranged in any way. For example, on a single box, it is possible for example to use one single chain for input. To do so create a file onechain with:
table global { chain one { table filter hook input priority 0; } }
and run
nft -f onechain
You can then add rule like:
nft add rule ip global one ip daddr 192.168.0.0/24
The advantage of this setup is that Netfilter filtering will only be active for packets coming to the box.
Set
You can used non named set with the following syntax:
nft add rule ip global one ip daddr {192.168.1.1, 192.168.1.4} drop
Named set can be used in a file. For example, you can create a simple file:
define ip_set = {192.168.1.2, 192.168.2.3} add rule filter output ip daddr $ip_set counter
and running:
nft -f simple
It is also possible to use named set. To declare one set containing ipv4 address:
nft add set global ipv4_ad { type ipv4_address\;}
To add elements to the set:
nft add element global ipv4_ad { 192.168.3.4 } nft add element global ipv4_ad { 192.168.1.4, 192.168.1.5 }
Listing the set is done via:
nft list set global ipv4_ad
The set can then be used in rule:
nft add rule ip global filter ip saddr @ipv4_ad drop
It is possible to remove element from an existing set:
nft delete element global ipv4_ad { 192.168.1.5 }
and to delete a set:
nft delete set global myset
Mapping
Mapping are a specific type of set which behave like a dictionary. For example, it is possible to map ipv4_address to a verdict:
# nft -i nft> add map filter verdict_map { type ipv4_address => verdict } nft> add element filter verdict_map { 1.2.3.5 => drop} nft> add element filter verdict_map { 1.2.3.4 => accept} nft> add rule filter output ip daddr vmap @verdict_map
To delete one element of a mapping, you can use the same syntax as the set operation:
nft> delete element filter verdict_map 1.2.3.5
To delete one set you can use:
nft delete set filter verdict_map
Mapping can also be used in a anonymous way:
nft add rule filter output ip daddr vmap {192.168.0.0/24 =\> drop, 192.168.0.1 =\> accept}
In command line mode,don’t forget to protect the ‘>’ !
To list a specific mapping:
nft list set filter nat_map -n
NAT
First of all, the nat module is needed:
modprobe nft_nat
Next, you need to make the kernel aware of NAT for the protocol (here IPv4):
modprobe nft_chain_nat_ipv4
Now, we can create NAT dedicated chain:
nft add table nat nft add chain nat post \{ type nat hook postrouting priority 0 \; \} nft add chain nat pre \{ type nat hook prerouting priority 0 \; \}
We can now add NAT rules:
nft add rule nat post ip saddr 192.168.56.0/24 meta oif wlan0 snat 192.168.1.137 nft add rule nat pre udp dport 53 ip saddr 192.168.56.0/24 dnat 8.8.8.8:53
First one is NATing all trafic from 192.168.56.0/24 outgoing to wlan0 interface to the IP 192.168.1.137. Second one is redirecting all DNS trafic from 192.168.56.0/24 to the 8.8.8.8 server. It is possible to NAT to a range of address:
nft add rule nat post ip saddr 192.168.56.0/24 meta oif wlan0 snat 192.168.1.137-192.168.1.140
IPv6 NAT is possible too. First, you need to load the module to declare the NAT capability for IPv6:
modprobe nft_chain_nat_ipv6
Once done, you can add rules like:
table ip6 nat { chain postrouting { type nat hook postrouting priority -150; ip6 saddr 2::/64 snat 1::3; } }
Building a basic ruleset
The following ruleset is a typical ruleset to protect one laptop in IPv4 and IPv6:
# IPv4 filtering table filter { chain input { table filter hook input priority 0; ct state established accept ct state related accept meta iif lo accept tcp dport ssh counter packets 0 bytes 0 accept counter packets 5 bytes 5 log drop } chain output { table filter hook output priority 0; ct state established accept ct state related accept meta oif lo accept ct state new counter packets 0 bytes 0 accept } } #IPv6 filtering table ip6 filter { chain input { table filter hook input priority 0; ct state established accept ct state related accept meta iif lo accept tcp dport ssh counter packets 0 bytes 0 accept icmpv6 type { nd-neighbor-solicit, echo-request, nd-router-advert, nd-neighbor-advert } accept counter packets 5 bytes 5 log drop } chain output { table filter hook output priority 0; ct state established accept ct state related accept meta oif lo accept ct state new counter packets 0 bytes 0 accept } }