Informática Numaboa - Linux
Administração de Largura de Banda no Linux II
Dom 22 Abr 2007 18:09 |
- Detalhes
- Categoria: Como fazer configurações
- Atualização: Terça, 21 Abril 2009 18:20
- Autor: vovó Vicki
- Acessos: 10403
No artigo Administração de Largura de Banda no Linux foram vistas filas e disciplinas. Também foram citadas as filas simples de disciplina sem classe. Este texto complementa o primeiro falando sobre as filas de disciplina com classe.
Filas de Disciplina com classe
Qdiscs com classe são muito úteis quando há tipos diferentes de tráfego que devem receber tratamento diferenciado. Uma das qdiscs com classe é conhecida como 'CBQ' (Class Based Queueing) e é citada com tanta frequência que as pessoas associam filas com classe apenas com a CBQ, o que não está correto.
A CBQ é apenas a mais antiga delas e também a mais complexa. Como velha ranzinza, ela nem sempre faz o que você quer. Isto pode chocar muita gente que já passou pelo 'efeito sendmail', o qual nos ensina que qualquer tecnologia complexa que é fornecida sem documentação precisa ser a melhor que existe.
Falaremos sobre a CBQ e suas alternativas logo a seguir.
O fluxo nas qdiscs com classe
Quando o tráfego entra numa qdisc com classe, ele precisa ser enviado para uma das classes da qdisc, ou seja, ele precisa ser 'classificado'. Para determinar o que deve ser feito com um pacote, os assim chamados 'filtros' são consultados. É importante saber que os filtros são chamados de dentro da qdisc!
Os filtros associados a esta qdisc retornam uma decisão que a qdisc utiliza para colocar o pacote na fila de uma das classes. Cada subclasse pode usar filtros adicionais para ver se mais alguma instrução é aplicável. Se não, a classe coloca o pacote na fila da sua qdisc.
Além de conter outras qdiscs, a maioria das qdisc com classe fazem modelagem. Isto é útil para estabelecer horários (com SFQ, por exemplo) e controlar taxas. Você vai precisar disto se tiver uma interface de alta velocidade (por exemplo, uma Ethernet) ligada a um dispositivo mais lento (um cable modem). Se você rodar apenas a SFQ, nada vai mudar porque os pacotes entram e saem do seu roteador sem retardo: a interface de saída é muita mais rápida do que a velocidade do link e não haverá fila para gerenciar.
A família qdisc: raízes, manipuladores, irmãos e pais
Cada interface possui uma 'qdisc raiz' (root qdisc) de egressão (saída). Por default, esta é a fila de disciplina sem classe pfifo_fast. Cada qdisc e cada classe recebe um manipulador (handle), o qual pode ser usado por declarações de configuração para referir esta qdisc. Além de uma qdisc de egressão, uma interface também pode ter uma qdisc de ingresso que policia o tráfego de entrada.
Os manipuladores destas qdiscs têm duas partes, um número principal (ou maior) e um secundário (ou menor) no formato
As classes precisam ter o mesmo número principal que seus pais. Este número precisa ser único num setup de ingresso ou de egressão. O número menor precisa ser único dentro de uma qdisc e suas classes.
Como os filtros são usados para classificar o tráfego
Uma hierarquia típica pode ter o seguinte aspecto:
1: qdisc raiz | 1:1 classe filha / | \ / | \ / | \ / | \ 1:10 1:11 1:12 classes filhas | | | | 11: | classe folha | | 10: 12: qdisc / \ / \ 10:1 10:2 12:1 12:2 classes folha
Mas não deixe que esta árvore te engane. O kernel NÃO está no topo da árvore e a rede abaixo dele! Os pacotes entram e saem das filas pela qdisc raiz, que é a única que pode falar com o kernel.
Um pacote pode ser classificado numa corrente como a mostrada a seguir:
1: -> 1:1 -> 1:12 -> 12: -> 12:2
Agora o pacote está numa fila de uma qdisc associada à classe 12:2. Neste exemplo cada um dos 'nós' da árvore possui um filtro associado, cada um deles escolhendo a ramificação que deve ser seguida. Isto é muito lógico, mas a possibilidade a seguir também é válida:
1: -> 12:2
Neste caso, um filtro associado à raiz decidiu enviar o pacote diretamente para 12:2.
Como os pacotes saem da fila para ir para o hardware
Quando o kernel decide que precisa retirar pacotes para enviá-los para a interface, a qdisc raiz 1: recebe uma solicitação de liberação, a qual é passada para 1:1, que a passa para 10:, 11: e 12:. Estas, por sua vez, acionam seus irmãos e tentam conseguir a liberação. Neste caso, o kernel precisa atravessar a árvore inteira porque apenas a 12:2 contém um pacote.
Resumindo, classes aninhadas SOMENTE falam com suas qdiscs pai, nunca falam com uma interface. Apenas a qdisc raiz é tirada da fila pelo kernel!.
O resultado disto é que as classes nunca saem da fila mais depressa do que seus pais permitem, e é exatamente isto o que queremos: deste modo podemos ter uma SFQ numa classe interna, que não faz nenhuma modelagem, apenas controla o horário, e ter uma qdisc externa que faça a modelagem.
A qdisc PRIO
A qdisc PRIO não faz modelagem, apenas subdivide o tráfego de acordo com a configuração dos filtros. A qdisc PRIO pode ser considerada uma espécie de pfifo_fast vitaminada onde cada banda é uma classe separada ao invés de ser uma FIFO simples.
Quando um pacote entra numa fila qdisc PRIO, uma classe é escolhida de acordo com os comandos de filtragem dados. Por default, três classes são criadas. Estas classes, por default, contém qdiscs FIFO puras sem uma estrutura interna, mas é possível substituí-las por qualquer qdisc disponível.
Sempre que um pacote precisar sair da fila, a classe :1 é acionada primeiro. Classes mais altas são usadas apenas se todas as bandas mais baixas não devolverem um pacote.
Esta qdisc é muito útil quando se quer priorizar certos tipos de tráfego sem usar apenas flags TOS, mas usando todo o poder dos filtros tc. Também é possível adicionar outra qdisc às três classes predefinidas, se bem que a pfifo_fast está limitada a qdiscs fifo simples.
Como a PRIO não faz modelagem, vale o mesmo alerta dado para a SFQ: deve ser usada apenas se o link físico estiver realmente lotado ou insira-a numa qdisc com classe que faça modelagem. A segunda alternativa vale para praticamente todos os cable modems e dispositivos DSL.
Formalmente, a qdisc PRIO é um controlador de precedência com Conservação de Trabalho.
Parâmetros e uso da PRIO
Os seguintes parâmetros são reconhecidos pelo tc:
- bands (bandas): O número de bandas que devem ser criadas. Na verdade, cada banda é uma classe. Se você mudar este número então também precisa alterar o priomap.
- priomap (mapa de prioridades): Se não forem fornecidos filtros tc para classificar o tráfego, a qdisc PRIO verifica a prioridade TC_PRIO para decidir como deve fazer a fila do tráfego.
Isto funciona do mesmo modo que para a pfifo_fast. Para maiores detalhes procure a pfifo_fast em Administração de Largura de Banda no Linux.As bandas são classes e são denominadas maior:1 a maior:3 por default. Se determinada qdisc PRIO é chamada de 12:, faça um tc filter traffic para 12:1 para aumentar a sua prioridade.
Repetindo: a banda 0 vai para o número menor 1, a banda 1 para o número menor 2, etc.
Exemplo de configuração
Vamos criar a seguinte árvore:
1: qdisc raiz / | \ / | \ / | \ 1:1 1:2 1:3 classes | | | 10: 20: 30: qdiscs qdiscs sfq tbf sfq banda 0 1 2
A massa do tráfego irá para 30:, o tráfego interativo para 20: ou 10:.
Linhas de comando:
# tc qdisc add dev eth0 root handle 1: prio ## Isto cria instantaneamente as classes 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
Agora vamos ver o que criamos:
# 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 você pode ver, a banda 0 já tem algum tráfego e um pacote foi enviado enquanto rodamos este comando!
Agora vamos fazer uma transferência de dados em massa com uma ferramenta que define as flags TOS corretamente:
# scp tc O endereço de e-mail address está sendo protegido de spambots. Você precisa ativar o JavaScript enabled para vê-lo. :./ O endereço de e-mail address está sendo protegido de spambots. Você precisa ativar o JavaScript enabled para vê-lo. '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 pode ser visto, todo o tráfego foi para o manipulador 30:, a banda de prioridade mais baixa - justamente o que queríamos que acontecesse. Agora, para verificar se o tráfego interativo vai para bandas mais altas, vamos criá-lo:
# 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)
Funcionou! Todo o tráfego interativo foi para 10:, a qdisc com a prioridade mais alta. Nenhum tráfego foi enviado para a prioridade mais baixa que, anteriormente, recebeu toda scp.
A famosa qdisc CBQ
A CBQ é a qdisc mais complexa, a mais conhecida, a menos compreendida e, provavelmente, a mais difícil de fazer funcionar. Isto não é porque seus autores sejam carrascos ou incompetentes, muito pelo contrário, é porque o algoritmo CBQ não é muito preciso e não combina com o jeito do Linux trabalhar.
Além se ser uma qdisc com classe, a CBQ também é um modelador e, justamente por este fato, ela não trabalha muito bem. Deveria ser o seguinte: se tentarmos modelar uma conexão de 100mbit/s para 1mbit/s, o link deveria ficar ocioso 90% do tempo; se não ficar, é preciso fazer um estrangulamento para que ele FIQUE 90% do tempo ocioso.
A ociosidade é difícil de ser medida, por isto a CBQ deriva o tempo ocioso do número de microsegundos decorridos entre solicitações de dados feitas pela camada de hardware. Isto pode ser usado para obter um valor aproximado de quanto o link está cheio ou vazio.
Este método é um tanto tortuoso e nem sempre fornece resultados apropriados. Por exemplo, qual será o tempo ocioso se a velocidade atual do link de uma interface não for suficiente para transmitir a carga total de 100mbit/s de dados? Um driver que não esteja bem implementado ou uma placa de rede PCMCIA, que também nunca chega nos 100mbit/s devido à arquitetura do bus, são duas situações nas quais o cálculo da ociosidade fica muito complicado.
A coisa fica ainda pior se considerarmos dispositivos de rede not-quite-real (não-bem-reais) como PPP sobre Ethernet ou PPTP sobre TCP/IP. A largura de banda efetiva nestes casos provavelmente é determinada pela eficiência de pipes para o espaço do usuário - que é imensamente grande.
Quem fizer estas medições vai descobrir que a CBQ nem sempre é precisa e que, algumas vezes, o resultado está completamente fora.
Em muitas circunstâncias, no entanto, a CBQ funciona bem. Com a documentação fornecida a seguir você deveria ser capaz de configurá-la para que funcione bem na maioria dos casos.
Modelagem CBQ em detalhes
A CBQ trabalha assegurando-se de que o link esteja ocioso o tempo suficiente para que ela possa diminuir a largura de banda para a taxa que foi configurada. Para isto, ela calcula o tempo que deveria decorrer entre a média de pacotes.
Durante as operações, o tempo ocioso efetivo é medido usando uma média exponencial, a exponential weighted moving average (EWMA), a qual considera que pacotes recentes sejam exponencialmente mais importantes que pacotes mais antigos. A loadaverage (média de carga) do UNIX é calculada da mesma forma.
O tempo ocioso calculado é subtraido do tempo calculado pela EWMA e o valor resultante é chamado de 'avgidle'. Um link perfeitamente carregado tem uma avgidle de zero: os pacotes chegam nos exatos intervalos calculados.
Um link com excesso de carga tem uma avgidle negativa. Se ela se tornar negativa em excesso, a CBQ interrompe seu trabalho por um tempo, ficando no estado 'overlimit' (acima do limite).
Por outro lado, um link ocioso pode acumular uma enorme avgidle, o que acaba ermitindo uma largura de banda infinita depois de algumas horas de silêncio. Para impedir que isto aconteça, a avgidle é truncada quando alcança o valor maxidle (ociosidade máxima).
Teoricamente, se a CBQ estiver em 'overlimit', ela poderia ajustar-se para o exato período de tempo calculado para a passagem dos pacotes, passar um pacote e depois ajustar-se novamente. Poderia... mas veja o parâmetro 'minburst' a seguir.
Estes são os parâmetros que podem ser utilizados para configurar modelagens:
- avpkt: (average packet) É o tamanho médio dos pacotes, medido em bytes. É necessário para calcular maxidle, que é derivado de maxburst, o qual é especificado em pacotes.
- bandwidth: (largura de banda) A largura de banda física do dispositivo, necessária para cálculos do tempo ocioso.
- cell: (célula) O tempo que um pacote leva para ser transmitido através de um dispositivo pode aumentar em passos, baseados no tamanho do pacote. Por exemplo, um pacote de tamanho 800 e um pacote de 806 podem levar o mesmo tempo para serem transmitidos - isto determina a granularidade. Geralmente fixada em '8'. Precisa ser um número inteiro, uma potência de dois (2, 4, 8, 16, etc).
- maxburst: (rajada máxima) Este número de pacotes é usado para calcular maxidle de modo que, quando avgidle for igual a maxidle, este número de pacotes médios podem ser disparados antes que avgidle caia para zero. Aumente este número para ser mais tolerante com as rajadas. Não é possível definir maxidle diretamente, só através deste parâmetro.
- minburst: (rajada mínima) Como já foi explicado, a CBQ precisa de um ajuste quando chega no overlimit. A solução ideal é fazer isto durante exatamente o tempo ocioso calculado e depois passar um pacote. Para kernels Unix, no entanto, geralmente é difícil programar eventos mais curtos do que 10ms. Por isto é melhor se ajustar durante um período maior, depois passar a quantidade de pacotes definida em minburst numa leva só e, a seguir, "dormir" por mais um tempo minburst.
O tempo de espera é chamado de offtime. Valores maiores de minburst resultam numa modelagem mais precisa a longo prazo, mas também a rajadas maiores na escala de tempo de milisegundos. - minidle: (ociosidade mínima) Se a avgidle estiver abaixo de zero, estamos em overlimit e será preciso aguardar até que a avgidle alcance o valor suficiente para enviar um pacote. Para evitar que uma rajada súbita feche o link por um período de tempo muito prolongado, a avgidle recebe o valor de minidle se ela estiver com um valor muito baixo.
A minidle é especificada em microsegundos negativos: 10 significa que a avgidle é truncada em -10µs. - mpu: (minimum packet unit) Tamanho mínimo do pacote. É necessário porque um pacote, mesmo tendo tamanho zero, é ampliado para 64 bytes pela Ethernet e leva um certo tempo para ser transmitido. A CBQ precisa conhecer este valor para poder calcular com precisão o tempo ocioso.
- rate: (taxa) A taxa desejada de tráfego que sai desta qdisc - este é o acelerador!
Internamente, a CBQ possui uma porção de ajustes finos. Por exemplo, as classes que sabidamente não têm dados na fila não são solicitadas. As classes em overlimit são penalizadas baixando sua prioridade efetiva. Tudo muito esperto... mas tudo muito complicado
Comportamento da BCQ
Além da modelagem, que usa aproximações de ociosidade conforme já mencionado, a CBQ também atua como uma fila PRIO no sentido de que as classes possuem prioridades diferentes e que valores mais baixos de prioridade são apurados antes dos valores mais altos.
Cada vez que a camada de hardware requisita um pacote para ser enviado para fora da rede, um processo de ciranda (round robin) ponderado (Weighted Round Robin - WRR) é iniciado, começando pelas classes de prioridade mais baixa. Estas são agrupadas e inquiridas se possuem dados disponíveis. Se tiverem, recebem autorização para despachar. Depois que uma classe despachou um certo número de bytes, é a vez da próxima com o mesma prioridade.
Os seguintes parâmetros controlam o processo WRR:
- allot: (designar) Quando a CBQ externa é chamada para enviar um pacote através de uma interface, ela tentará todas as qdics internas (das classes), uma de cada vez, de acordo com a prioridade. Quando chega a vez de uma classe, ela pode despachar apenas uma quantidade limitada de dados. 'Allot' é a unidade básica desta quantidade. Veja o parâmetro 'weight' para mais informações.
- prio: (prioridade) A CBQ também pode atuar como um dispositivo PRIO. Classes internas com prioridade mais alta são inquiridas primeiro e, enquanto tiverem tráfego, outras classes não são apuradas.
- weight: (peso) O peso ajuda no processo ponderado de ciranda (Weighted Round Robin). Todas as classes, uma depois da outra, têm a possibilidade de enviar pacotes. Se houver classes com uma largura de banda significantemente maior do que outras, faz sentido permitir que elas enviem uma quantidade maior de dados em cada rodada do que as outras.
Uma CBQ soma todos os pesos sob uma classe e os normaliza, o que permite usar números arbitrários: apenas as proporções são importantes. O pessoal tem chutado 'taxa/10', o que parece funcionar bem. O peso re-normalizado é multiplicado pelo parâmetro 'allot' para determinar o volume de dados que pode ser enviado numa rodada.
Por favor, observe que todas as classes na hierarquia CBQ precisam compartilhar o mesmo número maior (major)!
Parâmetros CBQ que determinam o compartilhamento e o empréstimo de links
Além de simplesmente limitar certos tipos de tráfego, também é possível especificar quais classes podem emprestar capacidade de outras classes ou, por outro lado, quais podem emprestar largura de banda.
- isolated/sharing: (isolada/compartilhando) A classe que é configurada com 'isolated' não irá compartilhar largura de banda com as classes irmãs. Use isto se houver agências competindo ou pouco amigáveis.
O programa de controle tc (traffic control) também aceita 'sharing', que é o contrário de 'isolated'. - bounded/borrow: (travada/emprestar) Uma classe também pode ser 'bounded', o que significa que não tentará emprestar largura de banda de classes irmãs. O tc também aceita 'borrow', o contrário de 'bounded'.
Uma situação típica pode ser a de um link com duas agências, ambas 'isolated' e 'bouded'. Neste caso, as duas ficam realmente limitadas às taxas que lhes foram designadas e também não vão existir empréstimos entre elas.
Dentro de uma classe de agenciamento deste tipo pode haver outras classes para as quais é permitido negociar largura de banda.
Exemplo de configuração
A configuração a seguir limita o tráfego do servidor web em 5mbit e o tráfego SMTP em 3mbit. Juntos, eles não podem ultrapassar 6mbit. A NIC é de 100mbit e as classes podem emprestar largura de banda.
1: qdisc raiz | 1:1 classe filha / \ / \ 1:3 1:4 classes folha | | 30: 40: qdiscs (sfq) (sfq)
Esta parte instala a classe raiz e a costumeira 1:1. A classe 1:1 é travada (bouded) de modo que o total da largura de banda não exceda 6mbit.
# 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
Como já foi dito anteriormente, a CBQ exige um "montão" de configurações. A configuração da HTB é muito mais simples.
A seguir vêm as duas classes folha. Observe como o peso é escalonado de acordo com a taxa. As duas classes não são travadas, mas estão conectadas à classe 1:1 que está travada. Deste modo, a soma da largura de banda das duas classes nunca será maior do que 6mbit. As IDs das classes precisam estar dentro do mesmo número maior (major) que o da qdisc pai.
As duas classes têm uma qdisc FIFO por default, mas estas são subsituidas por filas SFQ para que cada fluxo de dados seja tratado igualmente.
# tc qdisc add dev eth0 parent 1:3 handle 30: sfq # tc qdisc add dev eth0 parent 1:4 handle 40: sfq
Estes comando, anexados diretamente à raiz, enviam o tráfego para as qdiscs certas.
# 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
Observe que 'tc class add' é usado para CRIAR classes dentro de uma qdisc, mas, para adicionar qdiscs a estas classes, usa-se 'tc qdisc add'.
Talvez você esteja se perguntando o que acontece com o tráfego que não é classificado por uma destas duas regras. Parece que, neste caso, os dados não serão limitados e serão processados por 1:0.
Se juntos, SMTP e web, tentarem ultrapassar o limite de 6mbit/s, a largura de banda será dividida de acordo com o parâmetro 'weight' (peso), o que dá 5/8 do tráfego para o servidor web e 3/8 para o servidor de email.
Com esta configuração também é possível dizer que o tráfego do servidor web terá sempre no mínimo 5/8 * 6 mbit = 3.75 mbit.
Outros parâmetros CBQ: split e defmap
Uma qdisc com classe precisa chamar filtros para determinar qual das classes deve receber um pacote. Além de acionar o filtro, a CBQ oferece outras opções: split e defmap. Este negócio é um tanto complicado de entender e felizmente não é vital. Em todo caso, aqui vai uma explicação.
Como é frequente filtrar pacotes apenas pelo campo TOS (Type Of Service - Tipo de Serviço), existe uma sintaxe especial. Sempre que a CBQ precisar descobrir a fila na qual um pacote precisa entrar, ela verifica se este nó é um 'split node' (nó dividido). Se for, uma das sub-qdiscs indicou que quer receber todos os pacotes com uma determinada prioridade, o que pode ser obtido através do campo TOS ou ser uma opção do socket determinada por aplicativos.
É feita uma operação AND com os bits da prioridade dos pacotes e o campo defmap para ver se existe uma coincidência. Em outras palavras, este é um atalho para criar um filtro muito rápido que identifica certas prioridades. Um defmap (mapa de definição) com um valor hexadecimal FF coincide com todas as prioridades e não vai filtrar coisa nenhuma. Um exemplo de configuração talvez esclareça melhor as coisas:
# 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
Este é o preâmbulo padrão da CBQ. Como sempre, um montão de números!
O defmap se refere a bits TC_PRIO, que são definidos como:
TC_PRIO.. Num Corresponde ao TOS ------------------------------------------------- BESTEFFORT 0 Maximizar Confiança FILLER 1 Minimizar Custo BULK 2 Maximizar Throughput (0x8) INTERACTIVE_BULK 4 INTERACTIVE 6 Minimizar Delay (0x10) CONTROL 7
O TC_PRIO.. número corresponde a bits contados da direita para a esquerda. Veja pfifo_fast no tutorial Administração de Largura de Banda no Linux para maiores detalhes.
Agora as classes interativa e bulk:
# 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
O 'split qdisc' (qdisc dividido) é 1:0 e é onde a escolha será feita. C0 (hexa) é 1100 0000 binário e 3F é 0011 1111. Estes dois binários, em conjunto, cobrem todos os bits. A primeira classe analisa os dois bits à esquerda (bits 6 e 7) que correspondem a 'interactive' e controle de tráfego. A segunda classe confere os bits restantes.
O nó 1:0 tem agora uma tabela como esta:
prioridade enviar para 0 1:3 1 1:3 2 1:3 3 1:3 4 1:3 5 1:3 6 1:2 7 1:2
Só para se divertir, você também pode passar uma 'change mask' (máscara de mudança) que indique exatamente quais a prioridades que você quer mudar. Você só vai precisar desta máscara se estiver rodando 'tc class change'. Por exemplo, para adicionar 'best effort traffic' (tráfego do melhor esforço) a 1:2, podemos rodar o seguinte:
# tc class change dev eth1 classid 1:2 cbq defmap 01/01
O mapa de prioridades em 1:0 agora é o seguinte:
prioridade enviar para 0 1:2 1 1:3 2 1:3 3 1:3 4 1:3 5 1:3 6 1:2 7 1:2
Balde de Token Hierárquico
Martin Devera (
) percebeu que a CBQ é complexa e não parece otimizada para muitas situações típicas. Seu enfoque hierárquico é muito apropriado para setups onde há uma largura de banda fixa que deve ser dividida para propósitos diferentes, garantindo uma determinada largura para cada um deles, com a possibilidade de especificar quanto de largura de banda pode ser emprestado. A HTB (Hierarchical Token Bucket - Balde de Token Hierárquico) funciona como a CBQ, mas não recorre a cálculos de tempo de ociosidade para modelar. Ao invés disto, é um Token Bucket Filter com classe - daí o nome. Possui apenas alguns parâmetros bem documentados no site do autor.
A configuração da HTB pode ir se tornando mais complexa sem maiores problemas. Contrariamente, a da CBQ é complexa por natureza, mesmo nos casos mais simples! HTB3 (cheque na página do autor para maiores detalhes sobre versões) agora faz parte do código fonte oficial do kernel (desde 2.4.20-pre1 e 2.5.31). Entretanto, pode ser possível que você precise de uma versão com patch para HTB3 do 'tc': o kernel HTB kernel partes do espaço do usuário precisam ser da mesma versão maior (major version) ou o 'tc' não funcionará com a HTB.
Se você já tem um kernel moderno ou se puder fazer um patch no seu kernel, não deixe de experimentar a HTB.
Exemplo de configuração
Funcionalmente, esta configuração é praticamente a mesma do exemplo dado na 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
O autor recomenda SFQ para as seguintes classes:
# 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
Adicione os filtros que dirigem o tráfego para as classes certas:
# 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
E isto é tudo. Nada e números misteriosos não explicados, nem de parâmetros não documentados.
A HTB, com certeza, parece maravilhosa - se 10: e 20: tiverem suas larguras de banda garantidas e ainda sobrar banda para dividir, elas fazem um empréstimo na razão 5:3, exatamente como você espera que seja feito.
O tráfego não classificado é roteado para 30:, com pouca largura de banda própria, mas que pode emprestar toda banda que estiver sobrando. Como foi escolhida a SFQ internamente, ganhamos a imparcialidade das divisões de graça... ou 'de grátis', como se ouve por aí
Classificando pacotes com filtros
Para determinar qual das classes deve processar um pacote, a assim chamada 'cadeia de classificação' (classifier chain) é acionada toda vez que uma escolha precisa ser feita. Esta cadeia é composta por todos os filtros associados à qdisc com classe que precisa tomar a decisão.
Repetindo a árvore, que não é uma árvore:
raiz 1: | _1:1_ / | \ / | \ / | \ 10: 11: 12: / \ / \ 10:1 10:2 12:1 12:2
Quando um pacote entra numa fila, a cadeia de filtros é consultada em cada ramo para ver se há instruções relevantes. Um setup típico pode ter um filtro em 1:1 que direciona pacotes para 12: e um filtro em 12: que envia pacotes para 12:2.
Esta última regra também pode ser associada a 1:1, mas ganha-se em eficiência se os testes mais específicos estiverem em posições mais baixas na cadeia.
Aliás, é bom saber que não é possível filtrar um pacote 'para cima'. E mais uma coisa: com HTB, todos os filtros devem ser associados à raiz.
Repetindo: os pacotes só podem entrar na fila de cima para baixo! Quando saem da fila, sobem novamente na direção da interface. Eles NÃO saem pela parte de baixo da árvore para chegarem até o adaptador de rede!
Alguns exemplos de filtragem
Vamos começar com as coisas mais óbvias que, para nossa sorte, são bastante fáceis. Digamos que temos uma qdisc PRIO chamada '10:', que contém três classes. Queremos que todo o tráfego que chega e que sai da porta 22 tenha a banda com a prioridade mais alta. Neste caso, os filtros seriam:
# tc filter add dev eth0 protocol ip parent 10: prio 1 u32 match \ ip dport 22 0xffff flowid 10:1 # tc filter add dev eth0 protocol ip parent 10: prio 1 u32 match \ ip sport 80 0xffff flowid 10:1 # tc filter add dev eth0 protocol ip parent 10: prio 2 flowid 10:2
O que estes comandos dizem é: associar à eth0, nó 10:, um filtro de prioridade 1 u32 que separa apenas o que é destinado para a porta 22 - os pacotes filtrados devem ser enviados para a banda 10:1; na segunda linha, o mesmo é feito quando a origem é a porta 80; a última linha diz para enviar todo o resto para a banda 10:2, a próxima prioridade mais alta.
è preciso adicionar eth0 (ou qualquer outro nome que a interface tiver) porque cada interface possui um espaço de nomes de manipuladores próprio.
Para selecionar um endereço IP, use o seguinte:
# tc filter add dev eth0 parent 10:0 protocol ip prio 1 u32 \ match ip dst 4.3.2.1/32 flowid 10:1 # tc filter add dev eth0 parent 10:0 protocol ip prio 1 u32 \ match ip src 1.2.3.4/32 flowid 10:1 # tc filter add dev eth0 protocol ip parent 10: prio 2 \ flowid 10:2
Isto coloca o tráfego para 4.3.2.1 e o tráfego vindo de 1.2.3.4 na fila com a prioridade mais alta - o restante vai para a fila com a segunda prioridade mais alta.
É possível concatenar critérios. Para casar o tráfego vindo de 1.2.3.4 através da porta 80, faça o seguinte:
# tc filter add dev eth0 parent 10:0 protocol ip prio 1 u32 match ip src 4.3.2.1/32 \ match ip sport 80 0xffff flowid 10:1
Os comandos de filtro mais usados
A maioria dos comandos a seguir começam com este preâmbulo:
# tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 ..
Estas são as chamadas coincidências 'u32', as quais casam com QUALQUER parte do pacote.
- Para o endereço de origem/destino: Máscara para a origem 'match ip src 1.2.3.0/24', máscara para o destino 'match ip dst 4.3.2.0/24'. Para separar um host único, use /32 ou omita a máscara.
- Para a porta de origem/destino em todos os protocolos IP: Para a origem, 'match ip sport 80 0xffff'; para o destinao, 'match ip dport 80 0xffff'.
- Para o protocolo IP (tcp, udp, icmp, gre, ipsec): Use os números citados no arquivo /etc/protocols. Por examplo, icmp é 1, neste caso 'match ip protocol 1 0xff'.
- Para fwmark: Pode-se marcar os pacotes com ipchains ou iptables para que a marca fique preservada depois de roteamentos através das interfaces. Isto é muito útil para, por exemplo, modelar apenas o tráfego na eth1 que chegou através da eth0. A sintaxe é:
# tc filter add dev eth1 protocol ip parent 1:0 prio 1 handle 6 fw flowid 1:1
Observe que a filtragem não usa u32!
Você pode colocar uma marca como esta:# iptables -A PREROUTING -t mangle -i eth0 -j MARK --set-mark 6
O número 6 é arbitrário.
Se você não quer se preocupar com a sintaxe de filtro completa do tc, então use apenas iptables e aprenda apenas a selecionar com fwmark. - Para o campo TOS: Para selecionar tráfego interativo com um mínimo de retardo (delay)
# tc filter add dev ppp0 parent 1:0 protocol ip prio 10 u32 \ match ip tos 0x10 0xff \ flowid 1:4
Use 0x08 0xff para a massa (bulk) do tráfego.
O dispositivo de fila intermediário
O dispositivo de fila intermediária (intermediate queueing) não é uma qdisc, mas seu uso está intimamente ligado às qdiscs. No Linux, as qdiscs estão associadas a dispositivos de rede e, tudo o que for colocado na fila do dispositivo, entra antes na fila da qdisc. Devido a este conceito surgem duas limitações:
- A modelagem só é possível na egressão (saída) - existe uma qdisc de egressão, mas suas possibilidades são muito limitadas quando comparadas com qdiscs com classe.
- Uma qdisc só pode ver o tráfego de uma interface, ou seja, não se pode estabelecer limitações globais.
A IMQ existe para ajudar a resolver estas duas limitações. Resumindo, é possível colocar qualquer coisa que você escolher numa qdisc. Pacotes marcados são interceptados no netfilter NF_IP_PRE_ROUTING e nos ganchos NF_IP_POST_ROUTING para passar através da qdisc associada a um dispositivo imq. Um alvo iptables é usado para marcar os pacotes.
Isto possibilita fazer uma modelagem de egressão (saída) porque se pode marcar apenas os pacotes vindos de algum ponto e/ou tratar interfaces como classes para especificar os limites globais. Também é possível fazer uma porção de coisas como colocar apenas o tráfego http numa qdisc, uma nova solicitação de conexão numa qdisc, etc.
Exemplo de configuração
A primeira coisa que nos vem a cabeça é usar uma modelagem de egressão (saída) para assegurar uma largura de banda alta. Esta configuração é igual a de qualquer interface:
tc qdisc add dev imq0 root handle 1: htb default 20 tc class add dev imq0 parent 1: classid 1:1 htb rate 2mbit burst 15k tc class add dev imq0 parent 1:1 classid 1:10 htb rate 1mbit tc class add dev imq0 parent 1:1 classid 1:20 htb rate 1mbit tc qdisc add dev imq0 parent 1:10 handle 10: pfifo tc qdisc add dev imq0 parent 1:20 handle 20: sfq tc filter add dev imq0 parent 10:0 protocol ip prio 1 u32 match \ ip dst 10.0.0.230/32 flowid 1:10
Neste exemplo, a u32 é usada para a classificação. Outros classificadores devem funcionar como o esperado. Depois, o tráfego precisa ser selecionado e marcado para entrar na fila im10.
iptables -t mangle -A PREROUTING -i eth0 -j IMQ --todev 0 ip link set imq0 up
Os alvos iptables IMQ são válidos nas cadeias PREROUTING e POSTROUTING da tabela mangle. A sintaxe é
IMQ [ --todev n ] n : number of imq device
Um alvo ip6tables também está disponível.
Observe que o tráfego não entra na fila quando o alvo é atingido - isto só acontece posteriormente. A localização exata da entrada do tráfego no dispositivo imq depende da direção do tráfego (entrada/saída). Estas localizações são os ganchos netfilter pre-definidos usados pelo iptables:
enum nf_ip_hook_priorities { NF_IP_PRI_FIRST = INT_MIN, NF_IP_PRI_CONNTRACK = -200, NF_IP_PRI_MANGLE = -150, NF_IP_PRI_NAT_DST = -100, NF_IP_PRI_FILTER = 0, NF_IP_PRI_NAT_SRC = 100, NF_IP_PRI_LAST = INT_MAX, };
Para o tráfego de ingresso, o imq se registra com a prioridade NF_IP_PRI_MANGLE + 1, o que significa que os pacotes entram no dispositivo imq imediatamente depois que a cadeia mangle PREROUTING tiver passado.
Para a saída, a imq usa NF_IP_PRI_LAST, que respeita o fato de que pacotes descartados não vão ocupar largura de banda.
Os patches e alguma informação adicional pode ser encontrada no site da imq.
Fonte de Referência
- Linux Advanced Routing & Traffic Control HOWTO de Thomas Graf, Gregory Maxwell, Remco van Mook, Martijn van Oosterhout, Paul B. Schroeder, Jasper Spaans e Pedro Larroy.