[Twisted-Python] Setting source address of outgoing datagrams

Hello, I've got a problem when using Twisted UDP-based app with IPv6. I know UDP IPv6 is not yet officially supported, I am using the unoffcial hack found somewhere on the net. ----------------UNOFFICIAL HACK---------------------- In twisted/internet/udp.py: (...) def __init__(self, port, proto, interface='', maxPacketSize=8192, reactor=None): """ Initialize with a numeric port to listen on. """ base.BasePort.__init__(self, reactor) self.port = port self.protocol = proto self.maxPacketSize = maxPacketSize self.interface = interface self.setLogStr() self._connectedAddr = None + if interface and abstract.isIPv6Address(interface): ### + self.addressFamily = socket.AF_INET6 ### (...) def doRead(self): """ Called when my socket is ready for reading. """ read = 0 while read < self.maxThroughput: try: data, addr = self.socket.recvfrom(self.maxPacketSize) except socket.error as se: no = se.args[0] if no in _sockErrReadIgnore: return if no in _sockErrReadRefuse: if self._connectedAddr: self.protocol.connectionRefused() return raise else: read += len(data) try: + addr = (addr[0], addr[1]) ### self.protocol.datagramReceived(data, addr) except: log.err() --------------END OF UNOFFICIAL HACK------------------------ I deploy my protocol like that: reactor.listenUDP(5683, myapp.MyProtocol(), interface="::") reactor.run() The problem is: 1. My server receives a request from remote client (with destination address being valid global IPv6 address) 2. The server sends a response, but uses different source address (which is also valid and assigned to local wlan0 interface). 3. Remote host drops the response (destination address of the request and source address of the response do not match). It is common for IPv6 interfaces to have multiple global IPv6 addresses - because of privacy reasons (both on Windows and Linux). My question is - can I read manually the destination address from the incoming request datagram, and set it as a source address manually in the outgoing response datagram? Best Regards Maciej Wasilak

On 10/12/2013 10:27 AM, Maciej Wasilak wrote:
I deploy my protocol like that:
reactor.listenUDP(5683, myapp.MyProtocol(), interface="::") reactor.run()
The problem is: 1. My server receives a request from remote client (with destination address being valid global IPv6 address) 2. The server sends a response, but uses different source address (which is also valid and assigned to local wlan0 interface). 3. Remote host drops the response (destination address of the request and source address of the response do not match).
It is common for IPv6 interfaces to have multiple global IPv6 addresses - because of privacy reasons (both on Windows and Linux).
My question is - can I read manually the destination address from the incoming request datagram, and set it as a source address manually in the outgoing response datagram?
If you pass a specific IP to the interface keyword argument instead of "::", the UDP port will bind to that specific IP and will only send datagrams on that IP. You can use netifaces package (e.g. find it on PyPI) to list all local IPs. So you could do the equivalent of "::" by binding multiple times, once for each IPv6 address.

Itamar, If you pass a specific IP to the interface keyword argument instead of
"::", the UDP port will bind to that specific IP and will only send datagrams on that IP. You can use netifaces package (e.g. find it on PyPI) to list all local IPs. So you could do the equivalent of "::" by binding multiple times, once for each IPv6 address.
Thanks for the hint - I've ran a few tests with netifaces. Few observations: 1. Returned interface identifiers for Windows are not human-readable - it's hard to determine which interface is wireless, and which one is loopback from just the interface identifier. Results for Linux are better - identifiers are human readable (eth0, wlan0, lo, etc.) - it might be possible to implement some simple logic to automatically choose the right interface. 2. As far as I noticed it is not possible to use one DatagramProtocol instance to listen on multiple addresses, so I think to achieve "::" equivalent I have to use separate DatagramProtocol instance for each address. However I think in many cases it's enough to deploy server on a single address, provided it's a reachable and well-known one. 3. I've checked some docs and see no easy way to deploy a server which automatically listens on all interfaces and responds with a source address that was used in request . It seems that it has nothing to do with Twisted. It is rather caused by the underlying Python socket implementation. All in all the problem with using wrong source address can be easilly solved with a bit of user attention, however it's hard to find the solution that "just works". Best Regards Maciek

On 10/16/2013 03:36 AM, Maciej Wasilak wrote:
All in all the problem with using wrong source address can be easilly solved with a bit of user attention, however it's hard to find the solution that "just works".
There's an OS API (recvmsg?) that lets you get at this information; I believe Twisted has the start of a wrapper for it. Given a full wrapper we could create a new UDP API that did support this easily. -Itamar

On 16 October 2013 12:47, Itamar Turner-Trauring <itamar@itamarst.org> wrote:
On 10/16/2013 03:36 AM, Maciej Wasilak wrote:
All in all the problem with using wrong source address can be easilly solved with a bit of user attention, however it's hard to find the solution that "just works".
There's an OS API (recvmsg?) that lets you get at this information; I believe Twisted has the start of a wrapper for it. Given a full wrapper we could create a new UDP API that did support this easily.
Here's a good description of how PowerDNS solved this problem using recvmsg: * http://bert-hubert.blogspot.nl/2012/10/on-binding-datagram-udp-sockets-to-an... I don't think it's currently possible to listenUDP on a specific IPv6 address: * https://twistedmatrix.com/trac/ticket/5086 But the branch for that ticket is almost ready to be merged... it just needs a few custom exception classes I think. -RichardW.

Richard, thank you very much - this post is exactly what I need. Python supports recvmsg, and sendmsg from version 3.3, but Twisted has its own implementation that is 2.7 compatible. I'll try that right away. I think it's best to include the recvmsg/sendmsg code into my DatagramProtocol subclass. Best Regards Maciek 2013/10/16 Richard Wall <m-lists@the-moon.net>
On 16 October 2013 12:47, Itamar Turner-Trauring <itamar@itamarst.org> wrote:
On 10/16/2013 03:36 AM, Maciej Wasilak wrote:
All in all the problem with using wrong source address can be easilly solved with a bit of user attention, however it's hard to find the solution that "just works".
There's an OS API (recvmsg?) that lets you get at this information; I believe Twisted has the start of a wrapper for it. Given a full wrapper we could create a new UDP API that did support this easily.
Here's a good description of how PowerDNS solved this problem using recvmsg: * http://bert-hubert.blogspot.nl/2012/10/on-binding-datagram-udp-sockets-to-an...
I don't think it's currently possible to listenUDP on a specific IPv6 address: * https://twistedmatrix.com/trac/ticket/5086
But the branch for that ticket is almost ready to be merged... it just needs a few custom exception classes I think.
-RichardW.
_______________________________________________ Twisted-Python mailing list Twisted-Python@twistedmatrix.com http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python
participants (3)
-
Itamar Turner-Trauring
-
Maciej Wasilak
-
Richard Wall