if (ret_socket_address)
*ret_socket_address = sa;
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
+ * 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))) {
+ r = socket_bind_to_ifindex(fd, ifindex);
+ if (r < 0)
+ return r;
+
+ bound = true;
+ }
+
r = connect(fd, &sa.sa, salen);
if (r < 0 && errno != EINPROGRESS)
return -errno;
+
+ if (bound) {
+ r = socket_bind_to_ifindex(fd, 0);
+ if (r < 0)
+ return r;
+ }
}
return TAKE_FD(fd);
if (r < 0)
return log_error_errno(r, "Could not create runtime directory: %m");
- /* Drop privileges, but keep three caps. Note that we drop those too, later on (see below) */
+ /* Drop privileges, but keep three caps. Note that we drop two of those too, later on (see below) */
r = drop_privileges(uid, gid,
(UINT64_C(1) << CAP_NET_RAW)| /* needed for SO_BINDTODEVICE */
(UINT64_C(1) << CAP_NET_BIND_SERVICE)| /* needed to bind on port 53 */
(void) manager_check_resolv_conf(m);
/* Let's drop the remaining caps now */
- r = capability_bounding_set_drop(0, true);
+ r = capability_bounding_set_drop((UINT64_C(1) << CAP_NET_RAW), true);
if (r < 0)
return log_error_errno(r, "Failed to drop remaining caps: %m");