Understanding iifname vs iif in nftables: Key Differences and Best Practices for Interface Matching


10 views

In nftables, both iifname and iif are meta expressions used for matching incoming network interfaces, but they operate at different levels of abstraction with distinct performance characteristics.

# iifname matches interface by name (string comparison)
iifname "eth0" accept

# iif matches interface by index (numeric comparison)
iif 2 accept

The primary technical distinctions:

  • iifname performs string matching against the interface name
  • iif uses the kernel's internal interface index (ifindex)
  • iif is generally faster as it avoids string comparisons
  • iifname is more readable but potentially slower

Benchmarks show iif can be 10-15% faster in high-throughput scenarios. This becomes significant when processing millions of packets per second. However, the difference is negligible for most use cases.

# Using iif with dynamic interfaces requires special handling
meta iif "ppp0" counter comment "This WON'T work - ppp0 might get new ifindex"
  
# Better approach for dynamic interfaces
define ppp_iface = "ppp0"
iifname $ppp_iface counter

Use iif when:

  • Working with static interfaces
  • Performance is critical
  • Ruleset is machine-generated

Prefer iifname when:

  • Dealing with dynamic interfaces (VPNs, PPP, etc.)
  • Human readability is important
  • Writing temporary debug rules
# Mixed approach combining both methods
define trusted_ifaces = { "eth0", "eth1" }

table inet filter {
    chain input {
        type filter hook input priority 0;
        
        # Fast path for known static interfaces
        iif { 2, 3 } accept comment "eth0 and eth1 by ifindex";
        
        # Fallback to string matching for others
        iifname $trusted_ifaces accept;
        
        # Reject everything else
        counter reject with icmp type host-unreachable
    }
}

The fundamental difference between iif and iifname in nftables lies in how they reference network interfaces:

iifname "eth0"   # String-based interface name matching
iif 2           # Numeric interface index matching

For optimal packet filtering performance:

  • iif uses kernel-level interface indexes (faster resolution)
  • iifname requires string comparison (slightly more overhead)

When to use iifname

# Match traffic coming from specific interface names
iifname { "eth0", "wlan0" } accept

When to use iif

# First find interface index:
$ ip link show eth0 | grep -oP '(?<=eth0: <).*(?=>)'

# Then use in nftables:
iif 2 accept

Important behavioral differences:

  • iif remains valid even if interface name changes
  • iifname breaks if interfaces are renamed
  • iif persists across interface restarts (same index)

For most use cases, iifname provides better readability and maintainability. Reserve iif for:

  • Performance-critical rulesets
  • Dynamic interface environments
  • Cases where interface names may change

Combining both approaches for maximum flexibility:

table inet filter {
    set dynamic_ifaces {
        type ifname
        elements = { "eth0", "ppp0" }
    }
    
    chain input {
        # Static interfaces by name
        iifname { "lo", "vpn0" } accept
        
        # Dynamic interface set
        iifname @dynamic_ifaces accept
        
        # Fallback to index for critical path
        iif 2 tcp dport 22 accept
    }
}

To debug interface matching issues:

nft --debug=netlink add rule inet filter input iif eth0 counter
nft monitor trace

This reveals whether the kernel is properly resolving interface references.