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.

sexta-feira, 29 de abril de 2011

A quem interessa o IPv6?

Tava aqui filosofando com meus botões...

Os provedores de Internet receberam "de graça" um bem que são endereços IPv4. Era só justificar que levava e era assim no mundo inteiro. Enquanto tinha endereços "a vontade", tal bem não tinha preço. Segundo a lei da oferta e procura, se a oferta é ilimitada, o preço é zero. Bem, hoje, a oferta já não é mais ilimitada.

Os endereços IPv4 são necessários para acessar a Internet. Como o mundo não migrou para IPv6, ele ainda é fundamental. Bem, se a oferta reduz (ou melhor, a "produção parou") e a procura só aumenta, temos uma inflação no preço do endereço.

Agora voltamos ao provedor de Internet. Se vc tinha um bem que vale zero, ele não era tão importante assim. Bastava que ele suprisse suas necessidades de negócio e pronto. Agora, quando este bem, ou melhor, ativo, começa a valer alguns milhões, a história muda. O pior de tudo é que, até agora, ninguém disse que o ativo endereço IPv4 é ou não é negociável. Recebeu de graça e agora pode vender. Entendeu a jogada? Quase a galinha dos ovos de ouro. Só não é melhor porque a galinha só vai botar ovos até, no máximo, 2012.

A implementação de IPv6 envolve custos e não possui um retorno visível para o cliente, exceto pelos problemas no serviço durante a migração. Se um provedor implementa a sua rede IPv6, e todos o fizerem, a procura por endereços irá cair, e inclusive, a oferta pode aumentar com os endereços vagos. Com isto o preço do ativo que eles atualmente possuem irá cair abruptamente. Alguém que visa o lucro é louco de fazer isto?

Moral da história: os provedores comerciais não irão implementar IPv6 enquanto a curva de crescimento do valor do endereço ainda estiver subindo e começarão a fazer gambiarras nos seus serviços para poder vender para outros novos provedores o que puder de seus endereços enquanto o preço estive em alta.

Os provedores não irão implementar a rede IPv6 até que:

  1. Os clientes exijam o serviço (IPv6 ou vou para outro que tenha), coisa que não vai acontecer se o cliente não souber do que se trata;
  2. O governo force sua implementação por força de lei ou norma;
  3. Ou, no caso de mau planejamento, eles tenham vendido mais endereços do que podiam e agora precisam comprar.
É, esperem uma inversão no serviço de Internet daqui a um, dois anos. Preços mais altos e qualidade pior devido as gambiarras para usar menos endereços.

domingo, 24 de abril de 2011

Luiz 1 x 0 SW de gerenciamento de celular

Como responsável da área da computação em casa, eu estava realizando a migração do celular da mulher. Bem, vamos dizer que é um celular exótico: gradiente fabricado pela francesa Sagem. Para a época, o celular era muito bom e ainda funciona bem. Agora voltando ao assunto...

A ferramenta de exportação foi terceirizada para uma empresa que, claro, não suporta mais este celular e nem vende o produto que o fazia. Com muita luta, achei uma versão demo que faz o que eu queria, exceto pelo fato de ser limitada a 6 contatos (o celular tem algumas centenas deles). Procura aqui, ali e nada de solução. Conclui que eu teria que fazer isto sozinho!

Tem um debugger para windows muito bom: w32dsm. Faz milagres. Pena que eu não tive assembly x86 na universidade. Com ele, busquei qualquer referencia a string "contacts" na memoria e coloquei um breakpoint depois de cada uma. Rodei o programa e foi contando o breakpoint que batia 6 vezes. E não é que eu achei?!

String Resource ID=17035: "Reading contacts: Mobile memory"

Desta forma, existe algum loop do tipo:

while ??? {
    ???
    chamada1()
    ???
}
chamada1 ( ) { chamada2 () }
(...)
chamadaX () { char[] str = "Reading contacts: Mobile memory" }

Com esta informação, fuirastreando (stepover) as chamadas "ret" do assembly que representam o retorno da chamada de função até o ponto onde o stepover bateu novamente no breakpoint da string. Isto me indicou que eu havia chegado no loop de leitura dos contatos. Voltei para as intruções do loop e analisei uma passada completa. O resultado não foi nada inovador. Algumas comparações no começo do loop, muita coisa no meio e um incremental no final: o bom e velho "for":


for (i-0;???;i++) {
    ???
    chamada1()
    ???
}
Bastou observar com cuidado para ver qual comparação gerava o jump para fora do loop. Na sexta iteração, achei uma comparação interessante entre o EAX (neste caso, i) e uma região da memória EBP+018. Mais sugestivo é que a comparação é >=. Quando ambos eram 6, o processo saia do loop. Olhando este EAX+018, ele possui o valor 6 desde o início do loop.


a=6;
for (i-0;???;i++) {
    if (i >= a) break;
    chamada1()
    ???
}
Agora é a parte fácil :-) Durante um loop qualquer, ainda pelo debugger, troquei o valor da memória para algo como 0x1000, retirei o breakpoint e fui para o abraço. Se fosse fazer algo permanente, teria que trocar a intrução do jump para uma nop ou modificar a comparação para que esta sempre desse negativa.

Já tinha feito algo parecido no passado mas com if e não for. O loop é muito mais fácil de identificar.

O pior de tudo: vou ganhar só um "ah tah... obrigada" por todo este trabalho :-(

domingo, 3 de abril de 2011