Linux Traffic Shaper

Based on a systemd distro

test if tc is installed on your system

you should expect some usage output if not, install the iproute or iproute2 package

tc

create the shaper service

cd /usr/lib/systemd/system
nano shaper.service

[Unit]
Description="tc traffic shaper"

[Service]
WorkingDirectory=/usr/local/shaper
RemainAfterExit=yes
ExecStart=/usr/local/shaper/traffic-shaper.sh start
ExecStop=/usr/local/shaper/traffic-shaper.sh stop

[Install]
WantedBy=multi-user.target

save and exit

mkdir /usr/local/shaper
cd /usr/local/shaper/
touch traffic-shaper.sh
chmod +x traffic-shaper.sh
nano traffic-shaper.sh

#!/bin/bash

# enable tc traffic shaping for speedtests

# current config applies one rate to all traffic
# flowids tie your matched traffic to classids, so tc knows what traffic rate to allow

# If your speedtest server is commonly sending close to RATE of traffic, then you might want to consider
# multiple class filters for groups of customer endpoint networks. Then make RATE the line rate of your server's interface
# otherwise, your customers may soak the single 990mbit traffic rate and not get accurate speedtests
# this possiblity assumes the link to your server is either a 10gig link or possibly multiple 1gigs in a bond
# To do this, comment out the default filter statement, define CLASSRATE for class bandwidth rates.
# And either define your customer networks with more variables, or manually enter IP networks in the filter statements

##### Multi Class Example
# TC=/sbin/tc
# IF=p1p1
# RATE=10gbit
# CLASSRATE=990mbit
# NET2=192.168.200.0/24
#
# start() {
#   $TC qdisc add dev $IF handle 1: root htb
#   $TC class add dev $IF parent 1: classid 1:1 htb rate $RATE
#   $TC class add dev $IF parent 1:0 classid 1:10 htb rate $CLASSRATE
#   $TC class add dev $IF parent 1:0 classid 1:20 htb rate $CLASSRATE
#   $TC filter add dev $IF parent 1:0 protocol ip prio 1 u32 match ip dst 192.168.100.0/24 flowid 1:10
#   $TC filter add dev $IF parent 1:0 protocol ip prio 1 u32 match ip dst $NET2 flowid 1:20
# }

TC=/sbin/tc
# change IF to whatever interface your server is using - ex em1, ens160, etc
IF=p1p1
RATE=990mbit
IP=0.0.0.0/0

start() {
  $TC qdisc add dev $IF handle 1: root htb
  $TC class add dev $IF parent 1: classid 1:1 htb rate $RATE
  $TC filter add dev $IF parent 1:0 protocol ip prio 1 u32 match ip dst $IP flowid 1:1
}

stop() {
  $TC qdisc del dev $IF root
}

restart() {
  stop
  sleep 1
  start
}

# show current statistics

# only used when manually running this file, not systemctl - ex /usr/local/shaper/traffic-shaper.sh show

show() {
  $TC -s qdisc ls dev $IF
  $TC -s class ls dev $IF
}

case "$1" in
  start)
    echo -n "Starting bandwidth shaper... "
    start
    echo -n "Started"
    ;;

  stop)
    echo -n "Stopping bandwidth shaper... "
    stop
    echo -n "Stopped"
    ;;

  show)
    show
    ;;

  *)

esac

exit 0

save and exit

start the service and enable at boot

systemctl daemon-reload
systemctl enable shaper.service
systemctl start shaper.service
systemctl status shaper.service