QoS pour Quality of Service ou qualité de service en français, est un terme très à la mode en ce moment. Ce terme est utilisé à tout va comme gage de performance pour pratiquement tous les domaines, alors qu'il définit un principe bien précis. QoS est intimement lié à la gestion de trafic réseau. Il désigne un ensemble de paramètres permettant d'assurer la qualité d'un service réseau. Habituellement, il s'agit de vitesse et de temps de réponse concernant des objets précis.
Pourquoi donc attacher tant d'importance au fait de privilégier certains services plutôt que d'autres, alors que, somme toute, la liaison réseau est la même ? En fait, la démarche originelle et la source de cette nouvelle obsession est commerciale. En effet, il semblerait que la tolérence des visiteurs d'un site Web (pour ne prendre que cet exemple) soit devenue un point crucial dans la conception d'un serveur et le paramétrage réseau d'un système.
Ainsi, certaines études rapportent que la population américaine, sollicitant fortement les ressources d'Internet, utilise des critères de qualification basés presque entièrement sur la perception du site. Peu importe le contenu, les prix ou les offres, les visiteurs s'attacheraient principalement à la vitesse du site et à ses capacités à les satisfaire le plus vite possible. Le chiffre de 65% a été avancé pour estimer le nombre de transactions aboutissant à un échec. Autant dire que si les visiteurs se basent effectivement sur la responsivité d'un site, cela revient à dire que 65% des visiteurs abandonnent et ne reviendront pas sur le site en question.
Toute l'astuce consiste donc à satisfaire le client au mieux avec les ressources réseau dont on dispose. Il faut alors privilégier certains services ou certaines actions au détriment d'autres selon une politique bien réfléchie à l'avance. Ainsi, par exemple, on favorisera les connexions vers un site de vente au détriment du support technique ou au téléchargement de pilotes ou de documentations. Nous ne traiterons pas ici de la justesse de tels choix qui sont on ne peut plus marketing, l'objet de l'article étant technique et non mercantile. Pour arriver à ses fins, on utilise une technique consistant à marquer les trames IP selon des critères prédéfinis, puis à les traiter de manière différente en fonction dudit marquage : gestion QoS et routage des paquets.
Ces deux fonctionnalités sont implémentées en version stable depuis les premières versions de la série 2.2 du kernel Linux. En dehors de leur application pour des serveurs, ces fonctionnalités sont également utiles sur un poste client. Imaginez la situation suivante : vous écoutez une radio sur le Net sous la forme d'un stream (flux) MP3 ou Ogg. Dans le même temps, vous récupérez les dernières archives à la mode sur des serveurs FTP divers. Le problème qui se pose alors est que vous ne contrôlez absolument pas l'allocation des ressources réseau et que votre stream MP3 a toutes les chances d'être coupé ou saccadé (lags). Voilà le type de situation où ces fonctionnalités du kernel peuvent vous aider. Vous définissez une politique selon laquelle, par exemple, les streams MP3 DOIVENT bénéficier de la plus grande bande passante afin de garantir la fluidité audio. Cette qualité exigée est obtenue en jouant sur les autres connexions (téléchargement FTP ou HTTP).
Bien sûr, il ne s'agit là que d'un exemple simple. Il est possible d'envisager une grande quantité de situations plus ou moins conflictuelles où plusieurs connexions simultanées doivent être contrôlées. QoS (Quality of Service) est une fonctionnalité vous permettant de définir une qualité de service. Entendez par là que vous pouvez paramétrer des choix assurant qu'un lien de données doit bénéficier de certains privilèges. Habituellement, et comme c'est le cas dans notre exemple simpliste, cette qualité de service porte sur un débit minimum pour une connexion par rapport à d'autres.
Principe
La gestion QoS de Linux et le contrôle du trafic (IProute) permettent de limiter la masse de données circulant sur une interface réseau. Comprenez bien que cette limitation sur un certain type de connexion réseau aura pour effet de "faire de la place" pour d'autres types. En principe, le contrôle du trafic réseau sortant d'une interface est plus aisé que le contrôle du trafic réseau entrant. Il est important de relever ce détail, car sur une machine faisant office de passerelle et possédant deux interfaces, il sera plus simple de contrôler les sorties de l'une que les entrées de l'autre.
Le contrôle du trafic sous Linux repose sur un principe de base selon lequel toutes les données arrivent via un "gros tuyau". Ces données peuvent être dispatchées dans plusieurs canaux (tubes, files d'attente, utilisez la symbolique qui vous convient) en fonction d'une sélection préalable. La sélection des données ou classification (regroupement en classe) se fait d'après des critères que vous définissez. Vous pouvez ainsi opter pour une classification par protocole, port, provenance, destination, etc... Attention, multiplier les classes ne sert à rien. En effet, différencier les connexions sur le port 110 (POP) et 80 (HTTP) est parfaitement inutile si vous traitez ensuite les deux classes sur un pied d'égalité. On a habituellement tendance à trop fractionner les classes pour rien et le seul effet sera une surcharge de travail pour le système, et donc un ralentissement des opérations.
Autre point fort (ou faible selon l'appréciation de la chose), les classes que vous allez définir permettent un héritage. Entendez par là qu'une classe hérite des caractéristiques de la classe supérieure par défaut, et que vous n'avez plus alors qu'à spécifier les changements.
Prérequis
La gestion du trafic réseau demande un support particulier dans la configuration du kernel Linux. Si ces fonctionnalités ne sont pas présentes dans votre kernel, vous serez obligé de reconfigurer et recompiler un nouveau kernel et/ou de nouveaux modules :
- Dans un premier temps, il faudra activer Netfilter (Network Packet filtering) dans la section Networking Options. Activez toutes les fonctionnalités sous la forme de modules. Cela permet une utilisation beaucoup plus souple et évite de générer un kernel statique énorme.
- Ensuite, toujours dans les Networking Options, vous devez activer les fonctionnalités QoS and/or fair queuing. Activez toutes les options sous la forme de modules, la seule vraiment importante pour cet article étant CBQ packet scheduler.
Recompilez le kernel et les modules de la manière habituelle :
make dep clean bzImage modules modules_install
Configurez votre bootloader afin d'utiliser le nouveau kernel et redémarrez. Les fonctionnalités sont dès à présent utilisables dans le kernel, mais vous devrez encore installer les utilitaires de l'espace utilisateur permettant de définir les paramètres à utiliser :
CBQ
L'exemple que nous allons traiter ici concerne directement la gestion de la bande passante. Cette gestion se fera via des files d'attente basées sur du Class Based Queueing (CBQ). Nous ferons usage d'iptables pour sélectionner et marquer les paquets et donc leur attribuer une classe. Ensuite iproute2 se chargera des files d'attente et de la manière de les gérer les unes par rapport aux autres.
CBQ est une file d'attente permettant de stocker d'autres files en fonction des classes pré-déterminées. Nous avons parlé d'héritage plus haut, nous allons voir par exemple comment tout cela fonctionne. Dans un premier temps, nous devons définir une file d'attente racine pour le périphérique ppp0 (par exemple, mais cela pourrait parfaitement être eth0 pour une connexion câble) :
# tc qdisc add dev ppp0 root handle 10: cbq bandwidth 10Mbit avpkt 1000 mpu 64
L'utilitaire tc (comme Traffic Control) est utilisé ici pour créer (add) une queuing discipline (qdisc) pour la première interface réseau (ppp0). Cette file d'attente est racine (root) et de type CBQ (cbq). Nous lui attribuons un identifiant numérique (10:) qui sera utilisé dans les commandes suivantes. Pensez à cette racine CBQ comme à la racine d'une arborescence ou un tronc principal sur lequel s'ajouteront les classes sous forme de branches. Les trois derniers paramètres définissent une bande passante de 10 Mb/s avec une taille moyenne de paquet de 1000 octets et une taille minimum imposée par Ethernet (mpu).
Nous utilisons ensuite iptables pour marquer les paquets. Nous distinguons deux types de connexion ici :
Nous utilisons donc la table mangle pour marquer les paquets des sessions intéractives avec une valeur 3 et les autres avec une valeur 4 :
# iptables -t mangle -A OUTPUT -m length --length 0:500 -j MARK --set-mark 3 # iptables -t mangle -A OUTPUT -m length --length 500:1500 -j MARK --set-mark 4
Nous définissons ensuite deux classes (branches), une par type de connexion :
# tc class add dev ppp0 parent 10:0 classid 10:1 cbq bandwidth 10Mbit rate 51Kbit allot 1514 prio 1 maxburst 10 avpkt 100 isolated
Cette première classe nommée 10:1 possède le moins de bande passante (rate 51Kbit), mais une priorité supérieure (prio 1). En effet, une connexion intéractive n'a, habituellement, pas besoin d'une bande passante très grande, mais pour éviter le lag, ce type de connexion doit avoir priorité sur toutes les autres. Le paramètre isolated indique que la bande passante de 51Kbit ne doit pas être empruntée par les autres classes. Nous nous assurons un débit minimum. En revanche, cette classe possède la faculté de prendre de la bande passante aux autres dans la mesure du possible.
Voici la définition pour la seconde classe :
# tc class add dev ppp0 parent 10:0 classid 10:2 cbq bandwidth 10Mbit rate 461Kbit allot 1514 prio 8 maxburst 2 avpkt 1500 bounded
Cette classe limite sa bande passante à 461Kbit et ne doit jamais dépasser cette limite (bounded). Elle possède également une priorité bien inférieure à la précédente. Nos classes sont maintenant définies, il ne nous reste plus qu'à indiquer quelle classe utiliser pour chaque type de connexion. Cette fois, ce sera le paramètre filter de tc qui sera utilisé :
# tc filter add dev ppp0 parent 10:0 protocol ip handle 3 fw flowid 10:1 # tc filter add dev ppp0 parent 10:0 protocol ip handle 4 fw flowid 10:2
On retrouve notre marque (handle 3 et handle 4), le CBQ parent (10:0) et respectivement les classes à utiliser pour chaque type de connexion (flowid 10:1 et flowid 10:2).
Cet exemple, tiré du QoS Connection Tuning Howto d'Emmanuel Roger, permet, sur une machine client d'une connexion ADSL, de tirer pleinement avantage du QoS et d'iproute. En effet, si dans le même temps vous uploadez des fichiers, que vous écoutez une radio en stream MP3 et vous téléchargez activement des archives FTP, une connexion SSH restera parfaitement utilisable. Vous n'avez plus à vous soucier des problèmes de ralentissement inhérent à toutes ces activités.
Autre exemple
Nous venons de le voir, le QoS permet d'améliorer le confort d'utilisation d'une connexion Internet. Mais il est également possible de se servir de ces fonctionnalités pour protéger votre machine ou vos machines d'attaques DoS (dénis de service) qui consistent habituellement à saturer un serveur de demandes afin de provoquer une surcharge et un plantage.
Une manière de limiter le risque pour ce type d'attaque est de tout simplement limiter le trafic ICMP (ping) de manière à empêcher tout débordement. On commence alors par définir un CBQ :
# tc qdisc add dev eth0 root handle 10: cbq bandwidth 10Mbit avpkt 1000
Rien de nouveau ici. En revanche, nous allons procéder différemment pour ce qui concerne les classes et profiter pleinement des capacités d'héritage. Nous définissons une première classe :
# tc class add dev eth0 parent 10:0 classid 10:1 cbq bandwidth 10Mbit rate 10Mbit allot 1514 prio 5 maxburst 20 avpkt 1000
Cette classe 10:1 englobe toute la bande passante. Nous définissons ensuite une seconde classe ayant comme parent la classe 10:1 et non plus le CBQ 10:0 :
# tc class add dev eth0 parent 10:1 classid 10:100 cbq bandwidth 10Mbit rate 10Kbit allot 1514 weight 800Kbit \ prio 5 maxburst 20 avpkt 250 bounded
Notre nouvelle classe 10:100 limite la bande passante à 10 Kbit et cette limite ne peut être dépassée. Il ne nous reste plus ensuite qu'à ajouter un filtre pour utiliser cette classe :
# tc filter add dev eth0 parent 10:0 protocol ip prio 100 u32 match ip protocol 1 0xFF flowid 10:100
La principale différence entre cette dernière commande et l'exemple précédent est que nous nous passons de marquer les paquets avec iptables. Ici, le filtre analyse directement le trafic (u32 match ip protocol 1 0xFF).
Liens
* HOWTO du routage avancé et du contrôle de trafic sous Linux
* Linux QoS & TC
* QoS Connection Tuning HOWTO
* References on CBQ (Class-Based Queueing)
* Présentation de la gestion QoS de Linux
* Linux Advanced Routing & Traffic Control
* The LARTC Archives
* QLinux 2.4.x: A QoS enhanced Linux Kernel for Multimedia Computing
* Tracking down the QoS problem