Iproute 2

Avertissement

Ce qui suit suppose comme acquis tout ce qui est dit dans les chapitres relatifs au routage :

Au bon vieux temps...

Tous les utilisateurs de GNU/Linux connaissent les commandes route et ifconfig. Bien qu'elles soient toujours efficaces, les évolutions des fonctions de routage des noyaux 2.4.x et supérieurs ont fait qu'elles ne rendent plus compte que d'une partie des possibilités.

Pour exploiter au mieux les possibilités de routage des noyaux actuels, il faut installer le paquetage iproute (iproute2 dans d'autres distributions, comme la Mandrake).

Iproute2, allié à Netfilter, donne un couple capable de faire de grandes choses. Comme il faut bien commencer par un bout , voyons d'abord en quoi iproute2 peut nous aider.

Présentation rapide d'Iproute2

Les interfaces du réseau

Nous savons que la commande ifconfig nous montre les interfaces réseau en service sur notre machine :

betelgeuse:~# ifconfig
eth0      Link encap:Ethernet  HWaddr 00:20:18:2D:D2:91
          inet addr:192.168.100.30  Bcast:192.168.100.255  Mask:255.255.255.0
          inet6 addr: fe80::220:18ff:fe2d:d291/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:19370201 errors:0 dropped:0 overruns:0 frame:0
          TX packets:3279270 errors:0 dropped:0 overruns:0 carrier:0
          collisions:150124 txqueuelen:1000
          RX bytes:3979103861 (3.7 GiB)  TX bytes:376345956 (358.9 MiB)
          Interrupt:11 Base address:0x2000

eth1      Link encap:Ethernet  HWaddr 00:90:27:71:43:C7
          inet addr:192.168.0.250  Bcast:192.168.0.255  Mask:255.255.255.0
          inet6 addr: fe80::290:27ff:fe71:43c7/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:3129241 errors:0 dropped:0 overruns:0 frame:0
          TX packets:3344387 errors:0 dropped:0 overruns:0 carrier:21
          collisions:478776 txqueuelen:1000
          RX bytes:317538639 (302.8 MiB)  TX bytes:2507642937 (2.3 GiB)
          Interrupt:11 Base address:0x7000

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:28040 errors:0 dropped:0 overruns:0 frame:0
          TX packets:28040 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:3395473 (3.2 MiB)  TX bytes:3395473 (3.2 MiB)

ppp0      Link encap:Point-to-Point Protocol
          inet addr:80.8.154.12  P-t-P:80.8.128.1  Mask:255.255.255.255
          UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1492  Metric:1
          RX packets:6405 errors:0 dropped:0 overruns:0 frame:0
          TX packets:4791 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:3
          RX bytes:5209426 (4.9 MiB)  TX bytes:662798 (647.2 KiB)

tun1      Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
          inet addr:192.168.18.2  P-t-P:192.168.18.1  Mask:255.255.255.255
          UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1500  Metric:1
          RX packets:685 errors:0 dropped:0 overruns:0 frame:0
          TX packets:685 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:100
          RX bytes:54460 (53.1 KiB)  TX bytes:73640 (71.9 KiB)

Très verbeux, nous avons pas mal d'informations sur les diverses interfaces. L'exemple est pris sur une passerelle qui assure la connexion à l'internet via un lien PPP, et qui présente également un lien vers un tunnel Open VPN (tun1).

Iproute dispose d'une commande : ip suivie de plusieurs arguments. Utilisons ici ip addr :

betelgeuse:~# ip addr list
1: lo: <LOOPBACK,UP> mtu 16436 qdisc noqueue
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast qlen 1000
    link/ether 00:20:18:2d:d2:91 brd ff:ff:ff:ff:ff:ff
    inet 192.168.100.30/24 brd 192.168.100.255 scope global eth0
    inet6 fe80::220:18ff:fe2d:d291/64 scope link
       valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast qlen 1000
    link/ether 00:90:27:71:43:c7 brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.250/24 brd 192.168.0.255 scope global eth1
    inet6 fe80::290:27ff:fe71:43c7/64 scope link
       valid_lft forever preferred_lft forever
4: sit0: <NOARP> mtu 1480 qdisc noop
    link/sit 0.0.0.0 brd 0.0.0.0
254: ppp0: <POINTOPOINT,MULTICAST,NOARP,UP> mtu 1492 qdisc pfifo_fast qlen 3
    link/ppp
    inet 80.8.154.12 peer 80.8.128.1/32 scope global ppp0
255: tun1: <POINTOPOINT,MULTICAST,NOARP,UP> mtu 1500 qdisc pfifo_fast qlen 100
    link/[65534]
    inet 192.168.18.2 peer 192.168.18.1/32 scope global tun1

Je vous laisse passer du temps à comparer les deux informations. Formulées de manières différentes (et pas forcément plus lisibles), ce sont, en partie, les mêmes.

Nous avons aussi à notre disposition les commandes :

ip link list, qui affiche la liste des interfaces :

betelgeuse:~# ip link list
1: lo: <LOOPBACK,UP> mtu 16436 qdisc noqueue
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast qlen 1000
    link/ether 00:20:18:2d:d2:91 brd ff:ff:ff:ff:ff:ff
3: eth1: <BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast qlen 1000
    link/ether 00:90:27:71:43:c7 brd ff:ff:ff:ff:ff:ff
4: sit0: <NOARP> mtu 1480 qdisc noop
    link/sit 0.0.0.0 brd 0.0.0.0
913: ppp0: <POINTOPOINT,MULTICAST,NOARP,UP> mtu 1492 qdisc pfifo_fast qlen 3
    link/ppp
914: tun1: <POINTOPOINT,MULTICAST,NOARP,UP> mtu 1500 qdisc pfifo_fast qlen 100
    link/[65534]

ip neigh list, qui affiche la table ARP, un peu à la manière de la commande arp -a :

betelgeuse:~# ip neigh list
2001:4f8:0:2::8 dev eth0  nud failed
192.168.0.13 dev eth1 lladdr 00:20:18:2f:d1:8c nud stale
192.168.0.12 dev eth1 lladdr 00:20:18:2f:0e:35 nud reachable
192.168.0.11 dev eth1 lladdr 00:20:18:2a:f5:ca nud reachable
192.168.0.67 dev eth1 lladdr 00:90:96:c4:d9:e0 nud stale
192.168.0.10 dev eth1 lladdr 00:05:5d:4a:f1:c8 nud reachable
192.168.0.64 dev eth1 lladdr 00:40:05:de:68:c3 nud stale

Les routes

Voyons maintenant la commande route :

betelgeuse:~# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
192.168.18.1    0.0.0.0         255.255.255.255 UH    0      0        0 tun1
80.8.128.1      0.0.0.0         255.255.255.255 UH    0      0        0 ppp0
192.168.100.0   0.0.0.0         255.255.255.0   U     0      0        0 eth0
192.168.0.0     0.0.0.0         255.255.255.0   U     0      0        0 eth1
172.16.0.0      192.168.18.1    255.255.0.0     UG    0      0        0 tun1
0.0.0.0         80.8.128.1      0.0.0.0         UG    0      0        0 ppp0

La commande ip, suivie de l'argument route, nous donne :

betelgeuse:~# ip route list
192.168.18.1 dev tun1  proto kernel  scope link  src 192.168.18.2
80.8.128.1 dev ppp0  proto kernel  scope link  src 80.8.154.12
192.168.100.0/24 dev eth0  proto kernel  scope link  src 192.168.100.30
192.168.0.0/24 dev eth1  proto kernel  scope link  src 192.168.0.250
172.16.0.0/16 via 192.168.25.1 dev tun1
default via 80.8.128.1 dev ppp0

Moins lisible, assurément. Mais plus riche en informations. Voyons ça de plus près.

Avec route, nous pensions qu'il n'y avait qu'une table de routage. En réalité, il y en a plusieurs. Dans la terminologie iproute2, les tables sont exploitées en fonction de règles (rules en anglais). Nous pouvons avoir un aperçu d'un routage standard de la façon suivante :

betelgeuse:/etc/iproute2# ip rule list
0:      from all lookup local
32766:  from all lookup main
32767:  from all lookup default

Tout ceci voudrait dire qu'il y a au moins trois tables de routage, local, main et default, qui seraient auscultées pour router tous les paquets entrant dans le routeur, quelle que soit leur  provenance (from all) et dans l'ordre indiqué par l'index numérique placé juste avant chaque règle ?

Absolument, c'est bien comme ça que ça se passe.

Et donc, il serait possible, avec la commande ip, de voir ce qu'il y a dans chacune de ces tables ?

Oui. Il suffit d'en préciser le nom :

betelgeuse:/etc/iproute2# ip route list table main
192.168.18.1 dev tun1  proto kernel  scope link  src 192.168.18.2
80.8.128.1 dev ppp0  proto kernel  scope link  src 80.8.154.12
192.168.100.0/24 dev eth0  proto kernel  scope link  src 192.168.100.30
192.168.0.0/24 dev eth1  proto kernel  scope link  src 192.168.0.250
172.16.0.0/16 via 192.168.25.1 dev tun1
default via 80.8.128.1 dev ppp0

C'est la même que tout à l'heure !

Oui, c'est la même. Preuve que l'antique commande route ne nous dit pas tout, puisque avant main, il y a local, et que cette table n'est pas vide du tout :

betelgeuse:/etc/iproute2# ip route list table local
broadcast 192.168.100.0 dev eth0  proto kernel  scope link  src 192.168.100.30
broadcast 192.168.0.255 dev eth1  proto kernel  scope link  src 192.168.0.250
broadcast 127.255.255.255 dev lo  proto kernel  scope link  src 127.0.0.1
local 80.8.154.12 dev ppp0  proto kernel  scope host  src 80.8.154.12
local 192.168.0.250 dev eth1  proto kernel  scope host  src 192.168.0.250
local 192.168.18.2 dev tun1  proto kernel  scope host  src 192.168.18.2
broadcast 192.168.100.255 dev eth0  proto kernel  scope link  src 192.168.100.30
broadcast 192.168.0.0 dev eth1  proto kernel  scope link  src 192.168.0.250
broadcast 127.0.0.0 dev lo  proto kernel  scope link  src 127.0.0.1
local 192.168.100.30 dev eth0  proto kernel  scope host  src 192.168.100.30
local 127.0.0.1 dev lo  proto kernel  scope host  src 127.0.0.1
local 127.0.0.0/8 dev lo  proto kernel  scope host  src 127.0.0.1

Mais comme vous le voyez, cette table ne concerne que le routage local et les « broadcasts », rien de bien palpitant. Quant à la table default, elle est vide par défaut.

Mais alors, il y aurait la possibilité de créer des tables qui ne seraient utilisées que dans certaines conditions ?

C'est une partie de l'intérêt et de la puissance du modèle Iproute2. Parce qu'en plus de ça, Iproute2 sait faire plein d'autres choses. Pour vous en convaincre, le mieux est de lire la documentation officielle : Advanced Routing HOWTO, dont il existe une version en français. Il n'est bien entendu pas question ici de voir tout ce qu'il est possible de faire, l'objectif est juste de pouvoir réaliser un routage sélectif en fonction d'un numéro de port TCP.

Mise en place d'une règle de routage

Dans ce qui suit, la machine qui va servir de passerelle miroir s'appelle « saturne ».

La liste des tables qui existent sur votre machine se trouve dans le fichier /etc/iproute2/rt_tables. Vous ne pourrez pas créer de règles (rules) associées à une table qui n'est pas référencée dans ce fichier. Comme nous aurons besoin d'une table de routage spécifique pour le protocole POP3, nous allons créer une entrée supplémentaire dans ce fichier qui, après modification, aura l'allure suivante :

saturne:~# cat /etc/iproute2/rt_tables
#
# reserved values
#

200     pop3
255     local
254     main
253     default
0       unspec
#
# local
#
1       inr.ruhep

Cette manipulation, en elle même n'apporte rien aux règles en vigueur :

saturne:~# ip rule list
0:      from all lookup local
32766:  from all lookup main
32767:  from all lookup default

Si nous voulons ajouter une règle, il faudra le dire explicitement de la façon suivante (prenez-le comme une recette pour le moment) :

saturne:~# ip rule add fwmark 6e table pop3

Ceci a pour effet d'ajouter une règle, que nous comprendrons mieux par la suite. Disons pour le moment que, lorsqu'un paquet contient la marque 0x6e (valeur hexadécimale), il devra être routé en fonction des informations contenues dans la table de routage pop3.

saturne:~# ip rule list
0:      from all lookup local
32765:  from all fwmark       6e lookup pop3
32766:  from all lookup main
32767:  from all lookup default

Nous voyons effectivement apparaitre une ligne supplémentaire dans la liste des règles.

Mais la table de routage pop3 est vide. Il faut la peupler un petit peu. En réalité, qu'avons-nous besoin de faire, en fonction de la topologie donnée ? Il nous suffit de dire que pour ces paquets, la route par défaut n'est pas 192.168.0.1, mais 192.168.0.3, adresse ip du serveur mandataire. Faisons le :

ip route add default via 192.168.0.3 dev eth0 table pop3

Bien. Pour vérifier :

saturne:~# ip route list table pop3
default via 192.168.0.3 dev eth0

Et si nous regardons la table de routage principale :

saturne:~# ip route list table main
192.168.0.0/24 dev eth0  proto kernel  scope link  src 192.168.0.2
default via 192.168.0.1 dev eth0

Autrement dit, si tout se passe comme prévu, tous les paquets sortant du poste de travail (qui est configuré pour voir 192.168.0.2 comme passerelle par défaut) seront aiguillés vers 192.168.0.1 (la vraie passerelle vers l'internet), sauf les paquets marqués “6e”, qui, eux, seront aiguillés vers 192.168.0.3 (le futur proxy transparent POP3).

Pour pouvoir vérifier si tout ça fonctionne, il faut commencer par pouvoir marquer ces paquets avec le label 6e, ce que nous n'avons pas encore fait.

Pour réaliser cette opération, nous aurons recours à Netfilter, avec IPtables.