Pacotes quebrados: A fragmentação do IP é defeituosa
Como ao contrário da rede telefónica pública, a Internet tem um design Packet Switched. Mas quão grandes podem ser estes pacotes?
CC BY 2.0 imagem ajmexico, inspirada por
Esta é uma pergunta antiga e os RFCs IPv4 respondem muito claramente. A idéia era dividir o problema em duas preocupações distintas:
-
Qual é o tamanho máximo de pacote que pode ser tratado pelos sistemas operacionais em ambas as extremidades?
-
Qual é o tamanho máximo permitido de datagramas que pode ser empurrado com segurança através das conexões físicas entre os hosts?
Quando um pacote é muito grande para uma conexão física, um roteador intermediário pode cortá-lo em vários datagramas menores a fim de torná-lo adequado. Este processo é chamado de fragmentação de IP “forward” e os datagramas menores são chamados de fragmentos de IP.
Image by Geoff Huston, reproduzido com permissão
A especificação IPv4 define os requisitos mínimos. Do RFC791:
Every internet destination must be able to receive a datagramof 576 octets either in one piece or in fragments tobe reassembled. Every internet module must be able to forward a datagram of 68octets without further fragmentation.
O primeiro valor – tamanho de pacote remontado permitido – normalmente não é problemático. IPv4 define o mínimo como 576 bytes, mas sistemas operacionais populares podem lidar com pacotes muito grandes, tipicamente até 65KiB.
O segundo é mais problemático. Todas as conexões físicas têm limites inerentes de tamanho de datagramas, dependendo do meio específico que utilizam. Por exemplo, Frame Relay pode enviar datagramas entre 46 e 4.470 bytes. ATM usa 53 bytes fixos, a Ethernet clássica pode fazer entre 64 e 1500 bytes.
A especificação define o requisito mínimo – cada ligação física deve ser capaz de transmitir datagramas de pelo menos 68 bytes. Para o IPv6 esse valor mínimo foi colidido até 1.280 bytes (veja RFC2460).
Por outro lado, o tamanho máximo de datagramas que podem ser transmitidos sem fragmentação não é definido por nenhuma especificação e varia de acordo com o tipo de link. Este valor é chamado de MTU (Maximum Transmission Unit).
A MTU define um tamanho máximo de datagramas em um link físico local. A internet é criada a partir de redes não homogêneas, e no caminho entre dois hosts pode haver links com valores MTU mais curtos. O tamanho máximo do pacote que pode ser transmitido sem fragmentação entre dois hosts remotos é chamado de MTU Path, e pode ser potencialmente diferente para cada conexão.
Evite a fragmentação
Um pode pensar que é bom construir aplicações que transmitem pacotes muito grandes e dependem de roteadores para realizar a fragmentação do IP. Isto não é uma boa ideia. Os problemas com esta abordagem foram discutidos pela primeira vez por Kent e Mogul em 1987. Aqui estão alguns destaques:
-
Para remontar um pacote com sucesso, todos os fragmentos devem ser entregues. Nenhum fragmento pode se corromper ou se perder em vôo. Simplesmente não há como notificar a outra parte sobre fragmentos perdidos!
-
O último fragmento quase nunca terá o tamanho ideal. Para transferências grandes isto significa que uma parte significativa do tráfego será composta por datagramas curtos subótimos – um desperdício de preciosos recursos de roteador.
-
Antes da remontagem, um hospedeiro deve manter na memória datagramas parciais, fragmentos de datagramas. Isto abre uma oportunidade para ataques de esgotamento de memória.
-
Subsequentes fragmentos carecem do cabeçalho de camada superior. O cabeçalho TCP ou UDP está presente apenas no primeiro fragmento. Isto torna impossível que as firewalls filtrem os datagramas dos fragmentos com base em critérios como portas de origem ou destino.
Uma descrição mais elaborada dos problemas de fragmentação de IP pode ser encontrada nestes artigos por Geoff Huston:
- Avaliar a fragmentação de pacotes IPv4 e IPv6
- Fragmentar IPv6
Não fragmentar – Pacote ICMP muito grande
Image por Geoff Huston, reproduzido com permissão
Uma solução para estes problemas foi incluída no protocolo IPv4. Um remetente pode definir a bandeira DF (Don’t Fragment) no cabeçalho IP, pedindo aos roteadores intermediários que nunca realizem a fragmentação de um pacote. Em vez disso, um roteador com um link com uma MTU menor enviará uma mensagem ICMP “para trás” e informará ao remetente para reduzir a MTU para essa conexão.
O protocolo TCP sempre define o flag DF. A pilha de rede procura cuidadosamente por mensagens ICMP “Pacote muito grande” e mantém o controle da característica “caminho MTU” para cada conexão. Esta técnica é chamada de “path MTU discovery”, e é mais comumente usada para TCP, embora também possa ser aplicada a outros protocolos baseados em IP. Ser capaz de entregar as mensagens ICMP “Pacote muito grande” é crítico para manter a pilha TCP funcionando perfeitamente.
Como a Internet realmente funciona
Num mundo perfeito, dispositivos conectados à Internet cooperariam e lidariam corretamente com os datagramas de fragmentos e os pacotes ICMP associados. Na realidade, porém, fragmentos de IP e pacotes ICMP são muito frequentemente filtrados.
Isso ocorre porque a internet moderna é muito mais complexa do que o previsto há 36 anos atrás. Hoje, basicamente ninguém está conectado diretamente à Internet pública.
Os dispositivos do cliente conectam-se através de roteadores domésticos que fazem NAT (Network Address Translation) e normalmente aplicam regras de firewall. Cada vez com mais frequência há mais de uma instalação NAT no caminho do pacote (por exemplo, NAT em nível de portadora). Então, os pacotes atingem a infra-estrutura do ISP onde existem “middle boxes” do ISP. Eles executam todo tipo de coisa estranha no tráfego: forçam a limitação de planos, aceleram as conexões, executam o registro, seqüestram solicitações DNS, implementam proibições de sites mandatadas pelo governo, forçam o cache transparente ou indiscutivelmente “otimizam” o tráfego de alguma outra forma mágica. As caixas do meio são usadas especialmente por telcos móveis.
Simplesmente, existem frequentemente múltiplas camadas entre um servidor e a Internet pública. Os provedores de serviços às vezes usam o roteamento Anycast BGP. Isto é: eles lidam com os mesmos intervalos de IP de múltiplos locais físicos ao redor do mundo. Dentro de um datacenter, por outro lado, é cada vez mais popular usar ECMP Equal Cost Multi Path para balanceamento de carga.
Cada uma destas camadas entre um cliente e um servidor pode causar um problema de MTU Path. Permita-me ilustrar isto com quatro cenários.
1. Client -> Server DF+ / ICMP
No primeiro cenário, um cliente carrega alguns dados para o servidor usando TCP para que o flag DF seja definido em todos os pacotes. Se o cliente falhar em prever uma MTU apropriada, um roteador intermediário irá soltar os pacotes grandes e enviar uma notificação ICMP “Pacote muito grande” de volta para o cliente. Esses pacotes ICMP podem ser descartados por dispositivos NAT de clientes mal configurados ou caixas intermediárias do ISP.
De acordo com o artigo de Maikel de Boer e Jeffrey Bosma de 2012 cerca de 5% do IPv4 e 1% dos hosts IPv6 bloqueiam os pacotes ICMP de entrada.
A minha experiência confirma isso. As mensagens ICMP são, de fato, frequentemente descartadas por causa das vantagens de segurança percebidas, mas isso é relativamente fácil de corrigir. Um problema maior é com certos ISPs móveis com caixas intermediárias estranhas. Estes frequentemente ignoram completamente o ICMP e realizam uma reescrita de conexão muito agressiva. Por exemplo Orange Polska não só ignora mensagens ICMP “Pacote muito grande” de entrada, mas também reescreve o estado da conexão e clica o MSS em um 1344 bytes não negociáveis.
2. Cliente -> Servidor DF- / fragmentação
No próximo cenário, um cliente carrega alguns dados com um protocolo diferente do TCP, que tem o flag DF desmarcado. Por exemplo, este pode ser um usuário jogando um jogo usando UDP, ou tendo uma chamada de voz. Os grandes pacotes de saída podem ficar fragmentados em algum ponto do caminho.
Podemos emular isto lançando ping
com um grande tamanho de carga útil:
$ ping -s 2048 facebook.com
Este particular ping
falhará com cargas úteis maiores que 1472 bytes. Qualquer tamanho maior será fragmentado e não será entregue corretamente. Existem várias razões pelas quais os servidores podem não conseguir resolver os fragmentos, mas um dos problemas mais populares é o uso do balanceamento de carga ECMP. Devido ao hashing do ECMP, o primeiro datagrama contendo um cabeçalho de protocolo provavelmente será balanceado para um servidor diferente do resto dos fragmentos, impedindo a remontagem.
Para uma discussão mais detalhada desta questão, veja:
- Nosso anterior write up no ECMP.
- Como o Google tenta resolver problemas de fragmentação do ECMP com o balanceador de carga Maglev L4.
Outras vezes, a má configuração do servidor e do roteador é um problema significativo. De acordo com o RFC7852, entre 30% e 55% dos servidores deixam cair datagramas IPv6 contendo header de fragmentação.
3. Server -> Client DF+ / ICMP
O próximo cenário é sobre um cliente baixando alguns dados sobre TCP. Quando o servidor falha em prever a MTU correta, ele deve receber uma mensagem ICMP “Packet too big” (Pacote muito grande). Fácil, certo?
Sadly, não é, novamente devido ao roteamento ECMP. A mensagem ICMP provavelmente será entregue no servidor errado – o hash de 5 tubos do pacote ICMP não irá corresponder ao hash de 5 tubos da conexão problemática. Nós escrevemos sobre isso no passado, e desenvolvemos um daemon simples de espaço do usuário para resolvê-lo. Ele funciona transmitindo a notificação de entrada do ICMP “Pacote muito grande” para todos os servidores ECMP, esperando que aquele com a conexão problemática a veja.
Adicionalmente devido ao roteamento Anycast, o ICMP pode ser entregue no datacenter errado! O roteamento da Internet é frequentemente assimétrico e o melhor caminho de um roteador intermediário pode direcionar os pacotes ICMP para o lugar errado.
Notificações de ICMP “Pacote muito grande” podem resultar em conexões paradas e com tempo de inatividade. Isto é frequentemente chamado de buraco negro de PMTU. Para ajudar neste caso pessimista o Linux implementa uma alternativa – MTU Probing RFC4821. O MTU Probing tenta identificar automaticamente pacotes descartados devido ao MTU errado, e usa a heurística para afiná-lo. Esta funcionalidade é controlada através de um sysctl:
$ echo 1 > /proc/sys/net/ipv4/tcp_mtu_probing
Mas a sondagem MTU não é isenta de problemas próprios. Primeiro, ela tende a categorizar erroneamente a perda de pacotes relacionada ao congestionamento como problemas de MTU. Conexões de longa duração tendem a terminar com um MTU reduzido. Em segundo lugar, o Linux não implementa o MTU Probing para IPv6.
4. Server -> Client DF- / fragmentation
Finalmente, há uma situação em que o servidor envia grandes pacotes usando um protocolo não-TCP com o bit DF claro. Neste cenário, os grandes pacotes serão fragmentados no caminho para o cliente. Esta situação é melhor ilustrada com grandes respostas DNS. Aqui estão duas requisições DNS que irão gerar grandes respostas e serão entregues ao cliente como múltiplos fragmentos de IP:
$ dig +notcp +dnssec DNSKEY org @199.19.56.1$ dig +notcp +dnssec DNSKEY org @2001:500:f::1
Estas requisições podem falhar devido ao roteador doméstico já mencionado, NAT quebrado, instalações de ISP quebradas, ou configurações de firewall muito restritivas.
De acordo com Boer e Bosma cerca de 6% do IPv4 e 10% dos hosts IPv6 bloqueiam datagramas de fragmentos de entrada.
Aqui estão alguns links com mais informações sobre os problemas específicos de fragmentação que afectam o DNS:
- DNS-OARC Reply Size Test
- IPv6, Large UDP Packets and the DNS
Diga que a Internet ainda funciona!
Com todas estas coisas a correr mal, como é que a Internet ainda consegue funcionar?
CC BY-SA 3.0, fonte: Wikipedia
Isto deve-se principalmente ao sucesso da Ethernet. A grande maioria dos links na internet pública são Ethernet (ou derivados dela) e suportam a MTU de 1500 bytes.
Se você assumir cegamente a MTU de 1500, você ficará surpreso com a frequência com que ela funcionará bem. A Internet continua funcionando principalmente porque todos nós estamos usando uma MTU de 1500 e raramente precisamos fazer fragmentação de IP e enviar mensagens ICMP.
Isso deixa de funcionar em uma configuração incomum com links com uma MTU não-padrão. VPNs e outros softwares de túneis de rede devem ter cuidado para garantir que os fragmentos e mensagens ICMP estejam funcionando bem.
Isso é especialmente visível no mundo IPv6, onde muitos usuários se conectam através de túneis. Ter uma passagem saudável do ICMP em ambos os sentidos é muito importante, especialmente porque a fragmentação em IPv6 basicamente não funciona (citamos duas fontes que afirmam que entre 10% e 50% dos hosts IPv6 bloqueiam o cabeçalho do fragmento IPv6).
Desde que os problemas de MTU do Caminho em IPv6 são tão comuns, muitos servidores IPv6 bloqueiam o MTU do Caminho para o protocolo mandado no mínimo de 1280 bytes. Esta abordagem comercializa um pouco de desempenho para melhor confiabilidade.
Verificador de buraco negro ICMP online
Para ajudar a explorar e depurar estes problemas, nós construímos um verificador online. Você pode encontrar duas versões do teste:
- IPv4 versão: http://icmpcheck.popcount.org
- IPv6 versão: http://icmpcheckv6.popcount.org
Estes sites lançam dois testes:
- O primeiro teste irá entregar mensagens ICMP ao seu computador, com a intenção de reduzir o Path MTU a um valor ridiculamente pequeno.
- O segundo teste irá enviar datagramas de fragmentos de volta para si.
Recebendo um “passe” em ambos os testes deverá dar-lhe uma garantia razoável de que a Internet do seu lado do cabo está a comportar-se bem.
Também é fácil executar os testes a partir da linha de comandos, no caso de querer executá-lo no servidor:
perl -e "print 'packettoolongyaithuji6reeNab4XahChaeRah1diej4' x 180" > payload.bincurl -v -s http://icmpcheck.popcount.org/icmp --data @payload.bincurl -v -s http://icmpcheckv6.popcount.org/icmp --data @payload.bin
Isto deverá reduzir o caminho MTU para o nosso servidor para 905 bytes. Você pode verificar isso olhando para a tabela de cache de roteamento. No Linux você faz isso com:
ip route get `dig +short icmpcheck.popcount.org`
É possível limpar o cache de roteamento no Linux:
ip route flush cache to `dig +short icmpcheck.popcount.org`
O segundo teste verifica se os fragmentos são entregues corretamente ao cliente:
curl -v -s http://icmpcheck.popcount.org/frag -o /dev/nullcurl -v -s http://icmpcheckv6.popcount.org/frag -o /dev/null
Sumário
Neste post de blog descrevemos os problemas com a detecção de valores de MTU de caminho na internet. ICMP e datagramas fragmentados são frequentemente bloqueados em ambos os lados das conexões. Os clientes podem encontrar firewalls mal configurados, dispositivos NAT ou usar ISPs que interceptam conexões de forma agressiva. Os clientes também usam frequentemente túneis VPN ou IPv6 que, mal configurados, podem causar problemas de MTU.
Servidores, por outro lado, cada vez mais dependem de Anycast ou ECMP. Ambas estas coisas, assim como a má configuração do router e firewall são muitas vezes uma causa de queda de ICMP e fragmentos de datagramas.
Finalmente, esperamos que o teste online seja útil e possa dar-lhe mais informações sobre o funcionamento interno das suas redes. O teste tem exemplos úteis da sintaxe do tcpdump, útil para obter mais informações. Feliz depuração da rede!
É excitante resolver problemas de fragmentação para 10% da Internet? Estamos contratando engenheiros de sistemas de todas as faixas, programadores Golang, C wranglers, e estagiários em múltiplos locais! Junte-se a nós em São Francisco, Londres, Austin, Champaign e Varsóvia.
-
Em IPv6 a fragmentação “forward” funciona de forma ligeiramente diferente do que em IPv4. Os roteadores intermediários estão proibidos de fragmentar os pacotes, mas a fonte ainda o pode fazer. Isto é muitas vezes confuso – um host pode ser solicitado a fragmentar um pacote que ele transmitiu no passado. Isto faz pouco sentido para protocolos sem estado como o DNS. ︎
-
Em uma nota lateral, também existe uma “unidade mínima de transmissão”! No enquadramento Ethernet comumente utilizado, cada datagrama transmitido deve ter pelo menos 64 bytes na Camada 2. Isto significa 22 bytes no UDP e 10 bytes na camada TCP. Múltiplas implementações usadas para vazar memória não-inicializada em pacotes mais curtos! ︎
-
Stritamente falando em IPv4 o pacote ICMP é chamado “Destination Unreachable, Fragmentation Needed and Don’t Fragment was Set” (Destino Inacessível, Fragmentação Necessária e Não Fragmentar foi Definido). Mas eu acho a descrição de erro do ICMP IPv6 “Pacote muito grande” muito mais clara. ︎
-
Como dica, a pilha TCP também inclui um valor máximo permitido “MSS” nos pacotes SYN (MSS é basicamente um valor MTU reduzido pelo tamanho dos cabeçalhos IP e TCP). Isto permite aos hosts saber o que é a MTU nos seus links. Nota: isto não diz o que é a MTU nas dezenas de links de internet entre os dois hosts! ︎
-
Vamos errar no lado seguro. Uma MTU melhor é 1492, para acomodar para conexões DSL e PPPoE. ︎