When I powered on my new Raspberry Pi the other day, I realized I had a problem: even though we were both connected to the same WiFi network, I had no idea what its address was.
My anticlimatic solution was to connect it to a screen, but clearly this wouldn’t always be an option. No, we want to be able to locate a machine just by virtue of it being on the same network as us. The obvious idea is to have it broadcast some identifiable network packet periodically; then, we could wait for the packet, and, once we receive it, work our way back to the machine.
Let’s see if it works with UDP. On the local machine, we start netcat(1)
listening on UDP port \(23456\):
% nc -ulvn -p 23456
listening on [any] 23456 ...
On the Raspberry Pi, we have netcat
broadcast its hostname, lily
, to everybody on the above UDP port:
% echo "$(hostname)" | nc.traditional -ub -w 1 255.255.255.255 23456
Checking back on the local machine, we find lily
’s address, 192.168.0.6
:
listening on [any] 23456 ...
connect to [192.168.0.2] from (UNKNOWN) [192.168.0.6] 50028
lily
So, the idea works. But we’d like to get just the source address out of all that, and netcat
’s output is a bit hard to work with. Instead of trying to process it, let’s write whereis.py
, a Python script just for this task:
#!/usr/bin/python
from __future__ import print_function
import socket, sys
if __name__ == "__main__":
if len(sys.argv) != 2:
print("USAGE: %s <id>" % (sys.argv[0],))
exit(1)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(("0.0.0.0", 23456))
while True:
(data, (addr, port)) = sock.recvfrom(1024)
if data.decode().strip() == sys.argv[1]:
break
print(addr)
There’s nothing surprising about the above: we open a socket, bind it to a port, wait for packets, and exit only when the expected packet arrives. If we redo the experiment, we see:
% whereis.py lily
192.168.0.6
We still need to run the netcat
broadcast on lily
somehow. The simple solution is to add the command to its crontab(1)
so that it runs every minute:
% crontab -l
* * * * * echo "$(hostname)" | nc.traditional -ub -w 1 255.255.255.255 23456
As an example, we can now ssh
into lily
with:
scvalex@alita ~ % ssh $(whereis.py lily)
scvalex@lily ~ %
To recap, we wanted a way to find the address of a machine on the same network as us. We wrote a Python script that waits for a UDP packet with a certain content, and prints the source address of the packet. We then configured the remote machine to broadcast that packet using netcat
and cron
.
We’ve already mentioned netcat
’s unwieldy output, but a bigger problem is that there’s no standard netcat
. The above commands work with the traditional netcat
(which is nc
on Gentoo, and nc.traditional
on Debian); the flags for other netcat
variants are probably different.
Finally, note that our solution is basically a subset of Zeroconf. If you don’t want to get your hands dirty, or need more functionality, consider setting up Avahi.