diff --git a/src/basic/missing_network.h b/src/basic/missing_network.h index 257879405c..a25a1480f0 100644 --- a/src/basic/missing_network.h +++ b/src/basic/missing_network.h @@ -6,6 +6,11 @@ #define IPV6_UNICAST_IF 76 #endif +/* linux/in6.h or netinet/in.h */ +#ifndef IPV6_TRANSPARENT +#define IPV6_TRANSPARENT 75 +#endif + /* Not exposed but defined at include/net/ip.h */ #ifndef IPV4_MIN_MTU #define IPV4_MIN_MTU 68 diff --git a/src/basic/socket-label.c b/src/basic/socket-label.c index ec52d81653..dd69eaaac2 100644 --- a/src/basic/socket-label.c +++ b/src/basic/socket-label.c @@ -80,15 +80,15 @@ int socket_address_listen( } if (free_bind) { - r = setsockopt_int(fd, IPPROTO_IP, IP_FREEBIND, true); + r = socket_set_freebind(fd, socket_address_family(a), true); if (r < 0) - log_warning_errno(r, "IP_FREEBIND failed: %m"); + log_warning_errno(r, "IP_FREEBIND/IPV6_FREEBIND failed: %m"); } if (transparent) { - r = setsockopt_int(fd, IPPROTO_IP, IP_TRANSPARENT, true); + r = socket_set_transparent(fd, socket_address_family(a), true); if (r < 0) - log_warning_errno(r, "IP_TRANSPARENT failed: %m"); + log_warning_errno(r, "IP_TRANSPARENT/IPV6_TRANSPARENT failed: %m"); } } diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c index 7a3299672a..a6552b2687 100644 --- a/src/basic/socket-util.c +++ b/src/basic/socket-util.c @@ -26,6 +26,7 @@ #include "macro.h" #include "memory-util.h" #include "missing_socket.h" +#include "missing_network.h" #include "parse-util.h" #include "path-util.h" #include "process-util.h" @@ -1204,13 +1205,28 @@ ssize_t recvmsg_safe(int sockfd, struct msghdr *msg, int flags) { return n; } -int socket_pass_pktinfo(int fd, bool b) { +int socket_get_family(int fd, int *ret) { int af; socklen_t sl = sizeof(af); if (getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &af, &sl) < 0) return -errno; + if (sl != sizeof(af)) + return -EINVAL; + + return af; +} + +int socket_set_recvpktinfo(int fd, int af, bool b) { + int r; + + if (af == AF_UNSPEC) { + r = socket_get_family(fd, &af); + if (r < 0) + return r; + } + switch (af) { case AF_INET: @@ -1226,3 +1242,142 @@ int socket_pass_pktinfo(int fd, bool b) { return -EAFNOSUPPORT; } } + +int socket_set_recverr(int fd, int af, bool b) { + int r; + + if (af == AF_UNSPEC) { + r = socket_get_family(fd, &af); + if (r < 0) + return r; + } + + switch (af) { + + case AF_INET: + return setsockopt_int(fd, IPPROTO_IP, IP_RECVERR, b); + + case AF_INET6: + return setsockopt_int(fd, IPPROTO_IPV6, IPV6_RECVERR, b); + + default: + return -EAFNOSUPPORT; + } +} + +int socket_set_recvttl(int fd, int af, bool b) { + int r; + + if (af == AF_UNSPEC) { + r = socket_get_family(fd, &af); + if (r < 0) + return r; + } + + switch (af) { + + case AF_INET: + return setsockopt_int(fd, IPPROTO_IP, IP_RECVTTL, b); + + case AF_INET6: + return setsockopt_int(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, b); + + default: + return -EAFNOSUPPORT; + } +} + +int socket_set_ttl(int fd, int af, int ttl) { + int r; + + if (af == AF_UNSPEC) { + r = socket_get_family(fd, &af); + if (r < 0) + return r; + } + + switch (af) { + + case AF_INET: + return setsockopt_int(fd, IPPROTO_IP, IP_TTL, ttl); + + case AF_INET6: + return setsockopt_int(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, ttl); + + default: + return -EAFNOSUPPORT; + } +} + +int socket_set_unicast_if(int fd, int af, int ifi) { + be32_t ifindex_be = htobe32(ifi); + int r; + + if (af == AF_UNSPEC) { + r = socket_get_family(fd, &af); + if (r < 0) + return r; + } + + switch (af) { + + case AF_INET: + if (setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex_be, sizeof(ifindex_be)) < 0) + return -errno; + + return 0; + + case AF_INET6: + if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex_be, sizeof(ifindex_be)) < 0) + return -errno; + + return 0; + + default: + return -EAFNOSUPPORT; + } +} + +int socket_set_freebind(int fd, int af, bool b) { + int r; + + if (af == AF_UNSPEC) { + r = socket_get_family(fd, &af); + if (r < 0) + return r; + } + + switch (af) { + + case AF_INET: + return setsockopt_int(fd, IPPROTO_IP, IP_FREEBIND, b); + + case AF_INET6: + return setsockopt_int(fd, IPPROTO_IPV6, IPV6_FREEBIND, b); + + default: + return -EAFNOSUPPORT; + } +} + +int socket_set_transparent(int fd, int af, bool b) { + int r; + + if (af == AF_UNSPEC) { + r = socket_get_family(fd, &af); + if (r < 0) + return r; + } + + switch (af) { + + case AF_INET: + return setsockopt_int(fd, IPPROTO_IP, IP_TRANSPARENT, b); + + case AF_INET6: + return setsockopt_int(fd, IPPROTO_IPV6, IPV6_TRANSPARENT, b); + + default: + return -EAFNOSUPPORT; + } +} diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h index fee9055cec..c36f90f75f 100644 --- a/src/basic/socket-util.h +++ b/src/basic/socket-util.h @@ -261,4 +261,11 @@ int socket_bind_to_ifindex(int fd, int ifindex); ssize_t recvmsg_safe(int sockfd, struct msghdr *msg, int flags); -int socket_pass_pktinfo(int fd, bool b); +int socket_get_family(int fd, int *ret); +int socket_set_recvpktinfo(int fd, int af, bool b); +int socket_set_recverr(int fd, int af, bool b); +int socket_set_recvttl(int fd, int af, bool b); +int socket_set_ttl(int fd, int af, int ttl); +int socket_set_unicast_if(int fd, int af, int ifi); +int socket_set_freebind(int fd, int af, bool b); +int socket_set_transparent(int fd, int af, bool b); diff --git a/src/core/socket.c b/src/core/socket.c index 1da5f304aa..04177ea7a5 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -979,10 +979,11 @@ static void socket_close_fds(Socket *s) { (void) unlink(*i); } -static void socket_apply_socket_options(Socket *s, int fd) { +static void socket_apply_socket_options(Socket *s, SocketPort *p, int fd) { int r; assert(s); + assert(p); assert(fd >= 0); if (s->keep_alive) { @@ -1046,7 +1047,7 @@ static void socket_apply_socket_options(Socket *s, int fd) { } if (s->pass_pktinfo) { - r = socket_pass_pktinfo(fd, true); + r = socket_set_recvpktinfo(fd, socket_address_family(&p->address), true); if (r < 0) log_unit_warning_errno(UNIT(s), r, "Failed to enable packet info socket option: %m"); } @@ -1082,16 +1083,8 @@ static void socket_apply_socket_options(Socket *s, int fd) { } if (s->ip_ttl >= 0) { - int x; - - r = setsockopt_int(fd, IPPROTO_IP, IP_TTL, s->ip_ttl); - - if (socket_ipv6_is_supported()) - x = setsockopt_int(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, s->ip_ttl); - else - x = -EAFNOSUPPORT; - - if (r < 0 && x < 0) + r = socket_set_ttl(fd, socket_address_family(&p->address), s->ip_ttl); + if (r < 0) log_unit_warning_errno(UNIT(s), r, "IP_TTL/IPV6_UNICAST_HOPS failed: %m"); } @@ -1664,7 +1657,7 @@ static int socket_open_fds(Socket *_s) { if (p->fd < 0) return p->fd; - socket_apply_socket_options(s, p->fd); + socket_apply_socket_options(s, p, p->fd); socket_symlink(s); break; @@ -3004,7 +2997,7 @@ static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents, if (cfd < 0) goto fail; - socket_apply_socket_options(p->socket, cfd); + socket_apply_socket_options(p->socket, p, cfd); } socket_enter_running(p->socket, cfd); diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index e69ba3c758..2ad4544002 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -386,54 +386,27 @@ static int dns_scope_socket( } if (s->link) { - be32_t ifindex_be = htobe32(ifindex); - - if (sa.sa.sa_family == AF_INET) { - r = setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex_be, sizeof(ifindex_be)); - if (r < 0) - return -errno; - } else if (sa.sa.sa_family == AF_INET6) { - r = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex_be, sizeof(ifindex_be)); - if (r < 0) - return -errno; - } + r = socket_set_unicast_if(fd, sa.sa.sa_family, ifindex); + if (r < 0) + return r; } if (s->protocol == DNS_PROTOCOL_LLMNR) { /* RFC 4795, section 2.5 requires the TTL to be set to 1 */ - - if (sa.sa.sa_family == AF_INET) { - r = setsockopt_int(fd, IPPROTO_IP, IP_TTL, 1); - if (r < 0) - return r; - } else if (sa.sa.sa_family == AF_INET6) { - r = setsockopt_int(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, 1); - if (r < 0) - return r; - } + r = socket_set_ttl(fd, sa.sa.sa_family, 1); + if (r < 0) + return r; } if (type == SOCK_DGRAM) { /* Set IP_RECVERR or IPV6_RECVERR to get ICMP error feedback. See discussion in #10345. */ + r = socket_set_recverr(fd, sa.sa.sa_family, true); + if (r < 0) + return r; - if (sa.sa.sa_family == AF_INET) { - r = setsockopt_int(fd, IPPROTO_IP, IP_RECVERR, true); - if (r < 0) - return r; - - r = setsockopt_int(fd, IPPROTO_IP, IP_PKTINFO, true); - if (r < 0) - return r; - - } else if (sa.sa.sa_family == AF_INET6) { - r = setsockopt_int(fd, IPPROTO_IPV6, IPV6_RECVERR, true); - if (r < 0) - return r; - - r = setsockopt_int(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, true); - if (r < 0) - return r; - } + r = socket_set_recvpktinfo(fd, sa.sa.sa_family, true); + if (r < 0) + return r; } if (ret_socket_address) diff --git a/src/resolve/resolved-dns-stream.c b/src/resolve/resolved-dns-stream.c index a814de6a1f..e6f72f00b4 100644 --- a/src/resolve/resolved-dns-stream.c +++ b/src/resolve/resolved-dns-stream.c @@ -190,18 +190,10 @@ static int dns_stream_identify(DnsStream *s) { s->ifindex = manager_find_ifindex(s->manager, s->local.sa.sa_family, s->local.sa.sa_family == AF_INET ? (union in_addr_union*) &s->local.in.sin_addr : (union in_addr_union*) &s->local.in6.sin6_addr); if (s->protocol == DNS_PROTOCOL_LLMNR && s->ifindex > 0) { - be32_t ifindex = htobe32(s->ifindex); - /* Make sure all packets for this connection are sent on the same interface */ - if (s->local.sa.sa_family == AF_INET) { - r = setsockopt(s->fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex, sizeof(ifindex)); - if (r < 0) - log_debug_errno(errno, "Failed to invoke IP_UNICAST_IF: %m"); - } else if (s->local.sa.sa_family == AF_INET6) { - r = setsockopt(s->fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex, sizeof(ifindex)); - if (r < 0) - log_debug_errno(errno, "Failed to invoke IPV6_UNICAST_IF: %m"); - } + r = socket_set_unicast_if(s->fd, s->local.sa.sa_family, s->ifindex); + if (r < 0) + log_debug_errno(errno, "Failed to invoke IP_UNICAST_IF/IPV6_UNICAST_IF: %m"); } s->identified = true; diff --git a/src/resolve/resolved-dns-stub.c b/src/resolve/resolved-dns-stub.c index 572be26c2d..1d23a199e9 100644 --- a/src/resolve/resolved-dns-stub.c +++ b/src/resolve/resolved-dns-stub.c @@ -543,23 +543,13 @@ static int set_dns_stub_common_socket_options(int fd, int family) { if (r < 0) return r; - if (family == AF_INET) { - r = setsockopt_int(fd, IPPROTO_IP, IP_PKTINFO, true); - if (r < 0) - return r; + r = socket_set_recvpktinfo(fd, family, true); + if (r < 0) + return r; - r = setsockopt_int(fd, IPPROTO_IP, IP_RECVTTL, true); - if (r < 0) - return r; - } else { - r = setsockopt_int(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, true); - if (r < 0) - return r; - - r = setsockopt_int(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, true); - if (r < 0) - return r; - } + r = socket_set_recvttl(fd, family, true); + if (r < 0) + return r; return 0; } @@ -661,10 +651,7 @@ static int manager_dns_stub_fd_extra(Manager *m, DnsStubListenerExtra *l, int ty /* Do not set IP_TTL for extra DNS stub listners, as the address may not be local and in that case * people may want ttl > 1. */ - if (l->family == AF_INET) - r = setsockopt_int(fd, IPPROTO_IP, IP_FREEBIND, true); - else - r = setsockopt_int(fd, IPPROTO_IPV6, IPV6_FREEBIND, true); + r = socket_set_freebind(fd, l->family, true); if (r < 0) goto fail;