Routage sélectif vers une connexion internet secondaire

[size=150]NOTE : pour toute remarque ou question, merci d’utiliser le fil de discussion prévu à cet effet.[/size]

[size=125]ATTENTION, ce tuto est en cours de refonte complète pour intégrer une solution dans les cas où les interfaces réseau sont instables et susceptibles d’être déconnectées (par exemple une connexion PPP ou VPN fermée par le pair).[/size]

Avertissement : ce T&A suppose une configuration assez basique, où l’utilisateur n’a pas (trop) bidouillé avec ses tables de routage. Je me permets de prendre des raccourcis au niveau des explications pour simplifier certains concepts, en partant du principe que si vous êtes assez compétent pour altérer vos tables de routage sans mon aide, alors vous saurez corriger et compléter mes explications de vous-même. Si vous voulez faire des remarques à propos de certains raccourcis que j’emploie, demandez-vous avant s’il ne s’agit pas de généralisations / vulgarisations et si un utilisateur lambda a réellement besoin de faire la distinction. Ça évitera des pinaillages sans fin et stériles… :wink:
Mais que ça ne vous empêche pas de poser des questions si quelque chose n’est pas clair !

[size=200]0) Contexte[/size]

Une machine dispose de plusieurs accès internet distincts.
On veut forcer certaines connexions à utiliser un accès internet particulier, tout en continuant d’utiliser la route par défaut pour la majorité des connexions.

Autrement dit, le but du jeu est de forcer certaines connexions à utiliser une interface réseau et une passerelle spécifiques selon nos bons désirs.

Pour parvenir à ce résultat, nous allons utiliser une combinaison de marquage de paquets (iptables -j MARK), de translation NAT (iptables -j SNAT) et de tables de routage alternatives (iproute2).

On supposera par la suite que l’on dispose de la configuration suivante :

[code]$ cat /etc/network/interfaces

loopback

auto lo
iface lo inet loopback
address 127.0.0.1
netmask 255.0.0.0

interface primaire

allow-hotplug eth0
auto eth0
iface eth0 inet static
address 192.168.1.100
netmask 255.255.255.0
gateway 192.168.1.1

interface secondaire

allow-hotplug eth1
auto eth1
iface eth1 inet static
address 192.168.2.100
netmask 255.255.255.0
gateway 192.168.2.1[/code]# route Table de routage IP du noyau Destination Passerelle Genmask Indic Metric Ref Use Iface 192.168.2.0 * 255.255.255.0 U 0 0 0 eth1 192.168.1.0 * 255.255.255.0 U 0 0 0 eth0 default 192.168.1.1 0.0.0.0 UG 0 0 0 eth0
Autrement dit, eth0 permet d’accéder à internet via la passerelle par défaut (192.168.1.1), et la passerelle 192.168.2.1 (accessible via eth1) n’est pour le moment pas utilisée.

Il faudra bien entendu adapter ce T&A à votre configuration réelle, qui peut être très variable. Par exemple, vous pouvez très bien remplacer l’interface physique eth1 par n’importe quelle interface virtuelle telle qu’un tunnel IP ou une connexion point à point. À vous de voir…

[size=200]1) Stratégie de routage[/size]

Il faut tout d’abord déterminer quelles connexions nous souhaitons router vers quel accès internet.
Le choix est vaste, pour ma part j’ai choisi de router toutes les connexions de certains process vers un accès donné (eth1 / 192.168.2.1), et de laisser tout le reste utiliser l’accès par défaut (eth0 / 192.168.1.1).

La sélection des process concernés se fait à l’aide d’un groupe spécifique, auquel j’ajoute mon compte utilisateur :

[code]# addgroup route-eth1

adduser utilisateur route-eth1[/code]

Tous les process continueront d’utiliser l’accès fourni par eth0 / 192.168.1.1, sauf ceux que je lance avec ce groupe spécifique et qui utiliseront alors eth1 / 192.168.2.1 :

$ newgrp route-eth1 $ mon-process-routé-via-eth1$ sg route-eth1 "mon-process-routé-via-eth1"

Il est bien entendu possible de choisir d’autres stratégies (par exemple en fonction du port de destination), auquel cas il faudra adapter la règle de marquage de paquets dans iptables (voir la section (5) ci-après). Il est également possible d’avoir plusieurs stratégies actives en parallèle.
Si vous avez des idées / exemples de stratégies de routage utiles, n’hésitez pas à en faire part, je les inclurai dans ce T&A !

Important : pour chaque stratégie de routage, nous allons avoir besoin de deux choses :

  • une valeur arbitraire, utilisée pour le marquage des paquets dans iptables (de 0 à 4294967295)
  • une valeur arbitraire, utilisée pour identifier la table de routage alternative (de 1 à 2147483647 sauf 253, 254 et 255 – attention, toute table >= 256 ne peut plus être supprimée avec ip rule del table mais uniquement avec ip rule del priority, ces valeurs sont donc à utiliser en toute connaissance de cause)

Le plus simple est de faire correspondre le marquage iptables à la table de routage et de choisir une valeur entre 1 et 252 inclus. Par la suite, j’utiliserai la valeur 78 pour les deux.
Si vous souhaitez appliquer plusieurs stratégies différentes en parallèle, il faudra bien évidemment choisir une valeur pour chaque stratégie (qui ne rentre en conflit avec aucun marquage iptables précédent et aucune table de routage secondaire – si vous n’avez jamais joué avec ça avant ce T&A alors il y a peu de risques de conflit).

[size=200]2) Mise en place de la table de routage alternative[/size]

On va tout d’abord recopier la table de routage principale, en omettant toutefois l’entrée par défaut (qui correspond à la passerelle et donc à l’accès internet) :

[code]# ip route flush table 78

ip route show table main | grep -Ev ^default | while read ROUTE; do ip route add table 78 $ROUTE; done[/code]

On ajoute ensuite la route par défaut pour la passerelle secondaire :

[size=200]3) Activation du routage[/size]

Cette commande permet de faire le lien entre les paquets marqués en (5) et la table de routage alternative définie en (2) :

[code]# ip rule add fwmark 78 table 78

ip route flush cache[/code]

[size=200]4) Mise en place de la translation NAT[/size]

Pour le moment, on est (potentiellement) en présence de paquets qui sont systématiquement redirigés depuis eth0 vers eth1. Le problème est que ces paquets contiennent toujours l’adresse IP d’eth0 comme adresse source, ce qui va poser des problèmes si on ne fait rien : la passerelle 192.168.2.1 va avoir du mal à comprendre pourquoi elle reçoit des paquets provenant de 192.168.1.100 alors que cette adresse n’est pas sur le même réseau !
Qu’à cela ne tienne, il suffit d’effectuer une translation NAT de l’adresse source pour les paquets concernés :

[size=200]5) Marquage des paquets[/size]

Il faut enfin créer une règle iptables pour marquer les paquets correspondants à la stratégie choisie.
Cette règle devra toujours se trouver dans la table mangle, dans la chaîne OUTPUT.

Pour marquer uniquement les paquets des process lancés en utilisant le groupe route-eth1 :

# iptables -t mangle -A OUTPUT -m owner --gid-owner route-eth1 -j MARK --set-mark 78Encore une fois, adaptez cette règle selon la stratégie que vous avez choisie en (1).

Il est important que cette règle soit créée à la fin de la procédure : sans elle aucun paquet n’est routé par notre méthode, ce qui était primordial tant que toutes les autres règles et commandes n’étaient pas mises en place.

[size=200]6) Comportement si l’interface est down[/size]

Lorsque l’interface eth1 n’est plus disponible, toutes les entrées correspondantes dans les tables de routage sont supprimées.

Cela signifie que :

  • les paquets précédemment routés vers l’accès internet secondaire (eth1 / 192.168.2.1) vont à nouveau utiliser l’accès principal (eth0 / 192.168.1.1).
  • les connexions existantes vont être bloquées car le pair (à l’autre bout d’internet) attend des paquets provenant de l’IP publique de l’accès internet secondaire et non pas de celle de l’accès internet principal. Elles se fermeront d’elles-mêmes lorsque tous les timeouts auront pris effet, ce qui peut durer assez longtemps et bloquer le process émetteur en attendant.
  • les nouvelles connexions vont utiliser l’accès principal.

Dans une version précédente de ce T&A j’avais indiqué une méthode pour bloquer les connexions dans ce cas de figure, mais je me suis aperçu que ça déconnait sévère. En attendant que moi ou quelqu’un d’autre trouve une solution fiable, il faudra se contenter de subir les restrictions expliquées ci-dessus.

[size=200]7) Test du fonctionnement[/size]

Étant donné que nous avons affaire à deux accès internet distincts, il n’est pas déraisonnable de s’attendre à ce que leurs IPs publiques soient différentes :

[code]$ echo $(wget -q -O - http://www.whatismyip.org/)
80.120.xxx.xxx
$ echo "$(sg route-eth1 "wget -q -O - http://www.whatismyip.org/")"
73.98.xxx.xxx

ifdown eth1

$ echo "$(sg route-eth1 "wget -q -O - http://www.whatismyip.org/")"
80.120.xxx.xxx[/code]

[size=200]8) Chronologie du parcours d’un paquet[/size]

Histoire de comprendre un peu mieux comment le tout fonctionne, voici comment un paquet parcourt toute la chaîne des règles que nous avons mises en place :

  • création du paquet par un process local
  • règle (5), marquage du paquet ou non
  • règle (3), choix de la table de routage à utiliser : table définie en (2) ou bien la route principale, cela détermine quelle passerelle utiliser
  • règle (4), translation NAT (ignorée si le paquet n’est pas rerouté)
  • émission du paquet

[size=200]9) En résumé[/size]

Ce bout de script permet de bien séparer la création/suppression des règles génériques, et les stratégies employées :

[code]# Créer les règles de routage (avant de créer le marquage de paquets)

Usage:

createRoutingPolicy MARKER IFACE IFACE_IP GATEWAY

createRoutingPolicy()
{
local MARKER="$1"
local IFACE="$2"
local IFACE_IP="$3"
local GATEWAY="$4"

(2) table de routage alternative

ip route flush table "$MARKER"
ip route show table main | grep -Ev ^default | while read ROUTE; do ip route add table “$MARKER” $ROUTE; done
ip route add table “$MARKER” default via “$GATEWAY” dev “$IFACE”

(3) activation du routage

ip rule add fwmark “$MARKER” table "$MARKER"
ip route flush cache

(4) translation NAT

iptables -t nat -A POSTROUTING -o “$IFACE” -m mark --mark “$MARKER” ! -s “$IFACE_IP” -j SNAT --to-source “$IFACE_IP”
}

Créer le marquage de paquets (une fois les règles de routage en place)

Usage:

createMarkingPolicy MARKER [REGLES_IPTABLES]*

createMarkingPolicy()
{
local MARKER="$1"
shift

(5) ajout du marquage de paquets

iptables -t mangle -A OUTPUT “$@” -j MARK --set-mark “$MARKER”
}

Supprimer le marquage de paquets (avant de supprimer les règles de routage)

Usage:

removeMarkingPolicy MARKER [REGLES_IPTABLES]*

removeMarkingPolicy()
{
local MARKER="$1"
shift

(5) suppression du marquage de paquets

iptables -t mangle -D OUTPUT “$@” -j MARK --set-mark “$MARKER”
}

Supprimer les règles de routage (une fois le marquage de paquets supprimé)

Usage:

removeRoutingPolicy MARKER IFACE IFACE_IP

removeRoutingPolicy()
{
local MARKER="$1"
local IFACE="$2"
local IFACE_IP="$3"

(4) translation NAT

iptables -t nat -D POSTROUTING -o “$IFACE” -m mark --mark “$MARKER” ! -s “$IFACE_IP” -j SNAT --to-source “$IFACE_IP”

(3) désactivation du routage

ip rule del fwmark “$MARKER” table “$MARKER”

(2) table de routage alternative

ip route flush table "$MARKER"
ip route flush cache
}[/code][code]#

stratégie : router les process appartenant à un groupe spécifique

configuration des règles de routage

MARKER='78’
IFACE='eth1’
IFACE_IP='192.168.2.100’
GATEWAY=‘192.168.2.1’

configuration de la stratégie (groupe à router vers la connexion secondaire)

ROUTED_GROUP=‘route-eth1’

création de la stratégie

createRoutingPolicy “$MARKER” “$IFACE” “$IFACE_IP” "$GATEWAY"
createMarkingPolicy “$MARKER” -m owner --gid-owner “$ROUTED_GROUP”

suppression de la stratégie

removeMarkingPolicy “$MARKER” -m owner --gid-owner "$ROUTED_GROUP"
removeRoutingPolicy “$MARKER” “$IFACE” “$IFACE_IP”[/code]

[size=200]10) Enjoy…[/size]

:smiley: