MINOR: reload: determine the foreing binding status from the socket

Let's not look at the listener options passed by the original process
and determine from the socket itself whether it is configured for
transparent mode or not. This is cleaner and safer, and doesn't rely
on flag values that could possibly change between versions.
This commit is contained in:
Willy Tarreau 2020-08-26 10:23:40 +02:00
parent bca5a4e0a8
commit bf3b06b03d
3 changed files with 66 additions and 0 deletions

View File

@ -33,6 +33,7 @@ int tcp_pause_listener(struct listener *l);
int tcp_connect_server(struct connection *conn, int flags);
int tcp_get_src(int fd, struct sockaddr *sa, socklen_t salen, int dir);
int tcp_get_dst(int fd, struct sockaddr *sa, socklen_t salen, int dir);
int tcp_is_foreign(int fd, sa_family_t family);
/* Export some samples. */
int smp_fetch_src(const struct arg *args, struct sample *smp, const char *kw, void *private);

View File

@ -110,6 +110,7 @@
#include <haproxy/peers.h>
#include <haproxy/pool.h>
#include <haproxy/protocol.h>
#include <haproxy/proto_tcp.h>
#include <haproxy/proxy.h>
#include <haproxy/regex.h>
#include <haproxy/sample.h>
@ -1298,6 +1299,11 @@ static int get_old_sockets(const char *unixsocket)
sizeof(xfer_sock->options));
curoff += sizeof(xfer_sock->options);
/* determine the foreign status directly from the socket itself */
xfer_sock->options &= ~LI_O_FOREIGN;
if (tcp_is_foreign(fd, xfer_sock->addr.ss_family))
xfer_sock->options |= LI_O_FOREIGN;
/* keep only the v6only flag depending on what's currently
* active on the socket, and always drop the v4v6 one.
*/

View File

@ -681,6 +681,65 @@ static int compare_sockaddr(struct sockaddr_storage *a, struct sockaddr_storage
}
/* Returns true if the passed FD corresponds to a socket bound with LI_O_FOREIGN
* according to the various supported socket options. The socket's address family
* must be passed in <family>.
*/
int tcp_is_foreign(int fd, sa_family_t family)
{
int val __maybe_unused;
socklen_t len __maybe_unused;
switch (family) {
case AF_INET:
#if defined(IP_TRANSPARENT)
val = 0; len = sizeof(val);
if (getsockopt(fd, SOL_IP, IP_TRANSPARENT, &val, &len) == 0 && val)
return 1;
#endif
#if defined(IP_FREEBIND)
val = 0; len = sizeof(val);
if (getsockopt(fd, SOL_IP, IP_FREEBIND, &val, &len) == 0 && val)
return 1;
#endif
#if defined(IP_BINDANY)
val = 0; len = sizeof(val);
if (getsockopt(fd, IPPROTO_IP, IP_BINDANY, &val, &len) == 0 && val)
return 1;
#endif
#if defined(SO_BINDANY)
val = 0; len = sizeof(val);
if (getsockopt(fd, SOL_SOCKET, SO_BINDANY, &val, &len) == 0 && val)
return 1;
#endif
break;
case AF_INET6:
#if defined(IPV6_TRANSPARENT) && defined(SOL_IPV6)
val = 0; len = sizeof(val);
if (getsockopt(fd, SOL_IPV6, IPV6_TRANSPARENT, &val, &len) == 0 && val)
return 1;
#endif
#if defined(IP_FREEBIND)
val = 0; len = sizeof(val);
if (getsockopt(fd, SOL_IP, IP_FREEBIND, &val, &len) == 0 && val)
return 1;
#endif
#if defined(IPV6_BINDANY)
val = 0; len = sizeof(val);
if (getsockopt(fd, IPPROTO_IPV6, IPV6_BINDANY, &val, &len) == 0 && val)
return 1;
#endif
#if defined(SO_BINDANY)
val = 0; len = sizeof(val);
if (getsockopt(fd, SOL_SOCKET, SO_BINDANY, &val, &len) == 0 && val)
return 1;
#endif
break;
}
return 0;
}
/* sets the v6only_default flag according to the OS' default settings; for
* simplicity it's set to zero if not supported.
*/