TCP & UDP
UDP : User Datagram Protocol
Défini dans la RFC 768 le 28/8/1980, qui n'as pas été modifiée depuis, contrairement à tous les autres protocoles qui ont reçu beaucoup de correctifs.
Avec IP, on dialogue entre machines... Avec UDP, on dialogue entre processus : on ajoute un port source et un port de destination.
La notion de port introduit la notion de "service réseau" : Il peut y avoir plusieurs services réseau par équipement. Chaque service réseau écoute sur un port différent, et les services peuvent être totalement différent, et chacun écoute sur un port spécifique. UDP se place au-dessus d'IP, et son format de datagramme est le suivant:
- Un port source optionnel (0 par défaut)
- Un port de destination
- Une longueur de datagramme (en-tête + Données)
- Un checksum
- Des données.
Les données utiles sont générées par l'application; elles sont encapsulées dans UDP. On voit apparaître la notion d'extrémité de communication. Une extrémité de communication est : un tuple (@IP, Port UDP) (un socket, quoi...). Un port est l'équivalent d'une porte dans une maison; on peut avoir plusieurs portes sur un bâtiment.
Un serveur UDP est un tuple "@IP + port UDP" côté serveur, et la même chose pour un client UDP. Le client va ouvrir un port UDP (ou pas, c'est optionnel : il ne le fait que si il veut un réponse; si il n'ouvre pas de port le port source sera à 0) et envoyer des datagrammes au serveur UDP, qui répondra sur le port que le client a ouvert.
Le checksum (ou somme de contrôle) est réalisé lors de l'envoi et de la réception du datagramme. Si le checksum présent est différent du checksum fait à l'arrivée, la trame est détruite. Il est réalisé sur l'en-tête UDP + une pseudo-en-tête IP ainsi que les données, ce qui ne colle pas trop au modèle OSI. À noter : si le checkzsum est optionnel en IPv4, il est obligatoire en IPv6, car IPv6 n'as lui-même pas de checksum.
Lorsqu'une trame UDP est émise, on a pas d'A/R, cela doit être contrôlé par les couches d'au-dessus. On a pas de réémission en cas de perte ni de gestion de la congestion. UDP est utilisé lorsque l'on a peu de données à transporter, et avec peu d'échanges.
Les protocoles UDP les plus connus sont :
- DHCP
- DNS
- SNMP
- TFTP
- Les jeux en réseau
- SIP
TCP (Transmission Control Protocol)
Il représente 95% du traffic.
Tcp est un protocole complexe, avec beaucoup de choses à voir. Il a été devisé la première fois dans la RFC 793. Il se place au-dessus d'IP; Il envoie ses données à IP et se place à côté d'UDP.
Les points communs avec UDP :
- Les extremités de connexion : IP:PORT (source et destination)
- Un port TCP et un port UDP sont différents !
- Checksum : toujours sur une partie de l'en-tête IP+En-tête TCP+Données TCP. Il est mis dans une case et la destination revérifie le checksum à la réception.
- Encapsulation
Les différences essentielles:
- Mode connecté, avec différents statuts de la connexion
- 1e phase : établissement de la connexion
- 2e phase : Échange de données
- 3e phase : Terminaison de la connexion
- Gestion des pertes de données
- Gestion de l'ordre des segments
- Contrôle de flux
- Contrôle de congestion
On voit que le header est plus complexe qu'UDP, mais on a des élements communs : les ports, taille, checksum.
Établissement d'une connexion TCP
TCP utilise le three-way-handshake : connexion en 3 temps. Le serveur doit d'abord ouvrir un port TCP; le client passera ensuite par différents statuts. Il est important de comprendre le three-way-handshake pour pouvoir comprendre les liaisons qui ne s'établissent pas à cause d'un problème de filtrage réseau/sécurité (VPN, obligation d'utiliser un proxy pour https...).
On voit bien le SYN, SYNACK, ACK, ainsi que les changements d'états du client et du serveur au niveau des sockets.
Cependant, il faut des choses avant ce handshake :
- Le serveur ouvre un port (bind)
- Le serveur écoute sur ce port(listen)
- Le serveur accepte les connexions sur ce port(accept)
Sinon, le client aura droit à "connection refused".
On a donc trois échanges de synchronisation : aucune donnée n'est envoyée pendant le handshake (contrairement à UDP où on envoie les données tout de suite).
Avec un filtrage intermédiraire, on peut avoir:
- SYN envoyé mais pas reçu
- synack envoyé mais pas reçu (plus compliqué...)
- ACK non reçu.
Les flags TCP (ou drapeaux)
TCP SYN, ACK... sont en fait des flags. Positionné = 1.
Un drapeau est un bit de l'en-tête, actif si il est à 1 ("Drapeau x positionné") inactif si il est à 0 ("Drapeau X non positionné"). Les drapeaux sont utilisés pour gérer la connexion; plusieurs drapeaux peuvent être actifs en même temps (exemple : SYN + ACK). Un paquet peut transporter des données et avoir des flags à 1 : on a qu'un seul canal pour les données et la synchronisation.
Les drapeaux:
- U : Urgent, paquet prioritaire
- A : ACK, pour acquitter des paquets reçus précédemment (peut aussi être indique avec . : R. signifie RESET + ACK)
- P: PUSH, envoyer les données immédiatement à la couche supérieure sans attendre le reste des données dans le buffer. Par exemple, cela aide dans telnet à éviter la latence.
- R : RESET, coupure de connexion (ou refus) : connection closed, R est immédiat!
- F : FIN, demande de fin de connexion (plus "sympa" que R)
Numéros de séquence
Pour que je sache si les données sont reçues, et qu'elles soient bien réassemblées dans le bon ordre, TCP utilise plusieurs mécanismes. Une fois le three-way-handshake passé, on peut commencer à envoyer les données; on utilise alors des numéros de séquence:
- Il servent à s'assurer de l'ordre des paquets
- Chaque paquet en a un
- Le numéro de séquence est spécifique à un sens de transmission : dans une communication entre A et B, les paquets dans le sens A -> B auront des numéros de séquence successifs (1, 2, 3, 4...) et les paquets de B vers A auront aussi des numéros de séquence successifs (1, 2, 3, 4). Chaque sens de la connexion a son propre séquençage.
Les segments ne sont pas numérotés 1, 2, 3, 4... en réalité :
- On commence par Seq=1 pour le premier segment de données (Une fois qu'on a recu le SYN + ACK !)
- Ensuite, Seqi = (Seqi-1 + data length Seqi-1).
- Le deuxième segment envoyé aura pour numéro de segment 1 + la longueur de données (données uniquement !) du premier segment
- Et ainsi de suite.
Les numéros de segment commencent au premier octet transmis :
- SYN : séquence = 0
- Syn+ACK : séquence = 1
- ACK + DATA : séquence = 1
Numéros d'acquittement
Il servent à acquitter la réception des paquets. Si le flag ACK est activé, le paquet a un numéro d'acquittement : celui-ci est spécifique à un sens de transmission. Si j'acquitte un segement, avec un numéro d'acquittement X, alors cela veut dire "J'ai reçu tous les paquets précédents, jusqu'à X-1; envoie les données à partir de X". On acquitte les paquets précédents, c'est-à-dire les données reçues. Il n'est cependant pas obligatoire d'envoyer un acquittement pour chaque donnée reçue; on améliore les performances en envoyant moins d'ACK.
Exemple:
A envoie un paquet P, avec un numéro de séquence S et un volume de données V. Lorsque B va acquitter les données reçues par A, il va prendre SON PROPRE numéro de séquence pour l'envoi, et son acquittement sera : ACK == S+V+1.
Optimisation du transfert de données
TCP a besoin d'optimiser le transfert de données pour améliorer les perfs; les contrôles qu'il fait peuvent diminuer les performances, sinon.
Si l'on prend le temps d'acquitter tous les segments, on va perdre du temps à chaque paquet à acquitter tous les segments. C'est pour cela que l'on acquitte pas en permanence : on optimise la bande passante en évitant les attentes inutiles.
Quel est le meilleur paramètre à définir par défaut quant à l'attente avant d'envoyer des acquittements? Au bout de combien de temps considérer un segment comme perdu? Combien de temps avant de considérer l'acquittement comme non émis? Il n'y a pas de meilleur paramètre ! Cela dépend de:
- Des extrémités
- De l'utilisation des données transférées
- Du type de liaisons
- De la qualité de celle-ci
- Des intermédiaires
- etc...
TCP s'adapte : il commence par adapter les paramètres lors du 3-way-handshake, et adapte les paramètres en cours de communication:
- Si la liaison est stable, on augmente les perfs si possible
- Sinon, on adaptera le volume de données échangées en fonction.
Tout ça, c'est le gestion de flux et la gestion de congestion; c'est à la fois une difficulté et une force de TCP, qui pourra continuer à évoluer.
La gestion de flux, c'est gérer l'envoi de données par rapport à l'extrémité qui se trouve en face; chauq eextrémité a un tampon de réception et un temps de traitement de celui-ci, et il ne faut pas dépasser ses limites pour éviter les pertes de segments.
La gestion de congestion se fait par rapport au réseau : le réseau ne peut faire transiter qu'une certaine quantité d'informations, et il ne faut pas le saturer pour éviter la destruction intermédiaire de segments.
Gestion de flux et de congestion plus en détail
Principe général de ces gestions:
- Au début on ne connaît pas
- ON augmente les performances rapidement pour voir le point de rupture
- On affine ensuite pour éviter de trop perdre
- On réitère
C'est l'algorithme "slow start" : on commence doucement et on apprend à se connaitre pour augmenter rapidement.
Descritpion de l'algo:
- On envoie qu'un seul segment à la fois
- On analyse les RTT (Round Trip Time)
- On augment (vite) la valeur du paramètre cwnd (Congestion Window)
- On ne dépasse pas un paramètre ssthresh (Slow Start treshold, qui est la taille maximum d'une fenêtre cwnd en slow start)
- Lorsque on dépasse ssthresh, on passe à un autre algorithme
- Si perte pendant Slow Start :
- ssthresh = cwnd /2
- cwnd = 1
- Cela veut dire : "On est allés trop vite, on va augmenter moins vite". En divisant par deux la taille maximale, on va passer à l'aglorithme suivant pour augmenter moins rapidement (le deuxième algo a une courbe de progression de la vitesse plus basse) : "Congestion Avoidance":
- Débute au dépassement du sstresh
- Augmentation moins rapide du cw que SlowStart
- Augmentation d'un seul segment à chaque acquittement
- Si perte pendant Congestion Avoidance :
- sshtresh = cwnd /2 ("On a déjà une bonne connaissance du réseau, il est au moins aussi rapide que la moitié de ce que l'on est arrivé")
- On va alors entrer en Fast Retransmit:
- Le but est de détecter rapidement la perte du paquet et le retransmettre
- Il se déclenche en cas de perte de segment, ou en cas d'arrivée de 3 segments dans le désordre pour avertir l'émetteur
- On retransmet le paquet sans attendre le RTO (l'acquittement)
- C'est comme du FEC
- On passe en Fast Recovery:
- Son but est de revenir vite à un niveau optimal de transmission, Description :
- sshtresh = cwnd /2 ("Au moins aussir apide que la moitié d'avant")
- cwnd = sstresh + 3 ("Ce n'était pas aussi lent avant la perte, pas de raison")
- renvoyer le segment perdu
- si ACK, revenir en Congestion Avoidance
- si TimeOut, revenir en Slow Start
Ouf !
Ce sont les principaux algorithmes mis en place dans les OS, avec d'autres : les algos des OS les combinent avec des valeurs de base différentes, des fonctions différentes d'augmentation/réduction des valeurs. Sur Linux, on a CUBIC une amélioration de BIC; sur Windows on a Combound TCP (ou CTCP). Il vaut mieux éviter de changer les paramètres du kernel !