I'm trying to find a simple solution for high-performance broadcast/multicast communication between unprivileged processes running on a Linux host. I'm looking for a solution that is 1) simple, 2) unprivileged (no root), 3) language-independent, 4) packet-oriented and 5) efficient (Gbit/s and up).
To put this in context, my existing code simply uses UDP sockets for unicast communication, which neatly matches the above requirements (except being unicast). I've looked into expanding this to multicast by having multiple programs listen to the same UDP port (using
SO_REUSEPORT), but this doesn't actually distribute copies of the packet to all the processes.
I've also looked into using loopback broadcast (127.255.255.255) to reach multiple listening processes, but it seems that I'll need to bind to multiple IP-addresses on the loopback device for this to work, and adding these addresses requires root.Answer1:
Expanding on Pete's suggestion, I've found the following solution, which is not <em>too</em> complex.
The following Python 2 program implements a simple chat program over multicast/loopback. Tested on Linux 3.13 / Ubuntu 14.04.<pre class="lang-python prettyprint-override">
import os, socket, sys # Use an administratively scoped multicast IP (RFC 2365). mcast_group = '18.104.22.168' port = 1234 # Communicate over the loopback interface. ifc = '127.0.0.1' def send(data): sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0) # Send over loopback interface. sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, socket.inet_aton(ifc)) sock.sendto(data, (mcast_group, port)) def listen(): sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0) # Join group. sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton(mcast_group) + socket.inet_aton(ifc)) # Allow multiple subscribers. sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind((mcast_group, port)) while True: data, remote = sock.recvfrom(1024) print '#%d (%s:%d): %s' % ( (os.getpid(),) + remote + (data,) ) args = ' '.join(sys.argv[1:]) if args: send(args) else: listen()
This expands trivially to communication across the local broadcast segment (LAN) by setting
ifc = '0.0.0.0' and unsetting the
<strong>Receiving one's own traffic</strong>
The only problem is that there appears to be no simple way to join a group and send to everyone <em>else</em> in the group without getting your own traffic as well (even by using the same socket for listening and sending).
IPPROTO_IP socket option
IP_MULTICAST_LOOP does not work; it has no effect when using the loopback interface, and when communicating over the network it prevents other local clients from receiving the messages.
I can work around this by filtering on the source address, though.
(As a side note, I just realized that QEMU includes <a href="https://people.gnome.org/~markmc/qemu-networking.html" rel="nofollow">native support</a> for sending virtual network traffic using multicast. <a href="http://comments.gmane.org/gmane.comp.emulators.qemu/198572" rel="nofollow">Faced with the above problem,</a> the QEMU developers also discussed filtering based on source address, though it appears that they never actually did anything about it. As a result, the QEMU VM receives a copy of its own outgoing traffic, though the traffic is usually rejected in the network stack.)