Capire davvero il modello OSI — sezionare una singola richiesta HTTP nei suoi sette livelli
· Go Komura · Modello OSI, TCP/IP, Reti, Wireshark, Ethernet, TCP, HTTP, C#, .NET, Socket, Consulenza tecnica
Il modello OSI è ciò con cui inizia sempre qualsiasi manuale introduttivo sulle reti, eppure sentiamo spesso lo stesso commento: «so elencare i nomi dei sette livelli, ma onestamente non riesco a farmene un’idea concreta». Si memorizzano livello fisico, livello di collegamento dati, livello di rete… e così via, ma spesso resta oscuro come quel diagramma a sette livelli si colleghi al codice C# che si sta scrivendo o al guasto di comunicazione che si sta verificando proprio in questo momento.
Su questo blog abbiamo già trattato in passato il comportamento di L4 (il livello di trasporto) — il fraintendimento secondo cui si può ricevere con Receive le stesse unità inviate con Send su TCP e la diagnosi del motivo per cui la ritrasmissione TCP blocca la comunicazione con una fotocamera industriale — ma non avevamo mai scritto un articolo che raccogliesse l’idea di fondo di «livello» in sé.
L’approccio di questo articolo è semplice. Costruiamo davvero un frame Ethernet che trasporta una singola richiesta HTTP GET e lo sezioniamo dall’esterno verso l’interno. I sette livelli non esistono solo in un diagramma concettuale: sono fisicamente annidati all’interno dei 171 byte che scorrono sulla rete. Una volta visto questo con i propri occhi, tramite un dump esadecimale e Wireshark, il modello OSI smette di essere qualcosa da imparare a memoria.
Il codice che compare in questo articolo è pubblicato su GitHub come set di esempi completo, compilabile ed eseguibile (una libreria per assemblare e sezionare i frame, l’esportazione di un file pcap apribile con Wireshark e dei test unitari).
osi-model-packet-anatomy - komurasoft-blog-samples (GitHub)
1. Prima di tutto, la conclusione
- Il modello OSI è un «vocabolario», non un’«implementazione». Ciò che funziona realmente su Internet è la suite di protocolli TCP/IP (di fatto quattro livelli); i protocolli OSI a 7 livelli veri e propri hanno perso la corsa all’adozione e non sono mai stati realmente utilizzati. Ciò che è sopravvissuto è il modello come linguaggio condiviso — espressioni come «isoliamo questo a L2» o «è un problema di L7».12
- I sette livelli sono fisicamente annidati come sequenza di byte. In un frame che trasporta una richiesta HTTP GET, i primi 14 byte sono l’header Ethernet (L2), i 20 byte successivi sono l’header IPv4 (L3), i 20 byte successivi ancora sono l’header TCP (L4), e il resto è il testo HTTP (L7). Il modo in cui l’header di ciascun livello inizia subito dopo quello del livello precedente si può verificare direttamente nel dump esadecimale di questo articolo e in Wireshark.
- L’unica cosa che il vostro codice C# scrive direttamente è la sequenza di byte di L7. Ciò che si passa a
Socket.Sendè solo il dato applicativo: gli header TCP e IP vengono aggiunti dallo stack di protocolli del sistema operativo, mentre l’header Ethernet e il segnale elettrico vengono aggiunti dalla scheda di rete (NIC). Proprio per questo, scegliere traHttpClient,SslStreameSocketsignifica scegliere «da quale livello in giù delegare al sistema operativo».3 - L5 (livello di sessione) e L6 (livello di presentazione) non esistono come livelli indipendenti nello stack reale. TLS e la codifica dei caratteri si fanno carico di parte di questo ruolo, ma nel modello TCP/IP sono tutti raggruppati nel livello di applicazione. Se non capite cosa sia L6, non è colpa vostra: è proprio lì che il modello e la realtà divergono.2
- Il modello OSI si dimostra davvero utile nella pratica soprattutto nel troubleshooting e nella conversazione. Restringere il campo con il vocabolario dei livelli — «il ping funziona ma HTTP fallisce, quindi L3 è ancora attivo e bisogna sospettare L4 o superiori» — diventa un linguaggio condiviso che funziona tra chi si occupa di rete, di infrastruttura e di sviluppo applicativo.
2. Perché il modello OSI resta solo un esercizio di memoria
Molte spiegazioni iniziano con una tabella come questa.
| Livello | Nome | Descrizione |
|---|---|---|
| L7 | Livello di applicazione | Fornisce servizi di comunicazione alle applicazioni |
| L6 | Livello di presentazione | Trasforma il formato di rappresentazione dei dati |
| L5 | Livello di sessione | Gestisce l’inizio e la fine di una comunicazione |
| L4 | Livello di trasporto | Fornisce un trasferimento dati affidabile |
| L3 | Livello di rete | Si occupa dell’instradamento e dell’indirizzamento |
| L2 | Livello di collegamento dati | Trasferisce i dati tra nodi adiacenti |
| L1 | Livello fisico | Converte i bit in segnali elettrici |
Questa tabella è corretta, ma ogni riga è scritta in un linguaggio così astratto che leggerla non produce alcuna immagine mentale concreta. Se vi dicono «trasforma il formato di rappresentazione dei dati», nessuno saprebbe indicare a quale riga del proprio codice si riferisca.
C’è un altro fatto storico che vale la pena dichiarare apertamente. Il modello OSI è uno standard definito da ISO (Organizzazione internazionale per la normazione) e dall’ITU-T (ISO/IEC 7498-1, ITU-T X.200), e in origine era pensato per accompagnarsi a un ambizioso insieme di protocolli OSI corrispondenti a ciascuno dei sette livelli.1 Ma nella corsa all’adozione degli anni ‘90, il TCP/IP — che disponeva già di implementazioni funzionanti — è diventato lo standard di fatto, e i protocolli OSI stessi sono finiti per non essere quasi mai usati. La RFC 1122, che ha gettato le basi di Internet, descrive il mondo con quattro livelli di fatto — livello di collegamento, livello Internet (IP), livello di trasporto e livello di applicazione — senza alcun livello indipendente corrispondente a L5 o L6.2
In altre parole, il motivo per cui oggi studiamo ancora il modello OSI non è «perché è implementato esattamente così», ma per acquisire lo strumento del pensare per livelli e il vocabolario condiviso per il troubleshooting. Partendo da questa premessa, non c’è più bisogno di angosciarsi perché «non si trova il corrispettivo reale di L5 e L6», e tutto diventa improvvisamente molto più chiaro.
3. Il nocciolo della questione: sezionare una singola richiesta HTTP
Basta con le premesse: vediamo la cosa reale. Il seguente dump esadecimale è un frame Ethernet (171 byte in totale) che trasporta una singola richiesta HTTP, GET /index.html HTTP/1.1. È stato assemblato dal codice di esempio presentato all’inizio dell’articolo, e poiché sia il checksum dell’header IPv4 sia il checksum TCP sono calcolati correttamente, Wireshark lo mostra come un pacchetto perfettamente normale quando lo si carica.
0000 02 00 00 00 00 01 02 00 00 00 00 02 08 00 45 00 ..............E.
0010 00 9d 12 34 40 00 40 06 3b 99 c0 00 02 0a c6 33 ...4@.@.;......3
0020 64 50 cb 84 00 50 00 00 03 e8 00 00 07 d0 50 18 dP...P........P.
0030 ff ff a3 05 00 00 47 45 54 20 2f 69 6e 64 65 78 ......GET /index
0040 2e 68 74 6d 6c 20 48 54 54 50 2f 31 2e 31 0d 0a .html HTTP/1.1..
0050 48 6f 73 74 3a 20 65 78 61 6d 70 6c 65 2e 63 6f Host: example.co
0060 6d 0d 0a 55 73 65 72 2d 41 67 65 6e 74 3a 20 4b m..User-Agent: K
0070 6f 6d 75 72 61 53 6f 66 74 44 65 6d 6f 2f 31 2e omuraSoftDemo/1.
0080 30 0d 0a 41 63 63 65 70 74 3a 20 74 65 78 74 2f 0..Accept: text/
0090 68 74 6d 6c 0d 0a 43 6f 6e 6e 65 63 74 69 6f 6e html..Connection
00a0 3a 20 63 6c 6f 73 65 0d 0a 0d 0a : close....
Osservando la colonna ASCII a destra, si nota che a un certo punto (offset 0x36) inizia un testo leggibile da un essere umano, GET /index.html HTTP/1.1. Ma allora, cosa sono i 54 byte che lo precedono? Dando in pasto il frame al dissettore di esempio, ecco cosa viene riportato.
[ L2 Ethernet II | offset 0 | 14 bytes | dst=02:00:00:00:00:01 src=02:00:00:00:00:02 type=0x0800 (IPv4)
[ L3 IPv4 | offset 14 | 20 bytes | 192.0.2.10 -> 198.51.100.80 TTL=64 proto=6 (TCP) checksum=OK
[ L4 TCP | offset 34 | 20 bytes | 52100 -> 80 [Psh, Ack] seq=1000 win=65535
[ L7 HTTP | offset 54 | 117 bytes | GET /index.html HTTP/1.1
Questo è il diagramma che più di ogni altro vorrei che vedeste in questo articolo. Ci sono tre punti chiave.
- Ogni livello inizia esattamente dove finisce quello precedente. L’header Ethernet occupa i byte 0-13, l’header IPv4 i byte 14-33, l’header TCP i byte 34-53, e HTTP inizia dal byte 54. Un livello non è una categoria concettuale: lo si può indicare come un intervallo di byte a partire dall’inizio del frame.
- Un livello interno non è altro che il «payload» (il carico) del livello esterno. Dal punto di vista di Ethernet, tutto ciò che viene dopo — a partire da IPv4 — è semplicemente carico: non gli importa se il contenuto sia TCP oppure no. Dal punto di vista di IP, tutto ciò che viene dopo TCP è carico; dal punto di vista di TCP, HTTP è carico. Ogni livello legge solo il proprio header e passa il payload verso l’alto senza toccarlo. Questa struttura «indifferente» è esattamente l’incapsulamento.
- L1 (livello fisico) e L5/L6 non compaiono in questo diagramma. Il compito di L1 è convertire questa sequenza di byte in un segnale elettrico, luce o onde radio, quindi non compare mai in un dump; e come visto nel capitolo precedente, L5 e L6 non hanno un’entità indipendente nell’HTTP/1.1 in chiaro. «Dei sette livelli, solo quattro compaiono nel dump» — questa è la realtà dei fatti.
Lo ripeto: non c’è nulla di speciale in HTTP. Che si tratti di una connessione a un database, di gRPC o del GigE Vision di una fotocamera industriale, finché si tratta di comunicazione TCP/IP su Ethernet ogni singolo pacchetto ha questa stessa struttura annidata. L’unica cosa che cambia è il contenuto del carico di L7.
4. L2, il livello di collegamento dati — consegnare al «vicino»
Esaminiamo i risultati della dissezione procedendo dall’esterno verso l’interno. I primi 14 byte sono l’header Ethernet II.
02 00 00 00 00 01 Indirizzo MAC di destinazione (6 byte)
02 00 00 00 00 02 Indirizzo MAC sorgente (6 byte)
08 00 EtherType = 0x0800 (il payload è IPv4)
Il compito di L2 è «consegnare al nodo vicino all’interno dello stesso segmento di rete». Per specificare la destinazione si usa l’indirizzo MAC. L’indirizzo MAC è un identificatore assegnato alla scheda di rete (NIC) e, in linea di principio, non raggiunge mai la controparte attraversando un router. Un frame inviato da un PC d’ufficio a un server web ha come MAC di destinazione non il server web, ma il MAC del gateway predefinito (il router).
A questo punto, molti si chiedono almeno una volta: «se esiste già l’indirizzo IP, perché serve anche l’indirizzo MAC?». La risposta sta nella divisione dei ruoli tra i livelli. L’indirizzo IP è l’indirizzo che punta alla «destinazione finale», mentre l’indirizzo MAC è l’etichetta che indica «chi trasporterà il prossimo singolo tratto». Per usare un’analogia con la consegna pacchi, l’indirizzo IP è l’indirizzo di consegna sulla lettera di vettura, mentre l’indirizzo MAC è il nome del «prossimo centro di smistamento». La lettera di vettura (L3) non cambia mai fino alla fine, ma l’etichetta (L2) viene sostituita a ogni tratto. ARP è ciò che permette di risalire all’indirizzo MAC di un nodo vicino a partire da un indirizzo IP, e con il comando arp -a si può consultare la tabella di corrispondenza che il PC ha memorizzato.
In termini di dispositivi, quello che inoltra i dati leggendo L2 è lo switch (switching hub). Lo switch sceglie la porta di uscita guardando solo l’indirizzo MAC di destinazione, senza mai guardare l’indirizzo IP trasportato come carico.
5. L3, il livello di rete — consegnare fino ai confini del mondo
I successivi 20 byte sono l’header IPv4. Ecco i principali campi estratti.
45 Versione = 4, lunghezza header = 5 word (20 byte)
00 9d Lunghezza totale: 157 byte (header IP + TCP + HTTP; non include i 14 byte di Ethernet)
40 06 TTL = 64, protocollo = 6 (il payload è TCP)
3b 99 Checksum dell'header
c0 00 02 0a IP sorgente 192.0.2.10
c6 33 64 50 IP destinazione 198.51.100.80
Il compito di L3 è «consegnare all’host di destinazione finale, indipendentemente da quanti router bisogna attraversare». Mentre L2 trasporta solo per un tratto (un hop) alla volta, l’indirizzo IP di destinazione di L3 rimane invariato dall’inizio alla fine della comunicazione. Un router osserva l’IP di destinazione del pacchetto ricevuto, decide «a quale router passarlo ora» e lo invia dopo aver sostituito l’header L2.
Il TTL (Time To Live) è il campo che rende visibile questo movimento di «attraversamento dei router». Diminuisce di 1 ogni volta che attraversa un router e, quando arriva a 0, il pacchetto viene scartato e un errore viene restituito al mittente. tracert (il comando di Windows per tracciare il percorso) funziona inviando deliberatamente pacchetti con TTL impostato a 1, 2, 3 e così via, facendo emergere uno per uno i router lungo il percorso. In altre parole, ogni riga che compare nell’output di tracert è la manifestazione concreta di «un hop di L3».
6. L4, il livello di trasporto — decidere a quale processo consegnare
I successivi 20 byte sono l’header TCP.
cb 84 Porta sorgente 52100
00 50 Porta di destinazione 80
00 00 03 e8 Numero di sequenza
00 00 07 d0 Numero di conferma (ACK)
50 18 Lunghezza header = 5 word, flag [PSH, ACK]
ff ff Dimensione della finestra
a3 05 Checksum
Con L3, il pacchetto è arrivato all’host (la macchina) di destinazione. Ma su quella macchina, il server web, il database e molti altri processi potrebbero comunicare tutti contemporaneamente. Uno dei compiti di L4 è usare il numero di porta per decidere a quale processo (a quale socket) consegnare i dati. La porta di destinazione 80 è, per convenzione, «il numero su cui è in ascolto un server HTTP»; il sistema operativo guarda questo numero e accoda i dati nel buffer di ricezione del processo corrispondente.
L’altro grande compito di TCP è fornire un «flusso di byte affidabile» attraverso numeri di sequenza, conferme (ACK), ritrasmissione e controllo di flusso.4 Questo comportamento, e il modo di gestirlo, sono argomenti abbastanza profondi da meritare ciascuno un articolo a sé, quindi vi rimando a l’articolo sui confini dei messaggi TCP e a l’articolo sulla ritrasmissione TCP. Qui, l’importante è cogliere questa sensazione: «al di sopra di L4, le cose non hanno più la forma di una rete: sembrano piuttosto una pipe che collega direttamente un processo a un altro».
C’è un fatto curioso che mostra lo scarto tra l’ideale del modello e la realtà. Il checksum di TCP non è definito come calcolato sul solo segmento TCP, bensì anteponendo uno «pseudo-header» che elenca le informazioni di L3 (gli indirizzi IP sorgente e di destinazione).4 Se i livelli fossero davvero indipendenti in modo pulito, non ci sarebbe motivo per cui gli indirizzi di L3 si mescolino nel calcolo di L4. È un buon esempio di come il modello OSI sia solo una mappa utile per fare ordine nel pensiero, mentre i protocolli reali attraversano i confini tra livelli ogni volta che serve.
7. L5 e L6 — dove il modello e la realtà divergono
Ora, avrete notato che nel diagramma di dissezione non comparivano L5 (livello di sessione) e L6 (livello di presentazione). Questo è il motivo principale per cui si ha la sensazione di «non capire OSI», quindi diciamolo chiaramente.
L5 e L6 non esistono come livelli indipendenti nello stack TCP/IP reale. Nel modello Internet della RFC 1122, tutto ciò che sta sopra TCP è semplicemente «livello di applicazione».2 I ruoli previsti dal modello OSI sono, in realtà, dispersi e assorbiti come segue.
| Ciò che OSI prevedeva | Dove viene effettivamente assorbito nella realtà |
|---|---|
| L5: stabilire e gestire un dialogo (sessione) | Sessione/handshake TLS, cookie/token HTTP, gestione del login propria dell’applicazione |
| L6: trasformazione e cifratura della rappresentazione dei dati | Cifratura TLS, codifica dei caratteri (UTF-8), formati di serializzazione come JSON |
Per esempio, nel caso di HTTPS, TLS si inserisce sopra TCP (L4) e HTTP scorre al suo interno. «A quale livello appartiene TLS?» è il tipo di domanda che verrebbe voglia di mettere in un esame, ma non c’è alcun valore pratico nel fissare un’unica risposta corretta. Sapere spiegare che si colloca sopra L4, sotto L7, e combina sia la gestione della sessione (un ruolo simile a L5) sia la cifratura (un ruolo simile a L6) è più importante che saper rispondere immediatamente «è L6».
Nel codice .NET, questa relazione si manifesta direttamente nel modo in cui le classi si avvolgono l’una nell’altra. Se si avvolge con SslStream lo stream di L4 ottenuto da TcpClient.GetStream(), il testo in chiaro scritto con Write viene cifrato in record TLS prima di essere passato a TCP.5
using var client = new TcpClient("example.com", 443);
// Avvolge lo stream di byte di L4 con TLS (corrispondente a L5/L6)
using var ssl = new SslStream(client.GetStream());
await ssl.AuthenticateAsClientAsync("example.com");
// Qui si scrive testo in chiaro di L7 (HTTP). La cifratura è compito di SslStream
await ssl.WriteAsync(Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\n..."));
Questo schema — uno stream che ne avvolge un altro — è l’equivalente, a livello di codice, dell’incapsulamento. Osservando in Wireshark il traffico sulla porta 443, il payload TCP appare solo come un blocco opaco etichettato Application Data, il che permette di confermare, in direzione inversa, che «L7 è stato racchiuso in qualcosa di equivalente a L6».
8. Quale livello sta effettivamente toccando il vostro codice C#
Colleghiamo tutto quanto visto finora agli strumenti di uno sviluppatore di applicazioni Windows.
| Livello | Esempio reale | Esempio di API .NET | Chi aggiunge l’header |
|---|---|---|---|
| L7 Applicazione | HTTP, gRPC, protocollo personalizzato | HttpClient, Grpc.Net.Client, assemblaggio manuale dei messaggi |
Il vostro codice / una libreria |
| (Corrisponde a L5/L6) | TLS, serializzazione, codifica dei caratteri | SslStream, JsonSerializer, Encoding |
Una libreria |
| L4 Trasporto | TCP, UDP | Socket, TcpClient, UdpClient (la generazione dell’header è a carico del sistema operativo) |
Lo stack di protocolli del sistema operativo |
| L3 Rete | IP, instradamento, ICMP | Ping, NetworkInterface (solo lettura) |
Lo stack di protocolli del sistema operativo |
| L2 Collegamento dati | Ethernet, Wi-Fi, ARP | (normalmente non accessibile direttamente da un’app) | Il driver della NIC / la NIC |
| L1 Fisico | Segnale elettrico, luce, onde radio | – | La NIC / il cavo / l’aria |
Da questa tabella emergono due cose.
Primo, scegliere un’API significa scegliere «da quale livello in giù delegare». Usando HttpClient si può delegare fino all’assemblaggio di L7, mentre usando Socket si finisce per scrivere tutto L7 da soli. Al contrario, una normale applicazione Windows non tocca quasi mai direttamente L3 o livelli inferiori (i raw socket richiedono privilegi di amministratore e forti restrizioni).
Secondo, ciò che si passa a Socket.Send è «solo» la sequenza di byte di L7. La Part 2 del campione è una demo che invia realmente una richiesta HTTP tramite loopback, ma l’unica cosa che l’applicazione prepara sono 60 byte di testo HTTP; i 54 byte di header visti nel Capitolo 3 vengono aggiunti dal sistema operativo e dalla NIC. Il codice di rete della vostra app, in realtà, si limita a costruire il carico più interno dei sette livelli — questa è la relazione esatta tra il modello OSI e il vostro codice.
using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
await socket.ConnectAsync(IPAddress.Loopback, port);
byte[] request = Encoding.ASCII.GetBytes(
$"GET / HTTP/1.1\r\nHost: localhost:{port}\r\nConnection: close\r\n\r\n");
// Ciò che passiamo è solo la sequenza di byte di L7. Gli header TCP/IP/Ethernet
// vengono aggiunti dall'altra parte di questa chiamata (dal sistema operativo e dalla NIC)
int sent = await socket.SendAsync(request);
9. Mettere le mani in pasta — assemblare un frame e aprirlo in Wireshark
Per trasformare «ho letto e capito» in «ho visto e capito», vediamo come eseguire il codice di esempio.
Al centro del campione c’è SampleFrameBuilder, che assembla il frame del Capitolo 3 esattamente nell’ordine in cui viene avvolto da L7 a L2. Ciò che accade a ogni livello durante l’invio corrisponde direttamente all’ordine in cui compaiono i metodi.
public static byte[] BuildHttpGetFrame()
{
// L7: l'unica cosa che l'applicazione passa a Send è questa sequenza di byte
byte[] http = Encoding.ASCII.GetBytes(HttpRequest);
// L4: lo stack di protocolli del sistema operativo antepone l'header TCP
byte[] tcp = WrapInTcp(http);
// L3: si antepone inoltre l'header IPv4
byte[] ip = WrapInIpv4(tcp);
// L2: il driver della NIC antepone l'header Ethernet appena prima del cavo
return WrapInEthernet(ip);
}
Eseguendo la demo, oltre a mostrare il dump esadecimale e la vista annidata, questo frame viene anche scritto come file pcap.
dotnet run --project samples/Demo
Provate ad aprire il file sample-http-get.pcap generato con Wireshark.6 Nel riquadro centrale compariranno quattro righe in verticale — Ethernet II / Internet Protocol Version 4 / Transmission Control Protocol / Hypertext Transfer Protocol — e facendo clic su una qualsiasi di esse verrà evidenziato solo l’intervallo di byte corrispondente nel dump esadecimale sottostante. Uno strumento standard del settore vi mostra esattamente la stessa cosa della vista annidata del Capitolo 3. Una volta verificato questo, il passo successivo è il traffico reale. Avviate una cattura in Wireshark, impostate un filtro tipo tcp.port == 443, aprite un sito qualsiasi nel browser, e potrete confermare che tutta la comunicazione reale è costruita con questo stesso annidamento.
Va inoltre notato che anche nell’implementazione del dissettore (PacketDissector) è nascosto un insegnamento pratico. Ad esempio, quando si estrae il carico di IPv4, occorre fidarsi del campo TotalLength dell’header invece di prendere semplicemente tutto il resto del buffer di ricezione, perché Ethernet ha una lunghezza minima di frame (60 byte) e ai pacchetti brevi viene aggiunto in coda un padding privo di significato. «Rimuovere una necessità del livello esterno (il padding) usando l’informazione di lunghezza del livello interno» — è un altro esempio di come la divisione dei ruoli tra livelli si manifesti nell’implementazione, ed è verificato con un test unitario.
10. Usare il modello OSI nella pratica — restringere il campo con il vocabolario dei livelli
All’inizio ho scritto che «OSI è sopravvissuto come vocabolario». Vediamo due situazioni in cui quel vocabolario si dimostra davvero utile.
Il troubleshooting. Una segnalazione del tipo «non riesco a connettermi al server», tradotta nel vocabolario dei livelli, può essere ristretta in modo sistematico. La prassi consolidata è controllare dal basso verso l’alto.
| Verifica | Cosa si usa | Livello di cui si conferma il funzionamento |
|---|---|---|
| Spia di collegamento / indicatore di connessione Wi-Fi | Controllo visivo | L1-L2 |
| Ping al gateway sullo stesso segmento | ping 192.168.x.1 |
L3 nelle immediate vicinanze |
| Ping all’host remoto | ping <destinazione> |
L3 lungo l’intero percorso |
| Connessione TCP alla porta remota | Test-NetConnection <destinazione> -Port 443 |
L4 (più eventuali firewall lungo il percorso) |
| Invio di una richiesta HTTP | curl -v o l’app stessa |
L7 (più TLS) |
Per esempio, se «il ping funziona ma Test-NetConnection fallisce», tutto fino a L3 è sano e il sospetto si restringe a qualcosa che blocca la porta a livello L4 (un servizio fermo, un firewall, un numero di porta configurato male). Se «la connessione TCP funziona ma HTTP restituisce 400», il problema non è la rete ma L7 (il contenuto della richiesta). Invece di staccare e riattaccare cavi alla cieca, stabilire un livello alla volta fino a dove tutto funziona ancora — è così che il modello OSI si usa nella pratica.
Un linguaggio comune per la conversazione. Frasi come «sembra un problema di L2, potresti controllare la porta dello switch» o «quello è un problema di L7, quindi tocca al team applicativo» si capiscono con precisione tra chi si occupa di rete, di infrastruttura e sviluppo applicativo. Il numero del livello è una coordinata condivisa a livello di settore per indicare in modo conciso un punto di responsabilità.
11. Correggere i fraintendimenti più comuni
Per concludere, raccogliamo e correggiamo i fraintendimenti più comuni che si incontrano riguardo al modello OSI.
- «Internet funziona sui sette livelli di OSI» — Non è così. Ciò che è implementato è TCP/IP (di fatto quattro livelli); OSI è un modello di riferimento per la spiegazione e la conversazione.2
- «I quattro livelli di TCP/IP corrispondono esattamente ai sette livelli di OSI» — La corrispondenza tra L5 e L7 è intrinsecamente ambigua. Si vedono spesso tabelle che affermano «livello di applicazione di TCP/IP = L5+L6+L7 di OSI», ma come visto nel Capitolo 7, i ruoli di L5 e L6 sono in realtà dispersi tra TLS e i formati di serializzazione.
- «TLS è un protocollo di livello 6 (o di livello 5)» — Non ha senso fissarlo a un unico livello. La descrizione accurata è che si colloca sopra L4, sotto L7, e combina ruoli equivalenti sia a L5 sia a L6.
- «Basta guardare il numero di porta per sapere quale protocollo sia» — La porta 80 non garantisce che sia HTTP. Il numero di porta è una convenzione; non si può sapere davvero cosa stia scorrendo finché non si osserva il payload. È proprio per questo che il dissettore di esempio identifica HTTP guardando i caratteri iniziali del contenuto, non il numero di porta.
- «Gli switch sono dispositivi L2, i router sono dispositivi L3» — È corretto come punto di partenza, ma nella realtà esistono normalmente dispositivi che attraversano più livelli: switch L3, load balancer che smistano a L4, WAF che ispezionano L7. È più accurato pensare in termini di «fino a quale livello legge questo dispositivo».
12. Riepilogo
- Il modello OSI non è un’implementazione, ma un vocabolario per pensare per livelli. Ciò che funziona realmente è TCP/IP (di fatto quattro livelli)
- I sette livelli non sono un diagramma concettuale: sono fisicamente annidati all’interno della sequenza di byte di un singolo frame (L2: 0-13, L3: 14-33, L4: 34-53, L7: 54 in poi)
- Ogni livello legge solo il proprio header e non si preoccupa del carico (i livelli interni) — questo è l’incapsulamento
- L5 e L6 non esistono in modo indipendente nello stack reale; TLS e la codifica ne hanno assorbito i ruoli
- Il vostro codice C# scrive solo la sequenza di byte di L7. Gli header TCP/IP vengono aggiunti dal sistema operativo, Ethernet dalla NIC
- Nella pratica, l’utilità sta nel troubleshooting che verifica la sopravvivenza un livello alla volta dal basso verso l’alto, e in un linguaggio comune tra i team
- Assemblando un frame con il codice di esempio e aprendolo in Wireshark, si può verificare con i propri occhi tutto il contenuto di questo articolo
Articoli correlati
- Il fraintendimento secondo cui si può ricevere con Receive le stesse unità inviate con Send su TCP — progettare la ricezione trattando i dati come un flusso di byte
- Diagnosi del motivo per cui la ritrasmissione TCP blocca la comunicazione con una fotocamera industriale
- Tabella pratica per le decisioni su async/await in C# — Task.Run e ConfigureAwait
Aree di consulenza correlate
KomuraSoft LLC (合同会社小村ソフト) si occupa della progettazione e implementazione di applicazioni Windows che comunicano via TCP/IP, dell’indagine sulle cause di problemi di comunicazione con apparecchiature industriali (“a volte si interrompe”, “rallenta”), e del supporto al troubleshooting tramite cattura dei pacchetti.
- Sviluppo di applicazioni Windows
- Indagine su bug e analisi delle cause
- Consulenza tecnica e revisione della progettazione
- Contatti
Riferimenti
-
ITU-T, X.200 : Information technology - Open Systems Interconnection - Basic Reference Model: The basic model. L’origine del modello di riferimento di base OSI (equivalente per contenuto a ISO/IEC 7498-1). Sulla definizione di ciascuno dei sette livelli. ↩ ↩2
-
IETF, RFC 1122 - Requirements for Internet Hosts – Communication Layers. La RFC che definisce i requisiti che gli host Internet devono implementare. Sull’organizzazione della suite di protocolli in quattro livelli: livello di collegamento, livello IP, livello di trasporto e livello di applicazione. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Socket Class (System.Net.Sockets). L’API socket di .NET. Sul fatto che l’applicazione passa il payload mentre lo stack di protocolli del sistema operativo è responsabile della generazione degli header di protocollo. ↩
-
IETF, RFC 9293 - Transmission Control Protocol (TCP). La specifica attuale di TCP. Sulla fornitura di affidabilità tramite numeri di sequenza e conferme, e sull’uso di uno pseudo-header contenente gli indirizzi IP nel calcolo del checksum. ↩ ↩2
-
Microsoft Learn, SslStream Class (System.Net.Security). Sulla classe che avvolge uno stream esistente (in genere il
NetworkStreamdi TCP) per fornire cifratura e autenticazione TLS. ↩ -
Wireshark Foundation, Wireshark User’s Guide. Su come aprire un file di cattura, sulla corrispondenza tra il riquadro dei dettagli del pacchetto e quello della sequenza di byte, e su come usare i filtri di visualizzazione. ↩
Articoli correlati
Articoli recenti con gli stessi tag per approfondire argomenti vicini.
Icone nella system tray e notifiche toast nelle app Windows — le insidie di NotifyIcon e come scegliere l'AppNotification giusta
Una guida pratica per mantenere un'applicazione Windows aziendale residente nella system tray (area di notifica) e avvisare l'utente tram...
Fino a quando funzioneranno le applicazioni VB6? — Lo stato del supporto al runtime e un percorso pratico verso la migrazione a .NET
Fino a quando continueranno a funzionare le applicazioni VB6? Questo articolo chiarisce l'asimmetria tra la politica di supporto del runt...
Era giapponese, festività e date di chiusura nelle applicazioni aziendali — progettazione resiliente ai cambi di era, JapaneseCalendar e calcolo dei giorni lavorativi nella pratica
Mostrare «Reiwa 8» su un documento, calcolare i giorni lavorativi escludendo le festività, pagare entro la fine del mese successivo dopo ...
Come comprendere l'isolamento delle sessioni di Windows — Session 0, RDP ed esecuzione simultanea di più utenti
Questo articolo chiarisce il concetto di «sessione» di Windows, un argomento che genera costante confusione tra gli sviluppatori di appli...
Internazionalizzazione delle app WinForms/WPF — la pratica di resx, satellite assembly e cambio cultura
Analizziamo dal punto di vista pratico l'internazionalizzazione delle applicazioni desktop Windows: la differenza tra CurrentCulture e Cu...
Argomenti correlati
Queste pagine collocano l’argomento in un contesto più ampio di servizi e decisioni.
Argomenti tecnici Windows
Portale su sviluppo Windows, analisi dei problemi e valorizzazione delle risorse esistenti.
Analisi dei bug e guasti di lunga durata
Errori intermittenti, diagnosi delle comunicazioni, crash prolungati e test dei percorsi di errore.
Servizi collegati all’argomento
L’articolo è direttamente collegato ai servizi seguenti.
Sviluppo di applicazioni Windows
Applicazioni aziendali, integrazione di dispositivi e strumenti di comunicazione, dai requisiti allo sviluppo.
Domande frequenti
Domande che ricorrono nelle consulenze sull’argomento dell’articolo.
- I sette livelli del modello OSI sono effettivamente usati su Internet nella realtà?
- No, non lo sono. Ciò che funziona realmente su Internet è la suite di protocolli TCP/IP (di fatto quattro livelli); i protocolli OSI a 7 livelli veri e propri hanno perso la corsa all'adozione negli anni '90 e sono finiti per non essere quasi mai usati. La RFC 1122, che ha gettato le basi di Internet, descrive il mondo in termini di quattro livelli: il livello di collegamento, il livello IP, il livello di trasporto e il livello di applicazione. Ciò che è sopravvissuto è il modello come vocabolario condiviso per il troubleshooting e la conversazione — frasi come «isoliamo questo al livello L2» o «questo è un problema di L7».
- Dove si trovano realmente il livello di sessione (L5) e il livello di presentazione (L6)?
- Non esistono come livelli indipendenti nello stack TCP/IP reale. I ruoli previsti dal modello OSI sono in realtà dispersi e assorbiti altrove: L5 (stabilire e gestire un dialogo) corrisponde all'handshake TLS o ai cookie/token HTTP, e L6 (trasformare e cifrare la rappresentazione dei dati) corrisponde alla cifratura TLS, alla codifica dei caratteri e ai formati di serializzazione come JSON. Non c'è alcun valore pratico nello stabilire a quale singolo livello appartenga TLS — ciò che conta di più è saper spiegare che si colloca sopra L4 e sotto L7, combinando ruoli equivalenti a L5 e L6.
- Se esiste già un indirizzo IP, perché serve anche un indirizzo MAC?
- Perché i livelli si dividono le responsabilità in modo diverso. L'indirizzo IP (L3) è l'indirizzo che punta alla destinazione finale, e non cambia dall'inizio alla fine della comunicazione. L'indirizzo MAC (L2) è l'etichetta che indica chi trasporterà il prossimo singolo salto, e viene sostituito ogni volta che attraversa un router. Un frame inviato da un PC d'ufficio a un server web ha come MAC di destinazione non il server web, ma il gateway predefinito (il router). ARP è ciò che permette di risalire all'indirizzo MAC di un nodo vicino a partire da un indirizzo IP, e la tabella di corrispondenza si può consultare con il comando arp -a.
- A cosa serve concretamente il modello OSI nella pratica?
- Al troubleshooting e alla conversazione tra team. Una segnalazione del tipo «non riesco a connettermi al server» può essere ristretta sistematicamente verificando, livello per livello dal basso verso l'alto, cosa funziona ancora: controllo visivo della spia di collegamento (L1-L2), ping al gateway (L3), verifica di una connessione TCP verso la porta di destinazione (L4) e invio di una richiesta HTTP (L7). Ad esempio, se il ping funziona ma la connessione TCP fallisce, il sospetto si restringe a qualcosa che blocca la porta a livello L4, come un servizio fermo o un firewall. Funziona anche come sistema di coordinate condiviso a livello di settore per indicare in modo conciso un punto di responsabilità, come in «sembra un problema di L2, potresti controllare lo switch».
Profilo dell’autore
Pagina di presentazione dell’autore dell’articolo.
Go Komura
Rappresentante di KomuraSoft LLC
Specializzato nello sviluppo di software Windows, nella consulenza tecnica e nell’analisi dei malfunzionamenti, soprattutto nei progetti con sistemi esistenti e guasti difficili da riprodurre.
Link pubblici