diff options
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | man/systemd.socket.xml | 8 | ||||
-rw-r--r-- | src/basic/missing.h | 22 | ||||
-rw-r--r-- | src/basic/socket-util.c | 77 | ||||
-rw-r--r-- | src/basic/socket-util.h | 4 | ||||
-rw-r--r-- | src/core/service.c | 10 | ||||
-rw-r--r-- | src/core/socket.c | 21 | ||||
-rw-r--r-- | src/test/test-socket-util.c | 26 |
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) { |