1

i am using Debian and am trying to limit the upload-bandwidth on an ip basis.

The configuration is the following:

#!/bin/bash
tc=/sbin/tc

$tc qdisc del dev eth0 root 2> /dev/null > /dev/null
$tc qdisc del dev eth0 ingress 2> /dev/null > /dev/null

$tc qdisc del dev tun0 root 2> /dev/null > /dev/null
$tc qdisc del dev tun0 ingress 2> /dev/null > /dev/null

$tc qdisc add dev eth0 root handle 1: htb default 30

$tc class add dev eth0 parent 1: classid 1:1 htb rate 5.0mbit burst 6k
$tc class add dev eth0 parent 1:1 classid 1:10 htb rate 2.0mbit ceil 4.5mbit burst 2k prio 1
$tc class add dev eth0 parent 1:1 classid 1:20 htb rate 2.0mbit ceil 4.5mbit burst 3k prio 2
$tc class add dev eth0 parent 1:1 classid 1:30 htb rate 0.5mbit burst 1k prio 3

$tc qdisc add dev eth0 parent 1:10 handle 10: sfq perturb 10
$tc qdisc add dev eth0 parent 1:20 handle 20: sfq perturb 10
$tc qdisc add dev eth0 parent 1:30 handle 30: sfq perturb 10

$tc filter add dev eth0 parent 1: protocol ip prio 1 u32 match ip src 192.168.1.1 flowid 1:10
$tc filter add dev eth0 parent 1: protocol ip prio 1 u32 match ip src 192.168.1.2 flowid 1:20
$tc filter add dev eth0 parent 1: protocol ip prio 1 u32 match ip src 192.168.1.4 flowid 1:30

The "setup" seems to be fine:

filter parent 1: protocol ip pref 1 u32 chain 0 
filter parent 1: protocol ip pref 1 u32 chain 0 fh 800: ht divisor 1 
filter parent 1: protocol ip pref 1 u32 chain 0 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:10 not_in_hw 
  match c0a80101/ffffffff at 12
filter parent 1: protocol ip pref 1 u32 chain 0 fh 800::801 order 2049 key ht 800 bkt 0 flowid 1:20 not_in_hw 
  match c0a80102/ffffffff at 12
filter parent 1: protocol ip pref 1 u32 chain 0 fh 800::802 order 2050 key ht 800 bkt 0 flowid 1:30 not_in_hw 
  match c0a80104/ffffffff at 12

But I end up having all clients being in 1:30 despite the filter:

+---(1:1) htb rate 5Mbit ceil 5Mbit burst 6Kb cburst 1600b 
     |    Sent 18121225 bytes 29841 pkt (dropped 0, overlimits 1926 requeues 0) 
     |    backlog 0b 0p requeues 0
     |
     +---(1:10) htb prio 1 rate 2Mbit ceil 4500Kbit burst 2Kb cburst 1599b 
     |          Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0) 
     |          backlog 0b 0p requeues 0
     |     
     +---(1:20) htb prio 2 rate 2Mbit ceil 4500Kbit burst 3Kb cburst 1599b 
     |          Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0) 
     |          backlog 0b 0p requeues 0
     |     
     +---(1:30) htb prio 3 rate 500Kbit ceil 500Kbit burst 1Kb cburst 1600b 
                Sent 18121225 bytes 29841 pkt (dropped 23532, overlimits 27820 requeues 0) 
                backlog 244086b 117p requeues 0

What am I doing wrong?

UPDATE

I was able to apply the filtering rules by using iptables:

$tc filter add dev eth0 parent 1: protocol ip handle 2 fw flowid 1:20
$ipt -t mangle -A FORWARD -i eth1 -o eth0 -s 192.168.1.2 -j MARK --set-mark 2

It results in the traffic from 192.168.1.2 being put into 1:20:

+---(1:1) htb rate 5Mbit ceil 5Mbit burst 6Kb cburst 1600b 
     |    Sent 34671556 bytes 40595 pkt (dropped 0, overlimits 8591 requeues 0) 
     |    backlog 0b 0p requeues 0
     |
     +---(1:10) htb prio 1 rate 2Mbit ceil 4500Kbit burst 2Kb cburst 1599b 
     |          Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0) 
     |          backlog 0b 0p requeues 0
     |     
     +---(1:20) htb prio 2 rate 2Mbit ceil 3500Kbit burst 3Kb cburst 1599b 
     |          Sent 26070590 bytes 17443 pkt (dropped 0, overlimits 10466 requeues 0) 
     |          backlog 0b 0p requeues 0
     |     
     +---(1:30) htb prio 3 rate 500Kbit ceil 500Kbit burst 1Kb cburst 1600b 
                Sent 8600966 bytes 23152 pkt (dropped 0, overlimits 23146 requeues 0) 
                backlog 57060b 70p requeues 0

As of now it seems that the u32 filter does not work.

Christian
  • 121
  • 4
  • 1
    I copied your setup in a dummy environment (using 3 local source IPs, not routing) and I get filter hits for each of the 3 source IPs with tc alone. I'm using kernel 5.10 rather than 4.19 though, in case that matters. Are you sure you are not using a MASQUERADE rule? it would be invisible from the filter/FORWARD chain but very visible from tc at the end of the pipe: never matching 192.168.1.x – A.B Jan 30 '21 at 14:50
  • I am indeed using masquerade on two external interfaces: ``` $iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE $iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE ``` – Christian Jan 31 '21 at 10:02

1 Answers1

1

Thanks to @A-B who made me aware of the reason behind tc not filtering.

I do use MASQUERADING on all external interfaces and it seems that those rules are applied before tc can filter the packets. This results in the following recommendation:

If you are using a MASQUERADE-rule on the outgoing interface you need to use the fw-filter from traffic-control and mark the packets using iptables.

This might seem obvious to the more experienced users but its not mentioned anywhere and (at least for me) its hard to find out.

Example:

#!/bin/bash
tc=/sbin/tc
ipt=/sbin/iptables

$tc qdisc add dev eth0 root handle 1: htb default 30

# shape general uplink speed
$tc class add dev eth0 parent 1: classid 1:1 htb rate 5.0mbit burst 6k
$tc class add dev eth0 parent 1:1 classid 1:10 htb rate 2.0mbit ceil 4.0mbit burst 2k prio 1
$tc class add dev eth0 parent 1:1 classid 1:20 htb rate 2.5mbit ceil 4.0mbit burst 3k prio 2
$tc class add dev eth0 parent 1:1 classid 1:30 htb rate 0.5mbit ceil 1.0mbit burst 1k prio 3

# stochastic fairness
$tc qdisc add dev eth0 parent 1:10 handle 10: sfq perturb 10
$tc qdisc add dev eth0 parent 1:20 handle 20: sfq perturb 10
$tc qdisc add dev eth0 parent 1:30 handle 30: sfq perturb 10

# filtering on eth0
$tc filter add dev eth0 parent 1: protocol ip handle 10 fw flowid 1:10
$tc filter add dev eth0 parent 1: protocol ip handle 20 fw flowid 1:20
$tc filter add dev eth0 parent 1: protocol ip handle 30 fw flowid 1:30

# marking packets via iptables
$ipt -t mangle -I FORWARD -i eth1 -o eth0 -s 192.168.1.2 -j MARK --set-mark 20
$ipt -t mangle -I FORWARD -i eth1 -o eth0 -s 192.168.1.4 -j MARK --set-mark 30
$ipt -t mangle -I FORWARD -i eth1 -o eth0 -s 192.168.1.21 -j MARK --set-mark 10

Those settings result in packets from the mentioned sources (-s) being assigned to the classes defined above.

Christian
  • 121
  • 4