diff --git a/doc/configuration.txt b/doc/configuration.txt index 3acf5c15e..89cededa0 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -2193,6 +2193,7 @@ dns-accept-family [,...] - "ipv4": query and accept IPv4 addresses ("A" records) - "ipv6": query and accept IPv6 addresses ("AAAA" records) - "auto": use IPv4, and IPv6 if the system has a default gateway for it. + The result of the last check is cached for 30 seconds. When a single family is used, no request will be sent to resolvers for the other family, and any response for the othe family will be ignored. The diff --git a/include/haproxy/defaults.h b/include/haproxy/defaults.h index c18b2f079..7eba11ef6 100644 --- a/include/haproxy/defaults.h +++ b/include/haproxy/defaults.h @@ -349,6 +349,11 @@ #define SRV_CHK_INTER_THRES 1000 #endif +/* INET6 connectivity caching interval (in ms) */ +#ifndef INET6_CONNECTIVITY_CACHE_TIME +#define INET6_CONNECTIVITY_CACHE_TIME 30000 +#endif + /* Specifies the string used to report the version and release date on the * statistics page. May be defined to the empty string ("") to permanently * disable the feature. diff --git a/include/haproxy/sock_inet.h b/include/haproxy/sock_inet.h index 1d4a8b7a0..ad756558d 100644 --- a/include/haproxy/sock_inet.h +++ b/include/haproxy/sock_inet.h @@ -31,6 +31,7 @@ extern int sock_inet6_v6only_default; extern int sock_inet_tcp_maxseg_default; extern int sock_inet6_tcp_maxseg_default; extern int sock_inet6_seems_reachable; +extern uint last_inet6_check; #ifdef HA_HAVE_MPTCP extern int sock_inet_mptcp_maxseg_default; @@ -54,5 +55,6 @@ int sock_inet_is_foreign(int fd, sa_family_t family); int sock_inet4_make_foreign(int fd); int sock_inet6_make_foreign(int fd); int sock_inet_bind_receiver(struct receiver *rx, char **errmsg); +int is_inet6_reachable(void); #endif /* _HAPROXY_SOCK_INET_H */ diff --git a/src/resolvers.c b/src/resolvers.c index f8378a18b..d52303381 100644 --- a/src/resolvers.c +++ b/src/resolvers.c @@ -225,7 +225,7 @@ static inline int resolv_active_families(void) { if (resolv_accept_families & RSLV_AUTO_FAMILY) { /* Let's adjust our default resolver families based on apparent IPv6 connectivity */ - if (sock_inet6_seems_reachable) + if (is_inet6_reachable()) return RSLV_ACCEPT_IPV4 | RSLV_ACCEPT_IPV6; else return RSLV_ACCEPT_IPV4; diff --git a/src/sock_inet.c b/src/sock_inet.c index cf60c5a1c..abe99b360 100644 --- a/src/sock_inet.c +++ b/src/sock_inet.c @@ -81,6 +81,7 @@ int sock_inet6_tcp_maxseg_default = -1; /* indicate whether v6 looks reachable (this is only a hint) */ int sock_inet6_seems_reachable = 0; +uint last_inet6_check = TICK_ETERNITY; /* Default MPTCPv4/MPTCPv6 MSS settings. -1=unknown. */ #ifdef HA_HAVE_MPTCP @@ -472,6 +473,45 @@ int sock_inet_bind_receiver(struct receiver *rx, char **errmsg) goto bind_return; } + +/* Detects IPv6 reachability: for this we perform a UDP connect to address + * 2001:: on port 53. No packet will be sent, it will just check the routing + * table towards this prefix for the majority of public addresses. In case of + * error we assume no IPv6 connectivity. + * + * Returns non-zero if inet6 looks reachable, otherwise zero. This considers + * the last result if it ages less than 30s, otherwise triggers a new test + * which updates and . + */ +int is_inet6_reachable(void) +{ + uint last_check = HA_ATOMIC_LOAD(&last_inet6_check); + struct sockaddr_in6 dest = { }; + int ret = 0; + int fd; + + if (tick_isset(last_check) && + !tick_is_expired(tick_add(last_check, INET6_CONNECTIVITY_CACHE_TIME), HA_ATOMIC_LOAD(&global_now_ms))) + return HA_ATOMIC_LOAD(&sock_inet6_seems_reachable); + + /* update the test date to ensure nobody else does it in parallel */ + HA_ATOMIC_STORE(&last_inet6_check, HA_ATOMIC_LOAD(&global_now_ms)); + + fd = socket(AF_INET6, SOCK_DGRAM, 0); + if (fd >= 0) { + dest.sin6_family = AF_INET6; + dest.sin6_addr.s6_addr[0] = 0x20; + dest.sin6_addr.s6_addr[1] = 0x01; + dest.sin6_port = htons(53); + if (connect(fd, (struct sockaddr*)&dest, sizeof(dest)) == 0) + ret = 1; + close(fd); + } + + HA_ATOMIC_STORE(&sock_inet6_seems_reachable, ret); + return ret; +} + static void sock_inet_prepare() { int fd, val; @@ -529,24 +569,6 @@ static void sock_inet_prepare() close(fd); } #endif - - /* detect IPv6 reachability: for this we perform a UDP connect to - * address 2001:: on port 53. No packet will be sent, it will just - * check the routing table towards this prefix for the majority of - * public addresses. In case of error we assume no IPv6 connectivity. - */ - fd = socket(AF_INET6, SOCK_DGRAM, 0); - if (fd >= 0) { - struct sockaddr_in6 dest = { }; - - dest.sin6_family = AF_INET6; - dest.sin6_addr.s6_addr[0] = 0x20; - dest.sin6_addr.s6_addr[1] = 0x01; - dest.sin6_port = htons(53); - if (connect(fd, (struct sockaddr*)&dest, sizeof(dest)) == 0) - sock_inet6_seems_reachable = 1; - close(fd); - } } INITCALL0(STG_PREPARE, sock_inet_prepare);