The most surprising thing about nftables ICMP filtering is how often it breaks the internet for you, even when you think you’re doing it right.

Let’s see what a basic setup looks like. Imagine you’re building a firewall for a server. You want to allow inbound pings (ICMP echo requests) so you can monitor its availability, and also allow ICMPv6.

Here’s a snippet of an nftables ruleset that attempts to do just that:

table ip filter {
    chain input {
        type filter hook input priority 0; policy drop;

        # Allow established/related connections
        ct state established,related accept

        # Allow loopback
        iifname "lo" accept

        # Allow ICMPv4 echo request
        ip protocol icmp icmp type echo-request accept

        # Allow ICMPv6 echo request
        ip6 nexthdr icmpv6 icmpv6 type echo-request accept

        # Drop everything else
        reject
    }
}

table ip6 filter {
    chain input {
        type filter hook input priority 0; policy drop;

        # Allow established/related connections
        ct state established,related accept

        # Allow loopback
        iifname "lo" accept

        # Allow ICMPv6 echo request
        ip6 nexthdr icmpv6 icmpv6 type echo-request accept

        # Drop everything else
        reject
    }
}

This looks straightforward, right? You’re explicitly allowing echo-request for both IPv4 and IPv6. But if you deploy this and try to ping your server from another machine, it probably won’t work. You might get "Request timed out" or "Destination unreachable."

This is because ICMP is a family of protocols, not just ping. The nftables rules above are too specific. They only allow echo-request. When your server needs to send back an ICMP "destination unreachable" or "time exceeded" message (which it often does when packets are dropped by other rules or network devices), those packets are blocked by your policy drop because they aren’t echo-request.

The real problem isn’t just blocking pings in, it’s also blocking the outbound ICMP responses and other necessary ICMP messages that keep the network path healthy.

Here’s how to fix it, addressing the common issues:

  1. Allowing all necessary ICMP types: The most common mistake is only allowing echo-request. You need to allow other types that are crucial for network diagnostics and error reporting. For IPv4, this typically includes destination-unreachable, time-exceeded, and echo-reply (though echo-reply is usually sent in response to an incoming echo-request and thus covered by the ct state established,related rule if the outbound is allowed). For IPv6, it’s similar.

    • Diagnosis: Run nft list ruleset and check your ICMP rules. Compare them against the official ICMP type numbers or names.

    • Fix: Modify your rules to be more permissive. Instead of just icmp type echo-request, use a set of accepted types.

      # For IPv4
      ip protocol icmp icmp type { echo-request, echo-reply, destination-unreachable, time-exceeded } accept
      
      # For IPv6
      ip6 nexthdr icmpv6 icmpv6 type { echo-request, echo-reply, destination-unreachable, time-exceeded, packet-too-big } accept
      
    • Why it works: This explicitly permits the ICMP message types that are essential for basic network functionality, including responses to your pings and critical error notifications from the network.

  2. Implicitly allowing echo-reply via state tracking: Your ct state established,related accept rule is doing a lot of work. When a ping comes in (echo-request), the connection is marked as established. The server’s response (echo-reply) will then be accepted because it’s part of an established connection. However, if the initial echo-request was blocked, the established,related rule won’t save you.

    • Diagnosis: If echo-reply is still not working, and you have allowed echo-request, check if your ct state established,related rule is correctly placed before your ICMP-specific rules.

    • Fix: Ensure the state tracking rule is near the top of your input chain.

      chain input {
          # ... other rules ...
          ct state established,related accept
          # ... ICMP rules ...
      }
      
    • Why it works: State tracking allows return packets for established connections without needing explicit rules for every possible reply type, simplifying your ruleset significantly.

  3. Missing destination-unreachable for IPv6: IPv6 has a specific ICMPv6 type called packet-too-big that is crucial for Path MTU Discovery. If this is blocked, connections can become unstable or fail entirely.

    • Diagnosis: Look for packet-too-big in your IPv6 ICMP rules. If it’s not there, or if you’re seeing connection issues that don’t manifest as simple timeouts.

    • Fix: Add packet-too-big to your IPv6 ICMP type set.

      ip6 nexthdr icmpv6 icmpv6 type { echo-request, echo-reply, destination-unreachable, time-exceeded, packet-too-big } accept
      
    • Why it works: This allows the router or the destination server to inform your host that a packet was too large to be forwarded, enabling your system to adjust its packet sizes accordingly.

  4. Blocking outbound ICMP: Sometimes the issue isn’t inbound pings, but the server’s ability to send outbound ICMP messages. If you have an output chain with a policy drop and haven’t explicitly allowed necessary outbound ICMP types, your server might not be able to respond to anything.

    • Diagnosis: Test pinging from your server to an external host. If that fails, check your output chain.

    • Fix: Add similar ICMP type allowances to your output chain.

      table ip filter {
          chain output {
              type filter hook output priority 0; policy drop;
      
              # Allow established/related connections
              ct state established,related accept
      
              # Allow loopback
              oifname "lo" accept
      
              # Allow outbound ICMP
              ip protocol icmp icmp type { echo-request, echo-reply, destination-unreachable, time-exceeded } accept
              ip6 nexthdr icmpv6 icmpv6 type { echo-request, echo-reply, destination-unreachable, time-exceeded, packet-too-big } accept
      
              reject
          }
      }
      
    • Why it works: This ensures your server can send out necessary diagnostic and error messages, which are vital for network communication.

  5. Using nft sets for cleaner rules: For a large number of ICMP types, using sets makes the ruleset much more readable and manageable.

    • Diagnosis: If your ICMP rules are becoming very long and repetitive, it’s time to refactor.

    • Fix: Define sets for your allowed ICMP types.

      table ip filter {
          set allowed_icmp_types {
              type ipv4_icmp type
              elements = { echo-request, echo-reply, destination-unreachable, time-exceeded }
          }
          set allowed_icmpv6_types {
              type ipv6_icmp type
              elements = { echo-request, echo-reply, destination-unreachable, time-exceeded, packet-too-big }
          }
      
          chain input {
              type filter hook input priority 0; policy drop;
              # ... other rules ...
              ct state established,related accept
      
              # Allow ICMPv4
              ip protocol icmp icmp type @allowed_icmp_types accept
      
              # Allow ICMPv6
              ip6 nexthdr icmpv6 icmpv6 type @allowed_icmpv6_types accept
      
              reject
          }
      }
      
    • Why it works: Sets group related elements, making the rules cleaner and easier to update. Adding a new ICMP type only requires modifying the set definition, not multiple rule lines.

  6. Firewall on the host vs. between hosts: Remember that nftables controls traffic entering and leaving the host where it’s running. If you’re trying to ping a server and it’s not working, and you’ve confirmed your server’s nftables rules allow ICMP, the problem might be an intermediate firewall, router, or even the client’s own firewall.

    • Diagnosis: Use traceroute (or mtr) from the client to the server. If it stops at a specific hop, that hop is likely blocking ICMP.
    • Fix: Configure the intermediate firewall or router to allow the necessary ICMP types.
    • Why it works: Network troubleshooting often requires looking beyond the immediate system.

Once you’ve correctly configured your ICMP rules to allow essential types and leverage state tracking, you should find that pinging works reliably, and your server can communicate necessary network control messages.

The next error you’ll likely encounter is related to blocking other essential protocols like DNS (UDP/TCP port 53) or SSH (TCP port 22), as your policy drop is still in effect for everything else.

Want structured learning?

Take the full Nftables course →