PDA

Visualizza la versione completa : [C] Opzioni socket: IP_MTU_DISCOVER e IP_MTU


jobv
19-01-2011, 11:48
Salve a tutti!

Sto scrivendo un'applicazione client/server che si scambiano dei file utilizzando il protocollo UDP.

Fino a quando l'ho testata facendo girare client e server sulla stessa macchina o su due macchine diverse della stessa rete (classica rete domestica con modem-router di Alice Telecom) utilizzavo come dimensione fissa per i pacchetti (e quindi scrivevo e leggevo questa quantità fissa) 65507 byte.

Tale dimensione derivava da questo mio ragionamento:
un pacchetto UDP non dev'essere frammentato (se è troppo grande funzioni come sendto() ritornano l'errore EMSGSIZE) e quindi deve entrare tutto in un singolo pacchetto IP.
Un pacchetto IP può avere una dimensione massima di 65535 byte, togliendo i 20 dell'intestazione IP e gli 8 di quella UDP si arriva a 65507.

Tutto funzionava benissimo fino a quando non ho provato a far girare il server su un'altra rete collegandolo con una chiavetta USB per internet (Wind).
Il client non riceveva nulla.

Allora ho deciso di modificare il server per scoprire prima il valore della più bassa MTU nel percorso e poi spedire utilizzando quella dimensione (tolti, ovviamente, i 28 byte per le intestazioni IP e UDP).

A tele scopo, visto che utilizzavo nel server dei socket connessi, ho utilizzato le due opzioni dei socket: IP_MTU_DISCOVER (settandolo a IP_PMTUDISC_DO per far eseguire al kernel la procedura per determinare la Path MTU come richiesto da RCF 1191) e poi attraverso getsockopt() (utilizzando il valore IP_MTU) leggevo il valore per l'MTU dopo la chiamata a connect().

A questo punto però, tornando a far girare in locale o sulla rete domestica sia client che server, le cose non andavano più come prima.
Il valore letto per l'MTU dopo la connect() è molto più basso dei 65507 byte che utilizzavo prima...
certo il tutto funziona ma, naturalmente, il tempo di trasferimento è aumentato.

La mia teoria a riguardo è questa:

per qualche ragione (firewall che bloccano pacchetti IP che non contengono intestazioni di protocolli superiori o altro), anche se un pacchetto IP che contiene un singolo pacchetto UDP può essere frammentato (per MTU più basse) e ricomposto (e ciò avviene tranquillamente se client e server sono sulla stessa rete (e forse dipende anche dalla configurazione di rete) e lo dimostra il fatto che, anche se l'MTU che leggo è molto più bassa di 65507, prima tutto funzionava, anzi: se e solo se aumentavo tale valore (tipo a 65508) ricevevo EMSGSIZE), tra reti diverse la frammentazione del pacchetto IP può dare problemi.

Allora mi sono detto: torniamo ad utilizzare la chiavetta e proviamo a mandare un pacchetto di 65507 byte.
Se ricevo EMSGSIZE abilito la ricerca del Path MTU, ne determino il valore, e continuo.

Il problema è però che la prima sento(), cioè quella che manda 65507 byte, non mi ritorna EMSGSIZE! Anche quando dovrebbe! (e so che lo dovrebbe fare perché, come ho detto prima, facendo girare il server su una macchina connessa con chiavetta USB i 65507 byte erano troppi!)

L'altra opzione era di abilitare la ricerca del Path MTU ma di tentare prima di spedire 65007 byte e, se si riceve errore, leggere il valore per l'MTU e usare quello. Ma in questo caso il problema è che, se abilito la determinazione del Path MTU, la sendto() mi ritorna EMSGSIZE se cerco di spedire i 65507 byte sempre! Anche se client e server sono sulla stessa rete.

Spero di essermi riuscito a spiegare e so che è complicato ma vorrei veramente capire se le mie teorie sul perché di questi comportamenti sono esatte (mi interessa capire bene come funzionano le cose) e poi, magari, capire anche come risolvere.

Grazie a tutti!

jobv
20-01-2011, 08:54
Forse la cosa è così spinosa che nessuno mi capisce :messner:

Mettiamola così: con l'uso di queste opzioni riesco a determinare l'MTU ma ho visto che in realtà tutto può funzionare anche se si mandano pacchetti più grandi, esempio:

Anche se trovo l'MTU di 1500 se client e server sono sulla stessa rete tutto funziona anche con pacchetti di 65507.

Anche se trovo l'MTU di 1500 se client e server sono su reti diverse il tutto funziona anche con pacchetti più grandi (anche il doppio) di grandezza variabile.

Problema: se abilito l'MTU Path, sendto() mi ritorna errore non appena supero i 1500 anche se potrei andare oltre tranquillamente (ad esempio nel caso della stessa rete). Se lo disabilito la sendto() non mi ritorna errore anche se il client non riceve nulla.

deleted_29
20-01-2011, 19:27
ho capito poco di quanto hai scritto, perchè non mi intendo molto.
ma provo a farti questa domanda: se non vuoi avere il pacchetto frammentato, semplicemente, non hai modo.
perchè non sai quali siano le dimensioni dei frammenti che viaggiano su internet, semplicemente non ne hai il controllo.
visto che, nella maggior parte dei casi, sono frame ethernet (o comunque "finti" frame ethernet, ad esempio veicolati in celle ATM), hai quasi sempre quella dimensione come limite massimo di un pacchetto "quasi" infrangibile.

o forse no, boh.

Loading