TCP e UDP: il livello di trasporto

Dalle porte e dai socket al confronto tra TCP e UDP, fino al laboratorio con Netcat e Wireshark.

1. Dal dispositivo all’applicazione

Su un computer possono funzionare contemporaneamente browser, posta, chat e client DNS. Condividono la rete, ma ciascuno deve ricevere i propri dati. L’indirizzo IP individua un’interfaccia di rete: da solo non identifica l’applicazione destinataria.

Il livello di trasporto permette la comunicazione tra applicazioni su host diversi, usando anche le porte. È il livello 4 del modello OSI. Nel modello TCP/IP si trova tra Applicazione e Internet: TCP e UDP utilizzano IP per raggiungere la destinazione.

Collocazione del trasporto nei due modelli.
TCP/IP Corrispondenza OSI Ruolo
Applicazione 7–6–5 Servizi e regole dello scambio applicativo
Trasporto 4 TCP o UDP: comunicazione tra applicazioni
Internet 3 IP: indirizzamento e inoltro tra reti
Accesso alla rete 2–1 Frame, collegamenti e segnali

Nell’incapsulamento, ai dati applicativi si aggiunge l’header TCP o UDP; il risultato diventa il contenuto di un pacchetto IP, poi di un frame sul collegamento.

2. Porte, porte effimere e socket

Una porta è un numero a 16 bit, da 0 a 65535; la porta 0 è riservata e non è una normale porta di servizio. Le porte consentono al sistema operativo di distinguere gli endpoint delle comunicazioni. Non sono connettori fisici.

Un servizio Web HTTP su TCP può ascoltare sulla porta 80; SSH usa normalmente TCP 22, DNS usa sia UDP sia TCP 53. Sono associazioni convenzionali: la sola porta non prova quale applicazione stia realmente comunicando.

Il client utilizza normalmente una porta effimera, scelta temporaneamente dal sistema operativo. Il suo intervallo dipende dal sistema e dalla configurazione. In questo esempio 49152 è una possibile porta client, non un valore obbligatorio.

Richiesta TCP: 192.168.1.10:49152 → 192.168.1.50:80
Risposta TCP:  192.168.1.50:80    → 192.168.1.10:49152

Un socket è un endpoint software. In una rete IP lo descriviamo mediante protocollo di trasporto, indirizzo IP e porta. Una connessione TCP è identificata dalla coppia di endpoint: IP e porta locali, IP e porta remoti. Questo permette a molti client di collegarsi allo stesso servizio.

3. TCP: connessione e flusso affidabile

TCP — Transmission Control Protocol offre un flusso di byte affidabile e ordinato. Prima dello scambio normale dei dati, le due estremità stabiliscono una connessione logica. Non viene creato un cavo: i sistemi mantengono uno stato con numeri di sequenza, conferme e dati ancora da trasmettere o ritrasmettere.

Affidabile non significa che la rete non possa guastarsi. TCP recupera molte perdite mediante ritrasmissioni; se non riesce a proseguire, la comunicazione può terminare con un errore. Una conferma TCP indica ricezione da parte del trasporto remoto, non che l’applicazione abbia già elaborato o salvato i dati.

Il Three-Way Handshake

  1. SYN: il client propone il proprio numero di sequenza iniziale.
  2. SYN, ACK: il server propone il suo e conferma quello del client.
  3. ACK: il client conferma quello del server.
Client e server scambiano SYN Seq 1000, SYN ACK Seq 7000 Ack 1001, poi ACK Seq 1001 Ack 7001. Il tempo scorre verso il basso.
Apertura TCP ordinaria, senza dati nei tre segmenti. Numeri iniziali di esempio: ogni SYN occupa una posizione nello spazio di sequenza.

Lo schema mostra l’apertura ordinaria di una connessione TCP. Il terzo segmento può già trasportare dati; esistono inoltre estensioni specifiche. Non occorre aspettarsi sempre tre righe prive di payload in ogni cattura reale.

4. Sequence Number e ACK: contare i byte

TCP numera i byte, non i messaggi. Il campo Sequence Number indica il numero del primo byte di dati del segmento. Ogni direzione ha il proprio spazio di sequenza.

Quando il flag ACK è impostato, l’Acknowledgment Number indica il prossimo numero di sequenza atteso: è una conferma cumulativa dei byte precedenti ricevuti in ordine.

Esempio con numeri relativi:
Seq = 1, dati = 12 byte → byte da 1 a 12
Risposta: Ack = 13      → attendo il byte 13

Le sequenze iniziali reali non devono partire da 1. Wireshark mostra normalmente numeri relativi per facilitarne la lettura. Anche SYN e FIN consumano ciascuno un numero di sequenza; un ACK senza dati, SYN o FIN non ne consuma.

Perdite, riordino e ritrasmissione

Se i dati arrivano fuori ordine, TCP può conservarli in attesa dei byte mancanti. Timer e informazioni dalle conferme aiutano il mittente a rilevare perdite e ritrasmettere. Non esiste l’obbligo di un ACK separato per ogni segmento: sono possibili conferme ritardate o incluse in segmenti che trasportano anche dati.

In Wireshark, TCP Retransmission, Duplicate ACK e Out-Of-Order sono indicazioni dell’analizzatore da interpretare nel contesto della cattura, non ulteriori flag dell’header TCP.

5. Stream, finestre e congestione

Se l’applicazione scrive CIAO, poi COME, poi STAI, TCP trasporta il flusso CIAOCOMESTAI. Non conserva i confini di quelle tre scritture: una send() non corrisponde necessariamente a un segmento o a una recv().

Il protocollo applicativo deve riconoscere i messaggi con lunghezze, delimitatori o header propri. Per esempio, può stabilire che ogni messaggio termini con un carattere di fine riga.

  • Controllo del flusso: la finestra annunciata dal ricevitore comunica quanto spazio è disponibile per altri dati; protegge il ricevitore da un mittente troppo rapido.
  • Controllo della congestione: il mittente adatta la quantità di dati in volo alle condizioni della rete. Non coincide con la finestra annunciata nell’header.

6. Header, flag e chiusura TCP

Campi TCP da individuare nei dettagli di Wireshark.
Campo Che cosa osservare
Source / Destination Port Endpoint sorgente e destinazione
Sequence / Acknowledgment Number Posizione nel flusso e conferma cumulativa
Header Length Dimensione dell’header: da 20 a 60 byte
Flags Bit di controllo, anche contemporanei
Window Finestra annunciata dal ricevitore; considerare l’eventuale Window Scale
Checksum Controllo di integrità; non è cifratura né autenticazione
Flag principali: un segmento può impostarne più di uno.
Flag Significato
SYN Sincronizzazione dei numeri iniziali
ACK Il campo Acknowledgment Number è valido
FIN Il mittente non invierà altri dati in questa direzione
RST Interruzione/reset, per esempio in risposta a una connessione rifiutata
PSH Richiesta di non trattenere inutilmente i dati prima della consegna all’applicazione; non delimita un messaggio

[PSH, ACK] indica entrambi i flag impostati. Nella cattura può accompagnare dati, ma PSH non è il campo che misura la presenza o la lunghezza del payload: controllare tcp.len. Anche un segmento senza PSH può trasportare dati.

Una chiusura ordinata possibile

Client → Server: FIN, ACK
Server → Client: ACK
Server → Client: FIN, ACK
Client → Server: ACK

TCP è full-duplex: le due direzioni si chiudono indipendentemente. Le conferme possono essere combinate e non ogni cattura mostra esattamente quattro segmenti. Un RST indica una chiusura diversa da questo esempio ordinato.

7. UDP: datagrammi senza handshake

UDP — User Datagram Protocol è connectionless: non apre una connessione con un handshake del trasporto. L’applicazione può inviare subito un datagramma.

Tre invii di messaggi UNO, DUE e TRE generano tre datagrammi: UDP conserva i confini dei messaggi consegnati. Non garantisce però consegna, ordine o assenza di duplicati e non ritrasmette automaticamente i dati persi.

L’header UDP è di 8 byte: porta sorgente, porta destinazione, lunghezza (header più dati) e checksum. Non contiene i campi Seq/Ack o i flag TCP.

Una risposta applicativa “ricevuto” può essere inviata su UDP, ma non diventa un ACK del protocollo UDP. Eventuali conferme, riordino e recupero delle perdite devono essere realizzati dai protocolli che lo utilizzano. Anche l’uso di connect() su un socket UDP non introduce un handshake UDP.

Tre datagrammi UDP indipendenti: UNO arriva, DUE si perde, TRE arriva. UDP non invia conferme e non recupera automaticamente DUE.
Scenario didattico di perdita: non è una cattura reale. La perdita di DUE non impedisce a UDP di consegnare TRE.

8. Scegliere il trasporto

Confronto dei servizi offerti dal trasporto, non delle prestazioni assolute.
Caratteristica TCP UDP
Servizio Flusso di byte Datagrammi con confini conservati
Apertura ordinaria Handshake e stato di connessione Nessun handshake UDP
Consegna e ordine Recupero perdite e consegna ordinata, oppure fallimento della connessione Nessuna garanzia offerta da UDP
Conferme e ritrasmissioni Gestite da TCP Non fornite da UDP
Flusso e congestione Meccanismi TCP Non forniti da UDP stesso
Header 20–60 byte 8 byte

HTTP/1.1, HTTP/2 e SSH usano TCP. DNS può usare UDP e TCP; DHCP e NTP usano UDP. HTTP/3 usa QUIC, un protocollo basato su UDP che offre, tra le altre funzioni, flussi affidabili. Non tutto il traffico HTTPS usa quindi TCP.

9. Preparare il laboratorio: Netcat e loopback

Servono una macchina o VM Linux con Netcat e Wireshark, due terminali e la possibilità di catturare sull’interfaccia lo. Client e server girano sulla stessa macchina: 127.0.0.1 indica il loopback locale, anche dentro una VM. Questo traffico non esce sulla scheda Ethernet.

Netcat è un’applicazione, non un protocollo. Legge e scrive dati attraverso TCP o UDP; il kernel gestisce il trasporto. Wireshark osserva i pacchetti. Avviare la cattura su lo prima di collegare il client.

Schermata iniziale di Wireshark con l’interfaccia Loopback: lo selezionata ed evidenziata in rosso.
1. Selezionare Loopback: lo e avviare la cattura con un doppio clic: client e server comunicano sulla stessa macchina.

Controllare quale Netcat è installato

nc -h

La sintassi cambia tra implementazioni. Gli esempi TCP usano la sintassi OpenBSD Netcat; per UDP riportiamo il comando della schermata. Le opzioni -l e -u significano rispettivamente ascolto e UDP. Nella versione traditional i comandi di ascolto possono essere nc -l -p 5000 e nc -u -l -p 6000: seguire l’help della propria versione, senza mescolare le sintassi.

In Wireshark usare i seguenti come filtri di visualizzazione, nella barra sopra i pacchetti, non come filtri di cattura.

tcp.port == 5000
udp.port == 6000
Cattura Wireshark su loopback con il filtro di visualizzazione tcp.port == 5000 nella barra verde.
2. Inserire tcp.port == 5000 nella barra del filtro di visualizzazione e premere Invio. Sono mostrati i pacchetti TCP con porta sorgente o destinazione 5000; prima del collegamento del client la lista può essere vuota.

10. Esperimento TCP: prima la connessione, poi i dati

  1. Avviare la cattura su lo e impostare tcp.port == 5000.
  2. Nel terminale server avviare l’ascolto:
nc -l 127.0.0.1 5000

Nel secondo terminale collegare il client:

nc 127.0.0.1 5000

Prima di digitare: individuare SYN, SYN/ACK e ACK. Annotare la porta effimera del client, la porta server 5000, Seq e Ack. Lo stack TCP del kernel ha aperto la connessione richiesta da Netcat.

Due terminali con nc -l -p 5000 sul server e nc 127.0.0.1 5000 sul client; Wireshark mostra SYN, SYN ACK e ACK tra le porte 40528 e 5000.
3. In alto a sinistra il server in ascolto, in basso il client collegato a 127.0.0.1:5000. A destra Wireshark mostra SYN → SYN/ACK → ACK, prima dell’invio del testo.

La schermata usa il comando di ascolto nc -l -p 5000, una delle sintassi indicate sopra. Nella cattura la porta effimera del client è 40528: nella propria prova può essere diversa. Entrambi gli indirizzi sono 127.0.0.1; le porte distinguono i due endpoint.

Nel client digitare Ciao e premere Invio. Il testo compare sul server. Rispondere dal terminale server e osservare l’inversione di porte sorgente e destinazione.

Invio di Ciao dal client Netcat al server; Wireshark mostra il segmento PSH ACK con Seq 1 e 5 byte di dati, seguito dall’ACK 6 del server. Il payload esadecimale è 43 69 61 6f 0a.
4. Il messaggio Ciao compare sul server. La cattura mostra il segmento con i dati e la successiva conferma TCP.

Espandere TCP nel dettaglio del pacchetto e cercare il payload. Nella schermata Ciao contiene 4 byte ASCII; il fine riga aggiunge un byte, per un totale di 5 byte. I valori esadecimali sono 43 69 61 6f 0a: 0a rappresenta il fine riga.

Il pacchetto 6 mostra [PSH, ACK], Seq = 1 e Len = 5. Nel pacchetto 7 il server risponde con Ack = 6: attende il byte successivo ai cinque ricevuti. Sono numeri di sequenza relativi. La lunghezza del frame visualizzata nella colonna Length non coincide con i soli dati TCP: per il payload controllare tcp.len. In altre prove segmentazione e buffering possono variare.

tcp.port == 5000 && tcp.len > 0

Usare Follow → TCP Stream per ricostruire il dialogo. Non aspettarsi necessariamente PSH o un ACK separato per ogni riga. Terminare i processi con Ctrl+C; se si analizza la chiusura, distinguere FIN e RST realmente osservati, senza imporre lo schema teorico.

11. Esperimento UDP: osservare i datagrammi

Terminare l’esperimento precedente, avviare una nuova cattura su lo e usare udp.port == 6000.

Terminale server, con la sintassi usata nella schermata:

nc -u -l 127.0.0.1 -p 6000

L’opzione -p 6000 specifica la porta locale di ascolto. Se la propria versione OpenBSD non accetta questa combinazione, usare nc -u -l 127.0.0.1 6000, come indicato dal relativo help.

Terminale client:

nc -u 127.0.0.1 6000

Digitare Ciao Server! e premere Invio, come nella schermata. Il testo contiene 12 byte ASCII; con il fine riga il payload è di 13 byte. Il campo UDP Length vale quindi 21 byte: 8 di header più 13 di dati. Il riepilogo Len=13 nella colonna Info indica invece i soli dati.

Terminali Netcat UDP con invio di Ciao Server! e cattura Wireshark di un datagramma dalla porta 45664 alla porta 6000, con 13 byte di dati e senza handshake TCP.
5. Il client invia Ciao Server! al server UDP sulla porta 6000. Wireshark mostra il datagramma e il payload: UDP non esegue la sequenza SYN, SYN/ACK, ACK.

Nella cattura la porta effimera del client è 45664; nella propria prova può cambiare. Per controllare tutto il traffico della porta, anche senza limitarlo a UDP, si può usare udp.port == 6000 || tcp.port == 6000: il solo filtro UDP nasconderebbe eventuali pacchetti TCP.

Non compare un handshake UDP né un ACK UDP. Per osservare le due direzioni, dopo il primo messaggio rispondere dal server: quello è un nuovo datagramma con dati applicativi, non una conferma automatica di UDP.

Se non compare traffico

  • Verificare che entrambi i terminali siano nella stessa VM e che la cattura sia su lo.
  • Controllare protocollo, porta, processo in ascolto e sintassi della variante Netcat.
  • Premere Invio: il terminale può conservare il testo finché la riga non è completa.
  • Se Wireshark non elenca le interfacce catturabili, verificare con il docente i permessi di cattura.