REORG: sock_inet: move v6only_default from proto_tcp.c to sock_inet.c

The v6only_default variable is not specific to TCP but to AF_INET6, so
let's move it to the right file. It's now immediately filled on startup
during the PREPARE stage so that it doesn't have to be tested each time.
The variable's name was changed to sock_inet6_v6only_default.
This commit is contained in:
Willy Tarreau 2020-08-28 16:06:01 +02:00
parent 25140cc573
commit d88e8c06ac
3 changed files with 27 additions and 35 deletions

View File

@ -27,6 +27,8 @@
#include <haproxy/api.h>
extern int sock_inet6_v6only_default;
int sock_inet4_addrcmp(const struct sockaddr_storage *a, const struct sockaddr_storage *b);
int sock_inet6_addrcmp(const struct sockaddr_storage *a, const struct sockaddr_storage *b);
int sock_inet_get_dst(int fd, struct sockaddr *sa, socklen_t salen, int dir);

View File

@ -107,11 +107,6 @@ static THREAD_LOCAL int default_tcp_maxseg = -1;
static THREAD_LOCAL int default_tcp6_maxseg = -1;
#endif
/* determine if the operating system uses IPV6_V6ONLY by default.
* -1=unknown, 0=no, 1=yes.
*/
static int v6only_default = -1;
/* Binds ipv4/ipv6 address <local> to socket <fd>, unless <flags> is set, in which
* case we try to bind <remote>. <flags> is a 2-bit field consisting of :
* - 0 : ignore remote address (may even be a NULL pointer)
@ -581,32 +576,6 @@ int tcp_connect_server(struct connection *conn, int flags)
return SF_ERR_NONE; /* connection is OK */
}
/* sets the v6only_default flag according to the OS' default settings; for
* simplicity it's set to zero if not supported.
*/
static inline void tcp_test_v6only_default()
{
if (v6only_default == -1) {
#if defined(IPV6_V6ONLY)
int fd, val;
socklen_t len = sizeof(val);
v6only_default = 0;
fd = socket(AF_INET6, SOCK_STREAM, 0);
if (fd < 0)
return;
if (getsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, &len) == 0 && val > 0)
v6only_default = 1;
close(fd);
#else
v6only_default = 0;
#endif
}
}
#define LI_MANDATORY_FLAGS (LI_O_FOREIGN | LI_O_V6ONLY)
/* When binding the listeners, check if a socket has been sent to us by the
* previous process that we could reuse, instead of creating a new one.
@ -617,15 +586,13 @@ static int tcp_find_compatible_fd(struct listener *l)
int options = l->options & (LI_MANDATORY_FLAGS | LI_O_V4V6);
int ret = -1;
tcp_test_v6only_default();
/* Prepare to match the v6only option against what we really want. Note
* that sadly the two options are not exclusive to each other and that
* v6only is stronger than v4v6.
*/
if ((options & LI_O_V6ONLY) || (v6only_default && !(options & LI_O_V4V6)))
if ((options & LI_O_V6ONLY) || (sock_inet6_v6only_default && !(options & LI_O_V4V6)))
options |= LI_O_V6ONLY;
else if ((options & LI_O_V4V6) || !v6only_default)
else if ((options & LI_O_V4V6) || !sock_inet6_v6only_default)
options &= ~LI_O_V6ONLY;
options &= ~LI_O_V4V6;

View File

@ -35,6 +35,10 @@
* mentioned in the comment before the function definition.
*/
/* determine if the operating system uses IPV6_V6ONLY by default. 0=no, 1=yes.
* It also remains if IPv6 is not enabled/configured.
*/
int sock_inet6_v6only_default = 0;
/* Compares two AF_INET sockaddr addresses. Returns 0 if they match or non-zero
* if they do not match.
@ -165,3 +169,22 @@ int sock_inet_is_foreign(int fd, sa_family_t family)
}
return 0;
}
static void sock_inet_prepare()
{
int fd, val;
socklen_t len;
fd = socket(AF_INET6, SOCK_STREAM, 0);
if (fd >= 0) {
#if defined(IPV6_V6ONLY)
/* retrieve the OS' bindv6only value */
len = sizeof(val);
if (getsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, &len) == 0 && val > 0)
sock_inet6_v6only_default = 1;
#endif
close(fd);
}
}
INITCALL0(STG_PREPARE, sock_inet_prepare);