Skip to content

Instantly share code, notes, and snippets.

@joshrosso
Created March 28, 2020 01:22
Show Gist options
  • Save joshrosso/16ec07cea7483d1e6a277ec5b3bc4cdf to your computer and use it in GitHub Desktop.
Save joshrosso/16ec07cea7483d1e6a277ec5b3bc4cdf to your computer and use it in GitHub Desktop.
#!/bin/bash
# This script using tc (`man tc`) to shape egress traffic from a host to specific
# IP CIDR(s).
# RATES Bandwidths or rates. These parameters accept a floating point
# number, possibly followed by either a unit (both SI and IEC units supported),
# or a float followed by a '%' character to specify the rate as a percentage of
# the device's speed (e.g. 5%, 99.5%). Warning: specifying the rate as
# a percentage means a fraction of the current speed; if the speed changes, the
# value will not be recalculated.
#
# default Bits per second
#
# kbit Kilobits per second
#
# mbit Megabits per second
#
# gbit Gigabits per second
#
# tbit Terabits per second
#
# bps Bytes per second
#
# kbps Kilobytes per second
#
# mbps Megabytes per second
#
# gbps Gigabytes per second
#
# tbps Terabytes per second
#
#
# Location of tc command
TC=/sbin/tc
# Network Interface (`ip a`) to shape.
IF=ens160
# Egress limit
PARENT_LIMIT=300kbit
BASE=10kbit
CEIL=300kbit
# CIDR ranges
# There should be 1 per target edge network
EDGE_0=192.168.201.1/32 # Edge Site 0 Description
EDGE_1=192.168.201.2/32 # Edge Site 1 Description
EDGE_2=192.168.201.3/32 # Edge Site 2 Description
# Filter options for limiting the intended interface.
U32="$TC filter add dev $IF protocol ip parent 1:0 prio 1 u32"
create () {
echo "===> Setting up shaping"
# Hierarchical Token Bucket (HTB) is used as it provides bandwidth-based shaping
# HTB The Hierarchy Token Bucket implements a rich linksharing
# hierarchy of classes with an emphasis on conforming to existing
# practices. HTB facilitates guaranteeing bandwidth to classes, while
# also allowing specification of upper limits to inter-class sharing. It
# contains shaping elements, based on TBF and can prioritize classes.
# root qdisc
$TC qdisc add dev $IF root handle 1: htb default 30
echo "===> Setup root qdisc on $IF"
# classes
# shapers of bandwidth
#
# These include parent and leaf (child) classes. A parent class can specify a limit
# of bandwidth that can be borrowed by all children as they speed up from
# their rate ($BASE) to their ceiling ($CEIL).
#
# root 1: # root qdisc
# |
# /
# 1:1 (parent class (overall QoS))
# / \
# 1:10 1:20 (leaf classes (individual QoS))
#
# In the above, a root qdisc (1:) is attached to an interface. Since rate limits
# are not set on root, a new class is instantiated (1:1). This class is the
# parent of all subsequent classes (1:10 and 1:20).
#
# With a rate on the parent (1:1) of 300mbits (the ceiling is the rate unless
# otherwise specified). Each leaf can set their limit up to 300mbits. Each
# child's rate ($BASE) is the starting rate. If 1:10 and 1:20 are transmitting
# at maximum speed, assuming their ceilings are set to 300mbits, they will
# each be throttled around 150mbits, since 150mbits * 2 = 300mbits.
#
#
# parent class; it's ceiling (set implicitly by rate) is shared across leaves (children)
$TC class add dev $IF parent 1: classid 1:1 htb rate $PARENT_LIMIT # parent
# leaf classes; 1 per target CIDR.
# ceil is the max upload speed this class should ever transmit.
# rate is the starting upload speed; it should be much slower than ceiling
$TC class add dev $IF parent 1:1 classid 1:10 htb rate $BASE ceil $CEIL # leaf
$TC class add dev $IF parent 1:1 classid 1:20 htb rate $BASE ceil $CEIL # leaf
$TC class add dev $IF parent 1:1 classid 1:30 htb rate $BASE ceil $CEIL # leaf
echo "===> Setup child qdisc(s) on $IF"
# filters for child classes
# maps a destination IP with the appropriate (bandwidth limiting) class
# flowid should map to a classid defined above
$U32 match ip dst $EDGE_0 flowid 1:10
$U32 match ip dst $EDGE_1 flowid 1:20
$U32 match ip dst $EDGE_2 flowid 1:30
echo "===> Setup filters"
echo "===> Shaping configuration complete"
}
# clean removes *all* existing qdiscs from the target interface
clean () {
echo "===> Deleting existing qdiscs on $IF"
$TC qdisc del dev $IF root
echo "===> Deleted existing qdiscs on $IF"
}
# run script; always clean then create
clean
create
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment