9.5. Disciplinas de cola con clases

Las qdisc con clases son muy útiles si tiene diferentes tipos de tráfico a los que quiere dar un tratamiento separado. Una de las qdisc con clases se denomina «CBQ», «Class Based Queueing», y se la menciona tan frecuentemente que la gente suele identificar el encolado con clases sólo con CBQ, pero éste no es el caso.

CBQ es meramente la que lleva más tiempo (y también la más compleja). No siempre es la que necesita. Esto puede ser un trauma para los partidarios del «efecto sendmail», que nos enseña que cualquier tecnología compleja que no viene documentada debe ser lo mejor que hay disponible.

En breve diremos más sobre CBQ y sus alternativas.

9.5.1. El flujo dentro de las qdisc con clases y sus clases

Cuando entra tráfico dentro de una qdisc con clases, hay que enviarlo a alguna de las clases que contiene (se necesita «clasificarlo»). Para determinar qué hay que hacer con un paquete, se consulta a los «filtros». Es importante saber que los filtros se llaman desde dentro de una qdisc, ¡y no al revés!

Los filtros asociados a esa qdisc devuelven entonces una decisión, y la qdisc la usa para encolar el paquete en una de las clases. Cada subclase puede probar otros filtros para ver si se imparten más instrucciones. En caso contrario, la clase encola el paquete en la qdisc que contiene.

Aparte de contener otras qdisc, la mayoría de las qdisc con clases también realizan «shaping». Esto es útil tanto para reordenar paquetes (con SFQ, por ejemplo) como para controlar tasas. Necesitará esto en caso de tener una interfaz de gran velocidad (por ejemplo, ethernet) enviando a un dispositivo más lento (un cable módem).

Si sólo fuera a usar SFQ, no debería pasar nada, ya que los paquetes entrarían y saldrían de su router sin retrasos: la interfaz de salida es mucho más rápida que la velocidad del enlace en sí. No habrá cola que reordenar.

9.5.2. La familia qdisc: raíces, controladores, hermanos y padres

Cada interfaz tiene una «qdisc raíz» de salida, que por defecto es la disciplina de colas pfifo_fast sin clases que mencionamos anteriormente. A cada qdisc se le puede asignar un controlador (handle), que puede usar en posteriores sentencias de configuración para referirse a la qdisc. Aparte de la qdisc de salida, la interfaz también puede tener una de entrada, que dicta las normas sobre el tráfico que entra.

Los controladores de estas qdisc consisten en dos partes, un número mayor y un número menor. Es habitual darle a la qdisc de raíz el nombre «1:», que es lo mismo que «1:0». El número menor de una qdisc siempre es 0.

Las clases deben tener el mismo número mayor que sus padres.

9.5.2.1. Cómo se usan los filtros para clasificar el tráfico

Recapitulando, una jerarquía típica puede ser como ésta:

                    raíz 1:
                      |
                    _1:1_
                   /  |  \
                  /   |   \
                 /    |    \
               10:   11:   12:
              /   \       /   \
           10:1  10:2   12:1  12:2

¡Pero no deje que este árbol le engañe! *No* debe imaginarse que el núcleo está en la cima del árbol y la red abajo, ya que no es el caso. Los paquetes se encolan y desencolan en el qdisc raíz, que es la única cosa con la que habla el núcleo.

Un paquete se clasifica en una cadena como ésta:

1: -> 1:1 -> 12: -> 12:2

Ahora el paquete reside en una cola de una qdisc asociada a la clase 12:2. En este ejemplo, se asocia un filtro a cada «nodo» del árbol, y cada cual escoge qué rama se toma en su paso. Esto puede tener sentido, Sin embargo, también es posible:

1: -> 12:2

En este caso, un filtro asociado a la raíz decidió enviar el paquete directamente a 12:2.

9.5.2.2. Cómo se desencolan los paquetes para enviarlos al hardware

Cuando el núcleo decide que necesita extraer paquetes para enviarlos a la interfaz, la qdisc 1: raíz recibe una petición de desencolar, que se pasa a 1:1, que a su vez la pasa a 10:, 11:, y 12:, cada una de las cuales consulta a sus descendientes, e intenta hacer dequeue() sobre ellos. En este caso, el núcleo necesita recorrer todo el árbol, porque sólo 12:2 contiene un paquete.

En breve, las clases anidadas SOLO hablan a sus qdisc paternas, y nunca a una interfaz. ¡Sólo la qdisc raíz recibe peticiones de desencolado del núcleo!

La consecuencia de esto es que las clases nunca desencolan más rápido de lo que sus padres permiten. Y esto es exactamente lo que queremos: de esta manera, podemos tener SFQ como clase interna, que no hace ajustes, sólo reordena, y tenemos una qdisc externa, que es la que hace los ajustes.

9.5.3. La qdisc PRIO

La qdisc PRIO en realidad no hace ajustes, sino que sólo subdivide el tráfico basándose en cómo haya configurado los filtros. Puede considerar la qdisc PRIO como una pfifo_fast con esteroides, en la que cada banda es una clase separada, en lugar de una simple FIFO.

Cuando se encola un paquete a la qdisc PRIO, se escoge una clase basándose en las órdenes de filtrado que haya dado. Por defecto, se crean tres clases. Estas clases contienen qdisc que son puras FIFO sin estructura interna, pero puede sustituirlas por cualquier qdisc que haya disponible.

Siempre que se necesite desencolar un paquete, se intenta primero con la clase :1. Las clases más altas sólo se usan si no se ha conseguido el paquete en las clases más bajas.

Esta qdisc es muy útil en caso de que quiera dar prioridad a cierto tráfico sin usar sólo las marcas TOS sino usando el potencial de los filtros de tc. También puede contener cualquier qdisc, mientras que pfifo_fast está limitada a qdisc de fifo sencillas.

Como en realidad no hace ajustes, se le aplica el mismo aviso que a SFQ: úsela solamente si el enlace físico está realmente lleno o métala dentro de una qdisc con clases que haga ajustes. Esto último se aplica a la mayoría de dispositivos DSL y cable módems.

Hablando formalmente, la qdisc PRIO es un reorganizador conservativo.

9.5.3.1. Parámetros y uso de PRIO

tc reconoce los siguientes parámetros:

bands

Número de bandas a crear. Cada banda es una clase. Si cambia este número, también deberá cambiar:

priomap

Si no proporciona filtros de tc para clasificar el tráfico, la qdisc PRIO examina la prioridad TC_PRIO para decidir cómo encolar el tráfico.

Esto funciona igual que con la qdisc pfifo_fast mencionada previamente, refiérase a ella si desea más detalles.

Las bandas son clases, y todas se llaman de mayor:1 a mayor:3 por defecto, de manera que si nuestra qdisc PRIO se llama 12:, tc filtrará el tráfico a 12:1 para garantizar la mayor prioridad.

Repetimos: ¡la banda 0 va al número menor 1! La banda 1 al número menor 2, etc.

9.5.3.2. Configuración de ejemplo

Crearemos este árbol:

     raíz 1: prio
       /   |   \
     1:1  1:2  1:3
      |    |    |
     10:  20:  30:
     sfq  tbf  sfq
banda 0    1    2

El tráfico masivo irá a 30:, el interactivo a 20: o 10:.

Líneas de órdenes:

# tc qdisc add dev eth0 root handle 1: prio 
## Esto crea *instantáneamente las clases 1:1, 1:2, 1:3
  
# tc qdisc add dev eth0 parent 1:1 handle 10: sfq
# tc qdisc add dev eth0 parent 1:2 handle 20: tbf rate 20kbit buffer 1600 limit 3000
# tc qdisc add dev eth0 parent 1:3 handle 30: sfq                                

Ahora veamos qué hemos creado:

# tc -s qdisc ls dev eth0 
qdisc sfq 30: quantum 1514b 
 Sent 0 bytes 0 pkts (dropped 0, overlimits 0) 

 qdisc tbf 20: rate 20Kbit burst 1599b lat 667.6ms 
 Sent 0 bytes 0 pkts (dropped 0, overlimits 0) 

 qdisc sfq 10: quantum 1514b 
 Sent 132 bytes 2 pkts (dropped 0, overlimits 0) 

 qdisc prio 1: bands 3 priomap  1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
 Sent 174 bytes 3 pkts (dropped 0, overlimits 0) 
Como puede ver, la banda 0 ya ha tenido algo de tráfico, ¡y se envió un paquete mientras ejecutábamos la orden!

Ahora vamos a hacer alguna transferencia masiva con una herramienta que ajuste pertinentemente las marcas TOS, y echemos otro vistazo:

# scp tc ahu@10.0.0.11:./
ahu@10.0.0.11's password: 
tc                   100% |*****************************|   353 KB    00:00    
# tc -s qdisc ls dev eth0
qdisc sfq 30: quantum 1514b 
 Sent 384228 bytes 274 pkts (dropped 0, overlimits 0) 

 qdisc tbf 20: rate 20Kbit burst 1599b lat 667.6ms 
 Sent 2640 bytes 20 pkts (dropped 0, overlimits 0) 

 qdisc sfq 10: quantum 1514b 
 Sent 2230 bytes 31 pkts (dropped 0, overlimits 0) 

 qdisc prio 1: bands 3 priomap  1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
 Sent 389140 bytes 326 pkts (dropped 0, overlimits 0) 
Como puede ver, todo el tráfico fue al controlador 30:, que es la banda de menor prioridad, tal como esperábamos. Ahora, para verificar que el tráfico interactivo va a bandas más altas, crearemos un poco:

# tc -s qdisc ls dev eth0
qdisc sfq 30: quantum 1514b 
 Sent 384228 bytes 274 pkts (dropped 0, overlimits 0) 

 qdisc tbf 20: rate 20Kbit burst 1599b lat 667.6ms 
 Sent 2640 bytes 20 pkts (dropped 0, overlimits 0) 

 qdisc sfq 10: quantum 1514b 
 Sent 14926 bytes 193 pkts (dropped 0, overlimits 0) 

 qdisc prio 1: bands 3 priomap  1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
 Sent 401836 bytes 488 pkts (dropped 0, overlimits 0) 

Funcionó (todo el tráfico adicional se ha ido a 10:, que es nuestra qdisc de más alta prioridad). No se ha enviado tráfico a la de más baja prioridad, que recibió anteriormente todo nuestro scp.

9.5.4. La famosa qdisc CBQ

Como se dijo antes, CBQ es la qdisc más compleja disponible, la más publicitada, la menos comprendida, y probablemente la más difícil de configurar correctamente. Esto no se debe a que los autores sean malvados o incompetentes, ni mucho menos, sino sólo que el algoritmo CBQ no es tan preciso y realmente no se ajusta del todo a la manera de trabajar de Linux.

Además de ser de clases, CBQ también es ajustadora (shaper) y es en este aspecto en el que no funciona del todo bien. Debería funcionar de esta manera. Si intenta ajustar a 1mbit/s una conexión de 10mbit/s, el enlace debería estar ocioso el 90% del tiempo. Si no lo está, necesitamos acelerar de manera que realmente ESTE ocioso el 90% del tiempo.

Esto es bastante difícil de medir, de manera que en su lugar CBQ deriva el tiempo ocioso del número de microsegundos que se tarda entre cada petición de más datos por parte de la capa de hardware. Combinados, se pueden usar para aproximar cómo de lleno o vacío está el enlace.

Esto es bastante circunspecto y no siempre lleva a resultados adecuados. Por ejemplo, ¿qué pasaría si la verdadera velocidad del enlace no es capaz de transmitir realmente todos los 100mbit/s de datos, quizá debido a un driver mal implementado? Una tarjeta de red PCMCIA tampoco alcanzará nunca los 100mbit/s debido a la manera en que está diseñado el bus (de nuevo, ¿cómo calculamos el tiempo?)

Se vuelve aún peor si consideramos dispositivos de red no del todo reales, como PPP sobre Ethernet o PPTP sobre TCP/IP. El ancho de banda efectivo en ese caso probablemente se determina por la eficiencia de los canales al espacio de usuario (que es enorme).

La gente que ha hecho mediciones ha descubierto que CBQ no es siempre muy preciso, y a veces se pierde del todo.

Sin embargo, en muchas circunstancias funciona bien. Con la documentación que proporcionamos aquí, debería ser capaz de configurarlo para que funcione bien en la mayoría de los casos.

9.5.4.1. El «shaping» de CBQ en detalle

Como dije antes, CBQ trabaja asegurándose de que el enlace está ocioso sólo lo necesario para reducir el ancho de banda real hasta la tasa configurada. Para hacerlo, calcula el tiempo que debería pasar entre los paquetes medios.

Mientras opera, se mide el tiempo ocioso efectivo usando una media de movimiento por exponencial proporcional (EWMA - exponential weighted moving average), que considera los paquetes recientes exponencialmente más importantes que los pasados. La media de carga de UNIX (loadaverage) se calcula de la misma manera.

El tiempo ocioso calculado se resta al medido mediante EWMA, y el número resultante se llama «avgidle». Un enlace cargado perfectamente tiene un avgidle de cero: los paquetes llegan exactamente una vez cada intervalo calculado.

Un enlace sobrecargado tiene un avgidle negativo y si se vuelve muy negativo, CBQ lo cierra durante un rato y entonces se produce un «sobrelímite».

Por el contrario, un enlace ocioso puede amasar un avgidle enorme, lo que permitiría anchos de banda infinitos tras unas horas de silencio. Para evitarlo, avgidle se trunca en maxidle.

Si hay un sobrelímite, en teoría, la CBQ debería acelerarse a sí misma durante exactamente el tiempo que se ha calculado que pasa entre paquetes, entonces pasa un paquete, y se acelera de nuevo. Pero lea más adelante sobre el parámetro «minburst».

Estos son parámetros que puede especificar para configurar el ajuste:

avpkt

Tamaño medio de un paquete, medido en bytes. Se necesita para calcular maxidle, que se deriva de maxburst, que va especificado en paquetes.

bandwidth

El ancho de banda físico de su dispositivo, necesario para cálculos de tiempo ocioso.

cell

El tiempo que tarda un paquete en ser transmitido sobre un dispositivo está escalonado, y se basa en el tamaño del paquete. Un paquete de tamaño 800 y uno de 806 pueden tardar lo mismo en ser enviados, por ejemplo (esto establece la granularidad). A menudo está a "8". Debe ser una potencia entera de dos.

maxburst

Este número de paquetes se usa para calcular maxidel de manera que cuando avgidle esté a maxidel, se puede enviar una ráfaga de esta cantidad de paquetes medios antes de que avgidle caiga a 0. Póngalo alto si quiere que sea tolerante a ráfagas. No puede establecer maxidel directamente, sino sólo mediante este parámetro.

minburst

Como se mencionó previamente, CBQ necesita acelerar en caso de sobrelímite. La solución ideal es hacerlo exactamente durante el tiempo ocioso calculado, y pasar un paquete. Sin embargo, los núcleos de Unix normalmente lo pasan mal organizando eventos menores a 10ms, de manera que es mejor acelerar durante un periodo algo mayor, y hacer pasar entonces "minburst" paquetes de una sóla tanda, para después dormir "minburst" veces más.

El tiempo de espera se denomina offtime. Los valores altos de minburst llevan a ajustes más precisos a largo plazo, pero a ráfagas más grandes a escala de milisegundos.

minidle

Si avgidle está por debajo de 0, estaremos en sobrelímite y necesitaremos esperar hasta que avgidle sea suficientemente grande como para mandar un paquete. Para prevenir una ráfaga súbita después de haber detenido el enlace durante un periodo prolongado, avgidle se reinicia a minidle si cae demasiado bajo.

Minidle se especifica en microsegundos negativos, de manera que 10 significa que avgidle se corta a -10us.

mpu

Tamaño mínimo del paquete (necesario porque incluso los paquetes de tamaño cero se rellenan con 64 bytes en ethernet, y por tanto lleva cierto tiempo transmitirlos). CBQ necesita saber esto para calcular de forma adecuada el tiempo ocioso.

rate

La tasa deseada de tráfico saliente de esta qdisc (¡éste es el control de velocidad!)

Internamente, CBQ tiene un montón de ajustes más precisos. Por ejemplo, a las clases que se sabe no contienen datos encolados no se les pregunta. Se penaliza a las clases sobrelimitadas reduciendo su prioridad efectiva. Todo muy inteligente y complicado.

9.5.4.2. Comportamiento de la CBQ con clases

Aparte del ajuste, usando las aproximaciones de tiempo ocioso ya mencionadas, CBQ también actúa igual que la cola PRIO en el sentido de que sus clases pueden tener diferentes prioridades y que los números pequeños de prioridad se examinan antes que los grandes.

Cada vez que la capa de hardware pide un paquete para enviarlo a la red, se inicia un proceso de round robin por pesos («WRR»), comenzando por las clases de menor prioridad.

Estas se agrupan y se les pregunta si tienen datos disponibles. En tal caso, se devuelven. Después de que se haya permitido desencolar una serie de bytes a una clase, se prueba con la siguiente clase de esa misma prioridad.

Los siguientes parámetros controlan el proceso WRR:

allot

Cuando se le pide a la CBQ externa un paquete para enviar por la interfaz, buscará por turnos en todas sus qdisc internas (en las clases), en el orden del parámetro de «prioridad». Cada vez que le toca el turno a una clase, sólo puede enviar una cantidad limitada de datos. «Allot» es la unidad básica de esta cantidad. Vea el parámetro «weight» para más información.

prio

La CBQ también puede actuar como un dispositivo PRIO. Primero se prueba con las clases internas de menor prioridad y mientras tengan tráfico, no se mira en las otras clases.

weight

Weight ayuda en el proceso de Weighted Round Robin. Cada clase tiene una oportunidad por turnos para enviar. Si tiene una clase con un ancho de banda significativamente menor que las otras, tiene sentido permitirle enviar más datos en su ronda que a las otras.

Una CBQ suma todos los pesos bajo una clase, y los normaliza, de manera que puede usar números arbitrarios: sólo son importantes las equivalencias. La gente viene usando «tasa/10» como regla general y parece que funciona bien. El peso renormalizado se multiplica por el parámetro «allot» para determinar cuántos datos se envían en una ronda.

¡Tenga en cuenta que todas las clases dentro de una jerarquía CBQ necesitan compartir el mismo número mayor!

9.5.4.3. Parámetros CBQ que determinan la compartición y préstamo de enlaces

Aparte de meramente limitar determinados tipos de tráfico, también es posible especificar qué clases pueden tomar prestada capacidad de otras clases o, al revés, prestar ancho de banda.

Isolated/sharing

Una clase que está configurada como «isolated» (aislada) no prestará ancho de banda a sus hermanas. Uselo si tiene varios agentes competidores o mutuamente hostiles sobre el enlace que no quieren dejarse espacio entre sí.

El programa de control tc también conoce el «sharing», que es lo contrario que «isolated».

bounded/borrow

Una clase también puede estar «bounded» (limitada), lo que significa que no tratará de tomar ancho de banda prestado de sus clases hermanas. tc también conoce «borrow», que es lo contrario de «bounded».

En una situación típica podríamos encontrarnos a dos agentes sobre un mismo enlace que al mismo tiempo son «isolated» y «bounded», lo que significa que están realmente limitadas a sus tasas aisladas, y que no permitirán préstamos entre sí.

Junto a tales clases, puede haber otras que tengan permiso de ceder ancho de banda.

9.5.4.4. Configuración de ejemplo

Esta configuración limita el tráfico del servidor web a 5mbit y el SMTP a 3mbit. Juntos, no pueden alcanzar más de 6mbit. Tenemos una NIC de 100mbit y las clases pueden tomar ancho de banda prestado unas de otras.

# tc qdisc add dev eth0 root handle 1:0 cbq bandwidth 100Mbit         \
  avpkt 1000 cell 8
# tc class add dev eth0 parent 1:0 classid 1:1 cbq bandwidth 100Mbit  \
  rate 6Mbit weight 0.6Mbit prio 8 allot 1514 cell 8 maxburst 20      \
  avpkt 1000 bounded
Esta parte instala la raíz en la clase convenida 1:0. La clase 1:1 está limitada, de manera que su ancho de banda no puede pasar de 6mbit.

Como se dijo antes, CBQ necesita un *montón* de ajustes. Todos los parámetros quedaron explicados anteriormente, de todas maneras. La configuración HTB correspondiente es mucho más sencilla.

# tc class add dev eth0 parent 1:1 classid 1:3 cbq bandwidth 100Mbit  \
  rate 5Mbit weight 0.5Mbit prio 5 allot 1514 cell 8 maxburst 20      \
  avpkt 1000                       
# tc class add dev eth0 parent 1:1 classid 1:4 cbq bandwidth 100Mbit  \
  rate 3Mbit weight 0.3Mbit prio 5 allot 1514 cell 8 maxburst 20      \
  avpkt 1000

Estas son nuestras dos clases. Fíjese en cómo escalamos el peso con la tasa configurada. Ninguna clase está limitada, pero están conectadas a la clase 1:1, que sí lo está. De manera que la suma de anchos de banda de las dos clases pasará nunca de 6mbit. ¡Por cierto!, los identificadores de clase deben estar dentro del mismo número mayor que su CBQ madre.

# tc qdisc add dev eth0 parent 1:3 handle 30: sfq
# tc qdisc add dev eth0 parent 1:4 handle 40: sfq

Ambas clases llevan una qdisc FIFO por defecto. Pero las hemos reemplazado con una cola SFQ de manera que cada flujo de datos sea tratado de igual forma.

# tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 match ip \
  sport 80 0xffff flowid 1:3
# tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 match ip \
  sport 25 0xffff flowid 1:4

Estas órdenes, asociadas directamente a al raíz, envían el tráfico a las qdisc correctas.

Fíjese que usamos «tc class add» para CREAR clases dentro de una qdisc, pero que usamos «tc qdisc add» para añadir qdiscs a estas clases.

Puede preguntarse qué sucede al tráfico que no se clasifica en ninguna de esasa dos reglas. Parecería que en este caso, los datos serán procesados dentro de 1:0, y estarán sin límites.

Si el tráfico SMTP+web conjunto intenta exceder el límite impuesto de 6mbit/s, el ancho de banda se dividirá de acuerdo al parámetro de peso, dando 5/8 del tráfico al servidor web y 3/8 al servidor de correo.

Con esta configuración también podemos decir que el tráfico del servidor web tendrá como mínimo 5/8 * 6 mbit = 3.75 mbit.

9.5.4.5. Otros parámetros de CBQ: split y defmap

Como mencioné anteriormente, una qdisc con clases necesita llamar a los filtros para determinar en qué clase encolar un paquete.

Aparte de llamar al filtro, CBQ ofrece otras opciones: defmap y split. Es bastante complicado de entender, y no es vital. Pero como éste es el único sitio conocido donde se explican adecuadamente defmap y split, intentaré hacerlo lo mejor posible.

Como es probable que a menudo sólo quiera filtrar el Tipo de Servicio, se proporciona una sintaxis especial. Cuando CBQ necesita averiguar dónde encolar un paquete, comprueba si éste es un «split node». Si lo es, una de las sub-qdisc ha indicado que desea recibir todos los paquetes con una determinada prioridad, que se podría derivar del campo TOS, o por opciones de socket establecidas por las aplicaciones.

Se hace un OR con los bit de prioridad de los paquetes y el campo defmap para ver si coinciden. En otras palabras, es un atajo para crear filtros muy rápidos, que sólo se ajustan a ciertas prioridades. Un defmap de ff (hex) coincidirá con todo, un mapa de 0, con nada. Quizá una configuración de ejemplo ayude a aclarar las cosas:

# tc qdisc add dev eth1 root handle 1: cbq bandwidth 10Mbit allot 1514 \
  cell 8 avpkt 1000 mpu 64
 
# tc class add dev eth1 parent 1:0 classid 1:1 cbq bandwidth 10Mbit    \
  rate 10Mbit allot 1514 cell 8 weight 1Mbit prio 8 maxburst 20        \
  avpkt 1000
Preámbulo estándar de CBQ. ¡Nunca me acostumbraré a la inmensa cantidad de cosas que necesita!

Defmap se refiere a los bits TC_PRIO, que se definen así:

TC_PRIO..          Num  Corresponde al TOS
-------------------------------------------------
BESTEFFORT         0    Maximizar fiabilidad
FILLER             1    Minimizar coste
BULK               2    Maximizar transferencia (0x8)  
INTERACTIVE_BULK   4                               
INTERACTIVE        6    Minimizar retrasos (0x10)      
CONTROL            7                               

El número TC_PRIO.. se corresponde a bits, contando desde la derecha. Vea la sección pfifo_fast si desea más detalles sobre cómo se convierten los bits de TOS en prioridades.

Ahora, las clases interactiva y masiva:

# tc class add dev eth1 parent 1:1 classid 1:2 cbq bandwidth 10Mbit     \
  rate 1Mbit allot 1514 cell 8 weight 100Kbit prio 3 maxburst 20        \
  avpkt 1000 split 1:0 defmap c0

# tc class add dev eth1 parent 1:1 classid 1:3 cbq bandwidth 10Mbit     \
  rate 8Mbit allot 1514 cell 8 weight 800Kbit prio 7 maxburst 20        \
  avpkt 1000 split 1:0 defmap 3f

La «split qdisc» es la 1:0, que es donde se va a hacer la elección. C0 es el valor binario 11000000, 3F es 00111111, de manera que entre los dos coinciden con todo. La primera clase coincide con los bits 7 y 6, y por tanto se corresponde con el tráfico «interactivo» y «de control». La segunda clase coincide con el resto.

El nodo 1:0 tiene ahora una tabla como ésta:

prioridad	enviar a
0		1:3
1		1:3
2		1:3
3		1:3
4		1:3
5		1:3
6		1:2
7		1:2

Si desea más diversión, también puede pasar una «change mask» (máscara de cambio), que indica qué prioridades desea cambiar. Sólo necesita usar esto si está ejecutando «tc class change». Por ejemplo, para añadir los mejores esfuerzos al tráfico de 1:2, podría ejecutar esto:

# tc class change dev eth1 classid 1:2 cbq defmap 01/01

El mapa de prioridades de 1:0 ahora es así:

prioridad	enviar a
0		1:2
1		1:3
2		1:3
3		1:3
4		1:3
5		1:3
6		1:2
7		1:2

FIMXE: no he probado «tc class change», sólo he mirado los fuentes.

9.5.5. Hierarchical Token Bucket

Martin Devera (<devik>) se dio cuenta de que CBQ es complejo y que no parece óptimo en varias situaciones típicas. Su enfoque Jerárquico se ajusta bien a configuraciones donde se tiene una cantidad fija de ancho de banda a dividir para diferentes propósitos, dándole a cada propósito un ancho de banda garantizado, con la posibilidad de especificar cuánto ancho se puede tomar prestado.

HTB funciona igual que CBQ, pero no recurre a cálculos de tiempo ocioso para los ajustes. En su lugar, es un Token Bucket Filter con clases (de ahí el nombre). Sólo tiene unos pocos parámetros que están bien documentados en su sitio.

Según se complique su configuración HTB, verá que escala bien. ¡Con CBQ ya es complejo incluso en casos simples! HTB3 (vea los detalles sobre las versiones de HTB en su página web) es ahora parte de las fuentes oficiales del núcleo (desde 2.4.20-pre1 y 2.5.31 en adelante). Sin embargo, quizá necesite una versión de «tc» parcheada para HTB3: la versión de las partes del núcleo y espacio de usuario de HTB3 deben tener el mismo número mayor, o «tc» no trabajará con HTB.

Si ya tiene un núcleo moderno, o está en posición de parchear el núcleo, considere seriamente usar HTB.

9.5.5.1. Configuración de ejemplo

Funcionalmente es casi idéntica a la configuración de ejemplo anterior de CBQ:

# tc qdisc add dev eth0 root handle 1: htb default 30

# tc class add dev eth0 parent 1: classid 1:1 htb rate 6mbit burst 15k

# tc class add dev eth0 parent 1:1 classid 1:10 htb rate 5mbit burst 15k
# tc class add dev eth0 parent 1:1 classid 1:20 htb rate 3mbit ceil 6mbit burst 15k
# tc class add dev eth0 parent 1:1 classid 1:30 htb rate 1kbit ceil 6mbit burst 15k 

El autor recomienda SFQ por debajo de estas clases:

# tc qdisc add dev eth0 parent 1:10 handle 10: sfq perturb 10
# tc qdisc add dev eth0 parent 1:20 handle 20: sfq perturb 10
# tc qdisc add dev eth0 parent 1:30 handle 30: sfq perturb 10

Añada los filtros que dirigirán el tráfico a las clases correctas:

# U32="tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32"
# $U32 match ip dport 80 0xffff flowid 1:10
# $U32 match ip sport 25 0xffff flowid 1:20
Y esto es todo (nada de números desagradables sin explicación, ni parámetros sin documentar).

Realmente, HTB parece maravilloso (si 10: y 20: tienen ambos su ancho de banda garantizado, y se deja más a dividir, tomarán prestado en una relación de 5:3, tal como cabría esperar).

El tráfico sin clasificar va a parar a 30:, que tiene poco ancho de banda por sí mismo, pero que puede tomar prestado todo lo que queda. Como hemos escogido SFQ internamente, ¡de paso obtendremos equitatividad!