Concevoir son propre protocole réseau par-dessus UDP - Le problème du NAT

« Pourquoi moi j'arrive à rejoindre ton serveur mais tu n'arrives pas à rejoindre le mien ? »

Concevoir son propre protocole réseau par-dessus UDP - Le problème du NAT

« Pourquoi moi j'arrive à rejoindre ton serveur mais tu n'arrives pas à rejoindre le mien ? »

Sommaire

Contrairement à ce qu'on pourrait penser, chaque appareil connecté à Internet ne dispose pas de sa propre adresse IP, et n'est donc pas directement joignable par n'importe quel autre appareil connecté.

Un exemple simple qui illustre ce principe est votre propre réseau local, comme la majorité des particuliers il y a fort à parier que vous êtes connectés à Internet à l'aide d'une box vendue/louée par votre opérateur (Orange, Free, Belgacom, Voo, SFR, etc.) et cette box effectue plusieurs rôles, notamment celui de routeur : elle est responsable de l'acheminement des paquets au sein de votre réseau local.

Si votre appareil dispose d'une adresse locale comme 192.168.0.35, sachez vous êtes des millions à avoir cette même adresse et elle ne fonctionnera qu'au sein de votre propre réseau local. L'adresse IP qui vous identifie quand vous communiquez sur Internet est partagée entre tous les membres de votre réseau local, et au final seule votre box dispose réellement de sa propre adresse IP. Et encore.

Et encore car avec la montée du nombre d'appareils connectés à Internet et l'épuisement des adresses IPv4 qui en résulte, il arrive que votre opérateur doive attribuer la même adresse IP à plusieurs routeurs, et donc à plusieurs foyers ! C'est également très utilisé dans les réseaux mobiles, où l'adresse publique est celle de l'antenne à laquelle vous êtes connectés.

Mais donc, comment cela fonctionne-t-il quand vous accédez à Internet ? Comment est-ce que même un site web peut répondre aux requêtes de votre navigateur ? En effet si ce dernier envoie les paquets de la réponse à l'adresse IP du client, comment savoir à quel ordinateur est destiné la réponse ?

La réponse se trouve dans le NAT (Network address translation), une opération effectuée par votre routeur qui est donc la traduction de votre adresse IP locale vers votre adresse IP globale.

Concrètement, lorsque votre paquet réseau va traverser un routeur effectuant du NAT, celui-ci va le modifier et changer l'adresse IP par la sienne, ainsi que le port de réponse par un autre.
Il va au passage mémoriser l'association entre votre adresse IP (privée) / port de réponse et l'adresse IP cible / port cible, généralement pour quelques secondes.

Le NAT en imageLe NAT en image[1]

Lorsque le paquet de réponse va arriver, le NAT va recevoir le paquet et, s'il a encore le lien en mémoire, acheminer le paquet vers le bon destinataire en altérant de nouveau les en-têtes, et voilà !

Et comme il est très rare de se limiter à un seul échange entre deux hôtes, les paquets suivants envoyés à la même destination vont rafraichir l'association au niveau du routeur, le faisant garder en mémoire le lien.

Vous pouvez même enchaîner les NAT avec ce système, tant que les en-têtes restitués correspondent à ce qui est attendu, tout se passera bien.

Grâce à ce mécanisme, plusieurs appareils partageant une même adresse IP peuvent communiquer en même temps sur le réseau sans conflit (ou presque).
Comme nous l'avons vu plus haut, l'une des raisons majeures derrière la recrudescence de l'utilisation du NAT est la pénurie d'IPv4 en cours depuis des années, les routeurs NAT permettant donc à un groupe de personnes de n'utiliser qu'une seule adresse. C'est une solution temporaire en attendant une solution plus pérenne.

Mais en quoi le NAT nous concerne au juste ? C'est quelque chose de plutôt transparent dont vous n'aviez peut-être même pas conscience jusqu'ici, pourquoi s'en soucier ?

Le problème du NAT se pose particulièrement lorsque vous souhaitez envoyer des paquets à une machine se trouvant derrière un routeur effectuant du NAT sans que celle-ci ne vous ait envoyé de paquet à l'origine. Le routeur recevra bien vos paquets mais, ne sachant à qui les adresser, les ignorera sans autre mesure.

Cela devient problématique lorsqu'on cherche par exemple à héberger une partie de jeu vidéo et donc à faire office de serveur, les clients vont en effet vouloir nous joindre sans communication de notre part vers eux au préalable.

On parle parfois d'ouverture de port pour régler ce problème, ce qui consiste en fait à ajouter une entrée permanente dans le NAT de votre routeur pour acheminer les paquets sur tel port vers telle machine locale.

Exemple de configuration (sur une Freebox) pour pouvoir faire un serveur HTTPS :
Exemple chez Free

Il va sans dire que ce genre de configuration n'est pas possible si le NAT se situe au-dela de votre routeur, car vous n'avez pas la main sur la configuration des routeurs de votre fournisseur d'accès internet.

Heureusement, ce problème ne se pose pas dans le cas des serveurs dédiés. En effet ils ont une adresse IP qui leur est attribuée et n'ont donc pas de NAT en face d'eux.

Cependant, si vous souhaitez laisser la possibilité à vos joueurs d'héberger localement des parties, ou si vous prévoyez de faire du réseau en P2P (peer-to-peer) (sans notion de serveur propre, tous les clients communiquent potentiellement avec tous les autres clients), alors le NAT vous posera problème (comme il le fait pour les autres logiciels pair-à-pair, typiquement les clients torrents).

Heureusement il existe une solution pour résoudre ce problème : le NAT Punchthrough.
Le principe est simple : on utilise un troisième interlocuteur (connaissant les deux clients souhaitant se connecter l'un à l'autre, qui de préférence n'est pas derrière un NAT) et on les aide à ouvrir les NAT des routeurs correspondants.

Voici un exemple simple :

Client 1 (Donald) veut se connecter à Client 2 (Picsou). Dans cette optique il connait le Serveur (Géo) qui, lui-même, connait déjà Picsou (et est capable de communiquer avec lui).

Donald envoie donc une demande de connexion à Géo, ce dernier va donc recevoir son adresse IP et port de réponse et va les transmettre à Picsou, il va également envoyer l'adresse IP et port de réponse de Picsou à Donald (qu'il aura acquis lors de la connexion préalable de Picsou à Géo).

Une fois que les clients (Donald et Picsou) disposent de l'adresse IP et du port d'entrée "tunnels" ouverts par le serveur (Géo), ils vont alors tenter de s'envoyer des paquets UDP directement. Si l'opération est un succès ils vont être en mesure de communiquer l'un avec l'autre sans intermédiaire.

Et voilà, grâce à l'envoi de paquets entre nos deux clients, des routes (associations) ont été ouvertes dans le NAT, laissant passer les paquets. Au passage attention, comme ces routes sont éphèmères il faut continuellement envoyer des paquets pour les maintenir en vie, utilisez donc un système de keep-alive[2].

Malheureusement en pratique, ce n'est pas toujours aussi simple. Il existe plusieurs types de NAT dont certains vont restreindre l'utilisation du port de réponse à l'adresse IP initialement utilisée, choisissant un nouveau port à l'ouverture d'une nouvelle route.
Généralement ce port est séquentiel, et donc en bouclant sur les ports suivants il est possible de passer au travers, mais il peut arriver que ce port soit choisi aléatoirement[3].

Dans ces cas les plus problématiques, il est parfois tellement compliqué d'établir une communication pair-à-pair qu'on passe par des serveurs proxy. Dans notre exemple cela signifie que Donald et Picsou transmettraient leurs paquets à Géo, qui les retransmettrait lui-même au canard client concerné[4].

Bref.

Vous l'avez compris, le pair-à-pair en IPv4 c'est compliqué. Parce que oui, toute cette explication ne concerne réellement qu'IPv4 (malheureusement encore majoritairement utilisé), l'IPv6 ayant été conçu avec dans l'idée le rétablissement de la connexion pair-à-pair (ça ne veut pas dire que les NAT n'existent pas, mais ils sont beaucoup plus rares).

De plus, comme je l'ai rappelé, on se soucie très peu de ce problème dans le domaine du jeu vidéo, car on fonctionne le plus souvent sur un modèle client / serveur (le P2P étant très casse-gueule pour ce domaine). En revanche si vous souhaitez donner à vos joueurs la possibilité d'héberger eux-même des parties, ça peut être utile[5].

Néanmoins il est important de savoir que ça existe, histoire que vous ne soyiez pas étonnés le jour où vous n'arriverez pas à communiquer avec un ordinateur distant parce qu'il sera derrière un NAT.

Si le sujet vous intéresse, voici quelques liens pour approfondir :
https://networkengineering.stackexchange.com/questions/47276/how-udp-hole-punching-works
https://en.wikipedia.org/wiki/UDP_hole_punching
https://resources.infosecinstitute.com/udp-hole-punching/
https://keithjohnston.wordpress.com/2014/02/17/nat-punch-through-for-multiplayer-games/

Merci à Razakhel et -MoNsTeRRR pour leur relecture et conseils.


  1. Yangliy at English Wikibooks / Public domain ↩︎

  2. Assurez-vous d'échanger un paquet au moins toutes les X secondes, quitte à n'avoir rien à dire, un échange ping / pong est souvent utilisé à cet effet. ↩︎

  3. Et bardaf, c'est l'embardée. ↩︎

  4. Ce qui implique une latence accrue et puise directement dans la bande-passante d'un de vos serveurs. ↩︎

  5. Néanmoins au vu de la complexité du sujet, n'en faites pas une priorité, de nombreux jeux ne s'en soucient pas et laissent au joueur le soin de configurer sa box pour héberger lui-même des parties (lorsque ce n'est pas essentiel au jeu). ↩︎