En janvier, j'ai réalisé, avec trois autres étudiants (Mathieu Bivert, Calypso Petit et Sophie Valentin), un projet de fin de semestre à Polytech'Nice Sophia pendant 3 semaines. Nous avons pu choisir notre sujet parmi une liste prédéfinie. Nous avons décidé de réaliser un projet dont l'intitulé était "Créer un framework pour la mise en place des routeurs avec support pour notification de vitesse explicite (kernel space)". Le cahier des charges détaillé est ici. Ce sujet a été proposé par M. Dino Lopez qui nous a encadrés et aidés dans nos recherches.
Pour résumer, nous avons dû développer un module noyau destiné à effectuer des statistiques sur les paquets transitant par un routeur haut débit (> 100 Mbps) qui implémente un protocole de congestion nommé ERN (Explicit Rate Notification). Nous nous sommes concentrés sur le calcul de l'occupation de la file d'attente des paquets au sein du routeur. Le problème principal que nous avons rencontré est la localisation de cette file d'attente.
Nous sommes d'abord partis sur une mauvaise piste en essayant de créer un module Netfilter : en incrémentant une variable lors de l'entrée d'un paquet (dans le hook NF_IP_PRE_ROUTING) ou de son transfert pour routage (hook NF_IP_FORWARD) et en la décrémentant lors de la sortie d'un paquet (dans le hook NF_IP_POST_ROUTING). Cependant, les tests effectués nous ont montré qu'il n'y a aucune file d'attente entre ces hooks. En effet, même en limitant la vitesse maximale d'envoi (TX Rate) de l'interface de sortie du routeur, la valeur de la variable oscille entre 0 et 1.
Nous avons aussi essayé d'utiliser la fonction dev_get_stats() de linux/netdevice.h qui permet, entre autres, de connaître le nombre de paquets reçus et émis par une interface. Cependant, les statistiques fournies par cette fonction sont effectuées sur les paquets tous protocoles confondus : elle ne permet pas d'obtenir des statistiques uniquement sur les paquets TCP.
La file d'attente n'étant pas accessible au niveau de la couche réseau nous avons essayé de l'obtenir au niveau de la couche liaison. Pour cela, nous nous sommes résolus à créer un patch pour le noyau Linux, ce qui nous a demandé un énorme travail de recherche afin de comprendre au mieux la façon dont est implémentée la pile TCP/IP dans le noyau Linux (Linux Network Stack, plus d'informations ici et là).
Lorsqu'un paquet entre par une interface réseau, une interruption est levée et peut être traitée grâce à une fonction handler. Nous avons donc essayé d'incrémenter la variable correspondant au nombre de paquets présents dans la file d'attente dans ce handler en continuant de la décrémenter au niveau du hook NF_IP_POST_ROUTING de Netfilter, donc à la sortie d'un paquet. Nous avons cependant rapidement abandonné cette piste car, en plus d'être complexe à mettre en oeuvre, cela nous aurait fourni la taille de la file d'attente en entrée (ingress queue) or seule la taille de la file d'attente en sortie (egress queue) a un intérêt pour le protocole ERN.
Nous avons finalement réussi à déterminer que la file d'attente en sortie est présente entre les fonctions dev_queue_xmit() et dev_hard_start_xmit() du fichier net/core/dev.c (voir la partie 5.2 du rapport). Nous pouvons donc incrémenter notre variable dans dev_queue_xmit(), au moment où les paquets sont enfilés (enqueue) et la décrémenter dans dev_hard_start_xmit(), lorsque les paquets sont défilés pour être passés au driver de la carte réseau. Ces deux fonctions prennent un pointeur sur une struct sk_buff (qui représente un paquet) en paramètre. Il est possible d'extraire le type de protocole utilisé au niveau de la couche transport en passant cette structure en paramètre à la fonction skb_network_header() et en utilisant le champ protocol de la struct iphdr (le header IP) retournée par cette fonction. On peut donc modifier la variable uniquement si les paquets sont des paquets TCP (protocol == 6).
Vous trouverez ici une archive contenant le cahier des charges, le code du module netfilter, le patch noyau, le rapport, les diapos de la soutenance et un fichier ReadMe expliquant comment utiliser le code fourni.