Balanceamento de carga através de dois links de Internet utilizando Linux

por Marcos Paulo Ferreira Rebello (caco) rebello@mensa.org.br

Scripts para fazer o balanceamento usando IPROUTE2 em 2 links para internet (virtua + speedy por exemplo).

Funciona com DHCP (desde que sempre na mesma rede) e verifica se os links estão ativos a cada n segundos.

ps: Ainda não funciona com DHCP nas duas interfaces.

Explicações:

A tabela main foi colocada com prioridade 100 (ao invés do 255
padrão) para que outras tabelas possam ser colocadas com prioridade posterior à main.

A tabela main faz o roteamento básico de todas as interfaces. Dela foi excluído apenas o roteador padrão. As redes acessíveis
diretamente pelas interfaces são roteadas aqui.

Pacotes provenientes do IP da rede da interface 1 são roteados pela interface 1 através da tabela 1, com prioridade 110 (acima de 100).

Pacotes provenientes do IP da rede da interface 2 são roteados pela interface 2 através da tabela 2, com prioridade 111 (acima de 100).

A tabela 3 (prioridade 120) é a responsável pelo balanceamento. Ela roteia por qualquer um dos gateways disponíveis, lenvado em
consideração o peso (weight).

O script check_routes verifica se os gateways podem ser acessados
(fazendo um ping). O acesso aos gateways é feito pela rota padrão
da interface (tabela main). Caso um dos links não esteja disponível, adiciona a tabela 1 ou 2 com prioridade 115 (antes da tabela 3), fazendo com que a rota padrão passe a ser a rota da interface da tabela adicionada.

Caso os dois links estejam fora, não há rota que resolva...

Instalação:

Todos os arquivos devem ser colocados em /etc/two_routes.

cp /etc/two_routes/init.d.firewall /etc/init.d/firewall
cp /etc/two_routes/init.d.routes /etc/init.d/routes
chmod a+x /etc/init.d/firewall
chmod a+x /etc/init.d/routes

ln /etc/init.d/firewall /etc/rc2.d/S80firewall -s
ln /etc/init.d/routes /etc/rc2.d/S15routes -s

ps: rc2.d para o Debian. rc5.d em várias outras distros.

Configurar no arquivo /etc/two_routes/routes.conf quais são as
intefaces e os endereços de gateway (com ip fixo).

time_to_check = intervalo em segundos entre as verificações dos links.

ping_wait = time-out do ping usado na verificação.

ping_count = quantos pings enviar (precisa receber pelo menos um para considerar o link ativo

Caso use uma interface com DHCP:


ln /etc/two_routes/dhclient-enter-hooks /etc/dhcp3/dhclient-enter-hooks -s
ln /etc/two_routes/dhclient-exit-hooks /etc/dhcp3/dhclient-exit-hooks -s

dhclient-enter-hooks = desativa alterações no /etc/resolv.con,
hostname e default gateway (feitas por padrão pelo dhclient3)
e guarda o endereço de gateway obtido pelo dhcp.

dhclient-exit-hooks = caso o IP do dhcp seja alterado, reinicia
as rotas.

Caso use noip e/ou openvpn na interface do dhcp, editar o arquivo
/etc/dhcp3/dhclient-exit-hooks.

Não é necessário alterar outras configurações de DHCP ou na
inicialização da rede.

Arquivos:


/etc/two_routes/routes.conf


IP=/sbin/ip
log_file='/var/log/two_routes'
pid_file='/var/run/check_routes.pid'
link_file='/etc/two_routes/links'
dhcp_gateway_file='/etc/two_routes/gw_dhcp'

[ -e $dhcp_gateway_file ] && . $dhcp_gateway_file

eth1="eth0"
eth2="eth1"

# if interface address use DHCP
GW_eth1="$GW_dhcp"

#GW_eth1=189.1.2.1
GW_eth2=200.1.2.1

time_to_check=120
ping_wait=5
ping_count=3

check_routes=on


/etc/two_routes/set_routes


#!/bin/bash

[ -e /etc/two_routes/routes.conf ] && . /etc/two_routes/routes.conf

rm -f $link_file

NET_eth1=$(ip addr show $eth1 | grep "inet " | cut -d " " -f 6)
NET_eth2=$(ip addr show $eth2 | grep "inet " | cut -d " " -f 6)

[ -z "$GW_eth1" ] && GW_eth1=$GW_eth2
[ -z "$GW_eth2" ] && GW_eth2=$GW_eth1

$IP rule flush
$IP route flush table 1
$IP route flush table 2
$IP route flush table 3
$IP route del default
#BUG
$IP route del default
$IP route flush cache

$IP rule add table main prio 100
[ -n "$NET_eth1" ] && $IP rule add from $NET_eth1 table 1 prio 110
[ -n "$NET_eth2" ] && $IP rule add from $NET_eth2 table 2 prio 111
$IP rule add table 3 prio 120

$IP route add table 1 default via $GW_eth1
$IP route add table 2 default via $GW_eth2
if [ "$GW_eth1" == "$GW_eth2" ]; then
$IP route add table 3 default via $GW_eth1
else
$IP route add table 3 default nexthop via $GW_eth2 weight 1 dev $eth2 \
nexthop via $GW_eth1 weight 2 dev $eth1
fi

if [ "$check_routes" == "on" ]; then
[ -e $pid_file ] && kill `cat $pid_file`
daemon -n check_routes -F $pid_file -r -L $time_to_check /etc/two_routes/check_routes
fi


/etc/two_routes/check_routes


#!/bin/bash

[ -e /etc/two_routes/routes.conf ] && . /etc/two_routes/routes.conf
[ -e $link_file ] && . $link_file

I1=`ping $GW_eth1 -c $ping_count -W $ping_wait | grep "0 received"`
I2=`ping $GW_eth2 -c $ping_count -W $ping_wait | grep "0 received"`

if [ "$I1" == "" ]; then
I1="On"; L1="LA1=\"On\""
else
I1="Off"; L1="LA1=\"Off\""
fi

if [ "$I2" == "" ]; then
I2="On"; L2="LA2=\"On\""
else
I2="Off"; L2="LA2=\"Off\""
fi

if [ "$I1$I2" != "$LA1$LA2" ]; then
date >> $log_file
echo "$eth1=$I1, $eth2=$I2." >> $log_file
$IP rule del prio 115
if [ "$I1$I2" == "OffOn" ]; then
$IP rule add table 2 prio 115
fi
if [ "$I1$I2" == "OnOff" ]; then
$IP rule add table 1 prio 115
fi
fi

echo $L1 > $link_file
echo $L2 >> $link_file


/etc/two_routes/firewall


#!/bin/bash
IPTABLES=/sbin/iptables

$IPTABLES -t nat -A POSTROUTING -o eth0 -j MASQUERADE
$IPTABLES -t nat -A POSTROUTING -o eth1 -j MASQUERADE
#$IPTABLES -t nat -A POSTROUTING -o tun0 -j MASQUERADE


/etc/two_routes/init.d.routes


#!/bin/sh

case "$1" in
start|restart)
/etc/two_routes/set_routes
;;
stop)
[ -e /etc/two_routes/routes.conf ] && . /etc/two_routes/routes.conf
[ -e $pid_file ] && kill `cat $pid_file`
;;
*)
echo "Usage: $0 {start|stop|restart}"
exit 1
;;
esac
exit 0


/etc/two_routes/init.d.firewall


#!/bin/sh
IPTABLES=/sbin/iptables

$IPTABLES -F
$IPTABLES -F -t nat
$IPTABLES -F -t mangle
$IPTABLES -X -t nat
$IPTABLES -X -t mangle
$IPTABLES -X

case "$1" in
start|restart)
/etc/two_routes/firewall
;;
stop)
;;
*)
echo "Usage: $0 {start|stop|restart}"
exit 1
;;
esac
exit 0


/etc/two_routes/dhclient-enter-hooks


#!/bin/bash

# disable make /etc/resolv.conf
make_resolv_conf() {
x=1
}

# disable set hostname
set_hostname() {
x=1
}

[ -e /etc/two_routes/routes.conf ] && . /etc/two_routes/routes.conf

# save gateway address
echo "GW_dhcp=\"$new_routers\"" > $dhcp_gateway_file

# disable default gateway in dhclient-script
new_routers=""


/etc/two_routes/dhclient-exit-hooks


#!/bin/bash

if [ "$reason" != "PREINIT" ] && [ "$new_ip_address" != "$old_ip_address" ]; then
/etc/two_routes/set_routes

# /usr/local/bin/noip2 -i $new_ip_address -u user_email -p user_password
# /etc/init.d/openvpn stop
# /etc/init.d/openvpn start
fi