Linux Traffic Shaping: Combining Ingress Policing with IFB Mirroring for Multi-Interface Gateways


17 views

When implementing traffic shaping on Linux gateways with multiple LAN interfaces, engineers often face conflicts between ingress policing and IFB mirroring. The fundamental issue arises from attempting to use both ingress qdisc and handle ffff: ingress simultaneously on the same interface.

The Linux traffic control subsystem only allows one ingress qdisc per interface. When you execute:

/sbin/tc qdisc add dev $WAN_INTERFACE ingress
/sbin/tc qdisc add dev $WAN_INTERFACE handle ffff: ingress

You'll encounter the "file exists" error because these commands are trying to create two ingress handlers on the same interface.

Here's how to properly combine both approaches:

# Setup IFB device
modprobe ifb
ip link set dev ifb0 up

# Create single ingress handler
/sbin/tc qdisc add dev $WAN_INTERFACE handle ffff: ingress

# Redirect all traffic to IFB for shaping
/sbin/tc filter add dev $WAN_INTERFACE parent ffff: protocol ip \
    u32 match u32 0 0 action mirred egress redirect dev ifb0

# Apply policing to specific ports (example for SSH)
INTERACTIVE_PORT=22
/sbin/tc filter add dev $WAN_INTERFACE parent ffff: protocol ip \
    prio 1 u32 match ip sport $INTERACTIVE_PORT 0xffff flowid :1
/sbin/tc filter add dev $WAN_INTERFACE parent ffff: protocol ip \
    prio 1 u32 match ip dport $INTERACTIVE_PORT 0xffff flowid :1

# Global rate limiting on IFB
/sbin/tc qdisc add dev ifb0 root handle 1: htb default 10
/sbin/tc class add dev ifb0 parent 1: classid 1:1 htb rate 100mbit
/sbin/tc class add dev ifb0 parent 1:1 classid 1:10 htb rate 95mbit ceil 100mbit

The critical insight is that policing rules must be applied before the mirroring action in the filter chain. The priority values (prio) determine the evaluation order:

  • Higher priority numbers are evaluated later
  • The mirroring action should generally have the lowest priority
  • Policing rules should use higher priorities than the mirroring rule

To verify your configuration:

# Show qdiscs
tc qdisc show dev $WAN_INTERFACE
tc qdisc show dev ifb0

# Show filter rules with verbose output
tc -s filter show dev $WAN_INTERFACE parent ffff:
tc -s filter show dev ifb0 parent 1:

Check packet counters to ensure traffic is being properly classified and redirected.

When processing traffic twice (once for policing, once for mirroring), be aware of:

  • Increased CPU usage on the gateway
  • Potential latency spikes during high traffic periods
  • Bufferbloat effects on the IFB device

For high-bandwidth applications, consider using eBPF filters for better performance.


When configuring advanced traffic shaping on Linux gateways, combining ingress policing with IFB (Intermediate Functional Block) mirroring presents unique technical challenges. The fundamental issue arises from the exclusive nature of ingress qdisc attachment and the need to simultaneously handle both rate-limiting and traffic mirroring.

Linux's traffic control subsystem only allows one ingress qdisc per network interface. Attempting to create multiple ingress qdiscs will fail with "File exists" error:

# This will fail if another ingress qdisc exists
tc qdisc add dev eth0 handle ffff: ingress

The optimal approach combines both requirements in a single ingress qdisc structure:

# Initialize IFB device
modprobe ifb
ip link set dev ifb0 up

# Create single ingress qdisc
tc qdisc add dev $WAN_INTERFACE ingress

# Mirror ALL traffic to IFB first
tc filter add dev $WAN_INTERFACE parent ffff: protocol ip \
    u32 match u32 0 0 action mirred egress redirect dev ifb0

# Add policing rules with lower priority (higher number)
tc filter add dev $WAN_INTERFACE parent ffff: protocol ip \
    prio 10 u32 match ip sport $INTERACTIVE_PORT 0xffff \
    police rate $MAX_DOWNRATE_INGRESS burst 20k drop
    
# Additional rules can be added with different priorities

The solution lies in proper priority ordering of filters:

# High priority (low number) for mirroring
tc filter add dev eth0 parent ffff: protocol ip prio 1 \
    u32 match u32 0 0 action mirred egress redirect dev ifb0

# Lower priority (higher number) for policing
tc filter add dev eth0 parent ffff: protocol ip prio 10 \
    u32 match ip src 192.168.1.0/24 police rate 10mbit burst 15k drop

Check the configuration with these commands:

tc -s qdisc show dev $WAN_INTERFACE
tc -s filter show dev $WAN_INTERFACE parent ffff:
ip -s link show ifb0

For multiple LAN interfaces with different shaping requirements:

# Set up IFB devices
modprobe ifb numifbs=2
ip link set dev ifb0 up
ip link set dev ifb1 up

# Main ingress qdisc
tc qdisc add dev eth0 ingress

# Mirror to IFB0 with high priority
tc filter add dev eth0 parent ffff: protocol ip prio 1 \
    u32 match ip dport 80 0xffff action mirred egress redirect dev ifb0

# Mirror to IFB1 with medium priority  
tc filter add dev eth0 parent ffff: protocol ip prio 2 \
    u32 match ip dport 443 0xffff action mirred egress redirect dev ifb1

# Global rate limit with lowest priority
tc filter add dev eth0 parent ffff: protocol ip prio 20 \
    u32 match ip src 0.0.0.0/0 police rate 100mbit burst 50k drop

When implementing this solution:

  • Place the most specific matches first (higher priority)
  • Combine similar rules to minimize filter count
  • Monitor CPU usage during peak traffic
  • Adjust burst sizes based on observed latency

If IFB devices show no traffic:

  1. Verify filters are correctly ordered by priority
  2. Check for conflicting tc rules with tc filter show
  3. Ensure IFB devices are up (ip link show)
  4. Test with simple mirroring rules first