3
window scale sui due estremi della connessione e configurando le applicazioni affin-
ché utilizzino finestre sufficientemente grandi.
Operando in condizioni ideali, cioè senza errori e con finestre sufficientemente
grandi, il comportamento del TCP durante la fase di avvio è talvolta disastroso e può
entrare in uno stato, che può protrarsi per diversi minuti, in cui la velocità di trasmis-
sione è ridotta ad un pacchetto per RTT. Questo si verifica in corrispondenza della
perdita di un numero elevato di segmenti: ad una brevissima fase nella quale sono
trasmessi sia segmenti vecchi che nuovi (grazie alla notevole quantità di Ack dupli-
cati ricevuti), ne segue un’altra nella quale il trasmettitore invia un solo segmento per
RTT quando usa l’algoritmo NewReno, o un blocco di segmenti perduti quando usa
il Sack. Gli unici Ack ricevuti, infatti, sono quelli parziali, che informano ciascuno
della perdita di un solo segmento (o di un blocco di segmenti). Secondo il principio
di conservazione del pacchetto, il trasmettitore può inviare in risposta all’Ack par-
ziale (o al blocco Sack) ricevuto un solo segmento (o un blocco di segmenti) senza
aggravare la situazione di congestione della rete. Questo comportamento anomalo del
TCP è analizzato in prima istanza con analisi simulative e successivamente mediante
prove dirette su di un canale satellitare reale.
Il simulatore ns-2, ricreando uno scenario molto simile al caso reale, rende possi-
bile un’analisi simulativa completa e dettagliata dei principali algoritmi di controllo
della congestione del TCP: Reno, NewReno e Sack. Circa il secondo algoritmo, il
documento di riferimento ([RFC3782]) introduce due diverse varianti: la prima,
chiamata Slow but Steady, in cui ad ogni Ack parziale si riarma il timer di ritrasmis-
sione; la seconda, chiamata Impatient, in cui il riarmo avviene solo per il primo Ack
parziale.
Nelle classi del simulatore che implementano un trasferimento dati bidirezionale
(classe FullTcpAgent e le sue sottoclassi) è presente soltanto l’implementazione
della prima variante, la quale è causa del comportamento anomalo accennato
poc’anzi. L’assenza della variante Impatient richiede l’intervento sul codice sorgente
del simulatore e la sua modifica. La validazione dei cambiamenti apportati al codice
è condotta mediante la costruzione di scenari ad hoc nei quali si confrontano la ver-
sione originale del simulatore e quella modificata.
Contestualmente a questa modifica, è considerato anche il problema
dell’aggiornamento della finestra di congestione in seguito ad un timeout. RFC 3782
e simulatore ns-2 operano in modo diverso: nel primo, l’arrivo di Ack duplicati non
concorre all’aumento della finestra di congestione; nel secondo, per ogni Ack dupli-
cato successivo al terzo si opera l’incremento. Anche qui è proposta una modifica del
simulatore, per aggiungere la modalità di aggiornamento dettata da RFC 3782.
Come nel caso precedente, i cambiamenti apportati al simulatore sono soggetti ad
un successivo controllo mediante la creazione di scenari ad hoc e la verifica dei ri-
sultati ottenuti.
4
Gli ultimi due punti trattati in questa tesi riguardano lo studio di alcuni stack TCP
e la verifica dei risultati dell’analisi simulativa mediante esperimenti su un canale
satellitare reale. Visto che FreeBSD e Linux sono open source, è possibile ispezio-
nare il codice sorgente dei rispettivi kernel al fine di individuare quali sono i mecca-
nismi di controllo della congestione adottati. Gli esperimenti su un canale satellitare
sono svolti in ambiente Linux, utilizzando un sistema di accesso al satellite chiamato
Skyplex. Presso l’istituto ISTI del CNR di Pisa è disponibile, a scopo di sperimenta-
zione, un canale satellitare la cui larghezza di banda nominale è pari a 2 Mbit/s e il
ritardo è di circa 250 ms. I due algoritmi di controllo della congestione sottoposti al
confronto sono il NewReno e il Sack.
1.2 Organizzazione della relazione
La presente tesi è organizzata come segue. Nel capitolo 2 sono illustrate le no-
zioni base sul protocollo TCP e le sue principali varianti. Nel capitolo 3 è introdotto
il simulatore ns-2 utilizzato per l’analisi simulativa. Il capitolo 4 discute e giustifica
le modifiche apportate al codice del simulatore relative all’algoritmo NewReno. Nel
capitolo 5 sono commentati i risultati delle simulazioni del comportamento iniziale
del TCP con le principali varianti del protocollo (Reno, NewReno e Sack). Il capitolo
6 descrive i meccanismi di controllo della congestione utilizzati da FreeBSD e Linux,
importanti ai fini della comprensione dei risultati degli esperimenti su un canale sa-
tellitare riportati nel capitolo 7. Nell’ultimo capitolo, infine, vengono tratte le conclu-
sioni del lavoro di tesi.
5
6
Capitolo 2 Il protocollo TCP
2.1 Introduzione
Il TCP (Transmission Control Protocol) è un protocollo del livello trasporto pro-
gettato per fornire un flusso di byte affidabile, da sorgente a destinazione, su una rete
non affidabile.
Il protocollo TCP è definito formalmente in RFC 793 [RFC793]. Con il passare
degli anni, sono stati scoperti errori ed incoerenze, eliminati con RFC successivi: una
versione sostanzialmente definitiva è contenuta in RFC 1122 [RFC1122] e alcune
estensioni sono definite in RFC 1323 [RFC1323] e in altri successivi.
Anche se TCP e UDP usano lo stesso network layer (IP), il primo protocollo of-
fre al livello applicativo un servizio totalmente differente dall’altro. Il TCP infatti of-
fre un servizio “connection-oriented” e affidabile tra coppie di processi. Con il ter-
mine “connection-oriented” si intende che due applicazioni che fanno uso del TCP
(denominate usualmente client e server) devono stabilire una connessione prima che
possa avvenire lo scambio di dati.
L’affidabilità nel trasporto di dati è garantita dal TCP nel seguente modo:
• I dati provenienti dal livello applicativo sono suddivisi in blocchi di dimensione
stabilita dal protocollo TCP. L’unità di informazione trasmessa al protocollo IP è
chiamata segmento. Questa pacchettizzazione è invisibile all’applicazione, che
vede la connessione come un flusso di byte non strutturato.
• Quando il TCP manda un segmento, mantiene un timer, in attesa che l’altro lato
invii una conferma di ricezione (“positive Ack”) del segmento. Se l’Ack non è ri-
cevuto prima che scada il timer, viene ritrasmesso il segmento.
• Quando il TCP riceve un segmento dall’altro lato della connessione, invia un
Ack. Di solito l’Ack non è inviato immediatamente, ma è ritardato di una certa
frazione di secondi.
• Viene calcolato e trasmesso un checksum per ogni segmento. Lo scopo è di
individuare eventuali modifiche dei dati che transitano sulla rete. Se arriva a de-
7
stinazione un segmento con checksum non valido, il TCP lo scarta e non invia al-
cun Ack.
• Poiché i segmenti TCP sono incapsulati nei datagram IP e poiché questi possono
arrivare non in ordine, anche i segmenti TCP possono arrivare fuori ordine. Il lato
ricevente del TCP deve riordinare i dati ricevuti, in modo da inoltrare al livello
superiore i dati nell'ordine corretto.
• Poiché i datagram IP possono essere duplicati, il ricevitore TCP deve scartare i
segmenti duplicati.
• Il TCP effettua il recupero di dati danneggiati, persi, duplicati o consegnati fuori
ordine assegnando un numero di sequenza ad ogni byte trasmesso. I numeri di
sequenza sono usati dal ricevitore per ordinare correttamente i segmenti fuori or-
dine ed eliminare gli eventuali duplicati.
• Il TCP definisce anche un meccanismo di controllo del flusso. Entrambi i lati di
una connessione TCP hanno un buffer di dimensioni finite. Il ricevitore ha la pos-
sibilità di controllare la quantità di dati mandati dal trasmettitore. Questo è otte-
nuto restituendo con ogni Ack una “finestra” che indica il numero di byte che il
ricevitore è in grado di memorizzare.
Per permettere a più processi nello stesso host di utilizzare il protocollo TCP, il
TCP introduce il concetto di porta. La concatenazione di un indirizzo IP e di un nu-
mero di porta costituisce un socket. Una coppia di socket identifica univocamente
una connessione.
I meccanismi di affidabilità e controllo del flusso appena descritti richiedono che
il TCP inizializzi e mantenga alcune informazioni di stato per ogni stream di dati. La
combinazione di queste informazioni, dei socket, dei numero di sequenza e delle di-
mensioni delle finestre è chiamata connessione. Ogni connessione è specificata dalla
coppia di socket che identificano i due lati.
2.2 Struttura dello header TCP
La figura 2.1 mostra il formato dello header TCP. Lo header è di 20 byte, se non
sono presenti opzioni, come frequentemente avviene.
8
Figura 2.1. Struttura header TCP.
Ogni segmento TCP contiene il numero di porta sorgente e destinazione che sono
usati per il multiplexing/demultiplexing dei dati da/verso le applicazioni del livello
superiore. Avendo a disposizione 16 bit per indicare il numero di porta, si possono
avere 65536 possibilità: i numeri di porta minori di 1024 corrispondono alle cosid-
dette “well-known port” e sono associati a particolari servizi, alcuni dei quali sono
elencati in tabella 2.1:
Numero
di porta
Servizio
20 FTP (Data)
21 FTP (Control)
53 DNS
80 HTTP
389 LDAP
443 HTTPS
Tabella 2.1. Corrispondenza tra numeri di porta e servizi.
Il numero di sequenza identifica i byte nello stream di dati tra il trasmettitore e il
ricevitore. Concettualmente ad ogni ottetto di dati è assegnato un numero di se-
quenza. Il numero di sequenza del primo ottetto di dati in un segmento è riportato nel
campo Sequence Number dello header TCP ed è chiamato numero di sequenza del
segmento.
Poiché ogni byte di dati scambiato è numerato, il campo Acknowledgment Num-
ber contiene il prossimo numero di sequenza che il mittente dell’Ack si aspetta di ri-
cevere. In effetti questo è il numero di sequenza dell'ultimo byte di dati ricevuto cor-
rettamente, più uno. Il TCP fornisce un servizio full-duplex al livello applicativo.
Questo significa che i dati possono fluire in entrambe le direzioni e i due estremi di
Source port Destination port
Sequence number
Acknowledgment number
32
Window
Length Reserved
U
R
G
A
C
K
P
S
H
R
S
T
F
I
N
S
Y
N
Checksum Urgent pointer
Options Padding
Data
9
una connessione devono mantenere entrambi i numeri di sequenza dei dati che flui-
scono nelle due direzioni.
Il campo Length specifica la lunghezza dello header TCP in parole di 32 bit.
Il campo Flag contiene 6 bit.
• Il bit URG è usato per indicare che nel segmento ci sono dati che l’entità del li-
vello superiore ha contrassegnato come “urgenti”. La dislocazione dell’ultimo
byte di questi dati urgenti può essere individuato sommando al campo Sequence
Number il valore contenuto nel campo “urgent pointer”. La modalità “urgent
mode” è una tecnica che ha il trasmettitore per inviare dati urgenti all'altro lato.
• Il bit ACK è usato per indicare se il valore riportato nel campo Acknowledgment
Number è valido. E’ sempre attivato, tranne che nel primo segmento usato per
aprire una connessione.
• Il bit PSH indica la presenza di dati che devono essere consegnati all'applicazione
destinataria senza aspettare che si riempia il buffer del ricevitore.
• Il bit RST indica la richiesta del mittente di abbattimento della connessione.
• Il bit SYN è usato per aprire una connessione. Se tale bit è uguale a 1, nel campo
Sequence Number è presente il numero di sequenza iniziale.
• Il bit FIN è usato per chiudere una connessione, in quanto il mittente non ha ulte-
riori dati da spedire.
Il controllo del flusso è di tipo “a finestra scorrevole”: il campo Window indica il
numero di byte, a partire da quello specificato nel campo Acknowledgment Number,
che il ricevitore è disposto ad accettare. E’ un campo di 16 bit, che limita la finestra a
65535 byte.
Il campo Checksum è utilizzato per verificare dal lato ricevitore la presenza di er-
rori nel segmento. Per il calcolo del valore del campo checksum, si considera innan-
zitutto la lunghezza del payload (espressa in numero di byte): se è un numero dispari,
si aggiungono in coda 8 bit posti a 0. Successivamente si calcola il complemento a 1
della somma di tutte le parole di 16 bit del segmento. Il calcolo del checksum include
anche uno pseudoheader di 96 bit, la cui struttura è mostrata in figura 2.2.
Lo pseudoheader non è qualcosa che viene trasmesso unitamente al segmento
TCP. I suoi campi hanno le seguenti funzioni:
• Source IP address, destination IP address: indirizzi IP sorgente e destinazione;
• Protocol: il codice numerico del protocollo TCP (=6);
• TCP Length: il numero di byte del segmento TCP, header incluso.