Capire davvero il modello OSI — sezionare una singola richiesta HTTP nei suoi sette livelli

· · 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 tra HttpClient, SslStream e Socket significa 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.

  1. 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.
  2. 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.
  3. 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».
  • 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

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.

Riferimenti

  1. 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

  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

  3. 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. 

  4. 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

  5. Microsoft Learn, SslStream Class (System.Net.Security). Sulla classe che avvolge uno stream esistente (in genere il NetworkStream di TCP) per fornire cifratura e autenticazione TLS. 

  6. 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 recenti con gli stessi tag per approfondire argomenti vicini.

Queste pagine collocano l’argomento in un contesto più ampio di servizi e decisioni.

L’articolo è direttamente collegato ai servizi seguenti.

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.

Torna al blog