summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac1
-rw-r--r--man/systemd.socket.xml8
-rw-r--r--src/basic/missing.h22
-rw-r--r--src/basic/socket-util.c77
-rw-r--r--src/basic/socket-util.h4
-rw-r--r--src/core/service.c10
-rw-r--r--src/core/socket.c21
-rw-r--r--src/test/test-socket-util.c26
8 files changed, 157 insertions, 12 deletions
diff --git a/configure.ac b/configure.ac
index d58fff51c4..7d1ab2534c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -307,6 +307,7 @@ AM_CONDITIONAL([HAVE_PYTHON], [test "x$have_python" = "xyes"])
AC_CHECK_HEADERS([sys/capability.h], [], [AC_MSG_ERROR([*** POSIX caps headers not found])])
AC_CHECK_HEADERS([linux/btrfs.h], [], [])
AC_CHECK_HEADERS([linux/memfd.h], [], [])
+AC_CHECK_HEADERS([linux/vm_sockets.h], [], [], [#include <sys/socket.h>])
# unconditionally pull-in librt with old glibc versions
AC_SEARCH_LIBS([clock_gettime], [rt], [], [])
diff --git a/man/systemd.socket.xml b/man/systemd.socket.xml
index 0ce1203cfb..1d20a8f7f7 100644
--- a/man/systemd.socket.xml
+++ b/man/systemd.socket.xml
@@ -216,6 +216,14 @@
<varname>BindIPv6Only=</varname> setting (see below).
</para>
+ <para>If the address string is a string in the format
+ <literal>vsock:x:y</literal>, it is read as CID <literal>x</literal> on
+ a port <literal>y</literal> address in the
+ <constant>AF_VSOCK</constant> family. The CID is a unique 32-bit
+ integer identifier in <constant>AF_VSOCK</constant> analogous to an IP
+ address. Specifying the CID is optional, and may be set to the empty
+ string.</para>
+
<para>Note that <constant>SOCK_SEQPACKET</constant> (i.e.
<varname>ListenSequentialPacket=</varname>) is only available
for <constant>AF_UNIX</constant> sockets.
diff --git a/src/basic/missing.h b/src/basic/missing.h
index dd4425697f..480462357d 100644
--- a/src/basic/missing.h
+++ b/src/basic/missing.h
@@ -34,6 +34,7 @@
#include <net/ethernet.h>
#include <stdlib.h>
#include <sys/resource.h>
+#include <sys/socket.h>
#include <sys/syscall.h>
#include <uchar.h>
#include <unistd.h>
@@ -50,6 +51,23 @@
#include <linux/btrfs.h>
#endif
+#ifdef HAVE_LINUX_VM_SOCKETS_H
+#include <linux/vm_sockets.h>
+#else
+#define VMADDR_CID_ANY -1U
+struct sockaddr_vm {
+ unsigned short svm_family;
+ unsigned short svm_reserved1;
+ unsigned int svm_port;
+ unsigned int svm_cid;
+ unsigned char svm_zero[sizeof(struct sockaddr) -
+ sizeof(unsigned short) -
+ sizeof(unsigned short) -
+ sizeof(unsigned int) -
+ sizeof(unsigned int)];
+};
+#endif /* !HAVE_LINUX_VM_SOCKETS_H */
+
#include "macro.h"
#ifndef RLIMIT_RTTIME
@@ -1163,4 +1181,8 @@ struct ethtool_link_settings {
#define SOL_ALG 279
#endif
+#ifndef AF_VSOCK
+#define AF_VSOCK 40
+#endif
+
#include "missing_syscall.h"
diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c
index 4ebf106109..77f81a60ba 100644
--- a/src/basic/socket-util.c
+++ b/src/basic/socket-util.c
@@ -113,6 +113,30 @@ int socket_address_parse(SocketAddress *a, const char *s) {
memcpy(a->sockaddr.un.sun_path+1, s+1, l);
a->size = offsetof(struct sockaddr_un, sun_path) + 1 + l;
+ } else if (startswith(s, "vsock:")) {
+ /* AF_VSOCK socket in vsock:cid:port notation */
+ const char *cid_start = s + strlen("vsock:");
+
+ e = strchr(cid_start, ':');
+ if (!e)
+ return -EINVAL;
+
+ r = safe_atou(e+1, &u);
+ if (r < 0)
+ return r;
+
+ n = strndupa(cid_start, e - cid_start);
+ if (!isempty(n)) {
+ r = safe_atou(n, &a->sockaddr.vm.svm_cid);
+ if (r < 0)
+ return r;
+ } else
+ a->sockaddr.vm.svm_cid = VMADDR_CID_ANY;
+
+ a->sockaddr.vm.svm_family = AF_VSOCK;
+ a->sockaddr.vm.svm_port = u;
+ a->size = sizeof(struct sockaddr_vm);
+
} else {
e = strchr(s, ':');
if (e) {
@@ -289,6 +313,15 @@ int socket_address_verify(const SocketAddress *a) {
return 0;
+ case AF_VSOCK:
+ if (a->size != sizeof(struct sockaddr_vm))
+ return -EINVAL;
+
+ if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
+ return -EINVAL;
+
+ return 0;
+
default:
return -EAFNOSUPPORT;
}
@@ -394,6 +427,15 @@ bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
break;
+ case AF_VSOCK:
+ if (a->sockaddr.vm.svm_cid != b->sockaddr.vm.svm_cid)
+ return false;
+
+ if (a->sockaddr.vm.svm_port != b->sockaddr.vm.svm_port)
+ return false;
+
+ break;
+
default:
/* Cannot compare, so we assume the addresses are different */
return false;
@@ -480,15 +522,27 @@ bool socket_address_matches_fd(const SocketAddress *a, int fd) {
return socket_address_equal(a, &b);
}
-int sockaddr_port(const struct sockaddr *_sa) {
+int sockaddr_port(const struct sockaddr *_sa, unsigned *port) {
union sockaddr_union *sa = (union sockaddr_union*) _sa;
assert(sa);
- if (!IN_SET(sa->sa.sa_family, AF_INET, AF_INET6))
- return -EAFNOSUPPORT;
+ switch (sa->sa.sa_family) {
+ case AF_INET:
+ *port = be16toh(sa->in.sin_port);
+ return 0;
+
+ case AF_INET6:
+ *port = be16toh(sa->in6.sin6_port);
+ return 0;
+
+ case AF_VSOCK:
+ *port = sa->vm.svm_port;
+ return 0;
- return be16toh(sa->sa.sa_family == AF_INET6 ? sa->in6.sin6_port : sa->in.sin_port);
+ default:
+ return -EAFNOSUPPORT;
+ }
}
int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret) {
@@ -591,6 +645,18 @@ int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_
break;
+ case AF_VSOCK:
+ if (include_port)
+ r = asprintf(&p,
+ "vsock:%u:%u",
+ sa->vm.svm_cid,
+ sa->vm.svm_port);
+ else
+ r = asprintf(&p, "vsock:%u", sa->vm.svm_cid);
+ if (r < 0)
+ return -ENOMEM;
+ break;
+
default:
return -EOPNOTSUPP;
}
@@ -748,6 +814,9 @@ bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b
if (a->sa.sa_family == AF_INET6)
return memcmp(&a->in6.sin6_addr, &b->in6.sin6_addr, sizeof(a->in6.sin6_addr)) == 0;
+ if (a->sa.sa_family == AF_VSOCK)
+ return a->vm.svm_cid == b->vm.svm_cid;
+
return false;
}
diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h
index 2ef572badb..0df1a600af 100644
--- a/src/basic/socket-util.h
+++ b/src/basic/socket-util.h
@@ -30,6 +30,7 @@
#include <linux/if_packet.h>
#include "macro.h"
+#include "missing.h"
#include "util.h"
union sockaddr_union {
@@ -40,6 +41,7 @@ union sockaddr_union {
struct sockaddr_nl nl;
struct sockaddr_storage storage;
struct sockaddr_ll ll;
+ struct sockaddr_vm vm;
};
typedef struct SocketAddress {
@@ -100,7 +102,7 @@ const char* socket_address_get_path(const SocketAddress *a);
bool socket_ipv6_is_supported(void);
-int sockaddr_port(const struct sockaddr *_sa) _pure_;
+int sockaddr_port(const struct sockaddr *_sa, unsigned *port) _pure_;
int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret);
int getpeername_pretty(int fd, bool include_port, char **ret);
diff --git a/src/core/service.c b/src/core/service.c
index 73a8104d17..54074ff7bc 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -1292,10 +1292,10 @@ static int service_spawn(
return r;
}
- if (r == 0 && IN_SET(sa.sa.sa_family, AF_INET, AF_INET6)) {
+ if (r == 0 && IN_SET(sa.sa.sa_family, AF_INET, AF_INET6, AF_VSOCK)) {
_cleanup_free_ char *addr = NULL;
char *t;
- int port;
+ unsigned port;
r = sockaddr_pretty(&sa.sa, salen, true, false, &addr);
if (r < 0)
@@ -1306,9 +1306,9 @@ static int service_spawn(
return -ENOMEM;
our_env[n_env++] = t;
- port = sockaddr_port(&sa.sa);
- if (port < 0)
- return port;
+ r = sockaddr_port(&sa.sa, &port);
+ if (r < 0)
+ return r;
if (asprintf(&t, "REMOTE_PORT=%u", port) < 0)
return -ENOMEM;
diff --git a/src/core/socket.c b/src/core/socket.c
index 521688bed5..c4ce88c5f4 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -485,12 +485,13 @@ static void peer_address_hash_func(const void *p, struct siphash *state) {
const SocketPeer *s = p;
assert(s);
- assert(IN_SET(s->peer.sa.sa_family, AF_INET, AF_INET6));
if (s->peer.sa.sa_family == AF_INET)
siphash24_compress(&s->peer.in.sin_addr, sizeof(s->peer.in.sin_addr), state);
else if (s->peer.sa.sa_family == AF_INET6)
siphash24_compress(&s->peer.in6.sin6_addr, sizeof(s->peer.in6.sin6_addr), state);
+ else if (s->peer.sa.sa_family == AF_VSOCK)
+ siphash24_compress(&s->peer.vm.svm_cid, sizeof(s->peer.vm.svm_cid), state);
else
assert_not_reached("Unknown address family.");
}
@@ -508,6 +509,12 @@ static int peer_address_compare_func(const void *a, const void *b) {
return memcmp(&x->peer.in.sin_addr, &y->peer.in.sin_addr, sizeof(x->peer.in.sin_addr));
case AF_INET6:
return memcmp(&x->peer.in6.sin6_addr, &y->peer.in6.sin6_addr, sizeof(x->peer.in6.sin6_addr));
+ case AF_VSOCK:
+ if (x->peer.vm.svm_cid < y->peer.vm.svm_cid)
+ return -1;
+ if (x->peer.vm.svm_cid > y->peer.vm.svm_cid)
+ return 1;
+ return 0;
}
assert_not_reached("Black sheep in the family!");
}
@@ -594,7 +601,7 @@ int socket_acquire_peer(Socket *s, int fd, SocketPeer **p) {
if (r < 0)
return log_error_errno(errno, "getpeername failed: %m");
- if (!IN_SET(sa.peer.sa.sa_family, AF_INET, AF_INET6)) {
+ if (!IN_SET(sa.peer.sa.sa_family, AF_INET, AF_INET6, AF_VSOCK)) {
*p = NULL;
return 0;
}
@@ -941,6 +948,16 @@ static int instance_from_socket(int fd, unsigned nr, char **instance) {
break;
}
+ case AF_VSOCK:
+ if (asprintf(&r,
+ "%u-%u:%u-%u:%u",
+ nr,
+ local.vm.svm_cid, local.vm.svm_port,
+ remote.vm.svm_cid, remote.vm.svm_port) < 0)
+ return -ENOMEM;
+
+ break;
+
default:
assert_not_reached("Unhandled socket type.");
}
diff --git a/src/test/test-socket-util.c b/src/test/test-socket-util.c
index 1f853a7f16..d80613dc84 100644
--- a/src/test/test-socket-util.c
+++ b/src/test/test-socket-util.c
@@ -92,6 +92,14 @@ static void test_socket_address_parse(void) {
assert_se(socket_address_parse(&a, "@abstract") >= 0);
assert_se(a.sockaddr.sa.sa_family == AF_UNIX);
+
+ assert_se(socket_address_parse(&a, "vsock::1234") >= 0);
+ assert_se(a.sockaddr.sa.sa_family == AF_VSOCK);
+ assert_se(socket_address_parse(&a, "vsock:2:1234") >= 0);
+ assert_se(a.sockaddr.sa.sa_family == AF_VSOCK);
+ assert_se(socket_address_parse(&a, "vsock:2:1234x") < 0);
+ assert_se(socket_address_parse(&a, "vsock:2x:1234") < 0);
+ assert_se(socket_address_parse(&a, "vsock:2") < 0);
}
static void test_socket_address_parse_netlink(void) {
@@ -145,6 +153,14 @@ static void test_socket_address_equal(void) {
assert_se(socket_address_parse_netlink(&a, "firewall") >= 0);
assert_se(socket_address_parse_netlink(&b, "firewall") >= 0);
assert_se(socket_address_equal(&a, &b));
+
+ assert_se(socket_address_parse(&a, "vsock:2:1234") >= 0);
+ assert_se(socket_address_parse(&b, "vsock:2:1234") >= 0);
+ assert_se(socket_address_equal(&a, &b));
+ assert_se(socket_address_parse(&b, "vsock:2:1235") >= 0);
+ assert_se(!socket_address_equal(&a, &b));
+ assert_se(socket_address_parse(&b, "vsock:3:1234") >= 0);
+ assert_se(!socket_address_equal(&a, &b));
}
static void test_socket_address_get_path(void) {
@@ -161,6 +177,9 @@ static void test_socket_address_get_path(void) {
assert_se(socket_address_parse(&a, "/foo/bar") >= 0);
assert_se(streq(socket_address_get_path(&a), "/foo/bar"));
+
+ assert_se(socket_address_parse(&a, "vsock:2:1234") >= 0);
+ assert_se(!socket_address_get_path(&a));
}
static void test_socket_address_is(void) {
@@ -408,11 +427,18 @@ static void test_sockaddr_equal(void) {
.in6.sin6_port = 0,
.in6.sin6_addr = IN6ADDR_ANY_INIT,
};
+ union sockaddr_union e = {
+ .vm.svm_family = AF_VSOCK,
+ .vm.svm_port = 0,
+ .vm.svm_cid = VMADDR_CID_ANY,
+ };
assert_se(sockaddr_equal(&a, &a));
assert_se(sockaddr_equal(&a, &b));
assert_se(sockaddr_equal(&d, &d));
+ assert_se(sockaddr_equal(&e, &e));
assert_se(!sockaddr_equal(&a, &c));
assert_se(!sockaddr_equal(&b, &c));
+ assert_se(!sockaddr_equal(&a, &e));
}
static void test_sockaddr_un_len(void) {