nftables is dropping packets on the netdev ingress hook before they even get routed, and you’re seeing NF_DROP in your conntrack or xtables-multi output. This isn’t a firewall rule blocking traffic after it’s decided where to go; this is the kernel’s network stack saying "nope" to a packet the moment it hits the network interface, before it knows if it’s destined for this machine or just passing through.
The netdev ingress hook is special because it operates at a very low level, before the kernel has determined the packet’s ultimate destination (routing decision). This means rules here can affect all traffic hitting an interface, including transit traffic if you’re running a router or bridge.
Here’s why it’s happening and how to fix it:
1. Incorrect meta l3proto or meta nexthdr Usage
You’re matching on the wrong IP version or the wrong next header in your nftables rules. This can lead to packets being incorrectly classified and dropped.
-
Diagnosis: Use
nft list ruleset -ato see your ruleset with packet counters. Look for rules on thenetdevingress hook that have high packet counts but aren’t intended to match. Specifically, check if you’re usingmeta l3proto ipv4(oripv6) andmeta nexthdr(for IPv6 extension headers) correctly.nft list ruleset -a -
Fix: Ensure your
meta l3protomatches the actual IP version of the traffic you intend to match, and if you’re usingmeta nexthdr, ensure it’s for the correct protocol identifier in the IPv6 header.For example, if you’re seeing IPv6 traffic being dropped by a rule intended for IPv4:
table ip filter { chain ingress { type filter hook ingress device eth0 priority 0; policy accept; # Incorrect rule: Might be matching IPv6 if not careful # ip protocol icmpv6 accept # Correct rule: Explicitly for IPv4 ip protocol icmp accept } }If you’re dealing with IPv6 and need to match specific extension headers, use the correct IANA protocol numbers:
table ip6 filter { chain ingress { type filter hook ingress device eth0 priority 0; policy accept; # Example: Match IPv6 packets with Hop-by-Hop Options header (protocol 0) # This is often a trap for misconfiguration # meta nexthdr 0 accept # More common: Accept regular IPv6 traffic meta l4proto ipv6-icmp accept } } -
Why it works:
meta l3protoandmeta nexthdrare fundamental for correctly identifying the IP version and protocol. Mismatches here mean the wrong packet types are being evaluated by subsequent, potentially restrictive, rules.
2. Accidental reject or drop on Transit Traffic
If your system acts as a router or bridge, and you have rules on the netdev ingress hook that drop or reject packets not destined for the local machine without a specific iifname or oifname match, you’ll drop transit traffic.
-
Diagnosis: Examine your
nftablesruleset on thenetdevingress hook for anydroporrejectstatements. Check if these statements lack specific interface matches (iifname,oifname) or source/destination IP address checks that would exempt transit traffic.nft list ruleset -a -
Fix: Explicitly allow transit traffic or ensure your drop rules are specific enough. A common pattern is to accept traffic destined for the local machine or traffic that will be forwarded.
table bridge filter { chain ingress { type filter hook ingress device br0 priority 0; policy accept; # Accept traffic destined for the bridge's MAC address ether saddr <bridge_mac> accept ether daddr <bridge_mac> accept # If it's not for the bridge, and you want to bridge it, # you might let it pass to the bridge's forwarding logic. # This is a simplified example; actual bridging is complex. # For pure routing, you'd look at ip/ip6 daddr. # Explicitly drop anything else that shouldn't be here. # Make sure this is *after* your accept rules for local/transit. # drop } } table ip filter { chain ingress { type filter hook ingress device eth0 priority 0; policy accept; # Accept traffic destined for the local host oifname eth0 accept # Accept traffic that will be forwarded (if not already accepted) # This is tricky on netdev ingress. Often, you'd rely on # the forward hook for explicit forward policy. # If you must drop here, ensure you allow forwarding. # Example: Drop everything else if this is a strict ingress policy # drop } } -
Why it works: The
netdevingress hook sees all packets arriving at the interface. Without explicitacceptrules for traffic that should pass through (transit traffic), a broaddroprule will catch it.
3. Mismatched priority or hook Values
You’ve configured your nftables chain to hook into netdev ingress but with a priority that conflicts with other kernel or nftables chains, or you’re using the wrong hook entirely.
-
Diagnosis: Use
nft list ruleset -aand look at thepriorityvalue for youringresschain. Default priorities are0forraw,100formangle,200fornat,300forfilter. Fornetdevingress,0is common. Check if other critical network functions (like bonding or specific network drivers) might be using that priority.nft list ruleset -a -
Fix: Adjust the
priorityvalue to avoid conflicts. If you’re unsure,0is generally safe fornetdevingress if no othernftableschains are using it and it doesn’t clash with kernel modules.table ip filter { chain ingress { # Changed priority from, say, 100 to 0 type filter hook ingress device eth0 priority 0; policy accept; # ... your rules ... } } -
Why it works: Hooks and priorities define the order in which packet processing modules in the kernel see and act upon packets. A conflicting priority can mean your rules are evaluated too early or too late, or not at all, leading to unexpected drops.
4. Incorrect dev or iifname/oifname Matching
You’re applying rules to the wrong network interface, or you’re using iifname/oifname in a way that doesn’t match the packet’s actual ingress or egress interface at that hook.
-
Diagnosis: Verify the interface name in your
nftableschain definition (device <interface>) and within your rules (iifname,oifname). Useip aorbrctl showto confirm interface names.nft list ruleset -a -
Fix: Correct the interface names. For
netdevingress, thedeviceparameter in the chain definition is crucial.iifnameandoifnamecan also be used within rules, butdevicesets the scope for the entire chain.table ip filter { chain ingress { type filter hook ingress device eth1 priority 0; policy accept; # Corrected to eth1 # ... your rules ... # If using iifname/oifname within rules: # iifname eth1 accept } } -
Why it works: Packet filtering is interface-specific. Applying rules to the wrong interface means legitimate traffic on the intended interface is unaffected, while traffic on the other interface (where the rules are incorrectly applied) might be dropped.
5. Missing policy accept or Incorrect policy accept on Bridged Traffic
If you’re bridging interfaces and the policy for your netdev ingress chain is drop, you’ll drop all traffic that doesn’t match an explicit accept rule. This is especially common if you’re trying to set up a simple bridge and forget the default policy.
-
Diagnosis: Check the
policystatement for yournetdevingress chain.nft list ruleset -a -
Fix: Set the
policytoacceptif you want the default behavior to be to allow traffic, and then usedroprules for specific unwanted traffic. If you intend to filter strictly, ensure youracceptrules cover all valid traffic, including transit.table bridge filter { chain ingress { type filter hook ingress device br0 priority 0; policy accept; # Set to accept # ... your rules ... } } -
Why it works: The
policyof a chain dictates what happens to packets that fall through all the defined rules. Apolicy acceptlets everything through by default, whilepolicy dropblocks everything by default.
6. Kernel Module Interference (Rare but Possible)
Certain kernel modules or network configurations (like complex bonding setups or specific hardware offloads) might interact with the netdev ingress hook in unexpected ways, potentially dropping packets before nftables even sees them.
-
Diagnosis: This is the hardest to diagnose. Temporarily disable suspect kernel modules or simplify your network configuration. Check
dmesgfor any network-related errors.tcpdumpon the physical interface before it hits thenftableshook (if possible, e.g., via an SPAN port or a tap device) can help identify if packets are being dropped at an even lower level. -
Fix: This usually involves reordering
nftableshooks (e.g., moving filtering topreroutingif possible and appropriate for your use case), adjusting kernel module parameters, or reporting a bug to the kernel or driver developers. -
Why it works: The
netdevingress hook is very early. If something else in the kernel network stack is already messing with packets at this stage, it can be hard to untangle.
The next error you’ll hit is likely related to prerouting or forward hook filtering, as you move your filtering logic to a stage where the packet’s destination is known.