The tc filter add command is failing because the kernel’s traffic control subsystem is rejecting the filter definition, most commonly due to a mismatch between the filter’s protocol, parent, or intended action, or because the necessary kernel modules aren’t loaded.

Here are the most common reasons this command fails and how to fix them:

1. Invalid Parent Interface:

  • Diagnosis: You’re trying to attach a filter to a non-existent or incorrectly specified network interface. The kernel expects a valid dev argument pointing to a real network device.
  • Common Causes & Fixes:
    • Typo in Interface Name: Double-check eth0, ens192, wlan0, etc.
      • Command: ip link show
      • Fix: Correct the interface name in your tc command. For example, if ip link show lists ens192 but you typed ens193, change your command.
      • Why it works: tc operates on network devices. An invalid name means no device is found for the filter to attach to.
    • Filter Attached to a Loopback/Virtual Interface: Some virtual interfaces or loopback devices (lo) might not support the kind of traffic control you’re trying to apply.
      • Fix: Attach the filter to a physical interface that handles the traffic you intend to shape.
      • Why it works: Traffic control is typically implemented at the network device driver level. Not all drivers expose the necessary hooks.
    • Filter Attached to a Non-Existent Qdisc: If you’re trying to attach a filter to a specific qdisc (e.g., parent 1:) that hasn’t been created yet on the interface, tc will error.
      • Diagnosis: tc qdisc show dev <interface>
      • Fix: Ensure the parent qdisc exists. Often, you’ll need to add a root qdisc first (like htb or ingress) before adding filters.
        tc qdisc add dev eth0 root handle 1: htb default 1
        tc filter add dev eth0 parent 1: ...
        
      • Why it works: Filters are children of qdiscs. A filter cannot exist without a parent qdisc to attach to.

2. Incorrect Protocol Specification:

  • Diagnosis: The protocol argument (e.g., ip, ipv6, ether) does not match the actual traffic passing through the interface or the filter’s matching criteria.
  • Common Causes & Fixes:
    • Mismatched IP Version: Trying to filter IPv4 traffic with protocol ipv6 or vice-versa.
      • Fix: Use protocol ip for IPv4 and protocol ipv6 for IPv6. If you want to match all IP traffic, protocol ip is often sufficient as it can catch IPv6 too depending on the kernel version and configuration.
      • Why it works: The kernel uses the protocol field to know which header parsing and matching routines to invoke.
    • Using ether for IP-level Filtering: The ether protocol is for Layer 2 (MAC address) filtering. If you’re trying to match IP addresses, ports, or protocols, use ip or ipv6.
      • Fix: Change protocol ether to protocol ip if you’re specifying IP addresses or ports.
      • Why it works: ether filters operate on the Ethernet frame header, while ip filters operate on the IP packet header.

3. Invalid Filter Type or Action:

  • Diagnosis: The handle or action specified for the filter is not understood by the kernel or is incompatible with the parent qdisc.
  • Common Causes & Fixes:
    • Using flowid with non-prio or htb qdiscs: flowid is specific to prio and htb qdiscs.
      • Fix: Ensure your parent qdisc is htb or prio if you’re using flowid.
        tc qdisc add dev eth0 root handle 1: htb default 1
        tc filter add dev eth0 parent 1: protocol ip prio 1 flowid 1:1
        
      • Why it works: flowid is an identifier used by these specific qdisc types to route packets to sub-classes or bands.
    • Unsupported action: Trying to use an action (like mirred, tunnel_key, nat) that isn’t compiled into your kernel or isn’t supported by the qdisc.
      • Diagnosis: Check /proc/net/ip_tables_matches or kernel configuration.
      • Fix: Ensure the necessary kernel modules are loaded (e.g., xt_mirred). If not available, you might need to recompile the kernel or use a different approach.
        sudo modprobe xt_mirred
        
      • Why it works: tc actions are implemented as kernel modules. If the module isn’t present or loaded, the action is unknown.
    • Incorrect handle Format: The handle for a filter, especially when it’s directly under the root qdisc, needs to be unique and follow the major:minor convention.
      • Fix: Use a unique handle, often 1:, 2:, etc., for filters directly attached to the root. For filters attached to secondary qdiscs, use the qdisc’s handle as the parent (e.g., parent 1:1).
      • Why it works: Handles are internal identifiers the kernel uses to manage qdiscs and filters.

4. Missing Kernel Modules:

  • Diagnosis: The specific traffic control classifier or action you’re trying to use requires a kernel module that isn’t loaded.
  • Common Causes & Fixes:
    • Missing cls_u32 or cls_fw: For basic packet matching.
      • Fix: Load the module: sudo modprobe cls_u32 or sudo modprobe cls_fw.
      • Why it works: These modules provide the logic for classifying packets based on various criteria.
    • Missing act_mirred: For redirecting traffic.
      • Fix: Load the module: sudo modprobe act_mirred.
      • Why it works: This module implements the mirred action, allowing packets to be mirrored or redirected.
    • Missing ematch modules: For extended matching (e.g., ematch_meta).
      • Fix: Load relevant modules: sudo modprobe ematch_meta.
      • Why it works: ematch modules extend the matching capabilities beyond basic u32 or fw classifiers.
    • General Check: If you’re unsure, try loading modules related to the specific classifier or action you’re using. Check lsmod | grep sch or lsmod | grep cls.

5. Filter Specification Ambiguity or Conflict:

  • Diagnosis: The filter’s matching rules are too broad, too narrow, or conflict with existing filters.
  • Common Causes & Fixes:
    • Overlapping u32 Matches: If you have multiple u32 filters with overlapping match criteria, the kernel might pick the first one it encounters or reject the addition due to ambiguity, depending on the priority and classid.
      • Diagnosis: Carefully review your tc filter show dev <interface> output.
      • Fix: Assign distinct priorities (prio) to your filters, or refine the match criteria (u32) to be mutually exclusive.
        tc filter add dev eth0 parent 1: protocol ip prio 1 u32 ...
        tc filter add dev eth0 parent 1: protocol ip prio 2 u32 ...
        
      • Why it works: Priorities determine the order in which filters are evaluated. Distinct criteria ensure each packet matches only one intended filter.
    • Invalid classid: The classid specified in the filter’s action (e.g., flowid 1:10) does not correspond to an existing class within the parent qdisc.
      • Fix: Ensure you have created the class using tc class add.
        tc class add dev eth0 parent 1: classid 1:10 htb rate 100kbit
        tc filter add dev eth0 parent 1: protocol ip prio 1 u32 flowid 1:10 ...
        
      • Why it works: Filters direct traffic to classes. If the target class doesn’t exist, the filter cannot be applied.

The next error you’ll likely encounter after fixing these is related to tc not being able to establish a connection to the netlink socket, often due to insufficient permissions or a deeply corrupted network stack state.

Want structured learning?

Take the full Computer Networking course →