Les classificateurs sont les moyens par lesquels le noyau décide dans quelle file d'attente un paquet sera placé. Il y a divers classificateurs, chacun d'eux pouvant être utilisé pour différents buts.
Base la décision sur la façon dont la pare-feu a marqué les paquets.
Base la décision sur les champs à l'intérieur du paquet (c'est-à-dire l'adresse IP source, etc.)
Base la décision sur la route à emprunter par le paquet.
Base la décision sur la cible (destination, protocole) et, optionnellement, sur la source (je pense).
FIXME: Remplissez-moi
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 :
Le protocole que ce classificateur acceptera. Généralement, on n'acceptera que le trafic IP. Exigé.
La référence à laquelle ce classificateur est attaché. Cette référence doit être une classe déjà existante. Exigé.
La priorité de ce classificateur. Les plus grand nombres seront testés en premier.
Cette référence a plusieurs significations suivant les différents filtres.
FIXME: En ajouter d'autres
Toutes les sections suivantes supposeront que vous essayez de mettre
en forme le trafic allant vers HostA
. Ces sections
suppposeront 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 classificateur "fw" s'appuie sur le marquage des paquets à mettre en forme par le pare-feu. Donc, nous configurerons d'abord le pare-feu pour les marquer :
# iptables -I PREROUTING -t mangle -p tcp -d HostA \
-j MARK --set-mark 1
Maintenant, tous les paquets vers cette machine (HostA) sont balisés avec la marque 1. À présent, nous construisons les règles de mise en forme pour mettre en forme les paquets. Nous avons juste besoin d'indiquer que les paquets balisés avec la marque 1 vont vers la classe 1:1. C'est fait par la commande suivante :
# tc filter add dev eth1 protocol ip parent 1:0 prio 1 handle 1 fw classid 1:1
Cela devrait se comprendre de soi-même. On attache à la classe 1:0 un filtre avec la priorité 1 pour filtrer tous les paquets marqués à 1 par le pare-feu vers la classe 1:1. Noter l'utilisation du paramètre "handle" pour indiquer la marque attendue.
C'est tout ce qu'il y a à faire ! C'est le procédé le plus simple (à mon humble avis). Je pense que les autres procédés sont plus difficiles à comprendre. Notez que toute la puissance du pare-feu peut être utilisée avec ce classificateur. Cela inclut l'analyse des adresses MAC, des identificateurs d'utilisateurs (user ID) et tout ce que le firewall peut traiter.
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 accomplie. Le type d'action le plus simple serait de diriger le paquet vers une classe CBQ définie.
La ligne de commande du programme filtre tc
, 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 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.
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 \
Cette règle correspondra seulement aux paquets TCP avec le bit ACK
positionné. Ici, nous pouvons voir un exemple d'utilisation de deux
sélecteurs, le résultat final étant un ET logique de leurs
résultats. Si vous jetez un oeil sur un schéma de l'en-tête TCP, vous
pouvez 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
vous voulez vous compliquer la vie, vous pouvez é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. En revanche, dans cet exemple, vous ne pourrez
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.
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
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 le résultat 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.
FIXME: à remplir
FIXME: à remplir