Comme expliqué dans la section sur les gestionnaires de mise en
file d'attente basés sur des classes, les filtres sont nécessaires
pour classifier les paquets dans n'importe laquelle des sous-files
d'attente. Ces filtres sont appelés à l'intérieur des gestionnaires
de mise en files d'attente basés sur des classes.
Voici une liste incomplète des classificateurs
disponibles :
fw
Base la décision sur la façon dont le pare-feu a marqué les
paquets. Ceci peut être un passage facile si vous ne voulez pas
apprendre la syntaxe tc liée aux filtres. Voir le chapitre sur les
gestionnaires de mise en file d'attente pour plus de détails.
u32
Base la décision sur les champs à l'intérieur du paquet
(c'est-à-dire l'adresse IP source, etc.)
route
Base la décision sur la route que va emprunter le paquet.
rsvp, rsvp6
Route les paquets en se basant sur RSVP . Seulement
utile sur les réseaux que vous contrôlez. Internet ne respecte pas
RSVP.
tcindex
Utilisé par le gestionnaire de file d'attente DSMARK. Voir la
section appropriée.
Notez qu'il y a généralement plusieurs manières de classifier un
paquet. Cela dépend du système de classification que vous souhaitez
utiliser.
Les classificateurs acceptent en général quelques arguments
communs. Ils sont listés ici pour des raisons pratiques :
protocol
Le protocole que ce classificateur acceptera. Généralement, on
n'acceptera que le trafic IP. Exigé.
parent
Le descripteur auquel ce classificateur est attaché. Ce
descripteur doit être une classe déjà existante. Exigé.
prio
La priorité de ce classificateur. Les plus petits nombres seront
testés en premier.
handle
Cette référence a plusieurs significations suivant les
différents filtres.
Toutes les sections suivantes supposeront que vous essayez de
mettre en forme le trafic allant vers HostA. Ces
sections supposeront que la classe racine a été configurée sur 1:
et que la classe vers laquelle vous voulez envoyer le trafic
sélectionné est 1:1.
Le filtre u32 est le filtre le plus avancé dans l'implémentation
courante. Il est entièrement basé sur des tables de hachage, ce qui
le rend robuste quand il y a beaucoup de règles de filtrage.
Dans sa forme la plus simple, le filtre u32 est une liste
d'enregistrements, chacun consistant en deux champs : un
sélecteur et une action. Les sélecteurs, décrits ci-dessous, sont
comparés avec le paquet IP traité jusqu'à la première
correspondance, et l'action associée est réalisée. Le type d'action
le plus simple serait de diriger le paquet vers une classe CBQ
définie.
La ligne de commande du programme tc filter,
utilisé pour configurer le filtre, consiste en trois parties :
la spécification du filtre, un sélecteur et une action. La
spécification du filtre peut être définie comme :
tc filter add dev IF [ protocol PROTO ]
[ (preference|priority) PRIO ]
[ parent CBQ ]
Le champ protocol décrit le protocole sur lequel le
filtre sera appliqué. Nous ne discuterons que du cas du protocole
ip. Le champ preference
(priority peut être utilisé comme alternative) fixe la
priorité du filtre que l'on définit. C'est important dans la mesure
où vous pouvez avoir plusieurs filtres (listes de règles) avec des
priorités différentes. Chaque liste sera scrutée dans l'ordre
d'ajout des règles. Alors, la liste avec la priorité la plus faible
(celle qui a le numéro de préférence le plus élevé) sera traitée.
Le champ parent définit le sommet de l'arbre CBQ (par
ex. 1:0) auquel le filtre doit être attaché.
Les options décrites s'appliquent à tous les filtres, pas
seulement à u32.
Le sélecteur U32
Le sélecteur U32 contient la définition d'un modèle, qui sera
comparé au paquet traité. Plus précisément, il définit quels bits
doivent correspondre dans l'en-tête du paquet, et rien de plus,
mais cette méthode simple est très puissante. Jetons un oeil sur
l'exemple suivant, directement tiré d'un filtre assez complexe
réellement existant :
# filter parent 1: protocol ip pref 10 u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:3 \
match 00100000/00ff0000 at 0
Pour l'instant, laissons de côté la première ligne - tous ces
paramètres décrivent les tables de hachage du filtre.
Focalisons-nous sur la ligne de sélection contenant le mot-clé
match. Ce sélecteur fera correspondre les en-têtes IP
dont le second octet sera 0x10 (0010). Comme nous pouvons le
deviner, le nombre 00ff est le masque de correspondance, disant au
filtre quels bits il doit regarder. Ici, c'est 0xff, donc l'octet
correspondra si c'est exactement 0x10. Le mot-clé at
signifie que la correspondance doit démarrer au décalage spécifié
(en octets) - dans notre cas, c'est au début du paquet.
Traduisons tout cela en langage humain : le paquet
correspondra si son champ Type de Service (TOS) a le bit "faible
délai" positionné. Analysons une autre règle :
# filter parent 1: protocol ip pref 10 u32 fh 800::803 order 2051 key ht 800 bkt 0 flowid 1:3 \
match 00000016/0000ffff at nexthdr+0
L'option nexthdr désigne l'en-tête suivant
encapsulé dans le paquet IP, c'est à dire celui du protocole de la
couche supérieure. La correspondance commencera également au début
du prochain en-tête. Elle devrait avoir lieu dans le deuxième mot
de 32 bits de l'en-tête. Dans les protocoles TCP et UDP, ce champ
contient le port de destination du paquet. Le nombre est donné dans
le format big-endian, c'est-à-dire les bits les plus significatifs
en premier. Il faut donc lire 0x0016 comme 22 en décimal, qui
correspond au service SSH dans le cas de TCP. Comme vous le
devinez, cette correspondance est ambiguë sans un contexte, et nous
en discuterons plus loin.
Ayant compris tout cela, nous trouverons le sélecteur suivant
très facile à lire : match c0a80100/ffffff00 at
16. Ce que nous avons ici, c'est une correspondance de trois
octets au 17ème octet, en comptant à partir du début de l'en-tête
IP. Cela correspond aux paquets qui ont une adresse de destination
quelconque dans le réseau 192.168.1/24. Après avoir analysé les
exemples, nous pouvons résumer ce que nous avons appris.
Sélecteurs généraux
Les sélecteurs généraux définissent le modèle, le masque et le
décalage qui seront comparés au contenu du paquet. En utilisant les
sélecteurs généraux, vous pouvez rechercher des correspondances sur
n'importe quel bit de l'en-tête IP (ou des couches supérieures).
Ils sont quand même plus difficiles à écrire et à lire que les
sélecteurs spécifiques décrits ci-dessus. La syntaxe générale des
sélecteurs est :
match [ u32 | u16 | u8 ] PATTERN MASK [ at OFFSET | nexthdr+OFFSET]
Un des mots-clés u32,u16 ou
u8 doit spécifier la longueur du modèle en bits.
PATTERN et MASK se rapporteront à la longueur définie par ce
mot-clé. Le paramètre OFFSET est le décalage, en octets, pour le
démarrage de la recherche de correspondance. Si le mot-clef
nexthdr+ est présent, le décalage sera relatif à
l'en-tête de la couche réseau supérieure.
Quelques exemples :
# tc filter add dev ppp14 parent 1:0 prio 10 u32 \
match u8 64 0xff at 8 \
flowid 1:4
Un paquet correspondra à cette règle si sa "durée de vie" (TTL)
est de 64. TTL est le champ démarrant juste après le 8ème octet de
l'en-tête IP.
# tc filter add dev ppp14 parent 1:0 prio 10 u32 \
match u8 0x10 0xff at nexthdr+13 \
protocol tcp \
flowid 1:3 \
FIXME: Il a été montré que cette syntaxe ne marche pas
correctement.
Utilisez ceci pour déterminer la présence du ACK sur les paquets
d'une longueur inférieure à 64 octets :
## Vérifie la présence d'un ACK,
## protocol IP 6,
## longueur de l'entête IP 0x5(mots de 32 bits),
## longueur total IP 0x34 (ACK + 12 octets d'options TCP)
## TCP ack actif (bit 5, offset 33)
# tc filter add dev ppp14 parent 1:0 protocol ip prio 10 u32 \
match ip protocol 6 0xff \
match u8 0x05 0x0f at 0 \
match u16 0x0000 0xffc0 at 2 \
match u8 0x10 0xff at 33 \
flowid 1:3
Seuls les paquets TCP sans charge utile et avec le bit ACK
positionné vérifieront cette règle. Ici, nous pouvons voir un
exemple d'utilisation de deux sélecteurs, le résultat final étant
un ET logique de leur résultat. Si nous jetons un coup d'oeil sur
un schéma de l'en-tête TCP, nous pouvons voir que le bit ACK est le
second bit (0x10) du 14ème octet de l'en-tête TCP (at
nexthdr+13). Comme second sélecteur, si nous voulons nous
compliquer la vie, nous pouvons écrire match u8 0x06 0xff at
9 à la place du sélecteur spécifique protocol
tcp, puisque 6 est le numéro du protocole TCP, spécifié au
10ème octet de l'en-tête IP. D'un autre coté, dans cet exemple,
nous ne pourrons pas utiliser de sélecteur spécifique pour la
première correspondance, simplement parce qu'il n'y a pas de
sélecteur spécifique pour désigner les bits TCP ACK.
Les sélecteurs spécifiques
La table suivante contient la liste de tous les sélecteurs
spécifiques que les auteurs de cette section ont trouvés dans le
code source du programme tc. Ils rendent simplement la
vie plus facile en accroissant la lisibilité de la configuration du
filtre.
FIXME: emplacement de la table - la table est dans un
fichier séparé "selector.html"
FIXME: C'est encore en Polonais :-( FIXME: doit être
"sgmlisé"
Quelques exemples :
# tc filter add dev ppp0 parent 1:0 prio 10 u32 \
match ip tos 0x10 0xff \
flowid 1:4
FIXME: tcp dst match does not work as described below:
La règle ci-dessus correspondra à des paquets qui ont le champ
TOS égal à 0x10. Le champ TOS commence au deuxième octet du paquet
et occupe 1 octet, ce qui nous permet d'écrire un sélecteur général
équivalent : match u8 0x10 0xff at 1. Cela nous
donne une indication sur l'implémentation du filtre u32 ; les
règles spécifiques sont toujours traduites en règles générales, et
c'est sous cette forme qu'elles sont stockées en mémoire par le
noyau. Cela amène à une autre conclusion : les sélecteurs
tcp et udp sont exactement les mêmes et
c'est la raison pour laquelle vous ne pouvez pas utiliser un simple
sélecteur match tcp dst 53 0xffff pour désigner un
paquet TCP envoyé sur un port donné : cela désigne aussi les
paquets UDP envoyés sur ce port. Vous devez également spécifier le
protocole avec la règle suivante :
# tc filter add dev ppp0 parent 1:0 prio 10 u32 \
match tcp dst 53 0xffff \
match ip protocol 0x6 0xff \
flowid 1:2
Ce classificateur filtre en se basant sur les informations des
tables de routage. Quand un paquet passant à travers les classes en
atteint une qui est marquée avec le filtre "route", il divise le
paquet en se basant sur l'information de la table de routage.
# tc filter add dev eth1 parent 1:0 protocol ip prio 100 route
Ici, nous ajoutons un classificateur route sur le
noeud parent 1:0, avec la priorité 100. Quand un paquet atteint ce
noeud (ce qui, puisqu'il est racine, arrive immédiatement), il
consulte la table de routage et si une entrée de la table
correspond, il envoie le paquet vers la classe donnée et lui donne
une priorité de 100. Ensuite, pour finalement activer les choses,
vous ajoutez l'entrée de routage appropriée.
L'astuce ici est de définir "realm" en se basant soit sur la
destination, soit sur la source. Voici la façon de faire
cela :
# ip route add Host/Network via Gateway dev Device realm RealmNumber
Par exemple, nous pouvons définir notre réseau de destination
192.168.10.0 avec le nombre "realm" égal à 10 :
# ip route add 192.168.10.0/24 via 192.168.10.1 dev eth1 realm 10
Quand on ajoute des filtres "route", on peut utiliser les
nombres "realm" pour représenter les réseaux ou les hôtes et
spécifier quelle est la correspondance entre les routes et les
filtres.
# tc filter add dev eth1 parent 1:0 protocol ip prio 100 \
route to 10 classid 1:10
La règle ci-dessus indique que les paquets allant vers le réseau
192.168.10.0 correspondent à la classe 1:10.
Le filtre route peut aussi être utilisé avec les routes sources.
Par exemple, il y a un sous-réseau attaché à notre routeur Linux
sur eth2.
# ip route add 192.168.2.0/24 dev eth2 realm 2
# tc filter add dev eth1 parent 1:0 protocol ip prio 100 \
route from 2 classid 1:2
Ici, le filtre spécifie que les paquets venant du réseau
192.168.2.0 (realm 2) correspondront à la classe 1:2.
Pour réaliser des configurations encore plus compliquées, vous
pouvez avoir des filtres qui analysent le trafic que jusqu'à une
certaine bande passante. Vous pouvez configurer un filtre pour
qu'il cesse entièrement l'analyse de tout le trafic au-dessus d'un
certain débit ou pour qu'il n'analyse pas la bande passante
dépassant un certain débit.
Ainsi, si vous décidez de réglementer à 4mbit/s, mais qu'un
trafic de 5mbit/s est présent, vous pouvez cesser d'analyser
l'ensemble des 5mbit/s ou seulement cesser d'analyser le 1 mbit/s
supplémentaire et envoyer 4 mbit/s à la classe correspondante.
Si la bande passante dépasse le débit configuré, vous pouvez
rejeter un paquet, le reclassifier ou voir si un autre filtre y
correspond.
Manières de réglementer
Il y a essentiellement deux façons de réglementer. Si vous avez
compilé le noyau avec 'Estimators', celui-ci peut mesurer plus ou
moins pour chaque filtre le trafic qui est passé. Ces estimations
ne sont pas coûteuses en temps CPU, étant donné qu'il ne compte que
25 fois par seconde le nombre de données qui sont passées, et qu'il
calcule le débit à partir de cela.
L'autre manière utilise encore le Token Bucket Filter qui, cette
fois, réside à l'intérieur du filtre. Le TBF n'analyse seulement le
trafic que JUSQU'A la bande passante que vous avez configurée. Si
cette bande passante est dépassée, seul l'excès est traité par
l'action de dépassement de limite configurée.
Avec l'estimateur du noyau
Ceci est très simple et il n'y a qu'un seul paramètre :
avrate. Soit le flux demeure sous avrate, et le filtre classifie le
trafic vers la classe appropriée, soit votre débit le dépasse et,
dans ce cas, l'action indiquée est réalisée qui, par défaut, est la
'reclassification'.
Le noyau utilise l'algorithme EWMA pour votre bande passante, ce
qui la rend moins sensible aux courtes rafales de données.
Avec le Token Bucket Filter
Utilisez les paramètres suivants :
buffer/maxburst
mtu/minburst
mpu
rate
Ceux-ci se comporte la plupart du temps de manière identique à
ceux décrits dans la section Token Bucket Filter. Notez cependant
que si vous configurez le mtu du filtre de réglementation TBF trop
bas, aucun paquet ne passera, tandis que le gestionnaire de mise en
file d'attente de sortie TBF ne fait que les ralentir.
Une autre différence est que la réglementation ne peut que
laisser passer ou jeter un paquet. Il ne peut pas le retenir dans
le but de le retarder.
Actions de dépassement de limite (Overlimit actions)
Si votre filtre décide qu'un dépassement de limite est atteint,
il peut mettre en oeuvre des 'actions'. Actuellement, trois actions
sont disponibles :
continue
Provoque l'arrêt de l'analyse du filtre, bien que d'autres
filtres puissent peut-être continuer à le faire.
drop
Ceci est une option très féroce qui supprime simplement le
trafic excédant un certain débit. Elle est souvent employée dans le
"Ingress policer" et a des utilisations limitées. Par exemple, si
vous avez un serveur de nom qui s'écroule s'il traite plus de
5mbit/s de paquets, alors, dans ce cas, vous pourrez utiliser un
filtre d'entrée pour être sûr qu'il ne traitera jamais plus de
5mbit/s.
Pass/OK
Transmettre le trafic. Pourrait être utilisé pour mettre hors
service un filtre compliqué, tout en le laissant en place.
reclassify
Permet le plus souvent une reclassification vers Best Effort.
Ceci est l'action par défaut.
Exemples
Le seul vrai exemple connu est mentionné dans la section
'Protéger votre machine des inondations SYN'
FIXME: Si vous avez déjà utilisé ceci, partagez s'il vous plaît
votre expérience avec nous.
Si vous avez besoin de milliers de règles, par exemple, dans le
cas où vous avez beaucoup de clients ou d'ordinateurs, tous avec
des spécifications QoS différentes, vous pourrez constater que le
noyau passe beaucoup de temps à analyser toutes ces règles.
Par défaut, tous les filtres résident dans une grande chaîne qui
est analysée par ordre décroissant des priorités. Si vous avez 1000
règles, 1000 contrôles peuvent être nécessaires pour déterminer ce
qu'il faut faire d'un paquet.
La vérification irait plus vite s'il y avait 256 chaînes avec
chacune quatre règles. Si vous pouviez répartir les paquets sur ces
256 chaînes, afin que la bonne règle soit présente.
Ceci est rendu possible par le hachage. Imaginons que vous ayez
sur votre réseau 1024 clients avec des modems câble, avec des
adresses IP allant de 1.2.0.0 à 1.2.3.255, et que chacun doit avoir
un classement particulier, par exemple 'pauvre', 'moyen' et
'bourge'. Cela vous ferait alors 1024 règles, dans le
genre :
# tc filter add dev eth1 parent 1:0 protocol ip prio 100 match ip src \
1.2.0.0 classid 1:1
# tc filter add dev eth1 parent 1:0 protocol ip prio 100 match ip src \
1.2.0.1 classid 1:1
...
# tc filter add dev eth1 parent 1:0 protocol ip prio 100 match ip src \
1.2.3.254 classid 1:3
# tc filter add dev eth1 parent 1:0 protocol ip prio 100 match ip src \
1.2.3.255 classid 1:2
Pour aller plus vite, nous pouvons utiliser la dernière partie
de l'adresse IP comme 'clé de hachage'. Nous obtenons alors 256
tables, la première ressemblant à ceci :
# tc filter add dev eth1 parent 1:0 protocol ip prio 100 match ip src \
1.2.0.0 classid 1:1
# tc filter add dev eth1 parent 1:0 protocol ip prio 100 match ip src \
1.2.1.0 classid 1:1
# tc filter add dev eth1 parent 1:0 protocol ip prio 100 match ip src \
1.2.2.0 classid 1:3
# tc filter add dev eth1 parent 1:0 protocol ip prio 100 match ip src \
1.2.3.0 classid 1:2
La suivante commence comme ceci :
# tc filter add dev eth1 parent 1:0 protocol ip prio 100 match ip src \
1.2.0.1 classid 1:1
...
De cette manière, seuls quatre recherches au plus sont
nécessaires, deux en moyenne.
La configuration est plutôt compliqué, mais elle en vaut
vraiment la peine du fait des nombreuses règles. Nous créons
d'abord un filtre racine, puis une table avec 256
entrées :
# tc filter add dev eth1 parent 1:0 prio 5 protocol ip u32
# tc filter add dev eth1 parent 1:0 prio 5 handle 2: u32 divisor 256
Nous ajoutons maintenant des règles dans la table précédemment
créée :
# tc filter add dev eth1 protocol ip parent 1:0 prio 5 u32 ht 2:7b: \
match ip src 1.2.0.123 flowid 1:1
# tc filter add dev eth1 protocol ip parent 1:0 prio 5 u32 ht 2:7b: \
match ip src 1.2.1.123 flowid 1:2
# tc filter add dev eth1 protocol ip parent 1:0 prio 5 u32 ht 2:7b: \
match ip src 1.2.3.123 flowid 1:3
# tc filter add dev eth1 protocol ip parent 1:0 prio 5 u32 ht 2:7b: \
match ip src 1.2.4.123 flowid 1:2
Ceci est l'entrée 123, qui contient les correspondances pour
1.2.0.13, 1.2.1.123, 1.2.2.123 et 1.2.3.123 et qui les envoient
respectivement vers 1:1, 1:2, 1:3 et 1:2. Notez que nous devons
spécifier notre seau de hachage en hexadécimal, 0x7b pour 123.
Nous créons ensuite un 'filtre de hachage' qui dirige le trafic
vers la bonne entrée de la table de hachage :
# tc filter add dev eth1 protocol ip parent 1:0 prio 5 u32 ht 800:: \
match ip src 1.2.0.0/16 \
hashkey mask 0x000000ff at 12 \
link 2:
Ok, certains nombres doivent être expliqués. La table de hachage
par défaut est appelée 800:: et tous les filtres démarrent de là.
Nous sélectionnons alors l'adresse source qui est en position 12,
13, 14 et 15 dans l'entête IP, et indiquons que seule la dernière
partie nous intéresse. Ceci est envoyé vers la table de hachage 1:
qui a été créé plus tôt.
C'est plutôt compliqué, mais cela marche en pratique et les
performances seront époustouflantes. Notez que cet exemple pourrait
être améliorer pour que chaque chaîne contienne un filtre, ce qui
représenterait le cas idéal !