Dans le merveilleux monde d’IP, il est possible d’envoyer des paquets de type « broadcast », c’est à dire envoyés à tous les ordinateurs sur un réseau local. Cette fonctionnalité est décrite dans les RFC 919 et 922. Ces RFC définissent une adresse IP particulière, 255.255.255.255 (c’est à dire tous les bits de l’adresse à 1), qui caractérise un paquet broadcast envoyé à tous les hôtes quel que soit le sous-réseau auquel ils appartiennent. Pour cette raison, elle est généralement appelée adresse de broadcast global [1].

Concrètement, lorsqu’un paquet IP portant l’adresse de destination 255.255.255.255 est envoyé sur un réseau local, il est reçu par tous les hôtes se trouvant sur le même segment que la source. Typiquement, sur un réseau Ethernet, l’adresse MAC de destination du paquet sera FF:FF:FF:FF:FF:FF (l’adresse de broadcast Ethernet), ce qui pour un switch provoque la transmission du paquet sur tous les ports (sauf celui sur lequel le paquet a été reçu, pour des raisons évidentes). Ces paquets ne passent cependant pas les routeurs afin d’éviter une « inondation » d’Internet par des paquets broadcast. Le broadcast IP est donc clairement une fonctionnalité restreinte au réseau local, et n’a pas de sens sur Internet.

Maintenant que les présentations sont faites, entrons dans le vif du sujet. Le problème qui nous intéresse ici est que les RFC ne définissent pas explicitement le comportement à adopter lorsqu’une application envoie un paquet à destination de l’adresse de broadcast global sur une machine disposant de plusieurs interfaces réseau (qui peuvent être connectés à plusieurs réseaux locaux différents).

Intuitivement, on est tenté de penser que dans ce cas, il faut que le système d’exploitation envoie le paquet sur toutes les interfaces. Après tout, la RFC 919 indique bien dans sa section 7 :

Thus, a host on net 36, for example, may broadcast to all of its immediate neighbors by using 255.255.255.255

« Tous ses voisins immédiats ». On serait tentés d’en conclure que sont inclus dans cet ensemble l’intégralité des voisins de tous les réseaux auquel est connecté l’expéditeur du paquet. Mais la RFC ne le dit pas explicitement, et d’ailleurs, l’exemple indiqué ne parle que d’un seul réseau (« réseau 36 »).

Windows (en version 7) et Linux (en version 2.6.32) adoptent un comportement similaire sur ce sujet. Ils considèrent un paquet broadcast comme n’importe quel autre paquet IP. C’est à dire que si une application tente d’envoyer un paquet à 255.255.255.255 sans se lier (« bind ») à une adresse spécifique, le système d’exploitation consulte sa table de routage, cherche une route correspondant à 255.255.255.255, et envoie le paquet sur l’interface correspondant à la route [2]. Comme d’habitude, pourrait-on dire. C’est le comportement le plus souhaitable car il ne contredit aucun standard. En pratique, Windows ajoute automatiquement dans sa table de routage des entrées pour l’adresse de broadcast global pour chaque interface ; Linux en revanche ne le fait pas, et il faut les ajouter à la main si on veut permettre à une application de pouvoir envoyer un paquet broadcast sans avoir à se lier à une adresse.

Malheureusement, les développeurs d’applications, d’un naturel fainéant, ne semblent pas être conscients de ce phénomène. En effet, un grand nombre d’applications utilise le broadcast global pour rechercher la présence de services sur le réseau local. Intuitivement, le développeur aura programmé son application pour simplement envoyer ses paquets de recherche vers 255.255.255.255 sans se poser de questions. Cela fonctionne très bien dans le cas où la machine sur laquelle l’application s’exécute ne dispose que d’une seule interface réseau. Mais s’il y en a plusieurs, alors le paquet ne sera envoyé que sur une seule interface choisie arbitrairement, excluant ainsi les hôtes joignables via d’autres interfaces de la recherche, ce qui n’est probablement pas ce que le développeur de l’application aurait voulu !

L’exemple qui vient à l’esprit est le jeu multijoueur proposant une liste des parties qui sont joignables sur le réseau local. Pour trouver les parties, le jeu envoie des paquets d’interrogation vers l’adresse de broadcast global. Les autres instances du jeu sur le réseau local, écoutant sur le port correspondant, répondent pour signaler leur présence. Si plusieurs interfaces réseau sont présentes sur la machine du joueur, celui-ci ne verra que les parties d’un seul réseau, à l’exception de tous les autres auquel le joueur est connecté. Cette situation arrive plus souvent que l’on pourrait le penser car certains joueurs jouent sur un réseau privé virtuel (très souvent, Hamachi), qui vient s’ajouter à leur interface réseau physique. Dans ce cas, le joueur verra soit les parties présentes sur le réseau virtuel, soit sur le réseau physique (sans possibilité simple de choisir entre les deux). Ce qui, vous en conviendrez, n’est pas un comportement très judicieux.

D’aucuns pourraient objecter que ce comportement viole le principe de moindre surprise. Dans un sens, c’est le cas. Le problème, c’est que si le système d’exploitation envoyait les paquets broadcast sur toutes les interfaces, cela voudrait dire que ces paquets sont traités de manière spéciale, ce qui viole également le même principe. C’est donc bien aux développeurs d’applications de faire attention, et d’envoyer les paquets broadcast de manière explicite sur chacune des interfaces réseau présentes sur le système, pour être sûr de joindre tous les voisins de la machine.

Mais parce qu’on ne vit pas dans un monde de bisounours, et que l’écrasante majorité des applications utilisant le broadcast global le font mal, il serait intéressant de pouvoir modifier le comportement du système d’exploitation pour que les paquets broadcast soient réellement envoyés sur toutes les interfaces, quand bien même l’application ne le ferait pas elle-même. Cela n’est pas possible nativement, ce qui n’est pas surprenant. Une solution consiste à développer une application qui écouterait le trafic broadcast global, et renverrait elle-même les paquets sur chacune des interfaces « oubliées » par l’application.

C’est ce qu’a fait votre serviteur, et cela donne WinIPBroadcast : un minuscule programme Windows, fonctionnant en tant que service, qui se contente de dupliquer chacun des paquets broadcast envoyés par le système vers les interfaces autres que celle qui a la préférence de la table de routage. De cette manière, vos applications envoyant des paquets à l’adresse de broadcast global (par exemple, votre jeu multijoueur favori) les enverront sur toutes les interfaces d’une manière totalement transparente aux yeux de l’application comme de l’utilisateur. Je n’ai pas fait de version Linux (je n’en ai pas l’utilité), mais il devrait être aisé d’appliquer le même principe sur ce système, ou sur tout autre système d’exploitation.

Liens

Notes

[1] : il existe aussi l’adresse de broadcast dirigé, qui est composée de tous les bits du sous-réseau avec le reste des bits à 1 (par exemple, l’adresse de broadcast dirigé de 192.168.18.0/24 est 192.168.18.255).

[2] : Windows XP adopte un comportement étrange et envoie le paquet sur toutes les interfaces, mais avec une seule adresse source (celle de la route choisie), même lorsque l’adresse IP affectée à l’interface ne correspond pas à cette adresse ! Bien évidemment, cela n’a aucune chance de fonctionner sur les interfaces autres que celle correspondant à la route.