Does this qualify for Wine-bug: 50499 and 33008?
Am 12.10.2021 um 19:55 schrieb Paul Gofman pgofman@codeweavers.com:
Signed-off-by: Paul Gofman pgofman@codeweavers.com
Using SO_REUSEADDR is currently problematic when more than one UDP socket is bound to different interfaces. While that succeeds, it is unspecified which socket will receive a packet coming to INADDR_ANY. The filter being installed for each socket with SO_ATTACH_FILTER can only reject the packet coming for another interface but that rejected packed doesn't arrive to another socket.
SO_BINDTODEVICE seems to do exactly what we want without installing any socket filters and without requiring SO_REUSEADDR or SO_REUSEPORT. It originally considered for implementing broadcast listen on interface bound sockets but it wasn't much useful by that time as the SO_BINDTODEVICE required CAP_NET_RAW privilege which is normally not available for a non-root user. However, that changed since Linux 5.7 [2].
This patch uses SO_BINDTODEVICE but falls back to the previous way if that fails.
- https://www.winehq.org/pipermail/wine-devel/2011-October/092681.html
- https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/patch/?id...
server/sock.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-)
diff --git a/server/sock.c b/server/sock.c index 03716cba90f..af08cd6be24 100644 --- a/server/sock.c +++ b/server/sock.c @@ -1756,12 +1756,14 @@ static int accept_into_socket( struct sock *sock, struct sock *acceptsock )
#ifdef IP_BOUND_IF
-static int bind_to_index( int fd, in_addr_t bind_addr, unsigned int index ) +static int bind_to_index( int fd, in_addr_t bind_addr, const char *name, unsigned int index, BOOL *need_reuse_addr ) {
- *need_reuse_addr = TRUE;
- return setsockopt( fd, IPPROTO_IP, IP_BOUND_IF, &index, sizeof(index) );
}
-#elif defined(IP_UNICAST_IF) && defined(SO_ATTACH_FILTER) +#elif defined(IP_UNICAST_IF) && defined(SO_ATTACH_FILTER) && defined(SO_BINDTODEVICE)
struct interface_filter { @@ -1794,13 +1796,26 @@ static struct interface_filter generic_interface_filter = BPF_STMT(BPF_RET+BPF_K, 0) /* dump packet */ };
-static int bind_to_index( int fd, in_addr_t bind_addr, unsigned int index ) +static int bind_to_index( int fd, in_addr_t bind_addr, const char *name, unsigned int index, BOOL *need_reuse_addr ) { in_addr_t ifindex = htonl( index ); struct interface_filter specific_interface_filter; struct sock_fprog filter_prog; int ret;
- if (!setsockopt( fd, SOL_SOCKET, SO_BINDTODEVICE, name, strlen( name ) + 1 ))
- {
*need_reuse_addr = FALSE;
return 0;
- }
- /* SO_BINDTODEVICE requires NET_CAP_RAW until Linux 5.7. */
- if (debug_level)
fprintf( stderr, "setsockopt SO_BINDTODEVICE fd %d, name %s failed: %s, falling back to SO_REUSE_ADDR\n",
fd, name, strerror( errno ));
- *need_reuse_addr = TRUE;
- if ((ret = setsockopt( fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex, sizeof(ifindex) )) < 0) return ret;
@@ -1814,7 +1829,7 @@ static int bind_to_index( int fd, in_addr_t bind_addr, unsigned int index )
#else
-static int bind_to_index( int fd, in_addr_t bind_addr, unsigned int index ) +static int bind_to_index( int fd, in_addr_t bind_addr, const char *name, unsigned int index, BOOL *need_reuse_addr ) { errno = EOPNOTSUPP; return -1; @@ -1841,6 +1856,7 @@ static int bind_to_interface( struct sock *sock, const struct sockaddr_in *addr struct ifaddrs *ifaddrs, *ifaddr; int fd = get_unix_fd( sock->fd ); static const int enable = 1;
BOOL need_reuse_addr; unsigned int index;
if (bind_addr == htonl( INADDR_ANY ) || bind_addr == htonl( INADDR_LOOPBACK ))
@@ -1866,14 +1882,14 @@ static int bind_to_interface( struct sock *sock, const struct sockaddr_in *addr
freeifaddrs( ifaddrs );
if (bind_to_index( fd, bind_addr, index ) < 0)
if (bind_to_index( fd, bind_addr, ifaddr->ifa_name, index, &need_reuse_addr ) < 0) { if (debug_level) fprintf( stderr, "failed to bind to interface: %s\n", strerror( errno ) ); return 0; }
if (setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable) ) < 0)
if (need_reuse_addr && setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable) ) < 0) { if (debug_level) fprintf( stderr, "failed to reuse address: %s\n", strerror( errno ) );
-- 2.31.1