Pacchetti rotti: La frammentazione IP è difettosa
Al contrario della rete telefonica pubblica, internet ha un design Packet Switched. Ma quanto possono essere grandi questi pacchetti?
Immagine CC BY 2.0 di ajmexico, ispirata da
Questa è una vecchia domanda e le RFC IPv4 rispondono abbastanza chiaramente. L’idea era di dividere il problema in due preoccupazioni separate:
-
Qual è la dimensione massima dei pacchetti che può essere gestita dai sistemi operativi su entrambe le estremità?
-
Qual è la dimensione massima consentita del datagramma che può essere tranquillamente spinto attraverso le connessioni fisiche tra gli host?
Quando un pacchetto è troppo grande per un collegamento fisico, un router intermedio potrebbe tagliarlo in più datagrammi più piccoli per farlo entrare. Questo processo è chiamato frammentazione IP “forward” e i datagrammi più piccoli sono chiamati frammenti IP.
Immagine di Geoff Huston, riprodotta con permesso
La specifica IPv4 definisce i requisiti minimi. Dalla 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.
Il primo valore – la dimensione consentita del pacchetto riassemblato – non è tipicamente problematico. IPv4 definisce il minimo come 576 byte, ma i sistemi operativi popolari possono far fronte a pacchetti molto grandi, tipicamente fino a 65KiB.
Il secondo è più problematico. Tutte le connessioni fisiche hanno limiti intrinseci di dimensione del datagramma, a seconda del mezzo specifico che usano. Per esempio Frame Relay può inviare datagrammi tra 46 e 4.470 byte. ATM usa 53 byte fissi, Ethernet classica può fare tra 64 e 1500 byte.
La specifica definisce il requisito minimo – ogni collegamento fisico deve essere in grado di trasmettere datagrammi di almeno 68 byte. Per IPv6 questo valore minimo è stato portato a 1.280 byte (vedi RFC2460).
D’altra parte, la dimensione massima del datagramma che può essere trasmesso senza frammentazione non è definita da nessuna specifica e varia a seconda del tipo di collegamento. Questo valore è chiamato MTU (Maximum Transmission Unit).
La MTU definisce la dimensione massima del datagramma su un collegamento fisico locale. Internet è creata da reti non omogenee, e sul percorso tra due host potrebbero esserci link con valori MTU più corti. La dimensione massima del pacchetto che può essere trasmesso senza frammentazione tra due host remoti è chiamata Path MTU, e può potenzialmente essere diversa per ogni connessione.
Evitare la frammentazione
Si potrebbe pensare che vada bene costruire applicazioni che trasmettono pacchetti molto grandi e affidarsi ai router per eseguire la frammentazione IP. Questa non è una buona idea. I problemi con questo approccio sono stati discussi per la prima volta da Kent e Mogul nel 1987. Ecco un paio di punti salienti:
-
Per riassemblare con successo un pacchetto, tutti i frammenti devono essere consegnati. Nessun frammento può diventare corrotto o perdersi in volo. Semplicemente non c’è modo di notificare all’altra parte i frammenti mancanti!
-
L’ultimo frammento non avrà quasi mai la dimensione ottimale. Per trasferimenti di grandi dimensioni questo significa che una parte significativa del traffico sarà composta da brevi datagrammi subottimali – uno spreco di preziose risorse del router.
-
Prima del riassemblaggio un host deve tenere in memoria datagrammi parziali e frammentati. Questo apre un’opportunità per attacchi di esaurimento della memoria.
-
I frammenti successivi mancano dell’intestazione di livello superiore. L’intestazione TCP o UDP è presente solo nel primo frammento. Questo rende impossibile ai firewall di filtrare i frammenti di dati in base a criteri come le porte di origine o di destinazione.
Una descrizione più elaborata dei problemi di frammentazione IP può essere trovata in questi articoli di Geoff Huston:
- Valutazione della frammentazione dei pacchetti IPv4 e IPv6
- Frammentazione IPv6
Non frammentare – Pacchetto ICMP troppo grande
Immagine di Geoff Huston, riprodotta con permesso
Una soluzione a questi problemi era inclusa nel protocollo IPv4. Un mittente può impostare il flag DF (Don’t Fragment) nell’intestazione IP, chiedendo ai router intermedi di non eseguire mai la frammentazione di un pacchetto. Invece un router con un collegamento che ha un MTU più piccolo invierà un messaggio ICMP “all’indietro” e informerà il mittente di ridurre l’MTU per questa connessione.
Il protocollo TCP imposta sempre il flag DF. Lo stack di rete cerca attentamente i messaggi ICMP “Packet too big” in arrivo e tiene traccia della caratteristica “path MTU” per ogni connessione. Questa tecnica è chiamata “path MTU discovery”, ed è per lo più utilizzata per il TCP, anche se può essere applicata anche ad altri protocolli basati su IP. Essere in grado di consegnare i messaggi ICMP “Packet too big” è fondamentale per mantenere lo stack TCP funzionante in modo ottimale.
Come funziona realmente internet
In un mondo perfetto, i dispositivi connessi a internet coopererebbero e gestirebbero correttamente i frammenti di dati e i pacchetti ICMP associati. In realtà, però, i frammenti IP e i pacchetti ICMP sono molto spesso filtrati.
Questo perché l’internet moderno è molto più complesso di quanto previsto 36 anni fa. Oggi, fondamentalmente nessuno è collegato direttamente all’internet pubblica.
I dispositivi dei clienti si collegano attraverso i router domestici che fanno NAT (Network Address Translation) e di solito applicano le regole del firewall. Sempre più spesso c’è più di un’installazione NAT sul percorso dei pacchetti (per esempio, carrier-grade NAT). Poi, i pacchetti colpiscono l’infrastruttura ISP dove ci sono “middle boxes” ISP. Eseguono ogni sorta di cose strane sul traffico: applicano i limiti dei piani, strozzano le connessioni, eseguono la registrazione, dirottano le richieste DNS, implementano divieti di siti web imposti dal governo, forzano il caching trasparente o presumibilmente “ottimizzano” il traffico in qualche altro modo magico. Le scatole intermedie sono usate specialmente dalle telco mobili.
Similmente, ci sono spesso più livelli tra un server e l’internet pubblico. I fornitori di servizi a volte usano il routing Anycast BGP. Cioè: gestiscono gli stessi intervalli IP da più luoghi fisici in tutto il mondo. All’interno di un datacenter d’altra parte è sempre più popolare usare ECMP Equal Cost Multi Path per il bilanciamento del carico.
Ognuno di questi strati tra un client e un server può causare un problema di Path MTU. Permettetemi di illustrare questo con quattro scenari.
1. Client -> Server DF+ / ICMP
Nel primo scenario, un client carica alcuni dati al server usando TCP in modo che il flag DF sia impostato su tutti i pacchetti. Se il client non riesce a prevedere un MTU appropriato, un router intermedio farà cadere i pacchetti grandi e invierà una notifica ICMP “Packet too big” al client. Questi pacchetti ICMP potrebbero essere abbandonati da dispositivi NAT del cliente mal configurati o da middle box ISP.
Secondo il documento di Maikel de Boer e Jeffrey Bosma del 2012 circa il 5% degli host IPv4 e l’1% degli host IPv6 bloccano i pacchetti ICMP in entrata.
La mia esperienza lo conferma. I messaggi ICMP sono effettivamente spesso abbandonati per vantaggi percepiti di sicurezza, ma questo è relativamente facile da risolvere. Un problema più grande è con certi ISP mobili con strane scatole intermedie. Questi spesso ignorano completamente ICMP ed eseguono una riscrittura della connessione molto aggressiva. Per esempio Orange Polska non solo ignora i messaggi ICMP “Packet too big” in entrata, ma riscrive anche lo stato della connessione e blocca l’MSS ad un non negoziabile 1344 byte.
2. Client -> Server DF- / frammentazione
Nel prossimo scenario, un client carica alcuni dati con un protocollo diverso da TCP, che ha il flag DF cancellato. Per esempio, questo potrebbe essere un utente che sta giocando un gioco usando UDP, o che sta facendo una chiamata vocale. I grandi pacchetti in uscita potrebbero essere frammentati in qualche punto del percorso.
Possiamo emulare questo lanciando ping
con un payload di grandi dimensioni:
$ ping -s 2048 facebook.com
Questo particolare ping
fallirà con payload più grandi di 1472 byte. Qualsiasi dimensione più grande verrà frammentata e non verrà consegnata correttamente. Ci sono molteplici ragioni per cui i server potrebbero gestire male i frammenti, ma uno dei problemi più diffusi è l’uso del bilanciamento del carico ECMP. A causa dell’hashing ECMP, il primo datagramma che contiene un’intestazione di protocollo è probabile che venga bilanciato su un server diverso dal resto dei frammenti, impedendo il riassemblaggio.
Per una discussione più dettagliata di questo problema, vedere:
- Il nostro precedente articolo su ECMP.
- Come Google cerca di risolvere i problemi di frammentazione ECMP con Maglev L4 Load balancer.
Inoltre, la cattiva configurazione di server e router è un problema significativo. Secondo RFC7852 tra il 30% e il 55% dei server abbandonano i datagrammi IPv6 contenenti l’intestazione di frammentazione.
3. Server -> Client DF+ / ICMP
Lo scenario successivo riguarda un client che scarica alcuni dati su TCP. Quando il server non riesce a prevedere l’MTU corretto, dovrebbe ricevere un messaggio ICMP “Packet too big”. Facile, vero?
Sfortunatamente, non lo è, di nuovo a causa del routing ECMP. Il messaggio ICMP molto probabilmente verrà consegnato al server sbagliato – l’hash a 5 tuple del pacchetto ICMP non corrisponderà all’hash a 5 tuple della connessione problematica. Abbiamo scritto di questo in passato, e sviluppato un semplice demone userspace per risolverlo. Funziona trasmettendo la notifica ICMP in entrata “Packet too big” a tutti i server ECMP, sperando che quello con la connessione problematica la veda.
Inoltre, a causa del routing Anycast, l’ICMP potrebbe essere consegnato al datacenter sbagliato! Il routing di Internet è spesso asimmetrico e il miglior percorso da un router intermedio potrebbe indirizzare i pacchetti ICMP al posto sbagliato.
Le notifiche ICMP mancanti di “Packet too big” possono causare lo stallo delle connessioni e il timing out. Questo è spesso chiamato un blackhole PMTU. Per aiutare questo caso pessimistico Linux implementa un workaround – MTU Probing RFC4821. MTU Probing cerca di identificare automaticamente i pacchetti abbandonati a causa dell’MTU sbagliato, e usa l’euristica per sintonizzarlo. Questa funzione è controllata tramite un sysctl:
$ echo 1 > /proc/sys/net/ipv4/tcp_mtu_probing
Ma l’MTU probing non è senza problemi. In primo luogo, tende a classificare erroneamente la perdita di pacchetti legata alla congestione come problemi di MTU. Le connessioni lunghe tendono a finire con un MTU ridotto. In secondo luogo, Linux non implementa l’MTU Probing per IPv6.
4. Server -> Client DF- / frammentazione
Infine, c’è una situazione in cui il server invia grandi pacchetti usando un protocollo non TCP con il bit DF libero. In questo scenario, i pacchetti grandi saranno frammentati sul percorso verso il client. Questa situazione è meglio illustrata con grandi risposte DNS. Ecco due richieste DNS che genereranno grandi risposte e saranno consegnate al client come frammenti IP multipli:
$ dig +notcp +dnssec DNSKEY org @199.19.56.1$ dig +notcp +dnssec DNSKEY org @2001:500:f::1
Queste richieste potrebbero fallire a causa del già menzionato router domestico mal configurato, NAT rotto, installazioni ISP rotte, o impostazioni firewall troppo restrittive.
Secondo Boer e Bosma circa il 6% di IPv4 e il 10% degli host IPv6 bloccano i frammenti di dati in entrata.
Qui ci sono alcuni link con maggiori informazioni sui problemi specifici di frammentazione che riguardano il DNS:
- DNS-OARC Reply Size Test
- IPv6, pacchetti UDP grandi e il DNS
Ma internet funziona ancora!
Con tutte queste cose che vanno male, come fa internet a funzionare ancora?
CC BY-SA 3.0, fonte: Wikipedia
Questo è dovuto principalmente al successo di Ethernet. La grande maggioranza dei collegamenti nell’internet pubblica sono Ethernet (o derivati da essa) e supportano l’MTU di 1500 byte.
Se si assume ciecamente l’MTU di 1500, si sarà sorpresi di quanto spesso funzionerà bene. Internet continua a funzionare principalmente perché stiamo tutti usando un MTU di 1500 e raramente abbiamo bisogno di fare la frammentazione IP e inviare messaggi ICMP.
Questo smette di funzionare su una configurazione insolita con collegamenti che hanno un MTU non standard. Le VPN e altri software di tunnel di rete devono fare attenzione a garantire che i frammenti e i messaggi ICMP funzionino bene.
Questo è particolarmente visibile nel mondo IPv6, dove molti utenti si connettono attraverso tunnel. Avere un sano passaggio di ICMP in entrambe le direzioni è molto importante, specialmente perché la frammentazione in IPv6 fondamentalmente non funziona (abbiamo citato due fonti che affermano che tra il 10% e il 50% degli host IPv6 bloccano l’intestazione IPv6 Fragment).
Siccome i problemi di Path MTU in IPv6 sono così comuni, molti server IPv6 riducono il Path MTU al minimo previsto dal protocollo di 1280 byte. Questo approccio scambia un po’ di prestazioni per la migliore affidabilità.
Online ICMP blackhole checker
Per aiutare ad esplorare e debuggare questi problemi, abbiamo costruito un checker online. Puoi trovare due versioni del test:
- versione IPv4: http://icmpcheck.popcount.org
- versione IPv6: http://icmpcheckv6.popcount.org
Questi siti lanciano due test:
- Il primo test invierà messaggi ICMP al vostro computer, con l’intenzione di ridurre il Path MTU a un valore ridicolmente piccolo.
- Il secondo test invierà frammenti di dati indietro a voi.
Ricevere un “pass” in entrambi questi test dovrebbe darti una ragionevole garanzia che internet dalla tua parte del cavo si sta comportando bene.
È anche facile eseguire i test da linea di comando, nel caso tu voglia eseguirli sul server:
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
Questo dovrebbe ridurre il path MTU al nostro server a 905 byte. È possibile verificare questo guardando nella tabella della cache di routing. Su Linux lo si fa con:
ip route get `dig +short icmpcheck.popcount.org`
E’ possibile cancellare la cache di routing su Linux:
ip route flush cache to `dig +short icmpcheck.popcount.org`
Il secondo test verifica se i frammenti vengono consegnati correttamente al client:
curl -v -s http://icmpcheck.popcount.org/frag -o /dev/nullcurl -v -s http://icmpcheckv6.popcount.org/frag -o /dev/null
Sommario
In questo post del blog abbiamo descritto i problemi con il rilevamento dei valori Path MTU in internet. ICMP e frammenti di dati sono spesso bloccati su entrambi i lati delle connessioni. I clienti possono incontrare firewall mal configurati, dispositivi NAT o usare ISP che intercettano aggressivamente le connessioni. I clienti spesso usano anche VPN o tunnel IPv6 che, mal configurati, possono causare problemi di MTU del percorso.
I server d’altra parte si affidano sempre più spesso ad Anycast o ECMP. Entrambe queste cose, così come la configurazione errata di router e firewall, sono spesso causa di ICMP e frammenti di dati che vengono lasciati cadere.
Infine, speriamo che il test online sia utile e possa darvi una maggiore comprensione del funzionamento interno delle vostre reti. Il test ha utili esempi di sintassi di tcpdump, utili per ottenere maggiori informazioni. Buon debugging di rete!
Completare i problemi di frammentazione per il 10% di internet è eccitante? Stiamo assumendo ingegneri di sistema di tutti i tipi, programmatori Golang, C wranglers, e stagisti in diverse località! Unisciti a noi a San Francisco, Londra, Austin, Champaign e Varsavia.
-
In IPv6 la frammentazione “forward” funziona in modo leggermente diverso che in IPv4. Ai router intermedi è proibito frammentare i pacchetti, ma la fonte può ancora farlo. Questo è spesso fonte di confusione – ad un host potrebbe essere chiesto di frammentare un pacchetto che ha trasmesso in passato. Questo ha poco senso per protocolli stateless come il DNS. ︎
-
In una nota a margine, esiste anche una “unità minima di trasmissione”! Nel framing Ethernet comunemente usato, ogni datagramma trasmesso deve avere almeno 64 byte sul livello 2. Questo si traduce in 22 byte su UDP e 10 byte sul livello TCP. Molteplici implementazioni hanno usato per perdere memoria non inizializzata su pacchetti più corti! ︎
-
In senso stretto in IPv4 il pacchetto ICMP è chiamato “Destination Unreachable, Fragmentation Needed and Don’t Fragment was Set”. Ma trovo la descrizione dell’errore IPv6 ICMP “Packet too big” molto più chiara. ︎
-
Come suggerimento, lo stack TCP include anche un valore massimo consentito “MSS” nei pacchetti SYN (MSS è fondamentalmente un valore MTU ridotto dalla dimensione delle intestazioni IP e TCP). Questo permette agli host di sapere qual è l’MTU sui loro collegamenti. Nota: questo non dice qual è l’MTU sulle dozzine di collegamenti internet tra i due host! ︎
-
Erriamo sul sicuro. Un MTU migliore è 1492, per adattarsi alle connessioni DSL e PPPoE. ︎