From 78579f8b45ab22611ebc3b3e88564e83c38eac55 Mon Sep 17 00:00:00 2001
From: Ronan Pigott <ronan@rjp.ie>
Date: Thu, 29 Feb 2024 21:42:43 -0700
Subject: [PATCH 0430/1160] resolve: skip IP_UNICAST_IF for local sockets

SO_BINDTODEVICE was used during connect() to fix an issue where
IP_UNICAST_IF was improperly ignored for route lookups made by connect
in linux. This has since been resolved upstream [1][2], but as a result
we must apply the local socket excpetion to IP_UNICAST_IF as well.

The SO_BINDTODEVICE is no longer necessary, but left in place for 5.x
kernels.

[1] https://lore.kernel.org/all/20220829111554.GA1771@debian/
[2] https://lore.kernel.org/all/20221208145437.GA75680@debian/

(cherry picked from commit 51d056858eadc3068633b32c78acf248e0974f26)
---
 src/resolve/resolved-dns-scope.c | 24 +++++++++++++-----------
 1 file changed, 13 insertions(+), 11 deletions(-)

diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c
index 2e8b3e5580..b882d41d99 100644
--- a/src/resolve/resolved-dns-scope.c
+++ b/src/resolve/resolved-dns-scope.c
@@ -424,7 +424,15 @@ static int dns_scope_socket(
                         return r;
         }
 
-        if (ifindex != 0) {
+        bool addr_is_nonlocal = s->link &&
+            !manager_find_link_address(s->manager, sa.sa.sa_family, sockaddr_in_addr(&sa.sa)) &&
+            in_addr_is_localhost(sa.sa.sa_family, sockaddr_in_addr(&sa.sa)) == 0;
+
+        if (addr_is_nonlocal && ifindex != 0) {
+                /* As a special exception we don't use UNICAST_IF if we notice that the specified IP address
+                 * is on the local host. Otherwise, destination addresses on the local host result in
+                 * EHOSTUNREACH, since Linux won't send the packets out of the specified interface, but
+                 * delivers them directly to the local socket. */
                 r = socket_set_unicast_if(fd, sa.sa.sa_family, ifindex);
                 if (r < 0)
                         return r;
@@ -463,19 +471,13 @@ static int dns_scope_socket(
         else {
                 bool bound = false;
 
-                /* Let's temporarily bind the socket to the specified ifindex. The kernel currently takes
-                 * only the SO_BINDTODEVICE/SO_BINDTOINDEX ifindex into account when making routing decisions
+                /* Let's temporarily bind the socket to the specified ifindex. Older kernels only take
+                 * the SO_BINDTODEVICE/SO_BINDTOINDEX ifindex into account when making routing decisions
                  * in connect() — and not IP_UNICAST_IF. We don't really want any of the other semantics of
                  * SO_BINDTODEVICE/SO_BINDTOINDEX, hence we immediately unbind the socket after the fact
                  * again.
-                 *
-                 * As a special exception we don't do this if we notice that the specified IP address is on
-                 * the local host. SO_BINDTODEVICE in combination with destination addresses on the local
-                 * host result in EHOSTUNREACH, since Linux won't send the packets out of the specified
-                 * interface, but delivers them directly to the local socket. */
-                if (s->link &&
-                    !manager_find_link_address(s->manager, sa.sa.sa_family, sockaddr_in_addr(&sa.sa)) &&
-                    in_addr_is_localhost(sa.sa.sa_family, sockaddr_in_addr(&sa.sa)) == 0) {
+                 */
+                if (addr_is_nonlocal) {
                         r = socket_bind_to_ifindex(fd, ifindex);
                         if (r < 0)
                                 return r;
-- 
2.33.0