Artigos 02:18 PM
Roteando múltiplos links de internet
A empresa onde trabalho depende 100% da internet para seu funcionamento, devido a isso procurei escolher uma boa opção de link de internet levando em consideração o melhor preço X a melhor qualidade de serviço.
É claro que as melhores opções são sempre os links dedicados, no entanto esses serviços são caros se comparados aos serviços DSL disponíveis.
Depois de um bom tempo usando o Velox da Telemar, fui apresentado a MundiVox que até que tem um serviço de boa qualidade, só deixando a desejar no primeiro mês de uso.
Apesar da boa qualidade do serviço, inevitavelmente acontecem algumas pequenas interrupções que as vezes não passam de 1 minuto, mas as vezes pode passar dos 10. Sendo assim decidi contratar mais um link de internet de outro fornecedor para efeitos de backup.
Uma vez que o outro link já estava instalado e funcionando surgiu o desafio: Como inserir o novo link na rede de modo a garantir que a internet não pare nunca (failover), e como combinar os 2 links para aumentar a velocidade de acesso a internet (load balancing)?
Inicialmente pensei que a solução fosse utilizar o recurso de “bonding” do kernel, no entanto após ler bem a pouca documentação disponível descobri que o bonding só funciona com links que façam parte de uma mesma rede (lógica).
Pesquisando mais a fundo descobri que a solução está nas tabelas de roteamento do sistema, na verdade na parte Avançada de Roteamento do sistema (o conhecido Linux Advanced Routing and Traffic Control)
A idéia básica do sistema é de criar tabelas de roteamento separadas para cada link e utilizar um gateway multipath ( com rotas alternativas ) como rota padrão. Para isso é necessário utilizar o pacote iproute2, disponível em http://linux-net.osdl.org/index.php/Iproute2
O pacote iproute2 fornece ferramentas avançadas para a configuração de interfaces, endereços, rotas e filtros e pode substituir as ferramentas padrões já conhecidas: ifconfig, route, arp.
No kernel, a opção IP: advanced router provê o suporte necessário para o recurso.
A configuração é simples e basicamente se dá em configurar o kernel e adicionar algumas tabelas de roteamento.
Configuração do kernel
# cd /usr/src/linux
# make menuconfig
Networking —>
Networking options —>
[*] IP: advanced router
[*] IP: policy routing
[*] IP: use netfilter MARK value as routing key
[*] IP: equal cost multipath
[ ] IP: equal cost multipath with caching support (EXPERIMENTAL)
O suporte a equal cost multipath with caching ainda é experimental e não costuma funcionar corretamente, é altamente recomendado desativar essa opção.
Nos meus testes ao habilitar o recurso, as tabelas de roteamento demoravam muito mais tempo para serem trocadas (quando falhas) além de que não conseguia pingar endereços externos por ambas interfaces (embora o tráfego funcionasse).
Compile e instale o novo kernel
# make
# cp arch/i386/boot/bzImage /boot/kernel-2.6.17-gentoo-r4
Instalação do iproute2
O iproute2 está disponível na árvore do portage e portanto basta um emerge para instalar o pacote.
# emerge iproute2
Usuários de outras distribuições podem usar os devidos sistemas de pacotes para instalar o iproute2 ou instalar a partir dos fontes.
# cd /usr/src/
# wget http://developer.osdl.org/dev/iproute2/download/iproute2-2.6.18-061002.tar.gz
# tar xzf iproute2-2.6.18-061002.tar.gz
# cd iproute2
# ./configure
# make
# make install
Configurando
Supondo que temos 2 links A e B (com IPs estáticos) de provedores distintos e gostaríamos de compartilha-los para nossa rede local na interface de rede C, podemos considerar o seguinte esquema:
INTERNET INTERNET
| |
ISP1 ISP2
10.0.0.1 172.16.0.1
\eth1 eth2/
10.0.0.5 172.16.0.7
NAT ROUTER (eth0)
|192.168.0.1
----+-----------------+---
No esquema acima temos a seguinte configuração:
IP0 = 192.168.0.1 NET0 = 192.168.0.0 BCAST0 = 192.168.0.255 IP1 = 10.0.0.5 GW1 = 10.0.0.1 NET1 = 10.0.0.0 BCAST1 = 10.0.0.255 IP2 = 172.16.0.1 GW2 = 172.16.0.7 NET2 = 172.16.0.0 BCAST2 = 172.16.0.255
Onde IP(0,1,2) é o endereço IP da interface, GW(1,2) é o gateway, NET(0,1,2) é o endereço da rede e BCAST(0,1,2) é o endereço de broadcast.
Interfaces de rede
Devemos configurar as interfaces normalmente porém não deve ser adicionada uma rota default.
# ip link set eth0 up # ip addr add IP0/24 broadcast BCAST0 dev eth0 # ip link set eth1 up # ip addr add IP1/24 broadcast BCAST1 dev eth1 # ip link set eth2 up # ip addr add IP2/24 broadcast BCAST2 dev eth2 (o número 24 após a barra representa o endereço de rede)
Tabelas de roteamento
Com o suporte a advanced router e policy routing no kernel, é possível criar tabelas de roteamento separadas para cada link.
O arquivo /etc/iproute2/rt_tables define as tabelas de roteamento disponiveis no sistema. Nele são definidas 4 tabelas padrões do sistema que são: local, main, default e unspec.
A tabela local armazena as rotas para as redes configuradas no sistema e é manipulada diretamente pelo kernel. As rotas padrões (configuradas pelo usuário) são inseridas na tabela main.
É possível ainda adicionar novas tabelas ao arquivo embora isso não seja extremamente necessário a menos que as tabelas sejam sempre chamadas através do nome ao invés do seu número.
# vi /etc/iproute2/rt_tables
#
# reserved values
#
255 local
254 main
253 default
0 unspec
#
# local
#
20 isp1
30 isp2
100 netgw
Certifique-se de que não existe nenhuma rota padrão configurada e defina a prioridade da tabela main para um número menor que os das interfaces de rede.
# ip route show table main|grep default
# ip route del default
# ip rule add prio 10 table main
Para que a configuração funcione corretamente é preciso se certificar de que os pacotes voltem sempre pela interface pela qual entraram. Dessa forma é necessário criar uma tabela separada para cada link especificando sua rota padrão. Também é necessário definir uma regra para cada tabela.
# ip rule add prio 20 table isp1
# ip route add route from NET1/24 default via GW1 dev eth1
# ip route append prohibit default table isp1 metric 1
# ip rule add prio 30 table isp2
# ip route add route from NET2/24 default via GW2 dev eth2
# ip route append prohibit default table isp2 metric 1
A linha com o parâmetro prohibit funciona de forma similar ao REJECT do itptables. Quando um cliente está transferindo dados por uma certa interface e ela deixa de funcionar, uma mensagem ICMP é enviada ao cliente de modo a fazer com que o mesmo termine a conexão e comece uma nova usando uma rota funcional.
Também é necessário criar uma tabela para o gateway multipath.
# ip rule add prio 100 table netgw
# ip route add default nexthop via GW1/24 dev eth1 nexthop via GW2/24 dev eth2
Pode-se utilizar o parâmetro weight de modo a especificar o peso (preferência) da rota
NAT
O compartilhamento para a rede interna pode ser feito através do iptables mascarando a saída nas interfaces eth0 e eth1.
# iptables -t nat -A POSTROUTING -o eth1 -j MASQUERADE
# iptables -t nat -A POSTROUTING -o eth2 -j MASQUERADE
# echo 1 > /proc/sys/net/ipv4/ip_forward
Testando
Nesse momento já deve ser possível acessar um endereço externo por ambas as rotas.
# ping google.com -c 1 -I eth1 PING google.com (64.233.167.99) from 10.0.0.5 eth1: 56(84) bytes of data. 64 bytes from 64.233.167.99: icmp_seq=1 ttl=243 time=191 ms # ping google.com -c 1 -I eth2 PING google.com (64.233.167.99) from 172.16.0.1 eth2: 56(84) bytes of data. 64 bytes from 64.233.187.99: icmp_seq=1 ttl=243 time=115 ms
Vale lembrar que como o kernel guarda um cache das rotas utilizadas é provável que quando um dos links falhar demore alguns mimnutos até que a rota indisponível seja deletada e uma nova seja criada utilizando o link funcional.
É possível definir através do arquivo /proc/sys/net/ipv4/route/gc_timeout o tempo máximo que o kernel espera até tentar uma nova rota quando perceber que a atual está morta.
Também é possível forçar a “limpeza” do cache de roteamento com o seguinte comando:
# ip route flush cache dev ethX (onde X é o número da interface)
Dessa forma é possível criar um script que tente pingar os gateways (ou outro endereço público) e caso não haja resposta apagar o cache da tabela de roteamento.
Melhorando a detecção de rotas mortas
O kernel linux armazena as rotas de rede utilizadas em um tabela de cache que é consultada a cada acesso. Quando uma rota não é encontrada utiliza-se os gateways disponíveis para traçar uma nova rota e automaticamente ela é armazenada.
Quando tentamos acessar um endereço por uma certa rota e esta falha, não necessariamente temos problemas com o gateway ou com a interface, as vezes o problema pode ser com o endereço de destino. Dependendo do motivo da falha, certas ações são tomadas pelo sistema.
Após um certo tempo, o gateway será considerado como morto e marcado como indisponível (”unreachable”). Se o problema for com a interface o link será desativado e as rotas serão limpadas (flushed) do cache forçando a utilização de uma nova rota.
Caso o problema seja outro (um gateway “morto” no caminho por exemplo) não será possível detectar a falha e as tentativas de conexão continuarão tentando essa rota e falhan do. Após mais um certo tempo o cache de rotas irá expirar e só então uma nova rota (funcional) será tentatada.
Da mesma forma, caso a falha tenha sido da interface e/ou cabeamento, não será possível re-inserir as rotas automaticamente quando o problema se resolver automaticamente, necessitando assim de uma intervenção do administrador para ativação das mesmas.
O Julian Anastasov já há alguns anos disponibiliza alguns patchs para melhor esse recurso no kernel, tornando a detecção de gateway/rotas mortas mais inteligente. Os patches podem ser encontrados em http://www.ssi.bg/~ja/
Com os patches aplicados é possível por exemplo usar “prot static” para todas as rotas. Dessa forma, mesmo que uma interface se torne indisponível o kernel não irá remover as rotas e uma vez que a interface seja disponibilizada novamente tudo estará funcionando perfeitamente (ideal para esquemas de failover).
# cd /usr/src/
# wget http://www.ssi.bg/~ja/routes-2.6.17-12.diff
# patch -p1 < routes-2.6.17-12.diff
# cd linux
# make && make modules install
# cp arch/i386/boot/bzImage /boot/kernel-2.6.17-gentoo-r4
Configure o gerenciador de boot e reinicialize com o novo kernel
Atualizado: Gráficos Comparativos
O balanceamento de uso dos links pode ser comprovado nos gráficos abaixo. O primeiro gráfico representa as 2 semanas anteriores a instala~ao do segundo link. Os demais representam 2 semanas de uso com os 2 links.



Automatizando a configuração no Gentoo
No gentoo é possível definir funções no arquivo de configurações de redes. Tais funções são executadas antes/após carregar uma interface e antes/após descarregar uma interface. Dessa forma é possível automatizar o processo de criar as rotas e regras.
A função postup que implemento abaixo não é muito inteligente, porém dá uma idéia básica de como automatizar o processo de configuração das redes.
A cada interface que for configurada as ações de adicionar/remover regra/rota são executadas.
# vi /etc/conf.d/net
config_eth0=( “192.168.0.1 netmask 255.255.255.0″ )
config_eth1=( “10.0.0.5 netmask 255.255.255.0″ )
config_eth2=( “172.16.0.1 netmask 255.255.255.0″ )
append_eth0=( “default via 10.0.0.1 src 10.0.0.5 proto static table isp1″
“prohibit default table isp1 metric 1 proto static”)
append_eth1=( “default via 172.16.0.7 src 172.16.0.1 proto static table isp2″
“prohibit default table isp2 metric 1 proto static”)
rule_eth0=( “from 10.0.0.0/24 priority 20 table isp1″ )
rule_eth1=( “from 172.16.0.0/24 priority 30 table isp2″ )
multipath_route=( “table netgw proto static nexthop via 10.0.0.1
dev eth1 nexthop via 172.16.0.7 dev eth2″ )
multipath_rule=( “prio 100 table netgw”
“prio 10 table main” )
postup() {
local x=”append_${IFVAR}[@]”
local -a appends=( “${!x}” )
if [[ -n ${appends} ]] ; then
einfo “Appending route”
eindent
for x in “${appends[@]}”; do
ip route del ${x} 1>&2 2> /dev/null
ebegin “${x}”
ip route append ${x}
eend $?
done
eoutdent
#Flush the cache
ip route flush cache dev “${IFACE}”
fi
local x=”rule_${IFVAR}[@]”
local -a rules=( “${!x}” )
if [[ -n ${rules} ]]; then
einfo “Adding IP policy routing rules”
eindent
for x in “${rules[@]}”; do
ip rule del ${x} 1>&2 2> /dev/null
ebegin “${x}”
ip rule add ${x}
eend $?
done
eoutdent
fi
if [[ -n ${multipath_route} ]]; then
for x in “${multipath_route[@]}”; do
einfo “Adding multipath route”
eindent
ip route del default 1>&2 2> /dev/null
ip route del ${x} 1>&2 2> /dev/null
ebegin “${x}”
ip route add default ${x}
eend $?
eoutdent
done
fi
if [[ -n ${multipath_rule} ]]; then
for x in “${multipath_rule[@]}”; do
einfo “Adding multipath rule”
eindent
ip rule del ${x} 1>&2 2> /dev/null
ebegin “${x}”
ip rule add ${x}
eend $?
eoutdent
done
fi
}
Referências
- http://www.linuxhorizon.ro/iproute2.html
- http://linux-ip.net/html/adv-multi-internet.html
- http://www.lartc.org/lartc.html#LARTC.RPDB.MULTIPLE-LINKS
- http://www.ssi.bg/~ja/nano.txt
- http://www.ssi.bg/~ja/dgd-usage.txt
Posts Relacionados:
Related posts brought to you by Yet Another Related Posts Plugin.
on Friday November 17th, 2006 at 12:19 PM 1.Daniel Hoisel said …
Caro Claudinei, fiz alguns testes com balanceamento de carga com dois links usando iproute2, mas em todas as configurações que testei ocorreu um efeito colateral indesejado. Os acessos a sites seguros, principalmente a Home Banks eram bloqueados pelo fato de em uma mesma sessão HTTPS haverem várias conexões, cada uma saindo por uma rota diferente. Nesse seu exemplo acontece isso também?
on Friday November 17th, 2006 at 12:59 PM 2.Myers said …
Olá Daniel. Nos testes que fiz também detectei os mesmos problemas.
No post posterior (forçando as rotas) eu relato isso e mostro como rotear os pacotes destinados a porta 443 por um dos links apenas.
on Friday November 17th, 2006 at 02:08 PM 3.Marcio Correia said …
Caros,
Esse problema pode acontecer caso se defina um tempo de vida muito pequeno para as rotas cacheadas ou se utilize a função equalize do iproute2.
Fora essas situações, o cache de rotas garante esse tráfego por um único gateway.
Tenho algo parecido com isso no nosso produto (Nettion) e funciona perfeitamente.
on Friday November 17th, 2006 at 03:18 PM 4.Myers said …
Na verdade o problema que eu tive (imagino ser o mesmo do Daniel) ocorria principalmente com internet banking onde é comum mudar o servidor que processa a sessão. Nesse caso a solução foi forçar a rota de conexão segura (https/443) por um dos gateways.
on Saturday November 18th, 2006 at 09:58 PM 5.Hiram said …
Excelente dica!