#!/bin/bash
# Como la gente de "bufferbloat" ha recientemente re-descubierto y / o más ampliamente
# publicitado, los algoritmos para evitar la congestión (como las que se encuentran en TCP) hacen
# Un gran trabajo permitiendo a los puntos finales de la red negociar las tasas de transferencia que
# Maximizan el uso de ancho de banda de un enlace sin penalizar indebidamente ningún stream.
# Esto permite a los bulk-stream utilizar el máximo disponible de Ancho de banda
# sin afectar la latencia del non-bulk (por ejemplo stream interactivo)
#
# En otras palabras, TCP le permite tener su pastel y comérselo también - a la vez Descargas
# rápidas y baja latencia, todo al mismo tiempo.
# Sin embargo, esto sólo funciona si los Algoritmos para evitar la congestión de TCP
# antes mencionada realmente funcionan. El método más fiable de señalización de
# Congestión es descartar paquetes. (Hay otras maneras, tales como ECN, pero
# Lamentablemente todavía no están en amplio uso.)
# Descartar paquetes para que la red funcione mejor es un poco contra-intuitivo.
# Pero, así es como funciona TCP. Y si uno se aprovecha de eso, puede hacer que
# TCP funcione muy bien.
# Descartar paquetes llama rapidamente la atención de TCP. El criterio de valoración del envío
# es revisado para evitar una mayor congestión de la red. En otras palabras, si la
# Descarga rápida se ralentiza se baja la velocidad de envío y, siempre y cuando no haya más congestión,
# El que envia aumenta gradualmente la velocidad de transferencia repitiendo el ciclo.
# Se puede dar una explicación más compleja que esta, pero el Punto principal es:
# descartar paquetes cuando hay congestión es bueno.
# control de tráfico es reducir la velocidad y / o descartar (o marcado ECN) paquetes.
# La cosa es que, es mucho mejor para la latencia simplemente descartar paquetes
# que frenarlos. Linux tiene un par de cotroladores de tráfico que no dudan
# en descartar paquetes. Uno de los más conocido es TBF "Token Bucket Filter".
# Normalmente se ralentizan paquetes a una tasa específica. Pero también acepta una opción
# de "límite" para especificar el número máximo de paquetes de Cola. Cuando se supera
# el límite, los paquetes se descartan.
# El Algoritmo "tail-drop" de TBF es en realidad uno de los peores tipos de (AQM)
# "Gestión de cola activa" que se puede hacer. sin embargo, este puede hacer
# Una gran diferencia. Aplicando solo TBF (con un límite lo suficientemente corto) puede hacer un
# enlace de alta latencia utilizable de nuevo en poco tiempo.
# La gran desventaja de TBF es que es un shaper "sin clases". Eso significa que
# No se puede dar prioridad a un flujo TCP sobre otro. Ahí es donde entra HTB, la
# Hierarchical Token Bucket. HTB utiliza el mismo algoritmo general como
# TBF al mismo tiempo que le permite filtrar el tráfico específico de colas de prioridad.
# Pero HTB tiene una gran debilidad: no tiene una buena y fácil forma de especificar un
# límite de Cola como sí TBF. Eso significa que, en comparación con TBF, HTB opta por
# ralentizar paquetes en lugar de descartarlos. lo cual no es bueno para la latencia.
# Así que ahora llegamos al secreto mejor guardado de Linux controlando el tráfico: el shaper HFSC.
# HFSC significa "Curva jerárquica Justa de Servicio". La implementación en Linux es tremendamente
# compleja, lo suficiente como para tener una pregunta de 9 páginas en serverfault
# ( http://serverfault.com/questions/105014/does-anyone-really-understand-how-hfsc-scheduling-in-linux-bsd-works ).
# No obstante, HFSC puede entenderse de una manera simplificada como HTB con límites.
# HFSC le permite clasificar el tráfico (como HTB, a diferencia de TBF), pero también puede
# descartar paquetes (a diferencia de HTB, como TBF).
# HFSC hace un gran trabajo al mantener una baja latencia. Con ella, es posible
# Saturar totalmente un enlace mientras se mantiene una sesión de trafico interactivo .
# Es el santo grial de la modulación del tráfico, y esta en los kernel existentes.
# Para obtener los mejores resultados, HFSC debe combinarse con SFQ (Stochatic
# Fairness Queueing) y opcionalmente un filtro de entrada. Si los tres se utilizan,
# Es posible mantener sesiones interactivas de baja latencia, incluso sin ningúna
# Priorización de tráfico. Si además añadimos priorización entonces maximizamos la
# Interactividad.
# Here's how it's done:
# set this to your internet-facing network interface:
WAN_INTERFACE=eth1
# set this to your local network interface:
LAN_INTERFACE=eth0
# how fast is your downlink?
MAX_DOWNRATE=2550kbit
# how close should we get to max down? e.g. 90%
USE_DOWNPERCENT=0.90
# how fast is your uplink?
MAX_UPRATE=1792kbit
# how close should we get to max up? e.g. 80%
USE_UPPERCENT=0.80
# what port do you want to prioritize? e.g. for ssh, use 22
INTERACTIVE_PORT=22
## now for the magic
# remove any existing qdiscs
/sbin/tc qdisc del dev $WAN_INTERFACE root 2> /dev/null
/sbin/tc qdisc del dev $WAN_INTERFACE ingress 2> /dev/null
/sbin/tc qdisc del dev $LAN_INTERFACE root 2> /dev/null
/sbin/tc qdisc del dev $LAN_INTERFACE ingress 2> /dev/null
# computations
MAX_UPNUM=`echo $MAX_UPRATE | sed 's/[^0-9]//g'`
MAX_UPBASE=`echo $MAX_UPRATE | sed 's/[0-9]//g'`
MAX_DOWNNUM=`echo $MAX_DOWNRATE | sed 's/[^0-9]//g'`
MAX_DOWNBASE=`echo $MAX_DOWNRATE | sed 's/[0-9]//g'`
NEAR_MAX_UPNUM=`echo "$MAX_UPNUM * $USE_UPPERCENT" | bc | xargs printf "%.0f"`
NEAR_MAX_UPRATE="${NEAR_MAX_UPNUM}${MAX_UPBASE}"
NEAR_MAX_DOWNNUM=`echo "$MAX_DOWNNUM * $USE_DOWNPERCENT" | bc | xargs printf "%.0f"`
NEAR_MAX_DOWNRATE="${NEAR_MAX_DOWNNUM}${MAX_DOWNBASE}"
HALF_MAXUPNUM=$(( $MAX_UPNUM / 2 ))
HALF_MAXUP="${HALF_MAXUPNUM}${MAX_UPBASE}"
HALF_MAXDOWNNUM=$(( $MAX_DOWNNUM / 2 ))
HALF_MAXDOWN="${HALF_MAXDOWNNUM}${MAX_DOWNBASE}"
# install HFSC under WAN to limit upload
/sbin/tc qdisc add dev $WAN_INTERFACE root handle 1: hfsc default 11
/sbin/tc class add dev $WAN_INTERFACE parent 1: classid 1:1 hfsc sc rate $NEAR_MAX_UPRATE ul rate $NEAR_MAX_UPRATE
/sbin/tc class add dev $WAN_INTERFACE parent 1:1 classid 1:10 hfsc sc umax 1540 dmax 5ms rate $HALF_MAXUP ul rate $NEAR_MAX_UPRATE
/sbin/tc class add dev $WAN_INTERFACE parent 1:1 classid 1:11 hfsc sc umax 1540 dmax 5ms rate $HALF_MAXUP ul rate $HALF_MAXUP
# prioritize interactive ports
/sbin/tc filter add dev $WAN_INTERFACE protocol ip parent 1:0 prio 1 u32 match ip sport $INTERACTIVE_PORT 0xffff flowid 1:10
/sbin/tc filter add dev $WAN_INTERFACE protocol ip parent 1:0 prio 1 u32 match ip dport $INTERACTIVE_PORT 0xffff flowid 1:10
# add SFQ
/sbin/tc qdisc add dev $WAN_INTERFACE parent 1:10 handle 30: sfq perturb 10
/sbin/tc qdisc add dev $WAN_INTERFACE parent 1:11 handle 40: sfq perturb 10
# install ingress filter to limit download to 97% max
MAX_DOWNRATE_INGRESSNUM=`echo "$MAX_DOWNNUM * 0.97" | bc | xargs printf "%.0f"`
MAX_DOWNRATE_INGRESS="${MAX_DOWNRATE_INGRESSNUM}${MAX_DOWNBASE}"
/sbin/tc qdisc add dev $WAN_INTERFACE handle ffff: ingress
/sbin/tc filter add dev $WAN_INTERFACE parent ffff: protocol ip prio 1 u32 match ip sport $INTERACTIVE_PORT 0xffff flowid :1
/sbin/tc filter add dev $WAN_INTERFACE parent ffff: protocol ip prio 1 u32 match ip dport $INTERACTIVE_PORT 0xffff flowid :1
/sbin/tc filter add dev $WAN_INTERFACE parent ffff: protocol ip prio 50 u32 match ip src 0.0.0.0/0 police rate $MAX_DOWNRATE_INGRESS burst 20k drop flowid :2
# install HFSC under LAN to limit download
/sbin/tc qdisc add dev $LAN_INTERFACE root handle 1: hfsc default 11
/sbin/tc class add dev $LAN_INTERFACE parent 1: classid 1:1 hfsc sc rate 1000mbit ul rate 1000mbit
/sbin/tc class add dev $LAN_INTERFACE parent 1:1 classid 1:10 hfsc sc umax 1540 dmax 5ms rate 900mbit ul rate 900mbit
/sbin/tc class add dev $LAN_INTERFACE parent 1:1 classid 1:11 hfsc sc umax 1540 dmax 5ms rate $HALF_MAXDOWN ul rate $NEAR_MAX_DOWNRATE
# prioritize interactive ports
/sbin/tc filter add dev $LAN_INTERFACE protocol ip parent 1:0 prio 1 u32 match ip sport $INTERACTIVE_PORT 0xffff flowid 1:10
/sbin/tc filter add dev $LAN_INTERFACE protocol ip parent 1:0 prio 1 u32 match ip dport $INTERACTIVE_PORT 0xffff flowid 1:10
# add SFQ
/sbin/tc qdisc add dev $LAN_INTERFACE parent 1:10 handle 30: sfq perturb 10
/sbin/tc qdisc add dev $LAN_INTERFACE parent 1:11 handle 40: sfq perturb 10
### End of Script
Nota:
podemos chequear las estadísticas del queue con:
watch -d -- '/sbin/tc -s qdisc'