X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=tcpsock.tex;h=b40881921daf1b3946323a5a8f19490d5b13bfcc;hp=b07e6fd3b8d448488150c2f2ac930475857b4459;hb=43d2568d8a142054e3c8cf564da0e0cf4f839ec8;hpb=04a547df13e4c672d95e1060e1ada9ae2e1fcb2f diff --git a/tcpsock.tex b/tcpsock.tex index b07e6fd..b408819 100644 --- a/tcpsock.tex +++ b/tcpsock.tex @@ -1,6 +1,6 @@ %% tcpsock.tex %% -%% Copyright (C) 2000-2015 Simone Piccardi. Permission is granted to +%% Copyright (C) 2000-2016 Simone Piccardi. Permission is granted to %% copy, distribute and/or modify this document under the terms of the GNU Free %% Documentation License, Version 1.1 or any later version published by the %% Free Software Foundation; with the Invariant Sections being "Un preambolo", @@ -16,7 +16,7 @@ In questo capitolo tratteremo le basi dei socket TCP, iniziando con una descrizione delle principali caratteristiche del funzionamento di una connessione TCP; vedremo poi le varie funzioni che servono alla creazione di una connessione fra client e server, fornendo alcuni esempi elementari, e -finiremo prendendo in esame l'uso dell'I/O multiplexing. +finiremo prendendo in esame l'uso dell'\textit{I/O multiplexing}. \section{Il funzionamento di una connessione TCP} @@ -40,8 +40,9 @@ significato di alcuni dei vari \textsl{stati} ad essa associati. \label{sec:TCP_conn_cre} \itindbeg{three~way~handshake} -Il processo che porta a creare una connessione TCP è chiamato \textit{three - way handshake}; la successione tipica degli eventi (e dei + +Il processo che porta a creare una connessione TCP viene chiamato +\textit{three way handshake}; la successione tipica degli eventi (e dei \textsl{segmenti}\footnote{si ricordi che il segmento è l'unità elementare di dati trasmessa dal protocollo TCP al livello successivo; tutti i segmenti hanno un header che contiene le informazioni che servono allo \textit{stack @@ -135,11 +136,10 @@ comunicare all'altro capo una serie di parametri utili a regolare la connessione. Normalmente vengono usate le seguenti opzioni: \begin{itemize} -\item \textit{MSS option}, dove MMS sta per - \itindex{Maximum~Segment~Size~(MSS)} \textit{Maximum Segment Size}, con - questa opzione ciascun capo della connessione annuncia all'altro il massimo - ammontare di dati che vorrebbe accettare per ciascun segmento nella - connessione corrente. È possibile leggere e scrivere questo valore +\item \textit{MSS option}, con questa opzione ciascun capo della connessione + annuncia all'altro il massimo ammontare di dati (MMS sta appunto per + \textit{Maximum Segment Size}) che vorrebbe accettare per ciascun segmento + nella connessione corrente. È possibile leggere e scrivere questo valore attraverso l'opzione del socket \const{TCP\_MAXSEG} (vedi sez.~\ref{sec:sock_tcp_udp_options}). @@ -163,8 +163,7 @@ connessione. Normalmente vengono usate le seguenti opzioni: (espresso come numero di bit cui spostare a sinistra il valore della finestra annunciata inserito nel pacchetto). Con Linux è possibile indicare al kernel di far negoziare il fattore di scala in fase di creazione di una - connessione tramite la \textit{sysctl} \itindex{TCP~window~scaling} - \texttt{tcp\_window\_scaling} (vedi + connessione tramite la \textit{sysctl} \texttt{tcp\_window\_scaling} (vedi sez.~\ref{sec:sock_ipv4_sysctl}).\footnote{per poter usare questa funzionalità è comunque necessario ampliare le dimensioni dei buffer di ricezione e spedizione, cosa che può essere fatta sia a livello di sistema @@ -179,8 +178,8 @@ connessione. Normalmente vengono usate le seguenti opzioni: \end{itemize} -La MSS \itindex{Maximum~Segment~Size~(MSS)} è generalmente supportata da quasi -tutte le implementazioni del protocollo, le ultime due opzioni (trattate +La MSS è generalmente supportata da quasi tutte le implementazioni del +protocollo, le ultime due opzioni (trattate nell'\href{http://www.ietf.org/rfc/rfc1323.txt}{RFC~1323}) sono meno comuni; vengono anche dette \textit{long fat pipe options} dato che questo è il nome che viene dato alle connessioni caratterizzate da alta velocità o da ritardi @@ -239,9 +238,9 @@ deve ancora eseguire la chiusura passiva a quello che sta eseguendo la chiusura attiva. Nella sequenza indicata i dati verrebbero persi, dato che si è chiuso il socket dal lato che esegue la chiusura attiva; esistono tuttavia situazioni in cui si vuole poter sfruttare questa possibilità, usando una -procedura che è chiamata \itindex{half-close} \textit{half-close}; torneremo -su questo aspetto e su come utilizzarlo in sez.~\ref{sec:TCP_shutdown}, quando -parleremo della funzione \func{shutdown}. +procedura che è chiamata \textit{half-close}; torneremo su questo aspetto e su +come utilizzarlo in sez.~\ref{sec:TCP_shutdown}, quando parleremo della +funzione \func{shutdown}. La emissione del FIN avviene quando il socket viene chiuso, questo però non avviene solo per la chiamata esplicita della funzione \func{close}, ma anche @@ -303,10 +302,9 @@ che il protocollo viene ad assumere per i due lati, server e client. \label{fig:TCP_conn_example} \end{figure} -La connessione viene iniziata dal client che annuncia una -\itindex{Maximum~Segment~Size~(MSS)} MSS di 1460, un valore tipico con Linux -per IPv4 su Ethernet, il server risponde con lo stesso valore (ma potrebbe -essere anche un valore diverso). +La connessione viene iniziata dal client che annuncia una MSS di 1460, un +valore tipico con Linux per IPv4 su Ethernet, il server risponde con lo stesso +valore (ma potrebbe essere anche un valore diverso). Una volta che la connessione è stabilita il client scrive al server una richiesta (che assumiamo stare in un singolo segmento, cioè essere minore dei @@ -519,11 +517,11 @@ che solo l'amministratore possa allocare queste porte per far partire i relativi servizi. Le \textsl{glibc} definiscono in \headfile{netinet/in.h} -\const{IPPORT\_RESERVED} e \const{IPPORT\_USERRESERVED}, in cui la prima (che -vale 1024) indica il limite superiore delle porte riservate, e la seconda (che -vale 5000) il limite inferiore delle porte a disposizione degli utenti. La -convenzione vorrebbe che le porte \textsl{effimere} siano allocate fra questi -due valori. Nel caso di Linux questo è vero solo in uno dei due casi di +\constd{IPPORT\_RESERVED} e \constd{IPPORT\_USERRESERVED}, in cui la prima +(che vale 1024) indica il limite superiore delle porte riservate, e la seconda +(che vale 5000) il limite inferiore delle porte a disposizione degli utenti. +La convenzione vorrebbe che le porte \textsl{effimere} siano allocate fra +questi due valori. Nel caso di Linux questo è vero solo in uno dei due casi di fig.~\ref{fig:TCP_port_alloc}, e la scelta fra i due possibili intervalli viene fatta dinamicamente dal kernel a seconda della memoria disponibile per la gestione delle relative tabelle. @@ -577,7 +575,7 @@ posto in ascolto per connessioni provenienti da uno qualunque degli indirizzi associati alle interfacce locali. La notazione \texttt{0.0.0.0} usata da \cmd{netstat} è equivalente all'asterisco utilizzato per il numero di porta, indica il valore generico, e corrisponde al valore \const{INADDR\_ANY} -definito in \headfile{arpa/inet.h} (vedi \ref{tab:TCP_ipv4_addr}). +definito in \headfiled{arpa/inet.h} (vedi \ref{tab:TCP_ipv4_addr}). Inoltre si noti come la porta e l'indirizzo di ogni eventuale connessione esterna non sono specificati; in questo caso la \textit{socket pair} associata @@ -742,12 +740,11 @@ sempre la funzione \func{htonl}. \textbf{Costante} & \textbf{Significato} \\ \hline \hline - \const{INADDR\_ANY} & Indirizzo generico (\texttt{0.0.0.0})\\ - \const{INADDR\_BROADCAST}& Indirizzo di \itindex{broadcast} - \textit{broadcast}.\\ - \const{INADDR\_LOOPBACK} & Indirizzo di \textit{loopback} - (\texttt{127.0.0.1}).\\ - \const{INADDR\_NONE} & Indirizzo errato.\\ + \constd{INADDR\_ANY} & Indirizzo generico (\texttt{0.0.0.0})\\ + \constd{INADDR\_BROADCAST}& Indirizzo di \textit{broadcast}.\\ + \constd{INADDR\_LOOPBACK} & Indirizzo di \textit{loopback} + (\texttt{127.0.0.1}).\\ + \constd{INADDR\_NONE} & Indirizzo errato.\\ \hline \end{tabular} \caption{Costanti di definizione di alcuni indirizzi generici per IPv4.} @@ -761,12 +758,12 @@ con una struttura, perché il linguaggio C non consente l'uso di una struttura costante come operando a destra in una assegnazione. Per questo motivo nell'header \headfile{netinet/in.h} è definita una variabile -\macro{in6addr\_any} (dichiarata come \dirct{extern}, ed inizializzata dal -sistema al valore \const{IN6ADRR\_ANY\_INIT}) che permette di effettuare una +\var{in6addr\_any} (dichiarata come \dirct{extern}, ed inizializzata dal +sistema al valore \constd{IN6ADRR\_ANY\_INIT}) che permette di effettuare una assegnazione del tipo: \includecodesnip{listati/serv_addr_sin6_addr.c} in -maniera analoga si può utilizzare la variabile \macro{in6addr\_loopback} per +maniera analoga si può utilizzare la variabile \var{in6addr\_loopback} per indicare l'indirizzo di \textit{loopback}, che a sua volta viene inizializzata -staticamente a \const{IN6ADRR\_LOOPBACK\_INIT}. +staticamente a \constd{IN6ADRR\_LOOPBACK\_INIT}. \subsection{La funzione \func{connect}} @@ -779,8 +776,8 @@ connessione con un server TCP,\footnote{di nuovo la funzione è generica e limiterà ad impostare l'indirizzo dal quale e verso il quale saranno inviati e ricevuti i pacchetti, mentre per socket di tipo \const{SOCK\_STREAM} o \const{SOCK\_SEQPACKET}, essa attiverà la procedura di avvio (nel caso del - TCP il \itindex{three~way~handshake} \textit{three way handshake}) della - connessione.} il prototipo della funzione è il seguente: + TCP il \textit{three way handshake}) della connessione.} il prototipo della +funzione è il seguente: \begin{prototype}{sys/socket.h} {int connect(int sockfd, const struct sockaddr *servaddr, socklen\_t addrlen)} @@ -805,9 +802,8 @@ connessione con un server TCP,\footnote{di nuovo la funzione è generica e \item[\errcode{EAFNOSUPPORT}] l'indirizzo non ha una famiglia di indirizzi corretta nel relativo campo. \item[\errcode{EACCES}, \errcode{EPERM}] si è tentato di eseguire una - connessione ad un indirizzo \itindex{broadcast} \textit{broadcast} senza - che il socket fosse stato abilitato per il \itindex{broadcast} - \textit{broadcast}. + connessione ad un indirizzo \textit{broadcast} senza che il socket fosse + stato abilitato per il \textit{broadcast}. \end{errlist} altri errori possibili sono: \errval{EFAULT}, \errval{EBADF}, \errval{ENOTSOCK}, \errval{EISCONN} e \errval{EADDRINUSE}.} @@ -823,12 +819,12 @@ numero di porta del server a cui ci si vuole connettere, come mostrato nell'esempio sez.~\ref{sec:TCP_daytime_client}, usando le funzioni illustrate in sez.~\ref{sec:sock_addr_func}. -Nel caso di socket TCP la funzione \func{connect} avvia il -\itindex{three~way~handshake} \textit{three way handshake}, e ritorna solo -quando la connessione è stabilita o si è verificato un errore. Le possibili -cause di errore sono molteplici (ed i relativi codici riportati sopra), quelle -che però dipendono dalla situazione della rete e non da errori o problemi -nella chiamata della funzione sono le seguenti: +Nel caso di socket TCP la funzione \func{connect} avvia il \textit{three way + handshake}, e ritorna solo quando la connessione è stabilita o si è +verificato un errore. Le possibili cause di errore sono molteplici (ed i +relativi codici riportati sopra), quelle che però dipendono dalla situazione +della rete e non da errori o problemi nella chiamata della funzione sono le +seguenti: \begin{enumerate} \item Il client non riceve risposta al SYN: l'errore restituito è \errcode{ETIMEDOUT}. Stevens riporta che BSD invia un primo SYN alla @@ -923,27 +919,24 @@ infatti vengono mantenute due code: \begin{enumerate} \item La coda delle connessioni incomplete (\textit{incomplete connection queue}) che contiene un riferimento per ciascun socket per il quale è - arrivato un SYN ma il \itindex{three~way~handshake} \textit{three way - handshake} non si è ancora concluso. Questi socket sono tutti nello stato - \texttt{SYN\_RECV}. + arrivato un SYN ma il \textit{three way handshake} non si è ancora concluso. + Questi socket sono tutti nello stato \texttt{SYN\_RECV}. \item La coda delle connessioni complete (\textit{complete connection queue}) - che contiene un ingresso per ciascun socket per il quale il - \itindex{three~way~handshake} \textit{three way handshake} è stato - completato ma ancora \func{accept} non è ritornata. Questi socket sono - tutti nello stato \texttt{ESTABLISHED}. + che contiene un ingresso per ciascun socket per il quale il \textit{three + way handshake} è stato completato ma ancora \func{accept} non è ritornata. + Questi socket sono tutti nello stato \texttt{ESTABLISHED}. \end{enumerate} Lo schema di funzionamento è descritto in fig.~\ref{fig:TCP_listen_backlog}: quando arriva un SYN da un client il server crea una nuova voce nella coda delle connessioni incomplete, e poi risponde con il SYN$+$ACK. La voce resterà nella coda delle connessioni incomplete fino al ricevimento dell'ACK dal -client o fino ad un timeout. Nel caso di completamento del -\itindex{three~way~handshake} \textit{three way handshake} la voce viene -spostata nella coda delle connessioni complete. Quando il processo chiama la -funzione \func{accept} (vedi sez.~\ref{sec:TCP_func_accept}) la prima voce -nella coda delle connessioni complete è passata al programma, o, se la coda è -vuota, il processo viene posto in attesa e risvegliato all'arrivo della prima -connessione completa. +client o fino ad un timeout. Nel caso di completamento del \textit{three way + handshake} la voce viene spostata nella coda delle connessioni complete. +Quando il processo chiama la funzione \func{accept} (vedi +sez.~\ref{sec:TCP_func_accept}) la prima voce nella coda delle connessioni +complete è passata al programma, o, se la coda è vuota, il processo viene +posto in attesa e risvegliato all'arrivo della prima connessione completa. \begin{figure}[!htb] \centering \includegraphics[width=11cm]{img/tcp_listen_backlog} @@ -960,9 +953,9 @@ kernel, compreso Linux 2.0, che mostrano le differenze fra diverse implementazioni. In Linux il significato di questo valore è cambiato a partire dal kernel 2.2 -per prevenire l'attacco chiamato \index{SYN~flood} \textit{SYN flood}. Questo -si basa sull'emissione da parte dell'attaccante di un grande numero di -pacchetti SYN indirizzati verso una porta, forgiati con indirizzo IP +per prevenire l'attacco chiamato \itindex{SYN~flood} \textit{SYN + flood}. Questo si basa sull'emissione da parte dell'attaccante di un grande +numero di pacchetti SYN indirizzati verso una porta, forgiati con indirizzo IP fasullo\footnote{con la tecnica che viene detta \textit{ip spoofing}.} così che i SYN$+$ACK vanno perduti e la coda delle connessioni incomplete viene saturata, impedendo di fatto ulteriori connessioni. @@ -970,14 +963,14 @@ saturata, impedendo di fatto ulteriori connessioni. Per ovviare a questo il significato del \param{backlog} è stato cambiato a indicare la lunghezza della coda delle connessioni complete. La lunghezza della coda delle connessioni incomplete può essere ancora controllata usando -la funzione \func{sysctl} con il parametro \const{NET\_TCP\_MAX\_SYN\_BACKLOG} -o scrivendola direttamente in -\sysctlfile{net/ipv4/tcp\_max\_syn\_backlog}. Quando si attiva la -protezione dei syncookies però (con l'opzione da compilare nel kernel e da -attivare usando \sysctlfile{net/ipv4/tcp\_syncookies}) questo valore -viene ignorato e non esiste più un valore massimo. In ogni caso in Linux il -valore di \param{backlog} viene troncato ad un massimo di \const{SOMAXCONN} se -è superiore a detta costante (che di default vale 128).\footnote{il valore di +la funzione \func{sysctl} con il parametro +\constd{NET\_TCP\_MAX\_SYN\_BACKLOG} o scrivendola direttamente in +\sysctlfile{net/ipv4/tcp\_max\_syn\_backlog}. Quando si attiva la protezione +dei syncookies però (con l'opzione da compilare nel kernel e da attivare +usando \sysctlfile{net/ipv4/tcp\_syncookies}) questo valore viene ignorato e +non esiste più un valore massimo. In ogni caso in Linux il valore +di \param{backlog} viene troncato ad un massimo di \const{SOMAXCONN} se è +superiore a detta costante (che di default vale 128).\footnote{il valore di questa costante può essere controllato con un altro parametro di \func{sysctl}, vedi sez.~\ref{sec:sock_ioctl_IP}.} @@ -996,7 +989,7 @@ che il compito principale della coda sia quello di gestire il caso in cui il server è occupato fra chiamate successive alla \func{accept} (per cui la coda più occupata sarebbe quella delle connessioni completate), ma piuttosto quello di gestire la presenza di un gran numero di SYN in attesa di concludere il -\itindex{three~way~handshake} \textit{three way handshake}. +\textit{three way handshake}. Infine va messo in evidenza che, nel caso di socket TCP, quando un SYN arriva con tutte le code piene, il pacchetto deve essere ignorato. Questo perché la @@ -1014,10 +1007,10 @@ trasparente dal protocollo TCP. \label{sec:TCP_func_accept} La funzione \funcd{accept} è chiamata da un server per gestire la connessione -una volta che sia stato completato il \itindex{three~way~handshake} -\textit{three way handshake},\footnote{la funzione è comunque generica ed è - utilizzabile su socket di tipo \const{SOCK\_STREAM}, \const{SOCK\_SEQPACKET} - e \const{SOCK\_RDM}.} la funzione restituisce un nuovo socket descriptor su +una volta che sia stato completato il \textit{three way + handshake},\footnote{la funzione è comunque generica ed è utilizzabile su + socket di tipo \const{SOCK\_STREAM}, \const{SOCK\_SEQPACKET} e + \const{SOCK\_RDM}.} la funzione restituisce un nuovo socket descriptor su cui si potrà operare per effettuare la comunicazione. Se non ci sono connessioni completate il processo viene messo in attesa. Il prototipo della funzione è il seguente: @@ -1931,13 +1924,12 @@ connessioni da qualunque indirizzo e da qualunque porta e su qualunque interfaccia locale. A questo punto si può lanciare il client, esso chiamerà \func{socket} e -\func{connect}; una volta completato il \itindex{three~way~handshake} -\textit{three way handshake} la connessione è stabilita; la \func{connect} -ritornerà nel client\footnote{si noti che è sempre la \func{connect} del - client a ritornare per prima, in quanto questo avviene alla ricezione del - secondo segmento (l'ACK del server) del \itindex{three~way~handshake} - \textit{three way handshake}, la \func{accept} del server ritorna solo dopo - un altro mezzo RTT quando il terzo segmento (l'ACK del client) viene +\func{connect}; una volta completato il \textit{three way handshake} la +connessione è stabilita; la \func{connect} ritornerà nel client\footnote{si + noti che è sempre la \func{connect} del client a ritornare per prima, in + quanto questo avviene alla ricezione del secondo segmento (l'ACK del server) + del \textit{three way handshake}, la \func{accept} del server ritorna solo + dopo un altro mezzo RTT quando il terzo segmento (l'ACK del client) viene ricevuto.} e la \func{accept} nel server, ed usando di nuovo \cmd{netstat} otterremmo che: \begin{verbatim} @@ -2186,10 +2178,10 @@ perché nel server l'unica chiamata ad una \textit{system call} lenta, che può essere interrotta dall'arrivo di \signal{SIGCHLD}, è quella ad \func{accept}, che è l'unica funzione che può mettere il processo padre in stato di sleep nel periodo in cui un figlio può terminare; si noti infatti come le altre -\index{system~call~lente} \textit{system call} lente (si ricordi la -distinzione fatta in sez.~\ref{sec:sig_gen_beha}) o sono chiamate prima di -entrare nel ciclo principale, quando ancora non esistono processi figli, o -sono chiamate dai figli stessi e non risentono di \signal{SIGCHLD}. +\textit{system call} lente (si ricordi la distinzione fatta in +sez.~\ref{sec:sig_gen_beha}) o sono chiamate prima di entrare nel ciclo +principale, quando ancora non esistono processi figli, o sono chiamate dai +figli stessi e non risentono di \signal{SIGCHLD}. Per questo l'unica modifica sostanziale nel ciclo principale (\texttt{\small 23--42}), rispetto precedente versione di fig.~\ref{fig:TCP_ServEcho_first}, @@ -2282,13 +2274,12 @@ Benché questo non sia un fatto comune, un evento simile può essere osservato con dei server molto occupati. In tal caso, con una struttura del server simile a quella del nostro esempio, in cui la gestione delle singole connessioni è demandata a processi figli, può accadere che il \textit{three - way handshake} \itindex{three~way~handshake} venga completato e la relativa -connessione abortita subito dopo, prima che il padre, per via del carico della -macchina, abbia fatto in tempo ad eseguire la chiamata ad \func{accept}. Di -nuovo si ha una situazione analoga a quella illustrata in -fig.~\ref{fig:TCP_early_abort}, in cui la connessione viene stabilita, ma -subito dopo si ha una condizione di errore che la chiude prima che essa sia -stata accettata dal programma. + way handshake} venga completato e la relativa connessione abortita subito +dopo, prima che il padre, per via del carico della macchina, abbia fatto in +tempo ad eseguire la chiamata ad \func{accept}. Di nuovo si ha una situazione +analoga a quella illustrata in fig.~\ref{fig:TCP_early_abort}, in cui la +connessione viene stabilita, ma subito dopo si ha una condizione di errore che +la chiude prima che essa sia stata accettata dal programma. Questo significa che, oltre alla interruzione da parte di un segnale, che abbiamo trattato in sez.~\ref{sec:TCP_child_hand} nel caso particolare di @@ -2392,33 +2383,32 @@ anarres.echo > gont.34559: R 511689732:511689732(0) win 0 \end{verbatim} Le prime tre righe vengono prodotte al momento in cui lanciamo il nostro -client, e corrispondono ai tre pacchetti del \itindex{three~way~handshake} -\textit{three way handshake}. L'output del comando riporta anche i numeri di -sequenza iniziali, mentre la lettera \texttt{S} indica che per quel pacchetto -si aveva il SYN flag attivo. Si noti come a partire dal secondo pacchetto sia -sempre attivo il campo \texttt{ack}, seguito dal numero di sequenza per il -quale si da il ricevuto; quest'ultimo, a partire dal terzo pacchetto, viene -espresso in forma relativa per maggiore compattezza. Il campo \texttt{win} in -ogni riga indica la \textit{advertised window} di cui parlavamo in -sez.~\ref{sec:TCP_TCP_opt}. Allora si può verificare dall'output del comando -come venga appunto realizzata la sequenza di pacchetti descritta in -sez.~\ref{sec:TCP_conn_cre}: prima viene inviato dal client un primo pacchetto -con il SYN che inizia la connessione, a cui il server risponde dando il -ricevuto con un secondo pacchetto, che a sua volta porta un SYN, cui il client -risponde con un il terzo pacchetto di ricevuto. +client, e corrispondono ai tre pacchetti del \textit{three way handshake}. +L'output del comando riporta anche i numeri di sequenza iniziali, mentre la +lettera \texttt{S} indica che per quel pacchetto si aveva il SYN flag attivo. +Si noti come a partire dal secondo pacchetto sia sempre attivo il campo +\texttt{ack}, seguito dal numero di sequenza per il quale si da il ricevuto; +quest'ultimo, a partire dal terzo pacchetto, viene espresso in forma relativa +per maggiore compattezza. Il campo \texttt{win} in ogni riga indica la +\textit{advertised window} di cui parlavamo in sez.~\ref{sec:TCP_TCP_opt}. +Allora si può verificare dall'output del comando come venga appunto realizzata +la sequenza di pacchetti descritta in sez.~\ref{sec:TCP_conn_cre}: prima viene +inviato dal client un primo pacchetto con il SYN che inizia la connessione, a +cui il server risponde dando il ricevuto con un secondo pacchetto, che a sua +volta porta un SYN, cui il client risponde con un il terzo pacchetto di +ricevuto. Ritorniamo allora alla nostra sessione con il servizio echo: dopo le tre righe -del \itindex{three~way~handshake} \textit{three way handshake} non avremo -nulla fin tanto che non scriveremo una prima riga sul client; al momento in -cui facciamo questo si genera una sequenza di altri quattro pacchetti. Il -primo, dal client al server, contraddistinto da una lettera \texttt{P} che -significa che il flag PSH è impostato, contiene la nostra riga (che è appunto -di 11 caratteri), e ad esso il server risponde immediatamente con un pacchetto -vuoto di ricevuto. Poi tocca al server riscrivere indietro quanto gli è stato -inviato, per cui sarà lui a mandare indietro un terzo pacchetto con lo stesso -contenuto appena ricevuto, e a sua volta riceverà dal client un ACK nel quarto -pacchetto. Questo causerà la ricezione dell'eco nel client che lo stamperà a -video. +del \textit{three way handshake} non avremo nulla fin tanto che non scriveremo +una prima riga sul client; al momento in cui facciamo questo si genera una +sequenza di altri quattro pacchetti. Il primo, dal client al server, +contraddistinto da una lettera \texttt{P} che significa che il flag PSH è +impostato, contiene la nostra riga (che è appunto di 11 caratteri), e ad esso +il server risponde immediatamente con un pacchetto vuoto di ricevuto. Poi +tocca al server riscrivere indietro quanto gli è stato inviato, per cui sarà +lui a mandare indietro un terzo pacchetto con lo stesso contenuto appena +ricevuto, e a sua volta riceverà dal client un ACK nel quarto pacchetto. +Questo causerà la ricezione dell'eco nel client che lo stamperà a video. A questo punto noi procediamo ad interrompere l'esecuzione del server con un \texttt{C-c} (cioè con l'invio di \signal{SIGTERM}): nel momento in cui @@ -2619,8 +2609,7 @@ successivo, per tentare di ristabilire la connessione. Il risultato finale qui dipende dall'implementazione dello stack TCP, e nel caso di Linux anche dall'impostazione di alcuni dei parametri di sistema che si trovano in \file{/proc/sys/net/ipv4}, che ne controllano il comportamento: -in questo caso in particolare da -\sysctlrelfile{net/ipv4}{tcp\_retries2} (vedi +in questo caso in particolare da \sysctlrelfile{net/ipv4}{tcp\_retries2} (vedi sez.~\ref{sec:sock_ipv4_sysctl}). Questo parametro infatti specifica il numero di volte che deve essere ritentata la ritrasmissione di un pacchetto nel mezzo di una connessione prima di riportare un errore di timeout. Il valore @@ -2778,8 +2767,7 @@ nel caso dei socket, visto che possono intervenire tutte una serie di possibili condizioni di errore dovute alla rete. Occorre allora specificare chiaramente quali sono le condizioni per cui un socket risulta essere ``\textsl{pronto}'' quando viene passato come membro di uno dei tre -\itindex{file~descriptor~set} \textit{file descriptor set} usati da -\func{select}. +\textit{file descriptor set} usati da \func{select}. Le condizioni che fanno si che la funzione \func{select} ritorni segnalando che un socket (che sarà riportato nel primo insieme di file descriptor) è @@ -2840,9 +2828,8 @@ pronto per la scrittura sono le seguenti: Infine c'è una sola condizione che fa si che \func{select} ritorni segnalando che un socket (che sarà riportato nel terzo insieme di file descriptor) ha una condizione di eccezione pendente, e cioè la ricezione sul socket di -\textsl{dati urgenti} (o \itindex{out-of-band} \textit{out-of-band}), una -caratteristica specifica dei socket TCP su cui torneremo in -sez.~\ref{sec:TCP_urgent_data}. +\textsl{dati urgenti} (o \textit{out-of-band}), una caratteristica specifica +dei socket TCP su cui torneremo in sez.~\ref{sec:TCP_urgent_data}. Si noti come nel caso della lettura \func{select} si applichi anche ad operazioni che non hanno nulla a che fare con l'I/O di dati come il @@ -2917,27 +2904,27 @@ codice completo si trova nel file \file{TCP\_echo\_third.c} dei sorgenti allegati alla guida. In questo caso la funzione comincia (\texttt{\small 8--9}) con l'azzeramento -del \itindex{file~descriptor~set} \textit{file descriptor set} \var{fset} e -l'impostazione del valore \var{maxfd}, da passare a \func{select} come massimo -per il numero di file descriptor. Per determinare quest'ultimo si usa la macro -\code{max} definita nel nostro file \file{macro.h} che raccoglie una -collezione di macro di preprocessore di varia utilità. +del \textit{file descriptor set} \var{fset} e l'impostazione del valore +\var{maxfd}, da passare a \func{select} come massimo per il numero di file +descriptor. Per determinare quest'ultimo si usa la macro \code{max} definita +nel nostro file \file{macro.h} che raccoglie una collezione di macro di +preprocessore di varia utilità. La funzione prosegue poi (\texttt{\small 10--41}) con il ciclo principale, che viene ripetuto indefinitamente. Per ogni ciclo si reinizializza -(\texttt{\small 11--12}) il \itindex{file~descriptor~set} \textit{file - descriptor set}, impostando i valori per il file descriptor associato al -socket \var{socket} e per lo standard input (il cui valore si recupera con la -funzione \func{fileno}). Questo è necessario in quanto la successiva -(\texttt{\small 13}) chiamata a \func{select} comporta una modifica dei due -bit relativi, che quindi devono essere reimpostati all'inizio di ogni ciclo. +(\texttt{\small 11--12}) il \textit{file descriptor set}, impostando i valori +per il file descriptor associato al socket \var{socket} e per lo standard +input (il cui valore si recupera con la funzione \func{fileno}). Questo è +necessario in quanto la successiva (\texttt{\small 13}) chiamata a +\func{select} comporta una modifica dei due bit relativi, che quindi devono +essere reimpostati all'inizio di ogni ciclo. Si noti come la chiamata a \func{select} venga eseguita usando come primo argomento il valore di \var{maxfd}, precedentemente calcolato, e passando poi -il solo \itindex{file~descriptor~set} \textit{file descriptor set} per il -controllo dell'attività in lettura, negli altri argomenti sono passati tutti -puntatori nulli, non interessando né il controllo delle altre attività, né -l'impostazione di un valore di timeout. +il solo \textit{file descriptor set} per il controllo dell'attività in +lettura, negli altri argomenti sono passati tutti puntatori nulli, non +interessando né il controllo delle altre attività, né l'impostazione di un +valore di timeout. Al ritorno di \func{select} si provvede a controllare quale dei due file descriptor presenta attività in lettura, cominciando (\texttt{\small 14--24}) @@ -3042,13 +3029,15 @@ capi chiuda la connessione, quando l'altro capo la lascia aperta.\footnote{abbiamo incontrato questa situazione nei vari scenari critici di sez.~\ref{sec:TCP_echo_critical}.} +\itindbeg{half-close} + È pertanto possibile avere una situazione in cui un capo della connessione non avendo più nulla da scrivere, possa chiudere il socket, segnalando così l'avvenuta terminazione della trasmissione (l'altro capo riceverà infatti un -end-of-file in lettura) mentre dall'altra parte si potrà proseguire la -trasmissione dei dati scrivendo sul socket che da quel lato è ancora aperto. -Questa è quella situazione in cui si dice che il socket è \textit{half - closed}. +\textit{end-of-file} in lettura) mentre dall'altra parte si potrà proseguire +la trasmissione dei dati scrivendo sul socket che da quel lato è ancora +aperto. Questa è quella situazione in cui si dice che il socket è +``\textit{half closed}''. Il problema che si pone è che se la chiusura del socket è effettuata con la funzione \func{close}, come spiegato in sez.~\ref{sec:TCP_func_close}, si perde @@ -3076,24 +3065,26 @@ vuole operare e come secondo argomento un valore intero \param{how} che indica la modalità di chiusura del socket, quest'ultima può prendere soltanto tre valori: \begin{basedescript}{\desclabelwidth{2.2cm}\desclabelstyle{\nextlinelabel}} -\item[\const{SHUT\_RD}] chiude il lato in lettura del socket, non sarà più +\item[\constd{SHUT\_RD}] chiude il lato in lettura del socket, non sarà più possibile leggere dati da esso, tutti gli eventuali dati trasmessi dall'altro capo del socket saranno automaticamente scartati dal kernel, che, in caso di socket TCP, provvederà comunque ad inviare i relativi segmenti di ACK. -\item[\const{SHUT\_WR}] chiude il lato in scrittura del socket, non sarà più +\item[\constd{SHUT\_WR}] chiude il lato in scrittura del socket, non sarà più possibile scrivere dati su di esso. Nel caso di socket TCP la chiamata causa l'emissione di un segmento FIN, secondo la procedura chiamata - \itindex{half-close} \textit{half-close}. Tutti i dati presenti nel buffer - di scrittura prima della chiamata saranno inviati, seguiti dalla sequenza di - chiusura illustrata in sez.~\ref{sec:TCP_conn_term}. -\item[\const{SHUT\_RDWR}] chiude sia il lato in lettura che quello in + \textit{half-close}. Tutti i dati presenti nel buffer di scrittura prima + della chiamata saranno inviati, seguiti dalla sequenza di chiusura + illustrata in sez.~\ref{sec:TCP_conn_term}. +\item[\constd{SHUT\_RDWR}] chiude sia il lato in lettura che quello in scrittura del socket. È equivalente alla chiamata in sequenza con \const{SHUT\_RD} e \const{SHUT\_WR}. \end{basedescript} +\itindend{half-close} + Ci si può chiedere quale sia l'utilità di avere introdotto \const{SHUT\_RDWR} -quando questa sembra rendere \funcd{shutdown} del tutto equivalente ad una +quando questa sembra rendere \func{shutdown} del tutto equivalente ad una \func{close}. In realtà non è così, esiste infatti un'altra differenza con \func{close}, più sottile. Finora infatti non ci siamo presi la briga di sottolineare in maniera esplicita che, come per i file e le fifo, anche per i @@ -3213,8 +3204,8 @@ precedente versione causava l'immediato ritorno della funzione; in questo caso prima (\texttt{\small 19}) si imposta opportunamente \var{eof} ad un valore non nullo, dopo di che (\texttt{\small 20}) si effettua la chiusura del lato in scrittura del socket con \func{shutdown}. Infine (\texttt{\small 21}) si -usa la macro \macro{FD\_CLR} per togliere lo standard input dal -\itindex{file~descriptor~set} \textit{file descriptor set}. +usa la macro \macro{FD\_CLR} per togliere lo standard input dal \textit{file + descriptor set}. In questo modo anche se la lettura del file in ingresso è conclusa, la funzione non esce dal ciclo principale (\texttt{\small 11--50}), ma continua @@ -3295,15 +3286,14 @@ aperti viene impostato a quello del socket in ascolto,\footnote{in quanto esso alto.} che verrà anche (\texttt{\small 4}) inserito nella tabella. La prima sezione (\texttt{\small 7--10}) del ciclo principale esegue la -costruzione del \itindex{file~descriptor~set} \textit{file descriptor set} -\var{fset} in base ai socket connessi in un certo momento; all'inizio ci sarà -soltanto il socket in ascolto, ma nel prosieguo delle operazioni, verranno -utilizzati anche tutti i socket connessi registrati nella tabella -\var{fd\_open}. Dato che la chiamata di \func{select} modifica il valore del -\itindex{file~descriptor~set} \textit{file descriptor set}, è necessario -ripetere (\texttt{\small 7}) ogni volta il suo azzeramento, per poi procedere -con il ciclo (\texttt{\small 8--10}) in cui si impostano i socket trovati -attivi. +costruzione del \textit{file descriptor set} \var{fset} in base ai socket +connessi in un certo momento; all'inizio ci sarà soltanto il socket in +ascolto, ma nel prosieguo delle operazioni, verranno utilizzati anche tutti i +socket connessi registrati nella tabella \var{fd\_open}. Dato che la chiamata +di \func{select} modifica il valore del \textit{file descriptor set}, è +necessario ripetere (\texttt{\small 7}) ogni volta il suo azzeramento, per poi +procedere con il ciclo (\texttt{\small 8--10}) in cui si impostano i socket +trovati attivi. Per far questo si usa la caratteristica dei file descriptor, descritta in sez.~\ref{sec:file_open_close}, per cui il kernel associa sempre ad ogni nuovo @@ -3353,13 +3343,13 @@ vi sono dati sui socket connessi, per questo si ripete un ciclo diverso da zero; in questo modo se l'unico socket con attività era quello connesso, avendo opportunamente decrementato il contatore, il ciclo verrà saltato, e si ritornerà immediatamente (ripetuta l'inizializzazione del -\itindex{file~descriptor~set} \textit{file descriptor set} con i nuovi valori -nella tabella) alla chiamata di \func{accept}. Se il socket attivo non è -quello in ascolto, o ce ne sono comunque anche altri, il valore di \var{n} non -sarà nullo ed il controllo sarà eseguito. Prima di entrare nel ciclo comunque -si inizializza (\texttt{\small 28}) il valore della variabile \var{i} che -useremo come indice nella tabella \var{fd\_open} al valore minimo, -corrispondente al file descriptor del socket in ascolto. +\textit{file descriptor set} con i nuovi valori nella tabella) alla chiamata +di \func{accept}. Se il socket attivo non è quello in ascolto, o ce ne sono +comunque anche altri, il valore di \var{n} non sarà nullo ed il controllo sarà +eseguito. Prima di entrare nel ciclo comunque si inizializza (\texttt{\small + 28}) il valore della variabile \var{i} che useremo come indice nella tabella +\var{fd\_open} al valore minimo, corrispondente al file descriptor del socket +in ascolto. Il primo passo (\texttt{\small 30}) nella verifica è incrementare il valore dell'indice \var{i} per posizionarsi sul primo valore possibile per un file @@ -3405,18 +3395,17 @@ successiva \func{select} ritornerà immediatamente segnalando l'ulteriore disponibilità. Il nostro server comunque soffre di una vulnerabilità per un attacco di tipo -\itindex{Denial~of~Service~(DoS)} \textit{Denial of Service}. Il problema è -che in caso di blocco di una qualunque delle funzioni di I/O, non avendo usato -processi separati, tutto il server si ferma e non risponde più a nessuna -richiesta. Abbiamo scongiurato questa evenienza per l'I/O in ingresso con -l'uso di \func{select}, ma non vale altrettanto per l'I/O in uscita. Il -problema pertanto può sorgere qualora una delle chiamate a \func{write} -effettuate da \func{FullWrite} si blocchi. Con il funzionamento normale questo -non accade in quanto il server si limita a scrivere quanto riceve in ingresso, -ma qualora venga utilizzato un client malevolo che esegua solo scritture e non -legga mai indietro l'\textsl{eco} del server, si potrebbe giungere alla -saturazione del buffer di scrittura, ed al conseguente blocco del server su di -una \func{write}. +\textit{Denial of Service}. Il problema è che in caso di blocco di una +qualunque delle funzioni di I/O, non avendo usato processi separati, tutto il +server si ferma e non risponde più a nessuna richiesta. Abbiamo scongiurato +questa evenienza per l'I/O in ingresso con l'uso di \func{select}, ma non vale +altrettanto per l'I/O in uscita. Il problema pertanto può sorgere qualora una +delle chiamate a \func{write} effettuate da \func{FullWrite} si blocchi. Con +il funzionamento normale questo non accade in quanto il server si limita a +scrivere quanto riceve in ingresso, ma qualora venga utilizzato un client +malevolo che esegua solo scritture e non legga mai indietro l'\textsl{eco} del +server, si potrebbe giungere alla saturazione del buffer di scrittura, ed al +conseguente blocco del server su di una \func{write}. Le possibili soluzioni in questo caso sono quelle di ritornare ad eseguire il ciclo di risposta alle richieste all'interno di processi separati, utilizzare @@ -3435,8 +3424,7 @@ maggior parte dei casi, in quanto essa è nata sotto BSD proprio per affrontare queste problematiche con i socket. Abbiamo però visto in sez.~\ref{sec:file_multiplexing} come la funzione \func{poll} possa costituire una alternativa a \func{select}, con alcuni vantaggi.\footnote{non soffrendo - delle limitazioni dovute all'uso dei \itindex{file~descriptor~set} - \textit{file descriptor set}.} + delle limitazioni dovute all'uso dei \textit{file descriptor set}.} Ancora una volta in sez.~\ref{sec:file_poll} abbiamo trattato la funzione in maniera generica, parlando di file descriptor, ma come per \func{select} @@ -3449,7 +3437,7 @@ pertanto: \item i dati inviati su un socket vengono considerati traffico normale, pertanto vengono rilevati alla loro ricezione sull'altro capo da una selezione effettuata con \const{POLLIN} o \const{POLLRDNORM}; -\item i dati urgenti \itindex{out-of-band} \textit{out-of-band} (vedi +\item i dati urgenti \textit{out-of-band} (vedi sez.~\ref{sec:TCP_urgent_data}) su un socket TCP vengono considerati traffico prioritario e vengono rilevati da una condizione \const{POLLIN}, \const{POLLPRI} o \const{POLLRDBAND}. @@ -3572,11 +3560,9 @@ uscita e notifica in caso si errore (\texttt{\small 49--52}). Come si può notare la logica del programma è identica a quella vista in fig.~\ref{fig:TCP_SelectEchod} per l'analogo server basato su \func{select}; la sola differenza significativa è che in questo caso non c'è bisogno di -rigenerare i \itindex{file~descriptor~set} \textit{file descriptor set} in -quanto l'uscita è indipendente dai dati in ingresso. Si applicano comunque -anche a questo server le considerazioni finali di -sez.~\ref{sec:TCP_serv_select}. - +rigenerare i \textit{file descriptor set} in quanto l'uscita è indipendente +dai dati in ingresso. Si applicano comunque anche a questo server le +considerazioni finali di sez.~\ref{sec:TCP_serv_select}.