Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python twisted framework multicast bind on specific interface

After searching high and low across the google i have not found the definitive answer to the following question: by roughly following the following guide: http://twistedmatrix.com/documents/10.2.0/core/howto/udp.html#auto3

How is it possible to bind twisted Multicast listener to ONLY the multicast address and on specific or all interfaces.

While looking at reactor.listenMulticast it does not provide abstraction for hardware interface only an pseudo interface represented by an IP address. I cant find a method to bind only on the multicast address e.g. 224.0.0.1 of a specific interface or all interfaces. Can anyone provide further information on this?

like image 422
aiksa Avatar asked Mar 31 '11 10:03

aiksa


3 Answers

reactor.listenMulticast returns a twisted.internet.udp.MulticastPort object. That object owns the socket you're listening on. So hang on to the result of reactor.listenMulticast and set the appropriate socket option (in this case it looks like SO.BINDTODEVICE) along with a null terminated device string.

import IN, socket, struct

udp_port = reactor.listenMulticast(8005, MulticastServerUDP())
dev = "eth0" + "\0"
udp_port.socket.setsockopt(socket.SOL_SOCKET, IN.SO_BINDTODEVICE, dev)
reactor.run()

It would be nice if this were exposed directly through the listenMulticast call but assuming this works it would be a pretty easy patch. Let me know if this solves your problem.

like image 59
stderr Avatar answered Oct 05 '22 16:10

stderr


The other answers may solve the problem, but there is a "more twisted" (and probably easier) way to listen to a multicast group on multiple interfaces.

Listening to a multicast group on one or more interfaces

To listen to a multicast group on one or more interfaces, provide the IP of each desired interface in multiple calls to the protocol's transport.joinGroup() method as the 'interface' argument.

Below is an example that works on my Linux box, and will make your system listen to multicasts on specific interfaces. Replace the IP addresses with ones belonging to your system.

#!/usr/bin/env python

from twisted.internet import protocol
from twisted.internet import reactor

class MyProtocol(protocol.DatagramProtocol):
    def startProtocol(self):
        # Join a multicast group, 224.0.0.9, on the interface belonging
        # to each of these IPs.
        # XXX Replace the interface_ips with one or more IP addresses belonging
        # to your system.
        for interface_ip in ["192.168.2.2", "10.99.1.100"]:
            self.transport.joinGroup("224.0.0.9", interface_ip)

if __name__ == "__main__":
    reactor.listenMulticast(1520, MyProtocol())
    reactor.run()

You can check that the interface is listening to the new multicast group using the /sbin/ip maddr show command. Find the desired interface name in the command output, and verify that the multicast group shows up beneath it.

The UDP Server example linked in the original post should be able to do the same thing by changing the call to joinGroup() to include the second IP address argument, as above.

Sending multicasts from a particular IP

If you're receiving multicast data on a socket, chances are you will want to send multicast data too -- possibly out of multiple interfaces. Since it's closely related and there are very few examples around I'll throw it in here. Inside a twisted.internet.protocol.DatagramProtocol object you can use the self.transport.setOutgoingInterface() method to control the source IP that will be used for subsequent calls to self.transport.write(). Example showing sending a message from multiple IPs/interfaces:

class MyProtocol(protocol.DatagramProtocol):
    # ...
    def send_stuff(self, msg):
        for src_ip in ["10.0.0.1", "192.168.1.1"]:
            self.transport.setOutgoingInterface(src_ip)
            self.transport.write(msg, ("224.0.0.9", 1520))

Suppose these IPs were assigned to two different interfaces. Sniffing with Wireshark you would see the message sent out of the first interface, then the second interface, using each IP as a source IP address for the respective transmission.

like image 22
pfa Avatar answered Oct 05 '22 14:10

pfa


A simple, if heavy handed approach could be to just specify a route via something like:

sudo route add 239.255.0.0 -interface en0 -netmask 255.255.0.0
like image 34
alcoholiday Avatar answered Oct 05 '22 15:10

alcoholiday