Python twisted framework multicast bind on specific interface - python

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?

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.

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.

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

Related

Custom IP protocol with ACK

I would like to explorer the possibilites of creating a custom IP procotol.
Perhaps with scapy in Python.
I've got a tunnel where I receive all packets, so would like to see if I can dismiss destination IP, ports etc. and keep an absolut minimum - but with ACK.
Anyway this is possible with scapy or similar?
I've tried some simple stuff like:
p = IP(dst="192.168.0.2")/"My payload"
But it seems not doable in that manner.
What are you trying to achieve? Forgoing transport layer port numbers limits communication to a single process on each node.
You'll need to register a protocol (number) with the IP stack to make it pass everything with that protocol to your handler. Most often, it's much easier to use UDP transport and build your own stuff on top. You can also simply number your UDP datagrams and ACK them on the application level.

What is the purpose and result of using INADDR_ANY?

In Python when we want to make a script that listens for multicast traffic we set the IP_ADD_MEMBERSHIP option of the socket with a value that consists of the multicast group address and the address of a local interface on which it will listen for the group's traffic.
Many examples on the Internet pass to IP_ADD_MEMBERSHIP the INADDR_ANY wildcard address as the local interface, and some of them state that this will make the socket to listen on all interfaces for multicast packets. However the Linux ip(7) man page states that when using INADDR_ANY
"an appropriate interface is chosen by the system"
and the freebsd man page says that it will choose the "default interface".
So either some answers online are wrong, or there's something that I'm missing here. I believe there's a confusion with INADDR_ANY when used as a parameter in IP_ADD_MEMBERSHIP, and INADDR_ANY when used as a parameter in bind() (represented by an empty string) but I'm not really sure. Can someone please clarify what is happening with INADDR_ANY or 0.0.0.0 when used in IP_ADD_MEMBERSHIP (i.e. it chooses the default interface or all interfaces) and if it behaves differently when used with bind?
When INADDR_ANY is given as the address in a bind call, this causes the socket to listen on the given port for any network interface.
After calling bind in this way, you'll see an entry like this in the output of the netstat command:
udp 0 0 0.0.0.0:46162 0.0.0.0:*
This is a UDP socket that was bound to INADDR_ANY with port 46162.
When used as the interface address when setting the IP_ADD_MEMBERSHIP option, INADDR_ANY indicates that the OS will chose an interface to join the given multicast group on, i.e. the "default" network interface.
It does not mean that it will join the group on all interfaces. To do that, you would need to itereate over all network interfaces and set the IP_ADD_MEMBERSHIP option on each one.
I've worked with Linux, Solaris, FreeBSD, and Windows, and none of them joins a multicast group on all interfaces when using INADDR_ANY.

Python - Twisted - Simple UDP forwarder. Preserve source IP?

I have this basic UDP forward script in Python 3.
from twisted.internet.protocol import DatagramProtocol
from twisted.internet import reactor
class Forward(DatagramProtocol):
def __init__(self, targetTuples):
print ('in init, targetTuples are ', targetTuples)
self._targetTuples = targetTuples
def datagramReceived(self, data, hostAndPort):
print ('self._targetTuples is ', self._targetTuples)
for (targetHost, targetPort) in self._targetTuples:
self.transport.write(data, (targetHost, targetPort))
reactor.listenUDP(5005, Forward([('10.35.203.24', 5000), ('10.35.200.251', 5005)]))
reactor.run()
So I'm listening on port 5005 UDP, and forwarding those packets to the two IP addresses and different ports.
My question is this -
How do I preserve the original IP address that twisted gets while listening on port 5005?
Source IP (10.1.1.1) --> Twisted (10.30.1.1) --> Multiple Destinations
How can I get Multiple Destinations to see the packet source preserved from the Source IP of (10.1.1.1) ?
When sending UDP datagrams using the BSD socket API (around which, as a first approximation, Twisted is a wrapper), the source address is set to the address the socket is bound to. You can specify the IP of the bind address for a UDP socket in Twisted by passing a value for the interface argument to reactor.listenTCP. However, you are typically restricted in what addresses you are allowed to bind to. Typically the only values allowed are addresses which are assigned to a local network interface. If you are forwarding traffic for 10.1.1.1 but you are on host 10.30.1.1 then you probably cannot set the source address of the UDP packets you send to 10.1.1.1 because that address isn't assigned to a network interface on the host doing the forwarding. If you assigned it to one, routing on your network would probably break in weird ways because then two different hosts would have the same IP address.
This doesn't mean it's not possible to do what you want - but it does mean you probably cannot do it using Twisted's basic UDP support. There are a number of other approaches you could take. For example, you can rewrite source addresses using iptables on Linux. Or you can operate on the IP level and parse and generate full UDP datagrams yourself letting you specify any source address you want (you can do this with Twisted on Linux, too, using twisted.pair.tuntap). There are probably a number of other ways. The best solution for you may depend on which platforms you're targeting and the particular reasons you want to do this kind of forwarding.

Capture destination IP in TCP Python SocketServer

I have a Python script that is running on a Linux server that has a dozen IP addresses associated with it. I implemented a TCPSServer from Python's socketserver library and had it listen on all network interfaces.
Several devices will be connecting to this server, and we need to be able to somehow capture the ip address of the destination (not the IP address of the client, but the IP address of the server that the client thinks it is connecting to). Right now, I can receive client connections, I can see the client IP, but I cannot figure out a method for obtaining the destination IP.
Does anyone know a method for capturing the destination IP on the socketserver class? It would seem if I can listen to multiple interfaces, there would be a way to tell which interface was hit.
This will be installed on several servers eventually, each with an unknown number of network interfaces. However, we do know that this will only exist on Linux bases systems. So if there was an OS specific way to do this, I would be fine with that as well.
If you have a socket object, you can use socket.getsockname() to obtain the IP address it's bound to. So something along the lines of:
# IPv4
client = listening_socket.accept()
(ipv4,port) = client.getsockname()
# IPv6
client = listening_socket.accept()
(address, port, flow_info, scope_id) = client.getsockname()
Never tested it on a multihomed server with a socket bound to all interfaces though - might return IPv4 0.0.0.0 or the IPv6 equivalent, for all I know, which wouldn't be all that useful.

Twisted: source IP address for outbound connections

I'm in the process of implementing a service -- written in Python with the Twisted framework, running on Debian GNU/Linux -- that checks the availability of SIP servers. For this I use the OPTIONS method (a SIP protocol feature), as this seems to be a commonplace practice. In order to construct correct and RFC compliant headers, I need to know the source IP address and the source port for the connection that is going to be established. [How] can this be done with Twisted?
This is what I tried:
I subclassed protocol.DatagramProtocol and within startProtocol(self) I used self.transport.getHost().host and self.transport.getHost().port. The latter is indeed the port that's going to be used, whereas the former only yields 0.0.0.0.
I guess that at this point Twisted doesn't [yet?] know which interface and as such which source IP address will be used. Does Twisted provide a facility that could help me with this or do I need to interface with the OS (routing) in a different way? Or did I just use self.transport.getHost().host incorrectly?
For the sake of completeness I answer my own question:
Make sure you use connect() on the transport before trying to determine the host's source IP address. The following excerpt shows the relevant part of a protocol implementation:
class FooBarProtocol(protocol.DatagramProtocol):
def startProtocol(self):
self.transport.getHost().host # => 0.0.0.0
self.transport.connect(self.dstHost, self.dstPort)
self.transport.getHost().host # => 192.168.1.102
If you are using UDP then the endpoint is determined by either:
calling bind() on the socket and explicitly giving it an address
sending a packet
If you want a few more details, check this response.
The problem is that I'm not that familiar with twisted. From what I can tell by a quick perusal of the source, it looks like you might want to use a reactor like t.i.d.SelectReactor instead. This is what t.n.d.DNSDatagramProtocol does under the hood.
If you take twisted out of the picture, then the following snippet shows what is going on:
>>> import socket
>>> s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
<socket._socketobject object at 0x10025d670>
>>> s.getsockname() # this is an unbound or unnamed socket
('0.0.0.0', 0)
>>> s.bind( ('0.0.0.0', 0) ) # 0.0.0.0 is INADDR_ANY, 0 means pick a port
>>> s.getsockname() # IP is still zero, but it has picked a port
('0.0.0.0', 56814)
Get the host name is a little trickier if you need to support multiple network interfaces or IPv4 and IPv6. If you can make the interface used configurable, then pass it in as the first member of the tuple to socket.bind() and you are set.
Now the hard part is doing this within the confines of the abstractions that twisted provides. Unfortunately, I can't help a whole lot there. I would recommend looking for examples on how you can get access to the underlying socket or find a way to pass the socket information into the framework.
Good luck.
Did you see if that you want to do is possible with the SIP implementation that is part of Twisted?
In any case, how you set the source address and port for UDP in Twisted is quite similar to how you set them without Twisted. In Twisted, reactor.listenUDP(port, protocol, interface) binds an UDP socket to a specific port and interface and handles the received datagrams to your protocol. Inside the protocol, self.transport.write(msg, addr) sends a datagram to addr using the address that the protocol is bound to as source address.
Reading your question again, I think the only part you were missing was passing interface to reactor.listenUDP(...).

Categories