Table des matières
2.3. Le protocole TCP
Le protocole TCP (Transmission Control Protocol) est décrit dans le RFC 793. Ce RFC original a depuis été amendé et complété par de nombreux autres RFC, mais les principes de base sont toujours valides.
TCP fournit un tout autre service qu’UDP. Il est :
- orienté connexion, ce qui signifie que les deux entités communiquant vont au préalable ouvrir la connexion, puis la terminer lorsque les transmissions de données sont terminées
- en mode flux d’octets : les applications se transmettent les données sans délimitation. Cela peut être octet par octet ou par paquets de taille variable, selon les cas. A la réception, les données peuvent également arriver au compte-goutte ou par paquet, pas nécessairement selon le même rythme qu’à l’émission.
- fiable : il garantit que le flux d’octet sera reconstitué à l’identique, sans erreur, perte ou mélange d’octets.
Etant donné que les hôtes TCP doivent garder en mémoire l’état des connexions en cours (en cours de démarrage, ouverte, en cours de fermeture, …), TCP est un protocole dit Stateful.
L’en-tête TCP
Implémenter ce type de services est complexe, et cela se ressent dans l’en-tête du protocole, qui est nettement plus grand que celui d’UDP :
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Port | Destination Port |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Acknowledgment Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data | |U|A|P|R|S|F| |
| Offset| Reserved |R|C|S|S|Y|I| Window |
| | |G|K|H|T|N|N| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Checksum | Urgent Pointer |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| data |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Les champs identiques entre l’en-tête TCP et l’en-tête UDP sont :
- Les numéros de port source et destination (16 bits chacun)
- Le checksum TCP, sur 16 bits toujours
D’autres éléments y figurent, pour garantir le service spécifique à TCP, et complètent les 20 premiers octets de l’en-tête :
- Le numéro de séquence : Il identifie le numéro du premier octet de données dans l’ensemble du flux de données transmis. Ainsi, au cours d’une transmission d’un flux de 25 octets répartis dans 5 segments de 5 octets, les numéros de séquence seront 0, 5, 10, 15 et 20. Notez que cette numérotation ne commence en général pas à zéro, pour des raisons de sécurité. Ce numéro de séquence sert à reconstituer le flux de données (les segments pouvant arriver dans le désordre ou être dupliqués), mais également à détecter les pertes au niveau du destinataire.
- Le numéro d’accusé de réception (ACK number): Il permet au receveur d’annoncer à l’émetteur le numéro du prochain octet attendu, ce qui permet à ce dernier de vérifier si toutes les données sont bien arrivées à destination.
- Les flags permettent d’indiquer des éléments de contrôle. Il y a notamment :
- Le flag ACK, qui indique si le segment contient un numéro d’accusé de réception valide
- Le flag RST, qui indique que la connexion doit être interrompue immédiatement
- Le flag SYN, qui indique une demande d’ouverture de connexion
- Le flag FIN, qui indique qu’un des interlocuteurs a terminé ses transferts et que la connexion peut être interrompue dans cette direction.
- Le champ Window, qui indique la quantité maximum d’octets qui peuvent être en transit sur le réseau à un moment donné. Chaque interlocuteur indique la valeur acceptable pour lui, en fonction des ressources mémoire que l’OS peut allouer au stockage des données TCP en attente. Les deux interlocuteurs utiliseront la taille de fenêtre la plus petite parmi les deux valeurs échangées, pour s’adapter au fonctionnement de celui qui a le moins de ressources. La valeur de ce champs peut évoluer au cours d’une transmission.
- Le champ Data Offset indique l’emplacement du début du payload (décalage par rapport au début du segment). Il peut également être interprété comme étant la taille de l’en-tête TCP. Il est exprimé en mots de 32 bits (4 octets), et, comme il est sur 4 bits, sa valeur est comprise entre 0 et 15. Si le segment n’utilise pas d’option TCP, le champ Data Offset vaudra 5, ce qui représente un en-tête de 5 x 4 octets = 20 octets. Au maximum, l’en-tête TCP aura une valeur de 60 octets (Data Offset de 15).
- Le champ Urgent Pointer est défini pour permettre un traitement urgent de certaines données. Il est utilisé en conjonction avec le flag URG. Nous ne creuserons pas ce mécanisme ici.
Notons que les limitations en terme de taille de payload mentionnées pour UDP sont moins importantes pour TCP, puisqu’il sait gérer la fragmentation en reconstituant la conversation.
Déroulement d’une session TCP
Une connexion TCP normale contient 3 phases :
- l’établissement de la connexion (en vert sur le schéma ci-dessous), où l’initiateur s’assure que le destinataire est disponible, et où les deux interlocuteurs peuvent éventuellement se mettre d’accord sur l’utilisation d’options TCP.
- l’échange de données (en jaune), où chacun peut envoyer des données. Par exemple, dans le cas d’une connexion HTTP au dessus de TCP, le client enverra une requête HTTP et le serveur répondra avec une réponse contenant une page web. Sur la figure ci-dessus, l’hôte A et l’hôte B s’échangent des données. Les communications TCP bi-directionnelles.
- la fermeture de la connexion (en rouge), si tout s’est déroulé comme attendu. Chaque direction de transmission clôture son canal, et lorsque les deux directions sont fermées, toutes les données sont considérées comme transmises. Dans le cas où un problème technique perturbe le déroulement de la communication, la partie concernée envoie un segment avec le flag RST activé : cela signifie la fin abrupte et unilatérale de la communication. Ce cas de figure ne permet pas de garantir que toutes les données ont été transmises.

Ouverture de connexion TCP
L’établissement de la connexion se fait via un triple échange, appelé “Three-Way Handshake”. Tout d’abord, l’hôte qui souhaite établir la connexion (appelons-le A) envoie un segment SYN contenant le premier numéro de séquence de la transmission dans cette direction.Le destinataire (B) envoie à son tour un segment, qui aura deux significations :
- un flag ACK signalant que le segment contient l’accusé de réception du SYN reçu de A, validant la demande d’ouverture dans ce sens
- un flag SYN demandant l’ouverture de l’autre direction de transmission (de B vers A). Ce segment contiendra également le premier numéro de séquence du flux B-A.
Pour finir cet établissement de connexion, un troisième segment est envoyé, par A, contenant un ACK validant l’ouverture du flux B-A.
Remarquez que :
- Pour des raisons de sécurité, les numéros de séquence commencent à une valeur initiale aléatoire, et non à 0.
- Un segment avec le flag SYN activé “consomme” un octet de données : Le numéro de séquence suivant est incrémenté. Par contraste, le ACK ne consomme pas de numéro de séquence, et le segment suivant aura donc le même numéro.

Transmission de données TCP
Une fois la connexion établie, les deux entités échangent des données de manière bidirectionnelle. Chaque segment peut contenir des données et/ou des accusés de réception, selon la présence ou non d’un payload et du flag ACK.
Reprenons l’exemple de la conversation TCP complète présentée plus haut, et concentrons-nous sur la partie transfert de données. L’application cliente, sur l’hôte A, souhaite envoyer dans un premier temps les données ABC. Une fois la connexion établie, l’OS, via sa stack TCP/IP, va générer le premier segment de données à destination de B. Lorsque l’OS de l’hôte B reçoit le segment, il le décapsule et transmet les données ABC à l’application auxquelles elles sont destinées. Il transmet également un segment d’accusé de réception à A pour confirmer à ce dernier que la transmission s’est bien passée.
Un peu plus tard, l’application sur B envoie à son tour des données (DE). Un segment TCP est généré, toujours sur la même connexion. La connexion est donc bien bidirectionnelle, puisque les deux interlocuteurs envoient des données. Le segment DE est reçu par l’OS de A, qui le transmet à l’application et envoie un accusé de réception à B.

Numéros de séquence et d’accusé de réception
La première représentation ci-dessus est simplifiée : elle ne montre pas le détail des champs TCP des segments échangés. Pour bien comprendre les mécanismes de transport fiable, il faut aller creuser plus loin et s’intéresser aux numéros de séquence et au numéros d’accusé de réception (ACK).
Reprenons la même figure, et ajoutons cette fois les numéros de séquence et d’ACK. Pour rappels :
- le numéro de séquence initial est établi lors de l’ouverture de connexion.
- les numéros de séquence numérotent les octets d’un flux de données, et non les segments échangés.
- un numéro d’accusé de réception indique le numéro du prochain octet attendu par le receveur, et non le numéro du segment acquitté.
Dans notre exemple, le numéro de séquence initial de A correspondant au segment SYN de l’ouverture de connexion était 123. Le prochain numéro de séquence est donc le 124, utilisé ici pour le segment contenant ABC.
Lorsque B accuse réception du segment 124, il doit utiliser, comme numéro d’ACK, le numéro du prochain segment attendu : comme les numéros identifient les octets du flux de données et non les segments eux-mêmes, le prochain segment aura le numéro 127 (124 + les 3 octets du message ABC). La valeur du champ ACK dans le segment envoyé par B est donc 127.
La même logique s’applique pour les données envoyées par B : son numéro de séquence initial était 456. Son premier segment de données, contenant le message “DE”, aura donc le numéro 457. L’ACK envoyé par A pour ce segment aura donc le numéro d’ACK 459 (457 + les deux octets correspondant à DE) 
Piggybacking
Dans notre premier exemple, nous avons des segments contenant des données, et des segments contenant uniquement des ACK. En pratique, il est possible de combiner les deux, et d’avoir des segments avec, à la fois, des données et un ACK. Ce mécanisme s’appelle le “piggybacking”.
Prenons un second exemple pour illustrer cela : dans la figure ci-dessous, l’application A souhaite envoyer “ABCDE”. L’OS, ayant à ce moment une fenêtre TCP limitée, ne peut pas transmettre tout cette information en une fois. Il commence par envoyer un premier segment “ABC”, qui arrive à peu près au moment où l’application sur l’hôte B souhaite envoyer les données “XY”. L’OS de B va donc générer un segment qui conviendra à la fois les données “XY” et l’accusé de réception correspondant au message “ABC”.
A la réception du segment contenant XY, A va générer l’accusé de réception correspondant, et y joindre les données “DE” qu’il lui reste à transmettre pour répondre à la demande de l’application. Nous avons donc deux segments qui contiennent à la fois des données et des informations de contrôle (ACK).

Détection des pertes de segments
Durant une transmission, des pertes de segments peuvent survenir. Dans ce cas, l’émetteur doit pouvoir détecter ces pertes et retransmettre les données manquantes. Il utilise pour cela un timer de retransmission. Si ce dernier expire, il retransmet ses données, comme illustré dans l’exemple ci-dessous.
Dans cette transmission, l’application A souhaite transmettre les données ABCDE à B. L’OS A commence par transmettre un premier segment contenant AB avec le numéro de séquence 123, puis un second contenant CDE, avec le numéro 125. Malheureusement, ce dernier se perd.
L’hôte B reçoit bien le premier segment, et génère l’accusé de réception correspondant, qui est reçu par A. Ce dernier, avant de clôturer son envoi, attend l’accusé correspondant au segment perdu (ACK = 128). Cet accusé n’arrivera pas. Pour ne pas attendre indéfiniment, il faut donc utiliser un timer : il est démarré lors de l’envoi d’un segment, et annulé lors de la réception d’un accusé de réception. S’il expire sans que l’accusé ait été reçu, l’émetteur considère alors le segment comme perdu et le retransmet, comme indiqué sur la figure. On observe que, cette fois, l’accusé de réception avec la valeur 128 est bien envoyé, ce qui confirme à A que tous les octets jusqu’au numéro 127 sont bien arrivés.
Mécanismes de retransmission
Prenons un autre exemple, un peu plus complexe, dans la figure ci-dessous. Cette fois, A envoie ses données dans trois segments, et c’est le premier qui se perd. B reçoit donc bien des segments, mais pas ceux auxquels il s’attend : suite à l’ouverture de connexion ou à des transmissions précédentes, il sait que le prochain octet attendu est le 123. Comme il reçoit les octets 125 à 127, il en déduit qu’il y a un “trou” dans les numéros de séquence reçu. Il ne peut donc plus continuer à transmettre le flux de données au processus applicatif en attente, puisque livrer des octets dans le désordre ne respecterait pas le contrat de service.

Selon les implémentations TCP, l’OS B va soit jetter les segments ayant des numéros supérieurs à celui attendu, soit les mettre de côté dans un buffer pour les transmettre une fois les données manquantes reçues.La figure ci-dessus montre l’algorithme Go-Back-N : Le receveur jette les segments reçus hors séquence (i.e. qui ne correspondent pas au numéro de séquence attendu, 123 dans l’exemple ci-dessous). Du coup, lors de l’expiration du timer, le receveur retransmet tous les segments non acquittés de sa fenêtre de transmission.
Si, à l’inverse, le receveur dispose d’un buffer en mémoire pour stocker les segments reçus hors séquence, l’algorithme “Selective Repeat” peut être exploité, comme le montre la figure ci-dessous.

Lorsque les segments contenant CD et E arrivent à la destination, l’OS de l’hôte B ne les transmet pas à l’application, puisqu’ils sont hors séquence. Par contre, il les stocke temporairement dans un buffer. Les accusés de réception indiquent toujours que le prochain segment reçu est le 123. Par contre, le receveur peut inclure dans les options de l’en-tête TCP la liste des octets reçus hors séquence. Ici, il s’agit des octets 125-126 pour le premier accusé de réception, et 127 pour le second. Ces options TCP s’appellent des “Selective Acknowledgement” (SACK).
Du côté de l’émetteur, si celui-ci a connaissance de l’utilisation de l’algorithme Selective Repeat par le receveur et est en mesure de décoder les options TCP “SACK” qui indiquent exactement les informations reçues par la destination, il peut se contenter de ne retransmettre que le segment perdu. Une fois ce dernier enfin arrivé à destination, B peut enfin transmettre l’ensemble des données à l’application : L’information “AB” juste reçue, ainsi que “CDE” depuis le buffer.
En pratique, TCP utilise un algorithme hybride, basé sur Go-Back-N mais avec souvent, selon les implémentations, une stratégie de réception proche du Selective Repeat. Il propose également des optimisations dans les retransmissions, telles que le “Fast Retransmit” : Lorsque l’émetteur reçoit trois accusés de réception dupliqués pour un même segment, il en déduit qu’un segment s’est perdu mais que les suivants sont bien arrivés (cf. acquis dupliqués dans la figure illustrant Go-Back-N ci-dessus). Il va donc retransmettre le segment concerné sans attendre l’expiration du timer.
Une explication complète des mécanismes de retransmission est disponible dans le livre Computer Networking :
- le chapitre sur les retransmissions dans le cadre de la couche Liaison de données introduit les principes de fonctionnement des retransmissions
- Les spécificités de ces mécanismes dans le cadre de la couche Transport sont détaillés dans le chapitre “Data Transfert”.
- Et enfin, l’implémentation de ces mécanismes dans TCP est détaillée dans la section “TCP Reliable Data Transfer”.
Fermeture de connexion TCP
Comme expliqué plus haut, les données sont bi-directionnelles avec TCP. Il y a donc deux directions de transmission, et chacune doit être fermée spécifiquement et indépendamment. Pour cela, chaque direction, lorsqu’elle a fini sa transmission, envoie un FIN, puis attend un ACK de confirmation. La communication est entièrement terminée à la fin des deux échanges FIN/ACK, comme le montre la figure ci-dessous. Ce cas de figure est appelé “Fermeture Gracieuse”. Il garantit que l’ensemble des données de la transmission ont été correctement reçues.

Si, par contre, un événement imprévu interrompt la connexion (par ex. interruption du processus applicatif), l’OS va réagir en interrompant directement la connexion TCP par l’envoi d’un unique segment avec le flag RST. La fermeture est abrupte : les données en transit sont potentiellement perdues. Ce cas de figure est illustré dans la figure ci-dessous.

Usages de TCP
La qualité principale de TCP est sa fiabilité, mais cette fiabilité vient avec un coût : le temps nécessaire à la retransmission. TCP ajoute donc des délais lors de la transmission de données en milieu instable, ce qui ne convient pas à toutes les applications. Par contre, il conviendra parfaitement aux applications nécessitant des transferts de données précis (documents, exécutables, données critiques, … ), mais étant plus tolérant à des délais potentiellement instables. Les applications et protocoles utilisant principalement TCP sont :
- HTTP pour les échanges de documents Web
- SMTP, POP, IMAP pour les emails
- Les applications de transfert de fichiers (ex : FTP)
- Les protocoles d’accès distant (ex : SSH)
- Certaines applications multimédia non interactives (ex : streaming avec bufferisation possible)
[Pour aller plus loin] Les options TCP
Avec le temps, TCP a beaucoup évolué, et de nouveaux mécanismes additionnels sont apparus. Ces mécanismes sont supportés par certaines implémentations et pas par d’autres. Pour des raisons de rétro-compatibilité, ils sont donc disponibles sous forme d’options, et intégrés au segment TCP dans une partie additionnelle et optionnelle de l’en-tête.
Pour déterminer si un segment supporte des options, il faut considérer le champ “Data Offset” présenté plus haut : s’il vaut 5 (soit 20 octets), l’en-tête ne contient que les champs par défaut. Si sa valeur est supérieure à 5, cela indique l’utilisation d’options. Chaque option doit être un multiple de 8 bits. Comme l’en-tête complet doit être un multiple de 32 bits, il devra éventuellement être complété par du padding (bits à zéro).
Il y a deux types de format d’option :
- un “format court” : option exprimée comme un simple octet indiquant le type de l’option
- un second “format long” comportant trois champ : un octet indiquant le type, un octet indiquant la taille, et enfin, un troisième champ comprenant les octets de l’option elle-même.
Les options sont négociées lors de l’ouverture de connexion, dans les segments SYN et SYN+ACK : chaque interlocuteur liste les options qu’il supporte ainsi que les paramètres correspondants. Une option est adoptée dans la communication lorsque les deux interlocuteurs la supportent. Les paramètres adoptés dépendront du type d’option (ex : valeur minimum entre les deux paramètres proposés).
Exemples d’options
Options obligatoires
Trois options doivent être obligatoirement supportées par toutes les implémentations TCP :
- l’option End of Option List : option courte dont le type vaut 0. Comme son nom l’indique, elle indique la fin des options. Elle est utilisée lorsque les options ne sont pas alignées sur 32 bits, et permet d’indiquer que la suite de l’en-tête est constituée de padding.
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 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | <options> | Kind= O | Padding (0) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - L’option No Operation (NOP) : option courte dont le type vaut 1. Elle peut être utilisée pour aligner les options sur des mots de 4 octets. Il s’agit d’une sorte de “padding interne” entre options, comme le montre la figure ci-dessous.
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 ... +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | <option 1 - 3 octets> | NOP (Kind=1) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | <option 2 - 4 octets> | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ... - L’option Maximum Segment Size (MSS) : option longue, dont le champ type vaut 2 et le champ longueur 4. Cette option possède donc deux octets d’information, précisant la taille maximum de segment supportée par l’émetteur du segment.
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 ... +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Kind= 2 | Length = 4 | Maximum Segment Size (MSS) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ...
Quelques options facultatives
Parmi toutes les options possibles, en voici trois couramment utilisées. Elles ne sont pas obligatoires, ce qui signifie que certaines implémentations TCP ne les supportent pas. Elles sont alors simplement ignorées.
- Window Scale (voir RFC7323): Type 3, longueur 3. Ce champ permet d’augmenter la taille possible de la fenêtre TCP (champ Window) au delà de ce qui est possible dans l’en-tête de base. L’octet de données de l’option Window Scale doit être interprété comme l’exposant en base 2 du facteur multiplicatif à appliquer à la valeur initiale de la fenêtre. Par exemple, si Window Scale vaut 2, la taille de la fenêtre sera multipliée par 2^2 , soit un facteur multiplicatif de 4. La figure ci-dessous représente un segment TCP contenant l’option Window Scale. Notez la valeur du Data Offset, ainsi que la présence de padding pour garantir l’alignement sur 32 bits.
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Port | Destination Port |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Acknowledgment Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data | |U|A|P|R|S|F| |
| Offset| Reserved |R|C|S|S|Y|I| Window |
| = 6 | |G|K|H|T|N|N| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Checksum | Urgent Pointer |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Kind=3 | Length=3 | Scale=2 | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- Timestamps (voir RFC7323): Type 8, longueur 10 octets. Ce champ permet d’ajouter des informations temporelles aux segments TCP, afin de garder une trace du temps écoulé entre les envois (mesure du Round Trip Time). Si l’option est supportée par les deux interlocuteurs, dans chaque segment, il ajouteront le champ d’option Timestamps avec deux valeurs :
- Leur timestamp actuel, sur 4 octets (Timestamp Value)
- Une copie du timestamp du dernier segment reçu (Timestamp Echo Reply) Lors de la réception d’un segment, un interlocuteur peut calculer le RTT en soustrayant le Timestamp Echo Reply au timestamp de son horloge interne. Dans l’exemple ci-dessous, A envoie un segment avec un timestamp à 1 dans la valeur TSval. Lorsque B reçoit ce segment et envoie l’accusé de réception, il recopie le TSval du premier segment dans son propre segment (qui accuse réception du message de A), en le plaçant cette fois dans le champ TSecr. Lorsque A reçoit cet accusé de réception, il consulte son horloge. Si cette horloge vaut, par exemple, 10 à ce moment, A peut en déduire que le RTT de l’échange vaut 9.
TCP A TCP B <A,TSval=1,TSecr=120> -----> <---- <ACK(A),TSval=127,TSecr=1>Voici un extrait de l’en-tête correspondant au premier segment :
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 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | < autre option > | Kind=8 | Length = 10 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | TSval = 1 (4 octets) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | TSecr = 120 (4 octets) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ...Et ci-dessous, une capture d’écran du logiciel Wireshark montrant l’option Timestamp dans un segment TCP réel.

- Selective Acknowledgements (SACK) : Ce mécanisme permet à TCP d’optimiser les retransmissions en faisant du “Selective Repeat”. Cette option permet d’ajouter à la valeur de l’ACK cumulatif une liste de numéro de segments reçus de manière désordonnées, pour éviter à l’émetteur de devoir les retransmettre inutilement. Cette option possède le type 5, et a une longueur dépendant du nombre de valeurs SACK contenues. Ces valeurs identifient des intervalles : 4 octets identifient le début de la plage de numéros de séquences, et 4 octets la fin de cette plage.
- Exemple : imaginons que l’émetteur envoie les segments avec les numéros 0 à 8. Le numéro 5 est perdu. Le receveur va envoyé un ACK de valeur 5 (ack cumulatif classique). Il ajoutera également dans l’option SACK l’intervalle [6:8]. Grâce à l’option SACK, l’émetteur, en recevant l’accusé de réception, peut en déduire qu’il ne doit retransmettre que le segment 5, et non pas tout le bloc [5:8].
Voici le schéma du segment correspondant à cet exemple. Notez la valeur du Data Offset qui, avec les 10 octets pour l’option SACK et les 2 octets de NOP pour le remplissage, passe de 5 mots de 32 bits (20 octets) à 8 mots de 32 bits (32 octets) :
- Exemple : imaginons que l’émetteur envoie les segments avec les numéros 0 à 8. Le numéro 5 est perdu. Le receveur va envoyé un ACK de valeur 5 (ack cumulatif classique). Il ajoutera également dans l’option SACK l’intervalle [6:8]. Grâce à l’option SACK, l’émetteur, en recevant l’accusé de réception, peut en déduire qu’il ne doit retransmettre que le segment 5, et non pas tout le bloc [5:8].
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Port | Destination Port |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Acknowledgment Number = 5 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data | |U|A|P|R|S|F| |
| Offset| Reserved |R|C|S|S|Y|I| Window |
| = 8 | |G|K|H|T|N|N| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Checksum | Urgent Pointer |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| NOP | NOP | Type = 5 | Len = 10 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Left edge = 6 (4 octets) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Right edge = 8 (4 octets) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| DATA |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
L’utilisation de l’option SACK est activée si, lors de l’ouverture de la connexion, elle a été négociée via l’option dédiée SACK-PERMITTED.
Exemple de trace avec un segment avec plusieurs options
Pour illustrer les différentes options, voici l’en-tête TCP d’un segment qui a été capturé avec Wireshark. Afin d’identifier un segment avec plusieurs options, le filtre suivant a été utilisé :
tcp.options.timestamp && tcp.options.wscale && tcp.options.mss
Le paquet capturé est un paquet avec les flags SYN, ACK, appartenant au three-way-handshake d’une nouvelle connexion. On peut constater qu’en plus des trois options MSS, Window Scale et Timestamp, l’option SACK-permitted est utilisée par le serveur. Cela signifie qu’il est prêt à accepter les “selective acknowledgments” (option SACK) lors du transfert de données.
En récapitulant, nous avons donc :
- L’option Maximum Segment Size avec une valeur de 1360 bytes. L’option prend 4 octets.
- L’option SACK permitted, pour activer les SACK pour la suite de l’échange. Cette option prend 2 octets.
- L’option Timestamps, qui convient les valeurs TSval et TSecr en cours. Elle occupe 10 octets.
- L’option NOP (1 octet) comme padding
- L’option Window Scale, avec une valeur 8 indiquant un facteur multiplicatif de 2^8 (256) de la fenêtre TCP de base. Cette option comporte 3 octets.
- L’ensemble des options occupent un total de 20 octets, ce qui donne un en-tête de 40 octets. Cela correspond à la valeur “Data Offset” de 10 (1010).
Voici le schéma correspondant à cet en-tête IP. Les 5 lignes indiquées par des astérisques correspondent aux 20 octets d’en-tête.
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Port = 993 | Destination Port = 64937 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sequence Number = 2400152917 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Acknowledgment Number = 3217521636 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data | |U|A|P|R|S|F| |
| Offset| Reserved |R|C|S|S|Y|I| Window |
| 1010 | |G|K|H|T|N|N| = 65535 |
| (=10) | |0|1|0|0|1|0|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Checksum = 0x15e9 | Urgent Pointer = 0 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*| MSS : Kind = 2, Len = 4, value = 1360 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*| SACK perm. Kind = 4, Len = 2 | TS : Kind = 8, Len = 10 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*| Suite TS : TSval |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*| Suite TS : TSecr |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*| NOP (Len = 1) | Window Scale : Kind = 3, Len = 3, val. = 8 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| DATA |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+