Auteur Sujet: Timestamps - Horodatage TCP : Bug dans la pile TCP/IP de Windows ?  (Lu 7866 fois)

0 Membres et 1 Invité sur ce sujet

vivien

  • Administrateur
  • *
  • Messages: 47 287
    • Twitter LaFibre.info
Timestamps - Horodatage TCP

La RFC1323 (https://www.ietf.org/rfc/rfc1323.txt) décide les règles a suivre pour activer les Timestamps (Horodatage TCP). Le problème c'est que Microsoft et Linux n’interprètent pas de la même façon la RFC, ce qui empêche leur utilisation sur le cas le plus fréquent rencontré aujourd’hui : un serveur Linux et un client Windows.

Pour ceux qui lisent ce post et qui ne savent pas ce que c'est que Timestamps, je fais une explication rapide : Chaque paquet est numéroté et si il est perdu, il demande le renvoi de tous les paquets à partir du n° x (ou ne demande de renvoyer que le n° x si l'option SACK est activé). Le N° est codé sur 16bits donc 65535 possibilités. Le N° reviens donc a 0 tous les 65535. Avec des grandes Rwin, il y a risque d'avoir le même N° de paquet 2 fois. En cas d'erreur, il ne sauras donc pas quel paquet renvoyer. Les timestamps sont la pour résoudre ce problème. Ils sont donc indispensable pour les grandes Rwin, donc pour les connexions FTTH et satellites.

Le problème, c'est que les clients Windows font une demande de timestamp ne repectant pas strictement la RFC1323.
Dans les paquets initiants une connexion (flag SYN seul monté), on ne se préoccupe pas de la valeur de TSecr, mais uniquement de la valeur de TSval), Ensuite, la valeur de TSval est recopiée dans TSecr, lors de la réponse (flag ACK seul monté), or il est dit que Tsecr n'est pas valide si sa valeur est de zéro. "When TSecr is not valid, its value must be zero."
Windows XP mettant 0 TSval, on se retrouve avec 0 dans TSrec, du coup, la gestion des timestamps n'est pas activée avec les systèmes qui respectent la RFC (linux).

Un exemple avec un client linux : Tsval = 458372 et non 0
On voit que dans les paquets derrière les timestamps sont activés (les valeurs Tsval et Tsecr s'incrèmentent)



On voit que la Rwin du client est de 5792 au démarrage et qu'il va l'augmenter au fur et a mesure a chaque acquittement. (rwin adaptative)

Si une personne arrive a activer les timestamps avec windows correctement (en suivant la RFC, pas de TSval 0, TSecr 0 lors du SYN). Je suis preneur, je n'ai pas réussi à trouver comment faire.
Il est possible de s'entraîner sur lafibre.info, c'est un linux et il refuse les timestamps activé avec TSval 0, TSecr 0.

Sur cette capture, on voit clairement une activation des Timestamps avec TSval 0, TSecr 0 lors du SYN, ce qui est compris par linux comme une désactivation :


Voici le sujet de discussion traité avec Feyb64 sur le forum Debian : http://forum.debian-fr.org/viewtopic.php?p=98862#98862

Citation de: feyb64
Je ne suis pas d'accord avec cette assertion, et je vais vous le démontrer :

Reprenons la partie "litigieuse" de la RFC présenté phrase par phrase pour plus de clarté, et quelques annotations personnelles :

(1) - The Timestamp Value field (TSval) contains the current value of the timestamp clock of the TCP sending the option.
-> rien à dire, clair, précis, limpide.

(2) - The Timestamp Echo Reply field (TSecr) is only valid if the ACK bit is set in the TCP header;
-> TSecr n'est "valide" QUE dans un paquet avec le flag ACK

(3) - if it is valid, it echos a times-stamp value that was sent by the remote TCP in the TSval field of a Timestamps option.
-> C'est la continuation de (2).
On parle toujours de la validité de TSecr, donc, suivant (2) validité de TSecr acquise uniquement SI le TCP Header a le flag ACK.
En aucun cas ici on ne 'valide' ou 'dévalide' TSval reçu dans ce même paquet. On le prend comme il est en vertu de (1).

(4) - When TSecr is not valid, its value must be zero
On parle encore de la validité de TSecr, validité établie ou non encore par (2). En fait il est dit ici en subtance que SI on n'est pas en présence d'un paquet avec le flag ACK, cette valeur doit être mise à zéro (sous entendu, l'èmetteur doit mettre ce champ à zéro)
Encore ici, en aucun cas on ne 'valide' ou 'dévalide' TSval réçu dans ce même paquet. On le prend comme il est en vertu de (1).

J'ai beau relire la RFC je ne vois nul part l'interdiction d'utiliser comme valeur de départ pour TSval dans le SYN "initial" la valeur ZERO.
Je ne vois non plus aucune mention indiquant QUAND TSval doit être considéré comme 'invalide' sur le paquet SYN "initial"
La seule chose qui puisse 'invalider' la valeur de TSval, c'est qu'il soit inférieur à la valeur précédement reçu dans cette même session TCP ("3.4 Which Timestamp to Echo" et suivantes)
Difficile de comparer la valeur "initiale" à celle qui l'a précèdé, cette denière n'existe pas encore ;)

Donc, utiliser ZERO comme valeur initiale dans le paquet SYN (seul) est légal :)

Il semble donc que ce soit la pile TCP/IP de "linux" qui ne soit pas "RFC1323 compliant" en ne voulant pas faire du TimeStamp lorsque l'autre partie met son TSval initial à zéro
(tout au moins lorsque l'autre partie est le client, je n'ai pas encore fait le test inverse, voir si "linux" en tant que client initiateur de la session accepte ou pas un TSval initial dans le SYN du serveur. A vérifier donc aussi)

Ceci est bien sûr mon analyse personnelle de la RFC 1323 et n'engage que moi

Comme vous pouvez le voir, cela date de 2007.

Corrector, si je pouvais avoir ton avis sur ce sujet complexe du respect des RFC entre Windows et Linux  :D

corrector

  • Invité
Timestamps - Horodatage TCP
« Réponse #1 le: 25 mars 2012 à 04:46:00 »
Déjà, avant de regarder la RFC : tu te trompes sur le fonctionnement de TCP.

Citer
Chaque paquet est numéroté
Pour commencer, on parle ici de "segment".

Et non, les segment TCP ne sont ni comptés, ni mémorisés, et le TCP èmetteur n'est pas capable de réèmettre un segment. Seules les données peuvent être réémises.

Citer
Le N° est codé sur 16bits donc 65535 possibilités.
Pas du tout.

Le numéro de séquence est sur 32 bits, on se dit donc qu'il peut coder des taille jusqu'à 4 Go (ce qui n'est en fait pas vrai).

Citer
Le N° reviens donc a 0 tous les 65535. Avec des grandes Rwin, il y a risque d'avoir le même N° de paquet 2 fois.
Le numéro de séquence ne revient jamais "à zéro", parce qu'il n'y a pas de zéro : Seq = 0 est une valeur non distinguée. Oui, vous allez dire : 0 n'est pas distingué, mais Seq = Seq_initial, c'est pas un cas particulier?

Non, ça n'a rien de particulier. En théorie, on ne réutilise jamais des numéros de séquence qui sont en cours de transit. Cela créé donc une limite sur les segments "en vol" : il n'est pas possible d'avoir des segments représentant plus de 4 Go données voyageant en même temps (là on se dit que la limite n'en est pas une, mais ce n'est pas exact).

En fait, c'est plus complexe : les paquets IP peuvent arriver dans le désordre, y compris les réémissions de données TCP, par exemple un cas très simple de paquet qui arrive un peu en retard (je ne représente qu'un demi-TCP, l'envoi de données de A vers B) :

0. B => A [B -> A : Ack (x) ]
B envoie à A un acquittement pour x : les données jusqu'à l'octet x ont été reçues; A reçoit le paquet

1. A => [A -> B : Seq x "blablabla"]
A envoie un paquet  à B contenant un segment de données de numéro de séquence x "blablabla" (9 octets venant après la position x); le paquet n'arrive pas

2. B => A [B -> A : Ack (x) ]
B envoie à A un acquittement indiquant qu'il est toujours à la position x (acquittement dupliqué); A reçoit le paquet

3. A => B [A -> B : Seq x "blablabla"]
A réèmet "blablabla" (à la position x); B reçoit le paquet

B ajoute "blablabla" à son tampon de réception, la position du premier octet à lire passe donc à x+9

4. B => A [B -> A : Ack (x+9) ]
B confirme la réception; A reçoit le paquet

5. => B [A -> B : Seq x "blablabla"]
B reçoit le paquet de l'étape 2; la position actuelle étant x+9 > x, le segment est ignoré

Là l'arrivée du paquet est juste un peu en retard; mais en fait elle peut être bien plus en retard.

Quand on écrit x+9, on peut se demander ce qui se passe si x+9 n'est pas une valeur représentable comme numéro de séquence (et d'acquittement correspondant) sur 32 bits. En fait c'est de arithmétique modulaire : on envoi tout simplement x+9 mod 2**32 (en utilisant un unsigned int en C sur une machine 32 bits, x+9 est automatiquement effectué en arithmétique modulo 2**32). Mais là, on peut se demander ce qui se passe quand on franchit 2**32 - 1, puisqu'en principe, au moins en C : 4294967295U + 9 < 4294967295U (puisque 4294967295 = 2**32-1 et que 2**32-1 + 9 mod 2**32 = 8 < 2**32-1).

C'est que dans ces cas là, il ne faut pas utiliser le < conventionnel, qui commence à 0, mais une comparaison "circulaire"!

Athéisme

Sur un stade, si vous regardez des coureurs qui font une course, vous arrivez généralement à savoir qui est le premier à tout moment en regardant uniquement une photo instantanée de la course, pourtant vous ne savez pas combien de tours ils ont fait en tout, ils tournent en rond, vous ne pensez jamais que le premier est dernier, même quand le premier vient de repasser la ligne de départ et que les poursuivants sont avant la ligne de part; les poursuivants ont pourtant fait beaucoup de distance après la ligne de départ, presque un tour de stade, et le coureur de tête seulement quelques mètres! Tout simplement, vous partez du principe qu'aucun coureur ne peut être tellement en avance qu'il talonne ses poursuivants, ayant presque fait un tour en plus. Autrement dit que différence de distance parcourue par chaque coureur de moins d'un demi tour de stade. Si ce n'est pas le cas, et qu'un coureur va beaucoup plus vite qu'un autre au point qu'il arrive à plus d'un demi tour d'avance, vous ne pouvez pas du tout voir sur une photo instantanée s'il est en premier ou en dernier.)

Pour le segment, c'est la même chose : le stade fait une circonférence de 4 Go; il ne faut pas que le numéro de séquence ait fait un demi tour quand le paquet arrive, soit 2 Go d'avance sur le dernier.

La lettre tombée de la sacoche

Un problème délicat des réseaux par paquets, comme de la Poste, c'est le risque qu'une lettre tombe de la sacoche du facteur, soit retrouvée un mois plus, sont remise dans le circuit et arrive très, très, très en retard.

Si la lettre n'est pas datée, et que le récepteur suppose qu'elle a à peu près l'age habituel des lettre qu'il reçoit, ça peut poser problème, parce qu'il va en tenir compte comme si elle était récente :
- si la lettre fait référence à une affaire déjà terminée, le receveur va se douter que c'est une anomalie.
- si l'affaire référencée est toujours en négociation, le receveur va tenter d'appliquer ce qui est dans la lettre; si les instructions sembles cohérentes, c'est mauvais.

Avec un paquet en retard, c'est pareil : si le segment fait partie d'une connexion TCP toujours en cours, si le numéro de séquence est cohérent ... l'instruction contenues dans le segment seront appliquées; l'instruction d'un segment TCP est tout simplement "note que les données reçues de cette connexion TCP sont à partir de la position Seq : "blablabla". Et là, paf les données.

En principe un réseau ne doit pas garder des paquets pour les ressortir des plombes plus tard, et le TTL est supposé borner le temps de vie d'un paquet : un paquet TTL de 40 n'est pas censé encore se balader dans 40 secondes. En pratique, de nombreux routeurs négligent cette règle et sont capables de conserver un paquet une durée très longue (plus d'une seconde c'est extrêmement long) sans décrèmenter le TTL. (Bon, aucun routeur ne devrait conserver un paquet IP aussi longtemps, mais c'est une autre histoire : les administrateurs de routeurs ont pris la très mauvaise habitude de mettre des gros tampons qui évite les suppressions de paquets, alors que la suppression est très souhaitable dès que les paquets en attente s'accumulent.)

Pour éviter ce problème, IP ne propose aucun mécanisme particulièrement robuste pour limiter le temps de vie des paquets, ni pour détecter les vieux paquets. De mon point de vue c'est une des quelques grosses faiblesses de conception du protocole IP.

TCP a donc développé l'option d'horodatage pour cela (et pour une autre raison, mesurer précisèment le RTT), mais pour moi c'est le genre de fonctionnalités qui a sa place au niveau IP : le principe de l'horodatage a un sens pour n'importe ordonner dans le temps quel paquet provenant d'un même périphérique.

vivien

  • Administrateur
  • *
  • Messages: 47 287
    • Twitter LaFibre.info
Timestamps - Horodatage TCP
« Réponse #2 le: 25 mars 2012 à 09:16:33 »
Effectivement, le Numéro de séquence TCP est sur 32 bit, c'est le champ Identification de la pile IP, qui est lui sur 16 bits et permet d'identifier les fragments d'un même paquet (en cas de fragmentation, de moins en moins utilisée de nos jours)

Par contre sur la différence d'interprétation de la RFC 1323 qui fait qu'il est impossible d'activer les timestamps entre Windows et Linux, tu en penses quoi ?