Après avoir eu le même problème, j'ai finalement trouvé la cause première.
Sous Linux, lorsqu'une socket est sur TIME_WAIT et qu'un nouveau SYN est ajouté (pour la même paire ip/port src, ip/port dest), le noyau vérifie si le numéro de SEQ du SYN est < ou > au dernier SEQ reçu pour cette socket.
(PS : dans l'image de la sortie de wireshark jointe à ce problème, les numéros de seq sont montrés comme relatifs, si vous ne les mettez pas comme absolus vous ne pouvez pas voir le problème. La capture devrait également montrer l'ancienne session pour pouvoir comparer les numéros de SEQ).
- si le numéro SEQ du SYN est > au numéro SEQ du paquet précédent, une nouvelle connexion est créée et tout fonctionne.
- si le numéro de SEQ du SYN est < au numéro de SEQ du paquet précédent, le noyau enverra un ACK relatif à la socket précédente car le noyau pense que le SYN reçu est un paquet retardé de la socket précédente.
Ce comportement est dû au fait qu'au début de TCP, les numéros de SEQ générés par les ordinateurs étaient incrémentiels, il était presque impossible de recevoir un numéro de SEQ inférieur au numéro de SEQ d'une socket précédente toujours en TIME_WAIT.
L'augmentation de la bande passante des ordinateurs fait que cela est passé de presque impossible à rare. Mais le plus important est qu'aujourd'hui la plupart des systèmes utilisent un numéro ISN (initial SEQ number) aléatoire pour améliorer la sécurité. Rien n'empêche donc le numéro SEQ d'un nouveau socket d'être > au numéro SEQ d'un socket précédent.
Chaque système d'exploitation utilise des algorithmes différents qui sont plus ou moins sûrs pour éviter ce problème particulier. http://www.bsdcan.org/2006/papers/ImprovingTCPIP.pdf donner une bonne présentation de la question.
Il y a un dernier point délicat... le noyau enverra donc un ACK relatif à l'ancienne session, alors ? L'OS client devrait recevoir l'ACK (de la session précédente), ne le comprend pas car pour le client la session est fermée, envoie un RST. Lorsque le serveur reçoit ce RST, il efface immédiatement le socket (il n'est donc plus en TIME_WAIT). De son côté, le client attend un SYN/ACK, s'il ne le reçoit pas, il enverra un nouveau SYN . Entre-temps, le RST a été envoyé et la session a été effacée sur le serveur, donc ce second SYN fonctionnera et le serveur répondra SYN/ACK et ainsi de suite.
Le comportement normal est donc que la connexion devrait fonctionner mais être retardée d'une seconde (jusqu'à ce que le SYN secondaire soit envoyé). Dans le cas de Jeff, il a dit dans un commentaire qu'il utilise un firewall Fortinet, ce firewall (par défaut) abandonnera l'ACK lié à l'ancienne session (parce que le firewall ne voit pas de session ouverte liée à l'ACK), donc le client n'envoie pas de RST et le serveur ne peut pas effacer la session de l'état TIME_WAIT (sauf bien sûr à la fin du timer TIME_WAIT). La commande "set anti-replay loose" sur fortinet peut permettre à ce paquet ACK d'être transmis au lieu d'être abandonné.