2

I am trying to build a simple stateful firewall with nftables following the Arch Linux nftables guide. I posted this question on the Arch Linux forum and never received an answer.

After completing the guide and rebooting my machine, systemd failed to load the nftables.service. To troubleshoot the error I ran:

systemctl status nftables

Here is the relevant output:

/etc/nftables.conf:7:17-25: Error: conflicting protocols specified: inet-service v. icmp

The error is complaining about a rule that I set for accepting new pings (icmp) in the input chain. Here is the rule and I don’t see anything wrong with it:

icmp type echo-request ct state new accept

If I remove the rule it will work. But I want the rule.

Here is my ruleset in nftables.conf after completing the guide:

    table inet filter {
    chain input {
        type filter hook input priority 0; policy drop;
        ct state established,related accept
        iif "lo" accept
        ct state invalid drop
        icmp type echo-request ct state new accept
        ip protocol udp ct state new jump UDP
        tcp flags & (fin | syn | rst | ack) == syn ct state new jump TCP
        ip protocol udp reject
        ip protocol tcp reject with tcp reset
        meta nfproto ipv4 counter packets 0 bytes 0 reject with icmp type prot-unreachable
    }

    chain forward {
        type filter hook forward priority 0; policy drop;
    }

    chain output {
        type filter hook output priority 0; policy accept;
    }

    chain TCP {
        tcp dport http accept
        tcp dport https accept
        tcp dport ssh accept
        tcp dport domain accept
    }

    chain UDP {
        tcp dport domain accept
    }
}

What am I missing? Thank you in advance.

1 Answer 1

3

This was a syntax limitation of nftables 0.7 (or a few other versions): it didn't consider ICMP and ICMPv6 directly usable in the dual IPv4/IPv6 table inet without stating explicitly which IP protocol first:

So the rule:

icmp type echo-request ct state new accept

to work both on IPv4 and IPv6 has to be written twice like this:

UPDATE: actually one shouldn't rely for IPv6 on nexthdr pointing to the upper-layer protocol: there can be Extension Headers between the Fixed Header and the upper-layer header (which comes last). Adding the correct syntax (using the meta-informations already providing protocol informations), and leaving my original answer striked, because I don't know if the "correct" syntax is valid with nftables 0.7:

meta nfproto ipv4 meta l4proto icmp icmp type echo-request ct state new accept
meta nfproto ipv6 meta l4proto icmpv6 icmpv6 type echo-request ct state new accept
ip protocol icmp icmp type echo-request ct state new accept
ip6 nexthdr icmpv6 icmpv6 type echo-request ct state new accept

giving the corresponding bytecode (displayed using nft --debug=netlink list ruleset -a):

inet filter input 9 8 
  [ meta load nfproto => reg 1 ]
  [ cmp eq reg 1 0x00000002 ]
  [ payload load 1b @ network header + 9 => reg 1 ]
  [ cmp eq reg 1 0x00000001 ]
  [ payload load 1b @ transport header + 0 => reg 1 ]
  [ cmp eq reg 1 0x00000008 ]
  [ ct load state => reg 1 ]
  [ bitwise reg 1 = (reg=1 & 0x00000008 ) ^ 0x00000000 ]
  [ cmp neq reg 1 0x00000000 ]
  [ immediate reg 0 accept ]

inet filter input 10 9 
  [ meta load nfproto => reg 1 ]
  [ cmp eq reg 1 0x0000000a ]
  [ payload load 1b @ network header + 6 => reg 1 ]
  [ cmp eq reg 1 0x0000003a ]
  [ payload load 1b @ transport header + 0 => reg 1 ]
  [ cmp eq reg 1 0x00000080 ]
  [ ct load state => reg 1 ]
  [ bitwise reg 1 = (reg=1 & 0x00000008 ) ^ 0x00000000 ]
  [ cmp neq reg 1 0x00000000 ]
  [ immediate reg 0 accept ]

ICMP is IP protocol 1, echo-request value 8.
ICMPv6 is IPv6 protocol 58 (0x3a), its echo-request value 128 (0x80).

Newer nftables 0.9 accepts directly the rule icmp type echo-request ct state new accept, but its corresponding bytecode is then only:

inet filter input 9 8 
  [ meta load nfproto => reg 1 ]
  [ cmp eq reg 1 0x00000002 ]
  [ meta load l4proto => reg 1 ]
  [ cmp eq reg 1 0x00000001 ]
  [ payload load 1b @ transport header + 0 => reg 1 ]
  [ cmp eq reg 1 0x00000008 ]
  [ ct load state => reg 1 ]
  [ bitwise reg 1 = (reg=1 & 0x00000008 ) ^ 0x00000000 ]
  [ cmp neq reg 1 0x00000000 ]
  [ immediate reg 0 accept ]

meaning it's dealing only with ICMP, not also ICMPv6, which should still be added with an additional rule, simply as:

icmpv6 type echo-request ct state new accept

giving back the equivalent bytecode of former version:

inet filter input 10 9 
  [ meta load nfproto => reg 1 ]
  [ cmp eq reg 1 0x0000000a ]
  [ meta load l4proto => reg 1 ]
  [ cmp eq reg 1 0x0000003a ]
  [ payload load 1b @ transport header + 0 => reg 1 ]
  [ cmp eq reg 1 0x00000080 ]
  [ ct load state => reg 1 ]
  [ bitwise reg 1 = (reg=1 & 0x00000008 ) ^ 0x00000000 ]
  [ cmp neq reg 1 0x00000000 ]
  [ immediate reg 0 accept ]

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy