nftables adds in addition to protocol specific tables “ip” (IPv4) and “ip6” (IPv6) support for a IPv4/IPv6 aware table named “inet”. Using this table it's possible to add only one rule and match both protocols (in case of UDP and TCP).
Take care if rules are contained in more than one table, because the tables are checked in sequence:
IPv4-Packet --> table "ip" --> table "inet" --> further checks IPv6-Packet --> table "ip6" --> table "inet" --> further checks |
If table “ip6” accepts the packet, also table “inet” must accept the packet, otherwise it can be dropped by a later drop rule.
Install a Linux distribution which has nftables support already included. At time of writing (May 2014) at least Fedora Rawhide (upcoming version 21) has support in conjunction with nftables version 0.2.0.
Load kernel modules:
# modprobe nf_tables # modprobe nf_tables_ipv4 # modprobe nf_tables_ipv6 # modprobe nf_tables_inet |
Flush iptables and ip6tables to avoid interferences:
# iptables -F # ip6tables -F |
Create filter table:
# nft add table inet filter |
Create input chain:
# nft add chain inet filter input { type filter hook input priority 0 \; } |
Allow packets which are related to existing connection tracking entries
# nft add rule inet filter input ct state established,related counter accept |
Allow IPv4 and IPv6 ICMP echo-request (aka ping)
# nft add rule inet filter input meta nfproto ipv4 icmp type { echo-request } counter accept # nft add rule inet filter input meta nfproto ipv6 icmpv6 type echo-request counter accept |
Allow some important IPv6 ICMP traffic, without counter, but checking hop-limit for security
# nft add rule inet filter input meta nfproto ipv6 ¬ icmpv6 type { nd-neighbor-advert, nd-neighbor-solicit, nd-router-advert} ip6 hoplimit 1 accept # nft add rule inet filter input meta nfproto ipv6 ¬ icmpv6 type { nd-neighbor-advert, nd-neighbor-solicit, nd-router-advert} ip6 hoplimit 255 counter accept |
Allow incoming SSH for IPv4 and IPv6
# nft add rule inet filter input tcp dport 22 ct state new tcp flags \& \(syn \| ack\) == syn counter accept |
Reject/drop others
# nft add rule inet filter input tcp dport 0-65535 reject # nft add rule inet filter input udp dport 0-65535 counter drop # nft add rule inet filter input counter drop |
Table for IP version aware filter
table inet filter { chain input { type filter hook input priority 0; ct state established,related counter packets 0 bytes 0 accept ip protocol icmp icmp type { echo-request} counter packets 0 bytes 0 accept ip6 nexthdr ipv6-icmp icmpv6 type echo-request counter packets 0 bytes 0 accept ip6 nexthdr ipv6-icmp ip6 hoplimit 1 icmpv6 type { nd-neighbor-advert, nd-neighbor-solicit, nd-router-advert} accept ip6 nexthdr ipv6-icmp ip6 hoplimit 255 icmpv6 type { nd-neighbor-advert, nd-neighbor-solicit, nd-router-advert} accept tcp dport ssh ct state new tcp flags & (syn | ack) == syn counter packets 0 bytes 0 accept tcp dport >= 0 tcp dport <= 65535 counter packets 0 bytes 0 reject udp dport >= 0 udp dport <= 65535 counter packets 0 bytes 0 drop log prefix counter packets 0 bytes 0 drop } } |
To enable logging, an additonal kernel module must be loaded
# modprobe xt_LOG |
BUT TAKE CARE, IT LOOKS LIKE THAT NO LOG LEVEL CAN BE SPEFICIED CURRENTLY IN nftables, resulting that events are logged with kern.emerg - POSSIBILITY OF FLODDING THE CONSOLE WITH LOG ENTRIES!
Fir initial test with logging it can be useful to disable kernel console logging in e.g. /etc/rsyslog.conf by putting a “#” in front of the related entry and restart logging daemon
#*.emerg :omusrmsg:* |
Rule from above accepting SSH on port 22, but now with logging:
# nft add rule inet filter input tcp dport 22 ct state new tcp flags \& \(syn \| ack\) == syn log prefix \"inet/input/accept: \" counter accept |
As written above, if rules should be stored in related tables, it must be assured that earlier accepts are not discarded in the further table. This can be done using “meta mark set xxxx” on every accept rule and generic rules which accepts packets with “mark xxxx”. A resulting filter set would look like the following:
# for table in ip ip6 inet; do nft list table $table filter; done table ip filter { chain input { type filter hook input priority 0; ct state established,related counter packets 241 bytes 25193 accept counter packets 2 bytes 120 mark 0x00000100 accept icmp type { echo-request} counter packets 0 bytes 0 meta mark set 0x00000100 accept } } table ip6 filter { chain input { type filter hook input priority 0; ct state established,related counter packets 14 bytes 4077 accept counter packets 4 bytes 408 mark 0x00000100 accept icmpv6 type echo-request counter packets 1 bytes 104 meta mark set 0x00000100 icmpv6 type { nd-neighbor-advert, nd-neighbor-solicit, nd-router-advert} counter packets 2 bytes 224 meta mark set 0x00000100 accept } } table inet filter { chain input { type filter hook input priority 0; ct state established,related counter packets 307 bytes 31974 accept counter packets 6 bytes 528 mark 0x00000100 accept tcp dport ssh ct state new tcp flags & (syn | ack) == syn log prefix "inet/input/accept: " meta mark set 0x00000100 counter packets 3 bytes 200 accept log prefix "inet/input/reject: " counter packets 0 bytes 0 reject } } |