Created
April 4, 2019 18:20
-
-
Save mikkorantalainen/a38908e62d9618e8a4982af901b97b84 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# Traffic shaping script (AQM, fq_codel+tbf) | |
# Copyright 2018 Mikko Rantalainen <[email protected]> | |
# License: MIT (X11) | |
# Usage: | |
# 21/0.8 Mbps connection (ADSL2): DOWNLINK_RATE=21.7Mbit UPLINK_RATE=0.8Mbit TBF_LATENCY=500ms bin/traffic-shaping start | |
# 100/100 Mbps connection: ./traffic-shaping | |
# 1/1 GBps connection: DOWNLINK_RATE=1Gbit UPLINK_RATE=1Gbit TBF_LATENCY=10ms bin/traffic-shaping start | |
# Note that using low TBF_LATENCY will require powerful CPU. | |
# | |
# See also: https://www.bufferbloat.net/projects/codel/wiki/Best_practices_for_benchmarking_Codel_and_FQ_Codel/ | |
# See also: http://www.jfcarter.net/~jimc/documents/voip-qos-1609.html | |
# TODO: man 7 tc-hfcs (instead of tbf) | |
# TODO: try to limit bandwidth using fq_codel only (get rid of tbf) - https://gist.github.com/eqhmcow/939373/8d2e8ad745a7e0a8ddb21abde42538034c2ea65b | |
# | |
set -e # abort if a command returns non-zero status (failed) | |
#set -x # verbose execution | |
DEV="${DEV:=$(ip route | grep "^default " | grep -Po "(?<=dev )[^ ]+" | uniq)}" | |
# ingress: | |
DOWNLINK_RATE="${DOWNLINK_RATE:=106000kbit}" # or e.g. "21.5Mbit" | |
# egress: | |
UPLINK_RATE="${UPLINK_RATE:=101000kbit}" | |
CODEL_INTERVAL="${CODEL_INTERVAL:=100ms}" # usually 100ms, high speed links with low latency may need lower values | |
CODEL_TARGET="${CODEL_TARGET:=5ms}" # unit "us" is also available, usually 5%-10% of CODEL_INTERVAL | |
CODEL_LIMIT="${CODEL_LIMIT:=1001}" # decrease to reduce latency, too low values will limit throughput | |
CODEL_FLOWS="${CODEL_FLOWS:=1024}" | |
# set burst as high as possible without causing dropped packets at the start of the connections | |
DOWNLINK_BURST="${DOWNLINK_BURST:=6500}" | |
UPLINK_BURST="${UPLINK_BURST:=6500}" | |
TBF_LATENCY="${TBF_LATENCY:=14ms}" # set to lower latency to improve control over bandwidth limiting, UPLINK_BURST bytes must be able to be sent in this time | |
IFB="$DEV.ingress" | |
INITCWND="${INITCWND:=15}" # initial congestion window, decrease if packet loss is seen | |
INITRWND="${INITRWND:=40}" # initial receiving window (advertised from client to servers), can be safely pretty high if you have lots of bandwidth (Windows and OS X have this near 40) | |
# See also: https://www.cdnplanet.com/blog/tune-tcp-initcwnd-for-optimum-performance/ | |
# See also: https://www.acc.umu.se/~maswan/linux-netperf.txt | |
# See also: http://intronetworks.cs.luc.edu/1/html/newtcps.html | |
# See also: https://www.ietf.org/proceedings/84/slides/slides-84-iccrg-1.pdf | |
configure_shaping() | |
{ | |
# EGRESS (outgoing traffic, "uploads"): | |
# setup bandwidth limiting: | |
tc qdisc add dev "$DEV" root handle 1: tbf rate "$UPLINK_RATE" burst "$UPLINK_BURST" latency "$TBF_LATENCY" | |
# setup fq_codel for bandwidth shaping | |
tc qdisc add dev "$DEV" parent 1: fq_codel quantum 300 limit "$CODEL_LIMIT" target "$CODEL_TARGET" interval "$CODEL_INTERVAL" flows "$CODEL_FLOWS" noecn | |
# INGRESS (incoming traffic, "downloads"): | |
# setup bandwidth limiting (ingress limiting needs IFB or Intermediate Functional Block, see https://wiki.linuxfoundation.org/networking/ifb): | |
tc qdisc add dev "$DEV" handle ffff: ingress | |
ip link add name "$IFB" type ifb | |
tc qdisc add dev "$IFB" root handle 1: tbf rate "$DOWNLINK_RATE" burst "$DOWNLINK_BURST" latency "$TBF_LATENCY" | |
# setup fq_codel for bandwidth shaping | |
tc qdisc add dev "$IFB" parent 1: fq_codel quantum 300 limit "$CODEL_LIMIT" target "$CODEL_TARGET" interval "$CODEL_INTERVAL" flows "$CODEL_FLOWS" ecn | |
ip link set dev "$IFB" up | |
# connect ingress filtering to actual WAN device | |
tc filter add dev "$DEV" parent ffff: protocol all prio 10 u32 match u32 0 0 flowid 1:1 action mirred egress redirect dev "$IFB" | |
# configure initcwnd and initrwnd | |
ip route change $(ip route | grep ^default) initcwnd "$INITCWND" initrwnd "$INITRWND" | |
## configure CDG congestion control algorithm | |
##modprobe tcp_cdg && echo cdg > /proc/sys/net/ipv4/tcp_congestion_control | |
# cubic seems to be better overall with AQM, let's tune it | |
echo cubic > /proc/sys/net/ipv4/tcp_congestion_control || true | |
echo 13 > /sys/module/tcp_cubic/parameters/hystart_low_window | |
echo 0 > /proc/sys/net/ipv4/tcp_slow_start_after_idle | |
# TODO: try modprobe tcp_westwood | |
} | |
remove_shaping() | |
{ | |
#set -x | |
tc qdisc list | grep -q "ingress" && tc qdisc del dev "$DEV" ingress || true | |
# Note: we need to avoid removing root qdisc in case this kernel defaults to fq_codel, "qdisc list" will output "fq_codel 0:" for root qdisc so we look for something different | |
tc qdisc list | grep -q "fq_codel [1-9]" && tc qdisc del dev "$DEV" root || true | |
ip link show | grep -q "$IFB" && ip link del "$IFB" || true | |
# configure CDG congestion control algorithm | |
modprobe tcp_cdg && echo cdg > /proc/sys/net/ipv4/tcp_congestion_control || true | |
#set +x | |
} | |
status() | |
{ | |
echo "─── queue discipline configuration: ──────────────────" | |
tc qdisc list | |
echo " TIP: use e.g. 'sudo tc qdisc del dev $DEV ingress' to remove ingress filtering" | |
echo " TIP: use e.g. 'sudo tc qdisc del dev $DEV root' to remove egress filtering" | |
echo "─── ip link show: ────────────────────────────────────" | |
ip link show | |
echo " TIP: use e.g. 'sudo ip link del $IFB' to remove ingress device" | |
} | |
color_status() | |
{ | |
status | grep --color=auto -E "^|$DEV|$IFB|rate [^ ]+" | |
} | |
# handle parameters | |
ACTION="$1" | |
shift || true | |
while [ ! -z "$1" ] | |
do | |
case "$1" in | |
-v|--verbose) | |
echo "Device: $DEV" | |
echo "Downlink rate (ingress): $DOWNLINK_RATE" | |
echo "Uplink rate (egress): $UPLINK_RATE" | |
set -x | |
;; | |
*) | |
if [ ! -z "$2" ]; then | |
echo "Unknown parameter: '$2'" 1>&2 | |
exit 1 | |
fi | |
;; | |
esac | |
shift || true | |
done | |
case "$ACTION" in | |
start) | |
remove_shaping | |
configure_shaping | |
;; | |
stop) | |
remove_shaping | |
;; | |
status) | |
color_status | |
;; | |
restart) | |
remove_shaping | |
configure_shaping | |
;; | |
*) | |
echo "Unknown action: $1" 1>&2 | |
echo "Usage: $0 <start|stop|restart|status> [--verbose|-v]" 1>&2 | |
exit 1 | |
esac |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
See also: https://youtu.be/quAaZKBHvs8
See also: man tc-sfq