6. Détection de perte (Loss Detection)
Un expéditeur QUIC utilise les accusés de réception (acknowledgments) pour détecter les paquets perdus, et un PTO pour s'assurer que les accusés de réception sont reçus ; voir Section 6.2. Cette section fournit une description de ces algorithmes.
Si un paquet est perdu, le transport QUIC doit récupérer de cette perte, par exemple en retransmettant les données, en envoyant une trame mise à jour ou en abandonnant la trame. Pour plus d'informations, voir la Section 13.3 de [QUIC-TRANSPORT].
La détection de perte (loss detection) est séparée par espace de numéro de paquet (per packet number space), contrairement à la mesure RTT et au contrôle de congestion, car le RTT et le contrôle de congestion sont des propriétés du chemin, tandis que la détection de perte dépend également de la disponibilité des clés.
6.1. Détection basée sur les accusés de réception (Acknowledgment-Based Detection)
La détection de perte basée sur les accusés de réception implémente l'esprit de la retransmission rapide (Fast Retransmit) de TCP [RFC5681], de la retransmission précoce (Early Retransmit) [RFC5827], de l'accusé de réception anticipé (Forward Acknowledgment) [FACK], de la récupération de perte SACK [RFC6675] et de RACK-TLP [RFC8985]. Cette section fournit un aperçu de la manière dont ces algorithmes sont implémentés dans QUIC.
Un paquet est déclaré perdu s'il remplit toutes les conditions suivantes :
-
Le paquet n'est pas accusé réception, en vol (in flight), et a été envoyé avant un paquet accusé réception.
-
Le paquet a été envoyé
kPacketThresholdpaquets avant un paquet accusé réception (Section 6.1.1), ou il a été envoyé il y a suffisamment longtemps (Section 6.1.2).
L'accusé de réception indique qu'un paquet envoyé plus tard a été livré, et les seuils de paquet et de temps fournissent une certaine tolérance pour la réorganisation des paquets.
Déclarer de manière erronée des paquets comme perdus conduit à des retransmissions inutiles et peut entraîner une dégradation des performances en raison des actions du contrôleur de congestion lors de la détection de perte. Les implémentations peuvent détecter les retransmissions erronées et augmenter le seuil de réorganisation de paquet ou de temps pour réduire les futures retransmissions erronées et les événements de perte. Les implémentations avec des seuils de temps adaptatifs PEUVENT choisir de commencer avec des seuils de réorganisation initiaux plus petits pour minimiser la latence de récupération.
6.1.1. Seuil de paquet (Packet Threshold)
La valeur initiale RECOMMANDÉE pour le seuil de réorganisation de paquet (kPacketThreshold) est 3, basée sur les meilleures pratiques pour la détection de perte TCP [RFC5681] [RFC6675]. Afin de rester similaire à TCP, les implémentations NE DEVRAIENT PAS utiliser un seuil de paquet inférieur à 3 ; voir [RFC5681].
Certains réseaux peuvent présenter des degrés plus élevés de réorganisation de paquets, provoquant la détection par l'expéditeur de pertes erronées. De plus, la réorganisation de paquets pourrait être plus courante avec QUIC qu'avec TCP car les éléments de réseau qui pourraient observer et réorganiser les paquets TCP ne peuvent pas le faire avec QUIC et parce que les numéros de paquet QUIC sont chiffrés. Les algorithmes qui augmentent le seuil de réorganisation après avoir détecté de manière erronée une perte, comme RACK [RFC8985], se sont révélés utiles dans TCP et devraient être au moins aussi utiles dans QUIC.
6.1.2. Seuil de temps (Time Threshold)
Une fois qu'un paquet ultérieur dans le même espace de numéro de paquet a été accusé réception, un terminal DEVRAIT déclarer un paquet antérieur perdu s'il a été envoyé il y a un certain temps seuil. Pour éviter de déclarer les paquets perdus trop tôt, ce seuil de temps DOIT être défini au moins à la granularité du temporisateur local, comme indiqué par la constante kGranularity. Le seuil de temps est :
max(kTimeThreshold * max(smoothed_rtt, latest_rtt), kGranularity)
Si les paquets envoyés avant le paquet accusé réception le plus grand ne peuvent pas encore être déclarés perdus, alors un temporisateur DEVRAIT être défini pour le temps restant.
L'utilisation de max(smoothed_rtt, latest_rtt) protège contre les deux cas suivants :
-
le dernier échantillon RTT est inférieur au RTT lissé, peut-être en raison d'une réorganisation où l'accusé de réception a rencontré un chemin plus court ;
-
le dernier échantillon RTT est supérieur au RTT lissé, peut-être en raison d'une augmentation soutenue du RTT réel, mais le RTT lissé n'a pas encore rattrapé.
Le seuil de temps RECOMMANDÉ (kTimeThreshold), exprimé en tant que multiplicateur RTT, est 9/8. La valeur RECOMMANDÉE de la granularité du temporisateur (kGranularity) est 1 milliseconde.
Note : Le RACK de TCP [RFC8985] spécifie un seuil légèrement plus grand, équivalent à 5/4, dans un but similaire. L'expérience avec QUIC montre que 9/8 fonctionne bien.
Les implémentations PEUVENT expérimenter avec des seuils absolus, des seuils de connexions précédentes, des seuils adaptatifs ou des seuils qui incluent la variation RTT. Des seuils plus petits réduisent la résilience à la réorganisation et augmentent les retransmissions erronées, et des seuils plus grands augmentent le délai de détection de perte.
6.2. Temporisation de sonde (Probe Timeout)
Une temporisation de sonde (PTO) déclenche l'envoi d'un ou deux datagrammes de sonde lorsque les paquets sollicitant un accusé de réception (ack-eliciting packets) ne sont pas accusés réception dans la période de temps prévue ou que le serveur peut ne pas avoir validé l'adresse du client. Un PTO permet à une connexion de récupérer de la perte de paquets de queue ou d'accusés de réception.
Comme pour la détection de perte, le PTO est par espace de numéro de paquet (per packet number space). C'est-à-dire qu'une valeur PTO est calculée pour chaque espace de numéro de paquet.
Un événement d'expiration du temporisateur PTO n'indique pas une perte de paquet et NE DOIT PAS entraîner le marquage comme perdus des paquets antérieurs non accusés réception. Lorsqu'un accusé de réception est reçu pour un paquet nouvellement sollicité, la détection de perte se poursuit comme dicté par les mécanismes de seuil de paquet et de temps ; voir Section 6.1.
L'algorithme PTO utilisé dans QUIC implémente les fonctions de fiabilité de la sonde de perte de queue (Tail Loss Probe) [RFC8985], RTO [RFC5681] et des algorithmes F-RTO pour TCP [RFC5682]. Le calcul du délai d'expiration est basé sur la période RTO de TCP [RFC6298].
6.2.1. Calcul du PTO (Computing PTO)
Lorsqu'un paquet sollicitant un accusé de réception est transmis, l'expéditeur planifie un temporisateur pour la période PTO comme suit :
PTO = smoothed_rtt + max(4*rttvar, kGranularity) + max_ack_delay
La période PTO est la durée pendant laquelle un expéditeur devrait attendre un accusé de réception d'un paquet envoyé. Cette période de temps inclut le RTT réseau estimé (smoothed_rtt), la variation de l'estimation (4*rttvar), et max_ack_delay, pour tenir compte du temps maximum par lequel un récepteur pourrait retarder l'envoi d'un accusé de réception.
Lorsque le PTO est armé pour les espaces de numéro de paquet Initial ou Handshake, le max_ack_delay dans le calcul de la période PTO est défini à 0, car le pair est censé ne pas retarder ces paquets intentionnellement ; voir la Section 13.2.1 de [QUIC-TRANSPORT].
La période PTO DOIT être au moins kGranularity pour éviter que le temporisateur n'expire immédiatement.
Lorsque des paquets sollicitant un accusé de réception sont en vol dans plusieurs espaces de numéro de paquet, le temporisateur DOIT être défini à la valeur la plus précoce des espaces de numéro de paquet Initial et Handshake.
Un terminal NE DOIT PAS définir son temporisateur PTO pour l'espace de numéro de paquet de données d'application tant que la poignée de main n'est pas confirmée. Cela empêche le terminal de retransmettre des informations dans des paquets lorsque soit le pair n'a pas encore les clés pour les traiter, soit le terminal n'a pas encore les clés pour traiter leurs accusés de réception. Par exemple, cela peut se produire lorsqu'un client envoie des paquets 0-RTT au serveur ; il le fait sans savoir si le serveur pourra les déchiffrer. De même, cela peut se produire lorsqu'un serveur envoie des paquets 1-RTT avant de confirmer que le client a vérifié le certificat du serveur et peut donc lire ces paquets 1-RTT.
Un expéditeur DEVRAIT redémarrer son temporisateur PTO chaque fois qu'un paquet sollicitant un accusé de réception est envoyé ou accusé réception, ou lorsque les clés Initial ou Handshake sont supprimées (Section 4.9 de [QUIC-TLS]). Cela garantit que le PTO est toujours défini en fonction de l'estimation la plus récente du RTT et pour le paquet correct à travers les espaces de numéro de paquet.
Lorsqu'un temporisateur PTO expire, le recul PTO DOIT être augmenté, entraînant la définition de la période PTO au double de sa valeur actuelle. Le facteur de recul PTO est réinitialisé lorsqu'un accusé de réception est reçu, sauf dans le cas suivant. Un serveur peut prendre plus de temps pour répondre aux paquets pendant la poignée de main que normalement. Pour protéger un tel serveur des sondes client répétées, le recul PTO n'est pas réinitialisé sur un client qui n'est pas encore certain que le serveur a terminé de valider l'adresse du client. C'est-à-dire qu'un client ne réinitialise pas le facteur de recul PTO lors de la réception d'accusés de réception dans des paquets Initial.
Cette réduction exponentielle du débit de l'expéditeur est importante car des PTO consécutifs peuvent être causés par la perte de paquets ou d'accusés de réception en raison d'une congestion sévère. Même lorsqu'il y a des paquets sollicitant un accusé de réception en vol dans plusieurs espaces de numéro de paquet, l'augmentation exponentielle du PTO se produit dans tous les espaces pour éviter une charge excessive sur le réseau. Par exemple, un délai d'expiration dans l'espace de numéro de paquet Initial double la longueur du délai d'expiration dans l'espace de numéro de paquet Handshake.
La durée totale pendant laquelle des expirations PTO consécutives se produisent est limitée par le délai d'inactivité (idle timeout).
Le temporisateur PTO NE DOIT PAS être défini si un temporisateur est défini pour la détection de perte de seuil de temps ; voir Section 6.1.2. Un temporisateur défini pour la détection de perte de seuil de temps expirera plus tôt que le temporisateur PTO dans la plupart des cas et est moins susceptible de retransmettre des données de manière erronée.
6.2.2. Poignées de main et nouveaux chemins (Handshakes and New Paths)
Les connexions reprises sur le même réseau PEUVENT utiliser la valeur RTT lissée finale de la connexion précédente comme RTT initial de la connexion reprise. Lorsqu'aucun RTT précédent n'est disponible, le RTT initial DEVRAIT être défini à 333 millisecondes. Cela conduit à des poignées de main commençant avec un PTO de 1 seconde, comme recommandé pour le RTO initial de TCP ; voir la Section 2 de [RFC6298].
Une connexion PEUT utiliser le délai entre l'envoi d'un PATH_CHALLENGE et la réception d'un PATH_RESPONSE pour définir le RTT initial (voir kInitialRtt dans l'Annexe A.2) pour un nouveau chemin, mais le délai NE DEVRAIT PAS être considéré comme un échantillon RTT.
Lorsque les clés Initial et Handshake sont supprimées (voir Section 6.4), tous les paquets Initial et Handshake ne peuvent plus être accusés réception, ils sont donc retirés des octets en vol (bytes in flight). Les temporisateurs PTO et de détection de perte DOIVENT être réinitialisés, car la suppression des clés indique un progrès et le temporisateur de détection de perte pourrait avoir été défini pour un espace de numéro de paquet maintenant supprimé.
6.2.2.1. Avant la validation d'adresse (Before Address Validation)
Jusqu'à ce que le serveur ait validé l'adresse du client sur le chemin, la quantité de données qu'il peut envoyer est limitée à trois fois la quantité de données reçues, comme spécifié dans la Section 8.1 de [QUIC-TRANSPORT]. Si aucune donnée supplémentaire ne peut être envoyée, le temporisateur PTO du serveur NE DOIT PAS être armé jusqu'à ce que des datagrammes aient été reçus du client, car les paquets envoyés sur PTO comptent pour la limite anti-amplification.
Lorsqu'un serveur reçoit un datagramme du client, la limite d'amplification est augmentée et le serveur réinitialise le temporisateur PTO. Si le temporisateur PTO est alors défini à un moment dans le passé, il est exécuté immédiatement. Cela évite d'envoyer de nouveaux paquets 1-RTT avant les paquets critiques à l'achèvement de la poignée de main. En particulier, cela peut se produire lorsque 0-RTT est accepté mais que le serveur ne parvient pas à valider l'adresse du client.
Étant donné que le serveur pourrait être bloqué jusqu'à ce que davantage de datagrammes soient reçus du client, il est de la responsabilité du client d'envoyer des paquets pour débloquer le serveur jusqu'à ce qu'il soit certain que le serveur a terminé sa validation d'adresse (voir la Section 8 de [QUIC-TRANSPORT]). C'est-à-dire que le client DOIT définir le temporisateur PTO si le client n'a pas reçu d'accusé de réception pour l'un de ses paquets Handshake et que la poignée de main n'est pas confirmée (voir la Section 4.1.2 de [QUIC-TLS]), même s'il n'y a pas de paquets en vol. Lorsque le PTO se déclenche, le client DOIT envoyer un paquet Handshake s'il a des clés Handshake, sinon il DOIT envoyer un paquet Initial dans un datagramme UDP avec une charge utile d'au moins 1200 octets.
6.2.3. Accélération de l'achèvement de la poignée de main (Speeding up Handshake Completion)
Lorsqu'un serveur reçoit un paquet Initial contenant des données CRYPTO en double, il peut supposer que le client n'a pas reçu toutes les données CRYPTO du serveur envoyées dans les paquets Initial, ou que le RTT estimé du client est trop petit. Lorsqu'un client reçoit des paquets Handshake ou 1-RTT avant d'obtenir les clés Handshake, il peut supposer que certains ou tous les paquets Initial du serveur ont été perdus.
Pour accélérer l'achèvement de la poignée de main dans ces conditions, un terminal PEUT, pour un nombre limité de fois par connexion, envoyer un paquet contenant des données CRYPTO non accusées réception plus tôt que l'expiration PTO, sous réserve des limites de validation d'adresse dans la Section 8.1 de [QUIC-TRANSPORT]. Le faire au plus une fois par connexion est adéquat pour récupérer rapidement d'une perte de paquet unique. Un terminal qui retransmet toujours des paquets en réponse à la réception de paquets qu'il ne peut pas traiter risque de créer un échange infini de paquets.
Un terminal peut également utiliser des paquets coalescés (voir la Section 12.2 de [QUIC-TRANSPORT]) pour s'assurer que chaque datagramme suscite au moins un accusé de réception. Par exemple, un client peut coalescer un paquet Initial contenant des trames PING et PADDING avec un paquet de données 0-RTT, et un serveur peut coalescer un paquet Initial contenant une trame PING avec un ou plusieurs paquets dans son premier vol.
6.2.4. Envoi de paquets de sonde (Sending Probe Packets)
Lorsqu'un temporisateur PTO expire, un expéditeur DOIT envoyer au moins un paquet sollicitant un accusé de réception dans l'espace de numéro de paquet en tant que sonde. Un terminal PEUT envoyer jusqu'à deux datagrammes de taille complète contenant des paquets sollicitant un accusé de réception pour éviter une expiration PTO consécutive coûteuse due à un seul datagramme perdu, ou pour transmettre des données de plusieurs espaces de numéro de paquet. Tous les paquets de sonde envoyés sur un PTO DOIVENT solliciter un accusé de réception.
En plus d'envoyer des données dans l'espace de numéro de paquet pour lequel le temporisateur a expiré, l'expéditeur DEVRAIT envoyer des paquets sollicitant un accusé de réception d'autres espaces de numéro de paquet avec des données en vol, en coalescant les paquets si possible. Ceci est particulièrement précieux lorsque le serveur a à la fois des données Initial et Handshake en vol ou lorsque le client a à la fois des données Handshake et Application en vol, car le pair pourrait n'avoir que des clés de réception pour l'un des deux espaces de numéro de paquet.
Si l'expéditeur souhaite susciter un accusé de réception plus rapide sur PTO, il peut sauter un numéro de paquet pour éliminer le délai d'accusé de réception.
Un terminal DEVRAIT inclure de nouvelles données dans les paquets envoyés lors de l'expiration PTO. Les données précédemment envoyées PEUVENT être envoyées si aucune nouvelle donnée ne peut être envoyée. Les implémentations PEUVENT utiliser des stratégies alternatives pour déterminer le contenu des paquets de sonde, y compris l'envoi de données nouvelles ou retransmises en fonction des priorités de l'application.
Un expéditeur peut n'avoir aucune donnée nouvelle ou précédemment envoyée à envoyer. Par exemple, considérez la séquence d'événements suivante : de nouvelles données d'application sont envoyées dans une trame STREAM, considérées comme perdues, puis retransmises dans un nouveau paquet, puis la transmission originale est accusée réception. Lorsqu'il n'y a pas de données à envoyer, l'expéditeur DEVRAIT envoyer une trame PING ou une autre trame sollicitant un accusé de réception dans un seul paquet, en réarmant le temporisateur PTO.
Alternativement, au lieu d'envoyer un paquet sollicitant un accusé de réception, l'expéditeur PEUT marquer tous les paquets encore en vol comme perdus. Cela évite d'envoyer un paquet supplémentaire, mais augmente le risque de déclarer de manière erronée des paquets comme perdus, entraînant une réduction de débit inutile par le contrôleur de congestion.
Les périodes PTO consécutives augmentent exponentiellement, et en conséquence, la latence de récupération de connexion augmente exponentiellement à mesure que les paquets continuent d'être abandonnés dans le réseau. L'envoi de deux paquets lors de l'expiration PTO augmente la résilience aux abandons de paquets, réduisant ainsi la probabilité d'événements PTO consécutifs.
Lorsque le temporisateur PTO expire plusieurs fois et que de nouvelles données ne peuvent pas être envoyées, les implémentations doivent choisir entre envoyer la même charge utile à chaque fois ou envoyer des charges utiles différentes. Envoyer la même charge utile peut être plus simple et garantit que les trames de priorité la plus élevée arrivent en premier. Envoyer des charges utiles différentes à chaque fois réduit les chances de retransmission erronée.
6.3. Gestion des paquets Retry (Handling Retry Packets)
Un paquet Retry amène un client à envoyer un autre paquet Initial, redémarrant effectivement le processus de connexion. Un paquet Retry indique que le paquet Initial a été reçu mais non traité. Un paquet Retry ne peut pas être traité comme un accusé de réception, car il n'indique pas qu'un paquet a été traité ou ne spécifie pas le numéro de paquet.
Les clients qui reçoivent un paquet Retry réinitialisent l'état de contrôle de congestion et de récupération de perte, y compris la réinitialisation de tous les temporisateurs en attente. D'autres états de connexion, en particulier les messages de poignée de main cryptographique, sont conservés ; voir la Section 17.2.5 de [QUIC-TRANSPORT].
Le client PEUT calculer une estimation RTT au serveur comme la période de temps entre l'envoi du premier paquet Initial et la réception d'un paquet Retry ou de négociation de version. Le client PEUT utiliser cette valeur à la place de sa valeur par défaut pour l'estimation RTT initiale.
6.4. Suppression des clés et de l'état des paquets (Discarding Keys and Packet State)
Lorsque les clés de protection de paquet Initial et Handshake sont supprimées (voir la Section 4.9 de [QUIC-TLS]), tous les paquets envoyés avec ces clés ne peuvent plus être accusés réception car leurs accusés de réception ne peuvent pas être traités. L'expéditeur DOIT supprimer tout l'état de récupération associé à ces paquets et DOIT les retirer du décompte des octets en vol (bytes in flight).
Les terminaux arrêtent d'envoyer et de recevoir des paquets Initial une fois qu'ils commencent à échanger des paquets Handshake ; voir la Section 17.2.2.1 de [QUIC-TRANSPORT]. À ce stade, l'état de récupération pour tous les paquets Initial en vol est supprimé.
Lorsque 0-RTT est rejeté, l'état de récupération pour tous les paquets 0-RTT en vol est supprimé.
Si un serveur accepte 0-RTT, mais ne met pas en mémoire tampon les paquets 0-RTT qui arrivent avant les paquets Initial, les premiers paquets 0-RTT seront déclarés perdus, mais cela devrait être peu fréquent.
Il est prévu que les clés soient supprimées à un moment donné après que les paquets chiffrés avec elles aient été accusés réception ou déclarés perdus. Cependant, les secrets Initial et Handshake sont supprimés dès que les clés Handshake et 1-RTT sont prouvées disponibles à la fois pour le client et le serveur ; voir la Section 4.9.1 de [QUIC-TLS].