Redes de Computadores

Controle de Banda com HTB

Sendo você um administrador de rede, pode estar vivendo a situação onde no mesmo link estão trafegando dados vitais para empresa, lazer dos funcionários e até mesmo pacotes enviados por vírus de rede, e quando alguém resolve fazer download de um filme ou sintoniza uma rádio on-line as aplicações de missão crítica são seriamente prejudicadas. Uma das maneiras de resolver o problema é através do controle de banda, onde você pode definir uma hierarquia do uso de rede, privilegiando algumas portas TCP e UDP e/ou endereços de hosts e de redes, fornecendo a estes maior largura de banda em detrimento do tráfego menos importante. Para fazer este controle existem equipamentos dedicados a este serviço. Uma outra alternativa, de baixo custo e de grande flexibilidade, é através do Linux. É desta opção que irei falar neste artigo.
Quando o kernel do linux tem de enviar para a rede vários pacotes através de um dispositivo de rede qualquer, ele precisa decidir quais enviará primeiro, quais retardará e quais descartará. Utilizando diversos algoritmos o escalonador de pacotes do kernel procura executar esta tarefa da maneira mais equilibrada possível. A regra padrão para o escalonador de pacotes é a FIFO (Firs In, First Out) - o primeiro a chegar é o primeiro a ser enviado. Você pode alterar este comportamento padrão fazendo com que o escalonador envie pacotes da maneira que lhe for mais útil.

O kernel do linux oferece diversos recursos que permitem o total controle no envio de pacotes. Este conjunto de recursos é denominado Queueing Discipline (qdisc) - algo como regras de enfileiramento. Dentre estas regras existe uma denominada Hierarchical Token Bucket - HTB. O objetivo deste artigo é passar para você o funcionamento básico do HTB e como este pode lhe ajudar no controle do tráfego na rede.

O HTB fornece meios para você controlar o tráfego de saída em um link. Através dele você pode simular vários links lentos em um único link físico e estabelecer quais tipos de tráfegos vão transitar em cada um destes links virtuais. O que você precisa fazer é definir a divisão em links e decidir como os pacotes neles trafegarão.

Para utilizar o HTB você vai precisar de:
1. Kernel com o módulo HTB packet scheduler ativado. A partir da versão 2.4.20 o suporte ao HTB foi incluído ao kernel. Para versões anteriores é preciso aplicar um patch.
2. Ferramenta tc (Traffic Control) atualizada para manipular qdiscs HTB. A ferramenta tc faz parte do pacote iproute2. Ela é utilzada para instruir ao kernel como tratar tráfego de rede.

Você pode está se perguntando se o kernel de seu linux tem suporte para HTB. Para assegurar-se disto vá para /lib/modules/versao_do_kernel/kernel/net/sched e verifique a existência dos arquivos sch_htb.o, sch_sfq.o e cls_u32.o. Se você localizar TODOS os arquivos citados seu sistema suporta o HTB.

Nos testes que fiz utilizei as versões 7, 8 e 9 do Conectiva Linux. Esta última versão tem suporte para HTB. Se você é usuário das versões anteriores da Conectiva e não quer baixar os fontes mais atuais do kernel e compilá-lo, você pode instalar o kernel 2.4.21 disponível nos CDs do Conectiva Linux 9, inclusos na edição 77 da PCMaster, neste caso tenha cuidado para não substituir o kernel do seu sistema e sim instalar um novo kernel - se for instalar via linha de comando execute rpm -ivh e não rpm -Uvh - a não ser que seja exatamente isto que você quer. Independente da forma de atualização escolhida, configure seu carregador de boot e reinicie o sistema no novo kernel.

É importante dizer que o HTB funciona com qualquer Linux. Você pode usá-lo na distribuição de sua preferência. Mencionei algumas vezes o Conectiva Linux porque é este que utilizo e foi nele onde executei os testes.

Só falta obter a ferramenta tc atualizada para suportar HTB. Baixe o arquivo http://luxik.cdi.cz/~devik/qos/htb/v3/htb3.6-020525.tgz, descompacte-o com tar zxvf htb3.6-020525.tgz e copie o tc para /sbin.

Imagine o cenário onde você tem um link de 512kbit/s entre a matriz e a filial de uma empresa. A matriz tem conexão com a Internet de 2Mbit/s e compartilha este link com a filial, ou seja, todo acesso a Internet feito pela filial trafega também pelo link de 512kbit/s. Todos os servidores estão na matriz e as aplicações vitais para a empresa estão nestes servidores. Entre estas aplicações existe também uma ordem de importância, onde as de missão crítica atendem nas portas tcp 8800 e 22, logo abaixo a que atende na porta 8080, em seguida a que atende na porta 8008 e por último a que atende na porta 8000. Todos os demais tráfegos são irrelevantes. Como você pode observar o tráfego entre a Internet e a filial concorre com o tráfego entre a filial e a matriz e o problema é agravado pela superioridade de banda da Internet em relação a conexão matiz/filial. Esta situação torna a comunicação da filial com as aplicações vitais bastante vunerável a um tráfego mais intenso com a Internet.

Para que o tráfego entre a matriz (rede 192.168.0.0/24) e a filial (rede 192.168.1.0/24) fique adequada as necessidades da empresa configurei o HTB para garantir 272kbit/s nas portas 22 e 8800, 128kbit/s na porta 8080, 64kbit/s na porta 8008 e 32kbit/s na porta 8000. Os 16kbit/s restantes são destinados para assegurar uma largura de banda mínima para todo tráfego restante. Qualquer excedente de banda poderá ser utilizado por qualquer tráfego até o limite de 512kbit/s.

Vamos agora examinar o script com os comandos necessários para fazer esta configuração. Os comentários estão sempre após os comandos.


#!/bin/bash
tc qdisc del dev eth0 root
# Remove qualquer qdisc associado a interface eth0.
tc qdisc add dev eth0 root handle 1: htb default 50
# Associa uma regra de enfileiramento (qdisc) HTB a interface eth0 e vincula a
# esta o manipulador "1:". Este manipulador será referenciado por comandos
# abaixo para ajudar a criar a estrutura hierárquica. O "default 50" diz que
# todo tráfego que não estiver associado a uma classe específica será feito
# pela classe 50.
tc class add dev eth0 parent 1: classid 1:1 htb rate 512kbit
# Cria uma classe htb raiz identificando-a como "1:1" abaixo do qdisc "1:" com
# a taxa de 512kbit/s.
tc class add dev eth0 parent 1:1 classid 1:10 htb rate 272kbit ceil 512kbit
tc class add dev eth0 parent 1:1 classid 1:20 htb rate 128kbit ceil 512kbit
tc class add dev eth0 parent 1:1 classid 1:30 htb rate 64kbit ceil 512kbit
tc class add dev eth0 parent 1:1 classid 1:40 htb rate 32kbit ceil 512kbit
tc class add dev eth0 parent 1:1 classid 1:50 htb rate 16kbit ceil 512kbit
# Cria cinco classes filhas da classe raiz "1:1" identificando-as como "1:10",
# "1:20", "1:30", "1:40" e "1:50" e atribui a elas as taxas de 272kbit/s,
# 128kbit/s, 64kbit/s, 32kbit/s e 16kbit/s respectivamente. Instrui ao HTB que
# o tráfego de qualquer uma das classes poderá chegar ao limite de 512kbit/s
# (ceil). Se ceil não for especificado ele será igual a taxa (rate).
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
tc qdisc add dev eth0 parent 1:40 handle 40: sfq perturb 10
tc qdisc add dev eth0 parent 1:50 handle 50: sfq perturb 10
# Cria manipuladores sfq "10:" a "50:" sob as classes "1:10" a "1:50". O qdisc
# sfq objetiva fornecer uma distribuição justa entre os diversos tráfegos de
# uma mesma classe, fazendo com que as várias conexões sob uma mesma classe
# dividam de forma equilibrada entre si. O uso deste qdisc evita que uma
# conexão de uma classe "roube" os recursos das demais na mesma classe. O
# parâmetro "perturb 10" é o tempo em segundos onde será verificado a
# solicitação de uso de banda por conexão.
U32="tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 "
U32=$U32"match ip src 192.168.0.0/24 match ip dst 192.168.1.0/24"
# A variável "U32" foi criada para tornar as linhas de comando a seguir mais
# curtas. O filtro u32 é aplicado para indentificar endereços ip e/ou portas
# tcp/udp. O parâmetro prio diz qual é prioridade que os pacotes terão. Este
# parâmetro varia de 0 a 15. Quanto menor o valor de prio maior a prioridade.
$U32 match ip sport 22 0xffff flowid 1:10
$U32 match ip sport 8800 0xffff flowid 1:10
$U32 match ip sport 8080 0xffff flowid 1:20
$U32 match ip sport 8008 0xffff flowid 1:30
$U32 match ip sport 8000 0xffff flowid 1:40
# Define como o tráfego será direcionado para cada classe com base nos
# endereços e portas de origem e endereços de destino através dos parâmetros
# src, sport e dst respectivamente. A associação destes com suas classes é
# estabelecida através do parâmetro flowid. Lembre-se que todo tráfego não
# incluso em um destes filtros será através da classe 50. O número hexadecimal
# após a porta pode ser utilizado para identificar uma faixa de portas
# consecutivas. Para referenciar uma única porta coloque sempre "0xffff".

Após executar o script, para assegurar que o ajuste no tráfego de rede reflita realmente aquilo que você deseja é interessante proceder alguns testes antes de colocá-lo em ambiente de produção. Sugiro que você habilite o apache, na mesma máquina onde o HTB está configurado, para aceitar conexões nas portas acima referenciadas. Isto pode ser feito acrescentando quatro linhas no arquivo httpd.conf (normalmente em /etc/httpd/conf) com as seguintes instruções: Listen 8800, Listen 8080, Listen 8008 e Listen 8000. Não esqueça de reiniciar o apache com o comando /etc/init.d/httpd restart. Coloque também no diretório onde ficam as páginas do apache (DocumentRoot) arquivos com tamanhos de 512 kbytes, 1024 kbytes, 2048 kbytes, 4096 kbytes e 8192 kbytes. Agora o seu ambiente de testes está pronto.

Para executar os testes você vai precisar de dois computadores. O endereço ip da máquina servidora tem de estar conforme o parâmetro src do script e a máquina cliente tem de estar conforme o parâmetro dst. Caso sua rede não se assemelhe aos endereços constantes no script faça as alterações antes de testar. No cliente você pode usar qualquer programa que faça download de um servidor web, onde você possa definir a porta que o servidor está ouvindo e que mostre a taxa de transferência final dos dados. Por atender a todas a estas características utilizei o wget.

O primeiro teste consiste em fazer downloads simultâneos nas portas 80 - submetida a classe 1:50 por conta do parâmetro default, 8000 - classe 1:40, 8008 - classe 1:30, 8080 - classe 1:20 e 8800 - classe 1:10.


# wget http://192.168.0.252:80/teste/512.gz; rm -f 512.gz
--21:38:43-- http://192.168.0.252/teste/512.gz
21:42:45 (2.07 KB/s) - '512.gz' recebido [512100/512100]

# wget http://192.168.0.252:8000/teste/1024.gz; rm -f 1024.gz
--21:38:44-- http://192.168.0.252:8000/teste/1024.gz
21:43:10 (3.76 KB/s) - '1024.gz' recebido [1023785/1023785]

# wget http://192.168.0.252:8008/teste/2048.gz; rm -f 2048.gz
--21:38:45-- http://192.168.0.252:8008/teste/2048.gz
21:43:03 (7.76 KB/s) - '2048.gz' recebido [2047192/2047192]

# wget http://192.168.0.252:8080/teste/4096.gz; rm -f 4096.gz
--21:38:46-- http://192.168.0.252:8080/teste/4096.gz
21:43:06 (15.35 KB/s) - '4096.gz' recebido [4093945/4093945]

# wget http://192.168.0.252:8800/teste/8192.gz; rm -f 8192.gz
--21:38:47-- http://192.168.0.252:8800/teste/8192.gz
21:42:53 (32.46 KB/s) - '8192.gz' recebido [8187493/8187493]

Como você pode observar as taxas de transferência foram bastante aproximadas ao que foi configurado no script. Repare que elas estão demonstradas em Kbytes/s, para transformar em kbits/s multiplique por oito. Verifique também a hora de início e de término de cada download e o tamanho dos arquivos transferidos.

Para verificar a distribuição eqüitativa dentro de uma classe baixei duas vezes o mesmo arquivo simultaneamente na porta 8800:


# wget http://192.168.0.252:8800/teste/1024.gz; rm -f 1024.gz
--22:10:09-- http://192.168.0.252:8800/teste/1024.gz
22:10:43 (29.74 KB/s) - '1024.gz' recebido [1023785/1023785]

# wget http://192.168.0.252:8800/teste/1024.gz; rm -f 1024.gz
--22:10:11-- http://192.168.0.252:8800/teste/1024.gz
22:10:44 (29.88 KB/s) - '1024.gz' recebido [1023785/1023785]

Atente para a taxa de ambos downloads que é praticamente idêntica.

O que você viu é apenas uma demonstração do potencial do HTB. Configurações muito mais complexas podem ser feitas para atender inúmeras situações. Você pode criar uma estrutura de classes HTB com diversos níveis hierárquicos ou ainda ter mais de uma classe raiz vinculada a mesma interface de rede. Como você pode comprovar o HTB é uma ferramenta poderosa e bastante flexível para controle de tráfego de rede.

Dicas:

O controle de banda no Linux é sempre no tráfego de saída. Se você quer controlar o fluxo de dados da filial para a matriz, por exemplo o envio de email ou upload de ftp, você precisa colocar outro computador com linux também na filial para controlar a saída dos dados de lá.
Caso você ache a sintaxe do comando tc complexa - particularmente não a acho nada amigável - talvez queira utilizar o script htb.ini. Este script pode ser encontrado em http://freshmeat.net/projects/htb.ini e realmente simplifica bastante o uso do HTB, embora simplicidade e flexibilidade nem sempre andem juntas. Toda a documentação e exemplos de utilização estão no corpo do script.

Se você optar por compilar o kernel, verifique a existência de arquivos com o nome parecido com "kernel-versao_do_kernel-perfil_de_hardware.config" no diretório /usr/src/linux-versao_do_kernel_original/configs/ de seu linux. Algumas distribuições disponibilizam estes arquivos que espelham o kernel original e basta copiar um deles para o diretório dos fontes do kernel que você quer compilar com o nome .config. Escolha o arquivo que mais se assemelhe ao perfil de hardware de seu computador - número de processadores, família de processadores e quantidade de memória RAM. Desta forma é muito mais fácil e rápido do que selecionar dezenas de opções, pois quase tudo que você precisa já está no arquivo .config, afinal de contas você está partindo de um kernel que já está rodando no seu computador. Depois é só fazer as modificações que você desejar com make menuconfig ou make xconfig, não esquecendo de selecionar as opçoes de QoS pois o suporte a HTB é uma delas e em seguida executar make dep, make clean, make, make bzImage, make modules, make modules_install e finalmente make install. Lembre-se de atualizar o seu carregador de boot para poder reiniciar seu sistema com o novo kernel.

Para saber mais:

Sobre controle de banda com o Linux: Capitulo 9 do Linux Advanced Routing and Traffic Control HOWTO em http://lartc.org/howto/
Sobre HTB: Na home page do HTB em http://luxik.cdi.cz/~devik/qos/htb/
Traduzi o Manual HTB - guia do usuário. A tradução está em boa parte aceitável. Contribuições para melhorar a tradução são bem vindas. Falta um site para publicar.
Sobre como compilar o kernel: Guia do Hardware de Carlos E. Morimoto em http://www.guiadohardware.info/tutoriais/065/
Sobre como compilar as ferramentas do iproute2, inclusive tc com suporte HTB: Iproute2 and traffic shaping em http://www.linuxfromscratch.org/hints/downloads/files/iproute2.txt

(autor: Carlos Virgílio Beltrão Lessa - Este endereço de email está sendo protegido de spambots. Você precisa do JavaScript ativado para vê-lo. extraido de http://br-linux.org/tutoriais/001648.html)