#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
}
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");
}
}
#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"
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:
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;
+ }
+}
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);
(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) {
}
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");
}
}
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");
}
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;
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);
}
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)
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;
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;
}
/* 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;