diff options
| author | David Gibson <david@gibson.dropbear.id.au> | 2025-12-02 15:02:10 +1100 |
|---|---|---|
| committer | Stefano Brivio <sbrivio@redhat.com> | 2025-12-02 23:07:28 +0100 |
| commit | e6f6eb24b1945f3c271f0b49fd60b9944e85439d (patch) | |
| tree | 9964552751dccde8731d94ace17821ad48fe130f | |
| parent | 74e6f12f0a55c9ee6c1cad6de25ad69ebfc4f3af (diff) | |
| download | passt-e6f6eb24b1945f3c271f0b49fd60b9944e85439d.tar passt-e6f6eb24b1945f3c271f0b49fd60b9944e85439d.tar.gz passt-e6f6eb24b1945f3c271f0b49fd60b9944e85439d.tar.bz2 passt-e6f6eb24b1945f3c271f0b49fd60b9944e85439d.tar.lz passt-e6f6eb24b1945f3c271f0b49fd60b9944e85439d.tar.xz passt-e6f6eb24b1945f3c271f0b49fd60b9944e85439d.tar.zst passt-e6f6eb24b1945f3c271f0b49fd60b9944e85439d.zip | |
util: Fix setting of IPV6_V6ONLY socket option
Currently we only call setsockopt() on IPV6_V6ONLY when we want to set it
to 1, which we typically do on all IPv6 sockets except those explicitly for
dual stack listening. That's not quite right in two ways:
* Although IPV6_V6ONLY==0 is normally the default on Linux, that can be
changed with the net.ipv6.bindv6only sysctl. It may also have different
defaults on other OSes if we ever support them. We know we need it off
for dual stack sockets, so explicitly set it to 0 in that case.
* At the same time setting IPV6_V6ONLY to 1 for IPv6 sockets bound to a
specific address is harmless but pointless. Don't set the option at all
in this case, saving a syscall.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
| -rw-r--r-- | util.c | 29 |
1 files changed, 23 insertions, 6 deletions
@@ -45,13 +45,13 @@ * @type: epoll type * @sa: Socket address to bind to * @ifname: Interface for binding, NULL for any - * @v6only: Set IPV6_V6ONLY socket option + * @v6only: If >= 0, set IPV6_V6ONLY socket option to this value * * Return: newly created socket, negative error code on failure */ static int sock_l4_(const struct ctx *c, enum epoll_type type, const union sockaddr_inany *sa, const char *ifname, - bool v6only) + int v6only) { sa_family_t af = sa->sa_family; bool freebind = false; @@ -95,9 +95,13 @@ static int sock_l4_(const struct ctx *c, enum epoll_type type, return -EBADF; } - if (v6only) - if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &y, sizeof(y))) - debug("Failed to set IPV6_V6ONLY on socket %i", fd); + if (v6only >= 0) { + if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, + &v6only, sizeof(v6only))) { + debug("Failed to set IPV6_V6ONLY to %d on socket %i", + v6only, fd); + } + } if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &y, sizeof(y))) debug("Failed to set SO_REUSEADDR on socket %i", fd); @@ -181,7 +185,16 @@ static int sock_l4_(const struct ctx *c, enum epoll_type type, int sock_l4(const struct ctx *c, enum epoll_type type, const union sockaddr_inany *sa, const char *ifname) { - return sock_l4_(c, type, sa, ifname, sa->sa_family == AF_INET6); + int v6only = -1; + + /* The option doesn't exist for IPv4 sockets, and we don't care about it + * for IPv6 sockets with a non-wildcard address. + */ + if (sa->sa_family == AF_INET6 && + IN6_IS_ADDR_UNSPECIFIED(&sa->sa6.sin6_addr)) + v6only = 1; + + return sock_l4_(c, type, sa, ifname, v6only); } /** @@ -204,6 +217,10 @@ int sock_l4_dualstack(const struct ctx *c, enum epoll_type type, .sa6.sin6_port = htons(port), }; + /* Dual stack sockets require IPV6_V6ONLY == 0. Usually that's the + * default, but sysctl net.ipv6.bindv6only can change that, so set the + * sockopt explicitly. + */ return sock_l4_(c, type, &sa, ifname, 0); } |
