Skip to content

Bandbreddsbegränsning i Linux

Jag har gjort bandbreddsbegränsning i FreeBSD med ipfw och OpenBSD med pf så tidigt som 2005. I Linux testade jag att begränsa bandbredd först 2016 och slogs direkt av hur löjligt komplicerat det var jämfört med BSD alternativen. Därför måste jag skriva ner lite om det.

Guiden riktar sig mot CentOS och använder sig faktiskt av Firewalld så den tjänsten är inte avstängd, men det är en mindre detalj.

Skript

Ett skript som begränsar trafik på portarna 80 och 443.

# My understanding of Traffic Shaping on Linux, by Stefan Midjich.
# Run with sudo

# External NIC and IFB device. IFB device is used for ingress shaping.
extdev=enp2s0
ifbdev=ifb0
fwcmd=firewall-cmd --permanent --direct --passthrough ipv4

# Starts with output shaping

# Delete old traffic shaping devices
tc qdisc del dev $extdev root
tc qdisc del dev $ifbdev root

# Flush mangle table only if you don't have anything else important in it
#firewall-cmd --direct --passthrough ipv4 -t mangle -F

# Create a sort of root handle 1: (parent htb class)
tc qdisc add dev $extdev root handle 1: htb

# Set a global limit for the parent class 1:
# See man tc-htb for more info, there are for example settings like ceil
# to set a maximum ceiling of traffic. Rate is the minimum guaranteed traffic
# to a class.
tc class add dev $extdev parent 1: classid 1:1 htb rate 100Mbps

# Set sub limits on two sub-classes within that parent class, 1:5 and 1:6.
tc class add dev $extdev parent 1:1 classid 1:5 htb rate 20Mbps
tc class add dev $extdev parent 1:1 classid 1:6 htb rate 80Mbps

# Set priority on the two previous subclasses created, 0 is higher than 1.
# Also set a handle on each class which is the mark later used in iptables
# to mark the traffic. Mark 5 means handle 5 and so forth.
tc filter add dev $extdev parent 1:0 prio 1 protocol ip handle 5 fw flowid 1:5
tc filter add dev $extdev parent 1:0 prio 0 protocol ip handle 6 fw flowid 1:6

# Create iptables rules that reference the class using --set-mark, 5=1:5, 6=1:6.
#iptables -A OUTPUT -t mangle -p tcp --sport 80 -j MARK --set-mark 5
#iptables -A OUTPUT -t mangle -p tcp --sport 443 -j MARK --set-mark 6
# Or through firewall-cmd
$fwcmd -A OUTPUT -t mangle -p tcp --sport 80 -j MARK --set-mark 5
$fwcmd -A OUTPUT -t mangle -p tcp --sport 443 -j MARK --set-mark 6

# End of output shaping


# Input shaping

# Load ifb module and ensure device is not up
modprobe ifb
ip link set dev $ifbdev down

# Delete old traffic shaping devices
tc qdisc del dev $extdev ingress
tc qdisc del dev $ifbdev ingress

# Delete the custom chain QOS, created only for shaping ingress
$fwcmd -t mangle -X QOS

ip link set dev $ifbdev up

# Create new root htb class for ifb device. This largely follows the first
# example, except working with ifb device instead of external NIC.
tc qdisc add dev $ifbdev root handle 3: htb
tc class add dev $ifbdev parent 3: classid 3:1 htb rate 100Mbps
tc class add dev $ifbdev parent 3:1 classid 3:7 htb rate 20Mbps
tc class add dev $ifbdev parent 3:1 classid 3:8 htb rate 80Mbps

# Add handles to the above classes
tc filter add dev $ifbdev parent 3:0 prio 1 protocol ip handle 7 fw flowid 3:7
tc filter add dev $ifbdev parent 3:0 prio 0 protocol ip handle 8 fw flowid 3:8

# Create QOS chain
$fwcmd -t mangle -N QOS

# Forward traffic going out on extdev to QOS chain with --jump flag
$fwcmd -t mangle -A FORWARD -o $extdev -j QOS
$fwcmd -t mangle -A OUTPUT -o $extdev -j QOS

# Mark traffic in QOS with the above defined handles
$fwcmd -t mangle -A QOS -j CONNMARK --restore-mark
$fwcmd -t mangle -A QOS -p tcp --sport 80 -m mark --mark 0 -j MARK --set-mark 7
$fwcmd -t mangle -A QOS -p tcp --sport 443 -m mark --mark 0 -j MARK --set-mark 8
$fwcmd -t mangle -A QOS -j CONNMARK --save-mark

# Forward ingress traffic on extdev to the IFB device
tc qdisc add dev $extdev ingress handle ffff:
tc filter add dev $extdev parent ffff: protocol ip \
  u32 match u32 0 0 \
  action mirred egress redirect dev $ifbdev

Last update: June 6, 2020