diff --git a/include/haproxy/sock_inet.h b/include/haproxy/sock_inet.h index 48b23b3fd..6e3718a2d 100644 --- a/include/haproxy/sock_inet.h +++ b/include/haproxy/sock_inet.h @@ -27,6 +27,8 @@ #include +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); diff --git a/src/proto_tcp.c b/src/proto_tcp.c index fba60834b..144f4bcfa 100644 --- a/src/proto_tcp.c +++ b/src/proto_tcp.c @@ -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 to socket , unless is set, in which * case we try to bind . 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; diff --git a/src/sock_inet.c b/src/sock_inet.c index db1a8c00e..c5b80da1c 100644 --- a/src/sock_inet.c +++ b/src/sock_inet.c @@ -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);