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. */