Distribución del ancho de banda utilizando HTB e IPTables

Autor: Luis Vargas Castro
Correo: luisvargascastro arroba gmail punto com

Creative Commons Reconocimiento-NoComercial-CompartirIgual 2.1

© 2006 Usted es libre de copiar, distribuir y comunicar públicamente la obra y hacer obras derivadas bajo las condiciones siguientes: a) Debe reconocer y citar al autor original. b) No puede utilizar esta obra para fines comerciales (incluyendo su publicación, a través de cualquier medio, por entidades con fines de lucro). c) Si altera o transforma esta obra, o genera una obra derivada, sólo puede distribuir la obra generada bajo una licencia idéntica a ésta. Al reutilizar o distribuir la obra, tiene que dejar bien claro los términos de la licencia de esta obra. Alguna de estas condiciones puede no aplicarse si se obtiene el permiso del titular de los derechos de autor. Los derechos derivados de usos legítimos u otras limitaciones no se ven afectados por lo anterior. Licencia completa en castellano. La información contenida en este documento y los derivados de éste se proporcionan tal cual son y los autores no asumirán responsabilidad alguna si el usuario o lector hace mal uso de éstos.

Introducción.

En muchas ocasiones los administradores de red experimentan cuellos de botella prolongados debido al mal uso que algunos usuarios dan del acceso a internet. Gran parte de esos abusos son llevados a cabo por las descargas que se realizan ya sea música, video o software através de programas P2P. Si bien existen alternativas para limitar el ancho de banda como los Delay Pools de Squid, pero solo funciona para limitar el ancho de banda de aquellas descargas vía HTTP y para el FTP.

 

Para implementar una solución que permita distribuir al ancho de banda y la tasa de transferencia de una forma más justa que incluyan no solo a HTTP y FTP sino inclusive a los programas P2P existen herramientas como CBQ y HTB que realizan la tarea.

En este documento se presenta una guía básica y funcional para la correcta implementación de HTB+Iptables para una distribución justa del ancho de banda.

Hierarchical Token Bucket (HTB)

Para poder particionar el acceso a internet, HTB se vale de clases. Estas clases son instrucciones jerárquicas que indican cómo debe manejarse el canal y qué prioridad tiene una clase ante otra tomando el principio de colas. El primer paso es configurar la disciplina de encolamiento de HTB:

[root@lanserver]#tc qdisc add dev eth0 root handle 1: htb default 13

Este comando crea la disciplina de encolamienta raíz de HTB en la interfaz eth0 que en este caso, es la interfaz del servidor GNU/Linux que conecta hacia la LAN. Puede sustituirse por la interfaz que en su caso particular sea la interfaz hacia la LAN. Se indica que la disciplina 1:13 será la disciplina por defecto en tanto se indican más cambios.

[root@lanserver]#tc class add dev eth0 parent 1: classid 1:1 htb rate 140kbit ceil 140kbit

Esta instrucción indica la tasa máxima con la que la disciplina raíz cuenta para la división del canal. El parámetro rate establecerá el ancho de banda inicial permitido para la disciplina de encolamiento en cuestión y el parámetro ceil indica el ancho de banda máximo.

[root@lanserver]#tc class add dev eth0 parent 1:1 classid 1:10 htb rate 80kbit ceil 140kbit prio 0

Con esta instrucción se crea la clase 1:10 y se indica utilizar un ancho de banda incial de 80kbit y un máximo de 140kbit cuando el canal no está en uso.

tc class add dev eth0 parent 1:1 classid 1:11 htb rate 20kbit ceil ${CEIL}kbit prio 1
tc class add dev eth0 parent 1:1 classid 1:12 htb rate 20kbit ceil ${CEIL}kbit prio 2
tc class add dev eth0 parent 1:1 classid 1:13 htb rate 20kbit ceil ${CEIL}kbit prio 3

Lo anterior crea tres clases 1:11, 1:12 y 1:13. Estas clases utilizarán un ancho de banda incial de 20kbit cuando el canal está en uso y del máximo disponible del canal, 140kbit, cuando no exista carga en la red al igual que la clase anterior 1:10.

[root@lanserver]#tc qdisc add dev eth0 parent 1:11 handle 120: sfq perturb 10
[root@lanserver]#tc qdisc add dev eth0 parent 1:12 handle 130: sfq perturb 10
[root@lanserver]#tc qdisc add dev eth0 parent 1:13 handle 140: sfq perturb 10

Lo anterior añade 3 clases hijas a cada una de las clases padre creadas anteriormente: 1:11, 1:12 y 1:13 que permitirán que se distribuya de forma justa la oportunidad de transmitir datos hacia la red basándose en un número arbitrario de flujos. Esta funcionalidad está basada en el encolamiento estocástico justo (Stochastic Fair Queuing) SFQ.

Note el parámetro sfq perturb 10. Esto le indica a la disciplina de encolamiento de quien la instrucción es hija, que implemente una interrupción o perturbación de la transmisión de 10 segundos. En la medida que sea necesario reajuste este parámetro siempre y cuando no vaya en detrimendo de la transmisión de datos que enrute hacia esta disciplina.

Clasificación de paquetes

Hasta ahora se ha creado la configuración inicial de qdisc pero no se han clasificado los paquetes hacia las distintas rutas. Para hacerlo utilizaremos IPTables y clasificaremos los paquetes hacia las distintas clases creadas anteriormente.

tc filter add dev eth0 parent 1:0 protocol ip prio 1 handle 1 fw classid 1:10
tc filter add dev eth0 parent 1:0 protocol ip prio 2 handle 2 fw classid 1:11
tc filter add dev eth0 parent 1:0 protocol ip prio 3 handle 3 fw classid 1:12

Con lo anterior le hemos indicado al kernel que los paquetes que tienen una marca específica con el valor fwmark con los parámetros handle x fw vayan hacia clases específicas classid x:xx.

Debemos indicarle al kernel que debe realizar el reenvío de paquetes por las interfaces de red utilizadas para este caso. Edite el archivo /etc/sysconfig.ctl en el parámetro ipv4_forward=0 y cámbielo a 1:

ipv4_forward=1

Ahora debería poder ver el tráfico fluyendo através de la clase 1:13 con el siguiente comando:

[root@lanserver]#tc -s class show dev eth0

IPTables

Ahora puede empezar añadir reglas de clasificación a iptables ya sea por protocolo o bien por puerto según

[root@lanserver]#iptables -t mangle -A PREROUTING -p icmp -j MARK --set-mark 0x1
[root@lanserver]#iptables -t mangle -A PREROUTING -p icmp -j RETURN

o bien por puerto dependiendo lo que necesita:

[root@lanserver]#iptables -t mangle -A PREROUTING -p tcp -m tcp --sport 22 -j MARK --set-mark 0x1
[root@lanserver]#iptables -t mangle -A PREROUTING -p tcp -m tcp --sport 22 -j RETURN

observe el parámetro -j MARK –set-mark 0x1 y -j RETURN. Estos parámetros se encargan del marcado de paquetes según la prioridad; el primer parámetro indica el número de prioridad en forma hexadecimal 0xX desde 0 hasta 9 donde 0 es el indicador de mayor prioridad o más importancia, y 9 el de menor importancia.

Una buena práctica es priorizar con marca 0x1 todos aquellos paquetes propios de la comunicación de las redes LAN: SYN, ACK, RST pues un retardo en estos paquetes significaría un desastre de comunicaciones en la red:

iptables -t mangle -A PREROUTING -m tos --tos Minimize-Delay -j MARK --set-mark 0x1
iptables -t mangle -A PREROUTING -m tos --tos Minimize-Delay -j RETURN
iptables -t mangle -A PREROUTING -m tos --tos Minimize-Cost -j MARK --set-mark 0x5
iptables -t mangle -A PREROUTING -m tos --tos Minimize-Cost -j RETURN
iptables -t mangle -A PREROUTING -m tos --tos Maximize-Throughput -j MARK --set-mark 0x6
iptables -t mangle -A PREROUTING -m tos --tos Maximize-Throughput -j RETURN

Con lo anterior indicamos a iptables 3 tipos de T.O.S. y le decimos con qué número de marcado identificarlos. Minimize-Delay, Minimize-Cost y Maximize-Troughput marcándolos con 0x1, 0x5 y 0x6 respectivamente. Es imporante mencionar que no existen solo estos T.O.S. y que es imporante introducir las reglas pertinentes según sea necesario por parte del administrador de la red. Para motivos de explicación de este manual y del posterior ejemplo éstas reglas son las necesarias.

Es una buena práctica repetir las instrucciones creadas para iptables con PREROUTING con el parámetro OUTPUT de modo que el tráfico generado localmente también sea clasificado. Puede terminarse cada sentencia de -A OUTPUT con -j MARK –set-mark 0x3 de modo que el tráfico local tenga prioridad.

Poniendo la teoría en práctica

Ejemplo. Una red local que cuenta con 15 PCs con acceso a internet sale por medio de un servidor GNU/Linux através de una línea ADSL de 1024Kbps con 140Kbps de tasa de transferencia; las aplicaciones habituales del internet en la LAN son correo electrónico, chat, mensajería, FTP, HTTP, y programas P2P. Usualmente los programas P2P consumen casi todo el ancho de banda de la red. Se desea distribuir de forma justa el ancho de banda de modo que todo funcione bien y no hayan cuellos de botella debido en especial a los programas P2P.

Creamos un script que configure a HTB y le damos permisos de ejecución para root. edite con su editor de textos preferido el nuevo que llamaremos bwtc y ubíquelo en /etc/:

[root@lanserver]#gedit /etc/bwtc

añada el siguiente contenido:

CEIL=140
tc qdisc add dev eth0 root handle 1: htb default 13
tc class add dev eth0 parent 1: classid 1:1 htb rate ${CEIL}kbit ceil ${CEIL}kbit
tc class add dev eth0 parent 1:1 classid 1:10 htb rate 80kbit ceil ${CEIL}kbit prio 0
tc class add dev eth0 parent 1:1 classid 1:11 htb rate 20kbit ceil ${CEIL}kbit prio 1
tc class add dev eth0 parent 1:1 classid 1:12 htb rate 20kbit ceil ${CEIL}kbit prio 2
tc class add dev eth0 parent 1:1 classid 1:13 htb rate 20kbit ceil ${CEIL}kbit prio 3
tc qdisc add dev eth0 parent 1:11 handle 120: sfq perturb 10
tc qdisc add dev eth0 parent 1:12 handle 130: sfq perturb 10
tc qdisc add dev eth0 parent 1:13 handle 140: sfq perturb 10
tc filter add dev eth0 parent 1:0 protocol ip prio 1 handle 1 fw classid 1:10
tc filter add dev eth0 parent 1:0 protocol ip prio 2 handle 2 fw classid 1:11
tc filter add dev eth0 parent 1:0 protocol ip prio 3 handle 3 fw classid 1:12
tc filter add dev eth0 parent 1:0 protocol ip prio 4 handle 4 fw classid 1:13

edite /etc/sysctl.conf y cambie el parámetro ipv4_forward=0 a 1:

ipv4_forward=1

Configuración de IPTables para marcado de paquetes

Agregue las reglas necesarias a IPTables de la siguiente manera: (puede hacerlo directamente en consola con los comandos siguientes o bien editando el archivo /etc/sysconfig/iptables y agregando las reglas en la sección mangle, claro sin el prefijo iptables -t mangle) :

iptables -t mangle -A PREROUTING -p tcp -m tcp --tcp-flags SYN,RST,ACK SYN -j RETURNiptables -t mangle -A PREROUTING -p tcp -m tcp --tcp-flags SYN,RST,ACK SYN -j MARK --set-mark 0x1iptables -t mangle -A PREROUTING -p tcp -m tcp --sport 3128 -j MARK --set-mark 0x1iptables -t mangle -A PREROUTING -p tcp -m tcp --sport 3128 -j RETURN
iptables -t mangle -A PREROUTING -p tcp -m tcp --sport 80 -j MARK --set-mark 0x1

iptables -t mangle -A PREROUTING -p tcp -m tcp --sport 80 -j RETURN

iptables -t mangle -A PREROUTING -p tcp -m tcp --sport 25 -j MARK --set-mark 0x3iptables -t mangle -A PREROUTING -p tcp -m tcp --sport 25 -j RETURN

iptables -t mangle -A PREROUTING -p tcp -m tcp --sport 110 -j MARK --set-mark 0x3iptables -t mangle -A PREROUTING -p tcp -m tcp --sport 110 -j RETURN

iptables -t mangle -A PREROUTING -p icmp -j MARK --set-mark 0x1iptables -t mangle -A PREROUTING -p icmp -j RETURN
iptables -t mangle -A PREROUTING -m tos --tos Minimize-Delay -j MARK --set-mark 0x1

iptables -t mangle -A PREROUTING -m tos --tos Minimize-Delay -j RETURNiptables -t mangle -A PREROUTING -m tos --tos Minimize-Cost -j MARK --set-mark 0x5

iptables -t mangle -A PREROUTING -m tos --tos Minimize-Cost -j RETURNiptables -t mangle -A OUTPUT -p tcp -m tcp --tcp-flags SYN,RST,ACK SYN -j MARK --set-mark 0x1

iptables -t mangle -A OUTPUT -p tcp -m tcp --tcp-flags SYN,RST,ACK SYN -j RETURNiptables -t mangle -A OUTPUT -p tcp -m tcp --sport 3128 -j MARK --set-mark 0x1

iptables -t mangle -A OUTPUT -p tcp -m tcp --sport 3128 -j RETURNiptables -t mangle -A OUTPUT -p tcp -m tcp --sport 80 -j MARK --set-mark 0x1

iptables -t mangle -A OUTPUT -p tcp -m tcp --sport 80 -j RETURNiptables -t mangle -A OUTPUT -p tcp -m tcp --sport 25 -j MARK --set-mark 0x1

iptables -t mangle -A OUTPUT -p tcp -m tcp --sport 25 -j RETURNiptables -t mangle -A OUTPUT -p tcp -m tcp --sport 110 -j MARK --set-mark 0x1

iptables -t mangle -A OUTPUT -p tcp -m tcp --sport 110 -j RETURNiptables -t mangle -A OUTPUT -p icmp -j MARK --set-mark 0x1

iptables -t mangle -A OUTPUT -p icmp -j RETURN
iptables -t mangle -A OUTPUT -m tos --tos Minimize-Delay -j MARK --set-mark 0x1iptables -t mangle -A OUTPUT -m tos --tos Minimize-Delay -j RETURN

iptables -t mangle -A OUTPUT -m tos --tos Minimize-Cost -j MARK --set-mark 0x5iptables -t mangle -A OUTPUT -m tos --tos Minimize-Cost -j RETURN

iptables -t mangle -A OUTPUT -m tos --tos Maximize-Throughput -j MARK --set-mark 0x6
iptables -t mangle -A OUTPUT -m tos --tos Maximize-Throughput -j RETURN

Ejecute el comando:

service iptables save

en caso que haya ingresado las reglas para iptables en la terminal o bien guarde el archivo si lo editó con su editor de textos preferido. Reinicie el servicio iptables con el comando

service iptables restart

Dé privilegios de ejecución para el archivo /etc/bwtc:

[root@lanserver]#chmod +x /etc/bwtc

ejecute el archivo de configuración de HTB (/etc/bwtc):

[root@lanserver]#./etc/bwtc

y por último, monitoree el tráfico de red:

[root@lanserver]#tc -s class show dev eth0