Pyzor uses UDP/IP as the communication protocol. We recently switched the public server to a new machine, and started getting reports of many timeouts. I discovered that I could fix the problem if I changed the IP that was queried from eth0:1
to eth0
.
I can reproduce this problem with a simple example:
This is the server code:
#! /usr/bin/env python
import SocketServer
class RequestHandler(SocketServer.DatagramRequestHandler):
def handle(self):
print self.packet
self.wfile.write("Pong")
s = SocketServer.UDPServer(("0.0.0.0", 24440), RequestHandler)
s.serve_forever()
This is the client code (188.40.77.206
is eth0
. 188.40.77.236
is the same server, but is eth0:1
):
>>> import socket
>>> s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
>>> s.sendto('ping', 0, ("188.40.77.206", 24440))
4
>>> s.recvfrom(1024)
('Pong', ('188.40.77.206', 24440))
>>> s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
>>> s.sendto('ping', 0, ("188.40.77.236", 24440))
4
>>> s.recvfrom(1024)
[never gets anything]
The server gets the "ping" packet in both cases (and therefore sends the "pong" packet in both cases).
Oddly, this does work from some places (i.e. I'll get a response from both IPs). For example, it works from 188.40.37.137
(same network/datacenter, different server), but also from 89.18.189.160
(different datacenter). In those cases, the recvfrom
response does have the eth0
IP, rather than the one that was connected to.
Is this just a rule of UDP? Is this a problem/limitation with the Python UDPServer
class? Is it something I'm doing incorrectly? Is there any way that I can have this work apart from simply connecting to the eth0
IP (or listening on the specific IP rather than 0.0.0.0
)?
I came across this with a TFTP server. My server had two IP addresses facing the same network. Because UDP is connectionless, there can be issues with IP addresses not being set as expected in that situation. The sequence I had was:
The solution in my case was to specifically bind the TFTP server to the IP address that I wanted to listen to, rather than binding to all interfaces.
I found some text that may be relevant in a Linux man page for tftpd
(TFTP server). Here it is:
Unfortunately, on multi-homed systems, it is impossible for tftpd to
determine the address on which a packet was received. As a result, tftpd
uses two different mechanisms to guess the best source address to use for
replies. If the socket that inetd(8) passed to tftpd is bound to a par‐
ticular address, tftpd uses that address for replies. Otherwise, tftpd
uses ‘‘UDP connect’’ to let the kernel choose the reply address based on
the destination of the replies and the routing tables. This means that
most setups will work transparently, while in cases where the reply
address must be fixed, the virtual hosting feature of inetd(8) can be
used to ensure that replies go out from the correct address. These con‐
siderations are important, because most tftp clients will reject reply
packets that appear to come from an unexpected address.
See this answer which shows that on Linux it is possible to read the local address for incoming UDP packets, and set it for outgoing packets. It's possible in C; I'm not sure about Python though.