]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
tree-wide: add helper for IPv4/IPv6 sockopts
authorLennart Poettering <lennart@poettering.net>
Thu, 10 Sep 2020 14:31:31 +0000 (16:31 +0200)
committerLennart Poettering <lennart@poettering.net>
Fri, 11 Sep 2020 08:33:13 +0000 (10:33 +0200)
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.

src/basic/missing_network.h
src/basic/socket-label.c
src/basic/socket-util.c
src/basic/socket-util.h
src/core/socket.c
src/resolve/resolved-dns-scope.c
src/resolve/resolved-dns-stream.c
src/resolve/resolved-dns-stub.c

index 257879405c9268adccd403ebf5c0c60aa1e0a28f..a25a1480f0c44b04bdfae862d5806b4426f6d1b2 100644 (file)
@@ -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
index ec52d81653694e5cbdf3df7b0ba925cfc471ce17..dd69eaaac2da520d8468179ba936d4dacea6f2b6 100644 (file)
@@ -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");
                 }
         }
 
index 7a3299672abc8b968e8082f1e300364063119e1f..a6552b26876af802353ea5935745c286064a88f0 100644 (file)
@@ -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;
+        }
+}
index fee9055cecd105b2219e4b3bf6cb45e27180028b..c36f90f75f79e049ef8be79d66ee71e9bfbb9ecc 100644 (file)
@@ -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);
index 1da5f304aad390cbf2fc9f7bab7cf07042c2499a..04177ea7a51404437f15d8a267d5c953f12a77b3 100644 (file)
@@ -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);
index e69ba3c758e1a41d6e4f574aa82c6de51fd6133a..2ad4544002d5dc1eb47cc6cdea83c1803b115bd1 100644 (file)
@@ -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)
index a814de6a1fd5bdf952b5b883d9617bc2a3ff0d54..e6f72f00b43a28553b107f5ba52bd696481cbe48 100644 (file)
@@ -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;
index 572be26c2d5b991c7eb05d140d077dd9a2ab4b68..1d23a199e9e840b5fe7fd1387c22364b1b98dcd1 100644 (file)
@@ -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;