udp: fix server reply address for multi-address interfaces - #2077
Aniketnegi03 wants to merge 1 commit into
Conversation
A UDP test against a server that has more than one address on the
outgoing interface fails: the server replies from a different address
than the one the client sent its datagrams to, the client's connected
socket discards those replies, and the client eventually dies with
iperf3: error - unable to read from stream socket:
Resource temporarily unavailable
The server's UDP socket is created by netannounce() with no local
address unless -B was given, so it is bound to the wildcard address and
has no source address of its own. iperf_udp_accept() then connect(2)s
it to the client, at which point the kernel has to choose a source
address and does so with a route lookup towards the client. That yields
the interface's primary address, which need not be the address the
client used. TCP is unaffected because accept(2) inherits the full
address pair of the connection.
The client uses the same server address for the control connection and
for the data streams, so the local address of the control connection is
the address the client expects replies from. Bind the UDP socket to it
and leave the kernel nothing to guess. If that address cannot be
determined or cannot be bound, fall back to the previous wildcard bind,
so the change can only ever restore the old behaviour, never do worse.
As a side effect the server now also reports the correct local address
for UDP streams in its human-readable and JSON output.
Fixes esnet#2059
0cb6523 to
4319beb
Compare
|
Thanks for the PR! |
|
@Aniketnegi03 I am not from the iperf3 maintenance team and I did not try running the PR code. However, I am not sure I understand the fix, and I will appreciate it if you can briefly explain it. What I don't understand is that the main change is done for |
|
@davidBar-On, thanks for taking the time to review - let me explain in brief. You're right that So when the socket has no source address of its own, the kernel fills one in from a route lookup toward the client. That lookup returns the route's preferred source - the interface's primary address - which need not be the address the client actually contacted (or on which the connect message was received). The problem isn't that the right address is unavailable; it's that we let the route table choose instead of replying from the address the connect message arrived on. Regarding "Create a new "Listening" socket to replace the one we were using before" - I think you read that as the patch only fixing the next stream's socket. It isn't: the socket serving the current stream was already bound with the local address by The ordering makes this clearer. After the TCP handshake and parameter exchange ( Just an example to help get the context:This is also why the fix can't go in Once that socket is promoted to stream 1's data socket, a connected UDP socket only receives from its peer, so it can no longer hear the next client - hence the new listening socket in |
A UDP test against a server that has more than one address (IPv4 or IPv6) on the outgoing interface fails: the server replies from a different address than the one the client sent its datagrams to, the client's connected socket discards those replies, and the client eventually dies with
The server's UDP socket is created by netannounce() with no local address unless -B was given, so it is bound to the wildcard address and has no source address of its own. iperf_udp_accept() then connect(2)s it to the client, at which point the kernel has to choose a source address and does so with a route lookup towards the client. That yields the interface's primary address, which need not be the address the client used. TCP has been unaffected because accept(2) inherits the full address pair of the connection.
The client uses the same server address for the control connection and for the data streams, so the local address of the control connection is the address the client expects replies from. Bind the UDP socket to it and leave the kernel nothing to guess. If that address cannot be determined or cannot be bound, fall back to the previous wildcard bind.
As an effect the server now also reports the correct local address for UDP streams in its human-readable and JSON output.
Fixes #2059
PLEASE NOTE the following text from the iperf3 license. Submitting a
pull request to the iperf3 repository constitutes "[making]
Enhancements available...publicly":
The complete iperf3 license is available in the
LICENSEfile in thetop directory of the iperf3 source tree.