I'm using multicast UDP over localhost to implement a loose collection of cooperative programs running on a single machine. The following code works well on Mac OSX, Windows and linux. The flaw is that the code will receive UDP packets outside of the localhost network as well. For example, sendSock.sendto(pkt, ('192.168.0.25', 1600))
is received by my test machine when sent from another box on my network.
import platform, time, socket, select
addr = ("239.255.2.9", 1600)
sendSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sendSock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 24)
sendSock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF,
socket.inet_aton("127.0.0.1"))
recvSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
recvSock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
if hasattr(socket, 'SO_REUSEPORT'):
recvSock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, True)
recvSock.bind(("0.0.0.0", addr[1]))
status = recvSock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP,
socket.inet_aton(addr[0]) + socket.inet_aton("127.0.0.1"));
while 1:
pkt = "Hello host: {1} time: {0}".format(time.ctime(), platform.node())
print "SEND to: {0} data: {1}".format(addr, pkt)
r = sendSock.sendto(pkt, addr)
while select.select([recvSock], [], [], 0)[0]:
data, fromAddr = recvSock.recvfrom(1024)
print "RECV from: {0} data: {1}".format(fromAddr, data)
time.sleep(2)
I've attempted to recvSock.bind(("127.0.0.1", addr[1]))
, but that prevents the socket from receiving any multicast traffic. Is there a proper way to configure recvSock to only accept multicast packets from the 127/24 network, or do I need to test the address of each received packet?
Unfortunately, multicast IP doesn't have any such "filtering by subnetwork" feature -- so, unless you want to muck with IPTables (on Linux) or equivalent "firewall" SW/HW of your system/network to try and "drop on the floor" every multicast packet you don't like, I think you'll have to do it at application level (with a test on
fromAddr
in your inner loop, for example). Is the IP traffic from other hosts so much it degrades your performance...?Contrary to what has been stated in other answers here, IPv4 supports TTL-based multicast scoping, as follows:
(It also supports Administratively Scoped Multicast.)
Source: W.R. Stevens, Unix Network Programming, 2nd edition, Vol I, section 19.2, with corrections to match RFC 2365.
You can use
connect()
to 127.0.0.1 on the multicast socket, then the IP stack can filter for you.Updated with source code to demonstrate
You can run this script multiple times on one host and see the multicast packets distributed:
To initiate packets from a different interface:
I ran all three on Cygwin on Windows XP and verified the result to be as required.
Example output
Previously the output would show external packets, for example:
If your host supports IPv6, you can use the scope component of the multicast address (this is the 'x' in the multicast prefix FF0x:) to implicitly restrict both incoming and outbound packets to the local host, by specifying a scope of 1 (e.g. use the IPv6 multicast address FF01::107 for the "name service server" on the local host only). Unfortunately the IPv4 multicast mechanism does not have an explicit scope, and RFC 2365 (http://tools.ietf.org/html/rfc2365) which defines administratively scoped IPv4 multicast ranges, does not define a node-local scope address, only a link-local scope range.