Copyright © 2017, Marc Culler
LLamar is a "pure" Python 3 implementation of Microsoft's Link Local Multicast Name Resolution protocol LLMNR. It currently depends on the iproute command to locate network interfaces on the host system, and hence only runs on linux systems which provide iproute. However, the protocol implementation is not system dependent so, with a modest amount of additional work, LLamar could also be made to work on other systems.
The LLMNR protocol provides zero-configuration networking as does Apple's "Bonjour" and its linux implementation avahi. In other words, it can be used to find the IP addresses of other machines on the same LAN without a DNS server. It uses packets which are slightly modified from DNS packets and supports A, AAAA and PTR queries. LLMNR is much simpler and much quieter than the relatively bloated and noisy Bonjour/Avahi.
Modern Windows systems respond to LLMNR queries on any network interface which has "network discovery" enabled. This is enabled by default for a "Home Network" and disabled by default for a "Public Network".
The LLamar package provides a Sender class, which sends LLNMR queries and parses the responses, and a Responder class which receives and replies to LLMNR queries. The Responder.run() method, when run as a service, makes the host findable by Windows computers. There is also a command line program named busco with will run a query and report the result.
Here is a typical application for LLamar. You have an embedded linux system, say running on an Odroid Arm computer. It provides a webserver on port 80 to be used by other computers on your network. The Arm gets addresses dynamically from DHCP. You want to be able to connect to its server from a Windows laptop without having to figure out it current address. So you create /etc/llamar.conf specifying the name "odroid" for the wlan0 interface on the Arm. You start a service on the Arm which initializes a llamar.Responder and runs its run() method. Now you can open a web browser on the Windows laptop and simply type "odroid" into the go box.
I have a Virtual Box Windows client running on my Ubuntu laptop, which is named ace. The client, named VBoxPC, has a bridged network adapter installed with "network discovery" enabled.
From ace I can find the IP address of VBoxPC like this:
culler@ace:~/programs/llamar$ busco VboxPC A: 192.168.0.121 culler@ace:~/programs/llamar$ busco -q AAAA VboxPC AAAA: ::c82e:9545:4e69:b52b AAAA: fe80::c82e:9545:4e69:b52b culler@ace:~/programs/llamar$ busco -q PTR 192.168.0.121 PTR: VboxPC
Now I would like to find the address of ace from VBoxPC. First I start an LLMNR Responder running on ace. We will enable debug mode to see what happens.
culler@ace:~/programs/llamar$ python3 Python 3.5.2 (default, Nov 17 2016, 17:05:23) [GCC 5.4.0 20160609] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import llmnr >>> R = llmnr.Responder() >>> R.debug() >>> R.run()
Next, I will go over to VboxPC and ping ace. This will require the PC to find an address for ace, which it will do by broadcasting LLMNR queries. The Responder on ace will reply with an address. This is what I see on ace, in debug mode:
DEBUG:llamar:Received datagram. DEBUG:llamar:Received A query from ('fe80::c82e:9545:4e69:b52b%wlan0', 59293, 0, 2) for ace. DEBUG:llamar:Responding. DEBUG:llamar:Received datagram. DEBUG:llamar:Received A query from ('192.168.0.121', 54700) for ace. DEBUG:llamar:Responding. DEBUG:llamar:Received datagram. DEBUG:llamar:Received AAAA query from ('fe80::c82e:9545:4e69:b52b%wlan0', 60647, 0, 2) for ace. DEBUG:llamar:Responding.
And, on VBoxPC, when I run the ping command it looks like this:
C:\Users\culler>ping ace Pinging ace [::bcba:928d:7d4f:530a] with 32 bytes of data: Reply from ::bcba:928d:7d4f:530a: time<1ms Reply from ::bcba:928d:7d4f:530a: time<1ms Reply from ::bcba:928d:7d4f:530a: time<1ms Reply from ::bcba:928d:7d4f:530a: time<1ms Ping statistics for ::bcba:928d:7d4f:530a: Packets: Sent = 4, Received = 4, Lost = 0 (0% loss), Approximate round trip times in milli-seconds: Minimum = 0ms, Maximum = 0ms, Average = 0ms
Now let's kill the Responder and close all of its sockets:
^CTraceback (most recent call last): File "<stdin>", line 1, in <module> File "/home/culler/programs/llamar/llmnr/responder.py", line 244, in run [], []) KeyboardInterrupt >>> R.close()
And, this is what happens if I try the ping again with no responder running on ace:
C:\Users\culler>ping ace Ping request could not find host ace. Please check the name and try again.
Run
sudo python setup.py install
to install the python module. To run an LLMNR Responder you need to edit the template file llamar/etc/llmnr.conf provided in the package and copy the result into /etc. The configuration simply assigns a name to each interface which you want the responder to listen to. To run the Responder as a system service on your linux box, you need to install a service script in /etc/init (for upstart systems) or /etc/systemd (for systemd systems. Templates are provided in the llamar/etc directory