[C] Opzioni socket: IP_MTU_DISCOVER e IP_MTU chi le conosce?

di il
3 risposte

[C] Opzioni socket: IP_MTU_DISCOVER e IP_MTU chi le conosce?

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!

3 Risposte

  • Re: [C] Opzioni socket: IP_MTU_DISCOVER e IP_MTU chi le conosce?

    Forse la cosa è così spinosa che nessuno mi capisce

    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.
  • Re: [C] Opzioni socket: IP_MTU_DISCOVER e IP_MTU chi le conosce?

    Il problema al quale ti riferisci è dovuto alla grandezza del pacchetto e alla frammentazione.
    La MTU solitamente è 1500 (che puoi modificare con ifconfig) dal quale dovresti sottrarre eventuali header. Su reti differenti dovresti usare MTU path ed usare al massimo quella grandezza.

    Comunque, per vederci chiaro ti consiglio di usare dei tools di analisi tipo wireshark (o simili) per esaminare i packets che trasmetti/ricevi. Un'altro da avere è netcat GNU (non quello originale).
    Buona Visione

    ~Max
  • Re: [C] Opzioni socket: IP_MTU_DISCOVER e IP_MTU chi le conosce?

    Grazie dei suggerimenti Max!

    Ma a questo punto mi chiedo, ma questo comportamento della sendto() che non ritorna errore anche se spedisco pacchetti troppo grandi (a patto di non settare il l'MTU Path) capita solo a me?

    Se è un comportamento normale invece....allora c'è un'altra tecnica corretta (a parte quella di farlo fare al kernel) in cui un client ed un server si possono concordare su quanto spedire?
Devi accedere o registrarti per scrivere nel forum
3 risposte