This patch introduces socket_ipv6_is_supported() call that checks for
IPv6 availability. Code then check for it before using specific calls.
In order to be less intrusive, this patch avoids IPv6 entries being
parsed at all, this way we don't get such entries in the system and
all other code paths are automatically ignored. However an extra check
is done at socket_address_listen() to make sure of that.
As the number of Netlink messages is not know upfront anymore,
loopback-setup.c was refactored to dynamically calculate the sequence
number and count.
Lennart's suggestions were fixed and squashed with the original patch,
that was sent by Gustavo Sverzut Barbieri (barbieri@profusion.mobi).
#include "util.h"
#include "macro.h"
#include "loopback-setup.h"
#include "util.h"
#include "macro.h"
#include "loopback-setup.h"
-
-enum {
- REQUEST_NONE = 0,
- REQUEST_ADDRESS_IPV4 = 1,
- REQUEST_ADDRESS_IPV6 = 2,
- REQUEST_FLAGS = 4,
- REQUEST_ALL = 7
-};
+#include "socket-util.h"
#define NLMSG_TAIL(nmsg) \
((struct rtattr *) (((uint8_t*) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
#define NLMSG_TAIL(nmsg) \
((struct rtattr *) (((uint8_t*) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
-static int add_adresses(int fd, int if_loopback) {
+static int add_adresses(int fd, int if_loopback, unsigned *requests) {
union {
struct sockaddr sa;
struct sockaddr_nl nl;
union {
struct sockaddr sa;
struct sockaddr_nl nl;
request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
request.header.nlmsg_type = RTM_NEWADDR;
request.header.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_ACK;
request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
request.header.nlmsg_type = RTM_NEWADDR;
request.header.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_ACK;
- request.header.nlmsg_seq = REQUEST_ADDRESS_IPV4;
+ request.header.nlmsg_seq = *requests + 1;
ifaddrmsg = NLMSG_DATA(&request.header);
ifaddrmsg->ifa_family = AF_INET;
ifaddrmsg = NLMSG_DATA(&request.header);
ifaddrmsg->ifa_family = AF_INET;
if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0)
return -errno;
if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0)
return -errno;
+ (*requests)++;
+
+ if (!socket_ipv6_is_supported())
+ return 0;
request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
- request.header.nlmsg_seq = REQUEST_ADDRESS_IPV6;
+ request.header.nlmsg_seq = *requests + 1;
ifaddrmsg->ifa_family = AF_INET6;
ifaddrmsg->ifa_prefixlen = 128;
ifaddrmsg->ifa_family = AF_INET6;
ifaddrmsg->ifa_prefixlen = 128;
if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0)
return -errno;
if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0)
return -errno;
-static int start_interface(int fd, int if_loopback) {
+static int start_interface(int fd, int if_loopback, unsigned *requests) {
union {
struct sockaddr sa;
struct sockaddr_nl nl;
union {
struct sockaddr sa;
struct sockaddr_nl nl;
request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
request.header.nlmsg_type = RTM_NEWLINK;
request.header.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK;
request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
request.header.nlmsg_type = RTM_NEWLINK;
request.header.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK;
- request.header.nlmsg_seq = REQUEST_FLAGS;
+ request.header.nlmsg_seq = *requests + 1;
ifinfomsg = NLMSG_DATA(&request.header);
ifinfomsg->ifi_family = AF_UNSPEC;
ifinfomsg = NLMSG_DATA(&request.header);
ifinfomsg->ifi_family = AF_UNSPEC;
if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0)
return -errno;
if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0)
return -errno;
-static int read_response(int fd) {
+static int read_response(int fd, unsigned requests_max) {
union {
struct sockaddr sa;
struct sockaddr_nl nl;
union {
struct sockaddr sa;
struct sockaddr_nl nl;
if (response.header.nlmsg_type != NLMSG_ERROR ||
(pid_t) response.header.nlmsg_pid != getpid() ||
if (response.header.nlmsg_type != NLMSG_ERROR ||
(pid_t) response.header.nlmsg_pid != getpid() ||
- response.header.nlmsg_seq >= REQUEST_ALL)
+ response.header.nlmsg_seq >= requests_max)
return 0;
if ((size_t) l < NLMSG_LENGTH(sizeof(struct nlmsgerr)) ||
return 0;
if ((size_t) l < NLMSG_LENGTH(sizeof(struct nlmsgerr)) ||
struct sockaddr_nl nl;
struct sockaddr_storage storage;
} sa;
struct sockaddr_nl nl;
struct sockaddr_storage storage;
} sa;
- int requests = REQUEST_NONE;
-
+ unsigned requests = 0, i;
- if ((r = add_adresses(fd, if_loopback)) < 0)
+ if ((r = add_adresses(fd, if_loopback, &requests)) < 0)
- if ((r = start_interface(fd, if_loopback)) < 0)
+ if ((r = start_interface(fd, if_loopback, &requests)) < 0)
- do {
- if ((r = read_response(fd)) < 0)
+ for (i = 0; i < requests; i++) {
+ if ((r = read_response(fd, requests)) < 0)
-
- requests |= r;
-
- } while (requests != REQUEST_ALL);
#include "socket-util.h"
#include "missing.h"
#include "label.h"
#include "socket-util.h"
#include "missing.h"
#include "label.h"
int socket_address_parse(SocketAddress *a, const char *s) {
int r;
int socket_address_parse(SocketAddress *a, const char *s) {
int r;
if (*s == '[') {
/* IPv6 in [x:.....:z]:p notation */
if (*s == '[') {
/* IPv6 in [x:.....:z]:p notation */
+ if (!socket_ipv6_is_supported()) {
+ log_warning("Binding to IPv6 address not available since kernel does not support IPv6.");
+ return -EAFNOSUPPORT;
+ }
+
if (!(e = strchr(s+1, ']')))
return -EINVAL;
if (!(e = strchr(s+1, ']')))
return -EINVAL;
if (idx == 0)
return -EINVAL;
if (idx == 0)
return -EINVAL;
+ if (!socket_ipv6_is_supported()) {
+ log_warning("Binding to interface is not available since kernel does not support IPv6.");
+ return -EAFNOSUPPORT;
+ }
+
a->sockaddr.in6.sin6_family = AF_INET6;
a->sockaddr.in6.sin6_port = htons((uint16_t) u);
a->sockaddr.in6.sin6_scope_id = idx;
a->sockaddr.in6.sin6_addr = in6addr_any;
a->size = sizeof(struct sockaddr_in6);
a->sockaddr.in6.sin6_family = AF_INET6;
a->sockaddr.in6.sin6_port = htons((uint16_t) u);
a->sockaddr.in6.sin6_scope_id = idx;
a->sockaddr.in6.sin6_addr = in6addr_any;
a->size = sizeof(struct sockaddr_in6);
if (u <= 0 || u > 0xFFFF)
return -EINVAL;
if (u <= 0 || u > 0xFFFF)
return -EINVAL;
- a->sockaddr.in6.sin6_family = AF_INET6;
- a->sockaddr.in6.sin6_port = htons((uint16_t) u);
- a->sockaddr.in6.sin6_addr = in6addr_any;
- a->size = sizeof(struct sockaddr_in6);
+ if (socket_ipv6_is_supported()) {
+ a->sockaddr.in6.sin6_family = AF_INET6;
+ a->sockaddr.in6.sin6_port = htons((uint16_t) u);
+ a->sockaddr.in6.sin6_addr = in6addr_any;
+ a->size = sizeof(struct sockaddr_in6);
+ } else {
+ a->sockaddr.in4.sin_family = AF_INET;
+ a->sockaddr.in4.sin_port = htons((uint16_t) u);
+ a->sockaddr.in4.sin_addr.s_addr = INADDR_ANY;
+ a->size = sizeof(struct sockaddr_in);
+ }
if ((r = socket_address_verify(a)) < 0)
return r;
if ((r = socket_address_verify(a)) < 0)
return r;
+ if (socket_address_family(a) == AF_INET6 && !socket_ipv6_is_supported())
+ return -EAFNOSUPPORT;
+
r = label_socket_set(label);
if (r < 0)
return r;
r = label_socket_set(label);
if (r < 0)
return r;
return path_startswith(a->sockaddr.un.sun_path, prefix);
}
return path_startswith(a->sockaddr.un.sun_path, prefix);
}
+bool socket_ipv6_is_supported(void) {
+ return access("/sys/module/ipv6", F_OK) == 0;
+}
+
static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX] = {
[SOCKET_ADDRESS_DEFAULT] = "default",
[SOCKET_ADDRESS_BOTH] = "both",
static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX] = {
[SOCKET_ADDRESS_DEFAULT] = "default",
[SOCKET_ADDRESS_BOTH] = "both",
const char* socket_address_bind_ipv6_only_to_string(SocketAddressBindIPv6Only b);
SocketAddressBindIPv6Only socket_address_bind_ipv6_only_from_string(const char *s);
const char* socket_address_bind_ipv6_only_to_string(SocketAddressBindIPv6Only b);
SocketAddressBindIPv6Only socket_address_bind_ipv6_only_from_string(const char *s);
+bool socket_ipv6_is_supported(void);
+
int r, x;
r = setsockopt(fd, IPPROTO_IP, IP_TTL, &s->ip_ttl, sizeof(s->ip_ttl));
int r, x;
r = setsockopt(fd, IPPROTO_IP, IP_TTL, &s->ip_ttl, sizeof(s->ip_ttl));
- x = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &s->ip_ttl, sizeof(s->ip_ttl));
+
+ if (socket_ipv6_is_supported())
+ x = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &s->ip_ttl, sizeof(s->ip_ttl));
+ else {
+ x = -1;
+ errno = EAFNOSUPPORT;
+ }
if (r < 0 && x < 0)
log_warning("IP_TTL/IPV6_UNICAST_HOPS failed: %m");
if (r < 0 && x < 0)
log_warning("IP_TTL/IPV6_UNICAST_HOPS failed: %m");