Une gestion de trafic simple : traffic shaping et qualité de service (QoS)
Le QoS permet de réordonner les paquets dans la queue de sortie (buffer), pour donner la priorité au trafic tel que la navigation Web, l’ICMP ou le SSH.
Si votre upload est régulièrement encombré, et que vous utilisez des applications que demandent de l’interactivité (navigation web, SSH, jeux en ligne, téléphonie SIP, vidéoconférence…) vous ne pourrez plus vous en passer.
Gardez à l’esprit que le QoS de base ne s’applique qu’au trafic sortant, et s’applique idéalement sur Shorewall en tant que routeur internet.
Un exemple de ce que vous pouvez obtenir. La ligne est chargée à 100 % par une sauvegarde externalisée. À 14 h 30, un appel téléphonique rentre en SIP, et reçoit la classe de bande 1011 de priorité maximale.
Voici le troisième d’une série de 6 articles :
- Shorewall – Configuration de base en quelques minutes
- Shorewall – Routeur internet
- Shorewall – QoS & Traffic shaping pour améliorer les performances réseau
- Shorewall – Configurations avancées
- Shorewall – En entreprise : Haute disponibilité, sauvegarde & restauration
- Shorewall – En entreprise : Traçabilité, documentation des règles, revues d’audit, journalisation
Fonctionnement
Prenons l’exemple d’une ligne internet DSL, fibre ou câble surchargée. Les paquets IP sortants vont passer par une série de cartes réseau et routeurs. Chacun de ces éléments a des tampons (buffers) d’entrée et de sortie. Le plus important est d’identifier le tampon devant la ligne avec la plus petite bande passante, car c’est celui qui va se remplir et ralentir tout le trafic. Dans le cas d’une ligne internet surchargée, c’est le tampon de l’adaptateur internet (souvent appelé « modem ») qui se remplit. On a alors une sensation désagréable de lenteurs de connexion ou l’impossibilité d’avoir une conversation VOIP/vidéoconférence de qualité.
Pour que le QoS soit efficace il faut :
- Mise en forme du trafic (trafic shaping) : éviter à tout prix d’engorger le tampon de l’adaptateur internet (modem). Si l’adaptateur internet n’a pas de QoS / Traffic Shaping intégré, nous n’avons aucun contrôle sur ce tampon distant. Mais nous pouvons limiter le trafic en amont à un niveau légèrement inférieur à la capacité de la ligne, de manière que ce tampon reste toujours vide. Cela reviens à « ramener » le tampon saturé au niveau de notre serveur / routeur / station de travail.
- Ordonnancement : après avoir très légèrement réduit le débit de sortie, c’est maintenant le tampon local qui va se remplir. Et comme il est local, on peut y réordonner les paquets. Par exemple par ordre de priorité pour favoriser les paquets ayant besoin d’interactivité au détriment des paquets qui peuvent attendre.
Si le trafic est trop important, et si on a bien classé le trafique, il va s’empiler dans le tampon/la queue de moindre priorité. Toutes les 10 secondes, cette queue va être partiellement vidée (perturbée), et les paquets seront perdus. Ce n’est pas un problème, car la plupart des protocoles internet sont prévus pour gérer les engorgements et les pertes de paquets, en particulier TCP qui va demander à l’envoyeur de retransmettre et de diminuer son débit d’envoi.
Shorewall
Le QoS peut être très complexe. Mais Shorewall propose une implémentation « Simple » qui permet de couvrir la plupart des cas, et d’avoir un résultat satisfaisant rapidement. C’est ce mode que nous allons étudier et mettre en œuvre.
En mode TC Simple, Shorewall va créer 3 queues :
- Classe de bande 1 : Trafic à envoyer en priorité
- Classe de bande 2 : La bande par défaut (trafic non classé)
- Classe de bande 3 : Trafic de moindre importance (qui peut être retardé)
La classification en « bande » 1 ou en « bande » 3 se fait dans l’ordre :
- Avec des règles simples dans le fichier tcpri
- Avec des règles avancées dans le fichier mangle
- Avec un classement automatique d’après les flags TOS/DSCP du paquet
- Si le paquet n’est toujours pas classé, il se retrouve en classe de bande 2
En pratique
Il est préférable de mettre en place une configuration de qualité de service et de mise en forme de trafic le plus près possible du goulot d’étranglement, idéalement juste derrière votre modem ou internet adapter. Pour faire simple, et comme nous ne verrons les configurations avancées que dans le prochain chapitre, nous allons continuer dans la VM utilisée dans le premier chapitre.
Certains modems – routeurs intègrent des fonctions de base de qualité de service, mais en général très (trop) limités. Vérifiez et désactivez-la si vous implémentez cette solution.
Copiez les fichiers de templates
sudo cp /usr/share/shorewall/configfiles/tcinterfaces.annotated /etc/shorewall/tcinterfaces
sudo cp /usr/share/shorewall/configfiles/tcpri.annotated /etc/shorewall/tcpri
sudo cp /usr/share/shorewall/configfiles/mangle.annotated /etc/shorewall/mangle
Mettre le paramètre TC_ENABLED à «Simple» dans /etc/shorewall/shorewall.conf
. Vous devriez avoir ceci :
sudo grep ^TC_ /etc/shorewall/shorewall.conf
TC_ENABLED=Simple
TC_EXPERT=No
TC_PRIOMAP="2 3 3 3 2 3 1 1 2 2 2 2 2 2 2 2"
TC_BITS=
Le QoS va s’appliquer en priorité aux règles dans le fichier tcpri et éventuellement mangle (configuration avancée). Si aucune correspondance n’est trouvée, la priorité va être définie en fonction du type de service (ToS) du paquet IP en fonction de la TC_PRIOMAP
. (Laisser la priomap par défaut)
Déterminez votre interface de sortie
ip route show default
default via 192.168.130.1 dev internet proto static
Ici, c’est l’interface « internet »
Déterminez l’upload maximum de la ligne
Lorsque la ligne est libre (pas de gros upload encours), utilisez un service tel que https://www.speedtest.net/ pour mesurer le débit disponible.
Attention, seul l’upload nous intéresse ici. Dans l’exemple, Speedtest me donne upload de 8.99 Mbps. Pour pouvoir utiliser des nombres entier, on va convertir cette valeur en « Kilobits per second » en multipliant par 1024.
# echo "8.99*1024" | bc
9205.76
Nous allons prendre une valeur légèrement inférieure, ici nous allons commencer avec 9200kbit.
Définir l’interface dans tcinterfaces
Maintenant qu’on a la bande passante maximale en upload (9200kbit) et le nom de l’interface (internet), on peut définir l’interface de sortie dans le fichier /etc/shorewall/tcinterfaces
#INTERFACE TYPE IN_BANDWIDTH OUT_BANDWIDTH
internet external 0 9200kbit
On met l’interface en « external » puisque c’est celle qui va vers internet. Et 0 dans le IN_BANDWIDTH
car on ne veut pas limiter le débit en entrée.
Remarque : Vous pourriez limiter la bande passante entrante de la même manière, mais ça n’aura que peu d’effet : les paquets seront abandonnés sur votre firewall local au lieu du routeur distant de l’ISP (Internet Service Provider).
On peut maintenant redémarrer Shorewall: shorewall restart
Vérification
root@pivert-VirtualBox:/etc/shorewall# tc qdisc qdisc noqueue 0: dev lo root refcnt 2 qdisc tbf 1: dev enp0s3 root refcnt 2 rate 9200Kbit burst 10Kb lat 200ms qdisc sfq 1011: dev enp0s3 parent 101:1 limit 127p quantum 1875b depth 127 divisor 1024 perturb 10sec qdisc sfq 1013: dev enp0s3 parent 101:3 limit 127p quantum 1875b depth 127 divisor 1024 perturb 10sec qdisc prio 101: dev enp0s3 parent 1: bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1 qdisc sfq 1012: dev enp0s3 parent 101:2 limit 127p quantum 1875b depth 127 divisor 1024 perturb 10sec
Explications
Shorewall met en place un algorithme de «Simple» «Traffic Classification»
On retrouve :
- le « Tocken Bucket Filter » qui va limiter la bande passante en sortie à 9200kbits
- la discipline prio va toujours privilégier la/les bandes inférieures (1, puis 2 au détriment de la bande 3, qui correspondent aux sfq 1011, 1012 et 1013)
- La priomap par défaut, qui va classer le trafique en fonction du ToS des paquets sortants, si le ToS a été défini. (C’est rarement le cas). Remarquez que les bandes sont numérotées à partir de 0 dans la priomap : les bandes 0,1,2 correspondent aux sfq 1,2,3.
- Les 3 « Stochastic Fairness Queueing » (sfq) pour définir une queue pour chacune des 3 bandes.
Remarque : Shorewall nomme les bandes 1,2 et 3, alors que dans la documentation de tc-prio et dans la priomap elles sont nommées 0,1 et 2. Voici la définition de l’algorithme
When dequeueing, band 0 is tried first and only if it did not deliver a packet does PRIO try band 1, and so onwards. Maximum reliability packets should therefore go to band 0, minimum delay to band 1 and the rest to band 2.
Classification du trafic
Il s’agit probablement de la phase la plus critique. Il y a plusieurs possibilités :
- On peut matcher le trafic à partir de règles dans le fichier tcpri
- On peut matcher le trafic à partir de règles dans le fichier mangle qui offre beaucoup plus de possibilités
- Certaines applications permettent de définir le ToS ou le DSCP. Par exemple pour rclone:
rclone --dscp CS1 ...
Étude d’un cas de classification du trafic
Je vais prendre le cas d’une tâche de sauvegarde rclone
sur un serveur web (192.168.66.10) qui sature la connexion internet. C’est un cas intéressant, car on va pouvoir utiliser les 4 options décrites ci-dessus.
1. Utilisation du tcpri
#BAND PROTO PORT ADDRESS INTERFACE HELPER
1 TCP 853 - - - # DNS-over-TLS (stubby)
1 - - - - sip
3 - - 192.168.66.10 - - # ALL Traffic from Web Server in Band3
Dans le tcpri, on peut matcher :
- Le protocole et le port (source et destination), la première ligne passe en priorité (bande 1) le traffic DNS (DoT)
- Le “helper” iptables, dans ce cas sip. Pour donner la priorité à ce trafic. Vous pouvez lister les conntrack helpers avec la commande
ls -l /usr/src/linux-headers-$(uname -r)/include/linux/netfilter/
- La sélection sur l’IP source du serveur Web pour que les backups passent en bande 3.
Inconvénients de cette solution : tous les paquets en provenance du serveur web vont passer en bande 3 (basse priorité), y compris le trafic de clients externes navigants sur le site web.
2. Utilisation tu fichier mangle
Ce fichier permet des configurations beaucoup plus avancées que tcpri, et peut être utilisé avec le TC_ENABLED shorewall en mode “Simple”. On peut utiliser le fichier mangle en plus du fichier tcpri si il est bien activé dans la configuration de Shorewall.
Vérifier que MANGLE_ENABLED est à Yes (par défaut) :
root@burns2:/etc/shorewall# grep MANGLE shorewall.conf
MANGLE_ENABLED=Yes
Voici un exemple de configuration pour un serveur web
##############################################################################################################################################################
#ACTION SOURCE DEST PROTO DPORT SPORT USER TEST LENGTH TOS CONNBYTES HELPER PROBA DSCP SWITCH
MARK(1):T 192.168.66.11 - tcp - 443 - - - - - - - - - # browing traffic from Web Servers
MARK(3):T 192.168.66.11 - tcp 443 - - - - - - - - - - # backup traffic from Web Servers
Le fichier mangle étant plus souple, on peut:
- Isoler le trafic sortant, à direction du port 443, et donc le trafic de sauvegarde →Class 3
- Isoler le trafic sortant, à partir du port 443, et donc le trafic de navigation sur le site → Class 1
Avec le fichier mangle
, on peut aller beaucoup plus loin. Exemple plus avancé, recommandé pour commencer, et utilisé pour les tests suivants :
#ACTION SOURCE DEST PROTO DPORT SPORT USER TEST LENGTH TOS CONNBYTES HELPER PROBA DSCP SWITCH
MARK(1):T 192.168.0.0/16 - - - - - - 0:150 - - - - - - # very small traffic
MARK(3):T 192.168.0.0/16 - tcp 443 - - - - - 750::O:A - - - - # Paquets sortants d'une connexion https sortante dont la moyenne de la taille des packets est supérieure à 750bits, à mettre en bande de classe 3 (basse priorité)
- La première règle définit que tous les paquets de petite taille (<99 octets) sont prioritaires (→ Classe 1)
Cette règle est particulièrement efficace sur les appels de type VOIP, car les paquets sont souvent UDP de petite taille (128 octets) - La seconde règle définit que tous les paquets qui matchent les conditions suivantes sont en classe 3:
- La connexion provient du réseau interne (Source 192.168.0.0/16, et utilisation du CONNBYTES)
- La moyenne (:A pour average) de la taille des paquets sortants (:O pour original destination) est supérieure à 500 bytes (500:)
Si vous n’êtes pas certain des règles à utiliser, je vous recommande de laisser le fichier tcpri vide, et de commencer avec la paire de règles ci-dessus dans le fichier mangle.
Grâce aux actions CONNBYTES et aux commandes SAVE et RESTORE, il est possible de matcher sur les connexions, et non plus uniquement les paquets.
Gardez en tête que le QoS ne se fera que sur le marquage du paquet, et non de la connexion. Si la connexion est marquée, on peut “redescendre” le marquage sur le paquet avec l’action RESTORE.
3. Utilisation du ToS/DSCP
Si vous utilisez une application qui permet de définir directement le ToS ou le DSCP, il est plus facile de matcher son traffic. C’est l’idéal.
Pour rclone par exemple, il faut juste lui ajouter l’option –dscp, suivit de la classe. J’ai choisi la classe de service 1 (avec une moindre priorité), donc rclone est lancé avec l’option –dscp CS1. Et le trafic de sauvegarde sera automatiquement en classe 3.
- Explications sur le DSCP (anglais)
- Shorewall ne supporte pas toutes les classes DSCP, vérifiez dans les annotations du fichier
mangle
si vous êtes parti du fichier/usr/share/shorewall/configfiles/mangle.annotated
ouman shorewall-mangle
4. Tests
Pour pouvoir tester la solution, il faut saturer l’upload avec un trafic que vous avez classé en bande 3. Puis utilisez la commande tc -s (pour statistics). Pour les tests, je lancer une grosse sauvegarde sur un service S3. Voici le monitoring du modem.
P.S. Lorsque vous adaptez les règles de classification, un shorewall reload
suffit. Un shorewall reload
ou restart
va mettre à zéro les compteurs de statistiques.
Voici ce que ça donne avec les 2 règles dans le fichier mangle (règles recommandées ci-dessus).
Après quelques heures de saturation de l’upload avec une sauvegarde informatique, donc avec des paquets sortant vers le port 443 et dont la taille moyenne dépasse les 750 octets (deuxième règle du fichier mangle), on peut constater que les filtres fonctionnent :
- Les paquets sont bien droppés dans la bande 3 (la sfq 1013), et aucun dans les bandes 1012 et 1011 de plus haute priorité.
- On voit que l’upload est toujours en cours, car le backlog de la sfq 1013 se remplit régulièrement. (Il est à 95 paquets dans l’exemple). Les sfq sont limitées à 128 paquets (limit 127p). Les backlogs des sfq de plus hautes priorités sont vides.
- On voit également que le nombre de paquets droppés par le tbf est identique aux paquets droppés de la bande 3, puisqu’aucun paquet n’a été droppé dans les autres bandes.
- On peut également voir le volume important qui est passé dans la sfq 1013 depuis le dernier
shorewall reload
: 15022223566 octets
Pour l’avoir en Go :
root@burns2:/etc/shorewall# bc -l <<< "scale=3;15022223566/2^30"
13.990
J’en ai profité pour relancer un Speedtest, qui mondre un débit quasiment identique, malgré la saturation de la ligne:
Le cas de speedtest est un peu biaisé ici, car il a utilisé le port de destination 8080, et donc même si les paquets étaient volumineux, ce trafic s’est retrouvé d’office dans la bande 2 par défaut.
5. Problèmes courants
- Bien que vous saturiez la ligne en upload,
tc -s qdisc
ne montre aucun paquet abandonné (dropped). Cela signifie que la mise en forme du trafic ne se fait pas car vous avez choisi un débit de sortie plus élevé que ce que la ligne ne peut transporter. Les paquets sont donc abandonnés sur le modem, et non pas sur le Shorewall. Réduisez leOUT_BANDWIDTH
de l’interface de sortie dans le fichiertcinterfaces
. - Des paquets sont bien abandonnés, mais pas dans la bonne classe de bande. Vérifiez ou simplifiez les règles dans
tcpri
et dansmangle
et testez.
Monitoring avancé
On peut relancer la commande tc -s qdisc show dev internet
de multiple fois, idéalement après un shorewall reload
qui met à zéro les compteurs. Ou bien laisser tourner dans une console :
watch tc -s qdisc
La commande tc
a également une sortie en JSON qui la rend facile à exploiter à partir de scripts ou à partir de jq
jq ( apt install jq pour l’installer )
~# tc -s -j qdisc | jq -r '.[] | select( .kind == "sfq") | { Bande: ( .handle | match("[0-9]+([0-9]):") .captures[0] .string |tonumber ) , Megaoctets: (.bytes/pow(2;20) |round), Paquets: .packets, Abandons: .drops, Longueur_queue: .qlen}' | jq -s 'sort_by(.Bande)'
[
{
"Bande": 1,
"Megaoctets": 187,
"Paquets": 2423130,
"Abandons": 0,
"Longueur_queue": 0
},
{
"Bande": 2,
"Megaoctets": 624,
"Paquets": 974656,
"Abandons": 5754,
"Longueur_queue": 0
},
{
"Bande": 3,
"Megaoctets": 41295,
"Paquets": 28714328,
"Abandons": 25966,
"Longueur_queue": 94
}
]
Exporter en Python
Voici un mini exporter Prometheus en Python pour cette commande. Il dépend du package python3-prometheus-client
.
#!/usr/bin/env python # cSpell:includeRegExp /".*",/ # cSpell:includeRegExp /\(f?".*"/ # # Prometheus exporter for SFQ qdisc queues # # You should adapt the name of the inerface, and maybe queues match and listening port. # Install the python library # apt install python3-prometheus-client # Tested on Shorewall Simple TC / Ubuntu 22.04 / Python 3.10 # # François Delpierre 2022 # GNU General Public License # from __future__ import annotations import time from prometheus_client.core import GaugeMetricFamily, CounterMetricFamily, REGISTRY from prometheus_client import start_http_server import subprocess import shlex import json INTERFACE="internet" COMMAND=f"tc -s -j qdisc show dev {INTERFACE}" COUNTERS = ['bytes', 'drops', 'overlimits', 'packets', ] GAUGES = ['backlog', 'qlen' ] def execute(cmd) -> tuple: output = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.STDOUT) return output.communicate() class CustomCollector(object): def __init__(self) -> None: pass def collect(self): stdout = execute(COMMAND)[0] for k in COUNTERS: counter = CounterMetricFamily(f'trafic_control_{k}', f'Trafic Control {k}', labels=['interface', 'handle', 'kind']) for tc in json.loads(stdout): counter.add_metric(['internet', tc['handle'], tc['kind']], tc[k]) yield counter for k in GAUGES: gauge = GaugeMetricFamily(f'trafic_control_{k}', f'Trafic Control {k}', labels=['interface', 'handle', 'kind']) for tc in json.loads(stdout): gauge.add_metric(['internet', tc['handle'], tc['kind']], tc[k]) yield gauge if __name__ == '__main__': start_http_server(9102) REGISTRY.register(CustomCollector()) while True: time.sleep(1)
C’est rudimentaire, et une fois Prometheus et Grafana configurés, ça donne ceci.
On peut y retrouve la pointe du Speedtest réalisé précédemment (j’avais configuré ce script avant), et on remarque :
- La bande 2 (1012) reçoit toute la bande passante disponible au détriment de la bande 3 (1013).
- Les paquets sont habituellement abandonnés en bande 3, mais au moment du Speedtest, c’est dans la bande 2 que les paquets ont le plus été abandonnés.
- Seule la SFQ 1013 se remplit, sauf au moment du Speedtest où on voit la SFQ 1012 de la bande 2 se remplir.
Après 36 h, 145 Go transférés sur une ligne internet limitée à 9200 kbps d’upload et plus aucun impact sur la navigation, la téléphonie, les video-conférence ou les jeux en ligne.
Conclusions
Le trafic control
en mode « Simple » de Shorewall, n’est pas bien compliqué, et il permet déjà de couvrir tous les problèmes classiques de saturation d’upload internet. L’article parait long, mais c’est tellement plus simple quand on comprend ce qu’on fait ! J’espère que cet article vous aidera à tirer le meilleur parti de cette option intéressante de Shorewall.