Il protocollo TCP/IP
INTRODUZIONE A TCP/IP
Innanzi tutto spieghiamo il nome. TCP sta per Transfer Control Protocol, IP sta per Internet Protocol. In realtà il protocollo TCP/IP è una famiglia di protocolli, ognuno con una sua funzione particolare, TCP ed IP sono due protocolli importanti di questa famiglia. Alcuni di questi, come TCP, UDP, IP, ICMP e ARP (li vedremo meglio dopo) sono di basso livello. Questo significa che essi lavorano vicino al livello fisico della rete (vedremo infatti che i loro compiti sono strettamente correlati con la trasmissione effettiva dei dati). La loro funzione è di fornire servizi ai protocolli superiori e alle applicazioni. Se non avete capito bene questo concetto vi consiglio di leggere la pagina Il viaggio dei dati. Tutti i protocolli di livello superiore, sono specializzati nel compiere qualche servizio particolare. Ad esempio FTP, File Transfer Protocol, permette il trasferimento dei file, TELNET, network terminal protocol, permette di parlare con un computer remoto come se vi foste davanti, SMTP, Simple Mail Trasfer Protocol, serve a trasferire la posta elettronica, SNMP, Simple Network Management Protocol, per gestire e monitorare lo stato della rete. Questi non sono tutti, sono solo i più importanti. Per la loro diversità, ritengo che descriverli uno per uno in dettaglio, andrebbe al di là dello scopo di questo documento. I protocolli di livello superiore basano il loro lavoro su un’assunzione: che i protocolli di basso livello siano in grado di comunicare in modo affidabile.
IL PROTOCOLLO TCP
Vediamo questa struttura al lavoro facendo un semplice esempio, immaginate di voler inviare un messaggio su una rete TCP/IP (Internet è una di queste). Ciò che il protocollo di posta SMTP produce, viene passato in basso al TCP.
Innanzi tutto diciamo che TCP fornisce un servizio byte-stream. Questo termine indica che i dati da e per i livelli superiori vengono presentati e ricevuti come un unico flusso di byte, e non come pacchetti. Questo significa che è TCP, e non i protocolli superiori (e quindi le applicazioni), a doversi preoccupare di preparare i pacchetti, con l’evidente vantaggio di avere una chiara la distinzione dei ruoli: SMTP (o qualsiasi altra applicazione di alto livello) pensa a preparare il messaggio di posta, TCP pensa a come deve inviarlo. Un pacchetto TCP è detto segmento.
Essenzialmente i compiti di TCP sono:
- suddividere i dati da spedire in tanti segmenti indipendenti e numerati.
- riassemblare i dati arrivati all’altro capo, presentandoli nuovamente come un flusso di byte.
- rispedire i datagrammi non arrivati o arrivati corrotti.
- rimetterli nel giusto ordine se alcuni di essi non hanno „rispettato il turno“.
- controllare il flusso attraverso il meccanismo delle finestre e dell’acknowledgement che descrivo tra poco.
- multiplexing attraverso l’uso delle porte. Ne parlo nell’header TCP.
L’header TCP
Ad ogni segmento, TCP aggiunge il suo header. Se non sapete cos’è un header dovreste davvero leggere la pagina Il viaggio dei dati. Nell’header sono contenuti i seguenti campi; quelli in rosso sono i più importanti
- Source port/Destination port; (16 bit + 16 bit) quando iniziate ad inviare o a ricevere dati, qui vengono memorizzati due numeri di porta, uno al quale inviare i dati, uno sul quale i dati verranno spediti da parte dell’altro computer. Sull’altra macchina, i numeri di porta sono uguali ma scambiati. Per ogni comunicazione full-duplex, sul vostro computer (e su quello a cui vi siete connessi) vengono aperte due connessioni, una in trasmissione e una in ricezione, ognuna con il suo numero di porta. Fate attenzione a non confondere le porte con gli indirizzi IP.
Qui apro una piccola ma importante parentesi per far capire quanto sia utile il concetto di porta nelle comunicazioni TCP. Normalmente, quando stabilite una comunicazione generica, TCP genera un numero di porta casuale. Se però volete iniziare una connessione con un protocollo di alto livello (cioè il 99% dei casi), come per esempio il protocollo SMTP, perchè volete mandare un messaggio di posta all’altro computer, dovete prima di tutto specificare che volete una connessione attraverso la porta n.25 (o lo farà per voi il vostro lettore di posta). Solo dietro a questo numero di porta troverete SMTP in ascolto. Ci sono diversi „numeri magici“ standard (detti „well known services“, servizi ben noti), ad esempio 21 per FTP, 23 per Telnet, 53 per il servizio DNS, 80 per il Web Server. Potete trovare una lista dei principali servizi noti in C:WindowsServices, o in /etc/services in Linux.
- Sequence number; (32 bit) contiene il numero necessario per sapere quale sia l’ordine dei segmenti e per sapere se qualcuno è andato perduto. Diversamente da come si potrebbe pensare, non viene usato il numero di segmento, ma il numero del primo byte di quel segmento. Quindi se ogni segmento contiene 1500 byte, il sequence number sarà 0…1500…3000… , e non 0…1…2… .
- Acknowledgment number; (32 bit) viene usato per segnalare che avete ricevuto tutti i dati fino al numero di byte specificato meno uno, e dovrebbe essere uguale al valore del prossimo Sequence number che sarà ricevuto.
- Window; (16 bit) nei segmenti con il flag ACK, indica in byte l’ampiezza della finestra che il computer è in grado di ricevere. Per il funzionamento vedi il paragrafo Il meccanismo delle finestre scorrevoli.
- Data offset; (4 bit) è il numero di parole a 32-bit dell‘ header TCP. Indica dove iniziano i dati.
- Reserved; (6 bit) riservato ad usi futuri.
- Flags; (1 bit per ogni flag) è composto dai seguenti campi:
- Flag URG; (urgent) se attivo indica che il campo Urgent Pointer è valido e deve essere letto.
- Flag ACK; (acknowledgement) se attivo indica che il campo Acknowledgement number è valido e deve essere letto.
- Flag PSH; (push) usato per indicare che il segmento deve essere inviato immediatamente. Utile nei programmi interattivi come Telnet, dove vogliamo che i comandi digitati con la tastiera siano inoltrati quanto prima. Senza il Flag PSH, per motivi di efficienza il TCP accumula tutti i dati in un buffer interno, la cui spedizione viene ritardata fino a che non si sia completamente riempito.
- Flag RST; (reset) usato per reinizializzare completamente la connessione TCP corrente.
- Flag SYN; (synchronize) usato quando viene stabilita una sessione, indica che il ricevente dovrà leggere il campo Sequence number e sincronizzare il proprio con esso.
- Flag FIN; (finish) indica che non ci sono altri dati da trasmettere. La connessione rimane comunque aperta in ricezione.
- Checksum; (16 bit) un numero che serve per sapere se il datagramma corrente contiene errori nel campo dati.
- Urgent pointer; (16 bit) indica che il ricevente deve iniziare a leggere il campo dati a partire dal numero di byte specificato. Viene usato se si inviano comandi che danno inizio ad eventi asincroni „urgenti“. Ad esempio il comando Control-C in una sessione Telnet.
- Options; (lunghezza variabile) contiene varie opzioni di TCP (Maximum Segment Size, Window Scale, Sack Permitted, Sack, Time Stamp).
- Padding; (lunghezza variabile) una serie di 0 inserita perchè la lunghezza dell’header TCP sia un multiplo di 32 bit.
E finalmente
- Data; il nostro campo dati.
Il meccanismo delle finestre scorrevoli
Cosi come l’abbiamo descritto potremmo pensare che il lavoro del TCP consista semplicemente nel mandare un segmento, aspettare l’acknowledgement per quel pacchetto, mandare un altro pacchetto. Se l’Ack non arriva entro un certo intervallo di tempo ritrasmettere il primo segmento. Anche se funziona, questo modo di procedere è estremamente inefficiente poichè sfrutta solo una piccola parte della larghezza di banda disponibile. Un sistema molto più efficiente ed ugualmente affidabile, è il meccanismo a finestre. Il funzionamento è il seguente.
- Il mittente invia una „finestra“ di pacchetti TCP, da 1 a n, ma senza aspettare l’Ack, inoltre fa partire un timer per ognuno di essi. Il numero n è detto ampiezza della finestra.
- Il ricevente manda un Ack per ogni pacchetto.
- Il mittente sposta la finestra „in avanti“ di un pacchetto per ogni Ack ricevuto nell’ordine. Cioè quando arriva l’Ack del segmento 1, la finestra viene spostata in modo da coprire i pacchetti da 2 a n+1 e viene trasmesso il segmento n+1.
Consideriamo ora qualche caso particolare.
Se il pacchetto 2 non arrivasse a destinazione, la finestra non verrebbe spostata oltre il pacchetto 1. Il destinatario manderebbe gli Ack dei pacchetti 3, 4, 5… ma tutti uguali, cioè settati al valore 1, dato che è il questo l’ultimo pacchetto valido, ricevuto nell’ordine di consegna. Ad un certo punto il timer per 2 scade e il pacchetto viene ritrasmesso. A questo punto però sorge una domanda: dobbiamo ritrasmettere anche 3, 4, 5… ? Purtroppo non possiamo saperlo. Se mandiamo solo 2, ma anche 3, 4, 5… sono andati persi dovremo aspettare che scadano i timer di tutti questi altri segmenti. Alternativamente possiamo rimandare tutta la finestra. E‘ comunque chiaro che nessuna soluzione è priva di inefficienze, perchè l’informazione del campo Ack non è sufficientemente espressiva: non dice nulla del frame ricevuto, dice solo qual’è l’ultimo frame valido ricevuto nell’ordine di consegna. Altra caso particolare: il pacchetto 2 viene ricevuto correttamente, ma è l’Ack che viene perso. Semplicemente, il mittente riceverà prima o poi un Ack con valore 3, questo indica che tutti i pacchetti fino al terzo sono arrivati a destinazione, quindi anche il secondo. Dopo l’Ack 3 il mittente può spostare la finestra in avanti di 2 passi in una volta. La finestra ora coprirà i pacchetti da 4 a n+3.
Nella realtà, per identificare i segmenti si usa il Sequence number non il numero di pacchetto (vedi la def. di Sequence number, nell’header TCP). Inoltre l’ampiezza della finestra è variabile da parte del ricevente durante la connessione grazie al campo Window.
Stabilire una connessione TCP: l’handshake a tre vie
Quando due computer utilizzano TCP devono innanzitutto creare una sessione. La procedura attraverso la quale la sessione viene stabilita si chiama „three way handshacking“, o handshacking a tre vie.
Se il computer client PC1 vuole connettersi al computer server PC2, succede questo:
- PC1 manda a PC2 un segmento TCP attivando il flag SYN.
- PC2 risponde a PC1 con i flag SYN e ACK attivi.
- PC1 risponde a sua volta con il flag ACK.
Al primo passo, PC1 attiva nel primo segmento il flag SYN per indicare a PC2 che il campo Sequence number è valido e che quindi deve essere letto. Il valore settato da PC1 in quel campo è detto Sequence number iniziale, o ISN (Initial Sequence Number). PC2 risponde attivando il flag SYN ed indicando un proprio ISN, inoltre attiva l’ACK indicando l’ISN+1 di PC1. Infine PC1 attiva l’ACK indicando l’ISN+1 di PC2. A questo punto la comunicazione è stabilita e PC1 può iniziare ad inviare gli altri segmenti.
Ad esempio:
n.___mitt.___dest._____SYN Flag___ACK Flag_____Seq. n.__________Ack. n.
1____PC1____PC2_____Si_________No_________2481573249_____1875913495
2____PC2____PC1_____Si_________Si_________408548955______2481573250
3____PC1____PC2_____No________Si_________2481573250_____408548956
Un problema di questa tecnica è la vulnerabilità ad un famoso tipo di DoS (Denial of Service): il SYN-Flooding. In sostanza il client (PC1) richiede decine e decine di connessioni al server vittima (PC2) inondandolo di pacchetti SYN (inondare = to flood). PC2 risponde con tanti SYN-ACK, a cui però PC1 volutamente non risponde (in teoria dovrebbe completare gli handshake con un ACK per ogni connessione). PC2 dopo un pò smette di aspettare, ma quando i SYN ricevuti sono troppi, rimane pressochè paralizzato: dato che esiste un limite massimo al numero di connessioni che si possono stabilire su PC2, un eventuale utente PC3 che si collega a PC2 non verrebbe accettato perchè tutti i canali disponibili sono impegnati (ad aspettare ACK che non arrivano).
Chiudere una connessione TCP: il saluto a quattro vie
Quando si vuole terminare una connessione, si usa una procedura di terminazione a quattro vie. Un computer manda all’altro un segmento con il flag FIN attivo. Il flag FIN chiude la connessione in una sola direzione. All’altro capo verrà spedito tutto ciò che deve ancora essere spedito, dopodichè anche questo invierà un segmento con il flag FIN. Ora che la comunicazione è chiusa in entrambi i sensi, la sessione può considerarsi terminata..
Ad esempio:
n.___mitt.____dest._____FIN Flag___ACK Flag____Seq. n.__________Ack. n.
1____PC1_____PC2_____Si_________Si________408549508______2481575301
2____PC2_____PC1_____No________Si________2481575301_____408549509
3____PC2_____PC1_____Si_________Si________2481575301_____408549509
4____PC1_____PC2_____No________Si________408549509______2481575302
Gli stati di una connessione TCP
Una sessione TCP può essere in diversi stati. Potete visualizzare lo stato di tutte le connessioni in corso sul vostro computer con il comando:
netstat -na
Gli stati possibili sono i seguenti
- LISTEN: in attesa che qualcuno richieda una connessione
- SYN-SENT: durante la creazione di una connessione, indica che è stato inviato un segmento con flag SYN attivo e si sta aspettando il segmento di risposta (quello con i flag SYN/ACK).
- SYN-RECEIVED: durante la creazione di una connessione, indica che è stato ricevuto un segmento SYN, è stato inviato in risposta il SYN/ACK e che ora si sta attendendo l’ACK che completa l’handshake.
- ESTABLISHED: stato raggiunto dopo che l’handshake è stato completato con successo. La connessione è ora aperta e si possono trasferire dati.
- FIN-WAIT1: in attesa di una richiesta di terminazione della sessione da parte del computer remoto o di un acknowledgement della richiesta di terminazione della connessione precedentemente stabilita.
- FIN-WAIT2: in attesa di una richiesta di terminazione della sessione da parte del computer remoto.
- CLOSE-WAIT: in attesa di una richiesta di terminazione della sessione da parte del computer locale.
- CLOSING: in attesa dell’acknowledgement alla richiesta di terminazione da parte del computer remoto.
- LAST-ACK: in attesa dell’acknowledgement della richiesta di terminazione della connessione che è stata precedentemente inviata al computer remoto, include un acknowledgement della sua richiesta di terminazione della connessione
- TIME-WAIT: in attesa che passi abbastanza tempo in modo da essere sicuri che il computer remoto abbia ricevuto l’acknowledgement della sua richiesta di terminazione della connessione.
- CLOSED: quando la connessione è del tutto terminata.
Il controllo delle congestioni di TCP
Per evitare congestioni indesiderate sulla rete, TCP dispone di alcuni algoritmi di controllo.
Slow start (partenza lenta): quando due computer iniziano a comunicare, questo algoritmo impedisce al mittente di partire subito con una finestra di massima ampiezza (suggerita nel campo window del ricevente, detto anche „advertised window“). Questo meccanismo è molto utile quando la rete che unisce due computer non è una LAN diretta, ma una serie di collegamenti lenti (router e collegamenti WAN). Quando si stabilisce la comunicazione, abbiamo una finestra nel TCP del mittente detta congestion window, cioè finestra di congestione. All’inizio è fatta di un solo segmento, poi cresce esponenzialmente (2, 4, 8, 16…) mano a mano che arrivano gli ACK dal computer ricevente, finchè non si raggiunge il valore del campo window del ricevente. Spesso però, prima di raggiungere il valore indicato dall’advertised window, si arriva ad punto in cui alcuni pacchetti iniziano ad essere scartati dai collegamenti lenti intermedi. Questo indica che è opportuno fermare la crescita della finestra di congestione. Attenzione a non confondere la congestion window con l’advertised window, quest’ultima è usato solo dal ricevente per indicare quanti segmenti per finestra esso è in grado di accettare nel buffer di ricezione, mentre la congestion window è gestita dal mittente in base al numero di ACK giunti indietro.
Congestion Avoidance (evitare la congestione): Congestion Avoidance e Slow Start sono due algoritmi mutualmente esclusivi, cioè non possono mai avvenire contemporaneamente, tuttavia esiste un forte legame tra di loro. Senza scendere troppo nel tecnico, quando avviene una congestione e vengono persi segmenti durante lo Slow Start, viene salvata in una speciale variabile „ssthresh“ (che sta per „slow start threshold“) la metà del valore della congestion window per cui abbiamo perso segmenti. La finestra viene poi riportata ad uno, dopodichè, quando il valore della congestion window ricresce diventando uguale a ssthresh, la crescita della finestra diventa lineare e non più esponenziale. Quindi, se durante una slow start abbiamo una congestione per una congestion window pari a 32 byte, salviamo in ssthresh il valore 16, rimettiamo la congestion buffer a 1, poi rifacciamo una nuova slow start fino a 16 (crescita esponenziale: 1, 2, 4, 8, 16) dopodichè l’algoritmo di Congestion Avoidance interviene sulla velocità della crescita della congestion window (crescita lineare: 17, 18, 19, 20 …).
Fast Retrasmit (ritrasmissione veloce): come dicevamo nel paragrafo delle finestre scorrevoli, quando un pacchetto viene perso, gli ACK dei segmenti successivi arrivati a destinazione riportano come Acknowledgement number il valore del segmento mancante nell’ordine di consegna. In condizioni normali dovremmo aspettare lo scadere del timer per il segmento andato perduto prima di poterlo ritrasmettere. Questo perchè forse il segmento incriminato non è veramente andato perduto, ma è semplicemente molto lento. L’algoritmo di Fast Retrasmit permette di scavalcare il timer del pacchetto sospetto quando sono già arrivati TRE ACK riportanti lo stesso valore nell’Acknowledgement number (sintomo che quel pacchetto, più che essere lento, ha probabilmente già preso il volo!).
Facciamo un esempio per chiarire questo algoritmo.
Supponiamo che il mittente spedisca i segmenti: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
ma per un problema di trasmissione il segmento n.4 viene perso.
Al ricevente arriveranno: 1, 2, 3, 5, 6, 7, 8, 9, 10
Gli Ack. number spediti indietro al mittente saranno: 1, 2, 3, 3, 3, 3, 3, 3, 3
La conclusione è che il segmento 4 è probabilmente andato perduto e che sono già arrivati altri 6 pacchetti dopo che è arrivato il 3. Con il Fast Retrasmit invece di ricevere un sacco di ACK tutti uguali aspettando che il timer di 4 scada, ritrasmettiamo il segmento 4 (valoreACKduplicati+1) al terzo ACK uguale.
Fast Recovery (recupero veloce): per migliorare l’efficienza del Fast Retrasmit, quando ritrasmettiamo un segmento che probabilmente è andato perduto, Fast Recovery fa in modo che il valore della congestion window non venga ridotto proprio del tutto, e che non venga effettuata una Slow Start, bensì una Congestion Avoidance. Il valore della congestion window infatti non viene riportato a 1, ma a sshtresh + 3, dove ssthresh è ancora settato alla metà della congestion window per cui è avvenuta la congestione, e 3 è il numero di ACK duplicati, questo per aumentare la c.window del numero di segmenti che sono nel buffer di ricezione del destinatario, inoltre per ogni ulteriore ACK uguale incrementa ancora la congestion window di uno, questo per aumentare la c.window del numero di segmenti partiti. Se permesso dal nuovo valore della c.window, viene trasmesso un nuovo pacchetto. Quando arriva il successivo ACK relativo a nuovi dati, setta la c.window a ssthresh (la metà). Ed eccoci in Congestion Avoidance.
IL PROTOCOLLO UDP
Esiste anche un fratello minore del TCP, e si chiama UDP, User Datagram Protocol. E‘ usato in tutti quei casi in cui non è necessaria tutta la potenza di TCP, ad esempio quando dobbiamo inviare una richiesta che sta tutta in un solo datagramma. Infatti ogni datagramma UDP viene mandato in un singolo datagramma IP. In sostanza l’unica cosa che un datagramma UDP aggiunge al protocollo IP è la capacità di usare le porte. L’esempio classico è quando bisogna contattare un server DNS. Un server DNS, Domain Name Service, svolge un ruolo molto importante, specialmente in Internet: traduce indirizzi umani (www.xyz.com) in indirizzi IP (194.37.205.123). Una richiesta del genere è molto corta, non abbiamo bisogno di usare il TCP. L’UDP non si divide in datagrammi e non tiene conto di ciò che viene mandato o di ciò che non è arrivato. Dopo tutto, se qualcosa va storto e non si riceve risposta, basta reinviare lo stesso pacchetto UDP. In gergo tecnico, si dice che TCP fornisce un servizio connection-oriented, mentre UDP è un protocollo connectionless (senza connessione). L’header UDP è composto dai campi: porta sorgente/porta destinataria, equivalenti alle porte dell’header TCP, un campo Message Lenght che indica la lunghezza del datagramma incluso l’header, un campo di checksum di 16 bit ed infine il campo dati.
IL PROTOCOLLO ICMP
Questo protocollo è abbastanza particolare perchè si trova sullo stesso layer di IP (diversamente da TCP e UDP), ma usa comunque IP per spedire i suoi dati. ICMP non aggiunge garanzie a IP, quindi è possibile che i datagrammi ICMP non arrivino a destinazione o che arrivino corrotti (esattamente come per IP). Il compito di ICMP è quello di inviare messaggi di servizio e messaggi di errore.
Un datagramma ICMP è composto dai campi: type (8 bit), che indica il tipo di messaggio, code (8 bit), un codice di errore che dipende dal tipo di messaggio, checksum (16 bit) per sapere se ci sono errori nel datagramma, data (variabile), che contiene informazioni sul messaggio e di solito include parte del datagramma IP che ha generato l’errore.
I principali codici sono:
0 – Echo Reply: in risposta all’ICMP n.8
3 – Destination Unreachable: viene generato da un router intermedio quando questo non è in grado di trovare il computer desiderato, oppure viene generato dal computer destinatario se si richiede un servizio (protocollo o porta) che non è attivo.
4 – Source quench: nelle congestioni, viene generato da un router intermedio quando questo ha esaurito lo spazio dove accodare i messaggi in arrivo, oppure viene generato dal computer destinatario se i datagrammi stanno arrivando troppo velocemente.
5 – Redirect: se viene generato da un router intermedio conterrà un indirizzo IP relativo ad un router che andrebbe contattato al posto di questo.
8 – Echo: usato per controllare se un computer è attivo. E‘ il messaggio che genera il comando ping.
9 – Router Advertisement: Con questo messaggio ICMP, i router periodicamente segnalano la loro presenza sulle reti a cui sono collegati.
10 – Router solicitation: Usato per forzare i router in ascolto ad identificarsi per non dover attendere i Router Advertisement periodici.
11 – Time exceeded: viene generato da un router intermedio quando il campo Time To Live del datagramma è zero. Se generato dal computer destinatario indica che è scaduto il timer che controlla il riassemblamento di un datagramma, il datagramma sarà scartato.
12 – Parameter problem: indica che è stato incontrato un problema durante l’analisi dei paramentri dell’header IP.
13 – Time Stamp request: usato per misurare le prestazioni e per il debugging ma mai per sincronizzare orologi. Il mittente salva in un campo dell’ICMP n.13 il suo time stamp. Il ricevente prepara un ICMP 14 settando altri due campi: il time stamp di ricezione e di trasmissione.
14 – Time Stamp reply: in risposta all’ICMP n.13
17 – Address mask request: Una richiesta broadcast che serve per scoprire la maschera subnet usata dalle macchine in rete. Usato nelle richieste Reverse-ARP, un particolare tipo di ARP usato dalle macchine che non possiedono dischi e non possono memorizzare i dati della rete tra uno spegnimento e la riaccensione. Tutti i riceventi del broadcast ICMP 17 rispondono preparando un ICMP 18 con i dati della sottorete.
18 – Address mask reply: in risposta all’ICMP n.17
IL PROTOCOLLO IP
IP è un protocollo un pò particolare: non garantisce che i suoi pacchetti, detti datagrammi IP, arrivino a destinazione, che siano privi di errori, o che essi arrivino nell’ordine corretto. Questo lavoro viene lasciato ai protocolli superiori come TCP. Per questo motivo IP è un protocollo abbastanza semplice. L’header di IP si aggiunge a sua volta all’header di TCP visto in precedenza.
L’header IP
Contiene i seguenti campi; quelli in rosso sono i più importanti
- Version; (4 bit) la versione di IP usata. Vale 4 per l’IP standard e 6 per l’IPv6.
- IHL; (4 bit) IP Header Lenght; indica la lunghezza dell’header in parole di 32-bit.
- Type of Service; (8 bit) serve ad assegnare una priorità di consegna ai dati, ma è scarsamente adottato.
- Total Length; (16 bit) lunghezza totale del datagramma, header e dati, espressa in byte.
- Identification, Flags, Fragment Offset; (16+3+13 bit) sono usati per gestire e riunire i frammenti di datagramma. Spiego cos’è un frammento nel prossimo paragrafo.
- Identification; I frammenti di un datagramma hanno lo stesso numero di Identification.
- Flags; contiene i flag, 0, un flag riservato, DF (don’t fragment), se attivo indica che il datagramma non deve essere frammentato, MF (more fragments), se è zero indica che è l’ultimo frammento.
- Fragment Offset; contiene, misurato in unità di 8 byte, quanti dati sono presenti nei frammenti precedenti a questo. Se il frammento è il primo o è l’unico, il valore è zero.
- Time To Live; (8 bit) un numero che decresce ogni volta che viene attraversato un nuovo sistema. Se è zero il datagramma viene scartato. E‘ utile per evitare che un datagramma finisca in un loop infinito.
- Protocol Number; (8 bit) indica quale protocollo di alto livello riceverà il datagramma. Ad esempio per ICMP è 1, per IP è 4 (nei casi di incapsulamento), per TCP è 6, per UDP è 17.
- Header Checksum; (16 bit) per sapere se l’header contiene errori. E‘ indipendente dal checksum del TCP.
- Source Address/Destination Address; (32 bit / 32 bit) un campo molto importante, indica l’indirizzo IP del nodo sorgente/destinatario. Da non confondere con la source port e la destination port del livello di TCP.
- Option; (lunghezza variabile) alcune opzioni per l’IP;
- Padding; (lunghezza variabile) una serie di 0 inserita perchè la lunghezza dell’header IP sia un multiplo di 32 bit.
E alla fine
- Data; attenzione, qui non c’è solo il datagramma iniziale. Ricordate che prima abbiamo aggiunto anche l’header di TCP.
La frammentazione dei datagrammi IP
Un datagramma IP, quando viene passato al livello fisico, viene incapsulato in un header Ethernet per formare un frame fisico. Il problema è che le dimensioni massime di questo frame sono limitate. Il valore di questo limite è chiamato MTU, maximum transfer unit (unità massima di trasferimento). Cosa succede se il nostro datagramma IP è troppo grande per stare in una trama lunga al massimo MTU byte? La risposta a questo problema è la tecnica della frammentazione. Quello che succede è che il datagramma IP viene spezzettato in tanti frammenti abbastanza piccoli da entrare nel frame della rete. Ogni frammento possiede un proprio header molto simile all’header IP del datagramma originale, ma con i campi Identification, Flags, Fragment Offset settati opportunamente (per le loro funzioni vedere il paragrafo precedente). Innanzi tutto il flag DF non deve essere settato, altrimenti il datagramma non viene frammentato ma semplicemente scartato. Il flag MF è pari a uno in tutti i frammenti tranne l’ultimo. Il campo offset viene riempito con il valore opportuno per ogni frammento. Inoltre viene ricalcolato il contenuto di tutti quei campi dell’header IP contenenti valori variabili in funzione della lunghezza (checksum compreso).
Ora che i frammenti sono diventati nuovi datagrammi IP vengono inoltrati normalmente. Sul computer ricevente viene riassemblato il datagramma IP originale facendo uso di questi campi. La frammentazione è piuttosto fragile: la perdita di un solo frammento comporta la perdita dell’intero datagramma.
In generale avere un frame molto grande permette di avere una maggiore efficienza di trasmissione (anche se nei collegamenti soggetti a problemi di lentezza e corruzione dei pacchetti, avere un MTU grande sarà solo fonte di guai, dato che ogni pacchetto perso contiene molti dati). Normalmente si cerca di evitare di dover ricorrere alla frammentazione. La lunghezza finale del datagramma dipende sia dallo spazio occupato dagli header TCP e IP, che dalla lunghezza del segmento creato da TCP. Per evitare la frammentazione bisogna tenere conto di questi valori confrontandoli con il valore di MTU minimo tra quello delle due macchine. Anche così però non possiamo avere la garanzia che non avverrà una frammentazione, perchè è sempre possibile che alcune reti intermedie abbiano limiti MTU inferiori. E in alcuni casi avverrà anche più di una frammentazione.
Finito? Quasi… , ci siamo dimenticati un piccolo (?) particolare; stiamo usando una rete Ethernet, quindi dovremo aggiungere anche l’header di Ethernet. Lo trovate descritto per bene nella pagina Analisi delle trame Ethernet.
Se non avete voglia di leggerlo vi dico brevemente che nell’header vengono aggiunti l’indirizzo Ethernet del mittente e del destinatario; fate attenzione: in Ethernettiano questo non è un indirizzo IP a 32 bit, ma quello della scheda di rete a 48 bit. Prima di proseguire è bene vedere cosa sono questi due indirizzi.
Indirizzi IP – Indirizzi Ethernet
Un indirizzo IP, 32 bit, viene indicato come 4 numeri decimali, ognuno esprime 8 bit (8bit x 4n=32bit), es. 192.168.150.10. Essendo solo 8 i bit per ogni numero, i valori andranno da 0 a 255. Un indirizzo IP a 32 bit può essere visto come una coppia di due numeri: il numero di rete e il numero di host o nodo. Il numero di bit usato per il numero di rete dipende dalla classe di indirizzo. Esistono cinque classi di indirizzi IP:
Classe A: inizia con un bit a 0 (primo ottetto da 1 a 126 in decimale), 7 bit per la rete , 24 per l’host. Permette di avere 126 reti con 16777213 host ciascuno.
Classe B: inizia con due bit a 10 (primo ottetto da 128 a 191 in decimale), 14 bit per la rete , 16 per l’host. 16382 reti, di 65534 host ciascuno.
Classe C: inizia con tre bit a 110 (primo ottetto da 192 a 223 in decimale), 21 bit per la rete , 8 per l’host. 2097150 reti, di 254 host ciascuno.
Classe D: inizia con quattro bit a 1110 (primo ottetto da 224 a 239 in decimale), riservato per il multicasting
Classe E: inizia con quattro bit a 1111 (primo ottetto da 240 a 254 in decimale), riservato per usi futuri
Il numero di rete è assegnato da un ente centrale, l’InterNIC, il numero di host è invece deciso dal possessore di quel numero di rete. Quando il numero di host è fatto solo da ‚0‘, l’indirizzo esprime l’indirizzo di rete. Quando è fatto di soli ‚1‘, indica un broadcast a tutti i nodi della rete. Dato che la suddivisione per classi è piuttosto grezza, è stato creato il concetto di subnet, o sottorete, che permette di sottrarre qualche bit del numero dell’host in favore di una maggiore flessibilità di configurazione (ad esempio per separare il traffico in rete tramite un router), invisibile fuori dalla rete. In questo modo l’indirizzo è composto da: un numero di rete, un numero di subnet, un numero di host.
Se facciamo un AND bit a bit tra un indirizzo IP e una subnet mask quello che otteniamo è il numero di rete comprensivo del numero di subnet. I valori di default per le prime tre classi sono:
Classe A: 255.0.0.0, pari a 11111111.00000000.00000000.00000000
Classe B: 255.255.0.0, pari a 11111111.11111111.00000000.00000000
Classe C: 255.255.255.0, pari a 11111111.11111111.11111111.00000000
Usando queste maschere standard con un indirizzo IP, riotteniamo semplicemente il numero di rete. Se però volessimo fare tre sottoreti, dato un numero di rete di Classe C, possiamo „rubare“ tre bit al quarto ottetto (l’inizio del numero di host), bastano 3 bit perchè con 111 abbiamo il numero 7(>5). Quando facciamo questo otteniamo una Subnet mask di
11111111.11111111.11111111.11100000 (255.255.255.224)
Se la rete è 193.1.1.0, gli host delle varie sottoreti avranno indirizzi che iniziano per:
11000001.00000001.00000001.00000001.00000000 (193.1.1.0) 1° subnet
11000001.00000001.00000001.00000001.00100000 (193.1.1.32) 2° subnet
11000001.00000001.00000001.00000001.01000000 (193.1.1.64) 3° subnet
11000001.00000001.00000001.00000001.01100000 (193.1.1.96) 4° subnet
11000001.00000001.00000001.00000001.10000000 (193.1.1.128) 5° subnet
11000001.00000001.00000001.00000001.10100000 (193.1.1.160) 6° subnet
11000001.00000001.00000001.00000001.11000000 (193.1.1.192) 7° subnet
11000001.00000001.00000001.00000001.11100000 (193.1.1.224) 8° subnet
In realtà le subnet 1=000 e 8=111 è meglio non usarle perchè i numeri fatti di soli 0 o soli 1 hanno le funzioni particolari di cui parlavamo prima.
N.B. : quando assegnate gli indirizzi IP alle vostre macchine, è buona norma non assegnare mai valori già usati in Internet, per questo sono stati definiti indirizzi „sicuri“: 10.0.0.0 (numero di rete=primi 8 bit), 172.16.0.0 (numero di rete=primi 20 bit), 192.168.0.0 (numero di rete=primi 16 bit), se non avete particolari esigenze io vi consiglio quest’ultimo. Evitate come al solito i valori 0 e 255. Un’altra classe di indirizzi riservata è 127.0.0.0 (numero di rete=primi 8 bit), che identifica il localhost, ossia il proprio computer. In generale il mio consiglio è di assegnare alle macchine della vostra LAN gli indirizzi 192.168.x.y dove x rappresenta il numero di sottorete, e y il numero per il nodo, e come subnet 255.255.255.0. Tra parentesi, una macchina può essere collegata a più di una rete, avrà quindi due o più indirizzi IP, uno per ogni rete. Tale macchina viene detta multi-homed e può svolgere le funzione di un router (un altro nome usato è gateway, tuttavia questa parola ha anche altre definizioni e può generare confusione).
Un indirizzo Ethernet, 48 bit, viene invece indicato con una notazione differente: 6 numeri esadecimali, ma ognuno di essi continua ad esprime ancora 8 bit(8bit x 6n=48bit), es. 20-53-52-b8-1f-00. I valori vanno da 00 a ff (che per l’appunto sono poi equivalenti in decimale a 0 e 255, cambia solo il modo di scriverli). Anche qui i numeri 0 e ff non vanno usati, ma di questo non vi dovete preoccupare. Infatti gli indirizzi Ethernet sono già scritti nelle schede di rete quando vengono fabbricate. Essi devono seguire le disposizioni della IEEE in materia, secondo le quali non devono esistere due schede di rete con lo stesso indirizzo.
Ora la domanda sorge spontanea: cosa unisce l’indirizzo IP di una macchina con l’indirizzo fisico della scheda di rete di quella macchina?
Nulla! E‘ per questo che hanno inventato un altro protocollo molto importante: ARP, Address Resolution Protocol.
IL PROTOCOLLO ARP
Quando due computer su una stessa rete Ethernet vogliono comunicare, essi devono prima conoscere l’indirizzo fisico. Ogni volta che usiamo TCP/IP su Ethernet e vogliamo comunicare con un sistema di cui conosciamo solo l’indirizzo IP, viene spedita una richiesta ARP di tipo broadcast sulla rete. In essa si chiede ai computer in ascolto quale sia l’indirizzo fisico corrispondente al quell’indirizzo IP. Il computer interessato fornisce la risposta, noi la riceviamo, la mettiamo nella nostra ARP-Table, dopo di che possiamo parlare con quel computer in modo diretto. Se in seguito abbiamo nuovamente bisogno di parlargli, guardando nella ARP-Table, ci accorgeremo che conosciamo già l’indirizzo fisico, così non dovremo neppure inviare una richiesta ARP.
Già che siamo in argomento, volevo farvi notare una curiosità che forse non tutti conoscono (ed al tempo stesso vedrete ARP al lavoro sul vostro Pc). Quando siete in Internet, cioè adesso, lanciate da una finestra Dos il comando „arp -a“; questo comando serve per vedere la ARP-Table. Scoprirete una cosa strana: tutti gli indirizzi IP dei siti che avete visitato di recente, hanno l’indirizzo fisico uguale! Perchè? Perchè in realtà quell’indirizzo fisico corrisponde al router del vostro provider. Questo perchè quando cercate di accedere ad un sito dovete lasciare tutto in mano al router, il quale fare tutto il lavoro per voi, come se fosse un maggiordomo virtuale.
Ora che abbiamo tutto pronto, e che conosciamo grazie ad ARP l’indirizzo fisico del destinatario, non resta che spedire il tutto!
Nel sistema ricevente, tutti gli header saranno analizzati ed utilizzati opportunamente e via via rimossi.