Discuta este tópico no fórum

Se este conteúdo te ajudou, deixe um presente!

sexta-feira, 29 de julho de 2011

Arranhando roteamento avançado em Linux

Esse é para as pessoas que algum dia já conectaram duas placas de redes em um equipamento Linux e nada funcionou direito...

O Linux é um sistema operacional versátil. Opera desde celulares a grandes computadores. Dentro deste espectro, ele também é muito utilizado como o coração de uma gama enorme de roteadores. Um Linux não faz feio frente a um roteador Cisco, só talvez não com o mesmo desempenho. Enfim, com Linux, qualquer computador se torna um roteador avançado. Basta adicionar mais interfaces de rede.

Um dos problemas comuns de conectar 2 placas de rede a um computador é em relação ao roteador padrão. Sempre existe apenas um roteador padrão. Para uma única interface, o que em geral é encontrado em um computador convencional, temos as seguintes rotas:

# ip route
192.168.1.0/24 dev eth0  proto kernel  scope link  src 192.168.1.11
127.0.0.0/8 dev lo  scope link
default via 192.168.1.1 dev eth0

A primeira é da rede diretamente conectada pela eth0, a segunda, o mesmo da primeira para a interface de loopback. E a última é a rota para as outras redes (o roteador padrão). Quando adicionamos uma segunda interface de rede, aparece mais uma rota diretamente conectada:

# ip route
192.168.1.0/24 dev eth0  proto kernel  scope link  src 192.168.1.11
192.168.2.0/24 dev eth1  proto kernel  scope link  src 192.168.2.22
127.0.0.0/8 dev lo  scope link
default via 192.168.1.1 dev eth0

Em geral, a segunda interface de rede conecta uma rede isolada, acessível apenas através do próprio Linux (como roteador ou não).

Agora, quando a máquina que está conectada em duas redes não é a única forma de acessar a subrede, o que acontece? Imagine um cenário onde um Linux está conectado em duas redes. Simples não?

Rede A(192.168.1.0/24)              Rede B(192.168.2.0/24) 
       |............Roteador (R)...........|
   X...|                                   |....Y
       |........eth0 Linux (L) eth1........|                  
       |                                   |

Nem tanto. Se o equipamento X, conectado na rede A tenta falar com a eth0 do servidor Linux, ele responde sem problemas (pois está diretamente conectado). O mesmo ocorre quando o equipamento Y tenta falar com o servidor Linux pela interface eth1. Agora, se X precisa falar com o endereço da interface eth1? O que acontece?
  1. X prepara um pacote para 192.168.2.22
  2. X envia para o roteador pois o IP 192.168.2.22 não está nas suas redes locais
  3. Roteador recebe o pacote da rede A e repassa para a rede B
  4. O servidor Linux recebe o pacote pela interface eth1
O caminho de ida foi sem problemas. Agora a resposta:
  1. O servidor Linux prepara resposta para X
  2. X está em uma rede diretamente conectada (interface eth0). O pacote é enviado diretamente.
Olha que interessante! O pacote chegou pela interface eth1 mas saiu pela eth0 (e com o MAC dela). O roteador, se quisesse, não teria como restringir a resposta do servidor Linux.

Vamos a outro cenário. Uma máquina Z fora das duas redes, tenta falar com a a interface eth0.
  1. Roteador recebe o pacote vindo de Z, pela rede C, e repassa para a rede A
  2. O servidor Linux recebe o pacote pela interface eth0
  3. O servidor Linux prepara a resposta de Z 
  4. Como Z não é uma interface diretamente conectada, ele envia ao roteador padrão (192.168.1.1).
  5. O Roteador recebe o pacote vindo de (192.168.1.11) vindo da rede A e o repassa para Z
Sem problemas. Agora se Z estivesse falando com a eth1?
  1. Roteador recebe o pacote vindo de Z, pela rede C, e repassa para a rede B
  2. O servidor Linux recebe o pacote pela interface eth1
  3. O servidor Linux prepara a resposta de Z
  4. Como Z não é uma interface diretamente conectada, ele envia ao roteador padrão (192.168.1.1).
  5. O Roteador recebe o pacote de (192.168.2.22) vindo da rede A e REJEITA!. Como o pacote da rede B pode surgir dentro da rede A?
O problema é que a tabela de roteamento não faz distinção quanto ao ip de origem (ou interface de origem). Ele apenas observa o destino. Como o destino é fora da rede local, ele envia pelo roteador padrão. Não existe 2 roteadores "padrão". Como resolver esta questão?

O Linux permite que seja criada mais de uma tabela de roteamento. Basta acrescentar a qual tabela estamos nos referenciando no comando "ip route". Exemplo:

# ip route show table main
192.168.1.0/24 dev eth0  proto kernel  scope link  src 192.168.1.11
192.168.2.0/24 dev eth1  proto kernel  scope link  src 192.168.2.22
127.0.0.0/8 dev lo  scope link
default via 192.168.1.1 dev eth0

As tabelas "local" e "0" também são bem interessantes para quem só usava até agora o comando "route".

O que precisamos fazer é criar uma nova tabela. Primeiro, cadastre um nome (para não usar números)

echo "1 redeB" >>/etc/iproute2/rt_tables

Isto permite que redeB seja usada nos comandos ao invés de apenas números.

Agora, a nova tabela de roteamento pode ser recriada.

ip route add 192.168.2.0/24 dev eth1 table redeB
ip route add default via 192.168.2.1 dev eth1 table redeB

 Mas quando que esta tabela nova vai ser usada? Isto pode ser definido por regras. Esta regra faz com que todos os pacotes que chegam pela interface eth1 utilizem a tabela de rotas redeB ao invés das regras padrão.

ip rule add from 192.168.2.2/32 lookup redeB

E pode ser necessário (em geral é) limpar o cache de rotas:

ip route flush cache

Como sempre, toda esta configuração (exceto pelo cadastro do nome) é volátil e se perde ao derrubar a rede ou reiniciar o computador. Por isto, deve ser carregada em script junto ao processo de configuração da rede.

Um outro cenário onde a configuração de tabelas de roteamento distintas pode ser útil é o caso de uma VPN quando até o tráfego Internet da rede interna deve ser roteado para a VPN. O roteador VPN deve utilizar uma rota padrão que aponte para a Internet, para operar a VPN, mas os pacotes vindos da rede interna devem ser todos roteados pela VPN.

Outro caso seria em laptops com interface WLAN e LAN. Se as duas estiverem conectadas em segmentos distintos da rede local, para algumas máquinas uma das interfaces não será acessível.

Este é só um caso onde é necessário o roteamento avançado. Porém, roteamento avançado pode ser usado em uma infinidade de casos como múltiplas conexões para a Internet. Vale a pena buscar no Google...

Ah, se alguém sentiu falta dos comandos "ifconfig", "route", ta mais do que na hora de aprender o comando "ip". Ele é mais limpo, mais fácil e toda a saída pode ser usada como entrada, basta prefixar com "ip <modulo> add/del". Talvez eu escreva sobre isto no futuro...

Atualização: pode ocorrer casos de pacotes de "origem marciana", os "martian source". Por que isto? O Linux possui uma filtragem que bloqueia pacotes estranhos que chegam por uma interface mas seriam roteados por outra, chamado de "Reverse Path Filtering" (rp_filter). Ex: pacotes com IPs da intranet chegando pela interface conectada na internet ou vice-versa. Porém, para casos onde as duas redes podem se comunicar, esta filtragem não faz sentido desta forma. Por isto, é necessário mudar o modo de "1" ("Strict Reverse Path filtering") para "2" ("Loose Reverse Path filtering"). Isto deve ser definido nos parâmetros: net.ipv4.conf.default.rp_filter, net.ipv4.conf.all.rp_filter e/ou net.ipv4.conf.<interface>.rp_filter.