Skip to content

Tunnel inbound: Share the fake UDP reply socket address on OpenBSD - #6624

Open
adsdevdoo wants to merge 1 commit into
XTLS:mainfrom
adsdevdoo:openbsd-udp-reuseport
Open

Tunnel inbound: Share the fake UDP reply socket address on OpenBSD#6624
adsdevdoo wants to merge 1 commit into
XTLS:mainfrom
adsdevdoo:openbsd-udp-reuseport

Conversation

@adsdevdoo

Copy link
Copy Markdown
Contributor

Follow-up to #6546.

On OpenBSD every UDP reply to a client is sent from a socket bound to the
original destination with SO_BINDANY, and FakeUDP creates one such socket
per session. Two clients talking to the same destination at the same time, two
phones resolving over the same DNS server for instance, therefore bind the same
address and port twice. The second bind fails with EADDRINUSE, surfacing as
fake: bind ...: address already in use, and that session never gets an answer.

SO_REUSEADDR alone does not cover this. In in_pcbaddrisavail_lock() the
reuse flag starts as so->so_options & SO_REUSEPORT and is only widened to
accept SO_REUSEADDR for multicast addresses, so a duplicate binding on a
unicast address needs SO_REUSEPORT on both sockets:

int reuseport = (so->so_options & SO_REUSEPORT);

if (IN_MULTICAST(sin->sin_addr.s_addr)) {
	if (so->so_options & (SO_REUSEADDR|SO_REUSEPORT))
		reuseport = SO_REUSEADDR|SO_REUSEPORT;
}
...
if (t && (reuseport & t->inp_socket->so_options) == 0)
	error = EADDRINUSE;

proxy/dokodemo/fakeudp_linux.go already sets both options, this brings the
OpenBSD implementation in line.

Tested on OpenBSD 7.7/7.8/7.9 amd64 on a live router with several LAN clients.

Every reply to a client is sent from a socket bound to the original
destination with SO_BINDANY, and one such socket is created per session.
Two clients talking to the same destination, two phones resolving over
the same DNS server for instance, therefore bind the same address and
port twice, and the second bind fails with EADDRINUSE, leaving that
session without answers.

SO_REUSEADDR does not help here: on OpenBSD it is only promoted to allow
a duplicate binding for multicast addresses, so a unicast address needs
SO_REUSEPORT on both sockets. The Linux implementation of FakeUDP already
sets both options.
@Fangliding

Copy link
Copy Markdown
Member

6546晾了三个星期为什么刚好合并两天才发现。。

@adsdevdoo

Copy link
Copy Markdown
Contributor Author

Fair point. It only shows up when two sessions reach the same destination
at the same time, and my first round of testing was with a single client.
I hit it later, while running IPv6 on a live router with several devices
behind it.

The fix was already sitting in my IPv6 branch. I split it out into its own
PR so that a one-line fix to code that is already merged would not have to
wait for review of a whole feature.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants