From bf3b06b03d115883714d9e45cccfe185de490c21 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Wed, 26 Aug 2020 10:23:40 +0200 Subject: [PATCH] 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. --- include/haproxy/proto_tcp.h | 1 + src/haproxy.c | 6 ++++ src/proto_tcp.c | 59 +++++++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+) diff --git a/include/haproxy/proto_tcp.h b/include/haproxy/proto_tcp.h index 50748a68f..e3eeca91c 100644 --- a/include/haproxy/proto_tcp.h +++ b/include/haproxy/proto_tcp.h @@ -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); diff --git a/src/haproxy.c b/src/haproxy.c index 028ebcda0..5b3252256 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -110,6 +110,7 @@ #include #include #include +#include #include #include #include @@ -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. */ diff --git a/src/proto_tcp.c b/src/proto_tcp.c index 3d56cf60b..2f8ec22e8 100644 --- a/src/proto_tcp.c +++ b/src/proto_tcp.c @@ -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 . + */ +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. */