C'était un de ces soirs durant lesquels je geeke jusqu'à tomber de sommeil, sauf que là j'étais en vacances. C'est ennuyeux, les vacances. Alors j'ai pris la (sage) décision de mettre à profit mon temps disponible pour avancer mes nombreux projets. Et comme à mon habitude, je n'ai pas pu m'empêcher de diverger et d'aller titiller d'autres sujets. Je ne sais plus trop pourquoi, mais je me suis intéressé à un moyen d'évaluer de manière fiable ou presque le trafic d'un serveur distant, et la première chose qui m'est venue à l'esprit est l'analyse du champ Identification des datagrammes IP.
Premiers tests, et premières déceptions
J'ai donc entamé la recherche d'une méthode efficace permettant d'évaluer le trafic d'un serveur distant, en me basant sur l'évolution du champ Identification du protocole IP. Ce champ est en réalité un compteur sur 16 bits (entier non-signé), incrémenté de 1 à chaque émission de paquet. Au vu des quantités de paquets émis par un serveur, ce compteur boucle assez régulièrement, et il est donc très difficile de déduire quoi que ce soit. Ce champ est aussi souvent appelé IP/ID.
J'ai donc commencé mes tests par le développement d'un outil en Python, utilisant Scapy, et émettant à intervalle très court deux requêtes ICMP Echo Request, calculant le temps de parcours depuis l'envoi, et essayant de déterminer la vitesse d'évolution des IP/ID. L'implémentation n'a pas été un problème en soit, par contre les résultats n'étaient pas au rendez-vous, et pour cause: la mesure de temps n'est pas fiable. En réalité, nous ne disposons que d'une référence pour cette mesure de temps: la machine émettrice. Le réseau Internet étant ce qu'il est nous ne sommes pas sûrs de plusieurs choses: * les paquets peuvent avoir été routés via des chemins différents entre les deux requêtes * des latences réseau ont pu se produire, et faire varier le temps d'émission./réception des paquets Autant de facteurs qui rendent la mesure imprécise, et au vu du laps de temps très court séparant l'envoi des deux paquets, l'erreur introduite est élevée. Il n'est donc pas fiable d'employer cette méthode pour déterminer la quantité de trafic qu'émet un serveur distant.
Les timestamps à la rescousse
Heureusement, le protocole ICMP met à notre disposition d'autres types de messages, comme celui permet de demander à une machine distante de nous envoyer des indications sur son horloge interne (Timestamp), ce qui a pour intérêt de pouvoir nous renseigner sur l'heure d'émission du paquet. En effet, le protocole ICMP étant un protocole de couche 4 (Transport), ses datagrammes sont encapsulés dans des datagrammes IP (protocole couche 3). Autrement dit, en envoyant un message ICMP Timestamp Request (Code:0, Type:13), le serveur distant peut y répondre avec un paquet ICMP contenant notamment un Timestamp paramétré par la machine ayant effectué la requête, et un Timestamp correspondant à l'heure d'émission de la réponse.
Il ne me fallait pas plus de choses pour que j'implémente une variante de l'outil précédent, prenant en compte cette fois-ci une mesure de temps beaucoup plus précise. Bien sûr, il faut que le serveur distant testé réponde à ce type de requête ICMP, ce qui n'est pas monnaie courante. Mais j'ai pu identifier certains sites connus qui y répondent favorablement: www.hsc.fr, www.lemonde.fr, www.lefigaro.fr, etc ... L'exemple ci-dessous, réalisé avec l'utilitaire hping2, démontre bien les réponses reçues:
virtubox:/home/virtualabs# hping2 -1 -K 0 -C 13 -r www.hsc.fr HPING www.hsc.fr (wlan0 217.174.211.25): icmp mode set, 28 headers + 0 data bytes len=40 ip=217.174.211.25 ttl=53 id=21302 icmp_seq=0 rtt=23.3 ms ICMP timestamp: Originate=2647898 Receive=2647955 Transmit=2647955 ICMP timestamp RTT tsrtt=24 len=40 ip=217.174.211.25 ttl=53 id=+15 icmp_seq=1 rtt=24.0 ms ICMP timestamp: Originate=2648899 Receive=2648955 Transmit=2648955 ICMP timestamp RTT tsrtt=24 len=40 ip=217.174.211.25 ttl=53 id=+6 icmp_seq=2 rtt=23.5 ms ICMP timestamp: Originate=2649899 Receive=2649955 Transmit=2649955 ICMP timestamp RTT tsrtt=23
Le timestamp qui m'intéressa fut celui dénommé Transmit, correspondant à l'heure de transmission. Ce timestamp est en réalité le nombre de millisecondes écoulées depuis 00h00, et est donc suffisamment précis pour être utilisé. De là, j'ai développé un premier outil affichant un aperçu visuel du trafic, toujours en Python avec la bibliothèque curses. Le résultat n'est pas super joli, mais fait l'affaire:
J'étais assez satisfait du résultat, bien que cet outil ne casse pas trois pattes à un canard. Néanmoins, après différents tests sur des serveurs publics, j'ai pu noter les inconvénients suivants:
Et cela a commencé à m'ennuyer, je suis donc reparti à la recherche de la méthode ultime permettant de déterminer dans tous les cas de figures la quantité de trafic supporté par une machine distante. Et c'est devenu un poil plus complexe ...
Approche probabiliste
Je me suis rapidement rendu à l'évidence, après plusieurs tentatives infructueuses: réaliser une étude analytique pure de la variation du champ IP/ID est une pure perte de temps, à cause de la faible entropie de ce compteur. Il me fallait partir dans une autre direction, et trouver une approche différente, mais toutefois fiable. Et c'est là que m'est venu l'idée de prendre le problème dans l'autre sens. Si une machine subit un trafic conséquent, alors il devrait être très difficile de prédire la valeur du prochain IP/ID retourné. Il s'agit ici d'une simple constatation probabiliste. Plus il y a de trafic, plus le champ IP/ID varie, et plus il est difficile de prédire sa prochaine valeur. Il existe toutefois un cas de figure, mais qui reste très peu probable: celui où le trafic est conséquent et constant.
Il m'a donc semblé intéressant de tenter cette approche. J'ai donc entamé le développement d'une preuve de concept, toujours en Python, qui visait à réussir une prédiction pour un serveur donné. Malheureusement, ceci est très difficile à obtenir, surtout lorsque l'on cherche à prédire exactement la valeur du prochain IP/ID. J'ai donc introduit une variable représentant une marge d'erreur augmentant progressivement (suite aux tests infructueux), et en faisant varier cette marge d'erreur de manière exponentielle, j'ai pu obtenir des résultants probant.
La recherche de prédiction exacte et/ou approximative permet, lorsqu'elle est réalisée sur une durée suffisamment longue, de pouvoir associer un score (heuristique) à la quantité de trafic émis par la machine distante. Il y a dans le cas présent une relation qui lie cette quantité de trafic (et de manière indirecte, la variation de cette quantité de trafic) à la difficulté de prédiction du champ IP/ID. En évaluant la difficulté de prédiction, on peut tenter de déduire la quantité de trafic. Il sagit d'une méthode probabiliste, aboutissant à un résultat fiable (qualitatif) mais non-quantitatif, permettant d'apprécier la quantité de trafic par rapport à un trafic de référence. Il semblerait toutefois qu'en cas de présence de répartiteurs de charge ou lorsque l'intervalle de temps entre deux mesures est trop grand, la mesure ne soit pas fiable.
To be continued ...
Je suis actuellement en train d'approfondir cette méthode d'évaluation probabiliste, en tentant notamment de lier la difficulté de prédiction d'IP/ID à la quantité de paquets émis par le serveur, bien que cela ne me semble pas évident du tout. De même, je continue mes tests afin de vérifier la validité de mes résultats, bien que pour le moment ils soient positifs.
Je publierai mon analyse dans les détails par la suite, ainsi que le code source de ma preuve de concept.