RFC: 793
Statut : Standard
Retour à l'index des normes : INDEX FRANCAIS

TRANSMISSION CONTROL PROTOCOL

SPECIFICATION



Crédits : Jon Postel / ISI
Traduction : V.G. FREMAUX

Précédent - Glossaire - Retour au sommaire

3. SPECIFICATION FONCTIONNELLE

3.1. Format de l'en-tête

Les paquets TCP sont envoyés sous forme de datagrammes Internet. L'en-tête IP transmet un certain nombre de paramètres, tels que les adresses Internet source et destinataires. L'en-tête TCP est placée à la suite, contenant les informations spécifiques au protocole TCP. Cette division permet l'utilisation de protocoles autres que TCP, au dessus de la couche IP.

En-tête TCP
 0                   1                   2                   3   
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           Port source         |       Port destinataire       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       Numéro de séquence                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                      Accusé de réception                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Data |           |U|A|P|R|S|F|                               |
| Offset| Réservé   |R|C|S|S|Y|I|            Fenêtre            |
|       |           |G|K|H|T|N|N|                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           Checksum            |  Pointeur de données urgentes |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Options                    |    Padding    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                             données                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Notez qu'une case représente une position bit.

Port source: 16 bits

Le numéro de port de la source.

Port Destinataire: 16 bits

Le numéro de port du destinataire.

Numéro de séquence: 32 bits

Le numéro du premier octet de données par rapport au début de la transmission (sauf si SYN est marqué). Si SYN est marqué, le numéro de séquence est le numéro de séquence initial (ISN) et le premier octet à pour numéro ISN+1.

Accusé de réception: 32 bits

Si ACK est marqué ce champ contient le numéro de séquence du prochain octet que le récepteur s'attend à recevoir. Une fois la connexion établie, ce champ est toujours renseigné.

Data Offset: 4 bits

La taille de l'en-tête TCP en nombre de mots de 32 bits. Il indique là ou commence les données. L'en-tête TCP, dans tous les cas à une taille correspondant à un nombre entier de mots de 32 bits.

Réservé: 6 bits

Réservés pour usage futur. Doivent nécessairement être à 0.

Bits de contrôle: 6 bits (de gauche à droite):

Fenêtre: 16 bits

Le nombre d'octets à partir de la position marquée dans l'accusé de réception que le récepteur est capable de recevoir.

Checksum: 16 bits

Le Checksum est constitué en calculant le complément à 1 sur 16 bits de la somme des compléments à 1 des octets de l'en-tête et des données pris deux par deux (mots de 16 bits). Si le message entier contient un nombre impair d'octets, un 0 est ajouté à la fin du message pour terminer le calcul du Checksum. Cet octet supplémentaire n'est pas transmis. Lors du calcul du Checksum, les positions des bits attribués à celui-ci sont marqués à 0.

Le Checksum couvre de plus une pseudo en-tête de 96 bits préfixée à l'en-tête TCP. Cette pseudo en-tête comporte les adresses Internet source et destinataires, le type de protocole et la longueur du message TCP. Ceci protège TCP contre les erreurs de routage. Cette information sera véhiculée par IP, et est donnée comme argument par l'interface TCP/Réseau lors des appels d'IP par TCP.

+--------+--------+--------+--------+
|            Adresse source         |
+--------+--------+--------+--------+
|         Adresse destinataire      |
+--------+--------+--------+--------+
|  zéro  |  PTCL  |    Longueur TCP |
+--------+--------+--------+--------+

La longueur TCP compte le nombre d'octets de l'en-tête TCP et des données du message, en excluant les 12 octets de la pseudo en-tête.

Pointeur de données urgentes: 16 bits

Communique la position d'une donnée urgente en donnant son décalage par rapport au numéro de séquence. Le pointeur doit pointer sur l'octet suivant la donnée urgente. Ce champs n'est interprété que lorsque URG est marqué.

Options: variable

Les champs d'option peuvent occuper un espace de taille variable à la fin de l'en-tête TCP. Ils formeront toujours un multiple de 8 bits. Toutes les options sont prises en compte par le Checksum. Un paramètre d'option commence toujours sur un nouvel octet. Il est défini deux formats types pour les options:

Cas 1: Option mono-octet.

Cas 2: Octet de type d'option, octet de longueur d'option, octets de valeurs d'option.

La longueur d'option prend en compte l'octet de type, l'octet de longueur lui-même et tous les octets de valeur et est exprimée en octets.

Notez que la liste d'option peut être plus courte que ce que l'offset de données pourrait le faire supposer. Un octet de remplissage (padding) devra être dans ce cas rajouté après le code de fin d'options. Ce octet est nécessairement à 0.

TCP doit implémenter toutes les options.

Actuellement, les options définies sont (type indiqué en octal):

      Type     Longueur  Description
      ----     ------    -------
       0         -       Fin de liste d'option
       1         -       Nop
       2         4       Taille de segment maximal

Définition des options spécifiques

Fin de liste d'options

+--------+
|00000000|
+--------+

Type=0

Ce code indique la fin du champ d'options. Sa position peut ne pas coïncider avec l'indication du début du champ de données marqué dans l'Offset de données. Il doit être placé après toutes les options, et non après chaque option. Il ne doit être utilisé que dans le cas ou la fin des options ne coïncide pas avec le début du champ de données.

No-Operation

+--------+
|00000001|
+--------+

Type=1

Cette option peut être utilisée entre deux options, par exemple pour aligner le début d'une option sur un début de mot de 16 bits. L'utilisation de ce séparateur n'est pas une obligation. L'implémentation doit donc prévoir de pouvoir prendre en compte un option même au milieu d'un mot.

Taille maximale de segment

+--------+--------+---------+--------+
|00000010|00000100|  Taille max. seg |
+--------+--------+---------+--------+

Type=2 Longueur=4

Donnée d'option : Taille maximale de segment: 16 bits

Si cette option est présente, elle communique à l'émetteur la taille maximale des segments qu'il pourra envoyer. Ce champ doit être envoyé dans la requête de connexion initiale (avec SYN marqué). Si cette option est absente, le segment pourra être pris de n'importe quelle taille.

Bourrage (padding): variable

Les octets de bourrage terminent l'en-tête TCP:

3.2. Terminologie

Avant de préciser en profondeur le fonctionnement de TCP, nous devons tout d'abord prendre quelques conventions sur la terminologie. La maintenance d'une connexion TCP nécessite la mémorisation d'un certain nombre de variables. Nous avons prévu que ces données soient enregistrées dans une structure nommée "Transmission Control Block" ou TCB. Parmi les données enregistrées dans ce TCB, on trouvera les sockets local et distant, les informations de sécurité et de priorité de la connexion, les pointeurs vers les tampons de réception et d'émission, les pointeurs vers la pile de retransmission et vers le segment courant. On y trouvera de plus quelques données relatives à la gestion des numéro de séquence :

Variables de séquence d'émission

Variables de séquence de réception

Le diagramme suivant montre une vue des tampons d'émission et de réception et l'implication des données de contrôle de séquence ainsi définis.

Visualisation de la séquence d'émission (vue à plat du tampon d'émission)

                   1         2          3          4      
              ----------|----------|----------|----------
                     SND.UNA    SND.NXT    SND.UNA        
                                          +SND.WND        

  1. - anciens numéros de séquence ayant été acquittés
  2. - numéros de séquences non acquittés
  3. - numéros de séquence autorisés pour une nouvelle émission
  4. - futurs numéros de séquence non encore autorisés

On notera que la fenêtre d'émission donnée par le récepteur représente la portion de l'espace de séquence noté 3. Les segments symbolisés en gras ont déjà été émis sur le réseau, les autres segments n'ont pas encore été émis et sont toujours dans le tampon.

Visualisation de la séquence de réception (vue à plat du tampon de réception)

                       1          2          3      
                   ----------|----------|---------- 
                          RCV.NXT    RCV.NXT        
                                    +RCV.WND        

  1. - numéros de séquence reçus et acquittés
  2. - numéros de séquence autorisés en réception (fenêtre)
  3. - numéros de séquences futurs non encore autorisés en réception

La fenêtre de réception est la portion de séquence notée 2. Les segments symbolisés en gras ont déjà été reçus via le réseau. Les autres restent encore "à recevoir".

On trouvera enfin des variables dont les valeurs sont déduites des données inscrites dans le segment courant.

Variables du segment courant

Une connexion connaît plusieurs états durant sa durée de vie. Les états définis sont: LISTEN, SYN-SENT, SYN-RECEIVED, ESTABLISHED, FIN-WAIT-1, FIN-WAIT-2, CLOSE-WAIT, CLOSING, LAST-ACK, TIME-WAIT, et l'état fictif CLOSED. CLOSED est dit fictif car il correspond à une situation où la connexion elle-même n'existe plus (son TCB non plus). Nous donnons ci-dessous un descriptif rapide des états cités:

LISTEN - La connexion reste en attente d'une requête de connexion externe par un TCP distant. Cet état est atteint après une demande de connexion passive.

SYN-SENT - La connexion se met en attente d'une requête de connexion, après avoir envoyé elle-même une requête à un destinataire.

SYN-RECEIVED - Les deux requêtes de connexion se sont croisées. La connexion attend confirmation de son établissement.

ESTABLISHED - La connexion a été confirmé de part et d'autre et les données peuvent transiter sur la voie de communication. C'est l'état stable actif de la connexion.

FIN-WAIT-1 - Sur requête de déconnexion émise par l'application, la connexion demande la confirmation d'une requête de déconnexion qu'elle a elle-même émise vers le distant.

FIN-WAIT-2 - La connexion se met en attente d'une requête de déconnexion par le distant, une fois reçue la confirmation de sa propre requête.

CLOSE-WAIT - La connexion se met en attente d'une requête de déconnexion émise par l'application.

CLOSING - La connexion attend la confirmation de sa requête de déconnexion par le TCP distant, lequel avait auparavant émis sa propre requête de déconnexion.

LAST-ACK - La connexion attend la confirmation de sa requête de déconnexion, émise suite à une requête similaire à l'initiative du distant.

TIME-WAIT - Un temps de latence avant fermeture complète du canal, pour s'assurer que toutes les confirmations ont bien été reçues.

CLOSED - La connexion n'existe plus. C'est un pseudo-état.

Le changement d'état d'une connexion TCP intervient sur réception d'un certain nombre de signaux, appelée "événement". Les événements peuvent être des commandes émises par l'application, OPEN, SEND, RECEIVE, CLOSE, ABORT, et STATUS; la réception d'un flag dans un segment en provenance du distant SYN, ACK, RST et FIN; la fin d'une temporisation.

Le diagramme suivant montre l'enchaînement des états, en fonction des événements reçus, ainsi que les événements produits. Il occulte par contre le traitement des fautes, ainsi que tous les autres événements qui ne sont pas en relation avec les changements d'état. Un descriptif plus détaillé des événements et de leur fonctionnement sera exposé plus avant.

NOTA BENE: ce diagramme est un résumé et ne doit pas être compris comme une spécification complète.

                              +---------+ ---------\      active OPEN  
                              |  CLOSED |            \    -----------  
                              +---------+<---------\   \   create TCB  
                                |     ^              \   \  snd SYN    
                   passive OPEN |     |   CLOSE        \   \           
                   ------------ |     | ----------       \   \         
                    create TCB  |     | delete TCB         \   \       
                                V     |                      \   \     
                              +---------+            CLOSE    |    \   
                              |  LISTEN |          ---------- |     |  
                              +---------+          delete TCB |     |  
                   rcv SYN      |     |     SEND              |     |  
                  -----------   |     |    -------            |     V  
 +---------+      snd SYN,ACK  /       \   snd SYN          +---------+
 |         |<-----------------           ------------------>|         |
 |   SYN   |                    rcv SYN                     |   SYN   |
 |   RCVD  |<-----------------------------------------------|   SENT  |
 |         |                    snd ACK                     |         |
 |         |------------------           -------------------|         |
 +---------+   rcv ACK of SYN  \       /  rcv SYN,ACK       +---------+
   |           --------------   |     |   -----------                  
   |                  x         |     |     snd ACK                    
   |                            V     V                                
   |  CLOSE                   +---------+                              
   | -------                  |  ESTAB  |                              
   | snd FIN                  +---------+                              
   |                   CLOSE    |     |    rcv FIN                     
   V                  -------   |     |    -------                     
 +---------+          snd FIN  /       \   snd ACK          +---------+
 |  FIN    |<-----------------           ------------------>|  CLOSE  |
 | WAIT-1  |------------------                              |   WAIT  |
 +---------+          rcv FIN  \                            +---------+
   | rcv ACK of FIN   -------   |                            CLOSE  |  
   | --------------   snd ACK   |                           ------- |  
   V        x                   V                           snd FIN V  
 +---------+                  +---------+                   +---------+
 |FINWAIT-2|                  | CLOSING |                   | LAST-ACK|
 +---------+                  +---------+                   +---------+
   |                rcv ACK of FIN |                 rcv ACK of FIN |  
   |  rcv FIN       -------------- |    Timeout=2MSL -------------- |  
   |  -------              x       V    ------------        x       V  
    \ snd ACK                 +---------+delete TCB         +---------+
     ------------------------>|TIME WAIT|------------------>| CLOSED  |
                              +---------+                   +---------+

Diagramme d'état d'une connexion TCP

3.3. Numéros de séquence

Une notion fondamentale dans le design du protocole TCP est l'attribution à chaque octet de données d'un numéro de séquence. Cette technique de "marquage" permet de confirmer chaque octet individuellement. Le mécanisme d'acquittement est cumulatif, en ce sens que la confirmation de l'octet de numéro de séquence X indique que tous les octets précédents ont bel et bien été reçus. Ce mécanisme permet en outre l'élimination de toute donnée reçue en double par le principe de retransmission de séquences en faute. La technique de numération commence dès le premier octet de donnée, qui reçoit le numéro de séquence le plus faible. Les autres octets sont numérotés en séquence par ordre croissant.

Un des points essentiels à se souvenir est que l'espace de numérotation de séquence est fini, bien que très grand. Cet espace, codé sur 32 bits permet le comptage de 0 à 2**32 - 1 octets. Comme ce champ est de taille finie, toute opération arithmétique sur les numéros de séquence doit se faire modulo 2**32. Cette arithmétique non signée permet de préserver la continuité de numérotation, celle-ci repartant à 0 après la valeur 2**32 - 1. Toute opération arithmétique opérée sur les numéros de séquence devra être programmée avec beaucoup de précautions du fait de l'aspect cyclique de la numérotation, en particulier les comparaisons de numéros de séquence aux alentours de la limite supérieure.

Les principales comparaisons de numéro de séquence par TCP ont pour fonction:

(a) de déterminer qu'un accusé de réception reçu concerne bien des données émises et non encore confirmées.

(b) de déterminer que tous les numéros de séquence (donc, toutes les données) d'un segment ont bien été reçues (par exemple, pour purger la pile de retransmission).

(c) de déterminer qu'un segment reçu contient bien des numéros de séquence (et donc des données) attendues (c'est à dire que le principe de fenêtre de réception a bien été respecté par l'émetteur).

En réponse à l'émission de données, TCP reçoit des "accusés de réception". La confirmation de transmission doit s'appuyer sur les comparaisons suivantes:

SND.UNA = dernier numéro de séquence non acquitté

SND.NXT = prochain numéro de séquence à émettre

SEG.ACK = valeur d'accusé de réception (prochain numéro de séquence attendu par le distant)

SEG.SEQ = premier numéro de séquence du segment

SEG.LEN = la taille des données en octets dans le segment (y compris pour des segments SYN et FIN)

SEG.SEQ+SEG.LEN-1 = dernier numéro de séquence du segment

Un nouvel accusé de réception (appelé "ack acceptable"), est un accusé de réception pour lequel l'inégalité ci-dessous est vérifiée:

SND.UNA < SEG.ACK =< SND.NXT

Un segment ne peut être effacé de la pile de retransmission que si la somme de son numéro de séquence (premier octet de donnée) et sa longueur est inférieure au numéro de séquence du dernier accusé de réception reçu (ceci revient à dire en clair que l'acquittement doit pointer une donnée au delà de la fin du segment).

Lorsque des données sont reçues, les conditions ou affectations suivantes doivent être remplies:

RCV.NXT = numéro de séquence détecté sur le segment entrant. Doit être la limite gauche (ou inférieure) de la fenêtre de réception.

RCV.NXT+RCV.WND-1 = plus grand numéro de séquence admis sur le segment entrant. Est la limite droite (ou supérieure) de la fenêtre de réception.

SEG.SEQ = premier numéro de séquence du segment entrant

SEG.SEQ+SEG.LEN-1 = dernier numéro de séquence du segment entrant.

Un segment est considéré comme à l'intérieur de l'espace de réception si:

RCV.NXT =< SEG.SEQ < RCV.NXT+RCV.WND

et

RCV.NXT =< SEG.SEQ+SEG.LEN-1 < RCV.NXT+RCV.WND

Le premier test détermine si le premier octet des données du segment est dans la fenêtre. Le deuxième test vérifie que le dernier octet de données du segment reçu est aussi à l'intérieur de la fenêtre.

En pratique, cette vérification est un peu plus compliquée. Quatre cas d'acceptabilité sont discernable, à cause de la possibilité d'une largeur 0 pour la fenêtre et d'une longueur nulle de segment:

    Longueur      Fenêtre           Test
    du segment    de réception
    -------       -------           --------------------------------------
       0          0                 SEG.SEQ = RCV.NXT
       0          >0                RCV.NXT =< SEG.SEQ < RCV.NXT+RCV.WND
      >0          0                 non acceptable
      >0          >0                RCV.NXT =< SEG.SEQ < RCV.NXT+RCV.WND
                                    ou RCV.NXT =< SEG.SEQ+SEG.LEN-1 <
                                    RCV.NXT+RCV.WND

Notez que lorsque la largeur de fenêtre est nulle, aucun segment excepté un acquittement (sans données) ne peut être reçu. Il est donc possible à un TCP de maintenir une largeur de fenêtre à zéro, tout en transmettant des données et recevant des accusés de réception. Cependant, même lorsque la fenêtre à une largeur nulle, TCP doit examiner les champs RST et URG pour tous les segments entrants.

Le mécanisme de numérotation nous permet en outre de protéger quelques informations de contrôle. Ceci est réalisé en introduisant implicitement des flags de contrôle dans la séquence (ou plus explicitement, attribuer un numéro de séquence à un contrôle).Ces contrôles pourront alors être retransmis et acquittés sans confusion (ex., une instance unique du contrôle sera traitée). Mais les informations de contrôle ne sont pas physiquement transmises dans le champ de données, le seul à être numéroté. Il faudra donc mettre en place une technique pour assigner ce numéro de séquence au contrôle. Les bits SYN et FIN sont les seuls contrôles pouvant nécessiter cette protection. Ceux-ci ne sont émis que pendant une phase d'établissement ou de rupture d'une connexion. Pour des raisons de numérotation, SYN est toujours considéré arriver avant le premier octet de données du segment dans lequel il est marqué. A l'inverse FIN est toujours considéré arriver après le dernier octet du segment. La longueur du segment (SEG.LEN) prend en compte l'espace de données ainsi que celui des contrôle. Lorsque SYN est marqué SEG.SEQ est alors le numéro de séquence du associé au contrôle SYN.

Sélection du premier numéro de séquence

Le protocole n'impose pas de restrictions à l'établissement simultané de plusieurs connexions identiques. Un connexion est définie par une paire de sockets. De nouvelles instances d'une connexion peuvent être ouvertes. Le problème qui en découle est -- "comment TCP identifie les segments dupliqués lorsqu'ils arrivent d'instances différentes d'une connexion?" Ce problème devient apparent lorsqu'une connexion s'ouvre et se ferme à une cadence rapide, ou si une connexion se coupe par manque de mémoire et se rétablit par la suite.

Pour éviter toute confusion, il faut éviter que les numéros de séquence utilisés par une instance de connexion ne soient utilisés lorsque les mêmes numéros de séquence sont émis sur le réseau par une autre instance. Ceci doit être assuré y compris si TCP se bloque et perd toute connaissance des numéros de séquence qu'il utilisait. Lorsqu'une connexion est établie, un générateur de numéro de séquence initial (ISN) est utilisé, qui génère un nouvel ISN sur 32 bits. Ce générateur est basé sur une horloge (qui peut être virtuelle) 32 bit dont le bit de poids faible est incrémenté environ toutes les 4 microsecondes. De ce fait, le cycle des ISN dure environ 4,55 heures. Comme nous supposons qu'un segment ne peut être présent dans le réseau plus longtemps que sa durée de vie maximale (Maximum Segment Lifetime = MSL) et que MSL est inférieur à 4,55 heures, il est raisonnable de penser que l'ISN sera unique.

Pour chaque connexion, on met en place un numéro de séquence d'émission et un numéro de séquence de réception. Le numéro initial d'émission (ISS) est choisi par le TCP émetteur, le numéro de séquence initial de réception (IRS) est "appris" par le récepteur durant la phase d'établissement.

Pour qu'une connexion puisse être considérée comme établie, les deux TCPs doivent auparavant avoir pu synchroniser leurs numéros de séquence respectifs. Ceci est réalisé grâce à l'émission de segments particuliers de synchronisation avec le bit SYN marqué et les numéros de séquence initiaux. Par extension, les segments marqués du bit SYN seront nommés aussi SYN. La solution demande donc un mécanisme pour "prendre" un numéro de séquence initial, ainsi qu'un dialogue pour se communiquer les ISNs.

La solution consiste à faire envoyer par chaque extrémité son propre numéro de séquence initiale, à charge de l'autre bout d'acquitter cette information.

1) A --> B SYN "mon numéro de séquence est X"
2) A <-- B ACK "ton numéro de séquence est X"
3) A <-- B SYN "mon numéro de séquence est Y"
4) A --> B ACK "ton numéro de séquence est Y"

Comme les étapes 2 et 3 peuvent être rassemblées dans le même segment, ce dialogue s'appelle "ternaire".

Cet échange en trois étapes est nécessaire dans la mesure où le choix des numéros de séquence initiaux ne dépend pas d'une horloge commune à l'ensemble du réseau, et les diverses implémentations de TCP peuvent recourir à des méthodes diverses pour choisir un ISN. Le récepteur d'un premier segment SYN n'a aucun moyen de déterminer qu'il ne s'agit pas d'un ancien segment "perdu" sur le réseau, sauf s'il se "souvient" du dernier numéro de séquence utilisé par la dernière connexion (ce qui n'est pas toujours possible). Il doit donc demander à l'émetteur de vérifier ce segment SYN.

Savoir ne rien émettre

Pour être certain que TCP n'utilise pas un numéro de séquence déjà contenu dans un segment "perdu" en cours de transmission sur le réseau, TCP doit attendre au moins la durée d'un MSL (durée de vie maximale des segments) avant toute assignation d'un nouvel ISN pour une nouvelle connexion ou sur récupération d'une connexion dont l'historique de la numérotation de séquence a été perdue. Dans le cadre de cette spécification, MSL vaut 2 minutes. Cette valeur résulte de l'expérimentation, et est susceptible de changer si le besoin s'en fait sentir. Notez que si un TCP est réinitialisé en ayant pu conserver l'historique de séquence, alors cette attente ne sera pas nécessaire; il lui suffira de produire des numéros de séquence supérieurs aux dernier émis.

Le concept de "silence" TCP

Cette spécification prévoit que des ordinateurs en faute ayant perdu toute trace des numéros de séquence sur les connexions non fermées ne puisse émettre sur le réseau Internet des segments TCP pendant au minimum la durée MSL. Dan ce qui suit, une explication détaillée de cette spécification est fournie. Les développeurs implémentant TCP pourront toujours ne pas tenir compte de ce principe, mais au risque de voir des anciens segments acceptés comme nouveaux, ou des nouveaux rejetés car apparaissant comme des doubles d'anciens segments.

TCP consomme l'espace de numérotation des séquences chaque fois qu'un nouveau segment est constitué et placé dans la pile d'émission du pilote de réseau. La détection de doublons, et l'algorithme de séquence du protocole TCP s'appuie sur le champ de numérotation fini en supposant que la séquence est suffisamment longue pour que tous les segments, y compris toutes ses réémissions ou doublons aient été retirés du réseau avant que la séquence ne reboucle. Sans ce principe, deux segments TCP pourraient se voir attribuer des plages de séquence superposées, provoquant une extrême difficulté au récepteur pour savoir quelle donnée est ancienne et quelle donnée est récente. Souvenez vous ici que dans une transmission normale, les segments successifs d'une transmission utilisent des plages de séquence disjointes et contigus.

En temps normal, TCP garde la trace du numéro de séquence à émettre, et du plus ancien acquittement attendu. Ceci lui permet de ne pas utiliser un numéro de séquence attribué à un segment en attente d'acquittement. Ceci ne garantit pas que d'anciens doublons de paquets aient été totalement retirés du réseau. Le champ de numérotation a donc été choisi très grand pour éviter qu'un paquet "fantôme" ne vienne perturber une réception. A 2 megabits/seconde, l'utilisation des 2**32 valeurs de l'espace de numérotation prend environ 4,5 heures. Comme la durée de vie d'un paquet dans le réseau ne peut excéder quelques dizaines de secondes, on considère que cette protection est largement suffisante même lorsque la vitesse de transmission atteint 10 megabits/seconde. A 100 megabits/seconde, le cycle dure 5,4 minutes, ce qui peut paraître assez court, mais laisse encore une marge raisonnable. L'algorithme de détection de doublons et de séquencement de TCP peut cependant être mis en défaut, dans le cas où TCP perd la mémoire du numéro de séquence qu'il utilisait pour les dernières transmissions. Un exemple de ceci serait un TCP initiant toutes ses connexion au numéro de séquence 0, puis sur faute et réinitialisation, rétablit une connexion (ex. en exécutant l'analyse d'une demi connexion restée ouverte) et émet des paquets avec des plages de séquences recoupant celles de paquets toujours en transit dans le réseau, et provenant de la connexion avant la rupture. Lorsque cette synchronisation de séquence est perdue, la spécification TCP recommande de respecter un délai de MSL secondes avant de réemettre des segments sur la connexion, afin de permettre à tous les anciens segments encore en transit d'être éliminés du réseau.

Même les ordinateurs avec horloge interne absolue, et utilisant cette horloge pour le choix d'un ISN ne sont pas immunisés contre ce problème.

Supposons, pat exemple, qu'une connexion soit ouverte, disons, en partant du numéro de séquence S. Supposons de plus que cette connexion est très peu chargée et qu'en plus, la fonction de génération des numéros de séquence initiaux (ISN(t)) prenne comme base la valeur du numéro de séquence, disons S1, du dernier segment émis par ce TCP sur une connexion particulière, qui se trouve être cette celle-ci. Supposons enfin que l'ordinateur redémarre et qu'une connexion soit restaurée. Le numéro de séquence initial repris sera S1 = ISN(t) -- le même numéro que celui du dernier paquet envoyé par l'instance précédente de la connexion ! Pour peu que la récupération soit suffisamment rapide, tous les paquets sur le réseau portant un numéro de séquence proche de S1 risquent d'être compris comme des nouveau paquets, même s'ils proviennent de l'ancienne instance de la connexion.

Le problème est que l'ordinateur ayant eu la défaillance ne sait absolument pas combine de temps celle-ci a duré, et par conséquent si des paquets de l'ancienne connexion ne se trouvent pas encore en transit dans le réseau.

L'une des façons de se sortir de ce problème est d'imposer un silence d'au moins un MSL après récupération sur faute - C'est la spécification dite de "silence TCP". Les implémentation qui ne tiennent pas compte de ce temps de silence et l'ignorent risquent de provoquer des confusions entre nouveaux et anciens segments dans les TCP récepteurs. Au minimum, il est conseillé que les développeurs prévoient de laisser à l'utilisateur la possibilité d'ignorer le temps de silence connexion par connexion, ou, encore mieux, de systématiser de manière informelle ce fonctionnement. Bien évidemment, même lorsque l'attente TCP est implémentée, elle n'est pas indispensable lorsque la défaillance intervient quelques MSL après que la connexion ait cessé d'émettre.

Pour résumer: chaque segment occupe une plage de séquence à l'intérieur de l'espace de numérotation, tous les numéros à l'intérieur de ces plages sont "occupés" pendant MSL secondes. Après une défaillance, un bloc temporel est pris par la plage de séquence du dernier segment émis. Si une connexion reprend trop tôt, réémettant un segment utilisant un numéro de séquence compris dans la plage de séquence précédente, il y aura risque pour le récepteur de mal interpréter la séquence de segments et produire une erreur de réassemblage de données.

3.4. Etablissement d'une connexion

L'échange "ternaire" est la technique de négociation utilisée pour la connexion. Cette procédure est habituellement initiée par un TCP vers un autre TCP. La procédure fonctionne même si les deux TCP essaient de se connecter simultanément l'un à l'autre. Lorsqu'une telle tentative simultanée survient, chaque TCP reçoit un segment "SYN" ne transportant aucun accusé de réception après l'émission de son propre "SYN". Bien sûr, il restera toujours le cas d'un segment "SYN" d'une ancienne connexion qui peut faire croire à TCP qu'un établissement est en cours. Une bonne utilisation du segment de réinitialisation peut résoudre cette ambiguïté.

Plusieurs exemples d'initialisation d'une connexion suivent. Aucun de ces exemples ne montrent des segments d'initialisation transportant des données, ce qui est parfaitement légitime, dans la mesure où TCP n'envoie pas de donnée tant qu'il n'est pas sûr de la stabilité du canal (c'est-à-dire que TCP est sensé tamponner les données qui lui sont transmises par l'application tant que l'état ESTABLISHED n'a pas été atteint). La négociation "ternaire" réduit le risque de fausses connexions. L'implémentation des échanges entre la mémoire et les messages apportera les informations nécessaires à cette vérification.

La transaction ternaire la plus simple est décrite en figure 7. Ces figures doivent être interprétées de la façon suivante. Chaque ligne est numérotée pour référence. La flèche vers la droite (-->) indique le départ d'un paquet TCP du TCP A vers le TCP B, ou l'arrivée en B d'un segment issu de A. La flèche inverse (<--), la transmission dans le sens opposé. L'ellipse (...) indique un segment toujours sur le réseau (retardé). Un "XXX" indique un segment perdu, ou rejeté. Les commentaires apparaissent entre parenthèse. L'état TCP représente l'état obtenu APRES le départ ou l'arrivée du segment (dont le contenu est mentionné au centre de la ligne). Le contenu des segments est montré sous forme abrégée, avec son numéro de séquence, les flags marqués, et le champ ACK. Les autres champs tels qu'adresses, fenêtres, offset, etc. ne sont pas montrés par souci de clarté.

      TCP A                                                TCP B
  1.  CLOSED                                               LISTEN
  2.  SYN-SENT    -->                --> SYN-RECEIVED
  3.  ESTABLISHED <--   <-- SYN-RECEIVED
  4.  ESTABLISHED -->        --> ESTABLISHED
  5.  ESTABLISHED -->  --> ESTABLISHED

Dialogue "ternaire" basique de connexion
Figure 7.

En ligne 2 de la figure 7, TCP A émet une requête envoyant un segment SYN indiquant qu'il utilisera des numéros de séquence à partir de 100. En ligne 3, TCP B renvoie sa requête SYN tout en acquittant le SYN reçu de TCP A. Notez que le champ d'accusé de réception indique que TCP B attend maintenant un numéro de séquence égal à 101, le SYN de la première requête ayant consommé la valeur 100 suivant le principe du numérotage implicite des contrôles.

En ligne 4, TCP A répond par un segment vide contenant l'accusé de réception au SYN de TCP B; En ligne 5 enfin, TCP A envoie des données. Notez alors que le numéro de séquence de la ligne 5 est identique à celui de la ligne 4, car ACK n'occupe pas d'espace dans la séquence (si c'était le cas, il nous faudrait acquitter les acquittements et on n'en sortirait plus!).

L'établissement d'une double requête simultanée est légèrement plus complexe, comme cela apparaît en figure 8. Chaque TCP bascule de l'état CLOSED vers SYN-SENT puis vers SYN-RECEIVED et enfin vers ESTABLISHED.

      TCP A                                            TCP B
  1.  CLOSED                                           CLOSED
  2.  SYN-SENT     -->               ...
  3.  SYN-RECEIVED <--               <-- SYN-SENT
  4.               ...               --> SYN-RECEIVED
  5.  SYN-RECEIVED -->  ...
  6.  ESTABLISHED  <--  <-- SYN-RECEIVED
  7.               ...      --> ESTABLISHED

Synchronisation d'une requête de connexion simultanée
Figure 8.

La principale raison du dialogue "ternaire" est d'éviter que d'anciens paquets issus d'une précédente tentative de connexion ne vienne perturber la nouvelle. Pour maîtriser ce cas de figure, un message de contrôle spécial pour la réinitialisation, "reset", a été institué. Si le TCP récepteur est dans un état non synchronisé (c'est à dire, SYN-SENT ou SYN-RECEIVED), il peut retourner à l'état d'attente LISTEN sur réception d'une commande de réinitialisation valable. Si TCP est dans l'un des états synchronisés (ESTABLISHED, FIN-WAIT-1, FIN-WAIT-2, CLOSE-WAIT, CLOSING, LAST-ACK, TIME-WAIT), la commande de réinitialisation casse la connexion et l'application en est alors informée. Nous verrons ce cas plus en détail lorsque nous analyserons les connexion semi-ouvertes ou "demi-connexions".

      TCP A                                                TCP B
  1.  CLOSED                                               LISTEN
  2.  SYN-SENT    -->                ...
  3.  (duplicate) ...                --> SYN-RECEIVED
  4.  SYN-SENT    <--   <-- SYN-RECEIVED
  5.  SYN-SENT    -->                --> LISTEN
  6.              ...                --> SYN-RECEIVED
  7.  SYN-SENT    <--   <-- SYN-RECEIVED
  8.  ESTABLISHED -->       --> ESTABLISHED

Récupération sur doublon d'un ancien SYN
Figure 9.

La figure 9 présente un cas simple de récupération sur réception d'un SYN échappé d'une ancienne tentative. A la ligne 3, le doublon du SYN arrive au TCP B. TCP B n'a aucun moyen de savoir s'il s'agit d'un doublon ou d'un segment valide, Il répond donc normalement (ligne4). TCP A détecte que le champ d'accusé de réception est incorrect et renvoie une commande "reset" en marquant le bit RST en renseignant le champ SEQ pour maintenir la conformité du processus. TCP B, en recevant RST, revient à l'état LISTEN. Lorsque le SYN valide arrive enfin à la ligne 6, la synchronisation se déroulez normalement. Si le segment SYN de la ligne 6 était arrivé avant RST, un échange plus complexe aurait eu lieu, impliquant des RST envoyés dans les deux directions.

Connexions semi-ouvertes et autres anomalies

On parle de connexion "demi-ouverte" lorsque l'un des TCP a fermé ou coupé brutalement la connexion sans que l'autre extrémité n'en ait eu connaissance, ou si les deux extrémités se retrouvent désynchronisées suite à une défaillance avec perte de mémoire. De telles connexions vont déclencher l'opération de réinitialisation à la moindre tentative d'envoi de données dans l'un ou l'autre sens. Cependant, une telle situation est considéré comme inhabituelle ou du moins anormale, et la procédure de récupération ne sera pas toujours invoquée.

Si la connexion n'existe plus au site A, alors une tentative d'émission de données du site B va résulter en la réception d'un message de réinitialisation par le TCP du site B. Un tel message indique au site B que quelque chose ne fonctionne pas, et qu'il serait plus sûr d'arrêter la communication.

Supposez maintenant que deux applications A et B communiquent ensemble, et qu'une défaillance quelconque provoque une perte de mémoire au niveau du TCP A. Suivant le système d'exploitation qui supporte A, il se peut qu'il existe un mécanisme de récupération d'erreur. Dans ce cas, et lorsque le TCP est rétabli, A va certainement vouloir renvoyer les données à partir du début, ou à partir du point de récupération si cela lui est possible. En conséquence de quoi A va certainement essayer de rouvrir (OPEN) la connexion de nouveau ou essayer d'envoyer (SEND) des données sur la connexion qu'il croit encore ouverte. Dans ce dernier cas, il recevra un message du type "connexion inexistante" du TCP local (A) TCP. Dans l'intention de rouvrir la connexion, le TCP A va émettre un segment SYN. Ce scénario conduit à l'exemple de la figure 10. Après le crash du TCP A, l'application tente de rouvrir la connexion. TCP B, dans le même temps, pense toujours que la connexion est ouverte.

      TCP A                                           TCP B
  1.  (CRASH)                               (send 300,receive 100)
  2.  CLOSED                                           ESTABLISHED
  3.  SYN-SENT -->               --> (??)
  4.  (!!)     <--      <-- ESTABLISHED
  5.  SYN-SENT -->               --> (Abort!!)
  6.  SYN-SENT                                         CLOSED
  7.  SYN-SENT -->               -->

Découverte d'une connexion semi-ouverte
Figure 10.

Lorsque le segment SYN arrive en ligne 3, TCP B, toujours synchronisé, et le segment reçu se trouvant en dehors de la fenêtre de réception, répond par un acquittement indiquant quelle séquence il s'attend à recevoir (ACK 100). TCP A s'aperçoit que cet accusé de réception n'accuse rien du tout (puisqu'il est totalement désynchronisé). Il envoie donc un reset (RST), ayant détecté une situation de connexion semi-ouverte. TCP B abandonne en ligne 5. TCP A va continuer à tenter d'établir la connexion; le problème est alors résolu dans la mesure où l'on revient à la procédure de base "ternaire" de la figure 7.

Une alternative intéressante apparaît dans le cas d'un crash de TCP A alors que TCP B essaie d'envoyer des données sur ce qu'il pense être une connexion synchronisée. Ceci est illustré en figure 11. Dans ce cas, les données arrivant au TCP A à partir de TCP B (ligne 2) ne peuvent être accepté puisqu'aucune connexion n'existe, TCP A envoie donc un RST. RST est acceptable par TCP B qui abandonne la connexion.

        TCP A                                              TCP B
  1.  (CRASH)                                   (send 300,receive 100)
  2.  (??)    <--  <-- ESTABLISHED
  3.          -->                    --> (ABORT!!)

Découverte de connexion semi-ouverte par le côté actif
Figure 11.

En figure 12, nous trouvons les deux TCPs A et B en état de connexion passive attendant un SYN. Un doublon ancien arrivant au TCP B (ligne 2) réveille celui-ci. Un SYN-ACK est renvoyé (ligne 3) et force TCP A à générer un RST (l'acquittement de la ligne 3 n'est pas recevable). TCP B accepte la réinitialisation et retourne sagement dans l'état d'attente LISTEN.

      TCP A                                         TCP B
  1.  LISTEN                                        LISTEN
  2.       ...                 -->  SYN-RECEIVED
  3.  (??) <--    <--  SYN-RECEIVED
  4.       -->               -->  (return to LISTEN!)
  5.  LISTEN                                        LISTEN

Réinitialisation suite à réception d'un doublon sur deux connexions passives
Figure 12.

Une grande variété de cas est possible, chacune résultant dans une procédure de réinitialisation selon les règles qui suivent.

Génération d'un signal de réinitialisation

En règle générale, un signal de réinitialisation (RST) doit être émis sur toute réception d'un segment qui ne répond visiblement pas aux exigences de la connexion ouverte. Ce message ne doit pas être émis si il n'est pas certain que c'est le cas. .

On dénotera trois types de situations:

1. Si la connexion n'existe pas (CLOSED) alors un "reset" est envoyé sur toute réception d'un segment sur cette connexion, excepté s'il s'agit lui-même d'un "reset". En particulier, des segments SYN adressés à une connexion inexistante sont rejetés par ce moyen.

Si le segment entrant présente un accusé de réception, le segment RST émis récupère le numéro de séquence du champ ACK de ce segment. Autrement le numéro de séquence du "reset" est zéro et l'accusé de réception est fixé à la somme du numéro de séquence et de la longueur de segment du segment entrant. La connexion demeure fermée.

2. Si la connexion est dans un état non synchronisé (LISTEN, SYN-SENT, SYN-RECEIVED), et le segment entrant acquitte quelque chose qui n'a pas été encore envoyé (ACK non recevable), ou le segment entrant est sur un niveau de sécurité ou un compartiment de sécurité ne correspondant pas exactement à celui attendu pour la connexion, un segment RST est émis.

Si notre propre segment SYN n'a pas été acquitté et le niveau de priorité du segment entrant est supérieur à celui attendu, on pourra relever le niveau de priorité de la connexion (si l'application et le système d'exploitation l'autorisent) ou émettre un "reset"; ou si le niveau de priorité du segment entrant est inférieur à celui requis, on continuera à le traiter sans changer sa propre priorité (si le TCP distant ne peut augmenter le niveau de priorité pour répondre à nos exigences locales, cela sera signifié dans les prochains segments reçus, et la connexion se fermera). Dans le cas ou notre segment SYN a été acquitté (peut être dans ce segment entrant) le niveau de priorité doit correspondre exactement. Dans le cas contraire un "reset" doit être émis.

Si le segment entrant présente un accusé de réception, le segment RST émis récupère le numéro de séquence du champ ACK de ce segment. Autrement le numéro de séquence du "reset" est zéro et l'accusé de réception est fixé à la somme du numéro de séquence et de la longueur de segment du segment entrant. La connexion demeure dans le même état.

3. Si la connexion est dans un état synchronisé (ESTABLISHED, FIN-WAIT-1, FIN-WAIT-2, CLOSE-WAIT, CLOSING, LAST-ACK, TIME-WAIT), tout segment non recevable (hors fenêtre de réception, ou accusé de réception invalide) provoque l'émission d'un segment vide contenant le numéro de séquence courant en émission et l'accusé de réception indiquant le prochain numéro de séquence attendu en réception, et la connexion reste dans le même état.

Si un segment entrant a un niveau de sécurité, ou compartiment, ou une priorité qui ne correspond pas exactement à la sécurité, et compartiment, et priorité requise pour la connexion, une réinitialisation est envoyée et la connexion se referme. L'accusé de réception du segment RST est renseigné avec la valeur de l'accusé de réception du segment entrant.

Traitement sur réinitialisation

Dans tous les cas, excepté SYN-SENT, tous les segments de réinitialisation (RST) sont validés en inspectant le champ SEQ. Une réinitialisation n'est reconnue que si elle intervient dans la fenêtre de réception. Dans l'état SYN-SENT (correspondant à un RST reçu en réponse à un segment SYN initial), le segment RST est considéré comme valide si son champ ACK acquitte le message SYN auquel il répond.

Le récepteur d'un segment RST commence par le valider, puis change d'état. Si le récepteur était dans l'état LISTEN, il l'ignore. Si le récepteur était dans l'état SYN-RECEIVED après être sorti de l'état LISTEN, alors il revient dans l'état LISTEN. Dans tous les autres cas, la connexion est abandonnée. L'application est informée de la rupture de la connexion.

3.5. Fermeture d'une connexion

CLOSE est une opération signifiant "Je n'ai plus d'autre données à envoyer". La notion de fermeture d'une communication bidirectionnelle peut être sujette à une interprétation ambigu. L'analyse à faire du côté réception n'est pas de la plus grande simplicité. Nous avons choisi de l'expliquer de la façon la plus simple. L'application demandant la fermeture peut continuer à recevoir jusqu'à être averti que l'autre extrémité à fermé à son tour la connexion. Ainsi, un programme pourrait émettre plusieurs SEND suivi d'un CLOSE, et ensuite continuer à recevoir des données jusqu'à ce que TCP lui signale que le distant a fermé sa connexion. Du moins supposons nous que TCP le fera, même si aucune commande RECEIVE ne reste pendante, et ce dès que l'autre extrémité ferme la connexion, et permette à l'extrémité locale de terminer ses opérations proprement. TCP devra émettre toutes les données qui lui auront été passées par l'application avant l'activation de la commande CLOSE. Ainsi, une application peut sans problème fermer une connexion qui continuera à émettre des données et lancer une autre tâche. Par contre il faudra toujours lire les tampons de réception, jusqu'à ce que le TCP distant indique qu'il n'a plus de données à envoyer.

On distingue trois cas de figure principaux:

  1. 1) L'application demande la fermeture au TCP en activant la commande CLOSE.
  2. 2) La connexion est rompue par le distant qui marque son flag FIN
  3. 3) Les deux applications coupent simultanément la connexion

Cas 1: L'application locale déclenche la déconnexion

Dans ce cas, un segment FIN est constitué et inscrit au bas de la pile d'émission. Aucune commande SEND ne sera plus acceptée par TCP, celui-ci passant à l'état FIN-WAIT-1. Les commandes RECEIVE restent acceptées dans cet état, la demi-connexion inverse fonctionnant toujours. Tous les segments présents dans la pile, y compris le dernier segment FIN seront retransmis jusqu'à acquittement. Lorsque le TCP a acquitté le FIN, et envoyé son FIN propre, le local acquitte le FIN distant à son tour. Notez qu'un TCP recevant un segment FIN peut l'acquitter, mais n'enverra son propre segment FIN que lorsque son application aura exécuté la commande CLOSE.

Cas 2: TCP reçoit un segment FIN du distant

Si un segment FIN arrive inopinément par le réseau, TCP peut l'acquitter et avertit l'application de la notification de fermeture. L'application émettra une commande CLOSE après avoir pris ses dispositions, suite à laquelle TCP peut alors envoyer son propre FIN au TCP distant, une fois toutes les données restantes émises. TCP attend alors l'acquittement de ce FIN pour effacer le TCB de cette connexion. Si aucun acquittement ne survient après un certain laps de temps, la connexion est coupée et l'application en est avisée.

Cas 3: les deux applications ferment simultanément

Une commande CLOSE simultanée aux deux extrémités provoque un échange de segments FIN. Tous les segments de données restants, y compris le dernier FIN seront émis et acquittés, chaque TCP pourra acquitter le segment FIN reçu. Les deux côtés, sur acquittement, effaceront chacun le TCB de cette connexion.

      TCP A                                                TCP B
  1.  ESTABLISHED                                          ESTABLISHED
  2.  (Close)
      FIN-WAIT-1  -->   --> CLOSE-WAIT
  3.  FIN-WAIT-2  <--       <-- CLOSE-WAIT
  4.                                                       (Close)
      TIME-WAIT   <--   <-- LAST-ACK
  5.  TIME-WAIT   -->       --> CLOSED
  6.  (2 MSL)
      CLOSED                                                      

Séquence de fermeture normale
Figure 13.

      TCP A                                                TCP B
  1.  ESTABLISHED                                          ESTABLISHED
  2.  (Close)                                              (Close) 
      FIN-WAIT-1  -->   ... FIN-WAIT-1
                  <--   <--
                  ...   -->
  3.  CLOSING     -->       ... CLOSING
                  <--       <--
                  ...       -->
  4.  TIME-WAIT                                            TIME-WAIT
      (2 MSL)                                              (2 MSL) 
      CLOSED                                               CLOSED

Séquence de fermeture bilatérale Figure 14.

3.6. Priorité et Sécurité

Le propos de ce chapitre est de ne permettre l'établissement d'une connexion qu'entre deux ports fonctionnant exclusivement avec les mêmes valeurs de sécurité et compartiment, et la priorité la plus élevée des deux côtés.

Les paramètres de priorité et de sécurité utilisés dans TCP sont exactement ceux définis par le protocole Internet (IP). Dans la présente spécification, le couple "sécurité/compartiment" désigne l'ensemble des paramètres de sécurité d'IP dont le niveau de sécurité, le compartiment, le groupe d'utilisateur, et les restrictions d'usage.

Une tentative de connexion avec des valeurs de sécurité/compartiment non concordantes, ou un niveau de priorité plus faible devra être rejetée via un segment de réinitialisation "reset". Le rejet d'une connexion uniquement du à une priorité plus faible ne pourra être exécuté qu'après acquittement en bonne et due forme du segment SYN..

Notez qu'un module TCP travaillant à la priorité par défaut devra néanmoins surveiller la priorité des segments entrants, et augmenter son niveau de priorité si requis.

Les paramètres de sécurité peuvent être exploités y compris en dehors d'un environnement sécurisé (les valeurs indiquent des données non classifiées), Ainsi, les ordinateurs doivent toujours être en mesure, dans un environnement sécurisé, de lire les informations de sécurité, même s'ils n'ont pas l'obligation de les transmettre.

3.7. Transfert de données

Une fois la connexion établie, les données peuvent commencer à être échangées dans des segments. Ceux-ci pouvant être rejetés (erreur de Checksum), ou perdus dans le réseau (congestion du réseau), TCP utilise un principe de retransmission (au bout d'une temporisation) pour garantir l'acheminement de tous les segments de données. Ce principe de retransmission peut conduire à la réception de "doublons". Comme il a été précisé dans les paragraphes consacrés aux numéros de séquence, TCP effectue un certain nombre de tests sur les séquences et les acquittements afin de déduire l'acceptabilité des données reçues.

L'émetteur des données garde toujours en mémoire le prochain numéro de séquence à utiliser dans la variable SND.NXT. Le récepteur garde en mémoire le prochain numéro de séquence à recevoir dans la variable RCV.NXT. L'émetteur garde en mémoire la valeur du plus ancien numéro de séquence non acquitté dans la variable SND.UNA. Si le flux de données s'interrompt pendant un temps suffisant pour que tous les segments sur le réseau soient parvenus à destination, ces trois valeurs doivent être égales.

Lorsque l'émetteur constitue un segment et le transmet, il incrémente la valeur de SND.NXT (souvenez-vous: modulo 2**32 !). Lorsque le récepteur reçoit un segment, il incrémente la valeur de RCV.NXT et envoie un acquittement. Lorsque l'émetteur reçoit cet acquittement, il incrémente SND.UNA. L'écart moyen entre ces variables est une mesure du "temps de propagation" à l'intérieur du réseau. A chaque fois, c'est la longueur du paquet qui est ajouté à ces variables. Notez que dans un état ESTABLISHED tous les segments transporteront l'information d'acquittement courant.

Le déclenchement d'une commande CLOSE impose l'utilisation de la fonction push, et l'ajout final d'un segment FIN à la pile de transmission.

Temporisation de retransmission

Du fait de la grande variété de réseaux formant Internet, et l'emploi massif de connexions TCP à travers ce réseau la valeur de temporisation de retransmission doit être déterminée dynamiquement. Nous donnons ici un exemple de procédure permettant de définir cette temporisation.

Exemple de procédure de calcul de temporisation de retransmission

Mesurer tout d'abord le temps entre l'envoi d'un octet de numéro de séquence particulier (appelons le "marqueur") et la réception d'un acquittement englobant ce numéro de séquence (les plages de séquence dans les deux segments n'ont pas à être identique). Ceci mesure ce que nous appellerons le "temps de boucle" (RTT = Round Trip Time). Puis calculez un "temps de boucle" corrigé (SRTT = Smoothed Round Trip Time), par la formule:

SRTT = ( ALPHA * SRTT ) + ((1-ALPHA) * RTT)

et enfin, calculer la temporisation de retransmission (RTO = Retransmission Time Out) comme suit:

RTO = min[UBOUND,max[LBOUND,(BETA*SRTT)]]

où UBOUND est une valeur limite de la temporisation (ex. 1 minute), LBOUND une valeur limite inférieure (ex.1 seconde), ALPHA le facteur de correction (ex. 0,8 à 0,9), et BETA est un facteur de variance du temps de propagation (ex., 1,3 à 2,0).

Transmission d'informations urgentes

L'objectif du mécanisme d'urgence de TCP est de fournir un moyen à l'application émettrice d'inciter le récepteur à prendre en compte des données urgentes, et un moyen pour signaler en retour que ces données urgentes ont bien été prise en compte par le récepteur.

Ce mécanisme permet de marquer un numéro de séquence particulier comme étant la fin d'un bloc de données urgentes. Lorsque ce marqueur pointe sur un numéro de séquence non encore reçu (RCV.NXT) par le TCP récepteur, ce dernier doit prévenir l'application de passer en "mode d'urgence"; et lorsque le numéro de séquence reçu rattrape ce marqueur, le TCP doit signifier à l'application de repasser en "mode normal". Si le pointeur d'urgence est réactualisé en "mode d'urgence", l'application utilisatrice ne pourra en avoir connaissance.

Ce mécanisme utilise un champ dédié dans chaque segment émis, appelé "pointeur urgent". Le bit de contrôle URG indique que ce champ contient une information valide. Le TCP récepteur ajoutera la valeur de ce champ au numéro de séquence du segment concerné pour obtenir le "marqueur". Lorsque le bit URG n'est pas marqué, ce champs n'est pas marqué, aucune mention d'urgence n'est contenue dans le segment.

Pour signifier l'état d'urgence, le segment émis doit au moins contenir un octet de données. Si l'émetteur utilise de plus la fonction push, le temps de transmission des données urgentes sera optimisé.

Gestion de la fenêtre de transmission

La fenêtre transmise dans chaque segment indique la plage de numéros de séquence que l'émetteur de la fenêtre (celui qui reçoit les données) est prête à accepter. La taille de cette fenêtre est en relation avec la taille disponible des tampons de données associés à cette connexion.

Une grande taille de fenêtre encourage l'émission. Si le nombre de données reçues est supérieur à ce que la fenêtre indique, les données hors fenêtre seront rejetées. Cette déperdition entraîne un grand nombre de retransmissions et surcharge inutilement le réseau et les TCP. L'usage d'une petite taille de fenêtre morcelle le débit en ajoutant un certain retard supplémentaire au "temps de boucle", mais en limitant la surcharge du réseau due aux retransmissions.

Le mécanisme instauré n'interdit pas à un TCP de signifier une largeur de fenêtre importante, puis beaucoup plus mince dans le segment suivant sans avoir reçu pour autant la quantité de données qui le justifie. Cette technique dit "d'étranglement de fenêtre," est fortement déconseillée. Le principe de robustesse nous dicte qu'un TCP ne doit pas de lui même étrangler une fenêtre, mais devra être préparé à accepter un tel comportement de la part d'autres TCP.

Le TCP émetteur doit être prêt à accepter de l'application et à envoyer au moins un octet de nouvelles données même lorsque la fenêtre de transmission est de largeur nulle. L'émetteur devra retransmettre régulièrement le segment au TCP récepteur. L'intervalle recommandé entre deux retransmissions, lorsque la largeur de fenêtre est nulle est d'environ 2 minutes. Cette retransmission est essentielle pour s'assurer que la réouverture de la fenêtre par le récepteur sera bien signifiée au TCP émetteur.

A l'autre bout, lorsque le TCP récepteur affiche une largeur de fenêtre nulle et qu'un segment arrive, il doit nécessairement répondre an acquittant le segment, un bon moyen de confirmer régulièrement le nouveau numéro de séquence attendu et la largeur de fenêtre courante (zéro).

Le TCP émetteur met en forme les données afin de constituer des segments à émette tenant dans l'espace de séquence autorisé par la fenêtre, et répète la même opération pour inscrire les copies de segments dans la pile de retransmission. Cette dernière opération n'est pas obligatoire, mais cependant très utile.

Pour une connexion n'utilisant qu'un seul flux de données unidirectionnel (mais nécessairement "ouverte" en bidirectionnel), la largeur de fenêtre est envoyée dans les acquittements sous un numéro de séquence unique (un segment ACK ne "consomme" pas d'espace de séquence). Il n'est donc pas possible de remettre dans l'ordre l'historique de la largeur de fenêtre. Cela ne représente pas un problème critique, bien qu'il signifie que le TCP émetteur puisse se baser sur d'anciennes valeurs de largeur de fenêtre lors d'une émission. Une technique pour s'affranchir de ce problème est de toujours récupérer l'information de fenêtre dans l'accusé de réception contenant le plus grand numéro de séquence (une méthode indirecte de numérotation des segments ACK !).

La gestion de la largeur de fenêtre a une influence importante sur les performances de la communication. Les commentaires qui suivent sont à destination des développeurs.

Suggestion pour gestion de la largeur de fenêtre

L'ouverture d'une toute petite fenêtre réduit les performances en augmentant le poids des en-têtes par rapport aux données.

Côté récepteur: Une suggestion pour éviter des fenêtres trop petites est de ne différer l'ouverture de la fenêtre jusqu'à ce qu'un certain pourcentage minimum X de l'espace total des tampons alloués à la communication n'ait été libéré (X peut être pris entre 20 et 40%).

Côté émetteur: L'émetteur peut attendre que la fenêtre atteigne une certaine largeur avant de commencer à envoyer des données. La fonction push permettra d'envoyer un reliquat de données même si la largeur de fenêtre est inférieure à la limite choisie.

Notez qu'il n'est pas une bonne stratégie de retarder l'émission des acquittements, au risque de provoquer des retransmissions inutiles et coûteuses en termes de performances. Une stratégie est d'émettre des acquittements dès qu'un segment court arrive (sans modifier la largeur de fenêtre), la largeur de fenêtre étant transmise dans les acquittements donnés pour des segments de taille supérieure.

Le segment envoyé pour tester une largeur de fenêtre nulle peut conduire à une "atomisation" de la transmission. Lorsque le segment testant cette information et contenant un seul octet de données est accepté (à la réouverture de la fenêtre), il consomme un octet du nouvel espace ouvert. Si l'émetteur envoie systématiquement autant de données qu'il peut lorsque la fenêtre est ouverte, la transmission va se stabiliser en une succession de petits et longs segments. Au fur et à mesure du temps, les variations de débit interne entre les tampons et la connexion conduit à une séquence de segments courts et "pas si longs que ça". Après un certain temps, la connexion est principalement constituée de segments courts.

La suggestion faite ici est que les implémentations de TCP doivent gérer attentivement la variation de largeur de fenêtre, les versions les plus simplistes conduisant toujours à une fragmentation excessive de la transmission.

3.8. Interfaces

Deux interfaces sont concernées dans ce chapitre: l'interface application/TCP et l'application TCP/réseau (la dernière via le protocole de niveau inférieur). Nous définirons assez précisément le protocole entre l'application et l'interface, et passerons sous silence l'interface entre TCP et la couche inférieure, dans la mesure où celle-ci est parfaitement définie dans la spécification de ce protocole. Dans le cas où ce protocole est IP, on remarquera quelques paramètres communs à ces deux protocoles.

Interface application/TCP

La description à suivre des commandes à envoyer à TCP sera tout au plus une spécification d'intention, dans la mesure où les ressources système et leur forme diffèrent considérablement d'un système à l'autre. Par conséquent, nous devons prévenir le lecteur que différentes implémentations de TCP peuvent présenter des interfaces distinctes. Cependant, il existera toujours un noyau de fonctions que TCP devra fournir à toute application, afin d'assurer la compatibilité globale du système multicouches. Ce chapitre décrit les commandes essentielles qu'un TCP se doit d'accepter.

Commandes applicatives vers TCP

Les paragraphes suivants décrivent les aspects fonctionnels de l'interface Application/TCP. La notation utilisée est très proche de la syntaxe habituellement acceptée pour les appels de fonctions par les langages de haut niveau, mais cet usage n'interdit pas l'utilisation d'appels sous forme condensée (ex., SVC, UUO, EMT).

Les commandes de base décrites ici sont essentielles pour qu'un TCP puisse supporter une communication inter-processus. Chaque implémentation concrète pourra adopter sa propre syntaxe, et pourra y adjoindre des combinaisons ou fonctions partielles issues de ces fonctions de base. Par exemple, certains utilisateurs souhaiteront qu'une premier appel à la fonction SEND puisse automatiquement ouvrir la connexion (OPEN).

Dans son rôle d'intermédiaire de communication, TCP doit non seulement accepter des commandes, mais doit retourner un certain nombre d'informations à l'application, soit en réponse à une commande, soit de façon unilatérale. Ces dernières consistent en:

(a) une information générale sur la connexion (ex., interruptions, fermeture distante, connexion sur tel socket passif en attente).

Convention : Dans la formulation des commandes qui suivent, nous adoptons la convention de notation suivante pour les paramètres :