Netfilter : NAT version 2.4

Lorsque vous désirez partager une connexion Internet à travers tout un réseau local, la solution courante qui s'offre à vous est le NAT. Le NAT ou Network Adress Translation permet de modifier "au vol" les paquets qui passent par la machine, faisant la liaison entre votre réseau et Internet.

De manière plus claire, la machine faisant du NAT reçoit les paquets et les modifie pour offrir un accès au Net. Ainsi, les machines de l'ensemble du réseau local peuvent partager une seule et même connexion. Il existe grossièrement deux sortes de NAT. Le Destination NAT permet de modifier l'adresse de destination des paquets réseaux. Ce type de NAT, appelé également DNAT, est la technologie mise en oeuvre pour certains types de proxies et le port forwarding. Ce type de NAT se fait avant le routage; nous verrons plus loin dans l'article que ceci est très important.

Le NAT qui nous intéresse ici est le SNAT (Source NAT). Dans ce cas précis, c'est l'adresse source des paquets réseau qui est modifiée. Ainsi, toutes les connexions qui sont faites depuis l'intérieur du réseau local semblent provenir de la même machine : la machine faisant le NAT. Le SNAT se fait après le routage. Il est courant d'entendre des erreurs dans la terminologie de ce genre de procédé. Un proxy transparent est une sorte de NAT mais le masquerading est également une sorte de NAT.

Le support SNAT n'est pas une apparition récente sous Linux. En effet, les kernels 2.0 étaient déjà capables de réaliser ce genre de chose (ipfwadm). Ce support a été optimisé avec la série 2.2 (ipchains) et, à présent que le kernel 2.4 a vu le jour, les capacités NAT du kernel sont devenues vraiment étonnantes. A l'instar du passage 2.0 à 2.2, de grands changements dans la configuration sur NAT ont fait leur apparition dans le kernel 2.4. Précisons que nous considérons dans cet article que vous avez déjà eu affaire à ipchains, ne serait-ce qu'en partie.

Netfilter, iptables et le masquerading.
Avec la série 2.2, c'est l'utilitaire ipchains qui permettait de configurer et d'administrer le masquerading. Ainsi, ajouter une règle dans la chaîne forward se faisait ainsi :

# ipchains -A forward -s 192.168.1.0/24 -d 0.0.0.0/0.0.0.0 -j MASQ

Ici, nous permettons à l'ensemble des machines du réseau 192.168.1.0 d'accéder aux serveurs situés sur Internet. Bien sûr, il ne faut pas oublier d'activer IP forwarding :

# echo 1 > /proc/sys/net/ipv4/ip_forward

C'est d'ailleurs la seule chose qui reste d'usage avec iptables. Bien sûr, il est possible de garder une compatibilité avec ipchains mais autant utiliser les nouvelles fonctionnalités en se débarrassant une fois pour toutes des vieilles habitudes. Avec iptables, les choses sont bien différentes et les possibilités sont bien supérieures à ce que faisait ipchains. Pour bien comprendre le fonctionnement d'iptables, vous devez tout simplement oublier tout ce que vous saviez d'ipchains. Vous ne devez plus considérer le NAT sous Linux comme autre chose que de l'altération de paquets réseaux. Vous comprendrez ainsi plus facilement les différentes options.


Schema

Les tables

Avec le nouveau kernel, il n'existe plus une seule table mais trois :

  • filter est la table par défaut. Ainsi, lorsque vous listez les règles avec l'option -L sans spécifier de table, c'est celle-ci qui sera utilisée :

    # iptables -L
    
    Chain INPUT (policy ACCEPT)
    target     prot opt source               destination
    
    Chain FORWARD (policy ACCEPT)
    target     prot opt source               destination
    
    Chain OUTPUT (policy ACCEPT)
    target     prot opt source               destination
    

    On retrouve ici des termes déjà présents dans ipchains, mais ne vous y trompez pas, cela n'a pas grand chose à voir. Cette table contient trois chaînes. INPUT pour les paquets entrant sur la machine, FORWARD pour les paquets routés par la machine et enfin, OUTPUT pour les paquets générés par la machine.

  • nat est la table utilisée lorsqu'un paquet tente de créer une nouvelle connexion. Vous pouvez lister les règles concernant les différentes chaînes de cette table avec :

    # iptables -t nat -L
    
    Chain PREROUTING (policy ACCEPT)
    target     prot opt source               destination
    
    Chain POSTROUTING (policy ACCEPT)
    target     prot opt source               destination
    
    Chain OUTPUT (policy ACCEPT)
    target     prot opt source               destination
    

    Ici, les chaînes sont différentes. PREROUTING concerne la modification des paquets dès qu'ils arrivent sur la machine, POSTROUTING, la modification des paquets qui sont sur le point de quitter la machine et finalement, OUTPUT, la modification des paquets générés localement et ce, avant le routage.

  • mangle est la table pour les petits bricoleurs. C'est ici qu'il est possible de modifier les paquets de manière non standard. On parle d'altération spécialisée des paquets. Cette table comporte seulement deux chaînes :

    # iptables -t mangle -L
    
    Chain PREROUTING (policy ACCEPT)
    target     prot opt source               destination         
    
    Chain OUTPUT (policy ACCEPT)
    target     prot opt source               destination         
    

    La chaîne PREROUTING permet de stocker les règles concernant les paquets avant le routage et OUTPUT les paquets générés localement et ce, avant routage.

    Les cibles

    Les cibles ou targets du vieux ipchains étaient bien peu nombreuses (ACCEPT, DENY, REJECT, MASQ, REDIRECT et RETURN). Avec iptables, tout ceci a été grandement étendu et réorganisé. Les cibles sont maintenant dépendantes des chaînes et des tables. Il est donc très important de connaître correctement leur signification et ce à quoi elles servent. Cible est peut-être un terme mal choisi puisqu'il est plus facile de les voir comme comportement à adopter vis-à-vis des paquets :

  • LOG permet de journaliser les informations sur le paquet concerné. Ceci ce fera par l'intermédiaire d'un journal système et, en fonction du niveau de détail, vous obtiendrez plus ou moins d'informations. Une option très intéressante de cette cible est --log-prefix permettant de préfixer les informations envoyées au journal avec une chaîne de caractères. Ceci facilite grandement la lecture et le filtrage des informations post-problème.

  • MARK est uniquement valide pour la table mangle. Cette cible sert à définir une valeur de marquage netfilter pour le paquet en question. La seule option valide est --set-mark suivie du marquage en question.

  • REJECT était déjà présent dans ipchains. Cette cible permet de retourner une erreur sous forme de paquet à l'attention de la source. Cette cible n'est valide que pour les chaînes INPUT, FORWARD et OUTPUT. Grande nouveauté, il est maintenant possible de choisir l'erreur à retourner au "client". En fonction de la franchise de l'administrateur réseau, on pourra choisir un message allant de host-unreachable à host-prohibited :)

  • TOS permet de définir un type de service. Ceci étant une manipulation spécialisée, cette cible n'est valide que pour la table mangle. Elle accepte un paramètre correspondant au TOS à utiliser.

  • MIRROR est une cible expérimentale. Elle permet d'inverser les adresses de destination et de source dans le paquet concerné. Cette cible n'est valide que pour les chaînes INPUT, FORWARD et PREROUTING.

  • SNAT est l'une des deux options qui vous intéressera le plus. Elle permet, comme son nom l'indique, de faire du NAT sur la source. Attention, cette cible n'est valide, en principe, que pour les adresses statiques. Une autre cible est spécifiquement prévue pour les adresses obtenues de manière dynamique (via une connexion ppp par exemple). Le SNAT, nous l'avons dit plus haut, est du NAT fait après routage. Cette cible n'est donc valide que pour la chaîne POSTROUTING. Voici un petit exemple de la manière d'utiliser cette cible :

    # iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth1 -j SNAT --to 213.11.37.214
    

    Cette ligne est équivalente à celle donnée en début d'article et utilisant ipchains. Tous les paquets en provenance du réseau local 192.168.1.0 verront leur adresse source modifiée en 213.11.37.214 avant de passer par l'interface eth1. Il est indéniable que cette ligne est beaucoup plus explicite que celle utilisant ipchains.

  • DNAT est la cible équivalent à SNAT pour faire du Destination NAT. Comme il s'agit de NAT se faisant avant le routage effectif, cette cible n'est valide que pour les chaînes OUTPUT et PREROUTING. Voyons également un exemple pour clarifier les choses :

    # iptables -t nat -A PREROUTING -p tcp --dport 80 -i eth0 -j DNAT --to 213.11.37.214:8080
    

    Cette ligne permet de définir une règle de la chaîne PREROUTING pour la table nat ayant pour effet de changer l'adresse de destination des paquets arrivant via eth0 (port 80) en 213.11.37.214 (port 8080). Notez que cette modification étant faite AVANT le routage, l'ensemble du système verra uniquement un paquet à destination de 127.0.0.1 et rien d'autre.

  • MASQUERADE est la seconde cible la plus utilisée. Il s'agit d'une cible équivalent à SNAT mais utilisable pour les connexions utilisant une adresse dynamique. Si vous désirez partager une connexion Internet se faisant par modem/PPP, c'est cette cible que vous devrez utiliser :

    # iptables -t nat -A POSTROUTING -o ppp0 -j MASQUERADE
    

  • REDIRECT est uniquement valide dans la table nat et pour les chaînes PREROUTING et OUTPUT. Il s'agit d'une cible permettant de modifier l'adresse de destination des paquets en la remplaçant par l'adresse de la machine (PREROUTING) ou, s'il s'agit de paquets locaux par 127.0.0.1 (OUTPUT). Ceci s'avère très utile dans le cadre de l'utilisation d'un proxy transparent comme Squid. Une option --to-ports permet de préciser le port à utiliser.

    Critères de sélection

    Avant de choisir une cible ou, autrement dit, une modification à apporter aux paquets, il faut les sélectionner. Là encore, de nombreux nouveaux critères de sélection des paquets ont fait leur apparition. Ceux-ci sont présents sous la forme de modules. Il est possible de les spécifier explicitement avec l'option -m, mais la plupart sont chargés implicitement en fonction des choix de l'utilisateur. Les modules des critères de sélection (match, en anglais) sont les suivants :

  • tcp est chargé automatiquement lors de l'utilisation de l'option --protocol tcp. Les options --source-port et --destination-port permettent respectivement de préciser un port ou une plage de port source et de destination. Heureusment pour nous, il existe des alias, qui sont respectivement --sport et --dport, bien plus rapides à saisir.

  • udp est semblable à tcp dans son utilisation et ses options. Il sera chargé automatiquement lors de l'utilisation de --protocol udp.

  • mac est un module très intéressant. Il permet de sélectionner un paquet d'après l'adresse MAC de son interface d'origine. Ceci peut être extrêmement utile dans une situation où les utilisateurs du réseau local sont en mesure de changer leur adresse IP dans le but de contourner les restrictions imposées (vécu). Ce module est chargé automatiquement lors de l'utilisation de --mac-source (exemple --mac-source 00:40:05:34:A1:FE).

  • time est le module permettant de faire du (petit) load-sharing. Il est possible, avec l'option --limit, suivie d'un taux, de sélectionner un taux de paquets par seconde, par minute, horaire ou journalier. Voici un exemple :

    # iptables -m limit -t nat -A POSTROUTING -s 192.168.12.1 -o eth0 --limit 200/sec -j SNAT --to 192.168.0.10
    # iptables -t nat -L -n
    
    Chain PREROUTING (policy ACCEPT)
    target     prot opt source               destination
    
    Chain POSTROUTING (policy ACCEPT)
    target     prot opt source               destination
    SNAT       all  --  192.168.12.1         0.0.0.0/0
    limit: avg 200/sec burst 5 to:192.168.0.10
    
    Chain OUTPUT (policy ACCEPT)
    target     prot opt source               destination
    

    Nous ajoutons ici une règle dans la chaîne POSTROUTING de la table nat. Celle-ci définit que les paquets en provenance de l'interface ayant pour adresse 192.168.12.1 seront "SNATés" en 192.168.0.10. Cette modification sera faite tant que le critère de 43 paquets par seconde ne sera pas dépassé. Il est donc possible de contrôler le taux de connexion de chaque utilisateur du réseau local. Notez que nous avons été obligés de spécifier l'utilisation du module avec l'option -m limit.

  • owner est un module permettant de sélectionner un paquet généré localement en fonction des caractéristiques de son créateur. Ce module ne s'utilise qu'avec la chaîne OUTPUT. Les options sont les suivantes :

    --uid-owner permettant de spécifier l'UID de l'utilisateur qui a créé le paquet.
    --gid-owner idem pour le GID.
    --pid-owner permettant de spécifier l'ID du processus qui a créé le paquet.
    --sid-owner permettant de spécifier l'ID de session du processus qui a créé le paquet.

  • state permet de définir un critère de sélection sur l'état de la connexion à laquelle appartient le paquet. Les valeurs pouvant être passées via l'option --state sont :

    INVALID pour un paquet n'appartenant à aucune connexion.
    ESTABLISHED pour un paquet appartenant à une connexion pour laquelle des paquets ont déjà été envoyés dans les deux directions (E/S).
    NEW pour un paquet qui débute une nouvelle connexion.
    RELATED pour un paquet qui débute lui aussi une nouvelle connexion mais cette dernière est en relation avec une connexion existante.

  • unclean comme son nom l'indique permet de sélectionner les paquets endommagés ou corrompus. Attention, ce module est à considérer comme étant à un stade expérimental.

  • tos permet une sélection sur le champ "Type of Service" de l'en-tête IP. Les valeurs pouvant être passées via l'option --tos sont :

    16 Minimize-Delay
    8 Maximize-Throughput
    4 Maximize-Reliability
    2 Minimize-Cost
    0 Normal-Service

    Il est possible d'obtenir une aide concernant ces modules avec la commande iptables -m module -h où module est le nom d'un module valide. Sachez également qu'un certain nombre de critères acceptent un caractère préfixe ! signifiant la négation. Nous avons remarqué que bien qu'étant documentée, cette négation n'est pas valide pour le module limit (iptables v1.2)

    Il existe également un certain nombre de modules supplémentaires ne faisant pas encore partie de la distribution officielle d'iptables. Nous ne les documenterons donc pas ici. Reportez-vous directement à la page de manuel d'iptables pour plus d'information.

    Le kernel

    Comme avec ipchains, il est indispensable d'activer les fonctionnalités de filtrage de paquets réseau ("Network packet filtering (replace ipchains)". Nous vous conseillons de compiler tout cela sous la forme de modules plutôt que de manière statique. Un kernel modulaire au possible est toujours un excellent choix :

    
    <M>     Connection tracking (required for masq/NAT)
    <M>     FTP protocol support
    <>      Userspace queueing via NETLINK (EXPERIMENTAL)
    <M>     IP tables support (required for filtering/masq/NAT)
    <M>     limit match support
    <M>     MAC address match support
    <M>     netfilter MARK match support
    <M>     Multiple port match support
    <M>     TOS match support
    <M>     Connection state match support
    <M>     Unclean match support (EXPERIMENTAL)
    <M>     Owner match support (EXPERIMENTAL)
    <M>     Packet filtering
    <M>     REJECT target support
    <>      MIRROR target support (EXPERIMENTAL)
    <M>     Full NAT
    <M>     MASQUERADE target support
    <M>     REDIRECT target support
    <M>     Packet mangling
    <M>     TOS target support
    <M>     MARK target support
    <M>     LOG target support
    <>      ipchains (2.2-style) support
    <>      ipfwadm (2.0-style) support
    

    Vous obtiendrez alors des modules classés dans /lib/modules/2.4.0/kernel/net/ipv4/netfilter par un make modules_install :

    ip_conntrack.o
    ip_conntrack_ftp.o
    ip_nat_ftp.o
    ip_tables.o
    ipt_LOG.o
    ipt_MARK.o
    ipt_MASQUERADE.o
    ipt_REDIRECT.o
    ipt_REJECT.o
    ipt_TOS.o
    ipt_limit.o
    ipt_mac.o
    ipt_mark.o
    ipt_multiport.o
    ipt_owner.o
    ipt_state.o
    ipt_tos.o
    ipt_unclean.o
    iptable_filter.o
    iptable_mangle.o
    iptable_nat.o
    

    Un simple modprobe ip_tables devrait ensuite vous permettre d'accéder à l'ensemble des fonctionnalités disponibles. Les modules nécessaires seront automatiquement chargés en fonction de l'usage que vous en ferez de la commande iptables.

    Notez qu'une compatibilité avec ipchains OU ipfwadm est proposée comme fonctionnalité dans le kernel. Je vous invite plutôt à prendre quelques heures en pleine nuit (comme moi) pour bien comprendre iptables. Ceci vous sera davantage profitable. Nous avons grandement documenté ces nouvelles fonctionnalités, mais il est indéniable que seuls des essais et une bonne dose de pratique vous rendront à l'aise dans ce domaine. N'hésitez donc pas à faire des essais entre deux machines ou à travers une connexion PPP/null-modem.

    Les fonctionnalités présentes dans le kernel 2.4 en terme de filtrage et de NAT ont de quoi tansformer un simple 486DX en un matériel spécialisé haut de gamme. Il est très courant de surestimer le coût CPU de telles fonctionnalités. Chapeau bas messieurs Marc Boucher, James Morris, Harald Welte et Rusty Russell. Voilà un travail d'orfèvre qu'il convient de saluer à sa juste valeur... Merci.

    Liens

    * Tout sur le NAT et iptables en plusieurs langues
    * Archive iptables
    * FAQ netfilter / iptables

    Linux Magazine France n° 25 - Février 2001