#!/usr/bin/env bash #set -ex; # Wonder Shaper # # Set the following values to somewhat less than your actual download # and uplink speed. In kilobits. Also set the device that is to be shaped. # # License GPLv2 # # Copyright (c) 2002-2020 Bert Hubert , # Jacco Geul , Simon Séhier , # corbolais@gmail.com # # See the ChangeLog for information on the individual contributions of the authors. # Warn when make sure the script run as sudo user # https://stackoverflow.com/a/21372328 if [[ $(id -u) -ne 0 ]]; then echo "Running as non root / sudo user" fi QUANTUM="6000"; VERSION="1.4.1"; CONF="/etc/systemd/wondershaper.conf"; CONFLEGACY="/etc/conf.d/wondershaper.conf"; # shellcheck disable=SC2086 eval export HIPRIODST=$HIPRIODST; # shellcheck disable=SC2086 eval export COMMONOPTIONS=$COMMONOPTIONS; # shellcheck disable=SC2086 eval export NOPRIOHOSTSRC=$NOPRIOHOSTSRC; # shellcheck disable=SC2086 eval export NOPRIOHOSTDST=$NOPRIOHOSTDST; # shellcheck disable=SC2086 eval export NOPRIOPORTSRC=$NOPRIOPORTSRC; # shellcheck disable=SC2086 eval export NOPRIOPORTDST=$NOPRIOPORTDST; usage() { cat << EOF USAGE: $0 [-hcs] [-a ] [-d ] [-u ] Limit the bandwidth of an adapter OPTIONS: -h Show this message -a Set the adapter -d Set maximum download rate (in Kbps) and/or -u Set maximum upload rate (in Kbps) -p Use presets in "$CONF" -f Use alternative preset file -c Clear the limits from adapter -s Show the current status of adapter -v Show the current version Configure HIPRIODST in "$CONF" for hosts requiring high priority i.e. in case ssh uses dport 443. MODES: wondershaper -a -d -u wondershaper -c -a wondershaper -s -a EXAMPLES: wondershaper -a eth0 -d 1024 -u 512 wondershaper -a eth0 -u 512 wondershaper -c -a eth0 wondershaper -p -f foo.conf EOF } DSPEED=""; USPEED=""; IFACE=""; IFB="ifb0"; LOAD_PRESETS=false MODE="" while getopts hvf:d:u:a:pcs o; do case "$o" in h) usage; exit 1;; v) echo "Version $VERSION"; exit 0;; f) CONF="$OPTARG";; d) DSPEED="$OPTARG";; u) USPEED="$OPTARG";; a) IFACE="$OPTARG";; p) LOAD_PRESETS=true;; c) MODE="clear";; s) MODE="status";; [?]) usage; exit 1;; esac; done; if [ "$LOAD_PRESETS" = true ]; then if [ -f "$CONF" ]; then # shellcheck disable=SC1090 source "$CONF"; elif [ -f "$CONFLEGACY" ]; then # shellcheck disable=SC1090 source "$CONFLEGACY"; else echo "$CONF not found"; exit 1; fi; fi; if [[ -z "$IFACE" ]]; then echo "Please supply the adapter name for the mode." echo ""; usage; exit 1; fi; if [ "$MODE" = "status" ]; then tc -s qdisc ls dev "$IFACE"; tc -s class ls dev "$IFACE"; exit; fi; if [ "$MODE" = "clear" ]; then tc qdisc del dev "$IFACE" root 2> /dev/null > /dev/null; tc qdisc del dev "$IFACE" ingress 2> /dev/null > /dev/null; tc qdisc del dev "$IFB" root 2> /dev/null > /dev/null; tc qdisc del dev "$IFB" ingress 2> /dev/null > /dev/null; exit; fi; if [[ -z "$DSPEED" ]] && [[ -z "$USPEED" ]]; then usage; exit 1; fi; ###### uplink # install root HTB tc qdisc add dev "$IFACE" root handle 1: htb default 20; # shape everything at $USPEED speed - this prevents huge queues in your # DSL modem which destroy latency: # main class if [[ -n "$USPEED" ]]; then tc class add dev "$IFACE" parent 1: classid 1:1 htb \ rate "${USPEED}kbit" \ prio 5 ${COMMONOPTIONS[@]}; # high prio class 1:10: RATE=$((20*${USPEED}/100)) if [ "$RATE" -eq 0 ]; then RATE=1 ; fi tc class add dev "$IFACE" parent 1:1 classid 1:10 htb \ rate "${RATE}kbit" ceil $((95*${USPEED}/100))kbit \ prio 1 ${COMMONOPTIONS[@]}; # bulk and default class 1:20 - gets slightly less traffic, # and a lower priority: RATE=$((40*${USPEED}/100)) if [ "$RATE" -eq 0 ]; then RATE=1 ; fi tc class add dev "$IFACE" parent 1:1 classid 1:20 htb \ rate "${RATE}kbit" ceil $((95*${USPEED}/100))kbit \ prio 2 ${COMMONOPTIONS[@]}; # 'traffic we hate' RATE=$((20*${USPEED}/100)) if [ "$RATE" -eq 0 ]; then RATE=1 ; fi tc class add dev "$IFACE" parent 1:1 classid 1:30 htb \ rate "${RATE}kbit" ceil $((90*${USPEED}/100))kbit \ prio 3 ${COMMONOPTIONS[@]}; # all get Stochastic Fairness: tc qdisc add dev "$IFACE" parent 1:10 handle 10: sfq perturb 10 quantum "$QUANTUM"; tc qdisc add dev "$IFACE" parent 1:20 handle 20: sfq perturb 10 quantum "$QUANTUM"; tc qdisc add dev "$IFACE" parent 1:30 handle 30: sfq perturb 10 quantum "$QUANTUM"; # start filters # TOS Minimum Delay (ssh, NOT scp) in 1:10: tc filter add dev "$IFACE" parent 1: protocol ip prio 10 u32 \ match ip tos 0x10 0xff flowid 1:10; for dst in "${HIPRIODST[@]}"; do [ -n "$dst" ] || continue echo "$dst"; tc filter add dev "$IFACE" parent 1: protocol ip prio 10 u32 \ match ip dst "$dst" flowid 1:10; done; # ICMP (ip protocol 1) in the interactive class 1:10 so we # can do measurements & impress our friends: tc filter add dev "$IFACE" parent 1: protocol ip prio 11 u32 \ match ip protocol 1 0xff flowid 1:10; # prioritize small packets (<64 bytes) tc filter add dev "$IFACE" parent 1: protocol ip prio 12 u32 \ match ip protocol 6 0xff \ match u8 0x05 0x0f at 0 \ match u16 0x0000 0xffc0 at 2 \ flowid 1:10; # some traffic however suffers a worse fate for dport in "${NOPRIOPORTDST[@]}"; do [ -n "$dport" ] || continue tc filter add dev "$IFACE" parent 1: protocol ip prio 14 u32 \ match ip dport "$dport" 0xffff flowid 1:30; done; for sport in "${NOPRIOPORTSRC[@]}"; do [ -n "$sport" ] || continue tc filter add dev "$IFACE" parent 1: protocol ip prio 15 u32 \ match ip sport "$sport" 0xffff flowid 1:30; done; for src in "${NOPRIOHOSTSRC[@]}"; do [ -n "$src" ] || continue tc filter add dev "$IFACE" parent 1: protocol ip prio 16 u32 \ match ip src "$src" flowid 1:30; done; for dst in "${NOPRIOHOSTDST[@]}"; do [ -n "$dst" ] || continue tc filter add dev "$IFACE" parent 1: protocol ip prio 17 u32 \ match ip dst "$dst" flowid 1:30; done; # rest is 'non-interactive' ie 'bulk' and ends up in 1:20 tc filter add dev "$IFACE" parent 1: protocol ip prio 18 u32 \ match ip dst 0.0.0.0/0 flowid 1:20; fi; ########## downlink ############# # slow downloads down to somewhat less than the real speed to prevent # queuing at our ISP. Tune to see how high you can set it. # ISPs tend to have *huge* queues to make sure big downloads are fast # # attach ingress policer: if [[ -n "$DSPEED" ]]; then # Add the IFB interface modprobe ifb numifbs=1; ip link set dev "$IFB" up; # Redirect ingress (incoming) to egress ifb0 tc qdisc add dev "$IFACE" handle ffff: ingress tc filter add dev "$IFACE" parent ffff: protocol ip u32 match u32 0 0 \ action mirred egress redirect dev "$IFB"; # Add class and rules for virtual tc qdisc add dev "$IFB" root handle 2: htb; tc class add dev "$IFB" parent 2: classid 2:1 htb rate "${DSPEED}kbit"; # Add filter to rule for IP address tc filter add dev "$IFB" protocol ip parent 2: prio 1 u32 \ match ip src 0.0.0.0/0 flowid 2:1; fi; ### EOF ### vim:tw=80:et:sts=2:st=2:sw=2:com+=b\:###:fo+=cqtrw:tags=tags: