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


3 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.