aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorJohn Levon <john.levon@nutanix.com>2022-04-21 13:43:44 +0100
committerGitHub <noreply@github.com>2022-04-21 13:43:44 +0100
commit3779fca8c766b18b6d68feda9ed7958aa60bd4cf (patch)
tree07401acbf0d8656dc1de00b2a9ecb3fec1d2a932 /lib
parent9ad7474568a6c9f1fbb12fb8048f2083078a8144 (diff)
downloadlibvfio-user-3779fca8c766b18b6d68feda9ed7958aa60bd4cf.zip
libvfio-user-3779fca8c766b18b6d68feda9ed7958aa60bd4cf.tar.gz
libvfio-user-3779fca8c766b18b6d68feda9ed7958aa60bd4cf.tar.bz2
support AFL++ fuzzing (#623)
To support fuzzing with AFL++, add a "pipe" transport that reads from stdin and outputs to stdout: this is the most convenient way of doing fuzzing. Add some docs on how to run a fuzzing session. Signed-off-by: John Levon <john.levon@nutanix.com> Reviewed-by: Swapnil Ingle <swapnil.ingle@nutanix.com> Reviewed-by: Thanos Makatos <thanos.makatos@nutanix.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/CMakeLists.txt9
-rw-r--r--lib/libvfio-user.c15
-rw-r--r--lib/private.h26
-rw-r--r--lib/tran.c346
-rw-r--r--lib/tran.h82
-rw-r--r--lib/tran_pipe.c470
-rw-r--r--lib/tran_pipe.h43
-rw-r--r--lib/tran_sock.c332
-rw-r--r--lib/tran_sock.h19
9 files changed, 1002 insertions, 340 deletions
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index 5605a9f..968f911 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -44,14 +44,23 @@ set(LIBOBJS
$<TARGET_OBJECTS:libvfio-user>
$<TARGET_OBJECTS:migration>
$<TARGET_OBJECTS:pci>
+ $<TARGET_OBJECTS:tran>
$<TARGET_OBJECTS:tran_sock>)
+if (WITH_TRAN_PIPE EQUAL 1)
+ set(LIBOBJS ${LIBOBJS}
+ $<TARGET_OBJECTS:tran_pipe>)
+
+ add_library(tran_pipe OBJECT tran_pipe.c)
+endif()
+
add_library(pci_caps OBJECT pci_caps.c)
add_library(dma OBJECT dma.c)
add_library(irq OBJECT irq.c)
add_library(libvfio-user OBJECT libvfio-user.c)
add_library(migration OBJECT migration.c)
add_library(pci OBJECT pci.c)
+add_library(tran OBJECT tran.c)
add_library(tran_sock OBJECT tran_sock.c)
add_library(vfio-user-shared SHARED ${LIBOBJS})
diff --git a/lib/libvfio-user.c b/lib/libvfio-user.c
index 88e2350..5b95865 100644
--- a/lib/libvfio-user.c
+++ b/lib/libvfio-user.c
@@ -56,6 +56,7 @@
#include "migration.h"
#include "pci.h"
#include "private.h"
+#include "tran_pipe.h"
#include "tran_sock.h"
static int
@@ -1645,9 +1646,15 @@ vfu_create_ctx(vfu_trans_t trans, const char *path, int flags, void *pvt,
return ERROR_PTR(EINVAL);
}
+#ifdef WITH_TRAN_PIPE
+ if (trans != VFU_TRANS_SOCK && trans != VFU_TRANS_PIPE) {
+ return ERROR_PTR(ENOTSUP);
+ }
+#else
if (trans != VFU_TRANS_SOCK) {
return ERROR_PTR(ENOTSUP);
}
+#endif
if (dev_type != VFU_DEV_TYPE_PCI) {
return ERROR_PTR(ENOTSUP);
@@ -1659,7 +1666,13 @@ vfu_create_ctx(vfu_trans_t trans, const char *path, int flags, void *pvt,
}
vfu_ctx->dev_type = dev_type;
- vfu_ctx->tran = &tran_sock_ops;
+ if (trans == VFU_TRANS_SOCK) {
+ vfu_ctx->tran = &tran_sock_ops;
+ } else {
+#ifdef WITH_TRAN_PIPE
+ vfu_ctx->tran = &tran_pipe_ops;
+#endif
+ }
vfu_ctx->tran_data = NULL;
vfu_ctx->pvt = pvt;
vfu_ctx->flags = flags;
diff --git a/lib/private.h b/lib/private.h
index 1d19e6f..4c483f2 100644
--- a/lib/private.h
+++ b/lib/private.h
@@ -65,7 +65,7 @@
* Outgoing requests are either stored in out.iov.iov_base, or out_iovecs. In
* the latter case, the iovecs refer to data that should not be freed.
*/
-typedef struct {
+typedef struct vfu_msg {
/* in/out */
struct vfio_user_header hdr;
@@ -81,30 +81,6 @@ typedef struct {
size_t nr_out_iovecs;
} vfu_msg_t;
-struct transport_ops {
- int (*init)(vfu_ctx_t *vfu_ctx);
-
- int (*get_poll_fd)(vfu_ctx_t *vfu_ctx);
-
- int (*attach)(vfu_ctx_t *vfu_ctx);
-
- int (*get_request_header)(vfu_ctx_t *vfu_ctx, struct vfio_user_header *hdr,
- int *fds, size_t *nr_fds);
-
- int (*recv_body)(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg);
-
- int (*reply)(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg, int err);
-
- int (*send_msg)(vfu_ctx_t *vfu_ctx, uint16_t msg_id,
- enum vfio_user_command cmd,
- void *send_data, size_t send_len,
- struct vfio_user_header *hdr,
- void *recv_data, size_t recv_len);
-
- void (*detach)(vfu_ctx_t *vfu_ctx);
- void (*fini)(vfu_ctx_t *vfu_ctx);
-};
-
typedef struct {
int err_efd; /* eventfd for irq err */
int req_efd; /* eventfd for irq req */
diff --git a/lib/tran.c b/lib/tran.c
new file mode 100644
index 0000000..ba49fd6
--- /dev/null
+++ b/lib/tran.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright (c) 2020 Nutanix Inc. All rights reserved.
+ *
+ * Authors: Thanos Makatos <thanos@nutanix.com>
+ * Swapnil Ingle <swapnil.ingle@nutanix.com>
+ * Felipe Franciosi <felipe@nutanix.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Nutanix nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <json.h>
+
+#include "libvfio-user.h"
+#include "migration.h"
+#include "tran.h"
+
+// FIXME: is this the value we want?
+#define SERVER_MAX_FDS 8
+
+/*
+ * Expected JSON is of the form:
+ *
+ * {
+ * "capabilities": {
+ * "max_msg_fds": 32,
+ * "max_data_xfer_size": 1048576
+ * "migration": {
+ * "pgsize": 4096
+ * }
+ * }
+ * }
+ *
+ * with everything being optional. Note that json_object_get_uint64() is only
+ * available in newer library versions, so we don't use it.
+ */
+int
+tran_parse_version_json(const char *json_str, int *client_max_fdsp,
+ size_t *client_max_data_xfer_sizep, size_t *pgsizep)
+{
+ struct json_object *jo_caps = NULL;
+ struct json_object *jo_top = NULL;
+ struct json_object *jo = NULL;
+ int ret = EINVAL;
+
+ if ((jo_top = json_tokener_parse(json_str)) == NULL) {
+ goto out;
+ }
+
+ if (!json_object_object_get_ex(jo_top, "capabilities", &jo_caps)) {
+ ret = 0;
+ goto out;
+ }
+
+ if (json_object_get_type(jo_caps) != json_type_object) {
+ goto out;
+ }
+
+ if (json_object_object_get_ex(jo_caps, "max_msg_fds", &jo)) {
+ if (json_object_get_type(jo) != json_type_int) {
+ goto out;
+ }
+
+ errno = 0;
+ *client_max_fdsp = (int)json_object_get_int64(jo);
+
+ if (errno != 0) {
+ goto out;
+ }
+ }
+
+ if (json_object_object_get_ex(jo_caps, "max_data_xfer_size", &jo)) {
+ if (json_object_get_type(jo) != json_type_int) {
+ goto out;
+ }
+
+ errno = 0;
+ *client_max_data_xfer_sizep = (int)json_object_get_int64(jo);
+
+ if (errno != 0) {
+ goto out;
+ }
+ }
+ if (json_object_object_get_ex(jo_caps, "migration", &jo)) {
+ struct json_object *jo2 = NULL;
+
+ if (json_object_get_type(jo) != json_type_object) {
+ goto out;
+ }
+
+ if (json_object_object_get_ex(jo, "pgsize", &jo2)) {
+ if (json_object_get_type(jo2) != json_type_int) {
+ goto out;
+ }
+
+ errno = 0;
+ *pgsizep = (size_t)json_object_get_int64(jo2);
+
+ if (errno != 0) {
+ goto out;
+ }
+ }
+ }
+
+ ret = 0;
+
+out:
+ /* We just need to put our top-level object. */
+ json_object_put(jo_top);
+ if (ret != 0) {
+ return ERROR_INT(ret);
+ }
+ return 0;
+}
+
+static int
+recv_version(vfu_ctx_t *vfu_ctx, uint16_t *msg_idp,
+ struct vfio_user_version **versionp)
+{
+ struct vfio_user_version *cversion = NULL;
+ vfu_msg_t msg = { { 0 } };
+ int ret;
+
+ *versionp = NULL;
+
+ ret = vfu_ctx->tran->recv_msg(vfu_ctx, &msg);
+
+ if (ret < 0) {
+ vfu_log(vfu_ctx, LOG_ERR, "failed to receive version: %m");
+ return ret;
+ }
+
+ *msg_idp = msg.hdr.msg_id;
+
+ if (msg.hdr.cmd != VFIO_USER_VERSION) {
+ vfu_log(vfu_ctx, LOG_ERR, "msg%#hx: invalid cmd %hu (expected %u)",
+ *msg_idp, msg.hdr.cmd, VFIO_USER_VERSION);
+ ret = EINVAL;
+ goto out;
+ }
+
+ if (msg.in.nr_fds != 0) {
+ vfu_log(vfu_ctx, LOG_ERR,
+ "msg%#hx: VFIO_USER_VERSION: sent with %zu fds", *msg_idp,
+ msg.in.nr_fds);
+ ret = EINVAL;
+ goto out;
+ }
+
+ if (msg.in.iov.iov_len < sizeof(*cversion)) {
+ vfu_log(vfu_ctx, LOG_ERR,
+ "msg%#hx: VFIO_USER_VERSION: invalid size %lu",
+ *msg_idp, msg.in.iov.iov_len);
+ ret = EINVAL;
+ goto out;
+ }
+
+ cversion = msg.in.iov.iov_base;
+
+ if (cversion->major != LIB_VFIO_USER_MAJOR) {
+ vfu_log(vfu_ctx, LOG_ERR, "unsupported client major %hu (must be %u)",
+ cversion->major, LIB_VFIO_USER_MAJOR);
+ ret = EINVAL;
+ goto out;
+ }
+
+ vfu_ctx->client_max_fds = 1;
+ vfu_ctx->client_max_data_xfer_size = VFIO_USER_DEFAULT_MAX_DATA_XFER_SIZE;
+
+ if (msg.in.iov.iov_len > sizeof(*cversion)) {
+ const char *json_str = (const char *)cversion->data;
+ size_t len = msg.in.iov.iov_len - sizeof(*cversion);
+ size_t pgsize = 0;
+
+ if (json_str[len - 1] != '\0') {
+ vfu_log(vfu_ctx, LOG_ERR, "ignoring invalid JSON from client");
+ ret = EINVAL;
+ goto out;
+ }
+
+ ret = tran_parse_version_json(json_str, &vfu_ctx->client_max_fds,
+ &vfu_ctx->client_max_data_xfer_size,
+ &pgsize);
+
+ if (ret < 0) {
+ /* No client-supplied strings in the log for release build. */
+#ifdef DEBUG
+ vfu_log(vfu_ctx, LOG_ERR, "failed to parse client JSON \"%s\"",
+ json_str);
+#else
+ vfu_log(vfu_ctx, LOG_ERR, "failed to parse client JSON");
+#endif
+ ret = errno;
+ goto out;
+ }
+
+ if (vfu_ctx->migration != NULL && pgsize != 0) {
+ ret = migration_set_pgsize(vfu_ctx->migration, pgsize);
+
+ if (ret != 0) {
+ vfu_log(vfu_ctx, LOG_ERR, "refusing client page size of %zu",
+ pgsize);
+ ret = errno;
+ goto out;
+ }
+ }
+
+ // FIXME: is the code resilient against ->client_max_fds == 0?
+ if (vfu_ctx->client_max_fds < 0 ||
+ vfu_ctx->client_max_fds > VFIO_USER_CLIENT_MAX_MSG_FDS_LIMIT) {
+ vfu_log(vfu_ctx, LOG_ERR, "refusing client max_msg_fds of %d",
+ vfu_ctx->client_max_fds);
+ ret = EINVAL;
+ goto out;
+ }
+ }
+
+out:
+ if (ret != 0) {
+ vfu_msg_t rmsg = { { 0 } };
+ size_t i;
+
+ rmsg.hdr = msg.hdr;
+
+ (void) vfu_ctx->tran->reply(vfu_ctx, &rmsg, ret);
+
+ for (i = 0; i < msg.in.nr_fds; i++) {
+ if (msg.in.fds[i] != -1) {
+ close(msg.in.fds[i]);
+ }
+ }
+
+ free(msg.in.iov.iov_base);
+
+ *versionp = NULL;
+ return ERROR_INT(ret);
+ }
+
+ *versionp = cversion;
+ return 0;
+}
+
+static int
+send_version(vfu_ctx_t *vfu_ctx, uint16_t msg_id,
+ struct vfio_user_version *cversion)
+{
+ struct vfio_user_version sversion = { 0 };
+ struct iovec iovecs[2] = { { 0 } };
+ char server_caps[1024];
+ vfu_msg_t msg = { { 0 } };
+ int slen;
+
+ if (vfu_ctx->migration == NULL) {
+ slen = snprintf(server_caps, sizeof(server_caps),
+ "{"
+ "\"capabilities\":{"
+ "\"max_msg_fds\":%u,"
+ "\"max_data_xfer_size\":%u"
+ "}"
+ "}", SERVER_MAX_FDS, SERVER_MAX_DATA_XFER_SIZE);
+ } else {
+ slen = snprintf(server_caps, sizeof(server_caps),
+ "{"
+ "\"capabilities\":{"
+ "\"max_msg_fds\":%u,"
+ "\"max_data_xfer_size\":%u,"
+ "\"migration\":{"
+ "\"pgsize\":%zu"
+ "}"
+ "}"
+ "}", SERVER_MAX_FDS, SERVER_MAX_DATA_XFER_SIZE,
+ migration_get_pgsize(vfu_ctx->migration));
+ }
+
+ // FIXME: we should save the client minor here, and check that before trying
+ // to send unsupported things.
+ sversion.major = LIB_VFIO_USER_MAJOR;
+ sversion.minor = MIN(cversion->minor, LIB_VFIO_USER_MINOR);
+
+ iovecs[0].iov_base = &sversion;
+ iovecs[0].iov_len = sizeof(sversion);
+ iovecs[1].iov_base = server_caps;
+ /* Include the NUL. */
+ iovecs[1].iov_len = slen + 1;
+
+ msg.hdr.cmd = VFIO_USER_VERSION;
+ msg.hdr.msg_id = msg_id;
+ msg.out_iovecs = iovecs;
+ msg.nr_out_iovecs = 2;
+
+ return vfu_ctx->tran->reply(vfu_ctx, &msg, 0);
+}
+
+int
+tran_negotiate(vfu_ctx_t *vfu_ctx)
+{
+ struct vfio_user_version *client_version = NULL;
+ uint16_t msg_id = 0x0bad;
+ int ret;
+
+ ret = recv_version(vfu_ctx, &msg_id, &client_version);
+
+ if (ret < 0) {
+ vfu_log(vfu_ctx, LOG_ERR, "failed to recv version: %m");
+ return ret;
+ }
+
+ ret = send_version(vfu_ctx, msg_id, client_version);
+
+ free(client_version);
+
+ if (ret < 0) {
+ vfu_log(vfu_ctx, LOG_ERR, "failed to send version: %m");
+ }
+
+ return ret;
+}
+
+/* ex: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/lib/tran.h b/lib/tran.h
new file mode 100644
index 0000000..fee96e8
--- /dev/null
+++ b/lib/tran.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2019 Nutanix Inc. All rights reserved.
+ *
+ * Authors: Thanos Makatos <thanos@nutanix.com>
+ * Swapnil Ingle <swapnil.ingle@nutanix.com>
+ * Felipe Franciosi <felipe@nutanix.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Nutanix nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#ifndef LIB_VFIO_USER_TRAN_H
+#define LIB_VFIO_USER_TRAN_H
+
+#include "libvfio-user.h"
+#include "private.h"
+
+struct transport_ops {
+ int (*init)(vfu_ctx_t *vfu_ctx);
+
+ int (*get_poll_fd)(vfu_ctx_t *vfu_ctx);
+
+ int (*attach)(vfu_ctx_t *vfu_ctx);
+
+ int (*get_request_header)(vfu_ctx_t *vfu_ctx, struct vfio_user_header *hdr,
+ int *fds, size_t *nr_fds);
+
+ int (*recv_body)(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg);
+
+ int (*reply)(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg, int err);
+
+ int (*recv_msg)(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg);
+
+ int (*send_msg)(vfu_ctx_t *vfu_ctx, uint16_t msg_id,
+ enum vfio_user_command cmd,
+ void *send_data, size_t send_len,
+ struct vfio_user_header *hdr,
+ void *recv_data, size_t recv_len);
+
+ void (*detach)(vfu_ctx_t *vfu_ctx);
+ void (*fini)(vfu_ctx_t *vfu_ctx);
+};
+
+/* The largest number of fd's we are prepared to receive. */
+// FIXME: value?
+#define VFIO_USER_CLIENT_MAX_MSG_FDS_LIMIT (1024)
+
+/*
+ * Parse JSON supplied from the other side into the known parameters. Note: they
+ * will not be set if not found in the JSON.
+ */
+int
+tran_parse_version_json(const char *json_str, int *client_max_fdsp,
+ size_t *client_max_data_xfer_sizep, size_t *pgsizep);
+
+int
+tran_negotiate(vfu_ctx_t *vfu_ctx);
+
+#endif /* LIB_VFIO_USER_TRAN_H */
+
+/* ex: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/lib/tran_pipe.c b/lib/tran_pipe.c
new file mode 100644
index 0000000..5b47142
--- /dev/null
+++ b/lib/tran_pipe.c
@@ -0,0 +1,470 @@
+/*
+ * Copyright (c) 2021 Nutanix Inc. All rights reserved.
+ *
+ * Authors: Thanos Makatos <thanos@nutanix.com>
+ * Swapnil Ingle <swapnil.ingle@nutanix.com>
+ * Felipe Franciosi <felipe@nutanix.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Nutanix nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include <sys/param.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include "tran_pipe.h"
+
+typedef struct {
+ int in_fd;
+ int out_fd;
+} tran_pipe_t;
+
+static int
+tran_pipe_send_iovec(int fd, uint16_t msg_id, bool is_reply,
+ enum vfio_user_command cmd,
+ struct iovec *iovecs, size_t nr_iovecs, int err)
+{
+ struct vfio_user_header hdr = { .msg_id = msg_id };
+ ssize_t ret;
+
+ if (nr_iovecs == 0) {
+ iovecs = alloca(sizeof(*iovecs));
+ nr_iovecs = 1;
+ }
+
+ if (is_reply) {
+ hdr.flags.type = VFIO_USER_F_TYPE_REPLY;
+ hdr.cmd = cmd;
+ if (err != 0) {
+ hdr.flags.error = 1U;
+ hdr.error_no = err;
+ }
+ } else {
+ hdr.cmd = cmd;
+ hdr.flags.type = VFIO_USER_F_TYPE_COMMAND;
+ }
+
+ iovecs[0].iov_base = &hdr;
+ iovecs[0].iov_len = sizeof(hdr);
+
+ ret = writev(fd, iovecs, nr_iovecs);
+
+ if (ret == -1) {
+ /* Treat a failed write due to EPIPE the same as a short write. */
+ if (errno == EPIPE) {
+ return ERROR_INT(ECONNRESET);
+ }
+ return -1;
+ } else if (ret < hdr.msg_size) {
+ return ERROR_INT(ECONNRESET);
+ }
+
+ return 0;
+}
+
+static int
+tran_pipe_get_msg(void *data, size_t len, int fd)
+{
+ ssize_t ret;
+
+ ret = read(fd, data, len);
+
+ if (ret == -1) {
+ return -1;
+ } else if (ret == 0) {
+ return ERROR_INT(ENOMSG);
+ } else if ((size_t)ret < len) {
+ return ERROR_INT(ECONNRESET);
+ }
+
+ return ret;
+}
+
+/*
+ * Receive a vfio-user message. If "len" is set to non-zero, the message should
+ * include data of that length, which is stored in the pre-allocated "data"
+ * pointer.
+ */
+static int
+tran_pipe_recv(int fd, struct vfio_user_header *hdr, bool is_reply,
+ uint16_t *msg_id, void *data, size_t *len)
+{
+ int ret;
+
+ /* FIXME if ret == -1 then fcntl can overwrite recv's errno */
+
+ ret = tran_pipe_get_msg(hdr, sizeof(*hdr), fd);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (is_reply) {
+ if (msg_id != NULL && hdr->msg_id != *msg_id) {
+ return ERROR_INT(EPROTO);
+ }
+
+ if (hdr->flags.type != VFIO_USER_F_TYPE_REPLY) {
+ return ERROR_INT(EINVAL);
+ }
+
+ if (hdr->flags.error == 1U) {
+ if (hdr->error_no <= 0) {
+ hdr->error_no = EINVAL;
+ }
+ return ERROR_INT(hdr->error_no);
+ }
+ } else {
+ if (hdr->flags.type != VFIO_USER_F_TYPE_COMMAND) {
+ return ERROR_INT(EINVAL);
+ }
+ if (msg_id != NULL) {
+ *msg_id = hdr->msg_id;
+ }
+ }
+
+ if (hdr->msg_size < sizeof(*hdr) || hdr->msg_size > SERVER_MAX_MSG_SIZE) {
+ return ERROR_INT(EINVAL);
+ }
+
+ if (len != NULL && *len > 0 && hdr->msg_size > sizeof(*hdr)) {
+ ret = read(fd, data, MIN(hdr->msg_size - sizeof(*hdr), *len));
+ if (ret < 0) {
+ return -1;
+ } else if (ret == 0) {
+ return ERROR_INT(ENOMSG);
+ } else if (*len != (size_t)ret) {
+ return ERROR_INT(ECONNRESET);
+ }
+ *len = ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Like tran_pipe_recv(), but will automatically allocate reply data.
+ */
+static int
+tran_pipe_recv_alloc(int fd, struct vfio_user_header *hdr, bool is_reply,
+ uint16_t *msg_id, void **datap, size_t *lenp)
+{
+ void *data;
+ size_t len;
+ int ret;
+
+ ret = tran_pipe_recv(fd, hdr, is_reply, msg_id, NULL, NULL);
+
+ if (ret != 0) {
+ return ret;
+ }
+
+ assert(hdr->msg_size >= sizeof(*hdr));
+ assert(hdr->msg_size <= SERVER_MAX_MSG_SIZE);
+
+ len = hdr->msg_size - sizeof(*hdr);
+
+ if (len == 0) {
+ *datap = NULL;
+ *lenp = 0;
+ return 0;
+ }
+
+ data = calloc(1, len);
+
+ if (data == NULL) {
+ return -1;
+ }
+
+ ret = read(fd, data, len);
+ if (ret < 0) {
+ ret = errno;
+ free(data);
+ return ERROR_INT(ret);
+ } else if (ret == 0) {
+ free(data);
+ return ERROR_INT(ENOMSG);
+ } else if (len != (size_t)ret) {
+ free(data);
+ return ERROR_INT(ECONNRESET);
+ }
+
+ *datap = data;
+ *lenp = len;
+ return 0;
+}
+
+/*
+ * FIXME: all these send/recv handlers need to be made robust against async
+ * messages.
+ */
+static int
+tran_pipe_msg_iovec(tran_pipe_t *tp, uint16_t msg_id,
+ enum vfio_user_command cmd,
+ struct iovec *iovecs, size_t nr_iovecs,
+ struct vfio_user_header *hdr,
+ void *recv_data, size_t recv_len)
+{
+ int ret = tran_pipe_send_iovec(tp->out_fd, msg_id, false, cmd, iovecs,
+ nr_iovecs, 0);
+ if (ret < 0) {
+ return ret;
+ }
+ if (hdr == NULL) {
+ hdr = alloca(sizeof(*hdr));
+ }
+ return tran_pipe_recv(tp->in_fd, hdr, true, &msg_id, recv_data, &recv_len);
+}
+
+static int
+tran_pipe_init(vfu_ctx_t *vfu_ctx)
+{
+ tran_pipe_t *tp = NULL;
+ int ret = 0;
+
+ assert(vfu_ctx != NULL);
+
+ tp = calloc(1, sizeof(tran_pipe_t));
+
+ if (tp == NULL) {
+ return -1;
+ }
+
+ tp->in_fd = -1;
+ tp->out_fd = -1;
+
+ if (ret != 0) {
+ free(tp);
+ return ERROR_INT(ret);
+ }
+
+ vfu_ctx->tran_data = tp;
+ return 0;
+}
+
+static int
+tran_pipe_get_poll_fd(vfu_ctx_t *vfu_ctx)
+{
+ tran_pipe_t *tp = vfu_ctx->tran_data;
+
+ return tp->in_fd;
+}
+
+static int
+tran_pipe_attach(vfu_ctx_t *vfu_ctx)
+{
+ tran_pipe_t *tp;
+ int ret;
+
+ assert(vfu_ctx != NULL);
+ assert(vfu_ctx->tran_data != NULL);
+
+ tp = vfu_ctx->tran_data;
+
+ tp->in_fd = STDIN_FILENO;
+ tp->out_fd = STDOUT_FILENO;
+
+ ret = tran_negotiate(vfu_ctx);
+ if (ret < 0) {
+ ret = errno;
+ tp->in_fd = -1;
+ tp->out_fd = -1;
+ return ERROR_INT(ret);
+ }
+
+ return 0;
+}
+
+static int
+tran_pipe_get_request_header(vfu_ctx_t *vfu_ctx, struct vfio_user_header *hdr,
+ int *fds UNUSED, size_t *nr_fds)
+{
+ tran_pipe_t *tp;
+
+ assert(vfu_ctx != NULL);
+ assert(vfu_ctx->tran_data != NULL);
+
+ tp = vfu_ctx->tran_data;
+
+ *nr_fds = 0;
+
+ return tran_pipe_get_msg(hdr, sizeof(*hdr), tp->in_fd);
+}
+
+static int
+tran_pipe_recv_body(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg)
+{
+ tran_pipe_t *tp;
+ int ret;
+
+ assert(vfu_ctx != NULL);
+ assert(vfu_ctx->tran_data != NULL);
+ assert(msg != NULL);
+
+ tp = vfu_ctx->tran_data;
+
+ assert(msg->in.iov.iov_len <= SERVER_MAX_MSG_SIZE);
+
+ msg->in.iov.iov_base = malloc(msg->in.iov.iov_len);
+
+ if (msg->in.iov.iov_base == NULL) {
+ return -1;
+ }
+
+ ret = read(tp->in_fd, msg->in.iov.iov_base, msg->in.iov.iov_len);
+
+ if (ret < 0) {
+ ret = errno;
+ free(msg->in.iov.iov_base);
+ msg->in.iov.iov_base = NULL;
+ return ERROR_INT(ret);
+ } else if (ret == 0) {
+ free(msg->in.iov.iov_base);
+ msg->in.iov.iov_base = NULL;
+ return ERROR_INT(ENOMSG);
+ } else if (ret != (int)msg->in.iov.iov_len) {
+ vfu_log(vfu_ctx, LOG_ERR, "msg%#hx: short read: expected=%zu, actual=%d",
+ msg->hdr.msg_id, msg->in.iov.iov_len, ret);
+ free(msg->in.iov.iov_base);
+ msg->in.iov.iov_base = NULL;
+ return ERROR_INT(EINVAL);
+ }
+
+ return 0;
+}
+
+static int
+tran_pipe_recv_msg(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg)
+{
+ tran_pipe_t *tp;
+
+ assert(vfu_ctx != NULL);
+ assert(vfu_ctx->tran_data != NULL);
+ assert(msg != NULL);
+
+ tp = vfu_ctx->tran_data;
+
+ if (tp->in_fd == -1) {
+ vfu_log(vfu_ctx, LOG_ERR, "%s: not connected", __func__);
+ return ERROR_INT(ENOTCONN);
+ }
+
+ return tran_pipe_recv_alloc(tp->in_fd, &msg->hdr, false, NULL,
+ &msg->in.iov.iov_base, &msg->in.iov.iov_len);
+}
+
+static int
+tran_pipe_reply(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg, int err)
+{
+ struct iovec *iovecs;
+ size_t nr_iovecs;
+ tran_pipe_t *tp;
+ int ret;
+
+ assert(vfu_ctx != NULL);
+ assert(vfu_ctx->tran_data != NULL);
+ assert(msg != NULL);
+
+ tp = vfu_ctx->tran_data;
+
+ /* First iovec entry is for msg header. */
+ nr_iovecs = (msg->nr_out_iovecs != 0) ? (msg->nr_out_iovecs + 1) : 2;
+ iovecs = calloc(nr_iovecs, sizeof(*iovecs));
+
+ if (iovecs == NULL) {
+ return -1;
+ }
+
+ if (msg->out_iovecs != NULL) {
+ bcopy(msg->out_iovecs, iovecs + 1,
+ msg->nr_out_iovecs * sizeof(*iovecs));
+ } else {
+ iovecs[1].iov_base = msg->out.iov.iov_base;
+ iovecs[1].iov_len = msg->out.iov.iov_len;
+ }
+
+ ret = tran_pipe_send_iovec(tp->out_fd, msg->hdr.msg_id, true, msg->hdr.cmd,
+ iovecs, nr_iovecs, err);
+
+ free(iovecs);
+
+ return ret;
+}
+
+static int
+tran_pipe_send_msg(vfu_ctx_t *vfu_ctx, uint16_t msg_id,
+ enum vfio_user_command cmd,
+ void *send_data, size_t send_len,
+ struct vfio_user_header *hdr,
+ void *recv_data, size_t recv_len)
+{
+ /* [0] is for the header. */
+ struct iovec iovecs[2] = {
+ [1] = {
+ .iov_base = send_data,
+ .iov_len = send_len
+ }
+ };
+ tran_pipe_t *tp;
+
+ assert(vfu_ctx != NULL);
+ assert(vfu_ctx->tran_data != NULL);
+
+ tp = vfu_ctx->tran_data;
+
+ return tran_pipe_msg_iovec(tp, msg_id, cmd, iovecs, ARRAY_SIZE(iovecs),
+ hdr, recv_data, recv_len);
+}
+
+static void
+tran_pipe_detach(vfu_ctx_t *vfu_ctx)
+{
+ assert(vfu_ctx != NULL);
+}
+
+static void
+tran_pipe_fini(vfu_ctx_t *vfu_ctx)
+{
+ assert(vfu_ctx != NULL);
+
+ free(vfu_ctx->tran_data);
+ vfu_ctx->tran_data = NULL;
+}
+
+struct transport_ops tran_pipe_ops = {
+ .init = tran_pipe_init,
+ .get_poll_fd = tran_pipe_get_poll_fd,
+ .attach = tran_pipe_attach,
+ .get_request_header = tran_pipe_get_request_header,
+ .recv_body = tran_pipe_recv_body,
+ .reply = tran_pipe_reply,
+ .recv_msg = tran_pipe_recv_msg,
+ .send_msg = tran_pipe_send_msg,
+ .detach = tran_pipe_detach,
+ .fini = tran_pipe_fini
+};
+
+/* ex: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/lib/tran_pipe.h b/lib/tran_pipe.h
new file mode 100644
index 0000000..11c1eaf
--- /dev/null
+++ b/lib/tran_pipe.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2021 Nutanix Inc. All rights reserved.
+ *
+ * Authors: Thanos Makatos <thanos@nutanix.com>
+ * Swapnil Ingle <swapnil.ingle@nutanix.com>
+ * Felipe Franciosi <felipe@nutanix.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Nutanix nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#ifndef LIB_VFIO_USER_TRAN_PIPE_H
+#define LIB_VFIO_USER_TRAN_PIPE_H
+
+#include "libvfio-user.h"
+#include "tran.h"
+
+extern struct transport_ops tran_pipe_ops;
+
+#endif /* LIB_VFIO_USER_TRAN_PIPE_H */
+
+/* ex: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/lib/tran_sock.c b/lib/tran_sock.c
index 19bf370..b894fb3 100644
--- a/lib/tran_sock.c
+++ b/lib/tran_sock.c
@@ -30,27 +30,19 @@
*
*/
-#include <assert.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <json.h>
-#include <stdio.h>
-#include <string.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/un.h>
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
-#include "libvfio-user.h"
-#include "migration.h"
-#include "private.h"
#include "tran_sock.h"
-// FIXME: is this the value we want?
-#define SERVER_MAX_FDS 8
-
typedef struct {
int listen_fd;
int conn_fd;
@@ -145,18 +137,6 @@ tran_sock_send(int sock, uint16_t msg_id, bool is_reply,
ARRAY_SIZE(iovecs), NULL, 0, 0);
}
-/*
- * Send an empty reply back to the other end with the given errno.
- */
-static int
-tran_sock_send_error(int sock, uint16_t msg_id,
- enum vfio_user_command cmd,
- int error)
-{
- return tran_sock_send_iovec(sock, msg_id, true, cmd,
- NULL, 0, NULL, 0, error);
-}
-
static int
get_msg(void *data, size_t len, int *fds, size_t *nr_fds, int sock_fd,
int sock_flags)
@@ -395,8 +375,7 @@ tran_sock_init(vfu_ctx_t *vfu_ctx)
ts = calloc(1, sizeof(tran_sock_t));
if (ts == NULL) {
- ret = errno;
- goto out;
+ return -1;
}
ts->listen_fd = -1;
@@ -462,277 +441,6 @@ tran_sock_get_poll_fd(vfu_ctx_t *vfu_ctx)
return ts->listen_fd;
}
-/*
- * Expected JSON is of the form:
- *
- * {
- * "capabilities": {
- * "max_msg_fds": 32,
- * "max_data_xfer_size": 1048576
- * "migration": {
- * "pgsize": 4096
- * }
- * }
- * }
- *
- * with everything being optional. Note that json_object_get_uint64() is only
- * available in newer library versions, so we don't use it.
- */
-int
-tran_parse_version_json(const char *json_str, int *client_max_fdsp,
- size_t *client_max_data_xfer_sizep, size_t *pgsizep)
-{
- struct json_object *jo_caps = NULL;
- struct json_object *jo_top = NULL;
- struct json_object *jo = NULL;
- int ret = EINVAL;
-
- if ((jo_top = json_tokener_parse(json_str)) == NULL) {
- goto out;
- }
-
- if (!json_object_object_get_ex(jo_top, "capabilities", &jo_caps)) {
- ret = 0;
- goto out;
- }
-
- if (json_object_get_type(jo_caps) != json_type_object) {
- goto out;
- }
-
- if (json_object_object_get_ex(jo_caps, "max_msg_fds", &jo)) {
- if (json_object_get_type(jo) != json_type_int) {
- goto out;
- }
-
- errno = 0;
- *client_max_fdsp = (int)json_object_get_int64(jo);
-
- if (errno != 0) {
- goto out;
- }
- }
-
- if (json_object_object_get_ex(jo_caps, "max_data_xfer_size", &jo)) {
- if (json_object_get_type(jo) != json_type_int) {
- goto out;
- }
-
- errno = 0;
- *client_max_data_xfer_sizep = (int)json_object_get_int64(jo);
-
- if (errno != 0) {
- goto out;
- }
- }
- if (json_object_object_get_ex(jo_caps, "migration", &jo)) {
- struct json_object *jo2 = NULL;
-
- if (json_object_get_type(jo) != json_type_object) {
- goto out;
- }
-
- if (json_object_object_get_ex(jo, "pgsize", &jo2)) {
- if (json_object_get_type(jo2) != json_type_int) {
- goto out;
- }
-
- errno = 0;
- *pgsizep = (size_t)json_object_get_int64(jo2);
-
- if (errno != 0) {
- goto out;
- }
- }
- }
-
- ret = 0;
-
-out:
- /* We just need to put our top-level object. */
- json_object_put(jo_top);
- if (ret != 0) {
- return ERROR_INT(ret);
- }
- return 0;
-}
-
-static int
-recv_version(vfu_ctx_t *vfu_ctx, int sock, uint16_t *msg_idp,
- struct vfio_user_version **versionp)
-{
- struct vfio_user_version *cversion = NULL;
- struct vfio_user_header hdr;
- size_t vlen = 0;
- int ret;
-
- *versionp = NULL;
-
- ret = tran_sock_recv_alloc(sock, &hdr, false, msg_idp,
- (void **)&cversion, &vlen);
-
- if (ret < 0) {
- vfu_log(vfu_ctx, LOG_ERR, "failed to receive version: %m");
- return ret;
- }
-
- if (hdr.cmd != VFIO_USER_VERSION) {
- vfu_log(vfu_ctx, LOG_ERR, "msg%#hx: invalid cmd %hu (expected %u)",
- *msg_idp, hdr.cmd, VFIO_USER_VERSION);
- ret = EINVAL;
- goto out;
- }
-
- if (vlen < sizeof(*cversion)) {
- vfu_log(vfu_ctx, LOG_ERR,
- "msg%#hx: VFIO_USER_VERSION: invalid size %lu", *msg_idp, vlen);
- ret = EINVAL;
- goto out;
- }
-
- if (cversion->major != LIB_VFIO_USER_MAJOR) {
- vfu_log(vfu_ctx, LOG_ERR, "unsupported client major %hu (must be %u)",
- cversion->major, LIB_VFIO_USER_MAJOR);
- ret = EINVAL;
- goto out;
- }
-
- vfu_ctx->client_max_fds = 1;
- vfu_ctx->client_max_data_xfer_size = VFIO_USER_DEFAULT_MAX_DATA_XFER_SIZE;
-
- if (vlen > sizeof(*cversion)) {
- const char *json_str = (const char *)cversion->data;
- size_t len = vlen - sizeof(*cversion);
- size_t pgsize = 0;
-
- if (json_str[len - 1] != '\0') {
- vfu_log(vfu_ctx, LOG_ERR, "ignoring invalid JSON from client");
- ret = EINVAL;
- goto out;
- }
-
- ret = tran_parse_version_json(json_str, &vfu_ctx->client_max_fds,
- &vfu_ctx->client_max_data_xfer_size,
- &pgsize);
-
- if (ret < 0) {
- /* No client-supplied strings in the log for release build. */
-#ifdef DEBUG
- vfu_log(vfu_ctx, LOG_ERR, "failed to parse client JSON \"%s\"",
- json_str);
-#else
- vfu_log(vfu_ctx, LOG_ERR, "failed to parse client JSON");
-#endif
- ret = errno;
- goto out;
- }
-
- if (vfu_ctx->migration != NULL && pgsize != 0) {
- ret = migration_set_pgsize(vfu_ctx->migration, pgsize);
-
- if (ret != 0) {
- vfu_log(vfu_ctx, LOG_ERR, "refusing client page size of %zu",
- pgsize);
- ret = errno;
- goto out;
- }
- }
-
- // FIXME: is the code resilient against ->client_max_fds == 0?
- if (vfu_ctx->client_max_fds < 0 ||
- vfu_ctx->client_max_fds > VFIO_USER_CLIENT_MAX_MSG_FDS_LIMIT) {
- vfu_log(vfu_ctx, LOG_ERR, "refusing client max_msg_fds of %d",
- vfu_ctx->client_max_fds);
- ret = EINVAL;
- goto out;
- }
- }
-
-out:
- if (ret != 0) {
- // FIXME: spec, is it OK to just have the header?
- (void) tran_sock_send_error(sock, *msg_idp, hdr.cmd, ret);
- free(cversion);
- *versionp = NULL;
- return ERROR_INT(ret);
- }
-
- *versionp = cversion;
- return 0;
-}
-
-static int
-send_version(vfu_ctx_t *vfu_ctx, int sock, uint16_t msg_id,
- struct vfio_user_version *cversion)
-{
- struct vfio_user_version sversion = { 0 };
- struct iovec iovecs[3] = { { 0 } };
- char server_caps[1024];
- int slen;
-
- if (vfu_ctx->migration == NULL) {
- slen = snprintf(server_caps, sizeof(server_caps),
- "{"
- "\"capabilities\":{"
- "\"max_msg_fds\":%u,"
- "\"max_data_xfer_size\":%u"
- "}"
- "}", SERVER_MAX_FDS, SERVER_MAX_DATA_XFER_SIZE);
- } else {
- slen = snprintf(server_caps, sizeof(server_caps),
- "{"
- "\"capabilities\":{"
- "\"max_msg_fds\":%u,"
- "\"max_data_xfer_size\":%u,"
- "\"migration\":{"
- "\"pgsize\":%zu"
- "}"
- "}"
- "}", SERVER_MAX_FDS, SERVER_MAX_DATA_XFER_SIZE,
- migration_get_pgsize(vfu_ctx->migration));
- }
-
- // FIXME: we should save the client minor here, and check that before trying
- // to send unsupported things.
- sversion.major = LIB_VFIO_USER_MAJOR;
- sversion.minor = MIN(cversion->minor, LIB_VFIO_USER_MINOR);
-
- /* [0] is for the header. */
- iovecs[1].iov_base = &sversion;
- iovecs[1].iov_len = sizeof(sversion);
- iovecs[2].iov_base = server_caps;
- /* Include the NUL. */
- iovecs[2].iov_len = slen + 1;
-
- return tran_sock_send_iovec(sock, msg_id, true, VFIO_USER_VERSION,
- iovecs, ARRAY_SIZE(iovecs), NULL, 0, 0);
-}
-
-static int
-negotiate(vfu_ctx_t *vfu_ctx, int sock)
-{
- struct vfio_user_version *client_version = NULL;
- uint16_t msg_id = 0x0bad;
- int ret;
-
- ret = recv_version(vfu_ctx, sock, &msg_id, &client_version);
-
- if (ret < 0) {
- vfu_log(vfu_ctx, LOG_ERR, "failed to recv version: %m");
- return ret;
- }
-
- ret = send_version(vfu_ctx, sock, msg_id, client_version);
-
- free(client_version);
-
- if (ret < 0) {
- vfu_log(vfu_ctx, LOG_ERR, "failed to send version: %m");
- }
-
- return ret;
-}
-
static int
tran_sock_attach(vfu_ctx_t *vfu_ctx)
{
@@ -755,7 +463,7 @@ tran_sock_attach(vfu_ctx_t *vfu_ctx)
return -1;
}
- ret = negotiate(vfu_ctx, ts->conn_fd);
+ ret = tran_negotiate(vfu_ctx);
if (ret < 0) {
ret = errno;
close(ts->conn_fd);
@@ -806,6 +514,11 @@ tran_sock_recv_body(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg)
ts = vfu_ctx->tran_data;
+ if (ts->conn_fd == -1) {
+ vfu_log(vfu_ctx, LOG_ERR, "%s: not connected", __func__);
+ return ERROR_INT(ENOTCONN);
+ }
+
assert(msg->in.iov.iov_len <= SERVER_MAX_MSG_SIZE);
msg->in.iov.iov_base = malloc(msg->in.iov.iov_len);
@@ -837,6 +550,26 @@ tran_sock_recv_body(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg)
}
static int
+tran_sock_recv_msg(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg)
+{
+ tran_sock_t *ts;
+
+ assert(vfu_ctx != NULL);
+ assert(vfu_ctx->tran_data != NULL);
+ assert(msg != NULL);
+
+ ts = vfu_ctx->tran_data;
+
+ if (ts->conn_fd == -1) {
+ vfu_log(vfu_ctx, LOG_ERR, "%s: not connected", __func__);
+ return ERROR_INT(ENOTCONN);
+ }
+
+ return tran_sock_recv_alloc(ts->conn_fd, &msg->hdr, false, NULL,
+ &msg->in.iov.iov_base, &msg->in.iov.iov_len);
+}
+
+static int
tran_sock_reply(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg, int err)
{
struct iovec *iovecs;
@@ -935,6 +668,7 @@ struct transport_ops tran_sock_ops = {
.get_request_header = tran_sock_get_request_header,
.recv_body = tran_sock_recv_body,
.reply = tran_sock_reply,
+ .recv_msg = tran_sock_recv_msg,
.send_msg = tran_sock_send_msg,
.detach = tran_sock_detach,
.fini = tran_sock_fini
diff --git a/lib/tran_sock.h b/lib/tran_sock.h
index efc5d60..084c0a6 100644
--- a/lib/tran_sock.h
+++ b/lib/tran_sock.h
@@ -34,28 +34,17 @@
#define LIB_VFIO_USER_TRAN_SOCK_H
#include "libvfio-user.h"
+#include "tran.h"
+
+extern struct transport_ops tran_sock_ops;
/*
* These are not public routines, but for convenience, they are used by the
* sample/test code as well as privately within libvfio-user.
*
- * Note there is currently only one transport - talking over a UNIX socket.
+ * Note there is currently only one real transport - talking over a UNIX socket.
*/
-/* The largest number of fd's we are prepared to receive. */
-// FIXME: value?
-#define VFIO_USER_CLIENT_MAX_MSG_FDS_LIMIT (1024)
-
-extern struct transport_ops tran_sock_ops;
-
-/*
- * Parse JSON supplied from the other side into the known parameters. Note: they
- * will not be set if not found in the JSON.
- */
-int
-tran_parse_version_json(const char *json_str, int *client_max_fdsp,
- size_t *client_max_data_xfer_sizep, size_t *pgsizep);
-
/*
* Send a message to the other end. The iovecs array should leave the first
* entry empty, as it will be used for the header.