summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2020-09-10 16:31:31 +0200
committerLennart Poettering <lennart@poettering.net>2020-09-11 10:33:13 +0200
commit5d0fe4233b98301a2f8644824dd1dec0c5fc3403 (patch)
tree0aa90c72b02056121b6a7ce9a90008793e54d931
parent00df39a56a247a9936ca0c0a79c17cd0dda31daa (diff)
downloadsystemd-5d0fe4233b98301a2f8644824dd1dec0c5fc3403.tar.gz
tree-wide: add helper for IPv4/IPv6 sockopts
A variety of sockopts exist both for IPv4 and IPv6 but require a different pair of sockopt level/option number. Let's add helpers for these that internally determine the right sockopt to call. This should shorten code that generically wants to support both ipv4 + ipv6 and for the first time adds correct support for some cases where we only called the ipv4 versions, and not the ipv6 options.
-rw-r--r--src/basic/missing_network.h5
-rw-r--r--src/basic/socket-label.c8
-rw-r--r--src/basic/socket-util.c157
-rw-r--r--src/basic/socket-util.h9
-rw-r--r--src/core/socket.c21
-rw-r--r--src/resolve/resolved-dns-scope.c51
-rw-r--r--src/resolve/resolved-dns-stream.c14
-rw-r--r--src/resolve/resolved-dns-stub.c27
8 files changed, 202 insertions, 90 deletions
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 = 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 = socket_set_recvpktinfo(fd, family, 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;