Sur un réseau local, la découverte d’appareil IoT peut être pénible en DHCP. Des protocoles existent pour faciliter cela. J’implémente un serveur mDNS très minimaliste pour mieux comprendre comment cela fonctionne.
DHCP
En DHCP si avez une machine avec un hostname et que vous faites un scan du réseau local, par exemple avec un nmap vous pourrez voir ce nom apparaitre en résultat du scan.
Avec ce nom vous pouvez directement faire
~ » ping kobol.home alex@kobol
PING hostname.home (192.168.1.26) 56(84) octets de données.
64 octets de XXX (192.168.1.26) : icmp_seq=1 ttl=64 temps=0.045 ms
64 octets de XXX (192.168.1.26) : icmp_seq=2 ttl=64 temps=0.052 ms
64 octets de XXX (192.168.1.26) : icmp_seq=3 ttl=64 temps=0.051 ms
64 octets de XXX (192.168.1.26) : icmp_seq=4 ttl=64 temps=0.035 ms
^C
--- statistiques ping kobol.home ---
4 paquets transmis, 4 reçus, 0% packet loss, time 3054ms
rtt min/avg/max/mdev = 0.035/0.045/0.052/0.006 ms
et vous aurez une réponse ! Le problème étant que l’extension .home peut changer d’un routeur ou d’une box à l’autre. Si vous regardez votre fichier /etc/resolv.conf vous aurez par exemple les lignes suivantes indiquant bien que pour ce réseau le suffixe est home et l’adresse 192.168.1.1 est le résolveur DNS à utiliser.
search home
nameserver 192.168.1.1
Je n’avais jamais vraiment réfléchit à d’où venait cette gestion du nom d’hôte, ça marchait et ça me suffisait.
Seulement parfois, ça ne marche pas..
Cette configuration est poussée par le DHCP, donc très dépendante du réseau sur lequel l’appareil se trouve.
Mais alors quelle est la bonne façon de découvrir facilement des appareils sur son réseau ? Sans dépendre de la box, des adresses de réseau, du DHCP.
mDNS
Un protocole sert justement à ça et peut ressembler à la résolution faite par la box puisque les noms d’hôtes utilisent .local. Sous Linux avahi est le moyen le plus simple de l’utiliser, après avoir installé le paquet et démarré le daemon systemctl start avahi-daemon.service.
À partir de là si vous avez des appareils sur votre réseau : imprimante, objets connectés de domotique, vous devriez pouvoir le trouver sous son nom local.
Par exemple un capteur basé sur ESP Home va exposer un service: linky_meter._http._tcp.local et peut directement répondre sous linky_meter.local.
S’il existe plusieurs implémentations de client permettant de découvrir des services mDNS, je n’ai pas trouvé d’implémentation de serveur simple. Ptython-ZeroConf permet de le faire avec leur exemple async_registration.py mais je voulais un programme encore plus simple. C’est pourquoi j’ai fait la mienne.
Mon implémentation très minimale
Je me suis basé sur la RFC, quelques captures Wireshark et j’ai écrit mon mdns-server. Cette implémentation est très minimaliste, par exemple je ne gère pas les conflits. Si un autre appareil annonce le nom que je souhaite annoncer, la RFC dit qu’il faut adapter et par exemple annoncer -2 pour ne pas avoir de conflit.
Pour faire simple, mon implémentation
- écoute les demandes multicast sur 224.0.0.251 port 5353
- vérifie que c’est une question à laquelle on peut répondre, ici
_http._tcp._local - construit une réponse DNS avec
- un champ PTR pour faire pointer la question vers notre réponse
- un champ SRV avec le nom complet du service exposé
- un champ TXT, par exemple Shelly complète avec des informations de génération de capteurs
- un champ A pour indiquer notre propre adreses IP
- la question indique si l’on doit répondre une unicast juste au client ou en multicast à tous les autres serveurs mDNS et client découvrant des services.
Pour résumer
Les noms d’hôtes locaux en .home et .lan sont issus du serveur DHCP et ne sont pas fiables pour découvrir un réseau de manière automatique, mDNS-DNS-SD est la bonne approche. Elle repose sur un échange multicast et une réponse DNS très simple et vous pouvez voir mon implémentation mdns-server.
Ressources
- Avahi l’implémentation principale sous GNU/Linux
- RFC 6762 Multicast DNS détaille ce qu’une bonne implémentation doit faire
- mDNS-Unicast-Lookup pour l’inspiration même si nos implémentations ne font pas la même chose
- dnslib bibliothèque python pour facilement décoder et construire des requêtes DNS
- mdns-scan un scanner mDNS très pratique, alternative à avahi-browse